-
Notifications
You must be signed in to change notification settings - Fork 813
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
Implement drawing CGPaths through CGContext #1307
Changes from all commits
d43b075
5f79b50
5eab616
24f5707
34bd6d5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,8 @@ | |
|
||
#import "LoggingNative.h" | ||
|
||
#import "D2DWrapper.h" | ||
|
||
#pragma clang diagnostic push | ||
#pragma clang diagnostic ignored "-Wdeprecated-register" | ||
|
||
|
@@ -452,7 +454,7 @@ | |
BYTE* imageData = static_cast<BYTE*>(LockImageData()); | ||
ComPtr<IWICBitmap> wicBitmap = Make<CGIWICBitmap>(this, SurfaceFormat()); | ||
ComPtr<ID2D1Factory> d2dFactory; | ||
THROW_IF_FAILED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), &d2dFactory)); | ||
FAIL_FAST_IF_FAILED(_CGGetD2DFactory(&d2dFactory)); | ||
ComPtr<ID2D1RenderTarget> renderTarget; | ||
THROW_IF_FAILED(d2dFactory->CreateWicBitmapRenderTarget(wicBitmap.Get(), D2D1::RenderTargetProperties(), &renderTarget)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. :-( super sad at the inconsistencies here. I thought the decision was to be error returning out to the public layer at which point a decision would be made on the correct course of action. This still seems internal as it returns a D2D object. Additionally, returning a Com object with a raw * usually speaks to a problematic design pattern. This is because it requires the "unjacketing" of the ComObject to return it to the caller without a clear communication on transfer/sharing of ownership which is by definition occuring by handing it out. #Resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The render target generators in CGImage and friends are going away (and are only being updated as necessary as part of this PR); ideally, yes, we'd fix everything all at once. There's little point when it's gonna be turfed. :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code has gone away, i'll be sending out the PR soon. In reply to: 86612430 [](ancestors = 86612430) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
_renderTarget = renderTarget.Detach(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,7 @@ | |
#import <CoreGraphics/CGGradient.h> | ||
#import "CGColorSpaceInternal.h" | ||
#import "CGContextInternal.h" | ||
#import "CGPathInternal.h" | ||
|
||
#import <CFCppBase.h> | ||
|
||
|
@@ -1646,8 +1647,12 @@ void CGContextFillRects(CGContextRef context, const CGRect* rects, size_t count) | |
*/ | ||
void CGContextDrawPath(CGContextRef context, CGPathDrawingMode mode) { | ||
NOISY_RETURN_IF_NULL(context); | ||
UNIMPLEMENTED(); | ||
context->ClearPath(); | ||
if (context->HasPath()) { | ||
ComPtr<ID2D1Geometry> pGeometry; | ||
FAIL_FAST_IF_FAILED(_CGPathGetGeometry(context->Path(), &pGeometry)); | ||
FAIL_FAST_IF_FAILED(__CGContextDrawGeometry(context, pGeometry.Get(), mode)); | ||
context->ClearPath(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should do something with the HRESULT here #Resolved |
||
} | ||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,11 +24,12 @@ | |
#import <CoreFoundation/CoreFoundation.h> | ||
#import <CFRuntime.h> | ||
#import "D2DWrapper.h" | ||
#import "CGPathInternal.h" | ||
|
||
#include <COMIncludes.h> | ||
#import <wrl/client.h> | ||
#import <D2d1.h> | ||
#include <COMIncludes_End.h> | ||
|
||
#import <CFCPPBase.h> | ||
|
||
static const wchar_t* TAG = L"CGPath"; | ||
|
@@ -91,7 +92,8 @@ void SetStartingPoint(CGPoint newPoint) { | |
HRESULT PreparePathForEditing() { | ||
if (!_impl.geometrySink) { | ||
// Re-open this geometry. | ||
ComPtr<ID2D1Factory> factory = _GetD2DFactoryInstance(); | ||
ComPtr<ID2D1Factory> factory; | ||
RETURN_IF_FAILED(_CGGetD2DFactory(&factory)); | ||
|
||
// Create temp vars for new path/sink | ||
ComPtr<ID2D1PathGeometry> newPath; | ||
|
@@ -113,12 +115,13 @@ HRESULT PreparePathForEditing() { | |
return S_OK; | ||
} | ||
|
||
void ClosePath() { | ||
HRESULT ClosePath() { | ||
if (_impl.geometrySink) { | ||
EndFigure(D2D1_FIGURE_END_OPEN); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this fail? #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. virtual void EndFigure( It cannot. Calling it multiple times can result in a bad state though which makes this API slightly weird since it doesn't report that itself. In reply to: 86611357 [](ancestors = 86611357) |
||
_impl.geometrySink->Close(); | ||
RETURN_IF_FAILED(_impl.geometrySink->Close()); | ||
_impl.geometrySink = nullptr; | ||
} | ||
return S_OK; | ||
} | ||
|
||
void BeginFigure() { | ||
|
@@ -136,7 +139,8 @@ void EndFigure(D2D1_FIGURE_END figureStatus) { | |
} | ||
|
||
HRESULT InitializeGeometries() { | ||
ComPtr<ID2D1Factory> factory = _GetD2DFactoryInstance(); | ||
ComPtr<ID2D1Factory> factory; | ||
RETURN_IF_FAILED(_CGGetD2DFactory(&factory)); | ||
|
||
RETURN_IF_FAILED(factory->CreatePathGeometry(&_impl.pathGeometry)); | ||
RETURN_IF_FAILED(_impl.pathGeometry->Open(&_impl.geometrySink)); | ||
|
@@ -145,6 +149,14 @@ HRESULT InitializeGeometries() { | |
} | ||
}; | ||
|
||
HRESULT _CGPathGetGeometry(CGPathRef path, ID2D1Geometry** pGeometry) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
if pGeometry is invalid, we end up returning S_OK. RETURN_HR_IF_NULL(E_POINTER,pGeometry); |
||
RETURN_HR_IF_NULL(E_POINTER, pGeometry); | ||
RETURN_HR_IF_NULL(E_POINTER, path); | ||
RETURN_IF_FAILED(path->ClosePath()); | ||
path->GetPathGeometry().CopyTo(pGeometry); | ||
return S_OK; | ||
} | ||
|
||
CFTypeID CGPathGetTypeID() { | ||
return __CGPath::GetTypeID(); | ||
} | ||
|
@@ -159,8 +171,8 @@ static Boolean __CGPathEqual(CFTypeRef cf1, CFTypeRef cf2) { | |
__CGPath* path1 = (__CGPath*)cf1; | ||
__CGPath* path2 = (__CGPath*)cf2; | ||
|
||
path1->ClosePath(); | ||
path2->ClosePath(); | ||
RETURN_FALSE_IF_FAILED(path1->ClosePath()); | ||
RETURN_FALSE_IF_FAILED(path2->ClosePath()); | ||
|
||
// ID2D1 Geometries have no isEquals method. However, for two geometries to be equal they are both reported to contain the other. | ||
// Thus we must do two comparisons. | ||
|
@@ -212,7 +224,7 @@ CGMutablePathRef CGPathCreateMutableCopy(CGPathRef path) { | |
// In order to call stream and copy the contents of the original path into the | ||
// new copy we must close this path. | ||
// Otherwise the D2D calls will return that a bad state has been entered. | ||
path->ClosePath(); | ||
FAIL_FAST_IF_FAILED(path->ClosePath()); | ||
|
||
FAIL_FAST_IF_FAILED(path->GetPathGeometry()->Stream(mutableRet->GetGeometrySink().Get())); | ||
|
||
|
@@ -451,7 +463,7 @@ void CGPathAddPath(CGMutablePathRef path, const CGAffineTransform* transform, CG | |
RETURN_IF(!path || !toAdd); | ||
|
||
// Close the path we're adding and open the path we need to add to. | ||
toAdd->ClosePath(); | ||
FAIL_FAST_IF_FAILED(toAdd->ClosePath()); | ||
FAIL_FAST_IF_FAILED(path->PreparePathForEditing()); | ||
FAIL_FAST_IF_FAILED(toAdd->GetPathGeometry()->Stream(path->GetGeometrySink().Get())); | ||
} | ||
|
@@ -510,7 +522,9 @@ CGRect CGPathGetBoundingBox(CGPathRef path) { | |
|
||
D2D1_RECT_F bounds; | ||
|
||
path->ClosePath(); | ||
if (FAILED(path->ClosePath())) { | ||
return CGRectNull; | ||
} | ||
|
||
if (FAILED(path->GetPathGeometry()->GetBounds(D2D1::IdentityMatrix(), &bounds))) { | ||
return CGRectNull; | ||
|
@@ -530,7 +544,7 @@ bool CGPathIsEmpty(CGPathRef path) { | |
|
||
UINT32 count; | ||
|
||
path->ClosePath(); | ||
RETURN_FALSE_IF_FAILED(path->ClosePath()); | ||
|
||
RETURN_FALSE_IF_FAILED(path->GetPathGeometry()->GetFigureCount(&count)); | ||
return count == 0; | ||
|
@@ -673,7 +687,7 @@ bool CGPathContainsPoint(CGPathRef path, const CGAffineTransform* transform, CGP | |
|
||
BOOL containsPoint = FALSE; | ||
|
||
path->ClosePath(); | ||
RETURN_FALSE_IF_FAILED(path->ClosePath()); | ||
RETURN_FALSE_IF_FAILED(path->GetPathGeometry()->FillContainsPoint(_CGPointToD2D_F(point), D2D1::IdentityMatrix(), &containsPoint)); | ||
|
||
return (containsPoint ? true : false); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,16 +19,12 @@ | |
|
||
using namespace Microsoft::WRL; | ||
|
||
// Private helper for creating a D2DFactory | ||
static ComPtr<ID2D1Factory> __createD2DFactory() { | ||
ComPtr<ID2D1Factory> d2dFactory; | ||
FAIL_FAST_IF_FAILED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), &d2dFactory)); | ||
return d2dFactory; | ||
} | ||
HRESULT _CGGetD2DFactory(ID2D1Factory** factory) { | ||
static ComPtr<ID2D1Factory> sFactory; | ||
static HRESULT sHr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), &sFactory); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If one static referring to another as part of its initialization is safe, then this should be totally fine. |
||
ComPtr<ID2D1Factory> _GetD2DFactoryInstance() { | ||
static ComPtr<ID2D1Factory> factory = __createD2DFactory(); | ||
return factory; | ||
sFactory.CopyTo(factory); | ||
RETURN_HR(sHr); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
return sHR? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Figured this was better from result.h "// Always returns a known result (HRESULT) - logs failures in pre-release In reply to: 86633680 [](ancestors = 86633680,86633664) |
||
} | ||
|
||
static ComPtr<IWICImagingFactory> __createWICFactory() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -412,7 +412,7 @@ CGPathRef newPathForRoundRect(CGRect rect, CGFloat radius) { | |
return path; | ||
} | ||
|
||
TEST(CGPath, CGPathContainsPointOutsideRect) { | ||
DISABLED_TEST(CGPath, CGPathContainsPointOutsideRect) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
These tests should have been disabled initially since they involve work with arcs/curves. These will be re-enabled then. |
||
CGFloat originX = 10.0f; | ||
CGFloat originY = 20.0f; | ||
CGFloat pathWidth = 100.0f; | ||
|
@@ -438,7 +438,7 @@ CGPathRef newPathForRoundRect(CGRect rect, CGFloat radius) { | |
EXPECT_FALSE(test); | ||
} | ||
|
||
TEST(CGPath, CGPathContainsPointInsideRectOutsidePath) { | ||
DISABLED_TEST(CGPath, CGPathContainsPointInsideRectOutsidePath) { | ||
CGFloat originX = 10.0f; | ||
CGFloat originY = 20.0f; | ||
CGFloat pathWidth = 100.0f; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please no, THROW_IF_FAILED() is marginally better. FAIL_FAST is essentially abort, and as framework we should seldom be using it. And in other places too, per our offline discussion.