Skip to content

Commit

Permalink
Implementation of Tiled Images + tests (#1542)
Browse files Browse the repository at this point in the history
Implementation of Tiled Images + tests #1417
  • Loading branch information
msft-Jeyaram authored Dec 17, 2016
1 parent 132ebc2 commit 993690e
Show file tree
Hide file tree
Showing 30 changed files with 307 additions and 18 deletions.
101 changes: 83 additions & 18 deletions Frameworks/CoreGraphics/CGContext.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2082,30 +2082,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;
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);
return image;
}

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

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 @@ -2154,13 +2167,65 @@ 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));

// |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();
FAIL_FAST_IF(bitmapSize.width == 0);
FAIL_FAST_IF(bitmapSize.height == 0);

CGFloat sx = rect.size.width / bitmapSize.width;
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));

ComPtr<ID2D1BitmapBrush1> bitmapBrush;
ComPtr<ID2D1DeviceContext> deviceContext = context->DeviceContext();
FAIL_FAST_IF_FAILED(
deviceContext->CreateBitmapBrush(d2dBitmap.Get(),
D2D1::BitmapBrushProperties1(D2D1_EXTEND_MODE_WRAP,
D2D1_EXTEND_MODE_WRAP,
(CGImageGetShouldInterpolate(refImage.get()) ?
context->CurrentGState().bitmapInterpolationMode :
D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR)),
D2D1::BrushProperties(context->CurrentGState().alpha, __CGAffineTransformToD2D_F(transform)),
&bitmapBrush));

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

ComPtr<ID2D1CommandList> commandList;
HRESULT hr = context->DrawToCommandList(_kCGCoordinateModeDeviceSpace,
nullptr,
&commandList,
[&](CGContextRef context, ID2D1DeviceContext* deviceContext) {
deviceContext->FillRectangle(&region, bitmapBrush.Get());
return S_OK;
});
FAIL_FAST_IF_FAILED(hr);
FAIL_FAST_IF_FAILED(context->DrawImage(commandList.Get()));
}

#pragma endregion

#pragma region Drawing Operations - Gradient + Shading
Expand Down
140 changes: 140 additions & 0 deletions 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());
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

DISABLED_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
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

0 comments on commit 993690e

Please sign in to comment.