1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com/*
2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright 2006 The Android Open Source Project
3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com *
4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Use of this source code is governed by a BSD-style license that can be
5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * found in the LICENSE file.
6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com */
7ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com
88a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkScan.h"
98a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkBlitter.h"
104dbbd04314cc0606f8d3bafe515c97e52c180f73halcanary#include "SkMathPriv.h"
11045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com#include "SkRasterClip.h"
128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkFDot6.h"
13e28ff55d980d2992618b6b721c848aba96cf759areed@android.com#include "SkLineClipper.h"
148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1569bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.orgstatic void horiline(int x, int stopx, SkFixed fy, SkFixed dy,
1669bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.org                     SkBlitter* blitter) {
178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkASSERT(x < stopx);
188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    do {
208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        blitter->blitH(x, fy >> 16, 1);
218a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        fy += dy;
228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    } while (++x < stopx);
238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
248a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
2569bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.orgstatic void vertline(int y, int stopy, SkFixed fx, SkFixed dx,
2669bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.org                     SkBlitter* blitter) {
278a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkASSERT(y < stopy);
288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    do {
308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        blitter->blitH(fx >> 16, y, 1);
318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        fx += dx;
328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    } while (++y < stopy);
338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
350e51577a14f903ffeafa117a75954baeb173ffb9humper@google.com#ifdef SK_DEBUG
36bb39a297727fd70ba6b690b8bdfc62200854df86reed@google.comstatic bool canConvertFDot6ToFixed(SkFDot6 x) {
37bb39a297727fd70ba6b690b8bdfc62200854df86reed@google.com    const int maxDot6 = SK_MaxS32 >> (16 - 6);
38bb39a297727fd70ba6b690b8bdfc62200854df86reed@google.com    return SkAbs32(x) <= maxDot6;
39bb39a297727fd70ba6b690b8bdfc62200854df86reed@google.com}
400e51577a14f903ffeafa117a75954baeb173ffb9humper@google.com#endif
41bb39a297727fd70ba6b690b8bdfc62200854df86reed@google.com
425dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreedvoid SkScan::HairLineRgn(const SkPoint array[], int arrayCount, const SkRegion* clip,
435dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed                         SkBlitter* origBlitter) {
448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkBlitterClipper    clipper;
45e28ff55d980d2992618b6b721c848aba96cf759areed@android.com    SkIRect clipR, ptsR;
46e28ff55d980d2992618b6b721c848aba96cf759areed@android.com
475dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed    const SkScalar max = SkIntToScalar(32767);
485dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed    const SkRect fixedBounds = SkRect::MakeLTRB(-max, -max, max, max);
49bb39a297727fd70ba6b690b8bdfc62200854df86reed@google.com
505dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed    SkRect clipBounds;
51e28ff55d980d2992618b6b721c848aba96cf759areed@android.com    if (clip) {
525dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        clipBounds.set(clip->getBounds());
53e28ff55d980d2992618b6b721c848aba96cf759areed@android.com    }
548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
555dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed    for (int i = 0; i < arrayCount - 1; ++i) {
565dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        SkBlitter* blitter = origBlitter;
57fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
585dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        SkPoint pts[2];
59bb39a297727fd70ba6b690b8bdfc62200854df86reed@google.com
605dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        // We have to pre-clip the line to fit in a SkFixed, so we just chop
615dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        // the line. TODO find a way to actually draw beyond that range.
625dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        if (!SkLineClipper::IntersectLine(&array[i], fixedBounds, pts)) {
635dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            continue;
648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
665dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        // Perform a clip in scalar space, so we catch huge values which might
675dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        // be missed after we convert to SkFDot6 (overflow)
685dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        if (clip && !SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
695dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            continue;
708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
715dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed
725dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
735dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
745dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
755dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
765dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed
775dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        SkASSERT(canConvertFDot6ToFixed(x0));
785dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        SkASSERT(canConvertFDot6ToFixed(y0));
795dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        SkASSERT(canConvertFDot6ToFixed(x1));
805dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        SkASSERT(canConvertFDot6ToFixed(y1));
815dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed
825dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        if (clip) {
835dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            // now perform clipping again, as the rounding to dot6 can wiggle us
845dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            // our rects are really dot6 rects, but since we've already used
855dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            // lineclipper, we know they will fit in 32bits (26.6)
865dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            const SkIRect& bounds = clip->getBounds();
875dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed
885dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            clipR.set(SkIntToFDot6(bounds.fLeft), SkIntToFDot6(bounds.fTop),
895dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed                      SkIntToFDot6(bounds.fRight), SkIntToFDot6(bounds.fBottom));
905dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            ptsR.set(x0, y0, x1, y1);
915dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            ptsR.sort();
925dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed
935dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            // outset the right and bottom, to account for how hairlines are
945dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            // actually drawn, which may hit the pixel to the right or below of
955dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            // the coordinate
965dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            ptsR.fRight += SK_FDot6One;
975dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            ptsR.fBottom += SK_FDot6One;
985dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed
995dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            if (!SkIRect::Intersects(ptsR, clipR)) {
1005dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed                continue;
1015dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            }
1025dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            if (!clip->isRect() || !clipR.contains(ptsR)) {
1035dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed                blitter = clipper.apply(origBlitter, clip);
1045dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            }
10569bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.org        }
1068a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1075dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        SkFDot6 dx = x1 - x0;
1085dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        SkFDot6 dy = y1 - y0;
1098a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1105dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        if (SkAbs32(dx) > SkAbs32(dy)) { // mostly horizontal
1115dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            if (x0 > x1) {   // we want to go left-to-right
1125dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed                SkTSwap<SkFDot6>(x0, x1);
1135dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed                SkTSwap<SkFDot6>(y0, y1);
1145dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            }
1155dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            int ix0 = SkFDot6Round(x0);
1165dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            int ix1 = SkFDot6Round(x1);
1175dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            if (ix0 == ix1) {// too short to draw
1185dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed                continue;
1195dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            }
1208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1215dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            SkFixed slope = SkFixedDiv(dy, dx);
1225dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            SkFixed startY = SkFDot6ToFixed(y0) + (slope * ((32 - x0) & 63) >> 6);
1238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1245dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            horiline(ix0, ix1, startY, slope, blitter);
1255dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        } else {              // mostly vertical
1265dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            if (y0 > y1) {   // we want to go top-to-bottom
1275dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed                SkTSwap<SkFDot6>(x0, x1);
1285dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed                SkTSwap<SkFDot6>(y0, y1);
1295dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            }
1305dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            int iy0 = SkFDot6Round(y0);
1315dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            int iy1 = SkFDot6Round(y1);
1325dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            if (iy0 == iy1) { // too short to draw
1335dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed                continue;
1345dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            }
1355dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed
1365dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            SkFixed slope = SkFixedDiv(dx, dy);
1375dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            SkFixed startX = SkFDot6ToFixed(x0) + (slope * ((32 - y0) & 63) >> 6);
1385dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed
1395dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed            vertline(iy0, iy1, startX, slope, blitter);
1405dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        }
1418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com// we don't just draw 4 lines, 'cause that can leave a gap in the bottom-right
1458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com// and double-hit the top-left.
146e28ff55d980d2992618b6b721c848aba96cf759areed@android.com// TODO: handle huge coordinates on rect (before calling SkScalarToFixed)
147045e62d715f5ee9b03deb5af3c750f8318096179reed@google.comvoid SkScan::HairRect(const SkRect& rect, const SkRasterClip& clip,
14869bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.org                      SkBlitter* blitter) {
149045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com    SkAAClipBlitterWrapper wrapper;
1508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkBlitterClipper    clipper;
1518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkIRect             r;
1528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    r.set(SkScalarToFixed(rect.fLeft) >> 16,
1548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com          SkScalarToFixed(rect.fTop) >> 16,
1558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com          (SkScalarToFixed(rect.fRight) >> 16) + 1,
1568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com          (SkScalarToFixed(rect.fBottom) >> 16) + 1);
1578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
158045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com    if (clip.quickReject(r)) {
159045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com        return;
160045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com    }
161045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com    if (!clip.quickContains(r)) {
162045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com        const SkRegion* clipRgn;
163045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com        if (clip.isBW()) {
164045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com            clipRgn = &clip.bwRgn();
165045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com        } else {
166045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com            wrapper.init(clip, blitter);
167045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com            clipRgn = &wrapper.getRgn();
168045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com            blitter = wrapper.getBlitter();
16969bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.org        }
170045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com        blitter = clipper.apply(blitter, clipRgn);
1718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    int width = r.width();
1748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    int height = r.height();
175fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
17669bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.org    if ((width | height) == 0) {
1778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return;
17869bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.org    }
17969bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.org    if (width <= 2 || height <= 2) {
1808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        blitter->blitRect(r.fLeft, r.fTop, width, height);
1818a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return;
1828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1838a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // if we get here, we know we have 4 segments to draw
1848a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    blitter->blitH(r.fLeft, r.fTop, width);                     // top
1858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    blitter->blitRect(r.fLeft, r.fTop + 1, 1, height - 2);      // left
1868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    blitter->blitRect(r.fRight - 1, r.fTop + 1, 1, height - 2); // right
1878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    blitter->blitH(r.fLeft, r.fBottom - 1, width);              // bottom
1888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
19069bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.org///////////////////////////////////////////////////////////////////////////////
1918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1928a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkPath.h"
1938a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkGeometry.h"
1946983f66d8b3a489133b751e2cef03e72a03bfeaereed#include "SkNx.h"
1956983f66d8b3a489133b751e2cef03e72a03bfeaereed
19652b64619d99325bac2f9b53c9fb0a4b7c69e442fcaryclark#define kMaxCubicSubdivideLevel 9
1976983f66d8b3a489133b751e2cef03e72a03bfeaereed#define kMaxQuadSubdivideLevel  5
1988a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1998a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comstatic int compute_int_quad_dist(const SkPoint pts[3]) {
2008a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // compute the vector between the control point ([1]) and the middle of the
2018a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // line connecting the start and end ([0] and [2])
2028a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkScalar dx = SkScalarHalf(pts[0].fX + pts[2].fX) - pts[1].fX;
2038a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkScalar dy = SkScalarHalf(pts[0].fY + pts[2].fY) - pts[1].fY;
2048a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // we want everyone to be positive
2058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    dx = SkScalarAbs(dx);
2068a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    dy = SkScalarAbs(dy);
2078a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // convert to whole pixel values (use ceiling to be conservative)
208e1ca705cac4b946993f6cbf798e2a0ba27e739f3reed@google.com    int idx = SkScalarCeilToInt(dx);
209e1ca705cac4b946993f6cbf798e2a0ba27e739f3reed@google.com    int idy = SkScalarCeilToInt(dy);
2108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // use the cheap approx for distance
2118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (idx > idy) {
2128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return idx + (idy >> 1);
2138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    } else {
2148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return idy + (idx >> 1);
2158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
2168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
2178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
2184cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclarkstatic void hair_quad(const SkPoint pts[3], const SkRegion* clip,
2195dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed                     SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
2206983f66d8b3a489133b751e2cef03e72a03bfeaereed    SkASSERT(level <= kMaxQuadSubdivideLevel);
2216983f66d8b3a489133b751e2cef03e72a03bfeaereed
2225ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark    SkQuadCoeff coeff(pts);
2236983f66d8b3a489133b751e2cef03e72a03bfeaereed
2246983f66d8b3a489133b751e2cef03e72a03bfeaereed    const int lines = 1 << level;
2256983f66d8b3a489133b751e2cef03e72a03bfeaereed    Sk2s t(0);
2266983f66d8b3a489133b751e2cef03e72a03bfeaereed    Sk2s dt(SK_Scalar1 / lines);
2276983f66d8b3a489133b751e2cef03e72a03bfeaereed
2286983f66d8b3a489133b751e2cef03e72a03bfeaereed    SkPoint tmp[(1 << kMaxQuadSubdivideLevel) + 1];
2296983f66d8b3a489133b751e2cef03e72a03bfeaereed    SkASSERT((unsigned)lines < SK_ARRAY_COUNT(tmp));
2306983f66d8b3a489133b751e2cef03e72a03bfeaereed
2316983f66d8b3a489133b751e2cef03e72a03bfeaereed    tmp[0] = pts[0];
2325ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark    Sk2s A = coeff.fA;
2335ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark    Sk2s B = coeff.fB;
2345ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark    Sk2s C = coeff.fC;
2356983f66d8b3a489133b751e2cef03e72a03bfeaereed    for (int i = 1; i < lines; ++i) {
236f2fe0e03205c4d7f70496ec8c6bea9a82ae88329mtklein        t = t + dt;
237507ef6d68115ae9e6d884bb36436a1463523d893mtklein        ((A * t + B) * t + C).store(&tmp[i]);
2386983f66d8b3a489133b751e2cef03e72a03bfeaereed    }
2396983f66d8b3a489133b751e2cef03e72a03bfeaereed    tmp[lines] = pts[2];
2406983f66d8b3a489133b751e2cef03e72a03bfeaereed    lineproc(tmp, lines + 1, clip, blitter);
2418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
2428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
2434cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclarkstatic SkRect compute_nocheck_quad_bounds(const SkPoint pts[3]) {
2444cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark    SkASSERT(SkScalarsAreFinite(&pts[0].fX, 6));
2454cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark
2464cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark    Sk2s min = Sk2s::Load(pts);
2474cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark    Sk2s max = min;
2484cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark    for (int i = 1; i < 3; ++i) {
2494cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark        Sk2s pair = Sk2s::Load(pts+i);
2504cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark        min = Sk2s::Min(min, pair);
2514cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark        max = Sk2s::Max(max, pair);
2524cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark    }
2534cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark    return { min[0], min[1], max[0], max[1] };
2544cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark}
2554cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark
2564cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclarkstatic bool is_inverted(const SkRect& r) {
2574cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark    return r.fLeft > r.fRight || r.fTop > r.fBottom;
2584cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark}
2594cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark
2604cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark// Can't call SkRect::intersects, since it cares about empty, and we don't (since we tracking
2614cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark// something to be stroked, so empty can still draw something (e.g. horizontal line)
2624cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclarkstatic bool geometric_overlap(const SkRect& a, const SkRect& b) {
2634cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark    SkASSERT(!is_inverted(a) && !is_inverted(b));
2644cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark    return a.fLeft < b.fRight && b.fLeft < a.fRight &&
2654cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark            a.fTop < b.fBottom && b.fTop < a.fBottom;
2664cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark}
2674cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark
2684cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark// Can't call SkRect::contains, since it cares about empty, and we don't (since we tracking
2694cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark// something to be stroked, so empty can still draw something (e.g. horizontal line)
2704cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclarkstatic bool geometric_contains(const SkRect& outer, const SkRect& inner) {
2714cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark    SkASSERT(!is_inverted(outer) && !is_inverted(inner));
2724cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark    return inner.fRight <= outer.fRight && inner.fLeft >= outer.fLeft &&
2734cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark            inner.fBottom <= outer.fBottom && inner.fTop >= outer.fTop;
2744cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark}
2754cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark
2764cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclarkstatic inline void hairquad(const SkPoint pts[3], const SkRegion* clip, const SkRect* insetClip, const SkRect* outsetClip,
2774cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark    SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
2784cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark    if (insetClip) {
2794cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark        SkASSERT(outsetClip);
2804cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark        SkRect bounds = compute_nocheck_quad_bounds(pts);
2814cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark        if (!geometric_overlap(*outsetClip, bounds)) {
2824cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark            return;
2834cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark        } else if (geometric_contains(*insetClip, bounds)) {
2844cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark            clip = nullptr;
2854cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark        }
2864cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark    }
2874cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark
2884cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark    hair_quad(pts, clip, blitter, level, lineproc);
2894cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark}
2904cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark
2916983f66d8b3a489133b751e2cef03e72a03bfeaereedstatic inline Sk2s abs(const Sk2s& value) {
292f2fe0e03205c4d7f70496ec8c6bea9a82ae88329mtklein    return Sk2s::Max(value, Sk2s(0)-value);
2936983f66d8b3a489133b751e2cef03e72a03bfeaereed}
2946983f66d8b3a489133b751e2cef03e72a03bfeaereed
2956983f66d8b3a489133b751e2cef03e72a03bfeaereedstatic inline SkScalar max_component(const Sk2s& value) {
2966983f66d8b3a489133b751e2cef03e72a03bfeaereed    SkScalar components[2];
2976983f66d8b3a489133b751e2cef03e72a03bfeaereed    value.store(components);
2986983f66d8b3a489133b751e2cef03e72a03bfeaereed    return SkTMax(components[0], components[1]);
2996983f66d8b3a489133b751e2cef03e72a03bfeaereed}
3006983f66d8b3a489133b751e2cef03e72a03bfeaereed
3016983f66d8b3a489133b751e2cef03e72a03bfeaereedstatic inline int compute_cubic_segs(const SkPoint pts[4]) {
3026983f66d8b3a489133b751e2cef03e72a03bfeaereed    Sk2s p0 = from_point(pts[0]);
3036983f66d8b3a489133b751e2cef03e72a03bfeaereed    Sk2s p1 = from_point(pts[1]);
3046983f66d8b3a489133b751e2cef03e72a03bfeaereed    Sk2s p2 = from_point(pts[2]);
3056983f66d8b3a489133b751e2cef03e72a03bfeaereed    Sk2s p3 = from_point(pts[3]);
3066983f66d8b3a489133b751e2cef03e72a03bfeaereed
3076983f66d8b3a489133b751e2cef03e72a03bfeaereed    const Sk2s oneThird(1.0f / 3.0f);
3086983f66d8b3a489133b751e2cef03e72a03bfeaereed    const Sk2s twoThird(2.0f / 3.0f);
3096983f66d8b3a489133b751e2cef03e72a03bfeaereed
3106983f66d8b3a489133b751e2cef03e72a03bfeaereed    Sk2s p13 = oneThird * p3 + twoThird * p0;
3116983f66d8b3a489133b751e2cef03e72a03bfeaereed    Sk2s p23 = oneThird * p0 + twoThird * p3;
3126983f66d8b3a489133b751e2cef03e72a03bfeaereed
3136983f66d8b3a489133b751e2cef03e72a03bfeaereed    SkScalar diff = max_component(Sk2s::Max(abs(p1 - p13), abs(p2 - p23)));
3146983f66d8b3a489133b751e2cef03e72a03bfeaereed    SkScalar tol = SK_Scalar1 / 8;
3156983f66d8b3a489133b751e2cef03e72a03bfeaereed
3166983f66d8b3a489133b751e2cef03e72a03bfeaereed    for (int i = 0; i < kMaxCubicSubdivideLevel; ++i) {
3176983f66d8b3a489133b751e2cef03e72a03bfeaereed        if (diff < tol) {
3186983f66d8b3a489133b751e2cef03e72a03bfeaereed            return 1 << i;
3196983f66d8b3a489133b751e2cef03e72a03bfeaereed        }
3206983f66d8b3a489133b751e2cef03e72a03bfeaereed        tol *= 4;
3216983f66d8b3a489133b751e2cef03e72a03bfeaereed    }
3226983f66d8b3a489133b751e2cef03e72a03bfeaereed    return 1 << kMaxCubicSubdivideLevel;
3236983f66d8b3a489133b751e2cef03e72a03bfeaereed}
3246983f66d8b3a489133b751e2cef03e72a03bfeaereed
3256983f66d8b3a489133b751e2cef03e72a03bfeaereedstatic bool lt_90(SkPoint p0, SkPoint pivot, SkPoint p2) {
3266983f66d8b3a489133b751e2cef03e72a03bfeaereed    return SkVector::DotProduct(p0 - pivot, p2 - pivot) >= 0;
3276983f66d8b3a489133b751e2cef03e72a03bfeaereed}
3286983f66d8b3a489133b751e2cef03e72a03bfeaereed
3296983f66d8b3a489133b751e2cef03e72a03bfeaereed// The off-curve points are "inside" the limits of the on-curve pts
3306983f66d8b3a489133b751e2cef03e72a03bfeaereedstatic bool quick_cubic_niceness_check(const SkPoint pts[4]) {
3316983f66d8b3a489133b751e2cef03e72a03bfeaereed    return lt_90(pts[1], pts[0], pts[3]) &&
3326983f66d8b3a489133b751e2cef03e72a03bfeaereed           lt_90(pts[2], pts[0], pts[3]) &&
3336983f66d8b3a489133b751e2cef03e72a03bfeaereed           lt_90(pts[1], pts[3], pts[0]) &&
3346983f66d8b3a489133b751e2cef03e72a03bfeaereed           lt_90(pts[2], pts[3], pts[0]);
3356983f66d8b3a489133b751e2cef03e72a03bfeaereed}
3366983f66d8b3a489133b751e2cef03e72a03bfeaereed
3376983f66d8b3a489133b751e2cef03e72a03bfeaereedstatic void hair_cubic(const SkPoint pts[4], const SkRegion* clip, SkBlitter* blitter,
3386983f66d8b3a489133b751e2cef03e72a03bfeaereed                       SkScan::HairRgnProc lineproc) {
3396983f66d8b3a489133b751e2cef03e72a03bfeaereed    const int lines = compute_cubic_segs(pts);
3406983f66d8b3a489133b751e2cef03e72a03bfeaereed    SkASSERT(lines > 0);
3416983f66d8b3a489133b751e2cef03e72a03bfeaereed    if (1 == lines) {
3426983f66d8b3a489133b751e2cef03e72a03bfeaereed        SkPoint tmp[2] = { pts[0], pts[3] };
3436983f66d8b3a489133b751e2cef03e72a03bfeaereed        lineproc(tmp, 2, clip, blitter);
3446983f66d8b3a489133b751e2cef03e72a03bfeaereed        return;
3456983f66d8b3a489133b751e2cef03e72a03bfeaereed    }
3466983f66d8b3a489133b751e2cef03e72a03bfeaereed
3475ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark    SkCubicCoeff coeff(pts);
348f2fe0e03205c4d7f70496ec8c6bea9a82ae88329mtklein
3496983f66d8b3a489133b751e2cef03e72a03bfeaereed    const Sk2s dt(SK_Scalar1 / lines);
3506983f66d8b3a489133b751e2cef03e72a03bfeaereed    Sk2s t(0);
3516983f66d8b3a489133b751e2cef03e72a03bfeaereed
3526983f66d8b3a489133b751e2cef03e72a03bfeaereed    SkPoint tmp[(1 << kMaxCubicSubdivideLevel) + 1];
3536983f66d8b3a489133b751e2cef03e72a03bfeaereed    SkASSERT((unsigned)lines < SK_ARRAY_COUNT(tmp));
3546983f66d8b3a489133b751e2cef03e72a03bfeaereed
3556983f66d8b3a489133b751e2cef03e72a03bfeaereed    tmp[0] = pts[0];
3565ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark    Sk2s A = coeff.fA;
3575ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark    Sk2s B = coeff.fB;
3585ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark    Sk2s C = coeff.fC;
3595ba2b9612ae4bc3a244bf45f1ec55c3a5a41e181caryclark    Sk2s D = coeff.fD;
3606983f66d8b3a489133b751e2cef03e72a03bfeaereed    for (int i = 1; i < lines; ++i) {
361f2fe0e03205c4d7f70496ec8c6bea9a82ae88329mtklein        t = t + dt;
362507ef6d68115ae9e6d884bb36436a1463523d893mtklein        (((A * t + B) * t + C) * t + D).store(&tmp[i]);
3636983f66d8b3a489133b751e2cef03e72a03bfeaereed    }
3646983f66d8b3a489133b751e2cef03e72a03bfeaereed    tmp[lines] = pts[3];
3656983f66d8b3a489133b751e2cef03e72a03bfeaereed    lineproc(tmp, lines + 1, clip, blitter);
3666983f66d8b3a489133b751e2cef03e72a03bfeaereed}
3676983f66d8b3a489133b751e2cef03e72a03bfeaereed
36870509762c88df911c58c3984e6b1e673b5ecaeacreedstatic SkRect compute_nocheck_cubic_bounds(const SkPoint pts[4]) {
36970509762c88df911c58c3984e6b1e673b5ecaeacreed    SkASSERT(SkScalarsAreFinite(&pts[0].fX, 8));
37070509762c88df911c58c3984e6b1e673b5ecaeacreed
371507ef6d68115ae9e6d884bb36436a1463523d893mtklein    Sk2s min = Sk2s::Load(pts);
37270509762c88df911c58c3984e6b1e673b5ecaeacreed    Sk2s max = min;
37370509762c88df911c58c3984e6b1e673b5ecaeacreed    for (int i = 1; i < 4; ++i) {
374507ef6d68115ae9e6d884bb36436a1463523d893mtklein        Sk2s pair = Sk2s::Load(pts+i);
37570509762c88df911c58c3984e6b1e673b5ecaeacreed        min = Sk2s::Min(min, pair);
37670509762c88df911c58c3984e6b1e673b5ecaeacreed        max = Sk2s::Max(max, pair);
37770509762c88df911c58c3984e6b1e673b5ecaeacreed    }
3787c249e531900929c2fe2cdde76619fa6d2538c49mtklein    return { min[0], min[1], max[0], max[1] };
37970509762c88df911c58c3984e6b1e673b5ecaeacreed}
38070509762c88df911c58c3984e6b1e673b5ecaeacreed
38170509762c88df911c58c3984e6b1e673b5ecaeacreedstatic inline void haircubic(const SkPoint pts[4], const SkRegion* clip, const SkRect* insetClip, const SkRect* outsetClip,
3825dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed                      SkBlitter* blitter, int level, SkScan::HairRgnProc lineproc) {
38370509762c88df911c58c3984e6b1e673b5ecaeacreed    if (insetClip) {
38470509762c88df911c58c3984e6b1e673b5ecaeacreed        SkASSERT(outsetClip);
38570509762c88df911c58c3984e6b1e673b5ecaeacreed        SkRect bounds = compute_nocheck_cubic_bounds(pts);
38670509762c88df911c58c3984e6b1e673b5ecaeacreed        if (!geometric_overlap(*outsetClip, bounds)) {
38770509762c88df911c58c3984e6b1e673b5ecaeacreed            return;
38870509762c88df911c58c3984e6b1e673b5ecaeacreed        } else if (geometric_contains(*insetClip, bounds)) {
38970509762c88df911c58c3984e6b1e673b5ecaeacreed            clip = nullptr;
39070509762c88df911c58c3984e6b1e673b5ecaeacreed        }
39170509762c88df911c58c3984e6b1e673b5ecaeacreed    }
39270509762c88df911c58c3984e6b1e673b5ecaeacreed
3936983f66d8b3a489133b751e2cef03e72a03bfeaereed    if (quick_cubic_niceness_check(pts)) {
3946983f66d8b3a489133b751e2cef03e72a03bfeaereed        hair_cubic(pts, clip, blitter, lineproc);
3956983f66d8b3a489133b751e2cef03e72a03bfeaereed    } else {
3966983f66d8b3a489133b751e2cef03e72a03bfeaereed        SkPoint  tmp[13];
3976983f66d8b3a489133b751e2cef03e72a03bfeaereed        SkScalar tValues[3];
3988a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
3996983f66d8b3a489133b751e2cef03e72a03bfeaereed        int count = SkChopCubicAtMaxCurvature(pts, tmp, tValues);
4006983f66d8b3a489133b751e2cef03e72a03bfeaereed        for (int i = 0; i < count; i++) {
4016983f66d8b3a489133b751e2cef03e72a03bfeaereed            hair_cubic(&tmp[i * 3], clip, blitter, lineproc);
4026983f66d8b3a489133b751e2cef03e72a03bfeaereed        }
4036983f66d8b3a489133b751e2cef03e72a03bfeaereed    }
4046983f66d8b3a489133b751e2cef03e72a03bfeaereed}
405639a82855b94b93c4fa45560e67df8ec4a8bbb3areed
4064e05fd25c88bea64a988ededfc810770095ed97creed@google.comstatic int compute_quad_level(const SkPoint pts[3]) {
4074e05fd25c88bea64a988ededfc810770095ed97creed@google.com    int d = compute_int_quad_dist(pts);
4084e05fd25c88bea64a988ededfc810770095ed97creed@google.com    /*  quadratics approach the line connecting their start and end points
4094e05fd25c88bea64a988ededfc810770095ed97creed@google.com     4x closer with each subdivision, so we compute the number of
4104e05fd25c88bea64a988ededfc810770095ed97creed@google.com     subdivisions to be the minimum need to get that distance to be less
4114e05fd25c88bea64a988ededfc810770095ed97creed@google.com     than a pixel.
4124e05fd25c88bea64a988ededfc810770095ed97creed@google.com     */
4134e05fd25c88bea64a988ededfc810770095ed97creed@google.com    int level = (33 - SkCLZ(d)) >> 1;
4144e05fd25c88bea64a988ededfc810770095ed97creed@google.com    // sanity check on level (from the previous version)
4154e05fd25c88bea64a988ededfc810770095ed97creed@google.com    if (level > kMaxQuadSubdivideLevel) {
4164e05fd25c88bea64a988ededfc810770095ed97creed@google.com        level = kMaxQuadSubdivideLevel;
4174e05fd25c88bea64a988ededfc810770095ed97creed@google.com    }
4184e05fd25c88bea64a988ededfc810770095ed97creed@google.com    return level;
4194e05fd25c88bea64a988ededfc810770095ed97creed@google.com}
4204e05fd25c88bea64a988ededfc810770095ed97creed@google.com
4212028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark/* Extend the points in the direction of the starting or ending tangent by 1/2 unit to
4222028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark   account for a round or square cap. If there's no distance between the end point and
4232028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark   the control point, use the next control point to create a tangent. If the curve
4242028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark   is degenerate, move the cap out 1/2 unit horizontally. */
4252028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclarktemplate <SkPaint::Cap capStyle>
4262028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclarkvoid extend_pts(SkPath::Verb prevVerb, SkPath::Verb nextVerb, SkPoint* pts, int ptCount) {
4272028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark    SkASSERT(SkPaint::kSquare_Cap == capStyle || SkPaint::kRound_Cap == capStyle);
4282028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark    // The area of a circle is PI*R*R. For a unit circle, R=1/2, and the cap covers half of that.
4292028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark    const SkScalar capOutset = SkPaint::kSquare_Cap == capStyle ? 0.5f : SK_ScalarPI / 8;
4302028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark    if (SkPath::kMove_Verb == prevVerb) {
4312028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        SkPoint* first = pts;
4322028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        SkPoint* ctrl = first;
4332028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        int controls = ptCount - 1;
4342028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        SkVector tangent;
4352028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        do {
4362028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark            tangent = *first - *++ctrl;
4372028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        } while (tangent.isZero() && --controls > 0);
4382028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        if (tangent.isZero()) {
4392028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark            tangent.set(1, 0);
44040b7d3b48b522e233ee63d2c88cff82c4bd5fb15caryclark            controls = ptCount - 1;  // If all points are equal, move all but one
4412028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        } else {
4422028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark            tangent.normalize();
4432028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        }
44440b7d3b48b522e233ee63d2c88cff82c4bd5fb15caryclark        do {    // If the end point and control points are equal, loop to move them in tandem.
44540b7d3b48b522e233ee63d2c88cff82c4bd5fb15caryclark            first->fX += tangent.fX * capOutset;
44640b7d3b48b522e233ee63d2c88cff82c4bd5fb15caryclark            first->fY += tangent.fY * capOutset;
44740b7d3b48b522e233ee63d2c88cff82c4bd5fb15caryclark            ++first;
44840b7d3b48b522e233ee63d2c88cff82c4bd5fb15caryclark        } while (++controls < ptCount);
4492028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark    }
450f0ca0e0844021d39466c5b69cc9afd195e353bbaBrian Osman    if (SkPath::kMove_Verb == nextVerb || SkPath::kDone_Verb == nextVerb
451f0ca0e0844021d39466c5b69cc9afd195e353bbaBrian Osman            || SkPath::kClose_Verb == nextVerb) {
4522028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        SkPoint* last = &pts[ptCount - 1];
4532028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        SkPoint* ctrl = last;
4542028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        int controls = ptCount - 1;
4552028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        SkVector tangent;
4562028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        do {
4572028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark            tangent = *last - *--ctrl;
4582028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        } while (tangent.isZero() && --controls > 0);
4592028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        if (tangent.isZero()) {
4602028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark            tangent.set(-1, 0);
46140b7d3b48b522e233ee63d2c88cff82c4bd5fb15caryclark            controls = ptCount - 1;
4622028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        } else {
4632028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark            tangent.normalize();
4642028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        }
46540b7d3b48b522e233ee63d2c88cff82c4bd5fb15caryclark        do {
46640b7d3b48b522e233ee63d2c88cff82c4bd5fb15caryclark            last->fX += tangent.fX * capOutset;
46740b7d3b48b522e233ee63d2c88cff82c4bd5fb15caryclark            last->fY += tangent.fY * capOutset;
46840b7d3b48b522e233ee63d2c88cff82c4bd5fb15caryclark            --last;
46940b7d3b48b522e233ee63d2c88cff82c4bd5fb15caryclark        } while (++controls < ptCount);
4702028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark    }
4712028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark}
4722028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark
4732028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclarktemplate <SkPaint::Cap capStyle>
4742028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclarkvoid hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter,
4755dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed                      SkScan::HairRgnProc lineproc) {
47669bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.org    if (path.isEmpty()) {
4778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return;
47869bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.org    }
4798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
480045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com    SkAAClipBlitterWrapper wrap;
48196fcdcc219d2a0d3579719b84b28bede76efba64halcanary    const SkRegion* clip = nullptr;
48270509762c88df911c58c3984e6b1e673b5ecaeacreed    SkRect insetStorage, outsetStorage;
48370509762c88df911c58c3984e6b1e673b5ecaeacreed    const SkRect* insetClip = nullptr;
48470509762c88df911c58c3984e6b1e673b5ecaeacreed    const SkRect* outsetClip = nullptr;
4858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
486045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com    {
48740b7d3b48b522e233ee63d2c88cff82c4bd5fb15caryclark        const int capOut = SkPaint::kButt_Cap == capStyle ? 1 : 2;
48840b7d3b48b522e233ee63d2c88cff82c4bd5fb15caryclark        const SkIRect ibounds = path.getBounds().roundOut().makeOutset(capOut, capOut);
489045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com        if (rclip.quickReject(ibounds)) {
4908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            return;
49169bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.org        }
492045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com        if (!rclip.quickContains(ibounds)) {
493045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com            if (rclip.isBW()) {
494045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com                clip = &rclip.bwRgn();
495045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com            } else {
496045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com                wrap.init(rclip, blitter);
497045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com                blitter = wrap.getBlitter();
498045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com                clip = &wrap.getRgn();
499045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com            }
50070509762c88df911c58c3984e6b1e673b5ecaeacreed
50170509762c88df911c58c3984e6b1e673b5ecaeacreed            /*
50270509762c88df911c58c3984e6b1e673b5ecaeacreed             *  We now cache two scalar rects, to use for culling per-segment (e.g. cubic).
50370509762c88df911c58c3984e6b1e673b5ecaeacreed             *  Since we're hairlining, the "bounds" of the control points isn't necessairly the
50470509762c88df911c58c3984e6b1e673b5ecaeacreed             *  limit of where a segment can draw (it might draw up to 1 pixel beyond in aa-hairs).
50570509762c88df911c58c3984e6b1e673b5ecaeacreed             *
50670509762c88df911c58c3984e6b1e673b5ecaeacreed             *  Compute the pt-bounds per segment is easy, so we do that, and then inversely adjust
50770509762c88df911c58c3984e6b1e673b5ecaeacreed             *  the culling bounds so we can just do a straight compare per segment.
50870509762c88df911c58c3984e6b1e673b5ecaeacreed             *
50970509762c88df911c58c3984e6b1e673b5ecaeacreed             *  insetClip is use for quick-accept (i.e. the segment is not clipped), so we inset
51070509762c88df911c58c3984e6b1e673b5ecaeacreed             *  it from the clip-bounds (since segment bounds can be off by 1).
51170509762c88df911c58c3984e6b1e673b5ecaeacreed             *
51270509762c88df911c58c3984e6b1e673b5ecaeacreed             *  outsetClip is used for quick-reject (i.e. the segment is entirely outside), so we
51370509762c88df911c58c3984e6b1e673b5ecaeacreed             *  outset it from the clip-bounds.
51470509762c88df911c58c3984e6b1e673b5ecaeacreed             */
51570509762c88df911c58c3984e6b1e673b5ecaeacreed            insetStorage.set(clip->getBounds());
51670509762c88df911c58c3984e6b1e673b5ecaeacreed            outsetStorage = insetStorage.makeOutset(1, 1);
51770509762c88df911c58c3984e6b1e673b5ecaeacreed            insetStorage.inset(1, 1);
51870509762c88df911c58c3984e6b1e673b5ecaeacreed            if (is_inverted(insetStorage)) {
51970509762c88df911c58c3984e6b1e673b5ecaeacreed                /*
52070509762c88df911c58c3984e6b1e673b5ecaeacreed                 *  our bounds checks assume the rects are never inverted. If insetting has
52170509762c88df911c58c3984e6b1e673b5ecaeacreed                 *  created that, we assume that the area is too small to safely perform a
52270509762c88df911c58c3984e6b1e673b5ecaeacreed                 *  quick-accept, so we just mark the rect as empty (so the quick-accept check
52370509762c88df911c58c3984e6b1e673b5ecaeacreed                 *  will always fail.
52470509762c88df911c58c3984e6b1e673b5ecaeacreed                 */
52570509762c88df911c58c3984e6b1e673b5ecaeacreed                insetStorage.setEmpty();    // just so we don't pass an inverted rect
52670509762c88df911c58c3984e6b1e673b5ecaeacreed            }
5274cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark            if (rclip.isRect()) {
5284cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark                insetClip = &insetStorage;
5294cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark            }
53070509762c88df911c58c3984e6b1e673b5ecaeacreed            outsetClip = &outsetStorage;
53169bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.org        }
5328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
5338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
5345123eb760fcb1684394b73c2fc6bfad270b29bafreed    SkPath::RawIter     iter(path);
5355123eb760fcb1684394b73c2fc6bfad270b29bafreed    SkPoint             pts[4], firstPt, lastPt;
5362028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark    SkPath::Verb        verb, prevVerb;
5375123eb760fcb1684394b73c2fc6bfad270b29bafreed    SkAutoConicToQuads  converter;
5388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
5392028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark    if (SkPaint::kButt_Cap != capStyle) {
5402028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        prevVerb = SkPath::kDone_Verb;
5412028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark    }
5425123eb760fcb1684394b73c2fc6bfad270b29bafreed    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
5438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        switch (verb) {
5444e05fd25c88bea64a988ededfc810770095ed97creed@google.com            case SkPath::kMove_Verb:
5455123eb760fcb1684394b73c2fc6bfad270b29bafreed                firstPt = lastPt = pts[0];
5464e05fd25c88bea64a988ededfc810770095ed97creed@google.com                break;
54769bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.org            case SkPath::kLine_Verb:
5482028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark                if (SkPaint::kButt_Cap != capStyle) {
5492028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark                    extend_pts<capStyle>(prevVerb, iter.peek(), pts, 2);
5502028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark                }
5515dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed                lineproc(pts, 2, clip, blitter);
5525123eb760fcb1684394b73c2fc6bfad270b29bafreed                lastPt = pts[1];
55369bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.org                break;
5544e05fd25c88bea64a988ededfc810770095ed97creed@google.com            case SkPath::kQuad_Verb:
5552028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark                if (SkPaint::kButt_Cap != capStyle) {
5562028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark                    extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3);
5572028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark                }
5584cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark                hairquad(pts, clip, insetClip, outsetClip, blitter, compute_quad_level(pts), lineproc);
5595123eb760fcb1684394b73c2fc6bfad270b29bafreed                lastPt = pts[2];
5604e05fd25c88bea64a988ededfc810770095ed97creed@google.com                break;
5614e05fd25c88bea64a988ededfc810770095ed97creed@google.com            case SkPath::kConic_Verb: {
5622028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark                if (SkPaint::kButt_Cap != capStyle) {
5632028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark                    extend_pts<capStyle>(prevVerb, iter.peek(), pts, 3);
5642028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark                }
5654e05fd25c88bea64a988ededfc810770095ed97creed@google.com                // how close should the quads be to the original conic?
5664e05fd25c88bea64a988ededfc810770095ed97creed@google.com                const SkScalar tol = SK_Scalar1 / 4;
5674e05fd25c88bea64a988ededfc810770095ed97creed@google.com                const SkPoint* quadPts = converter.computeQuads(pts,
5684e05fd25c88bea64a988ededfc810770095ed97creed@google.com                                                       iter.conicWeight(), tol);
5694e05fd25c88bea64a988ededfc810770095ed97creed@google.com                for (int i = 0; i < converter.countQuads(); ++i) {
5704e05fd25c88bea64a988ededfc810770095ed97creed@google.com                    int level = compute_quad_level(quadPts);
5714cba202b7162fb5f364235dd29f0bdbd53a8e33ccaryclark                    hairquad(quadPts, clip, insetClip, outsetClip, blitter, level, lineproc);
5724e05fd25c88bea64a988ededfc810770095ed97creed@google.com                    quadPts += 2;
57369bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.org                }
5745123eb760fcb1684394b73c2fc6bfad270b29bafreed                lastPt = pts[2];
57569bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.org                break;
5768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
5776983f66d8b3a489133b751e2cef03e72a03bfeaereed            case SkPath::kCubic_Verb: {
5782028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark                if (SkPaint::kButt_Cap != capStyle) {
5792028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark                    extend_pts<capStyle>(prevVerb, iter.peek(), pts, 4);
5802028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark                }
58170509762c88df911c58c3984e6b1e673b5ecaeacreed                haircubic(pts, clip, insetClip, outsetClip, blitter, kMaxCubicSubdivideLevel, lineproc);
5825123eb760fcb1684394b73c2fc6bfad270b29bafreed                lastPt = pts[3];
5836983f66d8b3a489133b751e2cef03e72a03bfeaereed            } break;
5844e05fd25c88bea64a988ededfc810770095ed97creed@google.com            case SkPath::kClose_Verb:
5855123eb760fcb1684394b73c2fc6bfad270b29bafreed                pts[0] = lastPt;
5865123eb760fcb1684394b73c2fc6bfad270b29bafreed                pts[1] = firstPt;
5871f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                if (SkPaint::kButt_Cap != capStyle && prevVerb == SkPath::kMove_Verb) {
5881f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                    // cap moveTo/close to match svg expectations for degenerate segments
5891f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                    extend_pts<capStyle>(prevVerb, iter.peek(), pts, 2);
5901f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                }
5915123eb760fcb1684394b73c2fc6bfad270b29bafreed                lineproc(pts, 2, clip, blitter);
5924e05fd25c88bea64a988ededfc810770095ed97creed@google.com                break;
5934e05fd25c88bea64a988ededfc810770095ed97creed@google.com            case SkPath::kDone_Verb:
59469bc9943c742f97614cc5b44a9960fc76873dfe6mike@reedtribe.org                break;
5958a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
5962028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        if (SkPaint::kButt_Cap != capStyle) {
59767075f0723f7ac5a7822b8d5708357b36224f0bfcaryclark            if (prevVerb == SkPath::kMove_Verb &&
59867075f0723f7ac5a7822b8d5708357b36224f0bfcaryclark                    verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
59967075f0723f7ac5a7822b8d5708357b36224f0bfcaryclark                firstPt = pts[0];  // the curve moved the initial point, so close to it instead
60067075f0723f7ac5a7822b8d5708357b36224f0bfcaryclark            }
6012028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark            prevVerb = verb;
6022028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark        }
6038a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
6048a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
6058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
606054eef2b5ca84bf5b03e5ddba0c30055f5e474ccreedvoid SkScan::HairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
6072028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark    hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::HairLineRgn);
6088a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
6098a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
610054eef2b5ca84bf5b03e5ddba0c30055f5e474ccreedvoid SkScan::AntiHairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
6112028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark    hair_path<SkPaint::kButt_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
6122028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark}
6132028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark
6142028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclarkvoid SkScan::HairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
6152028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark    hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::HairLineRgn);
6162028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark}
6172028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark
6182028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclarkvoid SkScan::AntiHairSquarePath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
6192028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark    hair_path<SkPaint::kSquare_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
6202028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark}
6212028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark
6222028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclarkvoid SkScan::HairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
6232028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark    hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::HairLineRgn);
6242028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark}
6252028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark
6262028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclarkvoid SkScan::AntiHairRoundPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* blitter) {
6272028d7ff744c36855ed36d602e3e050e9f18ec9fcaryclark    hair_path<SkPaint::kRound_Cap>(path, clip, blitter, SkScan::AntiHairLineRgn);
6288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
6298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
630761fb62b0eb174783316d2a8b933fba896ca6355reed@google.com///////////////////////////////////////////////////////////////////////////////
6318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
632761fb62b0eb174783316d2a8b933fba896ca6355reed@google.comvoid SkScan::FrameRect(const SkRect& r, const SkPoint& strokeSize,
633045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com                       const SkRasterClip& clip, SkBlitter* blitter) {
634761fb62b0eb174783316d2a8b933fba896ca6355reed@google.com    SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
6358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
636761fb62b0eb174783316d2a8b933fba896ca6355reed@google.com    if (strokeSize.fX < 0 || strokeSize.fY < 0) {
6378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return;
638761fb62b0eb174783316d2a8b933fba896ca6355reed@google.com    }
6398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
640761fb62b0eb174783316d2a8b933fba896ca6355reed@google.com    const SkScalar dx = strokeSize.fX;
641761fb62b0eb174783316d2a8b933fba896ca6355reed@google.com    const SkScalar dy = strokeSize.fY;
642761fb62b0eb174783316d2a8b933fba896ca6355reed@google.com    SkScalar rx = SkScalarHalf(dx);
643761fb62b0eb174783316d2a8b933fba896ca6355reed@google.com    SkScalar ry = SkScalarHalf(dy);
6448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkRect   outer, tmp;
6458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
646761fb62b0eb174783316d2a8b933fba896ca6355reed@google.com    outer.set(r.fLeft - rx, r.fTop - ry,
647761fb62b0eb174783316d2a8b933fba896ca6355reed@google.com                r.fRight + rx, r.fBottom + ry);
6488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
6498151103535bab8639dab2cb308b1b9c04651b632reed    if (r.width() <= dx || r.height() <= dy) {
6508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkScan::FillRect(outer, clip, blitter);
6518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return;
6528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
6538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
654761fb62b0eb174783316d2a8b933fba896ca6355reed@google.com    tmp.set(outer.fLeft, outer.fTop, outer.fRight, outer.fTop + dy);
6558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkScan::FillRect(tmp, clip, blitter);
656761fb62b0eb174783316d2a8b933fba896ca6355reed@google.com    tmp.fTop = outer.fBottom - dy;
6578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    tmp.fBottom = outer.fBottom;
6588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkScan::FillRect(tmp, clip, blitter);
6598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
660761fb62b0eb174783316d2a8b933fba896ca6355reed@google.com    tmp.set(outer.fLeft, outer.fTop + dy, outer.fLeft + dx, outer.fBottom - dy);
6618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkScan::FillRect(tmp, clip, blitter);
662761fb62b0eb174783316d2a8b933fba896ca6355reed@google.com    tmp.fLeft = outer.fRight - dx;
6638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    tmp.fRight = outer.fRight;
6648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkScan::FillRect(tmp, clip, blitter);
6658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
6668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
6675dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreedvoid SkScan::HairLine(const SkPoint pts[], int count, const SkRasterClip& clip,
6685dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed                      SkBlitter* blitter) {
669045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com    if (clip.isBW()) {
6705dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        HairLineRgn(pts, count, &clip.bwRgn(), blitter);
671045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com    } else {
67296fcdcc219d2a0d3579719b84b28bede76efba64halcanary        const SkRegion* clipRgn = nullptr;
6735dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed
674045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com        SkRect r;
6755dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        r.set(pts, count);
67611fa2247b747eb75e2f158dc7571d458ed6c0115reed        r.outset(SK_ScalarHalf, SK_ScalarHalf);
677045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com
678045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com        SkAAClipBlitterWrapper wrap;
679b07a94f1cba3976596ae1a7f23d8c2043ba353f3reed        if (!clip.quickContains(r.roundOut())) {
680045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com            wrap.init(clip, blitter);
681045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com            blitter = wrap.getBlitter();
682045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com            clipRgn = &wrap.getRgn();
683045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com        }
6845dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        HairLineRgn(pts, count, clipRgn, blitter);
685045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com    }
686045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com}
687045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com
6885dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreedvoid SkScan::AntiHairLine(const SkPoint pts[], int count, const SkRasterClip& clip,
6895dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed                          SkBlitter* blitter) {
690045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com    if (clip.isBW()) {
6915dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        AntiHairLineRgn(pts, count, &clip.bwRgn(), blitter);
692045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com    } else {
69396fcdcc219d2a0d3579719b84b28bede76efba64halcanary        const SkRegion* clipRgn = nullptr;
6945dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed
695045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com        SkRect r;
6965dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        r.set(pts, count);
697fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
698045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com        SkAAClipBlitterWrapper wrap;
69911fa2247b747eb75e2f158dc7571d458ed6c0115reed        if (!clip.quickContains(r.roundOut().makeOutset(1, 1))) {
700045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com            wrap.init(clip, blitter);
701045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com            blitter = wrap.getBlitter();
702045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com            clipRgn = &wrap.getRgn();
703045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com        }
7045dc6b7d1a8bc591d62366ff83c434ff74f3e10fcreed        AntiHairLineRgn(pts, count, clipRgn, blitter);
705045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com    }
706045e62d715f5ee9b03deb5af3c750f8318096179reed@google.com}
707