]> mj.ucw.cz Git - libucw.git/blob - images/image-thumb.c
First try of libjpeg... much faster than graphicsmagick
[libucw.git] / images / image-thumb.c
1 /*
2  *      Image Library -- Thumbnails
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  *      - support for PNG (libpng seems to be fast)
11  *      - improve thumbnail creation in gatherer... faster compression,
12  *        only grayscale/RGB colorspaces and maybe fixed headers (abbreviated datastreams in libjpeg)
13  *      - hook memory allocation managers, get rid of multiple initializations
14  *      - optimize decompression parameters
15  */
16
17 #undef LOCAL_DEBUG
18
19 #include "sherlock/sherlock.h"
20 #include "lib/base224.h"
21 #include "lib/mempool.h"
22 #include "sherlock/object.h"
23 #include "images/images.h"
24 #include "images/image-thumb.h"
25
26 #include <stdio.h>
27 #include <jpeglib.h>
28 #include <setjmp.h>
29
30 struct hook_error_mgr {
31   struct jpeg_error_mgr pub;
32   jmp_buf setjmp_buf;
33 };
34
35 #define LONGJMP(cinfo) do{ longjmp(((struct hook_error_mgr *)(cinfo)->err)->setjmp_buf, 1); }while(0)
36
37 static void NONRET
38 hook_error_exit(j_common_ptr cinfo)
39
40   LONGJMP(cinfo); 
41 }
42
43 static void
44 hook_init_source(j_decompress_ptr cinfo UNUSED)
45 {
46 }
47
48 static boolean NONRET
49 hook_fill_input_buffer(j_decompress_ptr cinfo)
50
51   LONGJMP(cinfo);
52 }
53
54 static void
55 hook_skip_input_data(j_decompress_ptr cinfo, long num_bytes) 
56 {
57   if (num_bytes > 0)
58     {
59       cinfo->src->next_input_byte += (size_t) num_bytes;
60       cinfo->src->bytes_in_buffer -= (size_t) num_bytes;
61     }
62 }
63
64 int
65 decompress_thumbnail(struct odes *obj, struct mempool *pool, struct image *image)
66 {
67   DBG("Decompressing thumbnail from URL %s", obj_find_aval(obj_find_attr(obj, 'U' + OBJ_ATTR_SON)->son, 'U'));
68   
69   /* Find the attribute */
70   struct oattr *attr = obj_find_attr(obj, 'N');
71   if (!attr)
72     {
73       DBG("There is no thumbnail");
74       return -1;
75     }
76
77   /* Merge all instances of the attribute to continuous buffer */
78   uns attr_count = 0;
79   for (struct oattr *a = attr; a; a = a->same)
80     attr_count++;
81   byte b224[attr_count * MAX_ATTR_SIZE], *b = b224;
82   for (struct oattr *a = attr; a; a = a->same)
83     for (byte *s = a->val; *s; )
84       *b++ = *s++;
85   ASSERT(b != b224);
86
87   /* Decode base-224 data */
88   byte buf[b - b224];
89   uns len = base224_decode(buf, b224, b - b224);
90   DBG("Data length is %d", len);
91
92   /* Initialize libjpeg decompression object */
93   DBG("Creating libjpeg decompression object");
94   struct jpeg_decompress_struct cinfo;
95   struct hook_error_mgr err;
96   cinfo.err = jpeg_std_error(&err.pub); // FIXME: we need to hide error messages
97   err.pub.error_exit = hook_error_exit;
98   if (setjmp(err.setjmp_buf))
99     {
100       DBG("Error during the decompression, longjump saved us");
101       jpeg_destroy_decompress(&cinfo);
102       return -2;
103     }
104   jpeg_create_decompress(&cinfo);
105
106   /* Initialize source manager */
107   struct jpeg_source_mgr src;
108   cinfo.src = &src;
109   src.next_input_byte = buf;
110   src.bytes_in_buffer = len;
111   src.init_source = hook_init_source;
112   src.fill_input_buffer = hook_fill_input_buffer;
113   src.skip_input_data = hook_skip_input_data;
114   src.resync_to_restart = jpeg_resync_to_restart;
115   src.term_source = hook_init_source;
116   
117   /* Read JPEG header and setup decompression options */
118   DBG("Reading image header");
119   jpeg_read_header(&cinfo, TRUE);
120   image->flags = 0;
121   if (cinfo.out_color_space == JCS_GRAYSCALE)
122     image->flags |= IMAGE_GRAYSCALE;
123   else
124     cinfo.out_color_space = JCS_RGB;
125
126   /* Decompress the image */
127   DBG("Reading image data");
128   jpeg_start_decompress(&cinfo);
129   ASSERT(sizeof(JSAMPLE) == 1 && (
130       (cinfo.out_color_space == JCS_GRAYSCALE && cinfo.output_components == 1) ||
131       (cinfo.out_color_space == JCS_RGB && cinfo.output_components == 3)));
132   image->width = cinfo.output_width;
133   image->height = cinfo.output_height;
134   uns scanline = cinfo.output_width * cinfo.output_components;
135   uns size = scanline * cinfo.output_height;
136   ASSERT(size);
137   byte *pixels = image->pixels = mp_alloc(pool, size);
138   while (cinfo.output_scanline < cinfo.output_height)
139     {
140       jpeg_read_scanlines(&cinfo, (JSAMPLE **)&pixels, 1);
141       pixels += scanline;
142     }
143   jpeg_finish_decompress(&cinfo);
144
145   /* Destroy libjpeg object and leave */
146   jpeg_destroy_decompress(&cinfo);
147   DBG("Thumbnail decompressed successfully");
148   return 0;
149 }