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