Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Makes drawing more smoothly #14

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

waterzhang0423
Copy link

  1. calc draw steps per bezier dynamically
  2. use setNeedsDisplayInRect instead of setNeedsDisplay

1. calc draw steps per bezier dynamically

2. use setNeedsDisplayInRect instead of setNeedsDisplay
Add a demo pic
@maxmeyers
Copy link

This seems good, but it looks like it's causing tests to fail. Also, you've made changes to the README that don't make sense. Can you pass the tests and revert the readme changes?

@waterzhang0423
Copy link
Author

Sorry, I pushed a commit accidentally. I reverted all the changes except JotDrawView.m and JotTouchBezier.m,but Travis still build failed. I've read the Travis check details, just can't get it. Can you tell me what should I do to pass the test?

@lauraskelton
Copy link
Contributor

Hi @waterzhang0423, thanks for contributing to jot! I hope you've found it useful.

Currently, the changes to JotTouchBezier aren't working with non-black drawing colors, and they don't seem to actually make the line drawing smoother (I think they make it less smooth, unfortunately). That's why the tests aren't passing- you can see in the Travis CI logs that the snapshot tests are failing, which is happening because of the black lines that were added on top of the colored test drawing paths.

The "min width bezier" section you added to JotTouchBezier is drawing a bezier curve on top of the variable-width bezier curve drawn by the existing code. What's the reason for adding this? Also, since the overlaid constant-width bezier curve is black, the result looks like this if the drawing is set to a color-

simulator screen shot dec 22 2015 7 35 16 pm

Drawing a minimum-width bezier curve on top of the variable-width bezier curve shouldn't do anything to improve the smoothness, since most of the points on a variable-width curve will be wider than this overlaid curve.

Removing that code leaves the shadow that you've added, which is also black, and conflicts with colored drawings like this-

simulator screen shot dec 22 2015 7 35 32 pm

As you can see, there are some individual dots visible here and the drawing is not very smooth, which the shadow highlights. Using the original kJotDrawStepsPerBezier for the number of dots per segment instead of your pull request's MAX(MIN(length,kJotDrawStepsPerBezier),10) results in a much smoother line-

simulator screen shot dec 22 2015 8 01 59 pm

Unfortunately, I don't think these changes to JotTouchBezier help with drawing smoothness (they seem to actually make the line less smooth), and they also aren't currently working with colored drawing lines.

I'm interested in the changes you made to JotDrawView to use setNeedsDisplayInRect instead of setNeedsDisplay. Is this just intended to help with performance? Could you explain what you were trying to do there? It would be awesome if this change to JotDrawView speeds up drawing performance!

@waterzhang0423
Copy link
Author

Hi, @lauraskelton, thank you for reply such detail. I feel so sorry now, I haven't tested drawing with non-black. I fixed it. I forgot to call [self.strokeColor setStroke]; when drawing minimum width bezier path. I'll explain why I wrote those "trick" code.

First, I run the jot example, and set drawingColor to black, I got this.
jot_orig
It looks smoother when drawing with non-black color. Obviously, it's not smooth(or not smooth enough) with black color.

4 points can draw a bezier, but a shorter bezier path should draw less points, so I thought maybe use 300 points to draw each bezier can be changed. So I calculated each path length(sum distances between 4 points), the accuracy of result may not right in some curve cases. Calc draw steps per bezier will get this.
jot_calc_steps
I think it looks smoother.

Then, I tried to find a way to blur the path edge, I thought it would make bezier path smoother, so I set shadow for each path. Now it is like this.
jot_draw_shadow

If set drawingStrokeWidth to 4.0(or less than 4.0), I will get this.
draw_min_bezier
Some path are interrupted, so that's why I draw a minimum width bezier path.

Final I get this.
final
final_with_noblack

About the setNeedsDisplayInRect, cause I drawing bitmap on Android, Android useinvalidate to refresh screen, just like setNeedsDisplay, and the invalidate method can pass a Rect parameter which tells system just redraw the area by specified Rect, it is more efficient than redraw the whole screen. So I thought setNeedsDisplayInRect would do the same thing.

@waterzhang0423
Copy link
Author

Fixed non-black drawing bug.

//
//  JotTouchBezier.m
//  jot
//
//  Created by Laura Skelton on 4/30/15.
//
//

#import "JotTouchBezier.h"

NSUInteger const kJotDrawStepsPerBezier = 300;
CGFloat const kJotBezierShadow = 0.65f;

static inline CGFloat pointLength(const CGPoint point1, const CGPoint point2){
    return sqrt(pow(point1.x - point2.x, 2) + pow(point1.y - point2.y, 2));
}

@implementation JotTouchBezier

+ (instancetype)withColor:(UIColor *)color
{
    JotTouchBezier *touchBezier = [JotTouchBezier new];

    touchBezier.strokeColor = color;

    return touchBezier;
}

- (void)jotDrawBezier
{
    if (self.constantWidth) {
        UIBezierPath *bezierPath = [UIBezierPath new];
        [bezierPath moveToPoint:self.startPoint];
        [bezierPath addCurveToPoint:self.endPoint controlPoint1:self.controlPoint1 controlPoint2:self.controlPoint2];
        bezierPath.lineWidth = self.startWidth;
        bezierPath.lineCapStyle = kCGLineCapRound;
        [self.strokeColor setStroke];
        [bezierPath strokeWithBlendMode:kCGBlendModeNormal alpha:1.f];
    } else {
        [self.strokeColor setFill];

        CGFloat minWidth = self.startWidth;
        CGFloat widthDelta = self.endWidth - self.startWidth;

        CGFloat length = pointLength(self.startPoint, self.controlPoint1)
                        + pointLength(self.controlPoint1, self.controlPoint2)
                        + pointLength(self.controlPoint2, self.endPoint);

        CGFloat drawSteps = MAX(MIN(length,kJotDrawStepsPerBezier),10);

        CGContextRef context = UIGraphicsGetCurrentContext();
        if (!context) {
            return;
        }

        // Set shadow makes bezier path more smoothly
        CGContextSetShadowWithColor(context, CGSizeMake(0, 0), kJotBezierShadow, self.strokeColor.CGColor);

        for (NSUInteger i = 0; i < drawSteps; i++) {

            CGFloat t = ((CGFloat)i) / (CGFloat)drawSteps;
            CGFloat tt = t * t;
            CGFloat ttt = tt * t;
            CGFloat u = 1.f - t;
            CGFloat uu = u * u;
            CGFloat uuu = uu * u;

            CGFloat x = uuu * self.startPoint.x;
            x += 3 * uu * t * self.controlPoint1.x;
            x += 3 * u * tt * self.controlPoint2.x;
            x += ttt * self.endPoint.x;

            CGFloat y = uuu * self.startPoint.y;
            y += 3 * uu * t * self.controlPoint1.y;
            y += 3 * u * tt * self.controlPoint2.y;
            y += ttt * self.endPoint.y;

            CGFloat pointWidth = self.startWidth + (ttt * widthDelta);
            minWidth = MIN(pointWidth, minWidth);

            [self.class jotDrawBezierPoint:CGPointMake(x, y) withWidth:pointWidth];
        }

        // Draw a min width bezier, in case of drawSteps is not enough
        CGFloat span = 0.0f;
        CGPoint startPoint = CGPointMake(self.startPoint.x, self.startPoint.y + span);
        CGPoint controlPoint1 = CGPointMake(self.controlPoint1.x, self.controlPoint1.y + span);
        CGPoint controlPoint2 = CGPointMake(self.controlPoint2.x, self.controlPoint2.y + span);
        CGPoint endPoint = CGPointMake(self.endPoint.x, self.endPoint.y + span);

        [self.strokeColor setStroke];
        CGContextSetLineWidth(context, minWidth);
        CGContextSetAllowsAntialiasing(context, true);
        CGContextSetLineCap(context, kCGLineCapRound);
        CGContextSetLineJoin(context, kCGLineJoinRound);
        CGContextMoveToPoint(context, startPoint.x, startPoint.y);
        CGContextAddCurveToPoint(context, controlPoint1.x, controlPoint1.y,
                                 controlPoint2.x, controlPoint2.y, endPoint.x, endPoint.y);
        CGContextStrokePath(context);
    }
}

+ (void)jotDrawBezierPoint:(CGPoint)point withWidth:(CGFloat)width
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    if (!context) {
        return;
    }

    CGContextFillEllipseInRect(context, CGRectInset(CGRectMake(point.x, point.y, 0.f, 0.f), -width / 2.f, -width / 2.f));
}

@end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants