Skip to content

Commit

Permalink
Add support for colored and non-colored Patterns (#1952)
Browse files Browse the repository at this point in the history
* Add support for coloured and non-colored Patterns
* Enables proper anchoring for patterns
  • Loading branch information
msft-Jeyaram authored Feb 16, 2017
1 parent e6c7e4c commit a7a8da1
Show file tree
Hide file tree
Showing 143 changed files with 809 additions and 254 deletions.
54 changes: 38 additions & 16 deletions Frameworks/CoreGraphics/CGContext.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1777,32 +1777,37 @@ static CGImageRef __CGContextCreateRenderableImage(CGImageRef image) {

#pragma region Drawing Parameters - Stroke / Fill Patterns

template <typename ContextStageLambda> // Takes the form HRESULT(*)(CGContextRef)
static HRESULT _CreatePatternBrush(
CGContextRef context, CGPatternRef pattern, const CGFloat* components, ID2D1BitmapBrush1** brush, ContextStageLambda&& contextStage) {
// TODO #1592: change to support grayscale (masks) after dustins change.
template <typename ContextStageLambda> // Takes the form HRESULT(*)(CGContextRef,bool)
static HRESULT _CreatePatternBrush(CGContextRef context,
CGPatternRef pattern,
ID2D1BitmapBrush1** brush,
ContextStageLambda&& contextStage) {
woc::unique_cf<CGColorSpaceRef> colorspace{ CGColorSpaceCreateDeviceRGB() };

// We need to generate the pattern as an image (then tile it)
CGRect tileSize = _CGPatternGetFinalPatternSize(pattern);
RETURN_HR_IF(E_UNEXPECTED, CGRectIsNull(tileSize));

size_t bitsPerComponent = 8;
size_t bytesPerRow = 4 * tileSize.size.width;
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big;

woc::unique_cf<CGContextRef> patternContext{ CGBitmapContextCreate(
nullptr, tileSize.size.width, tileSize.size.height, bitsPerComponent, bytesPerRow, colorspace.get(), bitmapInfo) };
woc::unique_cf<CGContextRef> patternContext{ CGBitmapContextCreate(nullptr,
tileSize.size.width,
tileSize.size.height,
8,
4 * tileSize.size.width,
colorspace.get(),
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big) };
RETURN_HR_IF_NULL(E_UNEXPECTED, patternContext);

// Determine if this pattern is a colored pattern (the coloring is specified in the pattern callback) or if it is stencil pattern (the
// color is set outside and not within the pattern callback)
bool isColored = _CGPatternIsColored(pattern);

// Stage the drawing context
RETURN_IF_FAILED(std::forward<ContextStageLambda>(contextStage)(patternContext.get()));
RETURN_IF_FAILED(std::forward<ContextStageLambda>(contextStage)(patternContext.get(), isColored));

// Now we ask the user to draw
_CGPatternIssueCallBack(patternContext.get(), pattern);

// Get the image out of it

woc::unique_cf<CGImageRef> bitmapTiledimage{ CGBitmapContextCreateImage(patternContext.get()) };
woc::unique_cf<CGImageRef> tileImage{ __CGContextCreateRenderableImage(bitmapTiledimage.get()) };
RETURN_HR_IF_NULL(E_UNEXPECTED, tileImage);
Expand Down Expand Up @@ -1856,9 +1861,18 @@ void CGContextSetFillPattern(CGContextRef context, CGPatternRef pattern, const C
}

ComPtr<ID2D1BitmapBrush1> bitmapBrush;
FAIL_FAST_IF_FAILED(_CreatePatternBrush(context, pattern, components, &bitmapBrush, [&](CGContextRef drawingContext) {
FAIL_FAST_IF_FAILED(_CreatePatternBrush(context, pattern, &bitmapBrush, [&](CGContextRef drawingContext, bool isColored) {
CGContextSetFillColorSpace(drawingContext, context->FillColorSpace());
CGContextSetFillColor(drawingContext, components);
if (isColored) {
// It's a colored pattern, the color is specified by the pattern callback.
// The 'components' are alpha value.
CGContextSetAlpha(drawingContext, components[0]);
} else {
// It is a stencil pattern, we should stage the context's color
// The 'components' are the color for the stencil.
CGContextSetFillColor(drawingContext, components);
}

return S_OK;
}));
// set the fill brush
Expand Down Expand Up @@ -1887,9 +1901,17 @@ void CGContextSetStrokePattern(CGContextRef context, CGPatternRef pattern, const
}

ComPtr<ID2D1BitmapBrush1> bitmapBrush;
FAIL_FAST_IF_FAILED(_CreatePatternBrush(context, pattern, components, &bitmapBrush, [&](CGContextRef drawingContext) {
FAIL_FAST_IF_FAILED(_CreatePatternBrush(context, pattern, &bitmapBrush, [&](CGContextRef drawingContext, bool isColored) {
CGContextSetStrokeColorSpace(drawingContext, context->StrokeColorSpace());
CGContextSetStrokeColor(drawingContext, components);
if (isColored) {
// It's a colored pattern, the color is specified by the pattern callback.
// The 'components' are alpha value.
CGContextSetAlpha(drawingContext, components[0]);
} else {
// It is a stencil pattern, we should stage the context's color
// The 'components' are the color for the stencil.
CGContextSetStrokeColor(drawingContext, components);
}
return S_OK;
}));
// set the stroke brush
Expand Down
4 changes: 4 additions & 0 deletions Frameworks/CoreGraphics/CGPattern.mm
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,7 @@ CGRect _CGPatternGetFinalPatternSize(CGPatternRef pattern) {
RETURN_RESULT_IF_NULL(pattern, CGRectNull);
return { CGPointZero, { ((CGPattern*)pattern)->xStep, ((CGPattern*)pattern)->yStep } };
}

bool _CGPatternIsColored(CGPatternRef pattern) {
return ((CGPattern*)pattern)->isColored;
}
7 changes: 7 additions & 0 deletions Frameworks/include/CGPatternInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,10 @@ CGAffineTransform _CGPatternGetTransformation(CGPatternRef pattern);
* Get the final size of the pattern tile (after xStep and xStep has been applied).
*/
CGRect _CGPatternGetFinalPatternSize(CGPatternRef pattern);

/*
* Get the pattern colored value.
* If it is colored, then we have a colored pattern has inherent color,
* if it's false then we have a stencil pattern does not have inherent color.
*/
bool _CGPatternIsColored(CGPatternRef pattern);
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@
<ClangCompile Include="$(StarboardBasePath)\tests\unittests\CoreGraphics.Drawing\CGContextDrawing_ImageMaskTests.cpp" />
<ClangCompile Include="$(StarboardBasePath)\tests\unittests\CoreGraphics.Drawing\CGContextDrawing_GradientTests.cpp" />
<ClangCompile Include="$(StarboardBasePath)\tests\unittests\CoreGraphics.Drawing\CGContextDrawing_ImageDrawingTests.cpp" />
<ClangCompile Include="$(StarboardBasePath)\tests\unittests\CoreGraphics.Drawing\CGContextDrawing_PatternTests.cpp" />
<ClangCompile Include="$(StarboardBasePath)\tests\unittests\CoreGraphics.Drawing\CGPathDrawingTests.cpp" />
<ClangCompile Include="$(StarboardBasePath)\tests\unittests\CoreGraphics.Drawing\DrawingTest.cpp" />
<ClangCompile Include="$(StarboardBasePath)\tests\unittests\CoreGraphics.drawing\ImageComparison.cpp" />
Expand Down
204 changes: 0 additions & 204 deletions tests/UnitTests/CoreGraphics.drawing/CGContextDrawingTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,163 +19,6 @@
#include "ImageHelpers.h"
#include <windows.h>

static void drawPatternWindowsLogo(void* info, CGContextRef context) {
CGContextFillRect(context, CGRectMake(0, 0, 50, 50));

CGContextSetRGBFillColor(context, 0, 0.63, 0.94, 1);
CGContextFillRect(context, CGRectMake(0, 50, 50, 50));

CGContextSetRGBFillColor(context, 0.48, 0.73, 0, 1);
CGContextFillRect(context, CGRectMake(50, 50, 50, 50));

CGContextSetRGBFillColor(context, 1, 0.73, 0, 1);
CGContextFillRect(context, CGRectMake(50, 0, 50, 50));
}

static void drawPatternSliced(void* info, CGContextRef context) {
CGFloat red[] = { 1, 0, 0, 1 };
CGContextSetStrokeColor(context, red);

CGPoint points[] = { { 0.0, 0.0 }, { 100, 100 }, { 100, 0.0 }, { 0.0, 100 } };
CGContextStrokeLineSegments(context, points, 4);

CGFloat green[] = { 0, 1, 0, 1 };
CGContextSetFillColor(context, green);
CGRect middleSpot = CGRectMake(100 / 8 * 3, 100 / 8 * 3, 2 * 100 / 8, 2 * 100 / 8);
CGContextFillRect(context, middleSpot);
}

static void _SetPatternForStroke(CGContextRef context, CGRect rect, float xStep, float yStep, CGPatternDrawPatternCallback drawpattern) {
CGPatternCallbacks coloredPatternCallbacks = { 0, drawpattern, NULL };

CGPatternRef pattern =
CGPatternCreate(NULL, rect, CGAffineTransformIdentity, xStep, yStep, kCGPatternTilingNoDistortion, false, &coloredPatternCallbacks);

CFAutorelease(pattern);
woc::unique_cf<CGColorSpaceRef> rgbColorSpace(CGColorSpaceCreateDeviceRGB());
woc::unique_cf<CGColorSpaceRef> patternColorSpace{ CGColorSpaceCreatePattern(rgbColorSpace.get()) };

CGContextSetStrokeColorSpace(context, patternColorSpace.get());

CGFloat color[] = { 0.96, 0.32, 0.07, 1 };
CGContextSetStrokePattern(context, pattern, color);
}

static void _SetPatternForFill(CGContextRef context, CGRect rect, float xStep, float yStep, CGPatternDrawPatternCallback drawpattern) {
CGPatternCallbacks coloredPatternCallbacks = { 0, drawpattern, NULL };

CGPatternRef pattern =
CGPatternCreate(NULL, rect, CGAffineTransformIdentity, xStep, yStep, kCGPatternTilingNoDistortion, false, &coloredPatternCallbacks);

CFAutorelease(pattern);
woc::unique_cf<CGColorSpaceRef> rgbColorSpace(CGColorSpaceCreateDeviceRGB());
woc::unique_cf<CGColorSpaceRef> patternColorSpace{ CGColorSpaceCreatePattern(rgbColorSpace.get()) };

CGFloat color[] = { 0.96, 0.32, 0.07, 1 };

CGContextSetFillColorSpace(context, patternColorSpace.get());

CGContextSetFillPattern(context, pattern, color);
}

DISABLED_DRAW_TEST_F(CGContext, PatternStroke, UIKitMimicTest<>) {
CGContextRef context = GetDrawingContext();
CGRect bounds = GetDrawingBounds();

_SetPatternForStroke(context, CGRectMake(0, 0, 100, 100), 100, 100, drawPatternWindowsLogo);
CGRect borderRect = CGRectInset(bounds, 30, 50);
CGContextSetLineWidth(context, 45);
CGContextStrokeRect(context, borderRect);
}

DISABLED_DRAW_TEST_F(CGContext, PatternStrokeSliced, UIKitMimicTest<>) {
CGContextRef context = GetDrawingContext();
CGRect bounds = GetDrawingBounds();

_SetPatternForStroke(context, CGRectMake(0, 0, 100, 100), 100, 100, drawPatternSliced);
CGRect borderRect = CGRectInset(bounds, 30, 50);
CGContextSetLineWidth(context, 45);
CGContextStrokeRect(context, borderRect);
}

DISABLED_DRAW_TEST_F(CGContext, PatternDrawPath, UIKitMimicTest<>) {
CGContextRef context = GetDrawingContext();
CGRect bounds = GetDrawingBounds();

CGMutablePathRef theFirstPath = CGPathCreateMutable();
CGMutablePathRef theSecondPath = CGPathCreateMutable();

CGPathMoveToPoint(theFirstPath, NULL, 200, 35);
CGPathAddLineToPoint(theFirstPath, NULL, 165, 100);
CGPathAddLineToPoint(theFirstPath, NULL, 100, 100);
CGPathAddLineToPoint(theFirstPath, NULL, 150, 150);
CGPathAddLineToPoint(theFirstPath, NULL, 135, 225);
CGPathAddLineToPoint(theFirstPath, NULL, 200, 170);
CGPathAddLineToPoint(theFirstPath, NULL, 265, 225);

CGPathMoveToPoint(theSecondPath, NULL, 265, 225);

CGPathAddLineToPoint(theSecondPath, NULL, 350, 225);
CGPathAddLineToPoint(theSecondPath, NULL, 350, 35);
CGPathAddLineToPoint(theSecondPath, NULL, 200, 35);

CGPathAddPath(theFirstPath, NULL, theSecondPath);
CGContextAddPath(context, theFirstPath);

CGContextClosePath(context);

CGContextSetLineWidth(context, 15);
_SetPatternForFill(context, CGRectMake(0, 0, 100, 100), 100, 100, drawPatternWindowsLogo);
_SetPatternForStroke(context, CGRectMake(0, 0, 100, 100), 100, 100, drawPatternSliced);

CGContextDrawPath(context, kCGPathEOFillStroke);
CGPathRelease(theFirstPath);
CGPathRelease(theSecondPath);
}

DISABLED_DRAW_TEST_F(CGContext, PatternFill, UIKitMimicTest<>) {
CGContextRef context = GetDrawingContext();
CGRect bounds = GetDrawingBounds();

_SetPatternForFill(context, CGRectMake(0, 0, 100, 100), 100, 100, drawPatternWindowsLogo);
CGContextFillRect(context, bounds);
}

DRAW_TEST_F(CGContext, PatternFillNULLRect, UIKitMimicTest<>) {
CGContextRef context = GetDrawingContext();
CGRect bounds = GetDrawingBounds();

CGContextSetRGBFillColor(context, 0.5, 0.5, 0.5, 1.0);
CGContextFillRect(context, bounds);

_SetPatternForFill(context, CGRectNull, 100, 100, drawPatternWindowsLogo);

CGRect borderRect = CGRectInset(bounds, 30, 50);

CGContextFillRect(context, borderRect);
}

DRAW_TEST_F(CGContext, PatternStrokeNULLRect, UIKitMimicTest<>) {
CGContextRef context = GetDrawingContext();
CGRect bounds = GetDrawingBounds();

CGContextSetRGBFillColor(context, 0.5, 0.5, 0.5, 1.0);
CGContextFillRect(context, bounds);

_SetPatternForStroke(context, CGRectNull, 100, 100, drawPatternWindowsLogo);
CGRect borderRect = CGRectInset(bounds, 30, 50);
CGContextSetLineWidth(context, 45);
CGContextStrokeRect(context, borderRect);
}

DISABLED_DRAW_TEST_F(CGContext, PatternFillSliced, UIKitMimicTest<>) {
CGContextRef context = GetDrawingContext();
CGRect bounds = GetDrawingBounds();

_SetPatternForFill(context, CGRectMake(0, 0, 100, 100), 100, 100, drawPatternSliced);
CGContextFillRect(context, bounds);
}

DISABLED_DRAW_TEST_F(CGContext, Canva, UIKitMimicTest<>) {
CGContextRef context = GetDrawingContext();
CGRect bounds = GetDrawingBounds();
Expand All @@ -192,53 +35,6 @@ DISABLED_DRAW_TEST_F(CGContext, Canva, UIKitMimicTest<>) {
CGContextFillRect(context, middleSpot);
}

DISABLED_DRAW_TEST_F(CGContext, PatternFillWindowsLogoWithAlpha, UIKitMimicTest<>) {
CGContextRef context = GetDrawingContext();
CGRect bounds = GetDrawingBounds();

CGContextSetAlpha(context, 0.8);
_SetPatternForFill(context, CGRectMake(0, 0, 100, 100), 150, 150, drawPatternWindowsLogo);
CGContextFillRect(context, bounds);
}

DISABLED_DRAW_TEST_F(CGContext, PatternFillWindowsLogoRotate, UIKitMimicTest<>) {
CGContextRef context = GetDrawingContext();
CGRect bounds = GetDrawingBounds();

_SetPatternForFill(context, CGRectMake(0, 0, 100, 100), 100, 100, drawPatternWindowsLogo);
CGContextRotateCTM(context, 0.4);
CGContextFillRect(context, bounds);
}

DISABLED_DRAW_TEST_F(CGContext, PatternFillWindowsLogoRegion, UIKitMimicTest<>) {
CGContextRef context = GetDrawingContext();
CGRect bounds = GetDrawingBounds();

_SetPatternForFill(context, CGRectMake(0, 0, 100, 100), 100, 100, drawPatternWindowsLogo);
CGRect borderRect = CGRectInset(bounds, 30, 50);
CGContextFillRect(context, borderRect);
}

DISABLED_DRAW_TEST_F(CGContext, PatternFillWindowsLogoPath, UIKitMimicTest<>) {
CGContextRef context = GetDrawingContext();
CGRect bounds = GetDrawingBounds();

_SetPatternForFill(context, CGRectMake(0, 0, 100, 100), 100, 100, drawPatternWindowsLogo);

CGMutablePathRef thepath = CGPathCreateMutable();
CGPathMoveToPoint(thepath, NULL, 30, 100);
CGPathAddCurveToPoint(thepath, NULL, 47.0f, 67.0f, 50.0f, 55.0f, 45.0f, 50.0f);
CGPathAddCurveToPoint(thepath, NULL, 42.0f, 47.0f, 37.0f, 46.0f, 30.0f, 55.0f);

CGPathAddCurveToPoint(thepath, NULL, 23.0f, 46.0f, 18.0f, 47.0f, 15.0f, 50.0f);
CGPathAddCurveToPoint(thepath, NULL, 10.0f, 55.0f, 13.0f, 67.0f, 30.0f, 100.0f);

CGPathCloseSubpath(thepath);
CGContextAddPath(context, thepath);
CGContextFillPath(context);
CGPathRelease(thepath);
}

DRAW_TEST_F(CGContext, RedBox, UIKitMimicTest<>) {
CGContextRef context = GetDrawingContext();
CGRect bounds = GetDrawingBounds();
Expand Down
Loading

0 comments on commit a7a8da1

Please sign in to comment.