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