GrShape.cpp revision fb08327e592a1dd19a0c3107243ccd01f6b7f848
1/*
2 * Copyright 2016 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 "GrShape.h"
9
10GrShape& GrShape::operator=(const GrShape& that) {
11    bool wasPath = Type::kPath == fType;
12    fStyle = that.fStyle;
13    fType = that.fType;
14    switch (fType) {
15        case Type::kEmpty:
16            if (wasPath) {
17                fPath.reset();
18            }
19            break;
20        case Type::kRRect:
21            if (wasPath) {
22                fPath.reset();
23            }
24            fRRect = that.fRRect;
25            break;
26        case Type::kPath:
27            if (wasPath) {
28                *fPath.get() = *that.fPath.get();
29            } else {
30                fPath.set(*that.fPath.get());
31            }
32            break;
33    }
34    fInheritedKey.reset(that.fInheritedKey.count());
35    memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
36           sizeof(uint32_t) * fInheritedKey.count());
37    return *this;
38}
39
40int GrShape::unstyledKeySize() const {
41    if (fInheritedKey.count()) {
42        return fInheritedKey.count();
43    }
44    switch (fType) {
45        case Type::kEmpty:
46            return 1;
47        case Type::kRRect:
48            SkASSERT(!fInheritedKey.count());
49            SkASSERT(0 == SkRRect::kSizeInMemory % sizeof(uint32_t));
50            return SkRRect::kSizeInMemory / sizeof(uint32_t);
51        case Type::kPath:
52            if (fPath.get()->isVolatile()) {
53                return -1;
54            } else {
55                return 1;
56            }
57    }
58    SkFAIL("Should never get here.");
59    return 0;
60}
61
62void GrShape::writeUnstyledKey(uint32_t* key) const {
63    SkASSERT(this->unstyledKeySize());
64    SkDEBUGCODE(uint32_t* origKey = key;)
65    if (fInheritedKey.count()) {
66        memcpy(key, fInheritedKey.get(), sizeof(uint32_t) * fInheritedKey.count());
67        SkDEBUGCODE(key += fInheritedKey.count();)
68    } else {
69        switch (fType) {
70            case Type::kEmpty:
71                *key++ = 1;
72                break;
73            case Type::kRRect:
74                fRRect.writeToMemory(key);
75                key += SkRRect::kSizeInMemory / sizeof(uint32_t);
76                break;
77            case Type::kPath:
78                SkASSERT(!fPath.get()->isVolatile());
79                *key++ = fPath.get()->getGenerationID();
80                break;
81        }
82    }
83    SkASSERT(key - origKey == this->unstyledKeySize());
84}
85
86void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply) {
87    SkASSERT(!fInheritedKey.count());
88    // If the output shape turns out to be simple, then we will just use its geometric key
89    if (Type::kPath == fType) {
90        // We want ApplyFullStyle(ApplyPathEffect(shape)) to have the same key as
91        // ApplyFullStyle(shape).
92        // The full key is structured as (geo,path_effect,stroke).
93        // If we do ApplyPathEffect we get get,path_effect as the inherited key. If we then
94        // do ApplyFullStyle we'll memcpy geo,path_effect into the new inherited key
95        // and then append the style key (which should now be stroke only) at the end.
96        int parentCnt = parent.fInheritedKey.count();
97        bool useParentGeoKey = !parentCnt;
98        if (useParentGeoKey) {
99            parentCnt = parent.unstyledKeySize();
100            if (parentCnt < 0) {
101                // The parent's geometry has no key so we will have no key.
102                fPath.get()->setIsVolatile(true);
103                return;
104            }
105        }
106        int styleCnt = GrStyle::KeySize(parent.fStyle, apply);
107        if (styleCnt < 0) {
108            // The style doesn't allow a key, set the path to volatile so that we fail when
109            // we try to get a key for the shape.
110            fPath.get()->setIsVolatile(true);
111            return;
112        }
113        fInheritedKey.reset(parentCnt + styleCnt);
114        if (useParentGeoKey) {
115            // This will be the geo key.
116            parent.writeUnstyledKey(fInheritedKey.get());
117        } else {
118            // This should be (geo,path_effect).
119            memcpy(fInheritedKey.get(), parent.fInheritedKey.get(),
120                   parentCnt * sizeof(uint32_t));
121        }
122        // Now turn (geo,path_effect) or (geo) into (geo,path_effect,stroke)
123        GrStyle::WriteKey(fInheritedKey.get() + parentCnt, parent.fStyle, apply);
124    }
125}
126
127GrShape::GrShape(const GrShape& that) : fType(that.fType), fStyle(that.fStyle) {
128    switch (fType) {
129        case Type::kEmpty:
130            return;
131        case Type::kRRect:
132            fRRect = that.fRRect;
133            return;
134        case Type::kPath:
135            fPath.set(*that.fPath.get());
136            return;
137    }
138    fInheritedKey.reset(that.fInheritedKey.count());
139    memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
140           sizeof(uint32_t) * fInheritedKey.count());
141}
142
143GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply) {
144    if (!parent.style().applies() ||
145        (GrStyle::Apply::kPathEffectOnly == apply && !parent.style().pathEffect())) {
146        fType = Type::kEmpty;
147        *this = parent;
148        return;
149    }
150
151    SkPathEffect* pe = parent.fStyle.pathEffect();
152    SkTLazy<SkPath> tmpPath;
153    const GrShape* parentForKey = &parent;
154    SkTLazy<GrShape> tmpParent;
155    fType = Type::kPath;
156    fPath.init();
157    if (pe) {
158        SkPath* srcForPathEffect;
159        if (parent.fType == Type::kPath) {
160            srcForPathEffect = parent.fPath.get();
161        } else {
162            srcForPathEffect = tmpPath.init();
163            parent.asPath(tmpPath.get());
164        }
165        // Should we consider bounds? Would have to include in key, but it'd be nice to know
166        // if the bounds actually modified anything before including in key.
167        SkStrokeRec strokeRec = parent.fStyle.strokeRec();
168        if (!pe->filterPath(fPath.get(), *srcForPathEffect, &strokeRec, nullptr)) {
169            // Make an empty unstyled shape if filtering fails.
170            fType = Type::kEmpty;
171            fStyle = GrStyle();
172            fPath.reset();
173            return;
174        }
175        if (GrStyle::Apply::kPathEffectAndStrokeRec == apply) {
176            if (strokeRec.needToApply()) {
177                // The intermediate shape may not be a general path. If we we're just applying
178                // the path effect then attemptToReduceFromPath would catch it. This means that
179                // when we subsequently applied the remaining strokeRec we would have a non-path
180                // parent shape that would be used to determine the the stroked path's key.
181                // We detect that case here and change parentForKey to a temporary that represents
182                // the simpler shape so that applying both path effect and the strokerec all at
183                // once produces the same key.
184                SkRRect rrect;
185                Type parentType = AttemptToReduceFromPathImpl(*fPath.get(), &rrect, nullptr,
186                                                              strokeRec);
187                switch (parentType) {
188                    case Type::kEmpty:
189                        tmpParent.init();
190                        parentForKey = tmpParent.get();
191                        break;
192                    case Type::kRRect:
193                        tmpParent.init(rrect, GrStyle(strokeRec, nullptr));
194                        parentForKey = tmpParent.get();
195                    case Type::kPath:
196                        break;
197                }
198                SkAssertResult(strokeRec.applyToPath(fPath.get(), *fPath.get()));
199            } else {
200                fStyle = GrStyle(strokeRec, nullptr);
201            }
202        } else {
203            fStyle = GrStyle(strokeRec, nullptr);
204        }
205    } else {
206        const SkPath* srcForStrokeRec;
207        if (parent.fType == Type::kPath) {
208            srcForStrokeRec = parent.fPath.get();
209        } else {
210            srcForStrokeRec = tmpPath.init();
211            parent.asPath(tmpPath.get());
212        }
213        SkASSERT(parent.fStyle.strokeRec().needToApply());
214        SkAssertResult(parent.fStyle.strokeRec().applyToPath(fPath.get(), *srcForStrokeRec));
215        fStyle.resetToInitStyle(SkStrokeRec::kFill_InitStyle);
216    }
217    this->attemptToReduceFromPath();
218    this->setInheritedKey(*parentForKey, apply);
219}
220