diff options
author | Bastien Nocera <hadess@hadess.net> | 2007-11-25 18:07:01 +0000 |
---|---|---|
committer | Bastien Nocera <hadess@src.gnome.org> | 2007-11-25 18:07:01 +0000 |
commit | 6ecfe5ad19ea8684b86adaa8940e60d5fd959a5e (patch) | |
tree | d13a8e0af18885973cd897d9ae5ee1d297e58f8e /gdk-pixbuf/io-jasper.c | |
parent | 0031f9dcc7bf8fe983a108084ae8a097371c9b4c (diff) | |
download | gdk-pixbuf-6ecfe5ad19ea8684b86adaa8940e60d5fd959a5e.tar.gz |
Add detection for libjasper, used by the gdk-pixbuf JPEG2000 loader
2007-11-25 Bastien Nocera <hadess@hadess.net>
* configure.in: Add detection for libjasper, used by the
gdk-pixbuf JPEG2000 loader
2007-11-25 Bastien Nocera <hadess@hadess.net>
* Makefile.am:
* io-jasper.c: Add the libjasper JPEG2000 loader
(Closes: #469901)
svn path=/trunk/; revision=19042
Diffstat (limited to 'gdk-pixbuf/io-jasper.c')
-rw-r--r-- | gdk-pixbuf/io-jasper.c | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/gdk-pixbuf/io-jasper.c b/gdk-pixbuf/io-jasper.c new file mode 100644 index 000000000..5194ebd00 --- /dev/null +++ b/gdk-pixbuf/io-jasper.c @@ -0,0 +1,305 @@ +/* JPEG 2000 loader + * + * Copyright (c) 2007 Bastien Nocera <hadess@hadess.net> + * Inspired by work by Ben Karel <web+moz@eschew.org> + * + * 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 <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "gdk-pixbuf-private.h" +#include "gdk-pixbuf-io.h" + +#include <jasper/jasper.h> + +G_MODULE_EXPORT void fill_vtable (GdkPixbufModule * module); +G_MODULE_EXPORT void fill_info (GdkPixbufFormat * info); + +struct jasper_context { + GdkPixbuf *pixbuf; + + GdkPixbufModuleSizeFunc size_func; + GdkPixbufModuleUpdatedFunc updated_func; + GdkPixbufModulePreparedFunc prepared_func; + gpointer user_data; + + jas_stream_t *stream; + + int width, height; +}; + +static void +free_jasper_context (struct jasper_context *context) +{ + if (!context) + return; + + if (context->stream) { + jas_stream_close (context->stream); + context->stream = NULL; + } + + g_free (context); +} + +static gpointer +jasper_image_begin_load (GdkPixbufModuleSizeFunc size_func, + GdkPixbufModulePreparedFunc prepared_func, + GdkPixbufModuleUpdatedFunc updated_func, + gpointer user_data, GError **error) +{ + struct jasper_context *context; + jas_stream_t *stream; + + jas_init (); + + stream = jas_stream_memopen (NULL, -1); + if (!stream) { + g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for stream")); + return NULL; + } + + context = g_new0 (struct jasper_context, 1); + if (!context) + return NULL; + + context->size_func = size_func; + context->updated_func = updated_func; + context->prepared_func = prepared_func; + context->user_data = user_data; + context->width = context->height = -1; + + context->stream = stream; + + return context; +} + +static gboolean +jasper_image_try_load (struct jasper_context *context, GError **error) +{ + jas_image_t *raw_image, *image; + int num_components, colourspace_family; + int i, rowstride, shift; + guchar *pixels; + + raw_image = jas_image_decode (context->stream, -1, 0); + if (!raw_image) { + g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Couldn't decode image")); + return FALSE; + } + + if (context->width == -1 && context->height == -1) { + int width, height; + + context->width = width = jas_image_cmptwidth (raw_image, 0); + context->height = height = jas_image_cmptheight (raw_image, 0); + + if (context->size_func) { + (*context->size_func) (&width, &height, context->user_data); + + if (width == 0 || height == 0) { + jas_image_destroy(raw_image); + g_set_error (error, + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("Transformed JPEG2000 has zero width or height")); + return FALSE; + } + } + } + + /* We only know how to handle grayscale and RGB images */ + num_components = jas_image_numcmpts (raw_image); + colourspace_family = jas_clrspc_fam (jas_image_clrspc (raw_image)); + + if ((num_components != 3 && num_components != 4 && num_components != 1) || + (colourspace_family != JAS_CLRSPC_FAM_RGB && colourspace_family != JAS_CLRSPC_FAM_GRAY)) { + jas_image_destroy (raw_image); + g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Image type currently not supported")); + return FALSE; + } + + /* Apply the colour profile to the image, creating a new one */ + if (jas_image_clrspc (raw_image) != JAS_CLRSPC_SRGB) { + jas_cmprof_t *profile; + + profile = jas_cmprof_createfromclrspc (JAS_CLRSPC_SRGB); + if (!profile) { + jas_image_destroy (raw_image); + g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for color profile")); + return FALSE; + } + + image = jas_image_chclrspc (raw_image, profile, JAS_CMXFORM_INTENT_PER); + if (!image) { + jas_image_destroy (raw_image); + g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory for color profile")); + return FALSE; + } + } else { + image = raw_image; + } + + if (!context->pixbuf) { + int bits_per_sample; + guchar *data; + + /* Unfortunately, gdk-pixbuf doesn't support 16 bpp images + * bits_per_sample = jas_image_cmptprec (image, 0); + if (bits_per_sample < 8) + bits_per_sample = 8; + else if (bits_per_sample > 8) + bits_per_sample = 16; + */ + bits_per_sample = 8; + + data = g_try_malloc0 (context->width * context->height * bits_per_sample / 8); + if (data == NULL) { + g_set_error (error, + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, + _("Insufficient memory to open JPEG 2000 file")); + return FALSE; + } + context->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, + FALSE, bits_per_sample, + context->width, context->height); + if (context->prepared_func) + context->prepared_func (context->pixbuf, NULL, context->user_data); + } + + /* We calculate how much we should shift the pixel + * data by to make it fit into our pixbuf */ + shift = MAX (jas_image_cmptprec (image, 0) - gdk_pixbuf_get_bits_per_sample (context->pixbuf), 0); + + /* Loop over the 3 colourspaces */ + rowstride = gdk_pixbuf_get_rowstride (context->pixbuf); + pixels = gdk_pixbuf_get_pixels (context->pixbuf); + + for (i = 0; i < num_components; i++) { + jas_matrix_t *matrix; + int j; + + matrix = jas_matrix_create (context->height, context->width); + + /* in libjasper, R is 0, G is 1, etc. we're lucky :) + * but we need to handle the "opacity" channel ourselves */ + if (i != 4) { + jas_image_readcmpt (image, i, 0, 0, context->width, context->height, matrix); + } else { + jas_image_readcmpt (image, JAS_IMAGE_CT_OPACITY, 0, 0, context->width, context->height, matrix); + } + + for (j = 0; j < context->height; j++) { + int k; + + for (k = 0; k < context->width; k++) { + if (num_components == 3 || num_components == 4) { + pixels[j * rowstride + k * 3 + i] = jas_matrix_get (matrix, j, k) >> shift; + } else { + pixels[j * rowstride + k * 3] = + pixels[j * rowstride + k * 3 + 1] = + pixels[j * rowstride + k * 3 + 2] = jas_matrix_get (matrix, j, k) >> shift; + } + } + /* Update once per line for the last component, otherwise + * we might contain garbage */ + if (context->updated_func && (i == num_components - 1) && k != 0) { + context->updated_func (context->pixbuf, 0, j, k, 1, context->user_data); + } + } + + jas_matrix_destroy (matrix); + } + + if (image != raw_image) + jas_image_destroy (image); + jas_image_destroy (raw_image); + + return TRUE; +} + +static gboolean +jasper_image_stop_load (gpointer data, GError **error) +{ + struct jasper_context *context = (struct jasper_context *) data; + gboolean ret; + + jas_stream_rewind (context->stream); + ret = jasper_image_try_load (context, error); + + free_jasper_context (context); + + return ret; +} + +static gboolean +jasper_image_load_increment (gpointer data, const guchar *buf, guint size, GError **error) +{ + struct jasper_context *context = (struct jasper_context *) data; + + if (jas_stream_write (context->stream, buf, size) < 0) { + g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't allocate memory to buffer image data")); + return FALSE; + } + + return TRUE; +} + +void +fill_vtable (GdkPixbufModule * module) +{ + module->begin_load = jasper_image_begin_load; + module->stop_load = jasper_image_stop_load; + module->load_increment = jasper_image_load_increment; +} + +void +fill_info (GdkPixbufFormat * info) +{ + static GdkPixbufModulePattern signature[] = { + { " jP", "!!!! ", 100 }, /* file begins with 'jP' at offset 4 */ + { "\xff\x4f\xff\x51\x00", NULL, 100 }, /* file starts with FF 4F FF 51 00 */ + { NULL, NULL, 0 } + }; + static gchar *mime_types[] = { + "image/jp2", + "image/jpeg2000", + "image/jpx", + NULL + }; + static gchar *extensions[] = { + "jp2", + "jpc", + "jpx", + "j2k", + "jpf", + NULL + }; + + info->name = "jpeg2000"; + info->signature = signature; + info->description = N_("The JPEG 2000 image format"); + info->mime_types = mime_types; + info->extensions = extensions; + info->flags = GDK_PIXBUF_FORMAT_THREADSAFE; + info->license = "LGPL"; + info->disabled = FALSE; +} + |