blob: cd5ae7cef9666c1b84f84e800b8752233eb37e39 [file] [log] [blame]
bungeman@google.come8f05922012-08-16 16:13:40 +00001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkTypes.h"
Mike Klein8f11d4d2018-01-24 12:42:55 -05009#if defined(SK_BUILD_FOR_WIN)
bungeman@google.come8f05922012-08-16 16:13:40 +000010
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkPath.h"
12#include "src/utils/SkFloatUtils.h"
13#include "src/utils/win/SkDWriteGeometrySink.h"
Ben Wagner6cb6a072019-08-12 18:30:27 -040014#include "src/utils/win/SkObjBase.h"
bungeman@google.come8f05922012-08-16 16:13:40 +000015
16#include <dwrite.h>
17#include <d2d1.h>
18
Ben Wagnerd38f00a2020-01-27 17:43:41 -050019SkDWriteGeometrySink::SkDWriteGeometrySink(SkPath* path)
20 : fRefCount{1}, fPath{path}, fStarted{false}, fCurrent{0,0} {}
bungeman@google.come8f05922012-08-16 16:13:40 +000021
22SkDWriteGeometrySink::~SkDWriteGeometrySink() { }
23
Ben Wagner6cb6a072019-08-12 18:30:27 -040024SK_STDMETHODIMP SkDWriteGeometrySink::QueryInterface(REFIID iid, void **object) {
halcanary96fcdcc2015-08-27 07:41:13 -070025 if (nullptr == object) {
bungeman@google.come8f05922012-08-16 16:13:40 +000026 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 {
halcanary96fcdcc2015-08-27 07:41:13 -070033 *object = nullptr;
rmistry@google.comd6176b02012-08-23 18:14:13 +000034 return E_NOINTERFACE;
bungeman@google.come8f05922012-08-16 16:13:40 +000035 }
36}
37
Ben Wagner6cb6a072019-08-12 18:30:27 -040038SK_STDMETHODIMP_(ULONG) SkDWriteGeometrySink::AddRef(void) {
bungeman@google.come8f05922012-08-16 16:13:40 +000039 return static_cast<ULONG>(InterlockedIncrement(&fRefCount));
40}
41
Ben Wagner6cb6a072019-08-12 18:30:27 -040042SK_STDMETHODIMP_(ULONG) SkDWriteGeometrySink::Release(void) {
bungeman@google.come8f05922012-08-16 16:13:40 +000043 ULONG res = static_cast<ULONG>(InterlockedDecrement(&fRefCount));
44 if (0 == res) {
45 delete this;
46 }
47 return res;
48}
49
Ben Wagner6cb6a072019-08-12 18:30:27 -040050SK_STDMETHODIMP_(void) SkDWriteGeometrySink::SetFillMode(D2D1_FILL_MODE fillMode) {
bungeman@google.come8f05922012-08-16 16:13:40 +000051 switch (fillMode) {
52 case D2D1_FILL_MODE_ALTERNATE:
Mike Reed7d34dc72019-11-26 12:17:17 -050053 fPath->setFillType(SkPathFillType::kEvenOdd);
bungeman@google.come8f05922012-08-16 16:13:40 +000054 break;
55 case D2D1_FILL_MODE_WINDING:
Mike Reed7d34dc72019-11-26 12:17:17 -050056 fPath->setFillType(SkPathFillType::kWinding);
bungeman@google.come8f05922012-08-16 16:13:40 +000057 break;
58 default:
mtklein@google.com330313a2013-08-22 15:37:26 +000059 SkDEBUGFAIL("Unknown D2D1_FILL_MODE.");
bungeman@google.come8f05922012-08-16 16:13:40 +000060 break;
61 }
62}
63
Ben Wagner6cb6a072019-08-12 18:30:27 -040064SK_STDMETHODIMP_(void) SkDWriteGeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT vertexFlags) {
bungeman@google.come8f05922012-08-16 16:13:40 +000065 if (vertexFlags == D2D1_PATH_SEGMENT_NONE || vertexFlags == D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN) {
mtklein@google.com330313a2013-08-22 15:37:26 +000066 SkDEBUGFAIL("Invalid D2D1_PATH_SEGMENT value.");
bungeman@google.come8f05922012-08-16 16:13:40 +000067 }
68}
69
Ben Wagner6cb6a072019-08-12 18:30:27 -040070SK_STDMETHODIMP_(void) SkDWriteGeometrySink::BeginFigure(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) {
bungeman@google.come8f05922012-08-16 16:13:40 +000071 if (figureBegin == D2D1_FIGURE_BEGIN_HOLLOW) {
mtklein@google.com330313a2013-08-22 15:37:26 +000072 SkDEBUGFAIL("Invalid D2D1_FIGURE_BEGIN value.");
bungeman@google.come8f05922012-08-16 16:13:40 +000073 }
Ben Wagnerd38f00a2020-01-27 17:43:41 -050074 fStarted = false;
75 fCurrent = startPoint;
bungeman@google.come8f05922012-08-16 16:13:40 +000076}
77
Ben Wagner6cb6a072019-08-12 18:30:27 -040078SK_STDMETHODIMP_(void) SkDWriteGeometrySink::AddLines(const D2D1_POINT_2F *points, UINT pointsCount) {
bungeman@google.come8f05922012-08-16 16:13:40 +000079 for (const D2D1_POINT_2F *end = &points[pointsCount]; points < end; ++points) {
Ben Wagnerd38f00a2020-01-27 17:43:41 -050080 if (this->currentIsNot(*points)) {
81 this->goingTo(*points);
82 fPath->lineTo(points->x, points->y);
83 }
bungeman@google.come8f05922012-08-16 16:13:40 +000084 }
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;
Ben Wagnerd38f00a2020-01-27 17:43:41 -050095} Cubic[4], Point;
bungeman@google.come8f05922012-08-16 16:13:40 +000096
Ben Wagnerd38f00a2020-01-27 17:43:41 -050097static bool check_quadratic(const Cubic& cubic, Point& quadraticP1) {
bungeman@google.come8f05922012-08-16 16:13:40 +000098 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 }
Ben Wagnerd38f00a2020-01-27 17:43:41 -0500112 quadraticP1 = {midX, midY};
bungeman@google.come8f05922012-08-16 16:13:40 +0000113 return true;
114}
115
Ben Wagner6cb6a072019-08-12 18:30:27 -0400116SK_STDMETHODIMP_(void) SkDWriteGeometrySink::AddBeziers(const D2D1_BEZIER_SEGMENT *beziers, UINT beziersCount) {
bungeman@google.come8f05922012-08-16 16:13:40 +0000117 for (const D2D1_BEZIER_SEGMENT *end = &beziers[beziersCount]; beziers < end; ++beziers) {
Ben Wagnerd38f00a2020-01-27 17:43:41 -0500118 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 }
bungeman@google.come8f05922012-08-16 16:13:40 +0000136 }
bungeman@google.come8f05922012-08-16 16:13:40 +0000137 }
138}
139
Ben Wagner6cb6a072019-08-12 18:30:27 -0400140SK_STDMETHODIMP_(void) SkDWriteGeometrySink::EndFigure(D2D1_FIGURE_END figureEnd) {
Ben Wagnerd38f00a2020-01-27 17:43:41 -0500141 if (fStarted) {
142 fPath->close();
143 }
bungeman@google.come8f05922012-08-16 16:13:40 +0000144}
145
Ben Wagner6cb6a072019-08-12 18:30:27 -0400146SK_STDMETHODIMP SkDWriteGeometrySink::Close() {
bungeman@google.come8f05922012-08-16 16:13:40 +0000147 return S_OK;
148}
149
150HRESULT SkDWriteGeometrySink::Create(SkPath* path, IDWriteGeometrySink** geometryToPath) {
151 *geometryToPath = new SkDWriteGeometrySink(path);
152 return S_OK;
153}
mtklein1ee76512015-11-02 10:20:27 -0800154
Mike Klein8f11d4d2018-01-24 12:42:55 -0500155#endif//defined(SK_BUILD_FOR_WIN)