2 * Image Library -- Thumbnails
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 * - 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
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"
30 struct hook_error_mgr {
31 struct jpeg_error_mgr pub;
35 #define LONGJMP(cinfo) do{ longjmp(((struct hook_error_mgr *)(cinfo)->err)->setjmp_buf, 1); }while(0)
38 hook_error_exit(j_common_ptr cinfo)
44 hook_init_source(j_decompress_ptr cinfo UNUSED)
49 hook_fill_input_buffer(j_decompress_ptr cinfo)
55 hook_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
59 cinfo->src->next_input_byte += (size_t) num_bytes;
60 cinfo->src->bytes_in_buffer -= (size_t) num_bytes;
65 decompress_thumbnail(struct odes *obj, struct mempool *pool, struct image *image)
67 DBG("Decompressing thumbnail from URL %s", obj_find_aval(obj_find_attr(obj, 'U' + OBJ_ATTR_SON)->son, 'U'));
69 /* Find the attribute */
70 struct oattr *attr = obj_find_attr(obj, 'N');
73 DBG("There is no thumbnail");
77 /* Merge all instances of the attribute to continuous buffer */
79 for (struct oattr *a = attr; a; a = a->same)
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; )
87 /* Decode base-224 data */
89 uns len = base224_decode(buf, b224, b - b224);
90 DBG("Data length is %d", len);
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))
100 DBG("Error during the decompression, longjump saved us");
101 jpeg_destroy_decompress(&cinfo);
104 jpeg_create_decompress(&cinfo);
106 /* Initialize source manager */
107 struct jpeg_source_mgr 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;
117 /* Read JPEG header and setup decompression options */
118 DBG("Reading image header");
119 jpeg_read_header(&cinfo, TRUE);
121 if (cinfo.out_color_space == JCS_GRAYSCALE)
122 image->flags |= IMAGE_GRAYSCALE;
124 cinfo.out_color_space = JCS_RGB;
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;
137 byte *pixels = image->pixels = mp_alloc(pool, size);
138 while (cinfo.output_scanline < cinfo.output_height)
140 jpeg_read_scanlines(&cinfo, (JSAMPLE **)&pixels, 1);
143 jpeg_finish_decompress(&cinfo);
145 /* Destroy libjpeg object and leave */
146 jpeg_destroy_decompress(&cinfo);
147 DBG("Thumbnail decompressed successfully");