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