aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar jvanverth <jvanverth@google.com>2014-11-26 13:15:59 -0800
committerGravatar Commit bot <commit-bot@chromium.org>2014-11-26 13:15:59 -0800
commit936799204b34e7a2f20ac6c0868058799ceb851e (patch)
treec1dd34971c393de4bf7247aed276a91b123400fd
parent9881d63c57002ffbdf2adf623965ece280279989 (diff)
Add float-to-half (binary16) conversion functions.
Based on code by Fabian Giesen at https://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/. These will be needed for creating binary16 textures from floating point data. BUG=skia:3103 Review URL: https://codereview.chromium.org/760753003
-rw-r--r--gyp/core.gypi2
-rw-r--r--src/core/SkHalf.cpp97
-rw-r--r--src/core/SkHalf.h22
-rw-r--r--tests/MathTest.cpp57
4 files changed, 178 insertions, 0 deletions
diff --git a/gyp/core.gypi b/gyp/core.gypi
index a4d5c2c60a..2c6d7b1833 100644
--- a/gyp/core.gypi
+++ b/gyp/core.gypi
@@ -101,6 +101,8 @@
'<(skia_src_path)/core/SkGlyphCache.h',
'<(skia_src_path)/core/SkGlyphCache_Globals.h',
'<(skia_src_path)/core/SkGraphics.cpp',
+ '<(skia_src_path)/core/SkHalf.cpp',
+ '<(skia_src_path)/core/SkHalf.h',
'<(skia_src_path)/core/SkInstCnt.cpp',
'<(skia_src_path)/core/SkImageFilter.cpp',
'<(skia_src_path)/core/SkImageInfo.cpp',
diff --git a/src/core/SkHalf.cpp b/src/core/SkHalf.cpp
new file mode 100644
index 0000000000..0db979bc76
--- /dev/null
+++ b/src/core/SkHalf.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkHalf.h"
+#include "SkFloatBits.h"
+
+uint16_t halfMantissa(SkHalf h) {
+ return h & 0x03ff;
+}
+
+uint16_t halfExponent(SkHalf h) {
+ return (h >> 10) & 0x001f;
+}
+
+uint16_t halfSign(SkHalf h) {
+ return h >> 15;
+}
+
+union FloatUIntUnion {
+ uint32_t fUInt; // this must come first for the initializations below to work
+ float fFloat;
+};
+
+// based on Fabien Giesen's float_to_half_fast3()
+// see https://gist.github.com/rygorous/2156668
+SkHalf SkFloatToHalf(float f) {
+ static const uint32_t f32infty = { 255 << 23 };
+ static const uint32_t f16infty = { 31 << 23 };
+ static const FloatUIntUnion magic = { 15 << 23 };
+ static const uint32_t sign_mask = 0x80000000u;
+ static const uint32_t round_mask = ~0xfffu;
+ SkHalf o = 0;
+
+ FloatUIntUnion floatUnion;
+ floatUnion.fFloat = f;
+
+ uint32_t sign = floatUnion.fUInt & sign_mask;
+ floatUnion.fUInt ^= sign;
+
+ // NOTE all the integer compares in this function can be safely
+ // compiled into signed compares since all operands are below
+ // 0x80000000. Important if you want fast straight SSE2 code
+ // (since there's no unsigned PCMPGTD).
+
+ // Inf or NaN (all exponent bits set)
+ if (floatUnion.fUInt >= f32infty)
+ // NaN->qNaN and Inf->Inf
+ o = (floatUnion.fUInt > f32infty) ? 0x7e00 : 0x7c00;
+ // (De)normalized number or zero
+ else {
+ floatUnion.fUInt &= round_mask;
+ floatUnion.fFloat *= magic.fFloat;
+ floatUnion.fUInt -= round_mask;
+ // Clamp to signed infinity if overflowed
+ if (floatUnion.fUInt > f16infty) {
+ floatUnion.fUInt = f16infty;
+ }
+
+ o = floatUnion.fUInt >> 13; // Take the bits!
+ }
+
+ o |= sign >> 16;
+ return o;
+}
+
+// based on Fabien Giesen's half_to_float_fast2()
+// see https://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/
+float SkHalfToFloat(SkHalf h) {
+ static const FloatUIntUnion magic = { 126 << 23 };
+ FloatUIntUnion o;
+
+ if (halfExponent(h) == 0)
+ {
+ // Zero / Denormal
+ o.fUInt = magic.fUInt + halfMantissa(h);
+ o.fFloat -= magic.fFloat;
+ }
+ else
+ {
+ // Set mantissa
+ o.fUInt = halfMantissa(h) << 13;
+ // Set exponent
+ if (halfExponent(h) == 0x1f)
+ // Inf/NaN
+ o.fUInt |= (255 << 23);
+ else
+ o.fUInt |= ((127 - 15 + halfExponent(h)) << 23);
+ }
+
+ // Set sign
+ o.fUInt |= (halfSign(h) << 31);
+ return o.fFloat;
+}
diff --git a/src/core/SkHalf.h b/src/core/SkHalf.h
new file mode 100644
index 0000000000..3fd07793e9
--- /dev/null
+++ b/src/core/SkHalf.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkHalf_DEFINED
+#define SkHalf_DEFINED
+
+#include "SkTypes.h"
+
+// 16-bit floating point value
+// format is 1 bit sign, 5 bits exponent, 10 bits mantissa
+// only used for storage
+typedef uint16_t SkHalf;
+
+// convert between half and single precision floating point
+float SkHalfToFloat(SkHalf h);
+SkHalf SkFloatToHalf(float f);
+
+#endif
diff --git a/tests/MathTest.cpp b/tests/MathTest.cpp
index 20539369b8..4347096ea5 100644
--- a/tests/MathTest.cpp
+++ b/tests/MathTest.cpp
@@ -9,6 +9,7 @@
#include "SkEndian.h"
#include "SkFloatBits.h"
#include "SkFloatingPoint.h"
+#include "SkHalf.h"
#include "SkMathPriv.h"
#include "SkPoint.h"
#include "SkRandom.h"
@@ -326,6 +327,61 @@ static void unittest_isfinite(skiatest::Reporter* reporter) {
REPORTER_ASSERT(reporter, SkScalarIsFinite(0));
}
+static void unittest_half(skiatest::Reporter* reporter) {
+ static const float gFloats[] = {
+ 0.f, 1.f, 0.5f, 0.499999f, 0.5000001f, 1.f/3,
+ -0.f, -1.f, -0.5f, -0.499999f, -0.5000001f, -1.f/3
+ };
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gFloats); ++i) {
+ SkHalf h = SkFloatToHalf(gFloats[i]);
+ float f = SkHalfToFloat(h);
+ REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f, gFloats[i]));
+ }
+
+ // check some special values
+ union FloatUnion {
+ uint32_t fU;
+ float fF;
+ };
+
+ static const FloatUnion largestPositiveHalf = { ((142 << 23) | (1023 << 13)) };
+ SkHalf h = SkFloatToHalf(largestPositiveHalf.fF);
+ float f = SkHalfToFloat(h);
+ REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f, largestPositiveHalf.fF));
+
+ static const FloatUnion largestNegativeHalf = { (1u << 31) | (142u << 23) | (1023u << 13) };
+ h = SkFloatToHalf(largestNegativeHalf.fF);
+ f = SkHalfToFloat(h);
+ REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f, largestNegativeHalf.fF));
+
+ static const FloatUnion smallestPositiveHalf = { 102 << 23 };
+ h = SkFloatToHalf(smallestPositiveHalf.fF);
+ f = SkHalfToFloat(h);
+ REPORTER_ASSERT(reporter, SkScalarNearlyEqual(f, smallestPositiveHalf.fF));
+
+ static const FloatUnion overflowHalf = { ((143 << 23) | (1023 << 13)) };
+ h = SkFloatToHalf(overflowHalf.fF);
+ f = SkHalfToFloat(h);
+ REPORTER_ASSERT(reporter, !SkScalarIsFinite(f) );
+
+ static const FloatUnion underflowHalf = { 101 << 23 };
+ h = SkFloatToHalf(underflowHalf.fF);
+ f = SkHalfToFloat(h);
+ REPORTER_ASSERT(reporter, f == 0.0f );
+
+ static const FloatUnion inf32 = { 255 << 23 };
+ h = SkFloatToHalf(inf32.fF);
+ f = SkHalfToFloat(h);
+ REPORTER_ASSERT(reporter, !SkScalarIsFinite(f) );
+
+ static const FloatUnion nan32 = { 255 << 23 | 1 };
+ h = SkFloatToHalf(nan32.fF);
+ f = SkHalfToFloat(h);
+ REPORTER_ASSERT(reporter, SkScalarIsNaN(f) );
+
+}
+
static void test_muldiv255(skiatest::Reporter* reporter) {
for (int a = 0; a <= 255; a++) {
for (int b = 0; b <= 255; b++) {
@@ -464,6 +520,7 @@ DEF_TEST(Math, reporter) {
unittest_fastfloat(reporter);
unittest_isfinite(reporter);
+ unittest_half(reporter);
for (i = 0; i < 10000; i++) {
SkFixed numer = rand.nextS();