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 "Sk1DPathEffect.h" 11 #include "SkFlattenableBuffers.h" 12 #include "SkPathMeasure.h" 13 14 bool Sk1DPathEffect::filterPath(SkPath* dst, const SkPath& src, 15 SkStrokeRec*, const SkRect*) const { 16 SkPathMeasure meas(src, false); 17 do { 18 SkScalar length = meas.getLength(); 19 SkScalar distance = this->begin(length); 20 while (distance < length) { 21 SkScalar delta = this->next(dst, distance, meas); 22 if (delta <= 0) { 23 break; 24 } 25 distance += delta; 26 } 27 } while (meas.nextContour()); 28 return true; 29 } 30 31 /////////////////////////////////////////////////////////////////////////////// 32 33 SkPath1DPathEffect::SkPath1DPathEffect(const SkPath& path, SkScalar advance, 34 SkScalar phase, Style style) : fPath(path) 35 { 36 if (advance <= 0 || path.isEmpty()) { 37 SkDEBUGF(("SkPath1DPathEffect can't use advance <= 0\n")); 38 fAdvance = 0; // signals we can't draw anything 39 fInitialOffset = 0; 40 fStyle = kStyleCount; 41 } else { 42 // cleanup their phase parameter, inverting it so that it becomes an 43 // offset along the path (to match the interpretation in PostScript) 44 if (phase < 0) { 45 phase = -phase; 46 if (phase > advance) { 47 phase = SkScalarMod(phase, advance); 48 } 49 } else { 50 if (phase > advance) { 51 phase = SkScalarMod(phase, advance); 52 } 53 phase = advance - phase; 54 } 55 // now catch the edge case where phase == advance (within epsilon) 56 if (phase >= advance) { 57 phase = 0; 58 } 59 SkASSERT(phase >= 0); 60 61 fAdvance = advance; 62 fInitialOffset = phase; 63 64 if ((unsigned)style >= kStyleCount) { 65 SkDEBUGF(("SkPath1DPathEffect style enum out of range %d\n", style)); 66 } 67 fStyle = style; 68 } 69 } 70 71 bool SkPath1DPathEffect::filterPath(SkPath* dst, const SkPath& src, 72 SkStrokeRec* rec, const SkRect* cullRect) const { 73 if (fAdvance > 0) { 74 rec->setFillStyle(); 75 return this->INHERITED::filterPath(dst, src, rec, cullRect); 76 } 77 return false; 78 } 79 80 static bool morphpoints(SkPoint dst[], const SkPoint src[], int count, 81 SkPathMeasure& meas, SkScalar dist) { 82 for (int i = 0; i < count; i++) { 83 SkPoint pos; 84 SkVector tangent; 85 86 SkScalar sx = src[i].fX; 87 SkScalar sy = src[i].fY; 88 89 if (!meas.getPosTan(dist + sx, &pos, &tangent)) { 90 return false; 91 } 92 93 SkMatrix matrix; 94 SkPoint pt; 95 96 pt.set(sx, sy); 97 matrix.setSinCos(tangent.fY, tangent.fX, 0, 0); 98 matrix.preTranslate(-sx, 0); 99 matrix.postTranslate(pos.fX, pos.fY); 100 matrix.mapPoints(&dst[i], &pt, 1); 101 } 102 return true; 103 } 104 105 /* TODO 106 107 Need differentially more subdivisions when the follow-path is curvy. Not sure how to 108 determine that, but we need it. I guess a cheap answer is let the caller tell us, 109 but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out. 110 */ 111 static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas, 112 SkScalar dist) { 113 SkPath::Iter iter(src, false); 114 SkPoint srcP[4], dstP[3]; 115 SkPath::Verb verb; 116 117 while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) { 118 switch (verb) { 119 case SkPath::kMove_Verb: 120 if (morphpoints(dstP, srcP, 1, meas, dist)) { 121 dst->moveTo(dstP[0]); 122 } 123 break; 124 case SkPath::kLine_Verb: 125 srcP[2] = srcP[1]; 126 srcP[1].set(SkScalarAve(srcP[0].fX, srcP[2].fX), 127 SkScalarAve(srcP[0].fY, srcP[2].fY)); 128 // fall through to quad 129 case SkPath::kQuad_Verb: 130 if (morphpoints(dstP, &srcP[1], 2, meas, dist)) { 131 dst->quadTo(dstP[0], dstP[1]); 132 } 133 break; 134 case SkPath::kCubic_Verb: 135 if (morphpoints(dstP, &srcP[1], 3, meas, dist)) { 136 dst->cubicTo(dstP[0], dstP[1], dstP[2]); 137 } 138 break; 139 case SkPath::kClose_Verb: 140 dst->close(); 141 break; 142 default: 143 SkDEBUGFAIL("unknown verb"); 144 break; 145 } 146 } 147 } 148 149 SkPath1DPathEffect::SkPath1DPathEffect(SkFlattenableReadBuffer& buffer) { 150 fAdvance = buffer.readScalar(); 151 if (fAdvance > 0) { 152 buffer.readPath(&fPath); 153 fInitialOffset = buffer.readScalar(); 154 fStyle = (Style) buffer.readUInt(); 155 } else { 156 SkDEBUGF(("SkPath1DPathEffect can't use advance <= 0\n")); 157 // Make Coverity happy. 158 fInitialOffset = 0; 159 fStyle = kStyleCount; 160 } 161 } 162 163 SkScalar SkPath1DPathEffect::begin(SkScalar contourLength) const { 164 return fInitialOffset; 165 } 166 167 void SkPath1DPathEffect::flatten(SkFlattenableWriteBuffer& buffer) const { 168 this->INHERITED::flatten(buffer); 169 buffer.writeScalar(fAdvance); 170 if (fAdvance > 0) { 171 buffer.writePath(fPath); 172 buffer.writeScalar(fInitialOffset); 173 buffer.writeUInt(fStyle); 174 } 175 } 176 177 SkScalar SkPath1DPathEffect::next(SkPath* dst, SkScalar distance, 178 SkPathMeasure& meas) const { 179 switch (fStyle) { 180 case kTranslate_Style: { 181 SkPoint pos; 182 if (meas.getPosTan(distance, &pos, NULL)) { 183 dst->addPath(fPath, pos.fX, pos.fY); 184 } 185 } break; 186 case kRotate_Style: { 187 SkMatrix matrix; 188 if (meas.getMatrix(distance, &matrix)) { 189 dst->addPath(fPath, matrix); 190 } 191 } break; 192 case kMorph_Style: 193 morphpath(dst, fPath, meas, distance); 194 break; 195 default: 196 SkDEBUGFAIL("unknown Style enum"); 197 break; 198 } 199 return fAdvance; 200 } 201