Skip to content

Commit

Permalink
Implementation of drawing an Image into context (#1414)
Browse files Browse the repository at this point in the history
Implement UIKit image drawing via WIC/D2D #1245 
Added unit and semi functional tests for drawing into context and scaling.
  • Loading branch information
msft-Jeyaram committed Nov 23, 2016
1 parent 7349c69 commit 08f73bb
Show file tree
Hide file tree
Showing 15 changed files with 325 additions and 96 deletions.
123 changes: 87 additions & 36 deletions Frameworks/CoreGraphics/CGContext.mm
Original file line number Diff line number Diff line change
Expand Up @@ -840,7 +840,7 @@ CGRect CGContextGetClipBoundingBox(CGContextRef context) {
auto& state = context->CurrentGState();
if (!state.clippingGeometry) {
D2D1_SIZE_F targetSize = context->RenderTarget()->GetSize();
return CGContextConvertRectToUserSpace(context, CGRect{ CGPointZero, { targetSize.width, targetSize.height }});
return CGContextConvertRectToUserSpace(context, CGRect{ CGPointZero, { targetSize.width, targetSize.height } });
}

D2D1_RECT_F bounds;
Expand Down Expand Up @@ -1393,39 +1393,6 @@ void CGContextShowGlyphsWithAdvances(CGContextRef context, const CGGlyph* glyphs
}
#pragma endregion

#pragma region Drawing Operations - CGImage
/// TODO(DH): GH#1078 Image Drawing
/**
@Status Interoperable
*/
void CGContextDrawImage(CGContextRef context, CGRect rect, CGImageRef image) {
NOISY_RETURN_IF_NULL(context);
if (!image) {
TraceWarning(TAG, L"Img == nullptr!");
return;
}
if (!context) {
TraceWarning(TAG, L"CGContextDrawImage: context == nullptr!");
return;
}

UNIMPLEMENTED();
}

void CGContextDrawImageRect(CGContextRef context, CGImageRef image, CGRect src, CGRect dst) {
NOISY_RETURN_IF_NULL(context);
UNIMPLEMENTED();
}

/**
@Status Interoperable
*/
void CGContextDrawTiledImage(CGContextRef context, CGRect rect, CGImageRef image) {
NOISY_RETURN_IF_NULL(context);
UNIMPLEMENTED();
}
#pragma endregion

#pragma region Drawing Operations - Basic Shapes
/**
@Status Interoperable
Expand Down Expand Up @@ -1803,10 +1770,94 @@ void CGContextEOFillPath(CGContextRef context) {
}
#pragma endregion

#pragma region Drawing Operations - Gradient + Shading
#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);

// Obtain the pixel format for the image
WICPixelFormatGUID imagePixelFormat = _CGImageGetWICPixelFormat(image);

CFRetain(image);
woc::unique_cf<CGImageRef> refImage;
refImage.reset(image);

if (!_CGIsValidRenderTargetPixelFormat(imagePixelFormat)) {
// convert it to a valid pixelformat
refImage.reset(_CGImageCreateCopyWithPixelFormat(image, GUID_WICPixelFormat32bppPRGBA));
}

ComPtr<IWICBitmap> bmap;
FAIL_FAST_IF_FAILED(_CGImageGetWICImageSource(refImage.get(), &bmap));

ComPtr<ID2D1Bitmap> d2dBitmap;
FAIL_FAST_IF_FAILED(context->RenderTarget()->CreateBitmapFromWicBitmap(bmap.Get(), nullptr, &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));
flipImage = CGAffineTransformScale(flipImage, 1.0, -1.0);
flipImage = CGAffineTransformTranslate(flipImage, -rect.origin.x, -(rect.origin.y + (rect.size.height / 2.0)));

ComPtr<ID2D1CommandList> commandList;
HRESULT hr = __CGContextRenderToCommandList(context,
_kCGCoordinateModeUserSpace,
&flipImage,
&commandList,
[&](CGContextRef context, ID2D1DeviceContext* deviceContext) {
deviceContext->DrawBitmap(d2dBitmap.Get(), __CGRectToD2D_F(rect));
return S_OK;
});
FAIL_FAST_IF_FAILED(hr);
FAIL_FAST_IF_FAILED(__CGContextRenderImage(context, commandList.Get()));
}

void _CGContextDrawImageRect(CGContextRef context, CGImageRef image, CGRect src, CGRect dst) {
NOISY_RETURN_IF_NULL(context);
NOISY_RETURN_IF_NULL(image);

if (CGRectEqualToRect(src, dst)) {
CGContextDrawImage(context, dst, image);
return;
}

RETURN_IF(CGRectGetHeight(src) == 0);
RETURN_IF(CGRectGetWidth(src) == 0);

// we want the source region to be drawn into the dest region (scaled)
// The image needs to be scaled and translated for the destination.
CGRect drawRect = CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image));
// scale factor of the image
float scaleX = CGRectGetWidth(dst) / CGRectGetWidth(src);
float scaleY = CGRectGetHeight(dst) / CGRectGetHeight(src);
// calculate he translation offset
float deltaX = CGRectGetMinX(dst) - (CGRectGetMinX(src) * scaleX);
float deltaY = CGRectGetMinY(dst) - (CGRectGetMinY(src) * scaleY);
drawRect = CGRectMake(deltaX, deltaY, (float)CGImageGetWidth(image) * scaleX, (float)CGImageGetHeight(image) * scaleY);

CGContextSaveGState(context);
CGContextClipToRect(context, dst);
CGContextDrawImage(context, drawRect, image);
CGContextRestoreGState(context);
}

/**
@Status Stub
*/
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();
}
#pragma endregion

#pragma region Drawing Operations - Gradient + Shading
/**
@Status Stub
*/
void CGContextDrawLinearGradient(
CGContextRef context, CGGradientRef gradient, CGPoint startPoint, CGPoint endPoint, CGGradientDrawingOptions options) {
NOISY_RETURN_IF_NULL(context);
Expand All @@ -1816,7 +1867,7 @@ void CGContextDrawLinearGradient(
}

/**
@Status Interoperable
@Status Stub
*/
void CGContextDrawRadialGradient(CGContextRef context,
CGGradientRef gradient,
Expand Down
13 changes: 10 additions & 3 deletions Frameworks/CoreGraphics/CGImage.mm
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,11 @@ CGImageRef CGImageCreateWithMaskingColors(CGImageRef image, const CGFloat* compo

#pragma region WIC_HELPERS

WICPixelFormatGUID _CGImageGetWICPixelFormat(CGImageRef image) {
RETURN_RESULT_IF_NULL(image, GUID_WICPixelFormatUndefined);
return image->PixelFormat();
}

bool _CGIsValidRenderTargetPixelFormat(WICPixelFormatGUID pixelFormat) {
auto iterator = s_ValidRenderTargetPixelFormat.find(pixelFormat);
return iterator != s_ValidRenderTargetPixelFormat.end();
Expand All @@ -569,9 +574,11 @@ bool _CGIsValidRenderTargetPixelFormat(WICPixelFormatGUID pixelFormat) {
return &iterator->second;
}

IWICBitmap* _CGImageGetImageSource(CGImageRef image) {
RETURN_NULL_IF(!image);
return image->ImageSource().Get();
HRESULT _CGImageGetWICImageSource(CGImageRef image, IWICBitmap** source) {
RETURN_HR_IF_NULL(E_INVALIDARG, image);
RETURN_HR_IF_NULL(E_POINTER, source);
*source = image->ImageSource().Get();
return S_OK;
}

DisplayTexture* _CGImageGetDisplayTexture(CGImageRef image) {
Expand Down
6 changes: 3 additions & 3 deletions Frameworks/QuartzCore/CALayer.mm
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,8 @@ CGContextRef CreateLayerContentsBitmapContext32(int width, int height, float sca
releaseWritetableBitmapTexture.Dismiss();

Microsoft::WRL::ComPtr<IWICBitmap> customWICBtmap =
Microsoft::WRL::Make<CGIWICBitmap>(displayTexture.get(), GUID_WICPixelFormat32bppPBGRA, height, width);
// We want to convert it to GUID_WICPixelFormat32bppPBGRA for D2D Render Target compatibility.
Microsoft::WRL::Make<CGIWICBitmap>(displayTexture.get(), GUID_WICPixelFormat32bppPRGBA, height, width);
// We want to convert it to GUID_WICPixelFormat32bppPRGBA for D2D Render Target compatibility.
woc::unique_cf<CGImageRef> image(_CGImageCreateWithWICBitmap(customWICBtmap.Get()));

Microsoft::WRL::ComPtr<ID2D1Factory> factory;
Expand Down Expand Up @@ -481,7 +481,7 @@ - (void)renderInContext:(CGContextRef)ctx {
rect.size.width = priv->bounds.size.width * priv->contentsScale;
rect.size.height = -priv->bounds.size.height * priv->contentsScale;

CGContextDrawImageRect(ctx, priv->contents, rect, destRect);
_CGContextDrawImageRect(ctx, priv->contents, rect, destRect);
}

// Draw sublayers
Expand Down
2 changes: 1 addition & 1 deletion Frameworks/UIKit/StarboardXaml/CompositorInterface.mm
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ void SetNodeContent(DisplayNode* node, float width, float height, float scale) {

public:
DisplayTextureContent(CGImageRef img) : name("TextureBitmap") {
woc::unique_cf<CGImageRef> image(_CGImageCreateCopyWithPixelFormat(img, GUID_WICPixelFormat32bppPBGRA));
woc::unique_cf<CGImageRef> image(_CGImageCreateCopyWithPixelFormat(img, GUID_WICPixelFormat32bppPRGBA));
_xamlImage = CreateBitmapFromBits(_CGImageGetRawBytes(image.get()),
CGImageGetWidth(image.get()),
CGImageGetHeight(image.get()),
Expand Down
88 changes: 44 additions & 44 deletions Frameworks/UIKit/UIImage.mm
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ - (void)draw1PartImageInRect:(CGRect)pos {
srcRect.size.width = pos.size.width;
srcRect.size.height = -pos.size.height;

CGContextDrawImageRect(cur, getImage(self), srcRect, pos);
_CGContextDrawImageRect(cur, getImage(self), srcRect, pos);
}

/**
Expand Down Expand Up @@ -488,7 +488,7 @@ - (void)drawAtPoint:(CGPoint)point blendMode:(CGBlendMode)mode alpha:(float)alph
srcRect.size.width = img_width;
srcRect.size.height = -img_height;

CGContextDrawImageRect(cur, img, srcRect, pos);
_CGContextDrawImageRect(cur, img, srcRect, pos);

CGContextRestoreGState(cur);
}
Expand Down Expand Up @@ -541,24 +541,24 @@ static inline void drawPatches(CGContextRef context, UIImage* img, CGRect* dst)
if (dstHeight - dstTopCap - dstBotCap > 0) {
if (dstLeftCap) {
// MidHeightLeft
CGContextDrawImageRect(context,
cgImg,
CGRectMake(0, srcHeight - srcBotCap, srcLeftCap, -(srcHeight - srcTopCap - srcBotCap)),
CGRectMake(dstX, (dstY + dstBotCap), dstLeftCap, (dstHeight - dstTopCap - dstBotCap)));
_CGContextDrawImageRect(context,
cgImg,
CGRectMake(0, srcHeight - srcBotCap, srcLeftCap, -(srcHeight - srcTopCap - srcBotCap)),
CGRectMake(dstX, (dstY + dstBotCap), dstLeftCap, (dstHeight - dstTopCap - dstBotCap)));
}

if (dstWidth - dstLeftCap - dstRightCap > 0) {
// MidHeightMidWidth
CGContextDrawImageRect(context,
cgImg,
CGRectMake(srcLeftCap,
srcHeight - srcBotCap,
(srcWidth - srcLeftCap - srcRightCap),
-(srcHeight - srcTopCap - srcBotCap)),
CGRectMake((dstX + dstLeftCap),
(dstY + dstBotCap),
(dstWidth - dstLeftCap - dstRightCap),
(dstHeight - dstTopCap - dstBotCap)));
_CGContextDrawImageRect(context,
cgImg,
CGRectMake(srcLeftCap,
srcHeight - srcBotCap,
(srcWidth - srcLeftCap - srcRightCap),
-(srcHeight - srcTopCap - srcBotCap)),
CGRectMake((dstX + dstLeftCap),
(dstY + dstBotCap),
(dstWidth - dstLeftCap - dstRightCap),
(dstHeight - dstTopCap - dstBotCap)));
} else {
UNIMPLEMENTED_WITH_MSG(
"Patched draws only supported when sum of "
Expand All @@ -568,7 +568,7 @@ static inline void drawPatches(CGContextRef context, UIImage* img, CGRect* dst)

if (dstRightCap) {
// MidHeightRight
CGContextDrawImageRect(
_CGContextDrawImageRect(
context,
cgImg,
CGRectMake((srcWidth - srcRightCap), srcHeight - srcBotCap, srcRightCap, -(srcHeight - srcTopCap - srcBotCap)),
Expand All @@ -584,54 +584,54 @@ static inline void drawPatches(CGContextRef context, UIImage* img, CGRect* dst)
if (dstTopCap) {
if (dstLeftCap) {
// TL corner
CGContextDrawImageRect(context,
cgImg,
CGRectMake(0, srcTopCap, srcLeftCap, -srcTopCap),
CGRectMake(dstX, (dstY + dstHeight - dstTopCap), dstLeftCap, dstTopCap));
_CGContextDrawImageRect(context,
cgImg,
CGRectMake(0, srcTopCap, srcLeftCap, -srcTopCap),
CGRectMake(dstX, (dstY + dstHeight - dstTopCap), dstLeftCap, dstTopCap));
}

if (dstWidth - dstLeftCap - dstRightCap > 0) {
// TCenter
CGContextDrawImageRect(context,
cgImg,
CGRectMake(srcLeftCap, srcTopCap, (srcWidth - srcLeftCap - srcRightCap), -srcTopCap),
CGRectMake((dstX + dstLeftCap),
(dstY + dstHeight - dstTopCap),
(dstWidth - dstLeftCap - dstRightCap),
dstTopCap));
_CGContextDrawImageRect(context,
cgImg,
CGRectMake(srcLeftCap, srcTopCap, (srcWidth - srcLeftCap - srcRightCap), -srcTopCap),
CGRectMake((dstX + dstLeftCap),
(dstY + dstHeight - dstTopCap),
(dstWidth - dstLeftCap - dstRightCap),
dstTopCap));
}

if (dstRightCap) {
// TR corner
CGContextDrawImageRect(context,
cgImg,
CGRectMake((srcWidth - srcRightCap), srcTopCap, srcRightCap, -srcTopCap),
CGRectMake((dstX + dstWidth - dstRightCap), (dstY + dstHeight - dstTopCap), dstRightCap, dstTopCap));
_CGContextDrawImageRect(context,
cgImg,
CGRectMake((srcWidth - srcRightCap), srcTopCap, srcRightCap, -srcTopCap),
CGRectMake((dstX + dstWidth - dstRightCap), (dstY + dstHeight - dstTopCap), dstRightCap, dstTopCap));
}
}

if (dstBotCap) {
if (dstLeftCap) {
// BL Corner
CGContextDrawImageRect(context,
cgImg,
CGRectMake(0, srcHeight, srcLeftCap, -srcBotCap),
CGRectMake(dstX, dstY, dstLeftCap, dstBotCap));
_CGContextDrawImageRect(context,
cgImg,
CGRectMake(0, srcHeight, srcLeftCap, -srcBotCap),
CGRectMake(dstX, dstY, dstLeftCap, dstBotCap));
}

if (dstWidth - dstLeftCap - dstRightCap > 0) {
// bottomMidWidth
CGContextDrawImageRect(context,
cgImg,
CGRectMake(srcLeftCap, srcHeight, (srcWidth - srcLeftCap - srcRightCap), -srcBotCap),
CGRectMake((dstX + dstLeftCap), dstY, (dstWidth - dstLeftCap - dstRightCap), dstBotCap));
_CGContextDrawImageRect(context,
cgImg,
CGRectMake(srcLeftCap, srcHeight, (srcWidth - srcLeftCap - srcRightCap), -srcBotCap),
CGRectMake((dstX + dstLeftCap), dstY, (dstWidth - dstLeftCap - dstRightCap), dstBotCap));
}

if (dstRightCap) {
CGContextDrawImageRect(context,
cgImg,
CGRectMake((srcWidth - srcRightCap), srcHeight, srcRightCap, -srcBotCap),
CGRectMake((dstX + dstWidth - dstRightCap), dstY, dstRightCap, dstBotCap));
_CGContextDrawImageRect(context,
cgImg,
CGRectMake((srcWidth - srcRightCap), srcHeight, srcRightCap, -srcBotCap),
CGRectMake((dstX + dstWidth - dstRightCap), dstY, dstRightCap, dstBotCap));
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Frameworks/UIKit/UIPasteboard.mm
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ + (WSSInMemoryRandomAccessStream*)_grabStreamFromUIImage:(UIImage*)image {
CGImageRef img = [image CGImage];

// TODO #1338 - Support via encoded data from IWIC
woc::unique_cf<CGImageRef> imgRef(_CGImageCreateCopyWithPixelFormat(img, GUID_WICPixelFormat32bppPBGRA));
woc::unique_cf<CGImageRef> imgRef(_CGImageCreateCopyWithPixelFormat(img, GUID_WICPixelFormat32bppPRGBA));
NSData* data = static_cast<NSData*>(CGImageGetDataProvider(imgRef.get()));

WSSInMemoryRandomAccessStream* stream = [[WSSInMemoryRandomAccessStream make] autorelease];
Expand Down
2 changes: 1 addition & 1 deletion Frameworks/include/CGContextImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class CGContextImpl {
virtual CGAffineTransform CGContextGetCTM();
virtual void CGContextSetCTM(CGAffineTransform transform);
virtual void CGContextDrawImage(CGRect rct, CGImageRef img);
virtual void CGContextDrawImageRect(CGImageRef img, CGRect src, CGRect dst);
virtual void _CGContextDrawImageRect(CGImageRef img, CGRect src, CGRect dst);
virtual void CGContextDrawTiledImage(CGRect rct, CGImageRef img);
virtual void CGContextClipToMask(CGRect dest, CGImageRef img);
virtual void CGContextSaveGState();
Expand Down
2 changes: 1 addition & 1 deletion Frameworks/include/CGContextInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
COREGRAPHICS_EXPORT CGContextRef _CGBitmapContextCreateWithRenderTarget(ID2D1RenderTarget* renderTarget, CGImageRef img = nullptr);
COREGRAPHICS_EXPORT CGContextRef _CGBitmapContextCreateWithFormat(int width, int height, __CGSurfaceFormat fmt);
COREGRAPHICS_EXPORT CGImageRef CGBitmapContextGetImage(CGContextRef ctx);
COREGRAPHICS_EXPORT void CGContextDrawImageRect(CGContextRef ctx, CGImageRef img, CGRect src, CGRect dst);
COREGRAPHICS_EXPORT void _CGContextDrawImageRect(CGContextRef ctx, CGImageRef img, CGRect src, CGRect dst);
COREGRAPHICS_EXPORT void CGContextClearToColor(CGContextRef ctx, float r, float g, float b, float a);
COREGRAPHICS_EXPORT bool CGContextIsDirty(CGContextRef ctx);
COREGRAPHICS_EXPORT void CGContextSetDirty(CGContextRef ctx, bool dirty);
Expand Down
Loading

0 comments on commit 08f73bb

Please sign in to comment.