jpegrecoverymap: use lookup for applyRecovery

Bug: 261877699
Test: push files from tests/data to /sdcard/Documents and then \
 atest libjpegdecoder_test libjpegencoder_test libjpegrecoverymap_test
Change-Id: I1fe0650d53042985839185e31a7300f0fc33dd06
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
index f14fafc..0695bb7 100644
--- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
+++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h
@@ -17,12 +17,15 @@
 #ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H
 #define ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H
 
+#include <cmath>
 #include <stdint.h>
 
 #include <jpegrecoverymap/recoverymap.h>
 
 namespace android::recoverymap {
 
+#define CLIP3(x, min, max) ((x) < (min)) ? (min) : ((x) > (max)) ? (max) : (x)
+
 ////////////////////////////////////////////////////////////////////////////////
 // Framework
 
@@ -112,6 +115,31 @@
   return temp /= rhs;
 }
 
+constexpr size_t kRecoveryFactorPrecision = 10;
+constexpr size_t kRecoveryFactorNumEntries = 1 << kRecoveryFactorPrecision;
+struct RecoveryLUT {
+  RecoveryLUT(float hdrRatio) {
+    float increment = 2.0 / kRecoveryFactorNumEntries;
+    float value = -1.0f;
+    for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++, value += increment) {
+      mRecoveryTable[idx] = pow(hdrRatio, value);
+    }
+  }
+
+  ~RecoveryLUT() {
+  }
+
+  float getRecoveryFactor(float recovery) {
+    uint32_t value = static_cast<uint32_t>(((recovery + 1.0f) / 2.0f) * kRecoveryFactorNumEntries);
+    //TODO() : Remove once conversion modules have appropriate clamping in place
+    value = CLIP3(value, 0, kRecoveryFactorNumEntries - 1);
+    return mRecoveryTable[value];
+  }
+
+private:
+  float mRecoveryTable[kRecoveryFactorNumEntries];
+};
+
 struct ShepardsIDW {
   ShepardsIDW(int mapScaleFactor) : mMapScaleFactor{mapScaleFactor} {
     const int size = mMapScaleFactor * mMapScaleFactor * 4;
@@ -158,7 +186,6 @@
   void fillShepardsIDW(float *weights, int incR, int incB);
 };
 
-
 ////////////////////////////////////////////////////////////////////////////////
 // sRGB transformations
 // NOTE: sRGB has the same color primaries as BT.709, but different transfer
@@ -306,6 +333,7 @@
  * value, with the given hdr ratio, to the given sdr input in the range [0, 1].
  */
 Color applyRecovery(Color e, float recovery, float hdr_ratio);
+Color applyRecoveryLUT(Color e, float recovery, RecoveryLUT& recoveryLUT);
 
 /*
  * Helper for sampling from YUV 420 images.
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp
index a898f1e..ef5498c 100644
--- a/libs/jpegrecoverymap/recoverymap.cpp
+++ b/libs/jpegrecoverymap/recoverymap.cpp
@@ -46,6 +46,7 @@
 #define USE_PQ_OETF_LUT 1
 #define USE_HLG_INVOETF_LUT 1
 #define USE_PQ_INVOETF_LUT 1
+#define USE_APPLY_RECOVERY_LUT 1
 
 #define JPEGR_CHECK(x)          \
   {                             \
@@ -909,10 +910,12 @@
   dest->width = uncompressed_yuv_420_image->width;
   dest->height = uncompressed_yuv_420_image->height;
   ShepardsIDW idwTable(kMapDimensionScaleFactor);
+  RecoveryLUT recoveryLUT(metadata->rangeScalingFactor);
 
   JobQueue jobQueue;
   std::function<void()> applyRecMap = [uncompressed_yuv_420_image, uncompressed_recovery_map,
-                                       metadata, dest, &jobQueue, &idwTable]() -> void {
+                                       metadata, dest, &jobQueue, &idwTable,
+                                       &recoveryLUT]() -> void {
     const float hdr_ratio = metadata->rangeScalingFactor;
     size_t width = uncompressed_yuv_420_image->width;
     size_t height = uncompressed_yuv_420_image->height;
@@ -964,8 +967,11 @@
             recovery = sampleMap(uncompressed_recovery_map, map_scale_factor, x, y,
                                 idwTable);
           }
+#if USE_APPLY_RECOVERY_LUT
+          Color rgb_hdr = applyRecoveryLUT(rgb_sdr, recovery, recoveryLUT);
+#else
           Color rgb_hdr = applyRecovery(rgb_sdr, recovery, hdr_ratio);
-
+#endif
           Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->rangeScalingFactor);
           uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr);
 
diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp
index 64fa44b..4f21ac6 100644
--- a/libs/jpegrecoverymap/recoverymapmath.cpp
+++ b/libs/jpegrecoverymap/recoverymapmath.cpp
@@ -20,8 +20,6 @@
 
 namespace android::recoverymap {
 
-#define CLIP3(x, min, max) ((x) < (min)) ? (min) : ((x) > (max)) ? (max) : (x)
-
 constexpr size_t kPqOETFPrecision = 10;
 constexpr size_t kPqOETFNumEntries = 1 << kPqOETFPrecision;
 
@@ -481,6 +479,11 @@
   return e * recoveryFactor;
 }
 
+Color applyRecoveryLUT(Color e, float recovery, RecoveryLUT& recoveryLUT) {
+  float recoveryFactor = recoveryLUT.getRecoveryFactor(recovery);
+  return e * recoveryFactor;
+}
+
 Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
   size_t pixel_count = image->width * image->height;
 
diff --git a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
index 0d0e4fc..1d522d1 100644
--- a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
+++ b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp
@@ -557,6 +557,25 @@
     }
 }
 
+TEST_F(RecoveryMapMathTest, applyRecoveryLUT) {
+  float increment = 2.0 / kRecoveryFactorNumEntries;
+  for (float hdrRatio = 1.0f; hdrRatio <= 10.0f; hdrRatio += 1.0f)  {
+    RecoveryLUT recoveryLUT(hdrRatio);
+    for (float value = -1.0f; value <= -1.0f; value += increment) {
+      EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), value, hdrRatio),
+                      applyRecoveryLUT(RgbBlack(), value, recoveryLUT));
+      EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), value, hdrRatio),
+                      applyRecoveryLUT(RgbWhite(), value, recoveryLUT));
+      EXPECT_RGB_NEAR(applyRecovery(RgbRed(), value, hdrRatio),
+                      applyRecoveryLUT(RgbRed(), value, recoveryLUT));
+      EXPECT_RGB_NEAR(applyRecovery(RgbGreen(), value, hdrRatio),
+                      applyRecoveryLUT(RgbGreen(), value, recoveryLUT));
+      EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, hdrRatio),
+                      applyRecoveryLUT(RgbBlue(), value, recoveryLUT));
+    }
+  }
+}
+
 TEST_F(RecoveryMapMathTest, PqTransferFunctionRoundtrip) {
   EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(0.0f)), 0.0f);
   EXPECT_NEAR(pqInvOetf(pqOetf(0.01f)), 0.01f, ComparisonEpsilon());