]> mj.ucw.cz Git - libucw.git/blob - images/image-obj.c
Try to merge recent changes in v3.9 to image branch...
[libucw.git] / images / image-obj.c
1 /*
2  *      Image Library -- Image Cards Manipulations
3  *
4  *      (c) 2006 Pavel Charvat <pchar@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  *
9  *      FIXME:
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
17  */
18
19 #undef LOCAL_DEBUG
20
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"
27
28 #include <stdio.h>
29 #include <alloca.h>
30
31 /* Selection of libraries to use */
32 #define USE_LIBPNG
33 #define USE_LIBJPEG
34 #define USE_MAGICK
35
36 #if defined(USE_LIBPNG) && defined(USE_LIBJPEG)
37 #undef USE_MAGICK
38 #endif
39
40
41 /*********************************  LIBPNG Library ****************************************/
42
43 #ifdef USE_LIBPNG
44
45 #include <png.h>
46 #include <setjmp.h>
47
48 static struct mempool *libpng_pool;
49 static byte *libpng_buf;
50 static uns libpng_len;
51
52 static png_voidp
53 libpng_malloc(png_structp png_ptr UNUSED, png_size_t size)
54 {
55   DBG("libpng_malloc(): size=%d", (uns)size);
56   return mp_alloc(libpng_pool, size);
57 }
58
59 static void
60 libpng_free(png_structp png_ptr UNUSED, png_voidp ptr UNUSED)
61 {
62   DBG("libpng_free()");
63 }
64
65 static void NONRET
66 libpng_error(png_structp png_ptr, png_const_charp msg UNUSED)
67 {
68   DBG("libpng_error(): msg=%s", (byte *)msg);
69   longjmp(png_jmpbuf(png_ptr), 1);
70 }
71
72 static void
73 libpng_warning(png_structp png_ptr UNUSED, png_const_charp msg UNUSED)
74 {
75   DBG("libpng_warning(): msg=%s", (byte *)msg);
76 }
77
78 static void
79 libpng_read_data(png_structp png_ptr UNUSED, png_bytep data, png_size_t length)
80 {
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);
85   libpng_buf += length;
86   libpng_len -= length;
87 }
88
89 static inline void
90 libpng_decompress_thumbnails_init(void)
91 {
92 }
93
94 static inline void
95 libpng_decompress_thumbnails_done(void)
96 {
97 }
98
99 static int
100 libpng_decompress_thumbnail(struct image_obj *imo)
101 {
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))
111     return 0;
112   png_infop info_ptr = png_create_info_struct(png_ptr);
113   if (unlikely(!info_ptr))
114     {
115       png_destroy_read_struct(&png_ptr, NULL, NULL);
116       return 0;
117     }
118   png_infop end_ptr = png_create_info_struct(png_ptr);
119   if (unlikely(!end_ptr))
120     {
121       png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
122       return 0;
123     }
124   if (setjmp(png_jmpbuf(png_ptr)))
125     {
126       DBG("Libpng failed to read the image, longjump saved us");
127       png_destroy_read_struct(&png_ptr, &info_ptr, &end_ptr);
128       return 0;
129     }
130   png_set_read_fn(png_ptr, NULL, libpng_read_data);
131
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);
139
140   /* Apply transformations */
141   imo->thumb.flags = 0;
142   if (bit_depth == 16)
143     png_set_strip_16(png_ptr);
144   switch (color_type)
145     {
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))
149           {
150             png_set_tRNS_to_alpha(png_ptr);
151             imo->thumb.flags |= IMAGE_ALPHA;
152           }
153         else
154           png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
155         break;
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);
160         break;
161       case PNG_COLOR_TYPE_GRAY_ALPHA:
162         imo->thumb.flags |= IMAGE_GRAYSCALE | IMAGE_ALPHA;
163         png_set_gray_to_rgb(png_ptr);
164         break;
165       case PNG_COLOR_TYPE_RGB:
166         png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
167         break;
168       case PNG_COLOR_TYPE_RGB_ALPHA:
169         imo->thumb.flags |= IMAGE_ALPHA;
170         break;
171       default:
172         ASSERT(0);
173     }
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);
177
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);
186  
187   /* Destroy libpng read structure */
188   png_destroy_read_struct(&png_ptr, &info_ptr, &end_ptr);
189   return 1;
190 }
191
192 #endif /* USE_LIBPNG */
193
194
195
196 /*******************************  LIBJPEG Library *************************************/
197
198 #ifdef USE_LIBJPEG
199
200 #include <jpeglib.h>
201 #include <setjmp.h>
202
203 struct libjpeg_err {
204   struct jpeg_error_mgr pub;
205   jmp_buf setjmp_buf;
206 };
207
208 static void NONRET
209 libjpeg_error_exit(j_common_ptr cinfo)
210 {
211   DBG("libjpeg_error_exit()");
212   longjmp(((struct libjpeg_err *)(cinfo)->err)->setjmp_buf, 1);
213 }
214
215 static void
216 libjpeg_emit_message(j_common_ptr cinfo UNUSED, int msg_level UNUSED)
217 {
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); */
221 }
222
223 static void
224 libjpeg_init_source(j_decompress_ptr cinfo UNUSED)
225 {
226   DBG("libjpeg_init_source()");
227 }
228
229 static boolean NONRET
230 libjpeg_fill_input_buffer(j_decompress_ptr cinfo)
231
232   DBG("libjpeg_fill_input_buffer()");
233   longjmp(((struct libjpeg_err *)(cinfo)->err)->setjmp_buf, 1);
234 }
235
236 static void
237 libjpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes) 
238 {
239   DBG("libjpeg_skip_input_data(): len=%d", (int)num_bytes);
240   if (num_bytes > 0)
241     {
242       cinfo->src->next_input_byte += num_bytes;
243       cinfo->src->bytes_in_buffer -= num_bytes;
244     }
245 }
246
247 static inline void
248 libjpeg_decompress_thumbnails_init(void)
249 {
250 }
251
252 static inline void
253 libjpeg_decompress_thumbnails_done(void)
254 {
255 }
256
257 static int
258 libjpeg_decompress_thumbnail(struct image_obj *imo)
259 {
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))
268     {
269       DBG("Libjpeg failed to read the image, longjump saved us");
270       jpeg_destroy_decompress(&cinfo);
271       return 0;
272     }
273   jpeg_create_decompress(&cinfo);
274
275   /* Initialize source manager */
276   struct jpeg_source_mgr src;
277   cinfo.src = &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;
285
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;
292   else
293     cinfo.out_color_space = JCS_RGB;
294
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)
305         {
306           JSAMPLE *p = buf;
307           jpeg_read_scanlines(&cinfo, &p, 1);
308           for (; p != buf_end; p += 3)
309             {
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);
313               pixels->a = 255;
314               pixels++;
315             }
316         }
317     }
318   else
319     { /* Read grayscale pixels */
320       JSAMPLE buf[cinfo.output_width], *buf_end = buf + cinfo.output_width;
321       while (cinfo.output_scanline < cinfo.output_height)
322         {
323           JSAMPLE *p = buf;
324           jpeg_read_scanlines(&cinfo, &p, 1);
325           for (; p != buf_end; p++)
326             {
327               pixels->r = pixels->g = pixels->b = p[0] >> (sizeof(JSAMPLE) * 8 - 8);
328               pixels->a = 255;
329               pixels++;
330             }
331         }
332     }
333   jpeg_finish_decompress(&cinfo);
334
335   /* Destroy libjpeg object and leave */
336   jpeg_destroy_decompress(&cinfo);
337   return 1;
338 }
339
340 #endif /* USE_LIBJPEG */
341
342
343
344 /******************************  GraphicsMagick Library ******************************/
345
346 #ifdef USE_MAGICK
347
348 #include <magick/api.h>
349
350 static ExceptionInfo magick_exception;
351 static QuantizeInfo magick_quantize;
352 static ImageInfo *magick_info;
353
354 static void
355 magick_decompress_thumbnails_init(void)
356 {
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;
364 }
365
366 static void
367 magick_decompress_thumbnails_done(void)
368 {
369   DBG("Finalizing magick thumbnails decompression");
370   DestroyImageInfo(magick_info);
371   DestroyExceptionInfo(&magick_exception);
372   DestroyMagick();
373 }
374
375 static int
376 magick_decompress_thumbnail(struct image_obj *imo)
377 {
378   DBG("Reading image data");
379   Image *image = BlobToImage(magick_info, imo->thumb_data, imo->thumb_size, &magick_exception);
380   if (unlikely(!image))
381     return 0;
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);
387   ASSERT(pixels);
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++)
391     {
392       p->r = pixels->red >> (QuantumDepth - 8);
393       p->g = pixels->green >> (QuantumDepth - 8);
394       p->b = pixels->blue >> (QuantumDepth - 8);
395       p->a = 255;
396     }
397   DestroyImage(image);
398   return 1;
399 }
400
401 #endif /* USE_MAGICK */
402
403
404
405 /*************************************************************************************/
406
407 static int
408 extract_image_info(struct image_obj *imo)
409 {
410   DBG("Parsing image info attribute");
411   imo->flags |= IMAGE_OBJ_INFO;
412   byte *info = obj_find_aval(imo->obj, 'G');
413   if (!info)
414     {
415       DBG("Attribute G not found");
416       return 0;
417     }
418   uns colors;
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);
421   ASSERT(cnt == 7);
422   if (*thumb_format == 'j')
423     imo->flags |= IMAGE_OBJ_THUMB_JPEG;
424   return 1;
425 }
426
427 static int
428 extract_thumb_data(struct image_obj *imo)
429 {
430   DBG("Extracting thumbnail data");
431   imo->flags |= IMAGE_OBJ_THUMB_DATA;
432   struct oattr *attr = obj_find_attr(imo->obj, 'N');
433   if (!attr)
434     {
435       DBG("There is no thumbnail attribute N");
436       return 0;
437     }
438   uns count = 0;
439   for (struct oattr *a = attr; a; a = a->same)
440     count++;
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; )
444       *b++ = *s++;
445   ASSERT(b != b224);
446   uns size = b - b224;
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);
450   return 1;
451 }
452
453 static int
454 extract_thumb_image(struct image_obj *imo)
455 {
456   DBG("Decompressing thumbnail image");
457   imo->flags |= IMAGE_OBJ_THUMB_IMAGE;
458
459   /* JPEG format */
460   if (imo->flags & IMAGE_OBJ_THUMB_JPEG)
461     {
462 #if defined(USE_LIBJPEG)
463       return libjpeg_decompress_thumbnail(imo);
464 #elif defined(USE_MAGICK)
465       return magick_decompress_thumbnail(imo);
466 #else
467       DBG("JPEG not supported");
468       return 0;
469 #endif      
470     }
471   /* PNG format */
472   else
473     {
474 #if defined(USE_LIBPNG)
475       return libpng_decompress_thumbnail(imo);
476 #elif defined(USE_MAGICK)
477       return magick_decompress_thumbnail(imo);
478 #else
479       DBG("PNG not supported");
480       return 0;
481 #endif      
482     }
483 }
484
485 void
486 imo_decompress_thumbnails_init(void)
487 {
488 #ifdef USE_LIBPNG
489   libpng_decompress_thumbnails_init();
490 #endif
491 #ifdef USE_LIBJPEG
492   libjpeg_decompress_thumbnails_init();
493 #endif
494 #ifdef USE_MAGICK
495   magick_decompress_thumbnails_init();
496 #endif
497 }
498
499 void
500 imo_decompress_thumbnails_done(void)
501 {
502 #ifdef USE_MAGICK
503   magick_decompress_thumbnails_done();
504 #endif
505 #ifdef USE_LIBJPEG
506   libjpeg_decompress_thumbnails_done();
507 #endif
508 #ifdef USE_LIBPNG
509   libpng_decompress_thumbnails_done();
510 #endif
511 }
512
513 int
514 imo_decompress_thumbnail(struct image_obj *imo)
515 {
516   return
517     extract_image_info(imo) &&
518     extract_thumb_data(imo) &&
519     extract_thumb_image(imo);
520 }
521