1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "HelpersD2D.h"
9 #include "DrawTargetD2D.h"
11 #include "mozilla/Constants.h"
16 // This class exists as a wrapper for ID2D1SimplifiedGeometry sink, it allows
17 // a geometry to be duplicated into a geometry sink, while removing the final
18 // figure end and thus allowing a figure that was implicitly closed to be
20 class OpeningGeometrySink
: public ID2D1SimplifiedGeometrySink
23 OpeningGeometrySink(ID2D1SimplifiedGeometrySink
*aSink
)
25 , mNeedsFigureEnded(false)
29 HRESULT STDMETHODCALLTYPE
QueryInterface(const IID
&aIID
, void **aPtr
)
35 if (aIID
== IID_IUnknown
) {
36 *aPtr
= static_cast<IUnknown
*>(this);
38 } else if (aIID
== IID_ID2D1SimplifiedGeometrySink
) {
39 *aPtr
= static_cast<ID2D1SimplifiedGeometrySink
*>(this);
46 ULONG STDMETHODCALLTYPE
AddRef()
51 ULONG STDMETHODCALLTYPE
Release()
56 // We ignore SetFillMode, the copier will decide.
57 STDMETHOD_(void, SetFillMode
)(D2D1_FILL_MODE aMode
)
58 { EnsureFigureEnded(); return; }
59 STDMETHOD_(void, BeginFigure
)(D2D1_POINT_2F aPoint
, D2D1_FIGURE_BEGIN aBegin
)
60 { EnsureFigureEnded(); return mSink
->BeginFigure(aPoint
, aBegin
); }
61 STDMETHOD_(void, AddLines
)(const D2D1_POINT_2F
*aLines
, UINT aCount
)
62 { EnsureFigureEnded(); return mSink
->AddLines(aLines
, aCount
); }
63 STDMETHOD_(void, AddBeziers
)(const D2D1_BEZIER_SEGMENT
*aSegments
, UINT aCount
)
64 { EnsureFigureEnded(); return mSink
->AddBeziers(aSegments
, aCount
); }
66 { /* Should never be called! */ return S_OK
; }
67 STDMETHOD_(void, SetSegmentFlags
)(D2D1_PATH_SEGMENT aFlags
)
68 { return mSink
->SetSegmentFlags(aFlags
); }
70 // This function is special - it's the reason this class exists.
71 // It needs to intercept the very last endfigure. So that a user can
72 // continue writing to this sink as if they never stopped.
73 STDMETHOD_(void, EndFigure
)(D2D1_FIGURE_END aEnd
)
75 if (aEnd
== D2D1_FIGURE_END_CLOSED
) {
76 return mSink
->EndFigure(aEnd
);
78 mNeedsFigureEnded
= true;
82 void EnsureFigureEnded()
84 if (mNeedsFigureEnded
) {
85 mSink
->EndFigure(D2D1_FIGURE_END_OPEN
);
86 mNeedsFigureEnded
= false;
90 ID2D1SimplifiedGeometrySink
*mSink
;
91 bool mNeedsFigureEnded
;
94 PathBuilderD2D::~PathBuilderD2D()
99 PathBuilderD2D::MoveTo(const Point
&aPoint
)
102 mSink
->EndFigure(D2D1_FIGURE_END_OPEN
);
103 mFigureActive
= false;
105 EnsureActive(aPoint
);
106 mCurrentPoint
= aPoint
;
110 PathBuilderD2D::LineTo(const Point
&aPoint
)
112 EnsureActive(aPoint
);
113 mSink
->AddLine(D2DPoint(aPoint
));
115 mCurrentPoint
= aPoint
;
119 PathBuilderD2D::BezierTo(const Point
&aCP1
,
124 mSink
->AddBezier(D2D1::BezierSegment(D2DPoint(aCP1
),
128 mCurrentPoint
= aCP3
;
132 PathBuilderD2D::QuadraticBezierTo(const Point
&aCP1
,
136 mSink
->AddQuadraticBezier(D2D1::QuadraticBezierSegment(D2DPoint(aCP1
),
139 mCurrentPoint
= aCP2
;
143 PathBuilderD2D::Close()
146 mSink
->EndFigure(D2D1_FIGURE_END_CLOSED
);
148 mFigureActive
= false;
150 EnsureActive(mBeginPoint
);
155 PathBuilderD2D::Arc(const Point
&aOrigin
, Float aRadius
, Float aStartAngle
,
156 Float aEndAngle
, bool aAntiClockwise
)
158 if (aAntiClockwise
&& aStartAngle
< aEndAngle
) {
159 // D2D does things a little differently, and draws the arc by specifying an
160 // beginning and an end point. This means the circle will be the wrong way
161 // around if the start angle is smaller than the end angle. It might seem
162 // tempting to invert aAntiClockwise but that would change the sweeping
163 // direction of the arc to instead we exchange start/begin.
164 Float oldStart
= aStartAngle
;
165 aStartAngle
= aEndAngle
;
166 aEndAngle
= oldStart
;
169 // XXX - Workaround for now, D2D does not appear to do the desired thing when
170 // the angle sweeps a complete circle.
171 if (aEndAngle
- aStartAngle
>= 2 * M_PI
) {
172 aEndAngle
= Float(aStartAngle
+ M_PI
* 1.9999);
173 } else if (aStartAngle
- aEndAngle
>= 2 * M_PI
) {
174 aStartAngle
= Float(aEndAngle
+ M_PI
* 1.9999);
178 startPoint
.x
= aOrigin
.x
+ aRadius
* cos(aStartAngle
);
179 startPoint
.y
= aOrigin
.y
+ aRadius
* sin(aStartAngle
);
181 if (!mFigureActive
) {
182 EnsureActive(startPoint
);
184 mSink
->AddLine(D2DPoint(startPoint
));
188 endPoint
.x
= aOrigin
.x
+ aRadius
* cos(aEndAngle
);
189 endPoint
.y
= aOrigin
.y
+ aRadius
* sin(aEndAngle
);
191 D2D1_ARC_SIZE arcSize
= D2D1_ARC_SIZE_SMALL
;
193 if (aAntiClockwise
) {
194 if (aStartAngle
- aEndAngle
> M_PI
) {
195 arcSize
= D2D1_ARC_SIZE_LARGE
;
198 if (aEndAngle
- aStartAngle
> M_PI
) {
199 arcSize
= D2D1_ARC_SIZE_LARGE
;
203 mSink
->AddArc(D2D1::ArcSegment(D2DPoint(endPoint
),
204 D2D1::SizeF(aRadius
, aRadius
),
206 aAntiClockwise
? D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE
:
207 D2D1_SWEEP_DIRECTION_CLOCKWISE
,
210 mCurrentPoint
= endPoint
;
214 PathBuilderD2D::CurrentPoint() const
216 return mCurrentPoint
;
220 PathBuilderD2D::EnsureActive(const Point
&aPoint
)
222 if (!mFigureActive
) {
223 mSink
->BeginFigure(D2DPoint(aPoint
), D2D1_FIGURE_BEGIN_FILLED
);
224 mBeginPoint
= aPoint
;
225 mFigureActive
= true;
230 PathBuilderD2D::Finish()
233 mSink
->EndFigure(D2D1_FIGURE_END_OPEN
);
236 HRESULT hr
= mSink
->Close();
238 gfxDebug() << "Failed to close PathSink. Code: " << hr
;
242 return new PathD2D(mGeometry
, mFigureActive
, mCurrentPoint
, mFillRule
);
245 TemporaryRef
<PathBuilder
>
246 PathD2D::CopyToBuilder(FillRule aFillRule
) const
248 return TransformedCopyToBuilder(Matrix(), aFillRule
);
251 TemporaryRef
<PathBuilder
>
252 PathD2D::TransformedCopyToBuilder(const Matrix
&aTransform
, FillRule aFillRule
) const
254 RefPtr
<ID2D1PathGeometry
> path
;
255 HRESULT hr
= DrawTargetD2D::factory()->CreatePathGeometry(byRef(path
));
258 gfxWarning() << "Failed to create PathGeometry. Code: " << hr
;
262 RefPtr
<ID2D1GeometrySink
> sink
;
263 hr
= path
->Open(byRef(sink
));
265 gfxWarning() << "Failed to open Geometry for writing. Code: " << hr
;
269 if (aFillRule
== FILL_WINDING
) {
270 sink
->SetFillMode(D2D1_FILL_MODE_WINDING
);
274 OpeningGeometrySink
wrapSink(sink
);
275 mGeometry
->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES
,
276 D2DMatrix(aTransform
),
279 mGeometry
->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES
,
280 D2DMatrix(aTransform
),
284 RefPtr
<PathBuilderD2D
> pathBuilder
= new PathBuilderD2D(sink
, path
, mFillRule
);
286 pathBuilder
->mCurrentPoint
= aTransform
* mEndPoint
;
289 pathBuilder
->mFigureActive
= true;
296 PathD2D::ContainsPoint(const Point
&aPoint
, const Matrix
&aTransform
) const
300 HRESULT hr
= mGeometry
->FillContainsPoint(D2DPoint(aPoint
), D2DMatrix(aTransform
), 0.001f
, &result
);
311 PathD2D::StrokeContainsPoint(const StrokeOptions
&aStrokeOptions
,
313 const Matrix
&aTransform
) const
317 RefPtr
<ID2D1StrokeStyle
> strokeStyle
=
318 DrawTargetD2D::CreateStrokeStyleForOptions(aStrokeOptions
);
319 HRESULT hr
= mGeometry
->StrokeContainsPoint(D2DPoint(aPoint
),
320 aStrokeOptions
.mLineWidth
,
322 D2DMatrix(aTransform
),
334 PathD2D::GetBounds(const Matrix
&aTransform
) const
338 HRESULT hr
= mGeometry
->GetBounds(D2DMatrix(aTransform
), &bounds
);
341 gfxWarning() << "Failed to get stroked bounds for path. Code: " << hr
;
342 bounds
.bottom
= bounds
.left
= bounds
.right
= bounds
.top
= 0;
345 return ToRect(bounds
);
349 PathD2D::GetStrokedBounds(const StrokeOptions
&aStrokeOptions
,
350 const Matrix
&aTransform
) const
354 RefPtr
<ID2D1StrokeStyle
> strokeStyle
=
355 DrawTargetD2D::CreateStrokeStyleForOptions(aStrokeOptions
);
357 mGeometry
->GetWidenedBounds(aStrokeOptions
.mLineWidth
, strokeStyle
,
358 D2DMatrix(aTransform
), &bounds
);
361 gfxWarning() << "Failed to get stroked bounds for path. Code: " << hr
;
362 bounds
.bottom
= bounds
.left
= bounds
.right
= bounds
.top
= 0;
365 return ToRect(bounds
);