]> mj.ucw.cz Git - libucw.git/blob - images/image-obj.c
Merge with git+ssh://cvs.ucw.cz/projects/sherlock/GIT/sherlock.git
[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  *      - 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
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         png_set_strip_alpha(png_ptr);
149         break;
150       case PNG_COLOR_TYPE_GRAY:
151         imo->thumb.flags |= IMAGE_GRAYSCALE;
152         png_set_gray_to_rgb(png_ptr);
153         break;
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);
158         break;
159       case PNG_COLOR_TYPE_RGB:
160         break;
161       case PNG_COLOR_TYPE_RGB_ALPHA:
162         png_set_strip_alpha(png_ptr);
163         break;
164       default:
165         ASSERT(0);
166     }
167   png_read_update_info(png_ptr, info_ptr);
168   ASSERT(png_get_channels(png_ptr, info_ptr) == 3);
169
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);
178  
179   /* Destroy libpng read structure */
180   png_destroy_read_struct(&png_ptr, &info_ptr, &end_ptr);
181   return 1;
182 }
183
184 #endif /* USE_LIBPNG */
185
186
187
188 /*******************************  LIBJPEG Library *************************************/
189
190 #ifdef USE_LIBJPEG
191
192 #include <jpeglib.h>
193 #include <setjmp.h>
194
195 struct libjpeg_err {
196   struct jpeg_error_mgr pub;
197   jmp_buf setjmp_buf;
198 };
199
200 static void NONRET
201 libjpeg_error_exit(j_common_ptr cinfo)
202 {
203   DBG("libjpeg_error_exit()");
204   longjmp(((struct libjpeg_err *)(cinfo)->err)->setjmp_buf, 1);
205 }
206
207 static void
208 libjpeg_emit_message(j_common_ptr cinfo UNUSED, int msg_level UNUSED)
209 {
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); */
213 }
214
215 static void
216 libjpeg_init_source(j_decompress_ptr cinfo UNUSED)
217 {
218   DBG("libjpeg_init_source()");
219 }
220
221 static boolean NONRET
222 libjpeg_fill_input_buffer(j_decompress_ptr cinfo)
223
224   DBG("libjpeg_fill_input_buffer()");
225   longjmp(((struct libjpeg_err *)(cinfo)->err)->setjmp_buf, 1);
226 }
227
228 static void
229 libjpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes) 
230 {
231   DBG("libjpeg_skip_input_data(): len=%d", (int)num_bytes);
232   if (num_bytes > 0)
233     {
234       cinfo->src->next_input_byte += num_bytes;
235       cinfo->src->bytes_in_buffer -= num_bytes;
236     }
237 }
238
239 static inline void
240 libjpeg_decompress_thumbnails_init(void)
241 {
242 }
243
244 static inline void
245 libjpeg_decompress_thumbnails_done(void)
246 {
247 }
248
249 static int
250 libjpeg_decompress_thumbnail(struct image_obj *imo)
251 {
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))
260     {
261       DBG("Libjpeg failed to read the image, longjump saved us");
262       jpeg_destroy_decompress(&cinfo);
263       return 0;
264     }
265   jpeg_create_decompress(&cinfo);
266
267   /* Initialize source manager */
268   struct jpeg_source_mgr src;
269   cinfo.src = &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;
277
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;
284   else
285     cinfo.out_color_space = JCS_RGB;
286
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)
297         {
298           jpeg_read_scanlines(&cinfo, (JSAMPLE **)&pixels, 1);
299           pixels += size;
300         }
301     }
302   else
303     { /* Read grayscale pixels */
304       JSAMPLE buf[cinfo.output_width], *buf_end = buf + cinfo.output_width;
305       while (cinfo.output_scanline < cinfo.output_height)
306         {
307           JSAMPLE *p = buf;
308           jpeg_read_scanlines(&cinfo, &p, 1);
309           for (; p != buf_end; p++)
310             {
311               pixels[0] = pixels[1] = pixels[2] = p[0];
312               pixels += 3;
313             }
314         }
315     }
316   jpeg_finish_decompress(&cinfo);
317
318   /* Destroy libjpeg object and leave */
319   jpeg_destroy_decompress(&cinfo);
320   return 1;
321 }
322
323 #endif /* USE_LIBJPEG */
324
325
326
327 /******************************  GraphicsMagick Library ******************************/
328
329 #ifdef USE_MAGICK
330
331 #include <magick/api.h>
332
333 static ExceptionInfo magick_exception;
334 static QuantizeInfo magick_quantize;
335 static ImageInfo *magick_info;
336
337 static void
338 magick_decompress_thumbnails_init(void)
339 {
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;
347 }
348
349 static void
350 magick_decompress_thumbnails_done(void)
351 {
352   DBG("Finalizing magick thumbnails decompression");
353   DestroyImageInfo(magick_info);
354   DestroyExceptionInfo(&magick_exception);
355   DestroyMagick();
356 }
357
358 static int
359 magick_decompress_thumbnail(struct image_obj *imo)
360 {
361   DBG("Reading image data");
362   Image *image = BlobToImage(magick_info, imo->thumb_data, imo->thumb_size, &magick_exception);
363   if (unlikely(!image))
364     return 0;
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);
370   ASSERT(pixels);
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++)
374     {
375       p[0] = pixels->red >> (QuantumDepth - 8);
376       p[1] = pixels->green >> (QuantumDepth - 8);
377       p[2] = pixels->blue >> (QuantumDepth - 8);
378       p += 3;
379       pixels++;
380     }
381   DestroyImage(image);
382   return 1;
383 }
384
385 #endif /* USE_MAGICK */
386
387
388
389 /*************************************************************************************/
390
391 static int
392 extract_image_info(struct image_obj *imo)
393 {
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');
398   if (!info)
399     {
400       DBG("Attribute G not found");
401       return 0;
402     }
403   uns colors;
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);
406   ASSERT(cnt == 7);
407   switch (*thumb_format)
408     {
409       case 'j':
410         imo->thumb_format = IMAGE_OBJ_FORMAT_JPEG;
411         break;
412       case 'p':
413         imo->thumb_format = IMAGE_OBJ_FORMAT_PNG;
414         break;
415       default:
416         ASSERT(0);
417     }
418   return 1;
419 }
420
421 static int
422 extract_thumb_data(struct image_obj *imo)
423 {
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');
429   if (!attr)
430     {
431       DBG("There is no thumbnail attribute N");
432       return 0;
433     }
434   uns count = 0;
435   for (struct oattr *a = attr; a; a = a->same)
436     count++;
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; )
440       *b++ = *s++;
441   ASSERT(b != b224);
442   uns size = b - b224;
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);
446   return 1;
447 }
448
449 static int
450 extract_thumb_image(struct image_obj *imo)
451 {
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)
458     {
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);
464 #else
465         DBG("JPEG not supported");
466         return 0;
467 #endif
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);
473 #else
474         DBG("PNG not supported");
475         return 0;
476 #endif      
477       default:
478         ASSERT(0);
479     }
480 }
481
482 void
483 imo_decompress_thumbnails_init(void)
484 {
485 #ifdef USE_LIBPNG
486   libpng_decompress_thumbnails_init();
487 #endif
488 #ifdef USE_LIBJPEG
489   libjpeg_decompress_thumbnails_init();
490 #endif
491 #ifdef USE_MAGICK
492   magick_decompress_thumbnails_init();
493 #endif
494 }
495
496 void
497 imo_decompress_thumbnails_done(void)
498 {
499 #ifdef USE_MAGICK
500   magick_decompress_thumbnails_done();
501 #endif
502 #ifdef USE_LIBJPEG
503   libjpeg_decompress_thumbnails_done();
504 #endif
505 #ifdef USE_LIBPNG
506   libpng_decompress_thumbnails_done();
507 #endif
508 }
509
510 int
511 imo_decompress_thumbnail(struct image_obj *imo)
512 {
513   return
514     extract_image_info(imo) &&
515     extract_thumb_data(imo) &&
516     extract_thumb_image(imo);
517 }
518