Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of Tiled Images + tests #1542

Merged
merged 4 commits into from
Dec 17, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 76 additions & 18 deletions Frameworks/CoreGraphics/CGContext.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1950,30 +1950,43 @@ void CGContextEOFillPath(CGContextRef context) {

#pragma region Drawing Operations - CGImage

/**
@Status Interoperable
*/
void CGContextDrawImage(CGContextRef context, CGRect rect, CGImageRef image) {
NOISY_RETURN_IF_NULL(context);
NOISY_RETURN_IF_NULL(image);
static HRESULT __CreateD2DBitmapFromCGImage(CGContextRef context, CGImageRef image, ID2D1Bitmap** bitmap) {
RETURN_HR_IF_NULL(E_INVALIDARG, context);
RETURN_HR_IF_NULL(E_INVALIDARG, image);
RETURN_HR_IF_NULL(E_POINTER, bitmap);

// Obtain the pixel format for the image
WICPixelFormatGUID imagePixelFormat = _CGImageGetWICPixelFormat(image);
ComPtr<IWICBitmap> bmap;
RETURN_IF_FAILED(_CGImageGetWICImageSource(image, &bmap));

CFRetain(image);
woc::unique_cf<CGImageRef> refImage;
refImage.reset(image);
ComPtr<ID2D1Bitmap> d2dBitmap;
Copy link

@DHowett-MSFT DHowett-MSFT Dec 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requires a conversion into a valid render target format first.

There is some work around this in #1528. #ByDesign

Copy link

@DHowett-MSFT DHowett-MSFT Dec 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. I don't think this needs to be in two functions :P They'll always be found together. #ByDesign

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see it being used separately in CGImage usage in one of your CR, i'll leave it be for now.


In reply to: 92528114 [](ancestors = 92528114)

RETURN_IF_FAILED(context->DeviceContext()->CreateBitmapFromWicBitmap(bmap.Get(), nullptr, &d2dBitmap));
*bitmap = d2dBitmap.Detach();
return S_OK;
}

static CGImageRef __CGContextCreateRenderableImage(CGImageRef image) {
RETURN_NULL_IF(!image);
WICPixelFormatGUID imagePixelFormat = _CGImageGetWICPixelFormat(image);
if (!_CGIsValidRenderTargetPixelFormat(imagePixelFormat)) {
// convert it to a valid pixelformat
refImage.reset(_CGImageCreateCopyWithPixelFormat(image, GUID_WICPixelFormat32bppPBGRA));
return _CGImageCreateCopyWithPixelFormat(image, GUID_WICPixelFormat32bppPBGRA);
}

ComPtr<IWICBitmap> bmap;
FAIL_FAST_IF_FAILED(_CGImageGetWICImageSource(refImage.get(), &bmap));
CGImageRetain(image);
Copy link
Contributor

@aballway aballway Dec 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: these can be one line #Resolved

return image;
}

/**
@Status Interoperable
*/
void CGContextDrawImage(CGContextRef context, CGRect rect, CGImageRef image) {
NOISY_RETURN_IF_NULL(context);
NOISY_RETURN_IF_NULL(image);
Copy link
Contributor

@aballway aballway Dec 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should this be noisy or a simple no-op? #ByDesign

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noisy


In reply to: 92660158 [](ancestors = 92660158)


woc::unique_cf<CGImageRef> refImage{ __CGContextCreateRenderableImage(image) };

ComPtr<ID2D1Bitmap> d2dBitmap;
FAIL_FAST_IF_FAILED(context->DeviceContext()->CreateBitmapFromWicBitmap(bmap.Get(), nullptr, &d2dBitmap));
FAIL_FAST_IF_FAILED(__CreateD2DBitmapFromCGImage(context, refImage.get(), &d2dBitmap));

// Flip the image to account for change in coordinate system origin.
CGAffineTransform flipImage = CGAffineTransformMakeTranslation(rect.origin.x, rect.origin.y + (rect.size.height / 2.0));
Expand Down Expand Up @@ -2022,13 +2035,58 @@ void _CGContextDrawImageRect(CGContextRef context, CGImageRef image, CGRect src,
}

/**
@Status Stub
@Status Interoperable
*/
void CGContextDrawTiledImage(CGContextRef context, CGRect rect, CGImageRef image) {
// TODO #1417: This can be combined with brushes (brush + tiled fill);
NOISY_RETURN_IF_NULL(context);
UNIMPLEMENTED();
NOISY_RETURN_IF_NULL(image);

woc::unique_cf<CGImageRef> refImage{ __CGContextCreateRenderableImage(image) };

ComPtr<ID2D1Bitmap> d2dBitmap;
FAIL_FAST_IF_FAILED(__CreateD2DBitmapFromCGImage(context, refImage.get(), &d2dBitmap));

ComPtr<ID2D1BitmapBrush> bitmapBrush;
ComPtr<ID2D1DeviceContext> deviceContext = context->DeviceContext();
FAIL_FAST_IF_FAILED(deviceContext->CreateBitmapBrush(d2dBitmap.Get(), &bitmapBrush));

// set the bitmap properties for the brush to be repeated
bitmapBrush->SetExtendModeX(D2D1_EXTEND_MODE_WRAP);
bitmapBrush->SetExtendModeY(D2D1_EXTEND_MODE_WRAP);

// |1 0 0| is the transformation matrix for flipping a rect about its Y midpoint m. (m = (y + h/2))
// |0 -1 0|
// |0 2m 1|
//
// Combined with [scale sx * sy] * [translate X, Y], that becomes:
// |sx 0 0|
// | 0 -sy 0|
// | x -y+2m 0|
// Or, the transformation matrix for drawing a flipped rect at a scale and offset.
D2D1_SIZE_U bitmapSize = d2dBitmap->GetPixelSize();
CGFloat sx = rect.size.width / bitmapSize.width;
Copy link
Contributor

@aballway aballway Dec 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check that width, height != 0 #Resolved

CGFloat sy = rect.size.height / bitmapSize.height;
CGFloat m = rect.origin.y + (rect.size.height / 2.f);

CGAffineTransform transform{ sx, 0, 0, -sy, rect.origin.x, (2 * m) - rect.origin.y };
transform = CGAffineTransformConcat(transform, CGContextGetUserSpaceToDeviceSpaceTransform(context));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I saw the same block of code (with identical comments) for transforms in @DHowett-MSFT's PR. can we make a utility function out of this?

Copy link
Contributor Author

@msft-Jeyaram msft-Jeyaram Dec 16, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely., it's being used by all brushes that have image as source.

#ByDesign


// set the transform for the brush and alpha
bitmapBrush->SetTransform(__CGAffineTransformToD2D_F(transform));
bitmapBrush->SetOpacity(context->CurrentGState().alpha);

// set the interpolationMode
bitmapBrush->SetInterpolationMode(D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);

// Area to fill
D2D1_SIZE_F targetSize = deviceContext->GetSize();
D2D1_RECT_F region = D2D1::RectF(0, 0, targetSize.width, targetSize.height);

deviceContext->BeginDraw();
deviceContext->FillRectangle(&region, bitmapBrush.Get());
FAIL_FAST_IF_FAILED(deviceContext->EndDraw());
}

#pragma endregion

#pragma region Drawing Operations - Gradient + Shading
Expand Down
141 changes: 140 additions & 1 deletion tests/UnitTests/CoreGraphics.drawing/CGContextDrawingTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,146 @@ DISABLED_DRAW_TEST_F(CGContext, DrawIntoRect, UIKitMimicTest) {
}
#endif

static void _drawTiledImage(CGContextRef context, CGRect rect, const std::string& name) {
auto drawingConfig = DrawingTestConfig::Get();
woc::unique_cf<CFStringRef> testFilename{ _CFStringCreateWithStdString(drawingConfig->GetResourcePath(name)) };
woc::unique_cf<CGImageRef> image{ _CGImageCreateFromPNGFile(testFilename.get()) };
ASSERT_NE(image, nullptr);
CGContextDrawTiledImage(context, rect, image.get());
}

DRAW_TEST_F(CGContext, TiledImageHeart, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 128, 128 } };
_drawTiledImage(GetDrawingContext(), rect, "tiledImageHeart.png");
}

DRAW_TEST_F(CGContext, TiledImageHeartScaledUp, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 250, 250 } };
_drawTiledImage(GetDrawingContext(), rect, "tiledImageHeart.png");
}

DRAW_TEST_F(CGContext, TiledImageHeartScaledTiny, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 1, 1 } };
_drawTiledImage(GetDrawingContext(), rect, "tiledImageHeart.png");
}

DRAW_TEST_F(CGContext, TiledImageHeartScaledAlpha1, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 100, 100 } };
CGContextSetAlpha(GetDrawingContext(), 0.8);
_drawTiledImage(GetDrawingContext(), rect, "tiledImageHeart.png");
}

DRAW_TEST_F(CGContext, TiledImageHeartScaledAlpha2, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 256, 256 } };
CGContextSetAlpha(GetDrawingContext(), 0.24);
_drawTiledImage(GetDrawingContext(), rect, "tiledImageHeart.png");
}

DRAW_TEST_F(CGContext, TiledImageHeartScaledAlpha3, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 300, 513 } };
CGContextSetAlpha(GetDrawingContext(), 0.66);
_drawTiledImage(GetDrawingContext(), rect, "tiledImageHeart.png");
}

DRAW_TEST_F(CGContext, TiledImageHeartScaledDown, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 50, 50 } };
_drawTiledImage(GetDrawingContext(), rect, "tiledImageHeart.png");
}

DRAW_TEST_F(CGContext, TiledImageHeartScaled, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 250, 128 } };
_drawTiledImage(GetDrawingContext(), rect, "tiledImageHeart.png");
}

DRAW_TEST_F(CGContext, TiledImageDog, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 256, 256 } };
_drawTiledImage(GetDrawingContext(), rect, "tiledImageDog.png");
}

DRAW_TEST_F(CGContext, TiledImageDogScaledDown, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 50, 50 } };
_drawTiledImage(GetDrawingContext(), rect, "tiledImageDog.png");
}

DRAW_TEST_F(CGContext, TiledImageDogScaledUp, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 512, 512 } };
_drawTiledImage(GetDrawingContext(), rect, "tiledImageDog.png");
}

DRAW_TEST_F(CGContext, TiledImageDogScaled, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 350, 500 } };
_drawTiledImage(GetDrawingContext(), rect, "tiledImageDog.png");
}

DRAW_TEST_F(CGContext, TiledImageDogScaled2, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 128, 240 } };
_drawTiledImage(GetDrawingContext(), rect, "tiledImageDog.png");
}

DRAW_TEST_F(CGContext, TiledImageDogScaledAspectRatioWrong, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 1024, 25 } };
_drawTiledImage(GetDrawingContext(), rect, "tiledImageDog.png");
}

DRAW_TEST_F(CGContext, TiledImageDogScaledAspectRatio, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 1024, 1024 } };
_drawTiledImage(GetDrawingContext(), rect, "tiledImageDog.png");
}

DRAW_TEST_F(CGContext, TiledImageDogScaledAlpha, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 100, 100 } };
CGContextSetAlpha(GetDrawingContext(), 0.8);
_drawTiledImage(GetDrawingContext(), rect, "tiledImageDog.png");
}

DRAW_TEST_F(CGContext, TiledImageDogScaledAlpha2, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 256, 256 } };
CGContextSetAlpha(GetDrawingContext(), 0.24);
_drawTiledImage(GetDrawingContext(), rect, "tiledImageDog.png");
}

DRAW_TEST_F(CGContext, TiledImageDogScaledAlpha3, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 300, 513 } };
CGContextSetAlpha(GetDrawingContext(), 0.66);
_drawTiledImage(GetDrawingContext(), rect, "tiledImageDog.png");
}

DRAW_TEST_F(CGContext, TiledImageCustom, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 562, 469 } };
_drawTiledImage(GetDrawingContext(), rect, "tiledImageCircleMe.png");
}

DRAW_TEST_F(CGContext, TiledImageCustomScaledUp, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 2050, 2050 } };
_drawTiledImage(GetDrawingContext(), rect, "tiledImageCircleMe.png");
}

DRAW_TEST_F(CGContext, TiledImageCustomScaledDown, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 20, 20 } };
_drawTiledImage(GetDrawingContext(), rect, "tiledImageCircleMe.png");
}

DRAW_TEST_F(CGContext, TiledImageCustomScaledDownReallyLow, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 1, 1 } };
_drawTiledImage(GetDrawingContext(), rect, "tiledImageCircleMe.png");
}

DRAW_TEST_F(CGContext, TiledImageCustomScaled, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 10, 250 } };
_drawTiledImage(GetDrawingContext(), rect, "tiledImageCircleMe.png");
}

DRAW_TEST_F(CGContext, TiledImageCustomScaledObscure, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 253, 13 } };
_drawTiledImage(GetDrawingContext(), rect, "tiledImageCircleMe.png");
}

DRAW_TEST_F(CGContext, TiledImageCustomScaledAlpha, UIKitMimicTest) {
CGRect rect = { { 0, 0 }, { 128, 128 } };
CGContextSetAlpha(GetDrawingContext(), 0.88);
_drawTiledImage(GetDrawingContext(), rect, "tiledImageCircleMe.png");
}

DISABLED_DRAW_TEST_F(CGContext, DrawAnImage, UIKitMimicTest) {
// Load an Image and draw it into the canvas context
auto drawingConfig = DrawingTestConfig::Get();
Expand Down Expand Up @@ -98,7 +238,6 @@ DISABLED_DRAW_TEST_F(CGContext, DrawAContextIntoAnImage, UIKitMimicTest) {
CGContextDrawImage(context, bounds, image.get());
}


DISABLED_DRAW_TEST_F(CGContext, FillThenStrokeIsSameAsDrawFillStroke, WhiteBackgroundTest) {
CGContextRef context = GetDrawingContext();
CGRect bounds = GetDrawingBounds();
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions tests/unittests/CoreGraphics.drawing/data/tiledImageDog.png
3 changes: 3 additions & 0 deletions tests/unittests/CoreGraphics.drawing/data/tiledImageHeart.png