michael@0: michael@0: /* michael@0: * Copyright 2006 The Android Open Source Project michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: michael@0: #include "Sk1DPathEffect.h" michael@0: #include "SkReadBuffer.h" michael@0: #include "SkWriteBuffer.h" michael@0: #include "SkPathMeasure.h" michael@0: michael@0: bool Sk1DPathEffect::filterPath(SkPath* dst, const SkPath& src, michael@0: SkStrokeRec*, const SkRect*) const { michael@0: SkPathMeasure meas(src, false); michael@0: do { michael@0: SkScalar length = meas.getLength(); michael@0: SkScalar distance = this->begin(length); michael@0: while (distance < length) { michael@0: SkScalar delta = this->next(dst, distance, meas); michael@0: if (delta <= 0) { michael@0: break; michael@0: } michael@0: distance += delta; michael@0: } michael@0: } while (meas.nextContour()); michael@0: return true; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkPath1DPathEffect::SkPath1DPathEffect(const SkPath& path, SkScalar advance, michael@0: SkScalar phase, Style style) : fPath(path) michael@0: { michael@0: if (advance <= 0 || path.isEmpty()) { michael@0: SkDEBUGF(("SkPath1DPathEffect can't use advance <= 0\n")); michael@0: fAdvance = 0; // signals we can't draw anything michael@0: fInitialOffset = 0; michael@0: fStyle = kStyleCount; michael@0: } else { michael@0: // cleanup their phase parameter, inverting it so that it becomes an michael@0: // offset along the path (to match the interpretation in PostScript) michael@0: if (phase < 0) { michael@0: phase = -phase; michael@0: if (phase > advance) { michael@0: phase = SkScalarMod(phase, advance); michael@0: } michael@0: } else { michael@0: if (phase > advance) { michael@0: phase = SkScalarMod(phase, advance); michael@0: } michael@0: phase = advance - phase; michael@0: } michael@0: // now catch the edge case where phase == advance (within epsilon) michael@0: if (phase >= advance) { michael@0: phase = 0; michael@0: } michael@0: SkASSERT(phase >= 0); michael@0: michael@0: fAdvance = advance; michael@0: fInitialOffset = phase; michael@0: michael@0: if ((unsigned)style >= kStyleCount) { michael@0: SkDEBUGF(("SkPath1DPathEffect style enum out of range %d\n", style)); michael@0: } michael@0: fStyle = style; michael@0: } michael@0: } michael@0: michael@0: bool SkPath1DPathEffect::filterPath(SkPath* dst, const SkPath& src, michael@0: SkStrokeRec* rec, const SkRect* cullRect) const { michael@0: if (fAdvance > 0) { michael@0: rec->setFillStyle(); michael@0: return this->INHERITED::filterPath(dst, src, rec, cullRect); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: static bool morphpoints(SkPoint dst[], const SkPoint src[], int count, michael@0: SkPathMeasure& meas, SkScalar dist) { michael@0: for (int i = 0; i < count; i++) { michael@0: SkPoint pos; michael@0: SkVector tangent; michael@0: michael@0: SkScalar sx = src[i].fX; michael@0: SkScalar sy = src[i].fY; michael@0: michael@0: if (!meas.getPosTan(dist + sx, &pos, &tangent)) { michael@0: return false; michael@0: } michael@0: michael@0: SkMatrix matrix; michael@0: SkPoint pt; michael@0: michael@0: pt.set(sx, sy); michael@0: matrix.setSinCos(tangent.fY, tangent.fX, 0, 0); michael@0: matrix.preTranslate(-sx, 0); michael@0: matrix.postTranslate(pos.fX, pos.fY); michael@0: matrix.mapPoints(&dst[i], &pt, 1); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: /* TODO michael@0: michael@0: Need differentially more subdivisions when the follow-path is curvy. Not sure how to michael@0: determine that, but we need it. I guess a cheap answer is let the caller tell us, michael@0: but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out. michael@0: */ michael@0: static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas, michael@0: SkScalar dist) { michael@0: SkPath::Iter iter(src, false); michael@0: SkPoint srcP[4], dstP[3]; michael@0: SkPath::Verb verb; michael@0: michael@0: while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) { michael@0: switch (verb) { michael@0: case SkPath::kMove_Verb: michael@0: if (morphpoints(dstP, srcP, 1, meas, dist)) { michael@0: dst->moveTo(dstP[0]); michael@0: } michael@0: break; michael@0: case SkPath::kLine_Verb: michael@0: srcP[2] = srcP[1]; michael@0: srcP[1].set(SkScalarAve(srcP[0].fX, srcP[2].fX), michael@0: SkScalarAve(srcP[0].fY, srcP[2].fY)); michael@0: // fall through to quad michael@0: case SkPath::kQuad_Verb: michael@0: if (morphpoints(dstP, &srcP[1], 2, meas, dist)) { michael@0: dst->quadTo(dstP[0], dstP[1]); michael@0: } michael@0: break; michael@0: case SkPath::kCubic_Verb: michael@0: if (morphpoints(dstP, &srcP[1], 3, meas, dist)) { michael@0: dst->cubicTo(dstP[0], dstP[1], dstP[2]); michael@0: } michael@0: break; michael@0: case SkPath::kClose_Verb: michael@0: dst->close(); michael@0: break; michael@0: default: michael@0: SkDEBUGFAIL("unknown verb"); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: SkPath1DPathEffect::SkPath1DPathEffect(SkReadBuffer& buffer) { michael@0: fAdvance = buffer.readScalar(); michael@0: if (fAdvance > 0) { michael@0: buffer.readPath(&fPath); michael@0: fInitialOffset = buffer.readScalar(); michael@0: fStyle = (Style) buffer.readUInt(); michael@0: } else { michael@0: SkDEBUGF(("SkPath1DPathEffect can't use advance <= 0\n")); michael@0: // Make Coverity happy. michael@0: fInitialOffset = 0; michael@0: fStyle = kStyleCount; michael@0: } michael@0: } michael@0: michael@0: SkScalar SkPath1DPathEffect::begin(SkScalar contourLength) const { michael@0: return fInitialOffset; michael@0: } michael@0: michael@0: void SkPath1DPathEffect::flatten(SkWriteBuffer& buffer) const { michael@0: this->INHERITED::flatten(buffer); michael@0: buffer.writeScalar(fAdvance); michael@0: if (fAdvance > 0) { michael@0: buffer.writePath(fPath); michael@0: buffer.writeScalar(fInitialOffset); michael@0: buffer.writeUInt(fStyle); michael@0: } michael@0: } michael@0: michael@0: SkScalar SkPath1DPathEffect::next(SkPath* dst, SkScalar distance, michael@0: SkPathMeasure& meas) const { michael@0: switch (fStyle) { michael@0: case kTranslate_Style: { michael@0: SkPoint pos; michael@0: if (meas.getPosTan(distance, &pos, NULL)) { michael@0: dst->addPath(fPath, pos.fX, pos.fY); michael@0: } michael@0: } break; michael@0: case kRotate_Style: { michael@0: SkMatrix matrix; michael@0: if (meas.getMatrix(distance, &matrix)) { michael@0: dst->addPath(fPath, matrix); michael@0: } michael@0: } break; michael@0: case kMorph_Style: michael@0: morphpath(dst, fPath, meas, distance); michael@0: break; michael@0: default: michael@0: SkDEBUGFAIL("unknown Style enum"); michael@0: break; michael@0: } michael@0: return fAdvance; michael@0: }