xref: /third_party/skia/src/core/SkQuadClipper.cpp (revision cb93a386)
1/*
2 * Copyright 2009 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "src/core/SkGeometry.h"
9#include "src/core/SkQuadClipper.h"
10
11#include <utility>
12
13SkQuadClipper::SkQuadClipper() {
14    fClip.setEmpty();
15}
16
17void SkQuadClipper::setClip(const SkIRect& clip) {
18    // conver to scalars, since that's where we'll see the points
19    fClip.set(clip);
20}
21
22///////////////////////////////////////////////////////////////////////////////
23
24static bool chopMonoQuadAt(SkScalar c0, SkScalar c1, SkScalar c2,
25                           SkScalar target, SkScalar* t) {
26    /* Solve F(t) = y where F(t) := [0](1-t)^2 + 2[1]t(1-t) + [2]t^2
27     *  We solve for t, using quadratic equation, hence we have to rearrange
28     * our cooefficents to look like At^2 + Bt + C
29     */
30    SkScalar A = c0 - c1 - c1 + c2;
31    SkScalar B = 2*(c1 - c0);
32    SkScalar C = c0 - target;
33
34    SkScalar roots[2];  // we only expect one, but make room for 2 for safety
35    int count = SkFindUnitQuadRoots(A, B, C, roots);
36    if (count) {
37        *t = roots[0];
38        return true;
39    }
40    return false;
41}
42
43static bool chopMonoQuadAtY(SkPoint pts[3], SkScalar y, SkScalar* t) {
44    return chopMonoQuadAt(pts[0].fY, pts[1].fY, pts[2].fY, y, t);
45}
46
47///////////////////////////////////////////////////////////////////////////////
48
49/*  If we somehow returned the fact that we had to flip the pts in Y, we could
50 communicate that to setQuadratic, and then avoid having to flip it back
51 here (only to have setQuadratic do the flip again)
52 */
53bool SkQuadClipper::clipQuad(const SkPoint srcPts[3], SkPoint dst[3]) {
54    bool reverse;
55
56    // we need the data to be monotonically increasing in Y
57    if (srcPts[0].fY > srcPts[2].fY) {
58        dst[0] = srcPts[2];
59        dst[1] = srcPts[1];
60        dst[2] = srcPts[0];
61        reverse = true;
62    } else {
63        memcpy(dst, srcPts, 3 * sizeof(SkPoint));
64        reverse = false;
65    }
66
67    // are we completely above or below
68    const SkScalar ctop = fClip.fTop;
69    const SkScalar cbot = fClip.fBottom;
70    if (dst[2].fY <= ctop || dst[0].fY >= cbot) {
71        return false;
72    }
73
74    SkScalar t;
75    SkPoint tmp[5]; // for SkChopQuadAt
76
77    // are we partially above
78    if (dst[0].fY < ctop) {
79        if (chopMonoQuadAtY(dst, ctop, &t)) {
80            // take the 2nd chopped quad
81            SkChopQuadAt(dst, tmp, t);
82            dst[0] = tmp[2];
83            dst[1] = tmp[3];
84        } else {
85            // if chopMonoQuadAtY failed, then we may have hit inexact numerics
86            // so we just clamp against the top
87            for (int i = 0; i < 3; i++) {
88                if (dst[i].fY < ctop) {
89                    dst[i].fY = ctop;
90                }
91            }
92        }
93    }
94
95    // are we partially below
96    if (dst[2].fY > cbot) {
97        if (chopMonoQuadAtY(dst, cbot, &t)) {
98            SkChopQuadAt(dst, tmp, t);
99            dst[1] = tmp[1];
100            dst[2] = tmp[2];
101        } else {
102            // if chopMonoQuadAtY failed, then we may have hit inexact numerics
103            // so we just clamp against the bottom
104            for (int i = 0; i < 3; i++) {
105                if (dst[i].fY > cbot) {
106                    dst[i].fY = cbot;
107                }
108            }
109        }
110    }
111
112    if (reverse) {
113        using std::swap;
114        swap(dst[0], dst[2]);
115    }
116    return true;
117}
118