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 * - supply background color to transparent PNG images
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 png_set_strip_alpha(png_ptr);
150 case PNG_COLOR_TYPE_GRAY:
151 imo->thumb.flags |= IMAGE_GRAYSCALE;
152 png_set_gray_to_rgb(png_ptr);
154 case PNG_COLOR_TYPE_GRAY_ALPHA:
155 imo->thumb.flags |= IMAGE_GRAYSCALE;
156 png_set_gray_to_rgb(png_ptr);
157 png_set_strip_alpha(png_ptr);
159 case PNG_COLOR_TYPE_RGB:
161 case PNG_COLOR_TYPE_RGB_ALPHA:
162 png_set_strip_alpha(png_ptr);
167 png_read_update_info(png_ptr, info_ptr);
168 ASSERT(png_get_channels(png_ptr, info_ptr) == 3);
170 /* Read image data */
171 DBG("Reading image data");
172 byte *pixels = imo->thumb.pixels = mp_alloc(imo->pool, imo->thumb.size = width * height * 3);
173 png_bytep rows[height];
174 for (uns i = 0; i < height; i++, pixels += width * 3)
175 rows[i] = (png_bytep)pixels;
176 png_read_image(png_ptr, rows);
177 png_read_end(png_ptr, end_ptr);
179 /* Destroy libpng read structure */
180 png_destroy_read_struct(&png_ptr, &info_ptr, &end_ptr);
184 #endif /* USE_LIBPNG */
188 /******************************* LIBJPEG Library *************************************/
196 struct jpeg_error_mgr pub;
201 libjpeg_error_exit(j_common_ptr cinfo)
203 DBG("libjpeg_error_exit()");
204 longjmp(((struct libjpeg_err *)(cinfo)->err)->setjmp_buf, 1);
208 libjpeg_emit_message(j_common_ptr cinfo UNUSED, int msg_level UNUSED)
210 DBG("libjpeg_emit_message(): level=%d", msg_level);
211 /* if (unlikely(msg_level == -1))
212 longjmp(((struct libjpeg_err *)(cinfo)->err)->setjmp_buf, 1); */
216 libjpeg_init_source(j_decompress_ptr cinfo UNUSED)
218 DBG("libjpeg_init_source()");
221 static boolean NONRET
222 libjpeg_fill_input_buffer(j_decompress_ptr cinfo)
224 DBG("libjpeg_fill_input_buffer()");
225 longjmp(((struct libjpeg_err *)(cinfo)->err)->setjmp_buf, 1);
229 libjpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
231 DBG("libjpeg_skip_input_data(): len=%d", (int)num_bytes);
234 cinfo->src->next_input_byte += num_bytes;
235 cinfo->src->bytes_in_buffer -= num_bytes;
240 libjpeg_decompress_thumbnails_init(void)
245 libjpeg_decompress_thumbnails_done(void)
250 libjpeg_decompress_thumbnail(struct image_obj *imo)
252 /* Create libjpeg read structure */
253 DBG("Creating libjpeg read structure");
254 struct jpeg_decompress_struct cinfo;
255 struct libjpeg_err err;
256 cinfo.err = jpeg_std_error(&err.pub);
257 err.pub.error_exit = libjpeg_error_exit;
258 err.pub.emit_message = libjpeg_emit_message;
259 if (setjmp(err.setjmp_buf))
261 DBG("Libjpeg failed to read the image, longjump saved us");
262 jpeg_destroy_decompress(&cinfo);
265 jpeg_create_decompress(&cinfo);
267 /* Initialize source manager */
268 struct jpeg_source_mgr src;
270 src.next_input_byte = imo->thumb_data;
271 src.bytes_in_buffer = imo->thumb_size;
272 src.init_source = libjpeg_init_source;
273 src.fill_input_buffer = libjpeg_fill_input_buffer;
274 src.skip_input_data = libjpeg_skip_input_data;
275 src.resync_to_restart = jpeg_resync_to_restart;
276 src.term_source = libjpeg_init_source;
278 /* Read JPEG header and setup decompression options */
279 DBG("Reading image header");
280 jpeg_read_header(&cinfo, TRUE);
281 imo->thumb.flags = 0;
282 if (cinfo.out_color_space == JCS_GRAYSCALE)
283 imo->thumb.flags |= IMAGE_GRAYSCALE;
285 cinfo.out_color_space = JCS_RGB;
287 /* Decompress the image */
288 DBG("Reading image data");
289 jpeg_start_decompress(&cinfo);
290 ASSERT(imo->thumb.width == cinfo.output_width && imo->thumb.height == cinfo.output_height);
291 ASSERT(sizeof(JSAMPLE) == 1);
292 byte *pixels = imo->thumb.pixels = mp_alloc(imo->pool, imo->thumb.size = cinfo.output_width * cinfo.output_height * 3);
293 if (cinfo.out_color_space == JCS_RGB)
294 { /* Read RGB pixels */
295 uns size = cinfo.output_width * 3;
296 while (cinfo.output_scanline < cinfo.output_height)
298 jpeg_read_scanlines(&cinfo, (JSAMPLE **)&pixels, 1);
303 { /* Read grayscale pixels */
304 JSAMPLE buf[cinfo.output_width], *buf_end = buf + cinfo.output_width;
305 while (cinfo.output_scanline < cinfo.output_height)
308 jpeg_read_scanlines(&cinfo, &p, 1);
309 for (; p != buf_end; p++)
311 pixels[0] = pixels[1] = pixels[2] = p[0];
316 jpeg_finish_decompress(&cinfo);
318 /* Destroy libjpeg object and leave */
319 jpeg_destroy_decompress(&cinfo);
323 #endif /* USE_LIBJPEG */
327 /****************************** GraphicsMagick Library ******************************/
331 #include <magick/api.h>
333 static ExceptionInfo magick_exception;
334 static QuantizeInfo magick_quantize;
335 static ImageInfo *magick_info;
338 magick_decompress_thumbnails_init(void)
340 DBG("Initializing magick thumbnails decompression");
341 InitializeMagick(NULL);
342 GetExceptionInfo(&magick_exception);
343 magick_info = CloneImageInfo(NULL);
344 magick_info->subrange = 1;
345 GetQuantizeInfo(&magick_quantize);
346 magick_quantize.colorspace = RGBColorspace;
350 magick_decompress_thumbnails_done(void)
352 DBG("Finalizing magick thumbnails decompression");
353 DestroyImageInfo(magick_info);
354 DestroyExceptionInfo(&magick_exception);
359 magick_decompress_thumbnail(struct image_obj *imo)
361 DBG("Reading image data");
362 Image *image = BlobToImage(magick_info, imo->thumb_data, imo->thumb_size, &magick_exception);
363 if (unlikely(!image))
365 ASSERT(image->columns == imo->thumb.width && image->rows == imo->thumb.height);
366 DBG("Quantizing image");
367 QuantizeImage(&magick_quantize, image);
368 DBG("Converting pixels");
369 PixelPacket *pixels = (PixelPacket *)AcquireImagePixels(image, 0, 0, image->columns, image->rows, &magick_exception);
371 uns size = image->columns * image->rows;
372 byte *p = imo->thumb.pixels = mp_alloc(imo->pool, imo->thumb.size = size * 3);
373 for (uns i = 0; i < size; i++)
375 p[0] = pixels->red >> (QuantumDepth - 8);
376 p[1] = pixels->green >> (QuantumDepth - 8);
377 p[2] = pixels->blue >> (QuantumDepth - 8);
385 #endif /* USE_MAGICK */
389 /*************************************************************************************/
392 extract_image_info(struct image_obj *imo)
394 DBG("Parsing image info attribute");
395 ASSERT(!(imo->flags & IMAGE_OBJ_VALID_INFO));
396 imo->flags |= IMAGE_OBJ_VALID_INFO;
397 byte *info = obj_find_aval(imo->obj, 'G');
400 DBG("Attribute G not found");
404 byte color_space[MAX_ATTR_SIZE], thumb_format[MAX_ATTR_SIZE];
405 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);
407 switch (*thumb_format)
410 imo->thumb_format = IMAGE_OBJ_FORMAT_JPEG;
413 imo->thumb_format = IMAGE_OBJ_FORMAT_PNG;
422 extract_thumb_data(struct image_obj *imo)
424 DBG("Extracting thumbnail data");
425 ASSERT(!(imo->flags & IMAGE_OBJ_VALID_DATA) &&
426 (imo->flags & IMAGE_OBJ_VALID_INFO));
427 imo->flags |= IMAGE_OBJ_VALID_DATA;
428 struct oattr *attr = obj_find_attr(imo->obj, 'N');
431 DBG("There is no thumbnail attribute N");
435 for (struct oattr *a = attr; a; a = a->same)
437 byte b224[count * MAX_ATTR_SIZE], *b = b224;
438 for (struct oattr *a = attr; a; a = a->same)
439 for (byte *s = a->val; *s; )
443 imo->thumb_data = mp_alloc(imo->pool, size);
444 imo->thumb_size = base224_decode(imo->thumb_data, b224, size);
445 DBG("Thumbnail data size is %d", imo->thumb_size);
450 extract_thumb_image(struct image_obj *imo)
452 DBG("Decompressing thumbnail image");
453 ASSERT(!(imo->flags & IMAGE_OBJ_VALID_IMAGE) &&
454 (imo->flags & IMAGE_OBJ_VALID_INFO) &&
455 (imo->flags & IMAGE_OBJ_VALID_DATA));
456 imo->flags |= IMAGE_OBJ_VALID_IMAGE;
457 switch (imo->thumb_format)
459 case IMAGE_OBJ_FORMAT_JPEG:
460 #if defined(USE_LIBJPEG)
461 return libjpeg_decompress_thumbnail(imo);
462 #elif defined(USE_MAGICK)
463 return magick_decompress_thumbnail(imo);
465 DBG("JPEG not supported");
468 case IMAGE_OBJ_FORMAT_PNG:
469 #if defined(USE_LIBPNG)
470 return libpng_decompress_thumbnail(imo);
471 #elif defined(USE_MAGICK)
472 return magick_decompress_thumbnail(imo);
474 DBG("PNG not supported");
483 imo_decompress_thumbnails_init(void)
486 libpng_decompress_thumbnails_init();
489 libjpeg_decompress_thumbnails_init();
492 magick_decompress_thumbnails_init();
497 imo_decompress_thumbnails_done(void)
500 magick_decompress_thumbnails_done();
503 libjpeg_decompress_thumbnails_done();
506 libpng_decompress_thumbnails_done();
511 imo_decompress_thumbnail(struct image_obj *imo)
514 extract_image_info(imo) &&
515 extract_thumb_data(imo) &&
516 extract_thumb_image(imo);