|
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 "SkTypes.h" |
|
9 |
|
10 #include "SkDWriteGeometrySink.h" |
|
11 #include "SkFloatUtils.h" |
|
12 #include "SkPath.h" |
|
13 |
|
14 #include <dwrite.h> |
|
15 #include <d2d1.h> |
|
16 |
|
17 SkDWriteGeometrySink::SkDWriteGeometrySink(SkPath* path) : fRefCount(1), fPath(path) { } |
|
18 |
|
19 SkDWriteGeometrySink::~SkDWriteGeometrySink() { } |
|
20 |
|
21 HRESULT STDMETHODCALLTYPE SkDWriteGeometrySink::QueryInterface(REFIID iid, void **object) { |
|
22 if (NULL == object) { |
|
23 return E_INVALIDARG; |
|
24 } |
|
25 if (iid == __uuidof(IUnknown) || iid == __uuidof(IDWriteGeometrySink)) { |
|
26 *object = static_cast<IDWriteGeometrySink*>(this); |
|
27 this->AddRef(); |
|
28 return S_OK; |
|
29 } else { |
|
30 *object = NULL; |
|
31 return E_NOINTERFACE; |
|
32 } |
|
33 } |
|
34 |
|
35 ULONG STDMETHODCALLTYPE SkDWriteGeometrySink::AddRef(void) { |
|
36 return static_cast<ULONG>(InterlockedIncrement(&fRefCount)); |
|
37 } |
|
38 |
|
39 ULONG STDMETHODCALLTYPE SkDWriteGeometrySink::Release(void) { |
|
40 ULONG res = static_cast<ULONG>(InterlockedDecrement(&fRefCount)); |
|
41 if (0 == res) { |
|
42 delete this; |
|
43 } |
|
44 return res; |
|
45 } |
|
46 |
|
47 void STDMETHODCALLTYPE SkDWriteGeometrySink::SetFillMode(D2D1_FILL_MODE fillMode) { |
|
48 switch (fillMode) { |
|
49 case D2D1_FILL_MODE_ALTERNATE: |
|
50 fPath->setFillType(SkPath::kEvenOdd_FillType); |
|
51 break; |
|
52 case D2D1_FILL_MODE_WINDING: |
|
53 fPath->setFillType(SkPath::kWinding_FillType); |
|
54 break; |
|
55 default: |
|
56 SkDEBUGFAIL("Unknown D2D1_FILL_MODE."); |
|
57 break; |
|
58 } |
|
59 } |
|
60 |
|
61 void STDMETHODCALLTYPE SkDWriteGeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT vertexFlags) { |
|
62 if (vertexFlags == D2D1_PATH_SEGMENT_NONE || vertexFlags == D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN) { |
|
63 SkDEBUGFAIL("Invalid D2D1_PATH_SEGMENT value."); |
|
64 } |
|
65 } |
|
66 |
|
67 void STDMETHODCALLTYPE SkDWriteGeometrySink::BeginFigure(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) { |
|
68 fPath->moveTo(startPoint.x, startPoint.y); |
|
69 if (figureBegin == D2D1_FIGURE_BEGIN_HOLLOW) { |
|
70 SkDEBUGFAIL("Invalid D2D1_FIGURE_BEGIN value."); |
|
71 } |
|
72 } |
|
73 |
|
74 void STDMETHODCALLTYPE SkDWriteGeometrySink::AddLines(const D2D1_POINT_2F *points, UINT pointsCount) { |
|
75 for (const D2D1_POINT_2F *end = &points[pointsCount]; points < end; ++points) { |
|
76 fPath->lineTo(points->x, points->y); |
|
77 } |
|
78 } |
|
79 |
|
80 static bool approximately_equal(float a, float b) { |
|
81 const SkFloatingPoint<float, 10> lhs(a), rhs(b); |
|
82 return lhs.AlmostEquals(rhs); |
|
83 } |
|
84 |
|
85 typedef struct { |
|
86 float x; |
|
87 float y; |
|
88 } Cubic[4], Quadratic[3]; |
|
89 |
|
90 static bool check_quadratic(const Cubic& cubic, Quadratic& reduction) { |
|
91 float dx10 = cubic[1].x - cubic[0].x; |
|
92 float dx23 = cubic[2].x - cubic[3].x; |
|
93 float midX = cubic[0].x + dx10 * 3 / 2; |
|
94 //NOTE: !approximately_equal(midX - cubic[3].x, dx23 * 3 / 2) |
|
95 //does not work as subnormals get in between the left side and 0. |
|
96 if (!approximately_equal(midX, (dx23 * 3 / 2) + cubic[3].x)) { |
|
97 return false; |
|
98 } |
|
99 float dy10 = cubic[1].y - cubic[0].y; |
|
100 float dy23 = cubic[2].y - cubic[3].y; |
|
101 float midY = cubic[0].y + dy10 * 3 / 2; |
|
102 if (!approximately_equal(midY, (dy23 * 3 / 2) + cubic[3].y)) { |
|
103 return false; |
|
104 } |
|
105 reduction[0] = cubic[0]; |
|
106 reduction[1].x = midX; |
|
107 reduction[1].y = midY; |
|
108 reduction[2] = cubic[3]; |
|
109 return true; |
|
110 } |
|
111 |
|
112 void STDMETHODCALLTYPE SkDWriteGeometrySink::AddBeziers(const D2D1_BEZIER_SEGMENT *beziers, UINT beziersCount) { |
|
113 SkPoint lastPt; |
|
114 fPath->getLastPt(&lastPt); |
|
115 D2D1_POINT_2F prevPt = { SkScalarToFloat(lastPt.fX), SkScalarToFloat(lastPt.fY) }; |
|
116 |
|
117 for (const D2D1_BEZIER_SEGMENT *end = &beziers[beziersCount]; beziers < end; ++beziers) { |
|
118 Cubic cubic = { { prevPt.x, prevPt.y }, |
|
119 { beziers->point1.x, beziers->point1.y }, |
|
120 { beziers->point2.x, beziers->point2.y }, |
|
121 { beziers->point3.x, beziers->point3.y }, }; |
|
122 Quadratic quadratic; |
|
123 if (check_quadratic(cubic, quadratic)) { |
|
124 fPath->quadTo(quadratic[1].x, quadratic[1].y, |
|
125 quadratic[2].x, quadratic[2].y); |
|
126 } else { |
|
127 fPath->cubicTo(beziers->point1.x, beziers->point1.y, |
|
128 beziers->point2.x, beziers->point2.y, |
|
129 beziers->point3.x, beziers->point3.y); |
|
130 } |
|
131 prevPt = beziers->point3; |
|
132 } |
|
133 } |
|
134 |
|
135 void STDMETHODCALLTYPE SkDWriteGeometrySink::EndFigure(D2D1_FIGURE_END figureEnd) { |
|
136 fPath->close(); |
|
137 } |
|
138 |
|
139 HRESULT SkDWriteGeometrySink::Close() { |
|
140 return S_OK; |
|
141 } |
|
142 |
|
143 HRESULT SkDWriteGeometrySink::Create(SkPath* path, IDWriteGeometrySink** geometryToPath) { |
|
144 *geometryToPath = new SkDWriteGeometrySink(path); |
|
145 return S_OK; |
|
146 } |