/src/libultrahdr/lib/src/avifultrahdr.cpp
Line | Count | Source |
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 | | #ifdef UHDR_ENABLE_HEIF |
18 | | |
19 | | #include <cstdlib> |
20 | | #include <map> |
21 | | |
22 | | #include "ultrahdr/avifultrahdr.h" |
23 | | #include "ultrahdr/gainmapmath.h" |
24 | | #include "ultrahdr/gainmapmetadata.h" |
25 | | |
26 | 0 | #define UHDR_CG_BT_601 static_cast<uhdr_color_gamut_t>(3) |
27 | | |
28 | | namespace ultrahdr { |
29 | | |
30 | | class MemoryWriter { |
31 | | public: |
32 | 0 | MemoryWriter() : data_(nullptr), size_(0), capacity_(0) {} |
33 | | |
34 | 0 | ~MemoryWriter() { free(data_); } |
35 | | |
36 | 0 | const uint8_t* data() const { return data_; } |
37 | | |
38 | 0 | size_t size() const { return size_; } |
39 | | |
40 | 0 | void write(const void* data, size_t size) { |
41 | 0 | if (capacity_ - size_ < size) { |
42 | 0 | size_t new_capacity = capacity_ + size; |
43 | 0 | uint8_t* new_data = static_cast<uint8_t*>(malloc(new_capacity)); |
44 | 0 | if (data_) { |
45 | 0 | memcpy(new_data, data_, size_); |
46 | 0 | free(data_); |
47 | 0 | } |
48 | 0 | data_ = new_data; |
49 | 0 | capacity_ = new_capacity; |
50 | 0 | } |
51 | 0 | memcpy(&data_[size_], data, size); |
52 | 0 | size_ += size; |
53 | 0 | } |
54 | | |
55 | | public: |
56 | | uint8_t* data_; |
57 | | size_t size_; |
58 | | size_t capacity_; |
59 | | }; |
60 | | |
61 | | static struct heif_error writer_write([[maybe_unused]] struct heif_context* ctx, const void* data, |
62 | 0 | size_t size, void* userdata) { |
63 | 0 | MemoryWriter* writer = static_cast<MemoryWriter*>(userdata); |
64 | 0 | writer->write(data, size); |
65 | 0 | struct heif_error err { |
66 | 0 | heif_error_Ok, heif_suberror_Unspecified, nullptr |
67 | 0 | }; |
68 | 0 | return err; |
69 | 0 | } |
70 | | |
71 | | static struct heif_error fill_img_plane(heif_image* img, heif_channel channel, void* srcBuffer, |
72 | 0 | int width, int height, int srcRowStride) { |
73 | 0 | heif_error err = heif_image_add_plane(img, channel, width, height, 8 /*bitDepth */); |
74 | 0 | if (err.code != heif_error_Ok) return err; |
75 | | |
76 | 0 | int dstStride; |
77 | 0 | uint8_t* dstBuffer = heif_image_get_plane(img, channel, &dstStride); |
78 | 0 | uint8_t* srcRow = static_cast<uint8_t*>(srcBuffer); |
79 | |
|
80 | 0 | for (int y = 0; y < height; y++) { |
81 | 0 | memcpy(dstBuffer, srcRow, width); |
82 | 0 | srcRow += srcRowStride; |
83 | 0 | dstBuffer += dstStride; |
84 | 0 | } |
85 | 0 | return err; |
86 | 0 | } |
87 | | |
88 | | static struct heif_error copy_img_plane(heif_image* img, heif_channel channel, void* dstBuffer, |
89 | 0 | int width, int height, int dstRowStride, int bpp) { |
90 | 0 | if (channel != heif_channel_interleaved && channel != heif_channel_Y && |
91 | 0 | channel != heif_channel_Cb && channel != heif_channel_Cr) { |
92 | 0 | return {heif_error_Invalid_input, heif_suberror_Nonexisting_image_channel_referenced, |
93 | 0 | "supports copying only heif_channel_interleaved"}; |
94 | 0 | } |
95 | | |
96 | 0 | int srcStride; |
97 | 0 | uint8_t* srcBuffer = heif_image_get_plane(img, channel, &srcStride); |
98 | 0 | uint8_t* srcRow = static_cast<uint8_t*>(srcBuffer); |
99 | 0 | uint8_t* dstRow = static_cast<uint8_t*>(dstBuffer); |
100 | |
|
101 | 0 | for (int y = 0; y < height; y++) { |
102 | 0 | memcpy(dstRow, srcRow, width * bpp); |
103 | 0 | srcRow += srcStride; |
104 | 0 | dstRow += dstRowStride; |
105 | 0 | } |
106 | 0 | return {heif_error_Ok, heif_suberror_Unspecified, nullptr}; |
107 | 0 | } |
108 | | |
109 | | /*!\brief map of uhdr color primaries and libheif primaries */ |
110 | | static const std::map<uhdr_color_gamut_t, heif_color_primaries> map_primaries = { |
111 | | {UHDR_CG_BT_709, heif_color_primaries_ITU_R_BT_709_5}, |
112 | | {UHDR_CG_DISPLAY_P3, heif_color_primaries_SMPTE_EG_432_1}, |
113 | | {UHDR_CG_BT_2100, heif_color_primaries_ITU_R_BT_2020_2_and_2100_0}, |
114 | | {(uhdr_color_gamut_t)UHDR_CG_BT_601, heif_color_primaries_ITU_R_BT_601_6}, |
115 | | {UHDR_CG_UNSPECIFIED, heif_color_primaries_unspecified}, |
116 | | }; |
117 | | |
118 | | /*!\brief map of uhdr color transfer and libheif transfer characteristics */ |
119 | | static const std::map<uhdr_color_transfer_t, heif_transfer_characteristics> map_transfer = { |
120 | | {UHDR_CT_LINEAR, heif_transfer_characteristic_linear}, |
121 | | {UHDR_CT_PQ, heif_transfer_characteristic_ITU_R_BT_2100_0_PQ}, |
122 | | {UHDR_CT_HLG, heif_transfer_characteristic_ITU_R_BT_2100_0_HLG}, |
123 | | {UHDR_CT_SRGB, heif_transfer_characteristic_ITU_R_BT_709_5}, |
124 | | {UHDR_CT_UNSPECIFIED, heif_transfer_characteristic_unspecified}, |
125 | | }; |
126 | | |
127 | | /*!\brief map of uhdr color gamut and libheif matrix coefficients */ |
128 | | static const std::map<uhdr_color_gamut_t, heif_matrix_coefficients> map_matrix = { |
129 | | {UHDR_CG_BT_709, heif_matrix_coefficients_ITU_R_BT_709_5}, |
130 | | {UHDR_CG_DISPLAY_P3, heif_matrix_coefficients_chromaticity_derived_non_constant_luminance}, |
131 | | {UHDR_CG_BT_2100, heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance}, |
132 | | {(uhdr_color_gamut_t)UHDR_CG_BT_601, heif_matrix_coefficients_ITU_R_BT_601_6}, |
133 | | {UHDR_CG_UNSPECIFIED, heif_matrix_coefficients_unspecified}, |
134 | | }; |
135 | | |
136 | 0 | static uhdr_color_gamut_t map_primaries_inverse(heif_color_primaries primaries) { |
137 | 0 | for (const auto& [key, value] : map_primaries) { |
138 | 0 | if (value == primaries) return key; |
139 | 0 | } |
140 | 0 | return UHDR_CG_UNSPECIFIED; |
141 | 0 | } |
142 | | |
143 | 0 | static uhdr_color_transfer_t map_transfer_inverse(heif_transfer_characteristics transfer) { |
144 | 0 | for (const auto& [key, value] : map_transfer) { |
145 | 0 | if (value == transfer) return key; |
146 | 0 | } |
147 | 0 | return UHDR_CT_UNSPECIFIED; |
148 | 0 | } |
149 | | |
150 | 0 | static uhdr_color_gamut_t map_matrix_inverse(heif_matrix_coefficients matrix) { |
151 | 0 | for (const auto& [key, value] : map_matrix) { |
152 | 0 | if (value == matrix) return key; |
153 | 0 | } |
154 | 0 | return UHDR_CG_UNSPECIFIED; |
155 | 0 | } |
156 | | |
157 | | static heif_error map_fmt_to_heif_chroma_vars(uhdr_img_fmt_t fmt, unsigned int w, unsigned h, |
158 | | enum heif_chroma& heif_img_fmt, int& chromaWd, |
159 | 0 | int& chromaHt) { |
160 | 0 | if (fmt == UHDR_IMG_FMT_12bppYCbCr420) { |
161 | 0 | heif_img_fmt = heif_chroma_420; |
162 | 0 | chromaWd = w / 2; |
163 | 0 | chromaHt = h / 2; |
164 | 0 | } else if (fmt == UHDR_IMG_FMT_16bppYCbCr422) { |
165 | 0 | heif_img_fmt = heif_chroma_422; |
166 | 0 | chromaWd = w; |
167 | 0 | chromaHt = h / 2; |
168 | 0 | } else if (fmt == UHDR_IMG_FMT_24bppYCbCr444) { |
169 | 0 | heif_img_fmt = heif_chroma_444; |
170 | 0 | chromaWd = w; |
171 | 0 | chromaHt = h; |
172 | 0 | } else { |
173 | 0 | heif_error status; |
174 | 0 | status.code = heif_error_Unsupported_feature; |
175 | 0 | status.subcode = heif_suberror_Unsupported_color_conversion; |
176 | 0 | status.message = "mapping uhdr image format to heif chroma format failed"; |
177 | 0 | return status; |
178 | 0 | } |
179 | 0 | return {heif_error_Ok, heif_suberror_Unspecified, nullptr}; |
180 | 0 | } |
181 | | |
182 | | static heif_error map_heif_chroma_vars_to_fmt(enum heif_colorspace colorspace, |
183 | 0 | enum heif_chroma chroma_fmt, uhdr_img_fmt_t& fmt) { |
184 | 0 | if (colorspace == heif_colorspace_YCbCr) { |
185 | 0 | if (chroma_fmt == heif_chroma_420) { |
186 | 0 | fmt = UHDR_IMG_FMT_12bppYCbCr420; |
187 | 0 | } else if (chroma_fmt == heif_chroma_422) { |
188 | 0 | fmt = UHDR_IMG_FMT_16bppYCbCr422; |
189 | 0 | } else if (chroma_fmt == heif_chroma_444) { |
190 | 0 | fmt = UHDR_IMG_FMT_24bppYCbCr444; |
191 | 0 | } else { |
192 | 0 | heif_error status; |
193 | 0 | status.code = heif_error_Unsupported_feature; |
194 | 0 | status.subcode = heif_suberror_Unsupported_color_conversion; |
195 | 0 | status.message = "mapping heif image format to uhdr img format failed"; |
196 | 0 | return status; |
197 | 0 | } |
198 | 0 | } else if (colorspace == heif_colorspace_monochrome) { |
199 | 0 | fmt = UHDR_IMG_FMT_8bppYCbCr400; |
200 | 0 | } else if (colorspace == heif_colorspace_RGB) { |
201 | 0 | if (chroma_fmt == heif_chroma_interleaved_RGB) { |
202 | 0 | fmt = UHDR_IMG_FMT_24bppRGB888; |
203 | 0 | } else if (chroma_fmt == heif_chroma_interleaved_RGBA) { |
204 | 0 | fmt = UHDR_IMG_FMT_32bppRGBA8888; |
205 | 0 | } else { |
206 | 0 | heif_error status; |
207 | 0 | status.code = heif_error_Unsupported_feature; |
208 | 0 | status.subcode = heif_suberror_Unsupported_color_conversion; |
209 | 0 | status.message = "mapping heif image format to uhdr img format failed"; |
210 | 0 | return status; |
211 | 0 | } |
212 | 0 | } else { |
213 | 0 | heif_error status; |
214 | 0 | status.code = heif_error_Unsupported_feature; |
215 | 0 | status.subcode = heif_suberror_Unsupported_color_conversion; |
216 | 0 | status.message = "mapping heif image format to uhdr img format failed"; |
217 | 0 | return status; |
218 | 0 | } |
219 | 0 | return {heif_error_Ok, heif_suberror_Unspecified, nullptr}; |
220 | 0 | } |
221 | | |
222 | 0 | static heif_error set_internal_color_format(heif_encoder* encoder, enum heif_chroma heif_img_fmt) { |
223 | 0 | struct heif_error err { |
224 | 0 | heif_error_Ok, heif_suberror_Unspecified, nullptr |
225 | 0 | }; |
226 | 0 | switch (heif_img_fmt) { |
227 | 0 | case heif_chroma_420: |
228 | 0 | case heif_chroma_monochrome: |
229 | 0 | err = heif_encoder_set_parameter(encoder, "chroma", "420"); |
230 | 0 | break; |
231 | 0 | case heif_chroma_422: |
232 | 0 | err = heif_encoder_set_parameter(encoder, "chroma", "422"); |
233 | 0 | break; |
234 | 0 | case heif_chroma_444: |
235 | 0 | err = heif_encoder_set_parameter(encoder, "chroma", "444"); |
236 | 0 | break; |
237 | 0 | default: |
238 | 0 | err.code = heif_error_Invalid_input; |
239 | 0 | err.subcode = heif_suberror_Unsupported_parameter; |
240 | 0 | err.message = "unsupported heif image format setting"; |
241 | 0 | break; |
242 | 0 | } |
243 | 0 | return err; |
244 | 0 | } |
245 | | |
246 | | static uhdr_error_info_t heif_get_gainmap_metadata(struct heif_image_handle* base_handle, |
247 | 0 | uhdr_gainmap_metadata_ext_t* metadata) { |
248 | 0 | size_t iso_vec_sz = heif_image_handle_get_gain_map_metadata_size(base_handle); |
249 | 0 | std::vector<uint8_t> iso_vec(iso_vec_sz); |
250 | 0 | uhdr_gainmap_metadata_frac metadata_frac; |
251 | |
|
252 | 0 | heif_error err = heif_image_handle_get_gain_map_metadata(base_handle, iso_vec.data()); |
253 | 0 | if (err.code != heif_error_Ok) { |
254 | 0 | uhdr_error_info_t status; |
255 | 0 | status.error_code = UHDR_CODEC_ERROR; |
256 | 0 | status.has_detail = 1; |
257 | 0 | snprintf(status.detail, sizeof status.detail, "%s", err.message); |
258 | 0 | return status; |
259 | 0 | } |
260 | 0 | UHDR_ERR_CHECK(uhdr_gainmap_metadata_frac::decodeGainmapMetadata(iso_vec, &metadata_frac)) |
261 | 0 | UHDR_ERR_CHECK( |
262 | 0 | uhdr_gainmap_metadata_frac::gainmapMetadataFractionToFloat(&metadata_frac, metadata)) |
263 | 0 | return g_no_error; |
264 | 0 | } |
265 | | |
266 | | AvifUltraHdr::AvifUltraHdr(void* uhdrGLESCtxt, int mapDimensionScaleFactor, int mapCompressQuality, |
267 | | bool useMultiChannelGainMap, float gamma, uhdr_enc_preset_t preset, |
268 | | float minContentBoost, float maxContentBoost, float targetDispPeakBrightness, |
269 | | uhdr_codec_t codec) |
270 | 0 | : UltraHdr(uhdrGLESCtxt, mapDimensionScaleFactor, mapCompressQuality, useMultiChannelGainMap, |
271 | 0 | gamma, preset, minContentBoost, maxContentBoost, targetDispPeakBrightness), |
272 | 0 | mCodec(codec) {} |
273 | | |
274 | | /* Encode API-0 */ |
275 | | uhdr_error_info_t AvifUltraHdr::encodeAvifUltraHdr(uhdr_raw_image_t* hdr_intent, uhdr_compressed_image_t* dest, |
276 | 0 | int quality, uhdr_mem_block_t* exif) { |
277 | 0 | uhdr_img_fmt_t sdr_intent_fmt; |
278 | 0 | if (hdr_intent->fmt == UHDR_IMG_FMT_24bppYCbCrP010) { |
279 | 0 | sdr_intent_fmt = UHDR_IMG_FMT_12bppYCbCr420; |
280 | 0 | } else if (hdr_intent->fmt == UHDR_IMG_FMT_30bppYCbCr444) { |
281 | 0 | sdr_intent_fmt = UHDR_IMG_FMT_24bppYCbCr444; |
282 | 0 | } else if (hdr_intent->fmt == UHDR_IMG_FMT_32bppRGBA1010102 || |
283 | 0 | hdr_intent->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) { |
284 | 0 | sdr_intent_fmt = UHDR_IMG_FMT_32bppRGBA8888; |
285 | 0 | } else { |
286 | 0 | uhdr_error_info_t status; |
287 | 0 | status.error_code = UHDR_CODEC_INVALID_PARAM; |
288 | 0 | status.has_detail = 1; |
289 | 0 | snprintf(status.detail, sizeof status.detail, "unsupported hdr intent color format %d", |
290 | 0 | hdr_intent->fmt); |
291 | 0 | return status; |
292 | 0 | } |
293 | 0 | std::unique_ptr<uhdr_raw_image_ext_t> sdr_intent = std::make_unique<uhdr_raw_image_ext_t>( |
294 | 0 | sdr_intent_fmt, UHDR_CG_UNSPECIFIED, UHDR_CT_UNSPECIFIED, UHDR_CR_UNSPECIFIED, hdr_intent->w, |
295 | 0 | hdr_intent->h, 64); |
296 | | |
297 | | // tone map |
298 | 0 | UHDR_ERR_CHECK(toneMap(hdr_intent, sdr_intent.get())); |
299 | | |
300 | | // If hdr intent is tonemapped internally, it is observed from quality pov, |
301 | | // generateGainMapOnePass() is sufficient |
302 | 0 | mEncPreset = UHDR_USAGE_REALTIME; // overriding the config option |
303 | | |
304 | | // generate gain map |
305 | 0 | uhdr_gainmap_metadata_ext_t metadata(kJpegrVersion); |
306 | 0 | std::unique_ptr<uhdr_raw_image_ext_t> gainmap; |
307 | 0 | UHDR_ERR_CHECK(generateGainMap(sdr_intent.get(), hdr_intent, &metadata, gainmap, |
308 | 0 | sdr_intent->cg == UHDR_CG_DISPLAY_P3 /* sdr_is_601 */, |
309 | 0 | false /* use_luminance */)); |
310 | | |
311 | | // compress sdr image |
312 | 0 | std::unique_ptr<uhdr_raw_image_ext_t> sdr_intent_yuv_ext; |
313 | 0 | uhdr_raw_image_t* sdr_intent_yuv = sdr_intent.get(); |
314 | 0 | if (isPixelFormatRgb(sdr_intent->fmt)) { |
315 | | #if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON))) |
316 | | sdr_intent_yuv_ext = convert_raw_input_to_ycbcr_neon(sdr_intent.get()); |
317 | | #else |
318 | 0 | sdr_intent_yuv_ext = convert_raw_input_to_ycbcr(sdr_intent.get()); |
319 | 0 | #endif |
320 | 0 | sdr_intent_yuv = sdr_intent_yuv_ext.get(); |
321 | 0 | } |
322 | |
|
323 | 0 | std::shared_ptr<DataStruct> baseIcc = IccHelper::writeIccProfile(UHDR_CT_SRGB, sdr_intent->cg); |
324 | 0 | std::shared_ptr<DataStruct> alternateIcc = |
325 | 0 | IccHelper::writeIccProfile(gainmap->ct, gainmap->cg); |
326 | |
|
327 | 0 | return encodeAvifUltraHdr(sdr_intent_yuv, gainmap.get(), &metadata, dest, quality, exif, baseIcc.get(), |
328 | 0 | alternateIcc.get()); |
329 | 0 | } |
330 | | |
331 | | /* Encode API-1 */ |
332 | | uhdr_error_info_t AvifUltraHdr::encodeAvifUltraHdr(uhdr_raw_image_t* hdr_intent, uhdr_raw_image_t* sdr_intent, |
333 | | uhdr_compressed_image_t* dest, int quality, |
334 | 0 | uhdr_mem_block_t* exif) { |
335 | | // generate gain map |
336 | 0 | uhdr_gainmap_metadata_ext_t metadata(kJpegrVersion); |
337 | 0 | std::unique_ptr<uhdr_raw_image_ext_t> gainmap; |
338 | 0 | UHDR_ERR_CHECK(generateGainMap(sdr_intent, hdr_intent, &metadata, gainmap, |
339 | 0 | sdr_intent->cg == UHDR_CG_DISPLAY_P3 /* sdr_is_601 */)); |
340 | |
|
341 | 0 | std::unique_ptr<uhdr_raw_image_ext_t> sdr_intent_yuv_ext; |
342 | 0 | uhdr_raw_image_t* sdr_intent_yuv = sdr_intent; |
343 | 0 | if (isPixelFormatRgb(sdr_intent->fmt)) { |
344 | | #if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON))) |
345 | | sdr_intent_yuv_ext = convert_raw_input_to_ycbcr_neon(sdr_intent); |
346 | | #else |
347 | 0 | sdr_intent_yuv_ext = convert_raw_input_to_ycbcr(sdr_intent); |
348 | 0 | #endif |
349 | 0 | sdr_intent_yuv = sdr_intent_yuv_ext.get(); |
350 | 0 | } |
351 | |
|
352 | 0 | std::shared_ptr<DataStruct> baseIcc = IccHelper::writeIccProfile(UHDR_CT_SRGB, sdr_intent->cg); |
353 | 0 | std::shared_ptr<DataStruct> alternateIcc = |
354 | 0 | IccHelper::writeIccProfile(gainmap->ct, gainmap->cg); |
355 | |
|
356 | 0 | return encodeAvifUltraHdr(sdr_intent_yuv, gainmap.get(), &metadata, dest, quality, exif, baseIcc.get(), |
357 | 0 | alternateIcc.get()); |
358 | 0 | } |
359 | | |
360 | | uhdr_error_info_t AvifUltraHdr::encodeAvifUltraHdr(uhdr_raw_image_t* sdr_intent, uhdr_raw_image_t* gainmap_img, |
361 | | uhdr_gainmap_metadata_ext_t* metadata, |
362 | | uhdr_compressed_image_t* dest, int quality, |
363 | | uhdr_mem_block_t* exif, DataStruct* baseIcc, |
364 | 0 | DataStruct* alternateIcc) { |
365 | 0 | uhdr_error_info_t status = g_no_error; |
366 | 0 | heif_encoder* encoder = nullptr; |
367 | 0 | heif_encoding_options* options = nullptr; |
368 | 0 | heif_color_profile_nclx* sdrNclx = nullptr; |
369 | 0 | heif_color_profile_nclx* gainmapNclx = nullptr; |
370 | 0 | heif_color_profile_nclx* hdrNclx = nullptr; |
371 | 0 | heif_image* baseImage = nullptr; |
372 | 0 | heif_image_handle* baseHandle = nullptr; |
373 | 0 | heif_image* secondaryImage = nullptr; |
374 | 0 | heif_image_handle* secondaryHandle = nullptr; |
375 | 0 | int sdr_cg_matrix; |
376 | 0 | uhdr_gainmap_metadata_frac metadata_frac; |
377 | 0 | std::vector<uint8_t> iso_data; |
378 | |
|
379 | 0 | uhdr_raw_image_t* gainmap_img_yuv = gainmap_img; |
380 | 0 | std::unique_ptr<uhdr_raw_image_ext_t> gainmap_yuv_ext; |
381 | |
|
382 | 0 | MemoryWriter writer; |
383 | 0 | struct heif_writer w; |
384 | 0 | w.writer_api_version = 1; |
385 | 0 | w.write = writer_write; |
386 | |
|
387 | 0 | heif_context* ctx = heif_context_alloc(); |
388 | 0 | if (!ctx) { |
389 | 0 | status.error_code = UHDR_CODEC_MEM_ERROR; |
390 | 0 | status.has_detail = 1; |
391 | 0 | snprintf(status.detail, sizeof status.detail, "failed to allocate heif context"); |
392 | 0 | return status; |
393 | 0 | } |
394 | | |
395 | 0 | HEIF_ERR_CHECK(heif_context_get_encoder_for_format( |
396 | 0 | ctx, mCodec == UHDR_CODEC_AVIF ? heif_compression_AV1 : heif_compression_HEVC, &encoder)); |
397 | | |
398 | | // set the encoder parameters |
399 | 0 | HEIF_ERR_CHECK(heif_encoder_set_lossy_quality(encoder, quality)); |
400 | | |
401 | | // encode the primary image |
402 | 0 | enum heif_chroma heif_img_fmt; |
403 | 0 | int chromaWd, chromaHt; |
404 | 0 | HEIF_ERR_CHECK(map_fmt_to_heif_chroma_vars(sdr_intent->fmt, sdr_intent->w, sdr_intent->h, |
405 | 0 | heif_img_fmt, chromaWd, chromaHt)); |
406 | 0 | HEIF_ERR_CHECK(heif_image_create(sdr_intent->w, sdr_intent->h, heif_colorspace_YCbCr, |
407 | 0 | heif_img_fmt, &baseImage)); |
408 | 0 | HEIF_ERR_CHECK(fill_img_plane(baseImage, heif_channel_Y, sdr_intent->planes[UHDR_PLANE_Y], |
409 | 0 | sdr_intent->w, sdr_intent->h, sdr_intent->stride[UHDR_PLANE_Y])); |
410 | 0 | HEIF_ERR_CHECK(fill_img_plane(baseImage, heif_channel_Cb, sdr_intent->planes[UHDR_PLANE_U], |
411 | 0 | chromaWd, chromaHt, sdr_intent->stride[UHDR_PLANE_U])); |
412 | 0 | HEIF_ERR_CHECK(fill_img_plane(baseImage, heif_channel_Cr, sdr_intent->planes[UHDR_PLANE_V], |
413 | 0 | chromaWd, chromaHt, sdr_intent->stride[UHDR_PLANE_V])); |
414 | 0 | if (baseIcc != nullptr) { |
415 | 0 | void* ptr = (uint8_t*)baseIcc->getData() + kICCIdentifierSize; |
416 | 0 | HEIF_ERR_CHECK(heif_image_set_raw_color_profile(baseImage, "prof", ptr, |
417 | 0 | baseIcc->getLength() - kICCIdentifierSize)); |
418 | 0 | } |
419 | 0 | sdrNclx = heif_nclx_color_profile_alloc(); |
420 | 0 | if (!sdrNclx) { |
421 | 0 | status.error_code = UHDR_CODEC_MEM_ERROR; |
422 | 0 | status.has_detail = 1; |
423 | 0 | snprintf(status.detail, sizeof status.detail, |
424 | 0 | "failed to allocate nclx color profile for base image"); |
425 | 0 | goto CleanUp; |
426 | 0 | } |
427 | 0 | HEIF_ERR_CHECK(set_internal_color_format(encoder, heif_img_fmt)); |
428 | 0 | HEIF_ERR_CHECK(heif_nclx_color_profile_set_color_primaries( |
429 | 0 | sdrNclx, map_primaries.find(sdr_intent->cg)->second)); |
430 | 0 | HEIF_ERR_CHECK(heif_nclx_color_profile_set_transfer_characteristics( |
431 | 0 | sdrNclx, map_transfer.find(sdr_intent->ct)->second)); |
432 | 0 | sdr_cg_matrix = sdr_intent->cg == UHDR_CG_DISPLAY_P3 ? UHDR_CG_BT_601 : sdr_intent->cg; |
433 | 0 | HEIF_ERR_CHECK(heif_nclx_color_profile_set_matrix_coefficients( |
434 | 0 | sdrNclx, map_matrix.find((uhdr_color_gamut_t)sdr_cg_matrix)->second)); |
435 | 0 | sdrNclx->full_range_flag = sdr_intent->range == UHDR_CR_FULL_RANGE ? true : false; |
436 | 0 | options = heif_encoding_options_alloc(); |
437 | 0 | if (!options) { |
438 | 0 | status.error_code = UHDR_CODEC_MEM_ERROR; |
439 | 0 | status.has_detail = 1; |
440 | 0 | snprintf(status.detail, sizeof status.detail, |
441 | 0 | "failed to allocate nclx color profile for base image"); |
442 | 0 | goto CleanUp; |
443 | 0 | } |
444 | 0 | options->save_two_colr_boxes_when_ICC_and_nclx_available = 1; |
445 | 0 | options->output_nclx_profile = sdrNclx; |
446 | 0 | HEIF_ERR_CHECK(heif_image_set_nclx_color_profile(baseImage, sdrNclx)); |
447 | 0 | HEIF_ERR_CHECK(heif_context_encode_image(ctx, baseImage, encoder, options, &baseHandle)); |
448 | 0 | if (exif != nullptr) { |
449 | 0 | HEIF_ERR_CHECK(heif_context_add_exif_metadata(ctx, baseHandle, exif->data, exif->data_sz)); |
450 | 0 | } |
451 | | |
452 | | // encode the gain map image |
453 | 0 | if (isUsingMultiChannelGainMap()) { |
454 | 0 | gainmap_yuv_ext = convert_raw_input_to_ycbcr(gainmap_img, true /* use bt601 */); |
455 | 0 | gainmap_img_yuv = gainmap_yuv_ext.get(); |
456 | 0 | } |
457 | 0 | if (isUsingMultiChannelGainMap()) { |
458 | 0 | HEIF_ERR_CHECK(map_fmt_to_heif_chroma_vars(gainmap_img_yuv->fmt, gainmap_img_yuv->w, |
459 | 0 | gainmap_img_yuv->h, heif_img_fmt, chromaWd, |
460 | 0 | chromaHt)); |
461 | 0 | HEIF_ERR_CHECK(heif_image_create(gainmap_img_yuv->w, gainmap_img_yuv->h, heif_colorspace_YCbCr, |
462 | 0 | heif_img_fmt, &secondaryImage)); |
463 | 0 | HEIF_ERR_CHECK(fill_img_plane(secondaryImage, heif_channel_Y, |
464 | 0 | gainmap_img_yuv->planes[UHDR_PLANE_Y], gainmap_img_yuv->w, |
465 | 0 | gainmap_img_yuv->h, gainmap_img_yuv->stride[UHDR_PLANE_Y])); |
466 | 0 | HEIF_ERR_CHECK(fill_img_plane(secondaryImage, heif_channel_Cb, |
467 | 0 | gainmap_img_yuv->planes[UHDR_PLANE_U], chromaWd, chromaHt, |
468 | 0 | gainmap_img_yuv->stride[UHDR_PLANE_U])); |
469 | 0 | HEIF_ERR_CHECK(fill_img_plane(secondaryImage, heif_channel_Cr, |
470 | 0 | gainmap_img_yuv->planes[UHDR_PLANE_V], chromaWd, chromaHt, |
471 | 0 | gainmap_img_yuv->stride[UHDR_PLANE_V])); |
472 | 0 | HEIF_ERR_CHECK(set_internal_color_format(encoder, heif_img_fmt)); |
473 | 0 | } else { |
474 | 0 | HEIF_ERR_CHECK(heif_image_create(gainmap_img_yuv->w, gainmap_img_yuv->h, |
475 | 0 | heif_colorspace_monochrome, heif_chroma_monochrome, |
476 | 0 | &secondaryImage)); |
477 | 0 | HEIF_ERR_CHECK(fill_img_plane(secondaryImage, heif_channel_Y, |
478 | 0 | gainmap_img_yuv->planes[UHDR_PLANE_Y], gainmap_img_yuv->w, |
479 | 0 | gainmap_img_yuv->h, gainmap_img_yuv->stride[UHDR_PLANE_Y])); |
480 | 0 | HEIF_ERR_CHECK(heif_encoder_set_parameter(encoder, "chroma", "420")); |
481 | 0 | } |
482 | 0 | gainmapNclx = heif_nclx_color_profile_alloc(); |
483 | 0 | if (!gainmapNclx) { |
484 | 0 | status.error_code = UHDR_CODEC_MEM_ERROR; |
485 | 0 | status.has_detail = 1; |
486 | 0 | snprintf(status.detail, sizeof status.detail, |
487 | 0 | "failed to allocate nclx color profile for base image"); |
488 | 0 | goto CleanUp; |
489 | 0 | } |
490 | 0 | HEIF_ERR_CHECK( |
491 | 0 | heif_nclx_color_profile_set_color_primaries(gainmapNclx, heif_color_primaries_unspecified)); |
492 | 0 | HEIF_ERR_CHECK(heif_nclx_color_profile_set_transfer_characteristics( |
493 | 0 | gainmapNclx, heif_transfer_characteristic_unspecified)); |
494 | 0 | HEIF_ERR_CHECK(heif_nclx_color_profile_set_matrix_coefficients( |
495 | 0 | gainmapNclx, map_matrix.find((uhdr_color_gamut_t)UHDR_CG_BT_601)->second)); |
496 | 0 | gainmapNclx->full_range_flag = gainmap_img_yuv->range == UHDR_CR_FULL_RANGE ? true : false; |
497 | 0 | options->output_nclx_profile = gainmapNclx; |
498 | 0 | HEIF_ERR_CHECK(heif_image_set_nclx_color_profile(secondaryImage, gainmapNclx)); |
499 | |
|
500 | 0 | status = uhdr_gainmap_metadata_frac::gainmapMetadataFloatToFraction(metadata, &metadata_frac); |
501 | 0 | if (status.error_code != UHDR_CODEC_OK) goto CleanUp; |
502 | 0 | status = uhdr_gainmap_metadata_frac::encodeGainmapMetadata(&metadata_frac, iso_data); |
503 | 0 | if (status.error_code != UHDR_CODEC_OK) goto CleanUp; |
504 | | // TODO: pass alternateIcc write color properties of tmap item |
505 | | // TODO: write clli of tmap item |
506 | 0 | hdrNclx = heif_nclx_color_profile_alloc(); |
507 | 0 | if (!hdrNclx) { |
508 | 0 | status.error_code = UHDR_CODEC_MEM_ERROR; |
509 | 0 | status.has_detail = 1; |
510 | 0 | snprintf(status.detail, sizeof status.detail, |
511 | 0 | "failed to allocate nclx color profile for tmap image"); |
512 | 0 | goto CleanUp; |
513 | 0 | } |
514 | 0 | HEIF_ERR_CHECK(heif_nclx_color_profile_set_color_primaries( |
515 | 0 | hdrNclx, map_primaries.find(gainmap_img->cg)->second)); |
516 | 0 | HEIF_ERR_CHECK(heif_nclx_color_profile_set_transfer_characteristics( |
517 | 0 | hdrNclx, map_transfer.find(gainmap_img->ct)->second)); |
518 | 0 | HEIF_ERR_CHECK(heif_nclx_color_profile_set_matrix_coefficients( |
519 | 0 | hdrNclx, map_matrix.find(gainmap_img->cg)->second)); |
520 | 0 | hdrNclx->full_range_flag = gainmap_img->range == UHDR_CR_FULL_RANGE ? true : false; |
521 | 0 | (void)alternateIcc; |
522 | 0 | HEIF_ERR_CHECK(heif_encoder_set_lossy_quality(encoder, mMapCompressQuality)); |
523 | 0 | HEIF_ERR_CHECK(heif_context_encode_gain_map_image(ctx, baseHandle, encoder, secondaryImage, |
524 | 0 | options, iso_data.data(), iso_data.size(), |
525 | 0 | hdrNclx, &secondaryHandle)); |
526 | |
|
527 | 0 | heif_context_write(ctx, &w, &writer); |
528 | 0 | memcpy(dest->data, writer.data(), writer.size()); |
529 | 0 | dest->data_sz = dest->capacity = writer.size(); |
530 | |
|
531 | 0 | CleanUp: |
532 | 0 | if (baseImage) heif_image_release(baseImage); |
533 | 0 | baseImage = nullptr; |
534 | 0 | if (baseHandle) heif_image_handle_release(baseHandle); |
535 | 0 | baseHandle = nullptr; |
536 | 0 | if (secondaryImage) heif_image_release(secondaryImage); |
537 | 0 | secondaryImage = nullptr; |
538 | 0 | if (secondaryHandle) heif_image_handle_release(secondaryHandle); |
539 | 0 | secondaryHandle = nullptr; |
540 | 0 | if (options) heif_encoding_options_free(options); |
541 | 0 | options = nullptr; |
542 | 0 | if (hdrNclx) heif_nclx_color_profile_free(hdrNclx); |
543 | 0 | hdrNclx = nullptr; |
544 | 0 | if (gainmapNclx) heif_nclx_color_profile_free(gainmapNclx); |
545 | 0 | gainmapNclx = nullptr; |
546 | 0 | if (sdrNclx) heif_nclx_color_profile_free(sdrNclx); |
547 | 0 | sdrNclx = nullptr; |
548 | 0 | if (encoder) heif_encoder_release(encoder); |
549 | 0 | encoder = nullptr; |
550 | 0 | if (ctx) heif_context_free(ctx); |
551 | 0 | ctx = nullptr; |
552 | |
|
553 | 0 | return status; |
554 | 0 | } |
555 | | |
556 | | /* Decode API */ |
557 | | uhdr_error_info_t AvifUltraHdr::decodeAvifUltraHdr(uhdr_compressed_image_t* uhdr_compressed_img, |
558 | | uhdr_raw_image_t* dest, float max_display_boost, |
559 | | uhdr_color_transfer_t output_ct, uhdr_img_fmt_t output_format, |
560 | | uhdr_raw_image_t* gainmap_img, |
561 | 0 | uhdr_gainmap_metadata_t* gainmap_metadata) { |
562 | 0 | uhdr_error_info_t status = g_no_error; |
563 | 0 | struct heif_image_handle* base_handle = nullptr; |
564 | 0 | struct heif_image* sdr_image = nullptr; |
565 | 0 | struct heif_image_handle* gainmap_handle = nullptr; |
566 | 0 | struct heif_image* gainmap_image = nullptr; |
567 | 0 | struct heif_decoding_options* decoding_options = nullptr; |
568 | 0 | heif_color_profile_nclx* nclx = nullptr; |
569 | 0 | uhdr_gainmap_metadata_ext_t metadata(kJpegrVersion); |
570 | 0 | enum heif_colorspace out_colorspace; |
571 | 0 | enum heif_chroma out_chroma; |
572 | 0 | uhdr_img_fmt_t fmt; |
573 | 0 | int sdr_width = 0, sdr_height = 0; |
574 | 0 | int gainmap_width = 0, gainmap_height = 0; |
575 | 0 | std::unique_ptr<uhdr_raw_image_ext_t> sdr_intent, gainmap; |
576 | 0 | heif_context* ctx = nullptr; |
577 | 0 | heif_error nclx_err; |
578 | 0 | uhdr_color_gamut_t sdr_cg = UHDR_CG_BT_709; |
579 | 0 | uhdr_color_transfer_t sdr_ct = UHDR_CT_SRGB; |
580 | 0 | uhdr_color_range_t sdr_range = UHDR_CR_FULL_RANGE; |
581 | 0 | uhdr_color_gamut_t gm_cg = UHDR_CG_BT_709; |
582 | 0 | uhdr_color_transfer_t gm_ct = UHDR_CT_SRGB; |
583 | 0 | uhdr_color_range_t gm_range = UHDR_CR_FULL_RANGE; |
584 | 0 | heif_error err; |
585 | |
|
586 | 0 | ctx = heif_context_alloc(); |
587 | 0 | if (!ctx) { |
588 | 0 | status.error_code = UHDR_CODEC_MEM_ERROR; |
589 | 0 | status.has_detail = 1; |
590 | 0 | snprintf(status.detail, sizeof status.detail, "failed to allocate heif context"); |
591 | 0 | return status; |
592 | 0 | } |
593 | 0 | HEIF_ERR_CHECK(heif_context_read_from_memory_without_copy( |
594 | 0 | ctx, static_cast<const uint8_t*>(uhdr_compressed_img->data), uhdr_compressed_img->data_sz, |
595 | 0 | nullptr)) |
596 | 0 | HEIF_ERR_CHECK(heif_context_get_primary_image_handle(ctx, &base_handle)) |
597 | 0 | decoding_options = heif_decoding_options_alloc(); |
598 | 0 | out_colorspace = heif_colorspace_RGB; |
599 | 0 | out_chroma = heif_chroma_interleaved_RGBA; |
600 | 0 | HEIF_ERR_CHECK( |
601 | 0 | heif_decode_image(base_handle, &sdr_image, out_colorspace, out_chroma, decoding_options)) |
602 | 0 | nclx_err = heif_image_handle_get_nclx_color_profile(base_handle, &nclx); |
603 | 0 | if (nclx_err.code != heif_error_Ok) { |
604 | 0 | nclx_err = heif_image_get_nclx_color_profile(sdr_image, &nclx); |
605 | 0 | } |
606 | 0 | HEIF_ERR_CHECK(map_heif_chroma_vars_to_fmt(out_colorspace, out_chroma, fmt)) |
607 | 0 | sdr_width = heif_image_handle_get_width(base_handle); |
608 | 0 | sdr_height = heif_image_handle_get_height(base_handle); |
609 | 0 | sdr_cg = (nclx_err.code == heif_error_Ok && nclx != nullptr) |
610 | 0 | ? map_primaries_inverse(nclx->color_primaries) |
611 | 0 | : UHDR_CG_BT_709; |
612 | 0 | sdr_ct = (nclx_err.code == heif_error_Ok && nclx != nullptr) |
613 | 0 | ? map_transfer_inverse(nclx->transfer_characteristics) |
614 | 0 | : UHDR_CT_SRGB; |
615 | 0 | sdr_range = (nclx_err.code == heif_error_Ok && nclx != nullptr) |
616 | 0 | ? (nclx->full_range_flag ? UHDR_CR_FULL_RANGE : UHDR_CR_LIMITED_RANGE) |
617 | 0 | : UHDR_CR_FULL_RANGE; |
618 | 0 | sdr_intent = std::make_unique<uhdr_raw_image_ext_t>( |
619 | 0 | fmt, sdr_cg, sdr_ct, sdr_range, sdr_width, sdr_height, 64); |
620 | 0 | if (nclx) { |
621 | 0 | heif_nclx_color_profile_free(nclx); |
622 | 0 | nclx = nullptr; |
623 | 0 | } |
624 | 0 | HEIF_ERR_CHECK(copy_img_plane(sdr_image, heif_channel_interleaved, |
625 | 0 | sdr_intent->planes[UHDR_PLANE_PACKED], sdr_width, sdr_height, |
626 | 0 | sdr_intent->stride[UHDR_PLANE_PACKED] * 4, 4)) |
627 | 0 | if (gainmap_metadata != nullptr || output_ct != UHDR_CT_SRGB) { |
628 | 0 | HEIF_ERR_CHECK(heif_image_handle_get_gain_map_image_handle(base_handle, &gainmap_handle)) |
629 | 0 | status = heif_get_gainmap_metadata(base_handle, &metadata); |
630 | 0 | if (status.error_code != UHDR_CODEC_OK) return status; |
631 | 0 | if (gainmap_metadata != nullptr) { |
632 | 0 | std::copy(metadata.min_content_boost, metadata.min_content_boost + 3, |
633 | 0 | gainmap_metadata->min_content_boost); |
634 | 0 | std::copy(metadata.max_content_boost, metadata.max_content_boost + 3, |
635 | 0 | gainmap_metadata->max_content_boost); |
636 | 0 | std::copy(metadata.gamma, metadata.gamma + 3, gainmap_metadata->gamma); |
637 | 0 | std::copy(metadata.offset_sdr, metadata.offset_sdr + 3, gainmap_metadata->offset_sdr); |
638 | 0 | std::copy(metadata.offset_hdr, metadata.offset_hdr + 3, gainmap_metadata->offset_hdr); |
639 | 0 | gainmap_metadata->hdr_capacity_min = metadata.hdr_capacity_min; |
640 | 0 | gainmap_metadata->hdr_capacity_max = metadata.hdr_capacity_max; |
641 | 0 | gainmap_metadata->use_base_cg = metadata.use_base_cg; |
642 | 0 | } |
643 | 0 | HEIF_ERR_CHECK(heif_image_handle_get_preferred_decoding_colorspace( |
644 | 0 | gainmap_handle, &out_colorspace, &out_chroma)) |
645 | 0 | if (out_colorspace != heif_colorspace_monochrome) { |
646 | 0 | out_colorspace = heif_colorspace_RGB; |
647 | 0 | out_chroma = heif_chroma_interleaved_RGBA; |
648 | 0 | } |
649 | 0 | heif_error decode_err = heif_decode_image(gainmap_handle, &gainmap_image, out_colorspace, |
650 | 0 | out_chroma, decoding_options); |
651 | 0 | if (decode_err.code != heif_error_Ok && out_colorspace == heif_colorspace_monochrome) { |
652 | 0 | out_colorspace = heif_colorspace_RGB; |
653 | 0 | out_chroma = heif_chroma_interleaved_RGBA; |
654 | 0 | decode_err = heif_decode_image(gainmap_handle, &gainmap_image, out_colorspace, out_chroma, |
655 | 0 | decoding_options); |
656 | 0 | } |
657 | 0 | if (decode_err.code != heif_error_Ok) { |
658 | 0 | status.error_code = UHDR_CODEC_ERROR; |
659 | 0 | status.has_detail = 1; |
660 | 0 | snprintf(status.detail, sizeof status.detail, "%s", decode_err.message); |
661 | 0 | goto CleanUp; |
662 | 0 | } |
663 | 0 | nclx_err = heif_image_handle_get_nclx_color_profile(gainmap_handle, &nclx); |
664 | 0 | if (nclx_err.code != heif_error_Ok) { |
665 | 0 | nclx_err = heif_image_get_nclx_color_profile(gainmap_image, &nclx); |
666 | 0 | } |
667 | 0 | HEIF_ERR_CHECK(map_heif_chroma_vars_to_fmt(out_colorspace, out_chroma, fmt)) |
668 | 0 | gainmap_width = heif_image_handle_get_width(gainmap_handle); |
669 | 0 | gainmap_height = heif_image_handle_get_height(gainmap_handle); |
670 | 0 | gm_cg = (nclx_err.code == heif_error_Ok && nclx != nullptr) |
671 | 0 | ? map_matrix_inverse(nclx->matrix_coefficients) |
672 | 0 | : UHDR_CG_BT_709; |
673 | 0 | gm_ct = (nclx_err.code == heif_error_Ok && nclx != nullptr) |
674 | 0 | ? map_transfer_inverse(nclx->transfer_characteristics) |
675 | 0 | : UHDR_CT_SRGB; |
676 | 0 | gm_range = (nclx_err.code == heif_error_Ok && nclx != nullptr) |
677 | 0 | ? (nclx->full_range_flag ? UHDR_CR_FULL_RANGE : UHDR_CR_LIMITED_RANGE) |
678 | 0 | : UHDR_CR_FULL_RANGE; |
679 | 0 | gainmap = std::make_unique<uhdr_raw_image_ext_t>( |
680 | 0 | fmt, gm_cg, gm_ct, gm_range, gainmap_width, gainmap_height, 64); |
681 | 0 | if (nclx) { |
682 | 0 | heif_nclx_color_profile_free(nclx); |
683 | 0 | nclx = nullptr; |
684 | 0 | } |
685 | 0 | if (out_colorspace == heif_colorspace_monochrome) { |
686 | 0 | HEIF_ERR_CHECK(copy_img_plane(gainmap_image, heif_channel_Y, gainmap->planes[UHDR_PLANE_Y], |
687 | 0 | gainmap_width, gainmap_height, gainmap->stride[UHDR_PLANE_Y], |
688 | 0 | 1)) |
689 | 0 | } else { |
690 | 0 | HEIF_ERR_CHECK(copy_img_plane(gainmap_image, heif_channel_interleaved, |
691 | 0 | gainmap->planes[UHDR_PLANE_PACKED], gainmap_width, |
692 | 0 | gainmap_height, gainmap->stride[UHDR_PLANE_PACKED] * 4, 4)) |
693 | 0 | } |
694 | 0 | if (gainmap_img != nullptr) { |
695 | 0 | status = copy_raw_image(gainmap.get(), gainmap_img); |
696 | 0 | if (status.error_code != UHDR_CODEC_OK) goto CleanUp; |
697 | 0 | } |
698 | 0 | err = heif_image_handle_get_derived_image_nclx_color_profile(base_handle, &nclx); |
699 | 0 | if (err.code == heif_error_Ok) { |
700 | 0 | gainmap->cg = map_primaries_inverse(nclx->color_primaries); |
701 | 0 | gainmap->ct = map_transfer_inverse(nclx->transfer_characteristics); |
702 | 0 | gainmap->range = nclx->full_range_flag ? UHDR_CR_FULL_RANGE : UHDR_CR_LIMITED_RANGE; |
703 | 0 | heif_nclx_color_profile_free(nclx); |
704 | 0 | nclx = nullptr; |
705 | 0 | } else { |
706 | 0 | gainmap->cg = sdr_intent->cg; |
707 | 0 | gainmap->ct = sdr_intent->ct; |
708 | 0 | gainmap->range = sdr_intent->range; |
709 | 0 | } |
710 | 0 | } |
711 | | |
712 | 0 | if (output_ct != UHDR_CT_SRGB) { |
713 | 0 | status = applyGainMap(sdr_intent.get(), gainmap.get(), &metadata, output_ct, output_format, |
714 | 0 | max_display_boost, dest); |
715 | 0 | } else { |
716 | 0 | status = copy_raw_image(sdr_intent.get(), dest); |
717 | 0 | } |
718 | |
|
719 | 0 | CleanUp: |
720 | 0 | if (decoding_options) heif_decoding_options_free(decoding_options); |
721 | 0 | decoding_options = nullptr; |
722 | 0 | if (nclx) heif_nclx_color_profile_free(nclx); |
723 | 0 | nclx = nullptr; |
724 | 0 | if (sdr_image) heif_image_release(sdr_image); |
725 | 0 | sdr_image = nullptr; |
726 | 0 | if (gainmap_image) heif_image_release(gainmap_image); |
727 | 0 | gainmap_image = nullptr; |
728 | 0 | if (gainmap_handle) heif_image_handle_release(gainmap_handle); |
729 | 0 | gainmap_handle = nullptr; |
730 | 0 | if (base_handle) heif_image_handle_release(base_handle); |
731 | 0 | base_handle = nullptr; |
732 | 0 | if (ctx) heif_context_free(ctx); |
733 | 0 | ctx = nullptr; |
734 | |
|
735 | 0 | return status; |
736 | 0 | } |
737 | | |
738 | | } // namespace ultrahdr |
739 | | |
740 | | #endif |