LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/core - SkScan_Hairline.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 389 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 35 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 "SkScan.h"
       9             : #include "SkBlitter.h"
      10             : #include "SkMathPriv.h"
      11             : #include "SkRasterClip.h"
      12             : #include "SkFDot6.h"
      13             : #include "SkLineClipper.h"
      14             : 
      15           0 : static void horiline(int x, int stopx, SkFixed fy, SkFixed dy,
      16             :                      SkBlitter* blitter) {
      17           0 :     SkASSERT(x < stopx);
      18             : 
      19           0 :     do {
      20           0 :         blitter->blitH(x, fy >> 16, 1);
      21           0 :         fy += dy;
      22             :     } while (++x < stopx);
      23           0 : }
      24             : 
      25           0 : static void vertline(int y, int stopy, SkFixed fx, SkFixed dx,
      26             :                      SkBlitter* blitter) {
      27           0 :     SkASSERT(y < stopy);
      28             : 
      29           0 :     do {
      30           0 :         blitter->blitH(fx >> 16, y, 1);
      31           0 :         fx += dx;
      32             :     } while (++y < stopy);
      33           0 : }
      34             : 
      35             : #ifdef SK_DEBUG
      36           0 : static bool canConvertFDot6ToFixed(SkFDot6 x) {
      37           0 :     const int maxDot6 = SK_MaxS32 >> (16 - 6);
      38           0 :     return SkAbs32(x) <= maxDot6;
      39             : }
      40             : #endif
      41             : 
      42           0 : void SkScan::HairLineRgn(const SkPoint array[], int arrayCount, const SkRegion* clip,
      43             :                          SkBlitter* origBlitter) {
      44           0 :     SkBlitterClipper    clipper;
      45             :     SkIRect clipR, ptsR;
      46             : 
      47           0 :     const SkScalar max = SkIntToScalar(32767);
      48           0 :     const SkRect fixedBounds = SkRect::MakeLTRB(-max, -max, max, max);
      49             : 
      50             :     SkRect clipBounds;
      51           0 :     if (clip) {
      52           0 :         clipBounds.set(clip->getBounds());
      53             :     }
      54             : 
      55           0 :     for (int i = 0; i < arrayCount - 1; ++i) {
      56           0 :         SkBlitter* blitter = origBlitter;
      57             : 
      58             :         SkPoint pts[2];
      59             : 
      60             :         // We have to pre-clip the line to fit in a SkFixed, so we just chop
      61             :         // the line. TODO find a way to actually draw beyond that range.
      62           0 :         if (!SkLineClipper::IntersectLine(&array[i], fixedBounds, pts)) {
      63           0 :             continue;
      64             :         }
      65             : 
      66             :         // Perform a clip in scalar space, so we catch huge values which might
      67             :         // be missed after we convert to SkFDot6 (overflow)
      68           0 :         if (clip && !SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
      69           0 :             continue;
      70             :         }
      71             : 
      72           0 :         SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
      73           0 :         SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
      74           0 :         SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
      75           0 :         SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
      76             : 
      77           0 :         SkASSERT(canConvertFDot6ToFixed(x0));
      78           0 :         SkASSERT(canConvertFDot6ToFixed(y0));
      79           0 :         SkASSERT(canConvertFDot6ToFixed(x1));
      80           0 :         SkASSERT(canConvertFDot6ToFixed(y1));
      81             : 
      82           0 :         if (clip) {
      83             :             // now perform clipping again, as the rounding to dot6 can wiggle us
      84             :             // our rects are really dot6 rects, but since we've already used
      85             :             // lineclipper, we know they will fit in 32bits (26.6)
      86           0 :             const SkIRect& bounds = clip->getBounds();
      87             : 
      88           0 :             clipR.set(SkIntToFDot6(bounds.fLeft), SkIntToFDot6(bounds.fTop),
      89           0 :                       SkIntToFDot6(bounds.fRight), SkIntToFDot6(bounds.fBottom));
      90           0 :             ptsR.set(x0, y0, x1, y1);
      91           0 :             ptsR.sort();
      92             : 
      93             :             // outset the right and bottom, to account for how hairlines are
      94             :             // actually drawn, which may hit the pixel to the right or below of
      95             :             // the coordinate
      96           0 :             ptsR.fRight += SK_FDot6One;
      97           0 :             ptsR.fBottom += SK_FDot6One;
      98             : 
      99           0 :             if (!SkIRect::Intersects(ptsR, clipR)) {
     100           0 :                 continue;
     101             :             }
     102           0 :             if (!clip->isRect() || !clipR.contains(ptsR)) {
     103           0 :                 blitter = clipper.apply(origBlitter, clip);
     104             :             }
     105             :         }
     106             : 
     107           0 :         SkFDot6 dx = x1 - x0;
     108           0 :         SkFDot6 dy = y1 - y0;
     109             : 
     110           0 :         if (SkAbs32(dx) > SkAbs32(dy)) { // mostly horizontal
     111           0 :             if (x0 > x1) {   // we want to go left-to-right
     112           0 :                 SkTSwap<SkFDot6>(x0, x1);
     113           0 :                 SkTSwap<SkFDot6>(y0, y1);
     114             :             }
     115           0 :             int ix0 = SkFDot6Round(x0);
     116           0 :             int ix1 = SkFDot6Round(x1);
     117           0 :             if (ix0 == ix1) {// too short to draw
     118           0 :                 continue;
     119             :             }
     120             : 
     121           0 :             SkFixed slope = SkFixedDiv(dy, dx);
     122           0 :             SkFixed startY = SkFDot6ToFixed(y0) + (slope * ((32 - x0) & 63) >> 6);
     123             : 
     124           0 :             horiline(ix0, ix1, startY, slope, blitter);
     125             :         } else {              // mostly vertical
     126           0 :             if (y0 > y1) {   // we want to go top-to-bottom
     127           0 :                 SkTSwap<SkFDot6>(x0, x1);
     128           0 :                 SkTSwap<SkFDot6>(y0, y1);
     129             :             }
     130           0 :             int iy0 = SkFDot6Round(y0);
     131           0 :             int iy1 = SkFDot6Round(y1);
     132           0 :             if (iy0 == iy1) { // too short to draw
     133           0 :                 continue;
     134             :             }
     135             : 
     136           0 :             SkFixed slope = SkFixedDiv(dx, dy);
     137           0 :             SkFixed startX = SkFDot6ToFixed(x0) + (slope * ((32 - y0) & 63) >> 6);
     138             : 
     139           0 :             vertline(iy0, iy1, startX, slope, blitter);
     140             :         }
     141             :     }
     142           0 : }
     143             : 
     144             : // we don't just draw 4 lines, 'cause that can leave a gap in the bottom-right
     145             : // and double-hit the top-left.
     146             : // TODO: handle huge coordinates on rect (before calling SkScalarToFixed)
     147           0 : void SkScan::HairRect(const SkRect& rect, const SkRasterClip& clip,
     148             :                       SkBlitter* blitter) {
     149           0 :     SkAAClipBlitterWrapper wrapper;
     150           0 :     SkBlitterClipper    clipper;
     151             :     SkIRect             r;
     152             : 
     153           0 :     r.set(SkScalarToFixed(rect.fLeft) >> 16,
     154           0 :           SkScalarToFixed(rect.fTop) >> 16,
     155           0 :           (SkScalarToFixed(rect.fRight) >> 16) + 1,
     156           0 :           (SkScalarToFixed(rect.fBottom) >> 16) + 1);
     157             : 
     158           0 :     if (clip.quickReject(r)) {
     159           0 :         return;
     160             :     }
     161           0 :     if (!clip.quickContains(r)) {
     162             :         const SkRegion* clipRgn;
     163           0 :         if (clip.isBW()) {
     164           0 :             clipRgn = &clip.bwRgn();
     165             :         } else {
     166           0 :             wrapper.init(clip, blitter);
     167           0 :             clipRgn = &wrapper.getRgn();
     168           0 :             blitter = wrapper.getBlitter();
     169             :         }
     170           0 :         blitter = clipper.apply(blitter, clipRgn);
     171             :     }
     172             : 
     173           0 :     int width = r.width();
     174           0 :     int height = r.height();
     175             : 
     176           0 :     if ((width | height) == 0) {
     177           0 :         return;
     178             :     }
     179           0 :     if (width <= 2 || height <= 2) {
     180           0 :         blitter->blitRect(r.fLeft, r.fTop, width, height);
     181           0 :         return;
     182             :     }
     183             :     // if we get here, we know we have 4 segments to draw
     184           0 :     blitter->blitH(r.fLeft, r.fTop, width);                     // top
     185           0 :     blitter->blitRect(r.fLeft, r.fTop + 1, 1, height - 2);      // left
     186           0 :     blitter->blitRect(r.fRight - 1, r.fTop + 1, 1, height - 2); // right
     187           0 :     blitter->blitH(r.fLeft, r.fBottom - 1, width);              // bottom
     188             : }
     189             : 
     190             : ///////////////////////////////////////////////////////////////////////////////
     191             : 
     192             : #include "SkPath.h"
     193             : #include "SkGeometry.h"
     194             : #include "SkNx.h"
     195             : 
     196             : #define kMaxCubicSubdivideLevel 9
     197             : #define kMaxQuadSubdivideLevel  5
     198             : 
     199           0 : static int compute_int_quad_dist(const SkPoint pts[3]) {
     200             :     // compute the vector between the control point ([1]) and the middle of the
     201             :     // line connecting the start and end ([0] and [2])
     202           0 :     SkScalar dx = SkScalarHalf(pts[0].fX + pts[2].fX) - pts[1].fX;
     203           0 :     SkScalar dy = SkScalarHalf(pts[0].fY + pts[2].fY) - pts[1].fY;
     204             :     // we want everyone to be positive
     205           0 :     dx = SkScalarAbs(dx);
     206           0 :     dy = SkScalarAbs(dy);
     207             :     // convert to whole pixel values (use ceiling to be conservative)
     208           0 :     int idx = SkScalarCeilToInt(dx);
     209           0 :     int idy = SkScalarCeilToInt(dy);
     210             :     // use the cheap approx for distance
     211           0 :     if (idx > idy) {
     212           0 :         return idx + (idy >> 1);
     213             :     } else {
     214           0 :         return idy + (idx >> 1);
     215             :     }
     216             : }
     217             : 
     218           0 : static void hair_quad(const SkPoint pts[3], const SkRegion* clip,
     219             :                      SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
     220           0 :     SkASSERT(level <= kMaxQuadSubdivideLevel);
     221             : 
     222           0 :     SkQuadCoeff coeff(pts);
     223             : 
     224           0 :     const int lines = 1 << level;
     225             :     Sk2s t(0);
     226           0 :     Sk2s dt(SK_Scalar1 / lines);
     227             : 
     228             :     SkPoint tmp[(1 << kMaxQuadSubdivideLevel) + 1];
     229           0 :     SkASSERT((unsigned)lines < SK_ARRAY_COUNT(tmp));
     230             : 
     231           0 :     tmp[0] = pts[0];
     232           0 :     Sk2s A = coeff.fA;
     233           0 :     Sk2s B = coeff.fB;
     234           0 :     Sk2s C = coeff.fC;
     235           0 :     for (int i = 1; i < lines; ++i) {
     236           0 :         t = t + dt;
     237           0 :         ((A * t + B) * t + C).store(&tmp[i]);
     238             :     }
     239           0 :     tmp[lines] = pts[2];
     240           0 :     lineproc(tmp, lines + 1, clip, blitter);
     241           0 : }
     242             : 
     243           0 : static SkRect compute_nocheck_quad_bounds(const SkPoint pts[3]) {
     244           0 :     SkASSERT(SkScalarsAreFinite(&pts[0].fX, 6));
     245             : 
     246           0 :     Sk2s min = Sk2s::Load(pts);
     247           0 :     Sk2s max = min;
     248           0 :     for (int i = 1; i < 3; ++i) {
     249           0 :         Sk2s pair = Sk2s::Load(pts+i);
     250           0 :         min = Sk2s::Min(min, pair);
     251           0 :         max = Sk2s::Max(max, pair);
     252             :     }
     253           0 :     return { min[0], min[1], max[0], max[1] };
     254             : }
     255             : 
     256           0 : static bool is_inverted(const SkRect& r) {
     257           0 :     return r.fLeft > r.fRight || r.fTop > r.fBottom;
     258             : }
     259             : 
     260             : // Can't call SkRect::intersects, since it cares about empty, and we don't (since we tracking
     261             : // something to be stroked, so empty can still draw something (e.g. horizontal line)
     262           0 : static bool geometric_overlap(const SkRect& a, const SkRect& b) {
     263           0 :     SkASSERT(!is_inverted(a) && !is_inverted(b));
     264           0 :     return a.fLeft < b.fRight && b.fLeft < a.fRight &&
     265           0 :             a.fTop < b.fBottom && b.fTop < a.fBottom;
     266             : }
     267             : 
     268             : // Can't call SkRect::contains, since it cares about empty, and we don't (since we tracking
     269             : // something to be stroked, so empty can still draw something (e.g. horizontal line)
     270           0 : static bool geometric_contains(const SkRect& outer, const SkRect& inner) {
     271           0 :     SkASSERT(!is_inverted(outer) && !is_inverted(inner));
     272           0 :     return inner.fRight <= outer.fRight && inner.fLeft >= outer.fLeft &&
     273           0 :             inner.fBottom <= outer.fBottom && inner.fTop >= outer.fTop;
     274             : }
     275             : 
     276           0 : static inline void hairquad(const SkPoint pts[3], const SkRegion* clip, const SkRect* insetClip, const SkRect* outsetClip,
     277             :     SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
     278           0 :     if (insetClip) {
     279           0 :         SkASSERT(outsetClip);
     280           0 :         SkRect bounds = compute_nocheck_quad_bounds(pts);
     281           0 :         if (!geometric_overlap(*outsetClip, bounds)) {
     282           0 :             return;
     283           0 :         } else if (geometric_contains(*insetClip, bounds)) {
     284           0 :             clip = nullptr;
     285             :         }
     286             :     }
     287             : 
     288           0 :     hair_quad(pts, clip, blitter, level, lineproc);
     289             : }
     290             : 
     291           0 : static inline Sk2s abs(const Sk2s& value) {
     292           0 :     return Sk2s::Max(value, Sk2s(0)-value);
     293             : }
     294             : 
     295           0 : static inline SkScalar max_component(const Sk2s& value) {
     296             :     SkScalar components[2];
     297             :     value.store(components);
     298           0 :     return SkTMax(components[0], components[1]);
     299             : }
     300             : 
     301           0 : static inline int compute_cubic_segs(const SkPoint pts[4]) {
     302           0 :     Sk2s p0 = from_point(pts[0]);
     303           0 :     Sk2s p1 = from_point(pts[1]);
     304           0 :     Sk2s p2 = from_point(pts[2]);
     305           0 :     Sk2s p3 = from_point(pts[3]);
     306             : 
     307             :     const Sk2s oneThird(1.0f / 3.0f);
     308             :     const Sk2s twoThird(2.0f / 3.0f);
     309             : 
     310           0 :     Sk2s p13 = oneThird * p3 + twoThird * p0;
     311           0 :     Sk2s p23 = oneThird * p0 + twoThird * p3;
     312             : 
     313           0 :     SkScalar diff = max_component(Sk2s::Max(abs(p1 - p13), abs(p2 - p23)));
     314           0 :     SkScalar tol = SK_Scalar1 / 8;
     315             : 
     316           0 :     for (int i = 0; i < kMaxCubicSubdivideLevel; ++i) {
     317           0 :         if (diff < tol) {
     318           0 :             return 1 << i;
     319             :         }
     320           0 :         tol *= 4;
     321             :     }
     322           0 :     return 1 << kMaxCubicSubdivideLevel;
     323             : }
     324             : 
     325           0 : static bool lt_90(SkPoint p0, SkPoint pivot, SkPoint p2) {
     326           0 :     return SkVector::DotProduct(p0 - pivot, p2 - pivot) >= 0;
     327             : }
     328             : 
     329             : // The off-curve points are "inside" the limits of the on-curve pts
     330           0 : static bool quick_cubic_niceness_check(const SkPoint pts[4]) {
     331           0 :     return lt_90(pts[1], pts[0], pts[3]) &&
     332           0 :            lt_90(pts[2], pts[0], pts[3]) &&
     333           0 :            lt_90(pts[1], pts[3], pts[0]) &&
     334           0 :            lt_90(pts[2], pts[3], pts[0]);
     335             : }
     336             : 
     337           0 : static void hair_cubic(const SkPoint pts[4], const SkRegion* clip, SkBlitter* blitter,
     338             :                        SkScan::HairRgnProc lineproc) {
     339           0 :     const int lines = compute_cubic_segs(pts);
     340           0 :     SkASSERT(lines > 0);
     341           0 :     if (1 == lines) {
     342           0 :         SkPoint tmp[2] = { pts[0], pts[3] };
     343           0 :         lineproc(tmp, 2, clip, blitter);
     344           0 :         return;
     345             :     }
     346             : 
     347           0 :     SkCubicCoeff coeff(pts);
     348             : 
     349           0 :     const Sk2s dt(SK_Scalar1 / lines);
     350             :     Sk2s t(0);
     351             : 
     352             :     SkPoint tmp[(1 << kMaxCubicSubdivideLevel) + 1];
     353           0 :     SkASSERT((unsigned)lines < SK_ARRAY_COUNT(tmp));
     354             : 
     355           0 :     tmp[0] = pts[0];
     356           0 :     Sk2s A = coeff.fA;
     357           0 :     Sk2s B = coeff.fB;
     358           0 :     Sk2s C = coeff.fC;
     359           0 :     Sk2s D = coeff.fD;
     360           0 :     for (int i = 1; i < lines; ++i) {
     361           0 :         t = t + dt;
     362           0 :         (((A * t + B) * t + C) * t + D).store(&tmp[i]);
     363             :     }
     364           0 :     tmp[lines] = pts[3];
     365           0 :     lineproc(tmp, lines + 1, clip, blitter);
     366             : }
     367             : 
     368           0 : static SkRect compute_nocheck_cubic_bounds(const SkPoint pts[4]) {
     369           0 :     SkASSERT(SkScalarsAreFinite(&pts[0].fX, 8));
     370             : 
     371           0 :     Sk2s min = Sk2s::Load(pts);
     372           0 :     Sk2s max = min;
     373           0 :     for (int i = 1; i < 4; ++i) {
     374           0 :         Sk2s pair = Sk2s::Load(pts+i);
     375           0 :         min = Sk2s::Min(min, pair);
     376           0 :         max = Sk2s::Max(max, pair);
     377             :     }
     378           0 :     return { min[0], min[1], max[0], max[1] };
     379             : }
     380             : 
     381           0 : static inline void haircubic(const SkPoint pts[4], const SkRegion* clip, const SkRect* insetClip, const SkRect* outsetClip,
     382             :                       SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
     383           0 :     if (insetClip) {
     384           0 :         SkASSERT(outsetClip);
     385           0 :         SkRect bounds = compute_nocheck_cubic_bounds(pts);
     386           0 :         if (!geometric_overlap(*outsetClip, bounds)) {
     387           0 :             return;
     388           0 :         } else if (geometric_contains(*insetClip, bounds)) {
     389           0 :             clip = nullptr;
     390             :         }
     391             :     }
     392             : 
     393           0 :     if (quick_cubic_niceness_check(pts)) {
     394           0 :         hair_cubic(pts, clip, blitter, lineproc);
     395             :     } else {
     396             :         SkPoint  tmp[13];
     397             :         SkScalar tValues[3];
     398             : 
     399           0 :         int count = SkChopCubicAtMaxCurvature(pts, tmp, tValues);
     400           0 :         for (int i = 0; i < count; i++) {
     401           0 :             hair_cubic(&tmp[i * 3], clip, blitter, lineproc);
     402             :         }
     403             :     }
     404             : }
     405             : 
     406           0 : static int compute_quad_level(const SkPoint pts[3]) {
     407           0 :     int d = compute_int_quad_dist(pts);
     408             :     /*  quadratics approach the line connecting their start and end points
     409             :      4x closer with each subdivision, so we compute the number of
     410             :      subdivisions to be the minimum need to get that distance to be less
     411             :      than a pixel.
     412             :      */
     413           0 :     int level = (33 - SkCLZ(d)) >> 1;
     414             :     // sanity check on level (from the previous version)
     415           0 :     if (level > kMaxQuadSubdivideLevel) {
     416           0 :         level = kMaxQuadSubdivideLevel;
     417             :     }
     418           0 :     return level;
     419             : }
     420             : 
     421             : /* Extend the points in the direction of the starting or ending tangent by 1/2 unit to
     422             :    account for a round or square cap. If there's no distance between the end point and
     423             :    the control point, use the next control point to create a tangent. If the curve
     424             :    is degenerate, move the cap out 1/2 unit horizontally. */
     425             : template <SkPaint::Cap capStyle>
     426           0 : void extend_pts(SkPath::Verb prevVerb, SkPath::Verb nextVerb, SkPoint* pts, int ptCount) {
     427             :     SkASSERT(SkPaint::kSquare_Cap == capStyle || SkPaint::kRound_Cap == capStyle);
     428             :     // The area of a circle is PI*R*R. For a unit circle, R=1/2, and the cap covers half of that.
     429           0 :     const SkScalar capOutset = SkPaint::kSquare_Cap == capStyle ? 0.5f : SK_ScalarPI / 8;
     430           0 :     if (SkPath::kMove_Verb == prevVerb) {
     431           0 :         SkPoint* first = pts;
     432           0 :         SkPoint* ctrl = first;
     433           0 :         int controls = ptCount - 1;
     434             :         SkVector tangent;
     435           0 :         do {
     436           0 :             tangent = *first - *++ctrl;
     437           0 :         } while (tangent.isZero() && --controls > 0);
     438           0 :         if (tangent.isZero()) {
     439           0 :             tangent.set(1, 0);
     440           0 :             controls = ptCount - 1;  // If all points are equal, move all but one
     441             :         } else {
     442           0 :             tangent.normalize();
     443             :         }
     444           0 :         do {    // If the end point and control points are equal, loop to move them in tandem.
     445           0 :             first->fX += tangent.fX * capOutset;
     446           0 :             first->fY += tangent.fY * capOutset;
     447           0 :             ++first;
     448             :         } while (++controls < ptCount);
     449             :     }
     450           0 :     if (SkPath::kMove_Verb == nextVerb || SkPath::kDone_Verb == nextVerb) {
     451           0 :         SkPoint* last = &pts[ptCount - 1];
     452           0 :         SkPoint* ctrl = last;
     453           0 :         int controls = ptCount - 1;
     454             :         SkVector tangent;
     455           0 :         do {
     456           0 :             tangent = *last - *--ctrl;
     457           0 :         } while (tangent.isZero() && --controls > 0);
     458           0 :         if (tangent.isZero()) {
     459           0 :             tangent.set(-1, 0);
     460           0 :             controls = ptCount - 1;
     461             :         } else {
     462           0 :             tangent.normalize();
     463             :         }
     464           0 :         do {
     465           0 :             last->fX += tangent.fX * capOutset;
     466           0 :             last->fY += tangent.fY * capOutset;
     467           0 :             --last;
     468             :         } while (++controls < ptCount);
     469             :     }
     470           0 : }
     471             : 
     472             : template <SkPaint::Cap capStyle>
     473           0 : void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter,
     474             :                       SkScan::HairRgnProc lineproc) {
     475           0 :     if (path.isEmpty()) {
     476           0 :         return;
     477             :     }
     478             : 
     479           0 :     SkAAClipBlitterWrapper wrap;
     480           0 :     const SkRegion* clip = nullptr;
     481             :     SkRect insetStorage, outsetStorage;
     482           0 :     const SkRect* insetClip = nullptr;
     483           0 :     const SkRect* outsetClip = nullptr;
     484             : 
     485             :     {
     486           0 :         const int capOut = SkPaint::kButt_Cap == capStyle ? 1 : 2;
     487           0 :         const SkIRect ibounds = path.getBounds().roundOut().makeOutset(capOut, capOut);
     488           0 :         if (rclip.quickReject(ibounds)) {
     489           0 :             return;
     490             :         }
     491           0 :         if (!rclip.quickContains(ibounds)) {
     492           0 :             if (rclip.isBW()) {
     493           0 :                 clip = &rclip.bwRgn();
     494             :             } else {
     495           0 :                 wrap.init(rclip, blitter);
     496           0 :                 blitter = wrap.getBlitter();
     497           0 :                 clip = &wrap.getRgn();
     498             :             }
     499             : 
     500             :             /*
     501             :              *  We now cache two scalar rects, to use for culling per-segment (e.g. cubic).
     502             :              *  Since we're hairlining, the "bounds" of the control points isn't necessairly the
     503             :              *  limit of where a segment can draw (it might draw up to 1 pixel beyond in aa-hairs).
     504             :              *
     505             :              *  Compute the pt-bounds per segment is easy, so we do that, and then inversely adjust
     506             :              *  the culling bounds so we can just do a straight compare per segment.
     507             :              *
     508             :              *  insetClip is use for quick-accept (i.e. the segment is not clipped), so we inset
     509             :              *  it from the clip-bounds (since segment bounds can be off by 1).
     510             :              *
     511             :              *  outsetClip is used for quick-reject (i.e. the segment is entirely outside), so we
     512             :              *  outset it from the clip-bounds.
     513             :              */
     514           0 :             insetStorage.set(clip->getBounds());
     515           0 :             outsetStorage = insetStorage.makeOutset(1, 1);
     516           0 :             insetStorage.inset(1, 1);
     517           0 :             if (is_inverted(insetStorage)) {
     518             :                 /*
     519             :                  *  our bounds checks assume the rects are never inverted. If insetting has
     520             :                  *  created that, we assume that the area is too small to safely perform a
     521             :                  *  quick-accept, so we just mark the rect as empty (so the quick-accept check
     522             :                  *  will always fail.
     523             :                  */
     524           0 :                 insetStorage.setEmpty();    // just so we don't pass an inverted rect
     525             :             }
     526           0 :             if (rclip.isRect()) {
     527           0 :                 insetClip = &insetStorage;
     528             :             }
     529           0 :             outsetClip = &outsetStorage;
     530             :         }
     531             :     }
     532             : 
     533           0 :     SkPath::RawIter     iter(path);
     534             :     SkPoint             pts[4], firstPt, lastPt;
     535             :     SkPath::Verb        verb, prevVerb;
     536           0 :     SkAutoConicToQuads  converter;
     537             : 
     538             :     if (SkPaint::kButt_Cap != capStyle) {
     539           0 :         prevVerb = SkPath::kDone_Verb;
     540             :     }
     541           0 :     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
     542           0 :         switch (verb) {
     543             :             case SkPath::kMove_Verb:
     544           0 :                 firstPt = lastPt = pts[0];
     545           0 :                 break;
     546             :             case SkPath::kLine_Verb:
     547             :                 if (SkPaint::kButt_Cap != capStyle) {
     548           0 :                     extend_pts<capStyle>(prevVerb, iter.peek(), pts, 2);
     549             :                 }
     550           0 :                 lineproc(pts, 2, clip, blitter);
     551           0 :                 lastPt = pts[1];
     552           0 :                 break;
     553             :             case SkPath::kQuad_Verb:
     554             :                 if (SkPaint::kButt_Cap != capStyle) {
     555           0 :                     extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3);
     556             :                 }
     557           0 :                 hairquad(pts, clip, insetClip, outsetClip, blitter, compute_quad_level(pts), lineproc);
     558           0 :                 lastPt = pts[2];
     559           0 :                 break;
     560             :             case SkPath::kConic_Verb: {
     561             :                 if (SkPaint::kButt_Cap != capStyle) {
     562           0 :                     extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3);
     563             :                 }
     564             :                 // how close should the quads be to the original conic?
     565           0 :                 const SkScalar tol = SK_Scalar1 / 4;
     566           0 :                 const SkPoint* quadPts = converter.computeQuads(pts,
     567           0 :                                                        iter.conicWeight(), tol);
     568           0 :                 for (int i = 0; i < converter.countQuads(); ++i) {
     569           0 :                     int level = compute_quad_level(quadPts);
     570           0 :                     hairquad(quadPts, clip, insetClip, outsetClip, blitter, level, lineproc);
     571           0 :                     quadPts += 2;
     572             :                 }
     573           0 :                 lastPt = pts[2];
     574           0 :                 break;
     575             :             }
     576             :             case SkPath::kCubic_Verb: {
     577             :                 if (SkPaint::kButt_Cap != capStyle) {
     578           0 :                     extend_pts<capStyle>(prevVerb, iter.peek(), pts, 4);
     579             :                 }
     580           0 :                 haircubic(pts, clip, insetClip, outsetClip, blitter, kMaxCubicSubdivideLevel, lineproc);
     581           0 :                 lastPt = pts[3];
     582           0 :             } break;
     583             :             case SkPath::kClose_Verb:
     584           0 :                 pts[0] = lastPt;
     585           0 :                 pts[1] = firstPt;
     586           0 :                 if (SkPaint::kButt_Cap != capStyle && prevVerb == SkPath::kMove_Verb) {
     587             :                     // cap moveTo/close to match svg expectations for degenerate segments
     588           0 :                     extend_pts<capStyle>(prevVerb, iter.peek(), pts, 2);
     589             :                 }
     590           0 :                 lineproc(pts, 2, clip, blitter);
     591           0 :                 break;
     592             :             case SkPath::kDone_Verb:
     593           0 :                 break;
     594             :         }
     595             :         if (SkPaint::kButt_Cap != capStyle) {
     596           0 :             if (prevVerb == SkPath::kMove_Verb &&
     597           0 :                     verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
     598           0 :                 firstPt = pts[0];  // the curve moved the initial point, so close to it instead
     599             :             }
     600           0 :             prevVerb = verb;
     601             :         }
     602             :     }
     603             : }
     604             : 
     605           0 : void SkScan::HairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
     606           0 :     hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::HairLineRgn);
     607           0 : }
     608             : 
     609           0 : void SkScan::AntiHairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
     610           0 :     hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
     611           0 : }
     612             : 
     613           0 : void SkScan::HairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
     614           0 :     hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::HairLineRgn);
     615           0 : }
     616             : 
     617           0 : void SkScan::AntiHairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
     618           0 :     hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
     619           0 : }
     620             : 
     621           0 : void SkScan::HairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
     622           0 :     hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::HairLineRgn);
     623           0 : }
     624             : 
     625           0 : void SkScan::AntiHairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
     626           0 :     hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
     627           0 : }
     628             : 
     629             : ///////////////////////////////////////////////////////////////////////////////
     630             : 
     631           0 : void SkScan::FrameRect(const SkRect& r, const SkPoint& strokeSize,
     632             :                        const SkRasterClip& clip, SkBlitter* blitter) {
     633           0 :     SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
     634             : 
     635           0 :     if (strokeSize.fX < 0 || strokeSize.fY < 0) {
     636           0 :         return;
     637             :     }
     638             : 
     639           0 :     const SkScalar dx = strokeSize.fX;
     640           0 :     const SkScalar dy = strokeSize.fY;
     641           0 :     SkScalar rx = SkScalarHalf(dx);
     642           0 :     SkScalar ry = SkScalarHalf(dy);
     643             :     SkRect   outer, tmp;
     644             : 
     645           0 :     outer.set(r.fLeft - rx, r.fTop - ry,
     646           0 :                 r.fRight + rx, r.fBottom + ry);
     647             : 
     648           0 :     if (r.width() <= dx || r.height() <= dy) {
     649           0 :         SkScan::FillRect(outer, clip, blitter);
     650           0 :         return;
     651             :     }
     652             : 
     653           0 :     tmp.set(outer.fLeft, outer.fTop, outer.fRight, outer.fTop + dy);
     654           0 :     SkScan::FillRect(tmp, clip, blitter);
     655           0 :     tmp.fTop = outer.fBottom - dy;
     656           0 :     tmp.fBottom = outer.fBottom;
     657           0 :     SkScan::FillRect(tmp, clip, blitter);
     658             : 
     659           0 :     tmp.set(outer.fLeft, outer.fTop + dy, outer.fLeft + dx, outer.fBottom - dy);
     660           0 :     SkScan::FillRect(tmp, clip, blitter);
     661           0 :     tmp.fLeft = outer.fRight - dx;
     662           0 :     tmp.fRight = outer.fRight;
     663           0 :     SkScan::FillRect(tmp, clip, blitter);
     664             : }
     665             : 
     666           0 : void SkScan::HairLine(const SkPoint pts[], int count, const SkRasterClip& clip,
     667             :                       SkBlitter* blitter) {
     668           0 :     if (clip.isBW()) {
     669           0 :         HairLineRgn(pts, count, &clip.bwRgn(), blitter);
     670             :     } else {
     671           0 :         const SkRegion* clipRgn = nullptr;
     672             : 
     673             :         SkRect r;
     674           0 :         r.set(pts, count);
     675           0 :         r.outset(SK_ScalarHalf, SK_ScalarHalf);
     676             : 
     677           0 :         SkAAClipBlitterWrapper wrap;
     678           0 :         if (!clip.quickContains(r.roundOut())) {
     679           0 :             wrap.init(clip, blitter);
     680           0 :             blitter = wrap.getBlitter();
     681           0 :             clipRgn = &wrap.getRgn();
     682             :         }
     683           0 :         HairLineRgn(pts, count, clipRgn, blitter);
     684             :     }
     685           0 : }
     686             : 
     687           0 : void SkScan::AntiHairLine(const SkPoint pts[], int count, const SkRasterClip& clip,
     688             :                           SkBlitter* blitter) {
     689           0 :     if (clip.isBW()) {
     690           0 :         AntiHairLineRgn(pts, count, &clip.bwRgn(), blitter);
     691             :     } else {
     692           0 :         const SkRegion* clipRgn = nullptr;
     693             : 
     694             :         SkRect r;
     695           0 :         r.set(pts, count);
     696             : 
     697           0 :         SkAAClipBlitterWrapper wrap;
     698           0 :         if (!clip.quickContains(r.roundOut().makeOutset(1, 1))) {
     699           0 :             wrap.init(clip, blitter);
     700           0 :             blitter = wrap.getBlitter();
     701           0 :             clipRgn = &wrap.getRgn();
     702             :         }
     703           0 :         AntiHairLineRgn(pts, count, clipRgn, blitter);
     704             :     }
     705           0 : }

Generated by: LCOV version 1.13