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

Commit 9feb649e authored by Dichen Zhang's avatar Dichen Zhang Committed by Android (Google) Code Review
Browse files

Merge "ultrahdr: add support for stride for 420 input" into main

parents d130ae9c 43c3a805
Loading
Loading
Loading
Loading
+27 −17
Original line number Diff line number Diff line
@@ -598,14 +598,26 @@ Color applyGainLUT(Color e, float gain, GainLUT& gainLUT) {
}

Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
  size_t pixel_count = image->width * image->height;
  uint8_t* luma_data = reinterpret_cast<uint8_t*>(image->data);
  size_t luma_stride = image->luma_stride == 0 ? image->width : image->luma_stride;

  uint8_t* chroma_data;
  size_t chroma_stride;
  if (image->chroma_data == nullptr) {
     chroma_stride = luma_stride / 2;
     chroma_data = &reinterpret_cast<uint8_t*>(image->data)[luma_stride * image->height];
  } else {
     chroma_stride = image->chroma_stride;
     chroma_data = reinterpret_cast<uint8_t*>(image->chroma_data);
  }

  size_t pixel_y_idx = x + y * image->width;
  size_t pixel_uv_idx = x / 2 + (y / 2) * (image->width / 2);
  size_t offset_cr = chroma_stride * (image->height / 2);
  size_t pixel_y_idx = x + y * luma_stride;
  size_t pixel_chroma_idx = x / 2 + (y / 2) * chroma_stride;

  uint8_t y_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y_idx];
  uint8_t u_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count + pixel_uv_idx];
  uint8_t v_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_count * 5 / 4 + pixel_uv_idx];
  uint8_t y_uint = luma_data[pixel_y_idx];
  uint8_t u_uint = chroma_data[pixel_chroma_idx];
  uint8_t v_uint = chroma_data[offset_cr + pixel_chroma_idx];

  // 128 bias for UV given we are using jpeglib; see:
  // https://github.com/kornelski/libjpeg/blob/master/structure.doc
@@ -615,19 +627,17 @@ Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
}

Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
  size_t luma_stride = image->luma_stride;
  size_t chroma_stride = image->chroma_stride;
  uint16_t* luma_data = reinterpret_cast<uint16_t*>(image->data);
  uint16_t* chroma_data = reinterpret_cast<uint16_t*>(image->chroma_data);
  size_t luma_stride = image->luma_stride == 0 ? image->width : image->luma_stride;

  if (luma_stride == 0) {
    luma_stride = image->width;
  }
  if (chroma_stride == 0) {
  uint16_t* chroma_data;
  size_t chroma_stride;
  if (image->chroma_data == nullptr) {
     chroma_stride = luma_stride;
  }
  if (chroma_data == nullptr) {
     chroma_data = &reinterpret_cast<uint16_t*>(image->data)[luma_stride * image->height];
  } else {
     chroma_stride = image->chroma_stride;
     chroma_data = reinterpret_cast<uint16_t*>(image->chroma_data);
  }

  size_t pixel_y_idx = y * luma_stride + x;
+11 −8
Original line number Diff line number Diff line
@@ -49,16 +49,19 @@ struct jpegr_uncompressed_struct {

    // Values below are optional
    // Pointer to chroma data, if it's NULL, chroma plane is considered to be immediately
    // following after the luma plane.
    // Note: currently this feature is only supported for P010 image (HDR input).
    // after the luma plane.
    void* chroma_data = nullptr;
    // Strides of Y plane in number of pixels, using 0 to present uninitialized, must be
    // larger than or equal to luma width.
    // Note: currently this feature is only supported for P010 image (HDR input).
    // Stride of Y plane in number of pixels. 0 indicates the member is uninitialized. If
    // non-zero this value must be larger than or equal to luma width. If stride is
    // uninitialized then it is assumed to be equal to luma width.
    int luma_stride = 0;
    // Strides of UV plane in number of pixels, using 0 to present uninitialized, must be
    // larger than or equal to chroma width.
    // Note: currently this feature is only supported for P010 image (HDR input).
    // Stride of UV plane in number of pixels.
    // 1. If this handle points to P010 image then this value must be larger than
    //    or equal to luma width.
    // 2. If this handle points to 420 image then this value must be larger than
    //    or equal to (luma width / 2).
    // NOTE: if chroma_data is nullptr, chroma_stride is irrelevant. Just as the way,
    // chroma_data is derived from luma ptr, chroma stride is derived from luma stride.
    int chroma_stride = 0;
};

+65 −28
Original line number Diff line number Diff line
@@ -167,15 +167,18 @@ status_t JpegR::areInputArgumentsValid(jr_uncompressed_ptr uncompressed_p010_ima
    return ERROR_JPEGR_INVALID_NULL_PTR;
  }

  if (uncompressed_yuv_420_image->luma_stride != 0) {
    ALOGE("Stride is not supported for YUV420 image");
    return ERROR_JPEGR_UNSUPPORTED_FEATURE;
  if (uncompressed_yuv_420_image->luma_stride != 0
          && uncompressed_yuv_420_image->luma_stride < uncompressed_yuv_420_image->width) {
    ALOGE("Luma stride can not be smaller than width, stride=%d, width=%d",
                uncompressed_yuv_420_image->luma_stride, uncompressed_yuv_420_image->width);
    return ERROR_JPEGR_INVALID_INPUT_TYPE;
  }

  if (uncompressed_yuv_420_image->chroma_data != nullptr) {
    ALOGE("Pointer to chroma plane is not supported for YUV420 image, chroma data must"
          "be immediately after the luma data.");
    return ERROR_JPEGR_UNSUPPORTED_FEATURE;
  if (uncompressed_yuv_420_image->chroma_data != nullptr
          && uncompressed_yuv_420_image->chroma_stride < uncompressed_yuv_420_image->width / 2) {
    ALOGE("Chroma stride can not be smaller than 1/2 of the width, stride=%d, width=%d",
                uncompressed_yuv_420_image->chroma_stride, uncompressed_yuv_420_image->width);
    return ERROR_JPEGR_INVALID_INPUT_TYPE;
  }

  if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width
@@ -323,11 +326,49 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
  sp<DataStruct> icc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
                                                  uncompressed_yuv_420_image->colorGamut);

  // Convert to Bt601 YUV encoding for JPEG encode; make a copy so as to no clobber client data
  // Convert to Bt601 YUV encoding for JPEG encode and remove stride if needed;
  // make a copy so as to no clobber client data
  unique_ptr<uint8_t[]> yuv_420_bt601_data = make_unique<uint8_t[]>(
      uncompressed_yuv_420_image->width * uncompressed_yuv_420_image->height * 3 / 2);
  memcpy(yuv_420_bt601_data.get(), uncompressed_yuv_420_image->data,
         uncompressed_yuv_420_image->width * uncompressed_yuv_420_image->height * 3 / 2);
  // copy data
  {
    uint8_t* src_luma_data = reinterpret_cast<uint8_t*>(uncompressed_yuv_420_image->data);
    size_t src_luma_stride = uncompressed_yuv_420_image->luma_stride == 0
            ? uncompressed_yuv_420_image->width : uncompressed_yuv_420_image->luma_stride;
    uint8_t* src_chroma_data = reinterpret_cast<uint8_t*>(uncompressed_yuv_420_image->chroma_data);
    size_t src_chroma_stride = uncompressed_yuv_420_image->chroma_stride;
    if (uncompressed_yuv_420_image->chroma_data == nullptr) {
      src_chroma_data =
             &reinterpret_cast<uint8_t*>(uncompressed_yuv_420_image->data)[src_luma_stride
             * uncompressed_yuv_420_image->height];
    }
    if (src_chroma_stride == 0) {
      src_chroma_stride = src_luma_stride / 2;
    }
    // copy luma
    for (size_t i = 0; i < uncompressed_yuv_420_image->height; i++) {
      memcpy(yuv_420_bt601_data.get() + i * uncompressed_yuv_420_image->width,
             src_luma_data + i * src_luma_stride,
             uncompressed_yuv_420_image->width);
    }
    // copy cb
    for (size_t i = 0; i < uncompressed_yuv_420_image->height / 2; i++) {
      memcpy(yuv_420_bt601_data.get()
                   + uncompressed_yuv_420_image->width * uncompressed_yuv_420_image->height
                   + i * uncompressed_yuv_420_image->width / 2,
             src_chroma_data + i * src_chroma_stride,
             uncompressed_yuv_420_image->width / 2);
    }
    // copy cr
    for (size_t i = 0; i < uncompressed_yuv_420_image->height / 2; i++) {
      memcpy(yuv_420_bt601_data.get()
                   + uncompressed_yuv_420_image->width * uncompressed_yuv_420_image->height * 5 / 4
                   + i * uncompressed_yuv_420_image->width / 2,
             src_chroma_data + src_chroma_stride * (uncompressed_yuv_420_image->height / 2)
                    + i * src_chroma_stride,
             uncompressed_yuv_420_image->width / 2);
    }
  }

  jpegr_uncompressed_struct yuv_420_bt601_image = {
    yuv_420_bt601_data.get(), uncompressed_yuv_420_image->width, uncompressed_yuv_420_image->height,
@@ -792,16 +833,15 @@ status_t JpegR::generateGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,

  size_t image_width = uncompressed_yuv_420_image->width;
  size_t image_height = uncompressed_yuv_420_image->height;
  size_t map_width = image_width / kMapDimensionScaleFactor;
  size_t map_height = image_height / kMapDimensionScaleFactor;
  size_t map_stride = static_cast<size_t>(
          floor((map_width + kJpegBlock - 1) / kJpegBlock)) * kJpegBlock;
  size_t map_height_aligned = ((map_height + 1) >> 1) << 1;

  dest->width = map_stride;
  dest->height = map_height_aligned;
  size_t map_width = static_cast<size_t>(
          floor((image_width + kMapDimensionScaleFactor - 1) / kMapDimensionScaleFactor));
  size_t map_height = static_cast<size_t>(
          floor((image_height + kMapDimensionScaleFactor - 1) / kMapDimensionScaleFactor));

  dest->width = map_width;
  dest->height = map_height;
  dest->colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
  dest->data = new uint8_t[map_stride * map_height_aligned];
  dest->data = new uint8_t[map_width * map_height];
  std::unique_ptr<uint8_t[]> map_data;
  map_data.reset(reinterpret_cast<uint8_t*>(dest->data));

@@ -895,11 +935,9 @@ status_t JpegR::generateGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
                                       luminanceFn, sdrYuvToRgbFn, hdrYuvToRgbFn, hdr_white_nits,
                                       log2MinBoost, log2MaxBoost, &jobQueue]() -> void {
    size_t rowStart, rowEnd;
    size_t dest_map_width = uncompressed_yuv_420_image->width / kMapDimensionScaleFactor;
    size_t dest_map_stride = dest->width;
    while (jobQueue.dequeueJob(rowStart, rowEnd)) {
      for (size_t y = rowStart; y < rowEnd; ++y) {
        for (size_t x = 0; x < dest_map_width; ++x) {
        for (size_t x = 0; x < dest->width; ++x) {
          Color sdr_yuv_gamma =
              sampleYuv420(uncompressed_yuv_420_image, kMapDimensionScaleFactor, x, y);
          Color sdr_rgb_gamma = sdrYuvToRgbFn(sdr_yuv_gamma);
@@ -917,7 +955,7 @@ status_t JpegR::generateGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
          hdr_rgb = hdrGamutConversionFn(hdr_rgb);
          float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;

          size_t pixel_idx = x + y * dest_map_stride;
          size_t pixel_idx = x + y * dest->width;
          reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
              encodeGain(sdr_y_nits, hdr_y_nits, metadata, log2MinBoost, log2MaxBoost);
        }
@@ -981,11 +1019,10 @@ status_t JpegR::applyGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
  // TODO: remove once map scaling factor is computed based on actual map dims
  size_t image_width = uncompressed_yuv_420_image->width;
  size_t image_height = uncompressed_yuv_420_image->height;
  size_t map_width = image_width / kMapDimensionScaleFactor;
  size_t map_height = image_height / kMapDimensionScaleFactor;
  map_width = static_cast<size_t>(
          floor((map_width + kJpegBlock - 1) / kJpegBlock)) * kJpegBlock;
  map_height = ((map_height + 1) >> 1) << 1;
  size_t map_width = static_cast<size_t>(
          floor((image_width + kMapDimensionScaleFactor - 1) / kMapDimensionScaleFactor));
  size_t map_height = static_cast<size_t>(
          floor((image_height + kMapDimensionScaleFactor - 1) / kMapDimensionScaleFactor));
  if (map_width != uncompressed_gain_map->width
   || map_height != uncompressed_gain_map->height) {
    ALOGE("gain map dimensions and primary image dimensions are not to scale");
+195 −11
Original line number Diff line number Diff line
@@ -728,23 +728,23 @@ TEST(JpegRTest, EncodeAPI1WithInvalidArgs) {

    rawImg420->width = kWidth;
    rawImg420->height = kHeight;
    rawImg420->luma_stride = kWidth;
    rawImg420->luma_stride = kWidth - 2;
    ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
                                  ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
                                  jpgImg.getImageHandle(), kQuality, nullptr),
              OK)
            << "fail, API allows luma stride for 420";
            << "fail, API allows bad luma stride for 420";

    rawImg420->width = kWidth;
    rawImg420->height = kHeight;
    rawImg420->luma_stride = 0;
    rawImg420->chroma_data = rawImgP010->data;
    rawImg420->chroma_stride = kWidth;
    rawImg420->chroma_stride = kWidth / 2 - 2;
    ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
                                  ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
                                  jpgImg.getImageHandle(), kQuality, nullptr),
              OK)
            << "fail, API allows bad chroma pointer for 420";
            << "fail, API allows bad chroma stride for 420";
  }
}

@@ -1021,23 +1021,23 @@ TEST(JpegRTest, EncodeAPI2WithInvalidArgs) {

    rawImg420->width = kWidth;
    rawImg420->height = kHeight;
    rawImg420->luma_stride = kWidth;
    rawImg420->luma_stride = kWidth - 2;
    ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
                                  ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
                                  jpgImg.getImageHandle()),
              OK)
            << "fail, API allows luma stride for 420";
            << "fail, API allows bad luma stride for 420";

    rawImg420->width = kWidth;
    rawImg420->height = kHeight;
    rawImg420->luma_stride = 0;
    rawImg420->chroma_data = rawImgP010->data;
    rawImg420->chroma_stride = kWidth;
    rawImg420->chroma_stride = kWidth / 2 - 2;
    ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
                                  ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
                                  jpgImg.getImageHandle()),
              OK)
            << "fail, API allows bad chroma pointer for 420";
            << "fail, API allows bad chroma stride for 420";
  }
}

@@ -1445,6 +1445,24 @@ TEST(JpegRTest, EncodeAPI0AndDecodeTest) {
    ASSERT_EQ(jpg1->length, jpg2->length);
    ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
  }
  // encode with luma and chroma stride set but no chroma ptr
  {
    UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010);
    ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
    ASSERT_TRUE(rawImg2.setImageStride(kImageWidth, kImageWidth + 256));
    ASSERT_TRUE(rawImg2.allocateMemory());
    ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
    UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
    ASSERT_TRUE(jpgImg2.allocateMemory());
    ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
                                  ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
                                  jpgImg2.getImageHandle(), kQuality, nullptr),
              OK);
    auto jpg1 = jpgImg.getImageHandle();
    auto jpg2 = jpgImg2.getImageHandle();
    ASSERT_EQ(jpg1->length, jpg2->length);
    ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
  }

  auto jpg1 = jpgImg.getImageHandle();
#ifdef DUMP_OUTPUT
@@ -1473,7 +1491,7 @@ TEST(JpegRTest, EncodeAPI1AndDecodeTest) {
                                ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
                                jpgImg.getImageHandle(), kQuality, nullptr),
            OK);
  // encode with luma stride set
  // encode with luma stride set p010
  {
    UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
    ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
@@ -1491,7 +1509,7 @@ TEST(JpegRTest, EncodeAPI1AndDecodeTest) {
    ASSERT_EQ(jpg1->length, jpg2->length);
    ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
  }
  // encode with luma and chroma stride set
  // encode with luma and chroma stride set p010
  {
    UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
    ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
@@ -1510,7 +1528,7 @@ TEST(JpegRTest, EncodeAPI1AndDecodeTest) {
    ASSERT_EQ(jpg1->length, jpg2->length);
    ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
  }
  // encode with chroma stride set
  // encode with chroma stride set p010
  {
    UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
    ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
@@ -1529,6 +1547,98 @@ TEST(JpegRTest, EncodeAPI1AndDecodeTest) {
    ASSERT_EQ(jpg1->length, jpg2->length);
    ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
  }
  // encode with luma and chroma stride set but no chroma ptr p010
  {
    UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
    ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
    ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 64, kImageWidth + 256));
    ASSERT_TRUE(rawImg2P010.allocateMemory());
    ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
    UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
    ASSERT_TRUE(jpgImg2.allocateMemory());
    ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
                                  ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
                                  jpgImg2.getImageHandle(), kQuality, nullptr),
              OK);
    auto jpg1 = jpgImg.getImageHandle();
    auto jpg2 = jpgImg2.getImageHandle();
    ASSERT_EQ(jpg1->length, jpg2->length);
    ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
  }
  // encode with luma stride set 420
  {
    UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
    ASSERT_TRUE(rawImg2420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
    ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 128, 0));
    ASSERT_TRUE(rawImg2420.allocateMemory());
    ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
    UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
    ASSERT_TRUE(jpgImg2.allocateMemory());
    ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(),
                                  ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
                                  jpgImg2.getImageHandle(), kQuality, nullptr),
              OK);
    auto jpg1 = jpgImg.getImageHandle();
    auto jpg2 = jpgImg2.getImageHandle();
    ASSERT_EQ(jpg1->length, jpg2->length);
    ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
  }
  // encode with luma and chroma stride set 420
  {
    UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
    ASSERT_TRUE(rawImg2420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
    ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 128, kImageWidth / 2 + 256));
    ASSERT_TRUE(rawImg2420.setChromaMode(false));
    ASSERT_TRUE(rawImg2420.allocateMemory());
    ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
    UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
    ASSERT_TRUE(jpgImg2.allocateMemory());
    ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(),
                                  ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
                                  jpgImg2.getImageHandle(), kQuality, nullptr),
              OK);
    auto jpg1 = jpgImg.getImageHandle();
    auto jpg2 = jpgImg2.getImageHandle();
    ASSERT_EQ(jpg1->length, jpg2->length);
    ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
  }
  // encode with chroma stride set 420
  {
    UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
    ASSERT_TRUE(rawImg2420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
    ASSERT_TRUE(rawImg2420.setImageStride(0, kImageWidth / 2 + 64));
    ASSERT_TRUE(rawImg2420.setChromaMode(false));
    ASSERT_TRUE(rawImg2420.allocateMemory());
    ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
    UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
    ASSERT_TRUE(jpgImg2.allocateMemory());
    ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(),
                                  ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
                                  jpgImg2.getImageHandle(), kQuality, nullptr),
              OK);
    auto jpg1 = jpgImg.getImageHandle();
    auto jpg2 = jpgImg2.getImageHandle();
    ASSERT_EQ(jpg1->length, jpg2->length);
    ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
  }
  // encode with luma and chroma stride set but no chroma ptr 420
  {
    UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
    ASSERT_TRUE(rawImg2420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
    ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 128, kImageWidth / 2 + 64));
    ASSERT_TRUE(rawImg2420.allocateMemory());
    ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
    UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
    ASSERT_TRUE(jpgImg2.allocateMemory());
    ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(),
                                  ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
                                  jpgImg2.getImageHandle(), kQuality, nullptr),
              OK);
    auto jpg1 = jpgImg.getImageHandle();
    auto jpg2 = jpgImg2.getImageHandle();
    ASSERT_EQ(jpg1->length, jpg2->length);
    ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
  }

  auto jpg1 = jpgImg.getImageHandle();

@@ -1618,6 +1728,62 @@ TEST(JpegRTest, EncodeAPI2AndDecodeTest) {
    ASSERT_EQ(jpg1->length, jpg2->length);
    ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
  }
  // encode with luma stride set
  {
    UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
    ASSERT_TRUE(rawImg2420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
    ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 128, 0));
    ASSERT_TRUE(rawImg2420.allocateMemory());
    ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
    UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
    ASSERT_TRUE(jpgImg2.allocateMemory());
    ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(), sdr,
                                  ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
                                  jpgImg2.getImageHandle()),
              OK);
    auto jpg1 = jpgImg.getImageHandle();
    auto jpg2 = jpgImg2.getImageHandle();
    ASSERT_EQ(jpg1->length, jpg2->length);
    ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
  }
  // encode with luma and chroma stride set
  {
    UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
    ASSERT_TRUE(rawImg2420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
    ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 128, kImageWidth + 256));
    ASSERT_TRUE(rawImg2420.setChromaMode(false));
    ASSERT_TRUE(rawImg2420.allocateMemory());
    ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
    UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
    ASSERT_TRUE(jpgImg2.allocateMemory());
    ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(), sdr,
                                  ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
                                  jpgImg2.getImageHandle()),
              OK);
    auto jpg1 = jpgImg.getImageHandle();
    auto jpg2 = jpgImg2.getImageHandle();
    ASSERT_EQ(jpg1->length, jpg2->length);
    ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
  }
  // encode with chroma stride set
  {
    UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
    ASSERT_TRUE(rawImg2420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
    ASSERT_TRUE(rawImg2420.setImageStride(0, kImageWidth + 64));
    ASSERT_TRUE(rawImg2420.setChromaMode(false));
    ASSERT_TRUE(rawImg2420.allocateMemory());
    ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
    UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
    ASSERT_TRUE(jpgImg2.allocateMemory());
    ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(), sdr,
                                  ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
                                  jpgImg2.getImageHandle()),
              OK);
    auto jpg1 = jpgImg.getImageHandle();
    auto jpg2 = jpgImg2.getImageHandle();
    ASSERT_EQ(jpg1->length, jpg2->length);
    ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
  }

  auto jpg1 = jpgImg.getImageHandle();

@@ -1703,6 +1869,24 @@ TEST(JpegRTest, EncodeAPI3AndDecodeTest) {
    ASSERT_EQ(jpg1->length, jpg2->length);
    ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
  }
  // encode with luma and chroma stride set and no chroma ptr
  {
    UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
    ASSERT_TRUE(rawImg2P010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
    ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 32, kImageWidth + 256));
    ASSERT_TRUE(rawImg2P010.allocateMemory());
    ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
    UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
    ASSERT_TRUE(jpgImg2.allocateMemory());
    ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
                                  ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
                                  jpgImg2.getImageHandle()),
              OK);
    auto jpg1 = jpgImg.getImageHandle();
    auto jpg2 = jpgImg2.getImageHandle();
    ASSERT_EQ(jpg1->length, jpg2->length);
    ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
  }

  auto jpg1 = jpgImg.getImageHandle();