"Fossies" - the Fresh Open Source Software Archive 
Member "libgd-2.3.3/src/gd_heif.c" (11 Sep 2021, 14718 Bytes) of package /linux/www/libgd-2.3.3.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
1 /**
2 * File: HEIF IO
3 *
4 * Read and write HEIF images.
5 */
6
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif /* HAVE_CONFIG_H */
10
11
12 #include <stdio.h>
13 #include <math.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include "gd.h"
17 #include "gd_errors.h"
18 #include "gdhelpers.h"
19
20 #ifdef HAVE_LIBHEIF
21 #include <libheif/heif.h>
22
23 #define GD_HEIF_ALLOC_STEP (4*1024)
24 #define GD_HEIF_HEADER 12
25
26 typedef enum gd_heif_brand {
27 GD_HEIF_BRAND_AVIF = 1,
28 GD_HEIF_BRAND_MIF1 = 2,
29 GD_HEIF_BRAND_HEIC = 4,
30 GD_HEIF_BRAND_HEIX = 8,
31 } gd_heif_brand;
32
33 /*
34 Function: gdImageCreateFromHeif
35
36 <gdImageCreateFromHeif> is called to load truecolor images from
37 HEIF format files. Invoke <gdImageCreateFromHeif> with an
38 already opened pointer to a file containing the desired
39 image. <gdImageCreateFromHeif> returns a <gdImagePtr> to the new
40 truecolor image, or NULL if unable to load the image (most often
41 because the file is corrupt or does not contain a HEIF
42 image). <gdImageCreateFromHeif> does not close the file.
43
44 You can inspect the sx and sy members of the image to determine
45 its size. The image must eventually be destroyed using
46 <gdImageDestroy>.
47
48 *The returned image is always a truecolor image.*
49
50 Parameters:
51
52 infile - The input FILE pointer.
53
54 Returns:
55
56 A pointer to the new *truecolor* image. This will need to be
57 destroyed with <gdImageDestroy> once it is no longer needed.
58
59 On error, returns NULL.
60 */
61 BGD_DECLARE(gdImagePtr) gdImageCreateFromHeif(FILE *inFile)
62 {
63 gdImagePtr im;
64 gdIOCtx *in = gdNewFileCtx(inFile);
65
66 if (!in)
67 return NULL;
68 im = gdImageCreateFromHeifCtx(in);
69 in->gd_free(in);
70
71 return im;
72 }
73
74 /*
75 Function: gdImageCreateFromHeifPtr
76
77 See <gdImageCreateFromHeif>.
78
79 Parameters:
80
81 size - size of HEIF data in bytes.
82 data - pointer to HEIF data.
83 */
84 BGD_DECLARE(gdImagePtr) gdImageCreateFromHeifPtr(int size, void *data)
85 {
86 gdImagePtr im;
87 gdIOCtx *in = gdNewDynamicCtxEx(size, data, 0);
88
89 if (!in)
90 return NULL;
91 im = gdImageCreateFromHeifCtx(in);
92 in->gd_free(in);
93
94 return im;
95 }
96
97 static int _gdHeifCheckBrand(unsigned char *magic, gd_heif_brand expected_brand)
98 {
99 if (memcmp(magic + 4, "ftyp", 4) != 0)
100 return GD_FALSE;
101 if (memcmp(magic + 8, "avif", 4) == 0 && expected_brand & GD_HEIF_BRAND_AVIF)
102 return GD_TRUE;
103 if (memcmp(magic + 8, "heic", 4) == 0 && expected_brand & GD_HEIF_BRAND_HEIC)
104 return GD_TRUE;
105 if (memcmp(magic + 8, "heix", 4) == 0 && expected_brand & GD_HEIF_BRAND_HEIX)
106 return GD_TRUE;
107 if (memcmp(magic + 8, "mif1", 4) == 0 && expected_brand & GD_HEIF_BRAND_MIF1)
108 return GD_TRUE;
109
110 return GD_FALSE;
111 }
112
113 static gdImagePtr _gdImageCreateFromHeifCtx(gdIOCtx *infile, gd_heif_brand expected_brand)
114 {
115 struct heif_context *heif_ctx;
116 struct heif_decoding_options *heif_dec_opts;
117 struct heif_image_handle *heif_imhandle;
118 struct heif_image *heif_im;
119 struct heif_error err;
120 int width, height;
121 uint8_t *filedata = NULL;
122 uint8_t *rgba = NULL;
123 unsigned char *read, *temp, magic[GD_HEIF_HEADER];
124 int magic_len;
125 size_t size = 0, n = GD_HEIF_ALLOC_STEP;
126 gdImagePtr im;
127 int x, y;
128 uint8_t *p;
129
130 magic_len = gdGetBuf(magic, GD_HEIF_HEADER, infile);
131 if (magic_len != GD_HEIF_HEADER || !_gdHeifCheckBrand(magic, expected_brand)) {
132 gd_error("gd-heif incorrect type of file\n");
133 return NULL;
134 }
135 gdSeek(infile, 0);
136
137 while (n == GD_HEIF_ALLOC_STEP) {
138 temp = gdRealloc(filedata, size + GD_HEIF_ALLOC_STEP);
139 if (temp) {
140 filedata = temp;
141 read = temp + size;
142 } else {
143 gdFree(filedata);
144 gd_error("gd-heif decode realloc failed\n");
145 return NULL;
146 }
147
148 n = gdGetBuf(read, GD_HEIF_ALLOC_STEP, infile);
149 if (n > 0) {
150 size += n;
151 }
152 }
153
154 heif_ctx = heif_context_alloc();
155 if (heif_ctx == NULL) {
156 gd_error("gd-heif could not allocate context\n");
157 gdFree(filedata);
158 return NULL;
159 }
160 err = heif_context_read_from_memory_without_copy(heif_ctx, filedata, size, NULL);
161 if (err.code != heif_error_Ok) {
162 gd_error("gd-heif context creation failed\n");
163 gdFree(filedata);
164 heif_context_free(heif_ctx);
165 return NULL;
166 }
167
168 heif_imhandle = NULL;
169 err = heif_context_get_primary_image_handle(heif_ctx, &heif_imhandle);
170 if (err.code != heif_error_Ok) {
171 gd_error("gd-heif cannot retreive handle\n");
172 gdFree(filedata);
173 heif_context_free(heif_ctx);
174 return NULL;
175 }
176
177 heif_im = NULL;
178 heif_dec_opts = heif_decoding_options_alloc();
179 if (heif_dec_opts == NULL) {
180 gd_error("gd-heif could not allocate decode options\n");
181 gdFree(filedata);
182 heif_image_handle_release(heif_imhandle);
183 heif_context_free(heif_ctx);
184 return NULL;
185 }
186
187 heif_dec_opts->convert_hdr_to_8bit = GD_TRUE;
188 heif_dec_opts->ignore_transformations = GD_TRUE;
189 err = heif_decode_image(heif_imhandle, &heif_im, heif_colorspace_RGB, heif_chroma_interleaved_RGBA, heif_dec_opts);
190 heif_decoding_options_free(heif_dec_opts);
191 if (err.code != heif_error_Ok) {
192 gd_error("gd-heif decoding failed\n");
193 gdFree(filedata);
194 heif_image_handle_release(heif_imhandle);
195 heif_context_free(heif_ctx);
196 return NULL;
197 }
198
199 width = heif_image_get_width(heif_im, heif_channel_interleaved);
200 height = heif_image_get_height(heif_im, heif_channel_interleaved);
201
202 im = gdImageCreateTrueColor(width, height);
203 if (!im) {
204 gdFree(filedata);
205 heif_image_release(heif_im);
206 heif_image_handle_release(heif_imhandle);
207 heif_context_free(heif_ctx);
208 return NULL;
209 }
210 rgba = (uint8_t *)heif_image_get_plane_readonly(heif_im, heif_channel_interleaved, NULL);
211 if (!rgba) {
212 gd_error("gd-heif cannot get image plane\n");
213 gdFree(filedata);
214 heif_image_release(heif_im);
215 heif_image_handle_release(heif_imhandle);
216 heif_context_free(heif_ctx);
217 gdImageDestroy(im);
218 return NULL;
219 }
220 for (y = 0, p = rgba; y < height; y++) {
221 for (x = 0; x < width; x++) {
222 uint8_t r = *(p++);
223 uint8_t g = *(p++);
224 uint8_t b = *(p++);
225 uint8_t a = gdAlphaMax - (*(p++) >> 1);
226 im->tpixels[y][x] = gdTrueColorAlpha(r, g, b, a);
227 }
228 }
229 gdFree(filedata);
230 heif_image_release(heif_im);
231 heif_image_handle_release(heif_imhandle);
232 heif_context_free(heif_ctx);
233
234 return im;
235 }
236
237 /*
238 Function: gdImageCreateFromHeifCtx
239
240 See <gdImageCreateFromHeif>.
241 */
242 BGD_DECLARE(gdImagePtr) gdImageCreateFromHeifCtx(gdIOCtx *infile)
243 {
244 return _gdImageCreateFromHeifCtx(infile, GD_HEIF_BRAND_AVIF | GD_HEIF_BRAND_MIF1 | GD_HEIF_BRAND_HEIC | GD_HEIF_BRAND_HEIX);
245 }
246
247
248 static struct heif_error _gdImageWriteHeif(struct heif_context *heif_ctx, const void *data, size_t size, void *userdata)
249 {
250 ARG_NOT_USED(heif_ctx);
251 gdIOCtx *outfile;
252 struct heif_error err;
253
254 outfile = (gdIOCtx *)userdata;
255
256 gdPutBuf(data, size, outfile);
257
258 err.code = heif_error_Ok;
259 err.subcode = heif_suberror_Unspecified;
260 err.message = "";
261
262 return err;
263 }
264
265 /* returns GD_TRUE on success, GD_FALSE on failure */
266 static int _gdImageHeifCtx(gdImagePtr im, gdIOCtx *outfile, int quality, gdHeifCodec codec, gdHeifChroma chroma)
267 {
268 struct heif_context *heif_ctx;
269 struct heif_encoder *heif_enc;
270 struct heif_image *heif_im;
271 struct heif_writer heif_wr;
272 struct heif_error err;
273 uint8_t *rgba;
274 int x, y;
275 uint8_t *p;
276
277 if (im == NULL) {
278 return GD_FALSE;
279 }
280
281 if (codec != GD_HEIF_CODEC_HEVC && codec != GD_HEIF_CODEC_AV1) {
282 gd_error("Unsupported format by heif");
283 return GD_FALSE;
284 }
285
286 if (!gdImageTrueColor(im)) {
287 gd_error("Palette image not supported by heif\n");
288 return GD_FALSE;
289 }
290
291 if (overflow2(gdImageSX(im), 4)) {
292 return GD_FALSE;
293 }
294
295 if (overflow2(gdImageSX(im) * 4, gdImageSY(im))) {
296 return GD_FALSE;
297 }
298
299 heif_ctx = heif_context_alloc();
300 if (heif_ctx == NULL) {
301 gd_error("gd-heif could not allocate context\n");
302 return GD_FALSE;
303 }
304 err = heif_context_get_encoder_for_format(heif_ctx, (enum heif_compression_format)codec, &heif_enc);
305 if (err.code != heif_error_Ok) {
306 gd_error("gd-heif encoder acquisition failed (missing codec support?)\n");
307 heif_context_free(heif_ctx);
308 return GD_FALSE;
309 }
310
311 if (quality == 200) {
312 err = heif_encoder_set_lossless(heif_enc, GD_TRUE);
313 } else if (quality == -1) {
314 err = heif_encoder_set_lossy_quality(heif_enc, 80);
315 } else {
316 err = heif_encoder_set_lossy_quality(heif_enc, quality);
317 }
318 if (err.code != heif_error_Ok) {
319 gd_error("gd-heif invalid quality number\n");
320 heif_encoder_release(heif_enc);
321 heif_context_free(heif_ctx);
322 return GD_FALSE;
323 }
324
325 if (heif_get_version_number_major() >= 1 && heif_get_version_number_minor() >= 9) {
326 err = heif_encoder_set_parameter_string(heif_enc, "chroma", chroma);
327 if (err.code != heif_error_Ok) {
328 gd_error("gd-heif invalid chroma subsampling parameter\n");
329 heif_encoder_release(heif_enc);
330 heif_context_free(heif_ctx);
331 return GD_FALSE;
332 }
333 }
334
335 err = heif_image_create(gdImageSX(im), gdImageSY(im), heif_colorspace_RGB, heif_chroma_interleaved_RGBA, &heif_im);
336 if (err.code != heif_error_Ok) {
337 gd_error("gd-heif image creation failed");
338 heif_encoder_release(heif_enc);
339 heif_context_free(heif_ctx);
340 return GD_FALSE;
341 }
342
343 err = heif_image_add_plane(heif_im, heif_channel_interleaved, gdImageSX(im), gdImageSY(im), 32);
344 if (err.code != heif_error_Ok) {
345 gd_error("gd-heif cannot add image plane\n");
346 heif_image_release(heif_im);
347 heif_encoder_release(heif_enc);
348 heif_context_free(heif_ctx);
349 return GD_FALSE;
350 }
351
352 rgba = (uint8_t *)heif_image_get_plane_readonly(heif_im, heif_channel_interleaved, NULL);
353 if (!rgba) {
354 gd_error("gd-heif cannot get image plane\n");
355 heif_image_release(heif_im);
356 heif_encoder_release(heif_enc);
357 heif_context_free(heif_ctx);
358 return GD_FALSE;
359 }
360 p = rgba;
361 for (y = 0; y < gdImageSY(im); y++) {
362 for (x = 0; x < gdImageSX(im); x++) {
363 int c;
364 char a;
365 c = im->tpixels[y][x];
366 a = gdTrueColorGetAlpha(c);
367 if (a == 127) {
368 a = 0;
369 } else {
370 a = 255 - ((a << 1) + (a >> 6));
371 }
372 *(p++) = gdTrueColorGetRed(c);
373 *(p++) = gdTrueColorGetGreen(c);
374 *(p++) = gdTrueColorGetBlue(c);
375 *(p++) = a;
376 }
377 }
378 err = heif_context_encode_image(heif_ctx, heif_im, heif_enc, NULL, NULL);
379 heif_encoder_release(heif_enc);
380 if (err.code != heif_error_Ok) {
381 gd_error("gd-heif encoding failed\n");
382 heif_image_release(heif_im);
383 heif_context_free(heif_ctx);
384 return GD_FALSE;
385 }
386 heif_wr.write = _gdImageWriteHeif;
387 heif_wr.writer_api_version = 1;
388 err = heif_context_write(heif_ctx, &heif_wr, (void *)outfile);
389
390 heif_image_release(heif_im);
391 heif_context_free(heif_ctx);
392
393 return GD_TRUE;
394 }
395
396
397 /*
398 Function: gdImageHeifCtx
399
400 Write the image as HEIF data via a <gdIOCtx>. See <gdImageHeifEx>
401 for more details.
402
403 Parameters:
404
405 im - The image to write.
406 outfile - The output sink.
407 quality - Image quality.
408 codec - The output coding format.
409 chroma - The output chroma subsampling format.
410
411 Returns:
412
413 Nothing.
414 */
415 BGD_DECLARE(void) gdImageHeifCtx(gdImagePtr im, gdIOCtx *outfile, int quality, gdHeifCodec codec, gdHeifChroma chroma)
416 {
417 _gdImageHeifCtx(im, outfile, quality, codec, chroma);
418 }
419
420 /*
421 Function: gdImageHeifEx
422
423 <gdImageHeifEx> outputs the specified image to the specified file in
424 HEIF format. The file must be open for writing. Under MSDOS and
425 all versions of Windows, it is important to use "wb" as opposed to
426 simply "w" as the mode when opening the file, and under Unix there
427 is no penalty for doing so. <gdImageHeifEx> does not close the file;
428 your code must do so.
429
430 If _quality_ is -1, a reasonable quality value (which should yield a
431 good general quality / size tradeoff for most situations) is used. Otherwise
432 _quality_ should be a value in the range 0-100, higher quality values
433 usually implying both higher quality and larger image sizes or 200, for
434 lossless codec.
435
436 Variants:
437
438 <gdImageHeifCtx> stores the image using a <gdIOCtx> struct.
439
440 <gdImageHeifPtrEx> stores the image to RAM.
441
442 Parameters:
443
444 im - The image to save.
445 outFile - The FILE pointer to write to.
446 quality - Codec quality (0-100).
447 codec - The output coding format.
448 chroma - The output chroma subsampling format.
449
450 Returns:
451
452 Nothing.
453 */
454 BGD_DECLARE(void) gdImageHeifEx(gdImagePtr im, FILE *outFile, int quality, gdHeifCodec codec, gdHeifChroma chroma)
455 {
456 gdIOCtx *out = gdNewFileCtx(outFile);
457 if (out == NULL) {
458 return;
459 }
460 _gdImageHeifCtx(im, out, quality, codec, chroma);
461 out->gd_free(out);
462 }
463
464 /*
465 Function: gdImageHeif
466
467 Variant of <gdImageHeifEx> which uses the default quality (-1), the
468 default codec (GD_HEIF_Codec_HEVC) and the default chroma
469 subsampling (GD_HEIF_CHROMA_444).
470
471 Parameters:
472
473 im - The image to save
474 outFile - The FILE pointer to write to.
475
476 Returns:
477
478 Nothing.
479 */
480 BGD_DECLARE(void) gdImageHeif(gdImagePtr im, FILE *outFile)
481 {
482 gdIOCtx *out = gdNewFileCtx(outFile);
483 if (out == NULL) {
484 return;
485 }
486 _gdImageHeifCtx(im, out, -1, GD_HEIF_CODEC_HEVC, GD_HEIF_CHROMA_444);
487 out->gd_free(out);
488 }
489
490 /*
491 Function: gdImageHeifPtr
492
493 See <gdImageHeifEx>.
494 */
495 BGD_DECLARE(void *) gdImageHeifPtr(gdImagePtr im, int *size)
496 {
497 void *rv;
498 gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
499 if (out == NULL) {
500 return NULL;
501 }
502 if (_gdImageHeifCtx(im, out, -1, GD_HEIF_CODEC_HEVC, GD_HEIF_CHROMA_444)) {
503 rv = gdDPExtractData(out, size);
504 } else {
505 rv = NULL;
506 }
507 out->gd_free(out);
508
509 return rv;
510 }
511
512 /*
513 Function: gdImageHeifPtrEx
514
515 See <gdImageHeifEx>.
516 */
517 BGD_DECLARE(void *) gdImageHeifPtrEx(gdImagePtr im, int *size, int quality, gdHeifCodec codec, gdHeifChroma chroma)
518 {
519 void *rv;
520 gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
521 if (out == NULL) {
522 return NULL;
523 }
524 if (_gdImageHeifCtx(im, out, quality, codec, chroma)) {
525 rv = gdDPExtractData(out, size);
526 } else {
527 rv = NULL;
528 }
529 out->gd_free(out);
530 return rv;
531 }
532
533 #else /* HAVE_LIBHEIF */
534
535 static void _noHeifError(void)
536 {
537 gd_error("HEIF image support has been disabled\n");
538 }
539
540 BGD_DECLARE(gdImagePtr) gdImageCreateFromHeif(FILE *inFile)
541 {
542 _noHeifError();
543 return NULL;
544 }
545
546 BGD_DECLARE(gdImagePtr) gdImageCreateFromHeifPtr(int size, void *data)
547 {
548 _noHeifError();
549 return NULL;
550 }
551
552 BGD_DECLARE(gdImagePtr) gdImageCreateFromHeifCtx(gdIOCtx *infile)
553 {
554 _noHeifError();
555 return NULL;
556 }
557
558 BGD_DECLARE(void) gdImageHeifCtx(gdImagePtr im, gdIOCtx *outfile, int quality, gdHeifCodec codec, gdHeifChroma chroma)
559 {
560 _noHeifError();
561 }
562
563 BGD_DECLARE(void) gdImageHeifEx(gdImagePtr im, FILE *outFile, int quality, gdHeifCodec codec, gdHeifChroma chroma)
564 {
565 _noHeifError();
566 }
567
568 BGD_DECLARE(void) gdImageHeif(gdImagePtr im, FILE *outFile)
569 {
570 _noHeifError();
571 }
572
573 BGD_DECLARE(void *) gdImageHeifPtr(gdImagePtr im, int *size)
574 {
575 _noHeifError();
576 return NULL;
577 }
578
579 BGD_DECLARE(void *) gdImageHeifPtrEx(gdImagePtr im, int *size, int quality, gdHeifCodec codec, gdHeifChroma chroma)
580 {
581 _noHeifError();
582 return NULL;
583 }
584
585 #endif /* HAVE_LIBHEIF */