Chris Dalton | 419a94d | 2017-08-28 10:24:22 -0600 | [diff] [blame^] | 1 | /* |
| 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. |
| 18 | GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT); |
| 19 | GR_STATIC_ASSERT(2 * sizeof(float) == sizeof(SkPoint)); |
| 20 | GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX)); |
| 21 | |
| 22 | static inline Sk2f normalize(const Sk2f& n) { |
| 23 | Sk2f nn = n*n; |
| 24 | return n * (nn + SkNx_shuffle<1,0>(nn)).rsqrt(); |
| 25 | } |
| 26 | |
| 27 | static 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]. |
| 34 | static 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 | |
| 46 | static inline Sk2f lerp(const Sk2f& a, const Sk2f& b, const Sk2f& t) { |
| 47 | return SkNx_fma(t, b - a, a); |
| 48 | } |
| 49 | |
| 50 | bool 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 | } |