SkDashPathEffect.cpp revision 9fa60daad4d5f54c0dbe3dbcc7608a8f6d721187
1/* 2 * Copyright 2006 The Android Open Source Project 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "SkDashPathEffect.h" 9 10#include "SkDashPathPriv.h" 11#include "SkReadBuffer.h" 12#include "SkWriteBuffer.h" 13 14SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count, SkScalar phase) 15 : fPhase(0) 16 , fInitialDashLength(0) 17 , fInitialDashIndex(0) 18 , fIntervalLength(0) { 19 SkASSERT(intervals); 20 SkASSERT(count > 1 && SkAlign2(count) == count); 21 22 fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count); 23 fCount = count; 24 for (int i = 0; i < count; i++) { 25 SkASSERT(intervals[i] >= 0); 26 fIntervals[i] = intervals[i]; 27 } 28 29 // set the internal data members 30 SkDashPath::CalcDashParameters(phase, fIntervals, fCount, 31 &fInitialDashLength, &fInitialDashIndex, &fIntervalLength, &fPhase); 32} 33 34SkDashPathEffect::~SkDashPathEffect() { 35 sk_free(fIntervals); 36} 37 38bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src, 39 SkStrokeRec* rec, const SkRect* cullRect) const { 40 return SkDashPath::FilterDashPath(dst, src, rec, cullRect, fIntervals, fCount, 41 fInitialDashLength, fInitialDashIndex, fIntervalLength); 42} 43 44// Currently asPoints is more restrictive then it needs to be. In the future 45// we need to: 46// allow kRound_Cap capping (could allow rotations in the matrix with this) 47// allow paths to be returned 48bool SkDashPathEffect::asPoints(PointData* results, 49 const SkPath& src, 50 const SkStrokeRec& rec, 51 const SkMatrix& matrix, 52 const SkRect* cullRect) const { 53 // width < 0 -> fill && width == 0 -> hairline so requiring width > 0 rules both out 54 if (fInitialDashLength < 0 || 0 >= rec.getWidth()) { 55 return false; 56 } 57 58 // TODO: this next test could be eased up. We could allow any number of 59 // intervals as long as all the ons match and all the offs match. 60 // Additionally, they do not necessarily need to be integers. 61 // We cannot allow arbitrary intervals since we want the returned points 62 // to be uniformly sized. 63 if (fCount != 2 || 64 !SkScalarNearlyEqual(fIntervals[0], fIntervals[1]) || 65 !SkScalarIsInt(fIntervals[0]) || 66 !SkScalarIsInt(fIntervals[1])) { 67 return false; 68 } 69 70 SkPoint pts[2]; 71 72 if (!src.isLine(pts)) { 73 return false; 74 } 75 76 // TODO: this test could be eased up to allow circles 77 if (SkPaint::kButt_Cap != rec.getCap()) { 78 return false; 79 } 80 81 // TODO: this test could be eased up for circles. Rotations could be allowed. 82 if (!matrix.rectStaysRect()) { 83 return false; 84 } 85 86 SkScalar length = SkPoint::Distance(pts[1], pts[0]); 87 88 SkVector tangent = pts[1] - pts[0]; 89 if (tangent.isZero()) { 90 return false; 91 } 92 93 tangent.scale(SkScalarInvert(length)); 94 95 // TODO: make this test for horizontal & vertical lines more robust 96 bool isXAxis = true; 97 if (SK_Scalar1 == tangent.fX || -SK_Scalar1 == tangent.fX) { 98 results->fSize.set(SkScalarHalf(fIntervals[0]), SkScalarHalf(rec.getWidth())); 99 } else if (SK_Scalar1 == tangent.fY || -SK_Scalar1 == tangent.fY) { 100 results->fSize.set(SkScalarHalf(rec.getWidth()), SkScalarHalf(fIntervals[0])); 101 isXAxis = false; 102 } else if (SkPaint::kRound_Cap != rec.getCap()) { 103 // Angled lines don't have axis-aligned boxes. 104 return false; 105 } 106 107 if (NULL != results) { 108 results->fFlags = 0; 109 SkScalar clampedInitialDashLength = SkMinScalar(length, fInitialDashLength); 110 111 if (SkPaint::kRound_Cap == rec.getCap()) { 112 results->fFlags |= PointData::kCircles_PointFlag; 113 } 114 115 results->fNumPoints = 0; 116 SkScalar len2 = length; 117 if (clampedInitialDashLength > 0 || 0 == fInitialDashIndex) { 118 SkASSERT(len2 >= clampedInitialDashLength); 119 if (0 == fInitialDashIndex) { 120 if (clampedInitialDashLength > 0) { 121 if (clampedInitialDashLength >= fIntervals[0]) { 122 ++results->fNumPoints; // partial first dash 123 } 124 len2 -= clampedInitialDashLength; 125 } 126 len2 -= fIntervals[1]; // also skip first space 127 if (len2 < 0) { 128 len2 = 0; 129 } 130 } else { 131 len2 -= clampedInitialDashLength; // skip initial partial empty 132 } 133 } 134 int numMidPoints = SkScalarFloorToInt(SkScalarDiv(len2, fIntervalLength)); 135 results->fNumPoints += numMidPoints; 136 len2 -= numMidPoints * fIntervalLength; 137 bool partialLast = false; 138 if (len2 > 0) { 139 if (len2 < fIntervals[0]) { 140 partialLast = true; 141 } else { 142 ++numMidPoints; 143 ++results->fNumPoints; 144 } 145 } 146 147 results->fPoints = new SkPoint[results->fNumPoints]; 148 149 SkScalar distance = 0; 150 int curPt = 0; 151 152 if (clampedInitialDashLength > 0 || 0 == fInitialDashIndex) { 153 SkASSERT(clampedInitialDashLength <= length); 154 155 if (0 == fInitialDashIndex) { 156 if (clampedInitialDashLength > 0) { 157 // partial first block 158 SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles 159 SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, SkScalarHalf(clampedInitialDashLength)); 160 SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, SkScalarHalf(clampedInitialDashLength)); 161 SkScalar halfWidth, halfHeight; 162 if (isXAxis) { 163 halfWidth = SkScalarHalf(clampedInitialDashLength); 164 halfHeight = SkScalarHalf(rec.getWidth()); 165 } else { 166 halfWidth = SkScalarHalf(rec.getWidth()); 167 halfHeight = SkScalarHalf(clampedInitialDashLength); 168 } 169 if (clampedInitialDashLength < fIntervals[0]) { 170 // This one will not be like the others 171 results->fFirst.addRect(x - halfWidth, y - halfHeight, 172 x + halfWidth, y + halfHeight); 173 } else { 174 SkASSERT(curPt < results->fNumPoints); 175 results->fPoints[curPt].set(x, y); 176 ++curPt; 177 } 178 179 distance += clampedInitialDashLength; 180 } 181 182 distance += fIntervals[1]; // skip over the next blank block too 183 } else { 184 distance += clampedInitialDashLength; 185 } 186 } 187 188 if (0 != numMidPoints) { 189 distance += SkScalarHalf(fIntervals[0]); 190 191 for (int i = 0; i < numMidPoints; ++i) { 192 SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, distance); 193 SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, distance); 194 195 SkASSERT(curPt < results->fNumPoints); 196 results->fPoints[curPt].set(x, y); 197 ++curPt; 198 199 distance += fIntervalLength; 200 } 201 202 distance -= SkScalarHalf(fIntervals[0]); 203 } 204 205 if (partialLast) { 206 // partial final block 207 SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles 208 SkScalar temp = length - distance; 209 SkASSERT(temp < fIntervals[0]); 210 SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, distance + SkScalarHalf(temp)); 211 SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, distance + SkScalarHalf(temp)); 212 SkScalar halfWidth, halfHeight; 213 if (isXAxis) { 214 halfWidth = SkScalarHalf(temp); 215 halfHeight = SkScalarHalf(rec.getWidth()); 216 } else { 217 halfWidth = SkScalarHalf(rec.getWidth()); 218 halfHeight = SkScalarHalf(temp); 219 } 220 results->fLast.addRect(x - halfWidth, y - halfHeight, 221 x + halfWidth, y + halfHeight); 222 } 223 224 SkASSERT(curPt == results->fNumPoints); 225 } 226 227 return true; 228} 229 230SkPathEffect::DashType SkDashPathEffect::asADash(DashInfo* info) const { 231 if (info) { 232 if (info->fCount >= fCount && NULL != info->fIntervals) { 233 memcpy(info->fIntervals, fIntervals, fCount * sizeof(SkScalar)); 234 } 235 info->fCount = fCount; 236 info->fPhase = fPhase; 237 } 238 return kDash_DashType; 239} 240 241void SkDashPathEffect::flatten(SkWriteBuffer& buffer) const { 242 buffer.writeScalar(fPhase); 243 buffer.writeScalarArray(fIntervals, fCount); 244} 245 246SkFlattenable* SkDashPathEffect::CreateProc(SkReadBuffer& buffer) { 247 const SkScalar phase = buffer.readScalar(); 248 uint32_t count = buffer.getArrayCount(); 249 SkAutoSTArray<32, SkScalar> intervals(count); 250 if (buffer.readScalarArray(intervals.get(), count)) { 251 return Create(intervals.get(), SkToInt(count), phase); 252 } 253 return NULL; 254} 255 256#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING 257SkDashPathEffect::SkDashPathEffect(SkReadBuffer& buffer) 258 : INHERITED(buffer) 259 , fPhase(0) 260 , fInitialDashLength(0) 261 , fInitialDashIndex(0) 262 , fIntervalLength(0) { 263 bool useOldPic = buffer.isVersionLT(SkReadBuffer::kDashWritesPhaseIntervals_Version); 264 if (useOldPic) { 265 fInitialDashIndex = buffer.readInt(); 266 fInitialDashLength = buffer.readScalar(); 267 fIntervalLength = buffer.readScalar(); 268 buffer.readBool(); // Dummy for old ScalarToFit field 269 } else { 270 fPhase = buffer.readScalar(); 271 } 272 273 fCount = buffer.getArrayCount(); 274 size_t allocSize = sizeof(SkScalar) * fCount; 275 if (buffer.validateAvailable(allocSize)) { 276 fIntervals = (SkScalar*)sk_malloc_throw(allocSize); 277 buffer.readScalarArray(fIntervals, fCount); 278 } else { 279 fIntervals = NULL; 280 } 281 282 if (useOldPic) { 283 fPhase = 0; 284 if (fInitialDashLength != -1) { // Signal for bad dash interval 285 for (int i = 0; i < fInitialDashIndex; ++i) { 286 fPhase += fIntervals[i]; 287 } 288 fPhase += fIntervals[fInitialDashIndex] - fInitialDashLength; 289 } 290 } else { 291 // set the internal data members, fPhase should have been between 0 and intervalLength 292 // when written to buffer so no need to adjust it 293 SkDashPath::CalcDashParameters(fPhase, fIntervals, fCount, 294 &fInitialDashLength, &fInitialDashIndex, &fIntervalLength); 295 } 296} 297#endif 298 299