Skip to content
Commits on Source (10)
......@@ -43,6 +43,15 @@
#include "window.h"
#include "shared/cairo-util.h"
#include "shared/helpers.h"
#include "shared/image-loader.h"
bool verbose;
#define verbose_print(...) do { \
if (verbose) \
fprintf(stderr, __VA_ARGS__); \
} while (0)
struct image {
struct window *window;
......@@ -70,6 +79,39 @@ struct image {
cairo_matrix_t matrix;
};
struct cli_render_intent_option {
int render_intent;
const char *cli_option;
};
static const struct cli_render_intent_option
cli_ri_table[] = {
{
.render_intent = -1,
.cli_option = "off",
},
{
.render_intent = RENDER_INTENT_PERCEPTUAL,
.cli_option = "per",
},
{
.render_intent = RENDER_INTENT_RELATIVE,
.cli_option = "rel",
},
{
.render_intent = RENDER_INTENT_RELATIVE_BPC,
.cli_option = "rel-bpc",
},
{
.render_intent = RENDER_INTENT_SATURATION,
.cli_option = "sat",
},
{
.render_intent = RENDER_INTENT_ABSOLUTE,
.cli_option = "abs",
},
};
static double
get_scale(struct image *image)
{
......@@ -396,10 +438,13 @@ set_empty_input_region(struct widget *widget, struct display *display)
static struct image *
image_create(struct display *display, const char *filename,
int *image_counter)
int *image_counter, int render_intent)
{
struct image *image;
struct weston_image *wimage;
char *b, *copy, title[512];
char *err_msg;
bool ret;
image = zalloc(sizeof *image);
if (image == NULL)
......@@ -453,23 +498,124 @@ image_create(struct display *display, const char *filename,
widget_set_button_handler(image->image_widget, image_button_handler);
widget_set_axis_handler(image->image_widget, image_axis_handler);
wimage = load_cairo_surface_get_user_data(image->image);
assert(wimage);
if (wimage->icc_profile_data && render_intent != -1) {
verbose_print("Image contains ICC file embedded, let's try to use the Wayland\n" \
"color-management protocol to set the surface image description\n" \
"using this ICC file.\n");
ret = widget_set_image_description_icc(image->image_widget,
wimage->icc_profile_data->fd,
wimage->icc_profile_data->length,
wimage->icc_profile_data->offset,
render_intent, &err_msg);
if (ret) {
verbose_print("Successfully set surface image description " \
"using ICC file.\n");
} else {
fprintf(stderr, "Failed to set surface image description:\n%s\n",
err_msg);
free(err_msg);
}
}
/* TODO: investigate if/how to get colorimetry info from the
* PNG/JPEG/etc image. Then use that to create a parametric image
* description and set it as the widget image description. Also, if
* clients do not enforce us to avoid setting an image description (i.e.
* render_intent != -1) but no colorimetry data is present, we can
* create a sRGB image description (through parameters) and set it as
* the image description to use. For now Weston do not support creating
* image description from parameters, that's why we've added only the
* code above that depends on ICC profiles. */
widget_schedule_resize(image->frame_widget, 500, 400);
return image;
}
static void
print_usage(const char *program_name)
{
const struct render_intent_info *intent_info;
const char *desc;
unsigned int i;
fprintf(stderr, "Usage:\n %s [OPTIONS] [FILENAME0] [FILENAME1] ...\n\n" \
"Options:\n", program_name);
fprintf(stderr, "-v or --verbose to print verbose log information.\n\n");
fprintf(stderr, "-h or --help to open this HELP dialogue.\n\n");
fprintf(stderr, "-r or --rendering-intent to choose the color-management rendering intent.\n\n " \
"The rendering intent is used when an image file has colorimetry data embedded,\n " \
"and the compositor should present this image taking this into account. We use\n " \
"the Wayland color-management protocol extension to set the image description\n " \
"and a rendering intent, which is up to the client to decide. This is optional,\n " \
"and if nothing set we'll use 'perceptual'. Supported values:\n\n");
for (i = 0; i < ARRAY_LENGTH(cli_ri_table); i++) {
/* "off" option does not have a corresponding render_intent_info
* object from which we would be able to get the description. */
intent_info = render_intent_info_from(cli_ri_table[i].render_intent);
if (intent_info)
desc = intent_info->desc;
else
desc = "No render intent (do not set image description)";
fprintf(stderr, " %s: %s.\n", cli_ri_table[i].cli_option, desc);
}
}
static int
get_render_intent(int *render_intent, const char *opt_rendering_intent)
{
unsigned int i;
/* The default, if client does not set anything. */
if (!opt_rendering_intent) {
*render_intent = RENDER_INTENT_PERCEPTUAL;
return 0;
}
for (i = 0; i < ARRAY_LENGTH(cli_ri_table); i++) {
if (strcmp(opt_rendering_intent, cli_ri_table[i].cli_option) == 0) {
*render_intent = cli_ri_table[i].render_intent;
return 0;
}
}
fprintf(stderr, "Error: unknown rendering intent: %s.\n\n",
opt_rendering_intent);
return -1;
}
int
main(int argc, char *argv[])
{
struct display *d;
int i;
int image_counter = 0;
if (argc <= 1 || argv[1][0]=='-') {
printf("Usage: %s image...\n", argv[0]);
int render_intent;
bool opt_help = false;
char *opt_rendering_intent = NULL;
struct weston_option cli_options[] = {
{ WESTON_OPTION_BOOLEAN, "help", 'h', &opt_help },
{ WESTON_OPTION_BOOLEAN, "verbose", 'v', &verbose },
{ WESTON_OPTION_STRING, "rendering-intent", 'r', &opt_rendering_intent },
};
parse_options(cli_options, ARRAY_LENGTH(cli_options), &argc, argv);
if (argc <= 1 || opt_help ||
get_render_intent(&render_intent, opt_rendering_intent) < 0) {
free(opt_rendering_intent);
print_usage(argv[0]);
return 1;
}
free(opt_rendering_intent);
d = display_create(&argc, argv);
if (d == NULL) {
fprintf(stderr, "failed to create display: %s\n",
......@@ -478,7 +624,7 @@ main(int argc, char *argv[])
}
for (i = 1; i < argc; i++)
image_create(d, argv[i], &image_counter);
image_create(d, argv[i], &image_counter, render_intent);
if (image_counter > 0)
display_run(d);
......
......@@ -4,6 +4,8 @@ endif
srcs_toytoolkit = [
'window.c',
color_management_v1_client_protocol_h,
color_management_v1_protocol_c,
xdg_shell_client_protocol_h,
xdg_shell_protocol_c,
text_cursor_position_client_protocol_h,
......
......@@ -56,6 +56,7 @@
#include "shared/xalloc.h"
#include <libweston/zalloc.h>
#include "xdg-shell-client-protocol.h"
#include "color-management-v1-client-protocol.h"
#include "text-cursor-position-client-protocol.h"
#include "pointer-constraints-unstable-v1-client-protocol.h"
#include "relative-pointer-unstable-v1-client-protocol.h"
......@@ -90,11 +91,15 @@ struct display {
struct wl_data_device_manager *data_device_manager;
struct text_cursor_position *text_cursor_position;
struct xdg_wm_base *xdg_shell;
struct xx_color_manager_v2 *color_manager;
struct zwp_tablet_manager_v2 *tablet_manager;
struct zwp_relative_pointer_manager_v1 *relative_pointer_manager;
struct zwp_pointer_constraints_v1 *pointer_constraints;
uint32_t serial;
uint32_t color_manager_features;
uint32_t color_manager_rendering_intents;
int display_fd;
bool display_fd_was_read;
uint32_t display_fd_events;
......@@ -212,6 +217,8 @@ struct surface {
struct wl_callback *frame_cb;
uint32_t last_time;
struct xx_color_management_surface_v2 *cm_surface;
struct rectangle allocation;
struct rectangle server_allocation;
......@@ -472,6 +479,44 @@ struct shm_pool {
void *data;
};
struct cm_image_description {
struct xx_image_description_v2 *image_desc;
enum cm_image_desc_status {
CM_IMAGE_DESC_NOT_CREATED = 0,
CM_IMAGE_DESC_READY,
CM_IMAGE_DESC_FAILED,
} status;
};
static const struct render_intent_info
render_intent_info_table[] = {
{
.intent = RENDER_INTENT_PERCEPTUAL,
.desc = "Perceptual",
.protocol_intent = XX_COLOR_MANAGER_V2_RENDER_INTENT_PERCEPTUAL,
},
{
.intent = RENDER_INTENT_RELATIVE,
.desc = "Media-relative colorimetric",
.protocol_intent = XX_COLOR_MANAGER_V2_RENDER_INTENT_RELATIVE,
},
{
.intent = RENDER_INTENT_RELATIVE_BPC,
.desc = "Media-relative colorimetric + black point compensation",
.protocol_intent = XX_COLOR_MANAGER_V2_RENDER_INTENT_RELATIVE_BPC,
},
{
.intent = RENDER_INTENT_SATURATION,
.desc = "Saturation",
.protocol_intent = XX_COLOR_MANAGER_V2_RENDER_INTENT_SATURATION,
},
{
.intent = RENDER_INTENT_ABSOLUTE,
.desc = "ICC-absolute colorimetric",
.protocol_intent = XX_COLOR_MANAGER_V2_RENDER_INTENT_ABSOLUTE,
},
};
enum {
CURSOR_DEFAULT = 100,
CURSOR_UNSET
......@@ -522,6 +567,139 @@ debug_print(void *proxy, int line, const char *func, const char *fmt, ...)
#endif
static void
cm_image_desc_ready(void *data, struct xx_image_description_v2 *xx_image_description_v2,
uint32_t identity)
{
struct cm_image_description *cm_image_desc = data;
cm_image_desc->status = CM_IMAGE_DESC_READY;
}
static void
cm_image_desc_failed(void *data, struct xx_image_description_v2 *xx_image_description_v2,
uint32_t cause, const char *msg)
{
struct cm_image_description *cm_image_desc = data;
fprintf(stderr, "failed to create image description: %u - %s\n",
cause, msg);
cm_image_desc->status = CM_IMAGE_DESC_FAILED;
}
static const struct xx_image_description_v2_listener cm_image_desc_listener = {
.ready = cm_image_desc_ready,
.failed = cm_image_desc_failed,
};
const struct render_intent_info *
render_intent_info_from(enum render_intent intent)
{
unsigned int i;
for (i = 0; i < ARRAY_LENGTH(render_intent_info_table); i++)
if (render_intent_info_table[i].intent == intent)
return &render_intent_info_table[i];
return NULL;
}
bool
widget_set_image_description_icc(struct widget *widget, int icc_fd,
uint32_t length, uint32_t offset,
enum render_intent intent, char **err_msg)
{
struct xx_image_description_creator_icc_v2 *icc_creator;
struct display *display = widget->window->display;
struct surface *surface = widget->surface;
struct xx_color_manager_v2 *color_manager_wrapper;
struct wl_event_queue *queue;
struct cm_image_description cm_image_desc;
const struct render_intent_info *intent_info;
int ret = 0;
if (!display->color_manager) {
str_printf(err_msg,
"%s extension not supported by the Wayland " \
"compositor, ignoring image color profile.",
xx_color_manager_v2_interface.name);
return false;
}
if (!((display->color_manager_features >> XX_COLOR_MANAGER_V2_FEATURE_ICC_V2_V4) & 1)) {
str_printf(err_msg,
"Wayland compositor does not support creating image " \
"descriptions from ICC files, ignoring color profile.");
return false;
}
intent_info = render_intent_info_from(intent);
assert(intent_info && "error: unknown rendering intent\n");
if (!((display->color_manager_rendering_intents >> intent_info->protocol_intent) & 1)) {
str_printf(err_msg,
"Wayland compositor does not support creating image " \
"descriptions with the following rendering intent: %s. " \
"Ignoring color profile.", intent_info->desc);
return false;
}
color_manager_wrapper = wl_proxy_create_wrapper(display->color_manager);
queue = wl_display_create_queue(display->display);
wl_proxy_set_queue((struct wl_proxy *)color_manager_wrapper, queue);
/* Create ICC image description creator and set the ICC file. */
icc_creator = xx_color_manager_v2_new_icc_creator(color_manager_wrapper);
wl_proxy_wrapper_destroy(color_manager_wrapper);
xx_image_description_creator_icc_v2_set_icc_file(icc_creator,
icc_fd, offset, length);
/* Create the image description. It will also destroy the ICC creator. */
cm_image_desc.status = CM_IMAGE_DESC_NOT_CREATED;
cm_image_desc.image_desc = xx_image_description_creator_icc_v2_create(icc_creator);
xx_image_description_v2_add_listener(cm_image_desc.image_desc,
&cm_image_desc_listener, &cm_image_desc);
/* Wait until compositor creates the image description or gracefully
* fail to do that. */
while (ret != -1 && cm_image_desc.status == CM_IMAGE_DESC_NOT_CREATED)
ret = wl_display_dispatch_queue(display->display, queue);
if (ret == -1) {
xx_image_description_v2_destroy(cm_image_desc.image_desc);
wl_event_queue_destroy(queue);
str_printf(err_msg,
"Disconnected from the Wayland compositor, " \
"wl_display_dispatch() failed: %s", strerror(errno));
return false;
}
/* Gracefully failed to create image description. Error already printed
* in the handler. */
if (cm_image_desc.status == CM_IMAGE_DESC_FAILED) {
xx_image_description_v2_destroy(cm_image_desc.image_desc);
wl_event_queue_destroy(queue);
str_printf(err_msg,
"Image description creation gracefully failed.");
return false;
}
assert(cm_image_desc.status == CM_IMAGE_DESC_READY);
if (!surface->cm_surface)
surface->cm_surface =
xx_color_manager_v2_get_surface(display->color_manager,
surface->surface);
xx_color_management_surface_v2_set_image_description(surface->cm_surface,
cm_image_desc.image_desc,
intent_info->protocol_intent);
xx_image_description_v2_destroy(cm_image_desc.image_desc);
wl_event_queue_destroy(queue);
return true;
}
static void
surface_to_buffer_size (enum wl_output_transform buffer_transform, int32_t buffer_scale, int32_t *width, int32_t *height)
{
......@@ -1399,6 +1577,9 @@ surface_destroy(struct surface *surface)
if (surface->viewport)
wp_viewport_destroy(surface->viewport);
if (surface->cm_surface)
xx_color_management_surface_v2_destroy(surface->cm_surface);
wl_surface_destroy(surface->surface);
if (surface->toysurface)
......@@ -6489,6 +6670,45 @@ display_bind_tablets(struct display *d, uint32_t id)
}
}
static void
cm_supported_intent(void *data, struct xx_color_manager_v2 *xx_color_manager_v2,
uint32_t render_intent)
{
struct display *d = data;
d->color_manager_rendering_intents |= (1 << render_intent);
}
static void
cm_supported_feature(void *data, struct xx_color_manager_v2 *xx_color_manager_v2,
uint32_t feature)
{
struct display *d = data;
d->color_manager_features |= (1 << feature);
}
static void
cm_supported_tf_named(void *data, struct xx_color_manager_v2 *xx_color_manager_v2,
uint32_t tf_code)
{
/* unused in this file */
}
static void
cm_supported_primaries_named(void *data, struct xx_color_manager_v2 *xx_color_manager_v2,
uint32_t primaries_code)
{
/* unused in this file */
}
static const struct xx_color_manager_v2_listener cm_listener = {
.supported_intent = cm_supported_intent,
.supported_feature = cm_supported_feature,
.supported_tf_named = cm_supported_tf_named,
.supported_primaries_named = cm_supported_primaries_named,
};
static void
global_destroy(struct display *disp, struct global *g)
{
......@@ -6557,6 +6777,12 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t id,
&wp_viewporter_interface, 1);
} else if (strcmp(interface, "zwp_tablet_manager_v2") == 0) {
display_bind_tablets(d, id);
} else if (strcmp(interface, "xx_color_manager_v2") == 0) {
d->color_manager =
wl_registry_bind(registry, id,
&xx_color_manager_v2_interface, 1);
xx_color_manager_v2_add_listener(d->color_manager,
&cm_listener, d);
}
if (d->global_handler)
......@@ -6693,7 +6919,10 @@ display_create(int *argc, char *argv[])
d->registry = wl_display_get_registry(d->display);
wl_registry_add_listener(d->registry, &registry_listener, d);
if (wl_display_roundtrip(d->display) < 0) {
/* Two roundtrips to get the global and then the events of listeners
* that we register on such globals. */
if (wl_display_roundtrip(d->display) < 0 ||
wl_display_roundtrip(d->display) < 0) {
fprintf(stderr, "Failed to process Wayland connection: %s\n",
strerror(errno));
display_destroy(d);
......@@ -6775,6 +7004,9 @@ display_destroy(struct display *display)
if (display->xdg_shell)
xdg_wm_base_destroy(display->xdg_shell);
if (display->color_manager)
xx_color_manager_v2_destroy(display->color_manager);
if (display->shm)
wl_shm_destroy(display->shm);
......
......@@ -545,6 +545,28 @@ window_get_appid(struct window *window);
void
window_set_text_cursor_position(struct window *window, int32_t x, int32_t y);
enum render_intent {
RENDER_INTENT_PERCEPTUAL,
RENDER_INTENT_RELATIVE,
RENDER_INTENT_RELATIVE_BPC,
RENDER_INTENT_SATURATION,
RENDER_INTENT_ABSOLUTE,
};
struct render_intent_info {
enum render_intent intent;
uint32_t protocol_intent;
const char *desc;
};
const struct render_intent_info *
render_intent_info_from(enum render_intent intent);
bool
widget_set_image_description_icc(struct widget *widget, int icc_fd,
uint32_t length, uint32_t offset,
enum render_intent intent, char **err_msg);
int
widget_set_tooltip(struct widget *parent, char *entry, float x, float y);
......
......@@ -617,27 +617,27 @@ x11_output_set_icon(struct x11_backend *b,
{
uint32_t *icon;
int32_t width, height;
pixman_image_t *image;
struct weston_image *image;
image = load_image(filename);
image = weston_image_load(filename, WESTON_IMAGE_LOAD_IMAGE);
if (!image)
return;
width = pixman_image_get_width(image);
height = pixman_image_get_height(image);
width = pixman_image_get_width(image->pixman_image);
height = pixman_image_get_height(image->pixman_image);
icon = malloc(width * height * 4 + 8);
if (!icon) {
pixman_image_unref(image);
weston_image_destroy(image);
return;
}
icon[0] = width;
icon[1] = height;
memcpy(icon + 2, pixman_image_get_data(image), width * height * 4);
memcpy(icon + 2, pixman_image_get_data(image->pixman_image), width * height * 4);
xcb_change_property(b->conn, XCB_PROP_MODE_REPLACE, output->window,
b->atom.net_wm_icon, b->atom.cardinal, 32,
width * height + 2, icon);
free(icon);
pixman_image_unref(image);
weston_image_destroy(image);
}
static void
......
......@@ -351,9 +351,9 @@ rounded_rect(cairo_t *cr, int x0, int y0, int x1, int y1, int radius)
static void
loaded_cairo_surface_destructor(void *data)
{
pixman_image_t *image = data;
struct weston_image *image = data;
pixman_image_unref(image);
weston_image_destroy(image);
}
static const cairo_user_data_key_t weston_cairo_util_load_cairo_surface_key;
......@@ -363,19 +363,20 @@ load_cairo_surface(const char *filename)
{
cairo_surface_t *surface;
cairo_status_t ret;
pixman_image_t *image;
struct weston_image *image;
int width, height, stride;
void *data;
image = load_image(filename);
image = weston_image_load(filename, WESTON_IMAGE_LOAD_IMAGE |
WESTON_IMAGE_LOAD_ICC);
if (image == NULL) {
return NULL;
}
data = pixman_image_get_data(image);
width = pixman_image_get_width(image);
height = pixman_image_get_height(image);
stride = pixman_image_get_stride(image);
data = pixman_image_get_data(image->pixman_image);
width = pixman_image_get_width(image->pixman_image);
height = pixman_image_get_height(image->pixman_image);
stride = pixman_image_get_stride(image->pixman_image);
surface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32,
width, height, stride);
......@@ -394,10 +395,17 @@ load_cairo_surface(const char *filename)
fail:
cairo_surface_destroy(surface);
pixman_image_unref(image);
weston_image_destroy(image);
return NULL;
}
struct weston_image *
load_cairo_surface_get_user_data(cairo_surface_t *surface)
{
return cairo_surface_get_user_data(surface,
&weston_cairo_util_load_cairo_surface_key);
}
void
theme_set_background_source(struct theme *t, cairo_t *cr, uint32_t flags)
{
......
......@@ -52,6 +52,9 @@ rounded_rect(cairo_t *cr, int x0, int y0, int x1, int y1, int radius);
cairo_surface_t *
load_cairo_surface(const char *filename);
struct weston_image *
load_cairo_surface_get_user_data(cairo_surface_t *surface);
struct theme {
cairo_surface_t *active_frame;
cairo_surface_t *inactive_frame;
......
......@@ -27,14 +27,18 @@
#include "config.h"
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <png.h>
#include <pixman.h>
#include "shared/helpers.h"
#include "shared/os-compatibility.h"
#include "shared/xalloc.h"
#include "image-loader.h"
#ifdef HAVE_JPEG
......@@ -57,6 +61,37 @@ pixman_image_destroy_func(pixman_image_t *image, void *data)
free(data);
}
static struct icc_profile_data *
icc_profile_data_create(void *profdata, uint32_t proflen)
{
struct icc_profile_data *icc_profile_data;
int fd;
void *data;
fd = os_create_anonymous_file(proflen);
if (fd < 0) {
fprintf(stderr, "failed to create anonymous file: %s\n",
strerror(errno));
return NULL;
}
data = mmap(NULL, proflen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
close(fd);
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
return NULL;
}
memcpy(data, profdata, proflen);
munmap(data, proflen);
icc_profile_data = xzalloc(sizeof(*icc_profile_data));
icc_profile_data->fd = fd;
icc_profile_data->length = proflen;
icc_profile_data->offset = 0;
return icc_profile_data;
}
#ifdef HAVE_JPEG
static void
......@@ -74,74 +109,144 @@ 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
error_exit(j_common_ptr cinfo)
{
longjmp(cinfo->client_data, 1);
}
static pixman_image_t *
load_jpeg(FILE *fp)
static struct weston_image *
load_jpeg(FILE *fp, uint32_t image_load_flags)
{
struct weston_image *image;
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
pixman_image_t *pixman_image = NULL;
unsigned int i;
int stride, first;
JSAMPLE *data, *rows[4];
struct jpeg_image_data jpeg_image_data = { 0 };
jmp_buf env;
int ret;
cinfo.err = jpeg_std_error(&jerr);
jerr.error_exit = error_exit;
cinfo.client_data = env;
if (setjmp(env))
return NULL;
goto err;
jpeg_create_decompress(&cinfo);
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;
jpeg_start_decompress(&cinfo);
stride = cinfo.output_width * 4;
data = malloc(stride * cinfo.output_height);
if (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] = data + (first + i) * stride;
image = xzalloc(sizeof(*image));
jpeg_read_scanlines(&cinfo, rows, ARRAY_LENGTH(rows));
for (i = 0; first + i < cinfo.output_scanline; i++)
swizzle_row(rows[i], cinfo.output_width);
if (image_load_flags & WESTON_IMAGE_LOAD_IMAGE) {
image->pixman_image = load_jpeg_image(&cinfo, &jpeg_image_data);
if (!image->pixman_image)
goto err;
}
if (image_load_flags & WESTON_IMAGE_LOAD_ICC) {
ret = load_jpeg_icc(&cinfo, &image->icc_profile_data);
if (ret < 0)
goto err;
}
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return image;
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);
return pixman_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
static pixman_image_t *
load_jpeg(FILE *fp)
static struct weston_image *
load_jpeg(FILE *fp, uint32_t image_load_flags)
{
fprintf(stderr, "JPEG support disabled at compile-time\n");
return NULL;
......@@ -203,40 +308,20 @@ png_error_callback(png_structp png, png_const_charp error_msg)
longjmp (png_jmpbuf (png), 1);
}
struct png_image_data {
png_byte *volatile data;
png_byte **volatile row_pointers;
};
static pixman_image_t *
load_png(FILE *fp)
load_png_image(FILE *fp, png_struct *png, png_info *info,
struct png_image_data *png_image_data)
{
png_struct *png;
png_info *info;
png_byte *volatile data = NULL;
png_byte **volatile row_pointers = NULL;
png_uint_32 width, height;
int depth, color_type, interlace, stride;
unsigned int i;
pixman_image_t *pixman_image = NULL;
pixman_image_t *pixman_image;
png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
png_error_callback, NULL);
if (!png)
return NULL;
info = png_create_info_struct(png);
if (!info) {
png_destroy_read_struct(&png, NULL, NULL);
return NULL;
}
if (setjmp(png_jmpbuf(png))) {
if (data)
free(data);
if (row_pointers)
free(row_pointers);
png_destroy_read_struct(&png, &info, NULL);
return NULL;
}
png_set_read_fn(png, fp, read_func);
png_read_info(png, info);
png_get_IHDR(png, info,
&width, &height, &depth,
&color_type, &interlace, NULL, NULL);
......@@ -270,50 +355,129 @@ load_png(FILE *fp)
&width, &height, &depth,
&color_type, &interlace, NULL, NULL);
stride = stride_for_width(width);
data = malloc(stride * height);
if (!data) {
png_destroy_read_struct(&png, &info, NULL);
png_image_data->data = malloc(stride * height);
if (!png_image_data->data)
return NULL;
}
row_pointers = malloc(height * sizeof row_pointers[0]);
if (row_pointers == NULL) {
free(data);
png_destroy_read_struct(&png, &info, NULL);
png_image_data->row_pointers = malloc(height * sizeof png_image_data->row_pointers[0]);
if (png_image_data->row_pointers == NULL)
return NULL;
}
for (i = 0; i < height; i++)
row_pointers[i] = &data[i * stride];
png_image_data->row_pointers[i] = &png_image_data->data[i * stride];
png_read_image(png, row_pointers);
png_read_image(png, png_image_data->row_pointers);
png_read_end(png, info);
free(row_pointers);
png_destroy_read_struct(&png, &info, NULL);
free(png_image_data->row_pointers);
png_image_data->row_pointers = NULL;
pixman_image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
width, height, (uint32_t *) data, stride);
pixman_image = pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height,
(uint32_t *) png_image_data->data,
stride);
pixman_image_set_destroy_function(pixman_image,
pixman_image_destroy_func, data);
pixman_image_set_destroy_function(pixman_image, pixman_image_destroy_func,
png_image_data->data);
png_image_data->data = NULL;
return pixman_image;
}
static int
load_png_icc(FILE *fp, png_struct *png, png_info *info,
struct icc_profile_data **icc_profile_data)
{
png_charp name;
int compression_type;
png_bytep profdata;
png_uint_32 proflen;
png_uint_32 ret;
ret = png_get_iCCP(png, info, &name, &compression_type, &profdata, &proflen);
if (ret != PNG_INFO_iCCP) {
/* 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);
if (*icc_profile_data == NULL)
return -1;
return 0;
}
static struct weston_image *
load_png(FILE *fp, uint32_t image_load_flags)
{
struct weston_image *image = NULL;
struct png_image_data png_image_data = { 0 };
png_struct *png;
png_info *info;
int ret;
png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
png_error_callback, NULL);
if (!png)
return NULL;
info = png_create_info_struct(png);
if (!info) {
png_destroy_read_struct(&png, NULL, NULL);
return NULL;
}
if (setjmp(png_jmpbuf(png)))
goto err;
png_set_read_fn(png, fp, read_func);
png_read_info(png, info);
image = xzalloc(sizeof(*image));
if (image_load_flags & WESTON_IMAGE_LOAD_IMAGE) {
image->pixman_image = load_png_image(fp, png, info, &png_image_data);
if (!image->pixman_image)
goto err;
}
if (image_load_flags & WESTON_IMAGE_LOAD_ICC) {
ret = load_png_icc(fp, png, info, &image->icc_profile_data);
if (ret < 0)
goto err;
}
png_destroy_read_struct(&png, &info, NULL);
return image;
err:
free(png_image_data.data);
free(png_image_data.row_pointers);
png_destroy_read_struct(&png, &info, NULL);
if (image)
weston_image_destroy(image);
return NULL;
}
#ifdef HAVE_WEBP
static pixman_image_t *
load_webp(FILE *fp)
static struct weston_image *
load_webp(FILE *fp, uint32_t image_load_flags)
{
struct weston_image *image;
pixman_image_t *pixman_image;
WebPDecoderConfig config;
uint8_t buffer[16 * 1024];
int len;
VP8StatusCode status;
WebPIDecoder *idec;
if (image_load_flags & WESTON_IMAGE_LOAD_ICC)
fprintf(stderr, "We still don't support reading ICC profile from WebP\n");
if (!(image_load_flags & WESTON_IMAGE_LOAD_IMAGE))
return NULL;
if (!WebPInitDecoderConfig(&config)) {
fprintf(stderr, "Library version mismatch!\n");
return NULL;
......@@ -362,17 +526,21 @@ load_webp(FILE *fp)
WebPIDelete(idec);
WebPFreeDecBuffer(&config.output);
return pixman_image_create_bits(PIXMAN_a8r8g8b8,
config.input.width,
config.input.height,
(uint32_t *) config.output.u.RGBA.rgba,
config.output.u.RGBA.stride);
pixman_image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
config.input.width, config.input.height,
(uint32_t *) config.output.u.RGBA.rgba,
config.output.u.RGBA.stride);
image = xzalloc(sizeof(*image));
image->pixman_image = pixman_image;
return image;
}
#else
static pixman_image_t *
load_webp(FILE *fp)
static struct weston_image *
load_webp(FILE *fp, uint32_t image_load_flags)
{
fprintf(stderr, "WebP support disabled at compile-time\n");
return NULL;
......@@ -384,7 +552,7 @@ load_webp(FILE *fp)
struct image_loader {
unsigned char header[4];
int header_size;
pixman_image_t *(*load)(FILE *fp);
struct weston_image *(*load)(FILE *fp, uint32_t image_load_flags);
};
static const struct image_loader loaders[] = {
......@@ -393,10 +561,26 @@ static const struct image_loader loaders[] = {
{ { 'R', 'I', 'F', 'F' }, 4, load_webp }
};
pixman_image_t *
load_image(const char *filename)
/**
* Given a filename, loads the associated image.
*
* \param filename The full image filename, i.e. the path plus filename.
* \param image_load_flags Combination of enum weston_image_load_flags.
* \return A struct weston_image on success, NULL on failure.
*
* To normally load the image, use the flag WESTON_IMAGE_LOAD_IMAGE. If this
* function fails to load the image, it returns NULL. Otherwise the image will
* be stored in weston_image::pixman_image.
*
* As ICC profiles are not always embedded on image files, even if
* WESTON_IMAGE_LOAD_ICC is one of the given flags, the returned
* weston_image::icc_profile_data may be NULL. But if something fails, this
* function returns NULL.
*/
struct weston_image *
weston_image_load(const char *filename, uint32_t image_load_flags)
{
pixman_image_t *image = NULL;
struct weston_image *image = NULL;
unsigned char header[4];
FILE *fp;
unsigned int i;
......@@ -420,7 +604,7 @@ load_image(const char *filename)
for (i = 0; i < ARRAY_LENGTH(loaders); i++) {
if (memcmp(header, loaders[i].header,
loaders[i].header_size) == 0) {
image = loaders[i].load(fp);
image = loaders[i].load(fp, image_load_flags);
break;
}
}
......@@ -438,3 +622,22 @@ load_image(const char *filename)
return image;
}
/**
* Destroy a struct weston_image object.
*
* \param image The struct weston_image to destroy.
*/
void
weston_image_destroy(struct weston_image *image)
{
if (image->pixman_image)
pixman_image_unref(image->pixman_image);
if (image->icc_profile_data) {
close(image->icc_profile_data->fd);
free(image->icc_profile_data);
}
free(image);
}
......@@ -28,7 +28,26 @@
#include <pixman.h>
pixman_image_t *
load_image(const char *filename);
enum weston_image_load_flags {
WESTON_IMAGE_LOAD_IMAGE = 0x1,
WESTON_IMAGE_LOAD_ICC = 0x2,
};
struct icc_profile_data {
int fd;
uint32_t length;
uint32_t offset;
};
struct weston_image {
pixman_image_t *pixman_image;
struct icc_profile_data *icc_profile_data;
};
struct weston_image *
weston_image_load(const char *filename, uint32_t image_load_flags);
void
weston_image_destroy(struct weston_image *image);
#endif