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 GrShape& GrShape::operator=(const GrShape& that) {
     11     fStyle = that.fStyle;
     12     this->changeType(that.fType, Type::kPath == that.fType ? &that.path() : nullptr);
     13     switch (fType) {
     14         case Type::kEmpty:
     15             break;
     16         case Type::kRRect:
     17             fRRectData = that.fRRectData;
     18             break;
     19         case Type::kLine:
     20             fLineData = that.fLineData;
     21             break;
     22         case Type::kPath:
     23             fPathData.fGenID = that.fPathData.fGenID;
     24             break;
     25     }
     26     fInheritedKey.reset(that.fInheritedKey.count());
     27     sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
     28                       sizeof(uint32_t) * fInheritedKey.count());
     29     return *this;
     30 }
     31 
     32 SkRect GrShape::bounds() const {
     33     // Bounds where left == bottom or top == right can indicate a line or point shape. We return
     34     // inverted bounds for a truly empty shape.
     35     static constexpr SkRect kInverted = SkRect::MakeLTRB(1, 1, -1, -1);
     36     switch (fType) {
     37         case Type::kEmpty:
     38             return kInverted;
     39         case Type::kLine: {
     40             SkRect bounds;
     41             if (fLineData.fPts[0].fX < fLineData.fPts[1].fX) {
     42                 bounds.fLeft = fLineData.fPts[0].fX;
     43                 bounds.fRight = fLineData.fPts[1].fX;
     44             } else {
     45                 bounds.fLeft = fLineData.fPts[1].fX;
     46                 bounds.fRight = fLineData.fPts[0].fX;
     47             }
     48             if (fLineData.fPts[0].fY < fLineData.fPts[1].fY) {
     49                 bounds.fTop = fLineData.fPts[0].fY;
     50                 bounds.fBottom = fLineData.fPts[1].fY;
     51             } else {
     52                 bounds.fTop = fLineData.fPts[1].fY;
     53                 bounds.fBottom = fLineData.fPts[0].fY;
     54             }
     55             return bounds;
     56         }
     57         case Type::kRRect:
     58             return fRRectData.fRRect.getBounds();
     59         case Type::kPath:
     60             return this->path().getBounds();
     61     }
     62     SkFAIL("Unknown shape type");
     63     return kInverted;
     64 }
     65 
     66 SkRect GrShape::styledBounds() const {
     67     if (Type::kEmpty == fType && !fStyle.hasNonDashPathEffect()) {
     68         return SkRect::MakeEmpty();
     69     }
     70     SkRect bounds;
     71     fStyle.adjustBounds(&bounds, this->bounds());
     72     return bounds;
     73 }
     74 
     75 // If the path is small enough to be keyed from its data this returns key length, otherwise -1.
     76 static int path_key_from_data_size(const SkPath& path) {
     77     const int verbCnt = path.countVerbs();
     78     if (verbCnt > GrShape::kMaxKeyFromDataVerbCnt) {
     79         return -1;
     80     }
     81     const int pointCnt = path.countPoints();
     82     const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
     83 
     84     GR_STATIC_ASSERT(sizeof(SkPoint) == 2 * sizeof(uint32_t));
     85     GR_STATIC_ASSERT(sizeof(SkScalar) == sizeof(uint32_t));
     86     // 2 is for the verb cnt and a fill type. Each verb is a byte but we'll pad the verb data out to
     87     // a uint32_t length.
     88     return 2 + (SkAlign4(verbCnt) >> 2) + 2 * pointCnt + conicWeightCnt;
     89 }
     90 
     91 // Writes the path data key into the passed pointer.
     92 static void write_path_key_from_data(const SkPath& path, uint32_t* origKey) {
     93     uint32_t* key = origKey;
     94     // The check below should take care of negative values casted positive.
     95     const int verbCnt = path.countVerbs();
     96     const int pointCnt = path.countPoints();
     97     const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
     98     SkASSERT(verbCnt <= GrShape::kMaxKeyFromDataVerbCnt);
     99     SkASSERT(pointCnt && verbCnt);
    100     *key++ = path.getFillType();
    101     *key++ = verbCnt;
    102     memcpy(key, SkPathPriv::VerbData(path), verbCnt * sizeof(uint8_t));
    103     int verbKeySize = SkAlign4(verbCnt);
    104     // pad out to uint32_t alignment using value that will stand out when debugging.
    105     uint8_t* pad = reinterpret_cast<uint8_t*>(key)+ verbCnt;
    106     memset(pad, 0xDE, verbKeySize - verbCnt);
    107     key += verbKeySize >> 2;
    108 
    109     memcpy(key, SkPathPriv::PointData(path), sizeof(SkPoint) * pointCnt);
    110     GR_STATIC_ASSERT(sizeof(SkPoint) == 2 * sizeof(uint32_t));
    111     key += 2 * pointCnt;
    112     sk_careful_memcpy(key, SkPathPriv::ConicWeightData(path), sizeof(SkScalar) * conicWeightCnt);
    113     GR_STATIC_ASSERT(sizeof(SkScalar) == sizeof(uint32_t));
    114     SkDEBUGCODE(key += conicWeightCnt);
    115     SkASSERT(key - origKey == path_key_from_data_size(path));
    116 }
    117 
    118 int GrShape::unstyledKeySize() const {
    119     if (fInheritedKey.count()) {
    120         return fInheritedKey.count();
    121     }
    122     switch (fType) {
    123         case Type::kEmpty:
    124             return 1;
    125         case Type::kRRect:
    126             SkASSERT(!fInheritedKey.count());
    127             SkASSERT(0 == SkRRect::kSizeInMemory % sizeof(uint32_t));
    128             // + 1 for the direction, start index, and inverseness.
    129             return SkRRect::kSizeInMemory / sizeof(uint32_t) + 1;
    130         case Type::kLine:
    131             GR_STATIC_ASSERT(2 * sizeof(uint32_t) == sizeof(SkPoint));
    132             // 4 for the end points and 1 for the inverseness
    133             return 5;
    134         case Type::kPath: {
    135             if (0 == fPathData.fGenID) {
    136                 return -1;
    137             }
    138             int dataKeySize = path_key_from_data_size(fPathData.fPath);
    139             if (dataKeySize >= 0) {
    140                 return dataKeySize;
    141             }
    142             // The key is the path ID and fill type.
    143             return 2;
    144         }
    145     }
    146     SkFAIL("Should never get here.");
    147     return 0;
    148 }
    149 
    150 void GrShape::writeUnstyledKey(uint32_t* key) const {
    151     SkASSERT(this->unstyledKeySize());
    152     SkDEBUGCODE(uint32_t* origKey = key;)
    153     if (fInheritedKey.count()) {
    154         memcpy(key, fInheritedKey.get(), sizeof(uint32_t) * fInheritedKey.count());
    155         SkDEBUGCODE(key += fInheritedKey.count();)
    156     } else {
    157         switch (fType) {
    158             case Type::kEmpty:
    159                 *key++ = 1;
    160                 break;
    161             case Type::kRRect:
    162                 fRRectData.fRRect.writeToMemory(key);
    163                 key += SkRRect::kSizeInMemory / sizeof(uint32_t);
    164                 *key = (fRRectData.fDir == SkPath::kCCW_Direction) ? (1 << 31) : 0;
    165                 *key |= fRRectData.fInverted ? (1 << 30) : 0;
    166                 *key++ |= fRRectData.fStart;
    167                 SkASSERT(fRRectData.fStart < 8);
    168                 break;
    169             case Type::kLine:
    170                 memcpy(key, fLineData.fPts, 2 * sizeof(SkPoint));
    171                 key += 4;
    172                 *key++ = fLineData.fInverted ? 1 : 0;
    173                 break;
    174             case Type::kPath: {
    175                 SkASSERT(fPathData.fGenID);
    176                 int dataKeySize = path_key_from_data_size(fPathData.fPath);
    177                 if (dataKeySize >= 0) {
    178                     write_path_key_from_data(fPathData.fPath, key);
    179                     return;
    180                 }
    181                 *key++ = fPathData.fGenID;
    182                 // We could canonicalize the fill rule for paths that don't differentiate between
    183                 // even/odd or winding fill (e.g. convex).
    184                 *key++ = this->path().getFillType();
    185                 break;
    186             }
    187         }
    188     }
    189     SkASSERT(key - origKey == this->unstyledKeySize());
    190 }
    191 
    192 void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply, SkScalar scale) {
    193     SkASSERT(!fInheritedKey.count());
    194     // If the output shape turns out to be simple, then we will just use its geometric key
    195     if (Type::kPath == fType) {
    196         // We want ApplyFullStyle(ApplyPathEffect(shape)) to have the same key as
    197         // ApplyFullStyle(shape).
    198         // The full key is structured as (geo,path_effect,stroke).
    199         // If we do ApplyPathEffect we get get,path_effect as the inherited key. If we then
    200         // do ApplyFullStyle we'll memcpy geo,path_effect into the new inherited key
    201         // and then append the style key (which should now be stroke only) at the end.
    202         int parentCnt = parent.fInheritedKey.count();
    203         bool useParentGeoKey = !parentCnt;
    204         if (useParentGeoKey) {
    205             parentCnt = parent.unstyledKeySize();
    206             if (parentCnt < 0) {
    207                 // The parent's geometry has no key so we will have no key.
    208                 fPathData.fGenID = 0;
    209                 return;
    210             }
    211         }
    212         uint32_t styleKeyFlags = 0;
    213         if (parent.knownToBeClosed()) {
    214             styleKeyFlags |= GrStyle::kClosed_KeyFlag;
    215         }
    216         if (parent.asLine(nullptr, nullptr)) {
    217             styleKeyFlags |= GrStyle::kNoJoins_KeyFlag;
    218         }
    219         int styleCnt = GrStyle::KeySize(parent.fStyle, apply, styleKeyFlags);
    220         if (styleCnt < 0) {
    221             // The style doesn't allow a key, set the path gen ID to 0 so that we fail when
    222             // we try to get a key for the shape.
    223             fPathData.fGenID = 0;
    224             return;
    225         }
    226         fInheritedKey.reset(parentCnt + styleCnt);
    227         if (useParentGeoKey) {
    228             // This will be the geo key.
    229             parent.writeUnstyledKey(fInheritedKey.get());
    230         } else {
    231             // This should be (geo,path_effect).
    232             memcpy(fInheritedKey.get(), parent.fInheritedKey.get(),
    233                    parentCnt * sizeof(uint32_t));
    234         }
    235         // Now turn (geo,path_effect) or (geo) into (geo,path_effect,stroke)
    236         GrStyle::WriteKey(fInheritedKey.get() + parentCnt, parent.fStyle, apply, scale,
    237                           styleKeyFlags);
    238     }
    239 }
    240 
    241 GrShape::GrShape(const GrShape& that) : fStyle(that.fStyle) {
    242     const SkPath* thatPath = Type::kPath == that.fType ? &that.fPathData.fPath : nullptr;
    243     this->initType(that.fType, thatPath);
    244     switch (fType) {
    245         case Type::kEmpty:
    246             break;
    247         case Type::kRRect:
    248             fRRectData = that.fRRectData;
    249             break;
    250         case Type::kLine:
    251             fLineData = that.fLineData;
    252             break;
    253         case Type::kPath:
    254             fPathData.fGenID = that.fPathData.fGenID;
    255             break;
    256     }
    257     fInheritedKey.reset(that.fInheritedKey.count());
    258     sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
    259                       sizeof(uint32_t) * fInheritedKey.count());
    260 }
    261 
    262 GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) {
    263     // TODO: Add some quantization of scale for better cache performance here or leave that up
    264     // to caller?
    265     // TODO: For certain shapes and stroke params we could ignore the scale. (e.g. miter or bevel
    266     // stroke of a rect).
    267     if (!parent.style().applies() ||
    268         (GrStyle::Apply::kPathEffectOnly == apply && !parent.style().pathEffect())) {
    269         this->initType(Type::kEmpty);
    270         *this = parent;
    271         return;
    272     }
    273 
    274     SkPathEffect* pe = parent.fStyle.pathEffect();
    275     SkTLazy<SkPath> tmpPath;
    276     const GrShape* parentForKey = &parent;
    277     SkTLazy<GrShape> tmpParent;
    278     this->initType(Type::kPath);
    279     fPathData.fGenID = 0;
    280     if (pe) {
    281         const SkPath* srcForPathEffect;
    282         if (parent.fType == Type::kPath) {
    283             srcForPathEffect = &parent.path();
    284         } else {
    285             srcForPathEffect = tmpPath.init();
    286             parent.asPath(tmpPath.get());
    287         }
    288         // Should we consider bounds? Would have to include in key, but it'd be nice to know
    289         // if the bounds actually modified anything before including in key.
    290         SkStrokeRec strokeRec = parent.fStyle.strokeRec();
    291         if (!parent.fStyle.applyPathEffectToPath(&this->path(), &strokeRec, *srcForPathEffect,
    292                                                  scale)) {
    293             tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr));
    294             *this = tmpParent.get()->applyStyle(apply, scale);
    295             return;
    296         }
    297         // A path effect has access to change the res scale but we aren't expecting it to and it
    298         // would mess up our key computation.
    299         SkASSERT(scale == strokeRec.getResScale());
    300         if (GrStyle::Apply::kPathEffectAndStrokeRec == apply && strokeRec.needToApply()) {
    301             // The intermediate shape may not be a general path. If we we're just applying
    302             // the path effect then attemptToReduceFromPath would catch it. This means that
    303             // when we subsequently applied the remaining strokeRec we would have a non-path
    304             // parent shape that would be used to determine the the stroked path's key.
    305             // We detect that case here and change parentForKey to a temporary that represents
    306             // the simpler shape so that applying both path effect and the strokerec all at
    307             // once produces the same key.
    308             tmpParent.init(this->path(), GrStyle(strokeRec, nullptr));
    309             tmpParent.get()->setInheritedKey(parent, GrStyle::Apply::kPathEffectOnly, scale);
    310             if (!tmpPath.isValid()) {
    311                 tmpPath.init();
    312             }
    313             tmpParent.get()->asPath(tmpPath.get());
    314             SkStrokeRec::InitStyle fillOrHairline;
    315             // The parent shape may have simplified away the strokeRec, check for that here.
    316             if (tmpParent.get()->style().applies()) {
    317                 SkAssertResult(tmpParent.get()->style().applyToPath(&this->path(), &fillOrHairline,
    318                                                                     *tmpPath.get(), scale));
    319             } else if (tmpParent.get()->style().isSimpleFill()) {
    320                 fillOrHairline = SkStrokeRec::kFill_InitStyle;
    321             } else {
    322                 SkASSERT(tmpParent.get()->style().isSimpleHairline());
    323                 fillOrHairline = SkStrokeRec::kHairline_InitStyle;
    324             }
    325             fStyle.resetToInitStyle(fillOrHairline);
    326             parentForKey = tmpParent.get();
    327         } else {
    328             fStyle = GrStyle(strokeRec, nullptr);
    329         }
    330     } else {
    331         const SkPath* srcForParentStyle;
    332         if (parent.fType == Type::kPath) {
    333             srcForParentStyle = &parent.path();
    334         } else {
    335             srcForParentStyle = tmpPath.init();
    336             parent.asPath(tmpPath.get());
    337         }
    338         SkStrokeRec::InitStyle fillOrHairline;
    339         SkASSERT(parent.fStyle.applies());
    340         SkASSERT(!parent.fStyle.pathEffect());
    341         SkAssertResult(parent.fStyle.applyToPath(&this->path(), &fillOrHairline, *srcForParentStyle,
    342                                                  scale));
    343         fStyle.resetToInitStyle(fillOrHairline);
    344     }
    345     this->attemptToSimplifyPath();
    346     this->setInheritedKey(*parentForKey, apply, scale);
    347 }
    348 
    349 void GrShape::attemptToSimplifyPath() {
    350     SkRect rect;
    351     SkRRect rrect;
    352     SkPath::Direction rrectDir;
    353     unsigned rrectStart;
    354     bool inverted = this->path().isInverseFillType();
    355     SkPoint pts[2];
    356     if (this->path().isEmpty()) {
    357         this->changeType(Type::kEmpty);
    358     } else if (this->path().isLine(pts)) {
    359         this->changeType(Type::kLine);
    360         fLineData.fPts[0] = pts[0];
    361         fLineData.fPts[1] = pts[1];
    362         fLineData.fInverted = inverted;
    363     } else if (this->path().isRRect(&rrect, &rrectDir, &rrectStart)) {
    364         this->changeType(Type::kRRect);
    365         fRRectData.fRRect = rrect;
    366         fRRectData.fDir = rrectDir;
    367         fRRectData.fStart = rrectStart;
    368         fRRectData.fInverted = inverted;
    369         // Currently SkPath does not acknowledge that empty, rect, or oval subtypes as rrects.
    370         SkASSERT(!fRRectData.fRRect.isEmpty());
    371         SkASSERT(fRRectData.fRRect.getType() != SkRRect::kRect_Type);
    372         SkASSERT(fRRectData.fRRect.getType() != SkRRect::kOval_Type);
    373     } else if (this->path().isOval(&rect, &rrectDir, &rrectStart)) {
    374         this->changeType(Type::kRRect);
    375         fRRectData.fRRect.setOval(rect);
    376         fRRectData.fDir = rrectDir;
    377         fRRectData.fInverted = inverted;
    378         // convert from oval indexing to rrect indexiing.
    379         fRRectData.fStart = 2 * rrectStart;
    380     } else if (SkPathPriv::IsSimpleClosedRect(this->path(), &rect, &rrectDir, &rrectStart)) {
    381         this->changeType(Type::kRRect);
    382         // When there is a path effect we restrict rect detection to the narrower API that
    383         // gives us the starting position. Otherwise, we will retry with the more aggressive
    384         // isRect().
    385         fRRectData.fRRect.setRect(rect);
    386         fRRectData.fInverted = inverted;
    387         fRRectData.fDir = rrectDir;
    388         // convert from rect indexing to rrect indexiing.
    389         fRRectData.fStart = 2 * rrectStart;
    390     } else if (!this->style().hasPathEffect()) {
    391         bool closed;
    392         if (this->path().isRect(&rect, &closed, nullptr)) {
    393             if (closed || this->style().isSimpleFill()) {
    394                 this->changeType(Type::kRRect);
    395                 fRRectData.fRRect.setRect(rect);
    396                 // Since there is no path effect the dir and start index is immaterial.
    397                 fRRectData.fDir = kDefaultRRectDir;
    398                 fRRectData.fStart = kDefaultRRectStart;
    399                 // There isn't dashing so we will have to preserver inverseness.
    400                 fRRectData.fInverted = inverted;
    401             }
    402         }
    403     }
    404     if (Type::kPath != fType) {
    405         fInheritedKey.reset(0);
    406         if (Type::kRRect == fType) {
    407             this->attemptToSimplifyRRect();
    408         } else if (Type::kLine == fType) {
    409             this->attemptToSimplifyLine();
    410         }
    411     } else {
    412         if (fInheritedKey.count() || this->path().isVolatile()) {
    413             fPathData.fGenID = 0;
    414         } else {
    415             fPathData.fGenID = this->path().getGenerationID();
    416         }
    417         if (!this->style().hasNonDashPathEffect()) {
    418             if (this->style().strokeRec().getStyle() == SkStrokeRec::kStroke_Style ||
    419                 this->style().strokeRec().getStyle() == SkStrokeRec::kHairline_Style) {
    420                 // Stroke styles don't differentiate between winding and even/odd.
    421                 // Moreover, dashing ignores inverseness (skbug.com/5421)
    422                 bool inverse = !this->style().isDashed() && this->path().isInverseFillType();
    423                 if (inverse) {
    424                     this->path().setFillType(kDefaultPathInverseFillType);
    425                 } else {
    426                     this->path().setFillType(kDefaultPathFillType);
    427                 }
    428             } else if (this->path().isConvex()) {
    429                 // There is no distinction between even/odd and non-zero winding count for convex
    430                 // paths.
    431                 if (this->path().isInverseFillType()) {
    432                     this->path().setFillType(kDefaultPathInverseFillType);
    433                 } else {
    434                     this->path().setFillType(kDefaultPathFillType);
    435                 }
    436             }
    437         }
    438     }
    439 }
    440 
    441 void GrShape::attemptToSimplifyRRect() {
    442     SkASSERT(Type::kRRect == fType);
    443     SkASSERT(!fInheritedKey.count());
    444     if (fRRectData.fRRect.isEmpty()) {
    445         fType = Type::kEmpty;
    446         return;
    447     }
    448     if (!this->style().hasPathEffect()) {
    449         fRRectData.fDir = kDefaultRRectDir;
    450         fRRectData.fStart = kDefaultRRectStart;
    451     } else if (fStyle.isDashed()) {
    452         // Dashing ignores the inverseness (currently). skbug.com/5421
    453         fRRectData.fInverted = false;
    454     }
    455     // Turn a stroke-and-filled miter rect into a filled rect. TODO: more rrect stroke shortcuts.
    456     if (!fStyle.hasPathEffect() &&
    457         fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style &&
    458         fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join &&
    459         fStyle.strokeRec().getMiter() >= SK_ScalarSqrt2 &&
    460         fRRectData.fRRect.isRect()) {
    461         SkScalar r = fStyle.strokeRec().getWidth() / 2;
    462         fRRectData.fRRect = SkRRect::MakeRect(fRRectData.fRRect.rect().makeOutset(r, r));
    463         fStyle = GrStyle::SimpleFill();
    464     }
    465 }
    466 
    467 void GrShape::attemptToSimplifyLine() {
    468     SkASSERT(Type::kLine == fType);
    469     SkASSERT(!fInheritedKey.count());
    470     if (fStyle.isDashed()) {
    471         // Dashing ignores inverseness.
    472         fLineData.fInverted = false;
    473         return;
    474     } else if (fStyle.hasPathEffect()) {
    475         return;
    476     }
    477     if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
    478         // Make stroke + fill be stroke since the fill is empty.
    479         SkStrokeRec rec = fStyle.strokeRec();
    480         rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false);
    481         fStyle = GrStyle(rec, nullptr);
    482     }
    483     if (fStyle.isSimpleFill() && !fLineData.fInverted) {
    484         this->changeType(Type::kEmpty);
    485         return;
    486     }
    487     SkPoint* pts = fLineData.fPts;
    488     if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style) {
    489         // If it is horizontal or vertical we will turn it into a filled rrect.
    490         SkRect rect;
    491         rect.fLeft = SkTMin(pts[0].fX, pts[1].fX);
    492         rect.fRight = SkTMax(pts[0].fX, pts[1].fX);
    493         rect.fTop = SkTMin(pts[0].fY, pts[1].fY);
    494         rect.fBottom = SkTMax(pts[0].fY, pts[1].fY);
    495         bool eqX = rect.fLeft == rect.fRight;
    496         bool eqY = rect.fTop == rect.fBottom;
    497         if (eqX || eqY) {
    498             SkScalar r = fStyle.strokeRec().getWidth() / 2;
    499             bool inverted = fLineData.fInverted;
    500             this->changeType(Type::kRRect);
    501             switch (fStyle.strokeRec().getCap()) {
    502                 case SkPaint::kButt_Cap:
    503                     if (eqX && eqY) {
    504                         this->changeType(Type::kEmpty);
    505                         return;
    506                     }
    507                     if (eqX) {
    508                         rect.outset(r, 0);
    509                     } else {
    510                         rect.outset(0, r);
    511                     }
    512                     fRRectData.fRRect = SkRRect::MakeRect(rect);
    513                     break;
    514                 case SkPaint::kSquare_Cap:
    515                     rect.outset(r, r);
    516                     fRRectData.fRRect = SkRRect::MakeRect(rect);
    517                     break;
    518                 case SkPaint::kRound_Cap:
    519                     rect.outset(r, r);
    520                     fRRectData.fRRect = SkRRect::MakeRectXY(rect, r, r);
    521                     break;
    522             }
    523             fRRectData.fInverted = inverted;
    524             fRRectData.fDir = kDefaultRRectDir;
    525             fRRectData.fStart = kDefaultRRectStart;
    526             if (fRRectData.fRRect.isEmpty()) {
    527                 // This can happen when r is very small relative to the rect edges.
    528                 this->changeType(Type::kEmpty);
    529                 return;
    530             }
    531             fStyle = GrStyle::SimpleFill();
    532             return;
    533         }
    534     }
    535     // Only path effects could care about the order of the points. Otherwise canonicalize
    536     // the point order.
    537     if (pts[1].fY < pts[0].fY || (pts[1].fY == pts[0].fY && pts[1].fX < pts[0].fX)) {
    538         SkTSwap(pts[0], pts[1]);
    539     }
    540 }
    541