• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <cstdio>
18 #include <cstring>
19 
20 #include "ultrahdr_api.h"
21 #include "ultrahdr/ultrahdrcommon.h"
22 #include "ultrahdr/gainmapmath.h"
23 #include "ultrahdr/editorhelper.h"
24 #include "ultrahdr/jpegr.h"
25 #include "ultrahdr/jpegrutils.h"
26 
27 #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 {
432     for (int i = 0; i < 3; i++) {
433       if (!std::isfinite(metadata->min_content_boost[i]) ||
434           !std::isfinite(metadata->max_content_boost[i]) ||
435           !std::isfinite(metadata->offset_sdr[i]) || !std::isfinite(metadata->offset_hdr[i]) ||
436           !std::isfinite(metadata->hdr_capacity_min) ||
437           !std::isfinite(metadata->hdr_capacity_max) || !std::isfinite(metadata->gamma[i])) {
438         status.error_code = UHDR_CODEC_INVALID_PARAM;
439         status.has_detail = 1;
440         snprintf(
441             status.detail, sizeof status.detail,
442             "Field(s) of gainmap metadata descriptor are either NaN or infinite. min content "
443             "boost %f, max content boost %f, offset sdr %f, offset hdr %f, hdr capacity min %f, "
444             "hdr capacity max %f, gamma %f",
445             metadata->min_content_boost[i], metadata->max_content_boost[i], metadata->offset_sdr[i],
446             metadata->offset_hdr[i], metadata->hdr_capacity_min, metadata->hdr_capacity_max,
447             metadata->gamma[i]);
448       } else if (metadata->max_content_boost[i] < metadata->min_content_boost[i]) {
449         status.error_code = UHDR_CODEC_INVALID_PARAM;
450         status.has_detail = 1;
451         snprintf(
452             status.detail, sizeof status.detail,
453             "received bad value for content boost max %f, expects to be >= content boost min %f",
454             metadata->max_content_boost[i], metadata->min_content_boost[i]);
455       } else if (metadata->min_content_boost[i] <= 0.0f) {
456         status.error_code = UHDR_CODEC_INVALID_PARAM;
457         status.has_detail = 1;
458         snprintf(status.detail, sizeof status.detail,
459                  "received bad value for min boost %f, expects > 0.0f",
460                  metadata->min_content_boost[i]);
461         return status;
462       } else if (metadata->gamma[i] <= 0.0f) {
463         status.error_code = UHDR_CODEC_INVALID_PARAM;
464         status.has_detail = 1;
465         snprintf(status.detail, sizeof status.detail,
466                  "received bad value for gamma %f, expects > 0.0f", metadata->gamma[i]);
467       } else if (metadata->offset_sdr[i] < 0.0f) {
468         status.error_code = UHDR_CODEC_INVALID_PARAM;
469         status.has_detail = 1;
470         snprintf(status.detail, sizeof status.detail,
471                  "received bad value for offset sdr %f, expects to be >= 0.0f",
472                  metadata->offset_sdr[i]);
473       } else if (metadata->offset_hdr[i] < 0.0f) {
474         status.error_code = UHDR_CODEC_INVALID_PARAM;
475         status.has_detail = 1;
476         snprintf(status.detail, sizeof status.detail,
477                  "received bad value for offset hdr %f, expects to be >= 0.0f",
478                  metadata->offset_hdr[i]);
479       } else if (metadata->hdr_capacity_max <= metadata->hdr_capacity_min) {
480         status.error_code = UHDR_CODEC_INVALID_PARAM;
481         status.has_detail = 1;
482         snprintf(status.detail, sizeof status.detail,
483                  "received bad value for hdr capacity max %f, expects to be > hdr capacity min %f",
484                  metadata->hdr_capacity_max, metadata->hdr_capacity_min);
485       } else if (metadata->hdr_capacity_min < 1.0f) {
486         status.error_code = UHDR_CODEC_INVALID_PARAM;
487         status.has_detail = 1;
488         snprintf(status.detail, sizeof status.detail,
489                  "received bad value for hdr capacity min %f, expects to be >= 1.0f",
490                  metadata->hdr_capacity_min);
491       }
492     }
493   }
494   return status;
495 }
496 
497 }  // namespace ultrahdr
498 
~uhdr_codec_private()499 uhdr_codec_private::~uhdr_codec_private() {
500   for (auto it : m_effects) delete it;
501   m_effects.clear();
502 }
503 
uhdr_enc_validate_and_set_compressed_img(uhdr_codec_private_t * enc,uhdr_compressed_image_t * img,uhdr_img_label_t intent)504 uhdr_error_info_t uhdr_enc_validate_and_set_compressed_img(uhdr_codec_private_t* enc,
505                                                            uhdr_compressed_image_t* img,
506                                                            uhdr_img_label_t intent) {
507   uhdr_error_info_t status = g_no_error;
508 
509   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
510     status.error_code = UHDR_CODEC_INVALID_PARAM;
511     status.has_detail = 1;
512     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
513   } else if (img == nullptr) {
514     status.error_code = UHDR_CODEC_INVALID_PARAM;
515     status.has_detail = 1;
516     snprintf(status.detail, sizeof status.detail, "received nullptr for compressed image handle");
517   } else if (img->data == nullptr) {
518     status.error_code = UHDR_CODEC_INVALID_PARAM;
519     status.has_detail = 1;
520     snprintf(status.detail, sizeof status.detail,
521              "received nullptr for compressed img->data field");
522   } else if (img->capacity < img->data_sz) {
523     status.error_code = UHDR_CODEC_INVALID_PARAM;
524     status.has_detail = 1;
525     snprintf(status.detail, sizeof status.detail, "img->capacity %zd is less than img->data_sz %zd",
526              img->capacity, img->data_sz);
527   }
528   if (status.error_code != UHDR_CODEC_OK) return status;
529 
530   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
531   if (handle->m_sailed) {
532     status.error_code = UHDR_CODEC_INVALID_OPERATION;
533     status.has_detail = 1;
534     snprintf(status.detail, sizeof status.detail,
535              "An earlier call to uhdr_encode() has switched the context from configurable state to "
536              "end state. The context is no longer configurable. To reuse, call reset()");
537     return status;
538   }
539 
540   std::shared_ptr<DataSegment> seg =
541       DataSegment::Create(DataRange(0, img->data_sz), static_cast<const uint8_t*>(img->data),
542                           DataSegment::BufferDispositionPolicy::kDontDelete);
543   DataSegmentDataSource data_source(seg);
544   JpegInfoBuilder jpeg_info_builder;
545   JpegScanner jpeg_scanner(nullptr);
546   jpeg_scanner.Run(&data_source, &jpeg_info_builder);
547   data_source.Reset();
548   if (jpeg_scanner.HasError()) {
549     status.error_code = UHDR_CODEC_INVALID_PARAM;
550     snprintf(status.detail, sizeof status.detail,
551              "received bad/corrupted jpeg image as part of input configuration");
552     return status;
553   }
554 
555   const auto& image_ranges = jpeg_info_builder.GetInfo().GetImageRanges();
556   if (image_ranges.empty()) {
557     status.error_code = UHDR_CODEC_INVALID_PARAM;
558     status.has_detail = 1;
559     snprintf(status.detail, sizeof status.detail,
560              "compressed image received as part of input config contains no valid jpeg images");
561     return status;
562   }
563 
564   if (image_ranges.size() > 1) {
565     ALOGW(
566         "compressed image received as part of input config contains multiple jpeg images, "
567         "selecting first image for intent %d, rest are ignored",
568         intent);
569   }
570 
571   auto entry = std::make_unique<ultrahdr::uhdr_compressed_image_ext_t>(img->cg, img->ct, img->range,
572                                                                        image_ranges[0].GetLength());
573   memcpy(entry->data, static_cast<uint8_t*>(img->data) + image_ranges[0].GetBegin(),
574          image_ranges[0].GetLength());
575   entry->data_sz = image_ranges[0].GetLength();
576   handle->m_compressed_images.insert_or_assign(intent, std::move(entry));
577 
578   return status;
579 }
580 
uhdr_create_encoder(void)581 uhdr_codec_private_t* uhdr_create_encoder(void) {
582   uhdr_encoder_private* handle = new uhdr_encoder_private();
583 
584   if (handle != nullptr) {
585     uhdr_reset_encoder(handle);
586   }
587   return handle;
588 }
589 
uhdr_release_encoder(uhdr_codec_private_t * enc)590 void uhdr_release_encoder(uhdr_codec_private_t* enc) {
591   if (dynamic_cast<uhdr_encoder_private*>(enc) != nullptr) {
592     uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
593     delete handle;
594   }
595 }
596 
597 UHDR_EXTERN uhdr_error_info_t
uhdr_enc_set_using_multi_channel_gainmap(uhdr_codec_private_t * enc,int use_multi_channel_gainmap)598 uhdr_enc_set_using_multi_channel_gainmap(uhdr_codec_private_t* enc, int use_multi_channel_gainmap) {
599   uhdr_error_info_t status = g_no_error;
600 
601   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
602     status.error_code = UHDR_CODEC_INVALID_PARAM;
603     status.has_detail = 1;
604     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
605     return status;
606   }
607 
608   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
609 
610   if (handle->m_sailed) {
611     status.error_code = UHDR_CODEC_INVALID_OPERATION;
612     status.has_detail = 1;
613     snprintf(status.detail, sizeof status.detail,
614              "An earlier call to uhdr_encode() has switched the context from configurable state to "
615              "end state. The context is no longer configurable. To reuse, call reset()");
616     return status;
617   }
618 
619   handle->m_use_multi_channel_gainmap = use_multi_channel_gainmap;
620 
621   return status;
622 }
623 
uhdr_enc_set_gainmap_scale_factor(uhdr_codec_private_t * enc,int gainmap_scale_factor)624 UHDR_EXTERN uhdr_error_info_t uhdr_enc_set_gainmap_scale_factor(uhdr_codec_private_t* enc,
625                                                                 int gainmap_scale_factor) {
626   uhdr_error_info_t status = g_no_error;
627 
628   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
629     status.error_code = UHDR_CODEC_INVALID_PARAM;
630     status.has_detail = 1;
631     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
632     return status;
633   }
634 
635   if (gainmap_scale_factor <= 0 || gainmap_scale_factor > 128) {
636     status.error_code = UHDR_CODEC_INVALID_PARAM;
637     status.has_detail = 1;
638     snprintf(status.detail, sizeof status.detail,
639              "gainmap scale factor is expected to be in range (0, 128], received %d",
640              gainmap_scale_factor);
641     return status;
642   }
643 
644   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
645 
646   if (handle->m_sailed) {
647     status.error_code = UHDR_CODEC_INVALID_OPERATION;
648     status.has_detail = 1;
649     snprintf(status.detail, sizeof status.detail,
650              "An earlier call to uhdr_encode() has switched the context from configurable state to "
651              "end state. The context is no longer configurable. To reuse, call reset()");
652     return status;
653   }
654 
655   handle->m_gainmap_scale_factor = gainmap_scale_factor;
656 
657   return status;
658 }
659 
uhdr_enc_set_gainmap_gamma(uhdr_codec_private_t * enc,float gamma)660 UHDR_EXTERN uhdr_error_info_t uhdr_enc_set_gainmap_gamma(uhdr_codec_private_t* enc, float gamma) {
661   uhdr_error_info_t status = g_no_error;
662 
663   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
664     status.error_code = UHDR_CODEC_INVALID_PARAM;
665     status.has_detail = 1;
666     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
667     return status;
668   }
669 
670   if (!std::isfinite(gamma) || gamma <= 0.0f) {
671     status.error_code = UHDR_CODEC_INVALID_PARAM;
672     status.has_detail = 1;
673     snprintf(status.detail, sizeof status.detail, "unsupported gainmap gamma %f, expects to be > 0",
674              gamma);
675     return status;
676   }
677 
678   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
679 
680   if (handle->m_sailed) {
681     status.error_code = UHDR_CODEC_INVALID_OPERATION;
682     status.has_detail = 1;
683     snprintf(status.detail, sizeof status.detail,
684              "An earlier call to uhdr_encode() has switched the context from configurable state to "
685              "end state. The context is no longer configurable. To reuse, call reset()");
686     return status;
687   }
688 
689   handle->m_gamma = gamma;
690 
691   return status;
692 }
693 
uhdr_enc_set_preset(uhdr_codec_private_t * enc,uhdr_enc_preset_t preset)694 uhdr_error_info_t uhdr_enc_set_preset(uhdr_codec_private_t* enc, uhdr_enc_preset_t preset) {
695   uhdr_error_info_t status = g_no_error;
696 
697   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
698     status.error_code = UHDR_CODEC_INVALID_PARAM;
699     status.has_detail = 1;
700     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
701     return status;
702   }
703 
704   if (preset != UHDR_USAGE_REALTIME && preset != UHDR_USAGE_BEST_QUALITY) {
705     status.error_code = UHDR_CODEC_INVALID_PARAM;
706     status.has_detail = 1;
707     snprintf(status.detail, sizeof status.detail,
708              "invalid preset %d, expects one of {UHDR_USAGE_REALTIME, UHDR_USAGE_BEST_QUALITY}",
709              preset);
710     return status;
711   }
712 
713   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
714 
715   if (handle->m_sailed) {
716     status.error_code = UHDR_CODEC_INVALID_OPERATION;
717     status.has_detail = 1;
718     snprintf(status.detail, sizeof status.detail,
719              "An earlier call to uhdr_encode() has switched the context from configurable state to "
720              "end state. The context is no longer configurable. To reuse, call reset()");
721     return status;
722   }
723 
724   handle->m_enc_preset = preset;
725 
726   return status;
727 }
728 
uhdr_enc_set_min_max_content_boost(uhdr_codec_private_t * enc,float min_boost,float max_boost)729 uhdr_error_info_t uhdr_enc_set_min_max_content_boost(uhdr_codec_private_t* enc, float min_boost,
730                                                      float max_boost) {
731   uhdr_error_info_t status = g_no_error;
732 
733   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
734     status.error_code = UHDR_CODEC_INVALID_PARAM;
735     status.has_detail = 1;
736     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
737     return status;
738   }
739 
740   if (!std::isfinite(min_boost) || !std::isfinite(max_boost)) {
741     status.error_code = UHDR_CODEC_INVALID_PARAM;
742     status.has_detail = 1;
743     snprintf(status.detail, sizeof status.detail,
744              "received an argument with value either NaN or infinite. Configured min boost %f, "
745              "max boost %f",
746              max_boost, min_boost);
747     return status;
748   }
749 
750   if (max_boost < min_boost) {
751     status.error_code = UHDR_CODEC_INVALID_PARAM;
752     status.has_detail = 1;
753     snprintf(status.detail, sizeof status.detail,
754              "Invalid min boost / max boost configuration. configured max boost %f is less than "
755              "min boost %f",
756              max_boost, min_boost);
757     return status;
758   }
759 
760   if (min_boost <= 0.0f) {
761     status.error_code = UHDR_CODEC_INVALID_PARAM;
762     status.has_detail = 1;
763     snprintf(status.detail, sizeof status.detail,
764              "Invalid min boost configuration %f, expects > 0.0f", min_boost);
765     return status;
766   }
767 
768   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
769 
770   if (handle->m_sailed) {
771     status.error_code = UHDR_CODEC_INVALID_OPERATION;
772     status.has_detail = 1;
773     snprintf(status.detail, sizeof status.detail,
774              "An earlier call to uhdr_encode() has switched the context from configurable state to "
775              "end state. The context is no longer configurable. To reuse, call reset()");
776     return status;
777   }
778 
779   handle->m_min_content_boost = min_boost;
780   handle->m_max_content_boost = max_boost;
781 
782   return status;
783 }
784 
uhdr_enc_set_target_display_peak_brightness(uhdr_codec_private_t * enc,float nits)785 uhdr_error_info_t uhdr_enc_set_target_display_peak_brightness(uhdr_codec_private_t* enc,
786                                                               float nits) {
787   uhdr_error_info_t status = g_no_error;
788 
789   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
790     status.error_code = UHDR_CODEC_INVALID_PARAM;
791     status.has_detail = 1;
792     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
793     return status;
794   }
795 
796   if (!std::isfinite(nits) || nits < ultrahdr::kSdrWhiteNits || nits > ultrahdr::kPqMaxNits) {
797     status.error_code = UHDR_CODEC_INVALID_PARAM;
798     status.has_detail = 1;
799     snprintf(
800         status.detail, sizeof status.detail,
801         "unexpected target display peak brightness nits %f, expects to be with in range [%f, %f]",
802         nits, ultrahdr::kSdrWhiteNits, ultrahdr::kPqMaxNits);
803   }
804 
805   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
806 
807   if (handle->m_sailed) {
808     status.error_code = UHDR_CODEC_INVALID_OPERATION;
809     status.has_detail = 1;
810     snprintf(status.detail, sizeof status.detail,
811              "An earlier call to uhdr_encode() has switched the context from configurable state to "
812              "end state. The context is no longer configurable. To reuse, call reset()");
813     return status;
814   }
815 
816   handle->m_target_disp_max_brightness = nits;
817 
818   return status;
819 }
820 
uhdr_enc_set_raw_image(uhdr_codec_private_t * enc,uhdr_raw_image_t * img,uhdr_img_label_t intent)821 uhdr_error_info_t uhdr_enc_set_raw_image(uhdr_codec_private_t* enc, uhdr_raw_image_t* img,
822                                          uhdr_img_label_t intent) {
823   uhdr_error_info_t status = g_no_error;
824 
825   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
826     status.error_code = UHDR_CODEC_INVALID_PARAM;
827     status.has_detail = 1;
828     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
829   } else if (img == nullptr) {
830     status.error_code = UHDR_CODEC_INVALID_PARAM;
831     status.has_detail = 1;
832     snprintf(status.detail, sizeof status.detail, "received nullptr for raw image handle");
833   } else if (intent != UHDR_HDR_IMG && intent != UHDR_SDR_IMG) {
834     status.error_code = UHDR_CODEC_INVALID_PARAM;
835     status.has_detail = 1;
836     snprintf(status.detail, sizeof status.detail,
837              "invalid intent %d, expects one of {UHDR_HDR_IMG, UHDR_SDR_IMG}", intent);
838   } else if (intent == UHDR_HDR_IMG && (img->fmt != UHDR_IMG_FMT_24bppYCbCrP010 &&
839                                         img->fmt != UHDR_IMG_FMT_32bppRGBA1010102 &&
840                                         img->fmt != UHDR_IMG_FMT_64bppRGBAHalfFloat)) {
841     status.error_code = UHDR_CODEC_INVALID_PARAM;
842     status.has_detail = 1;
843     snprintf(status.detail, sizeof status.detail,
844              "unsupported input pixel format for hdr intent %d, expects one of "
845              "{UHDR_IMG_FMT_24bppYCbCrP010, UHDR_IMG_FMT_32bppRGBA1010102, "
846              "UHDR_IMG_FMT_64bppRGBAHalfFloat}",
847              img->fmt);
848   } else if (intent == UHDR_SDR_IMG &&
849              (img->fmt != UHDR_IMG_FMT_12bppYCbCr420 && img->fmt != UHDR_IMG_FMT_32bppRGBA8888)) {
850     status.error_code = UHDR_CODEC_INVALID_PARAM;
851     status.has_detail = 1;
852     snprintf(status.detail, sizeof status.detail,
853              "unsupported input pixel format for sdr intent %d, expects one of "
854              "{UHDR_IMG_FMT_12bppYCbCr420, UHDR_IMG_FMT_32bppRGBA8888}",
855              img->fmt);
856   } else if (img->cg != UHDR_CG_BT_2100 && img->cg != UHDR_CG_DISPLAY_P3 &&
857              img->cg != UHDR_CG_BT_709) {
858     status.error_code = UHDR_CODEC_INVALID_PARAM;
859     status.has_detail = 1;
860     snprintf(status.detail, sizeof status.detail,
861              "invalid input color gamut %d, expects one of {UHDR_CG_BT_2100, UHDR_CG_DISPLAY_P3, "
862              "UHDR_CG_BT_709}",
863              img->cg);
864   } else if (intent == UHDR_SDR_IMG && img->ct != UHDR_CT_SRGB) {
865     status.error_code = UHDR_CODEC_INVALID_PARAM;
866     status.has_detail = 1;
867     snprintf(status.detail, sizeof status.detail,
868              "invalid input color transfer for sdr intent image %d, expects UHDR_CT_SRGB", img->ct);
869   } else if (intent == UHDR_HDR_IMG && img->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat &&
870              img->ct != UHDR_CT_LINEAR) {
871     status.error_code = UHDR_CODEC_INVALID_PARAM;
872     status.has_detail = 1;
873     snprintf(status.detail, sizeof status.detail,
874              "invalid input color transfer for hdr intent image %d with format "
875              "UHDR_IMG_FMT_64bppRGBAHalfFloat, expects one of {UHDR_CT_LINEAR}",
876              img->ct);
877   } else if (intent == UHDR_HDR_IMG && img->fmt != UHDR_IMG_FMT_64bppRGBAHalfFloat &&
878              (img->ct != UHDR_CT_HLG && img->ct != UHDR_CT_PQ)) {
879     status.error_code = UHDR_CODEC_INVALID_PARAM;
880     status.has_detail = 1;
881     snprintf(status.detail, sizeof status.detail,
882              "invalid input color transfer for hdr intent image %d with format %d, expects one of "
883              "{UHDR_CT_HLG, UHDR_CT_PQ}",
884              img->fmt, img->ct);
885   } else if ((img->w % 2 != 0 || img->h % 2 != 0) &&
886              (img->fmt == UHDR_IMG_FMT_12bppYCbCr420 || img->fmt == UHDR_IMG_FMT_24bppYCbCrP010)) {
887     status.error_code = UHDR_CODEC_INVALID_PARAM;
888     status.has_detail = 1;
889     snprintf(status.detail, sizeof status.detail,
890              "image dimensions cannot be odd for formats {UHDR_IMG_FMT_12bppYCbCr420, "
891              "UHDR_IMG_FMT_24bppYCbCrP010}, received image dimensions %dx%d",
892              img->w, img->h);
893   } else if ((int)img->w < ultrahdr::kMinWidth || (int)img->h < ultrahdr::kMinHeight) {
894     status.error_code = UHDR_CODEC_INVALID_PARAM;
895     status.has_detail = 1;
896     snprintf(status.detail, sizeof status.detail,
897              "image dimensions cannot be less than %dx%d, received image dimensions %dx%d",
898              ultrahdr::kMinWidth, ultrahdr::kMinHeight, img->w, img->h);
899   } else if ((int)img->w > ultrahdr::kMaxWidth || (int)img->h > ultrahdr::kMaxHeight) {
900     status.error_code = UHDR_CODEC_INVALID_PARAM;
901     status.has_detail = 1;
902     snprintf(status.detail, sizeof status.detail,
903              "image dimensions cannot be larger than %dx%d, received image dimensions %dx%d",
904              ultrahdr::kMaxWidth, ultrahdr::kMaxHeight, img->w, img->h);
905   } else if (img->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
906     if (img->planes[UHDR_PLANE_Y] == nullptr || img->planes[UHDR_PLANE_UV] == nullptr) {
907       status.error_code = UHDR_CODEC_INVALID_PARAM;
908       status.has_detail = 1;
909       snprintf(status.detail, sizeof status.detail,
910                "received nullptr for data field(s), luma ptr %p, chroma_uv ptr %p",
911                img->planes[UHDR_PLANE_Y], img->planes[UHDR_PLANE_UV]);
912     } else if (img->stride[UHDR_PLANE_Y] < img->w) {
913       status.error_code = UHDR_CODEC_INVALID_PARAM;
914       status.has_detail = 1;
915       snprintf(status.detail, sizeof status.detail,
916                "luma stride must not be smaller than width, stride=%d, width=%d",
917                img->stride[UHDR_PLANE_Y], img->w);
918     } else if (img->stride[UHDR_PLANE_UV] < img->w) {
919       status.error_code = UHDR_CODEC_INVALID_PARAM;
920       status.has_detail = 1;
921       snprintf(status.detail, sizeof status.detail,
922                "chroma_uv stride must not be smaller than width, stride=%d, width=%d",
923                img->stride[UHDR_PLANE_UV], img->w);
924     } else if (img->fmt == UHDR_IMG_FMT_24bppYCbCrP010 &&
925                (img->range != UHDR_CR_FULL_RANGE && img->range != UHDR_CR_LIMITED_RANGE)) {
926       status.error_code = UHDR_CODEC_INVALID_PARAM;
927       status.has_detail = 1;
928       snprintf(status.detail, sizeof status.detail,
929                "invalid range, expects one of {UHDR_CR_FULL_RANGE, UHDR_CR_LIMITED_RANGE}");
930     } else if (img->fmt == UHDR_IMG_FMT_32bppRGBA1010102 && img->range != UHDR_CR_FULL_RANGE) {
931       status.error_code = UHDR_CODEC_INVALID_PARAM;
932       status.has_detail = 1;
933       snprintf(status.detail, sizeof status.detail,
934                "invalid range, expects one of {UHDR_CR_FULL_RANGE}");
935     }
936   } else if (img->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
937     if (img->planes[UHDR_PLANE_Y] == nullptr || img->planes[UHDR_PLANE_U] == nullptr ||
938         img->planes[UHDR_PLANE_V] == nullptr) {
939       status.error_code = UHDR_CODEC_INVALID_PARAM;
940       status.has_detail = 1;
941       snprintf(status.detail, sizeof status.detail,
942                "received nullptr for data field(s) luma ptr %p, chroma_u ptr %p, chroma_v ptr %p",
943                img->planes[UHDR_PLANE_Y], img->planes[UHDR_PLANE_U], img->planes[UHDR_PLANE_V]);
944     } else if (img->stride[UHDR_PLANE_Y] < img->w) {
945       status.error_code = UHDR_CODEC_INVALID_PARAM;
946       status.has_detail = 1;
947       snprintf(status.detail, sizeof status.detail,
948                "luma stride must not be smaller than width, stride=%d, width=%d",
949                img->stride[UHDR_PLANE_Y], img->w);
950     } else if (img->stride[UHDR_PLANE_U] < img->w / 2) {
951       status.error_code = UHDR_CODEC_INVALID_PARAM;
952       status.has_detail = 1;
953       snprintf(status.detail, sizeof status.detail,
954                "chroma_u stride must not be smaller than width / 2, stride=%d, width=%d",
955                img->stride[UHDR_PLANE_U], img->w);
956     } else if (img->stride[UHDR_PLANE_V] < img->w / 2) {
957       status.error_code = UHDR_CODEC_INVALID_PARAM;
958       status.has_detail = 1;
959       snprintf(status.detail, sizeof status.detail,
960                "chroma_v stride must not be smaller than width / 2, stride=%d, width=%d",
961                img->stride[UHDR_PLANE_V], img->w);
962     } else if (img->range != UHDR_CR_FULL_RANGE) {
963       status.error_code = UHDR_CODEC_INVALID_PARAM;
964       status.has_detail = 1;
965       snprintf(status.detail, sizeof status.detail,
966                "invalid range, expects one of {UHDR_CR_FULL_RANGE}");
967     }
968   } else if (img->fmt == UHDR_IMG_FMT_32bppRGBA1010102 || img->fmt == UHDR_IMG_FMT_32bppRGBA8888 ||
969              img->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
970     if (img->planes[UHDR_PLANE_PACKED] == nullptr) {
971       status.error_code = UHDR_CODEC_INVALID_PARAM;
972       status.has_detail = 1;
973       snprintf(status.detail, sizeof status.detail,
974                "received nullptr for data field(s) rgb plane packed ptr %p",
975                img->planes[UHDR_PLANE_PACKED]);
976     } else if (img->stride[UHDR_PLANE_PACKED] < img->w) {
977       status.error_code = UHDR_CODEC_INVALID_PARAM;
978       status.has_detail = 1;
979       snprintf(status.detail, sizeof status.detail,
980                "rgb planar stride must not be smaller than width, stride=%d, width=%d",
981                img->stride[UHDR_PLANE_PACKED], img->w);
982     } else if (img->range != UHDR_CR_FULL_RANGE) {
983       status.error_code = UHDR_CODEC_INVALID_PARAM;
984       status.has_detail = 1;
985       snprintf(status.detail, sizeof status.detail,
986                "invalid range, expects one of {UHDR_CR_FULL_RANGE}");
987     }
988   }
989   if (status.error_code != UHDR_CODEC_OK) return status;
990 
991   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
992   if (intent == UHDR_HDR_IMG &&
993       handle->m_raw_images.find(UHDR_SDR_IMG) != handle->m_raw_images.end()) {
994     auto& sdr_raw_entry = handle->m_raw_images.find(UHDR_SDR_IMG)->second;
995     if (img->w != sdr_raw_entry->w || img->h != sdr_raw_entry->h) {
996       status.error_code = UHDR_CODEC_INVALID_PARAM;
997       status.has_detail = 1;
998       snprintf(status.detail, sizeof status.detail,
999                "image resolutions mismatch: hdr intent: %dx%d, sdr intent: %dx%d", img->w, img->h,
1000                sdr_raw_entry->w, sdr_raw_entry->h);
1001       return status;
1002     }
1003   }
1004   if (intent == UHDR_SDR_IMG &&
1005       handle->m_raw_images.find(UHDR_HDR_IMG) != handle->m_raw_images.end()) {
1006     auto& hdr_raw_entry = handle->m_raw_images.find(UHDR_HDR_IMG)->second;
1007     if (img->w != hdr_raw_entry->w || img->h != hdr_raw_entry->h) {
1008       status.error_code = UHDR_CODEC_INVALID_PARAM;
1009       status.has_detail = 1;
1010       snprintf(status.detail, sizeof status.detail,
1011                "image resolutions mismatch: sdr intent: %dx%d, hdr intent: %dx%d", img->w, img->h,
1012                hdr_raw_entry->w, hdr_raw_entry->h);
1013       return status;
1014     }
1015   }
1016   if (handle->m_sailed) {
1017     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1018     status.has_detail = 1;
1019     snprintf(status.detail, sizeof status.detail,
1020              "An earlier call to uhdr_encode() has switched the context from configurable state to "
1021              "end state. The context is no longer configurable. To reuse, call reset()");
1022     return status;
1023   }
1024 
1025   std::unique_ptr<ultrahdr::uhdr_raw_image_ext_t> entry = ultrahdr::copy_raw_image(img);
1026   if (entry == nullptr) {
1027     status.error_code = UHDR_CODEC_UNKNOWN_ERROR;
1028     status.has_detail = 1;
1029     snprintf(status.detail, sizeof status.detail,
1030              "encountered unknown error during color space conversion");
1031     return status;
1032   }
1033 
1034   handle->m_raw_images.insert_or_assign(intent, std::move(entry));
1035 
1036   return status;
1037 }
1038 
uhdr_enc_set_compressed_image(uhdr_codec_private_t * enc,uhdr_compressed_image_t * img,uhdr_img_label_t intent)1039 uhdr_error_info_t uhdr_enc_set_compressed_image(uhdr_codec_private_t* enc,
1040                                                 uhdr_compressed_image_t* img,
1041                                                 uhdr_img_label_t intent) {
1042   uhdr_error_info_t status = g_no_error;
1043 
1044   if (intent != UHDR_HDR_IMG && intent != UHDR_SDR_IMG && intent != UHDR_BASE_IMG) {
1045     status.error_code = UHDR_CODEC_INVALID_PARAM;
1046     status.has_detail = 1;
1047     snprintf(status.detail, sizeof status.detail,
1048              "invalid intent %d, expects one of {UHDR_HDR_IMG, UHDR_SDR_IMG, UHDR_BASE_IMG}",
1049              intent);
1050   }
1051 
1052   return uhdr_enc_validate_and_set_compressed_img(enc, img, intent);
1053 }
1054 
uhdr_enc_set_gainmap_image(uhdr_codec_private_t * enc,uhdr_compressed_image_t * img,uhdr_gainmap_metadata_t * metadata)1055 uhdr_error_info_t uhdr_enc_set_gainmap_image(uhdr_codec_private_t* enc,
1056                                              uhdr_compressed_image_t* img,
1057                                              uhdr_gainmap_metadata_t* metadata) {
1058   uhdr_error_info_t status = ultrahdr::uhdr_validate_gainmap_metadata_descriptor(metadata);
1059   if (status.error_code != UHDR_CODEC_OK) return status;
1060 
1061   status = uhdr_enc_validate_and_set_compressed_img(enc, img, UHDR_GAIN_MAP_IMG);
1062   if (status.error_code != UHDR_CODEC_OK) return status;
1063 
1064   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
1065   memcpy(&handle->m_metadata, metadata, sizeof *metadata);
1066 
1067   return status;
1068 }
1069 
uhdr_enc_set_quality(uhdr_codec_private_t * enc,int quality,uhdr_img_label_t intent)1070 uhdr_error_info_t uhdr_enc_set_quality(uhdr_codec_private_t* enc, int quality,
1071                                        uhdr_img_label_t intent) {
1072   uhdr_error_info_t status = g_no_error;
1073 
1074   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
1075     status.error_code = UHDR_CODEC_INVALID_PARAM;
1076     status.has_detail = 1;
1077     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1078   } else if (quality < 0 || quality > 100) {
1079     status.error_code = UHDR_CODEC_INVALID_PARAM;
1080     status.has_detail = 1;
1081     snprintf(status.detail, sizeof status.detail,
1082              "invalid quality factor %d, expects in range [0-100]", quality);
1083   } else if (intent != UHDR_HDR_IMG && intent != UHDR_SDR_IMG && intent != UHDR_BASE_IMG &&
1084              intent != UHDR_GAIN_MAP_IMG) {
1085     status.error_code = UHDR_CODEC_INVALID_PARAM;
1086     status.has_detail = 1;
1087     snprintf(status.detail, sizeof status.detail,
1088              "invalid intent %d, expects one of {UHDR_HDR_IMG, UHDR_SDR_IMG, UHDR_BASE_IMG, "
1089              "UHDR_GAIN_MAP_IMG}",
1090              intent);
1091   }
1092   if (status.error_code != UHDR_CODEC_OK) return status;
1093 
1094   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
1095   if (handle->m_sailed) {
1096     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1097     status.has_detail = 1;
1098     snprintf(status.detail, sizeof status.detail,
1099              "An earlier call to uhdr_encode() has switched the context from configurable state to "
1100              "end state. The context is no longer configurable. To reuse, call reset()");
1101     return status;
1102   }
1103 
1104   handle->m_quality.insert_or_assign(intent, quality);
1105 
1106   return status;
1107 }
1108 
uhdr_enc_set_exif_data(uhdr_codec_private_t * enc,uhdr_mem_block_t * exif)1109 uhdr_error_info_t uhdr_enc_set_exif_data(uhdr_codec_private_t* enc, uhdr_mem_block_t* exif) {
1110   uhdr_error_info_t status = g_no_error;
1111 
1112   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
1113     status.error_code = UHDR_CODEC_INVALID_PARAM;
1114     status.has_detail = 1;
1115     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1116   } else if (exif == nullptr) {
1117     status.error_code = UHDR_CODEC_INVALID_PARAM;
1118     status.has_detail = 1;
1119     snprintf(status.detail, sizeof status.detail, "received nullptr for exif image handle");
1120   } else if (exif->data == nullptr) {
1121     status.error_code = UHDR_CODEC_INVALID_PARAM;
1122     status.has_detail = 1;
1123     snprintf(status.detail, sizeof status.detail, "received nullptr for exif->data field");
1124   } else if (exif->capacity < exif->data_sz) {
1125     status.error_code = UHDR_CODEC_INVALID_PARAM;
1126     status.has_detail = 1;
1127     snprintf(status.detail, sizeof status.detail,
1128              "exif->capacity %zd is less than exif->data_sz %zd", exif->capacity, exif->data_sz);
1129   }
1130   if (status.error_code != UHDR_CODEC_OK) return status;
1131 
1132   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
1133   if (handle->m_sailed) {
1134     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1135     status.has_detail = 1;
1136     snprintf(status.detail, sizeof status.detail,
1137              "An earlier call to uhdr_encode() has switched the context from configurable state to "
1138              "end state. The context is no longer configurable. To reuse, call reset()");
1139     return status;
1140   }
1141 
1142   uint8_t* data = static_cast<uint8_t*>(exif->data);
1143   std::vector<uint8_t> entry(data, data + exif->data_sz);
1144   handle->m_exif = std::move(entry);
1145 
1146   return status;
1147 }
1148 
uhdr_enc_set_output_format(uhdr_codec_private_t * enc,uhdr_codec_t media_type)1149 uhdr_error_info_t uhdr_enc_set_output_format(uhdr_codec_private_t* enc, uhdr_codec_t media_type) {
1150   uhdr_error_info_t status = g_no_error;
1151 
1152   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
1153     status.error_code = UHDR_CODEC_INVALID_PARAM;
1154     status.has_detail = 1;
1155     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1156   } else if (media_type != UHDR_CODEC_JPG) {
1157     status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE;
1158     status.has_detail = 1;
1159     snprintf(status.detail, sizeof status.detail,
1160              "invalid output format %d, expects {UHDR_CODEC_JPG}", media_type);
1161   }
1162   if (status.error_code != UHDR_CODEC_OK) return status;
1163 
1164   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
1165   if (handle->m_sailed) {
1166     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1167     status.has_detail = 1;
1168     snprintf(status.detail, sizeof status.detail,
1169              "An earlier call to uhdr_encode() has switched the context from configurable state to "
1170              "end state. The context is no longer configurable. To reuse, call reset()");
1171     return status;
1172   }
1173 
1174   handle->m_output_format = media_type;
1175 
1176   return status;
1177 }
1178 
uhdr_encode(uhdr_codec_private_t * enc)1179 uhdr_error_info_t uhdr_encode(uhdr_codec_private_t* enc) {
1180   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
1181     uhdr_error_info_t status;
1182     status.error_code = UHDR_CODEC_INVALID_PARAM;
1183     status.has_detail = 1;
1184     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1185     return status;
1186   }
1187 
1188   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
1189 
1190   if (handle->m_sailed) {
1191     return handle->m_encode_call_status;
1192   }
1193 
1194   handle->m_sailed = true;
1195 
1196   uhdr_error_info_t& status = handle->m_encode_call_status;
1197 
1198   if (handle->m_compressed_images.find(UHDR_BASE_IMG) != handle->m_compressed_images.end() &&
1199       handle->m_compressed_images.find(UHDR_GAIN_MAP_IMG) != handle->m_compressed_images.end()) {
1200     if (handle->m_effects.size() != 0) {
1201       status.error_code = UHDR_CODEC_INVALID_OPERATION;
1202       status.has_detail = 1;
1203       snprintf(status.detail, sizeof status.detail,
1204                "image effects are not enabled for inputs with compressed intent");
1205       return status;
1206     }
1207   } else if (handle->m_raw_images.find(UHDR_HDR_IMG) != handle->m_raw_images.end()) {
1208     if (handle->m_compressed_images.find(UHDR_SDR_IMG) == handle->m_compressed_images.end() &&
1209         handle->m_raw_images.find(UHDR_SDR_IMG) == handle->m_raw_images.end()) {
1210       // api - 0
1211       if (handle->m_effects.size() != 0) {
1212         status = ultrahdr::apply_effects(handle);
1213         if (status.error_code != UHDR_CODEC_OK) return status;
1214       }
1215     } else if (handle->m_compressed_images.find(UHDR_SDR_IMG) !=
1216                    handle->m_compressed_images.end() &&
1217                handle->m_raw_images.find(UHDR_SDR_IMG) == handle->m_raw_images.end()) {
1218       if (handle->m_effects.size() != 0) {
1219         status.error_code = UHDR_CODEC_INVALID_OPERATION;
1220         status.has_detail = 1;
1221         snprintf(status.detail, sizeof status.detail,
1222                  "image effects are not enabled for inputs with compressed intent");
1223         return status;
1224       }
1225     } else if (handle->m_raw_images.find(UHDR_SDR_IMG) != handle->m_raw_images.end()) {
1226       if (handle->m_compressed_images.find(UHDR_SDR_IMG) == handle->m_compressed_images.end()) {
1227         if (handle->m_effects.size() != 0) {
1228           status = ultrahdr::apply_effects(handle);
1229           if (status.error_code != UHDR_CODEC_OK) return status;
1230         }
1231       } else {
1232         if (handle->m_effects.size() != 0) {
1233           status.error_code = UHDR_CODEC_INVALID_OPERATION;
1234           status.has_detail = 1;
1235           snprintf(status.detail, sizeof status.detail,
1236                    "image effects are not enabled for inputs with compressed intent");
1237           return status;
1238         }
1239       }
1240     }
1241   }
1242 
1243   if (handle->m_output_format == UHDR_CODEC_JPG) {
1244     uhdr_mem_block_t exif{};
1245     if (handle->m_exif.size() > 0) {
1246       exif.data = handle->m_exif.data();
1247       exif.capacity = exif.data_sz = handle->m_exif.size();
1248     }
1249 
1250     ultrahdr::JpegR jpegr(nullptr, handle->m_gainmap_scale_factor,
1251                           handle->m_quality.find(UHDR_GAIN_MAP_IMG)->second,
1252                           handle->m_use_multi_channel_gainmap, handle->m_gamma,
1253                           handle->m_enc_preset, handle->m_min_content_boost,
1254                           handle->m_max_content_boost, handle->m_target_disp_max_brightness);
1255     if (handle->m_compressed_images.find(UHDR_BASE_IMG) != handle->m_compressed_images.end() &&
1256         handle->m_compressed_images.find(UHDR_GAIN_MAP_IMG) != handle->m_compressed_images.end()) {
1257       auto& base_entry = handle->m_compressed_images.find(UHDR_BASE_IMG)->second;
1258       auto& gainmap_entry = handle->m_compressed_images.find(UHDR_GAIN_MAP_IMG)->second;
1259 
1260       size_t size =
1261           (std::max)(((size_t)64 * 1024), 2 * (base_entry->data_sz + gainmap_entry->data_sz));
1262       handle->m_compressed_output_buffer = std::make_unique<ultrahdr::uhdr_compressed_image_ext_t>(
1263           UHDR_CG_UNSPECIFIED, UHDR_CT_UNSPECIFIED, UHDR_CR_UNSPECIFIED, size);
1264 
1265       ultrahdr::uhdr_gainmap_metadata_ext_t metadata(handle->m_metadata, ultrahdr::kJpegrVersion);
1266 
1267       // api - 4
1268       status = jpegr.encodeJPEGR(base_entry.get(), gainmap_entry.get(), &metadata,
1269                                  handle->m_compressed_output_buffer.get());
1270     } else if (handle->m_raw_images.find(UHDR_HDR_IMG) != handle->m_raw_images.end()) {
1271       auto& hdr_raw_entry = handle->m_raw_images.find(UHDR_HDR_IMG)->second;
1272 
1273       size_t size = (std::max)((64u * 1024), hdr_raw_entry->w * hdr_raw_entry->h * 3 * 2);
1274       handle->m_compressed_output_buffer = std::make_unique<ultrahdr::uhdr_compressed_image_ext_t>(
1275           UHDR_CG_UNSPECIFIED, UHDR_CT_UNSPECIFIED, UHDR_CR_UNSPECIFIED, size);
1276 
1277       if (handle->m_compressed_images.find(UHDR_SDR_IMG) == handle->m_compressed_images.end() &&
1278           handle->m_raw_images.find(UHDR_SDR_IMG) == handle->m_raw_images.end()) {
1279         // api - 0
1280         status = jpegr.encodeJPEGR(hdr_raw_entry.get(), handle->m_compressed_output_buffer.get(),
1281                                    handle->m_quality.find(UHDR_BASE_IMG)->second,
1282                                    handle->m_exif.size() > 0 ? &exif : nullptr);
1283       } else if (handle->m_compressed_images.find(UHDR_SDR_IMG) !=
1284                      handle->m_compressed_images.end() &&
1285                  handle->m_raw_images.find(UHDR_SDR_IMG) == handle->m_raw_images.end()) {
1286         auto& sdr_compressed_entry = handle->m_compressed_images.find(UHDR_SDR_IMG)->second;
1287         // api - 3
1288         status = jpegr.encodeJPEGR(hdr_raw_entry.get(), sdr_compressed_entry.get(),
1289                                    handle->m_compressed_output_buffer.get());
1290       } else if (handle->m_raw_images.find(UHDR_SDR_IMG) != handle->m_raw_images.end()) {
1291         auto& sdr_raw_entry = handle->m_raw_images.find(UHDR_SDR_IMG)->second;
1292 
1293         if (handle->m_compressed_images.find(UHDR_SDR_IMG) == handle->m_compressed_images.end()) {
1294           // api - 1
1295           status = jpegr.encodeJPEGR(hdr_raw_entry.get(), sdr_raw_entry.get(),
1296                                      handle->m_compressed_output_buffer.get(),
1297                                      handle->m_quality.find(UHDR_BASE_IMG)->second,
1298                                      handle->m_exif.size() > 0 ? &exif : nullptr);
1299         } else {
1300           auto& sdr_compressed_entry = handle->m_compressed_images.find(UHDR_SDR_IMG)->second;
1301           // api - 2
1302           status = jpegr.encodeJPEGR(hdr_raw_entry.get(), sdr_raw_entry.get(),
1303                                      sdr_compressed_entry.get(),
1304                                      handle->m_compressed_output_buffer.get());
1305         }
1306       }
1307     } else {
1308       status.error_code = UHDR_CODEC_INVALID_OPERATION;
1309       status.has_detail = 1;
1310       snprintf(status.detail, sizeof status.detail,
1311                "resources required for uhdr_encode() operation are not present");
1312     }
1313   }
1314 
1315   return status;
1316 }
1317 
uhdr_get_encoded_stream(uhdr_codec_private_t * enc)1318 uhdr_compressed_image_t* uhdr_get_encoded_stream(uhdr_codec_private_t* enc) {
1319   if (dynamic_cast<uhdr_encoder_private*>(enc) == nullptr) {
1320     return nullptr;
1321   }
1322 
1323   uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
1324   if (!handle->m_sailed || handle->m_encode_call_status.error_code != UHDR_CODEC_OK) {
1325     return nullptr;
1326   }
1327 
1328   return handle->m_compressed_output_buffer.get();
1329 }
1330 
uhdr_reset_encoder(uhdr_codec_private_t * enc)1331 void uhdr_reset_encoder(uhdr_codec_private_t* enc) {
1332   if (dynamic_cast<uhdr_encoder_private*>(enc) != nullptr) {
1333     uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc);
1334 
1335     // clear entries and restore defaults
1336     for (auto it : handle->m_effects) delete it;
1337     handle->m_effects.clear();
1338 #ifdef UHDR_ENABLE_GLES
1339     handle->m_uhdr_gl_ctxt.reset_opengl_ctxt();
1340     handle->m_enable_gles = false;
1341 #endif
1342     handle->m_sailed = false;
1343     handle->m_raw_images.clear();
1344     handle->m_compressed_images.clear();
1345     handle->m_quality.clear();
1346     handle->m_quality.emplace(UHDR_HDR_IMG, ultrahdr::kBaseCompressQualityDefault);
1347     handle->m_quality.emplace(UHDR_SDR_IMG, ultrahdr::kBaseCompressQualityDefault);
1348     handle->m_quality.emplace(UHDR_BASE_IMG, ultrahdr::kBaseCompressQualityDefault);
1349     handle->m_quality.emplace(UHDR_GAIN_MAP_IMG, ultrahdr::kMapCompressQualityDefault);
1350     handle->m_exif.clear();
1351     handle->m_output_format = UHDR_CODEC_JPG;
1352     handle->m_gainmap_scale_factor = ultrahdr::kMapDimensionScaleFactorDefault;
1353     handle->m_use_multi_channel_gainmap = ultrahdr::kUseMultiChannelGainMapDefault;
1354     handle->m_gamma = ultrahdr::kGainMapGammaDefault;
1355     handle->m_enc_preset = ultrahdr::kEncSpeedPresetDefault;
1356     handle->m_min_content_boost = FLT_MIN;
1357     handle->m_max_content_boost = FLT_MAX;
1358     handle->m_target_disp_max_brightness = -1.0f;
1359 
1360     handle->m_compressed_output_buffer.reset();
1361     handle->m_encode_call_status = g_no_error;
1362   }
1363 }
1364 
is_uhdr_image(void * data,int size)1365 int is_uhdr_image(void* data, int size) {
1366 #define RET_IF_ERR(x)                         \
1367   {                                           \
1368     uhdr_error_info_t status = (x);           \
1369     if (status.error_code != UHDR_CODEC_OK) { \
1370       uhdr_release_decoder(obj);              \
1371       return 0;                               \
1372     }                                         \
1373   }
1374 
1375   uhdr_codec_private_t* obj = uhdr_create_decoder();
1376   uhdr_compressed_image_t uhdr_image;
1377   uhdr_image.data = data;
1378   uhdr_image.data_sz = size;
1379   uhdr_image.capacity = size;
1380   uhdr_image.cg = UHDR_CG_UNSPECIFIED;
1381   uhdr_image.ct = UHDR_CT_UNSPECIFIED;
1382   uhdr_image.range = UHDR_CR_UNSPECIFIED;
1383 
1384   RET_IF_ERR(uhdr_dec_set_image(obj, &uhdr_image));
1385   RET_IF_ERR(uhdr_dec_probe(obj));
1386 #undef RET_IF_ERR
1387 
1388   uhdr_release_decoder(obj);
1389 
1390   return 1;
1391 }
1392 
uhdr_create_decoder(void)1393 uhdr_codec_private_t* uhdr_create_decoder(void) {
1394   uhdr_decoder_private* handle = new uhdr_decoder_private();
1395 
1396   if (handle != nullptr) {
1397     uhdr_reset_decoder(handle);
1398   }
1399   return handle;
1400 }
1401 
uhdr_release_decoder(uhdr_codec_private_t * dec)1402 void uhdr_release_decoder(uhdr_codec_private_t* dec) {
1403   if (dynamic_cast<uhdr_decoder_private*>(dec) != nullptr) {
1404     uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1405     delete handle;
1406   }
1407 }
1408 
uhdr_dec_set_image(uhdr_codec_private_t * dec,uhdr_compressed_image_t * img)1409 uhdr_error_info_t uhdr_dec_set_image(uhdr_codec_private_t* dec, uhdr_compressed_image_t* img) {
1410   uhdr_error_info_t status = g_no_error;
1411 
1412   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1413     status.error_code = UHDR_CODEC_INVALID_PARAM;
1414     status.has_detail = 1;
1415     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1416   } else if (img == nullptr) {
1417     status.error_code = UHDR_CODEC_INVALID_PARAM;
1418     status.has_detail = 1;
1419     snprintf(status.detail, sizeof status.detail, "received nullptr for compressed image handle");
1420   } else if (img->data == nullptr) {
1421     status.error_code = UHDR_CODEC_INVALID_PARAM;
1422     status.has_detail = 1;
1423     snprintf(status.detail, sizeof status.detail,
1424              "received nullptr for compressed img->data field");
1425   } else if (img->capacity < img->data_sz) {
1426     status.error_code = UHDR_CODEC_INVALID_PARAM;
1427     status.has_detail = 1;
1428     snprintf(status.detail, sizeof status.detail, "img->capacity %zd is less than img->data_sz %zd",
1429              img->capacity, img->data_sz);
1430   }
1431   if (status.error_code != UHDR_CODEC_OK) return status;
1432 
1433   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1434   if (handle->m_probed) {
1435     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1436     status.has_detail = 1;
1437     snprintf(status.detail, sizeof status.detail,
1438              "An earlier call to uhdr_decode() has switched the context from configurable state to "
1439              "end state. The context is no longer configurable. To reuse, call reset()");
1440     return status;
1441   }
1442 
1443   handle->m_uhdr_compressed_img = std::make_unique<ultrahdr::uhdr_compressed_image_ext_t>(
1444       img->cg, img->ct, img->range, img->data_sz);
1445   memcpy(handle->m_uhdr_compressed_img->data, img->data, img->data_sz);
1446   handle->m_uhdr_compressed_img->data_sz = img->data_sz;
1447 
1448   return status;
1449 }
1450 
uhdr_dec_set_out_img_format(uhdr_codec_private_t * dec,uhdr_img_fmt_t fmt)1451 uhdr_error_info_t uhdr_dec_set_out_img_format(uhdr_codec_private_t* dec, uhdr_img_fmt_t fmt) {
1452   uhdr_error_info_t status = g_no_error;
1453 
1454   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1455     status.error_code = UHDR_CODEC_INVALID_PARAM;
1456     status.has_detail = 1;
1457     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1458   } else if (fmt != UHDR_IMG_FMT_32bppRGBA8888 && fmt != UHDR_IMG_FMT_64bppRGBAHalfFloat &&
1459              fmt != UHDR_IMG_FMT_32bppRGBA1010102) {
1460     status.error_code = UHDR_CODEC_INVALID_PARAM;
1461     status.has_detail = 1;
1462     snprintf(status.detail, sizeof status.detail,
1463              "invalid output format %d, expects one of {UHDR_IMG_FMT_32bppRGBA8888,  "
1464              "UHDR_IMG_FMT_64bppRGBAHalfFloat, UHDR_IMG_FMT_32bppRGBA1010102}",
1465              fmt);
1466   }
1467   if (status.error_code != UHDR_CODEC_OK) return status;
1468 
1469   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1470   if (handle->m_probed) {
1471     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1472     status.has_detail = 1;
1473     snprintf(status.detail, sizeof status.detail,
1474              "An earlier call to uhdr_decode() has switched the context from configurable state to "
1475              "end state. The context is no longer configurable. To reuse, call reset()");
1476     return status;
1477   }
1478 
1479   handle->m_output_fmt = fmt;
1480 
1481   return status;
1482 }
1483 
uhdr_dec_set_out_color_transfer(uhdr_codec_private_t * dec,uhdr_color_transfer_t ct)1484 uhdr_error_info_t uhdr_dec_set_out_color_transfer(uhdr_codec_private_t* dec,
1485                                                   uhdr_color_transfer_t ct) {
1486   uhdr_error_info_t status = g_no_error;
1487 
1488   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1489     status.error_code = UHDR_CODEC_INVALID_PARAM;
1490     status.has_detail = 1;
1491     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1492   } else if (ct != UHDR_CT_HLG && ct != UHDR_CT_PQ && ct != UHDR_CT_LINEAR && ct != UHDR_CT_SRGB) {
1493     status.error_code = UHDR_CODEC_INVALID_PARAM;
1494     status.has_detail = 1;
1495     snprintf(status.detail, sizeof status.detail,
1496              "invalid output color transfer %d, expects one of {UHDR_CT_HLG, UHDR_CT_PQ, "
1497              "UHDR_CT_LINEAR, UHDR_CT_SRGB}",
1498              ct);
1499   }
1500   if (status.error_code != UHDR_CODEC_OK) return status;
1501 
1502   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1503   if (handle->m_probed) {
1504     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1505     status.has_detail = 1;
1506     snprintf(status.detail, sizeof status.detail,
1507              "An earlier call to uhdr_decode() has switched the context from configurable state to "
1508              "end state. The context is no longer configurable. To reuse, call reset()");
1509     return status;
1510   }
1511 
1512   handle->m_output_ct = ct;
1513 
1514   return status;
1515 }
1516 
uhdr_dec_set_out_max_display_boost(uhdr_codec_private_t * dec,float display_boost)1517 uhdr_error_info_t uhdr_dec_set_out_max_display_boost(uhdr_codec_private_t* dec,
1518                                                      float display_boost) {
1519   uhdr_error_info_t status = g_no_error;
1520 
1521   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1522     status.error_code = UHDR_CODEC_INVALID_PARAM;
1523     status.has_detail = 1;
1524     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1525   } else if (!std::isfinite(display_boost) || display_boost < 1.0f) {
1526     status.error_code = UHDR_CODEC_INVALID_PARAM;
1527     status.has_detail = 1;
1528     snprintf(status.detail, sizeof status.detail,
1529              "invalid display boost %f, expects to be >= 1.0f}", display_boost);
1530   }
1531   if (status.error_code != UHDR_CODEC_OK) return status;
1532 
1533   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1534   if (handle->m_probed) {
1535     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1536     status.has_detail = 1;
1537     snprintf(status.detail, sizeof status.detail,
1538              "An earlier call to uhdr_decode() has switched the context from configurable state to "
1539              "end state. The context is no longer configurable. To reuse, call reset()");
1540     return status;
1541   }
1542 
1543   handle->m_output_max_disp_boost = display_boost;
1544 
1545   return status;
1546 }
1547 
uhdr_dec_probe(uhdr_codec_private_t * dec)1548 uhdr_error_info_t uhdr_dec_probe(uhdr_codec_private_t* dec) {
1549   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1550     uhdr_error_info_t status;
1551     status.error_code = UHDR_CODEC_INVALID_PARAM;
1552     status.has_detail = 1;
1553     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1554     return status;
1555   }
1556 
1557   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1558   uhdr_error_info_t& status = handle->m_probe_call_status;
1559 
1560   if (!handle->m_probed) {
1561     handle->m_probed = true;
1562 
1563     if (handle->m_uhdr_compressed_img.get() == nullptr) {
1564       status.error_code = UHDR_CODEC_INVALID_OPERATION;
1565       status.has_detail = 1;
1566       snprintf(status.detail, sizeof status.detail, "did not receive any image for decoding");
1567       return status;
1568     }
1569 
1570     ultrahdr::jpeg_info_struct primary_image;
1571     ultrahdr::jpeg_info_struct gainmap_image;
1572     ultrahdr::jpegr_info_struct jpegr_info;
1573     jpegr_info.primaryImgInfo = &primary_image;
1574     jpegr_info.gainmapImgInfo = &gainmap_image;
1575 
1576     ultrahdr::JpegR jpegr;
1577     status = jpegr.getJPEGRInfo(handle->m_uhdr_compressed_img.get(), &jpegr_info);
1578     if (status.error_code != UHDR_CODEC_OK) return status;
1579 
1580     ultrahdr::uhdr_gainmap_metadata_ext_t metadata;
1581     status = jpegr.parseGainMapMetadata(gainmap_image.isoData.data(), gainmap_image.isoData.size(),
1582                                         gainmap_image.xmpData.data(), gainmap_image.xmpData.size(),
1583                                         &metadata);
1584     if (status.error_code != UHDR_CODEC_OK) return status;
1585     std::copy(metadata.max_content_boost, metadata.max_content_boost + 3,
1586               handle->m_metadata.max_content_boost);
1587     std::copy(metadata.min_content_boost, metadata.min_content_boost + 3,
1588               handle->m_metadata.min_content_boost);
1589     std::copy(metadata.gamma, metadata.gamma + 3, handle->m_metadata.gamma);
1590     std::copy(metadata.offset_sdr, metadata.offset_sdr + 3, handle->m_metadata.offset_sdr);
1591     std::copy(metadata.offset_hdr, metadata.offset_hdr + 3, handle->m_metadata.offset_hdr);
1592     handle->m_metadata.hdr_capacity_min = metadata.hdr_capacity_min;
1593     handle->m_metadata.hdr_capacity_max = metadata.hdr_capacity_max;
1594     handle->m_metadata.use_base_cg = metadata.use_base_cg;
1595 
1596     handle->m_img_wd = primary_image.width;
1597     handle->m_img_ht = primary_image.height;
1598     handle->m_gainmap_wd = gainmap_image.width;
1599     handle->m_gainmap_ht = gainmap_image.height;
1600     handle->m_gainmap_num_comp = gainmap_image.numComponents;
1601     handle->m_exif = std::move(primary_image.exifData);
1602     handle->m_exif_block.data = handle->m_exif.data();
1603     handle->m_exif_block.data_sz = handle->m_exif_block.capacity = handle->m_exif.size();
1604     handle->m_icc = std::move(primary_image.iccData);
1605     handle->m_icc_block.data = handle->m_icc.data();
1606     handle->m_icc_block.data_sz = handle->m_icc_block.capacity = handle->m_icc.size();
1607     handle->m_base_img = std::move(primary_image.imgData);
1608     handle->m_base_img_block.data = handle->m_base_img.data();
1609     handle->m_base_img_block.data_sz = handle->m_base_img_block.capacity =
1610         handle->m_base_img.size();
1611     handle->m_gainmap_img = std::move(gainmap_image.imgData);
1612     handle->m_gainmap_img_block.data = handle->m_gainmap_img.data();
1613     handle->m_gainmap_img_block.data_sz = handle->m_gainmap_img_block.capacity =
1614         handle->m_gainmap_img.size();
1615   }
1616 
1617   return status;
1618 }
1619 
uhdr_dec_get_image_width(uhdr_codec_private_t * dec)1620 int uhdr_dec_get_image_width(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_wd;
1631 }
1632 
uhdr_dec_get_image_height(uhdr_codec_private_t * dec)1633 int uhdr_dec_get_image_height(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_img_ht;
1644 }
1645 
uhdr_dec_get_gainmap_width(uhdr_codec_private_t * dec)1646 int uhdr_dec_get_gainmap_width(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_wd;
1657 }
1658 
uhdr_dec_get_gainmap_height(uhdr_codec_private_t * dec)1659 int uhdr_dec_get_gainmap_height(uhdr_codec_private_t* dec) {
1660   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1661     return -1;
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 -1;
1667   }
1668 
1669   return handle->m_gainmap_ht;
1670 }
1671 
uhdr_dec_get_exif(uhdr_codec_private_t * dec)1672 uhdr_mem_block_t* uhdr_dec_get_exif(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_exif_block;
1683 }
1684 
uhdr_dec_get_icc(uhdr_codec_private_t * dec)1685 uhdr_mem_block_t* uhdr_dec_get_icc(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_icc_block;
1696 }
1697 
uhdr_dec_get_base_image(uhdr_codec_private_t * dec)1698 uhdr_mem_block_t* uhdr_dec_get_base_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_base_img_block;
1709 }
1710 
uhdr_dec_get_gainmap_image(uhdr_codec_private_t * dec)1711 uhdr_mem_block_t* uhdr_dec_get_gainmap_image(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_gainmap_img_block;
1722 }
1723 
uhdr_dec_get_gainmap_metadata(uhdr_codec_private_t * dec)1724 uhdr_gainmap_metadata_t* uhdr_dec_get_gainmap_metadata(uhdr_codec_private_t* dec) {
1725   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1726     return nullptr;
1727   }
1728 
1729   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1730   if (!handle->m_probed || handle->m_probe_call_status.error_code != UHDR_CODEC_OK) {
1731     return nullptr;
1732   }
1733 
1734   return &handle->m_metadata;
1735 }
1736 
uhdr_decode(uhdr_codec_private_t * dec)1737 uhdr_error_info_t uhdr_decode(uhdr_codec_private_t* dec) {
1738   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1739     uhdr_error_info_t status;
1740     status.error_code = UHDR_CODEC_INVALID_PARAM;
1741     status.has_detail = 1;
1742     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1743     return status;
1744   }
1745 
1746   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1747 
1748   if (handle->m_sailed) {
1749     return handle->m_decode_call_status;
1750   }
1751 
1752   uhdr_error_info_t& status = handle->m_decode_call_status;
1753   status = uhdr_dec_probe(dec);
1754   if (status.error_code != UHDR_CODEC_OK) return status;
1755 
1756   handle->m_sailed = true;
1757 
1758   if ((handle->m_output_fmt == UHDR_IMG_FMT_32bppRGBA1010102 &&
1759        (handle->m_output_ct != UHDR_CT_HLG && handle->m_output_ct != UHDR_CT_PQ)) ||
1760       (handle->m_output_fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat &&
1761        handle->m_output_ct != UHDR_CT_LINEAR) ||
1762       (handle->m_output_fmt == UHDR_IMG_FMT_32bppRGBA8888 && handle->m_output_ct != UHDR_CT_SRGB)) {
1763     status.error_code = UHDR_CODEC_INVALID_PARAM;
1764     status.has_detail = 1;
1765     snprintf(status.detail, sizeof status.detail,
1766              "unsupported output pixel format and output color transfer pair");
1767     return status;
1768   }
1769 
1770   handle->m_decoded_img_buffer = std::make_unique<ultrahdr::uhdr_raw_image_ext_t>(
1771       handle->m_output_fmt, UHDR_CG_UNSPECIFIED, handle->m_output_ct, UHDR_CR_UNSPECIFIED,
1772       handle->m_img_wd, handle->m_img_ht, 1);
1773 
1774   handle->m_gainmap_img_buffer = std::make_unique<ultrahdr::uhdr_raw_image_ext_t>(
1775       handle->m_gainmap_num_comp == 1 ? UHDR_IMG_FMT_8bppYCbCr400 : UHDR_IMG_FMT_32bppRGBA8888,
1776       UHDR_CG_UNSPECIFIED, UHDR_CT_UNSPECIFIED, UHDR_CR_UNSPECIFIED, handle->m_gainmap_wd,
1777       handle->m_gainmap_ht, 1);
1778 
1779 #ifdef UHDR_ENABLE_GLES
1780   ultrahdr::uhdr_opengl_ctxt_t* uhdrGLESCtxt = nullptr;
1781   if (handle->m_enable_gles &&
1782       (handle->m_output_ct != UHDR_CT_SRGB || handle->m_effects.size() > 0)) {
1783     handle->m_uhdr_gl_ctxt.init_opengl_ctxt();
1784     status = handle->m_uhdr_gl_ctxt.mErrorStatus;
1785     if (status.error_code != UHDR_CODEC_OK) return status;
1786     uhdrGLESCtxt = &handle->m_uhdr_gl_ctxt;
1787   }
1788   ultrahdr::JpegR jpegr(uhdrGLESCtxt);
1789 #else
1790   ultrahdr::JpegR jpegr;
1791 #endif
1792 
1793   status =
1794       jpegr.decodeJPEGR(handle->m_uhdr_compressed_img.get(), handle->m_decoded_img_buffer.get(),
1795                         handle->m_output_max_disp_boost, handle->m_output_ct, handle->m_output_fmt,
1796                         handle->m_gainmap_img_buffer.get(), nullptr);
1797 
1798   if (status.error_code == UHDR_CODEC_OK && dec->m_effects.size() != 0) {
1799     status = ultrahdr::apply_effects(handle);
1800   }
1801 
1802 #ifdef UHDR_ENABLE_GLES
1803   if (handle->m_enable_gles) {
1804     if (handle->m_uhdr_gl_ctxt.mDecodedImgTexture != 0) {
1805       handle->m_uhdr_gl_ctxt.read_texture(
1806           &handle->m_uhdr_gl_ctxt.mDecodedImgTexture, handle->m_decoded_img_buffer->fmt,
1807           handle->m_decoded_img_buffer->w, handle->m_decoded_img_buffer->h,
1808           handle->m_decoded_img_buffer->planes[0]);
1809     }
1810     if (handle->m_uhdr_gl_ctxt.mGainmapImgTexture != 0 && dec->m_effects.size() != 0) {
1811       handle->m_uhdr_gl_ctxt.read_texture(
1812           &handle->m_uhdr_gl_ctxt.mGainmapImgTexture, handle->m_gainmap_img_buffer->fmt,
1813           handle->m_gainmap_img_buffer->w, handle->m_gainmap_img_buffer->h,
1814           handle->m_gainmap_img_buffer->planes[0]);
1815     }
1816   }
1817 #endif
1818   return status;
1819 }
1820 
uhdr_get_decoded_image(uhdr_codec_private_t * dec)1821 uhdr_raw_image_t* uhdr_get_decoded_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_decoded_img_buffer.get();
1832 }
1833 
uhdr_get_decoded_gainmap_image(uhdr_codec_private_t * dec)1834 uhdr_raw_image_t* uhdr_get_decoded_gainmap_image(uhdr_codec_private_t* dec) {
1835   if (dynamic_cast<uhdr_decoder_private*>(dec) == nullptr) {
1836     return nullptr;
1837   }
1838 
1839   uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1840   if (!handle->m_sailed || handle->m_decode_call_status.error_code != UHDR_CODEC_OK) {
1841     return nullptr;
1842   }
1843 
1844   return handle->m_gainmap_img_buffer.get();
1845 }
1846 
uhdr_reset_decoder(uhdr_codec_private_t * dec)1847 void uhdr_reset_decoder(uhdr_codec_private_t* dec) {
1848   if (dynamic_cast<uhdr_decoder_private*>(dec) != nullptr) {
1849     uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec);
1850 
1851     // clear entries and restore defaults
1852     for (auto it : handle->m_effects) delete it;
1853     handle->m_effects.clear();
1854 #ifdef UHDR_ENABLE_GLES
1855     handle->m_uhdr_gl_ctxt.reset_opengl_ctxt();
1856     handle->m_enable_gles = false;
1857 #endif
1858     handle->m_sailed = false;
1859     handle->m_uhdr_compressed_img.reset();
1860     handle->m_output_fmt = UHDR_IMG_FMT_64bppRGBAHalfFloat;
1861     handle->m_output_ct = UHDR_CT_LINEAR;
1862     handle->m_output_max_disp_boost = FLT_MAX;
1863 
1864     // ready to be configured
1865     handle->m_probed = false;
1866     handle->m_decoded_img_buffer.reset();
1867     handle->m_gainmap_img_buffer.reset();
1868     handle->m_img_wd = 0;
1869     handle->m_img_ht = 0;
1870     handle->m_gainmap_wd = 0;
1871     handle->m_gainmap_ht = 0;
1872     handle->m_gainmap_num_comp = 0;
1873     handle->m_exif.clear();
1874     memset(&handle->m_exif_block, 0, sizeof handle->m_exif_block);
1875     handle->m_icc.clear();
1876     memset(&handle->m_icc_block, 0, sizeof handle->m_icc_block);
1877     handle->m_base_img.clear();
1878     memset(&handle->m_base_img_block, 0, sizeof handle->m_base_img_block);
1879     handle->m_gainmap_img.clear();
1880     memset(&handle->m_gainmap_img_block, 0, sizeof handle->m_gainmap_img_block);
1881     memset(&handle->m_metadata, 0, sizeof handle->m_metadata);
1882     handle->m_probe_call_status = g_no_error;
1883     handle->m_decode_call_status = g_no_error;
1884   }
1885 }
1886 
uhdr_enable_gpu_acceleration(uhdr_codec_private_t * codec,int enable)1887 uhdr_error_info_t uhdr_enable_gpu_acceleration(uhdr_codec_private_t* codec,
1888                                                [[maybe_unused]] int enable) {
1889   uhdr_error_info_t status = g_no_error;
1890 
1891   if (codec == nullptr) {
1892     status.error_code = UHDR_CODEC_INVALID_PARAM;
1893     status.has_detail = 1;
1894     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1895     return status;
1896   }
1897 
1898   if (codec->m_sailed) {
1899     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1900     status.has_detail = 1;
1901     snprintf(
1902         status.detail, sizeof status.detail,
1903         "An earlier call to uhdr_encode()/uhdr_decode() has switched the context from configurable "
1904         "state to end state. The context is no longer configurable. To reuse, call reset()");
1905     return status;
1906   }
1907 
1908 #ifdef UHDR_ENABLE_GLES
1909   codec->m_enable_gles = enable;
1910 #endif
1911 
1912   return status;
1913 }
1914 
uhdr_add_effect_mirror(uhdr_codec_private_t * codec,uhdr_mirror_direction_t direction)1915 uhdr_error_info_t uhdr_add_effect_mirror(uhdr_codec_private_t* codec,
1916                                          uhdr_mirror_direction_t direction) {
1917   uhdr_error_info_t status = g_no_error;
1918 
1919   if (codec == nullptr) {
1920     status.error_code = UHDR_CODEC_INVALID_PARAM;
1921     status.has_detail = 1;
1922     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1923     return status;
1924   }
1925 
1926   if (direction != UHDR_MIRROR_HORIZONTAL && direction != UHDR_MIRROR_VERTICAL) {
1927     status.error_code = UHDR_CODEC_INVALID_PARAM;
1928     status.has_detail = 1;
1929     snprintf(
1930         status.detail, sizeof status.detail,
1931         "unsupported direction, expects one of {UHDR_MIRROR_HORIZONTAL, UHDR_MIRROR_VERTICAL}");
1932     return status;
1933   }
1934 
1935   if (codec->m_sailed) {
1936     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1937     status.has_detail = 1;
1938     snprintf(
1939         status.detail, sizeof status.detail,
1940         "An earlier call to uhdr_encode()/uhdr_decode() has switched the context from configurable "
1941         "state to end state. The context is no longer configurable. To reuse, call reset()");
1942     return status;
1943   }
1944 
1945   codec->m_effects.push_back(new ultrahdr::uhdr_mirror_effect_t(direction));
1946 
1947   return status;
1948 }
1949 
uhdr_add_effect_rotate(uhdr_codec_private_t * codec,int degrees)1950 uhdr_error_info_t uhdr_add_effect_rotate(uhdr_codec_private_t* codec, int degrees) {
1951   uhdr_error_info_t status = g_no_error;
1952 
1953   if (codec == nullptr) {
1954     status.error_code = UHDR_CODEC_INVALID_PARAM;
1955     status.has_detail = 1;
1956     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1957     return status;
1958   }
1959 
1960   if (degrees != 90 && degrees != 180 && degrees != 270) {
1961     status.error_code = UHDR_CODEC_INVALID_PARAM;
1962     status.has_detail = 1;
1963     snprintf(status.detail, sizeof status.detail,
1964              "unsupported degrees, expects one of {90, 180, 270}");
1965     return status;
1966   }
1967 
1968   if (codec->m_sailed) {
1969     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1970     status.has_detail = 1;
1971     snprintf(
1972         status.detail, sizeof status.detail,
1973         "An earlier call to uhdr_encode()/uhdr_decode() has switched the context from configurable "
1974         "state to end state. The context is no longer configurable. To reuse, call reset()");
1975     return status;
1976   }
1977 
1978   codec->m_effects.push_back(new ultrahdr::uhdr_rotate_effect_t(degrees));
1979 
1980   return status;
1981 }
1982 
uhdr_add_effect_crop(uhdr_codec_private_t * codec,int left,int right,int top,int bottom)1983 uhdr_error_info_t uhdr_add_effect_crop(uhdr_codec_private_t* codec, int left, int right, int top,
1984                                        int bottom) {
1985   uhdr_error_info_t status = g_no_error;
1986 
1987   if (codec == nullptr) {
1988     status.error_code = UHDR_CODEC_INVALID_PARAM;
1989     status.has_detail = 1;
1990     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
1991     return status;
1992   }
1993 
1994   if (codec->m_sailed) {
1995     status.error_code = UHDR_CODEC_INVALID_OPERATION;
1996     status.has_detail = 1;
1997     snprintf(
1998         status.detail, sizeof status.detail,
1999         "An earlier call to uhdr_encode()/uhdr_decode() has switched the context from configurable "
2000         "state to end state. The context is no longer configurable. To reuse, call reset()");
2001     return status;
2002   }
2003 
2004   codec->m_effects.push_back(new ultrahdr::uhdr_crop_effect_t(left, right, top, bottom));
2005 
2006   return status;
2007 }
2008 
uhdr_add_effect_resize(uhdr_codec_private_t * codec,int width,int height)2009 uhdr_error_info_t uhdr_add_effect_resize(uhdr_codec_private_t* codec, int width, int height) {
2010   uhdr_error_info_t status = g_no_error;
2011 
2012   if (codec == nullptr) {
2013     status.error_code = UHDR_CODEC_INVALID_PARAM;
2014     status.has_detail = 1;
2015     snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance");
2016     return status;
2017   }
2018 
2019   if (codec->m_sailed) {
2020     status.error_code = UHDR_CODEC_INVALID_OPERATION;
2021     status.has_detail = 1;
2022     snprintf(
2023         status.detail, sizeof status.detail,
2024         "An earlier call to uhdr_encode()/uhdr_decode() has switched the context from configurable "
2025         "state to end state. The context is no longer configurable. To reuse, call reset()");
2026     return status;
2027   }
2028 
2029   codec->m_effects.push_back(new ultrahdr::uhdr_resize_effect_t(width, height));
2030 
2031   return status;
2032 }
2033