【问题标题】:How to implement undo in a drawing app如何在绘图应用程序中实现撤消
【发布时间】:2011-01-06 10:38:33
【问题描述】:

下面是绘画的代码sn-p。我可以像存储点一样在矢量图中撤消,然后从可变数组中删除最高的点,然后重绘。但是,它在光栅图中无法正常工作。

如果我使用 UIGraphicsGetCurrentContext() 作为上下文引用,撤消效果很好。但是 CGBitmapContextCreate() 的上下文在发出撤消操作时没有。

- (id)initWithFrame:(CGRect)frame {  
    objArray = [[NSMutableArray alloc] init];

    CGColorSpaceRef  colorSpace = CGColorSpaceCreateDeviceRGB();

    canvas = CGBitmapContextCreate(NULL, drawImage.frame.size.width, drawImage.frame.size.height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast);
    CGColorSpaceRelease(colorSpace);
    }
    return self;
}

 

- (void)drawRect:(CGRect)rect {

    CGContextRef context = UIGraphicsGetCurrentContext();

    CGImageRef imgRef = CGBitmapContextCreateImage(canvas);

    CGRect r = self.bounds;
    CGContextDrawImage(context, CGRectMake(0, 0, r.size.width, r.size.height), imgRef); 

    if(ok) {
        for (int i = 0; i < [objArray count]; i++) {
            CGPoint point = [[[objArray objectAtIndex: i] objectAtIndex:0] CGPointValue];
            CGContextMoveToPoint(canvas, point.x, point.y);

            for (int j = 0; j < [[objArray objectAtIndex:i] count]; j++) {                
                point = [[[objArray objectAtIndex: i] objectAtIndex:j] CGPointValue];
                CGContextAddLineToPoint(canvas, point.x, point.y);
                CGContextStrokePath(**canvas**);
                CGContextMoveToPoint(**canvas**, point.x, point.y);
            }            
        }
    }
    CGImageRelease(imgRef); 
}

 

- (void)undo:(id) sender {
    NSLog(@"click");
    if([objArray count] > 0)
        [objArray removeLastObject];
    ok = YES;
    [self setNeedsDisplay];     
}

 

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

    NSMutableArray *points = [NSMutableArray array];

    UITouch *touch = nil;

    if (touchPoint) {
        touch = [touches member:touchPoint];
    }

    end = [touch locationInView:self];

    [points addObject:[NSValue valueWithCGPoint:start]];

    [points addObject:[NSValue valueWithCGPoint:end]];

    [objArray addObject:points];

    CGContextMoveToPoint(**canvas**, start.x, start.y);

    CGContextAddLineToPoint(**canvas**, end.x, end.y);  
    CGContextSetLineCap(**canvas**, kCGLineCapRound);

    CGContextSetLineWidth(**canvas**, 40.0);

    CGContextStrokePath(**canvas**); 

    start = end;

    [self setNeedsDisplay];
}

【问题讨论】:

  • 如果我们不必访问神秘链接,您就更有可能获得帮助。使所需的努力尽可能小。我有一个可能的答案,但不想冒险点击您的链接。
  • @Matthew 抱歉。我删除了链接并进行了简短描述。如果您能给我任何提示,我将不胜感激。

标签: iphone objective-c


【解决方案1】:

使用光栅绘图,您每次都在更改画布中的像素,没有像矢量绘图中那样的对象。

因此,您拥有的唯一“状态”就是画布本身。为了允许撤消,您实际上需要在每次更改之前保存画布的副本。在您进行更改之前,您将复制旧的位图上下文,然后进行更改。如果用户选择撤消,那么您只需将保存的上下文复制到正常上下文。如果要允许多次撤消,则必须保存多个副本。

显然这会占用大量内存。从技术上讲,您实际上不必保存整个画布,只需保存其上发生更改的部分,并记录更改部分的位置。如果更改很小,那么您将节省相当多的内存,但有些更改会影响整个画布,不会保存任何内容。

使用存储更改像素的算法可能会节省更多内存,但处理开销可能不值得。

【讨论】:

    【解决方案2】:

    假设您将图像存储在 Image 对象中,请创建一个堆栈:

    堆栈 undoStack = ... 堆栈重做堆栈 = ...

    高内存解决方案

    当用户对图像进行更改时,您可以存储下一张图像(w 进行更改),以及下一张和下一张,依此类推。当用户想要撤消时,您可以通过从 undoStack 弹出并推入重做堆栈来恢复图像:

    void undo(){
        redoStack.push(undoStack.pop());
    }
    

    要重做,请使用相同的过程,但要倒退。

    低内存解决方案

    浓度与上面相同,但现在不是存储整个图像,您可以将修改后的图像与前一个(或与原始图像)进行异或,并仅存储已更改的像素和这些像素所在的坐标发生变化。如果更改不是很大,您甚至可以考虑对这个新的 XORed 图像进行四叉树封装以节省内存。

    【讨论】:

      猜你喜欢
      • 2015-07-28
      • 2011-12-13
      • 2011-03-29
      • 2012-01-17
      • 1970-01-01
      • 1970-01-01
      • 2011-04-03
      • 1970-01-01
      • 2012-01-12
      相关资源
      最近更新 更多