void PathBuilderCG::Arc(const Point &aOrigin, Float aRadius, Float aStartAngle, Float aEndAngle, bool aAntiClockwise) { if (!aOrigin.IsFinite() || !IsFinite(aRadius) || !IsFinite(aStartAngle) || !IsFinite(aEndAngle)) { return; } // Disabled for now due to a CG bug when using CGPathAddArc with stroke // dashing and rotation transforms that are multiples of 90 degrees. See: // https://bugzilla.mozilla.org/show_bug.cgi?id=949661#c8 #if 0 // Core Graphic's initial coordinate system is y-axis up, whereas Moz2D's is // y-axis down. Core Graphics therefore considers "clockwise" to mean "sweep // in the direction of decreasing angle" whereas Moz2D considers it to mean // "sweep in the direction of increasing angle". In other words if this // Moz2D method is instructed to sweep anti-clockwise we need to tell // CGPathAddArc to sweep clockwise, and vice versa. Hence why we pass the // value of aAntiClockwise directly to CGPathAddArc's "clockwise" bool // parameter. CGPathAddArc(mCGPath, nullptr, aOrigin.x, aOrigin.y, aRadius, aStartAngle, aEndAngle, aAntiClockwise); #endif ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle, aAntiClockwise); }
void doRotatedEllipsesWithCGPath(CGContextRef context) { int i, totreps = 144.; CGMutablePathRef path = NULL; float tint = 1., tintIncrement = 1./totreps; // Create a new transform consisting of a 45 degree rotation. CGAffineTransform theTransform = CGAffineTransformMakeRotation(M_PI/4); // Apply a scaling transformation to the transform just created. theTransform = CGAffineTransformScale(theTransform, 1, 2); // Create a mutable CGPath object. path = CGPathCreateMutable(); if(!path){ fprintf(stderr, "Couldn't create path!\n"); return; } // Add a circular arc to the CGPath object, transformed // by an affine transform. CGPathAddArc(path, &theTransform, 0., 0., 45., 0., 2*M_PI, false); // Close the CGPath object. CGPathCloseSubpath(path); // Place the first ellipse at a good location. CGContextTranslateCTM(context, 100., 100.); for (i = 0 ; i < totreps ; i++){ CGContextBeginPath(context); // Add the CGPath object to the current path in the context. CGContextAddPath(context, path); // Set the fill color for this instance of the ellipse. CGContextSetRGBFillColor(context, tint, 0., 0., 1.); // Filling the path implicitly closes it. CGContextFillPath(context); // Compute the next tint color. tint -= tintIncrement; // Move over for the next ellipse. CGContextTranslateCTM(context, 1, 0.); } // Release the path when done with it. CGPathRelease(path); }
DRAW_TEST_P(CGContextFillMode, OverlappedEllipses) { CGContextRef context = GetDrawingContext(); CGRect bounds = GetDrawingBounds(); bounds = CGRectInset(bounds, 16.f, 16.f); CGFloat width = bounds.size.width; CGFloat height = bounds.size.height; CGFloat xstart = bounds.origin.x; CGFloat ystart = bounds.origin.y; CGPathDrawingMode fillMode = GetParam(); CGMutablePathRef leftCircles = CGPathCreateMutable(); CGPathMoveToPoint(leftCircles, NULL, xstart + .25 * width + .4 * height, ystart + .5 * height); CGPathAddArc(leftCircles, NULL, xstart + .25 * width, ystart + .5 * height, .4 * height, 0, M_PI, true); CGPathAddArc(leftCircles, NULL, xstart + .25 * width, ystart + .5 * height, .4 * height, M_PI, 0, true); CGPathMoveToPoint(leftCircles, NULL, xstart + .25 * width + .3 * height, ystart + .5 * height); CGPathAddArc(leftCircles, NULL, xstart + .25 * width, ystart + .5 * height, .3 * height, 0, M_PI, true); CGPathAddArc(leftCircles, NULL, xstart + .25 * width, ystart + .5 * height, .3 * height, M_PI, 0, true); CGPathMoveToPoint(leftCircles, NULL, xstart + .25 * width + .2 * height, ystart + .5 * height); CGPathAddArc(leftCircles, NULL, xstart + .25 * width, ystart + .5 * height, .2 * height, 0, M_PI, true); CGPathAddArc(leftCircles, NULL, xstart + .25 * width, ystart + .5 * height, .2 * height, M_PI, 0, true); CGPathMoveToPoint(leftCircles, NULL, xstart + .25 * width + .1 * height, ystart + .5 * height); CGPathAddArc(leftCircles, NULL, xstart + .25 * width, ystart + .5 * height, .1 * height, 0, M_PI, true); CGPathAddArc(leftCircles, NULL, xstart + .25 * width, ystart + .5 * height, .1 * height, M_PI, 0, true); CGPathCloseSubpath(leftCircles); CGMutablePathRef rightCircles = CGPathCreateMutable(); CGPathMoveToPoint(rightCircles, NULL, xstart + .75 * width + .4 * height, ystart + .5 * height); CGPathAddArc(rightCircles, NULL, xstart + .75 * width, ystart + .5 * height, .4 * height, 0, M_PI, false); CGPathAddArc(rightCircles, NULL, xstart + .75 * width, ystart + .5 * height, .4 * height, M_PI, 0, false); CGPathMoveToPoint(rightCircles, NULL, xstart + .75 * width + .3 * height, ystart + .5 * height); CGPathAddArc(rightCircles, NULL, xstart + .75 * width, ystart + .5 * height, .3 * height, 0, M_PI, true); CGPathAddArc(rightCircles, NULL, xstart + .75 * width, ystart + .5 * height, .3 * height, M_PI, 0, true); CGPathMoveToPoint(rightCircles, NULL, xstart + .75 * width + .2 * height, ystart + .5 * height); CGPathAddArc(rightCircles, NULL, xstart + .75 * width, ystart + .5 * height, .2 * height, 0, M_PI, false); CGPathAddArc(rightCircles, NULL, xstart + .75 * width, ystart + .5 * height, .2 * height, M_PI, 0, false); CGPathMoveToPoint(rightCircles, NULL, xstart + .75 * width + .1 * height, ystart + .5 * height); CGPathAddArc(rightCircles, NULL, xstart + .75 * width, ystart + .5 * height, .1 * height, 0, M_PI, true); CGPathAddArc(rightCircles, NULL, xstart + .75 * width, ystart + .5 * height, .1 * height, M_PI, 0, true); CGPathCloseSubpath(rightCircles); CGContextAddPath(context, leftCircles); CGContextAddPath(context, rightCircles); CGContextSetRGBFillColor(context, 0, 0, 1, 1); CGContextSetRGBStrokeColor(context, 1, 0, 0, 1); CGContextDrawPath(context, fillMode); CGPathRelease(leftCircles); CGPathRelease(rightCircles); }
void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool clockwise) { // Workaround for <rdar://problem/5189233> CGPathAddArc hangs or crashes when passed inf as start or end angle if (isfinite(sa) && isfinite(ea)) CGPathAddArc(m_path, 0, p.x(), p.y(), r, sa, ea, clockwise); }