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
19SkDWriteGeometrySink::SkDWriteGeometrySink(SkPath* path)
20 : fRefCount{1}, fPath{path}, fStarted{false}, fCurrent{0,0} {}
21
22SkDWriteGeometrySink::~SkDWriteGeometrySink() { }
23
24SK_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
38SK_STDMETHODIMP_(ULONG) SkDWriteGeometrySink::AddRef(void) {
39 return static_cast<ULONG>(InterlockedIncrement(&fRefCount));
40}
41
42SK_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
50SK_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
64SK_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
70SK_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
78SK_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
87static bool approximately_equal(float a, float b) {
88 const SkFloatingPoint<float, 10> lhs(a), rhs(b);
89 return lhs.AlmostEquals(rhs);
90}
91
92typedef struct {
93 float x;
94 float y;
95} Cubic[4], Point;
96
97static 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
116SK_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
140SK_STDMETHODIMP_(void) SkDWriteGeometrySink::EndFigure(D2D1_FIGURE_END figureEnd) {
141 if (fStarted) {
142 fPath->close();
143 }
144}
145
146SK_STDMETHODIMP SkDWriteGeometrySink::Close() {
147 return S_OK;
148}
149
150HRESULT 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