"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 */