在对角线移动时,绘制画笔不完全绘制?

时间:2012-03-04 23:05:35

标签: iphone objective-c ipad

currentPoint.xcurrentPoint.ylastPoint.xlastPoint.y进行绘制时  在对角线右上方或右下方,它给出了绘制线中的间隙(空格),如下图所示。

sample screenshot

 -(void)brushType{
            UIGraphicsBeginImageContext(drawImage.frame.size);
            CGContextRef Mycontext  = UIGraphicsGetCurrentContext();
            int  x, cx, deltax, xstep,y, cy, deltay, ystep,error, st, dupe;
            int x0, y0, x1, y1;

            x0 = currentPoint.x;
            y0 = currentPoint.y;
            x1 = lastPoint.x;
            y1 = lastPoint.y;
            // find largest delta for pixel steps
            st =(abs(y1 - y0) > abs(x1 - x0));
            // if deltay > deltax then swap x,y    
            if (st) {

                (x0 ^= y0); 
                (y0 ^= x0); 
                (x0 ^= y0); //swap(x0, y0);
                (x1 ^= y1); 
                (y1 ^= x1);
                (x1 ^= y1); // swap(x1, y1);
          }
            deltax = abs(x1 - x0);
            deltay = abs(y1 - y0);
            error = (deltax / 4);
           y = y0;
           if (x0 > x1) {
                xstep = -1;
            }
            else {
                xstep = 1;
            }
            if (y0 > y1) { 
                ystep = -1;
            }
            else { 
                ystep = 1; 
            }

            for ((x = x0); (x != (x1 + xstep)); (x += xstep))
            {
                (cx = x);
                (cy = y); // copy of x, copy of y

                // if x,y swapped above, swap them back now
                if (st) { 
                    (cx ^= cy); 
                    (cy ^= cx); 
                    (cx ^= cy); 
                }

                (dupe = 0); // initialize no dupe

                if(!dupe) {

                    CGContextSetRGBStrokeColor(Mycontext, 0.0, 0.0, 0.0, 1.00f);               
                    CGContextMoveToPoint(Mycontext, cx+brushSize*4,cy-brushSize/2);
                    CGContextAddLineToPoint(Mycontext, cx-brushSize/2, cy+brushSize*4);
          }

              (error -= deltay); // converge toward end of line
                      if (error < 0) { // not done yet
                    (y += ystep);
                    (error += deltax);}
              }
           CGContextStrokePath(Mycontext);
      [drawImage.image drawInRect:CGRectMake(0, 0, drawImage.frame.size.width, drawImage.frame.size.height)];  
            drawImage.image = UIGraphicsGetImageFromCurrentImageContext();    
            UIGraphicsEndImageContext();
            lastPoint = currentPoint;

        }

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

     brushSize = 4;

    mouseSwiped = YES;
    UITouch *touch = [touches anyObject];   
    currentPoint = [touch locationInView:drawImage];
    currentPoint.y -= 20; 

    [self brushType];


}

如果有人有想法,请帮助我解决这个问题!

1 个答案:

答案 0 :(得分:2)

我看到你的功能出现了很多问题,其中大多数都是糟糕的风格。您正在使用XOR-swap,这使得代码难以阅读。您将所有变量声明在方法的顶部,这使得更难理解每个变量的生命周期。你把不必要的括号放在一边,使代码更难阅读。你在循环中反复调用CGContextSetRGBStrokeColor,但你只需要设置一次笔触颜色。首先让我们重写你的方法来解决这些问题:

static inline void swap(int *a, int *b) {
    int t = *a;
    *a = *b;
    *b = t;
}

-(void)brushType {
    int x0 = currentPoint.x;
    int y0 = currentPoint.y;
    int x1 = lastPoint.x;
    int y1 = lastPoint.y;
    int deltax = abs(x1 - x0);
    int deltay = abs(y1 - y0);

    int needSwap = deltay > deltax;
    if (needSwap) {
        swap(&x0, &y0);
        swap(&x1, &y1);
        swap(&deltax, &deltay);
    }

    int error = deltax / 4;
    int y = y0;
    int xstep = x0 > x1 ? -1 : 1;
    int ystep = y0 > y1 ? -1 : 1;

    CGSize size = self.drawImage.bounds.size;
    UIGraphicsBeginImageContext(size); {
        CGContextRef gc  = UIGraphicsGetCurrentContext();
        for (int x = x0; x != x1 + xstep; x += xstep)
        {
            int cx = x;
            int cy = y;
            if (needSwap)
                swap(&cx, &cy);

            CGContextMoveToPoint(gc, cx + brushSize*4, cy - brushSize/2);
            CGContextAddLineToPoint(gc, cx - brushSize/2, cy + brushSize*4);

            error -= deltay; // converge toward end of line
            if (error < 0) { // not done yet
                y += ystep;
                error += deltax;
            }
        }
        [UIColor.blackColor setStroke];
        CGContextStrokePath(gc);
        [self.drawImage.image drawInRect:CGRectMake(0, 0, size.width, size.height)];
        self.drawImage.image = UIGraphicsGetImageFromCurrentImageContext();    
    } UIGraphicsEndImageContext();
    lastPoint = currentPoint;
}

所以现在更容易理解你是从lastPointcurrentPoint的直线踩踏。如果该线主要是水平线,则在每一步将x递增1(或-1),并根据需要增加y以保持接近真实直线。如果该行主要是垂直的,则交换x和y。

这是问题所在。假设直线是45度角。你将在每一步将x增加1和y增加1。毕达哥拉斯告诉我们,你每步移动的距离为sqrt(2)≈1.4142点。由于您的“画笔”以1点的默认笔触宽度进行描边,因此画笔的相邻图章之间存在间隙。

解决此问题的正确方法是停止使用int和错误更正术语来计算沿线的点数。核心图形和UIKit无论如何都会使用CGFloat,所以使用int你不仅会得到不太准确的结果,而且会触发从CGFloatint的额外转换然后回来。

您需要做的是将x和y存储为CGFloat,并在每一步增加它们,使刷子标记之间的距离为1.

-(void)brushType {
    CGFloat dx = currentPoint.x - lastPoint.x;
    CGFloat dy = currentPoint.y - lastPoint.y;
    CGFloat length = hypotf(dx, dy);
    dx /= length;
    dy /= length;

    CGSize size = self.drawImage.bounds.size;
    // Bonus!  This works correctly on Retina devices!
    UIGraphicsBeginImageContextWithOptions(size, NO, self.drawImage.window.screen.scale); {
        CGContextRef gc  = UIGraphicsGetCurrentContext();

        for (CGFloat i = 0; i < length; ++i) {
            CGFloat x = lastPoint.x + i * dx;
            CGFloat y = lastPoint.y + i * dy;
            CGContextMoveToPoint(gc, x + brushSize * 4, y - brushSize / 2);
            CGContextAddLineToPoint(gc, x - brushSize / 2, y + brushSize * 4);
        }

        [UIColor.blackColor setStroke];
        CGContextSetLineWidth(gc, 1.01);
        CGContextStrokePath(gc);
        [self.drawImage.image drawAtPoint:CGPointZero];
        self.drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
    } UIGraphicsEndImageContext();
    lastPoint = currentPoint;
}

如果您使用此版本的方法,您会发现它看起来好多了,但仍然不太完美:

better screenshot

我认为,问题在于使用抗锯齿绘制对角线笔划,而抗锯齿是近似。如果您将1条宽度为1的线条分开1点,则每条线条对公共像素的部分着色不会完美相加。

一种简单,俗气的修复方法是让你的笔触宽度更宽一些。只需在CGContextStrokePath之前添加:

        CGContextSetLineWidth(gc, 1.1);

结果:

best screen shot