1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2011 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "include/private/SkTo.h"
9cb93a386Sopenharmony_ci#include "src/core/SkLineClipper.h"
10cb93a386Sopenharmony_ci
11cb93a386Sopenharmony_ci#include <utility>
12cb93a386Sopenharmony_ci
13cb93a386Sopenharmony_citemplate <typename T> T pin_unsorted(T value, T limit0, T limit1) {
14cb93a386Sopenharmony_ci    if (limit1 < limit0) {
15cb93a386Sopenharmony_ci        using std::swap;
16cb93a386Sopenharmony_ci        swap(limit0, limit1);
17cb93a386Sopenharmony_ci    }
18cb93a386Sopenharmony_ci    // now the limits are sorted
19cb93a386Sopenharmony_ci    SkASSERT(limit0 <= limit1);
20cb93a386Sopenharmony_ci
21cb93a386Sopenharmony_ci    if (value < limit0) {
22cb93a386Sopenharmony_ci        value = limit0;
23cb93a386Sopenharmony_ci    } else if (value > limit1) {
24cb93a386Sopenharmony_ci        value = limit1;
25cb93a386Sopenharmony_ci    }
26cb93a386Sopenharmony_ci    return value;
27cb93a386Sopenharmony_ci}
28cb93a386Sopenharmony_ci
29cb93a386Sopenharmony_ci// return X coordinate of intersection with horizontal line at Y
30cb93a386Sopenharmony_cistatic SkScalar sect_with_horizontal(const SkPoint src[2], SkScalar Y) {
31cb93a386Sopenharmony_ci    SkScalar dy = src[1].fY - src[0].fY;
32cb93a386Sopenharmony_ci    if (SkScalarNearlyZero(dy)) {
33cb93a386Sopenharmony_ci        return SkScalarAve(src[0].fX, src[1].fX);
34cb93a386Sopenharmony_ci    } else {
35cb93a386Sopenharmony_ci        // need the extra precision so we don't compute a value that exceeds
36cb93a386Sopenharmony_ci        // our original limits
37cb93a386Sopenharmony_ci        double X0 = src[0].fX;
38cb93a386Sopenharmony_ci        double Y0 = src[0].fY;
39cb93a386Sopenharmony_ci        double X1 = src[1].fX;
40cb93a386Sopenharmony_ci        double Y1 = src[1].fY;
41cb93a386Sopenharmony_ci        double result = X0 + ((double)Y - Y0) * (X1 - X0) / (Y1 - Y0);
42cb93a386Sopenharmony_ci
43cb93a386Sopenharmony_ci        // The computed X value might still exceed [X0..X1] due to quantum flux
44cb93a386Sopenharmony_ci        // when the doubles were added and subtracted, so we have to pin the
45cb93a386Sopenharmony_ci        // answer :(
46cb93a386Sopenharmony_ci        return (float)pin_unsorted(result, X0, X1);
47cb93a386Sopenharmony_ci    }
48cb93a386Sopenharmony_ci}
49cb93a386Sopenharmony_ci
50cb93a386Sopenharmony_ci// return Y coordinate of intersection with vertical line at X
51cb93a386Sopenharmony_cistatic SkScalar sect_with_vertical(const SkPoint src[2], SkScalar X) {
52cb93a386Sopenharmony_ci    SkScalar dx = src[1].fX - src[0].fX;
53cb93a386Sopenharmony_ci    if (SkScalarNearlyZero(dx)) {
54cb93a386Sopenharmony_ci        return SkScalarAve(src[0].fY, src[1].fY);
55cb93a386Sopenharmony_ci    } else {
56cb93a386Sopenharmony_ci        // need the extra precision so we don't compute a value that exceeds
57cb93a386Sopenharmony_ci        // our original limits
58cb93a386Sopenharmony_ci        double X0 = src[0].fX;
59cb93a386Sopenharmony_ci        double Y0 = src[0].fY;
60cb93a386Sopenharmony_ci        double X1 = src[1].fX;
61cb93a386Sopenharmony_ci        double Y1 = src[1].fY;
62cb93a386Sopenharmony_ci        double result = Y0 + ((double)X - X0) * (Y1 - Y0) / (X1 - X0);
63cb93a386Sopenharmony_ci        return (float)result;
64cb93a386Sopenharmony_ci    }
65cb93a386Sopenharmony_ci}
66cb93a386Sopenharmony_ci
67cb93a386Sopenharmony_cistatic SkScalar sect_clamp_with_vertical(const SkPoint src[2], SkScalar x) {
68cb93a386Sopenharmony_ci    SkScalar y = sect_with_vertical(src, x);
69cb93a386Sopenharmony_ci    // Our caller expects y to be between src[0].fY and src[1].fY (unsorted), but due to the
70cb93a386Sopenharmony_ci    // numerics of floats/doubles, we might have computed a value slightly outside of that,
71cb93a386Sopenharmony_ci    // so we have to manually clamp afterwards.
72cb93a386Sopenharmony_ci    // See skbug.com/7491
73cb93a386Sopenharmony_ci    return pin_unsorted(y, src[0].fY, src[1].fY);
74cb93a386Sopenharmony_ci}
75cb93a386Sopenharmony_ci
76cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////
77cb93a386Sopenharmony_ci
78cb93a386Sopenharmony_cistatic inline bool nestedLT(SkScalar a, SkScalar b, SkScalar dim) {
79cb93a386Sopenharmony_ci    return a <= b && (a < b || dim > 0);
80cb93a386Sopenharmony_ci}
81cb93a386Sopenharmony_ci
82cb93a386Sopenharmony_ci// returns true if outer contains inner, even if inner is empty.
83cb93a386Sopenharmony_ci// note: outer.contains(inner) always returns false if inner is empty.
84cb93a386Sopenharmony_cistatic inline bool containsNoEmptyCheck(const SkRect& outer,
85cb93a386Sopenharmony_ci                                        const SkRect& inner) {
86cb93a386Sopenharmony_ci    return  outer.fLeft <= inner.fLeft && outer.fTop <= inner.fTop &&
87cb93a386Sopenharmony_ci            outer.fRight >= inner.fRight && outer.fBottom >= inner.fBottom;
88cb93a386Sopenharmony_ci}
89cb93a386Sopenharmony_ci
90cb93a386Sopenharmony_cibool SkLineClipper::IntersectLine(const SkPoint src[2], const SkRect& clip,
91cb93a386Sopenharmony_ci                                  SkPoint dst[2]) {
92cb93a386Sopenharmony_ci    SkRect bounds;
93cb93a386Sopenharmony_ci
94cb93a386Sopenharmony_ci    bounds.set(src[0], src[1]);
95cb93a386Sopenharmony_ci    if (containsNoEmptyCheck(clip, bounds)) {
96cb93a386Sopenharmony_ci        if (src != dst) {
97cb93a386Sopenharmony_ci            memcpy(dst, src, 2 * sizeof(SkPoint));
98cb93a386Sopenharmony_ci        }
99cb93a386Sopenharmony_ci        return true;
100cb93a386Sopenharmony_ci    }
101cb93a386Sopenharmony_ci    // check for no overlap, and only permit coincident edges if the line
102cb93a386Sopenharmony_ci    // and the edge are colinear
103cb93a386Sopenharmony_ci    if (nestedLT(bounds.fRight, clip.fLeft, bounds.width()) ||
104cb93a386Sopenharmony_ci        nestedLT(clip.fRight, bounds.fLeft, bounds.width()) ||
105cb93a386Sopenharmony_ci        nestedLT(bounds.fBottom, clip.fTop, bounds.height()) ||
106cb93a386Sopenharmony_ci        nestedLT(clip.fBottom, bounds.fTop, bounds.height())) {
107cb93a386Sopenharmony_ci        return false;
108cb93a386Sopenharmony_ci    }
109cb93a386Sopenharmony_ci
110cb93a386Sopenharmony_ci    int index0, index1;
111cb93a386Sopenharmony_ci
112cb93a386Sopenharmony_ci    if (src[0].fY < src[1].fY) {
113cb93a386Sopenharmony_ci        index0 = 0;
114cb93a386Sopenharmony_ci        index1 = 1;
115cb93a386Sopenharmony_ci    } else {
116cb93a386Sopenharmony_ci        index0 = 1;
117cb93a386Sopenharmony_ci        index1 = 0;
118cb93a386Sopenharmony_ci    }
119cb93a386Sopenharmony_ci
120cb93a386Sopenharmony_ci    SkPoint tmp[2];
121cb93a386Sopenharmony_ci    memcpy(tmp, src, sizeof(tmp));
122cb93a386Sopenharmony_ci
123cb93a386Sopenharmony_ci    // now compute Y intersections
124cb93a386Sopenharmony_ci    if (tmp[index0].fY < clip.fTop) {
125cb93a386Sopenharmony_ci        tmp[index0].set(sect_with_horizontal(src, clip.fTop), clip.fTop);
126cb93a386Sopenharmony_ci    }
127cb93a386Sopenharmony_ci    if (tmp[index1].fY > clip.fBottom) {
128cb93a386Sopenharmony_ci        tmp[index1].set(sect_with_horizontal(src, clip.fBottom), clip.fBottom);
129cb93a386Sopenharmony_ci    }
130cb93a386Sopenharmony_ci
131cb93a386Sopenharmony_ci    if (tmp[0].fX < tmp[1].fX) {
132cb93a386Sopenharmony_ci        index0 = 0;
133cb93a386Sopenharmony_ci        index1 = 1;
134cb93a386Sopenharmony_ci    } else {
135cb93a386Sopenharmony_ci        index0 = 1;
136cb93a386Sopenharmony_ci        index1 = 0;
137cb93a386Sopenharmony_ci    }
138cb93a386Sopenharmony_ci
139cb93a386Sopenharmony_ci    // check for quick-reject in X again, now that we may have been chopped
140cb93a386Sopenharmony_ci    if ((tmp[index1].fX <= clip.fLeft || tmp[index0].fX >= clip.fRight)) {
141cb93a386Sopenharmony_ci        // usually we will return false, but we don't if the line is vertical and coincident
142cb93a386Sopenharmony_ci        // with the clip.
143cb93a386Sopenharmony_ci        if (tmp[0].fX != tmp[1].fX || tmp[0].fX < clip.fLeft || tmp[0].fX > clip.fRight) {
144cb93a386Sopenharmony_ci            return false;
145cb93a386Sopenharmony_ci        }
146cb93a386Sopenharmony_ci    }
147cb93a386Sopenharmony_ci
148cb93a386Sopenharmony_ci    if (tmp[index0].fX < clip.fLeft) {
149cb93a386Sopenharmony_ci        tmp[index0].set(clip.fLeft, sect_with_vertical(src, clip.fLeft));
150cb93a386Sopenharmony_ci    }
151cb93a386Sopenharmony_ci    if (tmp[index1].fX > clip.fRight) {
152cb93a386Sopenharmony_ci        tmp[index1].set(clip.fRight, sect_with_vertical(src, clip.fRight));
153cb93a386Sopenharmony_ci    }
154cb93a386Sopenharmony_ci#ifdef SK_DEBUG
155cb93a386Sopenharmony_ci    bounds.set(tmp[0], tmp[1]);
156cb93a386Sopenharmony_ci    SkASSERT(containsNoEmptyCheck(clip, bounds));
157cb93a386Sopenharmony_ci#endif
158cb93a386Sopenharmony_ci    memcpy(dst, tmp, sizeof(tmp));
159cb93a386Sopenharmony_ci    return true;
160cb93a386Sopenharmony_ci}
161cb93a386Sopenharmony_ci
162cb93a386Sopenharmony_ci#ifdef SK_DEBUG
163cb93a386Sopenharmony_ci// return value between the two limits, where the limits are either ascending
164cb93a386Sopenharmony_ci// or descending.
165cb93a386Sopenharmony_cistatic bool is_between_unsorted(SkScalar value,
166cb93a386Sopenharmony_ci                                SkScalar limit0, SkScalar limit1) {
167cb93a386Sopenharmony_ci    if (limit0 < limit1) {
168cb93a386Sopenharmony_ci        return limit0 <= value && value <= limit1;
169cb93a386Sopenharmony_ci    } else {
170cb93a386Sopenharmony_ci        return limit1 <= value && value <= limit0;
171cb93a386Sopenharmony_ci    }
172cb93a386Sopenharmony_ci}
173cb93a386Sopenharmony_ci#endif
174cb93a386Sopenharmony_ci
175cb93a386Sopenharmony_ciint SkLineClipper::ClipLine(const SkPoint pts[], const SkRect& clip, SkPoint lines[],
176cb93a386Sopenharmony_ci                            bool canCullToTheRight) {
177cb93a386Sopenharmony_ci    int index0, index1;
178cb93a386Sopenharmony_ci
179cb93a386Sopenharmony_ci    if (pts[0].fY < pts[1].fY) {
180cb93a386Sopenharmony_ci        index0 = 0;
181cb93a386Sopenharmony_ci        index1 = 1;
182cb93a386Sopenharmony_ci    } else {
183cb93a386Sopenharmony_ci        index0 = 1;
184cb93a386Sopenharmony_ci        index1 = 0;
185cb93a386Sopenharmony_ci    }
186cb93a386Sopenharmony_ci
187cb93a386Sopenharmony_ci    // Check if we're completely clipped out in Y (above or below
188cb93a386Sopenharmony_ci
189cb93a386Sopenharmony_ci    if (pts[index1].fY <= clip.fTop) {  // we're above the clip
190cb93a386Sopenharmony_ci        return 0;
191cb93a386Sopenharmony_ci    }
192cb93a386Sopenharmony_ci    if (pts[index0].fY >= clip.fBottom) {  // we're below the clip
193cb93a386Sopenharmony_ci        return 0;
194cb93a386Sopenharmony_ci    }
195cb93a386Sopenharmony_ci
196cb93a386Sopenharmony_ci    // Chop in Y to produce a single segment, stored in tmp[0..1]
197cb93a386Sopenharmony_ci
198cb93a386Sopenharmony_ci    SkPoint tmp[2];
199cb93a386Sopenharmony_ci    memcpy(tmp, pts, sizeof(tmp));
200cb93a386Sopenharmony_ci
201cb93a386Sopenharmony_ci    // now compute intersections
202cb93a386Sopenharmony_ci    if (pts[index0].fY < clip.fTop) {
203cb93a386Sopenharmony_ci        tmp[index0].set(sect_with_horizontal(pts, clip.fTop), clip.fTop);
204cb93a386Sopenharmony_ci        SkASSERT(is_between_unsorted(tmp[index0].fX, pts[0].fX, pts[1].fX));
205cb93a386Sopenharmony_ci    }
206cb93a386Sopenharmony_ci    if (tmp[index1].fY > clip.fBottom) {
207cb93a386Sopenharmony_ci        tmp[index1].set(sect_with_horizontal(pts, clip.fBottom), clip.fBottom);
208cb93a386Sopenharmony_ci        SkASSERT(is_between_unsorted(tmp[index1].fX, pts[0].fX, pts[1].fX));
209cb93a386Sopenharmony_ci    }
210cb93a386Sopenharmony_ci
211cb93a386Sopenharmony_ci    // Chop it into 1..3 segments that are wholly within the clip in X.
212cb93a386Sopenharmony_ci
213cb93a386Sopenharmony_ci    // temp storage for up to 3 segments
214cb93a386Sopenharmony_ci    SkPoint resultStorage[kMaxPoints];
215cb93a386Sopenharmony_ci    SkPoint* result;    // points to our results, either tmp or resultStorage
216cb93a386Sopenharmony_ci    int lineCount = 1;
217cb93a386Sopenharmony_ci    bool reverse;
218cb93a386Sopenharmony_ci
219cb93a386Sopenharmony_ci    if (pts[0].fX < pts[1].fX) {
220cb93a386Sopenharmony_ci        index0 = 0;
221cb93a386Sopenharmony_ci        index1 = 1;
222cb93a386Sopenharmony_ci        reverse = false;
223cb93a386Sopenharmony_ci    } else {
224cb93a386Sopenharmony_ci        index0 = 1;
225cb93a386Sopenharmony_ci        index1 = 0;
226cb93a386Sopenharmony_ci        reverse = true;
227cb93a386Sopenharmony_ci    }
228cb93a386Sopenharmony_ci
229cb93a386Sopenharmony_ci    if (tmp[index1].fX <= clip.fLeft) {  // wholly to the left
230cb93a386Sopenharmony_ci        tmp[0].fX = tmp[1].fX = clip.fLeft;
231cb93a386Sopenharmony_ci        result = tmp;
232cb93a386Sopenharmony_ci        reverse = false;
233cb93a386Sopenharmony_ci    } else if (tmp[index0].fX >= clip.fRight) {    // wholly to the right
234cb93a386Sopenharmony_ci        if (canCullToTheRight) {
235cb93a386Sopenharmony_ci            return 0;
236cb93a386Sopenharmony_ci        }
237cb93a386Sopenharmony_ci        tmp[0].fX = tmp[1].fX = clip.fRight;
238cb93a386Sopenharmony_ci        result = tmp;
239cb93a386Sopenharmony_ci        reverse = false;
240cb93a386Sopenharmony_ci    } else {
241cb93a386Sopenharmony_ci        result = resultStorage;
242cb93a386Sopenharmony_ci        SkPoint* r = result;
243cb93a386Sopenharmony_ci
244cb93a386Sopenharmony_ci        if (tmp[index0].fX < clip.fLeft) {
245cb93a386Sopenharmony_ci            r->set(clip.fLeft, tmp[index0].fY);
246cb93a386Sopenharmony_ci            r += 1;
247cb93a386Sopenharmony_ci            r->set(clip.fLeft, sect_clamp_with_vertical(tmp, clip.fLeft));
248cb93a386Sopenharmony_ci            SkASSERT(is_between_unsorted(r->fY, tmp[0].fY, tmp[1].fY));
249cb93a386Sopenharmony_ci        } else {
250cb93a386Sopenharmony_ci            *r = tmp[index0];
251cb93a386Sopenharmony_ci        }
252cb93a386Sopenharmony_ci        r += 1;
253cb93a386Sopenharmony_ci
254cb93a386Sopenharmony_ci        if (tmp[index1].fX > clip.fRight) {
255cb93a386Sopenharmony_ci            r->set(clip.fRight, sect_clamp_with_vertical(tmp, clip.fRight));
256cb93a386Sopenharmony_ci            SkASSERT(is_between_unsorted(r->fY, tmp[0].fY, tmp[1].fY));
257cb93a386Sopenharmony_ci            r += 1;
258cb93a386Sopenharmony_ci            r->set(clip.fRight, tmp[index1].fY);
259cb93a386Sopenharmony_ci        } else {
260cb93a386Sopenharmony_ci            *r = tmp[index1];
261cb93a386Sopenharmony_ci        }
262cb93a386Sopenharmony_ci
263cb93a386Sopenharmony_ci        lineCount = SkToInt(r - result);
264cb93a386Sopenharmony_ci    }
265cb93a386Sopenharmony_ci
266cb93a386Sopenharmony_ci    // Now copy the results into the caller's lines[] parameter
267cb93a386Sopenharmony_ci    if (reverse) {
268cb93a386Sopenharmony_ci        // copy the pts in reverse order to maintain winding order
269cb93a386Sopenharmony_ci        for (int i = 0; i <= lineCount; i++) {
270cb93a386Sopenharmony_ci            lines[lineCount - i] = result[i];
271cb93a386Sopenharmony_ci        }
272cb93a386Sopenharmony_ci    } else {
273cb93a386Sopenharmony_ci        memcpy(lines, result, (lineCount + 1) * sizeof(SkPoint));
274cb93a386Sopenharmony_ci    }
275cb93a386Sopenharmony_ci    return lineCount;
276cb93a386Sopenharmony_ci}
277