Use a union inside GrShape to hold geometry
authorbsalomon <bsalomon@google.com>
Mon, 27 Jun 2016 17:00:19 +0000 (10:00 -0700)
committerCommit bot <commit-bot@chromium.org>
Mon, 27 Jun 2016 17:00:19 +0000 (10:00 -0700)
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2094023002

Review-Url: https://codereview.chromium.org/2094023002

src/gpu/GrShape.cpp
src/gpu/GrShape.h

index b40d696..45ddb77 100644 (file)
@@ -8,31 +8,19 @@
 #include "GrShape.h"
 
 GrShape& GrShape::operator=(const GrShape& that) {
-    bool wasPath = Type::kPath == fType;
     fStyle = that.fStyle;
-    fType = that.fType;
+    this->changeType(that.fType, Type::kPath == that.fType ? &that.path() : nullptr);
     switch (fType) {
         case Type::kEmpty:
-            if (wasPath) {
-                fPath.reset();
-            }
             break;
         case Type::kRRect:
-            if (wasPath) {
-                fPath.reset();
-            }
-            fRRect = that.fRRect;
-            fRRectDir = that.fRRectDir;
-            fRRectStart = that.fRRectStart;
-            fRRectIsInverted = that.fRRectIsInverted;
+            fRRectData.fRRect = that.fRRectData.fRRect;
+            fRRectData.fDir = that.fRRectData.fDir;
+            fRRectData.fStart = that.fRRectData.fStart;
+            fRRectData.fInverted = that.fRRectData.fInverted;
             break;
         case Type::kPath:
-            if (wasPath) {
-                *fPath.get() = *that.fPath.get();
-            } else {
-                fPath.set(*that.fPath.get());
-            }
-            fPathGenID = that.fPathGenID;
+            fPathData.fGenID = that.fPathData.fGenID;
             break;
     }
     fInheritedKey.reset(that.fInheritedKey.count());
@@ -47,9 +35,9 @@ const SkRect& GrShape::bounds() const {
         case Type::kEmpty:
             return kEmpty;
         case Type::kRRect:
-            return fRRect.getBounds();
+            return fRRectData.fRRect.getBounds();
         case Type::kPath:
-            return fPath.get()->getBounds();
+            return this->path().getBounds();
     }
     SkFAIL("Unknown shape type");
     return kEmpty;
@@ -76,7 +64,7 @@ int GrShape::unstyledKeySize() const {
             // + 1 for the direction, start index, and inverseness.
             return SkRRect::kSizeInMemory / sizeof(uint32_t) + 1;
         case Type::kPath:
-            if (0 == fPathGenID) {
+            if (0 == fPathData.fGenID) {
                 return -1;
             } else {
                 // The key is the path ID and fill type.
@@ -99,19 +87,19 @@ void GrShape::writeUnstyledKey(uint32_t* key) const {
                 *key++ = 1;
                 break;
             case Type::kRRect:
-                fRRect.writeToMemory(key);
+                fRRectData.fRRect.writeToMemory(key);
                 key += SkRRect::kSizeInMemory / sizeof(uint32_t);
-                *key = (fRRectDir == SkPath::kCCW_Direction) ? (1 << 31) : 0;
-                *key |= fRRectIsInverted ? (1 << 30) : 0;
-                *key++ |= fRRectStart;
-                SkASSERT(fRRectStart < 8);
+                *key = (fRRectData.fDir == SkPath::kCCW_Direction) ? (1 << 31) : 0;
+                *key |= fRRectData.fInverted ? (1 << 30) : 0;
+                *key++ |= fRRectData.fStart;
+                SkASSERT(fRRectData.fStart < 8);
                 break;
             case Type::kPath:
-                SkASSERT(fPathGenID);
-                *key++ = fPathGenID;
+                SkASSERT(fPathData.fGenID);
+                *key++ = fPathData.fGenID;
                 // We could canonicalize the fill rule for paths that don't differentiate between
                 // even/odd or winding fill (e.g. convex).
-                *key++ = fPath.get()->getFillType();
+                *key++ = this->path().getFillType();
                 break;
         }
     }
@@ -134,7 +122,7 @@ void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply, SkSca
             parentCnt = parent.unstyledKeySize();
             if (parentCnt < 0) {
                 // The parent's geometry has no key so we will have no key.
-                fPathGenID = 0;
+                fPathData.fGenID = 0;
                 return;
             }
         }
@@ -146,7 +134,7 @@ void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply, SkSca
         if (styleCnt < 0) {
             // The style doesn't allow a key, set the path gen ID to 0 so that we fail when
             // we try to get a key for the shape.
-            fPathGenID = 0;
+            fPathData.fGenID = 0;
             return;
         }
         fInheritedKey.reset(parentCnt + styleCnt);
@@ -164,19 +152,20 @@ void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply, SkSca
     }
 }
 
-GrShape::GrShape(const GrShape& that) : fType(that.fType), fStyle(that.fStyle) {
+GrShape::GrShape(const GrShape& that) : fStyle(that.fStyle) {
+    const SkPath* thatPath = Type::kPath == that.fType ? &that.fPathData.fPath : nullptr;
+    this->initType(that.fType, thatPath);
     switch (fType) {
         case Type::kEmpty:
             break;
         case Type::kRRect:
-            fRRect = that.fRRect;
-            fRRectDir = that.fRRectDir;
-            fRRectStart = that.fRRectStart;
-            fRRectIsInverted = that.fRRectIsInverted;
+            fRRectData.fRRect = that.fRRectData.fRRect;
+            fRRectData.fDir = that.fRRectData.fDir;
+            fRRectData.fStart = that.fRRectData.fStart;
+            fRRectData.fInverted = that.fRRectData.fInverted;
             break;
         case Type::kPath:
-            fPath.set(*that.fPath.get());
-            fPathGenID = that.fPathGenID;
+            fPathData.fGenID = that.fPathData.fGenID;
             break;
     }
     fInheritedKey.reset(that.fInheritedKey.count());
@@ -191,7 +180,7 @@ GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) {
     // stroke of a rect).
     if (!parent.style().applies() ||
         (GrStyle::Apply::kPathEffectOnly == apply && !parent.style().pathEffect())) {
-        fType = Type::kEmpty;
+        this->initType(Type::kEmpty);
         *this = parent;
         return;
     }
@@ -200,12 +189,12 @@ GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) {
     SkTLazy<SkPath> tmpPath;
     const GrShape* parentForKey = &parent;
     SkTLazy<GrShape> tmpParent;
-    fType = Type::kPath;
-    fPath.init();
+    this->initType(Type::kPath);
+    fPathData.fGenID = 0;
     if (pe) {
-        SkPath* srcForPathEffect;
+        const SkPath* srcForPathEffect;
         if (parent.fType == Type::kPath) {
-            srcForPathEffect = parent.fPath.get();
+            srcForPathEffect = &parent.path();
         } else {
             srcForPathEffect = tmpPath.init();
             parent.asPath(tmpPath.get());
@@ -213,18 +202,19 @@ GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) {
         // Should we consider bounds? Would have to include in key, but it'd be nice to know
         // if the bounds actually modified anything before including in key.
         SkStrokeRec strokeRec = parent.fStyle.strokeRec();
-        if (!parent.fStyle.applyPathEffectToPath(fPath.get(), &strokeRec, *srcForPathEffect,
+        if (!parent.fStyle.applyPathEffectToPath(&this->path(), &strokeRec, *srcForPathEffect,
                                                  scale)) {
             // If the path effect fails then we continue as though there was no path effect.
             // If the original was a rrect that we couldn't canonicalize because of the path
             // effect, then do so now.
-            if (parent.fType == Type::kRRect && (parent.fRRectDir != kDefaultRRectDir ||
-                                                 parent.fRRectStart != kDefaultRRectStart)) {
+            if (parent.fType == Type::kRRect && (parent.fRRectData.fDir != kDefaultRRectDir ||
+                                                 parent.fRRectData.fStart != kDefaultRRectStart)) {
                 SkASSERT(srcForPathEffect == tmpPath.get());
                 tmpPath.get()->reset();
-                tmpPath.get()->addRRect(parent.fRRect, kDefaultRRectDir, kDefaultRRectDir);
+                tmpPath.get()->addRRect(parent.fRRectData.fRRect, kDefaultRRectDir,
+                                        kDefaultRRectDir);
             }
-            *fPath.get() = *srcForPathEffect;
+            this->path() = *srcForPathEffect;
         }
         // A path effect has access to change the res scale but we aren't expecting it to and it
         // would mess up our key computation.
@@ -237,14 +227,14 @@ GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) {
             // We detect that case here and change parentForKey to a temporary that represents
             // the simpler shape so that applying both path effect and the strokerec all at
             // once produces the same key.
-            tmpParent.init(*fPath.get(), GrStyle(strokeRec, nullptr));
+            tmpParent.init(this->path(), GrStyle(strokeRec, nullptr));
             tmpParent.get()->setInheritedKey(parent, GrStyle::Apply::kPathEffectOnly, scale);
             if (!tmpPath.isValid()) {
                 tmpPath.init();
             }
             tmpParent.get()->asPath(tmpPath.get());
             SkStrokeRec::InitStyle fillOrHairline;
-            SkAssertResult(tmpParent.get()->style().applyToPath(fPath.get(), &fillOrHairline,
+            SkAssertResult(tmpParent.get()->style().applyToPath(&this->path(), &fillOrHairline,
                                                                 *tmpPath.get(), scale));
             fStyle.resetToInitStyle(fillOrHairline);
             parentForKey = tmpParent.get();
@@ -254,7 +244,7 @@ GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) {
     } else {
         const SkPath* srcForParentStyle;
         if (parent.fType == Type::kPath) {
-            srcForParentStyle = parent.fPath.get();
+            srcForParentStyle = &parent.path();
         } else {
             srcForParentStyle = tmpPath.init();
             parent.asPath(tmpPath.get());
@@ -262,7 +252,7 @@ GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) {
         SkStrokeRec::InitStyle fillOrHairline;
         SkASSERT(parent.fStyle.applies());
         SkASSERT(!parent.fStyle.pathEffect());
-        SkAssertResult(parent.fStyle.applyToPath(fPath.get(), &fillOrHairline, *srcForParentStyle,
+        SkAssertResult(parent.fStyle.applyToPath(&this->path(), &fillOrHairline, *srcForParentStyle,
                                                  scale));
         fStyle.resetToInitStyle(fillOrHairline);
     }
@@ -271,65 +261,72 @@ GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) {
 }
 
 void GrShape::attemptToSimplifyPath() {
-    SkASSERT(Type::kPath == fType);
     SkRect rect;
-    if (fPath.get()->isEmpty()) {
-        fType = Type::kEmpty;
-    } else if (fPath.get()->isRRect(&fRRect, &fRRectDir, &fRRectStart)) {
+    SkRRect rrect;
+    SkPath::Direction rrectDir;
+    unsigned rrectStart;
+    bool inverted = this->path().isInverseFillType();
+    if (this->path().isEmpty()) {
+        this->changeType(Type::kEmpty);
+    } else if (this->path().isRRect(&rrect, &rrectDir, &rrectStart)) {
+        this->changeType(Type::kRRect);
+        fRRectData.fRRect = rrect;
+        fRRectData.fDir = rrectDir;
+        fRRectData.fStart = rrectStart;
+        fRRectData.fInverted = inverted;
         // Currently SkPath does not acknowledge that empty, rect, or oval subtypes as rrects.
-        SkASSERT(!fRRect.isEmpty());
-        SkASSERT(fRRect.getType() != SkRRect::kRect_Type);
-        SkASSERT(fRRect.getType() != SkRRect::kOval_Type);
-        fRRectIsInverted = fPath.get()->isInverseFillType();
-        fType = Type::kRRect;
-    } else if (fPath.get()->isOval(&rect, &fRRectDir, &fRRectStart)) {
-        fRRect.setOval(rect);
-        fRRectIsInverted = fPath.get()->isInverseFillType();
+        SkASSERT(!fRRectData.fRRect.isEmpty());
+        SkASSERT(fRRectData.fRRect.getType() != SkRRect::kRect_Type);
+        SkASSERT(fRRectData.fRRect.getType() != SkRRect::kOval_Type);
+    } else if (this->path().isOval(&rect, &rrectDir, &rrectStart)) {
+        this->changeType(Type::kRRect);
+        fRRectData.fRRect.setOval(rect);
+        fRRectData.fDir = rrectDir;
+        fRRectData.fInverted = inverted;
         // convert from oval indexing to rrect indexiing.
-        fRRectStart *= 2;
-        fType = Type::kRRect;
-    } else if (SkPathPriv::IsSimpleClosedRect(*fPath.get(), &rect, &fRRectDir, &fRRectStart)) {
+        fRRectData.fStart = 2 * rrectStart;
+    } else if (SkPathPriv::IsSimpleClosedRect(this->path(), &rect, &rrectDir, &rrectStart)) {
+        this->changeType(Type::kRRect);
         // When there is a path effect we restrict rect detection to the narrower API that
         // gives us the starting position. Otherwise, we will retry with the more aggressive
         // isRect().
-        fRRect.setRect(rect);
-        fRRectIsInverted = fPath.get()->isInverseFillType();
+        fRRectData.fRRect.setRect(rect);
+        fRRectData.fInverted = inverted;
+        fRRectData.fDir = rrectDir;
         // convert from rect indexing to rrect indexiing.
-        fRRectStart *= 2;
-        fType = Type::kRRect;
+        fRRectData.fStart = 2 * rrectStart;
     } else if (!this->style().hasPathEffect()) {
         bool closed;
-        if (fPath.get()->isRect(&rect, &closed, nullptr)) {
+        if (this->path().isRect(&rect, &closed, nullptr)) {
             if (closed || this->style().isSimpleFill()) {
-                fRRect.setRect(rect);
+                this->changeType(Type::kRRect);
+                fRRectData.fRRect.setRect(rect);
                 // Since there is no path effect the dir and start index is immaterial.
-                fRRectDir = kDefaultRRectDir;
-                fRRectStart = kDefaultRRectStart;
+                fRRectData.fDir = kDefaultRRectDir;
+                fRRectData.fStart = kDefaultRRectStart;
                 // There isn't dashing so we will have to preserver inverseness.
-                fRRectIsInverted = fPath.get()->isInverseFillType();
-                fType = Type::kRRect;
+                fRRectData.fInverted = inverted;
             }
         }
     }
     if (Type::kPath != fType) {
-        fPath.reset();
         fInheritedKey.reset(0);
         if (Type::kRRect == fType) {
             this->attemptToSimplifyRRect();
         }
     } else {
-        if (fInheritedKey.count() || fPath.get()->isVolatile()) {
-            fPathGenID = 0;
+        if (fInheritedKey.count() || this->path().isVolatile()) {
+            fPathData.fGenID = 0;
         } else {
-            fPathGenID = fPath.get()->getGenerationID();
+            fPathData.fGenID = this->path().getGenerationID();
         }
         if (this->style().isSimpleFill()) {
             // Filled paths are treated as though all their contours were closed.
             // Since SkPath doesn't track individual contours, this will only close the last. :(
             // There is no point in closing lines, though, since they loose their line-ness.
-            if (!fPath.get()->isLine(nullptr)) {
-                fPath.get()->close();
-                fPath.get()->setIsVolatile(true);
+            if (!this->path().isLine(nullptr)) {
+                this->path().close();
+                this->path().setIsVolatile(true);
             }
         }
         if (!this->style().hasNonDashPathEffect()) {
@@ -337,19 +334,19 @@ void GrShape::attemptToSimplifyPath() {
                 this->style().strokeRec().getStyle() == SkStrokeRec::kHairline_Style) {
                 // Stroke styles don't differentiate between winding and even/odd.
                 // Moreover, dashing ignores inverseness (skbug.com/5421)
-                bool inverse = !this->style().isDashed() && fPath.get()->isInverseFillType();
+                bool inverse = !this->style().isDashed() && this->path().isInverseFillType();
                 if (inverse) {
-                    fPath.get()->setFillType(kDefaultPathInverseFillType);
+                    this->path().setFillType(kDefaultPathInverseFillType);
                 } else {
-                    fPath.get()->setFillType(kDefaultPathFillType);
+                    this->path().setFillType(kDefaultPathFillType);
                 }
-            } else if (fPath.get()->isConvex()) {
+            } else if (this->path().isConvex()) {
                 // There is no distinction between even/odd and non-zero winding count for convex
                 // paths.
-                if (fPath.get()->isInverseFillType()) {
-                    fPath.get()->setFillType(kDefaultPathInverseFillType);
+                if (this->path().isInverseFillType()) {
+                    this->path().setFillType(kDefaultPathInverseFillType);
                 } else {
-                    fPath.get()->setFillType(kDefaultPathFillType);
+                    this->path().setFillType(kDefaultPathFillType);
                 }
             }
         }
@@ -359,15 +356,15 @@ void GrShape::attemptToSimplifyPath() {
 void GrShape::attemptToSimplifyRRect() {
     SkASSERT(Type::kRRect == fType);
     SkASSERT(!fInheritedKey.count());
-    if (fRRect.isEmpty()) {
+    if (fRRectData.fRRect.isEmpty()) {
         fType = Type::kEmpty;
         return;
     }
     if (!this->style().hasPathEffect()) {
-        fRRectDir = kDefaultRRectDir;
-        fRRectStart = kDefaultRRectStart;
+        fRRectData.fDir = kDefaultRRectDir;
+        fRRectData.fStart = kDefaultRRectStart;
     } else if (fStyle.isDashed()) {
         // Dashing ignores the inverseness (currently). skbug.com/5421
-        fRRectIsInverted = false;
+        fRRectData.fInverted = false;
     }
 }
index 139b6ed..87ee26e 100644 (file)
  */
 class GrShape {
 public:
-    GrShape() : fType(Type::kEmpty) {}
+    GrShape() { this->initType(Type::kEmpty); }
 
-    explicit GrShape(const SkPath& path)
-        : fType(Type::kPath)
-        , fPath(&path) {
-        this->attemptToSimplifyPath();
-    }
+    explicit GrShape(const SkPath& path) : GrShape(path, GrStyle::SimpleFill()) {}
 
-    explicit GrShape(const SkRRect& rrect)
-        : fType(Type::kRRect)
-        , fRRect(rrect)
-        , fRRectIsInverted(false) {
-        fRRectStart = DefaultRRectDirAndStartIndex(rrect, false, &fRRectDir);
-        this->attemptToSimplifyRRect();
-    }
+    explicit GrShape(const SkRRect& rrect) : GrShape(rrect, GrStyle::SimpleFill()) {}
 
-    explicit GrShape(const SkRect& rect)
-        : fType(Type::kRRect)
-        , fRRect(SkRRect::MakeRect(rect))
-        , fRRectIsInverted(false) {
-        fRRectStart = DefaultRectDirAndStartIndex(rect, false, &fRRectDir);
-        this->attemptToSimplifyRRect();
-    }
+    explicit GrShape(const SkRect& rect) : GrShape(rect, GrStyle::SimpleFill()) {}
 
-    GrShape(const SkPath& path, const GrStyle& style)
-        : fType(Type::kPath)
-        , fPath(&path)
-        , fStyle(style) {
+    GrShape(const SkPath& path, const GrStyle& style) : fStyle(style) {
+        this->initType(Type::kPath, &path);
         this->attemptToSimplifyPath();
     }
 
     GrShape(const SkRRect& rrect, const GrStyle& style)
-        : fType(Type::kRRect)
-        , fRRect(rrect)
-        , fRRectIsInverted(false)
-        , fStyle(style) {
-        fRRectStart = DefaultRRectDirAndStartIndex(rrect, style.hasPathEffect(), &fRRectDir);
+        : fStyle(style) {
+        this->initType(Type::kRRect);
+        fRRectData.fRRect = rrect;
+        fRRectData.fInverted = false;
+        fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, style.hasPathEffect(),
+                                                         &fRRectData.fDir);
         this->attemptToSimplifyRRect();
     }
 
     GrShape(const SkRRect& rrect, SkPath::Direction dir, unsigned start, bool inverted,
             const GrStyle& style)
-        : fType(Type::kRRect)
-        , fRRect(rrect)
-        , fRRectIsInverted(inverted)
-        , fStyle(style) {
+        : fStyle(style) {
+        this->initType(Type::kRRect);
+        fRRectData.fRRect = rrect;
+        fRRectData.fInverted = inverted;
         if (style.pathEffect()) {
-            fRRectDir = dir;
-            fRRectStart = start;
-            if (fRRect.getType() == SkRRect::kRect_Type) {
-                fRRectStart = (fRRectStart + 1) & 0b110;
-            } else if (fRRect.getType() == SkRRect::kOval_Type) {
-                fRRectStart &= 0b110;
+            fRRectData.fDir = dir;
+            fRRectData.fStart = start;
+            if (fRRectData.fRRect.getType() == SkRRect::kRect_Type) {
+                fRRectData.fStart = (fRRectData.fStart + 1) & 0b110;
+            } else if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
+                fRRectData.fStart &= 0b110;
             }
         } else {
-            fRRectStart = DefaultRRectDirAndStartIndex(rrect, false, &fRRectDir);
+            fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, false, &fRRectData.fDir);
         }
         this->attemptToSimplifyRRect();
     }
 
     GrShape(const SkRect& rect, const GrStyle& style)
-        : fType(Type::kRRect)
-        , fRRect(SkRRect::MakeRect(rect))
-        , fRRectIsInverted(false)
-        , fStyle(style) {
-        fRRectStart = DefaultRectDirAndStartIndex(rect, style.hasPathEffect(), &fRRectDir);
+        : fStyle(style) {
+        this->initType(Type::kRRect);
+        fRRectData.fRRect = SkRRect::MakeRect(rect);
+        fRRectData.fInverted = false;
+        fRRectData.fStart = DefaultRectDirAndStartIndex(rect, style.hasPathEffect(),
+                                                        &fRRectData.fDir);
         this->attemptToSimplifyRRect();
     }
 
-    GrShape(const SkPath& path, const SkPaint& paint)
-        : fType(Type::kPath)
-        , fPath(&path)
-        , fStyle(paint) {
+    GrShape(const SkPath& path, const SkPaint& paint) : fStyle(paint) {
+        this->initType(Type::kPath, &path);
         this->attemptToSimplifyPath();
     }
 
     GrShape(const SkRRect& rrect, const SkPaint& paint)
-        : fType(Type::kRRect)
-        , fRRect(rrect)
-        , fRRectIsInverted(false)
-        , fStyle(paint) {
-        fRRectStart = DefaultRRectDirAndStartIndex(rrect, fStyle.hasPathEffect(), &fRRectDir);
+        : fStyle(paint) {
+        this->initType(Type::kRRect);
+        fRRectData.fRRect = rrect;
+        fRRectData.fInverted = false;
+        fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, fStyle.hasPathEffect(),
+                                                         &fRRectData.fDir);
         this->attemptToSimplifyRRect();
     }
 
     GrShape(const SkRect& rect, const SkPaint& paint)
-        : fType(Type::kRRect)
-        , fRRect(SkRRect::MakeRect(rect))
-        , fRRectIsInverted(false)
-        , fStyle(paint) {
-        fRRectStart = DefaultRectDirAndStartIndex(rect, fStyle.hasPathEffect(), &fRRectDir);
+        : fStyle(paint) {
+        this->initType(Type::kRRect);
+        fRRectData.fRRect = SkRRect::MakeRect(rect);
+        fRRectData.fInverted = false;
+        fRRectData.fStart = DefaultRectDirAndStartIndex(rect, fStyle.hasPathEffect(),
+                                                        &fRRectData.fDir);
         this->attemptToSimplifyRRect();
     }
 
     GrShape(const GrShape&);
     GrShape& operator=(const GrShape& that);
 
-    ~GrShape() {
-        if (Type::kPath == fType) {
-            fPath.reset();
-        }
-    }
+    ~GrShape() { this->changeType(Type::kEmpty); }
 
     const GrStyle& style() const { return fStyle; }
 
@@ -154,16 +134,16 @@ public:
             return false;
         }
         if (rrect) {
-            *rrect = fRRect;
+            *rrect = fRRectData.fRRect;
         }
         if (dir) {
-            *dir = fRRectDir;
+            *dir = fRRectData.fDir;
         }
         if (start) {
-            *start = fRRectStart;
+            *start = fRRectData.fStart;
         }
         if (inverted) {
-            *inverted = fRRectIsInverted;
+            *inverted = fRRectData.fInverted;
         }
         return true;
     }
@@ -176,7 +156,7 @@ public:
          if (fType != Type::kPath) {
              return false;
          }
-         return fPath.get()->isLine(pts);
+         return this->path().isLine(pts);
      }
 
     /** Returns the unstyled geometry as a path. */
@@ -187,16 +167,16 @@ public:
                 break;
             case Type::kRRect:
                 out->reset();
-                out->addRRect(fRRect, fRRectDir, fRRectStart);
+                out->addRRect(fRRectData.fRRect, fRRectData.fDir, fRRectData.fStart);
                 // Below matches the fill type that attemptToSimplifyPath uses.
-                if (fRRectIsInverted) {
+                if (fRRectData.fInverted) {
                     out->setFillType(kDefaultPathInverseFillType);
                 } else {
                     out->setFillType(kDefaultPathFillType);
                 }
                 break;
             case Type::kPath:
-                *out = *fPath.get();
+                *out = this->path();
                 break;
         }
     }
@@ -235,8 +215,8 @@ public:
                 // thus doesn't give the correct answer for stroked paths, hence we also check
                 // whether the path is either filled or closed. Convex paths may only have one
                 // contour hence isLastContourClosed() is a sufficient for a convex path.
-                return (this->style().isSimpleFill() || fPath.get()->isLastContourClosed()) &&
-                       fPath.get()->isConvex();
+                return (this->style().isSimpleFill() || this->path().isLastContourClosed()) &&
+                        this->path().isConvex();
         }
         return false;
     }
@@ -249,10 +229,10 @@ public:
                 ret = false;
                 break;
             case Type::kRRect:
-                ret = fRRectIsInverted;
+                ret = fRRectData.fInverted;
                 break;
             case Type::kPath:
-                ret = this->fPath.get()->isInverseFillType();
+                ret = this->path().isInverseFillType();
                 break;
         }
         // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
@@ -286,7 +266,7 @@ public:
                 return true;
             case Type::kPath:
                 // SkPath doesn't keep track of the closed status of each contour.
-                return SkPathPriv::IsClosedSingleContour(*fPath.get());
+                return SkPathPriv::IsClosedSingleContour(this->path());
         }
         return false;
     }
@@ -296,14 +276,14 @@ public:
             case Type::kEmpty:
                 return 0;
             case Type::kRRect:
-                if (fRRect.getType() == SkRRect::kOval_Type) {
+                if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
                     return SkPath::kConic_SegmentMask;
-                } else if (fRRect.getType() == SkRRect::kRect_Type) {
+                } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type) {
                     return SkPath::kLine_SegmentMask;
                 }
                 return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
             case Type::kPath:
-                return fPath.get()->getSegmentMasks();
+                return this->path().getSegmentMasks();
         }
         return 0;
     }
@@ -330,6 +310,41 @@ private:
         kPath,
     };
 
+    void initType(Type type, const SkPath* path = nullptr) {
+        fType = Type::kEmpty;
+        this->changeType(type, path);
+    }
+
+    void changeType(Type type, const SkPath* path = nullptr) {
+        bool wasPath = Type::kPath == fType;
+        fType = type;
+        bool isPath = Type::kPath == type;
+        SkASSERT(!path || isPath);
+        if (wasPath && !isPath) {
+            fPathData.fPath.~SkPath();
+        } else if (!wasPath && isPath) {
+            if (path) {
+                new (&fPathData.fPath) SkPath(*path);
+            } else {
+                new (&fPathData.fPath) SkPath();
+            }
+        } else if (isPath && path) {
+            fPathData.fPath = *path;
+        }
+        // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath.
+        fPathData.fGenID = 0;
+    }
+
+    SkPath& path() {
+        SkASSERT(Type::kPath == fType);
+        return fPathData.fPath;
+    }
+
+    const SkPath& path() const {
+        SkASSERT(Type::kPath == fType);
+        return fPathData.fPath;
+    }
+
     /** Constructor used by the applyStyle() function */
     GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
 
@@ -393,13 +408,19 @@ private:
     }
 
     Type                        fType;
-    SkRRect                     fRRect;
-    SkPath::Direction           fRRectDir;
-    unsigned                    fRRectStart;
-    bool                        fRRectIsInverted;
-    SkTLazy<SkPath>             fPath;
-    // Gen ID of the original path (fPath may be modified)
-    int32_t                     fPathGenID = 0;
+    union {
+        struct {
+            SkRRect                     fRRect;
+            SkPath::Direction           fDir;
+            unsigned                    fStart;
+            bool                        fInverted;
+        } fRRectData;
+        struct {
+            SkPath                      fPath;
+            // Gen ID of the original path (fPath may be modified)
+            int32_t                     fGenID;
+        } fPathData;
+    };
     GrStyle                     fStyle;
     SkAutoSTArray<8, uint32_t>  fInheritedKey;
 };