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