blob: f756f6e785119ca1c150426f68e39787e74c5020 [file] [log] [blame]
Chris Dalton419a94d2017-08-28 10:24:22 -06001/*
2 * Copyright 2017 Google Inc.
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 "GrCCPRGeometry.h"
9
10#include "GrTypes.h"
11#include "SkPoint.h"
12#include "SkNx.h"
13#include <algorithm>
14#include <cmath>
15#include <cstdlib>
16
17// We convert between SkPoint and Sk2f freely throughout this file.
18GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT);
19GR_STATIC_ASSERT(2 * sizeof(float) == sizeof(SkPoint));
20GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX));
21
22static inline Sk2f normalize(const Sk2f& n) {
23 Sk2f nn = n*n;
24 return n * (nn + SkNx_shuffle<1,0>(nn)).rsqrt();
25}
26
27static inline float dot(const Sk2f& a, const Sk2f& b) {
28 float product[2];
29 (a * b).store(product);
30 return product[0] + product[1];
31}
32
33// Returns whether the (convex) curve segment is monotonic with respect to [endPt - startPt].
34static inline bool is_convex_curve_monotonic(const Sk2f& startPt, const Sk2f& startTan,
35 const Sk2f& endPt, const Sk2f& endTan) {
36 Sk2f v = endPt - startPt;
37 float dot0 = dot(startTan, v);
38 float dot1 = dot(endTan, v);
39
40 // A small, negative tolerance handles floating-point error in the case when one tangent
41 // approaches 0 length, meaning the (convex) curve segment is effectively a flat line.
42 float tolerance = -std::max(std::abs(dot0), std::abs(dot1)) * SK_ScalarNearlyZero;
43 return dot0 >= tolerance && dot1 >= tolerance;
44}
45
46static inline Sk2f lerp(const Sk2f& a, const Sk2f& b, const Sk2f& t) {
47 return SkNx_fma(t, b - a, a);
48}
49
50bool GrCCPRChopMonotonicQuadratics(const SkPoint& startPt, const SkPoint& controlPt,
51 const SkPoint& endPt, SkPoint dst[5]) {
52 Sk2f p0 = Sk2f::Load(&startPt);
53 Sk2f p1 = Sk2f::Load(&controlPt);
54 Sk2f p2 = Sk2f::Load(&endPt);
55
56 Sk2f tan0 = p1 - p0;
57 Sk2f tan1 = p2 - p1;
58 // This should almost always be this case for well-behaved curves in the real world.
59 if (is_convex_curve_monotonic(p0, tan0, p2, tan1)) {
60 return false;
61 }
62
63 // Chop the curve into two segments with equal curvature. To do this we find the T value whose
64 // tangent is perpendicular to the vector that bisects tan0 and -tan1.
65 Sk2f n = normalize(tan0) - normalize(tan1);
66
67 // This tangent can be found where (dQ(t) dot n) = 0:
68 //
69 // 0 = (dQ(t) dot n) = | 2*t 1 | * | p0 - 2*p1 + p2 | * | n |
70 // | -2*p0 + 2*p1 | | . |
71 //
72 // = | 2*t 1 | * | tan1 - tan0 | * | n |
73 // | 2*tan0 | | . |
74 //
75 // = 2*t * ((tan1 - tan0) dot n) + (2*tan0 dot n)
76 //
77 // t = (tan0 dot n) / ((tan0 - tan1) dot n)
78 Sk2f dQ1n = (tan0 - tan1) * n;
79 Sk2f dQ0n = tan0 * n;
80 Sk2f t = (dQ0n + SkNx_shuffle<1,0>(dQ0n)) / (dQ1n + SkNx_shuffle<1,0>(dQ1n));
81 t = Sk2f::Min(Sk2f::Max(t, 0), 1); // Clamp for FP error.
82
83 Sk2f p01 = SkNx_fma(t, tan0, p0);
84 Sk2f p12 = SkNx_fma(t, tan1, p1);
85 Sk2f p012 = lerp(p01, p12, t);
86
87 p0.store(&dst[0]);
88 p01.store(&dst[1]);
89 p012.store(&dst[2]);
90 p12.store(&dst[3]);
91 p2.store(&dst[4]);
92
93 return true;
94}