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