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 | |
19 | SkDWriteGeometrySink::SkDWriteGeometrySink(SkPath* path) |
20 | : fRefCount{1}, fPath{path}, fStarted{false}, fCurrent{0,0} {} |
21 | |
22 | SkDWriteGeometrySink::~SkDWriteGeometrySink() { } |
23 | |
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 | |
38 | SK_STDMETHODIMP_(ULONG) SkDWriteGeometrySink::AddRef(void) { |
39 | return static_cast<ULONG>(InterlockedIncrement(&fRefCount)); |
40 | } |
41 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
140 | SK_STDMETHODIMP_(void) SkDWriteGeometrySink::EndFigure(D2D1_FIGURE_END figureEnd) { |
141 | if (fStarted) { |
142 | fPath->close(); |
143 | } |
144 | } |
145 | |
146 | SK_STDMETHODIMP SkDWriteGeometrySink::Close() { |
147 | return S_OK; |
148 | } |
149 | |
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 | |