blob: dfab76a2c92a78ee729946ef66be3e800e5b062f [file] [log] [blame]
Dichen Zhang85b37562022-10-11 11:08:28 -07001/*
2 * Copyright 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <jpegrecoverymap/recoverymap.h>
Harish Mahendrakar1e057282022-12-04 12:47:53 -080018#include <jpegrecoverymap/recoverymapmath.h>
Dichen Zhangdc8452b2022-11-23 17:17:56 +000019#include <jpegrecoverymap/recoverymaputils.h>
Dichen Zhang36c1e732022-11-23 01:25:34 +000020#include <fcntl.h>
21#include <fstream>
22#include <gtest/gtest.h>
23#include <utils/Log.h>
24
25#define RAW_P010_IMAGE "/sdcard/Documents/raw_p010_image.p010"
Dichen Zhang7e3ed122022-12-14 22:05:01 +000026#define RAW_YUV420_IMAGE "/sdcard/Documents/raw_yuv420_image.yuv420"
Dichen Zhang36c1e732022-11-23 01:25:34 +000027#define JPEG_IMAGE "/sdcard/Documents/jpeg_image.jpg"
Dichen Zhang7e3ed122022-12-14 22:05:01 +000028#define TEST_IMAGE_WIDTH 1280
29#define TEST_IMAGE_HEIGHT 720
30#define DEFAULT_JPEG_QUALITY 90
Dichen Zhang36c1e732022-11-23 01:25:34 +000031
32#define SAVE_ENCODING_RESULT true
33#define SAVE_DECODING_RESULT true
Harish Mahendrakar1e057282022-12-04 12:47:53 -080034#define SAVE_INPUT_RGBA true
Dichen Zhang85b37562022-10-11 11:08:28 -070035
Nick Deakin594a4ca2022-11-16 20:57:42 -050036namespace android::recoverymap {
Dichen Zhang85b37562022-10-11 11:08:28 -070037
Nick Deakin594a4ca2022-11-16 20:57:42 -050038class RecoveryMapTest : public testing::Test {
39public:
40 RecoveryMapTest();
41 ~RecoveryMapTest();
42protected:
43 virtual void SetUp();
44 virtual void TearDown();
Dichen Zhang36c1e732022-11-23 01:25:34 +000045
46 struct jpegr_uncompressed_struct mRawP010Image;
Dichen Zhang7e3ed122022-12-14 22:05:01 +000047 struct jpegr_uncompressed_struct mRawYuv420Image;
Dichen Zhang36c1e732022-11-23 01:25:34 +000048 struct jpegr_compressed_struct mJpegImage;
Nick Deakin594a4ca2022-11-16 20:57:42 -050049};
50
51RecoveryMapTest::RecoveryMapTest() {}
52RecoveryMapTest::~RecoveryMapTest() {}
53
54void RecoveryMapTest::SetUp() {}
Dichen Zhang36c1e732022-11-23 01:25:34 +000055void RecoveryMapTest::TearDown() {
56 free(mRawP010Image.data);
Dichen Zhang7e3ed122022-12-14 22:05:01 +000057 free(mRawYuv420Image.data);
Dichen Zhang36c1e732022-11-23 01:25:34 +000058 free(mJpegImage.data);
59}
60
61static size_t getFileSize(int fd) {
62 struct stat st;
63 if (fstat(fd, &st) < 0) {
64 ALOGW("%s : fstat failed", __func__);
65 return 0;
66 }
67 return st.st_size; // bytes
68}
69
70static bool loadFile(const char filename[], void*& result, int* fileLength) {
71 int fd = open(filename, O_CLOEXEC);
72 if (fd < 0) {
73 return false;
74 }
75 int length = getFileSize(fd);
76 if (length == 0) {
77 close(fd);
78 return false;
79 }
80 if (fileLength != nullptr) {
81 *fileLength = length;
82 }
83 result = malloc(length);
84 if (read(fd, result, length) != static_cast<ssize_t>(length)) {
85 close(fd);
86 return false;
87 }
88 close(fd);
89 return true;
90}
Nick Deakin594a4ca2022-11-16 20:57:42 -050091
92TEST_F(RecoveryMapTest, build) {
93 // Force all of the recovery map lib to be linked by calling all public functions.
94 RecoveryMap recovery_map;
Dichen Zhang636f5242022-12-07 20:25:44 +000095 recovery_map.encodeJPEGR(nullptr, static_cast<jpegr_transfer_function>(0), nullptr, 0, nullptr);
Nick Deakin6bd90432022-11-20 16:26:37 -050096 recovery_map.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
97 nullptr, 0, nullptr);
98 recovery_map.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<jpegr_transfer_function>(0),
99 nullptr);
100 recovery_map.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0), nullptr);
Nick Deakin594a4ca2022-11-16 20:57:42 -0500101 recovery_map.decodeJPEGR(nullptr, nullptr, nullptr, false);
102}
103
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000104TEST_F(RecoveryMapTest, writeXmpThenRead) {
105 jpegr_metadata metadata_expected;
106 metadata_expected.transferFunction = JPEGR_TF_HLG;
107 metadata_expected.rangeScalingFactor = 1.25;
108 int length_expected = 1000;
Fyodor Kyslov8f46e2a2023-01-20 04:21:56 +0000109 const std::string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
110 const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
111
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000112 std::string xmp = generateXmp(1000, metadata_expected);
113
Fyodor Kyslov8f46e2a2023-01-20 04:21:56 +0000114 std::vector<uint8_t> xmpData;
115 xmpData.reserve(nameSpaceLength + xmp.size());
116 xmpData.insert(xmpData.end(), reinterpret_cast<const uint8_t*>(nameSpace.c_str()),
117 reinterpret_cast<const uint8_t*>(nameSpace.c_str()) + nameSpaceLength);
118 xmpData.insert(xmpData.end(), reinterpret_cast<const uint8_t*>(xmp.c_str()),
119 reinterpret_cast<const uint8_t*>(xmp.c_str()) + xmp.size());
120
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000121 jpegr_metadata metadata_read;
Fyodor Kyslov8f46e2a2023-01-20 04:21:56 +0000122 EXPECT_TRUE(getMetadataFromXMP(xmpData.data(), xmpData.size(), &metadata_read));
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000123 ASSERT_EQ(metadata_expected.transferFunction, metadata_read.transferFunction);
124 ASSERT_EQ(metadata_expected.rangeScalingFactor, metadata_read.rangeScalingFactor);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000125}
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000126
127/* Test Encode API-0 and decode */
Dichen Zhangc3437ca2023-01-04 14:00:08 -0800128TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) {
129 int ret;
130
131 // Load input files.
132 if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
133 FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
134 }
135 mRawP010Image.width = TEST_IMAGE_WIDTH;
136 mRawP010Image.height = TEST_IMAGE_HEIGHT;
137 mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
138
139 RecoveryMap recoveryMap;
140
141 jpegr_compressed_struct jpegR;
142 jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
143 jpegR.data = malloc(jpegR.maxLength);
144 ret = recoveryMap.encodeJPEGR(
145 &mRawP010Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr);
146 if (ret != OK) {
147 FAIL() << "Error code is " << ret;
148 }
149 if (SAVE_ENCODING_RESULT) {
150 // Output image data to file
151 std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
152 std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
153 if (!imageFile.is_open()) {
154 ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
155 }
156 imageFile.write((const char*)jpegR.data, jpegR.length);
157 }
158
159 jpegr_uncompressed_struct decodedJpegR;
160 int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
161 decodedJpegR.data = malloc(decodedJpegRSize);
162 ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
163 if (ret != OK) {
164 FAIL() << "Error code is " << ret;
165 }
166 if (SAVE_DECODING_RESULT) {
167 // Output image data to file
168 std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
169 std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
170 if (!imageFile.is_open()) {
171 ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
172 }
173 imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
174 }
175
176 free(jpegR.data);
177 free(decodedJpegR.data);
178}
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000179
180/* Test Encode API-1 and decode */
181TEST_F(RecoveryMapTest, encodeFromRawHdrAndSdrThenDecode) {
Dichen Zhang54444382022-12-07 20:57:49 +0000182 int ret;
183
184 // Load input files.
185 if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
186 FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
187 }
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000188 mRawP010Image.width = TEST_IMAGE_WIDTH;
189 mRawP010Image.height = TEST_IMAGE_HEIGHT;
Dichen Zhang54444382022-12-07 20:57:49 +0000190 mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
191
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000192 if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
193 FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
194 }
195 mRawYuv420Image.width = TEST_IMAGE_WIDTH;
196 mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
197 mRawYuv420Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
198
Dichen Zhang54444382022-12-07 20:57:49 +0000199 RecoveryMap recoveryMap;
200
201 jpegr_compressed_struct jpegR;
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000202 jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
Dichen Zhang54444382022-12-07 20:57:49 +0000203 jpegR.data = malloc(jpegR.maxLength);
204 ret = recoveryMap.encodeJPEGR(
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000205 &mRawP010Image, &mRawYuv420Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR,
206 DEFAULT_JPEG_QUALITY, nullptr);
Dichen Zhang54444382022-12-07 20:57:49 +0000207 if (ret != OK) {
208 FAIL() << "Error code is " << ret;
209 }
210 if (SAVE_ENCODING_RESULT) {
211 // Output image data to file
212 std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
213 std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
214 if (!imageFile.is_open()) {
215 ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
216 }
217 imageFile.write((const char*)jpegR.data, jpegR.length);
218 }
219
220 jpegr_uncompressed_struct decodedJpegR;
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000221 int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
Dichen Zhang54444382022-12-07 20:57:49 +0000222 decodedJpegR.data = malloc(decodedJpegRSize);
223 ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
224 if (ret != OK) {
225 FAIL() << "Error code is " << ret;
226 }
227 if (SAVE_DECODING_RESULT) {
228 // Output image data to file
229 std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
230 std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
231 if (!imageFile.is_open()) {
232 ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
233 }
234 imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
235 }
236
237 free(jpegR.data);
238 free(decodedJpegR.data);
239}
240
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000241/* Test Encode API-2 and decode */
242TEST_F(RecoveryMapTest, encodeFromRawHdrAndSdrAndJpegThenDecode) {
243 int ret;
244
245 // Load input files.
246 if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
247 FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
248 }
249 mRawP010Image.width = TEST_IMAGE_WIDTH;
250 mRawP010Image.height = TEST_IMAGE_HEIGHT;
251 mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
252
253 if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
254 FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
255 }
256 mRawYuv420Image.width = TEST_IMAGE_WIDTH;
257 mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
258 mRawYuv420Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
259
260 if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
261 FAIL() << "Load file " << JPEG_IMAGE << " failed";
262 }
263 mJpegImage.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
264
265 RecoveryMap recoveryMap;
266
267 jpegr_compressed_struct jpegR;
268 jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
269 jpegR.data = malloc(jpegR.maxLength);
270 ret = recoveryMap.encodeJPEGR(
271 &mRawP010Image, &mRawYuv420Image, &mJpegImage, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR);
272 if (ret != OK) {
273 FAIL() << "Error code is " << ret;
274 }
275 if (SAVE_ENCODING_RESULT) {
276 // Output image data to file
277 std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
278 std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
279 if (!imageFile.is_open()) {
280 ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
281 }
282 imageFile.write((const char*)jpegR.data, jpegR.length);
283 }
284
285 jpegr_uncompressed_struct decodedJpegR;
286 int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
287 decodedJpegR.data = malloc(decodedJpegRSize);
288 ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
289 if (ret != OK) {
290 FAIL() << "Error code is " << ret;
291 }
292 if (SAVE_DECODING_RESULT) {
293 // Output image data to file
294 std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
295 std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
296 if (!imageFile.is_open()) {
297 ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
298 }
299 imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
300 }
301
302 free(jpegR.data);
303 free(decodedJpegR.data);
304}
305
306/* Test Encode API-3 and decode */
Dichen Zhang36c1e732022-11-23 01:25:34 +0000307TEST_F(RecoveryMapTest, encodeFromJpegThenDecode) {
308 int ret;
309
310 // Load input files.
311 if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
312 FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
313 }
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000314 mRawP010Image.width = TEST_IMAGE_WIDTH;
315 mRawP010Image.height = TEST_IMAGE_HEIGHT;
Dichen Zhang36c1e732022-11-23 01:25:34 +0000316 mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
317
Harish Mahendrakar1e057282022-12-04 12:47:53 -0800318 if (SAVE_INPUT_RGBA) {
319 size_t rgbaSize = mRawP010Image.width * mRawP010Image.height * sizeof(uint32_t);
320 uint32_t *data = (uint32_t *)malloc(rgbaSize);
321
322 for (size_t y = 0; y < mRawP010Image.height; ++y) {
323 for (size_t x = 0; x < mRawP010Image.width; ++x) {
324 Color hdr_yuv_gamma = getP010Pixel(&mRawP010Image, x, y);
325 Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
326 uint32_t rgba1010102 = colorToRgba1010102(hdr_rgb_gamma);
327 size_t pixel_idx = x + y * mRawP010Image.width;
328 reinterpret_cast<uint32_t*>(data)[pixel_idx] = rgba1010102;
329 }
330 }
331
332 // Output image data to file
333 std::string filePath = "/sdcard/Documents/input_from_p010.rgb10";
334 std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
335 if (!imageFile.is_open()) {
336 ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
337 }
338 imageFile.write((const char*)data, rgbaSize);
339 free(data);
340 }
Dichen Zhang36c1e732022-11-23 01:25:34 +0000341 if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
342 FAIL() << "Load file " << JPEG_IMAGE << " failed";
343 }
344 mJpegImage.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
345
346 RecoveryMap recoveryMap;
347
348 jpegr_compressed_struct jpegR;
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000349 jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
Dichen Zhang36c1e732022-11-23 01:25:34 +0000350 jpegR.data = malloc(jpegR.maxLength);
351 ret = recoveryMap.encodeJPEGR(
352 &mRawP010Image, &mJpegImage, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR);
353 if (ret != OK) {
354 FAIL() << "Error code is " << ret;
355 }
356 if (SAVE_ENCODING_RESULT) {
357 // Output image data to file
358 std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
359 std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
360 if (!imageFile.is_open()) {
361 ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
362 }
363 imageFile.write((const char*)jpegR.data, jpegR.length);
364 }
365
366 jpegr_uncompressed_struct decodedJpegR;
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000367 int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
Dichen Zhang36c1e732022-11-23 01:25:34 +0000368 decodedJpegR.data = malloc(decodedJpegRSize);
369 ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
370 if (ret != OK) {
371 FAIL() << "Error code is " << ret;
372 }
373 if (SAVE_DECODING_RESULT) {
374 // Output image data to file
375 std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
376 std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
377 if (!imageFile.is_open()) {
378 ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
379 }
380 imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
381 }
382
383 free(jpegR.data);
384 free(decodedJpegR.data);
385}
386
Nick Deakin594a4ca2022-11-16 20:57:42 -0500387} // namespace android::recoverymap