shared: allow to get ICC profile from JPEG using weston_image_load()

Follow up of "shared: allow to get ICC profile from PNG using
weston_image_load()". This adds the support to get ICC profiles from
JPEG files.

Signed-off-by: Leandro Ribeiro <leandro.ribeiro@collabora.com>
This commit is contained in:
Leandro Ribeiro 2024-06-06 11:49:29 -03:00
parent d3f393031f
commit 312efb2acb
1 changed files with 101 additions and 40 deletions

View File

@ -27,6 +27,7 @@
#include "config.h" #include "config.h"
#include <errno.h> #include <errno.h>
#include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
@ -108,6 +109,71 @@ swizzle_row(JSAMPLE *row, JDIMENSION width)
} }
} }
struct jpeg_image_data {
JSAMPLE *data;
bool all_data_read;
};
static pixman_image_t *
load_jpeg_image(struct jpeg_decompress_struct *cinfo,
struct jpeg_image_data *jpeg_image_data)
{
JSAMPLE *rows[4];
int stride, first;
unsigned int i;
pixman_image_t *pixman_image;
stride = cinfo->output_width * 4;
jpeg_image_data->data = malloc(stride * cinfo->output_height);
if (jpeg_image_data->data == NULL) {
fprintf(stderr, "couldn't allocate image data\n");
return NULL;
}
while (cinfo->output_scanline < cinfo->output_height) {
first = cinfo->output_scanline;
for (i = 0; i < ARRAY_LENGTH(rows); i++)
rows[i] = jpeg_image_data->data + (first + i) * stride;
jpeg_read_scanlines(cinfo, rows, ARRAY_LENGTH(rows));
for (i = 0; first + i < cinfo->output_scanline; i++)
swizzle_row(rows[i], cinfo->output_width);
}
jpeg_image_data->all_data_read = true;
pixman_image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
cinfo->output_width,
cinfo->output_height,
(uint32_t *)jpeg_image_data->data,
stride);
pixman_image_set_destroy_function(pixman_image, pixman_image_destroy_func,
jpeg_image_data->data);
jpeg_image_data->data = NULL;
return pixman_image;
}
static int
load_jpeg_icc(struct jpeg_decompress_struct *cinfo,
struct icc_profile_data **icc_profile_data)
{
JOCTET *profdata;
uint32_t proflen;
if (!jpeg_read_icc_profile(cinfo, &profdata, &proflen)) {
/* Not an error, the file simply does not have an ICC embedded. */
*icc_profile_data = NULL;
return 0;
}
*icc_profile_data = icc_profile_data_create(profdata, proflen);
free(profdata);
if (*icc_profile_data == NULL)
return -1;
return 0;
}
static void static void
error_exit(j_common_ptr cinfo) error_exit(j_common_ptr cinfo)
{ {
@ -120,66 +186,61 @@ load_jpeg(FILE *fp, uint32_t image_load_flags)
struct weston_image *image; struct weston_image *image;
struct jpeg_decompress_struct cinfo; struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr; struct jpeg_error_mgr jerr;
pixman_image_t *pixman_image; struct jpeg_image_data jpeg_image_data = { 0 };
unsigned int i;
int stride, first;
JSAMPLE *data, *rows[4];
jmp_buf env; jmp_buf env;
int ret;
if (image_load_flags & WESTON_IMAGE_LOAD_ICC)
fprintf(stderr, "We still don't support reading ICC profile from JPEG\n");
if (!(image_load_flags & WESTON_IMAGE_LOAD_IMAGE))
return NULL;
cinfo.err = jpeg_std_error(&jerr); cinfo.err = jpeg_std_error(&jerr);
jerr.error_exit = error_exit; jerr.error_exit = error_exit;
cinfo.client_data = env; cinfo.client_data = env;
if (setjmp(env)) if (setjmp(env))
return NULL; goto err;
jpeg_create_decompress(&cinfo); jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, fp); jpeg_stdio_src(&cinfo, fp);
jpeg_read_header(&cinfo, TRUE); /**
* libjpeg.txt says that if we want to call jpeg_read_icc_profile(), we
* need to call the function below before calling jpeg_read_header().
*/
if (image_load_flags & WESTON_IMAGE_LOAD_ICC)
jpeg_save_markers(&cinfo, JPEG_APP0 + 2, 0xFFFF);
jpeg_read_header(&cinfo, TRUE);
cinfo.out_color_space = JCS_RGB; cinfo.out_color_space = JCS_RGB;
jpeg_start_decompress(&cinfo); jpeg_start_decompress(&cinfo);
stride = cinfo.output_width * 4; image = xzalloc(sizeof(*image));
data = malloc(stride * cinfo.output_height);
if (data == NULL) { if (image_load_flags & WESTON_IMAGE_LOAD_IMAGE) {
fprintf(stderr, "couldn't allocate image data\n"); image->pixman_image = load_jpeg_image(&cinfo, &jpeg_image_data);
return NULL; if (!image->pixman_image)
goto err;
} }
if (image_load_flags & WESTON_IMAGE_LOAD_ICC) {
while (cinfo.output_scanline < cinfo.output_height) { ret = load_jpeg_icc(&cinfo, &image->icc_profile_data);
first = cinfo.output_scanline; if (ret < 0)
for (i = 0; i < ARRAY_LENGTH(rows); i++) goto err;
rows[i] = data + (first + i) * stride;
jpeg_read_scanlines(&cinfo, rows, ARRAY_LENGTH(rows));
for (i = 0; first + i < cinfo.output_scanline; i++)
swizzle_row(rows[i], cinfo.output_width);
} }
jpeg_finish_decompress(&cinfo); jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo); jpeg_destroy_decompress(&cinfo);
pixman_image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
cinfo.output_width,
cinfo.output_height,
(uint32_t *) data, stride);
pixman_image_set_destroy_function(pixman_image,
pixman_image_destroy_func, data);
image = xzalloc(sizeof(*image));
image->pixman_image = pixman_image;
return image; return image;
err:
free(jpeg_image_data.data);
/**
* libjpeg.txt says that it is an error to call finish_decompress()
* before reading the total number of scanlines. But it documents that
* destroy_decompress() also aborts the decompression, so we can safely
* call that if the reading process is not finished.
*/
if (jpeg_image_data.all_data_read)
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
if (image)
weston_image_destroy(image);
return NULL;
} }
#else #else