xref: /aosp_15_r20/external/libultrahdr/lib/src/ultrahdr_api.cpp (revision 89a0ef05262152531a00a15832a2d3b1e3990773)
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 #include "image_io/base/data_segment_data_source.h"
28 #include "image_io/jpeg/jpeg_info.h"
29 #include "image_io/jpeg/jpeg_info_builder.h"
30 #include "image_io/jpeg/jpeg_marker.h"
31 #include "image_io/jpeg/jpeg_scanner.h"
32 
33 using namespace photos_editing_formats::image_io;
34 
35 namespace ultrahdr {
36 
uhdr_memory_block(size_t capacity)37 uhdr_memory_block::uhdr_memory_block(size_t capacity) {
38   m_buffer = std::make_unique<uint8_t[]>(capacity);
39   m_capacity = capacity;
40 }
41 
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)42 uhdr_raw_image_ext::uhdr_raw_image_ext(uhdr_img_fmt_t fmt_, uhdr_color_gamut_t cg_,
43                                        uhdr_color_transfer_t ct_, uhdr_color_range_t range_,
44                                        unsigned w_, unsigned h_, unsigned align_stride_to) {
45   this->fmt = fmt_;
46   this->cg = cg_;
47   this->ct = ct_;
48   this->range = range_;
49 
50   this->w = w_;
51   this->h = h_;
52 
53   int aligned_width = ALIGNM(w_, align_stride_to);
54 
55   size_t bpp = 1;
56   if (fmt_ == UHDR_IMG_FMT_24bppYCbCrP010 || fmt_ == UHDR_IMG_FMT_30bppYCbCr444) {
57     bpp = 2;
58   } else if (fmt_ == UHDR_IMG_FMT_24bppRGB888) {
59     bpp = 3;
60   } else if (fmt_ == UHDR_IMG_FMT_32bppRGBA8888 || fmt_ == UHDR_IMG_FMT_32bppRGBA1010102) {
61     bpp = 4;
62   } else if (fmt_ == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
63     bpp = 8;
64   }
65 
66   size_t plane_1_sz = bpp * aligned_width * h_;
67   size_t plane_2_sz;
68   size_t plane_3_sz;
69   if (fmt_ == UHDR_IMG_FMT_24bppYCbCrP010) {
70     plane_2_sz = (2 /* planes */ * bpp * (aligned_width / 2) * (h_ / 2));
71     plane_3_sz = 0;
72   } else if (fmt_ == UHDR_IMG_FMT_30bppYCbCr444 || fmt_ == UHDR_IMG_FMT_24bppYCbCr444) {
73     plane_2_sz = bpp * aligned_width * h_;
74     plane_3_sz = bpp * aligned_width * h_;
75   } else if (fmt_ == UHDR_IMG_FMT_12bppYCbCr420) {
76     plane_2_sz = (bpp * (aligned_width / 2) * (h_ / 2));
77     plane_3_sz = (bpp * (aligned_width / 2) * (h_ / 2));
78   } else {
79     plane_2_sz = 0;
80     plane_3_sz = 0;
81   }
82   size_t total_size = plane_1_sz + plane_2_sz + plane_3_sz;
83   this->m_block = std::make_unique<uhdr_memory_block_t>(total_size);
84 
85   uint8_t* data = this->m_block->m_buffer.get();
86   this->planes[UHDR_PLANE_Y] = data;
87   this->stride[UHDR_PLANE_Y] = aligned_width;
88   if (fmt_ == UHDR_IMG_FMT_24bppYCbCrP010) {
89     this->planes[UHDR_PLANE_UV] = data + plane_1_sz;
90     this->stride[UHDR_PLANE_UV] = aligned_width;
91     this->planes[UHDR_PLANE_V] = nullptr;
92     this->stride[UHDR_PLANE_V] = 0;
93   } else if (fmt_ == UHDR_IMG_FMT_30bppYCbCr444 || fmt_ == UHDR_IMG_FMT_24bppYCbCr444) {
94     this->planes[UHDR_PLANE_U] = data + plane_1_sz;
95     this->stride[UHDR_PLANE_U] = aligned_width;
96     this->planes[UHDR_PLANE_V] = data + plane_1_sz + plane_2_sz;
97     this->stride[UHDR_PLANE_V] = aligned_width;
98   } else if (fmt_ == UHDR_IMG_FMT_12bppYCbCr420) {
99     this->planes[UHDR_PLANE_U] = data + plane_1_sz;
100     this->stride[UHDR_PLANE_U] = aligned_width / 2;
101     this->planes[UHDR_PLANE_V] = data + plane_1_sz + plane_2_sz;
102     this->stride[UHDR_PLANE_V] = aligned_width / 2;
103   } else {
104     this->planes[UHDR_PLANE_U] = nullptr;
105     this->stride[UHDR_PLANE_U] = 0;
106     this->planes[UHDR_PLANE_V] = nullptr;
107     this->stride[UHDR_PLANE_V] = 0;
108   }
109 }
110 
uhdr_compressed_image_ext(uhdr_color_gamut_t cg_,uhdr_color_transfer_t ct_,uhdr_color_range_t range_,size_t size)111 uhdr_compressed_image_ext::uhdr_compressed_image_ext(uhdr_color_gamut_t cg_,
112                                                      uhdr_color_transfer_t ct_,
113                                                      uhdr_color_range_t range_, size_t size) {
114   this->m_block = std::make_unique<uhdr_memory_block_t>(size);
115   this->data = this->m_block->m_buffer.get();
116   this->capacity = size;
117   this->data_sz = 0;
118   this->cg = cg_;
119   this->ct = ct_;
120   this->range = range_;
121 }
122 
apply_effects(uhdr_encoder_private * enc)123 uhdr_error_info_t apply_effects(uhdr_encoder_private* enc) {
124   for (auto& it : enc->m_effects) {
125     std::unique_ptr<ultrahdr::uhdr_raw_image_ext_t> hdr_img = nullptr;
126     std::unique_ptr<ultrahdr::uhdr_raw_image_ext_t> sdr_img = nullptr;
127 
128     if (nullptr != dynamic_cast<uhdr_rotate_effect_t*>(it)) {
129       auto& hdr_raw_entry = enc->m_raw_images.find(UHDR_HDR_IMG)->second;
130       hdr_img = apply_rotate(dynamic_cast<uhdr_rotate_effect_t*>(it), hdr_raw_entry.get());
131       if (enc->m_raw_images.find(UHDR_SDR_IMG) != enc->m_raw_images.end()) {
132         auto& sdr_raw_entry = enc->m_raw_images.find(UHDR_SDR_IMG)->second;
133         sdr_img = apply_rotate(dynamic_cast<uhdr_rotate_effect_t*>(it), sdr_raw_entry.get());
134       }
135     } else if (nullptr != dynamic_cast<uhdr_mirror_effect_t*>(it)) {
136       auto& hdr_raw_entry = enc->m_raw_images.find(UHDR_HDR_IMG)->second;
137       hdr_img = apply_mirror(dynamic_cast<uhdr_mirror_effect_t*>(it), hdr_raw_entry.get());
138       if (enc->m_raw_images.find(UHDR_SDR_IMG) != enc->m_raw_images.end()) {
139         auto& sdr_raw_entry = enc->m_raw_images.find(UHDR_SDR_IMG)->second;
140         sdr_img = apply_mirror(dynamic_cast<uhdr_mirror_effect_t*>(it), sdr_raw_entry.get());
141       }
142     } else if (nullptr != dynamic_cast<uhdr_crop_effect_t*>(it)) {
143       auto crop_effect = dynamic_cast<uhdr_crop_effect_t*>(it);
144       auto& hdr_raw_entry = enc->m_raw_images.find(UHDR_HDR_IMG)->second;
145       int left = (std::max)(0, crop_effect->m_left);
146       int right = (std::min)((int)hdr_raw_entry->w, crop_effect->m_right);
147       int crop_width = right - left;
148       if (crop_width <= 0) {
149         uhdr_error_info_t status;
150         status.error_code = UHDR_CODEC_INVALID_PARAM;
151         status.has_detail = 1;
152         snprintf(status.detail, sizeof status.detail,
153                  "unexpected crop dimensions. crop width is expected to be > 0, crop width is %d",
154                  crop_width);
155         return status;
156       }
157       if (crop_width % 2 != 0 && hdr_raw_entry->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
158         uhdr_error_info_t status;
159         status.error_code = UHDR_CODEC_INVALID_PARAM;
160         status.has_detail = 1;
161         snprintf(status.detail, sizeof status.detail,
162                  "unexpected crop dimensions. crop width is expected to even for format "
163                  "{UHDR_IMG_FMT_24bppYCbCrP010}, crop width is %d",
164                  crop_width);
165         return status;
166       }
167 
168       int top = (std::max)(0, crop_effect->m_top);
169       int bottom = (std::min)((int)hdr_raw_entry->h, crop_effect->m_bottom);
170       int crop_height = bottom - top;
171       if (crop_height <= 0) {
172         uhdr_error_info_t status;
173         status.error_code = UHDR_CODEC_INVALID_PARAM;
174         status.has_detail = 1;
175         snprintf(status.detail, sizeof status.detail,
176                  "unexpected crop dimensions. crop height is expected to be > 0, crop height is %d",
177                  crop_height);
178         return status;
179       }
180       if (crop_height % 2 != 0 && hdr_raw_entry->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
181         uhdr_error_info_t status;
182         status.error_code = UHDR_CODEC_INVALID_PARAM;
183         status.has_detail = 1;
184         snprintf(status.detail, sizeof status.detail,
185                  "unexpected crop dimensions. crop height is expected to even for format "
186                  "{UHDR_IMG_FMT_24bppYCbCrP010}. crop height is %d",
187                  crop_height);
188         return status;
189       }
190       hdr_img = apply_crop(dynamic_cast<ultrahdr::uhdr_crop_effect_t*>(it), hdr_raw_entry.get(),
191                            left, top, crop_width, crop_height);
192       if (enc->m_raw_images.find(UHDR_SDR_IMG) != enc->m_raw_images.end()) {
193         auto& sdr_raw_entry = enc->m_raw_images.find(UHDR_SDR_IMG)->second;
194         if (crop_width % 2 != 0 && sdr_raw_entry->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
195           uhdr_error_info_t status;
196           status.error_code = UHDR_CODEC_INVALID_PARAM;
197           status.has_detail = 1;
198           snprintf(status.detail, sizeof status.detail,
199                    "unexpected crop dimensions. crop width is expected to even for format "
200                    "{UHDR_IMG_FMT_12bppYCbCr420}, crop width is %d",
201                    crop_width);
202           return status;
203         }
204         if (crop_height % 2 != 0 && sdr_raw_entry->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
205           uhdr_error_info_t status;
206           status.error_code = UHDR_CODEC_INVALID_PARAM;
207           status.has_detail = 1;
208           snprintf(status.detail, sizeof status.detail,
209                    "unexpected crop dimensions. crop height is expected to even for format "
210                    "{UHDR_IMG_FMT_12bppYCbCr420}. crop height is %d",
211                    crop_height);
212           return status;
213         }
214         sdr_img = apply_crop(dynamic_cast<ultrahdr::uhdr_crop_effect_t*>(it), sdr_raw_entry.get(),
215                              left, top, crop_width, crop_height);
216       }
217     } else if (nullptr != dynamic_cast<uhdr_resize_effect_t*>(it)) {
218       auto resize_effect = dynamic_cast<uhdr_resize_effect_t*>(it);
219       int dst_w = resize_effect->m_width;
220       int dst_h = resize_effect->m_height;
221       auto& hdr_raw_entry = enc->m_raw_images.find(UHDR_HDR_IMG)->second;
222       if (dst_w <= 0 || dst_h <= 0 || dst_w > ultrahdr::kMaxWidth || dst_h > ultrahdr::kMaxHeight) {
223         uhdr_error_info_t status;
224         status.error_code = UHDR_CODEC_INVALID_PARAM;
225         snprintf(status.detail, sizeof status.detail,
226                  "destination dimensions must be in range (0, %d] x (0, %d]. dest image width "
227                  "is %d, dest image height is %d",
228                  ultrahdr::kMaxWidth, ultrahdr::kMaxHeight, dst_w, dst_h);
229         return status;
230       }
231       if ((dst_w % 2 != 0 || dst_h % 2 != 0) && hdr_raw_entry->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
232         uhdr_error_info_t status;
233         status.error_code = UHDR_CODEC_INVALID_PARAM;
234         snprintf(status.detail, sizeof status.detail,
235                  "destination dimensions cannot be odd for format {UHDR_IMG_FMT_24bppYCbCrP010}. "
236                  "dest image width is %d, dest image height is %d",
237                  dst_w, dst_h);
238         return status;
239       }
240       hdr_img =
241           apply_resize(dynamic_cast<uhdr_resize_effect_t*>(it), hdr_raw_entry.get(), dst_w, dst_h);
242       if (enc->m_raw_images.find(UHDR_SDR_IMG) != enc->m_raw_images.end()) {
243         auto& sdr_raw_entry = enc->m_raw_images.find(UHDR_SDR_IMG)->second;
244         if ((dst_w % 2 != 0 || dst_h % 2 != 0) &&
245             sdr_raw_entry->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
246           uhdr_error_info_t status;
247           status.error_code = UHDR_CODEC_INVALID_PARAM;
248           snprintf(status.detail, sizeof status.detail,
249                    "destination dimensions cannot be odd for format {UHDR_IMG_FMT_12bppYCbCr420}. "
250                    "dest image width is %d, dest image height is %d",
251                    dst_w, dst_h);
252           return status;
253         }
254         sdr_img = apply_resize(dynamic_cast<uhdr_resize_effect_t*>(it), sdr_raw_entry.get(), dst_w,
255                                dst_h);
256       }
257     }
258 
259     if (hdr_img == nullptr ||
260         (enc->m_raw_images.find(UHDR_SDR_IMG) != enc->m_raw_images.end() && sdr_img == nullptr)) {
261       uhdr_error_info_t status;
262       status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
263       status.has_detail = 1;
264       snprintf(status.detail, sizeof status.detail,
265                "encountered unknown error while applying effect %s", it->to_string().c_str());
266       return status;
267     }
268     enc->m_raw_images.insert_or_assign(UHDR_HDR_IMG, std::move(hdr_img));
269     if (sdr_img != nullptr) {
270       enc->m_raw_images.insert_or_assign(UHDR_SDR_IMG, std::move(sdr_img));
271     }
272   }
273 
274   return g_no_error;
275 }
276 
is_resize_effect(const ultrahdr::uhdr_effect_desc_t * effect)277 bool is_resize_effect(const ultrahdr::uhdr_effect_desc_t* effect) {
278   return dynamic_cast<const ultrahdr::uhdr_resize_effect_t*>(effect) != nullptr;
279 }
280 
apply_effects(uhdr_decoder_private * dec)281 uhdr_error_info_t apply_effects(uhdr_decoder_private* dec) {
282   void *gl_ctxt = nullptr, *disp_texture_ptr = nullptr, *gm_texture_ptr = nullptr;
283 #ifdef UHDR_ENABLE_GLES
284   if (dec->m_enable_gles) {
285     gl_ctxt = &dec->m_uhdr_gl_ctxt;
286     bool texture_created =
287         dec->m_uhdr_gl_ctxt.mDecodedImgTexture != 0 && dec->m_uhdr_gl_ctxt.mGainmapImgTexture != 0;
288     bool resize_effect_present = std::find_if(dec->m_effects.begin(), dec->m_effects.end(),
289                                               is_resize_effect) != dec->m_effects.end();
290     if (!texture_created && resize_effect_present &&
291         isBufferDataContiguous(dec->m_decoded_img_buffer.get()) &&
292         isBufferDataContiguous(dec->m_gainmap_img_buffer.get())) {
293       dec->m_uhdr_gl_ctxt.mDecodedImgTexture = dec->m_uhdr_gl_ctxt.create_texture(
294           dec->m_decoded_img_buffer->fmt, dec->m_decoded_img_buffer->w,
295           dec->m_decoded_img_buffer->h, dec->m_decoded_img_buffer->planes[0]);
296       dec->m_uhdr_gl_ctxt.mGainmapImgTexture = dec->m_uhdr_gl_ctxt.create_texture(
297           dec->m_gainmap_img_buffer->fmt, dec->m_gainmap_img_buffer->w,
298           dec->m_gainmap_img_buffer->h, dec->m_gainmap_img_buffer->planes[0]);
299     }
300     disp_texture_ptr = &dec->m_uhdr_gl_ctxt.mDecodedImgTexture;
301     gm_texture_ptr = &dec->m_uhdr_gl_ctxt.mGainmapImgTexture;
302   }
303 #endif
304   for (auto& it : dec->m_effects) {
305     std::unique_ptr<ultrahdr::uhdr_raw_image_ext_t> disp_img = nullptr;
306     std::unique_ptr<ultrahdr::uhdr_raw_image_ext_t> gm_img = nullptr;
307 
308     if (nullptr != dynamic_cast<uhdr_rotate_effect_t*>(it)) {
309       disp_img = apply_rotate(dynamic_cast<uhdr_rotate_effect_t*>(it),
310                               dec->m_decoded_img_buffer.get(), gl_ctxt, disp_texture_ptr);
311       gm_img = apply_rotate(dynamic_cast<uhdr_rotate_effect_t*>(it),
312                             dec->m_gainmap_img_buffer.get(), gl_ctxt, gm_texture_ptr);
313     } else if (nullptr != dynamic_cast<uhdr_mirror_effect_t*>(it)) {
314       disp_img = apply_mirror(dynamic_cast<uhdr_mirror_effect_t*>(it),
315                               dec->m_decoded_img_buffer.get(), gl_ctxt, disp_texture_ptr);
316       gm_img = apply_mirror(dynamic_cast<uhdr_mirror_effect_t*>(it),
317                             dec->m_gainmap_img_buffer.get(), gl_ctxt, gm_texture_ptr);
318     } else if (nullptr != dynamic_cast<uhdr_crop_effect_t*>(it)) {
319       auto crop_effect = dynamic_cast<uhdr_crop_effect_t*>(it);
320       uhdr_raw_image_t* disp = dec->m_decoded_img_buffer.get();
321       uhdr_raw_image_t* gm = dec->m_gainmap_img_buffer.get();
322       int left = (std::max)(0, crop_effect->m_left);
323       int right = (std::min)((int)disp->w, crop_effect->m_right);
324       if (right <= left) {
325         uhdr_error_info_t status;
326         status.error_code = UHDR_CODEC_INVALID_PARAM;
327         status.has_detail = 1;
328         snprintf(
329             status.detail, sizeof status.detail,
330             "unexpected crop dimensions. crop right is <= crop left, after crop image width is %d",
331             right - left);
332         return status;
333       }
334 
335       int top = (std::max)(0, crop_effect->m_top);
336       int bottom = (std::min)((int)disp->h, crop_effect->m_bottom);
337       if (bottom <= top) {
338         uhdr_error_info_t status;
339         status.error_code = UHDR_CODEC_INVALID_PARAM;
340         status.has_detail = 1;
341         snprintf(
342             status.detail, sizeof status.detail,
343             "unexpected crop dimensions. crop bottom is <= crop top, after crop image height is %d",
344             bottom - top);
345         return status;
346       }
347 
348       float wd_ratio = ((float)disp->w) / gm->w;
349       float ht_ratio = ((float)disp->h) / gm->h;
350       int gm_left = (int)(left / wd_ratio);
351       int gm_right = (int)(right / wd_ratio);
352       if (gm_right <= gm_left) {
353         uhdr_error_info_t status;
354         status.error_code = UHDR_CODEC_INVALID_PARAM;
355         status.has_detail = 1;
356         snprintf(status.detail, sizeof status.detail,
357                  "unexpected crop dimensions. crop right is <= crop left for gainmap image, after "
358                  "crop gainmap image width is %d",
359                  gm_right - gm_left);
360         return status;
361       }
362 
363       int gm_top = (int)(top / ht_ratio);
364       int gm_bottom = (int)(bottom / ht_ratio);
365       if (gm_bottom <= gm_top) {
366         uhdr_error_info_t status;
367         status.error_code = UHDR_CODEC_INVALID_PARAM;
368         status.has_detail = 1;
369         snprintf(status.detail, sizeof status.detail,
370                  "unexpected crop dimensions. crop bottom is <= crop top for gainmap image, after "
371                  "crop gainmap image height is %d",
372                  gm_bottom - gm_top);
373         return status;
374       }
375 
376       disp_img = apply_crop(dynamic_cast<ultrahdr::uhdr_crop_effect_t*>(it), disp, left, top,
377                             right - left, bottom - top, gl_ctxt, disp_texture_ptr);
378       gm_img = apply_crop(dynamic_cast<ultrahdr::uhdr_crop_effect_t*>(it), gm, gm_left, gm_top,
379                           (gm_right - gm_left), (gm_bottom - gm_top), gl_ctxt, gm_texture_ptr);
380     } else if (nullptr != dynamic_cast<uhdr_resize_effect_t*>(it)) {
381       auto resize_effect = dynamic_cast<uhdr_resize_effect_t*>(it);
382       int dst_w = resize_effect->m_width;
383       int dst_h = resize_effect->m_height;
384       float wd_ratio =
385           ((float)dec->m_decoded_img_buffer.get()->w) / dec->m_gainmap_img_buffer.get()->w;
386       float ht_ratio =
387           ((float)dec->m_decoded_img_buffer.get()->h) / dec->m_gainmap_img_buffer.get()->h;
388       int dst_gm_w = (int)(dst_w / wd_ratio);
389       int dst_gm_h = (int)(dst_h / ht_ratio);
390       if (dst_w <= 0 || dst_h <= 0 || dst_gm_w <= 0 || dst_gm_h <= 0 ||
391           dst_w > ultrahdr::kMaxWidth || dst_h > ultrahdr::kMaxHeight ||
392           dst_gm_w > ultrahdr::kMaxWidth || dst_gm_h > ultrahdr::kMaxHeight) {
393         uhdr_error_info_t status;
394         status.error_code = UHDR_CODEC_INVALID_PARAM;
395         snprintf(status.detail, sizeof status.detail,
396                  "destination dimension must be in range (0, %d] x (0, %d]. dest image width is "
397                  "%d, dest image height is %d, dest gainmap width is %d, dest gainmap height is %d",
398                  ultrahdr::kMaxWidth, ultrahdr::kMaxHeight, dst_w, dst_h, dst_gm_w, dst_gm_h);
399         return status;
400       }
401       disp_img =
402           apply_resize(dynamic_cast<uhdr_resize_effect_t*>(it), dec->m_decoded_img_buffer.get(),
403                        dst_w, dst_h, gl_ctxt, disp_texture_ptr);
404       gm_img =
405           apply_resize(dynamic_cast<uhdr_resize_effect_t*>(it), dec->m_gainmap_img_buffer.get(),
406                        dst_gm_w, dst_gm_h, gl_ctxt, gm_texture_ptr);
407     }
408 
409     if (disp_img == nullptr || gm_img == nullptr) {
410       uhdr_error_info_t status;
411       status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
412       status.has_detail = 1;
413       snprintf(status.detail, sizeof status.detail,
414                "encountered unknown error while applying effect %s", it->to_string().c_str());
415       return status;
416     }
417     dec->m_decoded_img_buffer = std::move(disp_img);
418     dec->m_gainmap_img_buffer = std::move(gm_img);
419   }
420   return g_no_error;
421 }
422 
uhdr_validate_gainmap_metadata_descriptor(uhdr_gainmap_metadata_t * metadata)423 uhdr_error_info_t uhdr_validate_gainmap_metadata_descriptor(uhdr_gainmap_metadata_t* metadata) {
424   uhdr_error_info_t status = g_no_error;
425 
426   if (metadata == nullptr) {
427     status.error_code = UHDR_CODEC_INVALID_PARAM;
428     status.has_detail = 1;
429     snprintf(status.detail, sizeof status.detail,
430              "received nullptr for gainmap metadata descriptor");
431   } else if (!std::isfinite(metadata->min_content_boost) ||
432              !std::isfinite(metadata->max_content_boost) || !std::isfinite(metadata->offset_sdr) ||
433              !std::isfinite(metadata->offset_hdr) || !std::isfinite(metadata->hdr_capacity_min) ||
434              !std::isfinite(metadata->hdr_capacity_max) || !std::isfinite(metadata->gamma)) {
435     status.error_code = UHDR_CODEC_INVALID_PARAM;
436     status.has_detail = 1;
437     snprintf(status.detail, sizeof status.detail,
438              "Field(s) of gainmap metadata descriptor are either NaN or infinite. min content "
439              "boost %f, max content boost %f, offset sdr %f, offset hdr %f, hdr capacity min %f, "
440              "hdr capacity max %f, gamma %f",
441              metadata->min_content_boost, metadata->max_content_boost, metadata->offset_sdr,
442              metadata->offset_hdr, metadata->hdr_capacity_min, metadata->hdr_capacity_max,
443              metadata->gamma);
444   } else if (metadata->max_content_boost < metadata->min_content_boost) {
445     status.error_code = UHDR_CODEC_INVALID_PARAM;
446     status.has_detail = 1;
447     snprintf(status.detail, sizeof status.detail,
448              "received bad value for content boost max %f, expects to be >= content boost min %f",
449              metadata->max_content_boost, metadata->min_content_boost);
450   } else if (metadata->min_content_boost <= 0.0f) {
451     status.error_code = UHDR_CODEC_INVALID_PARAM;
452     status.has_detail = 1;
453     snprintf(status.detail, sizeof status.detail,
454              "received bad value for min boost %f, expects > 0.0f", metadata->min_content_boost);
455     return status;
456   } else if (metadata->gamma <= 0.0f) {
457     status.error_code = UHDR_CODEC_INVALID_PARAM;
458     status.has_detail = 1;
459     snprintf(status.detail, sizeof status.detail, "received bad value for gamma %f, expects > 0.0f",
460              metadata->gamma);
461   } else if (metadata->offset_sdr < 0.0f) {
462     status.error_code = UHDR_CODEC_INVALID_PARAM;
463     status.has_detail = 1;
464     snprintf(status.detail, sizeof status.detail,
465              "received bad value for offset sdr %f, expects to be >= 0.0f", metadata->offset_sdr);
466   } else if (metadata->offset_hdr < 0.0f) {
467     status.error_code = UHDR_CODEC_INVALID_PARAM;
468     status.has_detail = 1;
469     snprintf(status.detail, sizeof status.detail,
470              "received bad value for offset hdr %f, expects to be >= 0.0f", metadata->offset_hdr);
471   } else if (metadata->hdr_capacity_max <= metadata->hdr_capacity_min) {
472     status.error_code = UHDR_CODEC_INVALID_PARAM;
473     status.has_detail = 1;
474     snprintf(status.detail, sizeof status.detail,
475              "received bad value for hdr capacity max %f, expects to be > hdr capacity min %f",
476              metadata->hdr_capacity_max, metadata->hdr_capacity_min);
477   } else if (metadata->hdr_capacity_min < 1.0f) {
478     status.error_code = UHDR_CODEC_INVALID_PARAM;
479     status.has_detail = 1;
480     snprintf(status.detail, sizeof status.detail,
481              "received bad value for hdr capacity min %f, expects to be >= 1.0f",
482              metadata->hdr_capacity_min);
483   }
484   return status;
485 }
486 
487 }  // namespace ultrahdr
488 
~uhdr_codec_private()489 uhdr_codec_private::~uhdr_codec_private() {
490   for (auto it : m_effects) delete it;
491   m_effects.clear();
492 }
493 
uhdr_enc_validate_and_set_compressed_img(uhdr_codec_private_t * enc,uhdr_compressed_image_t * img,uhdr_img_label_t intent)494 uhdr_error_info_t uhdr_enc_validate_and_set_compressed_img(uhdr_codec_private_t* enc,
495                                                            uhdr_compressed_image_t* img,
496                                                            uhdr_img_label_t intent) {
497   uhdr_error_info_t status = g_no_error;
498 
499   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
500     status.error_code = UHDR_CODEC_INVALID_PARAM;
501     status.has_detail = 1;
502     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
503   } else if (img == nullptr) {
504     status.error_code = UHDR_CODEC_INVALID_PARAM;
505     status.has_detail = 1;
506     snprintf(status.detail, sizeof status.detail, "received nullptr for compressed image handle");
507   } else if (img->data == nullptr) {
508     status.error_code = UHDR_CODEC_INVALID_PARAM;
509     status.has_detail = 1;
510     snprintf(status.detail, sizeof status.detail,
511              "received nullptr for compressed img->data field");
512   } else if (img->capacity < img->data_sz) {
513     status.error_code = UHDR_CODEC_INVALID_PARAM;
514     status.has_detail = 1;
515     snprintf(status.detail, sizeof status.detail, "img->capacity %zd is less than img->data_sz %zd",
516              img->capacity, img->data_sz);
517   }
518   if (status.error_code != UHDR_CODEC_OK) return status;
519 
520   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
521   if (handle->m_sailed) {
522     status.error_code = UHDR_CODEC_INVALID_OPERATION;
523     status.has_detail = 1;
524     snprintf(status.detail, sizeof status.detail,
525              "An earlier call to uhdr_encode() has switched the context from configurable state to "
526              "end state. The context is no longer configurable. To reuse, call reset()");
527     return status;
528   }
529 
530   std::shared_ptr<DataSegment> seg =
531       DataSegment::Create(DataRange(0, img->data_sz), static_cast<const uint8_t*>(img->data),
532                           DataSegment::BufferDispositionPolicy::kDontDelete);
533   DataSegmentDataSource data_source(seg);
534   JpegInfoBuilder jpeg_info_builder;
535   JpegScanner jpeg_scanner(nullptr);
536   jpeg_scanner.Run(&data_source, &jpeg_info_builder);
537   data_source.Reset();
538   if (jpeg_scanner.HasError()) {
539     status.error_code = UHDR_CODEC_INVALID_PARAM;
540     snprintf(status.detail, sizeof status.detail,
541              "received bad/corrupted jpeg image as part of input configuration");
542     return status;
543   }
544 
545   const auto& image_ranges = jpeg_info_builder.GetInfo().GetImageRanges();
546   if (image_ranges.empty()) {
547     status.error_code = UHDR_CODEC_INVALID_PARAM;
548     status.has_detail = 1;
549     snprintf(status.detail, sizeof status.detail,
550              "compressed image received as part of input config contains no valid jpeg images");
551     return status;
552   }
553 
554   if (image_ranges.size() > 1) {
555     ALOGW(
556         "compressed image received as part of input config contains multiple jpeg images, "
557         "selecting first image for intent %d, rest are ignored",
558         intent);
559   }
560 
561   auto entry = std::make_unique<ultrahdr::uhdr_compressed_image_ext_t>(img->cg, img->ct, img->range,
562                                                                        image_ranges[0].GetLength());
563   memcpy(entry->data, static_cast<uint8_t*>(img->data) + image_ranges[0].GetBegin(),
564          image_ranges[0].GetLength());
565   entry->data_sz = image_ranges[0].GetLength();
566   handle->m_compressed_images.insert_or_assign(intent, std::move(entry));
567 
568   return status;
569 }
570 
uhdr_create_encoder(void)571 uhdr_codec_private_t* uhdr_create_encoder(void) {
572   uhdr_encoder_private* handle = new uhdr_encoder_private();
573 
574   if (handle != nullptr) {
575     uhdr_reset_encoder(handle);
576   }
577   return handle;
578 }
579 
uhdr_release_encoder(uhdr_codec_private_t * enc)580 void uhdr_release_encoder(uhdr_codec_private_t* enc) {
581   if (dynamic_cast<uhdr_encoder_private*>(enc) != nullptr) {
582     uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
583     delete handle;
584   }
585 }
586 
587 UHDR_EXTERN uhdr_error_info_t
uhdr_enc_set_using_multi_channel_gainmap(uhdr_codec_private_t * enc,int use_multi_channel_gainmap)588 uhdr_enc_set_using_multi_channel_gainmap(uhdr_codec_private_t* enc, int use_multi_channel_gainmap) {
589   uhdr_error_info_t status = g_no_error;
590 
591   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
592     status.error_code = UHDR_CODEC_INVALID_PARAM;
593     status.has_detail = 1;
594     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
595     return status;
596   }
597 
598   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
599 
600   if (handle->m_sailed) {
601     status.error_code = UHDR_CODEC_INVALID_OPERATION;
602     status.has_detail = 1;
603     snprintf(status.detail, sizeof status.detail,
604              "An earlier call to uhdr_encode() has switched the context from configurable state to "
605              "end state. The context is no longer configurable. To reuse, call reset()");
606     return status;
607   }
608 
609   handle->m_use_multi_channel_gainmap = use_multi_channel_gainmap;
610 
611   return status;
612 }
613 
uhdr_enc_set_gainmap_scale_factor(uhdr_codec_private_t * enc,int gainmap_scale_factor)614 UHDR_EXTERN uhdr_error_info_t uhdr_enc_set_gainmap_scale_factor(uhdr_codec_private_t* enc,
615                                                                 int gainmap_scale_factor) {
616   uhdr_error_info_t status = g_no_error;
617 
618   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
619     status.error_code = UHDR_CODEC_INVALID_PARAM;
620     status.has_detail = 1;
621     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
622     return status;
623   }
624 
625   if (gainmap_scale_factor <= 0 || gainmap_scale_factor > 128) {
626     status.error_code = UHDR_CODEC_INVALID_PARAM;
627     status.has_detail = 1;
628     snprintf(status.detail, sizeof status.detail,
629              "gainmap scale factor is expected to be in range (0, 128], received %d",
630              gainmap_scale_factor);
631     return status;
632   }
633 
634   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
635 
636   if (handle->m_sailed) {
637     status.error_code = UHDR_CODEC_INVALID_OPERATION;
638     status.has_detail = 1;
639     snprintf(status.detail, sizeof status.detail,
640              "An earlier call to uhdr_encode() has switched the context from configurable state to "
641              "end state. The context is no longer configurable. To reuse, call reset()");
642     return status;
643   }
644 
645   handle->m_gainmap_scale_factor = gainmap_scale_factor;
646 
647   return status;
648 }
649 
uhdr_enc_set_gainmap_gamma(uhdr_codec_private_t * enc,float gamma)650 UHDR_EXTERN uhdr_error_info_t uhdr_enc_set_gainmap_gamma(uhdr_codec_private_t* enc, float gamma) {
651   uhdr_error_info_t status = g_no_error;
652 
653   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
654     status.error_code = UHDR_CODEC_INVALID_PARAM;
655     status.has_detail = 1;
656     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
657     return status;
658   }
659 
660   if (!std::isfinite(gamma) || gamma <= 0.0f) {
661     status.error_code = UHDR_CODEC_INVALID_PARAM;
662     status.has_detail = 1;
663     snprintf(status.detail, sizeof status.detail, "unsupported gainmap gamma %f, expects to be > 0",
664              gamma);
665     return status;
666   }
667 
668   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
669 
670   if (handle->m_sailed) {
671     status.error_code = UHDR_CODEC_INVALID_OPERATION;
672     status.has_detail = 1;
673     snprintf(status.detail, sizeof status.detail,
674              "An earlier call to uhdr_encode() has switched the context from configurable state to "
675              "end state. The context is no longer configurable. To reuse, call reset()");
676     return status;
677   }
678 
679   handle->m_gamma = gamma;
680 
681   return status;
682 }
683 
uhdr_enc_set_preset(uhdr_codec_private_t * enc,uhdr_enc_preset_t preset)684 uhdr_error_info_t uhdr_enc_set_preset(uhdr_codec_private_t* enc, uhdr_enc_preset_t preset) {
685   uhdr_error_info_t status = g_no_error;
686 
687   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
688     status.error_code = UHDR_CODEC_INVALID_PARAM;
689     status.has_detail = 1;
690     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
691     return status;
692   }
693 
694   if (preset != UHDR_USAGE_REALTIME && preset != UHDR_USAGE_BEST_QUALITY) {
695     status.error_code = UHDR_CODEC_INVALID_PARAM;
696     status.has_detail = 1;
697     snprintf(status.detail, sizeof status.detail,
698              "invalid preset %d, expects one of {UHDR_USAGE_REALTIME, UHDR_USAGE_BEST_QUALITY}",
699              preset);
700     return status;
701   }
702 
703   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
704 
705   if (handle->m_sailed) {
706     status.error_code = UHDR_CODEC_INVALID_OPERATION;
707     status.has_detail = 1;
708     snprintf(status.detail, sizeof status.detail,
709              "An earlier call to uhdr_encode() has switched the context from configurable state to "
710              "end state. The context is no longer configurable. To reuse, call reset()");
711     return status;
712   }
713 
714   handle->m_enc_preset = preset;
715 
716   return status;
717 }
718 
uhdr_enc_set_min_max_content_boost(uhdr_codec_private_t * enc,float min_boost,float max_boost)719 uhdr_error_info_t uhdr_enc_set_min_max_content_boost(uhdr_codec_private_t* enc, float min_boost,
720                                                      float max_boost) {
721   uhdr_error_info_t status = g_no_error;
722 
723   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
724     status.error_code = UHDR_CODEC_INVALID_PARAM;
725     status.has_detail = 1;
726     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
727     return status;
728   }
729 
730   if (!std::isfinite(min_boost) || !std::isfinite(max_boost)) {
731     status.error_code = UHDR_CODEC_INVALID_PARAM;
732     status.has_detail = 1;
733     snprintf(status.detail, sizeof status.detail,
734              "received an argument with value either NaN or infinite. Configured min boost %f, "
735              "max boost %f",
736              max_boost, min_boost);
737     return status;
738   }
739 
740   if (max_boost < min_boost) {
741     status.error_code = UHDR_CODEC_INVALID_PARAM;
742     status.has_detail = 1;
743     snprintf(status.detail, sizeof status.detail,
744              "Invalid min boost / max boost configuration. configured max boost %f is less than "
745              "min boost %f",
746              max_boost, min_boost);
747     return status;
748   }
749 
750   if (min_boost <= 0.0f) {
751     status.error_code = UHDR_CODEC_INVALID_PARAM;
752     status.has_detail = 1;
753     snprintf(status.detail, sizeof status.detail,
754              "Invalid min boost configuration %f, expects > 0.0f", min_boost);
755     return status;
756   }
757 
758   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
759 
760   if (handle->m_sailed) {
761     status.error_code = UHDR_CODEC_INVALID_OPERATION;
762     status.has_detail = 1;
763     snprintf(status.detail, sizeof status.detail,
764              "An earlier call to uhdr_encode() has switched the context from configurable state to "
765              "end state. The context is no longer configurable. To reuse, call reset()");
766     return status;
767   }
768 
769   handle->m_min_content_boost = min_boost;
770   handle->m_max_content_boost = max_boost;
771 
772   return status;
773 }
774 
uhdr_enc_set_target_display_peak_brightness(uhdr_codec_private_t * enc,float nits)775 uhdr_error_info_t uhdr_enc_set_target_display_peak_brightness(uhdr_codec_private_t* enc,
776                                                               float nits) {
777   uhdr_error_info_t status = g_no_error;
778 
779   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
780     status.error_code = UHDR_CODEC_INVALID_PARAM;
781     status.has_detail = 1;
782     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
783     return status;
784   }
785 
786   if (!std::isfinite(nits) || nits < ultrahdr::kSdrWhiteNits || nits > ultrahdr::kPqMaxNits) {
787     status.error_code = UHDR_CODEC_INVALID_PARAM;
788     status.has_detail = 1;
789     snprintf(
790         status.detail, sizeof status.detail,
791         "unexpected target display peak brightness nits %f, expects to be with in range [%f, %f]",
792         nits, ultrahdr::kSdrWhiteNits, ultrahdr::kPqMaxNits);
793   }
794 
795   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
796 
797   if (handle->m_sailed) {
798     status.error_code = UHDR_CODEC_INVALID_OPERATION;
799     status.has_detail = 1;
800     snprintf(status.detail, sizeof status.detail,
801              "An earlier call to uhdr_encode() has switched the context from configurable state to "
802              "end state. The context is no longer configurable. To reuse, call reset()");
803     return status;
804   }
805 
806   handle->m_target_disp_max_brightness = nits;
807 
808   return status;
809 }
810 
uhdr_enc_set_raw_image(uhdr_codec_private_t * enc,uhdr_raw_image_t * img,uhdr_img_label_t intent)811 uhdr_error_info_t uhdr_enc_set_raw_image(uhdr_codec_private_t* enc, uhdr_raw_image_t* img,
812                                          uhdr_img_label_t intent) {
813   uhdr_error_info_t status = g_no_error;
814 
815   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
816     status.error_code = UHDR_CODEC_INVALID_PARAM;
817     status.has_detail = 1;
818     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
819   } else if (img == nullptr) {
820     status.error_code = UHDR_CODEC_INVALID_PARAM;
821     status.has_detail = 1;
822     snprintf(status.detail, sizeof status.detail, "received nullptr for raw image handle");
823   } else if (intent != UHDR_HDR_IMG && intent != UHDR_SDR_IMG) {
824     status.error_code = UHDR_CODEC_INVALID_PARAM;
825     status.has_detail = 1;
826     snprintf(status.detail, sizeof status.detail,
827              "invalid intent %d, expects one of {UHDR_HDR_IMG, UHDR_SDR_IMG}", intent);
828   } else if (intent == UHDR_HDR_IMG && (img->fmt != UHDR_IMG_FMT_24bppYCbCrP010 &&
829                                         img->fmt != UHDR_IMG_FMT_32bppRGBA1010102 &&
830                                         img->fmt != UHDR_IMG_FMT_64bppRGBAHalfFloat)) {
831     status.error_code = UHDR_CODEC_INVALID_PARAM;
832     status.has_detail = 1;
833     snprintf(status.detail, sizeof status.detail,
834              "unsupported input pixel format for hdr intent %d, expects one of "
835              "{UHDR_IMG_FMT_24bppYCbCrP010, UHDR_IMG_FMT_32bppRGBA1010102, "
836              "UHDR_IMG_FMT_64bppRGBAHalfFloat}",
837              img->fmt);
838   } else if (intent == UHDR_SDR_IMG &&
839              (img->fmt != UHDR_IMG_FMT_12bppYCbCr420 && img->fmt != UHDR_IMG_FMT_32bppRGBA8888)) {
840     status.error_code = UHDR_CODEC_INVALID_PARAM;
841     status.has_detail = 1;
842     snprintf(status.detail, sizeof status.detail,
843              "unsupported input pixel format for sdr intent %d, expects one of "
844              "{UHDR_IMG_FMT_12bppYCbCr420, UHDR_IMG_FMT_32bppRGBA8888}",
845              img->fmt);
846   } else if (img->cg != UHDR_CG_BT_2100 && img->cg != UHDR_CG_DISPLAY_P3 &&
847              img->cg != UHDR_CG_BT_709) {
848     status.error_code = UHDR_CODEC_INVALID_PARAM;
849     status.has_detail = 1;
850     snprintf(status.detail, sizeof status.detail,
851              "invalid input color gamut %d, expects one of {UHDR_CG_BT_2100, UHDR_CG_DISPLAY_P3, "
852              "UHDR_CG_BT_709}",
853              img->cg);
854   } else if (intent == UHDR_SDR_IMG && img->ct != UHDR_CT_SRGB) {
855     status.error_code = UHDR_CODEC_INVALID_PARAM;
856     status.has_detail = 1;
857     snprintf(status.detail, sizeof status.detail,
858              "invalid input color transfer for sdr intent image %d, expects UHDR_CT_SRGB", img->ct);
859   } else if (intent == UHDR_HDR_IMG && img->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat &&
860              img->ct != UHDR_CT_LINEAR) {
861     status.error_code = UHDR_CODEC_INVALID_PARAM;
862     status.has_detail = 1;
863     snprintf(status.detail, sizeof status.detail,
864              "invalid input color transfer for hdr intent image %d with format "
865              "UHDR_IMG_FMT_64bppRGBAHalfFloat, expects one of {UHDR_CT_LINEAR}",
866              img->ct);
867   } else if (intent == UHDR_HDR_IMG && img->fmt != UHDR_IMG_FMT_64bppRGBAHalfFloat &&
868              (img->ct != UHDR_CT_HLG && img->ct != UHDR_CT_PQ)) {
869     status.error_code = UHDR_CODEC_INVALID_PARAM;
870     status.has_detail = 1;
871     snprintf(status.detail, sizeof status.detail,
872              "invalid input color transfer for hdr intent image %d with format %d, expects one of "
873              "{UHDR_CT_HLG, UHDR_CT_PQ}",
874              img->fmt, img->ct);
875   } else if ((img->w % 2 != 0 || img->h % 2 != 0) &&
876              (img->fmt == UHDR_IMG_FMT_12bppYCbCr420 || img->fmt == UHDR_IMG_FMT_24bppYCbCrP010)) {
877     status.error_code = UHDR_CODEC_INVALID_PARAM;
878     status.has_detail = 1;
879     snprintf(status.detail, sizeof status.detail,
880              "image dimensions cannot be odd for formats {UHDR_IMG_FMT_12bppYCbCr420, "
881              "UHDR_IMG_FMT_24bppYCbCrP010}, received image dimensions %dx%d",
882              img->w, img->h);
883   } else if ((int)img->w < ultrahdr::kMinWidth || (int)img->h < ultrahdr::kMinHeight) {
884     status.error_code = UHDR_CODEC_INVALID_PARAM;
885     status.has_detail = 1;
886     snprintf(status.detail, sizeof status.detail,
887              "image dimensions cannot be less than %dx%d, received image dimensions %dx%d",
888              ultrahdr::kMinWidth, ultrahdr::kMinHeight, img->w, img->h);
889   } else if ((int)img->w > ultrahdr::kMaxWidth || (int)img->h > ultrahdr::kMaxHeight) {
890     status.error_code = UHDR_CODEC_INVALID_PARAM;
891     status.has_detail = 1;
892     snprintf(status.detail, sizeof status.detail,
893              "image dimensions cannot be larger than %dx%d, received image dimensions %dx%d",
894              ultrahdr::kMaxWidth, ultrahdr::kMaxHeight, img->w, img->h);
895   } else if (img->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
896     if (img->planes[UHDR_PLANE_Y] == nullptr || img->planes[UHDR_PLANE_UV] == nullptr) {
897       status.error_code = UHDR_CODEC_INVALID_PARAM;
898       status.has_detail = 1;
899       snprintf(status.detail, sizeof status.detail,
900                "received nullptr for data field(s), luma ptr %p, chroma_uv ptr %p",
901                img->planes[UHDR_PLANE_Y], img->planes[UHDR_PLANE_UV]);
902     } else if (img->stride[UHDR_PLANE_Y] < img->w) {
903       status.error_code = UHDR_CODEC_INVALID_PARAM;
904       status.has_detail = 1;
905       snprintf(status.detail, sizeof status.detail,
906                "luma stride must not be smaller than width, stride=%d, width=%d",
907                img->stride[UHDR_PLANE_Y], img->w);
908     } else if (img->stride[UHDR_PLANE_UV] < img->w) {
909       status.error_code = UHDR_CODEC_INVALID_PARAM;
910       status.has_detail = 1;
911       snprintf(status.detail, sizeof status.detail,
912                "chroma_uv stride must not be smaller than width, stride=%d, width=%d",
913                img->stride[UHDR_PLANE_UV], img->w);
914     } else if (img->fmt == UHDR_IMG_FMT_24bppYCbCrP010 &&
915                (img->range != UHDR_CR_FULL_RANGE && img->range != UHDR_CR_LIMITED_RANGE)) {
916       status.error_code = UHDR_CODEC_INVALID_PARAM;
917       status.has_detail = 1;
918       snprintf(status.detail, sizeof status.detail,
919                "invalid range, expects one of {UHDR_CR_FULL_RANGE, UHDR_CR_LIMITED_RANGE}");
920     } else if (img->fmt == UHDR_IMG_FMT_32bppRGBA1010102 && img->range != UHDR_CR_FULL_RANGE) {
921       status.error_code = UHDR_CODEC_INVALID_PARAM;
922       status.has_detail = 1;
923       snprintf(status.detail, sizeof status.detail,
924                "invalid range, expects one of {UHDR_CR_FULL_RANGE}");
925     }
926   } else if (img->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
927     if (img->planes[UHDR_PLANE_Y] == nullptr || img->planes[UHDR_PLANE_U] == nullptr ||
928         img->planes[UHDR_PLANE_V] == nullptr) {
929       status.error_code = UHDR_CODEC_INVALID_PARAM;
930       status.has_detail = 1;
931       snprintf(status.detail, sizeof status.detail,
932                "received nullptr for data field(s) luma ptr %p, chroma_u ptr %p, chroma_v ptr %p",
933                img->planes[UHDR_PLANE_Y], img->planes[UHDR_PLANE_U], img->planes[UHDR_PLANE_V]);
934     } else if (img->stride[UHDR_PLANE_Y] < img->w) {
935       status.error_code = UHDR_CODEC_INVALID_PARAM;
936       status.has_detail = 1;
937       snprintf(status.detail, sizeof status.detail,
938                "luma stride must not be smaller than width, stride=%d, width=%d",
939                img->stride[UHDR_PLANE_Y], img->w);
940     } else if (img->stride[UHDR_PLANE_U] < img->w / 2) {
941       status.error_code = UHDR_CODEC_INVALID_PARAM;
942       status.has_detail = 1;
943       snprintf(status.detail, sizeof status.detail,
944                "chroma_u stride must not be smaller than width / 2, stride=%d, width=%d",
945                img->stride[UHDR_PLANE_U], img->w);
946     } else if (img->stride[UHDR_PLANE_V] < img->w / 2) {
947       status.error_code = UHDR_CODEC_INVALID_PARAM;
948       status.has_detail = 1;
949       snprintf(status.detail, sizeof status.detail,
950                "chroma_v stride must not be smaller than width / 2, stride=%d, width=%d",
951                img->stride[UHDR_PLANE_V], img->w);
952     } else if (img->range != UHDR_CR_FULL_RANGE) {
953       status.error_code = UHDR_CODEC_INVALID_PARAM;
954       status.has_detail = 1;
955       snprintf(status.detail, sizeof status.detail,
956                "invalid range, expects one of {UHDR_CR_FULL_RANGE}");
957     }
958   } else if (img->fmt == UHDR_IMG_FMT_32bppRGBA1010102 || img->fmt == UHDR_IMG_FMT_32bppRGBA8888 ||
959              img->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
960     if (img->planes[UHDR_PLANE_PACKED] == nullptr) {
961       status.error_code = UHDR_CODEC_INVALID_PARAM;
962       status.has_detail = 1;
963       snprintf(status.detail, sizeof status.detail,
964                "received nullptr for data field(s) rgb plane packed ptr %p",
965                img->planes[UHDR_PLANE_PACKED]);
966     } else if (img->stride[UHDR_PLANE_PACKED] < img->w) {
967       status.error_code = UHDR_CODEC_INVALID_PARAM;
968       status.has_detail = 1;
969       snprintf(status.detail, sizeof status.detail,
970                "rgb planar stride must not be smaller than width, stride=%d, width=%d",
971                img->stride[UHDR_PLANE_PACKED], img->w);
972     } else if (img->range != UHDR_CR_FULL_RANGE) {
973       status.error_code = UHDR_CODEC_INVALID_PARAM;
974       status.has_detail = 1;
975       snprintf(status.detail, sizeof status.detail,
976                "invalid range, expects one of {UHDR_CR_FULL_RANGE}");
977     }
978   }
979   if (status.error_code != UHDR_CODEC_OK) return status;
980 
981   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
982   if (intent == UHDR_HDR_IMG &&
983       handle->m_raw_images.find(UHDR_SDR_IMG) != handle->m_raw_images.end()) {
984     auto& sdr_raw_entry = handle->m_raw_images.find(UHDR_SDR_IMG)->second;
985     if (img->w != sdr_raw_entry->w || img->h != sdr_raw_entry->h) {
986       status.error_code = UHDR_CODEC_INVALID_PARAM;
987       status.has_detail = 1;
988       snprintf(status.detail, sizeof status.detail,
989                "image resolutions mismatch: hdr intent: %dx%d, sdr intent: %dx%d", img->w, img->h,
990                sdr_raw_entry->w, sdr_raw_entry->h);
991       return status;
992     }
993   }
994   if (intent == UHDR_SDR_IMG &&
995       handle->m_raw_images.find(UHDR_HDR_IMG) != handle->m_raw_images.end()) {
996     auto& hdr_raw_entry = handle->m_raw_images.find(UHDR_HDR_IMG)->second;
997     if (img->w != hdr_raw_entry->w || img->h != hdr_raw_entry->h) {
998       status.error_code = UHDR_CODEC_INVALID_PARAM;
999       status.has_detail = 1;
1000       snprintf(status.detail, sizeof status.detail,
1001                "image resolutions mismatch: sdr intent: %dx%d, hdr intent: %dx%d", img->w, img->h,
1002                hdr_raw_entry->w, hdr_raw_entry->h);
1003       return status;
1004     }
1005   }
1006   if (handle->m_sailed) {
1007     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1008     status.has_detail = 1;
1009     snprintf(status.detail, sizeof status.detail,
1010              "An earlier call to uhdr_encode() has switched the context from configurable state to "
1011              "end state. The context is no longer configurable. To reuse, call reset()");
1012     return status;
1013   }
1014 
1015   std::unique_ptr<ultrahdr::uhdr_raw_image_ext_t> entry = ultrahdr::copy_raw_image(img);
1016   if (entry == nullptr) {
1017     status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
1018     status.has_detail = 1;
1019     snprintf(status.detail, sizeof status.detail,
1020              "encountered unknown error during color space conversion");
1021     return status;
1022   }
1023 
1024   handle->m_raw_images.insert_or_assign(intent, std::move(entry));
1025 
1026   return status;
1027 }
1028 
uhdr_enc_set_compressed_image(uhdr_codec_private_t * enc,uhdr_compressed_image_t * img,uhdr_img_label_t intent)1029 uhdr_error_info_t uhdr_enc_set_compressed_image(uhdr_codec_private_t* enc,
1030                                                 uhdr_compressed_image_t* img,
1031                                                 uhdr_img_label_t intent) {
1032   uhdr_error_info_t status = g_no_error;
1033 
1034   if (intent != UHDR_HDR_IMG && intent != UHDR_SDR_IMG && intent != UHDR_BASE_IMG) {
1035     status.error_code = UHDR_CODEC_INVALID_PARAM;
1036     status.has_detail = 1;
1037     snprintf(status.detail, sizeof status.detail,
1038              "invalid intent %d, expects one of {UHDR_HDR_IMG, UHDR_SDR_IMG, UHDR_BASE_IMG}",
1039              intent);
1040   }
1041 
1042   return uhdr_enc_validate_and_set_compressed_img(enc, img, intent);
1043 }
1044 
uhdr_enc_set_gainmap_image(uhdr_codec_private_t * enc,uhdr_compressed_image_t * img,uhdr_gainmap_metadata_t * metadata)1045 uhdr_error_info_t uhdr_enc_set_gainmap_image(uhdr_codec_private_t* enc,
1046                                              uhdr_compressed_image_t* img,
1047                                              uhdr_gainmap_metadata_t* metadata) {
1048   uhdr_error_info_t status = ultrahdr::uhdr_validate_gainmap_metadata_descriptor(metadata);
1049   if (status.error_code != UHDR_CODEC_OK) return status;
1050 
1051   status = uhdr_enc_validate_and_set_compressed_img(enc, img, UHDR_GAIN_MAP_IMG);
1052   if (status.error_code != UHDR_CODEC_OK) return status;
1053 
1054   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
1055   memcpy(&handle->m_metadata, metadata, sizeof *metadata);
1056 
1057   return status;
1058 }
1059 
uhdr_enc_set_quality(uhdr_codec_private_t * enc,int quality,uhdr_img_label_t intent)1060 uhdr_error_info_t uhdr_enc_set_quality(uhdr_codec_private_t* enc, int quality,
1061                                        uhdr_img_label_t intent) {
1062   uhdr_error_info_t status = g_no_error;
1063 
1064   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
1065     status.error_code = UHDR_CODEC_INVALID_PARAM;
1066     status.has_detail = 1;
1067     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1068   } else if (quality < 0 || quality > 100) {
1069     status.error_code = UHDR_CODEC_INVALID_PARAM;
1070     status.has_detail = 1;
1071     snprintf(status.detail, sizeof status.detail,
1072              "invalid quality factor %d, expects in range [0-100]", quality);
1073   } else if (intent != UHDR_HDR_IMG && intent != UHDR_SDR_IMG && intent != UHDR_BASE_IMG &&
1074              intent != UHDR_GAIN_MAP_IMG) {
1075     status.error_code = UHDR_CODEC_INVALID_PARAM;
1076     status.has_detail = 1;
1077     snprintf(status.detail, sizeof status.detail,
1078              "invalid intent %d, expects one of {UHDR_HDR_IMG, UHDR_SDR_IMG, UHDR_BASE_IMG, "
1079              "UHDR_GAIN_MAP_IMG}",
1080              intent);
1081   }
1082   if (status.error_code != UHDR_CODEC_OK) return status;
1083 
1084   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
1085   if (handle->m_sailed) {
1086     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1087     status.has_detail = 1;
1088     snprintf(status.detail, sizeof status.detail,
1089              "An earlier call to uhdr_encode() has switched the context from configurable state to "
1090              "end state. The context is no longer configurable. To reuse, call reset()");
1091     return status;
1092   }
1093 
1094   handle->m_quality.insert_or_assign(intent, quality);
1095 
1096   return status;
1097 }
1098 
uhdr_enc_set_exif_data(uhdr_codec_private_t * enc,uhdr_mem_block_t * exif)1099 uhdr_error_info_t uhdr_enc_set_exif_data(uhdr_codec_private_t* enc, uhdr_mem_block_t* exif) {
1100   uhdr_error_info_t status = g_no_error;
1101 
1102   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
1103     status.error_code = UHDR_CODEC_INVALID_PARAM;
1104     status.has_detail = 1;
1105     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1106   } else if (exif == nullptr) {
1107     status.error_code = UHDR_CODEC_INVALID_PARAM;
1108     status.has_detail = 1;
1109     snprintf(status.detail, sizeof status.detail, "received nullptr for exif image handle");
1110   } else if (exif->data == nullptr) {
1111     status.error_code = UHDR_CODEC_INVALID_PARAM;
1112     status.has_detail = 1;
1113     snprintf(status.detail, sizeof status.detail, "received nullptr for exif->data field");
1114   } else if (exif->capacity < exif->data_sz) {
1115     status.error_code = UHDR_CODEC_INVALID_PARAM;
1116     status.has_detail = 1;
1117     snprintf(status.detail, sizeof status.detail,
1118              "exif->capacity %zd is less than exif->data_sz %zd", exif->capacity, exif->data_sz);
1119   }
1120   if (status.error_code != UHDR_CODEC_OK) return status;
1121 
1122   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
1123   if (handle->m_sailed) {
1124     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1125     status.has_detail = 1;
1126     snprintf(status.detail, sizeof status.detail,
1127              "An earlier call to uhdr_encode() has switched the context from configurable state to "
1128              "end state. The context is no longer configurable. To reuse, call reset()");
1129     return status;
1130   }
1131 
1132   uint8_t* data = static_cast<uint8_t*>(exif->data);
1133   std::vector<uint8_t> entry(data, data + exif->data_sz);
1134   handle->m_exif = std::move(entry);
1135 
1136   return status;
1137 }
1138 
uhdr_enc_set_output_format(uhdr_codec_private_t * enc,uhdr_codec_t media_type)1139 uhdr_error_info_t uhdr_enc_set_output_format(uhdr_codec_private_t* enc, uhdr_codec_t media_type) {
1140   uhdr_error_info_t status = g_no_error;
1141 
1142   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
1143     status.error_code = UHDR_CODEC_INVALID_PARAM;
1144     status.has_detail = 1;
1145     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1146   } else if (media_type != UHDR_CODEC_JPG) {
1147     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
1148     status.has_detail = 1;
1149     snprintf(status.detail, sizeof status.detail,
1150              "invalid output format %d, expects {UHDR_CODEC_JPG}", media_type);
1151   }
1152   if (status.error_code != UHDR_CODEC_OK) return status;
1153 
1154   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
1155   if (handle->m_sailed) {
1156     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1157     status.has_detail = 1;
1158     snprintf(status.detail, sizeof status.detail,
1159              "An earlier call to uhdr_encode() has switched the context from configurable state to "
1160              "end state. The context is no longer configurable. To reuse, call reset()");
1161     return status;
1162   }
1163 
1164   handle->m_output_format = media_type;
1165 
1166   return status;
1167 }
1168 
uhdr_encode(uhdr_codec_private_t * enc)1169 uhdr_error_info_t uhdr_encode(uhdr_codec_private_t* enc) {
1170   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
1171     uhdr_error_info_t status;
1172     status.error_code = UHDR_CODEC_INVALID_PARAM;
1173     status.has_detail = 1;
1174     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1175     return status;
1176   }
1177 
1178   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
1179 
1180   if (handle->m_sailed) {
1181     return handle->m_encode_call_status;
1182   }
1183 
1184   handle->m_sailed = true;
1185 
1186   uhdr_error_info_t& status = handle->m_encode_call_status;
1187 
1188   if (handle->m_compressed_images.find(UHDR_BASE_IMG) != handle->m_compressed_images.end() &&
1189       handle->m_compressed_images.find(UHDR_GAIN_MAP_IMG) != handle->m_compressed_images.end()) {
1190     if (handle->m_effects.size() != 0) {
1191       status.error_code = UHDR_CODEC_INVALID_OPERATION;
1192       status.has_detail = 1;
1193       snprintf(status.detail, sizeof status.detail,
1194                "image effects are not enabled for inputs with compressed intent");
1195       return status;
1196     }
1197   } else if (handle->m_raw_images.find(UHDR_HDR_IMG) != handle->m_raw_images.end()) {
1198     if (handle->m_compressed_images.find(UHDR_SDR_IMG) == handle->m_compressed_images.end() &&
1199         handle->m_raw_images.find(UHDR_SDR_IMG) == handle->m_raw_images.end()) {
1200       // api - 0
1201       if (handle->m_effects.size() != 0) {
1202         status = ultrahdr::apply_effects(handle);
1203         if (status.error_code != UHDR_CODEC_OK) return status;
1204       }
1205     } else if (handle->m_compressed_images.find(UHDR_SDR_IMG) !=
1206                    handle->m_compressed_images.end() &&
1207                handle->m_raw_images.find(UHDR_SDR_IMG) == handle->m_raw_images.end()) {
1208       if (handle->m_effects.size() != 0) {
1209         status.error_code = UHDR_CODEC_INVALID_OPERATION;
1210         status.has_detail = 1;
1211         snprintf(status.detail, sizeof status.detail,
1212                  "image effects are not enabled for inputs with compressed intent");
1213         return status;
1214       }
1215     } else if (handle->m_raw_images.find(UHDR_SDR_IMG) != handle->m_raw_images.end()) {
1216       if (handle->m_compressed_images.find(UHDR_SDR_IMG) == handle->m_compressed_images.end()) {
1217         if (handle->m_effects.size() != 0) {
1218           status = ultrahdr::apply_effects(handle);
1219           if (status.error_code != UHDR_CODEC_OK) return status;
1220         }
1221       } else {
1222         if (handle->m_effects.size() != 0) {
1223           status.error_code = UHDR_CODEC_INVALID_OPERATION;
1224           status.has_detail = 1;
1225           snprintf(status.detail, sizeof status.detail,
1226                    "image effects are not enabled for inputs with compressed intent");
1227           return status;
1228         }
1229       }
1230     }
1231   }
1232 
1233   if (handle->m_output_format == UHDR_CODEC_JPG) {
1234     uhdr_mem_block_t exif{};
1235     if (handle->m_exif.size() > 0) {
1236       exif.data = handle->m_exif.data();
1237       exif.capacity = exif.data_sz = handle->m_exif.size();
1238     }
1239 
1240     ultrahdr::JpegR jpegr(nullptr, handle->m_gainmap_scale_factor,
1241                           handle->m_quality.find(UHDR_GAIN_MAP_IMG)->second,
1242                           handle->m_use_multi_channel_gainmap, handle->m_gamma,
1243                           handle->m_enc_preset, handle->m_min_content_boost,
1244                           handle->m_max_content_boost, handle->m_target_disp_max_brightness);
1245     if (handle->m_compressed_images.find(UHDR_BASE_IMG) != handle->m_compressed_images.end() &&
1246         handle->m_compressed_images.find(UHDR_GAIN_MAP_IMG) != handle->m_compressed_images.end()) {
1247       auto& base_entry = handle->m_compressed_images.find(UHDR_BASE_IMG)->second;
1248       auto& gainmap_entry = handle->m_compressed_images.find(UHDR_GAIN_MAP_IMG)->second;
1249 
1250       size_t size =
1251           (std::max)(((size_t)8 * 1024), 2 * (base_entry->data_sz + gainmap_entry->data_sz));
1252       handle->m_compressed_output_buffer = std::make_unique<ultrahdr::uhdr_compressed_image_ext_t>(
1253           UHDR_CG_UNSPECIFIED, UHDR_CT_UNSPECIFIED, UHDR_CR_UNSPECIFIED, size);
1254 
1255       ultrahdr::uhdr_gainmap_metadata_ext_t metadata(handle->m_metadata, ultrahdr::kJpegrVersion);
1256 
1257       // api - 4
1258       status = jpegr.encodeJPEGR(base_entry.get(), gainmap_entry.get(), &metadata,
1259                                  handle->m_compressed_output_buffer.get());
1260     } else if (handle->m_raw_images.find(UHDR_HDR_IMG) != handle->m_raw_images.end()) {
1261       auto& hdr_raw_entry = handle->m_raw_images.find(UHDR_HDR_IMG)->second;
1262 
1263       size_t size = (std::max)((8u * 1024), hdr_raw_entry->w * hdr_raw_entry->h * 3 * 2);
1264       handle->m_compressed_output_buffer = std::make_unique<ultrahdr::uhdr_compressed_image_ext_t>(
1265           UHDR_CG_UNSPECIFIED, UHDR_CT_UNSPECIFIED, UHDR_CR_UNSPECIFIED, size);
1266 
1267       if (handle->m_compressed_images.find(UHDR_SDR_IMG) == handle->m_compressed_images.end() &&
1268           handle->m_raw_images.find(UHDR_SDR_IMG) == handle->m_raw_images.end()) {
1269         // api - 0
1270         status = jpegr.encodeJPEGR(hdr_raw_entry.get(), handle->m_compressed_output_buffer.get(),
1271                                    handle->m_quality.find(UHDR_BASE_IMG)->second,
1272                                    handle->m_exif.size() > 0 ? &exif : nullptr);
1273       } else if (handle->m_compressed_images.find(UHDR_SDR_IMG) !=
1274                      handle->m_compressed_images.end() &&
1275                  handle->m_raw_images.find(UHDR_SDR_IMG) == handle->m_raw_images.end()) {
1276         auto& sdr_compressed_entry = handle->m_compressed_images.find(UHDR_SDR_IMG)->second;
1277         // api - 3
1278         status = jpegr.encodeJPEGR(hdr_raw_entry.get(), sdr_compressed_entry.get(),
1279                                    handle->m_compressed_output_buffer.get());
1280       } else if (handle->m_raw_images.find(UHDR_SDR_IMG) != handle->m_raw_images.end()) {
1281         auto& sdr_raw_entry = handle->m_raw_images.find(UHDR_SDR_IMG)->second;
1282 
1283         if (handle->m_compressed_images.find(UHDR_SDR_IMG) == handle->m_compressed_images.end()) {
1284           // api - 1
1285           status = jpegr.encodeJPEGR(hdr_raw_entry.get(), sdr_raw_entry.get(),
1286                                      handle->m_compressed_output_buffer.get(),
1287                                      handle->m_quality.find(UHDR_BASE_IMG)->second,
1288                                      handle->m_exif.size() > 0 ? &exif : nullptr);
1289         } else {
1290           auto& sdr_compressed_entry = handle->m_compressed_images.find(UHDR_SDR_IMG)->second;
1291           // api - 2
1292           status = jpegr.encodeJPEGR(hdr_raw_entry.get(), sdr_raw_entry.get(),
1293                                      sdr_compressed_entry.get(),
1294                                      handle->m_compressed_output_buffer.get());
1295         }
1296       }
1297     } else {
1298       status.error_code = UHDR_CODEC_INVALID_OPERATION;
1299       status.has_detail = 1;
1300       snprintf(status.detail, sizeof status.detail,
1301                "resources required for uhdr_encode() operation are not present");
1302     }
1303   }
1304 
1305   return status;
1306 }
1307 
uhdr_get_encoded_stream(uhdr_codec_private_t * enc)1308 uhdr_compressed_image_t* uhdr_get_encoded_stream(uhdr_codec_private_t* enc) {
1309   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
1310     return nullptr;
1311   }
1312 
1313   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
1314   if (!handle->m_sailed || handle->m_encode_call_status.error_code != UHDR_CODEC_OK) {
1315     return nullptr;
1316   }
1317 
1318   return handle->m_compressed_output_buffer.get();
1319 }
1320 
uhdr_reset_encoder(uhdr_codec_private_t * enc)1321 void uhdr_reset_encoder(uhdr_codec_private_t* enc) {
1322   if (dynamic_cast<uhdr_encoder_private*>(enc) != nullptr) {
1323     uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
1324 
1325     // clear entries and restore defaults
1326     for (auto it : handle->m_effects) delete it;
1327     handle->m_effects.clear();
1328 #ifdef UHDR_ENABLE_GLES
1329     handle->m_uhdr_gl_ctxt.reset_opengl_ctxt();
1330     handle->m_enable_gles = false;
1331 #endif
1332     handle->m_sailed = false;
1333     handle->m_raw_images.clear();
1334     handle->m_compressed_images.clear();
1335     handle->m_quality.clear();
1336     handle->m_quality.emplace(UHDR_HDR_IMG, ultrahdr::kBaseCompressQualityDefault);
1337     handle->m_quality.emplace(UHDR_SDR_IMG, ultrahdr::kBaseCompressQualityDefault);
1338     handle->m_quality.emplace(UHDR_BASE_IMG, ultrahdr::kBaseCompressQualityDefault);
1339     handle->m_quality.emplace(UHDR_GAIN_MAP_IMG, ultrahdr::kMapCompressQualityDefault);
1340     handle->m_exif.clear();
1341     handle->m_output_format = UHDR_CODEC_JPG;
1342     handle->m_gainmap_scale_factor = ultrahdr::kMapDimensionScaleFactorDefault;
1343     handle->m_use_multi_channel_gainmap = ultrahdr::kUseMultiChannelGainMapDefault;
1344     handle->m_gamma = ultrahdr::kGainMapGammaDefault;
1345     handle->m_enc_preset = ultrahdr::kEncSpeedPresetDefault;
1346     handle->m_min_content_boost = FLT_MIN;
1347     handle->m_max_content_boost = FLT_MAX;
1348     handle->m_target_disp_max_brightness = -1.0f;
1349 
1350     handle->m_compressed_output_buffer.reset();
1351     handle->m_encode_call_status = g_no_error;
1352   }
1353 }
1354 
is_uhdr_image(void * data,int size)1355 int is_uhdr_image(void* data, int size) {
1356 #define RET_IF_ERR(x)                         \
1357   {                                           \
1358     uhdr_error_info_t status = (x);           \
1359     if (status.error_code != UHDR_CODEC_OK) { \
1360       uhdr_release_decoder(obj);              \
1361       return 0;                               \
1362     }                                         \
1363   }
1364 
1365   uhdr_codec_private_t* obj = uhdr_create_decoder();
1366   uhdr_compressed_image_t uhdr_image;
1367   uhdr_image.data = data;
1368   uhdr_image.data_sz = size;
1369   uhdr_image.capacity = size;
1370   uhdr_image.cg = UHDR_CG_UNSPECIFIED;
1371   uhdr_image.ct = UHDR_CT_UNSPECIFIED;
1372   uhdr_image.range = UHDR_CR_UNSPECIFIED;
1373 
1374   RET_IF_ERR(uhdr_dec_set_image(obj, &uhdr_image));
1375   RET_IF_ERR(uhdr_dec_probe(obj));
1376 #undef RET_IF_ERR
1377 
1378   uhdr_release_decoder(obj);
1379 
1380   return 1;
1381 }
1382 
uhdr_create_decoder(void)1383 uhdr_codec_private_t* uhdr_create_decoder(void) {
1384   uhdr_decoder_private* handle = new uhdr_decoder_private();
1385 
1386   if (handle != nullptr) {
1387     uhdr_reset_decoder(handle);
1388   }
1389   return handle;
1390 }
1391 
uhdr_release_decoder(uhdr_codec_private_t * dec)1392 void uhdr_release_decoder(uhdr_codec_private_t* dec) {
1393   if (dynamic_cast<uhdr_decoder_private*>(dec) != nullptr) {
1394     uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1395     delete handle;
1396   }
1397 }
1398 
uhdr_dec_set_image(uhdr_codec_private_t * dec,uhdr_compressed_image_t * img)1399 uhdr_error_info_t uhdr_dec_set_image(uhdr_codec_private_t* dec, uhdr_compressed_image_t* img) {
1400   uhdr_error_info_t status = g_no_error;
1401 
1402   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1403     status.error_code = UHDR_CODEC_INVALID_PARAM;
1404     status.has_detail = 1;
1405     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1406   } else if (img == nullptr) {
1407     status.error_code = UHDR_CODEC_INVALID_PARAM;
1408     status.has_detail = 1;
1409     snprintf(status.detail, sizeof status.detail, "received nullptr for compressed image handle");
1410   } else if (img->data == nullptr) {
1411     status.error_code = UHDR_CODEC_INVALID_PARAM;
1412     status.has_detail = 1;
1413     snprintf(status.detail, sizeof status.detail,
1414              "received nullptr for compressed img->data field");
1415   } else if (img->capacity < img->data_sz) {
1416     status.error_code = UHDR_CODEC_INVALID_PARAM;
1417     status.has_detail = 1;
1418     snprintf(status.detail, sizeof status.detail, "img->capacity %zd is less than img->data_sz %zd",
1419              img->capacity, img->data_sz);
1420   }
1421   if (status.error_code != UHDR_CODEC_OK) return status;
1422 
1423   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1424   if (handle->m_probed) {
1425     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1426     status.has_detail = 1;
1427     snprintf(status.detail, sizeof status.detail,
1428              "An earlier call to uhdr_decode() has switched the context from configurable state to "
1429              "end state. The context is no longer configurable. To reuse, call reset()");
1430     return status;
1431   }
1432 
1433   handle->m_uhdr_compressed_img = std::make_unique<ultrahdr::uhdr_compressed_image_ext_t>(
1434       img->cg, img->ct, img->range, img->data_sz);
1435   memcpy(handle->m_uhdr_compressed_img->data, img->data, img->data_sz);
1436   handle->m_uhdr_compressed_img->data_sz = img->data_sz;
1437 
1438   return status;
1439 }
1440 
uhdr_dec_set_out_img_format(uhdr_codec_private_t * dec,uhdr_img_fmt_t fmt)1441 uhdr_error_info_t uhdr_dec_set_out_img_format(uhdr_codec_private_t* dec, uhdr_img_fmt_t fmt) {
1442   uhdr_error_info_t status = g_no_error;
1443 
1444   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1445     status.error_code = UHDR_CODEC_INVALID_PARAM;
1446     status.has_detail = 1;
1447     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1448   } else if (fmt != UHDR_IMG_FMT_32bppRGBA8888 && fmt != UHDR_IMG_FMT_64bppRGBAHalfFloat &&
1449              fmt != UHDR_IMG_FMT_32bppRGBA1010102) {
1450     status.error_code = UHDR_CODEC_INVALID_PARAM;
1451     status.has_detail = 1;
1452     snprintf(status.detail, sizeof status.detail,
1453              "invalid output format %d, expects one of {UHDR_IMG_FMT_32bppRGBA8888,  "
1454              "UHDR_IMG_FMT_64bppRGBAHalfFloat, UHDR_IMG_FMT_32bppRGBA1010102}",
1455              fmt);
1456   }
1457   if (status.error_code != UHDR_CODEC_OK) return status;
1458 
1459   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1460   if (handle->m_probed) {
1461     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1462     status.has_detail = 1;
1463     snprintf(status.detail, sizeof status.detail,
1464              "An earlier call to uhdr_decode() has switched the context from configurable state to "
1465              "end state. The context is no longer configurable. To reuse, call reset()");
1466     return status;
1467   }
1468 
1469   handle->m_output_fmt = fmt;
1470 
1471   return status;
1472 }
1473 
uhdr_dec_set_out_color_transfer(uhdr_codec_private_t * dec,uhdr_color_transfer_t ct)1474 uhdr_error_info_t uhdr_dec_set_out_color_transfer(uhdr_codec_private_t* dec,
1475                                                   uhdr_color_transfer_t ct) {
1476   uhdr_error_info_t status = g_no_error;
1477 
1478   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1479     status.error_code = UHDR_CODEC_INVALID_PARAM;
1480     status.has_detail = 1;
1481     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1482   } else if (ct != UHDR_CT_HLG && ct != UHDR_CT_PQ && ct != UHDR_CT_LINEAR && ct != UHDR_CT_SRGB) {
1483     status.error_code = UHDR_CODEC_INVALID_PARAM;
1484     status.has_detail = 1;
1485     snprintf(status.detail, sizeof status.detail,
1486              "invalid output color transfer %d, expects one of {UHDR_CT_HLG, UHDR_CT_PQ, "
1487              "UHDR_CT_LINEAR, UHDR_CT_SRGB}",
1488              ct);
1489   }
1490   if (status.error_code != UHDR_CODEC_OK) return status;
1491 
1492   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1493   if (handle->m_probed) {
1494     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1495     status.has_detail = 1;
1496     snprintf(status.detail, sizeof status.detail,
1497              "An earlier call to uhdr_decode() has switched the context from configurable state to "
1498              "end state. The context is no longer configurable. To reuse, call reset()");
1499     return status;
1500   }
1501 
1502   handle->m_output_ct = ct;
1503 
1504   return status;
1505 }
1506 
uhdr_dec_set_out_max_display_boost(uhdr_codec_private_t * dec,float display_boost)1507 uhdr_error_info_t uhdr_dec_set_out_max_display_boost(uhdr_codec_private_t* dec,
1508                                                      float display_boost) {
1509   uhdr_error_info_t status = g_no_error;
1510 
1511   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1512     status.error_code = UHDR_CODEC_INVALID_PARAM;
1513     status.has_detail = 1;
1514     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1515   } else if (!std::isfinite(display_boost) || display_boost < 1.0f) {
1516     status.error_code = UHDR_CODEC_INVALID_PARAM;
1517     status.has_detail = 1;
1518     snprintf(status.detail, sizeof status.detail,
1519              "invalid display boost %f, expects to be >= 1.0f}", display_boost);
1520   }
1521   if (status.error_code != UHDR_CODEC_OK) return status;
1522 
1523   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1524   if (handle->m_probed) {
1525     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1526     status.has_detail = 1;
1527     snprintf(status.detail, sizeof status.detail,
1528              "An earlier call to uhdr_decode() has switched the context from configurable state to "
1529              "end state. The context is no longer configurable. To reuse, call reset()");
1530     return status;
1531   }
1532 
1533   handle->m_output_max_disp_boost = display_boost;
1534 
1535   return status;
1536 }
1537 
uhdr_dec_probe(uhdr_codec_private_t * dec)1538 uhdr_error_info_t uhdr_dec_probe(uhdr_codec_private_t* dec) {
1539   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1540     uhdr_error_info_t status;
1541     status.error_code = UHDR_CODEC_INVALID_PARAM;
1542     status.has_detail = 1;
1543     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1544     return status;
1545   }
1546 
1547   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1548   uhdr_error_info_t& status = handle->m_probe_call_status;
1549 
1550   if (!handle->m_probed) {
1551     handle->m_probed = true;
1552 
1553     if (handle->m_uhdr_compressed_img.get() == nullptr) {
1554       status.error_code = UHDR_CODEC_INVALID_OPERATION;
1555       status.has_detail = 1;
1556       snprintf(status.detail, sizeof status.detail, "did not receive any image for decoding");
1557       return status;
1558     }
1559 
1560     ultrahdr::jpeg_info_struct primary_image;
1561     ultrahdr::jpeg_info_struct gainmap_image;
1562     ultrahdr::jpegr_info_struct jpegr_info;
1563     jpegr_info.primaryImgInfo = &primary_image;
1564     jpegr_info.gainmapImgInfo = &gainmap_image;
1565 
1566     ultrahdr::JpegR jpegr;
1567     status = jpegr.getJPEGRInfo(handle->m_uhdr_compressed_img.get(), &jpegr_info);
1568     if (status.error_code != UHDR_CODEC_OK) return status;
1569 
1570     ultrahdr::uhdr_gainmap_metadata_ext_t metadata;
1571     status = jpegr.parseGainMapMetadata(gainmap_image.isoData.data(), gainmap_image.isoData.size(),
1572                                         gainmap_image.xmpData.data(), gainmap_image.xmpData.size(),
1573                                         &metadata);
1574     if (status.error_code != UHDR_CODEC_OK) return status;
1575     handle->m_metadata.max_content_boost = metadata.max_content_boost;
1576     handle->m_metadata.min_content_boost = metadata.min_content_boost;
1577     handle->m_metadata.gamma = metadata.gamma;
1578     handle->m_metadata.offset_sdr = metadata.offset_sdr;
1579     handle->m_metadata.offset_hdr = metadata.offset_hdr;
1580     handle->m_metadata.hdr_capacity_min = metadata.hdr_capacity_min;
1581     handle->m_metadata.hdr_capacity_max = metadata.hdr_capacity_max;
1582 
1583     handle->m_img_wd = primary_image.width;
1584     handle->m_img_ht = primary_image.height;
1585     handle->m_gainmap_wd = gainmap_image.width;
1586     handle->m_gainmap_ht = gainmap_image.height;
1587     handle->m_gainmap_num_comp = gainmap_image.numComponents;
1588     handle->m_exif = std::move(primary_image.exifData);
1589     handle->m_exif_block.data = handle->m_exif.data();
1590     handle->m_exif_block.data_sz = handle->m_exif_block.capacity = handle->m_exif.size();
1591     handle->m_icc = std::move(primary_image.iccData);
1592     handle->m_icc_block.data = handle->m_icc.data();
1593     handle->m_icc_block.data_sz = handle->m_icc_block.capacity = handle->m_icc.size();
1594     handle->m_base_img = std::move(primary_image.imgData);
1595     handle->m_base_img_block.data = handle->m_base_img.data();
1596     handle->m_base_img_block.data_sz = handle->m_base_img_block.capacity =
1597         handle->m_base_img.size();
1598     handle->m_gainmap_img = std::move(gainmap_image.imgData);
1599     handle->m_gainmap_img_block.data = handle->m_gainmap_img.data();
1600     handle->m_gainmap_img_block.data_sz = handle->m_gainmap_img_block.capacity =
1601         handle->m_gainmap_img.size();
1602   }
1603 
1604   return status;
1605 }
1606 
uhdr_dec_get_image_width(uhdr_codec_private_t * dec)1607 int uhdr_dec_get_image_width(uhdr_codec_private_t* dec) {
1608   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1609     return -1;
1610   }
1611 
1612   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1613   if (!handle->m_probed || handle->m_probe_call_status.error_code != UHDR_CODEC_OK) {
1614     return -1;
1615   }
1616 
1617   return handle->m_img_wd;
1618 }
1619 
uhdr_dec_get_image_height(uhdr_codec_private_t * dec)1620 int uhdr_dec_get_image_height(uhdr_codec_private_t* dec) {
1621   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1622     return -1;
1623   }
1624 
1625   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1626   if (!handle->m_probed || handle->m_probe_call_status.error_code != UHDR_CODEC_OK) {
1627     return -1;
1628   }
1629 
1630   return handle->m_img_ht;
1631 }
1632 
uhdr_dec_get_gainmap_width(uhdr_codec_private_t * dec)1633 int uhdr_dec_get_gainmap_width(uhdr_codec_private_t* dec) {
1634   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1635     return -1;
1636   }
1637 
1638   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1639   if (!handle->m_probed || handle->m_probe_call_status.error_code != UHDR_CODEC_OK) {
1640     return -1;
1641   }
1642 
1643   return handle->m_gainmap_wd;
1644 }
1645 
uhdr_dec_get_gainmap_height(uhdr_codec_private_t * dec)1646 int uhdr_dec_get_gainmap_height(uhdr_codec_private_t* dec) {
1647   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1648     return -1;
1649   }
1650 
1651   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1652   if (!handle->m_probed || handle->m_probe_call_status.error_code != UHDR_CODEC_OK) {
1653     return -1;
1654   }
1655 
1656   return handle->m_gainmap_ht;
1657 }
1658 
uhdr_dec_get_exif(uhdr_codec_private_t * dec)1659 uhdr_mem_block_t* uhdr_dec_get_exif(uhdr_codec_private_t* dec) {
1660   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1661     return nullptr;
1662   }
1663 
1664   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1665   if (!handle->m_probed || handle->m_probe_call_status.error_code != UHDR_CODEC_OK) {
1666     return nullptr;
1667   }
1668 
1669   return &handle->m_exif_block;
1670 }
1671 
uhdr_dec_get_icc(uhdr_codec_private_t * dec)1672 uhdr_mem_block_t* uhdr_dec_get_icc(uhdr_codec_private_t* dec) {
1673   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1674     return nullptr;
1675   }
1676 
1677   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1678   if (!handle->m_probed || handle->m_probe_call_status.error_code != UHDR_CODEC_OK) {
1679     return nullptr;
1680   }
1681 
1682   return &handle->m_icc_block;
1683 }
1684 
uhdr_dec_get_base_image(uhdr_codec_private_t * dec)1685 uhdr_mem_block_t* uhdr_dec_get_base_image(uhdr_codec_private_t* dec) {
1686   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1687     return nullptr;
1688   }
1689 
1690   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1691   if (!handle->m_probed || handle->m_probe_call_status.error_code != UHDR_CODEC_OK) {
1692     return nullptr;
1693   }
1694 
1695   return &handle->m_base_img_block;
1696 }
1697 
uhdr_dec_get_gainmap_image(uhdr_codec_private_t * dec)1698 uhdr_mem_block_t* uhdr_dec_get_gainmap_image(uhdr_codec_private_t* dec) {
1699   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1700     return nullptr;
1701   }
1702 
1703   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1704   if (!handle->m_probed || handle->m_probe_call_status.error_code != UHDR_CODEC_OK) {
1705     return nullptr;
1706   }
1707 
1708   return &handle->m_gainmap_img_block;
1709 }
1710 
uhdr_dec_get_gainmap_metadata(uhdr_codec_private_t * dec)1711 uhdr_gainmap_metadata_t* uhdr_dec_get_gainmap_metadata(uhdr_codec_private_t* dec) {
1712   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1713     return nullptr;
1714   }
1715 
1716   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1717   if (!handle->m_probed || handle->m_probe_call_status.error_code != UHDR_CODEC_OK) {
1718     return nullptr;
1719   }
1720 
1721   return &handle->m_metadata;
1722 }
1723 
uhdr_decode(uhdr_codec_private_t * dec)1724 uhdr_error_info_t uhdr_decode(uhdr_codec_private_t* dec) {
1725   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1726     uhdr_error_info_t status;
1727     status.error_code = UHDR_CODEC_INVALID_PARAM;
1728     status.has_detail = 1;
1729     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1730     return status;
1731   }
1732 
1733   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1734 
1735   if (handle->m_sailed) {
1736     return handle->m_decode_call_status;
1737   }
1738 
1739   uhdr_error_info_t& status = handle->m_decode_call_status;
1740   status = uhdr_dec_probe(dec);
1741   if (status.error_code != UHDR_CODEC_OK) return status;
1742 
1743   handle->m_sailed = true;
1744 
1745   if ((handle->m_output_fmt == UHDR_IMG_FMT_32bppRGBA1010102 &&
1746        (handle->m_output_ct != UHDR_CT_HLG && handle->m_output_ct != UHDR_CT_PQ)) ||
1747       (handle->m_output_fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat &&
1748        handle->m_output_ct != UHDR_CT_LINEAR) ||
1749       (handle->m_output_fmt == UHDR_IMG_FMT_32bppRGBA8888 && handle->m_output_ct != UHDR_CT_SRGB)) {
1750     status.error_code = UHDR_CODEC_INVALID_PARAM;
1751     status.has_detail = 1;
1752     snprintf(status.detail, sizeof status.detail,
1753              "unsupported output pixel format and output color transfer pair");
1754     return status;
1755   }
1756 
1757   handle->m_decoded_img_buffer = std::make_unique<ultrahdr::uhdr_raw_image_ext_t>(
1758       handle->m_output_fmt, UHDR_CG_UNSPECIFIED, handle->m_output_ct, UHDR_CR_UNSPECIFIED,
1759       handle->m_img_wd, handle->m_img_ht, 1);
1760 
1761   handle->m_gainmap_img_buffer = std::make_unique<ultrahdr::uhdr_raw_image_ext_t>(
1762       handle->m_gainmap_num_comp == 1 ? UHDR_IMG_FMT_8bppYCbCr400 : UHDR_IMG_FMT_32bppRGBA8888,
1763       UHDR_CG_UNSPECIFIED, UHDR_CT_UNSPECIFIED, UHDR_CR_UNSPECIFIED, handle->m_gainmap_wd,
1764       handle->m_gainmap_ht, 1);
1765 
1766 #ifdef UHDR_ENABLE_GLES
1767   ultrahdr::uhdr_opengl_ctxt_t* uhdrGLESCtxt = nullptr;
1768   if (handle->m_enable_gles &&
1769       (handle->m_output_ct != UHDR_CT_SRGB || handle->m_effects.size() > 0)) {
1770     handle->m_uhdr_gl_ctxt.init_opengl_ctxt();
1771     status = handle->m_uhdr_gl_ctxt.mErrorStatus;
1772     if (status.error_code != UHDR_CODEC_OK) return status;
1773     uhdrGLESCtxt = &handle->m_uhdr_gl_ctxt;
1774   }
1775   ultrahdr::JpegR jpegr(uhdrGLESCtxt);
1776 #else
1777   ultrahdr::JpegR jpegr;
1778 #endif
1779 
1780   status =
1781       jpegr.decodeJPEGR(handle->m_uhdr_compressed_img.get(), handle->m_decoded_img_buffer.get(),
1782                         handle->m_output_max_disp_boost, handle->m_output_ct, handle->m_output_fmt,
1783                         handle->m_gainmap_img_buffer.get(), nullptr);
1784 
1785   if (status.error_code == UHDR_CODEC_OK && dec->m_effects.size() != 0) {
1786     status = ultrahdr::apply_effects(handle);
1787   }
1788 
1789 #ifdef UHDR_ENABLE_GLES
1790   if (handle->m_enable_gles) {
1791     if (handle->m_uhdr_gl_ctxt.mDecodedImgTexture != 0) {
1792       handle->m_uhdr_gl_ctxt.read_texture(
1793           &handle->m_uhdr_gl_ctxt.mDecodedImgTexture, handle->m_decoded_img_buffer->fmt,
1794           handle->m_decoded_img_buffer->w, handle->m_decoded_img_buffer->h,
1795           handle->m_decoded_img_buffer->planes[0]);
1796     }
1797     if (handle->m_uhdr_gl_ctxt.mGainmapImgTexture != 0 && dec->m_effects.size() != 0) {
1798       handle->m_uhdr_gl_ctxt.read_texture(
1799           &handle->m_uhdr_gl_ctxt.mGainmapImgTexture, handle->m_gainmap_img_buffer->fmt,
1800           handle->m_gainmap_img_buffer->w, handle->m_gainmap_img_buffer->h,
1801           handle->m_gainmap_img_buffer->planes[0]);
1802     }
1803   }
1804 #endif
1805   return status;
1806 }
1807 
uhdr_get_decoded_image(uhdr_codec_private_t * dec)1808 uhdr_raw_image_t* uhdr_get_decoded_image(uhdr_codec_private_t* dec) {
1809   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1810     return nullptr;
1811   }
1812 
1813   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1814   if (!handle->m_sailed || handle->m_decode_call_status.error_code != UHDR_CODEC_OK) {
1815     return nullptr;
1816   }
1817 
1818   return handle->m_decoded_img_buffer.get();
1819 }
1820 
uhdr_get_decoded_gainmap_image(uhdr_codec_private_t * dec)1821 uhdr_raw_image_t* uhdr_get_decoded_gainmap_image(uhdr_codec_private_t* dec) {
1822   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1823     return nullptr;
1824   }
1825 
1826   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1827   if (!handle->m_sailed || handle->m_decode_call_status.error_code != UHDR_CODEC_OK) {
1828     return nullptr;
1829   }
1830 
1831   return handle->m_gainmap_img_buffer.get();
1832 }
1833 
uhdr_reset_decoder(uhdr_codec_private_t * dec)1834 void uhdr_reset_decoder(uhdr_codec_private_t* dec) {
1835   if (dynamic_cast<uhdr_decoder_private*>(dec) != nullptr) {
1836     uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1837 
1838     // clear entries and restore defaults
1839     for (auto it : handle->m_effects) delete it;
1840     handle->m_effects.clear();
1841 #ifdef UHDR_ENABLE_GLES
1842     handle->m_uhdr_gl_ctxt.reset_opengl_ctxt();
1843     handle->m_enable_gles = false;
1844 #endif
1845     handle->m_sailed = false;
1846     handle->m_uhdr_compressed_img.reset();
1847     handle->m_output_fmt = UHDR_IMG_FMT_64bppRGBAHalfFloat;
1848     handle->m_output_ct = UHDR_CT_LINEAR;
1849     handle->m_output_max_disp_boost = FLT_MAX;
1850 
1851     // ready to be configured
1852     handle->m_probed = false;
1853     handle->m_decoded_img_buffer.reset();
1854     handle->m_gainmap_img_buffer.reset();
1855     handle->m_img_wd = 0;
1856     handle->m_img_ht = 0;
1857     handle->m_gainmap_wd = 0;
1858     handle->m_gainmap_ht = 0;
1859     handle->m_gainmap_num_comp = 0;
1860     handle->m_exif.clear();
1861     memset(&handle->m_exif_block, 0, sizeof handle->m_exif_block);
1862     handle->m_icc.clear();
1863     memset(&handle->m_icc_block, 0, sizeof handle->m_icc_block);
1864     handle->m_base_img.clear();
1865     memset(&handle->m_base_img_block, 0, sizeof handle->m_base_img_block);
1866     handle->m_gainmap_img.clear();
1867     memset(&handle->m_gainmap_img_block, 0, sizeof handle->m_gainmap_img_block);
1868     memset(&handle->m_metadata, 0, sizeof handle->m_metadata);
1869     handle->m_probe_call_status = g_no_error;
1870     handle->m_decode_call_status = g_no_error;
1871   }
1872 }
1873 
uhdr_enable_gpu_acceleration(uhdr_codec_private_t * codec,int enable)1874 uhdr_error_info_t uhdr_enable_gpu_acceleration(uhdr_codec_private_t* codec,
1875                                                [[maybe_unused]] int enable) {
1876   uhdr_error_info_t status = g_no_error;
1877 
1878   if (codec == nullptr) {
1879     status.error_code = UHDR_CODEC_INVALID_PARAM;
1880     status.has_detail = 1;
1881     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1882     return status;
1883   }
1884 
1885   if (codec->m_sailed) {
1886     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1887     status.has_detail = 1;
1888     snprintf(
1889         status.detail, sizeof status.detail,
1890         "An earlier call to uhdr_encode()/uhdr_decode() has switched the context from configurable "
1891         "state to end state. The context is no longer configurable. To reuse, call reset()");
1892     return status;
1893   }
1894 
1895 #ifdef UHDR_ENABLE_GLES
1896   codec->m_enable_gles = enable;
1897 #endif
1898 
1899   return status;
1900 }
1901 
uhdr_add_effect_mirror(uhdr_codec_private_t * codec,uhdr_mirror_direction_t direction)1902 uhdr_error_info_t uhdr_add_effect_mirror(uhdr_codec_private_t* codec,
1903                                          uhdr_mirror_direction_t direction) {
1904   uhdr_error_info_t status = g_no_error;
1905 
1906   if (codec == nullptr) {
1907     status.error_code = UHDR_CODEC_INVALID_PARAM;
1908     status.has_detail = 1;
1909     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1910     return status;
1911   }
1912 
1913   if (direction != UHDR_MIRROR_HORIZONTAL && direction != UHDR_MIRROR_VERTICAL) {
1914     status.error_code = UHDR_CODEC_INVALID_PARAM;
1915     status.has_detail = 1;
1916     snprintf(
1917         status.detail, sizeof status.detail,
1918         "unsupported direction, expects one of {UHDR_MIRROR_HORIZONTAL, UHDR_MIRROR_VERTICAL}");
1919     return status;
1920   }
1921 
1922   if (codec->m_sailed) {
1923     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1924     status.has_detail = 1;
1925     snprintf(
1926         status.detail, sizeof status.detail,
1927         "An earlier call to uhdr_encode()/uhdr_decode() has switched the context from configurable "
1928         "state to end state. The context is no longer configurable. To reuse, call reset()");
1929     return status;
1930   }
1931 
1932   codec->m_effects.push_back(new ultrahdr::uhdr_mirror_effect_t(direction));
1933 
1934   return status;
1935 }
1936 
uhdr_add_effect_rotate(uhdr_codec_private_t * codec,int degrees)1937 uhdr_error_info_t uhdr_add_effect_rotate(uhdr_codec_private_t* codec, int degrees) {
1938   uhdr_error_info_t status = g_no_error;
1939 
1940   if (codec == nullptr) {
1941     status.error_code = UHDR_CODEC_INVALID_PARAM;
1942     status.has_detail = 1;
1943     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1944     return status;
1945   }
1946 
1947   if (degrees != 90 && degrees != 180 && degrees != 270) {
1948     status.error_code = UHDR_CODEC_INVALID_PARAM;
1949     status.has_detail = 1;
1950     snprintf(status.detail, sizeof status.detail,
1951              "unsupported degrees, expects one of {90, 180, 270}");
1952     return status;
1953   }
1954 
1955   if (codec->m_sailed) {
1956     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1957     status.has_detail = 1;
1958     snprintf(
1959         status.detail, sizeof status.detail,
1960         "An earlier call to uhdr_encode()/uhdr_decode() has switched the context from configurable "
1961         "state to end state. The context is no longer configurable. To reuse, call reset()");
1962     return status;
1963   }
1964 
1965   codec->m_effects.push_back(new ultrahdr::uhdr_rotate_effect_t(degrees));
1966 
1967   return status;
1968 }
1969 
uhdr_add_effect_crop(uhdr_codec_private_t * codec,int left,int right,int top,int bottom)1970 uhdr_error_info_t uhdr_add_effect_crop(uhdr_codec_private_t* codec, int left, int right, int top,
1971                                        int bottom) {
1972   uhdr_error_info_t status = g_no_error;
1973 
1974   if (codec == nullptr) {
1975     status.error_code = UHDR_CODEC_INVALID_PARAM;
1976     status.has_detail = 1;
1977     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1978     return status;
1979   }
1980 
1981   if (codec->m_sailed) {
1982     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1983     status.has_detail = 1;
1984     snprintf(
1985         status.detail, sizeof status.detail,
1986         "An earlier call to uhdr_encode()/uhdr_decode() has switched the context from configurable "
1987         "state to end state. The context is no longer configurable. To reuse, call reset()");
1988     return status;
1989   }
1990 
1991   codec->m_effects.push_back(new ultrahdr::uhdr_crop_effect_t(left, right, top, bottom));
1992 
1993   return status;
1994 }
1995 
uhdr_add_effect_resize(uhdr_codec_private_t * codec,int width,int height)1996 uhdr_error_info_t uhdr_add_effect_resize(uhdr_codec_private_t* codec, int width, int height) {
1997   uhdr_error_info_t status = g_no_error;
1998 
1999   if (codec == nullptr) {
2000     status.error_code = UHDR_CODEC_INVALID_PARAM;
2001     status.has_detail = 1;
2002     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
2003     return status;
2004   }
2005 
2006   if (codec->m_sailed) {
2007     status.error_code = UHDR_CODEC_INVALID_OPERATION;
2008     status.has_detail = 1;
2009     snprintf(
2010         status.detail, sizeof status.detail,
2011         "An earlier call to uhdr_encode()/uhdr_decode() has switched the context from configurable "
2012         "state to end state. The context is no longer configurable. To reuse, call reset()");
2013     return status;
2014   }
2015 
2016   codec->m_effects.push_back(new ultrahdr::uhdr_resize_effect_t(width, height));
2017 
2018   return status;
2019 }
2020