/* * Copyright 2020 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef skgpu_tessellate_WangsFormula_DEFINED #define skgpu_tessellate_WangsFormula_DEFINED #include "include/core/SkM44.h" #include "include/core/SkMatrix.h" #include "include/core/SkPoint.h" #include "include/core/SkTypes.h" #include "src/base/SkFloatBits.h" #include "src/base/SkUtils.h" #include "src/base/SkVx.h" #include <math.h> #include <algorithm> #include <cstdint> #include <limits> #define AI … // Wang's formula gives the minimum number of evenly spaced (in the parametric sense) line segments // that a bezier curve must be chopped into in order to guarantee all lines stay within a distance // of "1/precision" pixels from the true curve. Its definition for a bezier curve of degree "n" is // as follows: // // maxLength = max([length(p[i+2] - 2p[i+1] + p[i]) for (0 <= i <= n-2)]) // numParametricSegments = sqrt(maxLength * precision * n*(n - 1)/8) // // (Goldman, Ron. (2003). 5.6.3 Wang's Formula. "Pyramid Algorithms: A Dynamic Programming Approach // to Curves and Surfaces for Geometric Modeling". Morgan Kaufmann Publishers.) namespace skgpu::wangs_formula { // Returns the value by which to multiply length in Wang's formula. (See above.) template<int Degree> constexpr float length_term(float precision) { … } template<int Degree> constexpr float length_term_p2(float precision) { … } AI float root4(float x) { … } // For finite positive x > 1, return ceil(log2(x)) otherwise, return 0. // For +/- NaN return 0. // For +infinity return 128. // For -infinity return 0. // // nextlog2((-inf..1]) -> 0 // nextlog2((1..2]) -> 1 // nextlog2((2..4]) -> 2 // nextlog2((4..8]) -> 3 // ... AI int nextlog2(float x) { … } // Returns nextlog2(sqrt(x)): // // log2(sqrt(x)) == log2(x^(1/2)) == log2(x)/2 == log2(x)/log2(4) == log4(x) // AI int nextlog4(float x) { … } // Returns nextlog2(sqrt(sqrt(x))): // // log2(sqrt(sqrt(x))) == log2(x^(1/4)) == log2(x)/4 == log2(x)/log2(16) == log16(x) // AI int nextlog16(float x) { … } // Represents the upper-left 2x2 matrix of an affine transform for applying to vectors: // // VectorXform(p1 - p0) == M * float3(p1, 1) - M * float3(p0, 1) // class VectorXform { … }; // Returns Wang's formula, raised to the 4th power, specialized for a quadratic curve. AI float quadratic_p4(float precision, skvx::float2 p0, skvx::float2 p1, skvx::float2 p2, const VectorXform& vectorXform = VectorXform()) { … } AI float quadratic_p4(float precision, const SkPoint pts[], const VectorXform& vectorXform = VectorXform()) { … } // Returns Wang's formula specialized for a quadratic curve. AI float quadratic(float precision, const SkPoint pts[], const VectorXform& vectorXform = VectorXform()) { … } // Returns the log2 value of Wang's formula specialized for a quadratic curve, rounded up to the // next int. AI int quadratic_log2(float precision, const SkPoint pts[], const VectorXform& vectorXform = VectorXform()) { … } // Returns Wang's formula, raised to the 4th power, specialized for a cubic curve. AI float cubic_p4(float precision, skvx::float2 p0, skvx::float2 p1, skvx::float2 p2, skvx::float2 p3, const VectorXform& vectorXform = VectorXform()) { … } AI float cubic_p4(float precision, const SkPoint pts[], const VectorXform& vectorXform = VectorXform()) { … } // Returns Wang's formula specialized for a cubic curve. AI float cubic(float precision, const SkPoint pts[], const VectorXform& vectorXform = VectorXform()) { … } // Returns the log2 value of Wang's formula specialized for a cubic curve, rounded up to the next // int. AI int cubic_log2(float precision, const SkPoint pts[], const VectorXform& vectorXform = VectorXform()) { … } // Returns the maximum number of line segments a cubic with the given device-space bounding box size // would ever need to be divided into, raised to the 4th power. This is simply a special case of the // cubic formula where we maximize its value by placing control points on specific corners of the // bounding box. AI float worst_case_cubic_p4(float precision, float devWidth, float devHeight) { … } // Returns the maximum number of line segments a cubic with the given device-space bounding box size // would ever need to be divided into. AI float worst_case_cubic(float precision, float devWidth, float devHeight) { … } // Returns the maximum log2 number of line segments a cubic with the given device-space bounding box // size would ever need to be divided into. AI int worst_case_cubic_log2(float precision, float devWidth, float devHeight) { … } // Returns Wang's formula specialized for a conic curve, raised to the second power. // Input points should be in projected space. // // This is not actually due to Wang, but is an analogue from (Theorem 3, corollary 1): // J. Zheng, T. Sederberg. "Estimating Tessellation Parameter Intervals for // Rational Curves and Surfaces." ACM Transactions on Graphics 19(1). 2000. AI float conic_p2(float precision, skvx::float2 p0, skvx::float2 p1, skvx::float2 p2, float w, const VectorXform& vectorXform = VectorXform()) { … } AI float conic_p2(float precision, const SkPoint pts[], float w, const VectorXform& vectorXform = VectorXform()) { … } // Returns the value of Wang's formula specialized for a conic curve. AI float conic(float tolerance, const SkPoint pts[], float w, const VectorXform& vectorXform = VectorXform()) { … } // Returns the log2 value of Wang's formula specialized for a conic curve, rounded up to the next // int. AI int conic_log2(float tolerance, const SkPoint pts[], float w, const VectorXform& vectorXform = VectorXform()) { … } } // namespace skgpu::wangs_formula #undef AI #endif // skgpu_tessellate_WangsFormula_DEFINED