Implement asin and acos primitives in Raster Pipeline.
These follow the SkVM implementation very closely.
Change-Id: I0fb39f8c443e01de2140f8321c00375326be57bc
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/658019
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
diff --git a/src/core/SkVM.cpp b/src/core/SkVM.cpp
index 9c33e29..b063f55 100644
--- a/src/core/SkVM.cpp
+++ b/src/core/SkVM.cpp
@@ -909,16 +909,16 @@
return x;
}
- // http://mathforum.org/library/drmath/view/54137.html
- // referencing Handbook of Mathematical Functions,
- // by Milton Abramowitz and Irene Stegun
- F32 Builder::approx_asin(F32 x) {
+ // http://mathforum.org/library/drmath/view/54137.html
+ // referencing Handbook of Mathematical Functions,
+ // by Milton Abramowitz and Irene Stegun
+ F32 Builder::approx_asin(F32 x) {
I32 neg = (x < 0.0f);
x = select(neg, -x, x);
x = SK_ScalarPI/2 - sqrt(1-x) * poly(x, -0.0187293f, 0.0742610f, -0.2121144f, 1.5707288f);
x = select(neg, -x, x);
return x;
- }
+ }
/* Use 4th order polynomial approximation from https://arachnoid.com/polysolve/
* with 129 values of x,atan(x) for x:[0...1]
diff --git a/src/opts/SkRasterPipeline_opts.h b/src/opts/SkRasterPipeline_opts.h
index 33999c3..31cca2c 100644
--- a/src/opts/SkRasterPipeline_opts.h
+++ b/src/opts/SkRasterPipeline_opts.h
@@ -1561,6 +1561,23 @@
return x;
}
+
+// Handbook of Mathematical Functions, by Milton Abramowitz and Irene Stegun:
+// https://books.google.com/books/content?id=ZboM5tOFWtsC&pg=PA81&img=1&zoom=3&hl=en&bul=1&sig=ACfU3U2M75tG_iGVOS92eQspr14LTq02Nw&ci=0%2C15%2C999%2C1279&edge=0
+// http://screen/8YGJxUGFQ49bVX6
+SI F asin_(F x) {
+ I32 neg = (x < 0.0f);
+ x = if_then_else(neg, -x, x);
+ F poly = x * (x * (x * -0.0187293f + 0.0742610f) - 0.2121144f) + 1.5707288f;
+ x = SK_ScalarPI/2 - sqrt_(1 - x) * poly;
+ x = if_then_else(neg, -x, x);
+ return x;
+}
+
+SI F acos_(F x) {
+ return SK_ScalarPI/2 - asin_(x);
+}
+
/* Use identity atan(x) = pi/2 - atan(1/x) for x > 1
By swapping y,x to ensure the ratio is <= 1, we can safely call atan_unit()
which avoids a 2nd divide instruction if we had instead called atan().
diff --git a/tests/SkRasterPipelineOptsTest.cpp b/tests/SkRasterPipelineOptsTest.cpp
index 4bcc260..d4e4cea 100644
--- a/tests/SkRasterPipelineOptsTest.cpp
+++ b/tests/SkRasterPipelineOptsTest.cpp
@@ -122,6 +122,32 @@
}
}
+DEF_TEST(SkRasterPipelineOpts_Asin, r) {
+ using F = SK_OPTS_NS::F;
+
+ constexpr float kTolerance = 0.00175f;
+ for (float x = -1; x <= 1; x += 1.0f/64) {
+ F result = SK_OPTS_NS::asin_(x);
+ F expected = asinf(x);
+ F delta = SK_OPTS_NS::abs_(expected - result);
+
+ REPORTER_ASSERT(r, SK_OPTS_NS::all(delta < kTolerance));
+ }
+}
+
+DEF_TEST(SkRasterPipelineOpts_Acos, r) {
+ using F = SK_OPTS_NS::F;
+
+ constexpr float kTolerance = 0.00175f;
+ for (float x = -1; x <= 1; x += 1.0f/64) {
+ F result = SK_OPTS_NS::acos_(x);
+ F expected = acosf(x);
+ F delta = SK_OPTS_NS::abs_(expected - result);
+
+ REPORTER_ASSERT(r, SK_OPTS_NS::all(delta < kTolerance));
+ }
+}
+
DEF_TEST(SkRasterPipelineOpts_Atan, r) {
using F = SK_OPTS_NS::F;