blob: 11f54df506db2931acca40d96f63781538179974 [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;
109 std::string xmp = generateXmp(1000, metadata_expected);
110
111 jpegr_metadata metadata_read;
112 EXPECT_TRUE(getMetadataFromXMP(reinterpret_cast<uint8_t*>(xmp[0]), xmp.size(), &metadata_read));
113 ASSERT_EQ(metadata_expected.transferFunction, metadata_read.transferFunction);
114 ASSERT_EQ(metadata_expected.rangeScalingFactor, metadata_read.rangeScalingFactor);
115
116}
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000117
118/* Test Encode API-0 and decode */
Dichen Zhangc3437ca2023-01-04 14:00:08 -0800119TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) {
120 int ret;
121
122 // Load input files.
123 if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
124 FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
125 }
126 mRawP010Image.width = TEST_IMAGE_WIDTH;
127 mRawP010Image.height = TEST_IMAGE_HEIGHT;
128 mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
129
130 RecoveryMap recoveryMap;
131
132 jpegr_compressed_struct jpegR;
133 jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
134 jpegR.data = malloc(jpegR.maxLength);
135 ret = recoveryMap.encodeJPEGR(
136 &mRawP010Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR, DEFAULT_JPEG_QUALITY, nullptr);
137 if (ret != OK) {
138 FAIL() << "Error code is " << ret;
139 }
140 if (SAVE_ENCODING_RESULT) {
141 // Output image data to file
142 std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
143 std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
144 if (!imageFile.is_open()) {
145 ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
146 }
147 imageFile.write((const char*)jpegR.data, jpegR.length);
148 }
149
150 jpegr_uncompressed_struct decodedJpegR;
151 int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
152 decodedJpegR.data = malloc(decodedJpegRSize);
153 ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
154 if (ret != OK) {
155 FAIL() << "Error code is " << ret;
156 }
157 if (SAVE_DECODING_RESULT) {
158 // Output image data to file
159 std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
160 std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
161 if (!imageFile.is_open()) {
162 ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
163 }
164 imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
165 }
166
167 free(jpegR.data);
168 free(decodedJpegR.data);
169}
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000170
171/* Test Encode API-1 and decode */
172TEST_F(RecoveryMapTest, encodeFromRawHdrAndSdrThenDecode) {
Dichen Zhang54444382022-12-07 20:57:49 +0000173 int ret;
174
175 // Load input files.
176 if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
177 FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
178 }
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000179 mRawP010Image.width = TEST_IMAGE_WIDTH;
180 mRawP010Image.height = TEST_IMAGE_HEIGHT;
Dichen Zhang54444382022-12-07 20:57:49 +0000181 mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
182
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000183 if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
184 FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
185 }
186 mRawYuv420Image.width = TEST_IMAGE_WIDTH;
187 mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
188 mRawYuv420Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
189
Dichen Zhang54444382022-12-07 20:57:49 +0000190 RecoveryMap recoveryMap;
191
192 jpegr_compressed_struct jpegR;
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000193 jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
Dichen Zhang54444382022-12-07 20:57:49 +0000194 jpegR.data = malloc(jpegR.maxLength);
195 ret = recoveryMap.encodeJPEGR(
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000196 &mRawP010Image, &mRawYuv420Image, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR,
197 DEFAULT_JPEG_QUALITY, nullptr);
Dichen Zhang54444382022-12-07 20:57:49 +0000198 if (ret != OK) {
199 FAIL() << "Error code is " << ret;
200 }
201 if (SAVE_ENCODING_RESULT) {
202 // Output image data to file
203 std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
204 std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
205 if (!imageFile.is_open()) {
206 ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
207 }
208 imageFile.write((const char*)jpegR.data, jpegR.length);
209 }
210
211 jpegr_uncompressed_struct decodedJpegR;
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000212 int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
Dichen Zhang54444382022-12-07 20:57:49 +0000213 decodedJpegR.data = malloc(decodedJpegRSize);
214 ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
215 if (ret != OK) {
216 FAIL() << "Error code is " << ret;
217 }
218 if (SAVE_DECODING_RESULT) {
219 // Output image data to file
220 std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
221 std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
222 if (!imageFile.is_open()) {
223 ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
224 }
225 imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
226 }
227
228 free(jpegR.data);
229 free(decodedJpegR.data);
230}
231
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000232/* Test Encode API-2 and decode */
233TEST_F(RecoveryMapTest, encodeFromRawHdrAndSdrAndJpegThenDecode) {
234 int ret;
235
236 // Load input files.
237 if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
238 FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
239 }
240 mRawP010Image.width = TEST_IMAGE_WIDTH;
241 mRawP010Image.height = TEST_IMAGE_HEIGHT;
242 mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
243
244 if (!loadFile(RAW_YUV420_IMAGE, mRawYuv420Image.data, nullptr)) {
245 FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
246 }
247 mRawYuv420Image.width = TEST_IMAGE_WIDTH;
248 mRawYuv420Image.height = TEST_IMAGE_HEIGHT;
249 mRawYuv420Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
250
251 if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
252 FAIL() << "Load file " << JPEG_IMAGE << " failed";
253 }
254 mJpegImage.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
255
256 RecoveryMap recoveryMap;
257
258 jpegr_compressed_struct jpegR;
259 jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
260 jpegR.data = malloc(jpegR.maxLength);
261 ret = recoveryMap.encodeJPEGR(
262 &mRawP010Image, &mRawYuv420Image, &mJpegImage, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR);
263 if (ret != OK) {
264 FAIL() << "Error code is " << ret;
265 }
266 if (SAVE_ENCODING_RESULT) {
267 // Output image data to file
268 std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
269 std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
270 if (!imageFile.is_open()) {
271 ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
272 }
273 imageFile.write((const char*)jpegR.data, jpegR.length);
274 }
275
276 jpegr_uncompressed_struct decodedJpegR;
277 int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
278 decodedJpegR.data = malloc(decodedJpegRSize);
279 ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
280 if (ret != OK) {
281 FAIL() << "Error code is " << ret;
282 }
283 if (SAVE_DECODING_RESULT) {
284 // Output image data to file
285 std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
286 std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
287 if (!imageFile.is_open()) {
288 ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
289 }
290 imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
291 }
292
293 free(jpegR.data);
294 free(decodedJpegR.data);
295}
296
297/* Test Encode API-3 and decode */
Dichen Zhang36c1e732022-11-23 01:25:34 +0000298TEST_F(RecoveryMapTest, encodeFromJpegThenDecode) {
299 int ret;
300
301 // Load input files.
302 if (!loadFile(RAW_P010_IMAGE, mRawP010Image.data, nullptr)) {
303 FAIL() << "Load file " << RAW_P010_IMAGE << " failed";
304 }
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000305 mRawP010Image.width = TEST_IMAGE_WIDTH;
306 mRawP010Image.height = TEST_IMAGE_HEIGHT;
Dichen Zhang36c1e732022-11-23 01:25:34 +0000307 mRawP010Image.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT2100;
308
Harish Mahendrakar1e057282022-12-04 12:47:53 -0800309 if (SAVE_INPUT_RGBA) {
310 size_t rgbaSize = mRawP010Image.width * mRawP010Image.height * sizeof(uint32_t);
311 uint32_t *data = (uint32_t *)malloc(rgbaSize);
312
313 for (size_t y = 0; y < mRawP010Image.height; ++y) {
314 for (size_t x = 0; x < mRawP010Image.width; ++x) {
315 Color hdr_yuv_gamma = getP010Pixel(&mRawP010Image, x, y);
316 Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
317 uint32_t rgba1010102 = colorToRgba1010102(hdr_rgb_gamma);
318 size_t pixel_idx = x + y * mRawP010Image.width;
319 reinterpret_cast<uint32_t*>(data)[pixel_idx] = rgba1010102;
320 }
321 }
322
323 // Output image data to file
324 std::string filePath = "/sdcard/Documents/input_from_p010.rgb10";
325 std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
326 if (!imageFile.is_open()) {
327 ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
328 }
329 imageFile.write((const char*)data, rgbaSize);
330 free(data);
331 }
Dichen Zhang36c1e732022-11-23 01:25:34 +0000332 if (!loadFile(JPEG_IMAGE, mJpegImage.data, &mJpegImage.length)) {
333 FAIL() << "Load file " << JPEG_IMAGE << " failed";
334 }
335 mJpegImage.colorGamut = jpegr_color_gamut::JPEGR_COLORGAMUT_BT709;
336
337 RecoveryMap recoveryMap;
338
339 jpegr_compressed_struct jpegR;
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000340 jpegR.maxLength = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * sizeof(uint8_t);
Dichen Zhang36c1e732022-11-23 01:25:34 +0000341 jpegR.data = malloc(jpegR.maxLength);
342 ret = recoveryMap.encodeJPEGR(
343 &mRawP010Image, &mJpegImage, jpegr_transfer_function::JPEGR_TF_HLG, &jpegR);
344 if (ret != OK) {
345 FAIL() << "Error code is " << ret;
346 }
347 if (SAVE_ENCODING_RESULT) {
348 // Output image data to file
349 std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr";
350 std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
351 if (!imageFile.is_open()) {
352 ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
353 }
354 imageFile.write((const char*)jpegR.data, jpegR.length);
355 }
356
357 jpegr_uncompressed_struct decodedJpegR;
Dichen Zhang7e3ed122022-12-14 22:05:01 +0000358 int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4;
Dichen Zhang36c1e732022-11-23 01:25:34 +0000359 decodedJpegR.data = malloc(decodedJpegRSize);
360 ret = recoveryMap.decodeJPEGR(&jpegR, &decodedJpegR);
361 if (ret != OK) {
362 FAIL() << "Error code is " << ret;
363 }
364 if (SAVE_DECODING_RESULT) {
365 // Output image data to file
366 std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10";
367 std::ofstream imageFile(filePath.c_str(), std::ofstream::binary);
368 if (!imageFile.is_open()) {
369 ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str());
370 }
371 imageFile.write((const char*)decodedJpegR.data, decodedJpegRSize);
372 }
373
374 free(jpegR.data);
375 free(decodedJpegR.data);
376}
377
Nick Deakin594a4ca2022-11-16 20:57:42 -0500378} // namespace android::recoverymap