• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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