From fcb1728c35f97a67fa0297f12bb13d3cafb01fe1 Mon Sep 17 00:00:00 2001 From: brucedawson Date: Tue, 1 Nov 2016 11:22:25 -0700 Subject: Fix founding difference in pdfium_test on AdobeCMYK_to_sRGB An optimization to speed up float-to-int rounding caused a different result for one input value. This tweaks the conversion constant so that the results are identical across the entire valid range, and adds a test that checks the part of the range that is most sensitive to errors. BUG=pdfium:624 Review-Url: https://codereview.chromium.org/2466203002 --- core/fxcodec/codec/fx_codec_icc.cpp | 22 +++++++++++----------- core/fxcodec/codec/fx_codec_jpx_unittest.cpp | 23 +++++++++++++++++++++++ 2 files changed, 34 insertions(+), 11 deletions(-) (limited to 'core/fxcodec') diff --git a/core/fxcodec/codec/fx_codec_icc.cpp b/core/fxcodec/codec/fx_codec_icc.cpp index 8e48bfbfea..f0ea6bb43c 100644 --- a/core/fxcodec/codec/fx_codec_icc.cpp +++ b/core/fxcodec/codec/fx_codec_icc.cpp @@ -1663,17 +1663,17 @@ void AdobeCMYK_to_sRGB(FX_FLOAT c, // Convert to uint8_t with round-to-nearest. Avoid using FXSYS_round because // it is incredibly expensive with VC++ (tested on VC++ 2015) because round() // is very expensive. - // Adding 0.5f and truncating can round the wrong direction in some edge - // cases but these do not matter in this context. For instance, the float that - // is one ULP (unit in the last place) before 0.5 should round to zero but - // this will round it to one. These edge cases are never hit in this function - // due to the very limited precision of the input integers. - // This method also doesn't handle negative or extremely large numbers, but - // those are not needed here. - uint8_t c1 = int(c * 255.f + 0.5f); - uint8_t m1 = int(m * 255.f + 0.5f); - uint8_t y1 = int(y * 255.f + 0.5f); - uint8_t k1 = int(k * 255.f + 0.5f); + // The 'magic' value of 0.49999997f, the float that precedes 0.5f, was chosen + // because it gives identical results to FXSYS_round(). Using the constant + // 0.5f gives different results (1 instead of 0) for one value, 0.0019607842. + // That value is close to the cusp but zero is the correct answer, and + // getting the same answer as before is desirable. + // All floats from 0.0 to 1.0 were tested and now give the same results. + const float rounding_offset = 0.49999997f; + uint8_t c1 = int(c * 255.f + rounding_offset); + uint8_t m1 = int(m * 255.f + rounding_offset); + uint8_t y1 = int(y * 255.f + rounding_offset); + uint8_t k1 = int(k * 255.f + rounding_offset); ASSERT(c1 == FXSYS_round(c * 255)); ASSERT(m1 == FXSYS_round(m * 255)); diff --git a/core/fxcodec/codec/fx_codec_jpx_unittest.cpp b/core/fxcodec/codec/fx_codec_jpx_unittest.cpp index 820a618f5b..3ef14e62c3 100644 --- a/core/fxcodec/codec/fx_codec_jpx_unittest.cpp +++ b/core/fxcodec/codec/fx_codec_jpx_unittest.cpp @@ -19,6 +19,29 @@ static unsigned char stream_data[] = { 0x84, 0x85, 0x86, 0x87, // Include some hi-bytes, too. }; +union Float_t { + Float_t(float num = 0.0f) : f(num) {} + + int32_t i; + FX_FLOAT f; +}; + +TEST(fxcodec, CMYK_Rounding) { + // Testing all floats from 0.0 to 1.0 takes about 35 seconds in release + // builds and much longer in debug builds, so just test the known-dangerous + // range. + const FX_FLOAT startValue = 0.001f; + const FX_FLOAT endValue = 0.003f; + FX_FLOAT R = 0.0f, G = 0.0f, B = 0.0f; + // Iterate through floats by incrementing the representation, as discussed in + // https://randomascii.wordpress.com/2012/01/23/stupid-float-tricks-2/ + for (Float_t f = startValue; f.f < endValue; f.i++) { + AdobeCMYK_to_sRGB(f.f, f.f, f.f, f.f, R, G, B); + } + // Check various other 'special' numbers. + AdobeCMYK_to_sRGB(0.0f, 0.25f, 0.5f, 1.0f, R, G, B); +} + TEST(fxcodec, DecodeDataNullDecodeData) { unsigned char buffer[16]; DecodeData* ptr = nullptr; -- cgit v1.2.3