Home | History | Annotate | Download | only in effects
      1 
      2 /*
      3  * Copyright 2006 The Android Open Source Project
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 #include "SkDashPathEffect.h"
     11 #include "SkBuffer.h"
     12 #include "SkPathMeasure.h"
     13 
     14 static inline int is_even(int x) {
     15     return (~x) << 31;
     16 }
     17 
     18 static SkScalar FindFirstInterval(const SkScalar intervals[], SkScalar phase,
     19                                   int32_t* index) {
     20     int i;
     21 
     22     for (i = 0; phase > intervals[i]; i++) {
     23         phase -= intervals[i];
     24     }
     25     *index = i;
     26     return intervals[i] - phase;
     27 }
     28 
     29 SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count,
     30                                    SkScalar phase, bool scaleToFit)
     31         : fScaleToFit(scaleToFit) {
     32     SkASSERT(intervals);
     33     SkASSERT(count > 1 && SkAlign2(count) == count);
     34 
     35     fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count);
     36     fCount = count;
     37 
     38     SkScalar len = 0;
     39     for (int i = 0; i < count; i++) {
     40         SkASSERT(intervals[i] >= 0);
     41         fIntervals[i] = intervals[i];
     42         len += intervals[i];
     43     }
     44     fIntervalLength = len;
     45 
     46     if (len > 0) {  // we don't handle 0 length dash arrays
     47         if (phase < 0) {
     48             phase = -phase;
     49             if (phase > len) {
     50                 phase = SkScalarMod(phase, len);
     51             }
     52             phase = len - phase;
     53         } else if (phase >= len) {
     54             phase = SkScalarMod(phase, len);
     55         }
     56 
     57         // got to watch out for values that might make us go out of bounds
     58         if (!SkScalarIsFinite(phase) || !SkScalarIsFinite(len)) {
     59             goto BAD_DASH;
     60         }
     61 
     62         SkASSERT(phase >= 0 && phase < len);
     63         fInitialDashLength = FindFirstInterval(intervals, phase, &fInitialDashIndex);
     64 
     65         SkASSERT(fInitialDashLength >= 0);
     66         SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount);
     67     } else {
     68         BAD_DASH:
     69         fInitialDashLength = -1;    // signal bad dash intervals
     70     }
     71 }
     72 
     73 SkDashPathEffect::~SkDashPathEffect() {
     74     sk_free(fIntervals);
     75 }
     76 
     77 bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src,
     78                                   SkScalar* width) {
     79     // we do nothing if the src wants to be filled, or if our dashlength is 0
     80     if (*width < 0 || fInitialDashLength < 0) {
     81         return false;
     82     }
     83 
     84     SkPathMeasure   meas(src, false);
     85     const SkScalar* intervals = fIntervals;
     86 
     87     do {
     88         bool        skipFirstSegment = meas.isClosed();
     89         bool        addedSegment = false;
     90         SkScalar    length = meas.getLength();
     91         int         index = fInitialDashIndex;
     92         SkScalar    scale = SK_Scalar1;
     93 
     94         if (fScaleToFit) {
     95             if (fIntervalLength >= length) {
     96                 scale = SkScalarDiv(length, fIntervalLength);
     97             } else {
     98                 SkScalar div = SkScalarDiv(length, fIntervalLength);
     99                 int n = SkScalarFloor(div);
    100                 scale = SkScalarDiv(length, n * fIntervalLength);
    101             }
    102         }
    103 
    104         SkScalar    distance = 0;
    105         SkScalar    dlen = SkScalarMul(fInitialDashLength, scale);
    106 
    107         while (distance < length) {
    108             SkASSERT(dlen >= 0);
    109             addedSegment = false;
    110             if (is_even(index) && dlen > 0 && !skipFirstSegment) {
    111                 addedSegment = true;
    112                 meas.getSegment(distance, distance + dlen, dst, true);
    113             }
    114             distance += dlen;
    115 
    116             // clear this so we only respect it the first time around
    117             skipFirstSegment = false;
    118 
    119             // wrap around our intervals array if necessary
    120             index += 1;
    121             SkASSERT(index <= fCount);
    122             if (index == fCount) {
    123                 index = 0;
    124             }
    125 
    126             // fetch our next dlen
    127             dlen = SkScalarMul(intervals[index], scale);
    128         }
    129 
    130         // extend if we ended on a segment and we need to join up with the (skipped) initial segment
    131         if (meas.isClosed() && is_even(fInitialDashIndex) &&
    132                 fInitialDashLength > 0) {
    133             meas.getSegment(0, SkScalarMul(fInitialDashLength, scale), dst, !addedSegment);
    134         }
    135     } while (meas.nextContour());
    136     return true;
    137 }
    138 
    139 SkFlattenable::Factory SkDashPathEffect::getFactory() {
    140     return fInitialDashLength < 0 ? NULL : CreateProc;
    141 }
    142 
    143 void SkDashPathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
    144     SkASSERT(fInitialDashLength >= 0);
    145 
    146     buffer.write32(fCount);
    147     buffer.write32(fInitialDashIndex);
    148     buffer.writeScalar(fInitialDashLength);
    149     buffer.writeScalar(fIntervalLength);
    150     buffer.write32(fScaleToFit);
    151     buffer.writeMul4(fIntervals, fCount * sizeof(fIntervals[0]));
    152 }
    153 
    154 SkFlattenable* SkDashPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
    155     return SkNEW_ARGS(SkDashPathEffect, (buffer));
    156 }
    157 
    158 SkDashPathEffect::SkDashPathEffect(SkFlattenableReadBuffer& buffer) {
    159     fCount = buffer.readS32();
    160     fInitialDashIndex = buffer.readS32();
    161     fInitialDashLength = buffer.readScalar();
    162     fIntervalLength = buffer.readScalar();
    163     fScaleToFit = (buffer.readS32() != 0);
    164 
    165     fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * fCount);
    166     buffer.read(fIntervals, fCount * sizeof(fIntervals[0]));
    167 }
    168 
    169 ///////////////////////////////////////////////////////////////////////////////
    170 
    171 SK_DEFINE_FLATTENABLE_REGISTRAR(SkDashPathEffect)
    172