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