basegfx/source/color/bcolormodifier.cxx  |   30 +++++++++++-------------------
 basegfx/test/BColorModifierTest.cxx      |   21 +++++++++++++++++++++
 include/basegfx/color/bcolormodifier.hxx |    2 +-
 3 files changed, 33 insertions(+), 20 deletions(-)

New commits:
commit c03d752c614b3b19ed89a510d7791bc46604a452
Author:     Noel Grandin <noel.gran...@collabora.co.uk>
AuthorDate: Fri Apr 25 13:49:14 2025 +0200
Commit:     Noel Grandin <noel.gran...@collabora.co.uk>
CommitDate: Fri Apr 25 17:47:09 2025 +0200

    improve fast_pow in BColorModifier_gamma
    
    And add tests to demonstrate accuracy.
    
    This is taken from
       
https://martin.ankerl.com/2007/10/04/optimized-pow-approximation-for-java-and-c-c/
    and results in a maximum error of 3% over our input range.
    
    Change-Id: I1568dabb552d52a3eff085fd327ed3c1c246edcc
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184623
    Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk>
    Tested-by: Jenkins

diff --git a/basegfx/source/color/bcolormodifier.cxx 
b/basegfx/source/color/bcolormodifier.cxx
index 360a41db9285..44da250acdbf 100644
--- a/basegfx/source/color/bcolormodifier.cxx
+++ b/basegfx/source/color/bcolormodifier.cxx
@@ -18,6 +18,7 @@
  */
 
 #include <sal/config.h>
+#include <sal/mathconf.h>
 #include <algorithm>
 #include <float.h>
 #include <basegfx/color/bcolormodifier.hxx>
@@ -325,28 +326,19 @@ namespace basegfx
     }
 
     /**
-      A fast and approximate std::pow(), good enough for gamma calculations.
-
-      std::pow() is basically implemented using log's:
-          pow(a,b) = x^(logx(a) * b)
-      So we need a fast log and fast exponent - it doesn't matter what x is so 
we use 2.
-          pow(a,b) = 2^(log2(a) * b)
-      The trick is that a floating point number is already in a log style 
format:
-          a = M * 2^E
-      Taking the log of both sides gives:
-          log2(a) = log2(M) + E
-      or more simply:
-          log2(a) ~= E
-      In other words if we take the floating point representation of a number,
-      and extract the Exponent we've got something that's a good starting 
point as its log.
-      And then we can do:
-          pow(a,b) = 2^(E * b)
+      A fast and approximate std::pow(), good enough for gamma calculations,
+      given that the parameter a is in the range [0,1), and b is in the range 
(0, 0.1]
+
+      Google for "optimised power approximation". The below function is the 
result
+      of reducing various bit-twiddling tricks into fewer operations.
     */
     static double fast_pow(double a, double b)
     {
-        int a_exp;
-        std::frexp(a, &a_exp);
-        return std::exp2(a_exp * b);
+        sal_math_Double u;
+        u.value = a;
+        u.w32_parts.msw = static_cast<int>(b * 
(static_cast<int>(u.w32_parts.msw) - 1072632447) + 1072632447);
+        u.w32_parts.lsw = 0;
+        return u.value;
     }
 
     ::basegfx::BColor BColorModifier_gamma::getModifiedColor(const 
::basegfx::BColor& aSourceColor) const
diff --git a/basegfx/test/BColorModifierTest.cxx 
b/basegfx/test/BColorModifierTest.cxx
old mode 100755
new mode 100644
index 4a84c3662a6b..12c5af660138
--- a/basegfx/test/BColorModifierTest.cxx
+++ b/basegfx/test/BColorModifierTest.cxx
@@ -395,6 +395,26 @@ public:
         CPPUNIT_ASSERT(aBColorModifier->operator==(*aBColorModifier2));
     }
 
+    // Verify that our shortcut gamma calculation produces reasonably accurate 
results
+    void testGamma()
+    {
+        for (int i = 1; i < 10; i++)
+        {
+            BColorModifier_gamma g(i);
+            for (int j = 0; j < 100; j++)
+            {
+                double inputRed = j / 100.0;
+                // this is the "slow but correct" gamma calculation
+                double expectedOutputRed = std::pow(inputRed, 1 / double(i));
+                BColor col = g.getModifiedColor(BColor(inputRed, 0, 0));
+                auto msg = OString("col is " + OString::number(inputRed) + " 
and gamma is "
+                                   + OString::number(i));
+                CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(msg.getStr(), 
expectedOutputRed, col.getRed(),
+                                                     0.029);
+            }
+        }
+    }
+
     CPPUNIT_TEST_SUITE(bcolormodifier);
     CPPUNIT_TEST(testGray);
     CPPUNIT_TEST(testInvert);
@@ -407,6 +427,7 @@ public:
     CPPUNIT_TEST(testMatrixShift);
     CPPUNIT_TEST(testIdentityMatrix);
     CPPUNIT_TEST(testBlackAndWhite);
+    CPPUNIT_TEST(testGamma);
     CPPUNIT_TEST_SUITE_END();
 };
 
diff --git a/include/basegfx/color/bcolormodifier.hxx 
b/include/basegfx/color/bcolormodifier.hxx
index e74d6b43726e..f6052067767f 100644
--- a/include/basegfx/color/bcolormodifier.hxx
+++ b/include/basegfx/color/bcolormodifier.hxx
@@ -351,7 +351,7 @@ namespace basegfx
 
         col(r,g,b) = clamp(pow(col(r,g,b), 1.0 / gamma), 0.0, 1.0)
     */
-    class SAL_WARN_UNUSED UNLESS_MERGELIBS(BASEGFX_DLLPUBLIC) 
BColorModifier_gamma final : public BColorModifier
+    class SAL_WARN_UNUSED BASEGFX_DLLPUBLIC BColorModifier_gamma final : 
public BColorModifier
     {
     private:
         double                      mfValue;

Reply via email to