2 * Image Library -- Image Cards Manipulations
4 * (c) 2006 Pavel Charvat <pchar@ucw.cz>
6 * This software may be freely distributed and used according to the terms
7 * of the GNU Lesser General Public License.
10 * - improve thumbnail creation in gatherer... faster compression,
11 * only grayscale/RGB colorspaces and maybe fixed headers (abbreviated datastreams in libjpeg)
12 * - hook memory allocation managers, get rid of multiple initializations
13 * - alpha channel is propably useless (we could assume white background)
14 * - optimize decompression parameters
15 * - create interface for thumbnail compression (for gatherer) and reading (MUX)
16 * - benchmatk libraries
21 #include "sherlock/sherlock.h"
22 #include "lib/base224.h"
23 #include "lib/mempool.h"
24 #include "sherlock/object.h"
25 #include "images/images.h"
26 #include "images/image-obj.h"
31 /* Selection of libraries to use */
36 #if defined(USE_LIBPNG) && defined(USE_LIBJPEG)
41 /********************************* LIBPNG Library ****************************************/
48 static struct mempool *libpng_pool;
49 static byte *libpng_buf;
50 static uns libpng_len;
53 libpng_malloc(png_structp png_ptr UNUSED, png_size_t size)
55 DBG("libpng_malloc(): size=%d", (uns)size);
56 return mp_alloc(libpng_pool, size);
60 libpng_free(png_structp png_ptr UNUSED, png_voidp ptr UNUSED)
66 libpng_error(png_structp png_ptr, png_const_charp msg UNUSED)
68 DBG("libpng_error(): msg=%s", (byte *)msg);
69 longjmp(png_jmpbuf(png_ptr), 1);
73 libpng_warning(png_structp png_ptr UNUSED, png_const_charp msg UNUSED)
75 DBG("libpng_warning(): msg=%s", (byte *)msg);
79 libpng_read_data(png_structp png_ptr UNUSED, png_bytep data, png_size_t length)
81 DBG("libpng_read_data(): len=%d", (uns)length);
82 if (unlikely(libpng_len < length))
83 png_error(png_ptr, "Incomplete data");
84 memcpy(data, libpng_buf, length);
90 libpng_decompress_thumbnails_init(void)
95 libpng_decompress_thumbnails_done(void)
100 libpng_decompress_thumbnail(struct image_obj *imo)
102 /* create libpng read structure */
103 DBG("Creating libpng read structure");
104 libpng_pool = imo->pool;
105 libpng_buf = imo->thumb_data;
106 libpng_len = imo->thumb_size;
107 png_structp png_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING,
108 NULL, libpng_error, libpng_warning,
109 NULL, libpng_malloc, libpng_free);
110 if (unlikely(!png_ptr))
112 png_infop info_ptr = png_create_info_struct(png_ptr);
113 if (unlikely(!info_ptr))
115 png_destroy_read_struct(&png_ptr, NULL, NULL);
118 png_infop end_ptr = png_create_info_struct(png_ptr);
119 if (unlikely(!end_ptr))
121 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
124 if (setjmp(png_jmpbuf(png_ptr)))
126 DBG("Libpng failed to read the image, longjump saved us");
127 png_destroy_read_struct(&png_ptr, &info_ptr, &end_ptr);
130 png_set_read_fn(png_ptr, NULL, libpng_read_data);
132 /* Read image info */
133 DBG("Reading image info");
134 png_read_info(png_ptr, info_ptr);
135 png_uint_32 width, height;
136 int bit_depth, color_type;
137 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
138 ASSERT(width == imo->thumb.width && height == imo->thumb.height);
140 /* Apply transformations */
141 imo->thumb.flags = 0;
143 png_set_strip_16(png_ptr);
146 case PNG_COLOR_TYPE_PALETTE:
147 png_set_palette_to_rgb(png_ptr);
148 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
150 png_set_tRNS_to_alpha(png_ptr);
151 imo->thumb.flags |= IMAGE_ALPHA;
154 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
156 case PNG_COLOR_TYPE_GRAY:
157 imo->thumb.flags |= IMAGE_GRAYSCALE;
158 png_set_gray_to_rgb(png_ptr);
159 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
161 case PNG_COLOR_TYPE_GRAY_ALPHA:
162 imo->thumb.flags |= IMAGE_GRAYSCALE | IMAGE_ALPHA;
163 png_set_gray_to_rgb(png_ptr);
165 case PNG_COLOR_TYPE_RGB:
166 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
168 case PNG_COLOR_TYPE_RGB_ALPHA:
169 imo->thumb.flags |= IMAGE_ALPHA;
174 png_read_update_info(png_ptr, info_ptr);
175 ASSERT(png_get_channels(png_ptr, info_ptr) == sizeof(struct pixel));
176 ASSERT(png_get_rowbytes(png_ptr, info_ptr) == sizeof(struct pixel) * width);
178 /* Read image data */
179 DBG("Reading image data");
180 struct pixel *pixels = imo->thumb.pixels = mp_alloc(imo->pool, width * height * sizeof(struct pixel));
181 png_bytep rows[height];
182 for (uns i = 0; i < height; i++, pixels += width)
183 rows[i] = (png_bytep)pixels;
184 png_read_image(png_ptr, rows);
185 png_read_end(png_ptr, end_ptr);
187 /* Destroy libpng read structure */
188 png_destroy_read_struct(&png_ptr, &info_ptr, &end_ptr);
192 #endif /* USE_LIBPNG */
196 /******************************* LIBJPEG Library *************************************/
204 struct jpeg_error_mgr pub;
209 libjpeg_error_exit(j_common_ptr cinfo)
211 DBG("libjpeg_error_exit()");
212 longjmp(((struct libjpeg_err *)(cinfo)->err)->setjmp_buf, 1);
216 libjpeg_emit_message(j_common_ptr cinfo UNUSED, int msg_level UNUSED)
218 DBG("libjpeg_emit_message(): level=%d", msg_level);
219 /* if (unlikely(msg_level == -1))
220 longjmp(((struct libjpeg_err *)(cinfo)->err)->setjmp_buf, 1); */
224 libjpeg_init_source(j_decompress_ptr cinfo UNUSED)
226 DBG("libjpeg_init_source()");
229 static boolean NONRET
230 libjpeg_fill_input_buffer(j_decompress_ptr cinfo)
232 DBG("libjpeg_fill_input_buffer()");
233 longjmp(((struct libjpeg_err *)(cinfo)->err)->setjmp_buf, 1);
237 libjpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
239 DBG("libjpeg_skip_input_data(): len=%d", (int)num_bytes);
242 cinfo->src->next_input_byte += num_bytes;
243 cinfo->src->bytes_in_buffer -= num_bytes;
248 libjpeg_decompress_thumbnails_init(void)
253 libjpeg_decompress_thumbnails_done(void)
258 libjpeg_decompress_thumbnail(struct image_obj *imo)
260 /* Create libjpeg read structure */
261 DBG("Creating libjpeg read structure");
262 struct jpeg_decompress_struct cinfo;
263 struct libjpeg_err err;
264 cinfo.err = jpeg_std_error(&err.pub);
265 err.pub.error_exit = libjpeg_error_exit;
266 err.pub.emit_message = libjpeg_emit_message;
267 if (setjmp(err.setjmp_buf))
269 DBG("Libjpeg failed to read the image, longjump saved us");
270 jpeg_destroy_decompress(&cinfo);
273 jpeg_create_decompress(&cinfo);
275 /* Initialize source manager */
276 struct jpeg_source_mgr src;
278 src.next_input_byte = imo->thumb_data;
279 src.bytes_in_buffer = imo->thumb_size;
280 src.init_source = libjpeg_init_source;
281 src.fill_input_buffer = libjpeg_fill_input_buffer;
282 src.skip_input_data = libjpeg_skip_input_data;
283 src.resync_to_restart = jpeg_resync_to_restart;
284 src.term_source = libjpeg_init_source;
286 /* Read JPEG header and setup decompression options */
287 DBG("Reading image header");
288 jpeg_read_header(&cinfo, TRUE);
289 imo->thumb.flags = 0;
290 if (cinfo.out_color_space == JCS_GRAYSCALE)
291 imo->thumb.flags |= IMAGE_GRAYSCALE;
293 cinfo.out_color_space = JCS_RGB;
295 /* Decompress the image */
296 DBG("Reading image data");
297 jpeg_start_decompress(&cinfo);
298 ASSERT(imo->thumb.width == cinfo.output_width && imo->thumb.height == cinfo.output_height);
299 struct pixel *pixels = imo->thumb.pixels = mp_alloc(imo->pool, cinfo.output_width * cinfo.output_height * sizeof(struct pixel));
300 if (cinfo.out_color_space == JCS_RGB)
301 { /* Read RGB pixels */
302 uns size = cinfo.output_width * 3;
303 JSAMPLE buf[size], *buf_end = buf + size;
304 while (cinfo.output_scanline < cinfo.output_height)
307 jpeg_read_scanlines(&cinfo, &p, 1);
308 for (; p != buf_end; p += 3)
310 pixels->r = p[0] >> (sizeof(JSAMPLE) * 8 - 8);
311 pixels->g = p[1] >> (sizeof(JSAMPLE) * 8 - 8);
312 pixels->b = p[2] >> (sizeof(JSAMPLE) * 8 - 8);
319 { /* Read grayscale pixels */
320 JSAMPLE buf[cinfo.output_width], *buf_end = buf + cinfo.output_width;
321 while (cinfo.output_scanline < cinfo.output_height)
324 jpeg_read_scanlines(&cinfo, &p, 1);
325 for (; p != buf_end; p++)
327 pixels->r = pixels->g = pixels->b = p[0] >> (sizeof(JSAMPLE) * 8 - 8);
333 jpeg_finish_decompress(&cinfo);
335 /* Destroy libjpeg object and leave */
336 jpeg_destroy_decompress(&cinfo);
340 #endif /* USE_LIBJPEG */
344 /****************************** GraphicsMagick Library ******************************/
348 #include <magick/api.h>
350 static ExceptionInfo magick_exception;
351 static QuantizeInfo magick_quantize;
352 static ImageInfo *magick_info;
355 magick_decompress_thumbnails_init(void)
357 DBG("Initializing magick thumbnails decompression");
358 InitializeMagick(NULL);
359 GetExceptionInfo(&magick_exception);
360 magick_info = CloneImageInfo(NULL);
361 magick_info->subrange = 1;
362 GetQuantizeInfo(&magick_quantize);
363 magick_quantize.colorspace = RGBColorspace;
367 magick_decompress_thumbnails_done(void)
369 DBG("Finalizing magick thumbnails decompression");
370 DestroyImageInfo(magick_info);
371 DestroyExceptionInfo(&magick_exception);
376 magick_decompress_thumbnail(struct image_obj *imo)
378 DBG("Reading image data");
379 Image *image = BlobToImage(magick_info, imo->thumb_data, imo->thumb_size, &magick_exception);
380 if (unlikely(!image))
382 ASSERT(image->columns == imo->thumb.width && image->rows == imo->thumb.height);
383 DBG("Quantizing image");
384 QuantizeImage(&magick_quantize, image);
385 DBG("Converting pixels");
386 PixelPacket *pixels = (PixelPacket *)AcquireImagePixels(image, 0, 0, image->columns, image->rows, &magick_exception);
388 uns size = image->columns * image->rows;
389 struct pixel *p = imo->thumb.pixels = mp_alloc(imo->pool, size * sizeof(struct pixel));
390 for (uns i = 0; i < size; i++, p++, pixels++)
392 p->r = pixels->red >> (QuantumDepth - 8);
393 p->g = pixels->green >> (QuantumDepth - 8);
394 p->b = pixels->blue >> (QuantumDepth - 8);
401 #endif /* USE_MAGICK */
405 /*************************************************************************************/
408 extract_image_info(struct image_obj *imo)
410 DBG("Parsing image info attribute");
411 imo->flags |= IMAGE_OBJ_INFO;
412 byte *info = obj_find_aval(imo->obj, 'G');
415 DBG("Attribute G not found");
419 byte color_space[MAX_ATTR_SIZE], thumb_format[MAX_ATTR_SIZE];
420 UNUSED uns cnt = sscanf(info, "%d%d%s%d%d%d%s", &imo->width, &imo->height, color_space, &colors, &imo->thumb.width, &imo->thumb.height, thumb_format);
422 if (*thumb_format == 'j')
423 imo->flags |= IMAGE_OBJ_THUMB_JPEG;
428 extract_thumb_data(struct image_obj *imo)
430 DBG("Extracting thumbnail data");
431 imo->flags |= IMAGE_OBJ_THUMB_DATA;
432 struct oattr *attr = obj_find_attr(imo->obj, 'N');
435 DBG("There is no thumbnail attribute N");
439 for (struct oattr *a = attr; a; a = a->same)
441 byte b224[count * MAX_ATTR_SIZE], *b = b224;
442 for (struct oattr *a = attr; a; a = a->same)
443 for (byte *s = a->val; *s; )
447 imo->thumb_data = mp_alloc(imo->pool, size);
448 imo->thumb_size = base224_decode(imo->thumb_data, b224, size);
449 DBG("Thumbnail data size is %d", imo->thumb_size);
454 extract_thumb_image(struct image_obj *imo)
456 DBG("Decompressing thumbnail image");
457 imo->flags |= IMAGE_OBJ_THUMB_IMAGE;
460 if (imo->flags & IMAGE_OBJ_THUMB_JPEG)
462 #if defined(USE_LIBJPEG)
463 return libjpeg_decompress_thumbnail(imo);
464 #elif defined(USE_MAGICK)
465 return magick_decompress_thumbnail(imo);
467 DBG("JPEG not supported");
474 #if defined(USE_LIBPNG)
475 return libpng_decompress_thumbnail(imo);
476 #elif defined(USE_MAGICK)
477 return magick_decompress_thumbnail(imo);
479 DBG("PNG not supported");
486 imo_decompress_thumbnails_init(void)
489 libpng_decompress_thumbnails_init();
492 libjpeg_decompress_thumbnails_init();
495 magick_decompress_thumbnails_init();
500 imo_decompress_thumbnails_done(void)
503 magick_decompress_thumbnails_done();
506 libjpeg_decompress_thumbnails_done();
509 libpng_decompress_thumbnails_done();
514 imo_decompress_thumbnail(struct image_obj *imo)
517 extract_image_info(imo) &&
518 extract_thumb_data(imo) &&
519 extract_thumb_image(imo);