58b7a156c5
The current way was enabling WebP support whenever libwebp was found, giving no way to the user to disable it if they had the library installed but didn’t want to link against it. This adds a --without-webp configure option to never link against it, and a --with-webp one to fail the build if it isn’t found, the default being to use it if it is present. Additionally, we now tell the user when WebP support has been disabled and they try to load a WebP file. Signed-off-by: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> Reviewed-by: Bryce Harrington <bryce@osg.samsung.com>
425 lines
9.5 KiB
C
425 lines
9.5 KiB
C
/*
|
|
* Copyright © 2008-2012 Kristian Høgsberg
|
|
* Copyright © 2012 Intel Corporation
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the
|
|
* next paragraph) shall be included in all copies or substantial
|
|
* portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <jpeglib.h>
|
|
#include <png.h>
|
|
#include <pixman.h>
|
|
|
|
#include "shared/helpers.h"
|
|
#include "image-loader.h"
|
|
|
|
#ifdef HAVE_WEBP
|
|
#include <webp/decode.h>
|
|
#endif
|
|
|
|
static int
|
|
stride_for_width(int width)
|
|
{
|
|
return width * 4;
|
|
}
|
|
|
|
static void
|
|
swizzle_row(JSAMPLE *row, JDIMENSION width)
|
|
{
|
|
JSAMPLE *s;
|
|
uint32_t *d;
|
|
|
|
s = row + (width - 1) * 3;
|
|
d = (uint32_t *) (row + (width - 1) * 4);
|
|
while (s >= row) {
|
|
*d = 0xff000000 | (s[0] << 16) | (s[1] << 8) | (s[2] << 0);
|
|
s -= 3;
|
|
d--;
|
|
}
|
|
}
|
|
|
|
static void
|
|
error_exit(j_common_ptr cinfo)
|
|
{
|
|
longjmp(cinfo->client_data, 1);
|
|
}
|
|
|
|
static void
|
|
pixman_image_destroy_func(pixman_image_t *image, void *data)
|
|
{
|
|
free(data);
|
|
}
|
|
|
|
static pixman_image_t *
|
|
load_jpeg(FILE *fp)
|
|
{
|
|
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];
|
|
jmp_buf env;
|
|
|
|
cinfo.err = jpeg_std_error(&jerr);
|
|
jerr.error_exit = error_exit;
|
|
cinfo.client_data = env;
|
|
if (setjmp(env))
|
|
return NULL;
|
|
|
|
jpeg_create_decompress(&cinfo);
|
|
|
|
jpeg_stdio_src(&cinfo, fp);
|
|
|
|
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;
|
|
|
|
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_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);
|
|
|
|
return pixman_image;
|
|
}
|
|
|
|
static inline int
|
|
multiply_alpha(int alpha, int color)
|
|
{
|
|
int temp = (alpha * color) + 0x80;
|
|
|
|
return ((temp + (temp >> 8)) >> 8);
|
|
}
|
|
|
|
static void
|
|
premultiply_data(png_structp png,
|
|
png_row_infop row_info,
|
|
png_bytep data)
|
|
{
|
|
unsigned int i;
|
|
png_bytep p;
|
|
|
|
for (i = 0, p = data; i < row_info->rowbytes; i += 4, p += 4) {
|
|
png_byte alpha = p[3];
|
|
uint32_t w;
|
|
|
|
if (alpha == 0) {
|
|
w = 0;
|
|
} else {
|
|
png_byte red = p[0];
|
|
png_byte green = p[1];
|
|
png_byte blue = p[2];
|
|
|
|
if (alpha != 0xff) {
|
|
red = multiply_alpha(alpha, red);
|
|
green = multiply_alpha(alpha, green);
|
|
blue = multiply_alpha(alpha, blue);
|
|
}
|
|
w = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
|
|
}
|
|
|
|
* (uint32_t *) p = w;
|
|
}
|
|
}
|
|
|
|
static void
|
|
read_func(png_structp png, png_bytep data, png_size_t size)
|
|
{
|
|
FILE *fp = png_get_io_ptr(png);
|
|
|
|
if (fread(data, 1, size, fp) != size)
|
|
png_error(png, NULL);
|
|
}
|
|
|
|
static void
|
|
png_error_callback(png_structp png, png_const_charp error_msg)
|
|
{
|
|
longjmp (png_jmpbuf (png), 1);
|
|
}
|
|
|
|
static pixman_image_t *
|
|
load_png(FILE *fp)
|
|
{
|
|
png_struct *png;
|
|
png_info *info;
|
|
png_byte *data = NULL;
|
|
png_byte **row_pointers = NULL;
|
|
png_uint_32 width, height;
|
|
int depth, color_type, interlace, stride;
|
|
unsigned int i;
|
|
pixman_image_t *pixman_image = NULL;
|
|
|
|
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, &info, 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);
|
|
|
|
if (color_type == PNG_COLOR_TYPE_PALETTE)
|
|
png_set_palette_to_rgb(png);
|
|
|
|
if (color_type == PNG_COLOR_TYPE_GRAY)
|
|
png_set_expand_gray_1_2_4_to_8(png);
|
|
|
|
if (png_get_valid(png, info, PNG_INFO_tRNS))
|
|
png_set_tRNS_to_alpha(png);
|
|
|
|
if (depth == 16)
|
|
png_set_strip_16(png);
|
|
|
|
if (depth < 8)
|
|
png_set_packing(png);
|
|
|
|
if (color_type == PNG_COLOR_TYPE_GRAY ||
|
|
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
|
png_set_gray_to_rgb(png);
|
|
|
|
if (interlace != PNG_INTERLACE_NONE)
|
|
png_set_interlace_handling(png);
|
|
|
|
png_set_filler(png, 0xff, PNG_FILLER_AFTER);
|
|
png_set_read_user_transform_fn(png, premultiply_data);
|
|
png_read_update_info(png, info);
|
|
png_get_IHDR(png, info,
|
|
&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);
|
|
return NULL;
|
|
}
|
|
|
|
row_pointers = malloc(height * sizeof row_pointers[0]);
|
|
if (row_pointers == NULL) {
|
|
free(data);
|
|
png_destroy_read_struct(&png, &info, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < height; i++)
|
|
row_pointers[i] = &data[i * stride];
|
|
|
|
png_read_image(png, row_pointers);
|
|
png_read_end(png, info);
|
|
|
|
free(row_pointers);
|
|
png_destroy_read_struct(&png, &info, NULL);
|
|
|
|
pixman_image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
|
|
width, height, (uint32_t *) data, stride);
|
|
|
|
pixman_image_set_destroy_function(pixman_image,
|
|
pixman_image_destroy_func, data);
|
|
|
|
return pixman_image;
|
|
}
|
|
|
|
#ifdef HAVE_WEBP
|
|
|
|
static pixman_image_t *
|
|
load_webp(FILE *fp)
|
|
{
|
|
WebPDecoderConfig config;
|
|
uint8_t buffer[16 * 1024];
|
|
int len;
|
|
VP8StatusCode status;
|
|
WebPIDecoder *idec;
|
|
|
|
if (!WebPInitDecoderConfig(&config)) {
|
|
fprintf(stderr, "Library version mismatch!\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* webp decoding api doesn't seem to specify a min size that's
|
|
usable for GetFeatures, but 256 works... */
|
|
len = fread(buffer, 1, 256, fp);
|
|
status = WebPGetFeatures(buffer, len, &config.input);
|
|
if (status != VP8_STATUS_OK) {
|
|
fprintf(stderr, "failed to parse webp header\n");
|
|
WebPFreeDecBuffer(&config.output);
|
|
return NULL;
|
|
}
|
|
|
|
config.output.colorspace = MODE_BGRA;
|
|
config.output.u.RGBA.stride = stride_for_width(config.input.width);
|
|
config.output.u.RGBA.size =
|
|
config.output.u.RGBA.stride * config.input.height;
|
|
config.output.u.RGBA.rgba =
|
|
malloc(config.output.u.RGBA.stride * config.input.height);
|
|
config.output.is_external_memory = 1;
|
|
if (!config.output.u.RGBA.rgba) {
|
|
WebPFreeDecBuffer(&config.output);
|
|
return NULL;
|
|
}
|
|
|
|
rewind(fp);
|
|
idec = WebPINewDecoder(&config.output);
|
|
if (!idec) {
|
|
WebPFreeDecBuffer(&config.output);
|
|
return NULL;
|
|
}
|
|
|
|
while (!feof(fp)) {
|
|
len = fread(buffer, 1, sizeof buffer, fp);
|
|
status = WebPIAppend(idec, buffer, len);
|
|
if (status != VP8_STATUS_OK) {
|
|
fprintf(stderr, "webp decode status %d\n", status);
|
|
WebPIDelete(idec);
|
|
WebPFreeDecBuffer(&config.output);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
#else
|
|
|
|
static pixman_image_t *
|
|
load_webp(FILE *fp)
|
|
{
|
|
fprintf(stderr, "WebP support disabled at compile-time\n");
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
struct image_loader {
|
|
unsigned char header[4];
|
|
int header_size;
|
|
pixman_image_t *(*load)(FILE *fp);
|
|
};
|
|
|
|
static const struct image_loader loaders[] = {
|
|
{ { 0x89, 'P', 'N', 'G' }, 4, load_png },
|
|
{ { 0xff, 0xd8 }, 2, load_jpeg },
|
|
{ { 'R', 'I', 'F', 'F' }, 4, load_webp }
|
|
};
|
|
|
|
pixman_image_t *
|
|
load_image(const char *filename)
|
|
{
|
|
pixman_image_t *image;
|
|
unsigned char header[4];
|
|
FILE *fp;
|
|
unsigned int i;
|
|
|
|
if (!filename || !*filename)
|
|
return NULL;
|
|
|
|
fp = fopen(filename, "rb");
|
|
if (!fp) {
|
|
fprintf(stderr, "%s: %s\n", filename, strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
if (fread(header, sizeof header, 1, fp) != 1) {
|
|
fclose(fp);
|
|
fprintf(stderr, "%s: unable to read file header\n", filename);
|
|
return NULL;
|
|
}
|
|
|
|
rewind(fp);
|
|
for (i = 0; i < ARRAY_LENGTH(loaders); i++) {
|
|
if (memcmp(header, loaders[i].header,
|
|
loaders[i].header_size) == 0) {
|
|
image = loaders[i].load(fp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
if (i == ARRAY_LENGTH(loaders)) {
|
|
fprintf(stderr, "%s: unrecognized file header "
|
|
"0x%02x 0x%02x 0x%02x 0x%02x\n",
|
|
filename, header[0], header[1], header[2], header[3]);
|
|
image = NULL;
|
|
} else if (!image) {
|
|
/* load probably printed something, but just in case */
|
|
fprintf(stderr, "%s: error reading image\n", filename);
|
|
}
|
|
|
|
return image;
|
|
}
|