Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 42dbfb65 authored by Dichen Zhang's avatar Dichen Zhang Committed by Automerger Merge Worker
Browse files

Merge "jpegr: make output color format configurable for decoder" into udc-dev am: 56ebd52b

parents 3995ab13 56ebd52b
Loading
Loading
Loading
Loading
+16 −9
Original line number Diff line number Diff line
@@ -38,6 +38,14 @@ typedef enum {
  JPEGR_TF_SRGB = 3,
} jpegr_transfer_function;

// Target output formats for decoder
typedef enum {
  JPEGR_OUTPUT_SDR,          // SDR in RGBA_8888 color format
  JPEGR_OUTPUT_HDR_LINEAR,   // HDR in F16 color format (linear)
  JPEGR_OUTPUT_HDR_PQ,       // HDR in RGBA_1010102 color format (PQ transfer function)
  JPEGR_OUTPUT_HDR_HLG,      // HDR in RGBA_1010102 color format (HLG transfer function)
} jpegr_output_format;

struct jpegr_info_struct {
    size_t width;
    size_t height;
@@ -195,20 +203,15 @@ public:
     * @param compressed_jpegr_image compressed JPEGR image
     * @param dest destination of the uncompressed JPEGR image
     * @param exif destination of the decoded EXIF metadata.
     * @param request_sdr flag that request SDR output. If set to true, decoder will only decode
     *                    the primary image which is SDR. Setting of request_sdr and input source
     *                    (HDR or SDR) can be found in the table below:
     *                    |  input source  |  request_sdr  |  output of decoding  |
     *                    |       HDR      |     true      |          SDR         |
     *                    |       HDR      |     false     |          HDR         |
     *                    |       SDR      |     true      |          SDR         |
     *                    |       SDR      |     false     |          SDR         |
     * @param output_format flag for setting output color format. if set to
     *                      {@code JPEGR_OUTPUT_SDR}, decoder will only decode the primary image
     *                      which is SDR. Default value is JPEGR_OUTPUT_HDR_LINEAR.
     * @return NO_ERROR if decoding succeeds, error code if error occurs.
     */
    status_t decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
                         jr_uncompressed_ptr dest,
                         jr_exif_ptr exif = nullptr,
                         bool request_sdr = false);
                         jpegr_output_format output_format = JPEGR_OUTPUT_HDR_LINEAR);

    /*
    * Gets Info from JPEGR file without decoding it.
@@ -249,12 +252,16 @@ protected:
     * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
     * @param uncompressed_recovery_map uncompressed recovery map
     * @param metadata JPEG/R metadata extracted from XMP.
     * @param output_format flag for setting output color format. if set to
     *                      {@code JPEGR_OUTPUT_SDR}, decoder will only decode the primary image
     *                      which is SDR. Default value is JPEGR_OUTPUT_HDR_LINEAR.
     * @param dest reconstructed HDR image
     * @return NO_ERROR if calculation succeeds, error code if error occurs.
     */
    status_t applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
                              jr_uncompressed_ptr uncompressed_recovery_map,
                              jr_metadata_ptr metadata,
                              jpegr_output_format output_format,
                              jr_uncompressed_ptr dest);

private:
+15 −0
Original line number Diff line number Diff line
@@ -115,6 +115,14 @@ inline Color operator/(const Color& lhs, const float rhs) {
  return temp /= rhs;
}

inline uint16_t floatToHalf(float f) {
  uint32_t x = *((uint32_t*)&f);
  uint16_t h = ((x >> 16) & 0x8000)
             | ((((x & 0x7f800000) - 0x38000000) >> 13) & 0x7c00)
             | ((x >> 13) & 0x03ff);
  return h;
}

constexpr size_t kRecoveryFactorPrecision = 10;
constexpr size_t kRecoveryFactorNumEntries = 1 << kRecoveryFactorPrecision;
struct RecoveryLUT {
@@ -392,6 +400,13 @@ float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size
 */
uint32_t colorToRgba1010102(Color e_gamma);

/*
 * Convert from Color to F16.
 *
 * Alpha always set to 1.0.
 */
uint64_t colorToRgbaF16(Color e_gamma);

} // namespace android::jpegrecoverymap

#endif // ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H
+42 −14
Original line number Diff line number Diff line
@@ -351,14 +351,14 @@ status_t JpegR::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image, jr_info_p
status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
                            jr_uncompressed_ptr dest,
                            jr_exif_ptr exif,
                            bool request_sdr) {
                            jpegr_output_format output_format) {
  if (compressed_jpegr_image == nullptr || dest == nullptr) {
    return ERROR_JPEGR_INVALID_NULL_PTR;
  }
  // TODO: fill EXIF data
  (void) exif;

  if (request_sdr) {
  if (output_format == JPEGR_OUTPUT_SDR) {
    JpegDecoderHelper jpeg_decoder;
    if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length,
                                      true)) {
@@ -404,7 +404,7 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
    return ERROR_JPEGR_DECODE_ERROR;
  }

  JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
  JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, output_format, dest));
  return NO_ERROR;
}

@@ -639,6 +639,7 @@ status_t JpegR::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_ima
status_t JpegR::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
                                 jr_uncompressed_ptr uncompressed_recovery_map,
                                 jr_metadata_ptr metadata,
                                 jpegr_output_format output_format,
                                 jr_uncompressed_ptr dest) {
  if (uncompressed_yuv_420_image == nullptr
   || uncompressed_recovery_map == nullptr
@@ -654,18 +655,12 @@ status_t JpegR::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,

  JobQueue jobQueue;
  std::function<void()> applyRecMap = [uncompressed_yuv_420_image, uncompressed_recovery_map,
                                       metadata, dest, &jobQueue, &idwTable,
                                       metadata, dest, &jobQueue, &idwTable, output_format,
                                       &recoveryLUT]() -> void {
    const float hdr_ratio = metadata->maxContentBoost;
    size_t width = uncompressed_yuv_420_image->width;
    size_t height = uncompressed_yuv_420_image->height;

#if USE_HLG_OETF_LUT
    ColorTransformFn hdrOetf = hlgOetfLUT;
#else
    ColorTransformFn hdrOetf = hlgOetf;
#endif

    size_t rowStart, rowEnd;
    while (jobQueue.dequeueJob(rowStart, rowEnd)) {
      for (size_t y = rowStart; y < rowEnd; ++y) {
@@ -693,11 +688,44 @@ status_t JpegR::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
#else
          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);

          rgb_hdr = rgb_hdr / metadata->maxContentBoost;
          size_t pixel_idx = x + y * width;
          reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba1010102;

          switch (output_format) {
            case JPEGR_OUTPUT_HDR_LINEAR:
            {
              uint64_t rgba_f16 = colorToRgbaF16(rgb_hdr);
              reinterpret_cast<uint64_t*>(dest->data)[pixel_idx] = rgba_f16;
              break;
            }
            case JPEGR_OUTPUT_HDR_HLG:
            {
#if USE_HLG_OETF_LUT
              ColorTransformFn hdrOetf = hlgOetfLUT;
#else
              ColorTransformFn hdrOetf = hlgOetf;
#endif
              Color rgb_gamma_hdr = hdrOetf(rgb_hdr);
              uint32_t rgba_1010102 = colorToRgba1010102(rgb_gamma_hdr);
              reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba_1010102;
              break;
            }
            case JPEGR_OUTPUT_HDR_PQ:
            {
#if USE_HLG_OETF_LUT
              ColorTransformFn hdrOetf = pqOetfLUT;
#else
              ColorTransformFn hdrOetf = pqOetf;
#endif
              Color rgb_gamma_hdr = hdrOetf(rgb_hdr);
              uint32_t rgba_1010102 = colorToRgba1010102(rgb_gamma_hdr);
              reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba_1010102;
              break;
            }
            default:
            {}
              // Should be impossible to hit after input validation.
          }
        }
      }
    }
+7 −0
Original line number Diff line number Diff line
@@ -631,4 +631,11 @@ uint32_t colorToRgba1010102(Color e_gamma) {
       | (0x3 << 30);  // Set alpha to 1.0
}

uint64_t colorToRgbaF16(Color e_gamma) {
  return (uint64_t) floatToHalf(e_gamma.r)
       | (((uint64_t) floatToHalf(e_gamma.g)) << 16)
       | (((uint64_t) floatToHalf(e_gamma.b)) << 32)
       | (((uint64_t) floatToHalf(1.0f)) << 48);
}

} // namespace android::jpegrecoverymap
+10 −10
Original line number Diff line number Diff line
@@ -152,7 +152,7 @@ void JpegRBenchmark::BenchmarkApplyRecoveryMap(jr_uncompressed_ptr yuv420Image,

  timerStart(&applyRecMapTime);
  for (auto i = 0; i < kProfileCount; i++) {
      ASSERT_EQ(OK, applyRecoveryMap(yuv420Image, map, metadata, dest));
      ASSERT_EQ(OK, applyRecoveryMap(yuv420Image, map, metadata, JPEGR_OUTPUT_HDR_HLG, dest));
  }
  timerStop(&applyRecMapTime);

@@ -170,7 +170,7 @@ TEST_F(JpegRTest, build) {
  jpegRCodec.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
                         nullptr);
  jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0), nullptr);
  jpegRCodec.decodeJPEGR(nullptr, nullptr, nullptr, false);
  jpegRCodec.decodeJPEGR(nullptr, nullptr, nullptr);
}

TEST_F(JpegRTest, writeXmpThenRead) {
@@ -228,7 +228,7 @@ TEST_F(JpegRTest, encodeFromP010ThenDecode) {
  }

  jpegr_uncompressed_struct decodedJpegR;
  int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
  int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
  decodedJpegR.data = malloc(decodedJpegRSize);
  ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
  if (ret != OK) {
@@ -236,7 +236,7 @@ TEST_F(JpegRTest, encodeFromP010ThenDecode) {
  }
  if (SAVE_DECODING_RESULT) {
    // Output image data to file
    std::string filePath = "/sdcard/Documents/decoded_from_p010_input.rgb10";
    std::string filePath = "/sdcard/Documents/decoded_from_p010_input.rgb";
    std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
    if (!imageFile.is_open()) {
      ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
@@ -289,7 +289,7 @@ TEST_F(JpegRTest, encodeFromRawHdrAndSdrThenDecode) {
  }

  jpegr_uncompressed_struct decodedJpegR;
  int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
  int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
  decodedJpegR.data = malloc(decodedJpegRSize);
  ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
  if (ret != OK) {
@@ -297,7 +297,7 @@ TEST_F(JpegRTest, encodeFromRawHdrAndSdrThenDecode) {
  }
  if (SAVE_DECODING_RESULT) {
    // Output image data to file
    std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_input.rgb10";
    std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_input.rgb";
    std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
    if (!imageFile.is_open()) {
      ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
@@ -354,7 +354,7 @@ TEST_F(JpegRTest, encodeFromRawHdrAndSdrAndJpegThenDecode) {
  }

  jpegr_uncompressed_struct decodedJpegR;
  int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
  int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
  decodedJpegR.data = malloc(decodedJpegRSize);
  ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
  if (ret != OK) {
@@ -362,7 +362,7 @@ TEST_F(JpegRTest, encodeFromRawHdrAndSdrAndJpegThenDecode) {
  }
  if (SAVE_DECODING_RESULT) {
    // Output image data to file
    std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_jpeg_input.rgb10";
    std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_jpeg_input.rgb";
    std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
    if (!imageFile.is_open()) {
      ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
@@ -435,7 +435,7 @@ TEST_F(JpegRTest, encodeFromJpegThenDecode) {
  }

  jpegr_uncompressed_struct decodedJpegR;
  int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
  int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8;
  decodedJpegR.data = malloc(decodedJpegRSize);
  ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR);
  if (ret != OK) {
@@ -443,7 +443,7 @@ TEST_F(JpegRTest, encodeFromJpegThenDecode) {
  }
  if (SAVE_DECODING_RESULT) {
    // Output image data to file
    std::string filePath = "/sdcard/Documents/decoded_from_p010_jpeg_input.rgb10";
    std::string filePath = "/sdcard/Documents/decoded_from_p010_jpeg_input.rgb";
    std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
    if (!imageFile.is_open()) {
      ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());