gfx/2d/PathD2D.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "PathD2D.h"
michael@0 7 #include "HelpersD2D.h"
michael@0 8 #include <math.h>
michael@0 9 #include "DrawTargetD2D.h"
michael@0 10 #include "Logging.h"
michael@0 11 #include "mozilla/Constants.h"
michael@0 12
michael@0 13 namespace mozilla {
michael@0 14 namespace gfx {
michael@0 15
michael@0 16 // This class exists as a wrapper for ID2D1SimplifiedGeometry sink, it allows
michael@0 17 // a geometry to be duplicated into a geometry sink, while removing the final
michael@0 18 // figure end and thus allowing a figure that was implicitly closed to be
michael@0 19 // continued.
michael@0 20 class OpeningGeometrySink : public ID2D1SimplifiedGeometrySink
michael@0 21 {
michael@0 22 public:
michael@0 23 OpeningGeometrySink(ID2D1SimplifiedGeometrySink *aSink)
michael@0 24 : mSink(aSink)
michael@0 25 , mNeedsFigureEnded(false)
michael@0 26 {
michael@0 27 }
michael@0 28
michael@0 29 HRESULT STDMETHODCALLTYPE QueryInterface(const IID &aIID, void **aPtr)
michael@0 30 {
michael@0 31 if (!aPtr) {
michael@0 32 return E_POINTER;
michael@0 33 }
michael@0 34
michael@0 35 if (aIID == IID_IUnknown) {
michael@0 36 *aPtr = static_cast<IUnknown*>(this);
michael@0 37 return S_OK;
michael@0 38 } else if (aIID == IID_ID2D1SimplifiedGeometrySink) {
michael@0 39 *aPtr = static_cast<ID2D1SimplifiedGeometrySink*>(this);
michael@0 40 return S_OK;
michael@0 41 }
michael@0 42
michael@0 43 return E_NOINTERFACE;
michael@0 44 }
michael@0 45
michael@0 46 ULONG STDMETHODCALLTYPE AddRef()
michael@0 47 {
michael@0 48 return 1;
michael@0 49 }
michael@0 50
michael@0 51 ULONG STDMETHODCALLTYPE Release()
michael@0 52 {
michael@0 53 return 1;
michael@0 54 }
michael@0 55
michael@0 56 // We ignore SetFillMode, the copier will decide.
michael@0 57 STDMETHOD_(void, SetFillMode)(D2D1_FILL_MODE aMode)
michael@0 58 { EnsureFigureEnded(); return; }
michael@0 59 STDMETHOD_(void, BeginFigure)(D2D1_POINT_2F aPoint, D2D1_FIGURE_BEGIN aBegin)
michael@0 60 { EnsureFigureEnded(); return mSink->BeginFigure(aPoint, aBegin); }
michael@0 61 STDMETHOD_(void, AddLines)(const D2D1_POINT_2F *aLines, UINT aCount)
michael@0 62 { EnsureFigureEnded(); return mSink->AddLines(aLines, aCount); }
michael@0 63 STDMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *aSegments, UINT aCount)
michael@0 64 { EnsureFigureEnded(); return mSink->AddBeziers(aSegments, aCount); }
michael@0 65 STDMETHOD(Close)()
michael@0 66 { /* Should never be called! */ return S_OK; }
michael@0 67 STDMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT aFlags)
michael@0 68 { return mSink->SetSegmentFlags(aFlags); }
michael@0 69
michael@0 70 // This function is special - it's the reason this class exists.
michael@0 71 // It needs to intercept the very last endfigure. So that a user can
michael@0 72 // continue writing to this sink as if they never stopped.
michael@0 73 STDMETHOD_(void, EndFigure)(D2D1_FIGURE_END aEnd)
michael@0 74 {
michael@0 75 if (aEnd == D2D1_FIGURE_END_CLOSED) {
michael@0 76 return mSink->EndFigure(aEnd);
michael@0 77 } else {
michael@0 78 mNeedsFigureEnded = true;
michael@0 79 }
michael@0 80 }
michael@0 81 private:
michael@0 82 void EnsureFigureEnded()
michael@0 83 {
michael@0 84 if (mNeedsFigureEnded) {
michael@0 85 mSink->EndFigure(D2D1_FIGURE_END_OPEN);
michael@0 86 mNeedsFigureEnded = false;
michael@0 87 }
michael@0 88 }
michael@0 89
michael@0 90 ID2D1SimplifiedGeometrySink *mSink;
michael@0 91 bool mNeedsFigureEnded;
michael@0 92 };
michael@0 93
michael@0 94 class StreamingGeometrySink : public ID2D1SimplifiedGeometrySink
michael@0 95 {
michael@0 96 public:
michael@0 97 StreamingGeometrySink(PathSink *aSink)
michael@0 98 : mSink(aSink)
michael@0 99 {
michael@0 100 }
michael@0 101
michael@0 102 HRESULT STDMETHODCALLTYPE QueryInterface(const IID &aIID, void **aPtr)
michael@0 103 {
michael@0 104 if (!aPtr) {
michael@0 105 return E_POINTER;
michael@0 106 }
michael@0 107
michael@0 108 if (aIID == IID_IUnknown) {
michael@0 109 *aPtr = static_cast<IUnknown*>(this);
michael@0 110 return S_OK;
michael@0 111 } else if (aIID == IID_ID2D1SimplifiedGeometrySink) {
michael@0 112 *aPtr = static_cast<ID2D1SimplifiedGeometrySink*>(this);
michael@0 113 return S_OK;
michael@0 114 }
michael@0 115
michael@0 116 return E_NOINTERFACE;
michael@0 117 }
michael@0 118
michael@0 119 ULONG STDMETHODCALLTYPE AddRef()
michael@0 120 {
michael@0 121 return 1;
michael@0 122 }
michael@0 123
michael@0 124 ULONG STDMETHODCALLTYPE Release()
michael@0 125 {
michael@0 126 return 1;
michael@0 127 }
michael@0 128
michael@0 129 // We ignore SetFillMode, this depends on the destination sink.
michael@0 130 STDMETHOD_(void, SetFillMode)(D2D1_FILL_MODE aMode)
michael@0 131 { return; }
michael@0 132 STDMETHOD_(void, BeginFigure)(D2D1_POINT_2F aPoint, D2D1_FIGURE_BEGIN aBegin)
michael@0 133 { mSink->MoveTo(ToPoint(aPoint)); }
michael@0 134 STDMETHOD_(void, AddLines)(const D2D1_POINT_2F *aLines, UINT aCount)
michael@0 135 { for (UINT i = 0; i < aCount; i++) { mSink->LineTo(ToPoint(aLines[i])); } }
michael@0 136 STDMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *aSegments, UINT aCount)
michael@0 137 {
michael@0 138 for (UINT i = 0; i < aCount; i++) {
michael@0 139 mSink->BezierTo(ToPoint(aSegments[i].point1), ToPoint(aSegments[i].point2), ToPoint(aSegments[i].point3));
michael@0 140 }
michael@0 141 }
michael@0 142 STDMETHOD(Close)()
michael@0 143 { /* Should never be called! */ return S_OK; }
michael@0 144 STDMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT aFlags)
michael@0 145 { /* Should never be called! */ }
michael@0 146
michael@0 147 STDMETHOD_(void, EndFigure)(D2D1_FIGURE_END aEnd)
michael@0 148 {
michael@0 149 if (aEnd == D2D1_FIGURE_END_CLOSED) {
michael@0 150 return mSink->Close();
michael@0 151 }
michael@0 152 }
michael@0 153 private:
michael@0 154
michael@0 155 PathSink *mSink;
michael@0 156 };
michael@0 157
michael@0 158 PathBuilderD2D::~PathBuilderD2D()
michael@0 159 {
michael@0 160 }
michael@0 161
michael@0 162 void
michael@0 163 PathBuilderD2D::MoveTo(const Point &aPoint)
michael@0 164 {
michael@0 165 if (mFigureActive) {
michael@0 166 mSink->EndFigure(D2D1_FIGURE_END_OPEN);
michael@0 167 mFigureActive = false;
michael@0 168 }
michael@0 169 EnsureActive(aPoint);
michael@0 170 mCurrentPoint = aPoint;
michael@0 171 }
michael@0 172
michael@0 173 void
michael@0 174 PathBuilderD2D::LineTo(const Point &aPoint)
michael@0 175 {
michael@0 176 EnsureActive(aPoint);
michael@0 177 mSink->AddLine(D2DPoint(aPoint));
michael@0 178
michael@0 179 mCurrentPoint = aPoint;
michael@0 180 }
michael@0 181
michael@0 182 void
michael@0 183 PathBuilderD2D::BezierTo(const Point &aCP1,
michael@0 184 const Point &aCP2,
michael@0 185 const Point &aCP3)
michael@0 186 {
michael@0 187 EnsureActive(aCP1);
michael@0 188 mSink->AddBezier(D2D1::BezierSegment(D2DPoint(aCP1),
michael@0 189 D2DPoint(aCP2),
michael@0 190 D2DPoint(aCP3)));
michael@0 191
michael@0 192 mCurrentPoint = aCP3;
michael@0 193 }
michael@0 194
michael@0 195 void
michael@0 196 PathBuilderD2D::QuadraticBezierTo(const Point &aCP1,
michael@0 197 const Point &aCP2)
michael@0 198 {
michael@0 199 EnsureActive(aCP1);
michael@0 200 mSink->AddQuadraticBezier(D2D1::QuadraticBezierSegment(D2DPoint(aCP1),
michael@0 201 D2DPoint(aCP2)));
michael@0 202
michael@0 203 mCurrentPoint = aCP2;
michael@0 204 }
michael@0 205
michael@0 206 void
michael@0 207 PathBuilderD2D::Close()
michael@0 208 {
michael@0 209 if (mFigureActive) {
michael@0 210 mSink->EndFigure(D2D1_FIGURE_END_CLOSED);
michael@0 211
michael@0 212 mFigureActive = false;
michael@0 213
michael@0 214 EnsureActive(mBeginPoint);
michael@0 215 }
michael@0 216 }
michael@0 217
michael@0 218 void
michael@0 219 PathBuilderD2D::Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
michael@0 220 Float aEndAngle, bool aAntiClockwise)
michael@0 221 {
michael@0 222 if (aAntiClockwise && aStartAngle < aEndAngle) {
michael@0 223 // D2D does things a little differently, and draws the arc by specifying an
michael@0 224 // beginning and an end point. This means the circle will be the wrong way
michael@0 225 // around if the start angle is smaller than the end angle. It might seem
michael@0 226 // tempting to invert aAntiClockwise but that would change the sweeping
michael@0 227 // direction of the arc to instead we exchange start/begin.
michael@0 228 Float oldStart = aStartAngle;
michael@0 229 aStartAngle = aEndAngle;
michael@0 230 aEndAngle = oldStart;
michael@0 231 }
michael@0 232
michael@0 233 // XXX - Workaround for now, D2D does not appear to do the desired thing when
michael@0 234 // the angle sweeps a complete circle.
michael@0 235 if (aEndAngle - aStartAngle >= 2 * M_PI) {
michael@0 236 aEndAngle = Float(aStartAngle + M_PI * 1.9999);
michael@0 237 } else if (aStartAngle - aEndAngle >= 2 * M_PI) {
michael@0 238 aStartAngle = Float(aEndAngle + M_PI * 1.9999);
michael@0 239 }
michael@0 240
michael@0 241 Point startPoint;
michael@0 242 startPoint.x = aOrigin.x + aRadius * cos(aStartAngle);
michael@0 243 startPoint.y = aOrigin.y + aRadius * sin(aStartAngle);
michael@0 244
michael@0 245 if (!mFigureActive) {
michael@0 246 EnsureActive(startPoint);
michael@0 247 } else {
michael@0 248 mSink->AddLine(D2DPoint(startPoint));
michael@0 249 }
michael@0 250
michael@0 251 Point endPoint;
michael@0 252 endPoint.x = aOrigin.x + aRadius * cos(aEndAngle);
michael@0 253 endPoint.y = aOrigin.y + aRadius * sin(aEndAngle);
michael@0 254
michael@0 255 D2D1_ARC_SIZE arcSize = D2D1_ARC_SIZE_SMALL;
michael@0 256
michael@0 257 if (aAntiClockwise) {
michael@0 258 if (aStartAngle - aEndAngle > M_PI) {
michael@0 259 arcSize = D2D1_ARC_SIZE_LARGE;
michael@0 260 }
michael@0 261 } else {
michael@0 262 if (aEndAngle - aStartAngle > M_PI) {
michael@0 263 arcSize = D2D1_ARC_SIZE_LARGE;
michael@0 264 }
michael@0 265 }
michael@0 266
michael@0 267 mSink->AddArc(D2D1::ArcSegment(D2DPoint(endPoint),
michael@0 268 D2D1::SizeF(aRadius, aRadius),
michael@0 269 0.0f,
michael@0 270 aAntiClockwise ? D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE :
michael@0 271 D2D1_SWEEP_DIRECTION_CLOCKWISE,
michael@0 272 arcSize));
michael@0 273
michael@0 274 mCurrentPoint = endPoint;
michael@0 275 }
michael@0 276
michael@0 277 Point
michael@0 278 PathBuilderD2D::CurrentPoint() const
michael@0 279 {
michael@0 280 return mCurrentPoint;
michael@0 281 }
michael@0 282
michael@0 283 void
michael@0 284 PathBuilderD2D::EnsureActive(const Point &aPoint)
michael@0 285 {
michael@0 286 if (!mFigureActive) {
michael@0 287 mSink->BeginFigure(D2DPoint(aPoint), D2D1_FIGURE_BEGIN_FILLED);
michael@0 288 mBeginPoint = aPoint;
michael@0 289 mFigureActive = true;
michael@0 290 }
michael@0 291 }
michael@0 292
michael@0 293 TemporaryRef<Path>
michael@0 294 PathBuilderD2D::Finish()
michael@0 295 {
michael@0 296 if (mFigureActive) {
michael@0 297 mSink->EndFigure(D2D1_FIGURE_END_OPEN);
michael@0 298 }
michael@0 299
michael@0 300 HRESULT hr = mSink->Close();
michael@0 301 if (FAILED(hr)) {
michael@0 302 gfxDebug() << "Failed to close PathSink. Code: " << hr;
michael@0 303 return nullptr;
michael@0 304 }
michael@0 305
michael@0 306 return new PathD2D(mGeometry, mFigureActive, mCurrentPoint, mFillRule);
michael@0 307 }
michael@0 308
michael@0 309 TemporaryRef<PathBuilder>
michael@0 310 PathD2D::CopyToBuilder(FillRule aFillRule) const
michael@0 311 {
michael@0 312 return TransformedCopyToBuilder(Matrix(), aFillRule);
michael@0 313 }
michael@0 314
michael@0 315 TemporaryRef<PathBuilder>
michael@0 316 PathD2D::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
michael@0 317 {
michael@0 318 RefPtr<ID2D1PathGeometry> path;
michael@0 319 HRESULT hr = DrawTargetD2D::factory()->CreatePathGeometry(byRef(path));
michael@0 320
michael@0 321 if (FAILED(hr)) {
michael@0 322 gfxWarning() << "Failed to create PathGeometry. Code: " << hr;
michael@0 323 return nullptr;
michael@0 324 }
michael@0 325
michael@0 326 RefPtr<ID2D1GeometrySink> sink;
michael@0 327 hr = path->Open(byRef(sink));
michael@0 328 if (FAILED(hr)) {
michael@0 329 gfxWarning() << "Failed to open Geometry for writing. Code: " << hr;
michael@0 330 return nullptr;
michael@0 331 }
michael@0 332
michael@0 333 if (aFillRule == FillRule::FILL_WINDING) {
michael@0 334 sink->SetFillMode(D2D1_FILL_MODE_WINDING);
michael@0 335 }
michael@0 336
michael@0 337 if (mEndedActive) {
michael@0 338 OpeningGeometrySink wrapSink(sink);
michael@0 339 mGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
michael@0 340 D2DMatrix(aTransform),
michael@0 341 &wrapSink);
michael@0 342 } else {
michael@0 343 mGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
michael@0 344 D2DMatrix(aTransform),
michael@0 345 sink);
michael@0 346 }
michael@0 347
michael@0 348 RefPtr<PathBuilderD2D> pathBuilder = new PathBuilderD2D(sink, path, aFillRule);
michael@0 349
michael@0 350 pathBuilder->mCurrentPoint = aTransform * mEndPoint;
michael@0 351
michael@0 352 if (mEndedActive) {
michael@0 353 pathBuilder->mFigureActive = true;
michael@0 354 }
michael@0 355
michael@0 356 return pathBuilder;
michael@0 357 }
michael@0 358
michael@0 359 void
michael@0 360 PathD2D::StreamToSink(PathSink *aSink) const
michael@0 361 {
michael@0 362 HRESULT hr;
michael@0 363
michael@0 364 StreamingGeometrySink sink(aSink);
michael@0 365
michael@0 366 hr = mGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
michael@0 367 D2D1::IdentityMatrix(), &sink);
michael@0 368
michael@0 369 if (FAILED(hr)) {
michael@0 370 gfxWarning() << "Failed to stream D2D path to sink. Code: " << hr;
michael@0 371 return;
michael@0 372 }
michael@0 373 }
michael@0 374
michael@0 375 bool
michael@0 376 PathD2D::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
michael@0 377 {
michael@0 378 BOOL result;
michael@0 379
michael@0 380 HRESULT hr = mGeometry->FillContainsPoint(D2DPoint(aPoint), D2DMatrix(aTransform), 0.001f, &result);
michael@0 381
michael@0 382 if (FAILED(hr)) {
michael@0 383 // Log
michael@0 384 return false;
michael@0 385 }
michael@0 386
michael@0 387 return !!result;
michael@0 388 }
michael@0 389
michael@0 390 bool
michael@0 391 PathD2D::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
michael@0 392 const Point &aPoint,
michael@0 393 const Matrix &aTransform) const
michael@0 394 {
michael@0 395 BOOL result;
michael@0 396
michael@0 397 RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
michael@0 398 HRESULT hr = mGeometry->StrokeContainsPoint(D2DPoint(aPoint),
michael@0 399 aStrokeOptions.mLineWidth,
michael@0 400 strokeStyle,
michael@0 401 D2DMatrix(aTransform),
michael@0 402 &result);
michael@0 403
michael@0 404 if (FAILED(hr)) {
michael@0 405 // Log
michael@0 406 return false;
michael@0 407 }
michael@0 408
michael@0 409 return !!result;
michael@0 410 }
michael@0 411
michael@0 412 Rect
michael@0 413 PathD2D::GetBounds(const Matrix &aTransform) const
michael@0 414 {
michael@0 415 D2D1_RECT_F d2dBounds;
michael@0 416
michael@0 417 HRESULT hr = mGeometry->GetBounds(D2DMatrix(aTransform), &d2dBounds);
michael@0 418
michael@0 419 Rect bounds = ToRect(d2dBounds);
michael@0 420 if (FAILED(hr) || !bounds.IsFinite()) {
michael@0 421 gfxWarning() << "Failed to get stroked bounds for path. Code: " << hr;
michael@0 422 return Rect();
michael@0 423 }
michael@0 424
michael@0 425 return bounds;
michael@0 426 }
michael@0 427
michael@0 428 Rect
michael@0 429 PathD2D::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
michael@0 430 const Matrix &aTransform) const
michael@0 431 {
michael@0 432 D2D1_RECT_F d2dBounds;
michael@0 433
michael@0 434 RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
michael@0 435 HRESULT hr =
michael@0 436 mGeometry->GetWidenedBounds(aStrokeOptions.mLineWidth, strokeStyle,
michael@0 437 D2DMatrix(aTransform), &d2dBounds);
michael@0 438
michael@0 439 Rect bounds = ToRect(d2dBounds);
michael@0 440 if (FAILED(hr) || !bounds.IsFinite()) {
michael@0 441 gfxWarning() << "Failed to get stroked bounds for path. Code: " << hr;
michael@0 442 return Rect();
michael@0 443 }
michael@0 444
michael@0 445 return bounds;
michael@0 446 }
michael@0 447
michael@0 448 }
michael@0 449 }

mercurial