From 721ba296b11e0c19eda2695d2e21eee3968fc31e Mon Sep 17 00:00:00 2001 From: jeffMeador Date: Thu, 9 Jun 2016 14:28:09 -0400 Subject: [PATCH 01/10] Implemented CGPathApply. Replaced pathComponent with CGPathElement. Rewrote CGPathAddEllipseInRect, CGPathAddArc, CGPathAddArcToPoint to support CGPathElement. Implemented CGPathGetCurrentPoint for use in CGPathAddArcToPoint. Removed caveats for CGPathAddArc, CGPathAddEllipseInRect. Caveat for CGPathAddArcToPoint remains. --- Frameworks/CoreGraphics/CGPath.mm | 442 ++++++++++++++++------------ Frameworks/include/CGPathInternal.h | 56 +--- include/CoreGraphics/CGPath.h | 4 +- 3 files changed, 254 insertions(+), 248 deletions(-) diff --git a/Frameworks/CoreGraphics/CGPath.mm b/Frameworks/CoreGraphics/CGPath.mm index ce87c940da..bb9c068b39 100644 --- a/Frameworks/CoreGraphics/CGPath.mm +++ b/Frameworks/CoreGraphics/CGPath.mm @@ -59,6 +59,12 @@ void addPoint(float ptX, float ptY) { __CGPath::~__CGPath() { if (_components) { + for (unsigned i = 0; i < _count; i++) { + CGPoint * points = _components[i].points; + if( points ){ + IwFree(points); + } + } IwFree(_components); } } @@ -66,62 +72,36 @@ void addPoint(float ptX, float ptY) { void __CGPath::_applyPath(CGContextRef context) { for (unsigned i = 0; i < _count; i++) { switch (_components[i].type) { - case pathComponentRectangle: - CGContextAddRect(context, _components[i].rect); - break; - - case pathComponentMove: - TraceVerbose(TAG, L"Move to %d, %d", (int)_components[i].point.x, (int)_components[i].point.y); - CGContextMoveToPoint(context, _components[i].point.x, _components[i].point.y); - break; - case pathComponentLineTo: - TraceVerbose(TAG, L"Line to %d, %d", (int)_components[i].point.x, (int)_components[i].point.y); - CGContextAddLineToPoint(context, _components[i].point.x, _components[i].point.y); + case kCGPathElementMoveToPoint: + TraceVerbose(TAG, L"Move to %d, %d", (int)_components[i].points[0].x, (int)_components[i].points[0].y); + CGContextMoveToPoint(context, _components[i].points[0].x, _components[i].points[0].y); break; - case pathComponentArcToPoint: - CGContextAddArcToPoint(context, - _components[i].atp.x1, - _components[i].atp.y1, - _components[i].atp.x2, - _components[i].atp.y2, - _components[i].atp.radius); + case kCGPathElementAddLineToPoint: + TraceVerbose(TAG, L"Line to %d, %d", (int)_components[i].points[0].x, (int)_components[i].points[0].y); + CGContextAddLineToPoint(context, _components[i].points[0].x, _components[i].points[0].y); break; - case pathComponentArcAngle: - CGContextAddArc(context, - _components[i].aa.x, - _components[i].aa.y, - _components[i].aa.radius, - _components[i].aa.startAngle, - _components[i].aa.endAngle, - _components[i].aa.clockwise); - break; - - case pathComponentCurve: + case kCGPathElementAddCurveToPoint: CGContextAddCurveToPoint(context, - _components[i].ctp.x1, - _components[i].ctp.y1, - _components[i].ctp.x2, - _components[i].ctp.y2, - _components[i].ctp.x, - _components[i].ctp.y); - break; - - case pathComponentEllipseInRect: - CGContextAddEllipseInRect(context, _components[i].eir.rect); + _components[i].points[0].x, + _components[i].points[0].y, + _components[i].points[1].x, + _components[i].points[1].y, + _components[i].points[2].x, + _components[i].points[2].y); break; - case pathComponentQuadCurve: - CGContextAddQuadCurveToPoint(context, - _components[i].qtp.cpx, - _components[i].qtp.cpy, - _components[i].qtp.x, - _components[i].qtp.y); + case kCGPathElementAddQuadCurveToPoint: + CGContextAddQuadCurveToPoint(context, + _components[i].points[0].x, + _components[i].points[0].y, + _components[i].points[1].x, + _components[i].points[1].y); break; - case pathComponentClose: + case kCGPathElementCloseSubpath: CGContextClosePath(context); break; @@ -137,39 +117,23 @@ void addPoint(float ptX, float ptY) { for (unsigned i = 0; i < _count; i++) { switch (_components[i].type) { - case pathComponentMove: - case pathComponentLineTo: - bbox.addPoint(_components[i].point.x, _components[i].point.y); - break; - - case pathComponentArcToPoint: - bbox.addPoint(_components[i].atp.x1, _components[i].atp.y1); - bbox.addPoint(_components[i].atp.x2, _components[i].atp.y2); - break; - - case pathComponentCurve: - bbox.addPoint(_components[i].ctp.x1, _components[i].ctp.y1); - bbox.addPoint(_components[i].ctp.x2, _components[i].ctp.y2); - bbox.addPoint(_components[i].ctp.x, _components[i].ctp.y); - break; - - case pathComponentArcAngle: - bbox.addPoint(_components[i].aa.x - _components[i].aa.radius, _components[i].aa.y - _components[i].aa.radius); - bbox.addPoint(_components[i].aa.x + _components[i].aa.radius, _components[i].aa.y + _components[i].aa.radius); + case kCGPathElementMoveToPoint: + case kCGPathElementAddLineToPoint: + bbox.addPoint(_components[i].points[0].x, _components[i].points[0].y); break; - case pathComponentQuadCurve: - bbox.addPoint(_components[i].qtp.cpx, _components[i].qtp.cpy); - bbox.addPoint(_components[i].qtp.x, _components[i].qtp.y); + case kCGPathElementAddCurveToPoint: + bbox.addPoint(_components[i].points[0].x, _components[i].points[0].y); + bbox.addPoint(_components[i].points[1].x, _components[i].points[1].y); + bbox.addPoint(_components[i].points[2].x, _components[i].points[2].y); break; - case pathComponentEllipseInRect: - bbox.addPoint(_components[i].eir.rect.origin.x, _components[i].eir.rect.origin.y); - bbox.addPoint(_components[i].eir.rect.origin.x + _components[i].eir.rect.size.width, - _components[i].eir.rect.origin.y + _components[i].eir.rect.size.height); + case kCGPathElementAddQuadCurveToPoint: + bbox.addPoint(_components[i].points[0].x, _components[i].points[0].y); + bbox.addPoint(_components[i].points[1].x, _components[i].points[1].y); break; - case pathComponentClose: + case kCGPathElementCloseSubpath: break; default: @@ -214,8 +178,8 @@ CGMutablePathRef CGPathCreateMutableCopy(CGPathRef path) { auto ret = __CGPath::alloc(nil); ret->_max = path->_max; ret->_count = path->_count; - ret->_components = (pathComponent*)IwRealloc(ret->_components, ret->_max * sizeof(pathComponent)); - memcpy(ret->_components, path->_components, path->_count * sizeof(pathComponent)); + ret->_components = (CGPathElement*)IwRealloc(ret->_components, ret->_max * sizeof(CGPathElement)); + memcpy(ret->_components, path->_components, path->_count * sizeof(CGPathElement)); return ret; } @@ -239,47 +203,90 @@ void CGPathAddLineToPoint(CGMutablePathRef path, const CGAffineTransform* m, flo if (pathObj->_count + 1 >= pathObj->_max) { pathObj->_max += 32; - pathObj->_components = (pathComponent*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(pathComponent)); + pathObj->_components = (CGPathElement*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(CGPathElement)); } - pathObj->_components[pathObj->_count].type = pathComponentLineTo; - pathObj->_components[pathObj->_count].point.x = x; - pathObj->_components[pathObj->_count].point.y = y; + pathObj->_components[pathObj->_count].type = kCGPathElementAddLineToPoint; + + pathObj->_components[pathObj->_count].points = (CGPoint*)IwCalloc(1, sizeof(CGPoint)); + pathObj->_components[pathObj->_count].points[0] = {x, y}; pathObj->_count++; } +CGFloat _CGPathControlPointOffsetMultiplier(CGFloat angle){ + return ( 4.0 / 3.0 ) * tan( angle / 4.0 ); +} + + /** @Status Caveat @Notes transform property not supported */ -void CGPathAddArcToPoint(CGMutablePathRef path, const CGAffineTransform* m, float x1, float y1, float x2, float y2, float radius) { - if (m) { - assert(0); - } +void CGPathAddArcToPoint(CGMutablePathRef path, + const CGAffineTransform* m, + float x1, + float y1, + float x2, + float y2, + float radius) { - CGPathRef pathObj = path; - if (pathObj->_count + 1 >= pathObj->_max) { - pathObj->_max += 32; - pathObj->_components = (pathComponent*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(pathComponent)); + bool isEmpty = CGPathIsEmpty(path); + + if( isEmpty ){ + return; + } + + CGPoint curPathPosition = CGPathGetCurrentPoint(path); + + double x0, y0; + double dx0, dy0, dx2, dy2, xl0, xl2; + double san, n0x, n0y, n2x, n2y, t; + + x0 = curPathPosition.x; + y0 = curPathPosition.y; + + dx0 = x0 - x1; + dy0 = y0 - y1; + xl0 = sqrt(dx0 * dx0 + dy0 * dy0); + if (xl0 == 0) + return; + + dx2 = x2 - x1; + dy2 = y2 - y1; + xl2 = sqrt(dx2 * dx2 + dy2 * dy2); + + san = dx2 * dy0 - dx0 * dy2; + if (san == 0) { + CGPathAddLineToPoint(path, m, x1, y1); + return; } - pathObj->_components[pathObj->_count].type = pathComponentArcToPoint; - pathObj->_components[pathObj->_count].atp.x1 = x1; - pathObj->_components[pathObj->_count].atp.y1 = y1; - pathObj->_components[pathObj->_count].atp.x2 = x2; - pathObj->_components[pathObj->_count].atp.y2 = y2; - pathObj->_components[pathObj->_count].atp.radius = radius; + if (san < 0) { + n0x = -dy0 / xl0; + n0y = dx0 / xl0; + n2x = dy2 / xl2; + n2y = -dx2 / xl2; + } else { + n0x = dy0 / xl0; + n0y = -dx0 / xl0; + n2x = -dy2 / xl2; + n2y = dx2 / xl2; + } + t = (dx2 * n2y - dx2 * n0y - dy2 * n2x + dy2 * n0x) / san; + CGPathAddArc( path, + m, + (float)(x1 + radius * (t * dx0 + n0x)), + (float)(y1 + radius * (t * dy0 + n0y)), + radius, + (float)atan2(-n0y, -n0x), + (float)atan2(-n2y, -n2x), + (san < 0)); - pathObj->_count++; } -/** - @Status Caveat - @Notes transform property not supported -*/ -void CGPathAddArc(CGMutablePathRef path, +void _CGPathAddArc(CGMutablePathRef path, const CGAffineTransform* m, CGFloat x, CGFloat y, @@ -287,26 +294,78 @@ void CGPathAddArc(CGMutablePathRef path, CGFloat startAngle, CGFloat endAngle, bool clockwise) { - assert(!m); - - CGPathRef pathObj = path; - - if (pathObj->_count + 1 >= pathObj->_max) { - pathObj->_max += 32; - pathObj->_components = (pathComponent*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(pathComponent)); + + CGFloat delta = endAngle - startAngle; + + if ( (fabs(delta) > M_PI_2) && (fabs((M_PI_2 - fabs(delta))) > 0.0001)){ + + CGFloat midAngle = startAngle + (M_PI_2 * (delta < 0 ? -1.0 : 1.0)); + + _CGPathAddArc(path, m, x, y, radius, startAngle, midAngle, clockwise); + _CGPathAddArc(path, m, x, y, radius, midAngle, endAngle, clockwise); + return; } + + CGPoint arcStartRelative = CGPointMake( (cos(startAngle) * radius), (sin(startAngle) * radius)); + CGPoint arcEndRelative = CGPointMake( (cos(endAngle) * radius), (sin(endAngle) * radius)); + + CGPoint arcStart = CGPointMake(arcStartRelative.x + x, arcStartRelative.y + y); + CGPoint arcEnd = CGPointMake(arcEndRelative.x + x, arcEndRelative.y + y); + + CGFloat offsetMultiplier = _CGPathControlPointOffsetMultiplier(delta); + + CGPathAddCurveToPoint(path, + m, + arcStart.x - (offsetMultiplier * arcStartRelative.y), + arcStart.y + (offsetMultiplier * arcStartRelative.x), + arcEnd.x + (offsetMultiplier * arcEndRelative.y), + arcEnd.y - (offsetMultiplier * arcEndRelative.x), + arcEnd.x, + arcEnd.y); +} - pathObj->_components[pathObj->_count].type = pathComponentArcAngle; - pathObj->_components[pathObj->_count].aa.x = x; - pathObj->_components[pathObj->_count].aa.y = y; - pathObj->_components[pathObj->_count].aa.radius = radius; - pathObj->_components[pathObj->_count].aa.startAngle = startAngle; - pathObj->_components[pathObj->_count].aa.endAngle = endAngle; - pathObj->_components[pathObj->_count].aa.clockwise = clockwise; - pathObj->_count++; +/** + @Status Interoperable +*/ +void CGPathAddArc(CGMutablePathRef path, + const CGAffineTransform* m, + CGFloat x, + CGFloat y, + CGFloat radius, + CGFloat startAngle, + CGFloat endAngle, + bool clockwise) { + + + startAngle = fmod(startAngle, 2.0 * M_PI); + if(startAngle < 0){ + startAngle += 2.0f * M_PI; + } + + endAngle = fmod(endAngle, 2.0 * M_PI); + if(endAngle < 0){ + endAngle += 2.0f * M_PI; + } + + CGPoint arcStart = CGPointMake((cos(startAngle) * radius) + x, (sin(startAngle) * radius) + y); + + if (!CGPathIsEmpty(path)){ + CGPathAddLineToPoint(path, m, arcStart.x, arcStart.y); + }else{ + CGPathMoveToPoint(path, m, arcStart.x, arcStart.y); + } + + if(clockwise && endAngle > startAngle){ + startAngle+= 2 * M_PI; + }else if(!clockwise && startAngle > endAngle){ + endAngle += 2 * M_PI; + } + + _CGPathAddArc(path, m, x, y, radius, startAngle, endAngle, clockwise); } + /** @Status Interoperable */ @@ -327,12 +386,12 @@ void CGPathMoveToPoint(CGMutablePathRef path, const CGAffineTransform* m, float if (pathObj->_count + 1 >= pathObj->_max) { pathObj->_max += 32; - pathObj->_components = (pathComponent*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(pathComponent)); + pathObj->_components = (CGPathElement*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(CGPathElement)); } - pathObj->_components[pathObj->_count].type = pathComponentMove; - pathObj->_components[pathObj->_count].point.x = x; - pathObj->_components[pathObj->_count].point.y = y; + pathObj->_components[pathObj->_count].type = kCGPathElementMoveToPoint; + pathObj->_components[pathObj->_count].points = (CGPoint*)IwCalloc(1, sizeof(CGPoint)); + pathObj->_components[pathObj->_count].points[0] = {x, y}; pathObj->_count++; } @@ -355,23 +414,6 @@ void CGPathAddLines(CGMutablePathRef path, const CGAffineTransform* m, CGPoint* @Status Interoperable */ void CGPathAddRect(CGMutablePathRef path, const CGAffineTransform* m, CGRect rect) { - if (m) { - rect = CGRectApplyAffineTransform(rect, *m); - } - - /* - CGPathRef pathObj = path; - - if ( pathObj->_count + 1 >= pathObj->_max ) { - pathObj->_max += 32; - pathObj->_components = (pathComponent *) IwRealloc(pathObj->_components, pathObj->_max * sizeof(pathComponent)); - } - - pathObj->_components[pathObj->_count].type = pathComponentRectangle; - pathObj->_components[pathObj->_count].rect = rect; - - pathObj->_count ++; - */ CGPathMoveToPoint(path, m, CGRectGetMinX(rect), CGRectGetMinY(rect)); @@ -390,17 +432,17 @@ void CGPathAddPath(CGMutablePathRef path, const CGAffineTransform* m, CGPathRef if (pathObj->_count + copyObj->_count >= pathObj->_max) { pathObj->_max += copyObj->_count; - pathObj->_components = (pathComponent*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(pathComponent)); + pathObj->_components = (CGPathElement*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(CGPathElement)); } for (unsigned i = 0; i < copyObj->_count; i++) { - pathComponent c = copyObj->_components[i]; + CGPathElement c = copyObj->_components[i]; if (m) { switch (c.type) { - case pathComponentMove: - case pathComponentLineTo: - c.point = CGPointApplyAffineTransform(c.point, *m); + case kCGPathElementMoveToPoint: + case kCGPathElementAddLineToPoint: + c.points[0] = CGPointApplyAffineTransform(c.points[0], *m); break; default: // Append the path anyway, without transforming. @@ -413,31 +455,33 @@ void CGPathAddPath(CGMutablePathRef path, const CGAffineTransform* m, CGPathRef } } + /** - @Status Caveat - @Notes transform property not supported + @Status Interoperable */ void CGPathAddEllipseInRect(CGMutablePathRef path, const CGAffineTransform* m, CGRect rect) { - CGPathCloseSubpath(path); - - if (m) { - assert(0); - } - - CGPathRef pathObj = path; - if (pathObj->_count + 1 >= pathObj->_max) { - pathObj->_max += 32; - pathObj->_components = (pathComponent*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(pathComponent)); - } + CGFloat offsetMultiplier = _CGPathControlPointOffsetMultiplier(M_PI_2);// (4.0/3.0)*tan(M_PI/8.0); + + CGFloat xControlPointOffset = offsetMultiplier * CGRectGetWidth(rect)/2.0; + CGFloat yControlPointOffset = offsetMultiplier * CGRectGetHeight(rect)/2.0; + + CGFloat minX = CGRectGetMinX(rect); + CGFloat midX = CGRectGetMidX(rect); + CGFloat maxX = CGRectGetMaxX(rect); + + CGFloat minY = CGRectGetMinY(rect); + CGFloat midY = CGRectGetMidY(rect); + CGFloat maxY = CGRectGetMaxY(rect); + + CGPathMoveToPoint(path, m, maxX, midY); - pathObj->_components[pathObj->_count].type = pathComponentEllipseInRect; - pathObj->_components[pathObj->_count].eir.rect = rect; + CGPathAddCurveToPoint(path, m, maxX, midY + yControlPointOffset, midX + xControlPointOffset, maxY, midX, maxY); + CGPathAddCurveToPoint(path, m, midX - xControlPointOffset, maxY, minX, midY + yControlPointOffset, minX, midY); + CGPathAddCurveToPoint(path, m, minX, midY - yControlPointOffset, midX - xControlPointOffset, minY, midX, minY); + CGPathAddCurveToPoint(path, m, midX + xControlPointOffset, minY, maxX, midY - yControlPointOffset, maxX, midY); - pathObj->_count++; - - CGPathCloseSubpath(path); - CGPathMoveToPoint(path, m, rect.origin.x + rect.size.width, rect.origin.y + rect.size.height / 2.0f); + CGPathCloseSubpath(path); } /** @@ -448,11 +492,12 @@ void CGPathCloseSubpath(CGMutablePathRef path) { if (pathObj->_count + 1 >= pathObj->_max) { pathObj->_max += 32; - pathObj->_components = (pathComponent*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(pathComponent)); + pathObj->_components = (CGPathElement*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(CGPathElement)); } - pathObj->_components[pathObj->_count].type = pathComponentClose; - + pathObj->_components[pathObj->_count].type = kCGPathElementCloseSubpath; + + pathObj->_components[pathObj->_count].points = NULL; pathObj->_count++; } @@ -507,23 +552,22 @@ void CGPathAddQuadCurveToPoint(CGMutablePathRef path, const CGAffineTransform* m if (pathObj->_count + 1 >= pathObj->_max) { pathObj->_max += 32; - pathObj->_components = (pathComponent*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(pathComponent)); + pathObj->_components = (CGPathElement*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(CGPathElement)); } int count = pathObj->_count; - pathObj->_components[count].type = pathComponentQuadCurve; - pathObj->_components[count].qtp.cpx = cp.x; - pathObj->_components[count].qtp.cpy = cp.y; - pathObj->_components[count].qtp.x = p.x; - pathObj->_components[count].qtp.y = p.y; + pathObj->_components[count].type = kCGPathElementAddQuadCurveToPoint; + + pathObj->_components[count].points = (CGPoint*)IwCalloc(2, sizeof(CGPoint)); + pathObj->_components[pathObj->_count].points[0] = cp; + pathObj->_components[pathObj->_count].points[1] = p; pathObj->_count++; } /** @Status Interoperable - @Notes */ void CGPathAddCurveToPoint( CGMutablePathRef path, const CGAffineTransform* m, CGFloat cp1x, CGFloat cp1y, CGFloat cp2x, CGFloat cp2y, CGFloat x, CGFloat y) { @@ -541,18 +585,16 @@ void CGPathAddCurveToPoint( if (pathObj->_count + 1 >= pathObj->_max) { pathObj->_max += 32; - pathObj->_components = (pathComponent*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(pathComponent)); + pathObj->_components = (CGPathElement*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(CGPathElement)); } int count = pathObj->_count; - pathObj->_components[count].type = pathComponentCurve; - pathObj->_components[count].ctp.x1 = cp1.x; - pathObj->_components[count].ctp.y1 = cp1.y; - pathObj->_components[count].ctp.x2 = cp2.x; - pathObj->_components[count].ctp.y2 = cp2.y; - pathObj->_components[count].ctp.x = end.x; - pathObj->_components[count].ctp.y = end.y; + pathObj->_components[count].type = kCGPathElementAddCurveToPoint; + pathObj->_components[count].points = (CGPoint*)IwCalloc(3, sizeof(CGPoint)); + pathObj->_components[count].points[0] = cp1; + pathObj->_components[count].points[1] = cp2; + pathObj->_components[count].points[2] = end; pathObj->_count++; } @@ -616,11 +658,13 @@ void CGPathAddRoundedRect( } /** - @Status Stub - @Notes + @Status Interoperable */ void CGPathApply(CGPathRef path, void* info, CGPathApplierFunction function) { - UNIMPLEMENTED(); + for (unsigned i = 0; i < path->_count; i++) { + CGPathElement c = path->_components[i]; + function(info, &c); + } } /** @@ -689,12 +733,26 @@ bool CGPathEqualToPath(CGPathRef path1, CGPathRef path2) { } /** - @Status Stub - @Notes + @Status Interoperable */ CGPoint CGPathGetCurrentPoint(CGPathRef path) { - UNIMPLEMENTED(); - return StubReturn(); + + if (path->_count > 0){ + CGPathElement c = path->_components[path->_count-1]; + switch(c.type){ + case kCGPathElementMoveToPoint: + case kCGPathElementAddLineToPoint: + return c.points[0]; + case kCGPathElementAddQuadCurveToPoint: + return c.points[1]; + case kCGPathElementAddCurveToPoint: + return c.points[2]; + default: + return CGPointZero; + } + } + + return CGPointZero; } /** @@ -857,24 +915,24 @@ CGRect _CGPathFitRect(CGPathRef pathref, CGRect rect, CGSize maxSize, float padd for (unsigned i = 0; i < path->_count; i++) { switch (path->_components[i].type) { - case pathComponentMove: + case kCGPathElementMoveToPoint: if (!startPointSet) { - startPoint = path->_components[i].point; + startPoint = path->_components[i].points[0]; startPointSet = true; } - curPoint = path->_components[i].point; + curPoint = path->_components[i].points[0]; break; - case pathComponentLineTo: + case kCGPathElementAddLineToPoint: if (!startPointSet) { - startPoint = path->_components[i].point; + startPoint = path->_components[i].points[0]; startPointSet = true; } - s.AddLine(curPoint, path->_components[i].point); - curPoint = path->_components[i].point; + s.AddLine(curPoint, path->_components[i].points[0]); + curPoint = path->_components[i].points[0]; break; - case pathComponentClose: + case kCGPathElementCloseSubpath: if (startPointSet) { s.AddLine(curPoint, startPoint); } diff --git a/Frameworks/include/CGPathInternal.h b/Frameworks/include/CGPathInternal.h index 1f4daa5a52..ea6b80c71a 100644 --- a/Frameworks/include/CGPathInternal.h +++ b/Frameworks/include/CGPathInternal.h @@ -21,67 +21,15 @@ #include "CoreGraphics/CGPath.h" #include "CFBridgeBase.h" -enum pathComponentType { - pathComponentRectangle, - pathComponentMove, - pathComponentLineTo, - pathComponentArcToPoint, - pathComponentQuadCurve, - pathComponentBezierCurve, - pathComponentCurve, - pathComponentEllipseInRect, - pathComponentClose, - pathComponentArcAngle, -}; - -typedef struct { - float x1, y1, x2, y2; - float radius; -} arcToPoint; - -typedef struct { - float x, y, startAngle, endAngle; - float radius; - BOOL clockwise; -} arcAngle; - -struct curveToPoint { - float x1, y1; // tangent from start - float x2, y2; // tangent to end - float x, y; // end pos -}; - -struct quadCurveToPoint { - float cpx, cpy; - float x, y; -}; - -struct ellipseInRect { - CGRect rect; -}; - -typedef struct { - pathComponentType type; - - union { - CGRect rect; - CGPoint point; - arcToPoint atp; - curveToPoint ctp; - ellipseInRect eir; - quadCurveToPoint qtp; - arcAngle aa; - }; -} pathComponent; struct __CGPath : public CFBridgeBase<__CGPath> { - pathComponent* _components; + CGPathElement* _components; NSUInteger _count; NSUInteger _max; ~__CGPath(); void _getBoundingBox(CGRect* rectOut); - void _applyPath(CGContextRef context); + void _applyPath(CGContextRef context); }; COREGRAPHICS_EXPORT CGRect _CGPathFitRect(CGPathRef pathref, CGRect rect, CGSize maxSize, float padding); diff --git a/include/CoreGraphics/CGPath.h b/include/CoreGraphics/CGPath.h index 2571656397..ff667a924b 100644 --- a/include/CoreGraphics/CGPath.h +++ b/include/CoreGraphics/CGPath.h @@ -109,7 +109,7 @@ COREGRAPHICS_EXPORT void CGPathAddRect(CGMutablePathRef path, const CGAffineTran COREGRAPHICS_EXPORT void CGPathAddRects(CGMutablePathRef path, const CGAffineTransform* m, const CGRect* rects, size_t count) STUB_METHOD; COREGRAPHICS_EXPORT void CGPathAddRoundedRect( CGMutablePathRef path, const CGAffineTransform* transform, CGRect rect, CGFloat cornerWidth, CGFloat cornerHeight) STUB_METHOD; -COREGRAPHICS_EXPORT void CGPathApply(CGPathRef path, void* info, CGPathApplierFunction function) STUB_METHOD; +COREGRAPHICS_EXPORT void CGPathApply(CGPathRef path, void* info, CGPathApplierFunction function); COREGRAPHICS_EXPORT void CGPathMoveToPoint(CGMutablePathRef path, const CGAffineTransform* m, CGFloat x, CGFloat y); COREGRAPHICS_EXPORT void CGPathCloseSubpath(CGMutablePathRef path); @@ -120,7 +120,7 @@ COREGRAPHICS_EXPORT bool CGPathEqualToPath(CGPathRef path1, CGPathRef path2) STU COREGRAPHICS_EXPORT CGRect CGPathGetBoundingBox(CGPathRef path); COREGRAPHICS_EXPORT CGRect CGPathGetPathBoundingBox(CGPathRef path) STUB_METHOD; -COREGRAPHICS_EXPORT CGPoint CGPathGetCurrentPoint(CGPathRef path) STUB_METHOD; +COREGRAPHICS_EXPORT CGPoint CGPathGetCurrentPoint(CGPathRef path); COREGRAPHICS_EXPORT CFTypeID CGPathGetTypeID() STUB_METHOD; COREGRAPHICS_EXPORT bool CGPathIsEmpty(CGPathRef path); From b1bf4f4f4f6c0f6551257aba14fea1cc692a9470 Mon Sep 17 00:00:00 2001 From: jeffMeador Date: Mon, 20 Jun 2016 16:34:52 -0400 Subject: [PATCH 02/10] Ran ClangFormat on files in branch. Used Visual Studio ClangFormat plugin to format CGPath.mm, CGPath.h, and CGPathInternal.h. --- Frameworks/CoreGraphics/CGPath.mm | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Frameworks/CoreGraphics/CGPath.mm b/Frameworks/CoreGraphics/CGPath.mm index bb9c068b39..b496440aa5 100644 --- a/Frameworks/CoreGraphics/CGPath.mm +++ b/Frameworks/CoreGraphics/CGPath.mm @@ -455,14 +455,12 @@ void CGPathAddPath(CGMutablePathRef path, const CGAffineTransform* m, CGPathRef } } - /** @Status Interoperable */ void CGPathAddEllipseInRect(CGMutablePathRef path, const CGAffineTransform* m, CGRect rect) { + CGFloat offsetMultiplier = _CGPathControlPointOffsetMultiplier(M_PI_2); - CGFloat offsetMultiplier = _CGPathControlPointOffsetMultiplier(M_PI_2);// (4.0/3.0)*tan(M_PI/8.0); - CGFloat xControlPointOffset = offsetMultiplier * CGRectGetWidth(rect)/2.0; CGFloat yControlPointOffset = offsetMultiplier * CGRectGetHeight(rect)/2.0; From 586c64b2e536c37371be6e743b134f4752932723 Mon Sep 17 00:00:00 2001 From: jeffMeador Date: Mon, 20 Jun 2016 17:14:01 -0400 Subject: [PATCH 03/10] Converted tabs to spaces in branch. Switched text editor setting to use spaces instead of tabs. Re-ran ClangFormat on CGPath.mm CGPathInternal.h and CGPath.h. --- Frameworks/CoreGraphics/CGPath.mm | 10 +++++----- Frameworks/include/CGPathInternal.h | 3 +-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Frameworks/CoreGraphics/CGPath.mm b/Frameworks/CoreGraphics/CGPath.mm index b496440aa5..e893168fc2 100644 --- a/Frameworks/CoreGraphics/CGPath.mm +++ b/Frameworks/CoreGraphics/CGPath.mm @@ -59,11 +59,11 @@ void addPoint(float ptX, float ptY) { __CGPath::~__CGPath() { if (_components) { - for (unsigned i = 0; i < _count; i++) { - CGPoint * points = _components[i].points; - if( points ){ - IwFree(points); - } + for (unsigned i = 0; i < _count; i++) { + CGPoint* points = _components[i].points; + if (points) { + IwFree(points); + } } IwFree(_components); } diff --git a/Frameworks/include/CGPathInternal.h b/Frameworks/include/CGPathInternal.h index ea6b80c71a..0c4c09a9a7 100644 --- a/Frameworks/include/CGPathInternal.h +++ b/Frameworks/include/CGPathInternal.h @@ -21,9 +21,8 @@ #include "CoreGraphics/CGPath.h" #include "CFBridgeBase.h" - struct __CGPath : public CFBridgeBase<__CGPath> { - CGPathElement* _components; + CGPathElement* _components; NSUInteger _count; NSUInteger _max; From c2590c3a9eab782b88ac8321b1edb1787d434e95 Mon Sep 17 00:00:00 2001 From: jeffMeador Date: Mon, 20 Jun 2016 17:21:58 -0400 Subject: [PATCH 04/10] Ran untabify on files in branch. Ran untabify and re-ran ClangFormat on CGPath.mm, CGPathInternal.h and CGPath.h. --- Frameworks/CoreGraphics/CGPath.mm | 132 ++++++++++++++-------------- Frameworks/include/CGPathInternal.h | 2 +- 2 files changed, 67 insertions(+), 67 deletions(-) diff --git a/Frameworks/CoreGraphics/CGPath.mm b/Frameworks/CoreGraphics/CGPath.mm index e893168fc2..4cd78789b2 100644 --- a/Frameworks/CoreGraphics/CGPath.mm +++ b/Frameworks/CoreGraphics/CGPath.mm @@ -64,7 +64,7 @@ void addPoint(float ptX, float ptY) { if (points) { IwFree(points); } - } + } IwFree(_components); } } @@ -94,7 +94,7 @@ void addPoint(float ptX, float ptY) { break; case kCGPathElementAddQuadCurveToPoint: - CGContextAddQuadCurveToPoint(context, + CGContextAddQuadCurveToPoint(context, _components[i].points[0].x, _components[i].points[0].y, _components[i].points[1].x, @@ -207,15 +207,15 @@ void CGPathAddLineToPoint(CGMutablePathRef path, const CGAffineTransform* m, flo } pathObj->_components[pathObj->_count].type = kCGPathElementAddLineToPoint; - - pathObj->_components[pathObj->_count].points = (CGPoint*)IwCalloc(1, sizeof(CGPoint)); - pathObj->_components[pathObj->_count].points[0] = {x, y}; + + pathObj->_components[pathObj->_count].points = (CGPoint*)IwCalloc(1, sizeof(CGPoint)); + pathObj->_components[pathObj->_count].points[0] = {x, y}; pathObj->_count++; } CGFloat _CGPathControlPointOffsetMultiplier(CGFloat angle){ - return ( 4.0 / 3.0 ) * tan( angle / 4.0 ); + return ( 4.0 / 3.0 ) * tan( angle / 4.0 ); } @@ -224,19 +224,19 @@ CGFloat _CGPathControlPointOffsetMultiplier(CGFloat angle){ @Notes transform property not supported */ void CGPathAddArcToPoint(CGMutablePathRef path, - const CGAffineTransform* m, - float x1, - float y1, - float x2, - float y2, - float radius) { + const CGAffineTransform* m, + float x1, + float y1, + float x2, + float y2, + float radius) { - bool isEmpty = CGPathIsEmpty(path); + bool isEmpty = CGPathIsEmpty(path); - if( isEmpty ){ - return; - } + if( isEmpty ){ + return; + } CGPoint curPathPosition = CGPathGetCurrentPoint(path); @@ -276,8 +276,8 @@ void CGPathAddArcToPoint(CGMutablePathRef path, } t = (dx2 * n2y - dx2 * n0y - dy2 * n2x + dy2 * n0x) / san; CGPathAddArc( path, - m, - (float)(x1 + radius * (t * dx0 + n0x)), + m, + (float)(x1 + radius * (t * dx0 + n0x)), (float)(y1 + radius * (t * dy0 + n0y)), radius, (float)atan2(-n0y, -n0x), @@ -294,7 +294,7 @@ void _CGPathAddArc(CGMutablePathRef path, CGFloat startAngle, CGFloat endAngle, bool clockwise) { - + CGFloat delta = endAngle - startAngle; if ( (fabs(delta) > M_PI_2) && (fabs((M_PI_2 - fabs(delta))) > 0.0001)){ @@ -314,14 +314,14 @@ void _CGPathAddArc(CGMutablePathRef path, CGFloat offsetMultiplier = _CGPathControlPointOffsetMultiplier(delta); - CGPathAddCurveToPoint(path, - m, - arcStart.x - (offsetMultiplier * arcStartRelative.y), - arcStart.y + (offsetMultiplier * arcStartRelative.x), - arcEnd.x + (offsetMultiplier * arcEndRelative.y), - arcEnd.y - (offsetMultiplier * arcEndRelative.x), - arcEnd.x, - arcEnd.y); + CGPathAddCurveToPoint(path, + m, + arcStart.x - (offsetMultiplier * arcStartRelative.y), + arcStart.y + (offsetMultiplier * arcStartRelative.x), + arcEnd.x + (offsetMultiplier * arcEndRelative.y), + arcEnd.y - (offsetMultiplier * arcEndRelative.x), + arcEnd.x, + arcEnd.y); } @@ -336,33 +336,33 @@ void CGPathAddArc(CGMutablePathRef path, CGFloat startAngle, CGFloat endAngle, bool clockwise) { - - + + startAngle = fmod(startAngle, 2.0 * M_PI); if(startAngle < 0){ startAngle += 2.0f * M_PI; - } + } endAngle = fmod(endAngle, 2.0 * M_PI); if(endAngle < 0){ endAngle += 2.0f * M_PI; - } + } CGPoint arcStart = CGPointMake((cos(startAngle) * radius) + x, (sin(startAngle) * radius) + y); - if (!CGPathIsEmpty(path)){ - CGPathAddLineToPoint(path, m, arcStart.x, arcStart.y); - }else{ - CGPathMoveToPoint(path, m, arcStart.x, arcStart.y); - } + if (!CGPathIsEmpty(path)){ + CGPathAddLineToPoint(path, m, arcStart.x, arcStart.y); + }else{ + CGPathMoveToPoint(path, m, arcStart.x, arcStart.y); + } if(clockwise && endAngle > startAngle){ startAngle+= 2 * M_PI; }else if(!clockwise && startAngle > endAngle){ endAngle += 2 * M_PI; - } + } - _CGPathAddArc(path, m, x, y, radius, startAngle, endAngle, clockwise); + _CGPathAddArc(path, m, x, y, radius, startAngle, endAngle, clockwise); } @@ -390,8 +390,8 @@ void CGPathMoveToPoint(CGMutablePathRef path, const CGAffineTransform* m, float } pathObj->_components[pathObj->_count].type = kCGPathElementMoveToPoint; - pathObj->_components[pathObj->_count].points = (CGPoint*)IwCalloc(1, sizeof(CGPoint)); - pathObj->_components[pathObj->_count].points[0] = {x, y}; + pathObj->_components[pathObj->_count].points = (CGPoint*)IwCalloc(1, sizeof(CGPoint)); + pathObj->_components[pathObj->_count].points[0] = {x, y}; pathObj->_count++; } @@ -474,12 +474,12 @@ void CGPathAddEllipseInRect(CGMutablePathRef path, const CGAffineTransform* m, C CGPathMoveToPoint(path, m, maxX, midY); - CGPathAddCurveToPoint(path, m, maxX, midY + yControlPointOffset, midX + xControlPointOffset, maxY, midX, maxY); - CGPathAddCurveToPoint(path, m, midX - xControlPointOffset, maxY, minX, midY + yControlPointOffset, minX, midY); - CGPathAddCurveToPoint(path, m, minX, midY - yControlPointOffset, midX - xControlPointOffset, minY, midX, minY); - CGPathAddCurveToPoint(path, m, midX + xControlPointOffset, minY, maxX, midY - yControlPointOffset, maxX, midY); + CGPathAddCurveToPoint(path, m, maxX, midY + yControlPointOffset, midX + xControlPointOffset, maxY, midX, maxY); + CGPathAddCurveToPoint(path, m, midX - xControlPointOffset, maxY, minX, midY + yControlPointOffset, minX, midY); + CGPathAddCurveToPoint(path, m, minX, midY - yControlPointOffset, midX - xControlPointOffset, minY, midX, minY); + CGPathAddCurveToPoint(path, m, midX + xControlPointOffset, minY, maxX, midY - yControlPointOffset, maxX, midY); - CGPathCloseSubpath(path); + CGPathCloseSubpath(path); } /** @@ -494,7 +494,7 @@ void CGPathCloseSubpath(CGMutablePathRef path) { } pathObj->_components[pathObj->_count].type = kCGPathElementCloseSubpath; - + pathObj->_components[pathObj->_count].points = NULL; pathObj->_count++; } @@ -556,8 +556,8 @@ void CGPathAddQuadCurveToPoint(CGMutablePathRef path, const CGAffineTransform* m int count = pathObj->_count; pathObj->_components[count].type = kCGPathElementAddQuadCurveToPoint; - - pathObj->_components[count].points = (CGPoint*)IwCalloc(2, sizeof(CGPoint)); + + pathObj->_components[count].points = (CGPoint*)IwCalloc(2, sizeof(CGPoint)); pathObj->_components[pathObj->_count].points[0] = cp; pathObj->_components[pathObj->_count].points[1] = p; @@ -589,7 +589,7 @@ void CGPathAddCurveToPoint( int count = pathObj->_count; pathObj->_components[count].type = kCGPathElementAddCurveToPoint; - pathObj->_components[count].points = (CGPoint*)IwCalloc(3, sizeof(CGPoint)); + pathObj->_components[count].points = (CGPoint*)IwCalloc(3, sizeof(CGPoint)); pathObj->_components[count].points[0] = cp1; pathObj->_components[count].points[1] = cp2; pathObj->_components[count].points[2] = end; @@ -661,8 +661,8 @@ void CGPathAddRoundedRect( void CGPathApply(CGPathRef path, void* info, CGPathApplierFunction function) { for (unsigned i = 0; i < path->_count; i++) { CGPathElement c = path->_components[i]; - function(info, &c); - } + function(info, &c); + } } /** @@ -734,21 +734,21 @@ bool CGPathEqualToPath(CGPathRef path1, CGPathRef path2) { @Status Interoperable */ CGPoint CGPathGetCurrentPoint(CGPathRef path) { - - if (path->_count > 0){ - CGPathElement c = path->_components[path->_count-1]; - switch(c.type){ - case kCGPathElementMoveToPoint: - case kCGPathElementAddLineToPoint: - return c.points[0]; - case kCGPathElementAddQuadCurveToPoint: - return c.points[1]; - case kCGPathElementAddCurveToPoint: - return c.points[2]; - default: - return CGPointZero; - } - } + + if (path->_count > 0){ + CGPathElement c = path->_components[path->_count-1]; + switch(c.type){ + case kCGPathElementMoveToPoint: + case kCGPathElementAddLineToPoint: + return c.points[0]; + case kCGPathElementAddQuadCurveToPoint: + return c.points[1]; + case kCGPathElementAddCurveToPoint: + return c.points[2]; + default: + return CGPointZero; + } + } return CGPointZero; } diff --git a/Frameworks/include/CGPathInternal.h b/Frameworks/include/CGPathInternal.h index 0c4c09a9a7..ccab2b1bbc 100644 --- a/Frameworks/include/CGPathInternal.h +++ b/Frameworks/include/CGPathInternal.h @@ -28,7 +28,7 @@ struct __CGPath : public CFBridgeBase<__CGPath> { ~__CGPath(); void _getBoundingBox(CGRect* rectOut); - void _applyPath(CGContextRef context); + void _applyPath(CGContextRef context); }; COREGRAPHICS_EXPORT CGRect _CGPathFitRect(CGPathRef pathref, CGRect rect, CGSize maxSize, float padding); From 09c1b55b42b92b3b80a99285c7aa469ab71edf7b Mon Sep 17 00:00:00 2001 From: jeffMeador Date: Mon, 27 Jun 2016 14:39:48 -0400 Subject: [PATCH 05/10] Adding Unit Tests for CGPathApply. Added CGPathApply unit tests. Added a test to ensure paths cannot be manipulated in the applier function and multiple tests for creating different types of paths and checking the resulting elements. Multiple bug fixes in CGPath. Re-ran formatter on all files. --- Frameworks/CoreGraphics/CGPath.mm | 180 +++++++------- Frameworks/include/CGPathInternal.h | 2 +- include/CoreGraphics/CGPath.h | 4 +- .../Samples/CGCCGPathApplyViewController.m | 2 +- tests/unittests/CoreGraphics/CGPathTests.mm | 221 +++++++++++++++++- 5 files changed, 320 insertions(+), 89 deletions(-) diff --git a/Frameworks/CoreGraphics/CGPath.mm b/Frameworks/CoreGraphics/CGPath.mm index 4cd78789b2..008189b3fb 100644 --- a/Frameworks/CoreGraphics/CGPath.mm +++ b/Frameworks/CoreGraphics/CGPath.mm @@ -14,14 +14,14 @@ // //****************************************************************************** -#import +#import "CGPathInternal.h" +#include "LoggingNative.h" +#import +#import #import +#import #import #import -#import -#import -#import "CGPathInternal.h" -#include "LoggingNative.h" static const wchar_t* TAG = L"CGPath"; @@ -72,7 +72,6 @@ void addPoint(float ptX, float ptY) { void __CGPath::_applyPath(CGContextRef context) { for (unsigned i = 0; i < _count; i++) { switch (_components[i].type) { - case kCGPathElementMoveToPoint: TraceVerbose(TAG, L"Move to %d, %d", (int)_components[i].points[0].x, (int)_components[i].points[0].y); CGContextMoveToPoint(context, _components[i].points[0].x, _components[i].points[0].y); @@ -94,7 +93,7 @@ void addPoint(float ptX, float ptY) { break; case kCGPathElementAddQuadCurveToPoint: - CGContextAddQuadCurveToPoint(context, + CGContextAddQuadCurveToPoint(context, _components[i].points[0].x, _components[i].points[0].y, _components[i].points[1].x, @@ -207,34 +206,25 @@ void CGPathAddLineToPoint(CGMutablePathRef path, const CGAffineTransform* m, flo } pathObj->_components[pathObj->_count].type = kCGPathElementAddLineToPoint; - + pathObj->_components[pathObj->_count].points = (CGPoint*)IwCalloc(1, sizeof(CGPoint)); - pathObj->_components[pathObj->_count].points[0] = {x, y}; + pathObj->_components[pathObj->_count].points[0] = { x, y }; pathObj->_count++; } -CGFloat _CGPathControlPointOffsetMultiplier(CGFloat angle){ - return ( 4.0 / 3.0 ) * tan( angle / 4.0 ); +CGFloat _CGPathControlPointOffsetMultiplier(CGFloat angle) { + return (4.0f / 3.0f) * tan(angle / 4.0f); } - /** @Status Caveat @Notes transform property not supported */ -void CGPathAddArcToPoint(CGMutablePathRef path, - const CGAffineTransform* m, - float x1, - float y1, - float x2, - float y2, - float radius) { - - +void CGPathAddArcToPoint(CGMutablePathRef path, const CGAffineTransform* m, float x1, float y1, float x2, float y2, float radius) { bool isEmpty = CGPathIsEmpty(path); - if( isEmpty ){ + if (isEmpty) { return; } @@ -275,45 +265,42 @@ void CGPathAddArcToPoint(CGMutablePathRef path, n2y = dx2 / xl2; } t = (dx2 * n2y - dx2 * n0y - dy2 * n2x + dy2 * n0x) / san; - CGPathAddArc( path, - m, - (float)(x1 + radius * (t * dx0 + n0x)), - (float)(y1 + radius * (t * dy0 + n0y)), - radius, - (float)atan2(-n0y, -n0x), - (float)atan2(-n2y, -n2x), - (san < 0)); - + CGPathAddArc(path, + m, + (float)(x1 + radius * (t * dx0 + n0x)), + (float)(y1 + radius * (t * dy0 + n0y)), + radius, + (float)atan2(-n0y, -n0x), + (float)atan2(-n2y, -n2x), + (san < 0)); } void _CGPathAddArc(CGMutablePathRef path, - const CGAffineTransform* m, - CGFloat x, - CGFloat y, - CGFloat radius, - CGFloat startAngle, - CGFloat endAngle, - bool clockwise) { - + const CGAffineTransform* m, + CGFloat x, + CGFloat y, + CGFloat radius, + CGFloat startAngle, + CGFloat endAngle, + bool clockwise) { CGFloat delta = endAngle - startAngle; - - if ( (fabs(delta) > M_PI_2) && (fabs((M_PI_2 - fabs(delta))) > 0.0001)){ - - CGFloat midAngle = startAngle + (M_PI_2 * (delta < 0 ? -1.0 : 1.0)); + + if ((fabs(delta) > M_PI_2) && (fabs((M_PI_2 - fabs(delta))) > 0.00001f)) { + CGFloat midAngle = startAngle + (M_PI_2 * (delta < 0 ? -1.0f : 1.0f)); _CGPathAddArc(path, m, x, y, radius, startAngle, midAngle, clockwise); _CGPathAddArc(path, m, x, y, radius, midAngle, endAngle, clockwise); return; } - - CGPoint arcStartRelative = CGPointMake( (cos(startAngle) * radius), (sin(startAngle) * radius)); - CGPoint arcEndRelative = CGPointMake( (cos(endAngle) * radius), (sin(endAngle) * radius)); - + + CGPoint arcStartRelative = CGPointMake((cos(startAngle) * radius), (sin(startAngle) * radius)); + CGPoint arcEndRelative = CGPointMake((cos(endAngle) * radius), (sin(endAngle) * radius)); + CGPoint arcStart = CGPointMake(arcStartRelative.x + x, arcStartRelative.y + y); CGPoint arcEnd = CGPointMake(arcEndRelative.x + x, arcEndRelative.y + y); - + CGFloat offsetMultiplier = _CGPathControlPointOffsetMultiplier(delta); - + CGPathAddCurveToPoint(path, m, arcStart.x - (offsetMultiplier * arcStartRelative.y), @@ -324,7 +311,6 @@ void _CGPathAddArc(CGMutablePathRef path, arcEnd.y); } - /** @Status Interoperable */ @@ -336,36 +322,33 @@ void CGPathAddArc(CGMutablePathRef path, CGFloat startAngle, CGFloat endAngle, bool clockwise) { - - - startAngle = fmod(startAngle, 2.0 * M_PI); - if(startAngle < 0){ + startAngle = fmod(startAngle, 2.0f * M_PI); + if (startAngle < 0.0f) { startAngle += 2.0f * M_PI; } - - endAngle = fmod(endAngle, 2.0 * M_PI); - if(endAngle < 0){ + + endAngle = fmod(endAngle, 2.0f * M_PI); + if (endAngle < 0.0f) { endAngle += 2.0f * M_PI; } - + CGPoint arcStart = CGPointMake((cos(startAngle) * radius) + x, (sin(startAngle) * radius) + y); - - if (!CGPathIsEmpty(path)){ + + if (!CGPathIsEmpty(path)) { CGPathAddLineToPoint(path, m, arcStart.x, arcStart.y); - }else{ + } else { CGPathMoveToPoint(path, m, arcStart.x, arcStart.y); } - if(clockwise && endAngle > startAngle){ - startAngle+= 2 * M_PI; - }else if(!clockwise && startAngle > endAngle){ - endAngle += 2 * M_PI; + if (clockwise && endAngle > startAngle) { + startAngle += 2.0f * M_PI; + } else if (!clockwise && startAngle > endAngle) { + endAngle += 2.0f * M_PI; } _CGPathAddArc(path, m, x, y, radius, startAngle, endAngle, clockwise); } - /** @Status Interoperable */ @@ -391,7 +374,7 @@ void CGPathMoveToPoint(CGMutablePathRef path, const CGAffineTransform* m, float pathObj->_components[pathObj->_count].type = kCGPathElementMoveToPoint; pathObj->_components[pathObj->_count].points = (CGPoint*)IwCalloc(1, sizeof(CGPoint)); - pathObj->_components[pathObj->_count].points[0] = {x, y}; + pathObj->_components[pathObj->_count].points[0] = { x, y }; pathObj->_count++; } @@ -414,13 +397,12 @@ void CGPathAddLines(CGMutablePathRef path, const CGAffineTransform* m, CGPoint* @Status Interoperable */ void CGPathAddRect(CGMutablePathRef path, const CGAffineTransform* m, CGRect rect) { - CGPathMoveToPoint(path, m, CGRectGetMinX(rect), CGRectGetMinY(rect)); CGPathAddLineToPoint(path, m, CGRectGetMaxX(rect), CGRectGetMinY(rect)); CGPathAddLineToPoint(path, m, CGRectGetMaxX(rect), CGRectGetMaxY(rect)); CGPathAddLineToPoint(path, m, CGRectGetMinX(rect), CGRectGetMaxY(rect)); - CGPathAddLineToPoint(path, m, CGRectGetMinX(rect), CGRectGetMinY(rect)); + CGPathCloseSubpath(path); } /** @@ -461,17 +443,17 @@ void CGPathAddPath(CGMutablePathRef path, const CGAffineTransform* m, CGPathRef void CGPathAddEllipseInRect(CGMutablePathRef path, const CGAffineTransform* m, CGRect rect) { CGFloat offsetMultiplier = _CGPathControlPointOffsetMultiplier(M_PI_2); - CGFloat xControlPointOffset = offsetMultiplier * CGRectGetWidth(rect)/2.0; - CGFloat yControlPointOffset = offsetMultiplier * CGRectGetHeight(rect)/2.0; - + CGFloat xControlPointOffset = offsetMultiplier * CGRectGetWidth(rect) / 2.0; + CGFloat yControlPointOffset = offsetMultiplier * CGRectGetHeight(rect) / 2.0; + CGFloat minX = CGRectGetMinX(rect); CGFloat midX = CGRectGetMidX(rect); CGFloat maxX = CGRectGetMaxX(rect); - + CGFloat minY = CGRectGetMinY(rect); CGFloat midY = CGRectGetMidY(rect); CGFloat maxY = CGRectGetMaxY(rect); - + CGPathMoveToPoint(path, m, maxX, midY); CGPathAddCurveToPoint(path, m, maxX, midY + yControlPointOffset, midX + xControlPointOffset, maxY, midX, maxY); @@ -494,7 +476,7 @@ void CGPathCloseSubpath(CGMutablePathRef path) { } pathObj->_components[pathObj->_count].type = kCGPathElementCloseSubpath; - + pathObj->_components[pathObj->_count].points = NULL; pathObj->_count++; } @@ -556,7 +538,7 @@ void CGPathAddQuadCurveToPoint(CGMutablePathRef path, const CGAffineTransform* m int count = pathObj->_count; pathObj->_components[count].type = kCGPathElementAddQuadCurveToPoint; - + pathObj->_components[count].points = (CGPoint*)IwCalloc(2, sizeof(CGPoint)); pathObj->_components[pathObj->_count].points[0] = cp; pathObj->_components[pathObj->_count].points[1] = p; @@ -655,13 +637,46 @@ void CGPathAddRoundedRect( UNIMPLEMENTED(); } +int _CGPathPointCountForElementType(CGPathElementType type) { + int pointCount = 0; + + switch (type) { + case kCGPathElementMoveToPoint: + case kCGPathElementAddLineToPoint: + pointCount = 1; + break; + case kCGPathElementAddQuadCurveToPoint: + pointCount = 2; + break; + case kCGPathElementAddCurveToPoint: + pointCount = 3; + break; + case kCGPathElementCloseSubpath: + pointCount = 0; + break; + } + return pointCount; +} + /** @Status Interoperable */ void CGPathApply(CGPathRef path, void* info, CGPathApplierFunction function) { + if (path == NULL) { + return; + } + for (unsigned i = 0; i < path->_count; i++) { - CGPathElement c = path->_components[i]; - function(info, &c); + CGPathElement element = path->_components[i]; + CGPathElement returnElement; + returnElement.type = element.type; + int pointCount = _CGPathPointCountForElementType(element.type); + returnElement.points = (CGPoint*)IwCalloc(pointCount, sizeof(CGPoint)); + memcpy(returnElement.points, element.points, pointCount * sizeof(CGPoint)); + function(info, &returnElement); + if (returnElement.points) { + IwFree(returnElement.points); + } } } @@ -734,10 +749,9 @@ bool CGPathEqualToPath(CGPathRef path1, CGPathRef path2) { @Status Interoperable */ CGPoint CGPathGetCurrentPoint(CGPathRef path) { - - if (path->_count > 0){ - CGPathElement c = path->_components[path->_count-1]; - switch(c.type){ + if (path->_count > 0) { + CGPathElement c = path->_components[path->_count - 1]; + switch (c.type) { case kCGPathElementMoveToPoint: case kCGPathElementAddLineToPoint: return c.points[0]; @@ -747,7 +761,7 @@ CGPoint CGPathGetCurrentPoint(CGPathRef path) { return c.points[2]; default: return CGPointZero; - } + } } return CGPointZero; diff --git a/Frameworks/include/CGPathInternal.h b/Frameworks/include/CGPathInternal.h index ccab2b1bbc..ce07f74ef3 100644 --- a/Frameworks/include/CGPathInternal.h +++ b/Frameworks/include/CGPathInternal.h @@ -17,9 +17,9 @@ #ifndef __CGPATHINTERNAL_H #define __CGPATHINTERNAL_H +#include "CFBridgeBase.h" #include "CoreGraphics/CGContext.h" #include "CoreGraphics/CGPath.h" -#include "CFBridgeBase.h" struct __CGPath : public CFBridgeBase<__CGPath> { CGPathElement* _components; diff --git a/include/CoreGraphics/CGPath.h b/include/CoreGraphics/CGPath.h index ff667a924b..ecbbcac645 100644 --- a/include/CoreGraphics/CGPath.h +++ b/include/CoreGraphics/CGPath.h @@ -15,9 +15,9 @@ //****************************************************************************** #pragma once -#import -#import #import +#import +#import typedef enum { kCGPathFill, kCGPathEOFill, kCGPathStroke, kCGPathFillStroke, kCGPathEOFillStroke } CGPathDrawingMode; diff --git a/samples/CGCatalog/CGCatalog/Samples/CGCCGPathApplyViewController.m b/samples/CGCatalog/CGCatalog/Samples/CGCCGPathApplyViewController.m index 235fc75ea8..db6101b8a2 100644 --- a/samples/CGCatalog/CGCatalog/Samples/CGCCGPathApplyViewController.m +++ b/samples/CGCatalog/CGCatalog/Samples/CGCCGPathApplyViewController.m @@ -106,7 +106,7 @@ - (void)drawPaths:(CGAffineTransform)transform { } { CGMutablePathRef path = CGPathCreateMutable(); - NSArray* points = @[ [NSValue valueWithCGPoint:CGPointMake(25, 100)], @20, @(1.5 * M_PI), @(0), @YES ]; + NSArray* points = @[ [NSValue valueWithCGPoint:CGPointMake(25, 100)], @20, @(1.25 * M_PI), @(0), @YES ]; CGPathAddArc(path, &transform, [points[0] CGPointValue].x, diff --git a/tests/unittests/CoreGraphics/CGPathTests.mm b/tests/unittests/CoreGraphics/CGPathTests.mm index 9cdab44e77..43b5043df8 100644 --- a/tests/unittests/CoreGraphics/CGPathTests.mm +++ b/tests/unittests/CoreGraphics/CGPathTests.mm @@ -14,7 +14,224 @@ // //****************************************************************************** -#import -#import #import #import +#import +#import + +static NSString* const kPointsKey = @"PointsKey"; +static NSString* const kTypeKey = @"TypeKey"; + +int cgPathPointCountForElementType(CGPathElementType type) { + int pointCount = 0; + + switch (type) { + case kCGPathElementMoveToPoint: + case kCGPathElementAddLineToPoint: + pointCount = 1; + break; + case kCGPathElementAddQuadCurveToPoint: + pointCount = 2; + break; + case kCGPathElementAddCurveToPoint: + pointCount = 3; + break; + case kCGPathElementCloseSubpath: + pointCount = 0; + break; + } + return pointCount; +} + +// CGPathApplierFunction that attempts to modify elements inside a CGPath +void cgPathModifyingApplierFunction(void* info, const CGPathElement* element) { + int pointCount = cgPathPointCountForElementType(element->type); + + NSMutableArray* points = [NSMutableArray array]; + + for (int i = 0; i < pointCount; i++) { + element->points[i] = CGPointMake(44, 44); + } +} + +// CGPathApplierFunction that adds elements to an NSMutableArray +void cgPathApplierFunction(void* info, const CGPathElement* element) { + NSMutableArray* result = (__bridge NSMutableArray*)(info); + + int pointCount = cgPathPointCountForElementType(element->type); + + NSMutableArray* points = [NSMutableArray array]; + + for (int i = 0; i < pointCount; i++) { + CGPoint point = element->points[i]; + [points addObject:@(point.x)]; + [points addObject:@(point.y)]; + } + + [result addObject:@{ kTypeKey : @(element->type), kPointsKey : points }]; +} + +// Helper function that compares results from cgPathApplierFunction to expected results +void cgPathCompare(NSArray* expected, NSArray* result) { + ASSERT_EQ(expected.count, result.count) << "Counts do not match for expected and result"; + + [expected enumerateObjectsUsingBlock:^(NSDictionary* expectedDict, NSUInteger elementIndex, BOOL* stop) { + NSDictionary* resultDict = result[elementIndex]; + CGPathElementType expectedType = (CGPathElementType)[expectedDict[kTypeKey] integerValue]; + CGPathElementType resultType = (CGPathElementType)[resultDict[kTypeKey] integerValue]; + ASSERT_EQ(expectedType, resultType) << "Elements in result and expected do not match"; + ASSERT_EQ([expectedDict[kPointsKey] count], [resultDict[kPointsKey] count]) + << "Point count in result and expected element do not match"; + + [expectedDict[kPointsKey] enumerateObjectsUsingBlock:^(NSNumber* expectedPointValue, NSUInteger pointIndex, BOOL* stop) { + float resultPoint = [resultDict[kPointsKey][pointIndex] floatValue]; + ASSERT_FLOAT_EQ([expectedPointValue floatValue], resultPoint); + }]; + }]; +} + +TEST(CGPath, CGPathApplyTestModification) { + CGMutablePathRef path = CGPathCreateMutable(); + + CGRect rect = CGRectMake(2, 4, 8, 16); + + CGPathAddRect(path, NULL, rect); + + NSMutableArray* result = [NSMutableArray array]; + + CGPathApply(path, result, cgPathModifyingApplierFunction); + + CGPathApply(path, result, cgPathApplierFunction); + + NSArray* expected = @[ + @{ kTypeKey : @(kCGPathElementMoveToPoint), + kPointsKey : @[ @2, @4 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @10, @4 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @10, @20 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @2, @20 ] }, + @{ kTypeKey : @(kCGPathElementCloseSubpath), + kPointsKey : [NSArray array] } + ]; + + cgPathCompare(expected, result); + + CGPathRelease(path); +} + +TEST(CGPath, CGPathApplyAddRect) { + CGMutablePathRef path = CGPathCreateMutable(); + + CGRect rect = CGRectMake(2, 4, 8, 16); + + CGPathAddRect(path, NULL, rect); + + NSMutableArray* result = [NSMutableArray array]; + + CGPathApply(path, result, cgPathApplierFunction); + + NSArray* expected = @[ + @{ kTypeKey : @(kCGPathElementMoveToPoint), + kPointsKey : @[ @2, @4 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @10, @4 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @10, @20 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @2, @20 ] }, + @{ kTypeKey : @(kCGPathElementCloseSubpath), + kPointsKey : [NSArray array] } + ]; + + cgPathCompare(expected, result); + + CGPathRelease(path); +} + +TEST(CGPath, CGPathApplyAddEllipse) { + CGMutablePathRef path = CGPathCreateMutable(); + + CGRect rect = CGRectMake(40, 40, 200, 40); + + CGPathAddEllipseInRect(path, NULL, rect); + + NSMutableArray* result = [NSMutableArray array]; + + CGPathApply(path, result, cgPathApplierFunction); + + NSArray* expected = @[ + @{ kTypeKey : @(kCGPathElementMoveToPoint), + kPointsKey : @[ @240, @60 ] }, + @{ kTypeKey : @(kCGPathElementAddCurveToPoint), + kPointsKey : @[ @240, @71.045695, @195.228475, @80, @140, @80 ] }, + @{ kTypeKey : @(kCGPathElementAddCurveToPoint), + kPointsKey : @[ @84.771525, @80, @40, @71.045695, @40, @60 ] }, + @{ kTypeKey : @(kCGPathElementAddCurveToPoint), + kPointsKey : @[ @40, @48.954305, @84.771525, @40, @140, @40 ] }, + @{ kTypeKey : @(kCGPathElementAddCurveToPoint), + kPointsKey : @[ @195.228475, @40, @240, @48.954305, @240, @60 ] }, + @{ kTypeKey : @(kCGPathElementCloseSubpath), + kPointsKey : [NSArray array] } + ]; + + cgPathCompare(expected, result); + + CGPathRelease(path); +} + +TEST(CGPath, CGPathApplyAddArc) { + CGMutablePathRef path = CGPathCreateMutable(); + + CGPathAddArc(path, NULL, 25, 100, 20, M_PI * 1.25, 0, 1); + + NSMutableArray* result = [NSMutableArray array]; + + CGPathApply(path, result, cgPathApplierFunction); + + NSArray* expected = @[ + @{ kTypeKey : @(kCGPathElementMoveToPoint), + kPointsKey : @[ @10.857863, @85.857865 ] }, + @{ + kTypeKey : @(kCGPathElementAddCurveToPoint), + kPointsKey : @[ @3.047378, @93.668352, @3.047379, @106.331651, @10.857865, @114.142137 ] + }, + @{ + kTypeKey : @(kCGPathElementAddCurveToPoint), + kPointsKey : @[ @18.668352, @121.952622, @31.331651, @121.952621, @39.142137, @114.142135 ] + }, + @{ kTypeKey : @(kCGPathElementAddCurveToPoint), + kPointsKey : @[ @42.892864, @110.391407, @45, @105.304329, @45, @100 ] } + ]; + + cgPathCompare(expected, result); + + CGPathRelease(path); +} + +TEST(CGPath, CGPathApplyAddArcToPoint) { + CGMutablePathRef path = CGPathCreateMutable(); + + CGPathMoveToPoint(path, NULL, 400, 400); + CGPathAddArcToPoint(path, NULL, 140, 250, 110, 180, 50); + + NSMutableArray* result = [NSMutableArray array]; + + CGPathApply(path, result, cgPathApplierFunction); + + NSArray* expected = @[ + @{ kTypeKey : @(kCGPathElementMoveToPoint), + kPointsKey : @[ @400, @400 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @154.415379, @258.316565 ] }, + @{ + kTypeKey : @(kCGPathElementAddCurveToPoint), + kPointsKey : @[ @145.057503, @252.917790, @137.699975, @244.633276, @133.444250, @234.703250 ] + } + ]; + + cgPathCompare(expected, result); + + CGPathRelease(path); +} \ No newline at end of file From 93fd110a16c5da95e81c81a1f5f2678e249efd52 Mon Sep 17 00:00:00 2001 From: jeffMeador Date: Mon, 27 Jun 2016 15:47:47 -0400 Subject: [PATCH 06/10] Removing unused code in CGPathTests.mm Removed unused array in cgPathModifyingApplierFunction. --- tests/unittests/CoreGraphics/CGPathTests.mm | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/unittests/CoreGraphics/CGPathTests.mm b/tests/unittests/CoreGraphics/CGPathTests.mm index 43b5043df8..e904c2a2db 100644 --- a/tests/unittests/CoreGraphics/CGPathTests.mm +++ b/tests/unittests/CoreGraphics/CGPathTests.mm @@ -47,8 +47,6 @@ int cgPathPointCountForElementType(CGPathElementType type) { void cgPathModifyingApplierFunction(void* info, const CGPathElement* element) { int pointCount = cgPathPointCountForElementType(element->type); - NSMutableArray* points = [NSMutableArray array]; - for (int i = 0; i < pointCount; i++) { element->points[i] = CGPointMake(44, 44); } From 184e31f31f9ca6bd8918de7af31fc0d7b337093c Mon Sep 17 00:00:00 2001 From: jeffMeador Date: Tue, 28 Jun 2016 16:55:57 -0400 Subject: [PATCH 07/10] Refactored CGPath based on feedback. CGPathApply no longer prevents the maniuplation of points. Added _CGPathAddElement utility function to handle increasing size of elements array and creating point arrays. Point arrays are all now the max size, 3 points. Point arrays are copied in CGPathCreateMutableCopy to new path. CGPathEqualToPath implemented so path copies could be tested. Comments have been added for new math. CGPathAddArcToPoint marked as a todo as it needs to be refactored here, CGContextImpl::CGContextAddArcToPoint and CGContextCairo::CGContextAddArcToPoint. Fixed implementation of CGPathCreateWithEllipseInRect. Added points copy in CGPathAddPath and added unit test for CGPathAddPath. --- Frameworks/CoreGraphics/CGPath.mm | 244 ++++++++++---------- Frameworks/include/CGPathInternal.h | 4 +- include/CoreGraphics/CGPath.h | 2 +- tests/unittests/CoreGraphics/CGPathTests.mm | 195 ++++++++++++---- 4 files changed, 279 insertions(+), 166 deletions(-) diff --git a/Frameworks/CoreGraphics/CGPath.mm b/Frameworks/CoreGraphics/CGPath.mm index 008189b3fb..a5c6f346d3 100644 --- a/Frameworks/CoreGraphics/CGPath.mm +++ b/Frameworks/CoreGraphics/CGPath.mm @@ -58,46 +58,46 @@ void addPoint(float ptX, float ptY) { }; __CGPath::~__CGPath() { - if (_components) { + if (_elements) { for (unsigned i = 0; i < _count; i++) { - CGPoint* points = _components[i].points; + CGPoint* points = _elements[i].points; if (points) { IwFree(points); } } - IwFree(_components); + IwFree(_elements); } } void __CGPath::_applyPath(CGContextRef context) { for (unsigned i = 0; i < _count; i++) { - switch (_components[i].type) { + switch (_elements[i].type) { case kCGPathElementMoveToPoint: - TraceVerbose(TAG, L"Move to %d, %d", (int)_components[i].points[0].x, (int)_components[i].points[0].y); - CGContextMoveToPoint(context, _components[i].points[0].x, _components[i].points[0].y); + TraceVerbose(TAG, L"Move to %d, %d", (int)_elements[i].points[0].x, (int)_elements[i].points[0].y); + CGContextMoveToPoint(context, _elements[i].points[0].x, _elements[i].points[0].y); break; case kCGPathElementAddLineToPoint: - TraceVerbose(TAG, L"Line to %d, %d", (int)_components[i].points[0].x, (int)_components[i].points[0].y); - CGContextAddLineToPoint(context, _components[i].points[0].x, _components[i].points[0].y); + TraceVerbose(TAG, L"Line to %d, %d", (int)_elements[i].points[0].x, (int)_elements[i].points[0].y); + CGContextAddLineToPoint(context, _elements[i].points[0].x, _elements[i].points[0].y); break; case kCGPathElementAddCurveToPoint: CGContextAddCurveToPoint(context, - _components[i].points[0].x, - _components[i].points[0].y, - _components[i].points[1].x, - _components[i].points[1].y, - _components[i].points[2].x, - _components[i].points[2].y); + _elements[i].points[0].x, + _elements[i].points[0].y, + _elements[i].points[1].x, + _elements[i].points[1].y, + _elements[i].points[2].x, + _elements[i].points[2].y); break; case kCGPathElementAddQuadCurveToPoint: CGContextAddQuadCurveToPoint(context, - _components[i].points[0].x, - _components[i].points[0].y, - _components[i].points[1].x, - _components[i].points[1].y); + _elements[i].points[0].x, + _elements[i].points[0].y, + _elements[i].points[1].x, + _elements[i].points[1].y); break; case kCGPathElementCloseSubpath: @@ -115,21 +115,21 @@ void addPoint(float ptX, float ptY) { BBox bbox; for (unsigned i = 0; i < _count; i++) { - switch (_components[i].type) { + switch (_elements[i].type) { case kCGPathElementMoveToPoint: case kCGPathElementAddLineToPoint: - bbox.addPoint(_components[i].points[0].x, _components[i].points[0].y); + bbox.addPoint(_elements[i].points[0].x, _elements[i].points[0].y); break; case kCGPathElementAddCurveToPoint: - bbox.addPoint(_components[i].points[0].x, _components[i].points[0].y); - bbox.addPoint(_components[i].points[1].x, _components[i].points[1].y); - bbox.addPoint(_components[i].points[2].x, _components[i].points[2].y); + bbox.addPoint(_elements[i].points[0].x, _elements[i].points[0].y); + bbox.addPoint(_elements[i].points[1].x, _elements[i].points[1].y); + bbox.addPoint(_elements[i].points[2].x, _elements[i].points[2].y); break; case kCGPathElementAddQuadCurveToPoint: - bbox.addPoint(_components[i].points[0].x, _components[i].points[0].y); - bbox.addPoint(_components[i].points[1].x, _components[i].points[1].y); + bbox.addPoint(_elements[i].points[0].x, _elements[i].points[0].y); + bbox.addPoint(_elements[i].points[1].x, _elements[i].points[1].y); break; case kCGPathElementCloseSubpath: @@ -155,6 +155,22 @@ void _CGPathGetBoundingBoxInternal(CGPathRef pathref, CGRect* rectOut) { pathref->_getBoundingBox(rectOut); } +void _CGPathAddElement( + CGPathRef path, CGPathElementType type, CGPoint p0 = CGPointZero, CGPoint p1 = CGPointZero, CGPoint p2 = CGPointZero) { + if (path->_count + 1 >= path->_max) { + path->_max += 32; + path->_elements = (CGPathElement*)IwRealloc(path->_elements, path->_max * sizeof(CGPathElement)); + } + CGPathElement* element = &path->_elements[path->_count]; + element->points = (CGPoint*)IwCalloc(kCGPathMaxPointCount, sizeof(CGPoint)); + element->type = type; + element->points[0] = p0; + element->points[1] = p1; + element->points[2] = p2; + + path->_count++; +} + /** @Status Interoperable */ @@ -177,8 +193,12 @@ CGMutablePathRef CGPathCreateMutableCopy(CGPathRef path) { auto ret = __CGPath::alloc(nil); ret->_max = path->_max; ret->_count = path->_count; - ret->_components = (CGPathElement*)IwRealloc(ret->_components, ret->_max * sizeof(CGPathElement)); - memcpy(ret->_components, path->_components, path->_count * sizeof(CGPathElement)); + ret->_elements = (CGPathElement*)IwRealloc(ret->_elements, ret->_max * sizeof(CGPathElement)); + memcpy(ret->_elements, path->_elements, path->_count * sizeof(CGPathElement)); + for (unsigned i = 0; i < path->_count; i++) { + ret->_elements[i].points = (CGPoint*)IwCalloc(kCGPathMaxPointCount, sizeof(CGPoint)); + memcpy(ret->_elements[i].points, path->_elements[i].points, kCGPathMaxPointCount * sizeof(CGPoint)); + } return ret; } @@ -198,22 +218,14 @@ void CGPathAddLineToPoint(CGMutablePathRef path, const CGAffineTransform* m, flo y = pt.y; } - CGPathRef pathObj = path; - - if (pathObj->_count + 1 >= pathObj->_max) { - pathObj->_max += 32; - pathObj->_components = (CGPathElement*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(CGPathElement)); - } - - pathObj->_components[pathObj->_count].type = kCGPathElementAddLineToPoint; - - pathObj->_components[pathObj->_count].points = (CGPoint*)IwCalloc(1, sizeof(CGPoint)); - pathObj->_components[pathObj->_count].points[0] = { x, y }; - - pathObj->_count++; + _CGPathAddElement(path, kCGPathElementAddLineToPoint, { x, y }); } CGFloat _CGPathControlPointOffsetMultiplier(CGFloat angle) { + // Constant used to approximate circles with bezier curves. + // An n-piece cubic Bezier curve can approximate a circle, + // when each inner control point is the distance 4/3 * tan(t/4) + // from an outer control point on a unit circle, where t is 360/n degrees, and n > 2 return (4.0f / 3.0f) * tan(angle / 4.0f); } @@ -229,7 +241,8 @@ void CGPathAddArcToPoint(CGMutablePathRef path, const CGAffineTransform* m, floa } CGPoint curPathPosition = CGPathGetCurrentPoint(path); - + // TODO: Deduplicate this code from CGContextImpl::CGContextAddArcToPoint and + // CGContextCairo::CGContextAddArcToPoint double x0, y0; double dx0, dy0, dx2, dy2, xl0, xl2; double san, n0x, n0y, n2x, n2y, t; @@ -275,6 +288,7 @@ void CGPathAddArcToPoint(CGMutablePathRef path, const CGAffineTransform* m, floa (san < 0)); } +// Internal function to break down Arcs into pieces smaller than pi/2. void _CGPathAddArc(CGMutablePathRef path, const CGAffineTransform* m, CGFloat x, @@ -283,24 +297,35 @@ void _CGPathAddArc(CGMutablePathRef path, CGFloat startAngle, CGFloat endAngle, bool clockwise) { + // Get the difference between the start and end angle for this arc CGFloat delta = endAngle - startAngle; + // If the difference is larger than pi/2 then this arc needs to be + // broken down into more pieces + // .00001f is used instead of epsilon here to avoid infinite loops with + // values close to pi/2 if ((fabs(delta) > M_PI_2) && (fabs((M_PI_2 - fabs(delta))) > 0.00001f)) { + // Calculate the angle in the middle of the start & end angle. CGFloat midAngle = startAngle + (M_PI_2 * (delta < 0 ? -1.0f : 1.0f)); + // Call this function again now with the smaller arcs _CGPathAddArc(path, m, x, y, radius, startAngle, midAngle, clockwise); _CGPathAddArc(path, m, x, y, radius, midAngle, endAngle, clockwise); return; } + // The start and end points for the arc, not yet adjusted with the center of the arc CGPoint arcStartRelative = CGPointMake((cos(startAngle) * radius), (sin(startAngle) * radius)); CGPoint arcEndRelative = CGPointMake((cos(endAngle) * radius), (sin(endAngle) * radius)); + // The start and end points of the arc, adjuste for the center. CGPoint arcStart = CGPointMake(arcStartRelative.x + x, arcStartRelative.y + y); CGPoint arcEnd = CGPointMake(arcEndRelative.x + x, arcEndRelative.y + y); + // Use the angle size to determine the offset for control points CGFloat offsetMultiplier = _CGPathControlPointOffsetMultiplier(delta); + // Create the curve with the control points properly offset CGPathAddCurveToPoint(path, m, arcStart.x - (offsetMultiplier * arcStartRelative.y), @@ -322,30 +347,37 @@ void CGPathAddArc(CGMutablePathRef path, CGFloat startAngle, CGFloat endAngle, bool clockwise) { + // Normalize the start angle so it's between 0 and 2*pi startAngle = fmod(startAngle, 2.0f * M_PI); if (startAngle < 0.0f) { startAngle += 2.0f * M_PI; } + // Normalize the end angle so it's between 0 and 2*pi endAngle = fmod(endAngle, 2.0f * M_PI); if (endAngle < 0.0f) { endAngle += 2.0f * M_PI; } + // Calculate the starting point of the arc CGPoint arcStart = CGPointMake((cos(startAngle) * radius) + x, (sin(startAngle) * radius) + y); + // Either draw a line to or move to the start of the arc if (!CGPathIsEmpty(path)) { CGPathAddLineToPoint(path, m, arcStart.x, arcStart.y); } else { CGPathMoveToPoint(path, m, arcStart.x, arcStart.y); } + // Adjust the start/endangles to force the arc + // to be pieced together clockwise or counter-clockwise if (clockwise && endAngle > startAngle) { startAngle += 2.0f * M_PI; } else if (!clockwise && startAngle > endAngle) { endAngle += 2.0f * M_PI; } + // Call the internal function for breaking the arcs down into smaller segments _CGPathAddArc(path, m, x, y, radius, startAngle, endAngle, clockwise); } @@ -365,18 +397,7 @@ void CGPathMoveToPoint(CGMutablePathRef path, const CGAffineTransform* m, float y = pt.y; } - CGPathRef pathObj = path; - - if (pathObj->_count + 1 >= pathObj->_max) { - pathObj->_max += 32; - pathObj->_components = (CGPathElement*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(CGPathElement)); - } - - pathObj->_components[pathObj->_count].type = kCGPathElementMoveToPoint; - pathObj->_components[pathObj->_count].points = (CGPoint*)IwCalloc(1, sizeof(CGPoint)); - pathObj->_components[pathObj->_count].points[0] = { x, y }; - - pathObj->_count++; + _CGPathAddElement(path, kCGPathElementMoveToPoint, { x, y }); } /** @@ -414,11 +435,13 @@ void CGPathAddPath(CGMutablePathRef path, const CGAffineTransform* m, CGPathRef if (pathObj->_count + copyObj->_count >= pathObj->_max) { pathObj->_max += copyObj->_count; - pathObj->_components = (CGPathElement*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(CGPathElement)); + pathObj->_elements = (CGPathElement*)IwRealloc(pathObj->_elements, pathObj->_max * sizeof(CGPathElement)); } for (unsigned i = 0; i < copyObj->_count; i++) { - CGPathElement c = copyObj->_components[i]; + CGPathElement c = copyObj->_elements[i]; + c.points = (CGPoint*)IwCalloc(kCGPathMaxPointCount, sizeof(CGPoint)); + memcpy(c.points, copyObj->_elements[i].points, kCGPathMaxPointCount * sizeof(CGPoint)); if (m) { switch (c.type) { @@ -432,7 +455,7 @@ void CGPathAddPath(CGMutablePathRef path, const CGAffineTransform* m, CGPathRef } } - pathObj->_components[pathObj->_count] = c; + pathObj->_elements[pathObj->_count] = c; pathObj->_count++; } } @@ -441,6 +464,7 @@ void CGPathAddPath(CGMutablePathRef path, const CGAffineTransform* m, CGPathRef @Status Interoperable */ void CGPathAddEllipseInRect(CGMutablePathRef path, const CGAffineTransform* m, CGRect rect) { + // Determine the control point offset multiplier to create 4 arcs CGFloat offsetMultiplier = _CGPathControlPointOffsetMultiplier(M_PI_2); CGFloat xControlPointOffset = offsetMultiplier * CGRectGetWidth(rect) / 2.0; @@ -454,8 +478,10 @@ void CGPathAddEllipseInRect(CGMutablePathRef path, const CGAffineTransform* m, C CGFloat midY = CGRectGetMidY(rect); CGFloat maxY = CGRectGetMaxY(rect); + // Move to the center of the ellipse CGPathMoveToPoint(path, m, maxX, midY); + // Add the 4 curves for the ellipse CGPathAddCurveToPoint(path, m, maxX, midY + yControlPointOffset, midX + xControlPointOffset, maxY, midX, maxY); CGPathAddCurveToPoint(path, m, midX - xControlPointOffset, maxY, minX, midY + yControlPointOffset, minX, midY); CGPathAddCurveToPoint(path, m, minX, midY - yControlPointOffset, midX - xControlPointOffset, minY, midX, minY); @@ -468,17 +494,7 @@ void CGPathAddEllipseInRect(CGMutablePathRef path, const CGAffineTransform* m, C @Status Interoperable */ void CGPathCloseSubpath(CGMutablePathRef path) { - CGPathRef pathObj = path; - - if (pathObj->_count + 1 >= pathObj->_max) { - pathObj->_max += 32; - pathObj->_components = (CGPathElement*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(CGPathElement)); - } - - pathObj->_components[pathObj->_count].type = kCGPathElementCloseSubpath; - - pathObj->_components[pathObj->_count].points = NULL; - pathObj->_count++; + _CGPathAddElement(path, kCGPathElementCloseSubpath); } /** @@ -527,23 +543,10 @@ void CGPathAddQuadCurveToPoint(CGMutablePathRef path, const CGAffineTransform* m assert(!m); CGPathRef pathObj = path; - CGPoint p = { x, y }; CGPoint cp = { cpx, cpy }; + CGPoint p = { x, y }; - if (pathObj->_count + 1 >= pathObj->_max) { - pathObj->_max += 32; - pathObj->_components = (CGPathElement*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(CGPathElement)); - } - - int count = pathObj->_count; - - pathObj->_components[count].type = kCGPathElementAddQuadCurveToPoint; - - pathObj->_components[count].points = (CGPoint*)IwCalloc(2, sizeof(CGPoint)); - pathObj->_components[pathObj->_count].points[0] = cp; - pathObj->_components[pathObj->_count].points[1] = p; - - pathObj->_count++; + _CGPathAddElement(path, kCGPathElementAddQuadCurveToPoint, cp, p); } /** @@ -563,20 +566,7 @@ void CGPathAddCurveToPoint( end = CGPointApplyAffineTransform(end, *m); } - if (pathObj->_count + 1 >= pathObj->_max) { - pathObj->_max += 32; - pathObj->_components = (CGPathElement*)IwRealloc(pathObj->_components, pathObj->_max * sizeof(CGPathElement)); - } - - int count = pathObj->_count; - - pathObj->_components[count].type = kCGPathElementAddCurveToPoint; - pathObj->_components[count].points = (CGPoint*)IwCalloc(3, sizeof(CGPoint)); - pathObj->_components[count].points[0] = cp1; - pathObj->_components[count].points[1] = cp2; - pathObj->_components[count].points[2] = end; - - pathObj->_count++; + _CGPathAddElement(path, kCGPathElementAddCurveToPoint, cp1, cp2, end); } /** @@ -593,10 +583,8 @@ CGPathRef CGPathCreateWithRect(CGRect rect, const CGAffineTransform* trans) { @Status Interoperable */ CGPathRef CGPathCreateWithEllipseInRect(CGRect rect, const CGAffineTransform* transform) { - printf("Tried to create an ellipse, not implemented, creating rect..\n"); - CGMutablePathRef ret = CGPathCreateMutable(); - CGPathAddRect(ret, 0, rect); + CGPathAddEllipseInRect(ret, transform, rect); return (CGPathRef)ret; } @@ -662,21 +650,11 @@ int _CGPathPointCountForElementType(CGPathElementType type) { @Status Interoperable */ void CGPathApply(CGPathRef path, void* info, CGPathApplierFunction function) { - if (path == NULL) { - return; - } + // TODO: Add check for NULL and return. Add for other relevant functions in this file. for (unsigned i = 0; i < path->_count; i++) { - CGPathElement element = path->_components[i]; - CGPathElement returnElement; - returnElement.type = element.type; - int pointCount = _CGPathPointCountForElementType(element.type); - returnElement.points = (CGPoint*)IwCalloc(pointCount, sizeof(CGPoint)); - memcpy(returnElement.points, element.points, pointCount * sizeof(CGPoint)); - function(info, &returnElement); - if (returnElement.points) { - IwFree(returnElement.points); - } + CGPathElement element = path->_elements[i]; + function(info, &element); } } @@ -737,12 +715,32 @@ CGPathRef CGPathCreateWithRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat } /** - @Status Stub - @Notes + @Status Interoperable */ bool CGPathEqualToPath(CGPathRef path1, CGPathRef path2) { - UNIMPLEMENTED(); - return StubReturn(); + if (path1->_count != path2->_count) { + return false; + } + + for (unsigned i = 0; i < path1->_count; i++) { + CGPathElement element1 = path1->_elements[i]; + CGPathElement element2 = path2->_elements[i]; + if (element1.type != element2.type) { + return false; + } + unsigned pointCount = _CGPathPointCountForElementType(element1.type); + for (unsigned p = 0; p < pointCount; p++) { + CGPoint p1 = element1.points[p]; + CGPoint p2 = element2.points[p]; + if (abs(p1.x - p2.x) > FLT_EPSILON) { + return false; + } + if (abs(p1.y - p2.y) > FLT_EPSILON) { + return false; + } + } + } + return true; } /** @@ -750,7 +748,7 @@ bool CGPathEqualToPath(CGPathRef path1, CGPathRef path2) { */ CGPoint CGPathGetCurrentPoint(CGPathRef path) { if (path->_count > 0) { - CGPathElement c = path->_components[path->_count - 1]; + CGPathElement c = path->_elements[path->_count - 1]; switch (c.type) { case kCGPathElementMoveToPoint: case kCGPathElementAddLineToPoint: @@ -926,22 +924,22 @@ CGRect _CGPathFitRect(CGPathRef pathref, CGRect rect, CGSize maxSize, float padd bool startPointSet = false; for (unsigned i = 0; i < path->_count; i++) { - switch (path->_components[i].type) { + switch (path->_elements[i].type) { case kCGPathElementMoveToPoint: if (!startPointSet) { - startPoint = path->_components[i].points[0]; + startPoint = path->_elements[i].points[0]; startPointSet = true; } - curPoint = path->_components[i].points[0]; + curPoint = path->_elements[i].points[0]; break; case kCGPathElementAddLineToPoint: if (!startPointSet) { - startPoint = path->_components[i].points[0]; + startPoint = path->_elements[i].points[0]; startPointSet = true; } - s.AddLine(curPoint, path->_components[i].points[0]); - curPoint = path->_components[i].points[0]; + s.AddLine(curPoint, path->_elements[i].points[0]); + curPoint = path->_elements[i].points[0]; break; case kCGPathElementCloseSubpath: diff --git a/Frameworks/include/CGPathInternal.h b/Frameworks/include/CGPathInternal.h index ce07f74ef3..05e3882541 100644 --- a/Frameworks/include/CGPathInternal.h +++ b/Frameworks/include/CGPathInternal.h @@ -21,8 +21,10 @@ #include "CoreGraphics/CGContext.h" #include "CoreGraphics/CGPath.h" +const int kCGPathMaxPointCount = 3; + struct __CGPath : public CFBridgeBase<__CGPath> { - CGPathElement* _components; + CGPathElement* _elements; NSUInteger _count; NSUInteger _max; diff --git a/include/CoreGraphics/CGPath.h b/include/CoreGraphics/CGPath.h index ecbbcac645..9c1426ec36 100644 --- a/include/CoreGraphics/CGPath.h +++ b/include/CoreGraphics/CGPath.h @@ -115,7 +115,7 @@ COREGRAPHICS_EXPORT void CGPathMoveToPoint(CGMutablePathRef path, const CGAffine COREGRAPHICS_EXPORT void CGPathCloseSubpath(CGMutablePathRef path); COREGRAPHICS_EXPORT void CGPathAddEllipseInRect(CGMutablePathRef path, const CGAffineTransform* m, CGRect rect); -COREGRAPHICS_EXPORT bool CGPathEqualToPath(CGPathRef path1, CGPathRef path2) STUB_METHOD; +COREGRAPHICS_EXPORT bool CGPathEqualToPath(CGPathRef path1, CGPathRef path2); COREGRAPHICS_EXPORT CGRect CGPathGetBoundingBox(CGPathRef path); diff --git a/tests/unittests/CoreGraphics/CGPathTests.mm b/tests/unittests/CoreGraphics/CGPathTests.mm index e904c2a2db..b7425130c3 100644 --- a/tests/unittests/CoreGraphics/CGPathTests.mm +++ b/tests/unittests/CoreGraphics/CGPathTests.mm @@ -21,7 +21,7 @@ static NSString* const kPointsKey = @"PointsKey"; static NSString* const kTypeKey = @"TypeKey"; - +// Helper function to know # of points in an element int cgPathPointCountForElementType(CGPathElementType type) { int pointCount = 0; @@ -43,15 +43,6 @@ int cgPathPointCountForElementType(CGPathElementType type) { return pointCount; } -// CGPathApplierFunction that attempts to modify elements inside a CGPath -void cgPathModifyingApplierFunction(void* info, const CGPathElement* element) { - int pointCount = cgPathPointCountForElementType(element->type); - - for (int i = 0; i < pointCount; i++) { - element->points[i] = CGPointMake(44, 44); - } -} - // CGPathApplierFunction that adds elements to an NSMutableArray void cgPathApplierFunction(void* info, const CGPathElement* element) { NSMutableArray* result = (__bridge NSMutableArray*)(info); @@ -88,37 +79,6 @@ void cgPathCompare(NSArray* expected, NSArray* result) { }]; } -TEST(CGPath, CGPathApplyTestModification) { - CGMutablePathRef path = CGPathCreateMutable(); - - CGRect rect = CGRectMake(2, 4, 8, 16); - - CGPathAddRect(path, NULL, rect); - - NSMutableArray* result = [NSMutableArray array]; - - CGPathApply(path, result, cgPathModifyingApplierFunction); - - CGPathApply(path, result, cgPathApplierFunction); - - NSArray* expected = @[ - @{ kTypeKey : @(kCGPathElementMoveToPoint), - kPointsKey : @[ @2, @4 ] }, - @{ kTypeKey : @(kCGPathElementAddLineToPoint), - kPointsKey : @[ @10, @4 ] }, - @{ kTypeKey : @(kCGPathElementAddLineToPoint), - kPointsKey : @[ @10, @20 ] }, - @{ kTypeKey : @(kCGPathElementAddLineToPoint), - kPointsKey : @[ @2, @20 ] }, - @{ kTypeKey : @(kCGPathElementCloseSubpath), - kPointsKey : [NSArray array] } - ]; - - cgPathCompare(expected, result); - - CGPathRelease(path); -} - TEST(CGPath, CGPathApplyAddRect) { CGMutablePathRef path = CGPathCreateMutable(); @@ -232,4 +192,157 @@ void cgPathCompare(NSArray* expected, NSArray* result) { cgPathCompare(expected, result); CGPathRelease(path); +} + +TEST(CGPath, CGPathAddQuadCurveToPoint) { + CGMutablePathRef path = CGPathCreateMutable(); + + CGPathMoveToPoint(path, NULL, 400, 400); + CGPathAddQuadCurveToPoint(path, NULL, 140, 250, 110, 180); + + NSMutableArray* result = [NSMutableArray array]; + + CGPathApply(path, result, cgPathApplierFunction); + + NSArray* expected = @[ + @{ kTypeKey : @(kCGPathElementMoveToPoint), + kPointsKey : @[ @400, @400 ] }, + @{ kTypeKey : @(kCGPathElementAddQuadCurveToPoint), + kPointsKey : @[ @140, @250, @110, @180 ] } + ]; + + cgPathCompare(expected, result); + + CGPathRelease(path); +} + +TEST(CGPath, CGPathCreateMutableCopy) { + CGMutablePathRef path1 = CGPathCreateMutable(); + + CGRect rect = CGRectMake(2, 4, 8, 16); + + CGPathAddEllipseInRect(path1, NULL, rect); + + CGMutablePathRef path2 = CGPathCreateMutableCopy(path1); + + ASSERT_NE(path1, path2); + + ASSERT_TRUE(CGPathEqualToPath(path1, path2)); + + CGPathRelease(path1); + + CGPathRelease(path2); +} + +TEST(CGPath, CGPathEqualToPath) { + CGMutablePathRef path1 = CGPathCreateMutable(); + + CGRect rect = CGRectMake(2, 4, 8, 16); + + CGPathAddRect(path1, NULL, rect); + + // Create a copy of the path + CGMutablePathRef path2 = CGPathCreateMutableCopy(path1); + + // Change the copy + CGPathAddEllipseInRect(path2, NULL, rect); + + // Make sure the paths are not the same object + ASSERT_NE(path1, path2); + + // Make sure paths are not equal + ASSERT_FALSE(CGPathEqualToPath(path1, path2)); + + // Release the copy + CGPathRelease(path2); + + // Now test the original path to make sure it's still what we expect + NSArray* expected = @[ + @{ kTypeKey : @(kCGPathElementMoveToPoint), + kPointsKey : @[ @2, @4 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @10, @4 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @10, @20 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @2, @20 ] }, + @{ kTypeKey : @(kCGPathElementCloseSubpath), + kPointsKey : [NSArray array] } + ]; + + NSMutableArray* result = [NSMutableArray array]; + + CGPathApply(path1, result, cgPathApplierFunction); + + cgPathCompare(expected, result); + + CGPathRelease(path1); +} + +TEST(CGPath, CGPathAddPath) { + CGMutablePathRef path1 = CGPathCreateMutable(); + + CGRect rect1 = CGRectMake(2, 4, 8, 16); + + CGPathAddRect(path1, NULL, rect1); + + CGMutablePathRef path2 = CGPathCreateMutable(); + + CGRect rect2 = CGRectMake(4, 4, 8, 16); + + CGPathAddRect(path2, NULL, rect2); + + CGPathAddPath(path1, NULL, path2); + + NSArray* expected1 = @[ + @{ kTypeKey : @(kCGPathElementMoveToPoint), + kPointsKey : @[ @2, @4 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @10, @4 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @10, @20 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @2, @20 ] }, + @{ kTypeKey : @(kCGPathElementCloseSubpath), + kPointsKey : [NSArray array] }, + @{ kTypeKey : @(kCGPathElementMoveToPoint), + kPointsKey : @[ @4, @4 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @12, @4 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @12, @20 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @4, @20 ] }, + @{ kTypeKey : @(kCGPathElementCloseSubpath), + kPointsKey : [NSArray array] } + ]; + + NSArray* expected2 = @[ + @{ kTypeKey : @(kCGPathElementMoveToPoint), + kPointsKey : @[ @4, @4 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @12, @4 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @12, @20 ] }, + @{ kTypeKey : @(kCGPathElementAddLineToPoint), + kPointsKey : @[ @4, @20 ] }, + @{ kTypeKey : @(kCGPathElementCloseSubpath), + kPointsKey : [NSArray array] } + ]; + + NSMutableArray* result2 = [NSMutableArray array]; + + CGPathApply(path1, result2, cgPathApplierFunction); + + cgPathCompare(expected1, result2); + + CGPathRelease(path2); + + NSMutableArray* result1 = [NSMutableArray array]; + + CGPathApply(path1, result1, cgPathApplierFunction); + + cgPathCompare(expected1, result1); + + CGPathRelease(path1); } \ No newline at end of file From ccc3909bfed46cc157e6e839051c7ec60f678601 Mon Sep 17 00:00:00 2001 From: jeffMeador Date: Wed, 29 Jun 2016 08:40:16 -0400 Subject: [PATCH 08/10] Added CGPathElementInternal to simplify alloc. CGPath now uses CGPathElementInternal when creating an array of elements. CGPathElementInternal holds a fixed sized array big enough for the maximum number of points in an element. CGPathElementInternal inherits from CGPathElement and adjusts the points pointer to point at its internal points array as much as possible. Calls to updatePointsArrayPointer() need to be done manually when allocating CGPathElementInternal arrays dynamically or using memcpy. --- Frameworks/CoreGraphics/CGPath.mm | 55 +++++++++++++---------------- Frameworks/include/CGPathInternal.h | 30 +++++++++++++++- 2 files changed, 53 insertions(+), 32 deletions(-) diff --git a/Frameworks/CoreGraphics/CGPath.mm b/Frameworks/CoreGraphics/CGPath.mm index a5c6f346d3..254eea1721 100644 --- a/Frameworks/CoreGraphics/CGPath.mm +++ b/Frameworks/CoreGraphics/CGPath.mm @@ -59,12 +59,6 @@ void addPoint(float ptX, float ptY) { __CGPath::~__CGPath() { if (_elements) { - for (unsigned i = 0; i < _count; i++) { - CGPoint* points = _elements[i].points; - if (points) { - IwFree(points); - } - } IwFree(_elements); } } @@ -159,10 +153,12 @@ void _CGPathAddElement( CGPathRef path, CGPathElementType type, CGPoint p0 = CGPointZero, CGPoint p1 = CGPointZero, CGPoint p2 = CGPointZero) { if (path->_count + 1 >= path->_max) { path->_max += 32; - path->_elements = (CGPathElement*)IwRealloc(path->_elements, path->_max * sizeof(CGPathElement)); + path->_elements = (CGPathElementInternal*)IwRealloc(path->_elements, path->_max * sizeof(CGPathElementInternal)); } - CGPathElement* element = &path->_elements[path->_count]; - element->points = (CGPoint*)IwCalloc(kCGPathMaxPointCount, sizeof(CGPoint)); + CGPathElementInternal* element = &path->_elements[path->_count]; + // The new elements needs its points array pointer updated + // because it was created with alloc + element->updatePointsArrayPointer(); element->type = type; element->points[0] = p0; element->points[1] = p1; @@ -193,11 +189,12 @@ CGMutablePathRef CGPathCreateMutableCopy(CGPathRef path) { auto ret = __CGPath::alloc(nil); ret->_max = path->_max; ret->_count = path->_count; - ret->_elements = (CGPathElement*)IwRealloc(ret->_elements, ret->_max * sizeof(CGPathElement)); - memcpy(ret->_elements, path->_elements, path->_count * sizeof(CGPathElement)); + ret->_elements = (CGPathElementInternal*)IwRealloc(ret->_elements, ret->_max * sizeof(CGPathElementInternal)); + memcpy(ret->_elements, path->_elements, path->_count * sizeof(CGPathElementInternal)); + // All of the new elements need their points array pointer updated + // because they were created with alloc + memcpy for (unsigned i = 0; i < path->_count; i++) { - ret->_elements[i].points = (CGPoint*)IwCalloc(kCGPathMaxPointCount, sizeof(CGPoint)); - memcpy(ret->_elements[i].points, path->_elements[i].points, kCGPathMaxPointCount * sizeof(CGPoint)); + ret->_elements[i].updatePointsArrayPointer(); } return ret; } @@ -435,14 +432,11 @@ void CGPathAddPath(CGMutablePathRef path, const CGAffineTransform* m, CGPathRef if (pathObj->_count + copyObj->_count >= pathObj->_max) { pathObj->_max += copyObj->_count; - pathObj->_elements = (CGPathElement*)IwRealloc(pathObj->_elements, pathObj->_max * sizeof(CGPathElement)); + pathObj->_elements = (CGPathElementInternal*)IwRealloc(pathObj->_elements, pathObj->_max * sizeof(CGPathElementInternal)); } for (unsigned i = 0; i < copyObj->_count; i++) { - CGPathElement c = copyObj->_elements[i]; - c.points = (CGPoint*)IwCalloc(kCGPathMaxPointCount, sizeof(CGPoint)); - memcpy(c.points, copyObj->_elements[i].points, kCGPathMaxPointCount * sizeof(CGPoint)); - + CGPathElementInternal c = copyObj->_elements[i]; if (m) { switch (c.type) { case kCGPathElementMoveToPoint: @@ -653,8 +647,7 @@ void CGPathApply(CGPathRef path, void* info, CGPathApplierFunction function) { // TODO: Add check for NULL and return. Add for other relevant functions in this file. for (unsigned i = 0; i < path->_count; i++) { - CGPathElement element = path->_elements[i]; - function(info, &element); + function(info, &path->_elements[i]); } } @@ -723,15 +716,15 @@ bool CGPathEqualToPath(CGPathRef path1, CGPathRef path2) { } for (unsigned i = 0; i < path1->_count; i++) { - CGPathElement element1 = path1->_elements[i]; - CGPathElement element2 = path2->_elements[i]; - if (element1.type != element2.type) { + CGPathElement* element1 = &path1->_elements[i]; + CGPathElement* element2 = &path2->_elements[i]; + if (element1->type != element2->type) { return false; } - unsigned pointCount = _CGPathPointCountForElementType(element1.type); + unsigned pointCount = _CGPathPointCountForElementType(element1->type); for (unsigned p = 0; p < pointCount; p++) { - CGPoint p1 = element1.points[p]; - CGPoint p2 = element2.points[p]; + CGPoint p1 = element1->points[p]; + CGPoint p2 = element2->points[p]; if (abs(p1.x - p2.x) > FLT_EPSILON) { return false; } @@ -748,15 +741,15 @@ bool CGPathEqualToPath(CGPathRef path1, CGPathRef path2) { */ CGPoint CGPathGetCurrentPoint(CGPathRef path) { if (path->_count > 0) { - CGPathElement c = path->_elements[path->_count - 1]; - switch (c.type) { + CGPathElement* c = &path->_elements[path->_count - 1]; + switch (c->type) { case kCGPathElementMoveToPoint: case kCGPathElementAddLineToPoint: - return c.points[0]; + return c->points[0]; case kCGPathElementAddQuadCurveToPoint: - return c.points[1]; + return c->points[1]; case kCGPathElementAddCurveToPoint: - return c.points[2]; + return c->points[2]; default: return CGPointZero; } diff --git a/Frameworks/include/CGPathInternal.h b/Frameworks/include/CGPathInternal.h index 05e3882541..9712381a2c 100644 --- a/Frameworks/include/CGPathInternal.h +++ b/Frameworks/include/CGPathInternal.h @@ -23,8 +23,36 @@ const int kCGPathMaxPointCount = 3; +struct CGPathElementInternal : CGPathElement { + // Internal representation of points used to simplify size calculations + // CGPoint* points in CGPathElement should always point to this array + CGPoint internalPoints[kCGPathMaxPointCount]; + + // Constructor. Used to adjust array pointer after creation. + CGPathElementInternal() { + updatePointsArrayPointer(); + } + // Copy Constructor. Used to adjust array pointer after copy. + CGPathElementInternal(const CGPathElementInternal& copy) : CGPathElement(copy) { + memcpy(this->internalPoints, copy.internalPoints, sizeof(internalPoints)); + updatePointsArrayPointer(); + } + // Assignment operator. Used to adjust array pointer after assignment. + CGPathElementInternal& operator=(const CGPathElementInternal& copy) { + CGPathElement::operator=(copy); + memcpy(this->internalPoints, copy.internalPoints, sizeof(internalPoints)); + updatePointsArrayPointer(); + return *this; + } + // This should be called when elements are created with alloc/memcpy + void updatePointsArrayPointer() { + this->points = internalPoints; + } +}; +typedef struct CGPathElementInternal CGPathElementInternal; + struct __CGPath : public CFBridgeBase<__CGPath> { - CGPathElement* _elements; + CGPathElementInternal* _elements; NSUInteger _count; NSUInteger _max; From 13d52a8f73a6838d0ead1cb177a95bb55e9709da Mon Sep 17 00:00:00 2001 From: jeffMeador Date: Wed, 29 Jun 2016 08:59:04 -0400 Subject: [PATCH 09/10] Fixed constructor in CGPathElementInternal Added call to base struct constructor in CGPathElementInternal --- Frameworks/include/CGPathInternal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Frameworks/include/CGPathInternal.h b/Frameworks/include/CGPathInternal.h index 9712381a2c..fe8d3ddc60 100644 --- a/Frameworks/include/CGPathInternal.h +++ b/Frameworks/include/CGPathInternal.h @@ -29,7 +29,7 @@ struct CGPathElementInternal : CGPathElement { CGPoint internalPoints[kCGPathMaxPointCount]; // Constructor. Used to adjust array pointer after creation. - CGPathElementInternal() { + CGPathElementInternal() : CGPathElement() { updatePointsArrayPointer(); } // Copy Constructor. Used to adjust array pointer after copy. From 52c59486db2dd76954bd72d9168a662ee1ca78f1 Mon Sep 17 00:00:00 2001 From: jeffMeador Date: Wed, 29 Jun 2016 14:24:45 -0400 Subject: [PATCH 10/10] Reduced copies in CGPath Switched to point references in CGPathEqualToPath and element references in CGPathAddPath to avoid copying. Renamed CGPathElementInternal.updatePointsArrayPointer() to CGPathElementInternal.init() --- Frameworks/CoreGraphics/CGPath.mm | 28 ++++++++++++++-------------- Frameworks/include/CGPathInternal.h | 8 ++++---- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Frameworks/CoreGraphics/CGPath.mm b/Frameworks/CoreGraphics/CGPath.mm index 254eea1721..3e998a98e6 100644 --- a/Frameworks/CoreGraphics/CGPath.mm +++ b/Frameworks/CoreGraphics/CGPath.mm @@ -156,9 +156,9 @@ void _CGPathAddElement( path->_elements = (CGPathElementInternal*)IwRealloc(path->_elements, path->_max * sizeof(CGPathElementInternal)); } CGPathElementInternal* element = &path->_elements[path->_count]; - // The new elements needs its points array pointer updated + // The new element needs to call init // because it was created with alloc - element->updatePointsArrayPointer(); + element->init(); element->type = type; element->points[0] = p0; element->points[1] = p1; @@ -191,10 +191,10 @@ CGMutablePathRef CGPathCreateMutableCopy(CGPathRef path) { ret->_count = path->_count; ret->_elements = (CGPathElementInternal*)IwRealloc(ret->_elements, ret->_max * sizeof(CGPathElementInternal)); memcpy(ret->_elements, path->_elements, path->_count * sizeof(CGPathElementInternal)); - // All of the new elements need their points array pointer updated + // All of the new elements need to call init // because they were created with alloc + memcpy for (unsigned i = 0; i < path->_count; i++) { - ret->_elements[i].updatePointsArrayPointer(); + ret->_elements[i].init(); } return ret; } @@ -436,21 +436,21 @@ void CGPathAddPath(CGMutablePathRef path, const CGAffineTransform* m, CGPathRef } for (unsigned i = 0; i < copyObj->_count; i++) { - CGPathElementInternal c = copyObj->_elements[i]; + pathObj->_elements[pathObj->_count] = copyObj->_elements[i]; + pathObj->_count++; + CGPathElementInternal* c = &pathObj->_elements[pathObj->_count]; + if (m) { - switch (c.type) { + switch (c->type) { case kCGPathElementMoveToPoint: case kCGPathElementAddLineToPoint: - c.points[0] = CGPointApplyAffineTransform(c.points[0], *m); + c->points[0] = CGPointApplyAffineTransform(c->points[0], *m); break; default: // Append the path anyway, without transforming. break; } } - - pathObj->_elements[pathObj->_count] = c; - pathObj->_count++; } } @@ -723,12 +723,12 @@ bool CGPathEqualToPath(CGPathRef path1, CGPathRef path2) { } unsigned pointCount = _CGPathPointCountForElementType(element1->type); for (unsigned p = 0; p < pointCount; p++) { - CGPoint p1 = element1->points[p]; - CGPoint p2 = element2->points[p]; - if (abs(p1.x - p2.x) > FLT_EPSILON) { + CGPoint* p1 = &element1->points[p]; + CGPoint* p2 = &element2->points[p]; + if (abs(p1->x - p2->x) > FLT_EPSILON) { return false; } - if (abs(p1.y - p2.y) > FLT_EPSILON) { + if (abs(p1->y - p2->y) > FLT_EPSILON) { return false; } } diff --git a/Frameworks/include/CGPathInternal.h b/Frameworks/include/CGPathInternal.h index fe8d3ddc60..825a99cd92 100644 --- a/Frameworks/include/CGPathInternal.h +++ b/Frameworks/include/CGPathInternal.h @@ -30,22 +30,22 @@ struct CGPathElementInternal : CGPathElement { // Constructor. Used to adjust array pointer after creation. CGPathElementInternal() : CGPathElement() { - updatePointsArrayPointer(); + this->init(); } // Copy Constructor. Used to adjust array pointer after copy. CGPathElementInternal(const CGPathElementInternal& copy) : CGPathElement(copy) { memcpy(this->internalPoints, copy.internalPoints, sizeof(internalPoints)); - updatePointsArrayPointer(); + this->init(); } // Assignment operator. Used to adjust array pointer after assignment. CGPathElementInternal& operator=(const CGPathElementInternal& copy) { CGPathElement::operator=(copy); memcpy(this->internalPoints, copy.internalPoints, sizeof(internalPoints)); - updatePointsArrayPointer(); + this->init(); return *this; } // This should be called when elements are created with alloc/memcpy - void updatePointsArrayPointer() { + void init() { this->points = internalPoints; } };