tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SkPathPriv.h (21460B)


      1 /*
      2 * Copyright 2015 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 SkPathPriv_DEFINED
      9 #define SkPathPriv_DEFINED
     10 
     11 #include "include/core/SkArc.h"
     12 #include "include/core/SkPath.h"
     13 #include "include/core/SkPathBuilder.h"
     14 #include "include/core/SkPathTypes.h"
     15 #include "include/core/SkPoint.h"
     16 #include "include/core/SkRect.h"
     17 #include "include/core/SkRefCnt.h"
     18 #include "include/core/SkScalar.h"
     19 #include "include/core/SkSpan.h"
     20 #include "include/core/SkTypes.h"
     21 #include "include/private/SkIDChangeListener.h"
     22 #include "include/private/SkPathRef.h"
     23 #include "include/private/base/SkDebug.h"
     24 #include "src/core/SkPathEnums.h"
     25 #include "src/core/SkPathRaw.h"
     26 
     27 #include <cstddef>
     28 #include <cstdint>
     29 #include <iterator>
     30 #include <optional>
     31 #include <utility>
     32 
     33 class SkMatrix;
     34 class SkRRect;
     35 
     36 static_assert(0 == static_cast<int>(SkPathFillType::kWinding), "fill_type_mismatch");
     37 static_assert(1 == static_cast<int>(SkPathFillType::kEvenOdd), "fill_type_mismatch");
     38 static_assert(2 == static_cast<int>(SkPathFillType::kInverseWinding), "fill_type_mismatch");
     39 static_assert(3 == static_cast<int>(SkPathFillType::kInverseEvenOdd), "fill_type_mismatch");
     40 
     41 // These are computed from a stream of verbs
     42 struct SkPathVerbAnalysis {
     43    size_t   points, weights;
     44    unsigned segmentMask;
     45    bool     valid;
     46 };
     47 
     48 class SkPathPriv {
     49 public:
     50    static SkPathConvexity ComputeConvexity(SkSpan<const SkPoint> pts,
     51                                            SkSpan<const SkPathVerb> points,
     52                                            SkSpan<const float> conicWeights);
     53 
     54    static uint8_t ComputeSegmentMask(SkSpan<const SkPathVerb>);
     55 
     56    static SkPathVerbAnalysis AnalyzeVerbs(SkSpan<const SkPathVerb> verbs);
     57 
     58    // skbug.com/40041027: Not a perfect solution for W plane clipping, but 1/16384 is a
     59    // reasonable limit (roughly 5e-5)
     60    inline static constexpr SkScalar kW0PlaneDistance = 1.f / (1 << 14);
     61 
     62    static SkPathFirstDirection AsFirstDirection(SkPathDirection dir) {
     63        // since we agree numerically for the values in Direction, we can just cast.
     64        return (SkPathFirstDirection)dir;
     65    }
     66 
     67    /**
     68     *  Return the opposite of the specified direction. kUnknown is its own
     69     *  opposite.
     70     */
     71    static SkPathFirstDirection OppositeFirstDirection(SkPathFirstDirection dir) {
     72        static const SkPathFirstDirection gOppositeDir[] = {
     73            SkPathFirstDirection::kCCW, SkPathFirstDirection::kCW, SkPathFirstDirection::kUnknown,
     74        };
     75        return gOppositeDir[(unsigned)dir];
     76    }
     77 
     78    /**
     79     *  Tries to compute the direction of the outer-most non-degenerate
     80     *  contour. If it can be computed, return that direction. If it cannot be determined,
     81     *  or the contour is known to be convex, return kUnknown. If the direction was determined,
     82     *  it is cached to make subsequent calls return quickly.
     83     */
     84    static SkPathFirstDirection ComputeFirstDirection(const SkPathRaw&);
     85    static SkPathFirstDirection ComputeFirstDirection(const SkPath&);
     86 
     87    static bool IsClosedSingleContour(SkSpan<const SkPathVerb> verbs) {
     88        if (verbs.empty()) {
     89            return false;
     90        }
     91 
     92        int moveCount = 0;
     93        for (const auto& verb : verbs) {
     94            switch (verb) {
     95                case SkPathVerb::kMove:
     96                    if (++moveCount > 1) {
     97                        return false;
     98                    }
     99                    break;
    100                case SkPathVerb::kClose:
    101                    return &verb == &verbs.back();
    102                default:
    103                    break;
    104            }
    105        }
    106        return false;
    107    }
    108 
    109    static bool IsClosedSingleContour(const SkPath& path) {
    110        return IsClosedSingleContour(path.fPathRef->verbs());
    111    }
    112 
    113    /*
    114     *  If we're transforming a known shape (oval or rrect), this computes what happens to its
    115     *  - winding direction
    116     *  - start index
    117     */
    118    static std::pair<SkPathDirection, unsigned>
    119    TransformDirAndStart(const SkMatrix&, bool isRRect, SkPathDirection dir, unsigned start);
    120 
    121    static void AddGenIDChangeListener(const SkPath& path, sk_sp<SkIDChangeListener> listener) {
    122        path.fPathRef->addGenIDChangeListener(std::move(listener));
    123    }
    124 
    125    /**
    126     * This returns the info for a rect that has a move followed by 3 or 4 lines and a close. If
    127     * 'isSimpleFill' is true, an uncloseed rect will also be accepted as long as it starts and
    128     * ends at the same corner. This does not permit degenerate line or point rectangles.
    129     */
    130    static std::optional<SkPathRectInfo> IsSimpleRect(const SkPath& path, bool isSimpleFill);
    131 
    132    // Asserts the path contour was built from RRect, so it does not return
    133    // an optional. This exists so path's can have a flag that they are really
    134    // a RRect, without having to actually store the 4 radii... since those can
    135    // be deduced from the contour itself.
    136    //
    137    static SkRRect DeduceRRectFromContour(const SkRect& bounds,
    138                                          SkSpan<const SkPoint>, SkSpan<const SkPathVerb>);
    139 
    140    /**
    141     * Creates a path from arc params using the semantics of SkCanvas::drawArc. This function
    142     * assumes empty ovals and zero sweeps have already been filtered out.
    143     */
    144    static SkPath CreateDrawArcPath(const SkArc& arc, bool isFillNoPathEffect);
    145 
    146    /**
    147     * Determines whether an arc produced by CreateDrawArcPath will be convex. Assumes a non-empty
    148     * oval.
    149     */
    150    static bool DrawArcIsConvex(SkScalar sweepAngle, SkArc::Type arcType, bool isFillNoPathEffect);
    151 
    152    static void ShrinkToFit(SkPath* path) {
    153        path->shrinkToFit();
    154    }
    155 
    156    /**
    157      * Iterates through a raw range of path verbs, points, and conics. All values are returned
    158      * unaltered.
    159      *
    160      * NOTE: This class's definition will be moved into SkPathPriv once RangeIter is removed.
    161    */
    162    using RangeIter = SkPath::RangeIter;
    163 
    164    /**
    165     * Iterable object for traversing verbs, points, and conic weights in a path:
    166     *
    167     *   for (auto [verb, pts, weights] : SkPathPriv::Iterate(skPath)) {
    168     *       ...
    169     *   }
    170     */
    171    struct Iterate {
    172    public:
    173        Iterate(const SkPath& path)
    174                : Iterate(path.fPathRef->verbsBegin(),
    175                          // Don't allow iteration through non-finite points.
    176                          (!path.isFinite()) ? path.fPathRef->verbsBegin()
    177                                             : path.fPathRef->verbsEnd(),
    178                          path.fPathRef->points(), path.fPathRef->conicWeights()) {
    179        }
    180        Iterate(const SkPathVerb* verbsBegin, const SkPathVerb* verbsEnd, const SkPoint* points,
    181                const SkScalar* weights)
    182                : fVerbsBegin(verbsBegin), fVerbsEnd(verbsEnd), fPoints(points), fWeights(weights) {
    183        }
    184        SkPath::RangeIter begin() { return {fVerbsBegin, fPoints, fWeights}; }
    185        SkPath::RangeIter end() { return {fVerbsEnd, nullptr, nullptr}; }
    186    private:
    187        const SkPathVerb* fVerbsBegin;
    188        const SkPathVerb* fVerbsEnd;
    189        const SkPoint* fPoints;
    190        const SkScalar* fWeights;
    191    };
    192 
    193    /**
    194     * Returns a pointer to the verb data.
    195     */
    196    static const SkPathVerb* VerbData(const SkPath& path) {
    197        return path.fPathRef->verbsBegin();
    198    }
    199 
    200    /** Returns a raw pointer to the path points */
    201    static const SkPoint* PointData(const SkPath& path) {
    202        return path.fPathRef->points();
    203    }
    204 
    205    /** Returns the number of conic weights in the path */
    206    static int ConicWeightCnt(const SkPath& path) {
    207        return path.fPathRef->countWeights();
    208    }
    209 
    210    /** Returns a raw pointer to the path conic weights. */
    211    static const SkScalar* ConicWeightData(const SkPath& path) {
    212        return path.fPathRef->conicWeights();
    213    }
    214 
    215    /** Returns true if the underlying SkPathRef has one single owner. */
    216    static bool TestingOnly_unique(const SkPath& path) {
    217        return path.fPathRef->unique();
    218    }
    219 
    220    // Won't be needed once we can make path's immutable (with their bounds always computed)
    221    static bool HasComputedBounds(const SkPath& path) {
    222        return path.hasComputedBounds();
    223    }
    224 
    225    /** Returns the oval info if this path was created as an oval or circle, else returns {}.
    226     */
    227    static std::optional<SkPathOvalInfo> IsOval(const SkPath& path) {
    228        return path.fPathRef->isOval();
    229    }
    230 
    231    /** Returns the rrect info if this path was created as one, else returns {}.
    232     */
    233    static std::optional<SkPathRRectInfo> IsRRect(const SkPath& path) {
    234        return path.fPathRef->isRRect();
    235    }
    236 
    237    /**
    238     *  Sometimes in the drawing pipeline, we have to perform math on path coordinates, even after
    239     *  the path is in device-coordinates. Tessellation and clipping are two examples. Usually this
    240     *  is pretty modest, but it can involve subtracting/adding coordinates, or multiplying by
    241     *  small constants (e.g. 2,3,4). To try to preflight issues where these optionations could turn
    242     *  finite path values into infinities (or NaNs), we allow the upper drawing code to reject
    243     *  the path if its bounds (in device coordinates) is too close to max float.
    244     */
    245    static bool TooBigForMath(const SkRect& bounds) {
    246        // This value is just a guess. smaller is safer, but we don't want to reject largish paths
    247        // that we don't have to.
    248        constexpr SkScalar scale_down_to_allow_for_small_multiplies = 0.25f;
    249        constexpr SkScalar max = SK_ScalarMax * scale_down_to_allow_for_small_multiplies;
    250 
    251        // use ! expression so we return true if bounds contains NaN
    252        return !(bounds.fLeft >= -max && bounds.fTop >= -max &&
    253                 bounds.fRight <= max && bounds.fBottom <= max);
    254    }
    255 
    256    // Returns number of valid points for each SkPath::Iter verb
    257    static int PtsInIter(unsigned verb) {
    258        static const uint8_t gPtsInVerb[] = {
    259            1,  // kMove    pts[0]
    260            2,  // kLine    pts[0..1]
    261            3,  // kQuad    pts[0..2]
    262            3,  // kConic   pts[0..2]
    263            4,  // kCubic   pts[0..3]
    264            0,  // kClose
    265            0   // kDone
    266        };
    267 
    268        SkASSERT(verb < std::size(gPtsInVerb));
    269        return gPtsInVerb[verb];
    270    }
    271 
    272    static int PtsInIter(SkPathVerb verb) { return PtsInIter((unsigned)verb); }
    273 
    274    // Returns number of valid points for each verb, not including the "starter"
    275    // point that the Iterator adds for line/quad/conic/cubic
    276    static int PtsInVerb(unsigned verb) {
    277        static const uint8_t gPtsInVerb[] = {
    278            1,  // kMove    pts[0]
    279            1,  // kLine    pts[0..1]
    280            2,  // kQuad    pts[0..2]
    281            2,  // kConic   pts[0..2]
    282            3,  // kCubic   pts[0..3]
    283            0,  // kClose
    284            0   // kDone
    285        };
    286 
    287        SkASSERT(verb < std::size(gPtsInVerb));
    288        return gPtsInVerb[verb];
    289    }
    290 
    291    static int PtsInVerb(SkPathVerb verb) { return PtsInVerb((unsigned)verb); }
    292 
    293    static bool IsAxisAligned(SkSpan<const SkPoint>);
    294    static bool IsAxisAligned(const SkPath& path);
    295 
    296    static bool AllPointsEq(SkSpan<const SkPoint> pts) {
    297        for (size_t i = 1; i < pts.size(); ++i) {
    298            if (pts[0] != pts[i]) {
    299                return false;
    300            }
    301        }
    302        return true;
    303    }
    304 
    305    static int LastMoveToIndex(const SkPath& path) { return path.fLastMoveToIndex; }
    306 
    307    struct RectContour {
    308        SkRect          fRect;
    309        bool            fIsClosed;
    310        SkPathDirection fDirection;
    311        size_t          fPointsConsumed,
    312                        fVerbsConsumed;
    313    };
    314    static std::optional<RectContour> IsRectContour(SkSpan<const SkPoint> ptSpan,
    315                                                    SkSpan<const SkPathVerb> vbSpan,
    316                                                    bool allowPartial);
    317 
    318    /** Returns true if SkPath is equivalent to nested SkRect pair when filled.
    319     If false, rect and dirs are unchanged.
    320     If true, rect and dirs are written to if not nullptr:
    321     setting rect[0] to outer SkRect, and rect[1] to inner SkRect;
    322     setting dirs[0] to SkPathDirection of outer SkRect, and dirs[1] to SkPathDirection of
    323     inner SkRect.
    324 
    325     @param rect  storage for SkRect pair; may be nullptr
    326     @param dirs  storage for SkPathDirection pair; may be nullptr
    327     @return      true if SkPath contains nested SkRect pair
    328     */
    329    static bool IsNestedFillRects(const SkPathRaw&, SkRect rect[2],
    330                                  SkPathDirection dirs[2] = nullptr);
    331 
    332    static bool IsNestedFillRects(const SkPath& path, SkRect rect[2],
    333                                  SkPathDirection dirs[2] = nullptr) {
    334        return IsNestedFillRects(Raw(path), rect, dirs);
    335    }
    336 
    337 
    338    static bool IsInverseFillType(SkPathFillType fill) {
    339        return (static_cast<int>(fill) & 2) != 0;
    340    }
    341 
    342    /*
    343     *  We are effectively empty if we have zero or one verbs.
    344     *  Zero obviously means we're empty.
    345     *  One means we only have a MoveTo -- but no segments, so this is effectively
    346     *  empty (e.g. when adding another contour, this moveTo will be overwritten).
    347     */
    348    static bool IsEffectivelyEmpty(const SkPath& path) {
    349        return path.countVerbs() <= 1;
    350    }
    351    static bool IsEffectivelyEmpty(const SkPathBuilder& builder) {
    352        return builder.verbs().size() <= 1;
    353    }
    354 
    355    /** Returns equivalent SkPath::FillType representing SkPath fill inside its bounds.
    356     .
    357 
    358     @param fill  one of: kWinding_FillType, kEvenOdd_FillType,
    359     kInverseWinding_FillType, kInverseEvenOdd_FillType
    360     @return      fill, or kWinding_FillType or kEvenOdd_FillType if fill is inverted
    361     */
    362    static SkPathFillType ConvertToNonInverseFillType(SkPathFillType fill) {
    363        return (SkPathFillType)(static_cast<int>(fill) & 1);
    364    }
    365 
    366    /**
    367     *  If needed (to not blow-up under a perspective matrix), clip the path, returning the
    368     *  answer in "result", and return true.
    369     *
    370     *  Note result might be empty (if the path was completely clipped out).
    371     *
    372     *  If no clipping is needed, returns false and "result" is left unchanged.
    373     */
    374    static bool PerspectiveClip(const SkPath& src, const SkMatrix&, SkPath* result);
    375 
    376    /**
    377     * Gets the number of GenIDChangeListeners. If another thread has access to this path then
    378     * this may be stale before return and only indicates that the count was the return value
    379     * at some point during the execution of the function.
    380     */
    381    static int GenIDChangeListenersCount(const SkPath&);
    382 
    383    static void UpdatePathPoint(SkPath* path, int index, const SkPoint& pt) {
    384        SkASSERT(index < path->countPoints());
    385        SkPathRef::Editor ed(&path->fPathRef);
    386        ed.writablePoints()[index] = pt;
    387        path->dirtyAfterEdit();
    388    }
    389 
    390    static SkPathConvexity GetConvexity(const SkPath& path) {
    391        return path.getConvexity();
    392    }
    393    static SkPathConvexity GetConvexityOrUnknown(const SkPath& path) {
    394        return path.getConvexityOrUnknown();
    395    }
    396    static void SetConvexity(const SkPath& path, SkPathConvexity c) {
    397        path.setConvexity(c);
    398    }
    399    static void ForceComputeConvexity(const SkPath& path) {
    400        path.setConvexity(SkPathConvexity::kUnknown);
    401        (void)path.isConvex();
    402    }
    403 
    404    static void ReverseAddPath(SkPathBuilder* builder, const SkPath& reverseMe) {
    405        builder->privateReverseAddPath(reverseMe);
    406    }
    407 
    408    static void ReversePathTo(SkPathBuilder* builder, const SkPath& reverseMe) {
    409        builder->privateReversePathTo(reverseMe);
    410    }
    411 
    412    static SkPath ReversePath(const SkPath& reverseMe) {
    413        SkPathBuilder bu;
    414        bu.privateReverseAddPath(reverseMe);
    415        return bu.detach();
    416    }
    417 
    418    static std::optional<SkPoint> GetPoint(const SkPathBuilder& builder, int index) {
    419        if ((unsigned)index < (unsigned)builder.fPts.size()) {
    420            return builder.fPts.at(index);
    421        }
    422        return std::nullopt;
    423    }
    424 
    425    static SkSpan<const SkPathVerb> GetVerbs(const SkPathBuilder& builder) {
    426        return builder.fVerbs;
    427    }
    428 
    429    static int CountVerbs(const SkPathBuilder& builder) {
    430        return builder.fVerbs.size();
    431    }
    432 
    433    static SkPath MakePath(const SkPathVerbAnalysis& analysis,
    434                           const SkPoint points[],
    435                           SkSpan<const SkPathVerb> verbs,
    436                           const SkScalar conics[],
    437                           SkPathFillType fillType,
    438                           bool isVolatile) {
    439        return SkPath::MakeInternal(analysis, points, verbs, conics, fillType, isVolatile);
    440    }
    441 
    442    static SkPathRaw Raw(const SkPath& path) {
    443        const SkPathRef* ref = path.fPathRef.get();
    444        SkASSERT(ref);
    445        const SkRect bounds = ref->isFinite()
    446                                      ? ref->getBounds()
    447                                      : SkRect{SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN};
    448        return {
    449                ref->pointSpan(),
    450                ref->verbs(),
    451                ref->conicSpan(),
    452                bounds,
    453                path.getFillType(),
    454                path.isConvex(),
    455                SkTo<uint8_t>(ref->getSegmentMasks()),
    456        };
    457    }
    458 
    459    static SkPathRaw Raw(const SkPathBuilder& builder) {
    460        const SkRect bounds = builder.isFinite()
    461                                      ? builder.computeBounds()
    462                                      : SkRect{SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN};
    463        SkPathConvexity convexity = builder.fConvexity;
    464        if (convexity == SkPathConvexity::kUnknown) {
    465            convexity = SkPathPriv::ComputeConvexity(
    466                    builder.fPts, builder.fVerbs, builder.fConicWeights);
    467        }
    468        return {
    469                builder.points(),
    470                builder.verbs(),
    471                builder.conicWeights(),
    472                bounds,
    473                builder.fillType(),
    474                SkPathConvexity_IsConvex(convexity),
    475                SkTo<uint8_t>(builder.fSegmentMask),
    476        };
    477    }
    478 };
    479 
    480 // Lightweight variant of SkPath::Iter that only returns segments (e.g. lines/conics).
    481 // Does not return kMove or kClose.
    482 // Always "auto-closes" each contour.
    483 // Roughly the same as SkPath::Iter(path, true), but does not return moves or closes
    484 //
    485 class SkPathEdgeIter {
    486    const SkPathVerb* fVerbs;
    487    const SkPathVerb* fVerbsStop;
    488    const SkPoint*  fPts;
    489    const SkPoint*  fMoveToPtr;
    490    const SkScalar* fConicWeights;
    491    SkPoint         fScratch[2];    // for auto-close lines
    492    bool            fNeedsCloseLine;
    493    bool            fNextIsNewContour;
    494    SkDEBUGCODE(bool fIsConic;)
    495 
    496 public:
    497    SkPathEdgeIter(const SkPath& path);
    498    SkPathEdgeIter(const SkPathRaw&);
    499 
    500    SkScalar conicWeight() const {
    501        SkASSERT(fIsConic);
    502        return *fConicWeights;
    503    }
    504 
    505    enum class Edge {
    506        kLine = (int)SkPathVerb::kLine,
    507        kQuad = (int)SkPathVerb::kQuad,
    508        kConic = (int)SkPathVerb::kConic,
    509        kCubic = (int)SkPathVerb::kCubic,
    510        kInvalid = 99,
    511    };
    512 
    513    static SkPathVerb EdgeToVerb(Edge e) {
    514        return SkPathVerb(e);
    515    }
    516 
    517    // todo: return as optional? fPts become span?
    518    struct Result {
    519        const SkPoint*  fPts;   // points for the segment, or null if done
    520        Edge            fEdge;
    521        bool            fIsNewContour;
    522 
    523        // Returns true when it holds an Edge, false when the path is done.
    524        explicit operator bool() { return fPts != nullptr; }
    525    };
    526 
    527    Result next() {
    528        auto closeline = [&]() {
    529            fScratch[0] = fPts[-1];
    530            fScratch[1] = *fMoveToPtr;
    531            fNeedsCloseLine = false;
    532            fNextIsNewContour = true;
    533            return Result{ fScratch, Edge::kLine, false };
    534        };
    535 
    536        for (;;) {
    537            SkASSERT(fVerbs <= fVerbsStop);
    538            if (fVerbs == fVerbsStop) {
    539                return fNeedsCloseLine ? closeline() : Result{nullptr, Edge::kInvalid, false};
    540            }
    541 
    542            SkDEBUGCODE(fIsConic = false;)
    543 
    544            const auto verb = *fVerbs++;
    545            switch (verb) {
    546                case SkPathVerb::kMove: {
    547                    if (fNeedsCloseLine) {
    548                        auto res = closeline();
    549                        fMoveToPtr = fPts++;
    550                        return res;
    551                    }
    552                    fMoveToPtr = fPts++;
    553                    fNextIsNewContour = true;
    554                } break;
    555                case SkPathVerb::kClose:
    556                    if (fNeedsCloseLine) return closeline();
    557                    break;
    558                default: {
    559                    unsigned v = static_cast<unsigned>(verb);
    560                    // Actual edge.
    561                    const int pts_count = (v+2) / 2,
    562                              cws_count = (v & (v-1)) / 2;
    563                    SkASSERT(pts_count == SkPathPriv::PtsInIter(v) - 1);
    564 
    565                    fNeedsCloseLine = true;
    566                    fPts           += pts_count;
    567                    fConicWeights  += cws_count;
    568 
    569                    SkDEBUGCODE(fIsConic = (verb == SkPathVerb::kConic);)
    570                    SkASSERT(fIsConic == (cws_count > 0));
    571 
    572                    bool isNewContour = fNextIsNewContour;
    573                    fNextIsNewContour = false;
    574                    return { &fPts[-(pts_count + 1)], Edge(v), isNewContour };
    575                }
    576            }
    577        }
    578    }
    579 };
    580 
    581 #endif