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 "GrStyle.h"
      9 #include "SkDashPathPriv.h"
     10 
     11 int GrStyle::KeySize(const GrStyle &style, Apply apply, uint32_t flags) {
     12     GR_STATIC_ASSERT(sizeof(uint32_t) == sizeof(SkScalar));
     13     int size = 0;
     14     if (style.isDashed()) {
     15         // One scalar for scale, one for dash phase, and one for each dash value.
     16         size += 2 + style.dashIntervalCnt();
     17     } else if (style.pathEffect()) {
     18         // No key for a generic path effect.
     19         return -1;
     20     }
     21 
     22     if (Apply::kPathEffectOnly == apply) {
     23         return size;
     24     }
     25 
     26     if (style.strokeRec().needToApply()) {
     27         // One for res scale, one for style/cap/join, one for miter limit, and one for width.
     28         size += 4;
     29     }
     30     return size;
     31 }
     32 
     33 void GrStyle::WriteKey(uint32_t *key, const GrStyle &style, Apply apply, SkScalar scale,
     34                        uint32_t flags) {
     35     SkASSERT(key);
     36     SkASSERT(KeySize(style, apply) >= 0);
     37     GR_STATIC_ASSERT(sizeof(uint32_t) == sizeof(SkScalar));
     38 
     39     int i = 0;
     40     // The scale can influence both the path effect and stroking. We want to preserve the
     41     // property that the following two are equal:
     42     // 1. WriteKey with apply == kPathEffectAndStrokeRec
     43     // 2. WriteKey with apply == kPathEffectOnly followed by WriteKey of a GrStyle made
     44     //    from SkStrokeRec output by the the path effect (and no additional path effect).
     45     // Since the scale can affect both parts of 2 we write it into the key twice.
     46     if (style.isDashed()) {
     47         GR_STATIC_ASSERT(sizeof(style.dashPhase()) == sizeof(uint32_t));
     48         SkScalar phase = style.dashPhase();
     49         memcpy(&key[i++], &scale, sizeof(SkScalar));
     50         memcpy(&key[i++], &phase, sizeof(SkScalar));
     51 
     52         int32_t count = style.dashIntervalCnt();
     53         // Dash count should always be even.
     54         SkASSERT(0 == (count & 0x1));
     55         const SkScalar *intervals = style.dashIntervals();
     56         int intervalByteCnt = count * sizeof(SkScalar);
     57         memcpy(&key[i], intervals, intervalByteCnt);
     58         i += count;
     59     } else {
     60         SkASSERT(!style.pathEffect());
     61     }
     62 
     63     if (Apply::kPathEffectAndStrokeRec == apply && style.strokeRec().needToApply()) {
     64         memcpy(&key[i++], &scale, sizeof(SkScalar));
     65         enum {
     66             kStyleBits = 2,
     67             kJoinBits = 2,
     68             kCapBits = 32 - kStyleBits - kJoinBits,
     69 
     70             kJoinShift = kStyleBits,
     71             kCapShift = kJoinShift + kJoinBits,
     72         };
     73         GR_STATIC_ASSERT(SkStrokeRec::kStyleCount <= (1 << kStyleBits));
     74         GR_STATIC_ASSERT(SkPaint::kJoinCount <= (1 << kJoinBits));
     75         GR_STATIC_ASSERT(SkPaint::kCapCount <= (1 << kCapBits));
     76         // The cap type only matters for unclosed shapes. However, a path effect could unclose
     77         // the shape before it is stroked.
     78         SkPaint::Cap cap = SkPaint::kDefault_Cap;
     79         if (!(flags & kClosed_KeyFlag) || style.pathEffect()) {
     80             cap = style.strokeRec().getCap();
     81         }
     82         SkScalar miter = -1.f;
     83         SkPaint::Join join = SkPaint::kDefault_Join;
     84 
     85         // Dashing will not insert joins but other path effects may.
     86         if (!(flags & kNoJoins_KeyFlag) || style.hasNonDashPathEffect()) {
     87             join = style.strokeRec().getJoin();
     88             // Miter limit only affects miter joins
     89             if (SkPaint::kMiter_Join == join) {
     90                 miter = style.strokeRec().getMiter();
     91             }
     92         }
     93 
     94         key[i++] = style.strokeRec().getStyle() |
     95                    join << kJoinShift |
     96                    cap << kCapShift;
     97 
     98         memcpy(&key[i++], &miter, sizeof(miter));
     99 
    100         SkScalar width = style.strokeRec().getWidth();
    101         memcpy(&key[i++], &width, sizeof(width));
    102     }
    103     SkASSERT(KeySize(style, apply) == i);
    104 }
    105 
    106 void GrStyle::initPathEffect(sk_sp<SkPathEffect> pe) {
    107     SkASSERT(!fPathEffect);
    108     SkASSERT(SkPathEffect::kNone_DashType == fDashInfo.fType);
    109     SkASSERT(0 == fDashInfo.fIntervals.count());
    110     if (!pe) {
    111         return;
    112     }
    113     SkPathEffect::DashInfo info;
    114     if (SkPathEffect::kDash_DashType == pe->asADash(&info)) {
    115         SkStrokeRec::Style recStyle = fStrokeRec.getStyle();
    116         if (recStyle != SkStrokeRec::kFill_Style && recStyle != SkStrokeRec::kStrokeAndFill_Style) {
    117             fDashInfo.fType = SkPathEffect::kDash_DashType;
    118             fDashInfo.fIntervals.reset(info.fCount);
    119             fDashInfo.fPhase = info.fPhase;
    120             info.fIntervals = fDashInfo.fIntervals.get();
    121             pe->asADash(&info);
    122             fPathEffect = std::move(pe);
    123         }
    124     } else {
    125         fPathEffect = std::move(pe);
    126     }
    127 }
    128 
    129 bool GrStyle::applyPathEffect(SkPath* dst, SkStrokeRec* strokeRec, const SkPath& src) const {
    130     if (!fPathEffect) {
    131         return false;
    132     }
    133     if (SkPathEffect::kDash_DashType == fDashInfo.fType) {
    134         // We apply the dash ourselves here rather than using the path effect. This is so that
    135         // we can control whether the dasher applies the strokeRec for special cases. Our keying
    136         // depends on the strokeRec being applied separately.
    137         SkScalar phase = fDashInfo.fPhase;
    138         const SkScalar* intervals = fDashInfo.fIntervals.get();
    139         int intervalCnt = fDashInfo.fIntervals.count();
    140         SkScalar initialLength;
    141         int initialIndex;
    142         SkScalar intervalLength;
    143         SkDashPath::CalcDashParameters(phase, intervals, intervalCnt, &initialLength,
    144                                        &initialIndex, &intervalLength);
    145         if (!SkDashPath::InternalFilter(dst, src, strokeRec,
    146                                         nullptr, intervals, intervalCnt,
    147                                         initialLength, initialIndex, intervalLength,
    148                                         SkDashPath::StrokeRecApplication::kDisallow)) {
    149             return false;
    150         }
    151     } else if (!fPathEffect->filterPath(dst, src, strokeRec, nullptr)) {
    152         return false;
    153     }
    154     dst->setIsVolatile(true);
    155     return true;
    156 }
    157 
    158 bool GrStyle::applyPathEffectToPath(SkPath *dst, SkStrokeRec *remainingStroke,
    159                                     const SkPath &src, SkScalar resScale) const {
    160     SkASSERT(dst);
    161     SkStrokeRec strokeRec = fStrokeRec;
    162     strokeRec.setResScale(resScale);
    163     if (!this->applyPathEffect(dst, &strokeRec, src)) {
    164         return false;
    165     }
    166     *remainingStroke = strokeRec;
    167     return true;
    168 }
    169 
    170 bool GrStyle::applyToPath(SkPath* dst, SkStrokeRec::InitStyle* style, const SkPath& src,
    171                           SkScalar resScale) const {
    172     SkASSERT(style);
    173     SkASSERT(dst);
    174     SkStrokeRec strokeRec = fStrokeRec;
    175     strokeRec.setResScale(resScale);
    176     const SkPath* pathForStrokeRec = &src;
    177     if (this->applyPathEffect(dst, &strokeRec, src)) {
    178         pathForStrokeRec = dst;
    179     } else if (fPathEffect) {
    180         return false;
    181     }
    182     if (strokeRec.needToApply()) {
    183         if (!strokeRec.applyToPath(dst, *pathForStrokeRec)) {
    184             return false;
    185         }
    186         dst->setIsVolatile(true);
    187         *style = SkStrokeRec::kFill_InitStyle;
    188     } else if (!fPathEffect) {
    189         // Nothing to do for path effect or stroke, fail.
    190         return false;
    191     } else {
    192         SkASSERT(SkStrokeRec::kFill_Style == strokeRec.getStyle() ||
    193                  SkStrokeRec::kHairline_Style == strokeRec.getStyle());
    194         *style = strokeRec.getStyle() == SkStrokeRec::kFill_Style
    195                  ? SkStrokeRec::kFill_InitStyle
    196                  : SkStrokeRec::kHairline_InitStyle;
    197     }
    198     return true;
    199 }
    200