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 #ifndef GrStyle_DEFINED
      9 #define GrStyle_DEFINED
     10 
     11 #include "GrTypes.h"
     12 #include "SkPathEffect.h"
     13 #include "SkStrokeRec.h"
     14 #include "SkTemplates.h"
     15 
     16 /**
     17  * Represents the various ways that a GrShape can be styled. It has fill/stroking information
     18  * as well as an optional path effect. If the path effect represents dashing, the dashing
     19  * information is extracted from the path effect and stored explicitly.
     20  *
     21  * This will replace GrStrokeInfo as GrShape is deployed.
     22  */
     23 class GrStyle {
     24 public:
     25     /**
     26      * A style object that represents a fill with no path effect.
     27      * TODO: constexpr with C++14
     28      */
     29     static const GrStyle& SimpleFill() {
     30         static const GrStyle kFill(SkStrokeRec::kFill_InitStyle);
     31         return kFill;
     32         }
     33 
     34     /**
     35      * A style object that represents a hairline stroke with no path effect.
     36      * TODO: constexpr with C++14
     37      */
     38     static const GrStyle& SimpleHairline() {
     39         static const GrStyle kHairline(SkStrokeRec::kHairline_InitStyle);
     40         return kHairline;
     41     }
     42 
     43     enum class Apply {
     44         kPathEffectOnly,
     45         kPathEffectAndStrokeRec
     46     };
     47 
     48     /**
     49      * Optional flags for computing keys that may remove unnecessary variation in the key due to
     50      * style settings that don't affect particular classes of geometry.
     51      */
     52     enum KeyFlags {
     53         // The shape being styled has no open contours.
     54         kClosed_KeyFlag = 0x1,
     55         // The shape being styled doesn't have any joins and so isn't affected by join type.
     56         kNoJoins_KeyFlag = 0x2
     57     };
     58 
     59     /**
     60      * Computes the key length for a GrStyle. The return will be negative if it cannot be turned
     61      * into a key. This occurs when there is a path effect that is not a dash. The key can
     62      * either reflect just the path effect (if one) or the path effect and the strokerec. Note
     63      * that a simple fill has a zero sized key.
     64      */
     65     static int KeySize(const GrStyle&, Apply, uint32_t flags = 0);
     66 
     67     /**
     68      * Writes a unique key for the style into the provided buffer. This function assumes the buffer
     69      * has room for at least KeySize() values. It assumes that KeySize() returns a non-negative
     70      * value for the combination of GrStyle, Apply and flags params. This is written so that the key
     71      * for just dash application followed by the key for the remaining SkStrokeRec is the same as
     72      * the key for applying dashing and SkStrokeRec all at once.
     73      */
     74     static void WriteKey(uint32_t*, const GrStyle&, Apply, SkScalar scale, uint32_t flags = 0);
     75 
     76     GrStyle() : GrStyle(SkStrokeRec::kFill_InitStyle) {}
     77 
     78     explicit GrStyle(SkStrokeRec::InitStyle initStyle) : fStrokeRec(initStyle) {}
     79 
     80     GrStyle(const SkStrokeRec& strokeRec, sk_sp<SkPathEffect> pe) : fStrokeRec(strokeRec) {
     81         this->initPathEffect(std::move(pe));
     82     }
     83 
     84     GrStyle(const GrStyle& that) = default;
     85 
     86     explicit GrStyle(const SkPaint& paint) : fStrokeRec(paint) {
     87         this->initPathEffect(paint.refPathEffect());
     88     }
     89 
     90     explicit GrStyle(const SkPaint& paint, SkPaint::Style overrideStyle)
     91             : fStrokeRec(paint, overrideStyle) {
     92         this->initPathEffect(paint.refPathEffect());
     93     }
     94 
     95     GrStyle& operator=(const GrStyle& that) {
     96         fPathEffect = that.fPathEffect;
     97         fDashInfo = that.fDashInfo;
     98         fStrokeRec = that.fStrokeRec;
     99         return *this;
    100     }
    101 
    102     void resetToInitStyle(SkStrokeRec::InitStyle fillOrHairline) {
    103         fDashInfo.reset();
    104         fPathEffect.reset(nullptr);
    105         if (SkStrokeRec::kFill_InitStyle == fillOrHairline) {
    106             fStrokeRec.setFillStyle();
    107         } else {
    108             fStrokeRec.setHairlineStyle();
    109         }
    110     }
    111 
    112     /** Is this style a fill with no path effect? */
    113     bool isSimpleFill() const { return fStrokeRec.isFillStyle() && !fPathEffect; }
    114 
    115     /** Is this style a hairline with no path effect? */
    116     bool isSimpleHairline() const { return fStrokeRec.isHairlineStyle() && !fPathEffect; }
    117 
    118     SkPathEffect* pathEffect() const { return fPathEffect.get(); }
    119     sk_sp<SkPathEffect> refPathEffect() const { return fPathEffect; }
    120 
    121     bool hasPathEffect() const { return SkToBool(fPathEffect.get()); }
    122 
    123     bool hasNonDashPathEffect() const { return fPathEffect.get() && !this->isDashed(); }
    124 
    125     bool isDashed() const { return SkPathEffect::kDash_DashType == fDashInfo.fType; }
    126     SkScalar dashPhase() const {
    127         SkASSERT(this->isDashed());
    128         return fDashInfo.fPhase;
    129     }
    130     int dashIntervalCnt() const {
    131         SkASSERT(this->isDashed());
    132         return fDashInfo.fIntervals.count();
    133     }
    134     const SkScalar* dashIntervals() const {
    135         SkASSERT(this->isDashed());
    136         return fDashInfo.fIntervals.get();
    137     }
    138 
    139     const SkStrokeRec& strokeRec() const { return fStrokeRec; }
    140 
    141     /** Hairline or fill styles without path effects make no alterations to a geometry. */
    142     bool applies() const {
    143         return this->pathEffect() || (!fStrokeRec.isFillStyle() && !fStrokeRec.isHairlineStyle());
    144     }
    145 
    146     static SkScalar MatrixToScaleFactor(const SkMatrix& matrix) {
    147         // getMaxScale will return -1 if the matrix has perspective. In that case we can use a scale
    148         // factor of 1. This isn't necessarily a good choice and in the future we might consider
    149         // taking a bounds here for the perspective case.
    150         return SkScalarAbs(matrix.getMaxScale());
    151     }
    152     /**
    153      * Applies just the path effect and returns remaining stroke information. This will fail if
    154      * there is no path effect. dst may or may not have been overwritten on failure. Scale controls
    155      * geometric approximations made by the path effect. It is typically computed from the view
    156      * matrix.
    157      */
    158     bool SK_WARN_UNUSED_RESULT applyPathEffectToPath(SkPath* dst, SkStrokeRec* remainingStoke,
    159                                                      const SkPath& src, SkScalar scale) const;
    160 
    161     /**
    162      * If this succeeds then the result path should be filled or hairlined as indicated by the
    163      * returned SkStrokeRec::InitStyle value. Will fail if there is no path effect and the
    164      * strokerec doesn't change the geometry. When this fails the outputs may or may not have
    165      * been overwritten. Scale controls geometric approximations made by the path effect and
    166      * stroker. It is typically computed from the view matrix.
    167      */
    168     bool SK_WARN_UNUSED_RESULT applyToPath(SkPath* dst, SkStrokeRec::InitStyle* fillOrHairline,
    169                                            const SkPath& src, SkScalar scale) const;
    170 
    171     /** Given bounds of a path compute the bounds of path with the style applied. */
    172     void adjustBounds(SkRect* dst, const SkRect& src) const {
    173         if (this->pathEffect()) {
    174             this->pathEffect()->computeFastBounds(dst, src);
    175             // This may not be the correct SkStrokeRec to use. skbug.com/5299
    176             // It happens to work for dashing.
    177             SkScalar radius = fStrokeRec.getInflationRadius();
    178             dst->outset(radius, radius);
    179         } else {
    180             SkScalar radius = fStrokeRec.getInflationRadius();
    181             *dst = src.makeOutset(radius, radius);
    182         }
    183     }
    184 
    185 private:
    186     void initPathEffect(sk_sp<SkPathEffect> pe);
    187 
    188     struct DashInfo {
    189         DashInfo() : fType(SkPathEffect::kNone_DashType) {}
    190         DashInfo(const DashInfo& that) { *this = that; }
    191         DashInfo& operator=(const DashInfo& that) {
    192             fType = that.fType;
    193             fPhase = that.fPhase;
    194             fIntervals.reset(that.fIntervals.count());
    195             sk_careful_memcpy(fIntervals.get(), that.fIntervals.get(),
    196                               sizeof(SkScalar) * that.fIntervals.count());
    197             return *this;
    198         }
    199         void reset() {
    200             fType = SkPathEffect::kNone_DashType;
    201             fIntervals.reset(0);
    202         }
    203         SkPathEffect::DashType      fType;
    204         SkScalar                    fPhase;
    205         SkAutoSTArray<4, SkScalar>  fIntervals;
    206     };
    207 
    208     bool applyPathEffect(SkPath* dst, SkStrokeRec* strokeRec, const SkPath& src) const;
    209 
    210     SkStrokeRec         fStrokeRec;
    211     sk_sp<SkPathEffect> fPathEffect;
    212     DashInfo            fDashInfo;
    213 };
    214 
    215 #endif
    216