LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/effects - SkDashPathEffect.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 216 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 12 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : #include "SkStrokeRec.h"
      14             : 
      15           0 : SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count, SkScalar phase)
      16             :         : fPhase(0)
      17             :         , fInitialDashLength(-1)
      18             :         , fInitialDashIndex(0)
      19           0 :         , fIntervalLength(0) {
      20           0 :     SkASSERT(intervals);
      21           0 :     SkASSERT(count > 1 && SkIsAlign2(count));
      22             : 
      23           0 :     fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * count);
      24           0 :     fCount = count;
      25           0 :     for (int i = 0; i < count; i++) {
      26           0 :         fIntervals[i] = intervals[i];
      27             :     }
      28             : 
      29             :     // set the internal data members
      30           0 :     SkDashPath::CalcDashParameters(phase, fIntervals, fCount,
      31           0 :             &fInitialDashLength, &fInitialDashIndex, &fIntervalLength, &fPhase);
      32           0 : }
      33             : 
      34           0 : SkDashPathEffect::~SkDashPathEffect() {
      35           0 :     sk_free(fIntervals);
      36           0 : }
      37             : 
      38           0 : bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src,
      39             :                               SkStrokeRec* rec, const SkRect* cullRect) const {
      40           0 :     return SkDashPath::InternalFilter(dst, src, rec, cullRect, fIntervals, fCount,
      41           0 :                                       fInitialDashLength, fInitialDashIndex, fIntervalLength);
      42             : }
      43             : 
      44           0 : static void outset_for_stroke(SkRect* rect, const SkStrokeRec& rec) {
      45           0 :     SkScalar radius = SkScalarHalf(rec.getWidth());
      46           0 :     if (0 == radius) {
      47           0 :         radius = SK_Scalar1;    // hairlines
      48             :     }
      49           0 :     if (SkPaint::kMiter_Join == rec.getJoin()) {
      50           0 :         radius *= rec.getMiter();
      51             :     }
      52           0 :     rect->outset(radius, radius);
      53           0 : }
      54             : 
      55             : // Attempt to trim the line to minimally cover the cull rect (currently
      56             : // only works for horizontal and vertical lines).
      57             : // Return true if processing should continue; false otherwise.
      58           0 : static bool cull_line(SkPoint* pts, const SkStrokeRec& rec,
      59             :                       const SkMatrix& ctm, const SkRect* cullRect,
      60             :                       const SkScalar intervalLength) {
      61           0 :     if (nullptr == cullRect) {
      62           0 :         SkASSERT(false); // Shouldn't ever occur in practice
      63           0 :         return false;
      64             :     }
      65             : 
      66           0 :     SkScalar dx = pts[1].x() - pts[0].x();
      67           0 :     SkScalar dy = pts[1].y() - pts[0].y();
      68             : 
      69           0 :     if ((dx && dy) || (!dx && !dy)) {
      70           0 :         return false;
      71             :     }
      72             : 
      73           0 :     SkRect bounds = *cullRect;
      74           0 :     outset_for_stroke(&bounds, rec);
      75             : 
      76             :     // cullRect is in device space while pts are in the local coordinate system
      77             :     // defined by the ctm. We want our answer in the local coordinate system.
      78             : 
      79           0 :     SkASSERT(ctm.rectStaysRect());
      80             :     SkMatrix inv;
      81           0 :     if (!ctm.invert(&inv)) {
      82           0 :         return false;
      83             :     }
      84             : 
      85           0 :     inv.mapRect(&bounds);
      86             : 
      87           0 :     if (dx) {
      88           0 :         SkASSERT(dx && !dy);
      89           0 :         SkScalar minX = pts[0].fX;
      90           0 :         SkScalar maxX = pts[1].fX;
      91             : 
      92           0 :         if (dx < 0) {
      93           0 :             SkTSwap(minX, maxX);
      94             :         }
      95             : 
      96           0 :         SkASSERT(minX < maxX);
      97           0 :         if (maxX <= bounds.fLeft || minX >= bounds.fRight) {
      98           0 :             return false;
      99             :         }
     100             : 
     101             :         // Now we actually perform the chop, removing the excess to the left and
     102             :         // right of the bounds (keeping our new line "in phase" with the dash,
     103             :         // hence the (mod intervalLength).
     104             : 
     105           0 :         if (minX < bounds.fLeft) {
     106           0 :             minX = bounds.fLeft - SkScalarMod(bounds.fLeft - minX, intervalLength);
     107             :         }
     108           0 :         if (maxX > bounds.fRight) {
     109           0 :             maxX = bounds.fRight + SkScalarMod(maxX - bounds.fRight, intervalLength);
     110             :         }
     111             : 
     112           0 :         SkASSERT(maxX > minX);
     113           0 :         if (dx < 0) {
     114           0 :             SkTSwap(minX, maxX);
     115             :         }
     116           0 :         pts[0].fX = minX;
     117           0 :         pts[1].fX = maxX;
     118             :     } else {
     119           0 :         SkASSERT(dy && !dx);
     120           0 :         SkScalar minY = pts[0].fY;
     121           0 :         SkScalar maxY = pts[1].fY;
     122             : 
     123           0 :         if (dy < 0) {
     124           0 :             SkTSwap(minY, maxY);
     125             :         }
     126             : 
     127           0 :         SkASSERT(minY < maxY);
     128           0 :         if (maxY <= bounds.fTop || minY >= bounds.fBottom) {
     129           0 :             return false;
     130             :         }
     131             : 
     132             :         // Now we actually perform the chop, removing the excess to the top and
     133             :         // bottom of the bounds (keeping our new line "in phase" with the dash,
     134             :         // hence the (mod intervalLength).
     135             : 
     136           0 :         if (minY < bounds.fTop) {
     137           0 :             minY = bounds.fTop - SkScalarMod(bounds.fTop - minY, intervalLength);
     138             :         }
     139           0 :         if (maxY > bounds.fBottom) {
     140           0 :             maxY = bounds.fBottom + SkScalarMod(maxY - bounds.fBottom, intervalLength);
     141             :         }
     142             : 
     143           0 :         SkASSERT(maxY > minY);
     144           0 :         if (dy < 0) {
     145           0 :             SkTSwap(minY, maxY);
     146             :         }
     147           0 :         pts[0].fY = minY;
     148           0 :         pts[1].fY = maxY;
     149             :     }
     150             : 
     151           0 :     return true;
     152             : }
     153             : 
     154             : // Currently asPoints is more restrictive then it needs to be. In the future
     155             : // we need to:
     156             : //      allow kRound_Cap capping (could allow rotations in the matrix with this)
     157             : //      allow paths to be returned
     158           0 : bool SkDashPathEffect::asPoints(PointData* results,
     159             :                                 const SkPath& src,
     160             :                                 const SkStrokeRec& rec,
     161             :                                 const SkMatrix& matrix,
     162             :                                 const SkRect* cullRect) const {
     163             :     // width < 0 -> fill && width == 0 -> hairline so requiring width > 0 rules both out
     164           0 :     if (0 >= rec.getWidth()) {
     165           0 :         return false;
     166             :     }
     167             : 
     168             :     // TODO: this next test could be eased up. We could allow any number of
     169             :     // intervals as long as all the ons match and all the offs match.
     170             :     // Additionally, they do not necessarily need to be integers.
     171             :     // We cannot allow arbitrary intervals since we want the returned points
     172             :     // to be uniformly sized.
     173           0 :     if (fCount != 2 ||
     174           0 :         !SkScalarNearlyEqual(fIntervals[0], fIntervals[1]) ||
     175           0 :         !SkScalarIsInt(fIntervals[0]) ||
     176           0 :         !SkScalarIsInt(fIntervals[1])) {
     177           0 :         return false;
     178             :     }
     179             : 
     180             :     SkPoint pts[2];
     181             : 
     182           0 :     if (!src.isLine(pts)) {
     183           0 :         return false;
     184             :     }
     185             : 
     186             :     // TODO: this test could be eased up to allow circles
     187           0 :     if (SkPaint::kButt_Cap != rec.getCap()) {
     188           0 :         return false;
     189             :     }
     190             : 
     191             :     // TODO: this test could be eased up for circles. Rotations could be allowed.
     192           0 :     if (!matrix.rectStaysRect()) {
     193           0 :         return false;
     194             :     }
     195             : 
     196             :     // See if the line can be limited to something plausible.
     197           0 :     if (!cull_line(pts, rec, matrix, cullRect, fIntervalLength)) {
     198           0 :         return false;
     199             :     }
     200             : 
     201           0 :     SkScalar length = SkPoint::Distance(pts[1], pts[0]);
     202             : 
     203           0 :     SkVector tangent = pts[1] - pts[0];
     204           0 :     if (tangent.isZero()) {
     205           0 :         return false;
     206             :     }
     207             : 
     208           0 :     tangent.scale(SkScalarInvert(length));
     209             : 
     210             :     // TODO: make this test for horizontal & vertical lines more robust
     211           0 :     bool isXAxis = true;
     212           0 :     if (SkScalarNearlyEqual(SK_Scalar1, tangent.fX) ||
     213           0 :         SkScalarNearlyEqual(-SK_Scalar1, tangent.fX)) {
     214           0 :         results->fSize.set(SkScalarHalf(fIntervals[0]), SkScalarHalf(rec.getWidth()));
     215           0 :     } else if (SkScalarNearlyEqual(SK_Scalar1, tangent.fY) ||
     216           0 :                SkScalarNearlyEqual(-SK_Scalar1, tangent.fY)) {
     217           0 :         results->fSize.set(SkScalarHalf(rec.getWidth()), SkScalarHalf(fIntervals[0]));
     218           0 :         isXAxis = false;
     219           0 :     } else if (SkPaint::kRound_Cap != rec.getCap()) {
     220             :         // Angled lines don't have axis-aligned boxes.
     221           0 :         return false;
     222             :     }
     223             : 
     224           0 :     if (results) {
     225           0 :         results->fFlags = 0;
     226           0 :         SkScalar clampedInitialDashLength = SkMinScalar(length, fInitialDashLength);
     227             : 
     228           0 :         if (SkPaint::kRound_Cap == rec.getCap()) {
     229           0 :             results->fFlags |= PointData::kCircles_PointFlag;
     230             :         }
     231             : 
     232           0 :         results->fNumPoints = 0;
     233           0 :         SkScalar len2 = length;
     234           0 :         if (clampedInitialDashLength > 0 || 0 == fInitialDashIndex) {
     235           0 :             SkASSERT(len2 >= clampedInitialDashLength);
     236           0 :             if (0 == fInitialDashIndex) {
     237           0 :                 if (clampedInitialDashLength > 0) {
     238           0 :                     if (clampedInitialDashLength >= fIntervals[0]) {
     239           0 :                         ++results->fNumPoints;  // partial first dash
     240             :                     }
     241           0 :                     len2 -= clampedInitialDashLength;
     242             :                 }
     243           0 :                 len2 -= fIntervals[1];  // also skip first space
     244           0 :                 if (len2 < 0) {
     245           0 :                     len2 = 0;
     246             :                 }
     247             :             } else {
     248           0 :                 len2 -= clampedInitialDashLength; // skip initial partial empty
     249             :             }
     250             :         }
     251             :         // Too many midpoints can cause results->fNumPoints to overflow or
     252             :         // otherwise cause the results->fPoints allocation below to OOM.
     253             :         // Cap it to a sane value.
     254           0 :         SkScalar numIntervals = len2 / fIntervalLength;
     255           0 :         if (!SkScalarIsFinite(numIntervals) || numIntervals > SkDashPath::kMaxDashCount) {
     256           0 :             return false;
     257             :         }
     258           0 :         int numMidPoints = SkScalarFloorToInt(numIntervals);
     259           0 :         results->fNumPoints += numMidPoints;
     260           0 :         len2 -= numMidPoints * fIntervalLength;
     261           0 :         bool partialLast = false;
     262           0 :         if (len2 > 0) {
     263           0 :             if (len2 < fIntervals[0]) {
     264           0 :                 partialLast = true;
     265             :             } else {
     266           0 :                 ++numMidPoints;
     267           0 :                 ++results->fNumPoints;
     268             :             }
     269             :         }
     270             : 
     271           0 :         results->fPoints = new SkPoint[results->fNumPoints];
     272             : 
     273           0 :         SkScalar    distance = 0;
     274           0 :         int         curPt = 0;
     275             : 
     276           0 :         if (clampedInitialDashLength > 0 || 0 == fInitialDashIndex) {
     277           0 :             SkASSERT(clampedInitialDashLength <= length);
     278             : 
     279           0 :             if (0 == fInitialDashIndex) {
     280           0 :                 if (clampedInitialDashLength > 0) {
     281             :                     // partial first block
     282           0 :                     SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles
     283           0 :                     SkScalar x = pts[0].fX + tangent.fX * SkScalarHalf(clampedInitialDashLength);
     284           0 :                     SkScalar y = pts[0].fY + tangent.fY * SkScalarHalf(clampedInitialDashLength);
     285             :                     SkScalar halfWidth, halfHeight;
     286           0 :                     if (isXAxis) {
     287           0 :                         halfWidth = SkScalarHalf(clampedInitialDashLength);
     288           0 :                         halfHeight = SkScalarHalf(rec.getWidth());
     289             :                     } else {
     290           0 :                         halfWidth = SkScalarHalf(rec.getWidth());
     291           0 :                         halfHeight = SkScalarHalf(clampedInitialDashLength);
     292             :                     }
     293           0 :                     if (clampedInitialDashLength < fIntervals[0]) {
     294             :                         // This one will not be like the others
     295           0 :                         results->fFirst.addRect(x - halfWidth, y - halfHeight,
     296           0 :                                                 x + halfWidth, y + halfHeight);
     297             :                     } else {
     298           0 :                         SkASSERT(curPt < results->fNumPoints);
     299           0 :                         results->fPoints[curPt].set(x, y);
     300           0 :                         ++curPt;
     301             :                     }
     302             : 
     303           0 :                     distance += clampedInitialDashLength;
     304             :                 }
     305             : 
     306           0 :                 distance += fIntervals[1];  // skip over the next blank block too
     307             :             } else {
     308           0 :                 distance += clampedInitialDashLength;
     309             :             }
     310             :         }
     311             : 
     312           0 :         if (0 != numMidPoints) {
     313           0 :             distance += SkScalarHalf(fIntervals[0]);
     314             : 
     315           0 :             for (int i = 0; i < numMidPoints; ++i) {
     316           0 :                 SkScalar x = pts[0].fX + tangent.fX * distance;
     317           0 :                 SkScalar y = pts[0].fY + tangent.fY * distance;
     318             : 
     319           0 :                 SkASSERT(curPt < results->fNumPoints);
     320           0 :                 results->fPoints[curPt].set(x, y);
     321           0 :                 ++curPt;
     322             : 
     323           0 :                 distance += fIntervalLength;
     324             :             }
     325             : 
     326           0 :             distance -= SkScalarHalf(fIntervals[0]);
     327             :         }
     328             : 
     329           0 :         if (partialLast) {
     330             :             // partial final block
     331           0 :             SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles
     332           0 :             SkScalar temp = length - distance;
     333           0 :             SkASSERT(temp < fIntervals[0]);
     334           0 :             SkScalar x = pts[0].fX + tangent.fX * (distance + SkScalarHalf(temp));
     335           0 :             SkScalar y = pts[0].fY + tangent.fY * (distance + SkScalarHalf(temp));
     336             :             SkScalar halfWidth, halfHeight;
     337           0 :             if (isXAxis) {
     338           0 :                 halfWidth = SkScalarHalf(temp);
     339           0 :                 halfHeight = SkScalarHalf(rec.getWidth());
     340             :             } else {
     341           0 :                 halfWidth = SkScalarHalf(rec.getWidth());
     342           0 :                 halfHeight = SkScalarHalf(temp);
     343             :             }
     344           0 :             results->fLast.addRect(x - halfWidth, y - halfHeight,
     345           0 :                                    x + halfWidth, y + halfHeight);
     346             :         }
     347             : 
     348           0 :         SkASSERT(curPt == results->fNumPoints);
     349             :     }
     350             : 
     351           0 :     return true;
     352             : }
     353             : 
     354           0 : SkPathEffect::DashType SkDashPathEffect::asADash(DashInfo* info) const {
     355           0 :     if (info) {
     356           0 :         if (info->fCount >= fCount && info->fIntervals) {
     357           0 :             memcpy(info->fIntervals, fIntervals, fCount * sizeof(SkScalar));
     358             :         }
     359           0 :         info->fCount = fCount;
     360           0 :         info->fPhase = fPhase;
     361             :     }
     362           0 :     return kDash_DashType;
     363             : }
     364             : 
     365           0 : void SkDashPathEffect::flatten(SkWriteBuffer& buffer) const {
     366           0 :     buffer.writeScalar(fPhase);
     367           0 :     buffer.writeScalarArray(fIntervals, fCount);
     368           0 : }
     369             : 
     370           0 : sk_sp<SkFlattenable> SkDashPathEffect::CreateProc(SkReadBuffer& buffer) {
     371           0 :     const SkScalar phase = buffer.readScalar();
     372           0 :     uint32_t count = buffer.getArrayCount();
     373           0 :     SkAutoSTArray<32, SkScalar> intervals(count);
     374           0 :     if (buffer.readScalarArray(intervals.get(), count)) {
     375           0 :         return Make(intervals.get(), SkToInt(count), phase);
     376             :     }
     377           0 :     return nullptr;
     378             : }
     379             : 
     380             : #ifndef SK_IGNORE_TO_STRING
     381           0 : void SkDashPathEffect::toString(SkString* str) const {
     382           0 :     str->appendf("SkDashPathEffect: (");
     383           0 :     str->appendf("count: %d phase %.2f intervals: (", fCount, fPhase);
     384           0 :     for (int i = 0; i < fCount; ++i) {
     385           0 :         str->appendf("%.2f", fIntervals[i]);
     386           0 :         if (i < fCount-1) {
     387           0 :             str->appendf(", ");
     388             :         }
     389             :     }
     390           0 :     str->appendf("))");
     391           0 : }
     392             : #endif
     393             : 
     394             : //////////////////////////////////////////////////////////////////////////////////////////////////
     395             : 
     396           0 : sk_sp<SkPathEffect> SkDashPathEffect::Make(const SkScalar intervals[], int count, SkScalar phase) {
     397           0 :     if (!SkDashPath::ValidDashPath(phase, intervals, count)) {
     398           0 :         return nullptr;
     399             :     }
     400           0 :     return sk_sp<SkPathEffect>(new SkDashPathEffect(intervals, count, phase));
     401             : }

Generated by: LCOV version 1.13