• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkTypes.h"
9 #if defined(SK_BUILD_FOR_WIN)
10 
11 #include "include/core/SkPath.h"
12 #include "src/utils/SkFloatUtils.h"
13 #include "src/utils/win/SkDWriteGeometrySink.h"
14 #include "src/utils/win/SkObjBase.h"
15 
16 #include <dwrite.h>
17 #include <d2d1.h>
18 
SkDWriteGeometrySink(SkPath * path)19 SkDWriteGeometrySink::SkDWriteGeometrySink(SkPath* path)
20     : fRefCount{1}, fPath{path}, fStarted{false}, fCurrent{0,0} {}
21 
~SkDWriteGeometrySink()22 SkDWriteGeometrySink::~SkDWriteGeometrySink() { }
23 
QueryInterface(REFIID iid,void ** object)24 SK_STDMETHODIMP SkDWriteGeometrySink::QueryInterface(REFIID iid, void **object) {
25     if (nullptr == object) {
26         return E_INVALIDARG;
27     }
28     if (iid == __uuidof(IUnknown) || iid == __uuidof(IDWriteGeometrySink)) {
29         *object = static_cast<IDWriteGeometrySink*>(this);
30         this->AddRef();
31         return S_OK;
32     } else {
33         *object = nullptr;
34         return E_NOINTERFACE;
35     }
36 }
37 
SK_STDMETHODIMP_(ULONG)38 SK_STDMETHODIMP_(ULONG) SkDWriteGeometrySink::AddRef(void) {
39     return static_cast<ULONG>(InterlockedIncrement(&fRefCount));
40 }
41 
SK_STDMETHODIMP_(ULONG)42 SK_STDMETHODIMP_(ULONG) SkDWriteGeometrySink::Release(void) {
43     ULONG res = static_cast<ULONG>(InterlockedDecrement(&fRefCount));
44     if (0 == res) {
45         delete this;
46     }
47     return res;
48 }
49 
SK_STDMETHODIMP_(void)50 SK_STDMETHODIMP_(void) SkDWriteGeometrySink::SetFillMode(D2D1_FILL_MODE fillMode) {
51     switch (fillMode) {
52     case D2D1_FILL_MODE_ALTERNATE:
53         fPath->setFillType(SkPathFillType::kEvenOdd);
54         break;
55     case D2D1_FILL_MODE_WINDING:
56         fPath->setFillType(SkPathFillType::kWinding);
57         break;
58     default:
59         SkDEBUGFAIL("Unknown D2D1_FILL_MODE.");
60         break;
61     }
62 }
63 
SK_STDMETHODIMP_(void)64 SK_STDMETHODIMP_(void) SkDWriteGeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT vertexFlags) {
65     if (vertexFlags == D2D1_PATH_SEGMENT_NONE || vertexFlags == D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN) {
66         SkDEBUGFAIL("Invalid D2D1_PATH_SEGMENT value.");
67     }
68 }
69 
SK_STDMETHODIMP_(void)70 SK_STDMETHODIMP_(void) SkDWriteGeometrySink::BeginFigure(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) {
71     if (figureBegin == D2D1_FIGURE_BEGIN_HOLLOW) {
72         SkDEBUGFAIL("Invalid D2D1_FIGURE_BEGIN value.");
73     }
74     fStarted = false;
75     fCurrent = startPoint;
76 }
77 
SK_STDMETHODIMP_(void)78 SK_STDMETHODIMP_(void) SkDWriteGeometrySink::AddLines(const D2D1_POINT_2F *points, UINT pointsCount) {
79     for (const D2D1_POINT_2F *end = &points[pointsCount]; points < end; ++points) {
80         if (this->currentIsNot(*points)) {
81             this->goingTo(*points);
82             fPath->lineTo(points->x, points->y);
83         }
84     }
85 }
86 
approximately_equal(float a,float b)87 static bool approximately_equal(float a, float b) {
88     const SkFloatingPoint<float, 10> lhs(a), rhs(b);
89     return lhs.AlmostEquals(rhs);
90 }
91 
92 typedef struct {
93     float x;
94     float y;
95 } Cubic[4], Point;
96 
check_quadratic(const Cubic & cubic,Point & quadraticP1)97 static bool check_quadratic(const Cubic& cubic, Point& quadraticP1) {
98     float dx10 = cubic[1].x - cubic[0].x;
99     float dx23 = cubic[2].x - cubic[3].x;
100     float midX = cubic[0].x + dx10 * 3 / 2;
101     //NOTE: !approximately_equal(midX - cubic[3].x, dx23 * 3 / 2)
102     //does not work as subnormals get in between the left side and 0.
103     if (!approximately_equal(midX, (dx23 * 3 / 2) + cubic[3].x)) {
104         return false;
105     }
106     float dy10 = cubic[1].y - cubic[0].y;
107     float dy23 = cubic[2].y - cubic[3].y;
108     float midY = cubic[0].y + dy10 * 3 / 2;
109     if (!approximately_equal(midY, (dy23 * 3 / 2) + cubic[3].y)) {
110         return false;
111     }
112     quadraticP1 = {midX, midY};
113     return true;
114 }
115 
SK_STDMETHODIMP_(void)116 SK_STDMETHODIMP_(void) SkDWriteGeometrySink::AddBeziers(const D2D1_BEZIER_SEGMENT *beziers, UINT beziersCount) {
117     for (const D2D1_BEZIER_SEGMENT *end = &beziers[beziersCount]; beziers < end; ++beziers) {
118         if (this->currentIsNot(beziers->point1) ||
119             this->currentIsNot(beziers->point2) ||
120             this->currentIsNot(beziers->point3))
121         {
122             Cubic cubic = { {        fCurrent.x,        fCurrent.y },
123                             { beziers->point1.x, beziers->point1.y },
124                             { beziers->point2.x, beziers->point2.y },
125                             { beziers->point3.x, beziers->point3.y }, };
126             this->goingTo(beziers->point3);
127             Point quadraticP1;
128             if (check_quadratic(cubic, quadraticP1)) {
129                 fPath->quadTo(    quadraticP1.x,     quadraticP1.y,
130                               beziers->point3.x, beziers->point3.y);
131             } else {
132                 fPath->cubicTo(beziers->point1.x, beziers->point1.y,
133                                beziers->point2.x, beziers->point2.y,
134                                beziers->point3.x, beziers->point3.y);
135             }
136         }
137     }
138 }
139 
SK_STDMETHODIMP_(void)140 SK_STDMETHODIMP_(void) SkDWriteGeometrySink::EndFigure(D2D1_FIGURE_END figureEnd) {
141     if (fStarted) {
142         fPath->close();
143     }
144 }
145 
Close()146 SK_STDMETHODIMP SkDWriteGeometrySink::Close() {
147     return S_OK;
148 }
149 
Create(SkPath * path,IDWriteGeometrySink ** geometryToPath)150 HRESULT SkDWriteGeometrySink::Create(SkPath* path, IDWriteGeometrySink** geometryToPath) {
151     *geometryToPath = new SkDWriteGeometrySink(path);
152     return S_OK;
153 }
154 
155 #endif//defined(SK_BUILD_FOR_WIN)
156