1 /*
2 * Copyright 2024 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 <cstdio>
18 #include <cstring>
19
20 #include "ultrahdr_api.h"
21 #include "ultrahdr/ultrahdrcommon.h"
22 #include "ultrahdr/gainmapmath.h"
23 #include "ultrahdr/editorhelper.h"
24 #include "ultrahdr/jpegr.h"
25 #include "ultrahdr/jpegrutils.h"
26
27 static const uhdr_error_info_t g_no_error = {UHDR_CODEC_OK, 0, ""};
28
29 namespace ultrahdr {
30
uhdr_memory_block(size_t capacity)31 uhdr_memory_block::uhdr_memory_block(size_t capacity) {
32 m_buffer = std::make_unique<uint8_t[]>(capacity);
33 m_capacity = capacity;
34 }
35
uhdr_raw_image_ext(uhdr_img_fmt_t fmt_,uhdr_color_gamut_t cg_,uhdr_color_transfer_t ct_,uhdr_color_range_t range_,unsigned w_,unsigned h_,unsigned align_stride_to)36 uhdr_raw_image_ext::uhdr_raw_image_ext(uhdr_img_fmt_t fmt_, uhdr_color_gamut_t cg_,
37 uhdr_color_transfer_t ct_, uhdr_color_range_t range_,
38 unsigned w_, unsigned h_, unsigned align_stride_to) {
39 this->fmt = fmt_;
40 this->cg = cg_;
41 this->ct = ct_;
42 this->range = range_;
43
44 this->w = w_;
45 this->h = h_;
46
47 int aligned_width = ALIGNM(w_, align_stride_to);
48
49 int bpp = 1;
50 if (fmt_ == UHDR_IMG_FMT_24bppYCbCrP010) {
51 bpp = 2;
52 } else if (fmt_ == UHDR_IMG_FMT_32bppRGBA8888 || fmt_ == UHDR_IMG_FMT_32bppRGBA1010102) {
53 bpp = 4;
54 } else if (fmt_ == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
55 bpp = 8;
56 }
57
58 size_t plane_1_sz = bpp * aligned_width * h_;
59 size_t plane_2_sz;
60 size_t plane_3_sz;
61 if (fmt_ == UHDR_IMG_FMT_24bppYCbCrP010) {
62 plane_2_sz = (2 /* planes */ * ((aligned_width / 2) * (h_ / 2) * bpp));
63 plane_3_sz = 0;
64 } else if (fmt_ == UHDR_IMG_FMT_12bppYCbCr420) {
65 plane_2_sz = (((aligned_width / 2) * (h_ / 2) * bpp));
66 plane_3_sz = (((aligned_width / 2) * (h_ / 2) * bpp));
67 } else {
68 plane_2_sz = 0;
69 plane_3_sz = 0;
70 }
71 size_t total_size = plane_1_sz + plane_2_sz + plane_3_sz;
72 this->m_block = std::make_unique<uhdr_memory_block_t>(total_size);
73
74 uint8_t* data = this->m_block->m_buffer.get();
75 this->planes[UHDR_PLANE_Y] = data;
76 this->stride[UHDR_PLANE_Y] = aligned_width;
77 if (fmt_ == UHDR_IMG_FMT_24bppYCbCrP010) {
78 this->planes[UHDR_PLANE_UV] = data + plane_1_sz;
79 this->stride[UHDR_PLANE_UV] = aligned_width;
80 this->planes[UHDR_PLANE_V] = nullptr;
81 this->stride[UHDR_PLANE_V] = 0;
82 } else if (fmt_ == UHDR_IMG_FMT_12bppYCbCr420) {
83 this->planes[UHDR_PLANE_U] = data + plane_1_sz;
84 this->stride[UHDR_PLANE_U] = aligned_width / 2;
85 this->planes[UHDR_PLANE_V] = data + plane_1_sz + plane_2_sz;
86 this->stride[UHDR_PLANE_V] = aligned_width / 2;
87 } else {
88 this->planes[UHDR_PLANE_U] = nullptr;
89 this->stride[UHDR_PLANE_U] = 0;
90 this->planes[UHDR_PLANE_V] = nullptr;
91 this->stride[UHDR_PLANE_V] = 0;
92 }
93 }
94
uhdr_compressed_image_ext(uhdr_color_gamut_t cg_,uhdr_color_transfer_t ct_,uhdr_color_range_t range_,unsigned size)95 uhdr_compressed_image_ext::uhdr_compressed_image_ext(uhdr_color_gamut_t cg_,
96 uhdr_color_transfer_t ct_,
97 uhdr_color_range_t range_, unsigned size) {
98 this->m_block = std::make_unique<uhdr_memory_block_t>(size);
99 this->data = this->m_block->m_buffer.get();
100 this->capacity = size;
101 this->data_sz = 0;
102 this->cg = cg_;
103 this->ct = ct_;
104 this->range = range_;
105 }
106
apply_effects(uhdr_encoder_private * enc)107 uhdr_error_info_t apply_effects(uhdr_encoder_private* enc) {
108 for (auto& it : enc->m_effects) {
109 std::unique_ptr<ultrahdr::uhdr_raw_image_ext_t> hdr_img = nullptr;
110 std::unique_ptr<ultrahdr::uhdr_raw_image_ext_t> sdr_img = nullptr;
111
112 if (nullptr != dynamic_cast<uhdr_rotate_effect_t*>(it)) {
113 auto& hdr_raw_entry = enc->m_raw_images.find(UHDR_HDR_IMG)->second;
114 hdr_img = apply_rotate(dynamic_cast<uhdr_rotate_effect_t*>(it), hdr_raw_entry.get());
115 if (enc->m_raw_images.find(UHDR_SDR_IMG) != enc->m_raw_images.end()) {
116 auto& sdr_raw_entry = enc->m_raw_images.find(UHDR_SDR_IMG)->second;
117 sdr_img = apply_rotate(dynamic_cast<uhdr_rotate_effect_t*>(it), sdr_raw_entry.get());
118 }
119 } else if (nullptr != dynamic_cast<uhdr_mirror_effect_t*>(it)) {
120 auto& hdr_raw_entry = enc->m_raw_images.find(UHDR_HDR_IMG)->second;
121 hdr_img = apply_mirror(dynamic_cast<uhdr_mirror_effect_t*>(it), hdr_raw_entry.get());
122 if (enc->m_raw_images.find(UHDR_SDR_IMG) != enc->m_raw_images.end()) {
123 auto& sdr_raw_entry = enc->m_raw_images.find(UHDR_SDR_IMG)->second;
124 sdr_img = apply_mirror(dynamic_cast<uhdr_mirror_effect_t*>(it), sdr_raw_entry.get());
125 }
126 } else if (nullptr != dynamic_cast<uhdr_crop_effect_t*>(it)) {
127 auto crop_effect = dynamic_cast<uhdr_crop_effect_t*>(it);
128 auto& hdr_raw_entry = enc->m_raw_images.find(UHDR_HDR_IMG)->second;
129 int left = (std::max)(0, crop_effect->m_left);
130 int right = (std::min)((int)hdr_raw_entry->w, crop_effect->m_right);
131 int crop_width = right - left;
132 if (crop_width <= 0 || (crop_width % 2 != 0)) {
133 uhdr_error_info_t status;
134 status.error_code = UHDR_CODEC_INVALID_PARAM;
135 status.has_detail = 1;
136 snprintf(status.detail, sizeof status.detail,
137 "unexpected crop dimensions. crop width is expected to be > 0 and even, crop "
138 "width is %d",
139 crop_width);
140 return status;
141 }
142
143 int top = (std::max)(0, crop_effect->m_top);
144 int bottom = (std::min)((int)hdr_raw_entry->h, crop_effect->m_bottom);
145 int crop_height = bottom - top;
146 if (crop_height <= 0 || (crop_height % 2 != 0)) {
147 uhdr_error_info_t status;
148 status.error_code = UHDR_CODEC_INVALID_PARAM;
149 status.has_detail = 1;
150 snprintf(status.detail, sizeof status.detail,
151 "unexpected crop dimensions. crop height is expected to be > 0 and even, crop "
152 "height is %d",
153 crop_height);
154 return status;
155 }
156 apply_crop(hdr_raw_entry.get(), left, top, crop_width, crop_height);
157 if (enc->m_raw_images.find(UHDR_SDR_IMG) != enc->m_raw_images.end()) {
158 auto& sdr_raw_entry = enc->m_raw_images.find(UHDR_SDR_IMG)->second;
159 apply_crop(sdr_raw_entry.get(), left, top, crop_width, crop_height);
160 }
161 continue;
162 } else if (nullptr != dynamic_cast<uhdr_resize_effect_t*>(it)) {
163 auto resize_effect = dynamic_cast<uhdr_resize_effect_t*>(it);
164 int dst_w = resize_effect->m_width;
165 int dst_h = resize_effect->m_height;
166 if (dst_w == 0 || dst_h == 0 || dst_w % 2 != 0 || dst_h % 2 != 0) {
167 uhdr_error_info_t status;
168 status.error_code = UHDR_CODEC_INVALID_PARAM;
169 snprintf(status.detail, sizeof status.detail,
170 "destination dimension cannot be zero or odd. dest image width is %d, dest image "
171 "height is %d",
172 dst_w, dst_h);
173 return status;
174 }
175 auto& hdr_raw_entry = enc->m_raw_images.find(UHDR_HDR_IMG)->second;
176 hdr_img =
177 apply_resize(dynamic_cast<uhdr_resize_effect_t*>(it), hdr_raw_entry.get(), dst_w, dst_h);
178 if (enc->m_raw_images.find(UHDR_SDR_IMG) != enc->m_raw_images.end()) {
179 auto& sdr_raw_entry = enc->m_raw_images.find(UHDR_SDR_IMG)->second;
180 sdr_img = apply_resize(dynamic_cast<uhdr_resize_effect_t*>(it), sdr_raw_entry.get(), dst_w,
181 dst_h);
182 }
183 }
184
185 if (hdr_img == nullptr ||
186 (enc->m_raw_images.find(UHDR_SDR_IMG) != enc->m_raw_images.end() && sdr_img == nullptr)) {
187 uhdr_error_info_t status;
188 status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
189 status.has_detail = 1;
190 snprintf(status.detail, sizeof status.detail,
191 "encountered unknown error while applying effect %s", it->to_string().c_str());
192 return status;
193 }
194 enc->m_raw_images.insert_or_assign(UHDR_HDR_IMG, std::move(hdr_img));
195 if (sdr_img != nullptr) {
196 enc->m_raw_images.insert_or_assign(UHDR_SDR_IMG, std::move(sdr_img));
197 }
198 }
199 if (enc->m_effects.size() > 0) {
200 auto it = enc->m_effects.back();
201 if (nullptr != dynamic_cast<uhdr_crop_effect_t*>(it) &&
202 enc->m_raw_images.find(UHDR_SDR_IMG) != enc->m_raw_images.end()) {
203 // As cropping is handled via pointer arithmetic as opposed to buffer copy, u and v data of
204 // yuv420 inputs are no longer contiguous. As the library does not accept distinct buffer
205 // pointers for u and v for 420 input, copy the sdr intent to a contiguous buffer
206 auto& sdr_raw_entry = enc->m_raw_images.find(UHDR_SDR_IMG)->second;
207 enc->m_raw_images.insert_or_assign(UHDR_SDR_IMG,
208 convert_raw_input_to_ycbcr(sdr_raw_entry.get()));
209 }
210 }
211
212 return g_no_error;
213 }
214
apply_effects(uhdr_decoder_private * dec)215 uhdr_error_info_t apply_effects(uhdr_decoder_private* dec) {
216 for (auto& it : dec->m_effects) {
217 std::unique_ptr<ultrahdr::uhdr_raw_image_ext_t> disp_img = nullptr;
218 std::unique_ptr<ultrahdr::uhdr_raw_image_ext_t> gm_img = nullptr;
219
220 if (nullptr != dynamic_cast<uhdr_rotate_effect_t*>(it)) {
221 disp_img =
222 apply_rotate(dynamic_cast<uhdr_rotate_effect_t*>(it), dec->m_decoded_img_buffer.get());
223 gm_img =
224 apply_rotate(dynamic_cast<uhdr_rotate_effect_t*>(it), dec->m_gainmap_img_buffer.get());
225 } else if (nullptr != dynamic_cast<uhdr_mirror_effect_t*>(it)) {
226 disp_img =
227 apply_mirror(dynamic_cast<uhdr_mirror_effect_t*>(it), dec->m_decoded_img_buffer.get());
228 gm_img =
229 apply_mirror(dynamic_cast<uhdr_mirror_effect_t*>(it), dec->m_gainmap_img_buffer.get());
230 } else if (nullptr != dynamic_cast<uhdr_crop_effect_t*>(it)) {
231 auto crop_effect = dynamic_cast<uhdr_crop_effect_t*>(it);
232 uhdr_raw_image_t* disp = dec->m_decoded_img_buffer.get();
233 uhdr_raw_image_t* gm = dec->m_gainmap_img_buffer.get();
234 int left = (std::max)(0, crop_effect->m_left);
235 int right = (std::min)((int)disp->w, crop_effect->m_right);
236 if (right <= left) {
237 uhdr_error_info_t status;
238 status.error_code = UHDR_CODEC_INVALID_PARAM;
239 status.has_detail = 1;
240 snprintf(
241 status.detail, sizeof status.detail,
242 "unexpected crop dimensions. crop right is <= crop left, after crop image width is %d",
243 right - left);
244 return status;
245 }
246
247 int top = (std::max)(0, crop_effect->m_top);
248 int bottom = (std::min)((int)disp->h, crop_effect->m_bottom);
249 if (bottom <= top) {
250 uhdr_error_info_t status;
251 status.error_code = UHDR_CODEC_INVALID_PARAM;
252 status.has_detail = 1;
253 snprintf(
254 status.detail, sizeof status.detail,
255 "unexpected crop dimensions. crop bottom is <= crop top, after crop image height is %d",
256 bottom - top);
257 return status;
258 }
259
260 float wd_ratio = ((float)disp->w) / gm->w;
261 float ht_ratio = ((float)disp->h) / gm->h;
262 int gm_left = left / wd_ratio;
263 int gm_right = right / wd_ratio;
264 if (gm_right <= gm_left) {
265 uhdr_error_info_t status;
266 status.error_code = UHDR_CODEC_INVALID_PARAM;
267 status.has_detail = 1;
268 snprintf(status.detail, sizeof status.detail,
269 "unexpected crop dimensions. crop right is <= crop left for gainmap image, after "
270 "crop gainmap image width is %d",
271 gm_right - gm_left);
272 return status;
273 }
274
275 int gm_top = top / ht_ratio;
276 int gm_bottom = bottom / ht_ratio;
277 if (gm_bottom <= gm_top) {
278 uhdr_error_info_t status;
279 status.error_code = UHDR_CODEC_INVALID_PARAM;
280 status.has_detail = 1;
281 snprintf(status.detail, sizeof status.detail,
282 "unexpected crop dimensions. crop bottom is <= crop top for gainmap image, after "
283 "crop gainmap image height is %d",
284 gm_bottom - gm_top);
285 return status;
286 }
287
288 apply_crop(disp, left, top, right - left, bottom - top);
289 apply_crop(gm, gm_left, gm_top, (gm_right - gm_left), (gm_bottom - gm_top));
290 continue;
291 } else if (nullptr != dynamic_cast<uhdr_resize_effect_t*>(it)) {
292 auto resize_effect = dynamic_cast<uhdr_resize_effect_t*>(it);
293 int dst_w = resize_effect->m_width;
294 int dst_h = resize_effect->m_height;
295 float wd_ratio =
296 ((float)dec->m_decoded_img_buffer.get()->w) / dec->m_gainmap_img_buffer.get()->w;
297 float ht_ratio =
298 ((float)dec->m_decoded_img_buffer.get()->h) / dec->m_gainmap_img_buffer.get()->h;
299 int dst_gm_w = dst_w / wd_ratio;
300 int dst_gm_h = dst_h / ht_ratio;
301 if (dst_w == 0 || dst_h == 0 || dst_gm_w == 0 || dst_gm_h == 0) {
302 uhdr_error_info_t status;
303 status.error_code = UHDR_CODEC_INVALID_PARAM;
304 snprintf(status.detail, sizeof status.detail,
305 "destination dimension cannot be zero. dest image width is %d, dest image height "
306 "is %d, dest gainmap width is %d, dest gainmap height is %d",
307 dst_w, dst_h, dst_gm_w, dst_gm_h);
308 return status;
309 }
310 disp_img = apply_resize(dynamic_cast<uhdr_resize_effect_t*>(it),
311 dec->m_decoded_img_buffer.get(), dst_w, dst_h);
312 gm_img = apply_resize(dynamic_cast<uhdr_resize_effect_t*>(it),
313 dec->m_gainmap_img_buffer.get(), dst_gm_w, dst_gm_h);
314 }
315
316 if (disp_img == nullptr || gm_img == nullptr) {
317 uhdr_error_info_t status;
318 status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
319 status.has_detail = 1;
320 snprintf(status.detail, sizeof status.detail,
321 "encountered unknown error while applying effect %s", it->to_string().c_str());
322 return status;
323 }
324 dec->m_decoded_img_buffer = std::move(disp_img);
325 dec->m_gainmap_img_buffer = std::move(gm_img);
326 }
327
328 return g_no_error;
329 }
330
331 } // namespace ultrahdr
332
~uhdr_codec_private()333 uhdr_codec_private::~uhdr_codec_private() {
334 for (auto it : m_effects) delete it;
335 m_effects.clear();
336 }
337
map_cg_to_internal_cg(uhdr_color_gamut_t cg)338 ultrahdr::ultrahdr_color_gamut map_cg_to_internal_cg(uhdr_color_gamut_t cg) {
339 switch (cg) {
340 case UHDR_CG_BT_2100:
341 return ultrahdr::ULTRAHDR_COLORGAMUT_BT2100;
342 case UHDR_CG_BT_709:
343 return ultrahdr::ULTRAHDR_COLORGAMUT_BT709;
344 case UHDR_CG_DISPLAY_P3:
345 return ultrahdr::ULTRAHDR_COLORGAMUT_P3;
346 default:
347 return ultrahdr::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
348 }
349 }
350
map_internal_cg_to_cg(ultrahdr::ultrahdr_color_gamut cg)351 uhdr_color_gamut_t map_internal_cg_to_cg(ultrahdr::ultrahdr_color_gamut cg) {
352 switch (cg) {
353 case ultrahdr::ULTRAHDR_COLORGAMUT_BT2100:
354 return UHDR_CG_BT_2100;
355 case ultrahdr::ULTRAHDR_COLORGAMUT_BT709:
356 return UHDR_CG_BT_709;
357 case ultrahdr::ULTRAHDR_COLORGAMUT_P3:
358 return UHDR_CG_DISPLAY_P3;
359 default:
360 return UHDR_CG_UNSPECIFIED;
361 }
362 }
363
map_ct_to_internal_ct(uhdr_color_transfer_t ct)364 ultrahdr::ultrahdr_transfer_function map_ct_to_internal_ct(uhdr_color_transfer_t ct) {
365 switch (ct) {
366 case UHDR_CT_HLG:
367 return ultrahdr::ULTRAHDR_TF_HLG;
368 case UHDR_CT_PQ:
369 return ultrahdr::ULTRAHDR_TF_PQ;
370 case UHDR_CT_LINEAR:
371 return ultrahdr::ULTRAHDR_TF_LINEAR;
372 case UHDR_CT_SRGB:
373 return ultrahdr::ULTRAHDR_TF_SRGB;
374 default:
375 return ultrahdr::ULTRAHDR_TF_UNSPECIFIED;
376 }
377 }
378
map_ct_fmt_to_internal_output_fmt(uhdr_color_transfer_t ct,uhdr_img_fmt fmt)379 ultrahdr::ultrahdr_output_format map_ct_fmt_to_internal_output_fmt(uhdr_color_transfer_t ct,
380 uhdr_img_fmt fmt) {
381 if (ct == UHDR_CT_HLG && fmt == UHDR_IMG_FMT_32bppRGBA1010102) {
382 return ultrahdr::ULTRAHDR_OUTPUT_HDR_HLG;
383 } else if (ct == UHDR_CT_PQ && fmt == UHDR_IMG_FMT_32bppRGBA1010102) {
384 return ultrahdr::ULTRAHDR_OUTPUT_HDR_PQ;
385 } else if (ct == UHDR_CT_LINEAR && fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
386 return ultrahdr::ULTRAHDR_OUTPUT_HDR_LINEAR;
387 } else if (ct == UHDR_CT_SRGB && fmt == UHDR_IMG_FMT_32bppRGBA8888) {
388 return ultrahdr::ULTRAHDR_OUTPUT_SDR;
389 }
390 return ultrahdr::ULTRAHDR_OUTPUT_UNSPECIFIED;
391 }
392
map_internal_error_status_to_error_info(ultrahdr::status_t internal_status,uhdr_error_info_t & status)393 void map_internal_error_status_to_error_info(ultrahdr::status_t internal_status,
394 uhdr_error_info_t& status) {
395 if (internal_status == ultrahdr::JPEGR_NO_ERROR) {
396 status = g_no_error;
397 } else {
398 status.has_detail = 1;
399 if (internal_status == ultrahdr::ERROR_JPEGR_RESOLUTION_MISMATCH) {
400 status.error_code = UHDR_CODEC_INVALID_PARAM;
401 snprintf(status.detail, sizeof status.detail,
402 "dimensions of sdr intent and hdr intent do not match");
403 } else if (internal_status == ultrahdr::ERROR_JPEGR_ENCODE_ERROR) {
404 status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
405 snprintf(status.detail, sizeof status.detail, "encountered unknown error during encoding");
406 } else if (internal_status == ultrahdr::ERROR_JPEGR_DECODE_ERROR) {
407 status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
408 snprintf(status.detail, sizeof status.detail, "encountered unknown error during decoding");
409 } else if (internal_status == ultrahdr::ERROR_JPEGR_NO_IMAGES_FOUND) {
410 status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
411 snprintf(status.detail, sizeof status.detail, "input uhdr image does not any valid images");
412 } else if (internal_status == ultrahdr::ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND) {
413 status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
414 snprintf(status.detail, sizeof status.detail,
415 "input uhdr image does not contain gainmap image");
416 } else if (internal_status == ultrahdr::ERROR_JPEGR_BUFFER_TOO_SMALL) {
417 status.error_code = UHDR_CODEC_MEM_ERROR;
418 snprintf(status.detail, sizeof status.detail,
419 "output buffer to store compressed data is too small");
420 } else if (internal_status == ultrahdr::ERROR_JPEGR_MULTIPLE_EXIFS_RECEIVED) {
421 status.error_code = UHDR_CODEC_INVALID_OPERATION;
422 snprintf(status.detail, sizeof status.detail,
423 "received exif from uhdr_enc_set_exif_data() while the base image intent already "
424 "contains exif, unsure which one to use");
425 } else if (internal_status == ultrahdr::ERROR_JPEGR_UNSUPPORTED_MAP_SCALE_FACTOR) {
426 status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
427 snprintf(status.detail, sizeof status.detail,
428 "say base image wd to gain map image wd ratio is 'k1' and base image ht to gain map "
429 "image ht ratio is 'k2', we found k1 != k2.");
430 } else {
431 status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
432 status.has_detail = 0;
433 }
434 }
435 }
436
uhdr_enc_validate_and_set_compressed_img(uhdr_codec_private_t * enc,uhdr_compressed_image_t * img,uhdr_img_label_t intent)437 uhdr_error_info_t uhdr_enc_validate_and_set_compressed_img(uhdr_codec_private_t* enc,
438 uhdr_compressed_image_t* img,
439 uhdr_img_label_t intent) {
440 uhdr_error_info_t status = g_no_error;
441
442 if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
443 status.error_code = UHDR_CODEC_INVALID_PARAM;
444 status.has_detail = 1;
445 snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
446 } else if (img == nullptr) {
447 status.error_code = UHDR_CODEC_INVALID_PARAM;
448 status.has_detail = 1;
449 snprintf(status.detail, sizeof status.detail, "received nullptr for compressed image handle");
450 } else if (img->data == nullptr) {
451 status.error_code = UHDR_CODEC_INVALID_PARAM;
452 status.has_detail = 1;
453 snprintf(status.detail, sizeof status.detail,
454 "received nullptr for compressed img->data field");
455 } else if (img->capacity < img->data_sz) {
456 status.error_code = UHDR_CODEC_INVALID_PARAM;
457 status.has_detail = 1;
458 snprintf(status.detail, sizeof status.detail, "img->capacity %d is less than img->data_sz %d",
459 img->capacity, img->data_sz);
460 }
461 if (status.error_code != UHDR_CODEC_OK) return status;
462
463 uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
464 if (handle->m_sailed) {
465 status.error_code = UHDR_CODEC_INVALID_OPERATION;
466 status.has_detail = 1;
467 snprintf(status.detail, sizeof status.detail,
468 "An earlier call to uhdr_encode() has switched the context from configurable state to "
469 "end state. The context is no longer configurable. To reuse, call reset()");
470 return status;
471 }
472
473 auto entry = std::make_unique<ultrahdr::uhdr_compressed_image_ext_t>(img->cg, img->ct, img->range,
474 img->data_sz);
475 memcpy(entry->data, img->data, img->data_sz);
476 entry->data_sz = img->data_sz;
477 handle->m_compressed_images.insert_or_assign(intent, std::move(entry));
478
479 return status;
480 }
481
uhdr_create_encoder(void)482 uhdr_codec_private_t* uhdr_create_encoder(void) {
483 uhdr_encoder_private* handle = new uhdr_encoder_private();
484
485 if (handle != nullptr) {
486 uhdr_reset_encoder(handle);
487 }
488 return handle;
489 }
490
uhdr_release_encoder(uhdr_codec_private_t * enc)491 void uhdr_release_encoder(uhdr_codec_private_t* enc) {
492 if (dynamic_cast<uhdr_encoder_private*>(enc) != nullptr) {
493 uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
494 delete handle;
495 }
496 }
497
uhdr_enc_set_raw_image(uhdr_codec_private_t * enc,uhdr_raw_image_t * img,uhdr_img_label_t intent)498 uhdr_error_info_t uhdr_enc_set_raw_image(uhdr_codec_private_t* enc, uhdr_raw_image_t* img,
499 uhdr_img_label_t intent) {
500 uhdr_error_info_t status = g_no_error;
501
502 if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
503 status.error_code = UHDR_CODEC_INVALID_PARAM;
504 status.has_detail = 1;
505 snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
506 } else if (img == nullptr) {
507 status.error_code = UHDR_CODEC_INVALID_PARAM;
508 status.has_detail = 1;
509 snprintf(status.detail, sizeof status.detail, "received nullptr for raw image handle");
510 } else if (intent != UHDR_HDR_IMG && intent != UHDR_SDR_IMG) {
511 status.error_code = UHDR_CODEC_INVALID_PARAM;
512 status.has_detail = 1;
513 snprintf(status.detail, sizeof status.detail,
514 "invalid intent %d, expects one of {UHDR_HDR_IMG, UHDR_SDR_IMG}", intent);
515 } else if (intent == UHDR_HDR_IMG && (img->fmt != UHDR_IMG_FMT_24bppYCbCrP010 &&
516 img->fmt != UHDR_IMG_FMT_32bppRGBA1010102)) {
517 status.error_code = UHDR_CODEC_INVALID_PARAM;
518 status.has_detail = 1;
519 snprintf(status.detail, sizeof status.detail,
520 "unsupported input pixel format for hdr intent %d, expects one of "
521 "{UHDR_IMG_FMT_24bppYCbCrP010, UHDR_IMG_FMT_32bppRGBA1010102}",
522 img->fmt);
523 } else if (intent == UHDR_SDR_IMG &&
524 (img->fmt != UHDR_IMG_FMT_12bppYCbCr420 && img->fmt != UHDR_IMG_FMT_32bppRGBA8888)) {
525 status.error_code = UHDR_CODEC_INVALID_PARAM;
526 status.has_detail = 1;
527 snprintf(status.detail, sizeof status.detail,
528 "unsupported input pixel format for sdr intent %d, expects one of "
529 "{UHDR_IMG_FMT_12bppYCbCr420, UHDR_IMG_FMT_32bppRGBA8888}",
530 img->fmt);
531 } else if (img->cg != UHDR_CG_BT_2100 && img->cg != UHDR_CG_DISPLAY_P3 &&
532 img->cg != UHDR_CG_BT_709) {
533 status.error_code = UHDR_CODEC_INVALID_PARAM;
534 status.has_detail = 1;
535 snprintf(status.detail, sizeof status.detail,
536 "invalid input color gamut %d, expects one of {UHDR_CG_BT_2100, UHDR_CG_DISPLAY_P3, "
537 "UHDR_CG_BT_709}",
538 img->cg);
539 } else if (img->fmt == UHDR_IMG_FMT_12bppYCbCr420 && img->ct != UHDR_CT_SRGB) {
540 status.error_code = UHDR_CODEC_INVALID_PARAM;
541 status.has_detail = 1;
542 snprintf(status.detail, sizeof status.detail,
543 "invalid input color transfer for sdr intent image %d, expects UHDR_CT_SRGB", img->ct);
544 } else if (img->fmt == UHDR_IMG_FMT_24bppYCbCrP010 &&
545 (img->ct != UHDR_CT_HLG && img->ct != UHDR_CT_LINEAR && img->ct != UHDR_CT_PQ)) {
546 status.error_code = UHDR_CODEC_INVALID_PARAM;
547 status.has_detail = 1;
548 snprintf(status.detail, sizeof status.detail,
549 "invalid input color transfer for hdr intent image %d, expects one of {UHDR_CT_HLG, "
550 "UHDR_CT_LINEAR, UHDR_CT_PQ}",
551 img->ct);
552 } else if (img->w % 2 != 0 || img->h % 2 != 0) {
553 status.error_code = UHDR_CODEC_INVALID_PARAM;
554 status.has_detail = 1;
555 snprintf(status.detail, sizeof status.detail,
556 "image dimensions cannot be odd, received image dimensions %dx%d", img->w, img->h);
557 } else if (img->w < ultrahdr::kMinWidth || img->h < ultrahdr::kMinHeight) {
558 status.error_code = UHDR_CODEC_INVALID_PARAM;
559 status.has_detail = 1;
560 snprintf(status.detail, sizeof status.detail,
561 "image dimensions cannot be less than %dx%d, received image dimensions %dx%d",
562 ultrahdr::kMinWidth, ultrahdr::kMinHeight, img->w, img->h);
563 } else if (img->w > ultrahdr::kMaxWidth || img->h > ultrahdr::kMaxHeight) {
564 status.error_code = UHDR_CODEC_INVALID_PARAM;
565 status.has_detail = 1;
566 snprintf(status.detail, sizeof status.detail,
567 "image dimensions cannot be larger than %dx%d, received image dimensions %dx%d",
568 ultrahdr::kMaxWidth, ultrahdr::kMaxHeight, img->w, img->h);
569 } else if (img->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
570 if (img->planes[UHDR_PLANE_Y] == nullptr || img->planes[UHDR_PLANE_UV] == nullptr) {
571 status.error_code = UHDR_CODEC_INVALID_PARAM;
572 status.has_detail = 1;
573 snprintf(status.detail, sizeof status.detail,
574 "received nullptr for data field(s), luma ptr %p, chroma_uv ptr %p",
575 img->planes[UHDR_PLANE_Y], img->planes[UHDR_PLANE_UV]);
576 } else if (img->stride[UHDR_PLANE_Y] < img->w) {
577 status.error_code = UHDR_CODEC_INVALID_PARAM;
578 status.has_detail = 1;
579 snprintf(status.detail, sizeof status.detail,
580 "luma stride must not be smaller than width, stride=%d, width=%d",
581 img->stride[UHDR_PLANE_Y], img->w);
582 } else if (img->stride[UHDR_PLANE_UV] < img->w) {
583 status.error_code = UHDR_CODEC_INVALID_PARAM;
584 status.has_detail = 1;
585 snprintf(status.detail, sizeof status.detail,
586 "chroma_uv stride must not be smaller than width, stride=%d, width=%d",
587 img->stride[UHDR_PLANE_UV], img->w);
588 }
589 } else if (img->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
590 if (img->planes[UHDR_PLANE_Y] == nullptr || img->planes[UHDR_PLANE_U] == nullptr ||
591 img->planes[UHDR_PLANE_V] == nullptr) {
592 status.error_code = UHDR_CODEC_INVALID_PARAM;
593 status.has_detail = 1;
594 snprintf(status.detail, sizeof status.detail,
595 "received nullptr for data field(s) luma ptr %p, chroma_u ptr %p, chroma_v ptr %p",
596 img->planes[UHDR_PLANE_Y], img->planes[UHDR_PLANE_U], img->planes[UHDR_PLANE_V]);
597 } else if (img->stride[UHDR_PLANE_Y] < img->w) {
598 status.error_code = UHDR_CODEC_INVALID_PARAM;
599 status.has_detail = 1;
600 snprintf(status.detail, sizeof status.detail,
601 "luma stride must not be smaller than width, stride=%d, width=%d",
602 img->stride[UHDR_PLANE_Y], img->w);
603 } else if (img->stride[UHDR_PLANE_U] < img->w / 2) {
604 status.error_code = UHDR_CODEC_INVALID_PARAM;
605 status.has_detail = 1;
606 snprintf(status.detail, sizeof status.detail,
607 "chroma_u stride must not be smaller than width / 2, stride=%d, width=%d",
608 img->stride[UHDR_PLANE_U], img->w);
609 } else if (img->stride[UHDR_PLANE_V] < img->w / 2) {
610 status.error_code = UHDR_CODEC_INVALID_PARAM;
611 status.has_detail = 1;
612 snprintf(status.detail, sizeof status.detail,
613 "chroma_v stride must not be smaller than width / 2, stride=%d, width=%d",
614 img->stride[UHDR_PLANE_V], img->w);
615 }
616 }
617 if (status.error_code != UHDR_CODEC_OK) return status;
618
619 uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
620 if (intent == UHDR_HDR_IMG &&
621 handle->m_raw_images.find(UHDR_SDR_IMG) != handle->m_raw_images.end()) {
622 auto& sdr_raw_entry = handle->m_raw_images.find(UHDR_SDR_IMG)->second;
623 if (img->w != sdr_raw_entry->w || img->h != sdr_raw_entry->h) {
624 status.error_code = UHDR_CODEC_INVALID_PARAM;
625 status.has_detail = 1;
626 snprintf(status.detail, sizeof status.detail,
627 "image resolutions mismatch: hdr intent: %dx%d, sdr intent: %dx%d", img->w, img->h,
628 sdr_raw_entry->w, sdr_raw_entry->h);
629 return status;
630 }
631 }
632 if (intent == UHDR_SDR_IMG &&
633 handle->m_raw_images.find(UHDR_HDR_IMG) != handle->m_raw_images.end()) {
634 auto& hdr_raw_entry = handle->m_raw_images.find(UHDR_HDR_IMG)->second;
635 if (img->w != hdr_raw_entry->w || img->h != hdr_raw_entry->h) {
636 status.error_code = UHDR_CODEC_INVALID_PARAM;
637 status.has_detail = 1;
638 snprintf(status.detail, sizeof status.detail,
639 "image resolutions mismatch: sdr intent: %dx%d, hdr intent: %dx%d", img->w, img->h,
640 hdr_raw_entry->w, hdr_raw_entry->h);
641 return status;
642 }
643 }
644 if (handle->m_sailed) {
645 status.error_code = UHDR_CODEC_INVALID_OPERATION;
646 status.has_detail = 1;
647 snprintf(status.detail, sizeof status.detail,
648 "An earlier call to uhdr_encode() has switched the context from configurable state to "
649 "end state. The context is no longer configurable. To reuse, call reset()");
650 return status;
651 }
652
653 std::unique_ptr<ultrahdr::uhdr_raw_image_ext_t> entry = ultrahdr::convert_raw_input_to_ycbcr(img);
654 if (entry == nullptr) {
655 status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
656 status.has_detail = 1;
657 snprintf(status.detail, sizeof status.detail,
658 "encountered unknown error during color space conversion");
659 return status;
660 }
661
662 handle->m_raw_images.insert_or_assign(intent, std::move(entry));
663
664 return status;
665 }
666
uhdr_enc_set_compressed_image(uhdr_codec_private_t * enc,uhdr_compressed_image_t * img,uhdr_img_label_t intent)667 uhdr_error_info_t uhdr_enc_set_compressed_image(uhdr_codec_private_t* enc,
668 uhdr_compressed_image_t* img,
669 uhdr_img_label_t intent) {
670 uhdr_error_info_t status = g_no_error;
671
672 if (intent != UHDR_HDR_IMG && intent != UHDR_SDR_IMG && intent != UHDR_BASE_IMG) {
673 status.error_code = UHDR_CODEC_INVALID_PARAM;
674 status.has_detail = 1;
675 snprintf(status.detail, sizeof status.detail,
676 "invalid intent %d, expects one of {UHDR_HDR_IMG, UHDR_SDR_IMG, UHDR_BASE_IMG}",
677 intent);
678 }
679
680 return uhdr_enc_validate_and_set_compressed_img(enc, img, intent);
681 }
682
uhdr_enc_set_gainmap_image(uhdr_codec_private_t * enc,uhdr_compressed_image_t * img,uhdr_gainmap_metadata_t * metadata)683 uhdr_error_info_t uhdr_enc_set_gainmap_image(uhdr_codec_private_t* enc,
684 uhdr_compressed_image_t* img,
685 uhdr_gainmap_metadata_t* metadata) {
686 uhdr_error_info_t status = g_no_error;
687
688 if (metadata == nullptr) {
689 status.error_code = UHDR_CODEC_INVALID_PARAM;
690 status.has_detail = 1;
691 snprintf(status.detail, sizeof status.detail,
692 "received nullptr for gainmap metadata descriptor");
693 } else if (metadata->max_content_boost < metadata->min_content_boost) {
694 status.error_code = UHDR_CODEC_INVALID_PARAM;
695 status.has_detail = 1;
696 snprintf(status.detail, sizeof status.detail,
697 "received bad value for content boost min %f > max %f", metadata->min_content_boost,
698 metadata->max_content_boost);
699 } else if (metadata->gamma <= 0.0f) {
700 status.error_code = UHDR_CODEC_INVALID_PARAM;
701 status.has_detail = 1;
702 snprintf(status.detail, sizeof status.detail, "received bad value for gamma %f, expects > 0.0f",
703 metadata->gamma);
704 } else if (metadata->offset_sdr < 0.0f) {
705 status.error_code = UHDR_CODEC_INVALID_PARAM;
706 status.has_detail = 1;
707 snprintf(status.detail, sizeof status.detail,
708 "received bad value for offset sdr %f, expects to be >= 0.0f", metadata->offset_sdr);
709 } else if (metadata->offset_hdr < 0.0f) {
710 status.error_code = UHDR_CODEC_INVALID_PARAM;
711 status.has_detail = 1;
712 snprintf(status.detail, sizeof status.detail,
713 "received bad value for offset hdr %f, expects to be >= 0.0f", metadata->offset_hdr);
714 } else if (metadata->hdr_capacity_max < metadata->hdr_capacity_min) {
715 status.error_code = UHDR_CODEC_INVALID_PARAM;
716 status.has_detail = 1;
717 snprintf(status.detail, sizeof status.detail,
718 "received bad value for hdr capacity min %f > max %f", metadata->hdr_capacity_min,
719 metadata->hdr_capacity_max);
720 } else if (metadata->hdr_capacity_min < 1.0f) {
721 status.error_code = UHDR_CODEC_INVALID_PARAM;
722 status.has_detail = 1;
723 snprintf(status.detail, sizeof status.detail,
724 "received bad value for hdr capacity min %f, expects to be >= 1.0f",
725 metadata->hdr_capacity_min);
726 }
727 if (status.error_code != UHDR_CODEC_OK) return status;
728
729 status = uhdr_enc_validate_and_set_compressed_img(enc, img, UHDR_GAIN_MAP_IMG);
730 if (status.error_code != UHDR_CODEC_OK) return status;
731
732 uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
733 memcpy(&handle->m_metadata, metadata, sizeof *metadata);
734
735 return status;
736 }
737
uhdr_enc_set_quality(uhdr_codec_private_t * enc,int quality,uhdr_img_label_t intent)738 uhdr_error_info_t uhdr_enc_set_quality(uhdr_codec_private_t* enc, int quality,
739 uhdr_img_label_t intent) {
740 uhdr_error_info_t status = g_no_error;
741
742 if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
743 status.error_code = UHDR_CODEC_INVALID_PARAM;
744 status.has_detail = 1;
745 snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
746 } else if (quality < 0 || quality > 100) {
747 status.error_code = UHDR_CODEC_INVALID_PARAM;
748 status.has_detail = 1;
749 snprintf(status.detail, sizeof status.detail,
750 "invalid quality factor %d, expects in range [0-100]", quality);
751 } else if (intent != UHDR_HDR_IMG && intent != UHDR_SDR_IMG && intent != UHDR_BASE_IMG &&
752 intent != UHDR_GAIN_MAP_IMG) {
753 status.error_code = UHDR_CODEC_INVALID_PARAM;
754 status.has_detail = 1;
755 snprintf(status.detail, sizeof status.detail,
756 "invalid intent %d, expects one of {UHDR_HDR_IMG, UHDR_SDR_IMG, UHDR_BASE_IMG, "
757 "UHDR_GAIN_MAP_IMG}",
758 intent);
759 }
760 if (status.error_code != UHDR_CODEC_OK) return status;
761
762 uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
763 if (handle->m_sailed) {
764 status.error_code = UHDR_CODEC_INVALID_OPERATION;
765 status.has_detail = 1;
766 snprintf(status.detail, sizeof status.detail,
767 "An earlier call to uhdr_encode() has switched the context from configurable state to "
768 "end state. The context is no longer configurable. To reuse, call reset()");
769 return status;
770 }
771
772 handle->m_quality.insert_or_assign(intent, quality);
773
774 return status;
775 }
776
uhdr_enc_set_exif_data(uhdr_codec_private_t * enc,uhdr_mem_block_t * exif)777 uhdr_error_info_t uhdr_enc_set_exif_data(uhdr_codec_private_t* enc, uhdr_mem_block_t* exif) {
778 uhdr_error_info_t status = g_no_error;
779
780 if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
781 status.error_code = UHDR_CODEC_INVALID_PARAM;
782 status.has_detail = 1;
783 snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
784 } else if (exif == nullptr) {
785 status.error_code = UHDR_CODEC_INVALID_PARAM;
786 status.has_detail = 1;
787 snprintf(status.detail, sizeof status.detail, "received nullptr for exif image handle");
788 } else if (exif->data == nullptr) {
789 status.error_code = UHDR_CODEC_INVALID_PARAM;
790 status.has_detail = 1;
791 snprintf(status.detail, sizeof status.detail, "received nullptr for exif->data field");
792 } else if (exif->capacity < exif->data_sz) {
793 status.error_code = UHDR_CODEC_INVALID_PARAM;
794 status.has_detail = 1;
795 snprintf(status.detail, sizeof status.detail, "exif->capacity %d is less than exif->data_sz %d",
796 exif->capacity, exif->data_sz);
797 }
798 if (status.error_code != UHDR_CODEC_OK) return status;
799
800 uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
801 if (handle->m_sailed) {
802 status.error_code = UHDR_CODEC_INVALID_OPERATION;
803 status.has_detail = 1;
804 snprintf(status.detail, sizeof status.detail,
805 "An earlier call to uhdr_encode() has switched the context from configurable state to "
806 "end state. The context is no longer configurable. To reuse, call reset()");
807 return status;
808 }
809
810 uint8_t* data = static_cast<uint8_t*>(exif->data);
811 std::vector<uint8_t> entry(data, data + exif->data_sz);
812 handle->m_exif = std::move(entry);
813
814 return status;
815 }
816
uhdr_enc_set_output_format(uhdr_codec_private_t * enc,uhdr_codec_t media_type)817 uhdr_error_info_t uhdr_enc_set_output_format(uhdr_codec_private_t* enc, uhdr_codec_t media_type) {
818 uhdr_error_info_t status = g_no_error;
819
820 if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
821 status.error_code = UHDR_CODEC_INVALID_PARAM;
822 status.has_detail = 1;
823 snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
824 } else if (media_type != UHDR_CODEC_JPG) {
825 status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
826 status.has_detail = 1;
827 snprintf(status.detail, sizeof status.detail,
828 "invalid output format %d, expects {UHDR_CODEC_JPG}", media_type);
829 }
830 if (status.error_code != UHDR_CODEC_OK) return status;
831
832 uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
833 if (handle->m_sailed) {
834 status.error_code = UHDR_CODEC_INVALID_OPERATION;
835 status.has_detail = 1;
836 snprintf(status.detail, sizeof status.detail,
837 "An earlier call to uhdr_encode() has switched the context from configurable state to "
838 "end state. The context is no longer configurable. To reuse, call reset()");
839 return status;
840 }
841
842 handle->m_output_format = media_type;
843
844 return status;
845 }
846
uhdr_encode(uhdr_codec_private_t * enc)847 uhdr_error_info_t uhdr_encode(uhdr_codec_private_t* enc) {
848 if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
849 uhdr_error_info_t status;
850 status.error_code = UHDR_CODEC_INVALID_PARAM;
851 status.has_detail = 1;
852 snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
853 return status;
854 }
855
856 uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
857
858 if (handle->m_sailed) {
859 return handle->m_encode_call_status;
860 }
861
862 handle->m_sailed = true;
863
864 uhdr_error_info_t& status = handle->m_encode_call_status;
865
866 if (handle->m_compressed_images.find(UHDR_BASE_IMG) != handle->m_compressed_images.end() &&
867 handle->m_compressed_images.find(UHDR_GAIN_MAP_IMG) != handle->m_compressed_images.end()) {
868 if (handle->m_effects.size() != 0) {
869 status.error_code = UHDR_CODEC_INVALID_OPERATION;
870 status.has_detail = 1;
871 snprintf(status.detail, sizeof status.detail,
872 "image effects are not enabled for inputs with compressed intent");
873 return status;
874 }
875 } else if (handle->m_raw_images.find(UHDR_HDR_IMG) != handle->m_raw_images.end()) {
876 if (handle->m_compressed_images.find(UHDR_SDR_IMG) == handle->m_compressed_images.end() &&
877 handle->m_raw_images.find(UHDR_SDR_IMG) == handle->m_raw_images.end()) {
878 // api - 0
879 if (handle->m_effects.size() != 0) {
880 status = ultrahdr::apply_effects(handle);
881 if (status.error_code != UHDR_CODEC_OK) return status;
882 }
883 } else if (handle->m_compressed_images.find(UHDR_SDR_IMG) !=
884 handle->m_compressed_images.end() &&
885 handle->m_raw_images.find(UHDR_SDR_IMG) == handle->m_raw_images.end()) {
886 if (handle->m_effects.size() != 0) {
887 status.error_code = UHDR_CODEC_INVALID_OPERATION;
888 status.has_detail = 1;
889 snprintf(status.detail, sizeof status.detail,
890 "image effects are not enabled for inputs with compressed intent");
891 return status;
892 }
893 } else if (handle->m_raw_images.find(UHDR_SDR_IMG) != handle->m_raw_images.end()) {
894 if (handle->m_compressed_images.find(UHDR_SDR_IMG) == handle->m_compressed_images.end()) {
895 if (handle->m_effects.size() != 0) {
896 status = ultrahdr::apply_effects(handle);
897 if (status.error_code != UHDR_CODEC_OK) return status;
898 }
899 } else {
900 if (handle->m_effects.size() != 0) {
901 status.error_code = UHDR_CODEC_INVALID_OPERATION;
902 status.has_detail = 1;
903 snprintf(status.detail, sizeof status.detail,
904 "image effects are not enabled for inputs with compressed intent");
905 return status;
906 }
907 }
908 }
909 }
910
911 ultrahdr::status_t internal_status = ultrahdr::JPEGR_NO_ERROR;
912 if (handle->m_output_format == UHDR_CODEC_JPG) {
913 ultrahdr::jpegr_exif_struct exif{};
914 if (handle->m_exif.size() > 0) {
915 exif.data = handle->m_exif.data();
916 exif.length = handle->m_exif.size();
917 }
918
919 ultrahdr::JpegR jpegr;
920 ultrahdr::jpegr_compressed_struct dest{};
921 if (handle->m_compressed_images.find(UHDR_BASE_IMG) != handle->m_compressed_images.end() &&
922 handle->m_compressed_images.find(UHDR_GAIN_MAP_IMG) != handle->m_compressed_images.end()) {
923 auto& base_entry = handle->m_compressed_images.find(UHDR_BASE_IMG)->second;
924 ultrahdr::jpegr_compressed_struct primary_image;
925 primary_image.data = base_entry->data;
926 primary_image.length = primary_image.maxLength = base_entry->data_sz;
927 primary_image.colorGamut = map_cg_to_internal_cg(base_entry->cg);
928
929 auto& gainmap_entry = handle->m_compressed_images.find(UHDR_GAIN_MAP_IMG)->second;
930 ultrahdr::jpegr_compressed_struct gainmap_image;
931 gainmap_image.data = gainmap_entry->data;
932 gainmap_image.length = gainmap_image.maxLength = gainmap_entry->data_sz;
933 gainmap_image.colorGamut = map_cg_to_internal_cg(gainmap_entry->cg);
934
935 ultrahdr::ultrahdr_metadata_struct metadata;
936 metadata.version = ultrahdr::kJpegrVersion;
937 metadata.maxContentBoost = handle->m_metadata.max_content_boost;
938 metadata.minContentBoost = handle->m_metadata.min_content_boost;
939 metadata.gamma = handle->m_metadata.gamma;
940 metadata.offsetSdr = handle->m_metadata.offset_sdr;
941 metadata.offsetHdr = handle->m_metadata.offset_hdr;
942 metadata.hdrCapacityMin = handle->m_metadata.hdr_capacity_min;
943 metadata.hdrCapacityMax = handle->m_metadata.hdr_capacity_max;
944
945 size_t size = (std::max)((8 * 1024), 2 * (primary_image.length + gainmap_image.length));
946 handle->m_compressed_output_buffer = std::make_unique<ultrahdr::uhdr_compressed_image_ext_t>(
947 UHDR_CG_UNSPECIFIED, UHDR_CT_UNSPECIFIED, UHDR_CR_UNSPECIFIED, size);
948
949 dest.data = handle->m_compressed_output_buffer->data;
950 dest.length = 0;
951 dest.maxLength = handle->m_compressed_output_buffer->capacity;
952 dest.colorGamut = ultrahdr::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
953
954 // api - 4
955 internal_status = jpegr.encodeJPEGR(&primary_image, &gainmap_image, &metadata, &dest);
956 map_internal_error_status_to_error_info(internal_status, status);
957 } else if (handle->m_raw_images.find(UHDR_HDR_IMG) != handle->m_raw_images.end()) {
958 auto& hdr_raw_entry = handle->m_raw_images.find(UHDR_HDR_IMG)->second;
959
960 size_t size = (std::max)((8u * 1024), hdr_raw_entry->w * hdr_raw_entry->h * 3 * 2);
961 handle->m_compressed_output_buffer = std::make_unique<ultrahdr::uhdr_compressed_image_ext_t>(
962 UHDR_CG_UNSPECIFIED, UHDR_CT_UNSPECIFIED, UHDR_CR_UNSPECIFIED, size);
963
964 dest.data = handle->m_compressed_output_buffer->data;
965 dest.length = 0;
966 dest.maxLength = handle->m_compressed_output_buffer->capacity;
967 dest.colorGamut = ultrahdr::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
968
969 ultrahdr::jpegr_uncompressed_struct p010_image;
970 p010_image.data = hdr_raw_entry->planes[UHDR_PLANE_Y];
971 p010_image.width = hdr_raw_entry->w;
972 p010_image.height = hdr_raw_entry->h;
973 p010_image.colorGamut = map_cg_to_internal_cg(hdr_raw_entry->cg);
974 p010_image.luma_stride = hdr_raw_entry->stride[UHDR_PLANE_Y];
975 p010_image.chroma_data = hdr_raw_entry->planes[UHDR_PLANE_UV];
976 p010_image.chroma_stride = hdr_raw_entry->stride[UHDR_PLANE_UV];
977 p010_image.pixelFormat = hdr_raw_entry->fmt;
978
979 if (handle->m_compressed_images.find(UHDR_SDR_IMG) == handle->m_compressed_images.end() &&
980 handle->m_raw_images.find(UHDR_SDR_IMG) == handle->m_raw_images.end()) {
981 // api - 0
982 internal_status = jpegr.encodeJPEGR(&p010_image, map_ct_to_internal_ct(hdr_raw_entry->ct),
983 &dest, handle->m_quality.find(UHDR_BASE_IMG)->second,
984 handle->m_exif.size() > 0 ? &exif : nullptr);
985 } else if (handle->m_compressed_images.find(UHDR_SDR_IMG) !=
986 handle->m_compressed_images.end() &&
987 handle->m_raw_images.find(UHDR_SDR_IMG) == handle->m_raw_images.end()) {
988 auto& sdr_compressed_entry = handle->m_compressed_images.find(UHDR_SDR_IMG)->second;
989 ultrahdr::jpegr_compressed_struct sdr_compressed_image;
990 sdr_compressed_image.data = sdr_compressed_entry->data;
991 sdr_compressed_image.length = sdr_compressed_image.maxLength =
992 sdr_compressed_entry->data_sz;
993 sdr_compressed_image.colorGamut = map_cg_to_internal_cg(sdr_compressed_entry->cg);
994 // api - 3
995 internal_status = jpegr.encodeJPEGR(&p010_image, &sdr_compressed_image,
996 map_ct_to_internal_ct(hdr_raw_entry->ct), &dest);
997 } else if (handle->m_raw_images.find(UHDR_SDR_IMG) != handle->m_raw_images.end()) {
998 auto& sdr_raw_entry = handle->m_raw_images.find(UHDR_SDR_IMG)->second;
999
1000 ultrahdr::jpegr_uncompressed_struct yuv420_image;
1001 yuv420_image.data = sdr_raw_entry->planes[UHDR_PLANE_Y];
1002 yuv420_image.width = sdr_raw_entry->w;
1003 yuv420_image.height = sdr_raw_entry->h;
1004 yuv420_image.colorGamut = map_cg_to_internal_cg(sdr_raw_entry->cg);
1005 yuv420_image.luma_stride = sdr_raw_entry->stride[UHDR_PLANE_Y];
1006 yuv420_image.chroma_data = nullptr;
1007 yuv420_image.chroma_stride = 0;
1008 yuv420_image.pixelFormat = sdr_raw_entry->fmt;
1009
1010 if (handle->m_compressed_images.find(UHDR_SDR_IMG) == handle->m_compressed_images.end()) {
1011 // api - 1
1012 internal_status = jpegr.encodeJPEGR(&p010_image, &yuv420_image,
1013 map_ct_to_internal_ct(hdr_raw_entry->ct), &dest,
1014 handle->m_quality.find(UHDR_BASE_IMG)->second,
1015 handle->m_exif.size() > 0 ? &exif : nullptr);
1016 } else {
1017 auto& sdr_compressed_entry = handle->m_compressed_images.find(UHDR_SDR_IMG)->second;
1018 ultrahdr::jpegr_compressed_struct sdr_compressed_image;
1019 sdr_compressed_image.data = sdr_compressed_entry->data;
1020 sdr_compressed_image.length = sdr_compressed_image.maxLength =
1021 sdr_compressed_entry->data_sz;
1022 sdr_compressed_image.colorGamut = map_cg_to_internal_cg(sdr_compressed_entry->cg);
1023
1024 // api - 2
1025 internal_status = jpegr.encodeJPEGR(&p010_image, &yuv420_image, &sdr_compressed_image,
1026 map_ct_to_internal_ct(hdr_raw_entry->ct), &dest);
1027 }
1028 }
1029 map_internal_error_status_to_error_info(internal_status, status);
1030 } else {
1031 status.error_code = UHDR_CODEC_INVALID_OPERATION;
1032 status.has_detail = 1;
1033 snprintf(status.detail, sizeof status.detail,
1034 "resources required for uhdr_encode() operation are not present");
1035 }
1036 if (status.error_code == UHDR_CODEC_OK) {
1037 handle->m_compressed_output_buffer->data_sz = dest.length;
1038 handle->m_compressed_output_buffer->cg = map_internal_cg_to_cg(dest.colorGamut);
1039 }
1040 }
1041
1042 return status;
1043 }
1044
uhdr_get_encoded_stream(uhdr_codec_private_t * enc)1045 uhdr_compressed_image_t* uhdr_get_encoded_stream(uhdr_codec_private_t* enc) {
1046 if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
1047 return nullptr;
1048 }
1049
1050 uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
1051 if (!handle->m_sailed || handle->m_encode_call_status.error_code != UHDR_CODEC_OK) {
1052 return nullptr;
1053 }
1054
1055 return handle->m_compressed_output_buffer.get();
1056 }
1057
uhdr_reset_encoder(uhdr_codec_private_t * enc)1058 void uhdr_reset_encoder(uhdr_codec_private_t* enc) {
1059 if (dynamic_cast<uhdr_encoder_private*>(enc) != nullptr) {
1060 uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
1061
1062 // clear entries and restore defaults
1063 for (auto it : handle->m_effects) delete it;
1064 handle->m_effects.clear();
1065 handle->m_raw_images.clear();
1066 handle->m_compressed_images.clear();
1067 handle->m_quality.clear();
1068 handle->m_quality.emplace(UHDR_HDR_IMG, 95);
1069 handle->m_quality.emplace(UHDR_SDR_IMG, 95);
1070 handle->m_quality.emplace(UHDR_BASE_IMG, 95);
1071 handle->m_quality.emplace(UHDR_GAIN_MAP_IMG, 85);
1072 handle->m_exif.clear();
1073 handle->m_output_format = UHDR_CODEC_JPG;
1074
1075 handle->m_sailed = false;
1076 handle->m_compressed_output_buffer.reset();
1077 handle->m_encode_call_status = g_no_error;
1078 }
1079 }
1080
is_uhdr_image(void * data,int size)1081 int is_uhdr_image(void* data, int size) {
1082 #define RET_IF_ERR(x) \
1083 { \
1084 uhdr_error_info_t status = (x); \
1085 if (status.error_code != UHDR_CODEC_OK) { \
1086 uhdr_release_decoder(obj); \
1087 return 0; \
1088 } \
1089 }
1090
1091 uhdr_codec_private_t* obj = uhdr_create_decoder();
1092 uhdr_compressed_image_t uhdr_image;
1093 uhdr_image.data = data;
1094 uhdr_image.data_sz = size;
1095 uhdr_image.capacity = size;
1096 uhdr_image.cg = UHDR_CG_UNSPECIFIED;
1097 uhdr_image.ct = UHDR_CT_UNSPECIFIED;
1098 uhdr_image.range = UHDR_CR_UNSPECIFIED;
1099
1100 RET_IF_ERR(uhdr_dec_set_image(obj, &uhdr_image));
1101 RET_IF_ERR(uhdr_dec_probe(obj));
1102 #undef RET_IF_ERR
1103
1104 uhdr_release_decoder(obj);
1105
1106 return 1;
1107 }
1108
uhdr_create_decoder(void)1109 uhdr_codec_private_t* uhdr_create_decoder(void) {
1110 uhdr_decoder_private* handle = new uhdr_decoder_private();
1111
1112 if (handle != nullptr) {
1113 uhdr_reset_decoder(handle);
1114 }
1115 return handle;
1116 }
1117
uhdr_release_decoder(uhdr_codec_private_t * dec)1118 void uhdr_release_decoder(uhdr_codec_private_t* dec) {
1119 if (dynamic_cast<uhdr_decoder_private*>(dec) != nullptr) {
1120 uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1121 delete handle;
1122 }
1123 }
1124
uhdr_dec_set_image(uhdr_codec_private_t * dec,uhdr_compressed_image_t * img)1125 uhdr_error_info_t uhdr_dec_set_image(uhdr_codec_private_t* dec, uhdr_compressed_image_t* img) {
1126 uhdr_error_info_t status = g_no_error;
1127
1128 if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1129 status.error_code = UHDR_CODEC_INVALID_PARAM;
1130 status.has_detail = 1;
1131 snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1132 } else if (img == nullptr) {
1133 status.error_code = UHDR_CODEC_INVALID_PARAM;
1134 status.has_detail = 1;
1135 snprintf(status.detail, sizeof status.detail, "received nullptr for compressed image handle");
1136 } else if (img->data == nullptr) {
1137 status.error_code = UHDR_CODEC_INVALID_PARAM;
1138 status.has_detail = 1;
1139 snprintf(status.detail, sizeof status.detail,
1140 "received nullptr for compressed img->data field");
1141 } else if (img->capacity < img->data_sz) {
1142 status.error_code = UHDR_CODEC_INVALID_PARAM;
1143 status.has_detail = 1;
1144 snprintf(status.detail, sizeof status.detail, "img->capacity %d is less than img->data_sz %d",
1145 img->capacity, img->data_sz);
1146 }
1147 if (status.error_code != UHDR_CODEC_OK) return status;
1148
1149 uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1150 if (handle->m_probed) {
1151 status.error_code = UHDR_CODEC_INVALID_OPERATION;
1152 status.has_detail = 1;
1153 snprintf(status.detail, sizeof status.detail,
1154 "An earlier call to uhdr_decode() has switched the context from configurable state to "
1155 "end state. The context is no longer configurable. To reuse, call reset()");
1156 return status;
1157 }
1158
1159 handle->m_uhdr_compressed_img = std::make_unique<ultrahdr::uhdr_compressed_image_ext_t>(
1160 img->cg, img->ct, img->range, img->data_sz);
1161 memcpy(handle->m_uhdr_compressed_img->data, img->data, img->data_sz);
1162 handle->m_uhdr_compressed_img->data_sz = img->data_sz;
1163
1164 return status;
1165 }
1166
uhdr_dec_set_out_img_format(uhdr_codec_private_t * dec,uhdr_img_fmt_t fmt)1167 uhdr_error_info_t uhdr_dec_set_out_img_format(uhdr_codec_private_t* dec, uhdr_img_fmt_t fmt) {
1168 uhdr_error_info_t status = g_no_error;
1169
1170 if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1171 status.error_code = UHDR_CODEC_INVALID_PARAM;
1172 status.has_detail = 1;
1173 snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1174 } else if (fmt != UHDR_IMG_FMT_32bppRGBA8888 && fmt != UHDR_IMG_FMT_64bppRGBAHalfFloat &&
1175 fmt != UHDR_IMG_FMT_32bppRGBA1010102) {
1176 status.error_code = UHDR_CODEC_INVALID_PARAM;
1177 status.has_detail = 1;
1178 snprintf(status.detail, sizeof status.detail,
1179 "invalid output format %d, expects one of {UHDR_IMG_FMT_32bppRGBA8888, "
1180 "UHDR_IMG_FMT_64bppRGBAHalfFloat, UHDR_IMG_FMT_32bppRGBA1010102}",
1181 fmt);
1182 }
1183 if (status.error_code != UHDR_CODEC_OK) return status;
1184
1185 uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1186 if (handle->m_probed) {
1187 status.error_code = UHDR_CODEC_INVALID_OPERATION;
1188 status.has_detail = 1;
1189 snprintf(status.detail, sizeof status.detail,
1190 "An earlier call to uhdr_decode() has switched the context from configurable state to "
1191 "end state. The context is no longer configurable. To reuse, call reset()");
1192 return status;
1193 }
1194
1195 handle->m_output_fmt = fmt;
1196
1197 return status;
1198 }
1199
uhdr_dec_set_out_color_transfer(uhdr_codec_private_t * dec,uhdr_color_transfer_t ct)1200 uhdr_error_info_t uhdr_dec_set_out_color_transfer(uhdr_codec_private_t* dec,
1201 uhdr_color_transfer_t ct) {
1202 uhdr_error_info_t status = g_no_error;
1203
1204 if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1205 status.error_code = UHDR_CODEC_INVALID_PARAM;
1206 status.has_detail = 1;
1207 snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1208 } else if (ct != UHDR_CT_HLG && ct != UHDR_CT_PQ && ct != UHDR_CT_LINEAR && ct != UHDR_CT_SRGB) {
1209 status.error_code = UHDR_CODEC_INVALID_PARAM;
1210 status.has_detail = 1;
1211 snprintf(status.detail, sizeof status.detail,
1212 "invalid output color transfer %d, expects one of {UHDR_CT_HLG, UHDR_CT_PQ, "
1213 "UHDR_CT_LINEAR, UHDR_CT_SRGB}",
1214 ct);
1215 }
1216 if (status.error_code != UHDR_CODEC_OK) return status;
1217
1218 uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1219 if (handle->m_probed) {
1220 status.error_code = UHDR_CODEC_INVALID_OPERATION;
1221 status.has_detail = 1;
1222 snprintf(status.detail, sizeof status.detail,
1223 "An earlier call to uhdr_decode() has switched the context from configurable state to "
1224 "end state. The context is no longer configurable. To reuse, call reset()");
1225 return status;
1226 }
1227
1228 handle->m_output_ct = ct;
1229
1230 return status;
1231 }
1232
uhdr_dec_set_out_max_display_boost(uhdr_codec_private_t * dec,float display_boost)1233 uhdr_error_info_t uhdr_dec_set_out_max_display_boost(uhdr_codec_private_t* dec,
1234 float display_boost) {
1235 uhdr_error_info_t status = g_no_error;
1236
1237 if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1238 status.error_code = UHDR_CODEC_INVALID_PARAM;
1239 status.has_detail = 1;
1240 snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1241 } else if (display_boost < 1.0f) {
1242 status.error_code = UHDR_CODEC_INVALID_PARAM;
1243 status.has_detail = 1;
1244 snprintf(status.detail, sizeof status.detail,
1245 "invalid display boost %f, expects to be >= 1.0f}", display_boost);
1246 }
1247 if (status.error_code != UHDR_CODEC_OK) return status;
1248
1249 uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1250 if (handle->m_probed) {
1251 status.error_code = UHDR_CODEC_INVALID_OPERATION;
1252 status.has_detail = 1;
1253 snprintf(status.detail, sizeof status.detail,
1254 "An earlier call to uhdr_decode() has switched the context from configurable state to "
1255 "end state. The context is no longer configurable. To reuse, call reset()");
1256 return status;
1257 }
1258
1259 handle->m_output_max_disp_boost = display_boost;
1260
1261 return status;
1262 }
1263
uhdr_dec_probe(uhdr_codec_private_t * dec)1264 uhdr_error_info_t uhdr_dec_probe(uhdr_codec_private_t* dec) {
1265 if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1266 uhdr_error_info_t status;
1267 status.error_code = UHDR_CODEC_INVALID_PARAM;
1268 status.has_detail = 1;
1269 snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1270 return status;
1271 }
1272
1273 uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1274 uhdr_error_info_t& status = handle->m_probe_call_status;
1275
1276 if (!handle->m_probed) {
1277 handle->m_probed = true;
1278
1279 if (handle->m_uhdr_compressed_img.get() == nullptr) {
1280 status.error_code = UHDR_CODEC_INVALID_OPERATION;
1281 status.has_detail = 1;
1282 snprintf(status.detail, sizeof status.detail, "did not receive any image for decoding");
1283 return status;
1284 }
1285
1286 ultrahdr::jpeg_info_struct primary_image;
1287 ultrahdr::jpeg_info_struct gainmap_image;
1288 ultrahdr::jpegr_info_struct jpegr_info;
1289 jpegr_info.primaryImgInfo = &primary_image;
1290 jpegr_info.gainmapImgInfo = &gainmap_image;
1291
1292 ultrahdr::jpegr_compressed_struct uhdr_image;
1293 uhdr_image.data = handle->m_uhdr_compressed_img->data;
1294 uhdr_image.length = uhdr_image.maxLength = handle->m_uhdr_compressed_img->data_sz;
1295 uhdr_image.colorGamut = map_cg_to_internal_cg(handle->m_uhdr_compressed_img->cg);
1296
1297 ultrahdr::JpegR jpegr;
1298 ultrahdr::status_t internal_status = jpegr.getJPEGRInfo(&uhdr_image, &jpegr_info);
1299 map_internal_error_status_to_error_info(internal_status, status);
1300 if (status.error_code != UHDR_CODEC_OK) return status;
1301
1302 ultrahdr::ultrahdr_metadata_struct metadata;
1303 if (ultrahdr::getMetadataFromXMP(gainmap_image.xmpData.data(), gainmap_image.xmpData.size(),
1304 &metadata)) {
1305 handle->m_metadata.max_content_boost = metadata.maxContentBoost;
1306 handle->m_metadata.min_content_boost = metadata.minContentBoost;
1307 handle->m_metadata.gamma = metadata.gamma;
1308 handle->m_metadata.offset_sdr = metadata.offsetSdr;
1309 handle->m_metadata.offset_hdr = metadata.offsetHdr;
1310 handle->m_metadata.hdr_capacity_min = metadata.hdrCapacityMin;
1311 handle->m_metadata.hdr_capacity_max = metadata.hdrCapacityMax;
1312 } else {
1313 status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
1314 status.has_detail = 1;
1315 snprintf(status.detail, sizeof status.detail, "encountered error while parsing metadata");
1316 return status;
1317 }
1318
1319 handle->m_img_wd = primary_image.width;
1320 handle->m_img_ht = primary_image.height;
1321 handle->m_gainmap_wd = gainmap_image.width;
1322 handle->m_gainmap_ht = gainmap_image.height;
1323 handle->m_exif = std::move(primary_image.exifData);
1324 handle->m_exif_block.data = handle->m_exif.data();
1325 handle->m_exif_block.data_sz = handle->m_exif_block.capacity = handle->m_exif.size();
1326 handle->m_icc = std::move(primary_image.iccData);
1327 handle->m_icc_block.data = handle->m_icc.data();
1328 handle->m_icc_block.data_sz = handle->m_icc_block.capacity = handle->m_icc.size();
1329 handle->m_base_xmp = std::move(primary_image.xmpData);
1330 handle->m_gainmap_xmp = std::move(gainmap_image.xmpData);
1331 }
1332
1333 return status;
1334 }
1335
uhdr_dec_get_image_width(uhdr_codec_private_t * dec)1336 int uhdr_dec_get_image_width(uhdr_codec_private_t* dec) {
1337 if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1338 return -1;
1339 }
1340
1341 uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1342 if (!handle->m_probed || handle->m_probe_call_status.error_code != UHDR_CODEC_OK) {
1343 return -1;
1344 }
1345
1346 return handle->m_img_wd;
1347 }
1348
uhdr_dec_get_image_height(uhdr_codec_private_t * dec)1349 int uhdr_dec_get_image_height(uhdr_codec_private_t* dec) {
1350 if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1351 return -1;
1352 }
1353
1354 uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1355 if (!handle->m_probed || handle->m_probe_call_status.error_code != UHDR_CODEC_OK) {
1356 return -1;
1357 }
1358
1359 return handle->m_img_ht;
1360 }
1361
uhdr_dec_get_gainmap_width(uhdr_codec_private_t * dec)1362 int uhdr_dec_get_gainmap_width(uhdr_codec_private_t* dec) {
1363 if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1364 return -1;
1365 }
1366
1367 uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1368 if (!handle->m_probed || handle->m_probe_call_status.error_code != UHDR_CODEC_OK) {
1369 return -1;
1370 }
1371
1372 return handle->m_gainmap_wd;
1373 }
1374
uhdr_dec_get_gainmap_height(uhdr_codec_private_t * dec)1375 int uhdr_dec_get_gainmap_height(uhdr_codec_private_t* dec) {
1376 if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1377 return -1;
1378 }
1379
1380 uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1381 if (!handle->m_probed || handle->m_probe_call_status.error_code != UHDR_CODEC_OK) {
1382 return -1;
1383 }
1384
1385 return handle->m_gainmap_ht;
1386 }
1387
uhdr_dec_get_exif(uhdr_codec_private_t * dec)1388 uhdr_mem_block_t* uhdr_dec_get_exif(uhdr_codec_private_t* dec) {
1389 if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1390 return nullptr;
1391 }
1392
1393 uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1394 if (!handle->m_probed || handle->m_probe_call_status.error_code != UHDR_CODEC_OK) {
1395 return nullptr;
1396 }
1397
1398 return &handle->m_exif_block;
1399 }
1400
uhdr_dec_get_icc(uhdr_codec_private_t * dec)1401 uhdr_mem_block_t* uhdr_dec_get_icc(uhdr_codec_private_t* dec) {
1402 if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1403 return nullptr;
1404 }
1405
1406 uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1407 if (!handle->m_probed || handle->m_probe_call_status.error_code != UHDR_CODEC_OK) {
1408 return nullptr;
1409 }
1410
1411 return &handle->m_icc_block;
1412 }
1413
uhdr_dec_get_gain_map_metadata(uhdr_codec_private_t * dec)1414 uhdr_gainmap_metadata_t* uhdr_dec_get_gain_map_metadata(uhdr_codec_private_t* dec) {
1415 if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1416 return nullptr;
1417 }
1418
1419 uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1420 if (!handle->m_probed || handle->m_probe_call_status.error_code != UHDR_CODEC_OK) {
1421 return nullptr;
1422 }
1423
1424 return &handle->m_metadata;
1425 }
1426
uhdr_decode(uhdr_codec_private_t * dec)1427 uhdr_error_info_t uhdr_decode(uhdr_codec_private_t* dec) {
1428 if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1429 uhdr_error_info_t status;
1430 status.error_code = UHDR_CODEC_INVALID_PARAM;
1431 status.has_detail = 1;
1432 snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1433 return status;
1434 }
1435
1436 uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1437
1438 if (handle->m_sailed) {
1439 return handle->m_decode_call_status;
1440 }
1441
1442 uhdr_error_info_t& status = handle->m_decode_call_status;
1443 status = uhdr_dec_probe(dec);
1444 if (status.error_code != UHDR_CODEC_OK) return status;
1445
1446 handle->m_sailed = true;
1447
1448 ultrahdr::ultrahdr_output_format outputFormat =
1449 map_ct_fmt_to_internal_output_fmt(handle->m_output_ct, handle->m_output_fmt);
1450 if (outputFormat == ultrahdr::ultrahdr_output_format::ULTRAHDR_OUTPUT_UNSPECIFIED) {
1451 status.error_code = UHDR_CODEC_INVALID_PARAM;
1452 status.has_detail = 1;
1453 snprintf(status.detail, sizeof status.detail,
1454 "unsupported output pixel format and output color transfer pair");
1455 return status;
1456 }
1457
1458 ultrahdr::jpegr_compressed_struct uhdr_image;
1459 uhdr_image.data = handle->m_uhdr_compressed_img->data;
1460 uhdr_image.length = uhdr_image.maxLength = handle->m_uhdr_compressed_img->data_sz;
1461 uhdr_image.colorGamut = map_cg_to_internal_cg(handle->m_uhdr_compressed_img->cg);
1462
1463 handle->m_decoded_img_buffer = std::make_unique<ultrahdr::uhdr_raw_image_ext_t>(
1464 handle->m_output_fmt, UHDR_CG_UNSPECIFIED, handle->m_output_ct, UHDR_CR_UNSPECIFIED,
1465 handle->m_img_wd, handle->m_img_ht, 1);
1466 // alias
1467 ultrahdr::jpegr_uncompressed_struct dest;
1468 dest.data = handle->m_decoded_img_buffer->planes[UHDR_PLANE_PACKED];
1469 dest.colorGamut = ultrahdr::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
1470
1471 handle->m_gainmap_img_buffer = std::make_unique<ultrahdr::uhdr_raw_image_ext_t>(
1472 UHDR_IMG_FMT_8bppYCbCr400, UHDR_CG_UNSPECIFIED, UHDR_CT_UNSPECIFIED, UHDR_CR_UNSPECIFIED,
1473 handle->m_gainmap_wd, handle->m_gainmap_ht, 1);
1474 // alias
1475 ultrahdr::jpegr_uncompressed_struct dest_gainmap;
1476 dest_gainmap.data = handle->m_gainmap_img_buffer->planes[UHDR_PLANE_Y];
1477
1478 ultrahdr::JpegR jpegr;
1479 ultrahdr::status_t internal_status =
1480 jpegr.decodeJPEGR(&uhdr_image, &dest, handle->m_output_max_disp_boost, nullptr, outputFormat,
1481 &dest_gainmap, nullptr);
1482 map_internal_error_status_to_error_info(internal_status, status);
1483 if (status.error_code == UHDR_CODEC_OK) {
1484 handle->m_decoded_img_buffer->cg = map_internal_cg_to_cg(dest.colorGamut);
1485 }
1486
1487 if (status.error_code == UHDR_CODEC_OK && dec->m_effects.size() != 0) {
1488 status = ultrahdr::apply_effects(handle);
1489 }
1490
1491 return status;
1492 }
1493
uhdr_get_decoded_image(uhdr_codec_private_t * dec)1494 uhdr_raw_image_t* uhdr_get_decoded_image(uhdr_codec_private_t* dec) {
1495 if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1496 return nullptr;
1497 }
1498
1499 uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1500 if (!handle->m_sailed || handle->m_decode_call_status.error_code != UHDR_CODEC_OK) {
1501 return nullptr;
1502 }
1503
1504 return handle->m_decoded_img_buffer.get();
1505 }
1506
uhdr_get_gain_map_image(uhdr_codec_private_t * dec)1507 uhdr_raw_image_t* uhdr_get_gain_map_image(uhdr_codec_private_t* dec) {
1508 if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1509 return nullptr;
1510 }
1511
1512 uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1513 if (!handle->m_sailed || handle->m_decode_call_status.error_code != UHDR_CODEC_OK) {
1514 return nullptr;
1515 }
1516
1517 return handle->m_gainmap_img_buffer.get();
1518 }
1519
uhdr_reset_decoder(uhdr_codec_private_t * dec)1520 void uhdr_reset_decoder(uhdr_codec_private_t* dec) {
1521 if (dynamic_cast<uhdr_decoder_private*>(dec) != nullptr) {
1522 uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1523
1524 // clear entries and restore defaults
1525 for (auto it : handle->m_effects) delete it;
1526 handle->m_effects.clear();
1527 handle->m_uhdr_compressed_img.reset();
1528 handle->m_output_fmt = UHDR_IMG_FMT_64bppRGBAHalfFloat;
1529 handle->m_output_ct = UHDR_CT_LINEAR;
1530 handle->m_output_max_disp_boost = FLT_MAX;
1531
1532 // ready to be configured
1533 handle->m_probed = false;
1534 handle->m_sailed = false;
1535 handle->m_decoded_img_buffer.reset();
1536 handle->m_gainmap_img_buffer.reset();
1537 handle->m_img_wd = 0;
1538 handle->m_img_ht = 0;
1539 handle->m_gainmap_wd = 0;
1540 handle->m_gainmap_ht = 0;
1541 handle->m_exif.clear();
1542 memset(&handle->m_exif_block, 0, sizeof handle->m_exif_block);
1543 handle->m_icc.clear();
1544 memset(&handle->m_icc_block, 0, sizeof handle->m_icc_block);
1545 handle->m_base_xmp.clear();
1546 handle->m_gainmap_xmp.clear();
1547 memset(&handle->m_metadata, 0, sizeof handle->m_metadata);
1548 handle->m_probe_call_status = g_no_error;
1549 handle->m_decode_call_status = g_no_error;
1550 }
1551 }
1552
uhdr_add_effect_mirror(uhdr_codec_private_t * codec,uhdr_mirror_direction_t direction)1553 uhdr_error_info_t uhdr_add_effect_mirror(uhdr_codec_private_t* codec,
1554 uhdr_mirror_direction_t direction) {
1555 uhdr_error_info_t status = g_no_error;
1556
1557 if (codec == nullptr) {
1558 status.error_code = UHDR_CODEC_INVALID_PARAM;
1559 status.has_detail = 1;
1560 snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1561 return status;
1562 }
1563
1564 if (direction != UHDR_MIRROR_HORIZONTAL && direction != UHDR_MIRROR_VERTICAL) {
1565 status.error_code = UHDR_CODEC_INVALID_PARAM;
1566 status.has_detail = 1;
1567 snprintf(
1568 status.detail, sizeof status.detail,
1569 "unsupported direction, expects one of {UHDR_MIRROR_HORIZONTAL, UHDR_MIRROR_VERTICAL}");
1570 return status;
1571 }
1572
1573 codec->m_effects.push_back(new ultrahdr::uhdr_mirror_effect_t(direction));
1574
1575 return status;
1576 }
1577
uhdr_add_effect_rotate(uhdr_codec_private_t * codec,int degrees)1578 uhdr_error_info_t uhdr_add_effect_rotate(uhdr_codec_private_t* codec, int degrees) {
1579 uhdr_error_info_t status = g_no_error;
1580
1581 if (codec == nullptr) {
1582 status.error_code = UHDR_CODEC_INVALID_PARAM;
1583 status.has_detail = 1;
1584 snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1585 return status;
1586 }
1587
1588 if (degrees != 90 && degrees != 180 && degrees != 270) {
1589 status.error_code = UHDR_CODEC_INVALID_PARAM;
1590 status.has_detail = 1;
1591 snprintf(status.detail, sizeof status.detail,
1592 "unsupported degrees, expects one of {90, 180, 270}");
1593 return status;
1594 }
1595
1596 codec->m_effects.push_back(new ultrahdr::uhdr_rotate_effect_t(degrees));
1597
1598 return status;
1599 }
1600
uhdr_add_effect_crop(uhdr_codec_private_t * codec,int left,int right,int top,int bottom)1601 uhdr_error_info_t uhdr_add_effect_crop(uhdr_codec_private_t* codec, int left, int right, int top,
1602 int bottom) {
1603 uhdr_error_info_t status = g_no_error;
1604
1605 if (codec == nullptr) {
1606 status.error_code = UHDR_CODEC_INVALID_PARAM;
1607 status.has_detail = 1;
1608 snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1609 return status;
1610 }
1611
1612 codec->m_effects.push_back(new ultrahdr::uhdr_crop_effect_t(left, right, top, bottom));
1613
1614 return status;
1615 }
1616
uhdr_add_effect_resize(uhdr_codec_private_t * codec,int width,int height)1617 uhdr_error_info_t uhdr_add_effect_resize(uhdr_codec_private_t* codec, int width, int height) {
1618 uhdr_error_info_t status = g_no_error;
1619
1620 if (codec == nullptr) {
1621 status.error_code = UHDR_CODEC_INVALID_PARAM;
1622 status.has_detail = 1;
1623 snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1624 return status;
1625 }
1626
1627 codec->m_effects.push_back(new ultrahdr::uhdr_resize_effect_t(width, height));
1628
1629 return status;
1630 }
1631