3 * Copyright (c) 2007 Bastien Nocera <hadess@hadess.net>
4 * Inspired by work by Ben Karel <web+moz@eschew.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
26 #include "gdk-pixbuf-private.h"
27 #include "gdk-pixbuf-io.h"
29 #include <jasper/jasper.h>
31 G_MODULE_EXPORT void fill_vtable (GdkPixbufModule * module);
32 G_MODULE_EXPORT void fill_info (GdkPixbufFormat * info);
34 struct jasper_context {
37 GdkPixbufModuleSizeFunc size_func;
38 GdkPixbufModuleUpdatedFunc updated_func;
39 GdkPixbufModulePreparedFunc prepared_func;
48 free_jasper_context (struct jasper_context *context)
53 if (context->stream) {
54 jas_stream_close (context->stream);
55 context->stream = NULL;
62 jasper_image_begin_load (GdkPixbufModuleSizeFunc size_func,
63 GdkPixbufModulePreparedFunc prepared_func,
64 GdkPixbufModuleUpdatedFunc updated_func,
65 gpointer user_data, GError **error)
67 struct jasper_context *context;
72 stream = jas_stream_memopen (NULL, -1);
74 g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
75 _("Couldn't allocate memory for stream"));
79 context = g_new0 (struct jasper_context, 1);
83 context->size_func = size_func;
84 context->updated_func = updated_func;
85 context->prepared_func = prepared_func;
86 context->user_data = user_data;
87 context->width = context->height = -1;
89 context->stream = stream;
95 jasper_image_try_load (struct jasper_context *context, GError **error)
97 jas_image_t *raw_image, *image;
98 int num_components, colourspace_family;
99 int i, rowstride, shift;
102 raw_image = jas_image_decode (context->stream, -1, 0);
104 g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
105 _("Couldn't decode image"));
109 if (context->width == -1 && context->height == -1) {
112 context->width = width = jas_image_cmptwidth (raw_image, 0);
113 context->height = height = jas_image_cmptheight (raw_image, 0);
115 if (context->size_func) {
116 (*context->size_func) (&width, &height, context->user_data);
118 if (width == 0 || height == 0) {
119 jas_image_destroy(raw_image);
120 g_set_error_literal (error,
122 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
123 _("Transformed JPEG2000 has zero width or height"));
129 /* We only know how to handle grayscale and RGB images */
130 num_components = jas_image_numcmpts (raw_image);
131 colourspace_family = jas_clrspc_fam (jas_image_clrspc (raw_image));
133 if ((num_components != 3 && num_components != 4 && num_components != 1) ||
134 (colourspace_family != JAS_CLRSPC_FAM_RGB && colourspace_family != JAS_CLRSPC_FAM_GRAY)) {
135 jas_image_destroy (raw_image);
136 g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
137 _("Image type currently not supported"));
141 /* Apply the colour profile to the image, creating a new one */
142 if (jas_image_clrspc (raw_image) != JAS_CLRSPC_SRGB) {
143 jas_cmprof_t *profile;
145 profile = jas_cmprof_createfromclrspc (JAS_CLRSPC_SRGB);
147 jas_image_destroy (raw_image);
148 g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
149 _("Couldn't allocate memory for color profile"));
153 image = jas_image_chclrspc (raw_image, profile, JAS_CMXFORM_INTENT_PER);
155 jas_image_destroy (raw_image);
156 g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
157 _("Couldn't allocate memory for color profile"));
164 if (!context->pixbuf) {
167 /* Unfortunately, gdk-pixbuf doesn't support 16 bpp images
168 * bits_per_sample = jas_image_cmptprec (image, 0);
169 if (bits_per_sample < 8)
171 else if (bits_per_sample > 8)
172 bits_per_sample = 16;
176 context->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
177 FALSE, bits_per_sample,
178 context->width, context->height);
179 if (context->pixbuf == NULL) {
180 g_set_error_literal (error,
182 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
183 _("Insufficient memory to open JPEG 2000 file"));
186 if (context->prepared_func)
187 context->prepared_func (context->pixbuf, NULL, context->user_data);
190 /* We calculate how much we should shift the pixel
191 * data by to make it fit into our pixbuf */
192 shift = MAX (jas_image_cmptprec (image, 0) - gdk_pixbuf_get_bits_per_sample (context->pixbuf), 0);
194 /* Loop over the 3 colourspaces */
195 rowstride = gdk_pixbuf_get_rowstride (context->pixbuf);
196 pixels = gdk_pixbuf_get_pixels (context->pixbuf);
198 for (i = 0; i < num_components; i++) {
199 jas_matrix_t *matrix;
202 matrix = jas_matrix_create (context->height, context->width);
204 /* in libjasper, R is 0, G is 1, etc. we're lucky :)
205 * but we need to handle the "opacity" channel ourselves */
207 jas_image_readcmpt (image, i, 0, 0, context->width, context->height, matrix);
209 jas_image_readcmpt (image, JAS_IMAGE_CT_OPACITY, 0, 0, context->width, context->height, matrix);
212 for (j = 0; j < context->height; j++) {
215 for (k = 0; k < context->width; k++) {
216 if (num_components == 3 || num_components == 4) {
217 pixels[j * rowstride + k * 3 + i] = jas_matrix_get (matrix, j, k) >> shift;
219 pixels[j * rowstride + k * 3] =
220 pixels[j * rowstride + k * 3 + 1] =
221 pixels[j * rowstride + k * 3 + 2] = jas_matrix_get (matrix, j, k) >> shift;
224 /* Update once per line for the last component, otherwise
225 * we might contain garbage */
226 if (context->updated_func && (i == num_components - 1) && k != 0) {
227 context->updated_func (context->pixbuf, 0, j, k, 1, context->user_data);
231 jas_matrix_destroy (matrix);
234 if (image != raw_image)
235 jas_image_destroy (image);
236 jas_image_destroy (raw_image);
242 jasper_image_stop_load (gpointer data, GError **error)
244 struct jasper_context *context = (struct jasper_context *) data;
247 jas_stream_rewind (context->stream);
248 ret = jasper_image_try_load (context, error);
250 free_jasper_context (context);
256 jasper_image_load_increment (gpointer data, const guchar *buf, guint size, GError **error)
258 struct jasper_context *context = (struct jasper_context *) data;
260 if (jas_stream_write (context->stream, buf, size) < 0) {
261 g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
262 _("Couldn't allocate memory to buffer image data"));
269 #ifndef INCLUDE_jasper
270 #define MODULE_ENTRY(function) G_MODULE_EXPORT void function
272 #define MODULE_ENTRY(function) void _gdk_pixbuf__jasper_ ## function
275 MODULE_ENTRY (fill_vtable) (GdkPixbufModule * module)
277 module->begin_load = jasper_image_begin_load;
278 module->stop_load = jasper_image_stop_load;
279 module->load_increment = jasper_image_load_increment;
282 MODULE_ENTRY (fill_info) (GdkPixbufFormat * info)
284 static GdkPixbufModulePattern signature[] = {
285 { " jP", "!!!! ", 100 }, /* file begins with 'jP' at offset 4 */
286 { "\xff\x4f\xff\x51\x00", NULL, 100 }, /* file starts with FF 4F FF 51 00 */
289 static gchar *mime_types[] = {
295 static gchar *extensions[] = {
304 info->name = "jpeg2000";
305 info->signature = signature;
306 info->description = N_("The JPEG 2000 image format");
307 info->mime_types = mime_types;
308 info->extensions = extensions;
309 info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
310 info->license = "LGPL";
311 info->disabled = FALSE;