1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/core/SkScan_Hairline.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,417 @@ 1.4 + 1.5 +/* 1.6 + * Copyright 2006 The Android Open Source Project 1.7 + * 1.8 + * Use of this source code is governed by a BSD-style license that can be 1.9 + * found in the LICENSE file. 1.10 + */ 1.11 + 1.12 + 1.13 +#include "SkScan.h" 1.14 +#include "SkBlitter.h" 1.15 +#include "SkRasterClip.h" 1.16 +#include "SkFDot6.h" 1.17 +#include "SkLineClipper.h" 1.18 + 1.19 +static void horiline(int x, int stopx, SkFixed fy, SkFixed dy, 1.20 + SkBlitter* blitter) { 1.21 + SkASSERT(x < stopx); 1.22 + 1.23 + do { 1.24 + blitter->blitH(x, fy >> 16, 1); 1.25 + fy += dy; 1.26 + } while (++x < stopx); 1.27 +} 1.28 + 1.29 +static void vertline(int y, int stopy, SkFixed fx, SkFixed dx, 1.30 + SkBlitter* blitter) { 1.31 + SkASSERT(y < stopy); 1.32 + 1.33 + do { 1.34 + blitter->blitH(fx >> 16, y, 1); 1.35 + fx += dx; 1.36 + } while (++y < stopy); 1.37 +} 1.38 + 1.39 +#ifdef SK_DEBUG 1.40 +static bool canConvertFDot6ToFixed(SkFDot6 x) { 1.41 + const int maxDot6 = SK_MaxS32 >> (16 - 6); 1.42 + return SkAbs32(x) <= maxDot6; 1.43 +} 1.44 +#endif 1.45 + 1.46 +void SkScan::HairLineRgn(const SkPoint& pt0, const SkPoint& pt1, 1.47 + const SkRegion* clip, SkBlitter* blitter) { 1.48 + SkBlitterClipper clipper; 1.49 + SkRect r; 1.50 + SkIRect clipR, ptsR; 1.51 + SkPoint pts[2] = { pt0, pt1 }; 1.52 + 1.53 + // We have to pre-clip the line to fit in a SkFixed, so we just chop 1.54 + // the line. TODO find a way to actually draw beyond that range. 1.55 + { 1.56 + SkRect fixedBounds; 1.57 + const SkScalar max = SkIntToScalar(32767); 1.58 + fixedBounds.set(-max, -max, max, max); 1.59 + if (!SkLineClipper::IntersectLine(pts, fixedBounds, pts)) { 1.60 + return; 1.61 + } 1.62 + } 1.63 + 1.64 + if (clip) { 1.65 + // Perform a clip in scalar space, so we catch huge values which might 1.66 + // be missed after we convert to SkFDot6 (overflow) 1.67 + r.set(clip->getBounds()); 1.68 + if (!SkLineClipper::IntersectLine(pts, r, pts)) { 1.69 + return; 1.70 + } 1.71 + } 1.72 + 1.73 + SkFDot6 x0 = SkScalarToFDot6(pts[0].fX); 1.74 + SkFDot6 y0 = SkScalarToFDot6(pts[0].fY); 1.75 + SkFDot6 x1 = SkScalarToFDot6(pts[1].fX); 1.76 + SkFDot6 y1 = SkScalarToFDot6(pts[1].fY); 1.77 + 1.78 + SkASSERT(canConvertFDot6ToFixed(x0)); 1.79 + SkASSERT(canConvertFDot6ToFixed(y0)); 1.80 + SkASSERT(canConvertFDot6ToFixed(x1)); 1.81 + SkASSERT(canConvertFDot6ToFixed(y1)); 1.82 + 1.83 + if (clip) { 1.84 + // now perform clipping again, as the rounding to dot6 can wiggle us 1.85 + // our rects are really dot6 rects, but since we've already used 1.86 + // lineclipper, we know they will fit in 32bits (26.6) 1.87 + const SkIRect& bounds = clip->getBounds(); 1.88 + 1.89 + clipR.set(SkIntToFDot6(bounds.fLeft), SkIntToFDot6(bounds.fTop), 1.90 + SkIntToFDot6(bounds.fRight), SkIntToFDot6(bounds.fBottom)); 1.91 + ptsR.set(x0, y0, x1, y1); 1.92 + ptsR.sort(); 1.93 + 1.94 + // outset the right and bottom, to account for how hairlines are 1.95 + // actually drawn, which may hit the pixel to the right or below of 1.96 + // the coordinate 1.97 + ptsR.fRight += SK_FDot6One; 1.98 + ptsR.fBottom += SK_FDot6One; 1.99 + 1.100 + if (!SkIRect::Intersects(ptsR, clipR)) { 1.101 + return; 1.102 + } 1.103 + if (clip->isRect() && clipR.contains(ptsR)) { 1.104 + clip = NULL; 1.105 + } else { 1.106 + blitter = clipper.apply(blitter, clip); 1.107 + } 1.108 + } 1.109 + 1.110 + SkFDot6 dx = x1 - x0; 1.111 + SkFDot6 dy = y1 - y0; 1.112 + 1.113 + if (SkAbs32(dx) > SkAbs32(dy)) { // mostly horizontal 1.114 + if (x0 > x1) { // we want to go left-to-right 1.115 + SkTSwap<SkFDot6>(x0, x1); 1.116 + SkTSwap<SkFDot6>(y0, y1); 1.117 + } 1.118 + int ix0 = SkFDot6Round(x0); 1.119 + int ix1 = SkFDot6Round(x1); 1.120 + if (ix0 == ix1) {// too short to draw 1.121 + return; 1.122 + } 1.123 + 1.124 + SkFixed slope = SkFixedDiv(dy, dx); 1.125 + SkFixed startY = SkFDot6ToFixed(y0) + (slope * ((32 - x0) & 63) >> 6); 1.126 + 1.127 + horiline(ix0, ix1, startY, slope, blitter); 1.128 + } else { // mostly vertical 1.129 + if (y0 > y1) { // we want to go top-to-bottom 1.130 + SkTSwap<SkFDot6>(x0, x1); 1.131 + SkTSwap<SkFDot6>(y0, y1); 1.132 + } 1.133 + int iy0 = SkFDot6Round(y0); 1.134 + int iy1 = SkFDot6Round(y1); 1.135 + if (iy0 == iy1) { // too short to draw 1.136 + return; 1.137 + } 1.138 + 1.139 + SkFixed slope = SkFixedDiv(dx, dy); 1.140 + SkFixed startX = SkFDot6ToFixed(x0) + (slope * ((32 - y0) & 63) >> 6); 1.141 + 1.142 + vertline(iy0, iy1, startX, slope, blitter); 1.143 + } 1.144 +} 1.145 + 1.146 +// we don't just draw 4 lines, 'cause that can leave a gap in the bottom-right 1.147 +// and double-hit the top-left. 1.148 +// TODO: handle huge coordinates on rect (before calling SkScalarToFixed) 1.149 +void SkScan::HairRect(const SkRect& rect, const SkRasterClip& clip, 1.150 + SkBlitter* blitter) { 1.151 + SkAAClipBlitterWrapper wrapper; 1.152 + SkBlitterClipper clipper; 1.153 + SkIRect r; 1.154 + 1.155 + r.set(SkScalarToFixed(rect.fLeft) >> 16, 1.156 + SkScalarToFixed(rect.fTop) >> 16, 1.157 + (SkScalarToFixed(rect.fRight) >> 16) + 1, 1.158 + (SkScalarToFixed(rect.fBottom) >> 16) + 1); 1.159 + 1.160 + if (clip.quickReject(r)) { 1.161 + return; 1.162 + } 1.163 + if (!clip.quickContains(r)) { 1.164 + const SkRegion* clipRgn; 1.165 + if (clip.isBW()) { 1.166 + clipRgn = &clip.bwRgn(); 1.167 + } else { 1.168 + wrapper.init(clip, blitter); 1.169 + clipRgn = &wrapper.getRgn(); 1.170 + blitter = wrapper.getBlitter(); 1.171 + } 1.172 + blitter = clipper.apply(blitter, clipRgn); 1.173 + } 1.174 + 1.175 + int width = r.width(); 1.176 + int height = r.height(); 1.177 + 1.178 + if ((width | height) == 0) { 1.179 + return; 1.180 + } 1.181 + if (width <= 2 || height <= 2) { 1.182 + blitter->blitRect(r.fLeft, r.fTop, width, height); 1.183 + return; 1.184 + } 1.185 + // if we get here, we know we have 4 segments to draw 1.186 + blitter->blitH(r.fLeft, r.fTop, width); // top 1.187 + blitter->blitRect(r.fLeft, r.fTop + 1, 1, height - 2); // left 1.188 + blitter->blitRect(r.fRight - 1, r.fTop + 1, 1, height - 2); // right 1.189 + blitter->blitH(r.fLeft, r.fBottom - 1, width); // bottom 1.190 +} 1.191 + 1.192 +/////////////////////////////////////////////////////////////////////////////// 1.193 + 1.194 +#include "SkPath.h" 1.195 +#include "SkGeometry.h" 1.196 + 1.197 +static int compute_int_quad_dist(const SkPoint pts[3]) { 1.198 + // compute the vector between the control point ([1]) and the middle of the 1.199 + // line connecting the start and end ([0] and [2]) 1.200 + SkScalar dx = SkScalarHalf(pts[0].fX + pts[2].fX) - pts[1].fX; 1.201 + SkScalar dy = SkScalarHalf(pts[0].fY + pts[2].fY) - pts[1].fY; 1.202 + // we want everyone to be positive 1.203 + dx = SkScalarAbs(dx); 1.204 + dy = SkScalarAbs(dy); 1.205 + // convert to whole pixel values (use ceiling to be conservative) 1.206 + int idx = SkScalarCeilToInt(dx); 1.207 + int idy = SkScalarCeilToInt(dy); 1.208 + // use the cheap approx for distance 1.209 + if (idx > idy) { 1.210 + return idx + (idy >> 1); 1.211 + } else { 1.212 + return idy + (idx >> 1); 1.213 + } 1.214 +} 1.215 + 1.216 +typedef void (*LineProc)(const SkPoint&, const SkPoint&, const SkRegion*, 1.217 + SkBlitter*); 1.218 + 1.219 +static void hairquad(const SkPoint pts[3], const SkRegion* clip, 1.220 + SkBlitter* blitter, int level, LineProc lineproc) { 1.221 + if (level > 0) { 1.222 + SkPoint tmp[5]; 1.223 + 1.224 + SkChopQuadAtHalf(pts, tmp); 1.225 + hairquad(tmp, clip, blitter, level - 1, lineproc); 1.226 + hairquad(&tmp[2], clip, blitter, level - 1, lineproc); 1.227 + } else { 1.228 + lineproc(pts[0], pts[2], clip, blitter); 1.229 + } 1.230 +} 1.231 + 1.232 +static void haircubic(const SkPoint pts[4], const SkRegion* clip, 1.233 + SkBlitter* blitter, int level, LineProc lineproc) { 1.234 + if (level > 0) { 1.235 + SkPoint tmp[7]; 1.236 + 1.237 + SkChopCubicAt(pts, tmp, SK_Scalar1/2); 1.238 + haircubic(tmp, clip, blitter, level - 1, lineproc); 1.239 + haircubic(&tmp[3], clip, blitter, level - 1, lineproc); 1.240 + } else { 1.241 + lineproc(pts[0], pts[3], clip, blitter); 1.242 + } 1.243 +} 1.244 + 1.245 +#define kMaxCubicSubdivideLevel 6 1.246 +#define kMaxQuadSubdivideLevel 5 1.247 + 1.248 +static int compute_quad_level(const SkPoint pts[3]) { 1.249 + int d = compute_int_quad_dist(pts); 1.250 + /* quadratics approach the line connecting their start and end points 1.251 + 4x closer with each subdivision, so we compute the number of 1.252 + subdivisions to be the minimum need to get that distance to be less 1.253 + than a pixel. 1.254 + */ 1.255 + int level = (33 - SkCLZ(d)) >> 1; 1.256 + // sanity check on level (from the previous version) 1.257 + if (level > kMaxQuadSubdivideLevel) { 1.258 + level = kMaxQuadSubdivideLevel; 1.259 + } 1.260 + return level; 1.261 +} 1.262 + 1.263 +static void hair_path(const SkPath& path, const SkRasterClip& rclip, 1.264 + SkBlitter* blitter, LineProc lineproc) { 1.265 + if (path.isEmpty()) { 1.266 + return; 1.267 + } 1.268 + 1.269 + SkAAClipBlitterWrapper wrap; 1.270 + const SkRegion* clip = NULL; 1.271 + 1.272 + { 1.273 + SkIRect ibounds; 1.274 + path.getBounds().roundOut(&ibounds); 1.275 + ibounds.inset(-1, -1); 1.276 + 1.277 + if (rclip.quickReject(ibounds)) { 1.278 + return; 1.279 + } 1.280 + if (!rclip.quickContains(ibounds)) { 1.281 + if (rclip.isBW()) { 1.282 + clip = &rclip.bwRgn(); 1.283 + } else { 1.284 + wrap.init(rclip, blitter); 1.285 + blitter = wrap.getBlitter(); 1.286 + clip = &wrap.getRgn(); 1.287 + } 1.288 + } 1.289 + } 1.290 + 1.291 + SkPath::Iter iter(path, false); 1.292 + SkPoint pts[4]; 1.293 + SkPath::Verb verb; 1.294 + SkAutoConicToQuads converter; 1.295 + 1.296 + while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) { 1.297 + switch (verb) { 1.298 + case SkPath::kMove_Verb: 1.299 + break; 1.300 + case SkPath::kLine_Verb: 1.301 + lineproc(pts[0], pts[1], clip, blitter); 1.302 + break; 1.303 + case SkPath::kQuad_Verb: 1.304 + hairquad(pts, clip, blitter, compute_quad_level(pts), lineproc); 1.305 + break; 1.306 + case SkPath::kConic_Verb: { 1.307 + // how close should the quads be to the original conic? 1.308 + const SkScalar tol = SK_Scalar1 / 4; 1.309 + const SkPoint* quadPts = converter.computeQuads(pts, 1.310 + iter.conicWeight(), tol); 1.311 + for (int i = 0; i < converter.countQuads(); ++i) { 1.312 + int level = compute_quad_level(quadPts); 1.313 + hairquad(quadPts, clip, blitter, level, lineproc); 1.314 + quadPts += 2; 1.315 + } 1.316 + break; 1.317 + } 1.318 + case SkPath::kCubic_Verb: 1.319 + haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, lineproc); 1.320 + break; 1.321 + case SkPath::kClose_Verb: 1.322 + break; 1.323 + case SkPath::kDone_Verb: 1.324 + break; 1.325 + } 1.326 + } 1.327 +} 1.328 + 1.329 +void SkScan::HairPath(const SkPath& path, const SkRasterClip& clip, 1.330 + SkBlitter* blitter) { 1.331 + hair_path(path, clip, blitter, SkScan::HairLineRgn); 1.332 +} 1.333 + 1.334 +void SkScan::AntiHairPath(const SkPath& path, const SkRasterClip& clip, 1.335 + SkBlitter* blitter) { 1.336 + hair_path(path, clip, blitter, SkScan::AntiHairLineRgn); 1.337 +} 1.338 + 1.339 +/////////////////////////////////////////////////////////////////////////////// 1.340 + 1.341 +void SkScan::FrameRect(const SkRect& r, const SkPoint& strokeSize, 1.342 + const SkRasterClip& clip, SkBlitter* blitter) { 1.343 + SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0); 1.344 + 1.345 + if (strokeSize.fX < 0 || strokeSize.fY < 0) { 1.346 + return; 1.347 + } 1.348 + 1.349 + const SkScalar dx = strokeSize.fX; 1.350 + const SkScalar dy = strokeSize.fY; 1.351 + SkScalar rx = SkScalarHalf(dx); 1.352 + SkScalar ry = SkScalarHalf(dy); 1.353 + SkRect outer, tmp; 1.354 + 1.355 + outer.set(r.fLeft - rx, r.fTop - ry, 1.356 + r.fRight + rx, r.fBottom + ry); 1.357 + 1.358 + if (r.width() <= dx || r.height() <= dx) { 1.359 + SkScan::FillRect(outer, clip, blitter); 1.360 + return; 1.361 + } 1.362 + 1.363 + tmp.set(outer.fLeft, outer.fTop, outer.fRight, outer.fTop + dy); 1.364 + SkScan::FillRect(tmp, clip, blitter); 1.365 + tmp.fTop = outer.fBottom - dy; 1.366 + tmp.fBottom = outer.fBottom; 1.367 + SkScan::FillRect(tmp, clip, blitter); 1.368 + 1.369 + tmp.set(outer.fLeft, outer.fTop + dy, outer.fLeft + dx, outer.fBottom - dy); 1.370 + SkScan::FillRect(tmp, clip, blitter); 1.371 + tmp.fLeft = outer.fRight - dx; 1.372 + tmp.fRight = outer.fRight; 1.373 + SkScan::FillRect(tmp, clip, blitter); 1.374 +} 1.375 + 1.376 +void SkScan::HairLine(const SkPoint& p0, const SkPoint& p1, 1.377 + const SkRasterClip& clip, SkBlitter* blitter) { 1.378 + if (clip.isBW()) { 1.379 + HairLineRgn(p0, p1, &clip.bwRgn(), blitter); 1.380 + } else { 1.381 + const SkRegion* clipRgn = NULL; 1.382 + SkRect r; 1.383 + SkIRect ir; 1.384 + r.set(p0.fX, p0.fY, p1.fX, p1.fY); 1.385 + r.sort(); 1.386 + r.inset(-SK_ScalarHalf, -SK_ScalarHalf); 1.387 + r.roundOut(&ir); 1.388 + 1.389 + SkAAClipBlitterWrapper wrap; 1.390 + if (!clip.quickContains(ir)) { 1.391 + wrap.init(clip, blitter); 1.392 + blitter = wrap.getBlitter(); 1.393 + clipRgn = &wrap.getRgn(); 1.394 + } 1.395 + HairLineRgn(p0, p1, clipRgn, blitter); 1.396 + } 1.397 +} 1.398 + 1.399 +void SkScan::AntiHairLine(const SkPoint& p0, const SkPoint& p1, 1.400 + const SkRasterClip& clip, SkBlitter* blitter) { 1.401 + if (clip.isBW()) { 1.402 + AntiHairLineRgn(p0, p1, &clip.bwRgn(), blitter); 1.403 + } else { 1.404 + const SkRegion* clipRgn = NULL; 1.405 + SkRect r; 1.406 + SkIRect ir; 1.407 + r.set(p0.fX, p0.fY, p1.fX, p1.fY); 1.408 + r.sort(); 1.409 + r.roundOut(&ir); 1.410 + ir.inset(-1, -1); 1.411 + 1.412 + SkAAClipBlitterWrapper wrap; 1.413 + if (!clip.quickContains(ir)) { 1.414 + wrap.init(clip, blitter); 1.415 + blitter = wrap.getBlitter(); 1.416 + clipRgn = &wrap.getRgn(); 1.417 + } 1.418 + AntiHairLineRgn(p0, p1, clipRgn, blitter); 1.419 + } 1.420 +}