Home | History | Annotate | Download | only in effects
      1 /*
      2  * Copyright 2018 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 "SkPathMeasure.h"
      9 #include "SkTrimPathEffect.h"
     10 #include "SkTrimPE.h"
     11 #include "SkReadBuffer.h"
     12 #include "SkWriteBuffer.h"
     13 
     14 namespace {
     15 
     16 class Segmentator : public SkNoncopyable {
     17 public:
     18     Segmentator(const SkPath& src, SkPath* dst)
     19         : fMeasure(src, false)
     20         , fDst(dst) {}
     21 
     22     void add(SkScalar start, SkScalar stop) {
     23         SkASSERT(start < stop);
     24 
     25         // TODO: we appear to skip zero-length contours.
     26         do {
     27             const auto nextOffset = fCurrentSegmentOffset + fMeasure.getLength();
     28 
     29             if (start < nextOffset) {
     30                 fMeasure.getSegment(start - fCurrentSegmentOffset,
     31                                     stop  - fCurrentSegmentOffset,
     32                                     fDst, true);
     33 
     34                 if (stop < nextOffset)
     35                     break;
     36             }
     37 
     38             fCurrentSegmentOffset = nextOffset;
     39         } while (fMeasure.nextContour());
     40     }
     41 
     42 private:
     43     SkPathMeasure fMeasure;
     44     SkPath*       fDst;
     45 
     46     SkScalar fCurrentSegmentOffset = 0;
     47 
     48     using INHERITED = SkNoncopyable;
     49 };
     50 
     51 } // namespace
     52 
     53 SkTrimPE::SkTrimPE(SkScalar startT, SkScalar stopT, SkTrimPathEffect::Mode mode)
     54     : fStartT(startT), fStopT(stopT), fMode(mode) {}
     55 
     56 bool SkTrimPE::onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec,
     57                             const SkRect* cullRect) const {
     58     if (fStartT >= fStopT) {
     59         SkASSERT(fMode == SkTrimPathEffect::Mode::kNormal);
     60         return true;
     61     }
     62 
     63     // First pass: compute the total len.
     64     SkScalar len = 0;
     65     SkPathMeasure meas(src, false);
     66     do {
     67         len += meas.getLength();
     68     } while (meas.nextContour());
     69 
     70     const auto arcStart = len * fStartT,
     71                arcStop  = len * fStopT;
     72 
     73     // Second pass: actually add segments.
     74     Segmentator segmentator(src, dst);
     75     if (fMode == SkTrimPathEffect::Mode::kNormal) {
     76         if (arcStart < arcStop) segmentator.add(arcStart, arcStop);
     77     } else {
     78         if (0 <  arcStart) segmentator.add(0,  arcStart);
     79         if (arcStop < len) segmentator.add(arcStop, len);
     80     }
     81 
     82     return true;
     83 }
     84 
     85 void SkTrimPE::flatten(SkWriteBuffer& buffer) const {
     86     buffer.writeScalar(fStartT);
     87     buffer.writeScalar(fStopT);
     88     buffer.writeUInt(static_cast<uint32_t>(fMode));
     89 }
     90 
     91 sk_sp<SkFlattenable> SkTrimPE::CreateProc(SkReadBuffer& buffer) {
     92     const auto start = buffer.readScalar(),
     93                stop  = buffer.readScalar();
     94     const auto mode  = buffer.readUInt();
     95 
     96     return SkTrimPathEffect::Make(start, stop,
     97         (mode & 1) ? SkTrimPathEffect::Mode::kInverted : SkTrimPathEffect::Mode::kNormal);
     98 }
     99 
    100 //////////////////////////////////////////////////////////////////////////////////////////////////
    101 
    102 sk_sp<SkPathEffect> SkTrimPathEffect::Make(SkScalar startT, SkScalar stopT, Mode mode) {
    103     if (!SkScalarsAreFinite(startT, stopT)) {
    104         return nullptr;
    105     }
    106 
    107     if (startT <= 0 && stopT >= 1 && mode == Mode::kNormal) {
    108         return nullptr;
    109     }
    110 
    111     startT = SkTPin(startT, 0.f, 1.f);
    112     stopT  = SkTPin(stopT,  0.f, 1.f);
    113 
    114     if (startT >= stopT && mode == Mode::kInverted) {
    115         return nullptr;
    116     }
    117 
    118     return sk_sp<SkPathEffect>(new SkTrimPE(startT, stopT, mode));
    119 }
    120