blob: 0cc97b6b1507993bceeee00e724bb251d7f406f1 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
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
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkDashPathEffect.h"
11#include "SkBuffer.h"
12#include "SkPathMeasure.h"
13
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000014static inline int is_even(int x) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000015 return (~x) << 31;
16}
17
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000018static SkScalar FindFirstInterval(const SkScalar intervals[], SkScalar phase,
19 int32_t* index) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000020 int i;
21
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000022 for (i = 0; phase > intervals[i]; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000023 phase -= intervals[i];
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000024 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000025 *index = i;
26 return intervals[i] - phase;
27}
28
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000029SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count,
30 SkScalar phase, bool scaleToFit)
31 : fScaleToFit(scaleToFit) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000032 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;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000039 for (int i = 0; i < count; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000040 SkASSERT(intervals[i] >= 0);
41 fIntervals[i] = intervals[i];
42 len += intervals[i];
43 }
44 fIntervalLength = len;
45
epoger@google.com20bf4ca2012-04-27 13:34:52 +000046 // watch out for values that might make us go out of bounds
47 if ((len > 0) && SkScalarIsFinite(phase) && SkScalarIsFinite(len)) {
48
49 // Adjust phase to be between 0 and len, "flipping" phase if negative.
50 // e.g., if len is 100, then phase of -20 (or -120) is equivalent to 80
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000051 if (phase < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000052 phase = -phase;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000053 if (phase > len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000054 phase = SkScalarMod(phase, len);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000055 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000056 phase = len - phase;
epoger@google.com20bf4ca2012-04-27 13:34:52 +000057
58 // Due to finite precision, it's possible that phase == len,
59 // even after the subtract (if len >>> phase), so fix that here.
60 // This fixes http://crbug.com/124652 .
reed@google.com1df888b2012-04-24 22:47:21 +000061 SkASSERT(phase <= len);
62 if (phase == len) {
63 phase = 0;
64 }
epoger@google.com20bf4ca2012-04-27 13:34:52 +000065 } else if (phase >= len) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000066 phase = SkScalarMod(phase, len);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000067 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000068 SkASSERT(phase >= 0 && phase < len);
epoger@google.com20bf4ca2012-04-27 13:34:52 +000069
reed@android.com8a1c16f2008-12-17 15:59:43 +000070 fInitialDashLength = FindFirstInterval(intervals, phase, &fInitialDashIndex);
71
72 SkASSERT(fInitialDashLength >= 0);
73 SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000074 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +000075 fInitialDashLength = -1; // signal bad dash intervals
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000076 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000077}
78
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000079SkDashPathEffect::~SkDashPathEffect() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000080 sk_free(fIntervals);
81}
82
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000083bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src,
84 SkScalar* width) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000085 // we do nothing if the src wants to be filled, or if our dashlength is 0
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000086 if (*width < 0 || fInitialDashLength < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000087 return false;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000088 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000089
90 SkPathMeasure meas(src, false);
91 const SkScalar* intervals = fIntervals;
92
93 do {
94 bool skipFirstSegment = meas.isClosed();
95 bool addedSegment = false;
96 SkScalar length = meas.getLength();
97 int index = fInitialDashIndex;
98 SkScalar scale = SK_Scalar1;
99
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000100 if (fScaleToFit) {
101 if (fIntervalLength >= length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102 scale = SkScalarDiv(length, fIntervalLength);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000103 } else {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000104 SkScalar div = SkScalarDiv(length, fIntervalLength);
105 int n = SkScalarFloor(div);
106 scale = SkScalarDiv(length, n * fIntervalLength);
107 }
108 }
109
110 SkScalar distance = 0;
111 SkScalar dlen = SkScalarMul(fInitialDashLength, scale);
112
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000113 while (distance < length) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114 SkASSERT(dlen >= 0);
115 addedSegment = false;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000116 if (is_even(index) && dlen > 0 && !skipFirstSegment) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 addedSegment = true;
118 meas.getSegment(distance, distance + dlen, dst, true);
119 }
120 distance += dlen;
121
122 // clear this so we only respect it the first time around
123 skipFirstSegment = false;
124
125 // wrap around our intervals array if necessary
126 index += 1;
127 SkASSERT(index <= fCount);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000128 if (index == fCount) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129 index = 0;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000130 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131
132 // fetch our next dlen
133 dlen = SkScalarMul(intervals[index], scale);
134 }
135
136 // extend if we ended on a segment and we need to join up with the (skipped) initial segment
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000137 if (meas.isClosed() && is_even(fInitialDashIndex) &&
138 fInitialDashLength > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000139 meas.getSegment(0, SkScalarMul(fInitialDashLength, scale), dst, !addedSegment);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000140 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141 } while (meas.nextContour());
142 return true;
143}
144
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000145SkFlattenable::Factory SkDashPathEffect::getFactory() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000146 return fInitialDashLength < 0 ? NULL : CreateProc;
147}
148
djsollen@google.com54924242012-03-29 15:18:04 +0000149void SkDashPathEffect::flatten(SkFlattenableWriteBuffer& buffer) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150 SkASSERT(fInitialDashLength >= 0);
151
djsollen@google.com54924242012-03-29 15:18:04 +0000152 this->INHERITED::flatten(buffer);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000153 buffer.write32(fCount);
154 buffer.write32(fInitialDashIndex);
155 buffer.writeScalar(fInitialDashLength);
156 buffer.writeScalar(fIntervalLength);
157 buffer.write32(fScaleToFit);
158 buffer.writeMul4(fIntervals, fCount * sizeof(fIntervals[0]));
159}
160
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000161SkFlattenable* SkDashPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162 return SkNEW_ARGS(SkDashPathEffect, (buffer));
163}
164
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000165SkDashPathEffect::SkDashPathEffect(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166 fCount = buffer.readS32();
167 fInitialDashIndex = buffer.readS32();
168 fInitialDashLength = buffer.readScalar();
169 fIntervalLength = buffer.readScalar();
170 fScaleToFit = (buffer.readS32() != 0);
171
172 fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * fCount);
173 buffer.read(fIntervals, fCount * sizeof(fIntervals[0]));
174}
175
reed@google.com6bac9472011-06-21 19:24:00 +0000176///////////////////////////////////////////////////////////////////////////////
177
caryclark@google.comd26147a2011-12-15 14:16:43 +0000178SK_DEFINE_FLATTENABLE_REGISTRAR(SkDashPathEffect)