"Fossies" - the Fresh Open Source Software Archive

Member "libgd-2.3.2/src/gd_heif.c" (3 Mar 2021, 14598 Bytes) of package /linux/www/libgd-2.3.2.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. For more information about "gd_heif.c" see the Fossies "Dox" file reference documentation.

    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     gdIOCtx *outfile;
  251     struct heif_error err;
  252 
  253     outfile = (gdIOCtx *)userdata;
  254 
  255     gdPutBuf(data, size, outfile);
  256 
  257     err.code = heif_error_Ok;
  258     err.subcode = heif_suberror_Unspecified;
  259     err.message = "";
  260 
  261     return err;
  262 }
  263 
  264 /* returns GD_TRUE on success, GD_FALSE on failure */
  265 static int _gdImageHeifCtx(gdImagePtr im, gdIOCtx *outfile, int quality, gdHeifCodec codec, gdHeifChroma chroma)
  266 {
  267     struct heif_context *heif_ctx;
  268     struct heif_encoder *heif_enc;
  269     struct heif_image *heif_im;
  270     struct heif_writer heif_wr;
  271     struct heif_error err;
  272     uint8_t *rgba;
  273     int x, y;
  274     uint8_t *p;
  275 
  276     if (im == NULL) {
  277         return GD_FALSE;
  278     }
  279 
  280     if (codec != GD_HEIF_CODEC_HEVC && codec != GD_HEIF_CODEC_AV1) {
  281         gd_error("Unsupported format by heif");
  282         return GD_FALSE;
  283     }
  284 
  285     if (!gdImageTrueColor(im)) {
  286         gd_error("Palette image not supported by heif\n");
  287         return GD_FALSE;
  288     }
  289 
  290     if (overflow2(gdImageSX(im), 4)) {
  291         return GD_FALSE;
  292     }
  293 
  294     if (overflow2(gdImageSX(im) * 4, gdImageSY(im))) {
  295         return GD_FALSE;
  296     }
  297 
  298     heif_ctx = heif_context_alloc();
  299     if (heif_ctx == NULL) {
  300         gd_error("gd-heif could not allocate context\n");
  301         return GD_FALSE;
  302     }
  303     err = heif_context_get_encoder_for_format(heif_ctx, (enum heif_compression_format)codec, &heif_enc);
  304     if (err.code != heif_error_Ok) {
  305         gd_error("gd-heif encoder acquisition failed (missing codec support?)\n");
  306         heif_context_free(heif_ctx);
  307         return GD_FALSE;
  308     }
  309 
  310     if (quality == 200) {
  311         err = heif_encoder_set_lossless(heif_enc, GD_TRUE);
  312     } else if (quality == -1) {
  313         err = heif_encoder_set_lossy_quality(heif_enc, 80);
  314     } else {
  315         err = heif_encoder_set_lossy_quality(heif_enc, quality);
  316     }
  317     if (err.code != heif_error_Ok) {
  318         gd_error("gd-heif invalid quality number\n");
  319         heif_encoder_release(heif_enc);
  320         heif_context_free(heif_ctx);
  321         return GD_FALSE;
  322     }
  323 
  324     err = heif_encoder_set_parameter_string(heif_enc, "chroma", chroma);
  325     if (err.code != heif_error_Ok) {
  326         gd_error("gd-heif invalid chroma subsampling parameter\n");
  327         heif_encoder_release(heif_enc);
  328         heif_context_free(heif_ctx);
  329         return GD_FALSE;
  330     }
  331 
  332     err = heif_image_create(gdImageSX(im), gdImageSY(im), heif_colorspace_RGB, heif_chroma_interleaved_RGBA, &heif_im);
  333     if (err.code != heif_error_Ok) {
  334         gd_error("gd-heif image creation failed");
  335         heif_encoder_release(heif_enc);
  336         heif_context_free(heif_ctx);
  337         return GD_FALSE;
  338     }
  339 
  340     err = heif_image_add_plane(heif_im, heif_channel_interleaved, gdImageSX(im), gdImageSY(im), 32);
  341     if (err.code != heif_error_Ok) {
  342         gd_error("gd-heif cannot add image plane\n");
  343         heif_image_release(heif_im);
  344         heif_encoder_release(heif_enc);
  345         heif_context_free(heif_ctx);
  346         return GD_FALSE;
  347     }
  348 
  349     rgba = (uint8_t *)heif_image_get_plane_readonly(heif_im, heif_channel_interleaved, NULL);
  350     if (!rgba) {
  351         gd_error("gd-heif cannot get image plane\n");
  352         heif_image_release(heif_im);
  353         heif_encoder_release(heif_enc);
  354         heif_context_free(heif_ctx);
  355         return GD_FALSE;
  356     }
  357     p = rgba;
  358     for (y = 0; y < gdImageSY(im); y++) {
  359         for (x = 0; x < gdImageSX(im); x++) {
  360             int c;
  361             char a;
  362             c = im->tpixels[y][x];
  363             a = gdTrueColorGetAlpha(c);
  364             if (a == 127) {
  365                 a = 0;
  366             } else {
  367                 a = 255 - ((a << 1) + (a >> 6));
  368             }
  369             *(p++) = gdTrueColorGetRed(c);
  370             *(p++) = gdTrueColorGetGreen(c);
  371             *(p++) = gdTrueColorGetBlue(c);
  372             *(p++) = a;
  373         }
  374     }
  375     err = heif_context_encode_image(heif_ctx, heif_im, heif_enc, NULL, NULL);
  376     heif_encoder_release(heif_enc);
  377     if (err.code != heif_error_Ok) {
  378         gd_error("gd-heif encoding failed\n");
  379         heif_image_release(heif_im);
  380         heif_context_free(heif_ctx);
  381         return GD_FALSE;
  382     }
  383     heif_wr.write = _gdImageWriteHeif;
  384     heif_wr.writer_api_version = 1;
  385     err = heif_context_write(heif_ctx, &heif_wr, (void *)outfile);
  386 
  387     heif_image_release(heif_im);
  388     heif_context_free(heif_ctx);
  389 
  390     return GD_TRUE;
  391 }
  392 
  393 
  394 /*
  395   Function: gdImageHeifCtx
  396 
  397     Write the image as HEIF data via a <gdIOCtx>. See <gdImageHeifEx>
  398     for more details.
  399 
  400   Parameters:
  401 
  402     im          - The image to write.
  403     outfile     - The output sink.
  404     quality     - Image quality.
  405     codec       - The output coding format.
  406     chroma      - The output chroma subsampling format.
  407 
  408   Returns:
  409 
  410     Nothing.
  411 */
  412 BGD_DECLARE(void) gdImageHeifCtx(gdImagePtr im, gdIOCtx *outfile, int quality, gdHeifCodec codec, gdHeifChroma chroma)
  413 {
  414     _gdImageHeifCtx(im, outfile, quality, codec, chroma);
  415 }
  416 
  417 /*
  418   Function: gdImageHeifEx
  419 
  420     <gdImageHeifEx> outputs the specified image to the specified file in
  421     HEIF format. The file must be open for writing. Under MSDOS and
  422     all versions of Windows, it is important to use "wb" as opposed to
  423     simply "w" as the mode when opening the file, and under Unix there
  424     is no penalty for doing so. <gdImageHeifEx> does not close the file;
  425     your code must do so.
  426 
  427     If _quality_ is -1, a reasonable quality value (which should yield a
  428     good general quality / size tradeoff for most situations) is used. Otherwise
  429     _quality_ should be a value in the range 0-100, higher quality values
  430     usually implying both higher quality and larger image sizes or 200, for
  431     lossless codec.
  432 
  433   Variants:
  434 
  435     <gdImageHeifCtx> stores the image using a <gdIOCtx> struct.
  436 
  437     <gdImageHeifPtrEx> stores the image to RAM.
  438 
  439   Parameters:
  440 
  441     im          - The image to save.
  442     outFile     - The FILE pointer to write to.
  443     quality     - Codec quality (0-100).
  444     codec       - The output coding format.
  445     chroma      - The output chroma subsampling format.
  446 
  447   Returns:
  448 
  449     Nothing.
  450 */
  451 BGD_DECLARE(void) gdImageHeifEx(gdImagePtr im, FILE *outFile, int quality, gdHeifCodec codec, gdHeifChroma chroma)
  452 {
  453     gdIOCtx *out = gdNewFileCtx(outFile);
  454     if (out == NULL) {
  455         return;
  456     }
  457     _gdImageHeifCtx(im, out, quality, codec, chroma);
  458     out->gd_free(out);
  459 }
  460 
  461 /*
  462   Function: gdImageHeif
  463 
  464     Variant of <gdImageHeifEx> which uses the default quality (-1), the
  465     default codec (GD_HEIF_Codec_HEVC) and the default chroma
  466     subsampling (GD_HEIF_CHROMA_444).
  467 
  468   Parameters:
  469 
  470     im      - The image to save
  471     outFile - The FILE pointer to write to.
  472 
  473   Returns:
  474 
  475     Nothing.
  476 */
  477 BGD_DECLARE(void) gdImageHeif(gdImagePtr im, FILE *outFile)
  478 {
  479     gdIOCtx *out = gdNewFileCtx(outFile);
  480     if (out == NULL) {
  481         return;
  482     }
  483     _gdImageHeifCtx(im, out, -1, GD_HEIF_CODEC_HEVC, GD_HEIF_CHROMA_444);
  484     out->gd_free(out);
  485 }
  486 
  487 /*
  488   Function: gdImageHeifPtr
  489 
  490     See <gdImageHeifEx>.
  491 */
  492 BGD_DECLARE(void *) gdImageHeifPtr(gdImagePtr im, int *size)
  493 {
  494     void *rv;
  495     gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
  496     if (out == NULL) {
  497         return NULL;
  498     }
  499     if (_gdImageHeifCtx(im, out, -1, GD_HEIF_CODEC_HEVC, GD_HEIF_CHROMA_444)) {
  500         rv = gdDPExtractData(out, size);
  501     } else {
  502         rv = NULL;
  503     }
  504     out->gd_free(out);
  505 
  506     return rv;
  507 }
  508 
  509 /*
  510   Function: gdImageHeifPtrEx
  511 
  512     See <gdImageHeifEx>.
  513 */
  514 BGD_DECLARE(void *) gdImageHeifPtrEx(gdImagePtr im, int *size, int quality, gdHeifCodec codec, gdHeifChroma chroma)
  515 {
  516     void *rv;
  517     gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
  518     if (out == NULL) {
  519         return NULL;
  520     }
  521     if (_gdImageHeifCtx(im, out, quality, codec, chroma)) {
  522         rv = gdDPExtractData(out, size);
  523     } else {
  524         rv = NULL;
  525     }
  526     out->gd_free(out);
  527     return rv;
  528 }
  529 
  530 #else /* HAVE_LIBHEIF */
  531 
  532 static void _noHeifError(void)
  533 {
  534     gd_error("HEIF image support has been disabled\n");
  535 }
  536 
  537 BGD_DECLARE(gdImagePtr) gdImageCreateFromHeif(FILE *inFile)
  538 {
  539     _noHeifError();
  540     return NULL;
  541 }
  542 
  543 BGD_DECLARE(gdImagePtr) gdImageCreateFromHeifPtr(int size, void *data)
  544 {
  545     _noHeifError();
  546     return NULL;
  547 }
  548 
  549 BGD_DECLARE(gdImagePtr) gdImageCreateFromHeifCtx(gdIOCtx *infile)
  550 {
  551     _noHeifError();
  552     return NULL;
  553 }
  554 
  555 BGD_DECLARE(void) gdImageHeifCtx(gdImagePtr im, gdIOCtx *outfile, int quality, gdHeifCodec codec, gdHeifChroma chroma)
  556 {
  557     _noHeifError();
  558 }
  559 
  560 BGD_DECLARE(void) gdImageHeifEx(gdImagePtr im, FILE *outFile, int quality, gdHeifCodec codec, gdHeifChroma chroma)
  561 {
  562     _noHeifError();
  563 }
  564 
  565 BGD_DECLARE(void) gdImageHeif(gdImagePtr im, FILE *outFile)
  566 {
  567     _noHeifError();
  568 }
  569 
  570 BGD_DECLARE(void *) gdImageHeifPtr(gdImagePtr im, int *size)
  571 {
  572     _noHeifError();
  573     return NULL;
  574 }
  575 
  576 BGD_DECLARE(void *) gdImageHeifPtrEx(gdImagePtr im, int *size, int quality, gdHeifCodec codec, gdHeifChroma chroma)
  577 {
  578     _noHeifError();
  579     return NULL;
  580 }
  581 
  582 #endif /* HAVE_LIBHEIF */