Skip to content

Commit ccaac05

Browse files
MSFTFoxRaj Seshasankaran
authored andcommitted
Implement missing UIBezierPath APIs and add tests (microsoft#2753)
* Implement missing UIBezierPath APIs and add tests * Address CR Feedback * CR Feedback. * Implement with blendmodes * Add support for blend mode APIs * Update annotations for blend mode additions * Remove extra tracking for line pattern size
1 parent 28dca95 commit ccaac05

19 files changed

Lines changed: 667 additions & 219 deletions

Frameworks/CoreGraphics/CGPath.mm

Lines changed: 38 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@
4646
: m_geometrySink(sink),
4747
m_lastPoint{ 0, 0 },
4848
m_isFigureOpen(false),
49-
m_allowsFigureCalls(false),
49+
m_allowsFigureCalls(0),
5050
m_hasGeometryStarted(false),
5151
m_lastClose(D2D1_FIGURE_END_CLOSED),
52-
m_allowsFigureEndExceptFinal(false),
52+
m_allowsFigureEndExceptFinal(0),
5353
m_delayedEndFigure(false) {
5454
}
5555

@@ -115,7 +115,7 @@
115115
}
116116

117117
STDMETHOD_(void, BeginFigure)(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) {
118-
if (m_allowsFigureCalls) {
118+
if (m_allowsFigureCalls > 0) {
119119
_BeginFigure(startPoint, figureBegin);
120120
}
121121
}
@@ -135,10 +135,10 @@
135135

136136
// Only end the figure if we're requesting to actually close a figure, or if we have allowed the backing sink to dictate figure calls.
137137
STDMETHOD_(void, EndFigure)(D2D1_FIGURE_END figureEnd) {
138-
if (m_allowsFigureEndExceptFinal && figureEnd != D2D1_FIGURE_END_CLOSED) {
138+
if (m_allowsFigureEndExceptFinal > 0 && figureEnd != D2D1_FIGURE_END_CLOSED) {
139139
m_delayedEndFigure = true;
140140
m_delayedEndFigureClosure = figureEnd;
141-
} else if (figureEnd == D2D1_FIGURE_END_CLOSED || m_allowsFigureCalls) {
141+
} else if (figureEnd == D2D1_FIGURE_END_CLOSED || m_allowsFigureCalls > 0) {
142142
_EndFigure(figureEnd);
143143
}
144144
}
@@ -158,8 +158,9 @@
158158
return m_geometrySink.Get();
159159
}
160160

161-
void SetAllowsFigureCalls(bool allows) {
162-
m_allowsFigureCalls = allows;
161+
HRESULT SetAllowsFigureCalls(bool allows) {
162+
m_allowsFigureCalls += (allows ? 1 : -1);
163+
return m_allowsFigureCalls >= 0 ? S_OK : E_INVALIDARG;
163164
}
164165

165166
bool IsFigureEndClosed() {
@@ -198,9 +199,10 @@ void SetBackingSink(ID2D1GeometrySink* sink) {
198199
m_geometrySink = sink;
199200
}
200201

201-
void AllowAllExceptFinalEndFigure(bool allow) {
202-
m_allowsFigureEndExceptFinal = allow;
202+
HRESULT AllowAllExceptFinalEndFigure(bool allow) {
203+
m_allowsFigureEndExceptFinal += (allow ? 1 : -1);
203204
m_delayedEndFigure = false;
205+
return m_allowsFigureEndExceptFinal >= 0 ? S_OK : E_INVALIDARG;
204206
}
205207

206208
private:
@@ -216,15 +218,14 @@ void _delayedEndFigureCall() {
216218
D2D1_POINT_2F m_lastPoint;
217219
// The starting point this geometry will move to if it is closed.
218220
D2D1_POINT_2F m_startPoint;
219-
// Whether or not the figure is open and ready to recieve new path information
221+
// Whether or not the figure is open and ready to receive new path information
220222
bool m_isFigureOpen;
221-
// Whether or not any geometry has been started. This can be inferred from other APIs, but those would require the geometry to be
222-
// closed.
223+
// Whether any geometry has been started. This can be inferred from other APIs, but those would require the geometry to be closed.
223224
bool m_hasGeometryStarted;
224-
// True if this geometry can end its own figures.
225-
bool m_allowsFigureCalls;
226-
// True if this geometry will leave it's last EndFigure call ignored.
227-
bool m_allowsFigureEndExceptFinal;
225+
// Greater than 0 if this geometry can end its own figures.
226+
int m_allowsFigureCalls;
227+
// Greater than 0 if this geometry will leave it's last EndFigure call ignored.
228+
int m_allowsFigureEndExceptFinal;
228229
// True when the next figure created should end the previous figure first.
229230
bool m_delayedEndFigure;
230231
// The figure closure type to end the last figure with.
@@ -270,8 +271,8 @@ bool IsGeometryStarted() {
270271
return geometrySink && geometrySink->IsGeometryStarted();
271272
}
272273

273-
void SetAllowsFigureCalls(bool allows) {
274-
geometrySink->SetAllowsFigureCalls(allows);
274+
HRESULT SetAllowsFigureCalls(bool allows) {
275+
return geometrySink->SetAllowsFigureCalls(allows);
275276
}
276277

277278
CGPoint GetCurrentPoint() const {
@@ -303,9 +304,10 @@ void SetLastTransform(const CGAffineTransform* transform) {
303304
return &lastTransform;
304305
}
305306

306-
void AllowAllExceptFinalEndFigure(bool allow) {
307-
SetAllowsFigureCalls(allow);
308-
geometrySink->AllowAllExceptFinalEndFigure(allow);
307+
HRESULT AllowAllExceptFinalEndFigure(bool allow) {
308+
RETURN_IF_FAILED(SetAllowsFigureCalls(allow));
309+
RETURN_IF_FAILED(geometrySink->AllowAllExceptFinalEndFigure(allow));
310+
return S_OK;
309311
}
310312

311313
// A private helper function for re-opening a path geometry. CGPath does not
@@ -335,9 +337,9 @@ HRESULT PreparePathForEditing() {
335337

336338
geometrySink->SetBackingSink(newBackingSink.Get());
337339
// Allow all calls except final endfigure
338-
AllowAllExceptFinalEndFigure(true);
340+
RETURN_IF_FAILED(AllowAllExceptFinalEndFigure(true));
339341
RETURN_IF_FAILED(pathGeometry->Stream(geometrySink.Get()));
340-
AllowAllExceptFinalEndFigure(false);
342+
RETURN_IF_FAILED(AllowAllExceptFinalEndFigure(false));
341343
geometrySink->SetFillMode(D2D1_FILL_MODE_WINDING);
342344

343345
pathGeometry = newPath;
@@ -430,9 +432,9 @@ HRESULT WidenByStroking(
430432
d2dTransform = __CGAffineTransformToD2D_F(*transform);
431433
}
432434

433-
AllowAllExceptFinalEndFigure(true);
435+
RETURN_IF_FAILED(AllowAllExceptFinalEndFigure(true));
434436
RETURN_IF_FAILED(path->GetPathGeometry()->Widen(lineWidth, newStrokeStyle.Get(), d2dTransform, geometrySink.Get()));
435-
AllowAllExceptFinalEndFigure(false);
437+
RETURN_IF_FAILED(AllowAllExceptFinalEndFigure(false));
436438

437439
pathGeometry = newPath;
438440

@@ -597,9 +599,9 @@ CGMutablePathRef CGPathCreateMutableCopy(CGPathRef path) {
597599
// Otherwise the D2D calls will return that a bad state has been entered.
598600
FAIL_FAST_IF_FAILED(path->ClosePath());
599601

600-
mutableRet->AllowAllExceptFinalEndFigure(true);
602+
FAIL_FAST_IF_FAILED(mutableRet->AllowAllExceptFinalEndFigure(true));
601603
FAIL_FAST_IF_FAILED(path->GetPathGeometry()->Stream(mutableRet->GetGeometrySink()));
602-
mutableRet->AllowAllExceptFinalEndFigure(false);
604+
FAIL_FAST_IF_FAILED(mutableRet->AllowAllExceptFinalEndFigure(false));
603605

604606
mutableRet->SetCurrentPoint(path->GetCurrentPoint());
605607
mutableRet->SetLastTransform(path->GetLastTransform());
@@ -872,9 +874,9 @@ void CGPathAddPath(CGMutablePathRef path, const CGAffineTransform* transform, CG
872874
FAIL_FAST_IF_FAILED(toAdd->ClosePath());
873875
CGPoint newStart = toAdd->GetStartingPoint();
874876
// CGPathMoveToPoint(path, transform, newStart.x, newStart.y);
875-
path->AllowAllExceptFinalEndFigure(true);
877+
FAIL_FAST_IF_FAILED(path->AllowAllExceptFinalEndFigure(true));
876878
FAIL_FAST_IF_FAILED(path->AddGeometryToPathWithTransformation(toAdd->GetPathGeometry(), transform));
877-
path->AllowAllExceptFinalEndFigure(false);
879+
FAIL_FAST_IF_FAILED(path->AllowAllExceptFinalEndFigure(false));
878880
}
879881

880882
/**
@@ -894,9 +896,9 @@ void CGPathAddEllipseInRect(CGMutablePathRef path, const CGAffineTransform* tran
894896

895897
FAIL_FAST_IF_FAILED(factory->CreateEllipseGeometry(&ellipse, &ellipseGeometry));
896898

897-
path->SetAllowsFigureCalls(true);
899+
FAIL_FAST_IF_FAILED(path->SetAllowsFigureCalls(true));
898900
FAIL_FAST_IF_FAILED(path->AddGeometryToPathWithTransformation(ellipseGeometry.Get(), transform));
899-
path->SetAllowsFigureCalls(false);
901+
FAIL_FAST_IF_FAILED(path->SetAllowsFigureCalls(false));
900902
}
901903

902904
/**
@@ -969,12 +971,7 @@ bool CGPathIsEmpty(CGPathRef path) {
969971
return true;
970972
}
971973

972-
UINT32 count;
973-
974-
RETURN_FALSE_IF_FAILED(path->ClosePath());
975-
976-
RETURN_FALSE_IF_FAILED(path->GetPathGeometry()->GetFigureCount(&count));
977-
return count == 0;
974+
return !path->IsGeometryStarted();
978975
}
979976

980977
/**
@@ -1128,9 +1125,9 @@ void CGPathAddRoundedRect(
11281125

11291126
FAIL_FAST_IF_FAILED(factory->CreateRoundedRectangleGeometry(&roundedRectangle, &rectangleGeometry));
11301127

1131-
path->SetAllowsFigureCalls(true);
1128+
FAIL_FAST_IF_FAILED(path->SetAllowsFigureCalls(true));
11321129
FAIL_FAST_IF_FAILED(path->AddGeometryToPathWithTransformation(rectangleGeometry.Get(), transform));
1133-
path->SetAllowsFigureCalls(false);
1130+
FAIL_FAST_IF_FAILED(path->SetAllowsFigureCalls(false));
11341131
}
11351132

11361133
/**
@@ -1211,9 +1208,9 @@ CGMutablePathRef CGPathCreateMutableCopyByTransformingPath(CGPathRef path, const
12111208
CGMutablePathRef transformedPath = CGPathCreateMutable();
12121209
FAIL_FAST_IF_FAILED(path->ClosePath());
12131210

1214-
transformedPath->AllowAllExceptFinalEndFigure(true);
1211+
FAIL_FAST_IF_FAILED(transformedPath->AllowAllExceptFinalEndFigure(true));
12151212
FAIL_FAST_IF_FAILED(transformedPath->AddGeometryToPathWithTransformation(path->GetPathGeometry(), transform));
1216-
transformedPath->AllowAllExceptFinalEndFigure(false);
1213+
FAIL_FAST_IF_FAILED(transformedPath->AllowAllExceptFinalEndFigure(false));
12171214

12181215
transformedPath->SetStartingPoint(path->GetStartingPoint());
12191216
transformedPath->SetCurrentPoint(path->GetCurrentPoint());

0 commit comments

Comments
 (0)