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