From 788d3896722d027c063133af0b9f5a5129be2100 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 1 Jul 2016 16:18:07 -0400 Subject: [PATCH] Implemented CGContextSetPatternPhase (#596) Implemented CGContextSetPatternPhase using the translation portion of the pattern's transform matrix. Also contains a fix for a missing retain in CGContextSetFillPattern. Implements last item in Issue #524 --- Frameworks/CoreGraphics/CGContext.mm | 5 +- Frameworks/CoreGraphics/CGContextImpl.mm | 12 +- Frameworks/include/CGContextImpl.h | 7 ++ build/CoreGraphics/dll/CoreGraphics.def | 1 + .../CoreGraphics.UnitTests.vcxproj | 6 - include/CoreGraphics/CGContext.h | 2 +- .../Samples/CGCCGContextSetPatternPhase.m | 7 -- .../unittests/CoreGraphics/CGContextTests.mm | 103 ++++++++++++++++++ 8 files changed, 125 insertions(+), 18 deletions(-) diff --git a/Frameworks/CoreGraphics/CGContext.mm b/Frameworks/CoreGraphics/CGContext.mm index 0bd842cfab..87432a9bbf 100644 --- a/Frameworks/CoreGraphics/CGContext.mm +++ b/Frameworks/CoreGraphics/CGContext.mm @@ -93,11 +93,10 @@ void CGContextSetFillPattern(CGContextRef ctx, CGPatternRef pattern, const float } /** - @Status Stub + @Status Interoperable */ void CGContextSetPatternPhase(CGContextRef ctx, CGSize phase) { - UNIMPLEMENTED(); - TraceWarning(TAG, L"CGContextSetPatternPhase not implemented"); + return ctx->Backing()->CGContextSetPatternPhase(phase); } /** diff --git a/Frameworks/CoreGraphics/CGContextImpl.mm b/Frameworks/CoreGraphics/CGContextImpl.mm index 37d481790c..b5bb4fc001 100644 --- a/Frameworks/CoreGraphics/CGContextImpl.mm +++ b/Frameworks/CoreGraphics/CGContextImpl.mm @@ -375,7 +375,7 @@ void CGContextImpl::CGContextSetFillPattern(CGPatternRef pattern, const float* components) { CGPattern* intPattern = (CGPattern*)pattern; - curState->curFillColorObject = pattern; + curState->curFillColorObject = [pattern retain]; switch (intPattern->surfaceFmt) { case _ColorRGB: case _Color565: @@ -400,6 +400,16 @@ } } +void CGContextImpl::CGContextSetPatternPhase(CGSize phase) { + if ([curState->curFillColorObject isKindOfClass:[CGPattern class]]) { + CGPattern* pattern = curState->curFillColorObject; + CGAffineTransform matrix = pattern->matrix; + matrix.tx += phase.width; + matrix.ty += phase.height; + pattern->matrix = matrix; + } +} + void CGContextImpl::CGContextSelectFont(char* name, float size, DWORD encoding) { // curState->curFont = [UIFont class]("fontWithName:size:", @name, size); curState->fontSize = size; diff --git a/Frameworks/include/CGContextImpl.h b/Frameworks/include/CGContextImpl.h index ca3f030eb1..8ad28fd6b3 100644 --- a/Frameworks/include/CGContextImpl.h +++ b/Frameworks/include/CGContextImpl.h @@ -15,6 +15,11 @@ //****************************************************************************** #pragma once + +#ifndef __CGCONTEXTIMPL_TEST_FRIENDS +#define __CGCONTEXTIMPL_TEST_FRIENDS +#endif + #include "CGContextInternal.h" #include "CoreGraphics/CGPath.h" @@ -53,6 +58,7 @@ typedef struct { #define MAX_CG_STATES 16 class CGContextImpl { +__CGCONTEXTIMPL_TEST_FRIENDS; protected: CGContextRef _rootContext; CGImageRef _imgDest; @@ -107,6 +113,7 @@ class CGContextImpl { virtual void CGContextSetStrokeColorWithColor(id color); virtual void CGContextSetFillColorWithColor(id color); virtual void CGContextSetFillColor(float* components); + virtual void CGContextSetPatternPhase(CGSize phase); virtual void CGContextSetFillPattern(CGPatternRef pattern, const float* components); virtual void CGContextSelectFont(char* name, float size, DWORD encoding); virtual void CGContextGetTextPosition(CGPoint* pos); diff --git a/build/CoreGraphics/dll/CoreGraphics.def b/build/CoreGraphics/dll/CoreGraphics.def index 36571acfd2..aa155313b0 100644 --- a/build/CoreGraphics/dll/CoreGraphics.def +++ b/build/CoreGraphics/dll/CoreGraphics.def @@ -215,6 +215,7 @@ LIBRARY CoreGraphics CGContextIsDirty CGContextReleaseLock CGContextSetDirty + CGContextGetBacking EbrCenterTextInRectVertically ; CGDataConsumer.mm diff --git a/build/Tests/UnitTests/CoreGraphics/CoreGraphics.UnitTests.vcxproj b/build/Tests/UnitTests/CoreGraphics/CoreGraphics.UnitTests.vcxproj index 12481c1d5e..e818e0adc9 100644 --- a/build/Tests/UnitTests/CoreGraphics/CoreGraphics.UnitTests.vcxproj +++ b/build/Tests/UnitTests/CoreGraphics/CoreGraphics.UnitTests.vcxproj @@ -31,15 +31,9 @@ {26da08da-d0b9-4579-b168-e7f0a5f20e57} - - {8E79930B-7EF6-4A4E-B46C-EFC0A49C55D9} - {D036FDB1-F82C-40C7-B1B4-65F499EAE116} - - {037B568F-4104-417E-9FB8-B6899E398903} - {585b4870-0d6b-43a6-8e7e-ad08f7f507b6} diff --git a/include/CoreGraphics/CGContext.h b/include/CoreGraphics/CGContext.h index 7029dfe730..c256cbab69 100644 --- a/include/CoreGraphics/CGContext.h +++ b/include/CoreGraphics/CGContext.h @@ -95,7 +95,7 @@ COREGRAPHICS_EXPORT void CGContextSetLineJoin(CGContextRef c, CGLineJoin join); COREGRAPHICS_EXPORT void CGContextSetLineWidth(CGContextRef c, CGFloat width); COREGRAPHICS_EXPORT void CGContextSetMiterLimit(CGContextRef c, CGFloat limit); -COREGRAPHICS_EXPORT void CGContextSetPatternPhase(CGContextRef c, CGSize phase) STUB_METHOD; +COREGRAPHICS_EXPORT void CGContextSetPatternPhase(CGContextRef c, CGSize phase); COREGRAPHICS_EXPORT void CGContextSetFillPattern(CGContextRef c, CGPatternRef pattern, const CGFloat* components); COREGRAPHICS_EXPORT void CGContextSetRenderingIntent(CGContextRef c, CGColorRenderingIntent intent) STUB_METHOD; diff --git a/samples/CGCatalog/CGCatalog/Samples/CGCCGContextSetPatternPhase.m b/samples/CGCatalog/CGCatalog/Samples/CGCCGContextSetPatternPhase.m index c244d6c803..519a015087 100644 --- a/samples/CGCatalog/CGCatalog/Samples/CGCCGContextSetPatternPhase.m +++ b/samples/CGCatalog/CGCatalog/Samples/CGCCGContextSetPatternPhase.m @@ -33,12 +33,6 @@ - (void)drawRect:(CGRect)rect { CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); CGContextFillRect(context, CGRectMake(0, 0, maxWidth, maxHeight)); - // Create a base pattern color space - CGContextSaveGState(context); - CGColorSpaceRef colorSpace = CGColorSpaceCreatePattern(NULL); - CGContextSetFillColorSpace(context, colorSpace); - CGColorSpaceRelease(colorSpace); - // Create the pattern static const CGPatternCallbacks callbacks = { 0, &_DrawCustomPattern, NULL }; CGFloat alpha = 1; @@ -52,7 +46,6 @@ - (void)drawRect:(CGRect)rect { // Fill using the pattern CGContextFillRect(context, rect); - CGContextRestoreGState(context); } void _DrawCustomPattern(void* info, CGContextRef context) { diff --git a/tests/unittests/CoreGraphics/CGContextTests.mm b/tests/unittests/CoreGraphics/CGContextTests.mm index 55f53899fc..0a4bac2992 100644 --- a/tests/unittests/CoreGraphics/CGContextTests.mm +++ b/tests/unittests/CoreGraphics/CGContextTests.mm @@ -15,6 +15,109 @@ //****************************************************************************** #import + +#define __CGCONTEXTIMPL_TEST_FRIENDS \ + FRIEND_TEST(CGContext, CGContextSetPatternPhasePatternIsNil); \ + FRIEND_TEST(CGContext, CGContextSetPatternPhaseColorObjectIsFillColor); \ + FRIEND_TEST(CGContext, CGContextSetPatternPhasePositiveChange); \ + FRIEND_TEST(CGContext, CGContextSetPatternPhaseNegativeChange); + #import +#import "CGPatternInternal.h" #import +#import "CGContextInternal.h" +#import "CGContextImpl.h" #import +#import + +void _DrawCustomPattern(void* info, CGContextRef context) { + // Draw a circle inset from the pattern size + CGRect circleRect = CGRectMake(0, 0, 50, 50); + circleRect = CGRectInset(circleRect, 4, 4); + CGContextFillEllipseInRect(context, circleRect); + CGContextStrokeEllipseInRect(context, circleRect); +} + +TEST(CGContext, CGContextSetPatternPhasePatternIsNil) { + // Given + CGContextRef ctx = CGBitmapContextCreate24(1000, 1000); + CGContextImpl* backing = CGContextGetBacking(ctx); + + backing->curState->curFillColorObject = nil; + + // When + CGContextSetPatternPhase(ctx, CGSizeMake(100, 100)); + + // Then + ASSERT_EQ(0, backing->curState->curFillColorObject); + + CGContextRelease(ctx); +} + +TEST(CGContext, CGContextSetPatternPhaseColorObjectIsFillColor) { + // Given + CGContextRef ctx = CGBitmapContextCreate24(1000, 1000); + CGContextImpl* backing = CGContextGetBacking(ctx); + + CGContextSetFillColorWithColor(ctx, [UIColor blueColor].CGColor); + + // When + CGContextSetPatternPhase(ctx, CGSizeMake(100, 100)); + + // Then + ASSERT_EQ(0, (CGColorRef)backing->curState->curFillColorObject); + + CGContextRelease(ctx); +} + +TEST(CGContext, CGContextSetPatternPhasePositiveChange) { + // Given + CGContextRef ctx = CGBitmapContextCreate24(1000, 1000); + CGContextImpl* backing = CGContextGetBacking(ctx); + + CGRect boundsRect = CGRectMake(0, 0,1000, 1000); + const CGPatternCallbacks callbacks = { 0, &_DrawCustomPattern, NULL }; + CGFloat alpha = 1; + CGAffineTransform transform = CGAffineTransformMakeTranslation(10, 10); + CGPatternRef pattern = CGPatternCreate( + NULL, boundsRect, transform, 50, 50, kCGPatternTilingConstantSpacing, true, &callbacks); + CGContextSetFillPattern(ctx, pattern, &alpha); + CGPatternRelease(pattern); + + // When + CGContextSetPatternPhase(ctx, CGSizeMake(100, 200)); + + // Then + CGPattern* actualPattern = (CGPattern*)backing->curState->curFillColorObject; + CGAffineTransform matrix = [actualPattern getPatternTransform]; + ASSERT_EQ(110, matrix.tx); + ASSERT_EQ(210, matrix.ty); + + CGContextRelease(ctx); +} + +TEST(CGContext, CGContextSetPatternPhaseNegativeChange) { + // Given + CGContextRef ctx = CGBitmapContextCreate24(1000, 1000); + CGContextImpl* backing = CGContextGetBacking(ctx); + + CGRect boundsRect = CGRectMake(0, 0,1000, 1000); + const CGPatternCallbacks callbacks = { 0, &_DrawCustomPattern, NULL }; + CGFloat alpha = 1; + CGAffineTransform transform = CGAffineTransformMakeTranslation(300, 500); + CGPatternRef pattern = CGPatternCreate( + NULL, boundsRect, transform, 50, 50, kCGPatternTilingConstantSpacing, true, &callbacks); + CGContextSetFillPattern(ctx, pattern, &alpha); + CGPatternRelease(pattern); + + // When + CGContextSetPatternPhase(ctx, CGSizeMake(-100, -200)); + + // Then + CGPattern* actualPattern = (CGPattern*)backing->curState->curFillColorObject; + CGAffineTransform matrix = [actualPattern getPatternTransform]; + ASSERT_EQ(200, matrix.tx); + ASSERT_EQ(300, matrix.ty); + + CGContextRelease(ctx); +}