legacy-imlib2/src/modules/loaders/loader_heif.c

187 lines
5.4 KiB
C

/*
* Loader for HEIF images.
*
* Only loads the primary image for any file, whether it be a still image or an
* image sequence.
*/
#include "loader_common.h"
#include <libheif/heif.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#define HEIF_BYTES_TO_CHECK 12L
#define HEIF_8BIT_TO_PIXEL_ARGB(plane, has_alpha) \
PIXEL_ARGB((has_alpha) ? (plane)[3] : 0xff, (plane)[0], (plane)[1], (plane)[2])
int
load2(ImlibImage * im, int load_data)
{
int rc;
int encoded_fd;
struct stat stats;
void *address = MAP_FAILED;
uint8_t *encoded_data = NULL;
int img_has_alpha;
int stride = 0;
int bytes_per_px;
int y, x;
struct heif_error error;
struct heif_context *ctx = NULL;
struct heif_image_handle *img_handle = NULL;
struct heif_image *img_data = NULL;
struct heif_decoding_options *decode_opts = NULL;
DATA32 *ptr;
const uint8_t *img_plane = NULL;
rc = LOAD_FAIL;
encoded_fd = fileno(im->fp);
if (encoded_fd == -1)
goto quit;
if (fstat(encoded_fd, &stats) == -1)
goto quit;
/* input data needs to be atleast 12 bytes */
if (stats.st_size < HEIF_BYTES_TO_CHECK)
goto quit;
address = mmap(0, stats.st_size, PROT_READ, MAP_PRIVATE, encoded_fd, 0);
if (address == MAP_FAILED)
goto quit;
/* Convert address to array of uint8_t. */
encoded_data = (uint8_t *) address;
/* check signature */
switch (heif_check_filetype(encoded_data, stats.st_size))
{
case heif_filetype_no:
case heif_filetype_yes_unsupported:
goto quit;
/* Have to let heif_filetype_maybe through because mif1 brand gives
* heif_filetype_maybe on check */
case heif_filetype_maybe:
case heif_filetype_yes_supported:
break;
}
ctx = heif_context_alloc();
if (!ctx)
goto quit;
error =
heif_context_read_from_memory_without_copy(ctx, encoded_data,
stats.st_size, NULL);
if (error.code != heif_error_Ok)
goto quit;
error = heif_context_get_primary_image_handle(ctx, &img_handle);
if (error.code != heif_error_Ok)
goto quit;
/* Freeing heif_context, since we got primary image handle */
heif_context_free(ctx);
ctx = NULL;
im->w = heif_image_handle_get_width(img_handle);
im->h = heif_image_handle_get_height(img_handle);
if (!IMAGE_DIMENSIONS_OK(im->w, im->h))
goto quit;
img_has_alpha = heif_image_handle_has_alpha_channel(img_handle);
if (img_has_alpha)
SET_FLAG(im->flags, F_HAS_ALPHA);
else
UNSET_FLAG(im->flags, F_HAS_ALPHA);
if (!load_data)
{
rc = LOAD_SUCCESS;
goto quit;
}
/* load data */
/* Set decoding option to convert HDR to 8-bit if libheif>=1.7.0 and
* successful allocation of heif_decoding_options */
#if LIBHEIF_HAVE_VERSION(1, 7, 0)
decode_opts = heif_decoding_options_alloc();
if (decode_opts)
decode_opts->convert_hdr_to_8bit = 1;
#endif
error =
heif_decode_image(img_handle, &img_data, heif_colorspace_RGB,
img_has_alpha ? heif_chroma_interleaved_RGBA :
heif_chroma_interleaved_RGB, decode_opts);
heif_decoding_options_free(decode_opts);
decode_opts = NULL;
if (error.code != heif_error_Ok)
goto quit;
im->w = heif_image_get_width(img_data, heif_channel_interleaved);
im->h = heif_image_get_height(img_data, heif_channel_interleaved);
if (!IMAGE_DIMENSIONS_OK(im->w, im->h))
goto quit;
ptr = __imlib_AllocateData(im);
if (!ptr)
goto quit;
img_plane =
heif_image_get_plane_readonly(img_data, heif_channel_interleaved,
&stride);
if (!img_plane)
goto quit;
/* Divide the number of bits per pixel by 8, always rounding up */
bytes_per_px =
(heif_image_get_bits_per_pixel(img_data, heif_channel_interleaved) +
7) >> 3;
/* If somehow bytes_per_pixel < 1, set it to 1 */
bytes_per_px = bytes_per_px < 1 ? 1 : bytes_per_px;
/* Correct the stride, since img_plane will be incremented after each pixel */
stride -= im->w * bytes_per_px;
for (y = 0; y != im->h; y++, img_plane += stride)
{
for (x = 0; x != im->w; x++, img_plane += bytes_per_px)
*(ptr++) = HEIF_8BIT_TO_PIXEL_ARGB(img_plane, img_has_alpha);
/* Report progress of each row. */
if (im->lc && __imlib_LoadProgressRows(im, y, 1))
{
rc = LOAD_BREAK;
goto quit;
}
}
rc = LOAD_SUCCESS;
quit:
/* Remove mapping if address is not MAP_FAILED. */
if (address != MAP_FAILED)
munmap(address, stats.st_size);
/* Free memory if it is still allocated.
* Working this way means explicitly setting pointers to NULL if they were
* freed beforehand to avoid freeing twice. */
/* decode_opts was freed as soon as decoding was complete */
heif_image_release(img_data);
heif_image_handle_release(img_handle);
heif_context_free(ctx);
heif_decoding_options_free(decode_opts);
return rc;
}
void
formats(ImlibLoader * l)
{
static const char *const list_formats[] =
{ "heif", "heifs", "heic", "heics", "avci", "avcs", "avif", "avifs" };
__imlib_LoaderSetFormats(l, list_formats,
sizeof(list_formats) / sizeof(char *));
}