Skip to content

Commit

Permalink
Implement the CGContext non-drawing path functions in terms of CGPath.
Browse files Browse the repository at this point in the history
  • Loading branch information
DHowett committed Oct 27, 2016
1 parent 7a86a15 commit e529397
Showing 1 changed file with 106 additions and 38 deletions.
144 changes: 106 additions & 38 deletions Frameworks/CoreGraphics/CGContext.mm
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ inline void ComputeStrokeStyle(ID2D1DeviceContext* deviceContext) {
deviceContext->GetFactory(&factory);

std::vector<float> adjustedDashes(dashes.size());
std::transform(dashes.cbegin(), dashes.cend(), adjustedDashes.begin(), [this](const float& f) -> float { return f / lineWidth; });
std::transform(dashes.cbegin(), dashes.cend(), adjustedDashes.begin(), [this](const CGFloat& f) -> float { return f / lineWidth; });
FAIL_FAST_IF_FAILED(factory->CreateStrokeStyle(strokeProperties, adjustedDashes.data(), adjustedDashes.size(), &strokeStyle));
}

Expand Down Expand Up @@ -150,6 +150,17 @@ inline void ClearStrokeStyle() {
return GStateStack().top();
}

inline bool HasPath() {
return _impl.currentPath != nullptr;
}

inline CGMutablePathRef Path() {
if (!_impl.currentPath) {
_impl.currentPath.reset(CGPathCreateMutable());
}
return _impl.currentPath.get();
}

inline void ClearPath() {
_impl.currentPath.reset();
}
Expand Down Expand Up @@ -450,23 +461,27 @@ void CGContextSetCTM(CGContextRef context, CGAffineTransform transform) {
*/
void CGContextBeginPath(CGContextRef context) {
NOISY_RETURN_IF_NULL(context);
UNIMPLEMENTED();

// All subsequent path functions will create the new path as necessary.
context->ClearPath();
}

/**
@Status Interoperable
*/
void CGContextClosePath(CGContextRef context) {
NOISY_RETURN_IF_NULL(context);
UNIMPLEMENTED();

CGPathCloseSubpath(context->Path());
}

/**
@Status Interoperable
*/
void CGContextAddRect(CGContextRef context, CGRect rect) {
NOISY_RETURN_IF_NULL(context);
UNIMPLEMENTED();

CGPathAddRect(context->Path(), &context->CurrentGState().transform, rect);
}

/**
Expand All @@ -489,93 +504,126 @@ void CGContextAddRects(CGContextRef context, const CGRect* rects, unsigned count
*/
void CGContextAddLineToPoint(CGContextRef context, CGFloat x, CGFloat y) {
NOISY_RETURN_IF_NULL(context);
UNIMPLEMENTED();

CGPathAddLineToPoint(context->Path(), &context->CurrentGState().transform, x, y);
}

/**
@Status Interoperable
*/
void CGContextAddCurveToPoint(CGContextRef context, CGFloat cp1x, CGFloat cp1y, CGFloat cp2x, CGFloat cp2y, CGFloat x, CGFloat y) {
NOISY_RETURN_IF_NULL(context);
UNIMPLEMENTED();

CGPathAddCurveToPoint(context->Path(), &context->CurrentGState().transform, cp1x, cp1y, cp2x, cp2y, x, y);
}

/**
@Status Interoperable
*/
void CGContextAddQuadCurveToPoint(CGContextRef context, CGFloat cpx, CGFloat cpy, CGFloat x, CGFloat y) {
NOISY_RETURN_IF_NULL(context);
UNIMPLEMENTED();

CGPathAddQuadCurveToPoint(context->Path(), &context->CurrentGState().transform, cpx, cpy, x, y);
}

/**
@Status Interoperable
*/
void CGContextMoveToPoint(CGContextRef context, CGFloat x, CGFloat y) {
NOISY_RETURN_IF_NULL(context);
UNIMPLEMENTED();

CGPathMoveToPoint(context->Path(), &context->CurrentGState().transform, x, y);
}

/**
@Status Interoperable
*/
void CGContextAddArc(CGContextRef context, CGFloat x, CGFloat y, CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise) {
NOISY_RETURN_IF_NULL(context);
UNIMPLEMENTED();

CGPathAddArc(context->Path(), &context->CurrentGState().transform, x, y, radius, startAngle, endAngle, clockwise);
}

/**
@Status Interoperable
*/
void CGContextAddArcToPoint(CGContextRef context, CGFloat x1, CGFloat y1, CGFloat x2, CGFloat y2, CGFloat radius) {
NOISY_RETURN_IF_NULL(context);
UNIMPLEMENTED();

CGPathAddArcToPoint(context->Path(), &context->CurrentGState().transform, x1, y1, x2, y2, radius);
}

/**
@Status Interoperable
*/
void CGContextAddEllipseInRect(CGContextRef context, CGRect rect) {
NOISY_RETURN_IF_NULL(context);
UNIMPLEMENTED();

CGPathAddEllipseInRect(context->Path(), &context->CurrentGState().transform, rect);
}

/**
@Status Interoperable
*/
void CGContextAddPath(CGContextRef context, CGPathRef path) {
NOISY_RETURN_IF_NULL(context);
// The Apple SDK docs imply that passing a NULL path is valid.
// So avoid calling into the backing if NULL.
if (path) {
UNIMPLEMENTED();
if (!path) {
return;
}

CGPathAddPath(context->Path(), &context->CurrentGState().transform, path);
}

/**
@Status Stub
*/
void CGContextReplacePathWithStrokedPath(CGContextRef context) {
NOISY_RETURN_IF_NULL(context);
UNIMPLEMENTED();

if (!context->HasPath()) {
return;
}

// We're avoiding .Path() here since we want the actual unique_cf<CGMutablePathRef>.
woc::unique_cf<CGMutablePathRef>& currentPathRef{context->Impl().currentPath};

auto& state = context->CurrentGState();

// TODO GH#xxxx When CGPathCreateCopyByStrokingPath is no longer stubbed, remove the diagnostic suppression.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"

woc::unique_cf<CGPathRef> newPath{CGPathCreateCopyByStrokingPath(currentPathRef.get(), &state.transform, state.lineWidth, (CGLineCap)state.strokeProperties.startCap, (CGLineJoin)state.strokeProperties.lineJoin, state.strokeProperties.miterLimit)};

#pragma clang diagnostic pop

currentPathRef.reset(CGPathCreateMutableCopy(newPath.get()));
}

/**
@Status Interoperable
*/
bool CGContextIsPathEmpty(CGContextRef context) {
NOISY_RETURN_IF_NULL(context, StubReturn());
UNIMPLEMENTED();
return StubReturn();

if (!context->HasPath()) {
return true;
}

return CGPathIsEmpty(context->Path());
}

/**
@Status Interoperable
*/
CGRect CGContextGetPathBoundingBox(CGContextRef context) {
NOISY_RETURN_IF_NULL(context, StubReturn());
UNIMPLEMENTED();
return StubReturn();
NOISY_RETURN_IF_NULL(context, CGRectNull);

if (!context->HasPath()) {
return CGRectNull;
}

return CGPathGetBoundingBox(context->Path());
}

/**
Expand All @@ -588,11 +636,7 @@ void CGContextAddLines(CGContextRef context, const CGPoint* points, unsigned cou
return;
}

// TODO(DH) GH#1066 Switch to CGPathAddLines(context->path, points, count);
CGContextMoveToPoint(context, points[0].x, points[0].y);
for (unsigned int i = 1; i < count; i++) {
CGContextAddLineToPoint(context, points[i].x, points[i].y);
}
CGPathAddLines(context->Path(), &context->CurrentGState().transform, points, count);
}
#pragma endregion

Expand All @@ -602,29 +646,41 @@ void CGContextAddLines(CGContextRef context, const CGPoint* points, unsigned cou
@Notes
*/
CGPathRef CGContextCopyPath(CGContextRef context) {
NOISY_RETURN_IF_NULL(context, StubReturn());
UNIMPLEMENTED();
return StubReturn();
NOISY_RETURN_IF_NULL(context, nullptr);

if (!context->HasPath()) {
return nullptr;
}

return CGPathCreateCopy(context->Path());
}

/**
@Status Stub
@Notes
*/
CGPoint CGContextGetPathCurrentPoint(CGContextRef context) {
NOISY_RETURN_IF_NULL(context, StubReturn());
UNIMPLEMENTED();
return StubReturn();
NOISY_RETURN_IF_NULL(context, CGPointZero);

if (!context->HasPath()) {
return CGPointZero;
}

return CGPathGetCurrentPoint(context->Path());
}

/**
@Status Stub
@Notes
*/
bool CGContextPathContainsPoint(CGContextRef context, CGPoint point, CGPathDrawingMode mode) {
NOISY_RETURN_IF_NULL(context, StubReturn());
UNIMPLEMENTED();
return StubReturn();
NOISY_RETURN_IF_NULL(context, false);

if (!context->HasPath()) {
return false;
}

return CGPathContainsPoint(context->Path(), &context->CurrentGState().transform, point, (mode & kCGPathEOFill));
}
#pragma endregion

Expand Down Expand Up @@ -1329,6 +1385,7 @@ static void __CGContextDrawGeometry(CGContextRef context, ID2D1Geometry* geometr

/**
@Status Interoperable
@Notes The current path is cleared as a side effect of this function.
*/
void CGContextStrokeRect(CGContextRef context, CGRect rect) {
NOISY_RETURN_IF_NULL(context);
Expand All @@ -1346,6 +1403,7 @@ void CGContextStrokeRect(CGContextRef context, CGRect rect) {

/**
@Status Interoperable
@Notes The current path is cleared as a side effect of this function.
*/
void CGContextStrokeRectWithWidth(CGContextRef context, CGRect rect, CGFloat width) {
NOISY_RETURN_IF_NULL(context);
Expand All @@ -1357,6 +1415,7 @@ void CGContextStrokeRectWithWidth(CGContextRef context, CGRect rect, CGFloat wid

/**
@Status Interoperable
@Notes The current path is cleared as a side effect of this function.
*/
void CGContextFillRect(CGContextRef context, CGRect rect) {
NOISY_RETURN_IF_NULL(context);
Expand All @@ -1374,6 +1433,7 @@ void CGContextFillRect(CGContextRef context, CGRect rect) {

/**
@Status Interoperable
@Notes The current path is cleared as a side effect of this function.
*/
void CGContextStrokeEllipseInRect(CGContextRef context, CGRect rect) {
NOISY_RETURN_IF_NULL(context);
Expand All @@ -1393,6 +1453,7 @@ void CGContextStrokeEllipseInRect(CGContextRef context, CGRect rect) {

/**
@Status Interoperable
@Notes The current path is cleared as a side effect of this function.
*/
void CGContextFillEllipseInRect(CGContextRef context, CGRect rect) {
NOISY_RETURN_IF_NULL(context);
Expand All @@ -1412,6 +1473,7 @@ void CGContextFillEllipseInRect(CGContextRef context, CGRect rect) {

/**
@Status Interoperable
@Notes The current path is cleared as a side effect of this function.
*/
void CGContextStrokeLineSegments(CGContextRef context, const CGPoint* points, unsigned count) {
NOISY_RETURN_IF_NULL(context);
Expand All @@ -1432,6 +1494,7 @@ void CGContextStrokeLineSegments(CGContextRef context, const CGPoint* points, un

/**
@Status Interoperable
@Notes The current path is cleared as a side effect of this function.
*/
void CGContextFillRects(CGContextRef context, const CGRect* rects, size_t count) {
NOISY_RETURN_IF_NULL(context);
Expand All @@ -1448,34 +1511,39 @@ void CGContextFillRects(CGContextRef context, const CGRect* rects, size_t count)
#pragma region Drawing Operations - Paths
/**
@Status Interoperable
@Notes The current path is cleared as a side effect of this function.
*/
void CGContextDrawPath(CGContextRef context, CGPathDrawingMode mode) {
NOISY_RETURN_IF_NULL(context);
UNIMPLEMENTED();
context->ClearPath();
}

/**
@Status Interoperable
@Notes The current path is cleared as a side effect of this function.
*/
void CGContextStrokePath(CGContextRef context) {
NOISY_RETURN_IF_NULL(context);
CGContextDrawPath(context, kCGPathStroke);
CGContextDrawPath(context, kCGPathStroke); // Clears path.
}

/**
@Status Interoperable
@Notes The current path is cleared as a side effect of this function.
*/
void CGContextFillPath(CGContextRef context) {
NOISY_RETURN_IF_NULL(context);
CGContextDrawPath(context, kCGPathFill);
CGContextDrawPath(context, kCGPathFill); // Clears path.
}

/**
@Status Interoperable
@Notes The current path is cleared as a side effect of this function.
*/
void CGContextEOFillPath(CGContextRef context) {
NOISY_RETURN_IF_NULL(context);
CGContextDrawPath(context, kCGPathEOFill);
CGContextDrawPath(context, kCGPathEOFill); // Clears path.
}
#pragma endregion

Expand Down

0 comments on commit e529397

Please sign in to comment.