Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
6 #include "PathD2D.h"
7 #include "HelpersD2D.h"
8 #include <math.h>
9 #include "DrawTargetD2D.h"
10 #include "Logging.h"
11 #include "mozilla/Constants.h"
13 namespace mozilla {
14 namespace gfx {
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
19 // continued.
20 class OpeningGeometrySink : public ID2D1SimplifiedGeometrySink
21 {
22 public:
23 OpeningGeometrySink(ID2D1SimplifiedGeometrySink *aSink)
24 : mSink(aSink)
25 , mNeedsFigureEnded(false)
26 {
27 }
29 HRESULT STDMETHODCALLTYPE QueryInterface(const IID &aIID, void **aPtr)
30 {
31 if (!aPtr) {
32 return E_POINTER;
33 }
35 if (aIID == IID_IUnknown) {
36 *aPtr = static_cast<IUnknown*>(this);
37 return S_OK;
38 } else if (aIID == IID_ID2D1SimplifiedGeometrySink) {
39 *aPtr = static_cast<ID2D1SimplifiedGeometrySink*>(this);
40 return S_OK;
41 }
43 return E_NOINTERFACE;
44 }
46 ULONG STDMETHODCALLTYPE AddRef()
47 {
48 return 1;
49 }
51 ULONG STDMETHODCALLTYPE Release()
52 {
53 return 1;
54 }
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); }
65 STDMETHOD(Close)()
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)
74 {
75 if (aEnd == D2D1_FIGURE_END_CLOSED) {
76 return mSink->EndFigure(aEnd);
77 } else {
78 mNeedsFigureEnded = true;
79 }
80 }
81 private:
82 void EnsureFigureEnded()
83 {
84 if (mNeedsFigureEnded) {
85 mSink->EndFigure(D2D1_FIGURE_END_OPEN);
86 mNeedsFigureEnded = false;
87 }
88 }
90 ID2D1SimplifiedGeometrySink *mSink;
91 bool mNeedsFigureEnded;
92 };
94 class StreamingGeometrySink : public ID2D1SimplifiedGeometrySink
95 {
96 public:
97 StreamingGeometrySink(PathSink *aSink)
98 : mSink(aSink)
99 {
100 }
102 HRESULT STDMETHODCALLTYPE QueryInterface(const IID &aIID, void **aPtr)
103 {
104 if (!aPtr) {
105 return E_POINTER;
106 }
108 if (aIID == IID_IUnknown) {
109 *aPtr = static_cast<IUnknown*>(this);
110 return S_OK;
111 } else if (aIID == IID_ID2D1SimplifiedGeometrySink) {
112 *aPtr = static_cast<ID2D1SimplifiedGeometrySink*>(this);
113 return S_OK;
114 }
116 return E_NOINTERFACE;
117 }
119 ULONG STDMETHODCALLTYPE AddRef()
120 {
121 return 1;
122 }
124 ULONG STDMETHODCALLTYPE Release()
125 {
126 return 1;
127 }
129 // We ignore SetFillMode, this depends on the destination sink.
130 STDMETHOD_(void, SetFillMode)(D2D1_FILL_MODE aMode)
131 { return; }
132 STDMETHOD_(void, BeginFigure)(D2D1_POINT_2F aPoint, D2D1_FIGURE_BEGIN aBegin)
133 { mSink->MoveTo(ToPoint(aPoint)); }
134 STDMETHOD_(void, AddLines)(const D2D1_POINT_2F *aLines, UINT aCount)
135 { for (UINT i = 0; i < aCount; i++) { mSink->LineTo(ToPoint(aLines[i])); } }
136 STDMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *aSegments, UINT aCount)
137 {
138 for (UINT i = 0; i < aCount; i++) {
139 mSink->BezierTo(ToPoint(aSegments[i].point1), ToPoint(aSegments[i].point2), ToPoint(aSegments[i].point3));
140 }
141 }
142 STDMETHOD(Close)()
143 { /* Should never be called! */ return S_OK; }
144 STDMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT aFlags)
145 { /* Should never be called! */ }
147 STDMETHOD_(void, EndFigure)(D2D1_FIGURE_END aEnd)
148 {
149 if (aEnd == D2D1_FIGURE_END_CLOSED) {
150 return mSink->Close();
151 }
152 }
153 private:
155 PathSink *mSink;
156 };
158 PathBuilderD2D::~PathBuilderD2D()
159 {
160 }
162 void
163 PathBuilderD2D::MoveTo(const Point &aPoint)
164 {
165 if (mFigureActive) {
166 mSink->EndFigure(D2D1_FIGURE_END_OPEN);
167 mFigureActive = false;
168 }
169 EnsureActive(aPoint);
170 mCurrentPoint = aPoint;
171 }
173 void
174 PathBuilderD2D::LineTo(const Point &aPoint)
175 {
176 EnsureActive(aPoint);
177 mSink->AddLine(D2DPoint(aPoint));
179 mCurrentPoint = aPoint;
180 }
182 void
183 PathBuilderD2D::BezierTo(const Point &aCP1,
184 const Point &aCP2,
185 const Point &aCP3)
186 {
187 EnsureActive(aCP1);
188 mSink->AddBezier(D2D1::BezierSegment(D2DPoint(aCP1),
189 D2DPoint(aCP2),
190 D2DPoint(aCP3)));
192 mCurrentPoint = aCP3;
193 }
195 void
196 PathBuilderD2D::QuadraticBezierTo(const Point &aCP1,
197 const Point &aCP2)
198 {
199 EnsureActive(aCP1);
200 mSink->AddQuadraticBezier(D2D1::QuadraticBezierSegment(D2DPoint(aCP1),
201 D2DPoint(aCP2)));
203 mCurrentPoint = aCP2;
204 }
206 void
207 PathBuilderD2D::Close()
208 {
209 if (mFigureActive) {
210 mSink->EndFigure(D2D1_FIGURE_END_CLOSED);
212 mFigureActive = false;
214 EnsureActive(mBeginPoint);
215 }
216 }
218 void
219 PathBuilderD2D::Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
220 Float aEndAngle, bool aAntiClockwise)
221 {
222 if (aAntiClockwise && aStartAngle < aEndAngle) {
223 // D2D does things a little differently, and draws the arc by specifying an
224 // beginning and an end point. This means the circle will be the wrong way
225 // around if the start angle is smaller than the end angle. It might seem
226 // tempting to invert aAntiClockwise but that would change the sweeping
227 // direction of the arc to instead we exchange start/begin.
228 Float oldStart = aStartAngle;
229 aStartAngle = aEndAngle;
230 aEndAngle = oldStart;
231 }
233 // XXX - Workaround for now, D2D does not appear to do the desired thing when
234 // the angle sweeps a complete circle.
235 if (aEndAngle - aStartAngle >= 2 * M_PI) {
236 aEndAngle = Float(aStartAngle + M_PI * 1.9999);
237 } else if (aStartAngle - aEndAngle >= 2 * M_PI) {
238 aStartAngle = Float(aEndAngle + M_PI * 1.9999);
239 }
241 Point startPoint;
242 startPoint.x = aOrigin.x + aRadius * cos(aStartAngle);
243 startPoint.y = aOrigin.y + aRadius * sin(aStartAngle);
245 if (!mFigureActive) {
246 EnsureActive(startPoint);
247 } else {
248 mSink->AddLine(D2DPoint(startPoint));
249 }
251 Point endPoint;
252 endPoint.x = aOrigin.x + aRadius * cos(aEndAngle);
253 endPoint.y = aOrigin.y + aRadius * sin(aEndAngle);
255 D2D1_ARC_SIZE arcSize = D2D1_ARC_SIZE_SMALL;
257 if (aAntiClockwise) {
258 if (aStartAngle - aEndAngle > M_PI) {
259 arcSize = D2D1_ARC_SIZE_LARGE;
260 }
261 } else {
262 if (aEndAngle - aStartAngle > M_PI) {
263 arcSize = D2D1_ARC_SIZE_LARGE;
264 }
265 }
267 mSink->AddArc(D2D1::ArcSegment(D2DPoint(endPoint),
268 D2D1::SizeF(aRadius, aRadius),
269 0.0f,
270 aAntiClockwise ? D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE :
271 D2D1_SWEEP_DIRECTION_CLOCKWISE,
272 arcSize));
274 mCurrentPoint = endPoint;
275 }
277 Point
278 PathBuilderD2D::CurrentPoint() const
279 {
280 return mCurrentPoint;
281 }
283 void
284 PathBuilderD2D::EnsureActive(const Point &aPoint)
285 {
286 if (!mFigureActive) {
287 mSink->BeginFigure(D2DPoint(aPoint), D2D1_FIGURE_BEGIN_FILLED);
288 mBeginPoint = aPoint;
289 mFigureActive = true;
290 }
291 }
293 TemporaryRef<Path>
294 PathBuilderD2D::Finish()
295 {
296 if (mFigureActive) {
297 mSink->EndFigure(D2D1_FIGURE_END_OPEN);
298 }
300 HRESULT hr = mSink->Close();
301 if (FAILED(hr)) {
302 gfxDebug() << "Failed to close PathSink. Code: " << hr;
303 return nullptr;
304 }
306 return new PathD2D(mGeometry, mFigureActive, mCurrentPoint, mFillRule);
307 }
309 TemporaryRef<PathBuilder>
310 PathD2D::CopyToBuilder(FillRule aFillRule) const
311 {
312 return TransformedCopyToBuilder(Matrix(), aFillRule);
313 }
315 TemporaryRef<PathBuilder>
316 PathD2D::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
317 {
318 RefPtr<ID2D1PathGeometry> path;
319 HRESULT hr = DrawTargetD2D::factory()->CreatePathGeometry(byRef(path));
321 if (FAILED(hr)) {
322 gfxWarning() << "Failed to create PathGeometry. Code: " << hr;
323 return nullptr;
324 }
326 RefPtr<ID2D1GeometrySink> sink;
327 hr = path->Open(byRef(sink));
328 if (FAILED(hr)) {
329 gfxWarning() << "Failed to open Geometry for writing. Code: " << hr;
330 return nullptr;
331 }
333 if (aFillRule == FillRule::FILL_WINDING) {
334 sink->SetFillMode(D2D1_FILL_MODE_WINDING);
335 }
337 if (mEndedActive) {
338 OpeningGeometrySink wrapSink(sink);
339 mGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
340 D2DMatrix(aTransform),
341 &wrapSink);
342 } else {
343 mGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
344 D2DMatrix(aTransform),
345 sink);
346 }
348 RefPtr<PathBuilderD2D> pathBuilder = new PathBuilderD2D(sink, path, aFillRule);
350 pathBuilder->mCurrentPoint = aTransform * mEndPoint;
352 if (mEndedActive) {
353 pathBuilder->mFigureActive = true;
354 }
356 return pathBuilder;
357 }
359 void
360 PathD2D::StreamToSink(PathSink *aSink) const
361 {
362 HRESULT hr;
364 StreamingGeometrySink sink(aSink);
366 hr = mGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
367 D2D1::IdentityMatrix(), &sink);
369 if (FAILED(hr)) {
370 gfxWarning() << "Failed to stream D2D path to sink. Code: " << hr;
371 return;
372 }
373 }
375 bool
376 PathD2D::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
377 {
378 BOOL result;
380 HRESULT hr = mGeometry->FillContainsPoint(D2DPoint(aPoint), D2DMatrix(aTransform), 0.001f, &result);
382 if (FAILED(hr)) {
383 // Log
384 return false;
385 }
387 return !!result;
388 }
390 bool
391 PathD2D::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
392 const Point &aPoint,
393 const Matrix &aTransform) const
394 {
395 BOOL result;
397 RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
398 HRESULT hr = mGeometry->StrokeContainsPoint(D2DPoint(aPoint),
399 aStrokeOptions.mLineWidth,
400 strokeStyle,
401 D2DMatrix(aTransform),
402 &result);
404 if (FAILED(hr)) {
405 // Log
406 return false;
407 }
409 return !!result;
410 }
412 Rect
413 PathD2D::GetBounds(const Matrix &aTransform) const
414 {
415 D2D1_RECT_F d2dBounds;
417 HRESULT hr = mGeometry->GetBounds(D2DMatrix(aTransform), &d2dBounds);
419 Rect bounds = ToRect(d2dBounds);
420 if (FAILED(hr) || !bounds.IsFinite()) {
421 gfxWarning() << "Failed to get stroked bounds for path. Code: " << hr;
422 return Rect();
423 }
425 return bounds;
426 }
428 Rect
429 PathD2D::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
430 const Matrix &aTransform) const
431 {
432 D2D1_RECT_F d2dBounds;
434 RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
435 HRESULT hr =
436 mGeometry->GetWidenedBounds(aStrokeOptions.mLineWidth, strokeStyle,
437 D2DMatrix(aTransform), &d2dBounds);
439 Rect bounds = ToRect(d2dBounds);
440 if (FAILED(hr) || !bounds.IsFinite()) {
441 gfxWarning() << "Failed to get stroked bounds for path. Code: " << hr;
442 return Rect();
443 }
445 return bounds;
446 }
448 }
449 }