]> mj.ucw.cz Git - libucw.git/blob - images/io-libjpeg.c
Merge with git+ssh://cvs.ucw.cz/projects/sherlock/GIT/sherlock.git
[libucw.git] / images / io-libjpeg.c
1 /*
2  *      Image Library -- libjpeg
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
10 #undef LOCAL_DEBUG
11
12 #include "lib/lib.h"
13 #include "lib/mempool.h"
14 #include "lib/fastbuf.h"
15 #include "images/images.h"
16
17 #include <stdio.h>
18 #include <sys/types.h>
19 #include <jpeglib.h>
20 #include <setjmp.h>
21
22 struct libjpeg_err {
23   struct jpeg_error_mgr pub;
24   jmp_buf setjmp_buf;
25   struct image_io *io;
26 };
27
28 struct libjpeg_read_internals {
29   struct jpeg_decompress_struct cinfo;
30   struct jpeg_source_mgr src;
31   struct libjpeg_err err;
32   struct fastbuf *fastbuf;
33   byte *fastbuf_pos;
34 };
35
36 struct libjpeg_write_internals {
37   struct jpeg_compress_struct cinfo;
38   struct jpeg_destination_mgr dest;
39   struct libjpeg_err err;
40   struct fastbuf *fastbuf;
41   byte *fastbuf_pos;
42 };
43
44 static void NONRET
45 libjpeg_read_error_exit(j_common_ptr cinfo)
46 {
47   DBG("libjpeg_error_exit()");
48   struct libjpeg_err *e = (struct libjpeg_err *)cinfo->err;
49   byte buf[JMSG_LENGTH_MAX];
50   e->pub.format_message(cinfo, buf);
51   image_thread_err_dup(e->io->thread, IMAGE_ERR_READ_FAILED, buf);
52   longjmp(e->setjmp_buf, 1);
53 }
54
55 static void NONRET
56 libjpeg_write_error_exit(j_common_ptr cinfo)
57 {
58   DBG("libjpeg_error_exit()");
59   struct libjpeg_err *e = (struct libjpeg_err *)cinfo->err;
60   byte buf[JMSG_LENGTH_MAX];
61   e->pub.format_message(cinfo, buf);
62   image_thread_err_dup(e->io->thread, IMAGE_ERR_WRITE_FAILED,  buf);
63   longjmp(e->setjmp_buf, 1);
64 }
65
66 static void
67 libjpeg_emit_message(j_common_ptr cinfo UNUSED, int msg_level UNUSED)
68 {
69 #ifdef LOCAL_DEBUG  
70   byte buf[JMSG_LENGTH_MAX];
71   cinfo->err->format_message(cinfo, buf);
72   DBG("libjpeg_emit_message(): [%d] %s", msg_level, buf);
73 #endif  
74   if (unlikely(msg_level == -1))
75     longjmp(((struct libjpeg_err *)(cinfo)->err)->setjmp_buf, 1);
76 }
77
78 static inline uns
79 libjpeg_fastbuf_read_prepare(struct libjpeg_read_internals *i)
80 {
81   byte *start;
82   uns len = bdirect_read_prepare(i->fastbuf, &start);
83   i->fastbuf_pos = start + len;
84   i->src.next_input_byte = start;
85   i->src.bytes_in_buffer = len;
86   return len;
87 }
88
89 static inline void
90 libjpeg_fastbuf_read_commit(struct libjpeg_read_internals *i)
91 {
92   bdirect_read_commit(i->fastbuf, i->fastbuf_pos);
93 }
94
95 static void
96 libjpeg_init_source(j_decompress_ptr cinfo)
97 {
98   DBG("libjpeg_init_source()");
99   libjpeg_fastbuf_read_prepare((struct libjpeg_read_internals *)cinfo);
100 }
101
102 static void
103 libjpeg_term_source(j_decompress_ptr cinfo UNUSED)
104 {
105   DBG("libjpeg_term_source()");
106   //libjpeg_fastbuf_read_commit((struct libjpeg_read_internals *)cinfo);
107 }
108
109 static boolean
110 libjpeg_fill_input_buffer(j_decompress_ptr cinfo)
111 {
112   DBG("libjpeg_fill_input_buffer()");
113   struct libjpeg_read_internals *i = (struct libjpeg_read_internals *)cinfo;
114   libjpeg_fastbuf_read_commit(i);
115   return !!libjpeg_fastbuf_read_prepare(i);
116 }
117
118 static void
119 libjpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
120 {
121   DBG("libjpeg_skip_input_data(num_bytes=%d)", (int)num_bytes);
122   if (num_bytes > 0)
123     {
124       struct libjpeg_read_internals *i = (struct libjpeg_read_internals *)cinfo;
125       if ((unsigned long)num_bytes <= i->src.bytes_in_buffer)
126         {
127           i->src.next_input_byte += num_bytes;
128           i->src.bytes_in_buffer -= num_bytes;
129         }
130       else
131         {
132           num_bytes -= i->src.bytes_in_buffer;
133           libjpeg_fastbuf_read_commit(i);
134           bskip(i->fastbuf, num_bytes);
135           libjpeg_fastbuf_read_prepare(i);
136         }
137     }
138 }
139
140 static inline void
141 libjpeg_fastbuf_write_prepare(struct libjpeg_write_internals *i)
142 {
143   byte *start;
144   uns len = bdirect_write_prepare(i->fastbuf, &start);
145   i->fastbuf_pos = start + len;
146   i->dest.next_output_byte = start;
147   i->dest.free_in_buffer = len;
148   if (!len)
149     {
150       image_thread_err(i->err.io->thread, IMAGE_ERR_WRITE_FAILED, "Unexpected end of stream");
151       longjmp(i->err.setjmp_buf, 1);
152     }
153 }
154
155 static void
156 libjpeg_init_destination(j_compress_ptr cinfo)
157 {
158   DBG("libjpeg_init_destination()");
159   libjpeg_fastbuf_write_prepare((struct libjpeg_write_internals *)cinfo);
160 }
161
162 static void
163 libjpeg_term_destination(j_compress_ptr cinfo)
164 {
165   DBG("libjpeg_term_destination()");
166   struct libjpeg_write_internals *i = (struct libjpeg_write_internals *)cinfo;
167   bdirect_write_commit(i->fastbuf, (byte *)i->dest.next_output_byte);
168 }
169
170 static boolean
171 libjpeg_empty_output_buffer(j_compress_ptr cinfo)
172 {
173   DBG("libjpeg_empty_output_buffer()");
174   struct libjpeg_write_internals *i = (struct libjpeg_write_internals *)cinfo;
175   bdirect_write_commit(i->fastbuf, i->fastbuf_pos);
176   libjpeg_fastbuf_write_prepare(i);
177   return TRUE;
178 }
179
180 static void
181 libjpeg_read_cancel(struct image_io *io)
182 {
183   DBG("libjpeg_read_cancel()");
184   struct libjpeg_read_internals *i = io->read_data;
185   jpeg_destroy_decompress(&i->cinfo);
186 }
187
188 int
189 libjpeg_read_header(struct image_io *io)
190 {
191   DBG("libjpeg_read_header()");
192   struct libjpeg_read_internals *i = io->read_data = mp_alloc(io->internal_pool, sizeof(*i));
193   i->fastbuf = io->fastbuf;
194
195   /* Create libjpeg read structure */
196   DBG("Creating libjpeg read structure");
197   i->cinfo.err = jpeg_std_error(&i->err.pub);
198   i->err.pub.error_exit = libjpeg_read_error_exit;
199   i->err.pub.emit_message = libjpeg_emit_message;
200   i->err.io = io;
201   if (setjmp(i->err.setjmp_buf))
202     {
203       DBG("Libjpeg failed to read the image, longjump saved us");
204       jpeg_destroy_decompress(&i->cinfo);
205       return 0;
206     }
207   jpeg_create_decompress(&i->cinfo);
208
209   /* Initialize source manager */
210   i->cinfo.src = &i->src;
211   i->src.init_source = libjpeg_init_source;
212   i->src.fill_input_buffer = libjpeg_fill_input_buffer;
213   i->src.skip_input_data = libjpeg_skip_input_data;
214   i->src.resync_to_restart = jpeg_resync_to_restart;
215   i->src.term_source = libjpeg_term_source;
216
217   /* Read JPEG header and setup decompression options */
218   DBG("Reading image header");
219   jpeg_read_header(&i->cinfo, TRUE);
220   switch (i->cinfo.jpeg_color_space)
221     {
222       case JCS_GRAYSCALE:
223         io->flags = COLOR_SPACE_GRAYSCALE;
224         io->number_of_colors = 1 << 8;
225         break;
226       default:
227         io->flags = COLOR_SPACE_RGB;
228         io->number_of_colors = 1 << 24;
229         break;
230     }
231   io->cols = i->cinfo.image_width;
232   io->rows = i->cinfo.image_height;
233
234   io->read_cancel = libjpeg_read_cancel;
235   return 1;
236 }
237
238 int
239 libjpeg_read_data(struct image_io *io)
240 {
241   DBG("libjpeg_read_data()");
242
243   struct libjpeg_read_internals *i = io->read_data;
244  
245   /* Select color space */ 
246   switch (io->flags & IMAGE_COLOR_SPACE)
247     {
248       case COLOR_SPACE_GRAYSCALE:
249         i->cinfo.out_color_space = JCS_GRAYSCALE;
250         break;
251       case COLOR_SPACE_RGB:
252         i->cinfo.out_color_space = JCS_RGB;
253         break;
254       default:
255         jpeg_destroy_decompress(&i->cinfo);
256         image_thread_err(io->thread, IMAGE_ERR_INVALID_PIXEL_FORMAT, "Unsupported color space.");
257         return 0;
258     }
259
260   /* Allocate the image... FIXME: use libjpeg feature to speed up downscale */
261   volatile int need_scale = io->cols != i->cinfo.image_width || io->rows != i->cinfo.image_height;
262   struct image * volatile img = need_scale ?
263     image_new(io->thread, i->cinfo.image_width, i->cinfo.image_height, io->flags & IMAGE_PIXEL_FORMAT, NULL) :
264     image_new(io->thread, i->cinfo.image_width, i->cinfo.image_height, io->flags, io->pool);
265   if (!img)
266     {
267       image_thread_err(io->thread, IMAGE_ERR_INVALID_PIXEL_FORMAT, "Unsupported color space.");
268       return 0;
269     }
270   
271   /* Setup fallback */
272   if (setjmp(i->err.setjmp_buf))
273     {
274       DBG("Libjpeg failed to read the image, longjump saved us");
275       jpeg_destroy_decompress(&i->cinfo);
276       if (need_scale || !io->pool)
277         image_destroy(img);
278       return 0;
279     }
280
281   /* Decompress the image */
282   jpeg_start_decompress(&i->cinfo);
283   switch (img->pixel_size)
284     {
285       /* grayscale or RGB */
286       case 1:
287       case 3:
288         {
289           byte *pixels = img->pixels;
290           for (uns r = img->rows; r--; )
291             {
292               jpeg_read_scanlines(&i->cinfo, (JSAMPLE **)&pixels, 1);
293               pixels += img->row_size;
294             }
295         }
296         break;
297       /* garscale with alpha */
298       case 2:
299         {
300           byte buf[img->cols], *src;
301 #         define IMAGE_WALK_INLINE
302 #         define IMAGE_WALK_UNROLL 4
303 #         define IMAGE_WALK_COL_STEP 2
304 #         define IMAGE_WALK_DO_ROW_START do{ src = buf; jpeg_read_scanlines(&i->cinfo, (JSAMPLE **)&src, 1); }while(0)
305 #         define IMAGE_WALK_DO_STEP do{ pos[0] = *src++; pos[1] = 255; }while(0)
306 #         include "images/image-walk.h"
307         }
308         break;
309       /* RGBA or aligned RGB */
310       case 4:
311         {
312           byte buf[img->cols * 3], *src;
313 #         define IMAGE_WALK_INLINE
314 #         define IMAGE_WALK_UNROLL 4
315 #         define IMAGE_WALK_COL_STEP 4
316 #         define IMAGE_WALK_DO_ROW_START do{ src = buf; jpeg_read_scanlines(&i->cinfo, (JSAMPLE **)&src, 1); }while(0)
317 #         define IMAGE_WALK_DO_STEP do{ *(u32 *)pos = *(u32 *)src; pos[3] = 255; src += 3; }while(0)
318 #         include "images/image-walk.h"
319         }
320         break;
321       default:
322         ASSERT(0);
323     }
324   ASSERT(i->cinfo.output_scanline == i->cinfo.output_height);
325   
326   /* Destroy libjpeg object */
327   jpeg_finish_decompress(&i->cinfo);
328   jpeg_destroy_decompress(&i->cinfo);
329
330   /* Scale result if necessary */
331   if (need_scale)
332     {
333       DBG("Scaling image");
334       struct image *dest = image_new(io->thread, io->cols, io->rows, io->flags, io->pool);
335       if (!dest)
336         {
337           image_destroy(img);
338           return 0;
339         }
340       if (!(image_scale(io->thread, dest, img)))
341         {
342           image_destroy(img);
343           if (!io->pool)
344             image_destroy(dest);
345           return 0;
346         }
347       image_destroy(img);
348       io->image = dest;
349     }
350   else
351     io->image = img;
352   io->image_destroy = !io->pool;
353
354   DBG("Image readed");
355   return 1;
356 }
357
358 int
359 libjpeg_write(struct image_io *io)
360 {
361   DBG("libjpeg_write()");
362   struct libjpeg_write_internals i;
363   i.fastbuf = io->fastbuf;
364
365   /* Create libjpeg write structure */
366   DBG("Creating libjpeg write structure");
367   i.cinfo.err = jpeg_std_error(&i.err.pub);
368   i.err.pub.error_exit = libjpeg_write_error_exit;
369   i.err.pub.emit_message = libjpeg_emit_message;
370   i.err.io = io;
371   if (setjmp(i.err.setjmp_buf))
372     {
373       DBG("Libjpeg failed to write the image, longjump saved us");
374       jpeg_destroy_compress(&i.cinfo);
375       return 0;
376     }
377   jpeg_create_compress(&i.cinfo);
378   
379   /* Initialize destination manager */
380   i.cinfo.dest = &i.dest;
381   i.dest.init_destination = libjpeg_init_destination;
382   i.dest.term_destination = libjpeg_term_destination;
383   i.dest.empty_output_buffer = libjpeg_empty_output_buffer;
384
385   /* Set output parameters */
386   struct image *img = io->image;
387   i.cinfo.image_width = img->cols;
388   i.cinfo.image_height = img->rows;
389   switch (img->flags & IMAGE_COLOR_SPACE)
390     {
391       case COLOR_SPACE_GRAYSCALE:
392         i.cinfo.input_components = 1;
393         i.cinfo.in_color_space = JCS_GRAYSCALE;
394         break;
395       case COLOR_SPACE_RGB:
396         i.cinfo.input_components = 3;
397         i.cinfo.in_color_space = JCS_RGB;
398         break;
399       default:
400         jpeg_destroy_compress(&i.cinfo);
401         image_thread_err(io->thread, IMAGE_ERR_INVALID_PIXEL_FORMAT, "Unsupported pixel format.");
402         return 0;
403     }
404   jpeg_set_defaults(&i.cinfo);
405   if (io->jpeg_quality)
406     jpeg_set_quality(&i.cinfo, MIN(io->jpeg_quality, 100), 1);
407
408   /* Compress the image */
409   jpeg_start_compress(&i.cinfo, TRUE);
410   switch (img->pixel_size)
411     {
412       /* grayscale or RGB */
413       case 1:
414       case 3:
415         {
416           byte *pixels = img->pixels;
417           for (uns r = img->rows; r--; )
418             {
419               jpeg_write_scanlines(&i.cinfo, (JSAMPLE **)&pixels, 1);
420               pixels += img->row_size;
421             }
422         }
423         break;
424       /* grayscale with alpha (ignore alpha) */
425       case 2:
426         {
427           byte buf[img->cols], *dest = buf;
428 #         define IMAGE_WALK_INLINE
429 #         define IMAGE_WALK_UNROLL 4
430 #         define IMAGE_WALK_COL_STEP 2
431 #         define IMAGE_WALK_DO_ROW_END do{ dest = buf; jpeg_write_scanlines(&i.cinfo, (JSAMPLE **)&dest, 1); }while(0)
432 #         define IMAGE_WALK_DO_STEP do{ *dest++ = pos[0]; }while(0)
433 #         include "images/image-walk.h"
434         }
435         break;
436       /* RGBA (ignore alpha) or aligned RGB */
437       case 4:
438         {
439           byte buf[img->cols * 3], *dest = buf;
440 #         define IMAGE_WALK_INLINE
441 #         define IMAGE_WALK_UNROLL 4
442 #         define IMAGE_WALK_COL_STEP 4
443 #         define IMAGE_WALK_DO_ROW_END do{ dest = buf; jpeg_write_scanlines(&i.cinfo, (JSAMPLE **)&dest, 1); }while(0)
444 #         define IMAGE_WALK_DO_STEP do{ *dest++ = pos[0]; *dest++ = pos[1]; *dest++ = pos[2]; }while(0)
445 #         include "images/image-walk.h"
446         }
447         break;
448       default:
449         ASSERT(0);
450     }
451   ASSERT(i.cinfo.next_scanline == i.cinfo.image_height);
452   jpeg_finish_compress(&i.cinfo);
453   jpeg_destroy_compress(&i.cinfo);
454   return 1;
455 }