Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
imageio_heif.c
Go to the documentation of this file.
1/*
2 * This file is part of darktable,
3 * Copyright (C) 2021 Daniel Vogelbacher.
4 * Copyright (C) 2021 Pascal Obry.
5 * Copyright (C) 2022 Martin Bařinka.
6 * Copyright (C) 2022 Miloš Komarčević.
7 * Copyright (C) 2022 Philipp Lutz.
8 * Copyright (C) 2023 Alynx Zhou.
9 *
10 * darktable is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * darktable is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with darktable. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24#include "common/image.h"
25#include <libheif/heif.h>
26#if LIBHEIF_HAVE_VERSION(1, 17, 0)
27#include <libheif/heif_properties.h>
28#endif
29
30#ifdef HAVE_CONFIG_H
31#include "config.h"
32#endif
33
34#include <inttypes.h>
35#include <memory.h>
36#include <stdio.h>
37#include <strings.h>
38
39#include "control/control.h"
40#include "common/colorspaces.h"
41#include "common/darktable.h"
42#include "common/exif.h"
43#include "control/conf.h"
44#include "develop/develop.h"
45#include "imageio.h"
46#include "imageio_heif.h"
47
48
50 const char *filename,
52{
54 struct heif_error err;
55 struct heif_image_handle* handle = NULL;
56 struct heif_image* heif_img = NULL;
57
58 struct heif_context* ctx = heif_context_alloc();
59 if(IS_NULL_PTR(ctx))
60 {
62 "Unable to allocate HEIF context\n");
64 }
65
66 err = heif_context_read_from_file(ctx, filename, NULL);
67 if(err.code != heif_error_Ok)
68 {
70 "Failed to read HEIF file [%s]\n",
71 filename);
73 switch(err.code)
74 {
75 case heif_error_Unsupported_filetype:
76 case heif_error_Unsupported_feature:
77 fprintf(stderr, "[imageio_heif] Unsupported file: `%s'! Is your libheif compiled with HEVC support?\n", filename);
78 break;
79 default:
80 break;
81 }
82
83 goto out;
84 }
85
86 // HEIF may contain multiple images or none.
87 const int num_images = heif_context_get_number_of_top_level_images(ctx);
88 if(num_images == 0)
89 {
91 "No images found in HEIF file [%s]\n",
92 filename);
94 goto out;
95 }
96
97 // We can only process a single image
98 err = heif_context_get_primary_image_handle(ctx, &handle);
99 if(err.code != heif_error_Ok)
100 {
102 "Failed to read primary image from HEIF file [%s]\n",
103 filename);
105 goto out;
106 }
107
108 // Darktable only supports LITTLE_ENDIAN systems, so RRGGBB_LE should be fine
109 err = heif_decode_image(handle, &heif_img, heif_colorspace_RGB, heif_chroma_interleaved_RRGGBB_LE, NULL);
110 if(err.code != heif_error_Ok)
111 {
113 "Failed to decode HEIF file [%s]\n",
114 filename);
116 goto out;
117 }
118
119 int rowbytes = 0;
120 const uint8_t* data = heif_image_get_plane_readonly(heif_img, heif_channel_interleaved, &rowbytes);
121 const size_t width = heif_image_handle_get_width(handle);
122 const size_t height = heif_image_handle_get_height(handle);
123
124 /* Initialize cached image buffer */
125 img->width = width;
126 img->height = height;
127
128 img->dsc.channels = 4;
129 img->dsc.datatype = TYPE_FLOAT;
130 img->dsc.bpp = 4 * sizeof(float);
131 img->dsc.cst = IOP_CS_RGB;
132 img->dsc.filters = 0u;
133 img->flags &= ~DT_IMAGE_RAW;
134 img->flags &= ~DT_IMAGE_S_RAW;
135
136 // Get original pixel values bit depth by querying the luma channel depth (this may differ from decoded values bit depth)
137 const int original_values_bit_depth = heif_image_handle_get_luma_bits_per_pixel(handle);
139 "Bit depth: '%d' for HEIF image [%s]\n",
140 original_values_bit_depth,
141 filename);
142
143 if(original_values_bit_depth > 8)
144 {
145 img->flags |= DT_IMAGE_HDR;
146 img->flags &= ~DT_IMAGE_LDR;
147 }
148 else
149 {
150 img->flags |= DT_IMAGE_LDR;
151 img->flags &= ~DT_IMAGE_HDR;
152 }
153
154 img->loader = LOADER_HEIF;
155
156 if(IS_NULL_PTR(mbuf))
157 {
158 ret = DT_IMAGEIO_OK;
159 goto out;
160 }
161
162 float *mipbuf = (float *)dt_mipmap_cache_alloc(mbuf, img);
163 if(IS_NULL_PTR(mipbuf))
164 {
166 "Failed to allocate mipmap buffer for HEIF image [%s]\n",
167 filename);
169 goto out;
170 }
171 // Get decoded pixel values bit depth (this is used to scale values to [0..1] range)
172 const int decoded_values_bit_depth = heif_image_get_bits_per_pixel_range(heif_img, heif_channel_interleaved);
173
174 float max_channel_f = (float)((1 << decoded_values_bit_depth) - 1);
175
176 const uint8_t *const restrict in = (const uint8_t *)data;
177 __OMP_PARALLEL_FOR_SIMD__(collapse(2))
178 for(size_t y = 0; y < height; y++)
179 {
180 for(size_t x = 0; x < width; x++)
181 {
182 uint16_t *in_pixel = (uint16_t *)&in[(y * rowbytes) + (3 * sizeof(uint16_t) * x)];
183 float *out_pixel = &mipbuf[(size_t)4 * ((y * width) + x)];
184
185 /* max_channel_f is 1023.0f for 10bit */
186 out_pixel[0] = ((float)in_pixel[0]) * (1.0f / max_channel_f);
187 out_pixel[1] = ((float)in_pixel[1]) * (1.0f / max_channel_f);
188 out_pixel[2] = ((float)in_pixel[2]) * (1.0f / max_channel_f);
189 out_pixel[3] = 0.0f; /* alpha */
190 }
191 }
192
193 /* Get the ICC profile if available */
194 size_t icc_size = heif_image_handle_get_raw_color_profile_size(handle);
195 if(icc_size)
196 {
197 img->profile = (uint8_t *)g_malloc0(icc_size);
198 heif_image_handle_get_raw_color_profile(handle, img->profile);
199 img->profile_size = icc_size;
200 }
201 ret = DT_IMAGEIO_OK;
202
203out:
204 // cleanup handles
205 if(heif_img)
206 {
207 heif_image_release(heif_img);
208 }
209 if(handle)
210 {
211 heif_image_handle_release(handle);
212 }
213 heif_context_free(ctx);
214
215 return ret;
216}
217
218
219int dt_imageio_heif_read_profile(const char *filename,
220 uint8_t **out,
222{
223 /* set default return values */
224 int size = 0;
225 *out = NULL;
226 cicp->color_primaries = (uint16_t)heif_color_primaries_unspecified;
227 cicp->transfer_characteristics = (uint16_t)heif_transfer_characteristic_unspecified;
228 cicp->matrix_coefficients = (uint16_t)heif_matrix_coefficients_unspecified;
229
230 struct heif_image_handle* handle = NULL;
231 struct heif_error err;
232 struct heif_color_profile_nclx *profile_info_nclx = NULL;
233 size_t icc_size = 0;
234 uint8_t *icc_data = NULL;
235
236 struct heif_context* ctx = heif_context_alloc();
237 if(IS_NULL_PTR(ctx))
238 {
240 "Unable to allocate HEIF context\n");
241 goto out;
242 }
243
244 err = heif_context_read_from_file(ctx, filename, NULL);
245 if(err.code != heif_error_Ok)
246 {
248 "Failed to read HEIF file [%s]\n",
249 filename);
250 goto out;
251 }
252
253 // HEIF may contain multiple images or none.
254 const int num_images = heif_context_get_number_of_top_level_images(ctx);
255 if(num_images == 0)
256 {
258 "No images found in HEIF file [%s]\n",
259 filename);
260 goto out;
261 }
262
263 // We can only process a single image
264 err = heif_context_get_primary_image_handle(ctx, &handle);
265 if(err.code != heif_error_Ok)
266 {
268 "Failed to read primary image from HEIF file [%s]\n",
269 filename);
270 goto out;
271 }
272
273 // Get profile information from HEIF file
274 enum heif_color_profile_type profile_type = heif_image_handle_get_color_profile_type(handle);
275
276 switch (profile_type)
277 {
278 case heif_color_profile_type_nclx:
280 "Found NCLX color profile for HEIF file [%s]\n",
281 filename);
282 err = heif_image_handle_get_nclx_color_profile(handle, &profile_info_nclx);
283 if(err.code != heif_error_Ok)
284 {
286 "Failed to get NCLX color profile data from HEIF file [%s]\n",
287 filename);
288 goto out;
289 }
290 cicp->color_primaries = (uint16_t)profile_info_nclx->color_primaries;
291 cicp->transfer_characteristics = (uint16_t)profile_info_nclx->transfer_characteristics;
292 cicp->matrix_coefficients = (uint16_t)profile_info_nclx->matrix_coefficients;
293
294 /* fix up mistagged legacy AVIFs */
295 if(profile_info_nclx->color_primaries == heif_color_primaries_ITU_R_BT_709_5)
296 {
297 gboolean over = FALSE;
298 /* mistagged Rec. 709 AVIFs exported before dt 3.6 */
299 if(profile_info_nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_470_6_System_M
300 && profile_info_nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_709_5)
301 {
302 /* must be actual Rec. 709 instead of 2.2 gamma*/
303 cicp->transfer_characteristics = (uint16_t)heif_transfer_characteristic_ITU_R_BT_709_5;
304 over = TRUE;
305 }
306
307 if(over)
308 {
309 dt_print(DT_DEBUG_IMAGEIO, "Overriding nclx color profile for HEIF file `%s': 1/%d/%d to 1/%d/%d\n",
310 filename, profile_info_nclx->transfer_characteristics, profile_info_nclx->matrix_coefficients,
312 }
313 }
314 break; /* heif_color_profile_type_nclx */
315
316 case heif_color_profile_type_rICC:
317 case heif_color_profile_type_prof:
318 icc_size = heif_image_handle_get_raw_color_profile_size(handle);
319 if(icc_size == 0)
320 {
321 // image has no embedded ICC profile
322 goto out;
323 }
324 icc_data = (uint8_t *)g_malloc0(sizeof(uint8_t) * icc_size);
325 err = heif_image_handle_get_raw_color_profile(handle, icc_data);
326 if(err.code != heif_error_Ok)
327 {
329 "Failed to read embedded ICC profile from HEIF image [%s]\n",
330 filename);
331 dt_free(icc_data);
332 goto out;
333 }
334 size = icc_size;
335 *out = icc_data;
336 break; /* heif_color_profile_type_rICC / heif_color_profile_type_prof */
337
338 case heif_color_profile_type_not_present:
340 "No color profile for HEIF file [%s]\n",
341 filename);
342 break; /* heif_color_profile_type_not_present */
343
344 default:
346 "Unknown color profile data from HEIF file [%s]\n",
347 filename);
348 break;
349 }
350
351out:
352 // cleanup handles
353 if(profile_info_nclx)
354 {
355 heif_nclx_color_profile_free(profile_info_nclx);
356 }
357 if(handle)
358 {
359 heif_image_handle_release(handle);
360 }
361 heif_context_free(ctx);
362
363 return size;
364}
365
366// clang-format off
367// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
368// vim: shiftwidth=2 expandtab tabstop=2 cindent
369// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
370// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
@ IOP_CS_RGB
const dt_colormatrix_t dt_aligned_pixel_t out
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
@ DT_DEBUG_IMAGEIO
Definition darktable.h:733
#define dt_free(ptr)
Definition darktable.h:456
#define __OMP_PARALLEL_FOR_SIMD__(...)
Definition darktable.h:259
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
@ TYPE_FLOAT
Definition format.h:46
dt_imageio_retval_t
Definition image.h:78
@ DT_IMAGEIO_OK
Definition image.h:79
@ DT_IMAGEIO_CACHE_FULL
Definition image.h:82
@ DT_IMAGEIO_FILE_CORRUPTED
Definition image.h:81
@ DT_IMAGE_HDR
Definition image.h:113
@ DT_IMAGE_LDR
Definition image.h:109
@ LOADER_HEIF
Definition image.h:235
int dt_imageio_heif_read_profile(const char *filename, uint8_t **out, dt_colorspaces_cicp_t *cicp)
dt_imageio_retval_t dt_imageio_open_heif(dt_image_t *img, const char *filename, dt_mipmap_buffer_t *mbuf)
static const float x
void * dt_mipmap_cache_alloc(dt_mipmap_buffer_t *buf, const dt_image_t *img)
size_t size
Definition mipmap_cache.c:3
dt_colorspaces_cicp_matrix_coefficients_t matrix_coefficients
dt_colorspaces_cicp_transfer_characteristics_t transfer_characteristics
dt_colorspaces_cicp_color_primaries_t color_primaries
int32_t height
Definition image.h:315
dt_image_loader_t loader
Definition image.h:335
int32_t flags
Definition image.h:319
int32_t width
Definition image.h:315
uint32_t profile_size
Definition image.h:341
dt_iop_buffer_dsc_t dsc
Definition image.h:337
uint8_t * profile
Definition image.h:340
uint32_t filters
Definition format.h:60
unsigned int channels
Definition format.h:54
dt_iop_buffer_type_t datatype
Definition format.h:56