diff options
author | jvanverth <jvanverth@google.com> | 2014-11-26 13:15:59 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-11-26 13:15:59 -0800 |
commit | 936799204b34e7a2f20ac6c0868058799ceb851e (patch) | |
tree | c1dd34971c393de4bf7247aed276a91b123400fd | |
parent | 9881d63c57002ffbdf2adf623965ece280279989 (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.gypi | 2 | ||||
-rw-r--r-- | src/core/SkHalf.cpp | 97 | ||||
-rw-r--r-- | src/core/SkHalf.h | 22 | ||||
-rw-r--r-- | tests/MathTest.cpp | 57 |
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(); |