diff options
author | Kevin Peng <kevin@zycomtech.com> | 2009-10-25 22:03:19 +0100 |
---|---|---|
committer | Javier Jardón <jjardon@gnome.org> | 2009-10-26 00:08:08 +0100 |
commit | 4b22b461b6ece49563fef808ff8d3b2fe6660713 (patch) | |
tree | 37677ae2d1c125bba3954930574edb9476bf324c /gdk-pixbuf | |
parent | 3c510f028f9a399c80851d2cb8c230e930abd4ff (diff) | |
download | gtk+-4b22b461b6ece49563fef808ff8d3b2fe6660713.tar.gz |
GdkPixbuf loader for QTIF format
Fixes https://bugzilla.gnome.org/show_bug.cgi?id=366217
Diffstat (limited to 'gdk-pixbuf')
-rw-r--r-- | gdk-pixbuf/Makefile.am | 16 | ||||
-rw-r--r-- | gdk-pixbuf/gdk-pixbuf-io.c | 7 | ||||
-rw-r--r-- | gdk-pixbuf/io-qtif.c | 607 |
3 files changed, 630 insertions, 0 deletions
diff --git a/gdk-pixbuf/Makefile.am b/gdk-pixbuf/Makefile.am index 39b52f3ce2..ae6e2eaba0 100644 --- a/gdk-pixbuf/Makefile.am +++ b/gdk-pixbuf/Makefile.am @@ -193,6 +193,14 @@ libpixbufloader_jasper_la_SOURCES = io-jasper.c libpixbufloader_jasper_la_LDFLAGS = -avoid-version -module $(no_undefined) libpixbufloader_jasper_la_LIBADD = $(LIBJASPER) $(module_libs) +# +# The QTIF loader +# +libstatic_pixbufloader_qtif_la_SOURCES = io-qtif.c +libpixbufloader_qtif_la_SOURCES = io-qtif.c +libpixbufloader_qtif_la_LDFLAGS = -avoid-version -module $(no_undefined) +libpixbufloader_qtif_la_LIBADD = $(module_libs) + if BUILD_GDIPLUS_LOADERS if INCLUDE_GDIPLUS @@ -451,6 +459,12 @@ JASPER_LIB = libpixbufloader-jasper.la endif endif +if INCLUDE_QTIF +STATIC_QTIF_LIB = libstatic-pixbufloader-qtif.la +else +QTIF_LIB = libpixbufloader-qtif.la +endif + if BUILD_DYNAMIC_MODULES loader_LTLIBRARIES = \ @@ -470,6 +484,7 @@ loader_LTLIBRARIES = \ $(ICNS_LIB) \ $(PCX_LIB) \ $(JASPER_LIB) \ + $(QTIF_LIB) \ $(GDIPLUS_LIBS) @@ -492,6 +507,7 @@ noinst_LTLIBRARIES = \ $(STATIC_ICNS_LIB) \ $(STATIC_PCX_LIB) \ $(STATIC_JASPER_LIB) \ + $(STATIC_QTIF_LIB) \ $(STATIC_GDIPLUS_LIBS) builtin_objs = @INCLUDED_LOADER_OBJ@ diff --git a/gdk-pixbuf/gdk-pixbuf-io.c b/gdk-pixbuf/gdk-pixbuf-io.c index 646b0f1eaa..56271e16e0 100644 --- a/gdk-pixbuf/gdk-pixbuf-io.c +++ b/gdk-pixbuf/gdk-pixbuf-io.c @@ -396,6 +396,9 @@ gdk_pixbuf_io_init (void) #ifdef INCLUDE_jasper load_one_builtin_module (jasper); #endif +#ifdef INCLUDE_qtif + load_one_builtin_module (qtif); +#endif #ifdef INCLUDE_gdiplus /* We don't bother having the GDI+ loaders individually selectable * for building in or not. @@ -589,6 +592,7 @@ module (tga); module (pcx); module (icns); module (jasper); +module (qtif); module (gdip_ico); module (gdip_wmf); module (gdip_emf); @@ -667,6 +671,9 @@ gdk_pixbuf_load_module_unlocked (GdkPixbufModule *image_module, #ifdef INCLUDE_jasper try_module (jasper,jasper); #endif +#ifdef INCLUDE_qtif + try_module (qtif,qtif); +#endif #ifdef INCLUDE_gdiplus try_module (ico,gdip_ico); try_module (wmf,gdip_wmf); diff --git a/gdk-pixbuf/io-qtif.c b/gdk-pixbuf/io-qtif.c new file mode 100644 index 0000000000..0b80fe0e63 --- /dev/null +++ b/gdk-pixbuf/io-qtif.c @@ -0,0 +1,607 @@ +/* -*- mode: C; c-file-style: "linux" -*- */ +/* GdkPixbuf library - QTIF image loader + * + * This module extracts image data from QTIF format and uses + * other GDK pixbuf modules to decode the image data. + * + * Copyright (C) 2008 Kevin Peng + * + * Authors: Kevin Peng <kevin@zycomtech.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#include "config.h" +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <setjmp.h> +#include "gdk-pixbuf.h" +#include "gdk-pixbuf-private.h" +#include "gdk-pixbuf-io.h" + +/*** + * Definitions + */ +/* Read buffer size */ +#define READ_BUFFER_SIZE 8192 + +/* Only allow atom of size up to 10MB. */ +#define ATOM_SIZE_MAX 100000000 + +/* Aborts after going to through this many atoms. */ +#define QTIF_ATOM_COUNT_MAX 10u + +/* QTIF static image data tag "idat". */ +#define QTIF_TAG_IDATA 0x69646174u + + +/*** + * Types + */ +/* QTIF State */ +typedef enum { + STATE_READY, + STATE_DATA, + STATE_OTHER +} QTIFState; + +/* QTIF Atom Header */ +typedef struct { + guint32 length; + guint32 tag; +} QtHeader; + +/* QTIF loader context */ +typedef struct { + GdkPixbufLoader *loader; + gpointer user_data; + QTIFState state; + guint32 run_length; + gint atom_count; + + guchar header_buffer[sizeof(QtHeader)]; + + GdkPixbufModuleSizeFunc size_func; + GdkPixbufModulePreparedFunc prepare_func; + GdkPixbufModuleUpdatedFunc update_func; + gint cb_prepare_count; + gint cb_update_count; +} QTIFContext; + +/*** + * Local function prototypes + */ +static GdkPixbuf *gdk_pixbuf__qtif_image_load (FILE *f, GError **error); +static gpointer gdk_pixbuf__qtif_image_begin_load (GdkPixbufModuleSizeFunc size_func, + GdkPixbufModulePreparedFunc prepare_func, + GdkPixbufModuleUpdatedFunc update_func, + gpointer user_data, + GError **error); +static gboolean gdk_pixbuf__qtif_image_stop_load (gpointer context, GError **error); +static gboolean gdk_pixbuf__qtif_image_load_increment(gpointer context, + const guchar *buf, guint size, + GError **error); +static gboolean gdk_pixbuf__qtif_image_create_loader (QTIFContext *context, GError **error); +static gboolean gdk_pixbuf__qtif_image_free_loader (QTIFContext *context, GError **error); + +static void gdk_pixbuf__qtif_cb_size_prepared(GdkPixbufLoader *loader, + gint width, + gint height, + gpointer user_data); +static void gdk_pixbuf__qtif_cb_area_prepared(GdkPixbufLoader *loader, gpointer user_data); +static void gdk_pixbuf__qtif_cb_area_updated(GdkPixbufLoader *loader, + gint x, + gint y, + gint width, + gint height, + gpointer user_data); + +/*** + * Function definitions. + */ + +/* Load QTIF from a file handler. */ +static GdkPixbuf *gdk_pixbuf__qtif_image_load (FILE *f, GError **error) +{ + guint count; + + if(f == NULL) + { + g_set_error_literal (error, GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_BAD_OPTION, + _("Input file descriptor is NULL.")); + return NULL; + } + + for(count = QTIF_ATOM_COUNT_MAX; count != 0u; count--) + { + QtHeader hdr; + size_t rd; + + /* Read QtHeader. */ + rd = fread(&hdr, 1, sizeof(QtHeader), f); + if(rd != sizeof(QtHeader)) + { + g_set_error_literal(error, GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("Failed to read QTIF header")); + return NULL; + } + + hdr.length = GUINT32_FROM_BE(hdr.length) - sizeof(QtHeader); + if(hdr.length > ATOM_SIZE_MAX) + { + g_set_error(error, GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("QTIF atom size too large (%d bytes)"), hdr.length); + return NULL; + } + + switch(GUINT32_FROM_BE(hdr.tag)) + { + case QTIF_TAG_IDATA: /* "idat" data atom. */ + { + /* Load image using GdkPixbufLoader. */ + guchar *buf; + GdkPixbufLoader *loader; + GdkPixbuf *pixbuf = NULL; + GError *tmp = NULL; + + /* Allocate read buffer. */ + buf = g_try_malloc(READ_BUFFER_SIZE); + if(buf == NULL) + { + g_set_error(error, GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, + _("Failed to allocate %d bytes for file read buffer"), READ_BUFFER_SIZE); + return NULL; + } + + /* Create GdkPixbufLoader. */ + loader = gdk_pixbuf_loader_new(); + if(loader == NULL) + { + g_set_error(error, GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("QTIF atom size too large (%d bytes)"), hdr.length); + goto clean_up; + } + + /* Read atom data. */ + while(hdr.length != 0u) + { + rd = (hdr.length > READ_BUFFER_SIZE) ? READ_BUFFER_SIZE : hdr.length; + + rd = fread(buf, 1, rd, f); + if(rd < 0) + { + g_set_error(error, GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("File error when reading QTIF atom: %s"), g_strerror(errno)); + break; + } + + if(!gdk_pixbuf_loader_write(loader, buf, rd, &tmp)) + { + g_propagate_error (error, tmp); + break; + } + hdr.length -= rd; + } + +clean_up: + /* Release loader */ + if(loader != NULL) + { + gdk_pixbuf_loader_close(loader, NULL); + pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); + if(pixbuf != NULL) + { + g_object_ref(pixbuf); + } + g_object_unref(loader); + } + if(buf != NULL) + { + g_free(buf); + } + return pixbuf; + } + + default: + /* Skip any other types of atom. */ + if(!fseek(f, hdr.length, SEEK_CUR)) + { + g_set_error(error, GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("Failed to skip the next %d bytes with seek()."), hdr.length); + return NULL; + } + break; + } + } + return NULL; +} + +/* Incremental load begin. */ +static gpointer gdk_pixbuf__qtif_image_begin_load (GdkPixbufModuleSizeFunc size_func, + GdkPixbufModulePreparedFunc prepare_func, + GdkPixbufModuleUpdatedFunc update_func, + gpointer user_data, + GError **error) +{ + QTIFContext *context; + + /* Create context struct. */ + context = g_new0(QTIFContext, 1); + if(context == NULL) + { + g_set_error_literal (error, GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, + _("Failed to QTIF context structure.")); + return NULL; + } + + /* Fill context parameters. */ + context->loader = NULL; + context->user_data = user_data; + context->state = STATE_READY; + context->run_length = 0u; + context->atom_count = QTIF_ATOM_COUNT_MAX; + context->size_func = size_func; + context->prepare_func = prepare_func; + context->update_func = update_func; + + return context; +} + +/* Incremental load clean up. */ +static gboolean gdk_pixbuf__qtif_image_stop_load (gpointer data, GError **error) +{ + QTIFContext *context = (QTIFContext *)data; + gboolean ret; + + if(context->loader != NULL) + { + GError *tmp = NULL; + + ret = gdk_pixbuf__qtif_image_free_loader(context, &tmp); + if(!ret) + { + g_propagate_error (error, tmp); + } + } + g_free(context); + return ret; +} + +/* Create a new GdkPixbufLoader and connect to its signals. */ +static gboolean gdk_pixbuf__qtif_image_create_loader (QTIFContext *context, GError **error) +{ + GError *tmp = NULL; + + if(context == NULL) + { + return FALSE; + } + + /* Free existing loader. */ + if(context->loader != NULL) + { + gdk_pixbuf__qtif_image_free_loader(context, &tmp); + } + + /* Create GdkPixbufLoader object. */ + context->loader = gdk_pixbuf_loader_new(); + if(context->loader == NULL) + { + g_set_error_literal (error, GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_FAILED, + _("Failed to create GdkPixbufLoader object.")); + return FALSE; + } + + /* Connect signals. */ + context->cb_prepare_count = 0; + context->cb_update_count = 0; + if(context->size_func != NULL) + { + g_signal_connect(context->loader, "size-prepared", + G_CALLBACK(gdk_pixbuf__qtif_cb_size_prepared), + context); + } + if(context->prepare_func != NULL) + { + g_signal_connect(context->loader, "area-prepared", + G_CALLBACK(gdk_pixbuf__qtif_cb_area_prepared), + context); + } + if(context->update_func != NULL) + { + g_signal_connect(context->loader, "area-updated", + G_CALLBACK(gdk_pixbuf__qtif_cb_area_updated), + context); + } + return TRUE; +} + +/* Free the GdkPixbufLoader and perform callback if haven't done so. */ +static gboolean gdk_pixbuf__qtif_image_free_loader (QTIFContext *context, GError **error) +{ + GdkPixbuf *pixbuf; + GError *tmp = NULL; + gboolean ret; + + if((context == NULL) || (context->loader == NULL)) + { + return FALSE; + } + + /* Close GdkPixbufLoader. */ + ret = gdk_pixbuf_loader_close(context->loader, &tmp); + if(!ret) + { + g_propagate_error (error, tmp); + } + + + /* Get GdkPixbuf from GdkPixbufLoader. */ + pixbuf = gdk_pixbuf_loader_get_pixbuf(context->loader); + if(pixbuf != NULL) + { + g_object_ref(pixbuf); + } + + /* Free GdkPixbufLoader. */ + g_object_ref(context->loader); + context->loader = NULL; + + if(pixbuf != NULL) + { + /* Callback functions should be called for at least once. */ + if((context->prepare_func != NULL) && (context->cb_prepare_count == 0)) + { + (context->prepare_func)(pixbuf, NULL, context->user_data); + } + + if((context->update_func != NULL) && (context->cb_update_count == 0)) + { + gint width; + gint height; + + width = gdk_pixbuf_get_width(pixbuf); + height = gdk_pixbuf_get_height(pixbuf); + (context->update_func)(pixbuf, 0, 0, width, height, context->user_data); + } + + /* Free GdkPixbuf (callback function should ref it). */ + g_object_ref(pixbuf); + } + + return ret; +} + + +/* Incrementally load the next chunk of data. */ +static gboolean gdk_pixbuf__qtif_image_load_increment (gpointer data, + const guchar *buf, guint size, + GError **error) +{ + QTIFContext *context = (QTIFContext *)data; + GError *tmp = NULL; + gboolean ret = TRUE; /* Return TRUE for insufficient data. */ + + while(ret && (size != 0u)) + { + switch(context->state) + { + case STATE_READY: + /* Abort if we have seen too mant atoms. */ + if(context->atom_count == 0u) + { + g_set_error_literal (error, GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("Failed to find an image data atom.")); + return FALSE; + } + context->atom_count--; + + /* Copy to header buffer in context, in case supplied data is not enough. */ + while(context->run_length < sizeof(QtHeader)) + { + context->header_buffer[context->run_length] = *buf; + context->run_length++; + buf++; + size--; + } + + /* Parse buffer as QT header. */ + if(context->run_length == sizeof(QtHeader)) + { + QtHeader *hdr = (QtHeader *)context->header_buffer; + context->run_length = GUINT32_FROM_BE(hdr->length) - sizeof(QtHeader); + + /* Atom max size check. */ + if(context->run_length > ATOM_SIZE_MAX) + { + g_set_error(error, GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("QTIF atom size too large (%d bytes)"), hdr->length); + return FALSE; + } + + /* Set state according to atom type. */ + if(GUINT32_FROM_BE(hdr->tag) == QTIF_TAG_IDATA) + { + GError *tmp = NULL; + + context->state = STATE_DATA; + + /* Create GdkPixbufLoader for this image data. */ + ret = gdk_pixbuf__qtif_image_create_loader(context, &tmp); + if(!ret) + { + g_propagate_error (error, tmp); + } + } + else + { + context->state = STATE_OTHER; + } + } + break; + + default: /* Both STATE_DATA and STATE_OTHER will come here. */ + /* Check for atom boundary. */ + if(context->run_length > size) + { + /* Supply image data to GdkPixbufLoader if in STATE_DATA. */ + if(context->state == STATE_DATA) + { + tmp = NULL; + ret = gdk_pixbuf_loader_write(context->loader, buf, size, &tmp); + if(!ret && (error != NULL) && (*error == NULL)) + { + g_propagate_error (error, tmp); + } + } + context->run_length -= size; + size = 0u; + } + else + { + /* Supply image data to GdkPixbufLoader if in STATE_DATA. */ + if(context->state == STATE_DATA) + { + gboolean r; + + /* Here we should have concluded a complete image atom. */ + tmp = NULL; + ret = gdk_pixbuf_loader_write(context->loader, buf, context->run_length, &tmp); + if(!ret && (error != NULL) && (*error == NULL)) + { + g_propagate_error (error, tmp); + } + + /* Free GdkPixbufLoader and handle callback. */ + tmp = NULL; + r = gdk_pixbuf__qtif_image_free_loader(context, &tmp); + if(!r) + { + if((error != NULL) && (*error == NULL)) + { + g_propagate_error (error, tmp); + } + ret = FALSE; + } + } + buf = &buf[context->run_length]; + size -= context->run_length; + context->run_length = 0u; + context->state = STATE_READY; + } + break; + } + } + + return ret; +} + +/* Event handlers */ +static void gdk_pixbuf__qtif_cb_size_prepared(GdkPixbufLoader *loader, + gint width, + gint height, + gpointer user_data) +{ + QTIFContext *context = (QTIFContext *)user_data; + if((context != NULL) && (context->size_func != NULL)) + { + (context->size_func)(&width, &height, context->user_data); + context->cb_prepare_count++; + } +} + +static void gdk_pixbuf__qtif_cb_area_prepared(GdkPixbufLoader *loader, gpointer user_data) +{ + QTIFContext *context = (QTIFContext *)user_data; + if((loader != NULL) && (context != NULL) && (context->prepare_func != NULL)) + { + GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(context->loader); + (context->prepare_func)(pixbuf, NULL, context->user_data); + context->cb_update_count++; + } +} + +static void gdk_pixbuf__qtif_cb_area_updated(GdkPixbufLoader *loader, + gint x, + gint y, + gint width, + gint height, + gpointer user_data) +{ + QTIFContext *context = (QTIFContext *)user_data; + if((loader != NULL) && (context != NULL) && (context->update_func != NULL)) + { + GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(context->loader); + (context->update_func)(pixbuf, x, y, width, height, context->user_data); + } +} + + +#ifndef INCLUDE_qtif +#define MODULE_ENTRY(function) G_MODULE_EXPORT void function +#else +#define MODULE_ENTRY(function) void _gdk_pixbuf__qtif_ ## function +#endif + +MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module) +{ + module->load = gdk_pixbuf__qtif_image_load; + module->begin_load = gdk_pixbuf__qtif_image_begin_load; + module->stop_load = gdk_pixbuf__qtif_image_stop_load; + module->load_increment = gdk_pixbuf__qtif_image_load_increment; +} + +MODULE_ENTRY (fill_info) (GdkPixbufFormat *info) +{ + static GdkPixbufModulePattern signature[] = { + { "abcdidsc", "xxxx ", 100 }, + { "abcdidat", "xxxx ", 100 }, + { NULL, NULL, 0 } + }; + static gchar * mime_types[] = { + "image/x-quicktime", + "image/qtif", + NULL + }; + static gchar * extensions[] = { + "qtif", + "qif", + NULL + }; + + info->name = "qtif"; + info->signature = signature; + info->description = N_("The QTIF image format"); + info->mime_types = mime_types; + info->extensions = extensions; + info->flags = GDK_PIXBUF_FORMAT_THREADSAFE; + info->license = "LGPL"; +} + |