gfx/skia/trunk/src/core/SkScan_Hairline.cpp

changeset 0
6474c204b198
     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 +}

mercurial