hide or rename any symbols that are likely to conflict with other
[imager.git] / jpeg.c
1 /*
2 =head1 NAME
3
4 jpeg.c - implement saving and loading JPEG images
5
6 =head1 SYNOPSIS
7
8   io_glue *ig;
9   if (!i_writejpeg_wiol(im, ig, quality)) {
10     .. error ..
11   }
12   im = i_readjpeg_wiol(ig, length, iptc_text, itlength);
13
14 =head1 DESCRIPTION
15
16 Reads and writes JPEG images
17
18 =over
19
20 =cut
21 */
22
23 #include <stdio.h>
24 #include <sys/stat.h>
25 #ifndef _MSC_VER
26 #include <unistd.h>
27 #endif
28 #include <setjmp.h>
29
30 #include "iolayer.h"
31 #include "image.h"
32 #include "jpeglib.h"
33 #include "jerror.h"
34 #include <errno.h>
35
36 #define JPEG_APP13       0xED    /* APP13 marker code */
37 #define JPGS 16384
38
39 static unsigned char fake_eoi[]={(JOCTET) 0xFF,(JOCTET) JPEG_EOI};
40
41 /* Bad design right here */
42
43 static int tlength=0;
44 static char **iptc_text=NULL;
45
46
47 /* Source and Destination managers */
48
49
50 typedef struct {
51   struct jpeg_source_mgr pub;   /* public fields */
52   io_glue *data;
53   JOCTET *buffer;               /* start of buffer */
54   int length;                   /* Do I need this? */
55   boolean start_of_file;        /* have we gotten any data yet? */
56 } wiol_source_mgr;
57
58 typedef struct {
59   struct jpeg_destination_mgr pub; /* public fields */
60   io_glue *data;
61   JOCTET *buffer;               /* start of buffer */
62   boolean start_of_file;        /* have we gotten any data yet? */
63 } wiol_destination_mgr;
64
65 typedef wiol_source_mgr *wiol_src_ptr;
66 typedef wiol_destination_mgr *wiol_dest_ptr;
67
68
69 /*
70  * Methods for io manager objects 
71  * 
72  * Methods for source managers: 
73  *
74  * init_source         (j_decompress_ptr cinfo);
75  * skip_input_data     (j_decompress_ptr cinfo, long num_bytes);
76  * fill_input_buffer   (j_decompress_ptr cinfo);
77  * term_source         (j_decompress_ptr cinfo);
78  */
79
80
81
82 static void
83 wiol_init_source (j_decompress_ptr cinfo) {
84   wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
85   
86   /* We reset the empty-input-file flag for each image, but we don't clear
87    * the input buffer.   This is correct behavior for reading a series of
88    * images from one source.
89    */
90   src->start_of_file = TRUE;
91 }
92
93
94
95 static boolean
96 wiol_fill_input_buffer(j_decompress_ptr cinfo) {
97   wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
98   ssize_t nbytes; /* We assume that reads are "small" */
99   
100   mm_log((1,"wiol_fill_input_buffer(cinfo 0x%p)\n"));
101   
102   nbytes = src->data->readcb(src->data, src->buffer, JPGS);
103   
104   if (nbytes <= 0) { /* Insert a fake EOI marker */
105     src->pub.next_input_byte = fake_eoi;
106     src->pub.bytes_in_buffer = 2;
107   } else {
108     src->pub.next_input_byte = src->buffer;
109     src->pub.bytes_in_buffer = nbytes;
110   }
111   src->start_of_file = FALSE;
112   return TRUE;
113 }
114
115
116 static void
117 wiol_skip_input_data (j_decompress_ptr cinfo, long num_bytes) {
118   wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
119   
120   /* Just a dumb implementation for now.  Could use fseek() except
121    * it doesn't work on pipes.  Not clear that being smart is worth
122    * any trouble anyway --- large skips are infrequent.
123    */
124   
125   if (num_bytes > 0) {
126     while (num_bytes > (long) src->pub.bytes_in_buffer) {
127       num_bytes -= (long) src->pub.bytes_in_buffer;
128       (void) wiol_fill_input_buffer(cinfo);
129       /* note we assume that fill_input_buffer will never return FALSE,
130        * so suspension need not be handled.
131        */
132     }
133     src->pub.next_input_byte += (size_t) num_bytes;
134     src->pub.bytes_in_buffer -= (size_t) num_bytes;
135   }
136 }
137
138 static void
139 wiol_term_source (j_decompress_ptr cinfo) {
140   /* no work necessary here */ 
141   wiol_src_ptr src;
142   if (cinfo->src != NULL) {
143     src = (wiol_src_ptr) cinfo->src;
144     myfree(src->buffer);
145   }
146 }
147
148
149 /* Source manager Constructor */
150
151 static void
152 jpeg_wiol_src(j_decompress_ptr cinfo, io_glue *ig, int length) {
153   wiol_src_ptr src;
154   
155   if (cinfo->src == NULL) {     /* first time for this JPEG object? */
156     cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small) 
157       ((j_common_ptr) cinfo, JPOOL_PERMANENT,sizeof(wiol_source_mgr));
158     src = (wiol_src_ptr) cinfo->src;
159   }
160
161   /* put the request method call in here later */
162   io_glue_commit_types(ig);
163   
164   src         = (wiol_src_ptr) cinfo->src;
165   src->data   = ig;
166   src->buffer = mymalloc( JPGS );
167   src->length = length;
168
169   src->pub.init_source       = wiol_init_source;
170   src->pub.fill_input_buffer = wiol_fill_input_buffer;
171   src->pub.skip_input_data   = wiol_skip_input_data;
172   src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
173   src->pub.term_source       = wiol_term_source;
174   src->pub.bytes_in_buffer   = 0; /* forces fill_input_buffer on first read */
175   src->pub.next_input_byte   = NULL; /* until buffer loaded */
176 }
177
178
179
180
181 /*
182  * Methods for destination managers:
183  *
184  * init_destination    (j_compress_ptr cinfo);
185  * empty_output_buffer (j_compress_ptr cinfo);
186  * term_destination    (j_compress_ptr cinfo);
187  *
188  */
189
190 static void
191 wiol_init_destination (j_compress_ptr cinfo) {
192   wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
193   
194   /* We reset the empty-input-file flag for each image, but we don't clear
195    * the input buffer.   This is correct behavior for reading a series of
196    * images from one source.
197    */
198   dest->start_of_file = TRUE;
199 }
200
201 static boolean
202 wiol_empty_output_buffer(j_compress_ptr cinfo) {
203   wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
204   ssize_t rc;
205   /*
206     Previously this code was checking free_in_buffer to see how much 
207     needed to be written.  This does not follow the documentation:
208
209                        "In typical applications, it should write out the
210         *entire* buffer (use the saved start address and buffer length;
211         ignore the current state of next_output_byte and free_in_buffer)."
212
213   ssize_t nbytes     = JPGS - dest->pub.free_in_buffer;
214   */
215
216   mm_log((1,"wiol_empty_output_buffer(cinfo 0x%p)\n"));
217   rc = dest->data->writecb(dest->data, dest->buffer, JPGS);
218
219   if (rc != JPGS) { /* XXX: Should raise some jpeg error */
220     myfree(dest->buffer);
221     mm_log((1, "wiol_empty_output_buffer: Error: nbytes = %d != rc = %d\n", JPGS, rc));
222     ERREXIT(cinfo, JERR_FILE_WRITE);
223   }
224   dest->pub.free_in_buffer = JPGS;
225   dest->pub.next_output_byte = dest->buffer;
226   return TRUE;
227 }
228
229 static void
230 wiol_term_destination (j_compress_ptr cinfo) {
231   wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
232   size_t nbytes = JPGS - dest->pub.free_in_buffer;
233   /* yes, this needs to flush the buffer */
234   /* needs error handling */
235
236   if (dest->data->writecb(dest->data, dest->buffer, nbytes) != nbytes) {
237     myfree(dest->buffer);
238     ERREXIT(cinfo, JERR_FILE_WRITE);
239   }
240   
241   if (dest != NULL) myfree(dest->buffer);
242 }
243
244
245 /* Destination manager Constructor */
246
247 static void
248 jpeg_wiol_dest(j_compress_ptr cinfo, io_glue *ig) {
249   wiol_dest_ptr dest;
250   
251   if (cinfo->dest == NULL) {    /* first time for this JPEG object? */
252     cinfo->dest = 
253       (struct jpeg_destination_mgr *)
254       (*cinfo->mem->alloc_small) 
255       ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(wiol_destination_mgr));
256   }
257   
258   dest = (wiol_dest_ptr) cinfo->dest;
259   dest->data                    = ig;
260   dest->buffer                  = mymalloc( JPGS );
261
262   dest->pub.init_destination    = wiol_init_destination;
263   dest->pub.empty_output_buffer = wiol_empty_output_buffer;
264   dest->pub.term_destination    = wiol_term_destination;
265   dest->pub.free_in_buffer      = JPGS;
266   dest->pub.next_output_byte    = dest->buffer;
267 }
268
269 LOCAL(unsigned int)
270 jpeg_getc (j_decompress_ptr cinfo)
271 /* Read next byte */
272 {
273   struct jpeg_source_mgr * datasrc = cinfo->src;
274
275   if (datasrc->bytes_in_buffer == 0) {
276     if (! (*datasrc->fill_input_buffer) (cinfo))
277       { fprintf(stderr,"Jpeglib: cant suspend.\n"); exit(3); }
278       /*      ERREXIT(cinfo, JERR_CANT_SUSPEND);*/
279   }
280   datasrc->bytes_in_buffer--;
281   return GETJOCTET(*datasrc->next_input_byte++);
282 }
283
284 METHODDEF(boolean)
285 APP13_handler (j_decompress_ptr cinfo) {
286   INT32 length;
287   unsigned int cnt=0;
288   
289   length = jpeg_getc(cinfo) << 8;
290   length += jpeg_getc(cinfo);
291   length -= 2;  /* discount the length word itself */
292   
293   tlength=length;
294
295   if ( ((*iptc_text)=mymalloc(length)) == NULL ) return FALSE;
296   while (--length >= 0) (*iptc_text)[cnt++] = jpeg_getc(cinfo); 
297  
298   return TRUE;
299 }
300
301 METHODDEF(void)
302 my_output_message (j_common_ptr cinfo) {
303   char buffer[JMSG_LENGTH_MAX];
304
305   /* Create the message */
306   (*cinfo->err->format_message) (cinfo, buffer);
307
308   i_push_error(0, buffer);
309
310   /* Send it to stderr, adding a newline */
311   mm_log((1, "%s\n", buffer));
312 }
313
314 struct my_error_mgr {
315   struct jpeg_error_mgr pub;    /* "public" fields */
316   jmp_buf setjmp_buffer;        /* for return to caller */
317 };
318
319 typedef struct my_error_mgr * my_error_ptr;
320
321 /* Here's the routine that will replace the standard error_exit method */
322
323 METHODDEF(void)
324 my_error_exit (j_common_ptr cinfo) {
325   /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
326   my_error_ptr myerr = (my_error_ptr) cinfo->err;
327   
328   /* Always display the message. */
329   /* We could postpone this until after returning, if we chose. */
330   (*cinfo->err->output_message) (cinfo);
331
332   /* Return control to the setjmp point */
333   longjmp(myerr->setjmp_buffer, 1);
334 }
335
336 /*
337 =item i_readjpeg_wiol(data, length, iptc_itext, itlength)
338
339 =cut
340 */
341 i_img*
342 i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) {
343   i_img *im;
344
345   struct jpeg_decompress_struct cinfo;
346   struct my_error_mgr jerr;
347   JSAMPARRAY buffer;            /* Output row buffer */
348   int row_stride;               /* physical row width in output buffer */
349
350   mm_log((1,"i_readjpeg_wiol(data 0x%p, length %d,iptc_itext 0x%p)\n", data, iptc_itext));
351
352   i_clear_error();
353
354   iptc_text = iptc_itext;
355   cinfo.err = jpeg_std_error(&jerr.pub);
356   jerr.pub.error_exit     = my_error_exit;
357   jerr.pub.output_message = my_output_message;
358
359   /* Set error handler */
360   if (setjmp(jerr.setjmp_buffer)) {
361     jpeg_destroy_decompress(&cinfo); 
362     *iptc_itext=NULL;
363     *itlength=0;
364     return NULL;
365   }
366   
367   jpeg_create_decompress(&cinfo);
368   jpeg_set_marker_processor(&cinfo, JPEG_APP13, APP13_handler);
369   jpeg_wiol_src(&cinfo, data, length);
370
371   (void) jpeg_read_header(&cinfo, TRUE);
372   (void) jpeg_start_decompress(&cinfo);
373   im=i_img_empty_ch(NULL,cinfo.output_width,cinfo.output_height,cinfo.output_components);
374   row_stride = cinfo.output_width * cinfo.output_components;
375   buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
376   while (cinfo.output_scanline < cinfo.output_height) {
377     (void) jpeg_read_scanlines(&cinfo, buffer, 1);
378     memcpy(im->idata+im->channels*im->xsize*(cinfo.output_scanline-1),buffer[0],row_stride);
379   }
380   (void) jpeg_finish_decompress(&cinfo);
381   jpeg_destroy_decompress(&cinfo);
382   *itlength=tlength;
383   mm_log((1,"i_readjpeg_wiol -> (0x%x)\n",im));
384   return im;
385 }
386
387 /*
388 =item i_writejpeg_wiol(im, ig, qfactor)
389
390 =cut
391 */
392
393 undef_int
394 i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) {
395   struct stat stbuf;
396   JSAMPLE *image_buffer;
397   int quality;
398
399   struct jpeg_compress_struct cinfo;
400   struct my_error_mgr jerr;
401
402   JSAMPROW row_pointer[1];      /* pointer to JSAMPLE row[s] */
403   int row_stride;               /* physical row width in image buffer */
404   unsigned char * data = NULL;
405
406   mm_log((1,"i_writejpeg(im %p, ig %p, qfactor %d)\n", im, ig, qfactor));
407   
408   i_clear_error();
409
410   if (!(im->channels==1 || im->channels==3)) { 
411     i_push_error(0, "only 1 or 3 channels images can be saved as JPEG");
412     return 0;
413   }
414   quality = qfactor;
415
416   cinfo.err = jpeg_std_error(&jerr.pub);
417   jerr.pub.error_exit = my_error_exit;
418   jerr.pub.output_message = my_output_message;
419   
420   jpeg_create_compress(&cinfo);
421
422   if (setjmp(jerr.setjmp_buffer)) {
423     jpeg_destroy_compress(&cinfo);
424     if (data)
425       myfree(data);
426     return 0;
427   }
428
429   io_glue_commit_types(ig);
430   jpeg_wiol_dest(&cinfo, ig);
431
432   cinfo.image_width  = im -> xsize;     /* image width and height, in pixels */
433   cinfo.image_height = im -> ysize;
434
435   if (im->channels==3) {
436     cinfo.input_components = 3;         /* # of color components per pixel */
437     cinfo.in_color_space = JCS_RGB;     /* colorspace of input image */
438   }
439
440   if (im->channels==1) {
441     cinfo.input_components = 1;         /* # of color components per pixel */
442     cinfo.in_color_space = JCS_GRAYSCALE;       /* colorspace of input image */
443   }
444
445   jpeg_set_defaults(&cinfo);
446   jpeg_set_quality(&cinfo, quality, TRUE);  /* limit to baseline-JPEG values */
447
448   jpeg_start_compress(&cinfo, TRUE);
449
450   row_stride = im->xsize * im->channels;        /* JSAMPLEs per row in image_buffer */
451
452   if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits) {
453     image_buffer=im->idata;
454
455     while (cinfo.next_scanline < cinfo.image_height) {
456       /* jpeg_write_scanlines expects an array of pointers to scanlines.
457        * Here the array is only one element long, but you could pass
458        * more than one scanline at a time if that's more convenient.
459        */
460       row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
461       (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
462     }
463   }
464   else {
465     data = mymalloc(im->xsize * im->channels);
466     if (data) {
467       while (cinfo.next_scanline < cinfo.image_height) {
468         /* jpeg_write_scanlines expects an array of pointers to scanlines.
469          * Here the array is only one element long, but you could pass
470          * more than one scanline at a time if that's more convenient.
471          */
472         i_gsamp(im, 0, im->xsize, cinfo.next_scanline, data, 
473                 NULL, im->channels);
474         row_pointer[0] = data;
475         (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
476       }
477     }
478     else {
479       jpeg_destroy_compress(&cinfo);
480       i_push_error(0, "out of memory");
481       return 0; /* out of memory? */
482     }
483   }
484
485   /* Step 6: Finish compression */
486
487   jpeg_finish_compress(&cinfo);
488
489   jpeg_destroy_compress(&cinfo);
490
491   ig->closecb(ig);
492
493   return(1);
494 }
495
496 /*
497 =back
498
499 =head1 AUTHOR
500
501 Arnar M. Hrafnkelsson, addi@umich.edu
502
503 =head1 SEE ALSO
504
505 Imager(3)
506
507 =cut
508 */