1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2016 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/geometry/GrStyledShape.h"
8*c8dee2aaSAndroid Build Coastguard Worker
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkArc.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathEffect.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStrokeRec.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/SkIDChangeListener.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAlign.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkMalloc.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
19*c8dee2aaSAndroid Build Coastguard Worker
20*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
21*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
22*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
23*c8dee2aaSAndroid Build Coastguard Worker
24*c8dee2aaSAndroid Build Coastguard Worker
operator =(const GrStyledShape & that)25*c8dee2aaSAndroid Build Coastguard Worker GrStyledShape& GrStyledShape::operator=(const GrStyledShape& that) {
26*c8dee2aaSAndroid Build Coastguard Worker fShape = that.fShape;
27*c8dee2aaSAndroid Build Coastguard Worker fStyle = that.fStyle;
28*c8dee2aaSAndroid Build Coastguard Worker fGenID = that.fGenID;
29*c8dee2aaSAndroid Build Coastguard Worker fSimplified = that.fSimplified;
30*c8dee2aaSAndroid Build Coastguard Worker
31*c8dee2aaSAndroid Build Coastguard Worker fInheritedKey.reset(that.fInheritedKey.count());
32*c8dee2aaSAndroid Build Coastguard Worker sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
33*c8dee2aaSAndroid Build Coastguard Worker sizeof(uint32_t) * fInheritedKey.count());
34*c8dee2aaSAndroid Build Coastguard Worker if (that.fInheritedPathForListeners.isValid()) {
35*c8dee2aaSAndroid Build Coastguard Worker fInheritedPathForListeners.set(*that.fInheritedPathForListeners);
36*c8dee2aaSAndroid Build Coastguard Worker } else {
37*c8dee2aaSAndroid Build Coastguard Worker fInheritedPathForListeners.reset();
38*c8dee2aaSAndroid Build Coastguard Worker }
39*c8dee2aaSAndroid Build Coastguard Worker return *this;
40*c8dee2aaSAndroid Build Coastguard Worker }
41*c8dee2aaSAndroid Build Coastguard Worker
is_inverted(bool originalIsInverted,GrStyledShape::FillInversion inversion)42*c8dee2aaSAndroid Build Coastguard Worker static bool is_inverted(bool originalIsInverted, GrStyledShape::FillInversion inversion) {
43*c8dee2aaSAndroid Build Coastguard Worker switch (inversion) {
44*c8dee2aaSAndroid Build Coastguard Worker case GrStyledShape::FillInversion::kPreserve:
45*c8dee2aaSAndroid Build Coastguard Worker return originalIsInverted;
46*c8dee2aaSAndroid Build Coastguard Worker case GrStyledShape::FillInversion::kFlip:
47*c8dee2aaSAndroid Build Coastguard Worker return !originalIsInverted;
48*c8dee2aaSAndroid Build Coastguard Worker case GrStyledShape::FillInversion::kForceInverted:
49*c8dee2aaSAndroid Build Coastguard Worker return true;
50*c8dee2aaSAndroid Build Coastguard Worker case GrStyledShape::FillInversion::kForceNoninverted:
51*c8dee2aaSAndroid Build Coastguard Worker return false;
52*c8dee2aaSAndroid Build Coastguard Worker }
53*c8dee2aaSAndroid Build Coastguard Worker return false;
54*c8dee2aaSAndroid Build Coastguard Worker }
55*c8dee2aaSAndroid Build Coastguard Worker
MakeFilled(const GrStyledShape & original,FillInversion inversion)56*c8dee2aaSAndroid Build Coastguard Worker GrStyledShape GrStyledShape::MakeFilled(const GrStyledShape& original, FillInversion inversion) {
57*c8dee2aaSAndroid Build Coastguard Worker bool newIsInverted = is_inverted(original.fShape.inverted(), inversion);
58*c8dee2aaSAndroid Build Coastguard Worker if (original.style().isSimpleFill() && newIsInverted == original.fShape.inverted()) {
59*c8dee2aaSAndroid Build Coastguard Worker // By returning the original rather than falling through we can preserve any inherited style
60*c8dee2aaSAndroid Build Coastguard Worker // key. Otherwise, we wipe it out below since the style change invalidates it.
61*c8dee2aaSAndroid Build Coastguard Worker return original;
62*c8dee2aaSAndroid Build Coastguard Worker }
63*c8dee2aaSAndroid Build Coastguard Worker GrStyledShape result;
64*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(result.fStyle.isSimpleFill());
65*c8dee2aaSAndroid Build Coastguard Worker if (original.fInheritedPathForListeners.isValid()) {
66*c8dee2aaSAndroid Build Coastguard Worker result.fInheritedPathForListeners.set(*original.fInheritedPathForListeners);
67*c8dee2aaSAndroid Build Coastguard Worker }
68*c8dee2aaSAndroid Build Coastguard Worker
69*c8dee2aaSAndroid Build Coastguard Worker result.fShape = original.fShape;
70*c8dee2aaSAndroid Build Coastguard Worker result.fGenID = original.fGenID;
71*c8dee2aaSAndroid Build Coastguard Worker result.fShape.setInverted(newIsInverted);
72*c8dee2aaSAndroid Build Coastguard Worker
73*c8dee2aaSAndroid Build Coastguard Worker if (!original.style().isSimpleFill()) {
74*c8dee2aaSAndroid Build Coastguard Worker // Going from a non-filled style to fill may allow additional simplifications (e.g.
75*c8dee2aaSAndroid Build Coastguard Worker // closing an open rect that wasn't closed in the original shape because it had
76*c8dee2aaSAndroid Build Coastguard Worker // stroke style).
77*c8dee2aaSAndroid Build Coastguard Worker result.simplify();
78*c8dee2aaSAndroid Build Coastguard Worker // The above simplify() call only sets simplified to true if its geometry was changed,
79*c8dee2aaSAndroid Build Coastguard Worker // since it already sees its style as a simple fill. Since the original style was not a
80*c8dee2aaSAndroid Build Coastguard Worker // simple fill, MakeFilled always simplifies.
81*c8dee2aaSAndroid Build Coastguard Worker result.fSimplified = true;
82*c8dee2aaSAndroid Build Coastguard Worker }
83*c8dee2aaSAndroid Build Coastguard Worker
84*c8dee2aaSAndroid Build Coastguard Worker // Verify that lines/points were converted to empty by the style change
85*c8dee2aaSAndroid Build Coastguard Worker SkASSERT((!original.fShape.isLine() && !original.fShape.isPoint()) || result.fShape.isEmpty());
86*c8dee2aaSAndroid Build Coastguard Worker
87*c8dee2aaSAndroid Build Coastguard Worker // We don't copy the inherited key since it can contain path effect information that we just
88*c8dee2aaSAndroid Build Coastguard Worker // stripped.
89*c8dee2aaSAndroid Build Coastguard Worker return result;
90*c8dee2aaSAndroid Build Coastguard Worker }
91*c8dee2aaSAndroid Build Coastguard Worker
styledBounds() const92*c8dee2aaSAndroid Build Coastguard Worker SkRect GrStyledShape::styledBounds() const {
93*c8dee2aaSAndroid Build Coastguard Worker if (this->isEmpty() && !fStyle.hasNonDashPathEffect()) {
94*c8dee2aaSAndroid Build Coastguard Worker return SkRect::MakeEmpty();
95*c8dee2aaSAndroid Build Coastguard Worker }
96*c8dee2aaSAndroid Build Coastguard Worker
97*c8dee2aaSAndroid Build Coastguard Worker SkRect bounds;
98*c8dee2aaSAndroid Build Coastguard Worker fStyle.adjustBounds(&bounds, this->bounds());
99*c8dee2aaSAndroid Build Coastguard Worker return bounds;
100*c8dee2aaSAndroid Build Coastguard Worker }
101*c8dee2aaSAndroid Build Coastguard Worker
102*c8dee2aaSAndroid Build Coastguard Worker // If the path is small enough to be keyed from its data this returns key length, otherwise -1.
path_key_from_data_size(const SkPath & path)103*c8dee2aaSAndroid Build Coastguard Worker static int path_key_from_data_size(const SkPath& path) {
104*c8dee2aaSAndroid Build Coastguard Worker const int verbCnt = path.countVerbs();
105*c8dee2aaSAndroid Build Coastguard Worker if (verbCnt > GrStyledShape::kMaxKeyFromDataVerbCnt) {
106*c8dee2aaSAndroid Build Coastguard Worker return -1;
107*c8dee2aaSAndroid Build Coastguard Worker }
108*c8dee2aaSAndroid Build Coastguard Worker const int pointCnt = path.countPoints();
109*c8dee2aaSAndroid Build Coastguard Worker const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
110*c8dee2aaSAndroid Build Coastguard Worker
111*c8dee2aaSAndroid Build Coastguard Worker static_assert(sizeof(SkPoint) == 2 * sizeof(uint32_t));
112*c8dee2aaSAndroid Build Coastguard Worker static_assert(sizeof(SkScalar) == sizeof(uint32_t));
113*c8dee2aaSAndroid Build Coastguard Worker // 1 is for the verb count. Each verb is a byte but we'll pad the verb data out to
114*c8dee2aaSAndroid Build Coastguard Worker // a uint32_t length.
115*c8dee2aaSAndroid Build Coastguard Worker return 1 + (SkAlign4(verbCnt) >> 2) + 2 * pointCnt + conicWeightCnt;
116*c8dee2aaSAndroid Build Coastguard Worker }
117*c8dee2aaSAndroid Build Coastguard Worker
118*c8dee2aaSAndroid Build Coastguard Worker // Writes the path data key into the passed pointer.
write_path_key_from_data(const SkPath & path,uint32_t * origKey)119*c8dee2aaSAndroid Build Coastguard Worker static void write_path_key_from_data(const SkPath& path, uint32_t* origKey) {
120*c8dee2aaSAndroid Build Coastguard Worker uint32_t* key = origKey;
121*c8dee2aaSAndroid Build Coastguard Worker // The check below should take care of negative values casted positive.
122*c8dee2aaSAndroid Build Coastguard Worker const int verbCnt = path.countVerbs();
123*c8dee2aaSAndroid Build Coastguard Worker const int pointCnt = path.countPoints();
124*c8dee2aaSAndroid Build Coastguard Worker const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
125*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(verbCnt <= GrStyledShape::kMaxKeyFromDataVerbCnt);
126*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(pointCnt && verbCnt);
127*c8dee2aaSAndroid Build Coastguard Worker *key++ = verbCnt;
128*c8dee2aaSAndroid Build Coastguard Worker memcpy(key, SkPathPriv::VerbData(path), verbCnt * sizeof(uint8_t));
129*c8dee2aaSAndroid Build Coastguard Worker int verbKeySize = SkAlign4(verbCnt);
130*c8dee2aaSAndroid Build Coastguard Worker // pad out to uint32_t alignment using value that will stand out when debugging.
131*c8dee2aaSAndroid Build Coastguard Worker uint8_t* pad = reinterpret_cast<uint8_t*>(key)+ verbCnt;
132*c8dee2aaSAndroid Build Coastguard Worker memset(pad, 0xDE, verbKeySize - verbCnt);
133*c8dee2aaSAndroid Build Coastguard Worker key += verbKeySize >> 2;
134*c8dee2aaSAndroid Build Coastguard Worker
135*c8dee2aaSAndroid Build Coastguard Worker memcpy(key, SkPathPriv::PointData(path), sizeof(SkPoint) * pointCnt);
136*c8dee2aaSAndroid Build Coastguard Worker static_assert(sizeof(SkPoint) == 2 * sizeof(uint32_t));
137*c8dee2aaSAndroid Build Coastguard Worker key += 2 * pointCnt;
138*c8dee2aaSAndroid Build Coastguard Worker sk_careful_memcpy(key, SkPathPriv::ConicWeightData(path), sizeof(SkScalar) * conicWeightCnt);
139*c8dee2aaSAndroid Build Coastguard Worker static_assert(sizeof(SkScalar) == sizeof(uint32_t));
140*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(key += conicWeightCnt);
141*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(key - origKey == path_key_from_data_size(path));
142*c8dee2aaSAndroid Build Coastguard Worker }
143*c8dee2aaSAndroid Build Coastguard Worker
unstyledKeySize() const144*c8dee2aaSAndroid Build Coastguard Worker int GrStyledShape::unstyledKeySize() const {
145*c8dee2aaSAndroid Build Coastguard Worker if (fInheritedKey.count()) {
146*c8dee2aaSAndroid Build Coastguard Worker return fInheritedKey.count();
147*c8dee2aaSAndroid Build Coastguard Worker }
148*c8dee2aaSAndroid Build Coastguard Worker
149*c8dee2aaSAndroid Build Coastguard Worker int count = 1; // Every key has the state flags from the GrShape
150*c8dee2aaSAndroid Build Coastguard Worker switch(fShape.type()) {
151*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kPoint:
152*c8dee2aaSAndroid Build Coastguard Worker static_assert(0 == sizeof(SkPoint) % sizeof(uint32_t));
153*c8dee2aaSAndroid Build Coastguard Worker count += sizeof(SkPoint) / sizeof(uint32_t);
154*c8dee2aaSAndroid Build Coastguard Worker break;
155*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kRect:
156*c8dee2aaSAndroid Build Coastguard Worker static_assert(0 == sizeof(SkRect) % sizeof(uint32_t));
157*c8dee2aaSAndroid Build Coastguard Worker count += sizeof(SkRect) / sizeof(uint32_t);
158*c8dee2aaSAndroid Build Coastguard Worker break;
159*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kRRect:
160*c8dee2aaSAndroid Build Coastguard Worker static_assert(0 == SkRRect::kSizeInMemory % sizeof(uint32_t));
161*c8dee2aaSAndroid Build Coastguard Worker count += SkRRect::kSizeInMemory / sizeof(uint32_t);
162*c8dee2aaSAndroid Build Coastguard Worker break;
163*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kArc:
164*c8dee2aaSAndroid Build Coastguard Worker static_assert(0 == sizeof(SkArc) % sizeof(uint32_t));
165*c8dee2aaSAndroid Build Coastguard Worker count += sizeof(SkArc) / sizeof(uint32_t);
166*c8dee2aaSAndroid Build Coastguard Worker break;
167*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kLine:
168*c8dee2aaSAndroid Build Coastguard Worker static_assert(0 == sizeof(GrLineSegment) % sizeof(uint32_t));
169*c8dee2aaSAndroid Build Coastguard Worker count += sizeof(GrLineSegment) / sizeof(uint32_t);
170*c8dee2aaSAndroid Build Coastguard Worker break;
171*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kPath: {
172*c8dee2aaSAndroid Build Coastguard Worker if (0 == fGenID) {
173*c8dee2aaSAndroid Build Coastguard Worker return -1; // volatile, so won't be keyed
174*c8dee2aaSAndroid Build Coastguard Worker }
175*c8dee2aaSAndroid Build Coastguard Worker int dataKeySize = path_key_from_data_size(fShape.path());
176*c8dee2aaSAndroid Build Coastguard Worker if (dataKeySize >= 0) {
177*c8dee2aaSAndroid Build Coastguard Worker count += dataKeySize;
178*c8dee2aaSAndroid Build Coastguard Worker } else {
179*c8dee2aaSAndroid Build Coastguard Worker count++; // Just adds the gen ID.
180*c8dee2aaSAndroid Build Coastguard Worker }
181*c8dee2aaSAndroid Build Coastguard Worker break; }
182*c8dee2aaSAndroid Build Coastguard Worker default:
183*c8dee2aaSAndroid Build Coastguard Worker // else it's empty, which just needs the state flags for its key
184*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fShape.isEmpty());
185*c8dee2aaSAndroid Build Coastguard Worker }
186*c8dee2aaSAndroid Build Coastguard Worker return count;
187*c8dee2aaSAndroid Build Coastguard Worker }
188*c8dee2aaSAndroid Build Coastguard Worker
writeUnstyledKey(uint32_t * key) const189*c8dee2aaSAndroid Build Coastguard Worker void GrStyledShape::writeUnstyledKey(uint32_t* key) const {
190*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->unstyledKeySize());
191*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(uint32_t* origKey = key;)
192*c8dee2aaSAndroid Build Coastguard Worker if (fInheritedKey.count()) {
193*c8dee2aaSAndroid Build Coastguard Worker memcpy(key, fInheritedKey.get(), sizeof(uint32_t) * fInheritedKey.count());
194*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(key += fInheritedKey.count();)
195*c8dee2aaSAndroid Build Coastguard Worker } else {
196*c8dee2aaSAndroid Build Coastguard Worker // Dir and start are only used for rect and rrect shapes, so are not included in other
197*c8dee2aaSAndroid Build Coastguard Worker // shape type keys. Make sure that they are the defaults for other shapes so it doesn't
198*c8dee2aaSAndroid Build Coastguard Worker // matter that we universally include them in the flag key value.
199*c8dee2aaSAndroid Build Coastguard Worker SkASSERT((fShape.isRect() || fShape.isRRect()) ||
200*c8dee2aaSAndroid Build Coastguard Worker (fShape.dir() == GrShape::kDefaultDir &&
201*c8dee2aaSAndroid Build Coastguard Worker fShape.startIndex() == GrShape::kDefaultStart));
202*c8dee2aaSAndroid Build Coastguard Worker
203*c8dee2aaSAndroid Build Coastguard Worker // Every key starts with the state from the GrShape (this includes path fill type,
204*c8dee2aaSAndroid Build Coastguard Worker // and any tracked winding, start, inversion, as well as the class of geometry).
205*c8dee2aaSAndroid Build Coastguard Worker *key++ = fShape.stateKey();
206*c8dee2aaSAndroid Build Coastguard Worker
207*c8dee2aaSAndroid Build Coastguard Worker switch(fShape.type()) {
208*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kPath: {
209*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fGenID != 0);
210*c8dee2aaSAndroid Build Coastguard Worker // Ensure that the path's inversion matches our state so that the path's key suffices.
211*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fShape.inverted() == fShape.path().isInverseFillType());
212*c8dee2aaSAndroid Build Coastguard Worker
213*c8dee2aaSAndroid Build Coastguard Worker int dataKeySize = path_key_from_data_size(fShape.path());
214*c8dee2aaSAndroid Build Coastguard Worker if (dataKeySize >= 0) {
215*c8dee2aaSAndroid Build Coastguard Worker write_path_key_from_data(fShape.path(), key);
216*c8dee2aaSAndroid Build Coastguard Worker return;
217*c8dee2aaSAndroid Build Coastguard Worker } else {
218*c8dee2aaSAndroid Build Coastguard Worker *key++ = fGenID;
219*c8dee2aaSAndroid Build Coastguard Worker }
220*c8dee2aaSAndroid Build Coastguard Worker break; }
221*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kPoint:
222*c8dee2aaSAndroid Build Coastguard Worker memcpy(key, &fShape.point(), sizeof(SkPoint));
223*c8dee2aaSAndroid Build Coastguard Worker key += sizeof(SkPoint) / sizeof(uint32_t);
224*c8dee2aaSAndroid Build Coastguard Worker break;
225*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kRect:
226*c8dee2aaSAndroid Build Coastguard Worker memcpy(key, &fShape.rect(), sizeof(SkRect));
227*c8dee2aaSAndroid Build Coastguard Worker key += sizeof(SkRect) / sizeof(uint32_t);
228*c8dee2aaSAndroid Build Coastguard Worker break;
229*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kRRect:
230*c8dee2aaSAndroid Build Coastguard Worker fShape.rrect().writeToMemory(key);
231*c8dee2aaSAndroid Build Coastguard Worker key += SkRRect::kSizeInMemory / sizeof(uint32_t);
232*c8dee2aaSAndroid Build Coastguard Worker break;
233*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kArc:
234*c8dee2aaSAndroid Build Coastguard Worker // Write dense floats first
235*c8dee2aaSAndroid Build Coastguard Worker memcpy(key, &fShape.arc(), sizeof(SkRect) + 2 * sizeof(float));
236*c8dee2aaSAndroid Build Coastguard Worker key += (sizeof(SkArc) / sizeof(uint32_t) - 1);
237*c8dee2aaSAndroid Build Coastguard Worker // Then write the final bool as an int, to make sure upper bits are set
238*c8dee2aaSAndroid Build Coastguard Worker *key++ = fShape.arc().isWedge() ? 1 : 0;
239*c8dee2aaSAndroid Build Coastguard Worker break;
240*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kLine:
241*c8dee2aaSAndroid Build Coastguard Worker memcpy(key, &fShape.line(), sizeof(GrLineSegment));
242*c8dee2aaSAndroid Build Coastguard Worker key += sizeof(GrLineSegment) / sizeof(uint32_t);
243*c8dee2aaSAndroid Build Coastguard Worker break;
244*c8dee2aaSAndroid Build Coastguard Worker default:
245*c8dee2aaSAndroid Build Coastguard Worker // Nothing other than the flag state is needed in the key for an empty shape
246*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fShape.isEmpty());
247*c8dee2aaSAndroid Build Coastguard Worker }
248*c8dee2aaSAndroid Build Coastguard Worker }
249*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(key - origKey == this->unstyledKeySize());
250*c8dee2aaSAndroid Build Coastguard Worker }
251*c8dee2aaSAndroid Build Coastguard Worker
setInheritedKey(const GrStyledShape & parent,GrStyle::Apply apply,SkScalar scale)252*c8dee2aaSAndroid Build Coastguard Worker void GrStyledShape::setInheritedKey(const GrStyledShape &parent, GrStyle::Apply apply,
253*c8dee2aaSAndroid Build Coastguard Worker SkScalar scale) {
254*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fInheritedKey.count());
255*c8dee2aaSAndroid Build Coastguard Worker // If the output shape turns out to be simple, then we will just use its geometric key
256*c8dee2aaSAndroid Build Coastguard Worker if (fShape.isPath()) {
257*c8dee2aaSAndroid Build Coastguard Worker // We want ApplyFullStyle(ApplyPathEffect(shape)) to have the same key as
258*c8dee2aaSAndroid Build Coastguard Worker // ApplyFullStyle(shape).
259*c8dee2aaSAndroid Build Coastguard Worker // The full key is structured as (geo,path_effect,stroke).
260*c8dee2aaSAndroid Build Coastguard Worker // If we do ApplyPathEffect we get geo,path_effect as the inherited key. If we then
261*c8dee2aaSAndroid Build Coastguard Worker // do ApplyFullStyle we'll memcpy geo,path_effect into the new inherited key
262*c8dee2aaSAndroid Build Coastguard Worker // and then append the style key (which should now be stroke only) at the end.
263*c8dee2aaSAndroid Build Coastguard Worker int parentCnt = parent.fInheritedKey.count();
264*c8dee2aaSAndroid Build Coastguard Worker bool useParentGeoKey = !parentCnt;
265*c8dee2aaSAndroid Build Coastguard Worker if (useParentGeoKey) {
266*c8dee2aaSAndroid Build Coastguard Worker parentCnt = parent.unstyledKeySize();
267*c8dee2aaSAndroid Build Coastguard Worker if (parentCnt < 0) {
268*c8dee2aaSAndroid Build Coastguard Worker // The parent's geometry has no key so we will have no key.
269*c8dee2aaSAndroid Build Coastguard Worker fGenID = 0;
270*c8dee2aaSAndroid Build Coastguard Worker return;
271*c8dee2aaSAndroid Build Coastguard Worker }
272*c8dee2aaSAndroid Build Coastguard Worker }
273*c8dee2aaSAndroid Build Coastguard Worker uint32_t styleKeyFlags = 0;
274*c8dee2aaSAndroid Build Coastguard Worker if (parent.knownToBeClosed()) {
275*c8dee2aaSAndroid Build Coastguard Worker styleKeyFlags |= GrStyle::kClosed_KeyFlag;
276*c8dee2aaSAndroid Build Coastguard Worker }
277*c8dee2aaSAndroid Build Coastguard Worker if (parent.asLine(nullptr, nullptr)) {
278*c8dee2aaSAndroid Build Coastguard Worker styleKeyFlags |= GrStyle::kNoJoins_KeyFlag;
279*c8dee2aaSAndroid Build Coastguard Worker }
280*c8dee2aaSAndroid Build Coastguard Worker int styleCnt = GrStyle::KeySize(parent.fStyle, apply, styleKeyFlags);
281*c8dee2aaSAndroid Build Coastguard Worker if (styleCnt < 0) {
282*c8dee2aaSAndroid Build Coastguard Worker // The style doesn't allow a key, set the path gen ID to 0 so that we fail when
283*c8dee2aaSAndroid Build Coastguard Worker // we try to get a key for the shape.
284*c8dee2aaSAndroid Build Coastguard Worker fGenID = 0;
285*c8dee2aaSAndroid Build Coastguard Worker return;
286*c8dee2aaSAndroid Build Coastguard Worker }
287*c8dee2aaSAndroid Build Coastguard Worker fInheritedKey.reset(parentCnt + styleCnt);
288*c8dee2aaSAndroid Build Coastguard Worker if (useParentGeoKey) {
289*c8dee2aaSAndroid Build Coastguard Worker // This will be the geo key.
290*c8dee2aaSAndroid Build Coastguard Worker parent.writeUnstyledKey(fInheritedKey.get());
291*c8dee2aaSAndroid Build Coastguard Worker } else {
292*c8dee2aaSAndroid Build Coastguard Worker // This should be (geo,path_effect).
293*c8dee2aaSAndroid Build Coastguard Worker memcpy(fInheritedKey.get(), parent.fInheritedKey.get(),
294*c8dee2aaSAndroid Build Coastguard Worker parentCnt * sizeof(uint32_t));
295*c8dee2aaSAndroid Build Coastguard Worker }
296*c8dee2aaSAndroid Build Coastguard Worker // Now turn (geo,path_effect) or (geo) into (geo,path_effect,stroke)
297*c8dee2aaSAndroid Build Coastguard Worker GrStyle::WriteKey(fInheritedKey.get() + parentCnt, parent.fStyle, apply, scale,
298*c8dee2aaSAndroid Build Coastguard Worker styleKeyFlags);
299*c8dee2aaSAndroid Build Coastguard Worker }
300*c8dee2aaSAndroid Build Coastguard Worker }
301*c8dee2aaSAndroid Build Coastguard Worker
originalPathForListeners() const302*c8dee2aaSAndroid Build Coastguard Worker const SkPath* GrStyledShape::originalPathForListeners() const {
303*c8dee2aaSAndroid Build Coastguard Worker if (fInheritedPathForListeners.isValid()) {
304*c8dee2aaSAndroid Build Coastguard Worker return fInheritedPathForListeners.get();
305*c8dee2aaSAndroid Build Coastguard Worker } else if (fShape.isPath() && !fShape.path().isVolatile()) {
306*c8dee2aaSAndroid Build Coastguard Worker return &fShape.path();
307*c8dee2aaSAndroid Build Coastguard Worker }
308*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
309*c8dee2aaSAndroid Build Coastguard Worker }
310*c8dee2aaSAndroid Build Coastguard Worker
addGenIDChangeListener(sk_sp<SkIDChangeListener> listener) const311*c8dee2aaSAndroid Build Coastguard Worker void GrStyledShape::addGenIDChangeListener(sk_sp<SkIDChangeListener> listener) const {
312*c8dee2aaSAndroid Build Coastguard Worker if (const auto* lp = this->originalPathForListeners()) {
313*c8dee2aaSAndroid Build Coastguard Worker SkPathPriv::AddGenIDChangeListener(*lp, std::move(listener));
314*c8dee2aaSAndroid Build Coastguard Worker }
315*c8dee2aaSAndroid Build Coastguard Worker }
316*c8dee2aaSAndroid Build Coastguard Worker
MakeArc(const SkArc & arc,const GrStyle & style,DoSimplify doSimplify)317*c8dee2aaSAndroid Build Coastguard Worker GrStyledShape GrStyledShape::MakeArc(const SkArc& arc,
318*c8dee2aaSAndroid Build Coastguard Worker const GrStyle& style,
319*c8dee2aaSAndroid Build Coastguard Worker DoSimplify doSimplify) {
320*c8dee2aaSAndroid Build Coastguard Worker GrStyledShape result;
321*c8dee2aaSAndroid Build Coastguard Worker result.fShape.setArc(
322*c8dee2aaSAndroid Build Coastguard Worker SkArc::Make(arc.fOval.makeSorted(), arc.fStartAngle, arc.fSweepAngle, arc.fType));
323*c8dee2aaSAndroid Build Coastguard Worker result.fStyle = style;
324*c8dee2aaSAndroid Build Coastguard Worker if (doSimplify == DoSimplify::kYes) {
325*c8dee2aaSAndroid Build Coastguard Worker result.simplify();
326*c8dee2aaSAndroid Build Coastguard Worker }
327*c8dee2aaSAndroid Build Coastguard Worker return result;
328*c8dee2aaSAndroid Build Coastguard Worker }
329*c8dee2aaSAndroid Build Coastguard Worker
GrStyledShape(const GrStyledShape & that)330*c8dee2aaSAndroid Build Coastguard Worker GrStyledShape::GrStyledShape(const GrStyledShape& that)
331*c8dee2aaSAndroid Build Coastguard Worker : fShape(that.fShape)
332*c8dee2aaSAndroid Build Coastguard Worker , fStyle(that.fStyle)
333*c8dee2aaSAndroid Build Coastguard Worker , fGenID(that.fGenID)
334*c8dee2aaSAndroid Build Coastguard Worker , fSimplified(that.fSimplified) {
335*c8dee2aaSAndroid Build Coastguard Worker fInheritedKey.reset(that.fInheritedKey.count());
336*c8dee2aaSAndroid Build Coastguard Worker sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
337*c8dee2aaSAndroid Build Coastguard Worker sizeof(uint32_t) * fInheritedKey.count());
338*c8dee2aaSAndroid Build Coastguard Worker if (that.fInheritedPathForListeners.isValid()) {
339*c8dee2aaSAndroid Build Coastguard Worker fInheritedPathForListeners.set(*that.fInheritedPathForListeners);
340*c8dee2aaSAndroid Build Coastguard Worker }
341*c8dee2aaSAndroid Build Coastguard Worker }
342*c8dee2aaSAndroid Build Coastguard Worker
GrStyledShape(const GrStyledShape & parent,GrStyle::Apply apply,SkScalar scale)343*c8dee2aaSAndroid Build Coastguard Worker GrStyledShape::GrStyledShape(const GrStyledShape& parent, GrStyle::Apply apply, SkScalar scale) {
344*c8dee2aaSAndroid Build Coastguard Worker // TODO: Add some quantization of scale for better cache performance here or leave that up
345*c8dee2aaSAndroid Build Coastguard Worker // to caller?
346*c8dee2aaSAndroid Build Coastguard Worker // TODO: For certain shapes and stroke params we could ignore the scale. (e.g. miter or bevel
347*c8dee2aaSAndroid Build Coastguard Worker // stroke of a rect).
348*c8dee2aaSAndroid Build Coastguard Worker if (!parent.style().applies() ||
349*c8dee2aaSAndroid Build Coastguard Worker (GrStyle::Apply::kPathEffectOnly == apply && !parent.style().pathEffect())) {
350*c8dee2aaSAndroid Build Coastguard Worker *this = parent;
351*c8dee2aaSAndroid Build Coastguard Worker return;
352*c8dee2aaSAndroid Build Coastguard Worker }
353*c8dee2aaSAndroid Build Coastguard Worker
354*c8dee2aaSAndroid Build Coastguard Worker SkPathEffect* pe = parent.fStyle.pathEffect();
355*c8dee2aaSAndroid Build Coastguard Worker SkTLazy<SkPath> tmpPath;
356*c8dee2aaSAndroid Build Coastguard Worker const GrStyledShape* parentForKey = &parent;
357*c8dee2aaSAndroid Build Coastguard Worker SkTLazy<GrStyledShape> tmpParent;
358*c8dee2aaSAndroid Build Coastguard Worker
359*c8dee2aaSAndroid Build Coastguard Worker // Start out as an empty path that is filled in by the applied style
360*c8dee2aaSAndroid Build Coastguard Worker fShape.setPath(SkPath());
361*c8dee2aaSAndroid Build Coastguard Worker
362*c8dee2aaSAndroid Build Coastguard Worker if (pe) {
363*c8dee2aaSAndroid Build Coastguard Worker const SkPath* srcForPathEffect;
364*c8dee2aaSAndroid Build Coastguard Worker if (parent.fShape.isPath()) {
365*c8dee2aaSAndroid Build Coastguard Worker srcForPathEffect = &parent.fShape.path();
366*c8dee2aaSAndroid Build Coastguard Worker } else {
367*c8dee2aaSAndroid Build Coastguard Worker srcForPathEffect = tmpPath.init();
368*c8dee2aaSAndroid Build Coastguard Worker parent.asPath(tmpPath.get());
369*c8dee2aaSAndroid Build Coastguard Worker }
370*c8dee2aaSAndroid Build Coastguard Worker // Should we consider bounds? Would have to include in key, but it'd be nice to know
371*c8dee2aaSAndroid Build Coastguard Worker // if the bounds actually modified anything before including in key.
372*c8dee2aaSAndroid Build Coastguard Worker SkStrokeRec strokeRec = parent.fStyle.strokeRec();
373*c8dee2aaSAndroid Build Coastguard Worker if (!parent.fStyle.applyPathEffectToPath(&fShape.path(), &strokeRec, *srcForPathEffect,
374*c8dee2aaSAndroid Build Coastguard Worker scale)) {
375*c8dee2aaSAndroid Build Coastguard Worker tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr));
376*c8dee2aaSAndroid Build Coastguard Worker *this = tmpParent->applyStyle(apply, scale);
377*c8dee2aaSAndroid Build Coastguard Worker return;
378*c8dee2aaSAndroid Build Coastguard Worker }
379*c8dee2aaSAndroid Build Coastguard Worker // A path effect has access to change the res scale but we aren't expecting it to and it
380*c8dee2aaSAndroid Build Coastguard Worker // would mess up our key computation.
381*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(scale == strokeRec.getResScale());
382*c8dee2aaSAndroid Build Coastguard Worker if (GrStyle::Apply::kPathEffectAndStrokeRec == apply && strokeRec.needToApply()) {
383*c8dee2aaSAndroid Build Coastguard Worker // The intermediate shape may not be a general path. If we we're just applying
384*c8dee2aaSAndroid Build Coastguard Worker // the path effect then attemptToReduceFromPath would catch it. This means that
385*c8dee2aaSAndroid Build Coastguard Worker // when we subsequently applied the remaining strokeRec we would have a non-path
386*c8dee2aaSAndroid Build Coastguard Worker // parent shape that would be used to determine the the stroked path's key.
387*c8dee2aaSAndroid Build Coastguard Worker // We detect that case here and change parentForKey to a temporary that represents
388*c8dee2aaSAndroid Build Coastguard Worker // the simpler shape so that applying both path effect and the strokerec all at
389*c8dee2aaSAndroid Build Coastguard Worker // once produces the same key.
390*c8dee2aaSAndroid Build Coastguard Worker tmpParent.init(fShape.path(), GrStyle(strokeRec, nullptr));
391*c8dee2aaSAndroid Build Coastguard Worker tmpParent->setInheritedKey(parent, GrStyle::Apply::kPathEffectOnly, scale);
392*c8dee2aaSAndroid Build Coastguard Worker if (!tmpPath.isValid()) {
393*c8dee2aaSAndroid Build Coastguard Worker tmpPath.init();
394*c8dee2aaSAndroid Build Coastguard Worker }
395*c8dee2aaSAndroid Build Coastguard Worker tmpParent->asPath(tmpPath.get());
396*c8dee2aaSAndroid Build Coastguard Worker SkStrokeRec::InitStyle fillOrHairline;
397*c8dee2aaSAndroid Build Coastguard Worker // The parent shape may have simplified away the strokeRec, check for that here.
398*c8dee2aaSAndroid Build Coastguard Worker if (tmpParent->style().applies()) {
399*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(tmpParent.get()->style().applyToPath(&fShape.path(), &fillOrHairline,
400*c8dee2aaSAndroid Build Coastguard Worker *tmpPath.get(), scale));
401*c8dee2aaSAndroid Build Coastguard Worker } else if (tmpParent->style().isSimpleFill()) {
402*c8dee2aaSAndroid Build Coastguard Worker fillOrHairline = SkStrokeRec::kFill_InitStyle;
403*c8dee2aaSAndroid Build Coastguard Worker } else {
404*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(tmpParent.get()->style().isSimpleHairline());
405*c8dee2aaSAndroid Build Coastguard Worker fillOrHairline = SkStrokeRec::kHairline_InitStyle;
406*c8dee2aaSAndroid Build Coastguard Worker }
407*c8dee2aaSAndroid Build Coastguard Worker fStyle.resetToInitStyle(fillOrHairline);
408*c8dee2aaSAndroid Build Coastguard Worker parentForKey = tmpParent.get();
409*c8dee2aaSAndroid Build Coastguard Worker } else {
410*c8dee2aaSAndroid Build Coastguard Worker fStyle = GrStyle(strokeRec, nullptr);
411*c8dee2aaSAndroid Build Coastguard Worker }
412*c8dee2aaSAndroid Build Coastguard Worker } else {
413*c8dee2aaSAndroid Build Coastguard Worker const SkPath* srcForParentStyle;
414*c8dee2aaSAndroid Build Coastguard Worker if (parent.fShape.isPath()) {
415*c8dee2aaSAndroid Build Coastguard Worker srcForParentStyle = &parent.fShape.path();
416*c8dee2aaSAndroid Build Coastguard Worker } else {
417*c8dee2aaSAndroid Build Coastguard Worker srcForParentStyle = tmpPath.init();
418*c8dee2aaSAndroid Build Coastguard Worker parent.asPath(tmpPath.get());
419*c8dee2aaSAndroid Build Coastguard Worker }
420*c8dee2aaSAndroid Build Coastguard Worker SkStrokeRec::InitStyle fillOrHairline;
421*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(parent.fStyle.applies());
422*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!parent.fStyle.pathEffect());
423*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(parent.fStyle.applyToPath(&fShape.path(), &fillOrHairline,
424*c8dee2aaSAndroid Build Coastguard Worker *srcForParentStyle, scale));
425*c8dee2aaSAndroid Build Coastguard Worker fStyle.resetToInitStyle(fillOrHairline);
426*c8dee2aaSAndroid Build Coastguard Worker }
427*c8dee2aaSAndroid Build Coastguard Worker
428*c8dee2aaSAndroid Build Coastguard Worker if (parent.fInheritedPathForListeners.isValid()) {
429*c8dee2aaSAndroid Build Coastguard Worker fInheritedPathForListeners.set(*parent.fInheritedPathForListeners);
430*c8dee2aaSAndroid Build Coastguard Worker } else if (parent.fShape.isPath() && !parent.fShape.path().isVolatile()) {
431*c8dee2aaSAndroid Build Coastguard Worker fInheritedPathForListeners.set(parent.fShape.path());
432*c8dee2aaSAndroid Build Coastguard Worker }
433*c8dee2aaSAndroid Build Coastguard Worker this->simplify();
434*c8dee2aaSAndroid Build Coastguard Worker this->setInheritedKey(*parentForKey, apply, scale);
435*c8dee2aaSAndroid Build Coastguard Worker }
436*c8dee2aaSAndroid Build Coastguard Worker
asRRect(SkRRect * rrect,bool * inverted) const437*c8dee2aaSAndroid Build Coastguard Worker bool GrStyledShape::asRRect(SkRRect* rrect, bool* inverted) const {
438*c8dee2aaSAndroid Build Coastguard Worker if (!fShape.isRRect() && !fShape.isRect()) {
439*c8dee2aaSAndroid Build Coastguard Worker return false;
440*c8dee2aaSAndroid Build Coastguard Worker }
441*c8dee2aaSAndroid Build Coastguard Worker
442*c8dee2aaSAndroid Build Coastguard Worker if (rrect) {
443*c8dee2aaSAndroid Build Coastguard Worker *rrect = fShape.isRect() ? SkRRect::MakeRect(fShape.rect()) : fShape.rrect();
444*c8dee2aaSAndroid Build Coastguard Worker }
445*c8dee2aaSAndroid Build Coastguard Worker
446*c8dee2aaSAndroid Build Coastguard Worker if (inverted) {
447*c8dee2aaSAndroid Build Coastguard Worker *inverted = fShape.inverted();
448*c8dee2aaSAndroid Build Coastguard Worker }
449*c8dee2aaSAndroid Build Coastguard Worker
450*c8dee2aaSAndroid Build Coastguard Worker return true;
451*c8dee2aaSAndroid Build Coastguard Worker }
452*c8dee2aaSAndroid Build Coastguard Worker
asLine(SkPoint pts[2],bool * inverted) const453*c8dee2aaSAndroid Build Coastguard Worker bool GrStyledShape::asLine(SkPoint pts[2], bool* inverted) const {
454*c8dee2aaSAndroid Build Coastguard Worker if (!fShape.isLine()) {
455*c8dee2aaSAndroid Build Coastguard Worker return false;
456*c8dee2aaSAndroid Build Coastguard Worker }
457*c8dee2aaSAndroid Build Coastguard Worker
458*c8dee2aaSAndroid Build Coastguard Worker if (pts) {
459*c8dee2aaSAndroid Build Coastguard Worker pts[0] = fShape.line().fP1;
460*c8dee2aaSAndroid Build Coastguard Worker pts[1] = fShape.line().fP2;
461*c8dee2aaSAndroid Build Coastguard Worker }
462*c8dee2aaSAndroid Build Coastguard Worker if (inverted) {
463*c8dee2aaSAndroid Build Coastguard Worker *inverted = fShape.inverted();
464*c8dee2aaSAndroid Build Coastguard Worker }
465*c8dee2aaSAndroid Build Coastguard Worker return true;
466*c8dee2aaSAndroid Build Coastguard Worker }
467*c8dee2aaSAndroid Build Coastguard Worker
asNestedRects(SkRect rects[2]) const468*c8dee2aaSAndroid Build Coastguard Worker bool GrStyledShape::asNestedRects(SkRect rects[2]) const {
469*c8dee2aaSAndroid Build Coastguard Worker if (!fShape.isPath()) {
470*c8dee2aaSAndroid Build Coastguard Worker return false;
471*c8dee2aaSAndroid Build Coastguard Worker }
472*c8dee2aaSAndroid Build Coastguard Worker
473*c8dee2aaSAndroid Build Coastguard Worker // TODO: it would be better two store DRRects natively in the shape rather than converting
474*c8dee2aaSAndroid Build Coastguard Worker // them to a path and then reextracting the nested rects
475*c8dee2aaSAndroid Build Coastguard Worker if (fShape.path().isInverseFillType()) {
476*c8dee2aaSAndroid Build Coastguard Worker return false;
477*c8dee2aaSAndroid Build Coastguard Worker }
478*c8dee2aaSAndroid Build Coastguard Worker
479*c8dee2aaSAndroid Build Coastguard Worker SkPathDirection dirs[2];
480*c8dee2aaSAndroid Build Coastguard Worker if (!SkPathPriv::IsNestedFillRects(fShape.path(), rects, dirs)) {
481*c8dee2aaSAndroid Build Coastguard Worker return false;
482*c8dee2aaSAndroid Build Coastguard Worker }
483*c8dee2aaSAndroid Build Coastguard Worker
484*c8dee2aaSAndroid Build Coastguard Worker if (SkPathFillType::kWinding == fShape.path().getFillType() && dirs[0] == dirs[1]) {
485*c8dee2aaSAndroid Build Coastguard Worker // The two rects need to be wound opposite to each other
486*c8dee2aaSAndroid Build Coastguard Worker return false;
487*c8dee2aaSAndroid Build Coastguard Worker }
488*c8dee2aaSAndroid Build Coastguard Worker
489*c8dee2aaSAndroid Build Coastguard Worker // Right now, nested rects where the margin is not the same width
490*c8dee2aaSAndroid Build Coastguard Worker // all around do not render correctly
491*c8dee2aaSAndroid Build Coastguard Worker const SkScalar* outer = rects[0].asScalars();
492*c8dee2aaSAndroid Build Coastguard Worker const SkScalar* inner = rects[1].asScalars();
493*c8dee2aaSAndroid Build Coastguard Worker
494*c8dee2aaSAndroid Build Coastguard Worker bool allEq = true;
495*c8dee2aaSAndroid Build Coastguard Worker
496*c8dee2aaSAndroid Build Coastguard Worker SkScalar margin = SkScalarAbs(outer[0] - inner[0]);
497*c8dee2aaSAndroid Build Coastguard Worker bool allGoE1 = margin >= SK_Scalar1;
498*c8dee2aaSAndroid Build Coastguard Worker
499*c8dee2aaSAndroid Build Coastguard Worker for (int i = 1; i < 4; ++i) {
500*c8dee2aaSAndroid Build Coastguard Worker SkScalar temp = SkScalarAbs(outer[i] - inner[i]);
501*c8dee2aaSAndroid Build Coastguard Worker if (temp < SK_Scalar1) {
502*c8dee2aaSAndroid Build Coastguard Worker allGoE1 = false;
503*c8dee2aaSAndroid Build Coastguard Worker }
504*c8dee2aaSAndroid Build Coastguard Worker if (!SkScalarNearlyEqual(margin, temp)) {
505*c8dee2aaSAndroid Build Coastguard Worker allEq = false;
506*c8dee2aaSAndroid Build Coastguard Worker }
507*c8dee2aaSAndroid Build Coastguard Worker }
508*c8dee2aaSAndroid Build Coastguard Worker
509*c8dee2aaSAndroid Build Coastguard Worker return allEq || allGoE1;
510*c8dee2aaSAndroid Build Coastguard Worker }
511*c8dee2aaSAndroid Build Coastguard Worker
512*c8dee2aaSAndroid Build Coastguard Worker class AutoRestoreInverseness {
513*c8dee2aaSAndroid Build Coastguard Worker public:
AutoRestoreInverseness(GrShape * shape,const GrStyle & style)514*c8dee2aaSAndroid Build Coastguard Worker AutoRestoreInverseness(GrShape* shape, const GrStyle& style)
515*c8dee2aaSAndroid Build Coastguard Worker // Dashing ignores inverseness skbug.com/5421.
516*c8dee2aaSAndroid Build Coastguard Worker : fShape(shape), fInverted(!style.isDashed() && fShape->inverted()) {}
517*c8dee2aaSAndroid Build Coastguard Worker
~AutoRestoreInverseness()518*c8dee2aaSAndroid Build Coastguard Worker ~AutoRestoreInverseness() {
519*c8dee2aaSAndroid Build Coastguard Worker // Restore invertedness after any modifications were made to the shape type
520*c8dee2aaSAndroid Build Coastguard Worker fShape->setInverted(fInverted);
521*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fShape->isPath() || fInverted == fShape->path().isInverseFillType());
522*c8dee2aaSAndroid Build Coastguard Worker }
523*c8dee2aaSAndroid Build Coastguard Worker
524*c8dee2aaSAndroid Build Coastguard Worker private:
525*c8dee2aaSAndroid Build Coastguard Worker GrShape* fShape;
526*c8dee2aaSAndroid Build Coastguard Worker bool fInverted;
527*c8dee2aaSAndroid Build Coastguard Worker };
528*c8dee2aaSAndroid Build Coastguard Worker
simplify()529*c8dee2aaSAndroid Build Coastguard Worker void GrStyledShape::simplify() {
530*c8dee2aaSAndroid Build Coastguard Worker AutoRestoreInverseness ari(&fShape, fStyle);
531*c8dee2aaSAndroid Build Coastguard Worker
532*c8dee2aaSAndroid Build Coastguard Worker unsigned simplifyFlags = 0;
533*c8dee2aaSAndroid Build Coastguard Worker if (fStyle.isSimpleFill()) {
534*c8dee2aaSAndroid Build Coastguard Worker simplifyFlags = GrShape::kAll_Flags;
535*c8dee2aaSAndroid Build Coastguard Worker } else if (!fStyle.hasPathEffect()) {
536*c8dee2aaSAndroid Build Coastguard Worker // Everything but arcs with caps that might extend beyond the oval edge can ignore winding
537*c8dee2aaSAndroid Build Coastguard Worker if (!fShape.isArc() || fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) {
538*c8dee2aaSAndroid Build Coastguard Worker simplifyFlags |= GrShape::kIgnoreWinding_Flag;
539*c8dee2aaSAndroid Build Coastguard Worker }
540*c8dee2aaSAndroid Build Coastguard Worker simplifyFlags |= GrShape::kMakeCanonical_Flag;
541*c8dee2aaSAndroid Build Coastguard Worker } // else if there's a path effect, every destructive simplification is disabledd
542*c8dee2aaSAndroid Build Coastguard Worker
543*c8dee2aaSAndroid Build Coastguard Worker // Remember if the original shape was closed; in the event we simplify to a point or line
544*c8dee2aaSAndroid Build Coastguard Worker // because of degenerate geometry, we need to update joins and caps.
545*c8dee2aaSAndroid Build Coastguard Worker GrShape::Type oldType = fShape.type();
546*c8dee2aaSAndroid Build Coastguard Worker fClosed = fShape.simplify(simplifyFlags);
547*c8dee2aaSAndroid Build Coastguard Worker fSimplified = oldType != fShape.type();
548*c8dee2aaSAndroid Build Coastguard Worker
549*c8dee2aaSAndroid Build Coastguard Worker if (fShape.isPath()) {
550*c8dee2aaSAndroid Build Coastguard Worker // The shape remains a path, so configure the gen ID and canonicalize fill type if possible
551*c8dee2aaSAndroid Build Coastguard Worker if (fInheritedKey.count() || fShape.path().isVolatile()) {
552*c8dee2aaSAndroid Build Coastguard Worker fGenID = 0;
553*c8dee2aaSAndroid Build Coastguard Worker } else {
554*c8dee2aaSAndroid Build Coastguard Worker fGenID = fShape.path().getGenerationID();
555*c8dee2aaSAndroid Build Coastguard Worker }
556*c8dee2aaSAndroid Build Coastguard Worker if (!fStyle.hasNonDashPathEffect() &&
557*c8dee2aaSAndroid Build Coastguard Worker (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style ||
558*c8dee2aaSAndroid Build Coastguard Worker fStyle.strokeRec().getStyle() == SkStrokeRec::kHairline_Style ||
559*c8dee2aaSAndroid Build Coastguard Worker fShape.path().isConvex())) {
560*c8dee2aaSAndroid Build Coastguard Worker // Stroke styles don't differentiate between winding and even/odd. There is no
561*c8dee2aaSAndroid Build Coastguard Worker // distinction between even/odd and non-zero winding count for convex paths.
562*c8dee2aaSAndroid Build Coastguard Worker // Moreover, dashing ignores inverseness (skbug.com/5421)
563*c8dee2aaSAndroid Build Coastguard Worker fShape.path().setFillType(GrShape::kDefaultFillType);
564*c8dee2aaSAndroid Build Coastguard Worker }
565*c8dee2aaSAndroid Build Coastguard Worker } else {
566*c8dee2aaSAndroid Build Coastguard Worker fInheritedKey.reset(0);
567*c8dee2aaSAndroid Build Coastguard Worker // Whenever we simplify to a non-path, break the chain so we no longer refer to the
568*c8dee2aaSAndroid Build Coastguard Worker // original path. This prevents attaching genID listeners to temporary paths created when
569*c8dee2aaSAndroid Build Coastguard Worker // drawing simple shapes.
570*c8dee2aaSAndroid Build Coastguard Worker fInheritedPathForListeners.reset();
571*c8dee2aaSAndroid Build Coastguard Worker // Further simplifications to the shape based on the style
572*c8dee2aaSAndroid Build Coastguard Worker this->simplifyStroke();
573*c8dee2aaSAndroid Build Coastguard Worker }
574*c8dee2aaSAndroid Build Coastguard Worker }
575*c8dee2aaSAndroid Build Coastguard Worker
simplifyStroke()576*c8dee2aaSAndroid Build Coastguard Worker void GrStyledShape::simplifyStroke() {
577*c8dee2aaSAndroid Build Coastguard Worker AutoRestoreInverseness ari(&fShape, fStyle);
578*c8dee2aaSAndroid Build Coastguard Worker
579*c8dee2aaSAndroid Build Coastguard Worker // For stroke+filled rects, a mitered shape becomes a larger rect and a rounded shape
580*c8dee2aaSAndroid Build Coastguard Worker // becomes a round rect.
581*c8dee2aaSAndroid Build Coastguard Worker if (!fStyle.hasPathEffect() && fShape.isRect() &&
582*c8dee2aaSAndroid Build Coastguard Worker fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
583*c8dee2aaSAndroid Build Coastguard Worker if (fStyle.strokeRec().getJoin() == SkPaint::kBevel_Join ||
584*c8dee2aaSAndroid Build Coastguard Worker (fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join &&
585*c8dee2aaSAndroid Build Coastguard Worker fStyle.strokeRec().getMiter() < SK_ScalarSqrt2)) {
586*c8dee2aaSAndroid Build Coastguard Worker // Bevel-stroked rect needs path rendering
587*c8dee2aaSAndroid Build Coastguard Worker return;
588*c8dee2aaSAndroid Build Coastguard Worker }
589*c8dee2aaSAndroid Build Coastguard Worker
590*c8dee2aaSAndroid Build Coastguard Worker SkScalar r = fStyle.strokeRec().getWidth() / 2;
591*c8dee2aaSAndroid Build Coastguard Worker fShape.rect().outset(r, r);
592*c8dee2aaSAndroid Build Coastguard Worker if (fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) {
593*c8dee2aaSAndroid Build Coastguard Worker // There's no dashing to worry about if we got here, so it's okay that this resets
594*c8dee2aaSAndroid Build Coastguard Worker // winding parameters
595*c8dee2aaSAndroid Build Coastguard Worker fShape.setRRect(SkRRect::MakeRectXY(fShape.rect(), r, r));
596*c8dee2aaSAndroid Build Coastguard Worker }
597*c8dee2aaSAndroid Build Coastguard Worker fStyle = GrStyle::SimpleFill();
598*c8dee2aaSAndroid Build Coastguard Worker fSimplified = true;
599*c8dee2aaSAndroid Build Coastguard Worker return;
600*c8dee2aaSAndroid Build Coastguard Worker }
601*c8dee2aaSAndroid Build Coastguard Worker
602*c8dee2aaSAndroid Build Coastguard Worker // Otherwise, if we're a point or a line, we might be able to explicitly apply some of the
603*c8dee2aaSAndroid Build Coastguard Worker // stroking (and even some of the dashing). Any other shape+style is too complicated to reduce.
604*c8dee2aaSAndroid Build Coastguard Worker if ((!fShape.isPoint() && !fShape.isLine()) || fStyle.hasNonDashPathEffect() ||
605*c8dee2aaSAndroid Build Coastguard Worker fStyle.strokeRec().isHairlineStyle()) {
606*c8dee2aaSAndroid Build Coastguard Worker return;
607*c8dee2aaSAndroid Build Coastguard Worker }
608*c8dee2aaSAndroid Build Coastguard Worker
609*c8dee2aaSAndroid Build Coastguard Worker // Tracks style simplifications, even if the geometry can't be further simplified.
610*c8dee2aaSAndroid Build Coastguard Worker bool styleSimplified = false;
611*c8dee2aaSAndroid Build Coastguard Worker if (fStyle.isDashed()) {
612*c8dee2aaSAndroid Build Coastguard Worker // For dashing a point, if the first interval is on, we can drop the dash and just draw
613*c8dee2aaSAndroid Build Coastguard Worker // the caps. For dashing a line, if every off interval is 0 length, its a stroke.
614*c8dee2aaSAndroid Build Coastguard Worker bool dropDash = false;
615*c8dee2aaSAndroid Build Coastguard Worker if (fShape.isPoint()) {
616*c8dee2aaSAndroid Build Coastguard Worker dropDash = fStyle.dashIntervalCnt() > 0 &&
617*c8dee2aaSAndroid Build Coastguard Worker SkToBool(fStyle.dashIntervals()[0]);
618*c8dee2aaSAndroid Build Coastguard Worker } else {
619*c8dee2aaSAndroid Build Coastguard Worker dropDash = true;
620*c8dee2aaSAndroid Build Coastguard Worker for (int i = 1; i < fStyle.dashIntervalCnt(); i += 2) {
621*c8dee2aaSAndroid Build Coastguard Worker if (SkToBool(fStyle.dashIntervals()[i])) {
622*c8dee2aaSAndroid Build Coastguard Worker // An off interval has non-zero length so this won't convert to a simple line
623*c8dee2aaSAndroid Build Coastguard Worker dropDash = false;
624*c8dee2aaSAndroid Build Coastguard Worker break;
625*c8dee2aaSAndroid Build Coastguard Worker }
626*c8dee2aaSAndroid Build Coastguard Worker }
627*c8dee2aaSAndroid Build Coastguard Worker }
628*c8dee2aaSAndroid Build Coastguard Worker
629*c8dee2aaSAndroid Build Coastguard Worker if (!dropDash) {
630*c8dee2aaSAndroid Build Coastguard Worker return;
631*c8dee2aaSAndroid Build Coastguard Worker }
632*c8dee2aaSAndroid Build Coastguard Worker // Fall through to modifying the shape to respect the new stroke geometry
633*c8dee2aaSAndroid Build Coastguard Worker fStyle = GrStyle(fStyle.strokeRec(), nullptr);
634*c8dee2aaSAndroid Build Coastguard Worker // Since the reduced the line or point after dashing is dependent on the caps of the dashes,
635*c8dee2aaSAndroid Build Coastguard Worker // we reset to be unclosed so we don't override the style based on joins later.
636*c8dee2aaSAndroid Build Coastguard Worker fClosed = false;
637*c8dee2aaSAndroid Build Coastguard Worker styleSimplified = true;
638*c8dee2aaSAndroid Build Coastguard Worker }
639*c8dee2aaSAndroid Build Coastguard Worker
640*c8dee2aaSAndroid Build Coastguard Worker // At this point, we're a line or point with no path effects. Any fill portion of the style
641*c8dee2aaSAndroid Build Coastguard Worker // is empty, so a fill-only style can be empty, and a stroke+fill becomes a stroke.
642*c8dee2aaSAndroid Build Coastguard Worker if (fStyle.isSimpleFill()) {
643*c8dee2aaSAndroid Build Coastguard Worker fShape.reset();
644*c8dee2aaSAndroid Build Coastguard Worker fSimplified = true;
645*c8dee2aaSAndroid Build Coastguard Worker return;
646*c8dee2aaSAndroid Build Coastguard Worker } else if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
647*c8dee2aaSAndroid Build Coastguard Worker // Stroke only
648*c8dee2aaSAndroid Build Coastguard Worker SkStrokeRec rec = fStyle.strokeRec();
649*c8dee2aaSAndroid Build Coastguard Worker rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false);
650*c8dee2aaSAndroid Build Coastguard Worker fStyle = GrStyle(rec, nullptr);
651*c8dee2aaSAndroid Build Coastguard Worker styleSimplified = true;
652*c8dee2aaSAndroid Build Coastguard Worker }
653*c8dee2aaSAndroid Build Coastguard Worker
654*c8dee2aaSAndroid Build Coastguard Worker // A point or line that was formed by a degenerate closed shape needs its style updated to
655*c8dee2aaSAndroid Build Coastguard Worker // reflect the fact that it doesn't actually produce caps.
656*c8dee2aaSAndroid Build Coastguard Worker if (fClosed) {
657*c8dee2aaSAndroid Build Coastguard Worker SkPaint::Cap cap;
658*c8dee2aaSAndroid Build Coastguard Worker if (fShape.isLine() && fStyle.strokeRec().getJoin() == SkPaint::kRound_Join) {
659*c8dee2aaSAndroid Build Coastguard Worker // As a closed shape, the line moves from a to b and back to a, producing a 180 degree
660*c8dee2aaSAndroid Build Coastguard Worker // turn. With round joins, this would make a semi-circle at each end, which is visually
661*c8dee2aaSAndroid Build Coastguard Worker // identical to a round cap on the reduced line geometry.
662*c8dee2aaSAndroid Build Coastguard Worker cap = SkPaint::kRound_Cap;
663*c8dee2aaSAndroid Build Coastguard Worker } else {
664*c8dee2aaSAndroid Build Coastguard Worker // If this were a closed line, the 180 degree turn either is a miter join that exceeds
665*c8dee2aaSAndroid Build Coastguard Worker // the miter limit and becomes a bevel, or a bevel join. In either case, the bevel shape
666*c8dee2aaSAndroid Build Coastguard Worker // of a 180 degreen corner is equivalent to a butt cap.
667*c8dee2aaSAndroid Build Coastguard Worker // - to match the SVG spec, the 0-length sides of an empty rectangle are skipped, so
668*c8dee2aaSAndroid Build Coastguard Worker // it fits this closed line description (it is not two 90 degree turns that could
669*c8dee2aaSAndroid Build Coastguard Worker // produce miter geometry).
670*c8dee2aaSAndroid Build Coastguard Worker cap = SkPaint::kButt_Cap;
671*c8dee2aaSAndroid Build Coastguard Worker }
672*c8dee2aaSAndroid Build Coastguard Worker
673*c8dee2aaSAndroid Build Coastguard Worker if (cap != fStyle.strokeRec().getCap() ||
674*c8dee2aaSAndroid Build Coastguard Worker SkPaint::kDefault_Join != fStyle.strokeRec().getJoin()) {
675*c8dee2aaSAndroid Build Coastguard Worker SkStrokeRec rec = fStyle.strokeRec();
676*c8dee2aaSAndroid Build Coastguard Worker rec.setStrokeParams(cap, SkPaint::kDefault_Join, fStyle.strokeRec().getMiter());
677*c8dee2aaSAndroid Build Coastguard Worker fStyle = GrStyle(rec, nullptr);
678*c8dee2aaSAndroid Build Coastguard Worker styleSimplified = true;
679*c8dee2aaSAndroid Build Coastguard Worker }
680*c8dee2aaSAndroid Build Coastguard Worker }
681*c8dee2aaSAndroid Build Coastguard Worker
682*c8dee2aaSAndroid Build Coastguard Worker if (fShape.isPoint()) {
683*c8dee2aaSAndroid Build Coastguard Worker // The drawn geometry is entirely based on the cap style and stroke width. A butt cap point
684*c8dee2aaSAndroid Build Coastguard Worker // doesn't draw anything, a round cap is an oval and a square cap is a square.
685*c8dee2aaSAndroid Build Coastguard Worker if (fStyle.strokeRec().getCap() == SkPaint::kButt_Cap) {
686*c8dee2aaSAndroid Build Coastguard Worker fShape.reset();
687*c8dee2aaSAndroid Build Coastguard Worker } else {
688*c8dee2aaSAndroid Build Coastguard Worker SkScalar w = fStyle.strokeRec().getWidth() / 2.f;
689*c8dee2aaSAndroid Build Coastguard Worker SkRect r = {fShape.point().fX, fShape.point().fY, fShape.point().fX, fShape.point().fY};
690*c8dee2aaSAndroid Build Coastguard Worker r.outset(w, w);
691*c8dee2aaSAndroid Build Coastguard Worker
692*c8dee2aaSAndroid Build Coastguard Worker if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
693*c8dee2aaSAndroid Build Coastguard Worker fShape.setRRect(SkRRect::MakeOval(r));
694*c8dee2aaSAndroid Build Coastguard Worker } else {
695*c8dee2aaSAndroid Build Coastguard Worker fShape.setRect(r);
696*c8dee2aaSAndroid Build Coastguard Worker }
697*c8dee2aaSAndroid Build Coastguard Worker }
698*c8dee2aaSAndroid Build Coastguard Worker } else {
699*c8dee2aaSAndroid Build Coastguard Worker // Stroked lines reduce to rectangles or round rects when they are axis-aligned. If we
700*c8dee2aaSAndroid Build Coastguard Worker // allowed rotation angle, this would work for any lines.
701*c8dee2aaSAndroid Build Coastguard Worker SkRect rect;
702*c8dee2aaSAndroid Build Coastguard Worker SkVector outset;
703*c8dee2aaSAndroid Build Coastguard Worker if (fShape.line().fP1.fY == fShape.line().fP2.fY) {
704*c8dee2aaSAndroid Build Coastguard Worker rect.fLeft = std::min(fShape.line().fP1.fX, fShape.line().fP2.fX);
705*c8dee2aaSAndroid Build Coastguard Worker rect.fRight = std::max(fShape.line().fP1.fX, fShape.line().fP2.fX);
706*c8dee2aaSAndroid Build Coastguard Worker rect.fTop = rect.fBottom = fShape.line().fP1.fY;
707*c8dee2aaSAndroid Build Coastguard Worker outset.fY = fStyle.strokeRec().getWidth() / 2.f;
708*c8dee2aaSAndroid Build Coastguard Worker outset.fX = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fY;
709*c8dee2aaSAndroid Build Coastguard Worker } else if (fShape.line().fP1.fX == fShape.line().fP2.fX) {
710*c8dee2aaSAndroid Build Coastguard Worker rect.fTop = std::min(fShape.line().fP1.fY, fShape.line().fP2.fY);
711*c8dee2aaSAndroid Build Coastguard Worker rect.fBottom = std::max(fShape.line().fP1.fY, fShape.line().fP2.fY);
712*c8dee2aaSAndroid Build Coastguard Worker rect.fLeft = rect.fRight = fShape.line().fP1.fX;
713*c8dee2aaSAndroid Build Coastguard Worker outset.fX = fStyle.strokeRec().getWidth() / 2.f;
714*c8dee2aaSAndroid Build Coastguard Worker outset.fY = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fX;
715*c8dee2aaSAndroid Build Coastguard Worker } else {
716*c8dee2aaSAndroid Build Coastguard Worker // Geometrically can't apply the style and turn into a fill, but might still be simpler
717*c8dee2aaSAndroid Build Coastguard Worker // than before based solely on changes to fStyle.
718*c8dee2aaSAndroid Build Coastguard Worker fSimplified |= styleSimplified;
719*c8dee2aaSAndroid Build Coastguard Worker return;
720*c8dee2aaSAndroid Build Coastguard Worker }
721*c8dee2aaSAndroid Build Coastguard Worker rect.outset(outset.fX, outset.fY);
722*c8dee2aaSAndroid Build Coastguard Worker if (rect.isEmpty()) {
723*c8dee2aaSAndroid Build Coastguard Worker fShape.reset();
724*c8dee2aaSAndroid Build Coastguard Worker } else if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
725*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(outset.fX == outset.fY);
726*c8dee2aaSAndroid Build Coastguard Worker fShape.setRRect(SkRRect::MakeRectXY(rect, outset.fX, outset.fY));
727*c8dee2aaSAndroid Build Coastguard Worker } else {
728*c8dee2aaSAndroid Build Coastguard Worker fShape.setRect(rect);
729*c8dee2aaSAndroid Build Coastguard Worker }
730*c8dee2aaSAndroid Build Coastguard Worker }
731*c8dee2aaSAndroid Build Coastguard Worker // If we made it here, the stroke was fully applied to the new shape so we can become a fill.
732*c8dee2aaSAndroid Build Coastguard Worker fStyle = GrStyle::SimpleFill();
733*c8dee2aaSAndroid Build Coastguard Worker fSimplified = true;
734*c8dee2aaSAndroid Build Coastguard Worker }
735