Home | History | Annotate | Download | only in gpu
      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 
     10 #include <utility>
     11 
     12 GrShape& GrShape::operator=(const GrShape& that) {
     13     fStyle = that.fStyle;
     14     this->changeType(that.fType, Type::kPath == that.fType ? &that.path() : nullptr);
     15     switch (fType) {
     16         case Type::kEmpty:
     17             break;
     18         case Type::kInvertedEmpty:
     19             break;
     20         case Type::kRRect:
     21             fRRectData = that.fRRectData;
     22             break;
     23         case Type::kArc:
     24             fArcData = that.fArcData;
     25             break;
     26         case Type::kLine:
     27             fLineData = that.fLineData;
     28             break;
     29         case Type::kPath:
     30             fPathData.fGenID = that.fPathData.fGenID;
     31             break;
     32     }
     33     fInheritedKey.reset(that.fInheritedKey.count());
     34     sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
     35                       sizeof(uint32_t) * fInheritedKey.count());
     36     if (that.fInheritedPathForListeners.isValid()) {
     37         fInheritedPathForListeners.set(*that.fInheritedPathForListeners.get());
     38     } else {
     39         fInheritedPathForListeners.reset();
     40     }
     41     return *this;
     42 }
     43 
     44 static bool flip_inversion(bool originalIsInverted, GrShape::FillInversion inversion) {
     45     switch (inversion) {
     46         case GrShape::FillInversion::kPreserve:
     47             return false;
     48         case GrShape::FillInversion::kFlip:
     49             return true;
     50         case GrShape::FillInversion::kForceInverted:
     51             return !originalIsInverted;
     52         case GrShape::FillInversion::kForceNoninverted:
     53             return originalIsInverted;
     54     }
     55     return false;
     56 }
     57 
     58 static bool is_inverted(bool originalIsInverted, GrShape::FillInversion inversion) {
     59     switch (inversion) {
     60         case GrShape::FillInversion::kPreserve:
     61             return originalIsInverted;
     62         case GrShape::FillInversion::kFlip:
     63             return !originalIsInverted;
     64         case GrShape::FillInversion::kForceInverted:
     65             return true;
     66         case GrShape::FillInversion::kForceNoninverted:
     67             return false;
     68     }
     69     return false;
     70 }
     71 
     72 GrShape GrShape::MakeFilled(const GrShape& original, FillInversion inversion) {
     73     if (original.style().isSimpleFill() && !flip_inversion(original.inverseFilled(), inversion)) {
     74         // By returning the original rather than falling through we can preserve any inherited style
     75         // key. Otherwise, we wipe it out below since the style change invalidates it.
     76         return original;
     77     }
     78     GrShape result;
     79     if (original.fInheritedPathForListeners.isValid()) {
     80         result.fInheritedPathForListeners.set(*original.fInheritedPathForListeners.get());
     81     }
     82     switch (original.fType) {
     83         case Type::kRRect:
     84             result.fType = original.fType;
     85             result.fRRectData.fRRect = original.fRRectData.fRRect;
     86             result.fRRectData.fDir = kDefaultRRectDir;
     87             result.fRRectData.fStart = kDefaultRRectStart;
     88             result.fRRectData.fInverted = is_inverted(original.fRRectData.fInverted, inversion);
     89             break;
     90         case Type::kArc:
     91             result.fType = original.fType;
     92             result.fArcData.fOval = original.fArcData.fOval;
     93             result.fArcData.fStartAngleDegrees = original.fArcData.fStartAngleDegrees;
     94             result.fArcData.fSweepAngleDegrees = original.fArcData.fSweepAngleDegrees;
     95             result.fArcData.fUseCenter = original.fArcData.fUseCenter;
     96             result.fArcData.fInverted = is_inverted(original.fArcData.fInverted, inversion);
     97             break;
     98         case Type::kLine:
     99             // Lines don't fill.
    100             if (is_inverted(original.fLineData.fInverted, inversion)) {
    101                 result.fType = Type::kInvertedEmpty;
    102             } else {
    103                 result.fType = Type::kEmpty;
    104             }
    105             break;
    106         case Type::kEmpty:
    107             result.fType = is_inverted(false, inversion) ? Type::kInvertedEmpty :  Type::kEmpty;
    108             break;
    109         case Type::kInvertedEmpty:
    110             result.fType = is_inverted(true, inversion) ? Type::kInvertedEmpty :  Type::kEmpty;
    111             break;
    112         case Type::kPath:
    113             result.initType(Type::kPath, &original.fPathData.fPath);
    114             result.fPathData.fGenID = original.fPathData.fGenID;
    115             if (flip_inversion(original.fPathData.fPath.isInverseFillType(), inversion)) {
    116                 result.fPathData.fPath.toggleInverseFillType();
    117             }
    118             if (!original.style().isSimpleFill()) {
    119                 // Going from a non-filled style to fill may allow additional simplifications (e.g.
    120                 // closing an open rect that wasn't closed in the original shape because it had
    121                 // stroke style).
    122                 result.attemptToSimplifyPath();
    123             }
    124             break;
    125     }
    126     // We don't copy the inherited key since it can contain path effect information that we just
    127     // stripped.
    128     return result;
    129 }
    130 
    131 SkRect GrShape::bounds() const {
    132     // Bounds where left == bottom or top == right can indicate a line or point shape. We return
    133     // inverted bounds for a truly empty shape.
    134     static constexpr SkRect kInverted = SkRect::MakeLTRB(1, 1, -1, -1);
    135     switch (fType) {
    136         case Type::kEmpty:
    137             return kInverted;
    138         case Type::kInvertedEmpty:
    139             return kInverted;
    140         case Type::kLine: {
    141             SkRect bounds;
    142             if (fLineData.fPts[0].fX < fLineData.fPts[1].fX) {
    143                 bounds.fLeft = fLineData.fPts[0].fX;
    144                 bounds.fRight = fLineData.fPts[1].fX;
    145             } else {
    146                 bounds.fLeft = fLineData.fPts[1].fX;
    147                 bounds.fRight = fLineData.fPts[0].fX;
    148             }
    149             if (fLineData.fPts[0].fY < fLineData.fPts[1].fY) {
    150                 bounds.fTop = fLineData.fPts[0].fY;
    151                 bounds.fBottom = fLineData.fPts[1].fY;
    152             } else {
    153                 bounds.fTop = fLineData.fPts[1].fY;
    154                 bounds.fBottom = fLineData.fPts[0].fY;
    155             }
    156             return bounds;
    157         }
    158         case Type::kRRect:
    159             return fRRectData.fRRect.getBounds();
    160         case Type::kArc:
    161             // Could make this less conservative by looking at angles.
    162             return fArcData.fOval;
    163         case Type::kPath:
    164             return this->path().getBounds();
    165     }
    166     SK_ABORT("Unknown shape type");
    167     return kInverted;
    168 }
    169 
    170 SkRect GrShape::styledBounds() const {
    171     if (this->isEmpty() && !fStyle.hasNonDashPathEffect()) {
    172         return SkRect::MakeEmpty();
    173     }
    174 
    175     SkRect bounds;
    176     fStyle.adjustBounds(&bounds, this->bounds());
    177     return bounds;
    178 }
    179 
    180 // If the path is small enough to be keyed from its data this returns key length, otherwise -1.
    181 static int path_key_from_data_size(const SkPath& path) {
    182     const int verbCnt = path.countVerbs();
    183     if (verbCnt > GrShape::kMaxKeyFromDataVerbCnt) {
    184         return -1;
    185     }
    186     const int pointCnt = path.countPoints();
    187     const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
    188 
    189     GR_STATIC_ASSERT(sizeof(SkPoint) == 2 * sizeof(uint32_t));
    190     GR_STATIC_ASSERT(sizeof(SkScalar) == sizeof(uint32_t));
    191     // 2 is for the verb cnt and a fill type. Each verb is a byte but we'll pad the verb data out to
    192     // a uint32_t length.
    193     return 2 + (SkAlign4(verbCnt) >> 2) + 2 * pointCnt + conicWeightCnt;
    194 }
    195 
    196 // Writes the path data key into the passed pointer.
    197 static void write_path_key_from_data(const SkPath& path, uint32_t* origKey) {
    198     uint32_t* key = origKey;
    199     // The check below should take care of negative values casted positive.
    200     const int verbCnt = path.countVerbs();
    201     const int pointCnt = path.countPoints();
    202     const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
    203     SkASSERT(verbCnt <= GrShape::kMaxKeyFromDataVerbCnt);
    204     SkASSERT(pointCnt && verbCnt);
    205     *key++ = path.getFillType();
    206     *key++ = verbCnt;
    207     memcpy(key, SkPathPriv::VerbData(path), verbCnt * sizeof(uint8_t));
    208     int verbKeySize = SkAlign4(verbCnt);
    209     // pad out to uint32_t alignment using value that will stand out when debugging.
    210     uint8_t* pad = reinterpret_cast<uint8_t*>(key)+ verbCnt;
    211     memset(pad, 0xDE, verbKeySize - verbCnt);
    212     key += verbKeySize >> 2;
    213 
    214     memcpy(key, SkPathPriv::PointData(path), sizeof(SkPoint) * pointCnt);
    215     GR_STATIC_ASSERT(sizeof(SkPoint) == 2 * sizeof(uint32_t));
    216     key += 2 * pointCnt;
    217     sk_careful_memcpy(key, SkPathPriv::ConicWeightData(path), sizeof(SkScalar) * conicWeightCnt);
    218     GR_STATIC_ASSERT(sizeof(SkScalar) == sizeof(uint32_t));
    219     SkDEBUGCODE(key += conicWeightCnt);
    220     SkASSERT(key - origKey == path_key_from_data_size(path));
    221 }
    222 
    223 int GrShape::unstyledKeySize() const {
    224     if (fInheritedKey.count()) {
    225         return fInheritedKey.count();
    226     }
    227     switch (fType) {
    228         case Type::kEmpty:
    229             return 1;
    230         case Type::kInvertedEmpty:
    231             return 1;
    232         case Type::kRRect:
    233             SkASSERT(!fInheritedKey.count());
    234             GR_STATIC_ASSERT(0 == SkRRect::kSizeInMemory % sizeof(uint32_t));
    235             // + 1 for the direction, start index, and inverseness.
    236             return SkRRect::kSizeInMemory / sizeof(uint32_t) + 1;
    237         case Type::kArc:
    238             SkASSERT(!fInheritedKey.count());
    239             GR_STATIC_ASSERT(0 == sizeof(fArcData) % sizeof(uint32_t));
    240             return sizeof(fArcData) / sizeof(uint32_t);
    241         case Type::kLine:
    242             GR_STATIC_ASSERT(2 * sizeof(uint32_t) == sizeof(SkPoint));
    243             // 4 for the end points and 1 for the inverseness
    244             return 5;
    245         case Type::kPath: {
    246             if (0 == fPathData.fGenID) {
    247                 return -1;
    248             }
    249             int dataKeySize = path_key_from_data_size(fPathData.fPath);
    250             if (dataKeySize >= 0) {
    251                 return dataKeySize;
    252             }
    253             // The key is the path ID and fill type.
    254             return 2;
    255         }
    256     }
    257     SK_ABORT("Should never get here.");
    258     return 0;
    259 }
    260 
    261 void GrShape::writeUnstyledKey(uint32_t* key) const {
    262     SkASSERT(this->unstyledKeySize());
    263     SkDEBUGCODE(uint32_t* origKey = key;)
    264     if (fInheritedKey.count()) {
    265         memcpy(key, fInheritedKey.get(), sizeof(uint32_t) * fInheritedKey.count());
    266         SkDEBUGCODE(key += fInheritedKey.count();)
    267     } else {
    268         switch (fType) {
    269             case Type::kEmpty:
    270                 *key++ = 1;
    271                 break;
    272             case Type::kInvertedEmpty:
    273                 *key++ = 2;
    274                 break;
    275             case Type::kRRect:
    276                 fRRectData.fRRect.writeToMemory(key);
    277                 key += SkRRect::kSizeInMemory / sizeof(uint32_t);
    278                 *key = (fRRectData.fDir == SkPath::kCCW_Direction) ? (1 << 31) : 0;
    279                 *key |= fRRectData.fInverted ? (1 << 30) : 0;
    280                 *key++ |= fRRectData.fStart;
    281                 SkASSERT(fRRectData.fStart < 8);
    282                 break;
    283             case Type::kArc:
    284                 memcpy(key, &fArcData, sizeof(fArcData));
    285                 key += sizeof(fArcData) / sizeof(uint32_t);
    286                 break;
    287             case Type::kLine:
    288                 memcpy(key, fLineData.fPts, 2 * sizeof(SkPoint));
    289                 key += 4;
    290                 *key++ = fLineData.fInverted ? 1 : 0;
    291                 break;
    292             case Type::kPath: {
    293                 SkASSERT(fPathData.fGenID);
    294                 int dataKeySize = path_key_from_data_size(fPathData.fPath);
    295                 if (dataKeySize >= 0) {
    296                     write_path_key_from_data(fPathData.fPath, key);
    297                     return;
    298                 }
    299                 *key++ = fPathData.fGenID;
    300                 // We could canonicalize the fill rule for paths that don't differentiate between
    301                 // even/odd or winding fill (e.g. convex).
    302                 *key++ = this->path().getFillType();
    303                 break;
    304             }
    305         }
    306     }
    307     SkASSERT(key - origKey == this->unstyledKeySize());
    308 }
    309 
    310 void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply, SkScalar scale) {
    311     SkASSERT(!fInheritedKey.count());
    312     // If the output shape turns out to be simple, then we will just use its geometric key
    313     if (Type::kPath == fType) {
    314         // We want ApplyFullStyle(ApplyPathEffect(shape)) to have the same key as
    315         // ApplyFullStyle(shape).
    316         // The full key is structured as (geo,path_effect,stroke).
    317         // If we do ApplyPathEffect we get geo,path_effect as the inherited key. If we then
    318         // do ApplyFullStyle we'll memcpy geo,path_effect into the new inherited key
    319         // and then append the style key (which should now be stroke only) at the end.
    320         int parentCnt = parent.fInheritedKey.count();
    321         bool useParentGeoKey = !parentCnt;
    322         if (useParentGeoKey) {
    323             parentCnt = parent.unstyledKeySize();
    324             if (parentCnt < 0) {
    325                 // The parent's geometry has no key so we will have no key.
    326                 fPathData.fGenID = 0;
    327                 return;
    328             }
    329         }
    330         uint32_t styleKeyFlags = 0;
    331         if (parent.knownToBeClosed()) {
    332             styleKeyFlags |= GrStyle::kClosed_KeyFlag;
    333         }
    334         if (parent.asLine(nullptr, nullptr)) {
    335             styleKeyFlags |= GrStyle::kNoJoins_KeyFlag;
    336         }
    337         int styleCnt = GrStyle::KeySize(parent.fStyle, apply, styleKeyFlags);
    338         if (styleCnt < 0) {
    339             // The style doesn't allow a key, set the path gen ID to 0 so that we fail when
    340             // we try to get a key for the shape.
    341             fPathData.fGenID = 0;
    342             return;
    343         }
    344         fInheritedKey.reset(parentCnt + styleCnt);
    345         if (useParentGeoKey) {
    346             // This will be the geo key.
    347             parent.writeUnstyledKey(fInheritedKey.get());
    348         } else {
    349             // This should be (geo,path_effect).
    350             memcpy(fInheritedKey.get(), parent.fInheritedKey.get(),
    351                    parentCnt * sizeof(uint32_t));
    352         }
    353         // Now turn (geo,path_effect) or (geo) into (geo,path_effect,stroke)
    354         GrStyle::WriteKey(fInheritedKey.get() + parentCnt, parent.fStyle, apply, scale,
    355                           styleKeyFlags);
    356     }
    357 }
    358 
    359 const SkPath* GrShape::originalPathForListeners() const {
    360     if (fInheritedPathForListeners.isValid()) {
    361         return fInheritedPathForListeners.get();
    362     } else if (Type::kPath == fType && !fPathData.fPath.isVolatile()) {
    363         return &fPathData.fPath;
    364     }
    365     return nullptr;
    366 }
    367 
    368 void GrShape::addGenIDChangeListener(sk_sp<SkPathRef::GenIDChangeListener> listener) const {
    369     if (const auto* lp = this->originalPathForListeners()) {
    370         SkPathPriv::AddGenIDChangeListener(*lp, std::move(listener));
    371     }
    372 }
    373 
    374 GrShape GrShape::MakeArc(const SkRect& oval, SkScalar startAngleDegrees, SkScalar sweepAngleDegrees,
    375                          bool useCenter, const GrStyle& style) {
    376     GrShape result;
    377     result.changeType(Type::kArc);
    378     result.fArcData.fOval = oval;
    379     result.fArcData.fStartAngleDegrees = startAngleDegrees;
    380     result.fArcData.fSweepAngleDegrees = sweepAngleDegrees;
    381     result.fArcData.fUseCenter = useCenter;
    382     result.fArcData.fInverted = false;
    383     result.fStyle = style;
    384     result.attemptToSimplifyArc();
    385     return result;
    386 }
    387 
    388 GrShape::GrShape(const GrShape& that) : fStyle(that.fStyle) {
    389     const SkPath* thatPath = Type::kPath == that.fType ? &that.fPathData.fPath : nullptr;
    390     this->initType(that.fType, thatPath);
    391     switch (fType) {
    392         case Type::kEmpty:
    393             break;
    394         case Type::kInvertedEmpty:
    395             break;
    396         case Type::kRRect:
    397             fRRectData = that.fRRectData;
    398             break;
    399         case Type::kArc:
    400             fArcData = that.fArcData;
    401             break;
    402         case Type::kLine:
    403             fLineData = that.fLineData;
    404             break;
    405         case Type::kPath:
    406             fPathData.fGenID = that.fPathData.fGenID;
    407             break;
    408     }
    409     fInheritedKey.reset(that.fInheritedKey.count());
    410     sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
    411                       sizeof(uint32_t) * fInheritedKey.count());
    412     if (that.fInheritedPathForListeners.isValid()) {
    413         fInheritedPathForListeners.set(*that.fInheritedPathForListeners.get());
    414     }
    415 }
    416 
    417 GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) {
    418     // TODO: Add some quantization of scale for better cache performance here or leave that up
    419     // to caller?
    420     // TODO: For certain shapes and stroke params we could ignore the scale. (e.g. miter or bevel
    421     // stroke of a rect).
    422     if (!parent.style().applies() ||
    423         (GrStyle::Apply::kPathEffectOnly == apply && !parent.style().pathEffect())) {
    424         this->initType(Type::kEmpty);
    425         *this = parent;
    426         return;
    427     }
    428 
    429     SkPathEffect* pe = parent.fStyle.pathEffect();
    430     SkTLazy<SkPath> tmpPath;
    431     const GrShape* parentForKey = &parent;
    432     SkTLazy<GrShape> tmpParent;
    433     this->initType(Type::kPath);
    434     fPathData.fGenID = 0;
    435     if (pe) {
    436         const SkPath* srcForPathEffect;
    437         if (parent.fType == Type::kPath) {
    438             srcForPathEffect = &parent.path();
    439         } else {
    440             srcForPathEffect = tmpPath.init();
    441             parent.asPath(tmpPath.get());
    442         }
    443         // Should we consider bounds? Would have to include in key, but it'd be nice to know
    444         // if the bounds actually modified anything before including in key.
    445         SkStrokeRec strokeRec = parent.fStyle.strokeRec();
    446         if (!parent.fStyle.applyPathEffectToPath(&this->path(), &strokeRec, *srcForPathEffect,
    447                                                  scale)) {
    448             tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr));
    449             *this = tmpParent.get()->applyStyle(apply, scale);
    450             return;
    451         }
    452         // A path effect has access to change the res scale but we aren't expecting it to and it
    453         // would mess up our key computation.
    454         SkASSERT(scale == strokeRec.getResScale());
    455         if (GrStyle::Apply::kPathEffectAndStrokeRec == apply && strokeRec.needToApply()) {
    456             // The intermediate shape may not be a general path. If we we're just applying
    457             // the path effect then attemptToReduceFromPath would catch it. This means that
    458             // when we subsequently applied the remaining strokeRec we would have a non-path
    459             // parent shape that would be used to determine the the stroked path's key.
    460             // We detect that case here and change parentForKey to a temporary that represents
    461             // the simpler shape so that applying both path effect and the strokerec all at
    462             // once produces the same key.
    463             tmpParent.init(this->path(), GrStyle(strokeRec, nullptr));
    464             tmpParent.get()->setInheritedKey(parent, GrStyle::Apply::kPathEffectOnly, scale);
    465             if (!tmpPath.isValid()) {
    466                 tmpPath.init();
    467             }
    468             tmpParent.get()->asPath(tmpPath.get());
    469             SkStrokeRec::InitStyle fillOrHairline;
    470             // The parent shape may have simplified away the strokeRec, check for that here.
    471             if (tmpParent.get()->style().applies()) {
    472                 SkAssertResult(tmpParent.get()->style().applyToPath(&this->path(), &fillOrHairline,
    473                                                                     *tmpPath.get(), scale));
    474             } else if (tmpParent.get()->style().isSimpleFill()) {
    475                 fillOrHairline = SkStrokeRec::kFill_InitStyle;
    476             } else {
    477                 SkASSERT(tmpParent.get()->style().isSimpleHairline());
    478                 fillOrHairline = SkStrokeRec::kHairline_InitStyle;
    479             }
    480             fStyle.resetToInitStyle(fillOrHairline);
    481             parentForKey = tmpParent.get();
    482         } else {
    483             fStyle = GrStyle(strokeRec, nullptr);
    484         }
    485     } else {
    486         const SkPath* srcForParentStyle;
    487         if (parent.fType == Type::kPath) {
    488             srcForParentStyle = &parent.path();
    489         } else {
    490             srcForParentStyle = tmpPath.init();
    491             parent.asPath(tmpPath.get());
    492         }
    493         SkStrokeRec::InitStyle fillOrHairline;
    494         SkASSERT(parent.fStyle.applies());
    495         SkASSERT(!parent.fStyle.pathEffect());
    496         SkAssertResult(parent.fStyle.applyToPath(&this->path(), &fillOrHairline, *srcForParentStyle,
    497                                                  scale));
    498         fStyle.resetToInitStyle(fillOrHairline);
    499     }
    500     if (parent.fInheritedPathForListeners.isValid()) {
    501         fInheritedPathForListeners.set(*parent.fInheritedPathForListeners.get());
    502     } else if (Type::kPath == parent.fType && !parent.fPathData.fPath.isVolatile()) {
    503         fInheritedPathForListeners.set(parent.fPathData.fPath);
    504     }
    505     this->attemptToSimplifyPath();
    506     this->setInheritedKey(*parentForKey, apply, scale);
    507 }
    508 
    509 void GrShape::attemptToSimplifyPath() {
    510     SkRect rect;
    511     SkRRect rrect;
    512     SkPath::Direction rrectDir;
    513     unsigned rrectStart;
    514     bool inverted = this->path().isInverseFillType();
    515     SkPoint pts[2];
    516     if (this->path().isEmpty()) {
    517         // Dashing ignores inverseness skbug.com/5421.
    518         this->changeType(inverted && !this->style().isDashed() ? Type::kInvertedEmpty
    519                                                                : Type::kEmpty);
    520     } else if (this->path().isLine(pts)) {
    521         this->changeType(Type::kLine);
    522         fLineData.fPts[0] = pts[0];
    523         fLineData.fPts[1] = pts[1];
    524         fLineData.fInverted = inverted;
    525     } else if (SkPathPriv::IsRRect(this->path(), &rrect, &rrectDir, &rrectStart)) {
    526         this->changeType(Type::kRRect);
    527         fRRectData.fRRect = rrect;
    528         fRRectData.fDir = rrectDir;
    529         fRRectData.fStart = rrectStart;
    530         fRRectData.fInverted = inverted;
    531         SkASSERT(!fRRectData.fRRect.isEmpty());
    532     } else if (SkPathPriv::IsOval(this->path(), &rect, &rrectDir, &rrectStart)) {
    533         this->changeType(Type::kRRect);
    534         fRRectData.fRRect.setOval(rect);
    535         fRRectData.fDir = rrectDir;
    536         fRRectData.fInverted = inverted;
    537         // convert from oval indexing to rrect indexiing.
    538         fRRectData.fStart = 2 * rrectStart;
    539     } else if (SkPathPriv::IsSimpleClosedRect(this->path(), &rect, &rrectDir, &rrectStart)) {
    540         this->changeType(Type::kRRect);
    541         // When there is a path effect we restrict rect detection to the narrower API that
    542         // gives us the starting position. Otherwise, we will retry with the more aggressive
    543         // isRect().
    544         fRRectData.fRRect.setRect(rect);
    545         fRRectData.fInverted = inverted;
    546         fRRectData.fDir = rrectDir;
    547         // convert from rect indexing to rrect indexiing.
    548         fRRectData.fStart = 2 * rrectStart;
    549     } else if (!this->style().hasPathEffect()) {
    550         bool closed;
    551         if (this->path().isRect(&rect, &closed, nullptr)) {
    552             if (closed || this->style().isSimpleFill()) {
    553                 this->changeType(Type::kRRect);
    554                 fRRectData.fRRect.setRect(rect);
    555                 // Since there is no path effect the dir and start index is immaterial.
    556                 fRRectData.fDir = kDefaultRRectDir;
    557                 fRRectData.fStart = kDefaultRRectStart;
    558                 // There isn't dashing so we will have to preserver inverseness.
    559                 fRRectData.fInverted = inverted;
    560             }
    561         }
    562     }
    563     if (Type::kPath != fType) {
    564         fInheritedKey.reset(0);
    565         // Whenever we simplify to a non-path, break the chain so we no longer refer to the
    566         // original path. This prevents attaching genID listeners to temporary paths created when
    567         // drawing simple shapes.
    568         fInheritedPathForListeners.reset();
    569         if (Type::kRRect == fType) {
    570             this->attemptToSimplifyRRect();
    571         } else if (Type::kLine == fType) {
    572             this->attemptToSimplifyLine();
    573         }
    574     } else {
    575         if (fInheritedKey.count() || this->path().isVolatile()) {
    576             fPathData.fGenID = 0;
    577         } else {
    578             fPathData.fGenID = this->path().getGenerationID();
    579         }
    580         if (!this->style().hasNonDashPathEffect()) {
    581             if (this->style().strokeRec().getStyle() == SkStrokeRec::kStroke_Style ||
    582                 this->style().strokeRec().getStyle() == SkStrokeRec::kHairline_Style) {
    583                 // Stroke styles don't differentiate between winding and even/odd.
    584                 // Moreover, dashing ignores inverseness (skbug.com/5421)
    585                 bool inverse = !this->style().isDashed() && this->path().isInverseFillType();
    586                 if (inverse) {
    587                     this->path().setFillType(kDefaultPathInverseFillType);
    588                 } else {
    589                     this->path().setFillType(kDefaultPathFillType);
    590                 }
    591             } else if (this->path().isConvex()) {
    592                 // There is no distinction between even/odd and non-zero winding count for convex
    593                 // paths.
    594                 if (this->path().isInverseFillType()) {
    595                     this->path().setFillType(kDefaultPathInverseFillType);
    596                 } else {
    597                     this->path().setFillType(kDefaultPathFillType);
    598                 }
    599             }
    600         }
    601     }
    602 }
    603 
    604 void GrShape::attemptToSimplifyRRect() {
    605     SkASSERT(Type::kRRect == fType);
    606     SkASSERT(!fInheritedKey.count());
    607     if (fRRectData.fRRect.isEmpty()) {
    608         // An empty filled rrect is equivalent to a filled empty path with inversion preserved.
    609         if (fStyle.isSimpleFill()) {
    610             fType = fRRectData.fInverted ? Type::kInvertedEmpty : Type::kEmpty;
    611             fStyle = GrStyle::SimpleFill();
    612             return;
    613         }
    614         // Dashing a rrect with no width or height is equivalent to filling an emtpy path.
    615         // When skbug.com/7387 is fixed this should be modified or removed as a dashed zero length
    616         // line  will produce cap geometry if the effect begins in an "on" interval.
    617         if (fStyle.isDashed() && !fRRectData.fRRect.width() && !fRRectData.fRRect.height()) {
    618             // Dashing ignores the inverseness (currently). skbug.com/5421.
    619             fType = Type::kEmpty;
    620             fStyle = GrStyle::SimpleFill();
    621             return;
    622         }
    623     }
    624     if (!this->style().hasPathEffect()) {
    625         fRRectData.fDir = kDefaultRRectDir;
    626         fRRectData.fStart = kDefaultRRectStart;
    627     } else if (fStyle.isDashed()) {
    628         // Dashing ignores the inverseness (currently). skbug.com/5421
    629         fRRectData.fInverted = false;
    630         // Possible TODO here: Check whether the dash results in a single arc or line.
    631     }
    632     // Turn a stroke-and-filled miter rect into a filled rect. TODO: more rrect stroke shortcuts.
    633     if (!fStyle.hasPathEffect() &&
    634         fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style &&
    635         fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join &&
    636         fStyle.strokeRec().getMiter() >= SK_ScalarSqrt2 &&
    637         fRRectData.fRRect.isRect()) {
    638         SkScalar r = fStyle.strokeRec().getWidth() / 2;
    639         fRRectData.fRRect = SkRRect::MakeRect(fRRectData.fRRect.rect().makeOutset(r, r));
    640         fStyle = GrStyle::SimpleFill();
    641     }
    642 }
    643 
    644 void GrShape::attemptToSimplifyLine() {
    645     SkASSERT(Type::kLine == fType);
    646     SkASSERT(!fInheritedKey.count());
    647     if (fStyle.isDashed()) {
    648         bool allOffsZero = true;
    649         for (int i = 1; i < fStyle.dashIntervalCnt() && allOffsZero; i += 2) {
    650             allOffsZero = !fStyle.dashIntervals()[i];
    651         }
    652         if (allOffsZero && this->attemptToSimplifyStrokedLineToRRect()) {
    653             return;
    654         }
    655         // Dashing ignores inverseness.
    656         fLineData.fInverted = false;
    657         return;
    658     } else if (fStyle.hasPathEffect()) {
    659         return;
    660     }
    661     if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
    662         // Make stroke + fill be stroke since the fill is empty.
    663         SkStrokeRec rec = fStyle.strokeRec();
    664         rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false);
    665         fStyle = GrStyle(rec, nullptr);
    666     }
    667     if (fStyle.isSimpleFill()) {
    668         this->changeType(fLineData.fInverted ? Type::kInvertedEmpty : Type::kEmpty);
    669         return;
    670     }
    671     if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style &&
    672         this->attemptToSimplifyStrokedLineToRRect()) {
    673         return;
    674     }
    675     // Only path effects could care about the order of the points. Otherwise canonicalize
    676     // the point order.
    677     SkPoint* pts = fLineData.fPts;
    678     if (pts[1].fY < pts[0].fY || (pts[1].fY == pts[0].fY && pts[1].fX < pts[0].fX)) {
    679         using std::swap;
    680         swap(pts[0], pts[1]);
    681     }
    682 }
    683 
    684 void GrShape::attemptToSimplifyArc() {
    685     SkASSERT(fType == Type::kArc);
    686     SkASSERT(!fArcData.fInverted);
    687     if (fArcData.fOval.isEmpty() || !fArcData.fSweepAngleDegrees) {
    688         this->changeType(Type::kEmpty);
    689         return;
    690     }
    691 
    692     // Assuming no path effect, a filled, stroked, hairline, or stroke-and-filled arc that traverses
    693     // the full circle and doesn't use the center point is an oval. Unless it has square or round
    694     // caps. They may protrude out of the oval. Round caps can't protrude out of a circle but we're
    695     // ignoring that for now.
    696     if (fStyle.isSimpleFill() || (!fStyle.pathEffect() && !fArcData.fUseCenter &&
    697                                   fStyle.strokeRec().getCap() == SkPaint::kButt_Cap)) {
    698         if (fArcData.fSweepAngleDegrees >= 360.f || fArcData.fSweepAngleDegrees <= -360.f) {
    699             auto oval = fArcData.fOval;
    700             this->changeType(Type::kRRect);
    701             this->fRRectData.fRRect.setOval(oval);
    702             this->fRRectData.fDir = kDefaultRRectDir;
    703             this->fRRectData.fStart = kDefaultRRectStart;
    704             this->fRRectData.fInverted = false;
    705             return;
    706         }
    707     }
    708     if (!fStyle.pathEffect()) {
    709         // Canonicalize the arc such that the start is always in [0, 360) and the sweep is always
    710         // positive.
    711         if (fArcData.fSweepAngleDegrees < 0) {
    712             fArcData.fStartAngleDegrees = fArcData.fStartAngleDegrees + fArcData.fSweepAngleDegrees;
    713             fArcData.fSweepAngleDegrees = -fArcData.fSweepAngleDegrees;
    714         }
    715     }
    716     if (this->fArcData.fStartAngleDegrees < 0 || this->fArcData.fStartAngleDegrees >= 360.f) {
    717         this->fArcData.fStartAngleDegrees = SkScalarMod(this->fArcData.fStartAngleDegrees, 360.f);
    718     }
    719     // Possible TODOs here: Look at whether dash pattern results in a single dash and convert to
    720     // non-dashed stroke. Stroke and fill can be fill if circular and no path effect. Just stroke
    721     // could as well if the stroke fills the center.
    722 }
    723 
    724 bool GrShape::attemptToSimplifyStrokedLineToRRect() {
    725     SkASSERT(Type::kLine == fType);
    726     SkASSERT(fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style);
    727 
    728     SkRect rect;
    729     SkVector outset;
    730     // If we allowed a rotation angle for rrects we could capture all cases here.
    731     if (fLineData.fPts[0].fY == fLineData.fPts[1].fY) {
    732         rect.fLeft = SkTMin(fLineData.fPts[0].fX, fLineData.fPts[1].fX);
    733         rect.fRight = SkTMax(fLineData.fPts[0].fX, fLineData.fPts[1].fX);
    734         rect.fTop = rect.fBottom = fLineData.fPts[0].fY;
    735         outset.fY = fStyle.strokeRec().getWidth() / 2.f;
    736         outset.fX = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fY;
    737     } else if (fLineData.fPts[0].fX == fLineData.fPts[1].fX) {
    738         rect.fTop = SkTMin(fLineData.fPts[0].fY, fLineData.fPts[1].fY);
    739         rect.fBottom = SkTMax(fLineData.fPts[0].fY, fLineData.fPts[1].fY);
    740         rect.fLeft = rect.fRight = fLineData.fPts[0].fX;
    741         outset.fX = fStyle.strokeRec().getWidth() / 2.f;
    742         outset.fY = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fX;
    743     } else {
    744         return false;
    745     }
    746     rect.outset(outset.fX, outset.fY);
    747     if (rect.isEmpty()) {
    748         this->changeType(Type::kEmpty);
    749         fStyle = GrStyle::SimpleFill();
    750         return true;
    751     }
    752     SkRRect rrect;
    753     if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
    754         SkASSERT(outset.fX == outset.fY);
    755         rrect = SkRRect::MakeRectXY(rect, outset.fX, outset.fY);
    756     } else {
    757         rrect = SkRRect::MakeRect(rect);
    758     }
    759     bool inverted = fLineData.fInverted && !fStyle.hasPathEffect();
    760     this->changeType(Type::kRRect);
    761     fRRectData.fRRect = rrect;
    762     fRRectData.fInverted = inverted;
    763     fRRectData.fDir = kDefaultRRectDir;
    764     fRRectData.fStart = kDefaultRRectStart;
    765     fStyle = GrStyle::SimpleFill();
    766     return true;
    767 }
    768