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