1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2006 The Android Open Source Project 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci 9cb93a386Sopenharmony_ci#include "include/core/SkPathMeasure.h" 10cb93a386Sopenharmony_ci#include "include/core/SkStrokeRec.h" 11cb93a386Sopenharmony_ci#include "include/effects/Sk1DPathEffect.h" 12cb93a386Sopenharmony_ci#include "src/core/SkPathEffectBase.h" 13cb93a386Sopenharmony_ci#include "src/core/SkReadBuffer.h" 14cb93a386Sopenharmony_ci#include "src/core/SkWriteBuffer.h" 15cb93a386Sopenharmony_ci 16cb93a386Sopenharmony_ci// Since we are stepping by a float, the do/while loop might go on forever (or nearly so). 17cb93a386Sopenharmony_ci// Put in a governor to limit crash values from looping too long (and allocating too much ram). 18cb93a386Sopenharmony_ci#define MAX_REASONABLE_ITERATIONS 100000 19cb93a386Sopenharmony_ci 20cb93a386Sopenharmony_ciclass Sk1DPathEffect : public SkPathEffectBase { 21cb93a386Sopenharmony_cipublic: 22cb93a386Sopenharmony_ciprotected: 23cb93a386Sopenharmony_ci bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*, 24cb93a386Sopenharmony_ci const SkMatrix&) const override { 25cb93a386Sopenharmony_ci SkPathMeasure meas(src, false); 26cb93a386Sopenharmony_ci do { 27cb93a386Sopenharmony_ci int governor = MAX_REASONABLE_ITERATIONS; 28cb93a386Sopenharmony_ci SkScalar length = meas.getLength(); 29cb93a386Sopenharmony_ci SkScalar distance = this->begin(length); 30cb93a386Sopenharmony_ci while (distance < length && --governor >= 0) { 31cb93a386Sopenharmony_ci SkScalar delta = this->next(dst, distance, meas); 32cb93a386Sopenharmony_ci if (delta <= 0) { 33cb93a386Sopenharmony_ci break; 34cb93a386Sopenharmony_ci } 35cb93a386Sopenharmony_ci distance += delta; 36cb93a386Sopenharmony_ci } 37cb93a386Sopenharmony_ci } while (meas.nextContour()); 38cb93a386Sopenharmony_ci return true; 39cb93a386Sopenharmony_ci } 40cb93a386Sopenharmony_ci 41cb93a386Sopenharmony_ci /** Called at the start of each contour, returns the initial offset 42cb93a386Sopenharmony_ci into that contour. 43cb93a386Sopenharmony_ci */ 44cb93a386Sopenharmony_ci virtual SkScalar begin(SkScalar contourLength) const = 0; 45cb93a386Sopenharmony_ci /** Called with the current distance along the path, with the current matrix 46cb93a386Sopenharmony_ci for the point/tangent at the specified distance. 47cb93a386Sopenharmony_ci Return the distance to travel for the next call. If return <= 0, then that 48cb93a386Sopenharmony_ci contour is done. 49cb93a386Sopenharmony_ci */ 50cb93a386Sopenharmony_ci virtual SkScalar next(SkPath* dst, SkScalar dist, SkPathMeasure&) const = 0; 51cb93a386Sopenharmony_ci 52cb93a386Sopenharmony_ciprivate: 53cb93a386Sopenharmony_ci // For simplicity, assume fast bounds cannot be computed 54cb93a386Sopenharmony_ci bool computeFastBounds(SkRect*) const override { return false; } 55cb93a386Sopenharmony_ci 56cb93a386Sopenharmony_ci using INHERITED = SkPathEffect; 57cb93a386Sopenharmony_ci}; 58cb93a386Sopenharmony_ci 59cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 60cb93a386Sopenharmony_ci 61cb93a386Sopenharmony_ciclass SkPath1DPathEffectImpl : public Sk1DPathEffect { 62cb93a386Sopenharmony_cipublic: 63cb93a386Sopenharmony_ci SkPath1DPathEffectImpl(const SkPath& path, SkScalar advance, SkScalar phase, 64cb93a386Sopenharmony_ci SkPath1DPathEffect::Style style) : fPath(path) { 65cb93a386Sopenharmony_ci SkASSERT(advance > 0 && !path.isEmpty()); 66cb93a386Sopenharmony_ci 67cb93a386Sopenharmony_ci // Make the path thread-safe. 68cb93a386Sopenharmony_ci fPath.updateBoundsCache(); 69cb93a386Sopenharmony_ci (void)fPath.getGenerationID(); 70cb93a386Sopenharmony_ci 71cb93a386Sopenharmony_ci // cleanup their phase parameter, inverting it so that it becomes an 72cb93a386Sopenharmony_ci // offset along the path (to match the interpretation in PostScript) 73cb93a386Sopenharmony_ci if (phase < 0) { 74cb93a386Sopenharmony_ci phase = -phase; 75cb93a386Sopenharmony_ci if (phase > advance) { 76cb93a386Sopenharmony_ci phase = SkScalarMod(phase, advance); 77cb93a386Sopenharmony_ci } 78cb93a386Sopenharmony_ci } else { 79cb93a386Sopenharmony_ci if (phase > advance) { 80cb93a386Sopenharmony_ci phase = SkScalarMod(phase, advance); 81cb93a386Sopenharmony_ci } 82cb93a386Sopenharmony_ci phase = advance - phase; 83cb93a386Sopenharmony_ci } 84cb93a386Sopenharmony_ci // now catch the edge case where phase == advance (within epsilon) 85cb93a386Sopenharmony_ci if (phase >= advance) { 86cb93a386Sopenharmony_ci phase = 0; 87cb93a386Sopenharmony_ci } 88cb93a386Sopenharmony_ci SkASSERT(phase >= 0); 89cb93a386Sopenharmony_ci 90cb93a386Sopenharmony_ci fAdvance = advance; 91cb93a386Sopenharmony_ci fInitialOffset = phase; 92cb93a386Sopenharmony_ci fStyle = style; 93cb93a386Sopenharmony_ci } 94cb93a386Sopenharmony_ci 95cb93a386Sopenharmony_ci bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec, 96cb93a386Sopenharmony_ci const SkRect* cullRect, const SkMatrix& ctm) const override { 97cb93a386Sopenharmony_ci rec->setFillStyle(); 98cb93a386Sopenharmony_ci return this->INHERITED::onFilterPath(dst, src, rec, cullRect, ctm); 99cb93a386Sopenharmony_ci } 100cb93a386Sopenharmony_ci 101cb93a386Sopenharmony_ci SkScalar begin(SkScalar contourLength) const override { 102cb93a386Sopenharmony_ci return fInitialOffset; 103cb93a386Sopenharmony_ci } 104cb93a386Sopenharmony_ci 105cb93a386Sopenharmony_ci SkScalar next(SkPath*, SkScalar, SkPathMeasure&) const override; 106cb93a386Sopenharmony_ci 107cb93a386Sopenharmony_ci static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) { 108cb93a386Sopenharmony_ci SkScalar advance = buffer.readScalar(); 109cb93a386Sopenharmony_ci SkPath path; 110cb93a386Sopenharmony_ci buffer.readPath(&path); 111cb93a386Sopenharmony_ci SkScalar phase = buffer.readScalar(); 112cb93a386Sopenharmony_ci SkPath1DPathEffect::Style style = buffer.read32LE(SkPath1DPathEffect::kLastEnum_Style); 113cb93a386Sopenharmony_ci return buffer.isValid() ? SkPath1DPathEffect::Make(path, advance, phase, style) : nullptr; 114cb93a386Sopenharmony_ci } 115cb93a386Sopenharmony_ci 116cb93a386Sopenharmony_ci void flatten(SkWriteBuffer& buffer) const override { 117cb93a386Sopenharmony_ci buffer.writeScalar(fAdvance); 118cb93a386Sopenharmony_ci buffer.writePath(fPath); 119cb93a386Sopenharmony_ci buffer.writeScalar(fInitialOffset); 120cb93a386Sopenharmony_ci buffer.writeUInt(fStyle); 121cb93a386Sopenharmony_ci } 122cb93a386Sopenharmony_ci 123cb93a386Sopenharmony_ci Factory getFactory() const override { return CreateProc; } 124cb93a386Sopenharmony_ci const char* getTypeName() const override { return "SkPath1DPathEffectImpl"; } 125cb93a386Sopenharmony_ci 126cb93a386Sopenharmony_ciprivate: 127cb93a386Sopenharmony_ci SkPath fPath; // copied from constructor 128cb93a386Sopenharmony_ci SkScalar fAdvance; // copied from constructor 129cb93a386Sopenharmony_ci SkScalar fInitialOffset; // computed from phase 130cb93a386Sopenharmony_ci SkPath1DPathEffect::Style fStyle; // copied from constructor 131cb93a386Sopenharmony_ci 132cb93a386Sopenharmony_ci using INHERITED = Sk1DPathEffect; 133cb93a386Sopenharmony_ci}; 134cb93a386Sopenharmony_ci 135cb93a386Sopenharmony_cistatic bool morphpoints(SkPoint dst[], const SkPoint src[], int count, 136cb93a386Sopenharmony_ci SkPathMeasure& meas, SkScalar dist) { 137cb93a386Sopenharmony_ci for (int i = 0; i < count; i++) { 138cb93a386Sopenharmony_ci SkPoint pos; 139cb93a386Sopenharmony_ci SkVector tangent; 140cb93a386Sopenharmony_ci 141cb93a386Sopenharmony_ci SkScalar sx = src[i].fX; 142cb93a386Sopenharmony_ci SkScalar sy = src[i].fY; 143cb93a386Sopenharmony_ci 144cb93a386Sopenharmony_ci if (!meas.getPosTan(dist + sx, &pos, &tangent)) { 145cb93a386Sopenharmony_ci return false; 146cb93a386Sopenharmony_ci } 147cb93a386Sopenharmony_ci 148cb93a386Sopenharmony_ci SkMatrix matrix; 149cb93a386Sopenharmony_ci SkPoint pt; 150cb93a386Sopenharmony_ci 151cb93a386Sopenharmony_ci pt.set(sx, sy); 152cb93a386Sopenharmony_ci matrix.setSinCos(tangent.fY, tangent.fX, 0, 0); 153cb93a386Sopenharmony_ci matrix.preTranslate(-sx, 0); 154cb93a386Sopenharmony_ci matrix.postTranslate(pos.fX, pos.fY); 155cb93a386Sopenharmony_ci matrix.mapPoints(&dst[i], &pt, 1); 156cb93a386Sopenharmony_ci } 157cb93a386Sopenharmony_ci return true; 158cb93a386Sopenharmony_ci} 159cb93a386Sopenharmony_ci 160cb93a386Sopenharmony_ci/* TODO 161cb93a386Sopenharmony_ci 162cb93a386Sopenharmony_ciNeed differentially more subdivisions when the follow-path is curvy. Not sure how to 163cb93a386Sopenharmony_cidetermine that, but we need it. I guess a cheap answer is let the caller tell us, 164cb93a386Sopenharmony_cibut that seems like a cop-out. Another answer is to get Rob Johnson to figure it out. 165cb93a386Sopenharmony_ci*/ 166cb93a386Sopenharmony_cistatic void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas, 167cb93a386Sopenharmony_ci SkScalar dist) { 168cb93a386Sopenharmony_ci SkPath::Iter iter(src, false); 169cb93a386Sopenharmony_ci SkPoint srcP[4], dstP[3]; 170cb93a386Sopenharmony_ci SkPath::Verb verb; 171cb93a386Sopenharmony_ci 172cb93a386Sopenharmony_ci while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) { 173cb93a386Sopenharmony_ci switch (verb) { 174cb93a386Sopenharmony_ci case SkPath::kMove_Verb: 175cb93a386Sopenharmony_ci if (morphpoints(dstP, srcP, 1, meas, dist)) { 176cb93a386Sopenharmony_ci dst->moveTo(dstP[0]); 177cb93a386Sopenharmony_ci } 178cb93a386Sopenharmony_ci break; 179cb93a386Sopenharmony_ci case SkPath::kLine_Verb: 180cb93a386Sopenharmony_ci srcP[2] = srcP[1]; 181cb93a386Sopenharmony_ci srcP[1].set(SkScalarAve(srcP[0].fX, srcP[2].fX), 182cb93a386Sopenharmony_ci SkScalarAve(srcP[0].fY, srcP[2].fY)); 183cb93a386Sopenharmony_ci [[fallthrough]]; 184cb93a386Sopenharmony_ci case SkPath::kQuad_Verb: 185cb93a386Sopenharmony_ci if (morphpoints(dstP, &srcP[1], 2, meas, dist)) { 186cb93a386Sopenharmony_ci dst->quadTo(dstP[0], dstP[1]); 187cb93a386Sopenharmony_ci } 188cb93a386Sopenharmony_ci break; 189cb93a386Sopenharmony_ci case SkPath::kConic_Verb: 190cb93a386Sopenharmony_ci if (morphpoints(dstP, &srcP[1], 2, meas, dist)) { 191cb93a386Sopenharmony_ci dst->conicTo(dstP[0], dstP[1], iter.conicWeight()); 192cb93a386Sopenharmony_ci } 193cb93a386Sopenharmony_ci break; 194cb93a386Sopenharmony_ci case SkPath::kCubic_Verb: 195cb93a386Sopenharmony_ci if (morphpoints(dstP, &srcP[1], 3, meas, dist)) { 196cb93a386Sopenharmony_ci dst->cubicTo(dstP[0], dstP[1], dstP[2]); 197cb93a386Sopenharmony_ci } 198cb93a386Sopenharmony_ci break; 199cb93a386Sopenharmony_ci case SkPath::kClose_Verb: 200cb93a386Sopenharmony_ci dst->close(); 201cb93a386Sopenharmony_ci break; 202cb93a386Sopenharmony_ci default: 203cb93a386Sopenharmony_ci SkDEBUGFAIL("unknown verb"); 204cb93a386Sopenharmony_ci break; 205cb93a386Sopenharmony_ci } 206cb93a386Sopenharmony_ci } 207cb93a386Sopenharmony_ci} 208cb93a386Sopenharmony_ci 209cb93a386Sopenharmony_ciSkScalar SkPath1DPathEffectImpl::next(SkPath* dst, SkScalar distance, 210cb93a386Sopenharmony_ci SkPathMeasure& meas) const { 211cb93a386Sopenharmony_ci#if defined(SK_BUILD_FOR_FUZZER) 212cb93a386Sopenharmony_ci if (dst->countPoints() > 100000) { 213cb93a386Sopenharmony_ci return fAdvance; 214cb93a386Sopenharmony_ci } 215cb93a386Sopenharmony_ci#endif 216cb93a386Sopenharmony_ci switch (fStyle) { 217cb93a386Sopenharmony_ci case SkPath1DPathEffect::kTranslate_Style: { 218cb93a386Sopenharmony_ci SkPoint pos; 219cb93a386Sopenharmony_ci if (meas.getPosTan(distance, &pos, nullptr)) { 220cb93a386Sopenharmony_ci dst->addPath(fPath, pos.fX, pos.fY); 221cb93a386Sopenharmony_ci } 222cb93a386Sopenharmony_ci } break; 223cb93a386Sopenharmony_ci case SkPath1DPathEffect::kRotate_Style: { 224cb93a386Sopenharmony_ci SkMatrix matrix; 225cb93a386Sopenharmony_ci if (meas.getMatrix(distance, &matrix)) { 226cb93a386Sopenharmony_ci dst->addPath(fPath, matrix); 227cb93a386Sopenharmony_ci } 228cb93a386Sopenharmony_ci } break; 229cb93a386Sopenharmony_ci case SkPath1DPathEffect::kMorph_Style: 230cb93a386Sopenharmony_ci morphpath(dst, fPath, meas, distance); 231cb93a386Sopenharmony_ci break; 232cb93a386Sopenharmony_ci } 233cb93a386Sopenharmony_ci return fAdvance; 234cb93a386Sopenharmony_ci} 235cb93a386Sopenharmony_ci 236cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////////////////////////// 237cb93a386Sopenharmony_ci 238cb93a386Sopenharmony_cisk_sp<SkPathEffect> SkPath1DPathEffect::Make(const SkPath& path, SkScalar advance, SkScalar phase, 239cb93a386Sopenharmony_ci Style style) { 240cb93a386Sopenharmony_ci if (advance <= 0 || !SkScalarIsFinite(advance) || !SkScalarIsFinite(phase) || path.isEmpty()) { 241cb93a386Sopenharmony_ci return nullptr; 242cb93a386Sopenharmony_ci } 243cb93a386Sopenharmony_ci return sk_sp<SkPathEffect>(new SkPath1DPathEffectImpl(path, advance, phase, style)); 244cb93a386Sopenharmony_ci} 245cb93a386Sopenharmony_ci 246cb93a386Sopenharmony_civoid SkPath1DPathEffect::RegisterFlattenables() { 247cb93a386Sopenharmony_ci SK_REGISTER_FLATTENABLE(SkPath1DPathEffectImpl); 248cb93a386Sopenharmony_ci} 249