diff options
| -rw-r--r-- | libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h | 28 | ||||
| -rw-r--r-- | libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h | 36 | ||||
| -rw-r--r-- | libs/jpegrecoverymap/recoverymap.cpp | 51 | ||||
| -rw-r--r-- | libs/jpegrecoverymap/recoverymapmath.cpp | 72 | ||||
| -rw-r--r-- | libs/jpegrecoverymap/tests/recoverymap_test.cpp | 150 | ||||
| -rw-r--r-- | libs/jpegrecoverymap/tests/recoverymapmath_test.cpp | 332 | 
6 files changed, 463 insertions, 206 deletions
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h index aee6602aa4..1fd129b991 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h @@ -88,6 +88,8 @@ struct jpegr_metadata {    uint32_t version;    // Max Content Boost for the map    float maxContentBoost; +  // Min Content Boost for the map +  float minContentBoost;  };  typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr; @@ -219,16 +221,9 @@ public:      */      status_t getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,                            jr_info_ptr jpegr_info); -private: -    /* -     * This method is called in the encoding pipeline. It will encode the recovery map. -     * -     * @param uncompressed_recovery_map uncompressed recovery map -     * @param dest encoded recover map -     * @return NO_ERROR if encoding succeeds, error code if error occurs. -     */ -    status_t compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map, -                               jr_compressed_ptr dest); + +protected: +    // Following functions protected instead of private for testing.      /*       * This method is called in the encoding pipeline. It will take the uncompressed 8-bit and @@ -239,7 +234,7 @@ private:       * @param uncompressed_p010_image uncompressed HDR image in P010 color format       * @param hdr_tf transfer function of the HDR image       * @param dest recovery map; caller responsible for memory of data -     * @param metadata max_content_boost is filled in +     * @param metadata minContentBoost and maxContentBoost are filled in       * @return NO_ERROR if calculation succeeds, error code if error occurs.       */      status_t generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, @@ -265,6 +260,17 @@ private:                                jr_metadata_ptr metadata,                                jr_uncompressed_ptr dest); +private: +    /* +     * This method is called in the encoding pipeline. It will encode the recovery map. +     * +     * @param uncompressed_recovery_map uncompressed recovery map +     * @param dest encoded recover map +     * @return NO_ERROR if encoding succeeds, error code if error occurs. +     */ +    status_t compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map, +                               jr_compressed_ptr dest); +      /*       * This methoud is called to separate primary image and recovery map image from JPEGR       * diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h index 0695bb74ac..6eed08afb4 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h @@ -118,11 +118,12 @@ inline Color operator/(const Color& lhs, const float 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(jr_metadata_ptr metadata) { +    for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) { +      float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1); +      float logBoost = log2(metadata->minContentBoost) * (1.0f - value) +                     + log2(metadata->maxContentBoost) * value; +      mRecoveryTable[idx] = exp2(logBoost);      }    } @@ -130,10 +131,10 @@ struct RecoveryLUT {    }    float getRecoveryFactor(float recovery) { -    uint32_t value = static_cast<uint32_t>(((recovery + 1.0f) / 2.0f) * kRecoveryFactorNumEntries); +    uint32_t idx = static_cast<uint32_t>(recovery * (kRecoveryFactorNumEntries - 1));      //TODO() : Remove once conversion modules have appropriate clamping in place -    value = CLIP3(value, 0, kRecoveryFactorNumEntries - 1); -    return mRecoveryTable[value]; +    idx = CLIP3(idx, 0, kRecoveryFactorNumEntries - 1); +    return mRecoveryTable[idx];    }  private: @@ -219,6 +220,9 @@ Color srgbInvOetf(Color e_gamma);  float srgbInvOetfLUT(float e_gamma);  Color srgbInvOetfLUT(Color e_gamma); +constexpr size_t kSrgbInvOETFPrecision = 10; +constexpr size_t kSrgbInvOETFNumEntries = 1 << kSrgbInvOETFPrecision; +  ////////////////////////////////////////////////////////////////////////////////  // Display-P3 transformations @@ -260,6 +264,9 @@ Color hlgOetf(Color e);  float hlgOetfLUT(float e);  Color hlgOetfLUT(Color e); +constexpr size_t kHlgOETFPrecision = 10; +constexpr size_t kHlgOETFNumEntries = 1 << kHlgOETFPrecision; +  /*   * Convert from HLG to scene luminance.   * @@ -270,6 +277,9 @@ Color hlgInvOetf(Color e_gamma);  float hlgInvOetfLUT(float e_gamma);  Color hlgInvOetfLUT(Color e_gamma); +constexpr size_t kHlgInvOETFPrecision = 10; +constexpr size_t kHlgInvOETFNumEntries = 1 << kHlgInvOETFPrecision; +  /*   * Convert from scene luminance to PQ.   * @@ -280,6 +290,9 @@ Color pqOetf(Color e);  float pqOetfLUT(float e);  Color pqOetfLUT(Color e); +constexpr size_t kPqOETFPrecision = 10; +constexpr size_t kPqOETFNumEntries = 1 << kPqOETFPrecision; +  /*   * Convert from PQ to scene luminance in nits.   * @@ -290,6 +303,9 @@ Color pqInvOetf(Color e_gamma);  float pqInvOetfLUT(float e_gamma);  Color pqInvOetfLUT(Color e_gamma); +constexpr size_t kPqInvOETFPrecision = 10; +constexpr size_t kPqInvOETFNumEntries = 1 << kPqInvOETFPrecision; +  ////////////////////////////////////////////////////////////////////////////////  // Color space conversions @@ -326,13 +342,13 @@ ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gam   * Calculate the 8-bit unsigned integer recovery value for the given SDR and HDR   * luminances in linear space, and the hdr ratio to encode against.   */ -uint8_t encodeRecovery(float y_sdr, float y_hdr, float hdr_ratio); +uint8_t encodeRecovery(float y_sdr, float y_hdr, jr_metadata_ptr metadata);  /*   * Calculates the linear luminance in nits after applying the given recovery   * 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 applyRecovery(Color e, float recovery, jr_metadata_ptr metadata);  Color applyRecoveryLUT(Color e, float recovery, RecoveryLUT& recoveryLUT);  /* diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp index 218c43017b..349223bb6b 100644 --- a/libs/jpegrecoverymap/recoverymap.cpp +++ b/libs/jpegrecoverymap/recoverymap.cpp @@ -573,19 +573,20 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4    }    std::mutex mutex; -  float hdr_y_nits_max = 0.0f; -  double hdr_y_nits_avg = 0.0f; +  float max_gain = 0.0f; +  float min_gain = 1.0f;    const int threads = std::clamp(GetCPUCoreCount(), 1, 4);    size_t rowStep = threads == 1 ? image_height : kJobSzInRows;    JobQueue jobQueue; -  std::function<void()> computeMetadata = [uncompressed_p010_image, hdrInvOetf, -                                           hdrGamutConversionFn, luminanceFn, hdr_white_nits, -                                           threads, &mutex, &hdr_y_nits_avg, -                                           &hdr_y_nits_max, &jobQueue]() -> void { +  std::function<void()> computeMetadata = [uncompressed_p010_image, uncompressed_yuv_420_image, +                                           hdrInvOetf, hdrGamutConversionFn, luminanceFn, +                                           hdr_white_nits, threads, &mutex, &max_gain, &min_gain, +                                           &jobQueue]() -> void {      size_t rowStart, rowEnd; -    float hdr_y_nits_max_th = 0.0f; -    double hdr_y_nits_avg_th = 0.0f; +    float max_gain_th = 0.0f; +    float min_gain_th = 1.0f; +      while (jobQueue.dequeueJob(rowStart, rowEnd)) {        for (size_t y = rowStart; y < rowEnd; ++y) {          for (size_t x = 0; x < uncompressed_p010_image->width; ++x) { @@ -595,16 +596,25 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4            hdr_rgb = hdrGamutConversionFn(hdr_rgb);            float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits; -          hdr_y_nits_avg_th += hdr_y_nits; -          if (hdr_y_nits > hdr_y_nits_max_th) { -            hdr_y_nits_max_th = hdr_y_nits; -          } +          Color sdr_yuv_gamma = +              getYuv420Pixel(uncompressed_yuv_420_image, x, y); +          Color sdr_rgb_gamma = srgbYuvToRgb(sdr_yuv_gamma); +#if USE_SRGB_INVOETF_LUT +          Color sdr_rgb = srgbInvOetfLUT(sdr_rgb_gamma); +#else +          Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma); +#endif +          float sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits; + +          float gain = hdr_y_nits / sdr_y_nits; +          max_gain_th = std::max(max_gain_th, gain); +          min_gain_th = std::min(min_gain_th, gain);          }        }      }      std::unique_lock<std::mutex> lock{mutex}; -    hdr_y_nits_avg += hdr_y_nits_avg_th; -    hdr_y_nits_max = std::max(hdr_y_nits_max, hdr_y_nits_max_th); +    max_gain = std::max(max_gain, max_gain_th); +    min_gain = std::min(min_gain, min_gain_th);    };    std::function<void()> generateMap = [uncompressed_yuv_420_image, uncompressed_p010_image, @@ -634,7 +644,7 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4            size_t pixel_idx = x + y * dest_map_stride;            reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] = -              encodeRecovery(sdr_y_nits, hdr_y_nits, metadata->maxContentBoost); +              encodeRecovery(sdr_y_nits, hdr_y_nits, metadata);          }        }      } @@ -655,9 +665,9 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4    computeMetadata();    std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); });    workers.clear(); -  hdr_y_nits_avg /= image_width * image_height; -  metadata->maxContentBoost = hdr_y_nits_max / kSdrWhiteNits; +  metadata->maxContentBoost = max_gain; +  metadata->minContentBoost = min_gain;    // generate map    jobQueue.reset(); @@ -693,7 +703,7 @@ status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_    dest->width = uncompressed_yuv_420_image->width;    dest->height = uncompressed_yuv_420_image->height;    ShepardsIDW idwTable(kMapDimensionScaleFactor); -  RecoveryLUT recoveryLUT(metadata->maxContentBoost); +  RecoveryLUT recoveryLUT(metadata);    JobQueue jobQueue;    std::function<void()> applyRecMap = [uncompressed_yuv_420_image, uncompressed_recovery_map, @@ -729,13 +739,12 @@ status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_            if (map_scale_factor != floorf(map_scale_factor)) {              recovery = sampleMap(uncompressed_recovery_map, map_scale_factor, x, y);            } else { -            recovery = sampleMap(uncompressed_recovery_map, map_scale_factor, x, y, -                                idwTable); +            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); +          Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata);  #endif            Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->maxContentBoost);            uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr); diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp index 4f21ac6372..9c89c8afd4 100644 --- a/libs/jpegrecoverymap/recoverymapmath.cpp +++ b/libs/jpegrecoverymap/recoverymapmath.cpp @@ -20,65 +20,46 @@  namespace android::recoverymap { -constexpr size_t kPqOETFPrecision = 10; -constexpr size_t kPqOETFNumEntries = 1 << kPqOETFPrecision; -  static const std::vector<float> kPqOETF = [] {      std::vector<float> result; -    float increment = 1.0 / kPqOETFNumEntries; -    float value = 0.0f; -    for (int idx = 0; idx < kPqOETFNumEntries; idx++, value += increment) { +    for (int idx = 0; idx < kPqOETFNumEntries; idx++) { +      float value = static_cast<float>(idx) / static_cast<float>(kPqOETFNumEntries - 1);        result.push_back(pqOetf(value));      }      return result;  }(); -constexpr size_t kPqInvOETFPrecision = 10; -constexpr size_t kPqInvOETFNumEntries = 1 << kPqInvOETFPrecision; -  static const std::vector<float> kPqInvOETF = [] {      std::vector<float> result; -    float increment = 1.0 / kPqInvOETFNumEntries; -    float value = 0.0f; -    for (int idx = 0; idx < kPqInvOETFNumEntries; idx++, value += increment) { +    for (int idx = 0; idx < kPqInvOETFNumEntries; idx++) { +      float value = static_cast<float>(idx) / static_cast<float>(kPqInvOETFNumEntries - 1);        result.push_back(pqInvOetf(value));      }      return result;  }(); -constexpr size_t kHlgOETFPrecision = 10; -constexpr size_t kHlgOETFNumEntries = 1 << kHlgOETFPrecision; -  static const std::vector<float> kHlgOETF = [] {      std::vector<float> result; -    float increment = 1.0 / kHlgOETFNumEntries; -    float value = 0.0f; -    for (int idx = 0; idx < kHlgOETFNumEntries; idx++, value += increment) { +    for (int idx = 0; idx < kHlgOETFNumEntries; idx++) { +      float value = static_cast<float>(idx) / static_cast<float>(kHlgOETFNumEntries - 1);        result.push_back(hlgOetf(value));      }      return result;  }(); -constexpr size_t kHlgInvOETFPrecision = 10; -constexpr size_t kHlgInvOETFNumEntries = 1 << kHlgInvOETFPrecision; -  static const std::vector<float> kHlgInvOETF = [] {      std::vector<float> result; -    float increment = 1.0 / kHlgInvOETFNumEntries; -    float value = 0.0f; -    for (int idx = 0; idx < kHlgInvOETFNumEntries; idx++, value += increment) { +    for (int idx = 0; idx < kHlgInvOETFNumEntries; idx++) { +      float value = static_cast<float>(idx) / static_cast<float>(kHlgInvOETFNumEntries - 1);        result.push_back(hlgInvOetf(value));      }      return result;  }(); -constexpr size_t kSRGBInvOETFPrecision = 10; -constexpr size_t kSRGBInvOETFNumEntries = 1 << kSRGBInvOETFPrecision; -static const std::vector<float> kSRGBInvOETF = [] { +static const std::vector<float> kSrgbInvOETF = [] {      std::vector<float> result; -    float increment = 1.0 / kSRGBInvOETFNumEntries; -    float value = 0.0f; -    for (int idx = 0; idx < kSRGBInvOETFNumEntries; idx++, value += increment) { +    for (int idx = 0; idx < kSrgbInvOETFNumEntries; idx++) { +      float value = static_cast<float>(idx) / static_cast<float>(kSrgbInvOETFNumEntries - 1);        result.push_back(srgbInvOetf(value));      }      return result; @@ -182,10 +163,10 @@ Color srgbInvOetf(Color e_gamma) {  // See IEC 61966-2-1, Equations F.5 and F.6.  float srgbInvOetfLUT(float e_gamma) { -  uint32_t value = static_cast<uint32_t>(e_gamma * kSRGBInvOETFNumEntries); +  uint32_t value = static_cast<uint32_t>(e_gamma * kSrgbInvOETFNumEntries);    //TODO() : Remove once conversion modules have appropriate clamping in place -  value = CLIP3(value, 0, kSRGBInvOETFNumEntries - 1); -  return kSRGBInvOETF[value]; +  value = CLIP3(value, 0, kSrgbInvOETFNumEntries - 1); +  return kSrgbInvOETF[value];  }  Color srgbInvOetfLUT(Color e_gamma) { @@ -461,21 +442,24 @@ ColorTransformFn getHdrConversionFn(jpegr_color_gamut sdr_gamut, jpegr_color_gam  ////////////////////////////////////////////////////////////////////////////////  // Recovery map calculations - -uint8_t encodeRecovery(float y_sdr, float y_hdr, float hdr_ratio) { +uint8_t encodeRecovery(float y_sdr, float y_hdr, jr_metadata_ptr metadata) {    float gain = 1.0f;    if (y_sdr > 0.0f) {      gain = y_hdr / y_sdr;    } -  if (gain < (1.0f / hdr_ratio)) gain = 1.0f / hdr_ratio; -  if (gain > hdr_ratio) gain = hdr_ratio; +  if (gain < metadata->minContentBoost) gain = metadata->minContentBoost; +  if (gain > metadata->maxContentBoost) gain = metadata->maxContentBoost; -  return static_cast<uint8_t>(log2(gain) / log2(hdr_ratio) * 127.5f  + 127.5f); +  return static_cast<uint8_t>((log2(gain) - log2(metadata->minContentBoost)) +                            / (log2(metadata->maxContentBoost) - log2(metadata->minContentBoost)) +                            * 255.0f);  } -Color applyRecovery(Color e, float recovery, float hdr_ratio) { -  float recoveryFactor = pow(hdr_ratio, recovery); +Color applyRecovery(Color e, float recovery, jr_metadata_ptr metadata) { +  float logBoost = log2(metadata->minContentBoost) * (1.0f - recovery) +                 + log2(metadata->maxContentBoost) * recovery; +  float recoveryFactor = exp2(logBoost);    return e * recoveryFactor;  } @@ -550,7 +534,7 @@ static size_t clamp(const size_t& val, const size_t& low, const size_t& high) {  }  static float mapUintToFloat(uint8_t map_uint) { -  return (static_cast<float>(map_uint) - 127.5f) / 127.5f; +  return static_cast<float>(map_uint) / 255.0f;  }  static float pythDistance(float x_diff, float y_diff) { @@ -558,9 +542,9 @@ static float pythDistance(float x_diff, float y_diff) {  }  // TODO: If map_scale_factor is guaranteed to be an integer, then remove the following. -float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y) { -  float x_map = static_cast<float>(x) / static_cast<float>(map_scale_factor); -  float y_map = static_cast<float>(y) / static_cast<float>(map_scale_factor); +float sampleMap(jr_uncompressed_ptr map, float map_scale_factor, size_t x, size_t y) { +  float x_map = static_cast<float>(x) / map_scale_factor; +  float y_map = static_cast<float>(y) / map_scale_factor;    size_t x_lower = static_cast<size_t>(floor(x_map));    size_t x_upper = x_lower + 1; diff --git a/libs/jpegrecoverymap/tests/recoverymap_test.cpp b/libs/jpegrecoverymap/tests/recoverymap_test.cpp index 3e9a76d47a..1b73d94057 100644 --- a/libs/jpegrecoverymap/tests/recoverymap_test.cpp +++ b/libs/jpegrecoverymap/tests/recoverymap_test.cpp @@ -20,6 +20,7 @@  #include <fcntl.h>  #include <fstream>  #include <gtest/gtest.h> +#include <sys/time.h>  #include <utils/Log.h>  #define RAW_P010_IMAGE "/sdcard/Documents/raw_p010_image.p010" @@ -35,27 +36,24 @@  namespace android::recoverymap { -class RecoveryMapTest : public testing::Test { -public: -  RecoveryMapTest(); -  ~RecoveryMapTest(); -protected: -  virtual void SetUp(); -  virtual void TearDown(); - -  struct jpegr_uncompressed_struct mRawP010Image; -  struct jpegr_uncompressed_struct mRawYuv420Image; -  struct jpegr_compressed_struct mJpegImage; +struct Timer { +  struct timeval StartingTime; +  struct timeval EndingTime; +  struct timeval ElapsedMicroseconds;  }; -RecoveryMapTest::RecoveryMapTest() {} -RecoveryMapTest::~RecoveryMapTest() {} +void timerStart(Timer *t) { +  gettimeofday(&t->StartingTime, nullptr); +} -void RecoveryMapTest::SetUp() {} -void RecoveryMapTest::TearDown() { -  free(mRawP010Image.data); -  free(mRawYuv420Image.data); -  free(mJpegImage.data); +void timerStop(Timer *t) { +  gettimeofday(&t->EndingTime, nullptr); +} + +int64_t elapsedTime(Timer *t) { +  t->ElapsedMicroseconds.tv_sec = t->EndingTime.tv_sec - t->StartingTime.tv_sec; +  t->ElapsedMicroseconds.tv_usec = t->EndingTime.tv_usec - t->StartingTime.tv_usec; +  return t->ElapsedMicroseconds.tv_sec * 1000000 + t->ElapsedMicroseconds.tv_usec;  }  static size_t getFileSize(int fd) { @@ -89,6 +87,80 @@ static bool loadFile(const char filename[], void*& result, int* fileLength) {    return true;  } +class RecoveryMapTest : public testing::Test { +public: +  RecoveryMapTest(); +  ~RecoveryMapTest(); + +protected: +  virtual void SetUp(); +  virtual void TearDown(); + +  struct jpegr_uncompressed_struct mRawP010Image; +  struct jpegr_uncompressed_struct mRawYuv420Image; +  struct jpegr_compressed_struct mJpegImage; +}; + +RecoveryMapTest::RecoveryMapTest() {} +RecoveryMapTest::~RecoveryMapTest() {} + +void RecoveryMapTest::SetUp() {} +void RecoveryMapTest::TearDown() { +  free(mRawP010Image.data); +  free(mRawYuv420Image.data); +  free(mJpegImage.data); +} + +class RecoveryMapBenchmark : public RecoveryMap { +public: + void BenchmarkGenerateRecoveryMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr p010Image, +                                   jr_metadata_ptr metadata, jr_uncompressed_ptr map); + void BenchmarkApplyRecoveryMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map, +                                jr_metadata_ptr metadata, jr_uncompressed_ptr dest); +private: + const int kProfileCount = 10; +}; + +void RecoveryMapBenchmark::BenchmarkGenerateRecoveryMap(jr_uncompressed_ptr yuv420Image, +                                                        jr_uncompressed_ptr p010Image, +                                                        jr_metadata_ptr metadata, +                                                        jr_uncompressed_ptr map) { +  ASSERT_EQ(yuv420Image->width, p010Image->width); +  ASSERT_EQ(yuv420Image->height, p010Image->height); + +  Timer genRecMapTime; + +  timerStart(&genRecMapTime); +  for (auto i = 0; i < kProfileCount; i++) { +      ASSERT_EQ(OK, generateRecoveryMap( +          yuv420Image, p010Image, jpegr_transfer_function::JPEGR_TF_HLG, metadata, map)); +      if (i != kProfileCount - 1) delete[] static_cast<uint8_t *>(map->data); +  } +  timerStop(&genRecMapTime); + +  ALOGE("Generate Recovery Map:- Res = %i x %i, time = %f ms", +        yuv420Image->width, yuv420Image->height, +        elapsedTime(&genRecMapTime) / (kProfileCount * 1000.f)); + +} + +void RecoveryMapBenchmark::BenchmarkApplyRecoveryMap(jr_uncompressed_ptr yuv420Image, +                                                     jr_uncompressed_ptr map, +                                                     jr_metadata_ptr metadata, +                                                     jr_uncompressed_ptr dest) { +  Timer applyRecMapTime; + +  timerStart(&applyRecMapTime); +  for (auto i = 0; i < kProfileCount; i++) { +      ASSERT_EQ(OK, applyRecoveryMap(yuv420Image, map, metadata, dest)); +  } +  timerStop(&applyRecMapTime); + +  ALOGE("Apply Recovery Map:- Res = %i x %i, time = %f ms", +        yuv420Image->width, yuv420Image->height, +        elapsedTime(&applyRecMapTime) / (kProfileCount * 1000.f)); +} +  TEST_F(RecoveryMapTest, build) {    // Force all of the recovery map lib to be linked by calling all public functions.    RecoveryMap recovery_map; @@ -382,4 +454,46 @@ TEST_F(RecoveryMapTest, encodeFromJpegThenDecode) {    free(decodedJpegR.data);  } +TEST_F(RecoveryMapTest, ProfileRecoveryMapFuncs) { +  const size_t kWidth = TEST_IMAGE_WIDTH; +  const size_t kHeight = TEST_IMAGE_HEIGHT; + +  // Load input files. +  if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) { +    FAIL() << "Load file " << RAW_P010_IMAGE << " failed"; +  } +  mRawP010Image.width = kWidth; +  mRawP010Image.height = kHeight; +  mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100; + +  if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) { +    FAIL() << "Load file " << RAW_P010_IMAGE << " failed"; +  } +  mRawYuv420Image.width = kWidth; +  mRawYuv420Image.height = kHeight; +  mRawYuv420Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709; + +  RecoveryMapBenchmark benchmark; + +  jpegr_metadata metadata = { .version = 1, +                              .maxContentBoost = 8.0f, +                              .minContentBoost = 1.0f / 8.0f }; + +  jpegr_uncompressed_struct map = { .data = NULL, +                                    .width = 0, +                                    .height = 0, +                                    .colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED }; + +  benchmark.BenchmarkGenerateRecoveryMap(&mRawYuv420Image, &mRawP010Image, &metadata, &map); + +  const int dstSize = mRawYuv420Image.width * mRawYuv420Image.height * 4; +  auto bufferDst = std::make_unique<uint8_t[]>(dstSize); +  jpegr_uncompressed_struct dest = { .data = bufferDst.get(), +                                     .width = 0, +                                     .height = 0, +                                     .colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED }; + +  benchmark.BenchmarkApplyRecoveryMap(&mRawYuv420Image, &map, &metadata, &dest); +} +  } // namespace android::recoverymap diff --git a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp index 2eec95f01b..80a9596e3c 100644 --- a/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp +++ b/libs/jpegrecoverymap/tests/recoverymapmath_test.cpp @@ -42,7 +42,7 @@ public:    }    float Map(uint8_t e) { -    return (static_cast<float>(e) - 127.5f) / 127.5f; +    return static_cast<float>(e) / 255.0f;    }    Color ColorMin(Color e1, Color e2) { @@ -88,10 +88,10 @@ public:      return luminance_scaled * scale_factor;    } -  Color Recover(Color yuv_gamma, float recovery, float max_content_boost) { +  Color Recover(Color yuv_gamma, float recovery, jr_metadata_ptr metadata) {      Color rgb_gamma = srgbYuvToRgb(yuv_gamma);      Color rgb = srgbInvOetf(rgb_gamma); -    return applyRecovery(rgb, recovery, max_content_boost); +    return applyRecovery(rgb, recovery, metadata);    }    jpegr_uncompressed_struct Yuv420Image() { @@ -518,59 +518,95 @@ TEST_F(RecoveryMapMathTest, PqInvOetf) {  }  TEST_F(RecoveryMapMathTest, PqInvOetfLUT) { -    float increment = 1.0 / 1024.0; -    float value = 0.0f; -    for (int idx = 0; idx < 1024; idx++, value += increment) { +    for (int idx = 0; idx < kPqInvOETFNumEntries; idx++) { +      float value = static_cast<float>(idx) / static_cast<float>(kPqInvOETFNumEntries - 1);        EXPECT_FLOAT_EQ(pqInvOetf(value), pqInvOetfLUT(value));      }  }  TEST_F(RecoveryMapMathTest, HlgInvOetfLUT) { -    float increment = 1.0 / 1024.0; -    float value = 0.0f; -    for (int idx = 0; idx < 1024; idx++, value += increment) { +    for (int idx = 0; idx < kHlgInvOETFNumEntries; idx++) { +      float value = static_cast<float>(idx) / static_cast<float>(kHlgInvOETFNumEntries - 1);        EXPECT_FLOAT_EQ(hlgInvOetf(value), hlgInvOetfLUT(value));      }  }  TEST_F(RecoveryMapMathTest, pqOetfLUT) { -    float increment = 1.0 / 1024.0; -    float value = 0.0f; -    for (int idx = 0; idx < 1024; idx++, value += increment) { +    for (int idx = 0; idx < kPqOETFNumEntries; idx++) { +      float value = static_cast<float>(idx) / static_cast<float>(kPqOETFNumEntries - 1);        EXPECT_FLOAT_EQ(pqOetf(value), pqOetfLUT(value));      }  }  TEST_F(RecoveryMapMathTest, hlgOetfLUT) { -    float increment = 1.0 / 1024.0; -    float value = 0.0f; -    for (int idx = 0; idx < 1024; idx++, value += increment) { +    for (int idx = 0; idx < kHlgOETFNumEntries; idx++) { +      float value = static_cast<float>(idx) / static_cast<float>(kHlgOETFNumEntries - 1);        EXPECT_FLOAT_EQ(hlgOetf(value), hlgOetfLUT(value));      }  }  TEST_F(RecoveryMapMathTest, srgbInvOetfLUT) { -    float increment = 1.0 / 1024.0; -    float value = 0.0f; -    for (int idx = 0; idx < 1024; idx++, value += increment) { +    for (int idx = 0; idx < kSrgbInvOETFNumEntries; idx++) { +      float value = static_cast<float>(idx) / static_cast<float>(kSrgbInvOETFNumEntries - 1);        EXPECT_FLOAT_EQ(srgbInvOetf(value), srgbInvOetfLUT(value));      }  }  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), +  for (int boost = 1; boost <= 10; boost++) { +    jpegr_metadata metadata = { .maxContentBoost = static_cast<float>(boost), +                                .minContentBoost = 1.0f / static_cast<float>(boost) }; +    RecoveryLUT recoveryLUT(&metadata); +    for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) { +      float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1); +      EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), value, &metadata),                        applyRecoveryLUT(RgbBlack(), value, recoveryLUT)); -      EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), value, hdrRatio), +      EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), value, &metadata),                        applyRecoveryLUT(RgbWhite(), value, recoveryLUT)); -      EXPECT_RGB_NEAR(applyRecovery(RgbRed(), value, hdrRatio), +      EXPECT_RGB_NEAR(applyRecovery(RgbRed(), value, &metadata),                        applyRecoveryLUT(RgbRed(), value, recoveryLUT)); -      EXPECT_RGB_NEAR(applyRecovery(RgbGreen(), value, hdrRatio), +      EXPECT_RGB_NEAR(applyRecovery(RgbGreen(), value, &metadata),                        applyRecoveryLUT(RgbGreen(), value, recoveryLUT)); -      EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, hdrRatio), +      EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, &metadata), +                      applyRecoveryLUT(RgbBlue(), value, recoveryLUT)); +    } +  } + +  for (int boost = 1; boost <= 10; boost++) { +    jpegr_metadata metadata = { .maxContentBoost = static_cast<float>(boost), +                                .minContentBoost = 1.0f }; +    RecoveryLUT recoveryLUT(&metadata); +    for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) { +      float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1); +      EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), value, &metadata), +                      applyRecoveryLUT(RgbBlack(), value, recoveryLUT)); +      EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), value, &metadata), +                      applyRecoveryLUT(RgbWhite(), value, recoveryLUT)); +      EXPECT_RGB_NEAR(applyRecovery(RgbRed(), value, &metadata), +                      applyRecoveryLUT(RgbRed(), value, recoveryLUT)); +      EXPECT_RGB_NEAR(applyRecovery(RgbGreen(), value, &metadata), +                      applyRecoveryLUT(RgbGreen(), value, recoveryLUT)); +      EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, &metadata), +                      applyRecoveryLUT(RgbBlue(), value, recoveryLUT)); +    } +  } + +  for (int boost = 1; boost <= 10; boost++) { +    jpegr_metadata metadata = { .maxContentBoost = static_cast<float>(boost), +                                .minContentBoost = 1.0f / pow(static_cast<float>(boost), +                                                              1.0f / 3.0f) }; +    RecoveryLUT recoveryLUT(&metadata); +    for (int idx = 0; idx < kRecoveryFactorNumEntries; idx++) { +      float value = static_cast<float>(idx) / static_cast<float>(kRecoveryFactorNumEntries - 1); +      EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), value, &metadata), +                      applyRecoveryLUT(RgbBlack(), value, recoveryLUT)); +      EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), value, &metadata), +                      applyRecoveryLUT(RgbWhite(), value, recoveryLUT)); +      EXPECT_RGB_NEAR(applyRecovery(RgbRed(), value, &metadata), +                      applyRecoveryLUT(RgbRed(), value, recoveryLUT)); +      EXPECT_RGB_NEAR(applyRecovery(RgbGreen(), value, &metadata), +                      applyRecoveryLUT(RgbGreen(), value, recoveryLUT)); +      EXPECT_RGB_NEAR(applyRecovery(RgbBlue(), value, &metadata),                        applyRecoveryLUT(RgbBlue(), value, recoveryLUT));      }    } @@ -623,60 +659,121 @@ TEST_F(RecoveryMapMathTest, ColorConversionLookup) {  }  TEST_F(RecoveryMapMathTest, EncodeRecovery) { -  EXPECT_EQ(encodeRecovery(0.0f, 0.0f, 4.0f), 127); -  EXPECT_EQ(encodeRecovery(0.0f, 1.0f, 4.0f), 127); -  EXPECT_EQ(encodeRecovery(1.0f, 0.0f, 4.0f), 0); -  EXPECT_EQ(encodeRecovery(0.5f, 0.0f, 4.0f), 0); - -  EXPECT_EQ(encodeRecovery(1.0f, 1.0f, 4.0f), 127); -  EXPECT_EQ(encodeRecovery(1.0f, 4.0f, 4.0f), 255); -  EXPECT_EQ(encodeRecovery(1.0f, 5.0f, 4.0f), 255); -  EXPECT_EQ(encodeRecovery(4.0f, 1.0f, 4.0f), 0); -  EXPECT_EQ(encodeRecovery(4.0f, 0.5f, 4.0f), 0); -  EXPECT_EQ(encodeRecovery(1.0f, 2.0f, 4.0f), 191); -  EXPECT_EQ(encodeRecovery(2.0f, 1.0f, 4.0f), 63); - -  EXPECT_EQ(encodeRecovery(1.0f, 2.0f, 2.0f), 255); -  EXPECT_EQ(encodeRecovery(2.0f, 1.0f, 2.0f), 0); -  EXPECT_EQ(encodeRecovery(1.0f, 1.41421f, 2.0f), 191); -  EXPECT_EQ(encodeRecovery(1.41421f, 1.0f, 2.0f), 63); - -  EXPECT_EQ(encodeRecovery(1.0f, 8.0f, 8.0f), 255); -  EXPECT_EQ(encodeRecovery(8.0f, 1.0f, 8.0f), 0); -  EXPECT_EQ(encodeRecovery(1.0f, 2.82843f, 8.0f), 191); -  EXPECT_EQ(encodeRecovery(2.82843f, 1.0f, 8.0f), 63); +  jpegr_metadata metadata = { .maxContentBoost = 4.0f, +                              .minContentBoost = 1.0f / 4.0f }; + +  EXPECT_EQ(encodeRecovery(0.0f, 0.0f, &metadata), 127); +  EXPECT_EQ(encodeRecovery(0.0f, 1.0f, &metadata), 127); +  EXPECT_EQ(encodeRecovery(1.0f, 0.0f, &metadata), 0); +  EXPECT_EQ(encodeRecovery(0.5f, 0.0f, &metadata), 0); + +  EXPECT_EQ(encodeRecovery(1.0f, 1.0f, &metadata), 127); +  EXPECT_EQ(encodeRecovery(1.0f, 4.0f, &metadata), 255); +  EXPECT_EQ(encodeRecovery(1.0f, 5.0f, &metadata), 255); +  EXPECT_EQ(encodeRecovery(4.0f, 1.0f, &metadata), 0); +  EXPECT_EQ(encodeRecovery(4.0f, 0.5f, &metadata), 0); +  EXPECT_EQ(encodeRecovery(1.0f, 2.0f, &metadata), 191); +  EXPECT_EQ(encodeRecovery(2.0f, 1.0f, &metadata), 63); + +  metadata.maxContentBoost = 2.0f; +  metadata.minContentBoost = 1.0f / 2.0f; + +  EXPECT_EQ(encodeRecovery(1.0f, 2.0f, &metadata), 255); +  EXPECT_EQ(encodeRecovery(2.0f, 1.0f, &metadata), 0); +  EXPECT_EQ(encodeRecovery(1.0f, 1.41421f, &metadata), 191); +  EXPECT_EQ(encodeRecovery(1.41421f, 1.0f, &metadata), 63); + +  metadata.maxContentBoost = 8.0f; +  metadata.minContentBoost = 1.0f / 8.0f; + +  EXPECT_EQ(encodeRecovery(1.0f, 8.0f, &metadata), 255); +  EXPECT_EQ(encodeRecovery(8.0f, 1.0f, &metadata), 0); +  EXPECT_EQ(encodeRecovery(1.0f, 2.82843f, &metadata), 191); +  EXPECT_EQ(encodeRecovery(2.82843f, 1.0f, &metadata), 63); + +  metadata.maxContentBoost = 8.0f; +  metadata.minContentBoost = 1.0f; + +  EXPECT_EQ(encodeRecovery(0.0f, 0.0f, &metadata), 0); +  EXPECT_EQ(encodeRecovery(1.0f, 0.0f, &metadata), 0); + +  EXPECT_EQ(encodeRecovery(1.0f, 1.0f, &metadata), 0); +  EXPECT_EQ(encodeRecovery(1.0f, 8.0f, &metadata), 255); +  EXPECT_EQ(encodeRecovery(1.0f, 4.0f, &metadata), 170); +  EXPECT_EQ(encodeRecovery(1.0f, 2.0f, &metadata), 85); + +  metadata.maxContentBoost = 8.0f; +  metadata.minContentBoost = 0.5f; + +  EXPECT_EQ(encodeRecovery(0.0f, 0.0f, &metadata), 63); +  EXPECT_EQ(encodeRecovery(1.0f, 0.0f, &metadata), 0); + +  EXPECT_EQ(encodeRecovery(1.0f, 1.0f, &metadata), 63); +  EXPECT_EQ(encodeRecovery(1.0f, 8.0f, &metadata), 255); +  EXPECT_EQ(encodeRecovery(1.0f, 4.0f, &metadata), 191); +  EXPECT_EQ(encodeRecovery(1.0f, 2.0f, &metadata), 127); +  EXPECT_EQ(encodeRecovery(1.0f, 0.7071f, &metadata), 31); +  EXPECT_EQ(encodeRecovery(1.0f, 0.5f, &metadata), 0);  }  TEST_F(RecoveryMapMathTest, ApplyRecovery) { -  EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), -1.0f, 4.0f), RgbBlack()); -  EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.0f, 4.0f), RgbBlack()); -  EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 1.0f, 4.0f), RgbBlack()); - -  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 4.0f), RgbWhite() / 4.0f); -  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 4.0f), RgbWhite() / 2.0f); -  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 4.0f), RgbWhite()); -  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 4.0f), RgbWhite() * 2.0f); -  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 4.0f), RgbWhite() * 4.0f); - -  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 2.0f), RgbWhite() / 2.0f); -  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 2.0f), RgbWhite() / 1.41421f); -  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 2.0f), RgbWhite()); -  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 2.0f), RgbWhite() * 1.41421f); -  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 2.0f), RgbWhite() * 2.0f); - -  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -1.0f, 8.0f), RgbWhite() / 8.0f); -  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), -0.5f, 8.0f), RgbWhite() / 2.82843f); -  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, 8.0f), RgbWhite()); -  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, 8.0f), RgbWhite() * 2.82843f); -  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, 8.0f), RgbWhite() * 8.0f); +  jpegr_metadata metadata = { .maxContentBoost = 4.0f, +                              .minContentBoost = 1.0f / 4.0f }; + +  EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.0f, &metadata), RgbBlack()); +  EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 0.5f, &metadata), RgbBlack()); +  EXPECT_RGB_NEAR(applyRecovery(RgbBlack(), 1.0f, &metadata), RgbBlack()); + +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite() / 4.0f); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.25f, &metadata), RgbWhite() / 2.0f); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, &metadata), RgbWhite()); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.75f, &metadata), RgbWhite() * 2.0f); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 4.0f); + +  metadata.maxContentBoost = 2.0f; +  metadata.minContentBoost = 1.0f / 2.0f; + +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite() / 2.0f); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.25f, &metadata), RgbWhite() / 1.41421f); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, &metadata), RgbWhite()); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.75f, &metadata), RgbWhite() * 1.41421f); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 2.0f); + +  metadata.maxContentBoost = 8.0f; +  metadata.minContentBoost = 1.0f / 8.0f; + +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite() / 8.0f); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.25f, &metadata), RgbWhite() / 2.82843f); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, &metadata), RgbWhite()); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.75f, &metadata), RgbWhite() * 2.82843f); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f); + +  metadata.maxContentBoost = 8.0f; +  metadata.minContentBoost = 1.0f; + +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite()); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f / 3.0f, &metadata), RgbWhite() * 2.0f); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 2.0f / 3.0f, &metadata), RgbWhite() * 4.0f); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f); + +  metadata.maxContentBoost = 8.0f; +  metadata.minContentBoost = 0.5f; + +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.0f, &metadata), RgbWhite() / 2.0f); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.25f, &metadata), RgbWhite()); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.5f, &metadata), RgbWhite() * 2.0f); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 0.75f, &metadata), RgbWhite() * 4.0f); +  EXPECT_RGB_NEAR(applyRecovery(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);    Color e = {{{ 0.0f, 0.5f, 1.0f }}}; - -  EXPECT_RGB_NEAR(applyRecovery(e, -1.0f, 4.0f), e / 4.0f); -  EXPECT_RGB_NEAR(applyRecovery(e, -0.5f, 4.0f), e / 2.0f); -  EXPECT_RGB_NEAR(applyRecovery(e, 0.0f, 4.0f), e); -  EXPECT_RGB_NEAR(applyRecovery(e, 0.5f, 4.0f), e * 2.0f); -  EXPECT_RGB_NEAR(applyRecovery(e, 1.0f, 4.0f), e * 4.0f); +  metadata.maxContentBoost = 4.0f; +  metadata.minContentBoost = 1.0f / 4.0f; + +  EXPECT_RGB_NEAR(applyRecovery(e, 0.0f, &metadata), e / 4.0f); +  EXPECT_RGB_NEAR(applyRecovery(e, 0.25f, &metadata), e / 2.0f); +  EXPECT_RGB_NEAR(applyRecovery(e, 0.5f, &metadata), e); +  EXPECT_RGB_NEAR(applyRecovery(e, 0.75f, &metadata), e * 2.0f); +  EXPECT_RGB_NEAR(applyRecovery(e, 1.0f, &metadata), e * 4.0f);  }  TEST_F(RecoveryMapMathTest, GetYuv420Pixel) { @@ -785,8 +882,10 @@ TEST_F(RecoveryMapMathTest, SampleMap) {        // Instead of reimplementing the sampling algorithm, confirm that the        // sample output is within the range of the min and max of the nearest        // points. -      EXPECT_THAT(sampleMap(&image, kMapScaleFactor, x, y, idwTable), +      EXPECT_THAT(sampleMap(&image, kMapScaleFactor, x, y),                    testing::AllOf(testing::Ge(min), testing::Le(max))); +      EXPECT_EQ(sampleMap(&image, kMapScaleFactor, x, y, idwTable), +                sampleMap(&image, kMapScaleFactor, x, y));      }    }  } @@ -882,60 +981,89 @@ TEST_F(RecoveryMapMathTest, GenerateMapLuminancePq) {  }  TEST_F(RecoveryMapMathTest, ApplyMap) { -  EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, 8.0f), +  jpegr_metadata metadata = { .maxContentBoost = 8.0f, +                              .minContentBoost = 1.0f / 8.0f }; + +  EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata),                  RgbWhite() * 8.0f); -  EXPECT_RGB_EQ(Recover(YuvBlack(), 1.0f, 8.0f), +  EXPECT_RGB_EQ(Recover(YuvBlack(), 1.0f, &metadata),                  RgbBlack()); -  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 1.0f, 8.0f), +  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 1.0f, &metadata),                    RgbRed() * 8.0f); -  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 1.0f, 8.0f), +  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 1.0f, &metadata),                    RgbGreen() * 8.0f); -  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 1.0f, 8.0f), +  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 1.0f, &metadata),                    RgbBlue() * 8.0f); -  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, 8.0f), +  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75f, &metadata),                  RgbWhite() * sqrt(8.0f)); -  EXPECT_RGB_EQ(Recover(YuvBlack(), 0.5f, 8.0f), +  EXPECT_RGB_EQ(Recover(YuvBlack(), 0.75f, &metadata),                  RgbBlack()); -  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.5f, 8.0f), +  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.75f, &metadata),                    RgbRed() * sqrt(8.0f)); -  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.5f, 8.0f), +  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.75f, &metadata),                    RgbGreen() * sqrt(8.0f)); -  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.5f, 8.0f), +  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.75f, &metadata),                    RgbBlue() * sqrt(8.0f)); -  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, 8.0f), +  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata),                  RgbWhite()); -  EXPECT_RGB_EQ(Recover(YuvBlack(), 0.0f, 8.0f), +  EXPECT_RGB_EQ(Recover(YuvBlack(), 0.5f, &metadata),                  RgbBlack()); -  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.0f, 8.0f), +  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.5f, &metadata),                    RgbRed()); -  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.0f, 8.0f), +  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.5f, &metadata),                    RgbGreen()); -  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.0f, 8.0f), +  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.5f, &metadata),                    RgbBlue()); -  EXPECT_RGB_EQ(Recover(YuvWhite(), -0.5f, 8.0f), +  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata),                  RgbWhite() / sqrt(8.0f)); -  EXPECT_RGB_EQ(Recover(YuvBlack(), -0.5f, 8.0f), +  EXPECT_RGB_EQ(Recover(YuvBlack(), 0.25f, &metadata),                  RgbBlack()); -  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), -0.5f, 8.0f), +  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.25f, &metadata),                    RgbRed() / sqrt(8.0f)); -  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), -0.5f, 8.0f), +  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.25f, &metadata),                    RgbGreen() / sqrt(8.0f)); -  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), -0.5f, 8.0f), +  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.25f, &metadata),                    RgbBlue() / sqrt(8.0f)); -  EXPECT_RGB_EQ(Recover(YuvWhite(), -1.0f, 8.0f), +  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata),                  RgbWhite() / 8.0f); -  EXPECT_RGB_EQ(Recover(YuvBlack(), -1.0f, 8.0f), +  EXPECT_RGB_EQ(Recover(YuvBlack(), 0.0f, &metadata),                  RgbBlack()); -  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), -1.0f, 8.0f), +  EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.0f, &metadata),                    RgbRed() / 8.0f); -  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), -1.0f, 8.0f), +  EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.0f, &metadata),                    RgbGreen() / 8.0f); -  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), -1.0f, 8.0f), +  EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.0f, &metadata),                    RgbBlue() / 8.0f); + +  metadata.maxContentBoost = 8.0f; +  metadata.minContentBoost = 1.0f; + +  EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata), +                RgbWhite() * 8.0f); +  EXPECT_RGB_EQ(Recover(YuvWhite(), 2.0f / 3.0f, &metadata), +                RgbWhite() * 4.0f); +  EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f / 3.0f, &metadata), +                RgbWhite() * 2.0f); +  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata), +                RgbWhite()); + +  metadata.maxContentBoost = 8.0f; +  metadata.minContentBoost = 0.5f;; + +  EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata), +                RgbWhite() * 8.0f); +  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75, &metadata), +                RgbWhite() * 4.0f); +  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata), +                RgbWhite() * 2.0f); +  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata), +                RgbWhite()); +  EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata), +                RgbWhite() / 2.0f);  }  } // namespace android::recoverymap  |