【问题标题】:Draw Brush Is Not Completly Drawing When Moving Diagonally?画笔在对角线移动时画不完整?
【发布时间】:2012-03-04 23:05:34
【问题描述】:

currentPoint.xcurrentPoint.y 绘制到lastPoint.xlastPoint.y 时 在对角线的正上方或正下方,它在绘制的线中给了我间隙(空格),如下图所示。

 -(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];


}

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

【问题讨论】:

标签: iphone objective-c ipad


【解决方案1】:

我发现你的函数存在很多问题,其中大部分是糟糕的风格。您正在使用 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 点的默认笔画宽度进行描边,因此画笔的相邻图章之间存在间隙。

解决此问题的正确方法是停止使用ints 和纠错项来计算沿线的点。无论如何,Core Graphics 和 UIKit 都使用CGFloats,因此使用ints 不仅会获得不太准确的结果,还会触发从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;
}

如果你使用这个版本的方法,你会发现它看起来好多了,但仍然不是很完美:

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

解决这个问题的一个简单、俗气的方法是让你的笔画宽度稍微宽一点。只需在CGContextStrokePath 之前添加:

        CGContextSetLineWidth(gc, 1.1);

结果:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-01-16
    • 1970-01-01
    • 1970-01-01
    • 2013-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-11
    相关资源
    最近更新 更多