/* * Copyright 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #define RAW_P010_IMAGE "/sdcard/Documents/raw_p010_image.p010" #define RAW_P010_IMAGE_WITH_STRIDE "/sdcard/Documents/raw_p010_image_with_stride.p010" #define RAW_YUV420_IMAGE "/sdcard/Documents/raw_yuv420_image.yuv420" #define JPEG_IMAGE "/sdcard/Documents/jpeg_image.jpg" #define TEST_IMAGE_WIDTH 1280 #define TEST_IMAGE_HEIGHT 720 #define TEST_IMAGE_STRIDE 1288 #define DEFAULT_JPEG_QUALITY 90 #define SAVE_ENCODING_RESULT true #define SAVE_DECODING_RESULT true #define SAVE_INPUT_RGBA true namespace android::ultrahdr { struct Timer { struct timeval StartingTime; struct timeval EndingTime; struct timeval ElapsedMicroseconds; }; void timerStart(Timer *t) { gettimeofday(&t->StartingTime, nullptr); } 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) { struct stat st; if (fstat(fd, &st) < 0) { ALOGW("%s : fstat failed", __func__); return 0; } return st.st_size; // bytes } static bool loadFile(const char filename[], void*& result, int* fileLength) { int fd = open(filename, O_CLOEXEC); if (fd < 0) { return false; } int length = getFileSize(fd); if (length == 0) { close(fd); return false; } if (fileLength != nullptr) { *fileLength = length; } result = malloc(length); if (read(fd, result, length) != static_cast(length)) { close(fd); return false; } close(fd); return true; } static bool loadP010Image(const char *filename, jr_uncompressed_ptr img, bool isUVContiguous) { int fd = open(filename, O_CLOEXEC); if (fd < 0) { return false; } const int bpp = 2; int lumaStride = img->luma_stride == 0 ? img->width : img->luma_stride; int lumaSize = bpp * lumaStride * img->height; int chromaSize = bpp * (img->height / 2) * (isUVContiguous ? lumaStride : img->chroma_stride); img->data = malloc(lumaSize + (isUVContiguous ? chromaSize : 0)); if (img->data == nullptr) { ALOGE("loadP010Image(): failed to allocate memory for luma data."); return false; } uint8_t *mem = static_cast(img->data); for (int i = 0; i < img->height; i++) { if (read(fd, mem, img->width * bpp) != img->width * bpp) { close(fd); return false; } mem += lumaStride * bpp; } int chromaStride = lumaStride; if (!isUVContiguous) { img->chroma_data = malloc(chromaSize); if (img->chroma_data == nullptr) { ALOGE("loadP010Image(): failed to allocate memory for chroma data."); return false; } mem = static_cast(img->chroma_data); chromaStride = img->chroma_stride; } for (int i = 0; i < img->height / 2; i++) { if (read(fd, mem, img->width * bpp) != img->width * bpp) { close(fd); return false; } mem += chromaStride * bpp; } close(fd); return true; } class JpegRTest : public testing::Test { public: JpegRTest(); ~JpegRTest(); protected: virtual void SetUp(); virtual void TearDown(); struct jpegr_uncompressed_struct mRawP010Image{}; struct jpegr_uncompressed_struct mRawP010ImageWithStride{}; struct jpegr_uncompressed_struct mRawP010ImageWithChromaData{}; struct jpegr_uncompressed_struct mRawYuv420Image{}; struct jpegr_compressed_struct mJpegImage{}; }; JpegRTest::JpegRTest() {} JpegRTest::~JpegRTest() {} void JpegRTest::SetUp() {} void JpegRTest::TearDown() { free(mRawP010Image.data); free(mRawP010Image.chroma_data); free(mRawP010ImageWithStride.data); free(mRawP010ImageWithStride.chroma_data); free(mRawP010ImageWithChromaData.data); free(mRawP010ImageWithChromaData.chroma_data); free(mRawYuv420Image.data); free(mJpegImage.data); } class JpegRBenchmark : public JpegR { public: void BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr p010Image, ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr map); void BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map, ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr dest); private: const int kProfileCount = 10; }; void JpegRBenchmark::BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr p010Image, ultrahdr_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, generateGainMap( yuv420Image, p010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, metadata, map)); if (i != kProfileCount - 1) delete[] static_cast(map->data); } timerStop(&genRecMapTime); ALOGE("Generate Gain Map:- Res = %i x %i, time = %f ms", yuv420Image->width, yuv420Image->height, elapsedTime(&genRecMapTime) / (kProfileCount * 1000.f)); } void JpegRBenchmark::BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map, ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr dest) { Timer applyRecMapTime; timerStart(&applyRecMapTime); for (auto i = 0; i < kProfileCount; i++) { ASSERT_EQ(OK, applyGainMap(yuv420Image, map, metadata, ULTRAHDR_OUTPUT_HDR_HLG, metadata->maxContentBoost /* displayBoost */, dest)); } timerStop(&applyRecMapTime); ALOGE("Apply Gain Map:- Res = %i x %i, time = %f ms", yuv420Image->width, yuv420Image->height, elapsedTime(&applyRecMapTime) / (kProfileCount * 1000.f)); } TEST_F(JpegRTest, build) { // Force all of the gain map lib to be linked by calling all public functions. JpegR jpegRCodec; jpegRCodec.encodeJPEGR(nullptr, static_cast(0), nullptr, 0, nullptr); jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast(0), nullptr, 0, nullptr); jpegRCodec.encodeJPEGR(nullptr, nullptr, nullptr, static_cast(0), nullptr); jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast(0), nullptr); jpegRCodec.decodeJPEGR(nullptr, nullptr); } /* Test Encode API-0 invalid arguments */ TEST_F(JpegRTest, encodeAPI0ForInvalidArgs) { int ret; // we are not really compressing anything so lets keep allocs to a minimum jpegr_compressed_struct jpegR; jpegR.maxLength = 16 * sizeof(uint8_t); jpegR.data = malloc(jpegR.maxLength); JpegR jpegRCodec; // we are not really compressing anything so lets keep allocs to a minimum mRawP010ImageWithStride.data = malloc(16); mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE; mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100; // test quality factor EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, -1, nullptr)) << "fail, API allows bad jpeg quality factor"; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, 101, nullptr)) << "fail, API allows bad jpeg quality factor"; // test hdr transfer function EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function"; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, static_cast(ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1), &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function"; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, static_cast(-10), &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function"; // test dest EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr dest"; // test p010 input EXPECT_NE(OK, jpegRCodec.encodeJPEGR( nullptr, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr p010 image"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad p010 color gamut"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.colorGamut = static_cast( ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1); EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad p010 color gamut"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH - 1; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image width"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT - 1; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image height"; mRawP010ImageWithStride.width = 0; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image width"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = 0; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image height"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH - 2; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad luma stride"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE; mRawP010ImageWithStride.chroma_data = mRawP010ImageWithStride.data; mRawP010ImageWithStride.chroma_stride = TEST_IMAGE_WIDTH - 2; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad chroma stride"; mRawP010ImageWithStride.chroma_data = nullptr; free(jpegR.data); } /* Test Encode API-1 invalid arguments */ TEST_F(JpegRTest, encodeAPI1ForInvalidArgs) { int ret; // we are not really compressing anything so lets keep allocs to a minimum jpegr_compressed_struct jpegR; jpegR.maxLength = 16 * sizeof(uint8_t); jpegR.data = malloc(jpegR.maxLength); JpegR jpegRCodec; // we are not really compressing anything so lets keep allocs to a minimum mRawP010ImageWithStride.data = malloc(16); mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE; mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100; // we are not really compressing anything so lets keep allocs to a minimum mRawYuv420Image.data = malloc(16); mRawYuv420Image.width = TEST_IMAGE_WIDTH; mRawYuv420Image.height = TEST_IMAGE_HEIGHT; mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709; // test quality factor EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, -1, nullptr)) << "fail, API allows bad jpeg quality factor"; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, 101, nullptr)) << "fail, API allows bad jpeg quality factor"; // test hdr transfer function EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function"; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, static_cast(ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1), &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function"; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, static_cast(-10), &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad hdr transfer function"; // test dest EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr dest"; // test p010 input EXPECT_NE(OK, jpegRCodec.encodeJPEGR( nullptr, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr p010 image"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad p010 color gamut"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.colorGamut = static_cast( ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1); EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad p010 color gamut"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH - 1; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image width"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT - 1; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image height"; mRawP010ImageWithStride.width = 0; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image width"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = 0; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad image height"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH - 2; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad luma stride"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE; mRawP010ImageWithStride.chroma_data = mRawP010ImageWithStride.data; mRawP010ImageWithStride.chroma_stride = TEST_IMAGE_WIDTH - 2; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad chroma stride"; // test 420 input mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE; mRawP010ImageWithStride.chroma_data = nullptr; mRawP010ImageWithStride.chroma_stride = 0; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, nullptr, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows nullptr for 420 image"; mRawYuv420Image.width = TEST_IMAGE_WIDTH; mRawYuv420Image.height = TEST_IMAGE_HEIGHT - 2; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad 420 image width"; mRawYuv420Image.width = TEST_IMAGE_WIDTH - 2; mRawYuv420Image.height = TEST_IMAGE_HEIGHT; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad 420 image height"; mRawYuv420Image.width = TEST_IMAGE_WIDTH; mRawYuv420Image.height = TEST_IMAGE_HEIGHT; mRawYuv420Image.luma_stride = TEST_IMAGE_STRIDE; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad luma stride for 420"; mRawYuv420Image.width = TEST_IMAGE_WIDTH; mRawYuv420Image.height = TEST_IMAGE_HEIGHT; mRawYuv420Image.luma_stride = 0; mRawYuv420Image.chroma_data = mRawYuv420Image.data; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows chroma pointer for 420"; mRawYuv420Image.width = TEST_IMAGE_WIDTH; mRawYuv420Image.height = TEST_IMAGE_HEIGHT; mRawYuv420Image.luma_stride = 0; mRawYuv420Image.chroma_data = nullptr; mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad 420 color gamut"; mRawYuv420Image.colorGamut = static_cast( ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1); EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr)) << "fail, API allows bad 420 color gamut"; free(jpegR.data); } /* Test Encode API-2 invalid arguments */ TEST_F(JpegRTest, encodeAPI2ForInvalidArgs) { int ret; // we are not really compressing anything so lets keep allocs to a minimum jpegr_compressed_struct jpegR; jpegR.maxLength = 16 * sizeof(uint8_t); jpegR.data = malloc(jpegR.maxLength); JpegR jpegRCodec; // we are not really compressing anything so lets keep allocs to a minimum mRawP010ImageWithStride.data = malloc(16); mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE; mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100; // we are not really compressing anything so lets keep allocs to a minimum mRawYuv420Image.data = malloc(16); mRawYuv420Image.width = TEST_IMAGE_WIDTH; mRawYuv420Image.height = TEST_IMAGE_HEIGHT; mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709; // test hdr transfer function EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED, &jpegR)) << "fail, API allows bad hdr transfer function"; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR, static_cast(ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1), &jpegR)) << "fail, API allows bad hdr transfer function"; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR, static_cast(-10), &jpegR)) << "fail, API allows bad hdr transfer function"; // test dest EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr)) << "fail, API allows nullptr dest"; // test p010 input EXPECT_NE(OK, jpegRCodec.encodeJPEGR( nullptr, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows nullptr p010 image"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad p010 color gamut"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.colorGamut = static_cast( ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1); EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad p010 color gamut"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH - 1; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image width"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT - 1; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image height"; mRawP010ImageWithStride.width = 0; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image width"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = 0; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image height"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH - 2; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad luma stride"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE; mRawP010ImageWithStride.chroma_data = mRawP010ImageWithStride.data; mRawP010ImageWithStride.chroma_stride = TEST_IMAGE_WIDTH - 2; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad chroma stride"; // test 420 input mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE; mRawP010ImageWithStride.chroma_data = nullptr; mRawP010ImageWithStride.chroma_stride = 0; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, nullptr, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows nullptr for 420 image"; mRawYuv420Image.width = TEST_IMAGE_WIDTH; mRawYuv420Image.height = TEST_IMAGE_HEIGHT - 2; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad 420 image width"; mRawYuv420Image.width = TEST_IMAGE_WIDTH - 2; mRawYuv420Image.height = TEST_IMAGE_HEIGHT; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad 420 image height"; mRawYuv420Image.width = TEST_IMAGE_WIDTH; mRawYuv420Image.height = TEST_IMAGE_HEIGHT; mRawYuv420Image.luma_stride = TEST_IMAGE_STRIDE; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad luma stride for 420"; mRawYuv420Image.width = TEST_IMAGE_WIDTH; mRawYuv420Image.height = TEST_IMAGE_HEIGHT; mRawYuv420Image.luma_stride = 0; mRawYuv420Image.chroma_data = mRawYuv420Image.data; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows chroma pointer for 420"; mRawYuv420Image.width = TEST_IMAGE_WIDTH; mRawYuv420Image.height = TEST_IMAGE_HEIGHT; mRawYuv420Image.luma_stride = 0; mRawYuv420Image.chroma_data = nullptr; mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad 420 color gamut"; mRawYuv420Image.colorGamut = static_cast( ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1); EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad 420 color gamut"; // bad compressed image mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &mRawYuv420Image, nullptr, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad 420 color gamut"; free(jpegR.data); } /* Test Encode API-3 invalid arguments */ TEST_F(JpegRTest, encodeAPI3ForInvalidArgs) { int ret; // we are not really compressing anything so lets keep allocs to a minimum jpegr_compressed_struct jpegR; jpegR.maxLength = 16 * sizeof(uint8_t); jpegR.data = malloc(jpegR.maxLength); JpegR jpegRCodec; // we are not really compressing anything so lets keep allocs to a minimum mRawP010ImageWithStride.data = malloc(16); mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE; mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100; // test hdr transfer function EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED, &jpegR)) << "fail, API allows bad hdr transfer function"; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &jpegR, static_cast(ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1), &jpegR)) << "fail, API allows bad hdr transfer function"; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &jpegR, static_cast(-10), &jpegR)) << "fail, API allows bad hdr transfer function"; // test dest EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr)) << "fail, API allows nullptr dest"; // test p010 input EXPECT_NE(OK, jpegRCodec.encodeJPEGR( nullptr, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows nullptr p010 image"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad p010 color gamut"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.colorGamut = static_cast( ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1); EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad p010 color gamut"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH - 1; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image width"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT - 1; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image height"; mRawP010ImageWithStride.width = 0; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image width"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = 0; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad image height"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH - 2; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad luma stride"; mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE; mRawP010ImageWithStride.chroma_data = mRawP010ImageWithStride.data; mRawP010ImageWithStride.chroma_stride = TEST_IMAGE_WIDTH - 2; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, &jpegR, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad chroma stride"; mRawP010ImageWithStride.chroma_data = nullptr; // bad compressed image EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, nullptr, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR)) << "fail, API allows bad 420 color gamut"; free(jpegR.data); } /* Test Encode API-4 invalid arguments */ TEST_F(JpegRTest, encodeAPI4ForInvalidArgs) { int ret; // we are not really compressing anything so lets keep allocs to a minimum jpegr_compressed_struct jpegR; jpegR.maxLength = 16 * sizeof(uint8_t); jpegR.data = malloc(jpegR.maxLength); JpegR jpegRCodec; // test dest EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &jpegR, &jpegR, nullptr, nullptr)) << "fail, API allows nullptr dest"; // test primary image EXPECT_NE(OK, jpegRCodec.encodeJPEGR( nullptr, &jpegR, nullptr, &jpegR)) << "fail, API allows nullptr primary image"; // test gain map EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &jpegR, nullptr, nullptr, &jpegR)) << "fail, API allows nullptr gainmap image"; // test metadata ultrahdr_metadata_struct good_metadata; good_metadata.version = "1.0"; good_metadata.minContentBoost = 1.0f; good_metadata.maxContentBoost = 2.0f; good_metadata.gamma = 1.0f; good_metadata.offsetSdr = 0.0f; good_metadata.offsetHdr = 0.0f; good_metadata.hdrCapacityMin = 1.0f; good_metadata.hdrCapacityMax = 2.0f; ultrahdr_metadata_struct metadata = good_metadata; metadata.version = "1.1"; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata version"; metadata = good_metadata; metadata.minContentBoost = 3.0f; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata content boost"; metadata = good_metadata; metadata.gamma = -0.1f; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata gamma"; metadata = good_metadata; metadata.offsetSdr = -0.1f; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata offset sdr"; metadata = good_metadata; metadata.offsetHdr = -0.1f; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata offset hdr"; metadata = good_metadata; metadata.hdrCapacityMax = 0.5f; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata hdr capacity max"; metadata = good_metadata; metadata.hdrCapacityMin = 0.5f; EXPECT_NE(OK, jpegRCodec.encodeJPEGR( &jpegR, nullptr, &metadata, &jpegR)) << "fail, API allows bad metadata hdr capacity min"; free(jpegR.data); } /* Test Decode API invalid arguments */ TEST_F(JpegRTest, decodeAPIForInvalidArgs) { int ret; // we are not really compressing anything so lets keep allocs to a minimum jpegr_compressed_struct jpegR; jpegR.maxLength = 16 * sizeof(uint8_t); jpegR.data = malloc(jpegR.maxLength); // we are not really decoding anything so lets keep allocs to a minimum mRawP010Image.data = malloc(16); JpegR jpegRCodec; // test jpegr image EXPECT_NE(OK, jpegRCodec.decodeJPEGR( nullptr, &mRawP010Image)) << "fail, API allows nullptr for jpegr img"; // test dest image EXPECT_NE(OK, jpegRCodec.decodeJPEGR( &jpegR, nullptr)) << "fail, API allows nullptr for dest"; // test max display boost EXPECT_NE(OK, jpegRCodec.decodeJPEGR( &jpegR, &mRawP010Image, 0.5)) << "fail, API allows invalid max display boost"; // test output format EXPECT_NE(OK, jpegRCodec.decodeJPEGR( &jpegR, &mRawP010Image, 0.5, nullptr, static_cast(-1))) << "fail, API allows invalid output format"; EXPECT_NE(OK, jpegRCodec.decodeJPEGR( &jpegR, &mRawP010Image, 0.5, nullptr, static_cast(ULTRAHDR_OUTPUT_MAX + 1))) << "fail, API allows invalid output format"; free(jpegR.data); } TEST_F(JpegRTest, writeXmpThenRead) { ultrahdr_metadata_struct metadata_expected; metadata_expected.version = "1.0"; metadata_expected.maxContentBoost = 1.25f; metadata_expected.minContentBoost = 0.75f; metadata_expected.gamma = 1.0f; metadata_expected.offsetSdr = 0.0f; metadata_expected.offsetHdr = 0.0f; metadata_expected.hdrCapacityMin = 1.0f; metadata_expected.hdrCapacityMax = metadata_expected.maxContentBoost; const std::string nameSpace = "http://ns.adobe.com/xap/1.0/\0"; const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator std::string xmp = generateXmpForSecondaryImage(metadata_expected); std::vector xmpData; xmpData.reserve(nameSpaceLength + xmp.size()); xmpData.insert(xmpData.end(), reinterpret_cast(nameSpace.c_str()), reinterpret_cast(nameSpace.c_str()) + nameSpaceLength); xmpData.insert(xmpData.end(), reinterpret_cast(xmp.c_str()), reinterpret_cast(xmp.c_str()) + xmp.size()); ultrahdr_metadata_struct metadata_read; EXPECT_TRUE(getMetadataFromXMP(xmpData.data(), xmpData.size(), &metadata_read)); EXPECT_FLOAT_EQ(metadata_expected.maxContentBoost, metadata_read.maxContentBoost); EXPECT_FLOAT_EQ(metadata_expected.minContentBoost, metadata_read.minContentBoost); EXPECT_FLOAT_EQ(metadata_expected.gamma, metadata_read.gamma); EXPECT_FLOAT_EQ(metadata_expected.offsetSdr, metadata_read.offsetSdr); EXPECT_FLOAT_EQ(metadata_expected.offsetHdr, metadata_read.offsetHdr); EXPECT_FLOAT_EQ(metadata_expected.hdrCapacityMin, metadata_read.hdrCapacityMin); EXPECT_FLOAT_EQ(metadata_expected.hdrCapacityMax, metadata_read.hdrCapacityMax); } /* Test Encode API-0 */ TEST_F(JpegRTest, encodeFromP010) { int ret; mRawP010Image.width = TEST_IMAGE_WIDTH; mRawP010Image.height = TEST_IMAGE_HEIGHT; mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100; // Load input files. if (!loadP010Image(RAW_P010_IMAGE, &mRawP010Image, true)) { FAIL() << "Load file " << RAW_P010_IMAGE << " failed"; } JpegR jpegRCodec; jpegr_compressed_struct jpegR; jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t); jpegR.data = malloc(jpegR.maxLength); ret = jpegRCodec.encodeJPEGR( &mRawP010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr); if (ret != OK) { FAIL() << "Error code is " << ret; } mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.luma_stride = TEST_IMAGE_WIDTH + 128; mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100; // Load input files. if (!loadP010Image(RAW_P010_IMAGE, &mRawP010ImageWithStride, true)) { FAIL() << "Load file " << RAW_P010_IMAGE << " failed"; } jpegr_compressed_struct jpegRWithStride; jpegRWithStride.maxLength = jpegR.length; jpegRWithStride.data = malloc(jpegRWithStride.maxLength); ret = jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegRWithStride, DEFAULT_JPEG_QUALITY, nullptr); if (ret != OK) { FAIL() << "Error code is " << ret; } ASSERT_EQ(jpegR.length, jpegRWithStride.length) << "Same input is yielding different output"; ASSERT_EQ(0, memcmp(jpegR.data, jpegRWithStride.data, jpegR.length)) << "Same input is yielding different output"; mRawP010ImageWithChromaData.width = TEST_IMAGE_WIDTH; mRawP010ImageWithChromaData.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithChromaData.luma_stride = TEST_IMAGE_WIDTH + 64; mRawP010ImageWithChromaData.chroma_stride = TEST_IMAGE_WIDTH + 256; mRawP010ImageWithChromaData.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100; // Load input files. if (!loadP010Image(RAW_P010_IMAGE, &mRawP010ImageWithChromaData, false)) { FAIL() << "Load file " << RAW_P010_IMAGE << " failed"; } jpegr_compressed_struct jpegRWithChromaData; jpegRWithChromaData.maxLength = jpegR.length; jpegRWithChromaData.data = malloc(jpegRWithChromaData.maxLength); ret = jpegRCodec.encodeJPEGR( &mRawP010ImageWithChromaData, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegRWithChromaData, DEFAULT_JPEG_QUALITY, nullptr); if (ret != OK) { FAIL() << "Error code is " << ret; } ASSERT_EQ(jpegR.length, jpegRWithChromaData.length) << "Same input is yielding different output"; ASSERT_EQ(0, memcmp(jpegR.data, jpegRWithChromaData.data, jpegR.length)) << "Same input is yielding different output"; free(jpegR.data); free(jpegRWithStride.data); free(jpegRWithChromaData.data); } /* Test Encode API-0 and decode */ TEST_F(JpegRTest, encodeFromP010ThenDecode) { int ret; // Load input files. if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) { FAIL() << "Load file " << RAW_P010_IMAGE << " failed"; } mRawP010Image.width = TEST_IMAGE_WIDTH; mRawP010Image.height = TEST_IMAGE_HEIGHT; mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100; JpegR jpegRCodec; jpegr_compressed_struct jpegR; jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t); jpegR.data = malloc(jpegR.maxLength); ret = jpegRCodec.encodeJPEGR( &mRawP010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr); if (ret != OK) { FAIL() << "Error code is " << ret; } if (SAVE_ENCODING_RESULT) { // Output image data to file std::string filePath = "/sdcard/Documents/encoded_from_p010_input.jpgr"; std::ofstream imageFile(filePath.c_str(), std::ofstream::binary); if (!imageFile.is_open()) { ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str()); } imageFile.write((const char*)jpegR.data, jpegR.length); } jpegr_uncompressed_struct decodedJpegR; int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8; decodedJpegR.data = malloc(decodedJpegRSize); ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR); if (ret != OK) { FAIL() << "Error code is " << ret; } if (SAVE_DECODING_RESULT) { // Output image data to file 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()); } imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize); } free(jpegR.data); free(decodedJpegR.data); } /* Test Encode API-0 (with stride) and decode */ TEST_F(JpegRTest, encodeFromP010WithStrideThenDecode) { int ret; // Load input files. if (!loadFile(RAW_P010_IMAGE_WITH_STRIDE, mRawP010ImageWithStride.data, nullptr)) { FAIL() << "Load file " << RAW_P010_IMAGE_WITH_STRIDE << " failed"; } mRawP010ImageWithStride.width = TEST_IMAGE_WIDTH; mRawP010ImageWithStride.height = TEST_IMAGE_HEIGHT; mRawP010ImageWithStride.luma_stride = TEST_IMAGE_STRIDE; mRawP010ImageWithStride.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100; JpegR jpegRCodec; jpegr_compressed_struct jpegR; jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t); jpegR.data = malloc(jpegR.maxLength); ret = jpegRCodec.encodeJPEGR( &mRawP010ImageWithStride, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr); if (ret != OK) { FAIL() << "Error code is " << ret; } if (SAVE_ENCODING_RESULT) { // Output image data to file std::string filePath = "/sdcard/Documents/encoded_from_p010_input.jpgr"; std::ofstream imageFile(filePath.c_str(), std::ofstream::binary); if (!imageFile.is_open()) { ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str()); } imageFile.write((const char*)jpegR.data, jpegR.length); } jpegr_uncompressed_struct decodedJpegR; int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8; decodedJpegR.data = malloc(decodedJpegRSize); ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR); if (ret != OK) { FAIL() << "Error code is " << ret; } if (SAVE_DECODING_RESULT) { // Output image data to file 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()); } imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize); } free(jpegR.data); free(decodedJpegR.data); } /* Test Encode API-1 and decode */ TEST_F(JpegRTest, encodeFromRawHdrAndSdrThenDecode) { int ret; // Load input files. if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) { FAIL() << "Load file " << RAW_P010_IMAGE << " failed"; } mRawP010Image.width = TEST_IMAGE_WIDTH; mRawP010Image.height = TEST_IMAGE_HEIGHT; mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100; if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) { FAIL() << "Load file " << RAW_P010_IMAGE << " failed"; } mRawYuv420Image.width = TEST_IMAGE_WIDTH; mRawYuv420Image.height = TEST_IMAGE_HEIGHT; mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709; JpegR jpegRCodec; jpegr_compressed_struct jpegR; jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t); jpegR.data = malloc(jpegR.maxLength); ret = jpegRCodec.encodeJPEGR( &mRawP010Image, &mRawYuv420Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr); if (ret != OK) { FAIL() << "Error code is " << ret; } if (SAVE_ENCODING_RESULT) { // Output image data to file std::string filePath = "/sdcard/Documents/encoded_from_p010_yuv420p_input.jpgr"; std::ofstream imageFile(filePath.c_str(), std::ofstream::binary); if (!imageFile.is_open()) { ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str()); } imageFile.write((const char*)jpegR.data, jpegR.length); } jpegr_uncompressed_struct decodedJpegR; int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8; decodedJpegR.data = malloc(decodedJpegRSize); ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR); if (ret != OK) { FAIL() << "Error code is " << ret; } if (SAVE_DECODING_RESULT) { // Output image data to file 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()); } imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize); } free(jpegR.data); free(decodedJpegR.data); } /* Test Encode API-2 and decode */ TEST_F(JpegRTest, encodeFromRawHdrAndSdrAndJpegThenDecode) { int ret; // Load input files. if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) { FAIL() << "Load file " << RAW_P010_IMAGE << " failed"; } mRawP010Image.width = TEST_IMAGE_WIDTH; mRawP010Image.height = TEST_IMAGE_HEIGHT; mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100; if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) { FAIL() << "Load file " << RAW_P010_IMAGE << " failed"; } mRawYuv420Image.width = TEST_IMAGE_WIDTH; mRawYuv420Image.height = TEST_IMAGE_HEIGHT; mRawYuv420Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709; if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) { FAIL() << "Load file " << JPEG_IMAGE << " failed"; } mJpegImage.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709; JpegR jpegRCodec; jpegr_compressed_struct jpegR; jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t); jpegR.data = malloc(jpegR.maxLength); ret = jpegRCodec.encodeJPEGR( &mRawP010Image, &mRawYuv420Image, &mJpegImage, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR); if (ret != OK) { FAIL() << "Error code is " << ret; } if (SAVE_ENCODING_RESULT) { // Output image data to file std::string filePath = "/sdcard/Documents/encoded_from_p010_yuv420p_jpeg_input.jpgr"; std::ofstream imageFile(filePath.c_str(), std::ofstream::binary); if (!imageFile.is_open()) { ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str()); } imageFile.write((const char*)jpegR.data, jpegR.length); } jpegr_uncompressed_struct decodedJpegR; int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8; decodedJpegR.data = malloc(decodedJpegRSize); ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR); if (ret != OK) { FAIL() << "Error code is " << ret; } if (SAVE_DECODING_RESULT) { // Output image data to file 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()); } imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize); } free(jpegR.data); free(decodedJpegR.data); } /* Test Encode API-3 and decode */ TEST_F(JpegRTest, encodeFromJpegThenDecode) { int ret; // Load input files. if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) { FAIL() << "Load file " << RAW_P010_IMAGE << " failed"; } mRawP010Image.width = TEST_IMAGE_WIDTH; mRawP010Image.height = TEST_IMAGE_HEIGHT; mRawP010Image.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100; if (SAVE_INPUT_RGBA) { size_t rgbaSize = mRawP010Image.width * mRawP010Image.height * sizeof(uint32_t); uint32_t *data = (uint32_t *)malloc(rgbaSize); for (size_t y = 0; y < mRawP010Image.height; ++y) { for (size_t x = 0; x < mRawP010Image.width; ++x) { Color hdr_yuv_gamma = getP010Pixel(&mRawP010Image, x, y); Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma); uint32_t rgba1010102 = colorToRgba1010102(hdr_rgb_gamma); size_t pixel_idx = x + y * mRawP010Image.width; reinterpret_cast(data)[pixel_idx] = rgba1010102; } } // Output image data to file std::string filePath = "/sdcard/Documents/input_from_p010.rgb10"; std::ofstream imageFile(filePath.c_str(), std::ofstream::binary); if (!imageFile.is_open()) { ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str()); } imageFile.write((const char*)data, rgbaSize); free(data); } if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) { FAIL() << "Load file " << JPEG_IMAGE << " failed"; } mJpegImage.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709; JpegR jpegRCodec; jpegr_compressed_struct jpegR; jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t); jpegR.data = malloc(jpegR.maxLength); ret = jpegRCodec.encodeJPEGR( &mRawP010Image, &mJpegImage, ultrahdr_transfer_function::ULTRAHDR_TF_HLG, &jpegR); if (ret != OK) { FAIL() << "Error code is " << ret; } if (SAVE_ENCODING_RESULT) { // Output image data to file std::string filePath = "/sdcard/Documents/encoded_from_p010_jpeg_input.jpgr"; std::ofstream imageFile(filePath.c_str(), std::ofstream::binary); if (!imageFile.is_open()) { ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str()); } imageFile.write((const char*)jpegR.data, jpegR.length); } jpegr_uncompressed_struct decodedJpegR; int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8; decodedJpegR.data = malloc(decodedJpegRSize); ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR); if (ret != OK) { FAIL() << "Error code is " << ret; } if (SAVE_DECODING_RESULT) { // Output image data to file 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()); } imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize); } free(jpegR.data); free(decodedJpegR.data); } TEST_F(JpegRTest, ProfileGainMapFuncs) { 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 = ultrahdr_color_gamut::ULTRAHDR_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 = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709; JpegRBenchmark benchmark; ultrahdr_metadata_struct metadata = { .version = "1.0" }; jpegr_uncompressed_struct map = { .data = NULL, .width = 0, .height = 0, .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED }; benchmark.BenchmarkGenerateGainMap(&mRawYuv420Image, &mRawP010Image, &metadata, &map); const int dstSize = mRawYuv420Image.width * mRawYuv420Image.height * 4; auto bufferDst = std::make_unique(dstSize); jpegr_uncompressed_struct dest = { .data = bufferDst.get(), .width = 0, .height = 0, .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED }; benchmark.BenchmarkApplyGainMap(&mRawYuv420Image, &map, &metadata, &dest); } } // namespace android::ultrahdr