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