progressive JPEG support
[imager.git] / JPEG / imjpeg.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 "imjpeg.h"
24 #include "imext.h"
25 #include <stdio.h>
26 #include <sys/stat.h>
27 #ifndef _MSC_VER
28 #include <unistd.h>
29 #endif
30 #include <setjmp.h>
31
32 #include "jpeglib.h"
33 #include "jerror.h"
34 #include <errno.h>
35 #include <stdlib.h>
36
37 #define JPEG_APP13       0xED    /* APP13 marker code */
38 #define JPEG_APP1 (JPEG_APP0 + 1)
39 #define JPGS 16384
40
41 static unsigned char fake_eoi[]={(JOCTET) 0xFF,(JOCTET) JPEG_EOI};
42
43 /* Bad design right here */
44
45 static int tlength=0;
46 static char **iptc_text=NULL;
47
48
49 /* Source and Destination managers */
50
51
52 typedef struct {
53   struct jpeg_source_mgr pub;   /* public fields */
54   io_glue *data;
55   JOCTET *buffer;               /* start of buffer */
56   int length;                   /* Do I need this? */
57   boolean start_of_file;        /* have we gotten any data yet? */
58 } wiol_source_mgr;
59
60 typedef struct {
61   struct jpeg_destination_mgr pub; /* public fields */
62   io_glue *data;
63   JOCTET *buffer;               /* start of buffer */
64   boolean start_of_file;        /* have we gotten any data yet? */
65 } wiol_destination_mgr;
66
67 typedef wiol_source_mgr *wiol_src_ptr;
68 typedef wiol_destination_mgr *wiol_dest_ptr;
69
70
71 /*
72  * Methods for io manager objects 
73  * 
74  * Methods for source managers: 
75  *
76  * init_source         (j_decompress_ptr cinfo);
77  * skip_input_data     (j_decompress_ptr cinfo, long num_bytes);
78  * fill_input_buffer   (j_decompress_ptr cinfo);
79  * term_source         (j_decompress_ptr cinfo);
80  */
81
82
83
84 static void
85 wiol_init_source (j_decompress_ptr cinfo) {
86   wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
87   
88   /* We reset the empty-input-file flag for each image, but we don't clear
89    * the input buffer.   This is correct behavior for reading a series of
90    * images from one source.
91    */
92   src->start_of_file = TRUE;
93 }
94
95
96
97 static boolean
98 wiol_fill_input_buffer(j_decompress_ptr cinfo) {
99   wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
100   ssize_t nbytes; /* We assume that reads are "small" */
101   
102   mm_log((1,"wiol_fill_input_buffer(cinfo 0x%p)\n", cinfo));
103   
104   nbytes = src->data->readcb(src->data, src->buffer, JPGS);
105   
106   if (nbytes <= 0) { /* Insert a fake EOI marker */
107     src->pub.next_input_byte = fake_eoi;
108     src->pub.bytes_in_buffer = 2;
109   } else {
110     src->pub.next_input_byte = src->buffer;
111     src->pub.bytes_in_buffer = nbytes;
112   }
113   src->start_of_file = FALSE;
114   return TRUE;
115 }
116
117
118 static void
119 wiol_skip_input_data (j_decompress_ptr cinfo, long num_bytes) {
120   wiol_src_ptr src = (wiol_src_ptr) cinfo->src;
121   
122   /* Just a dumb implementation for now.  Could use fseek() except
123    * it doesn't work on pipes.  Not clear that being smart is worth
124    * any trouble anyway --- large skips are infrequent.
125    */
126   
127   if (num_bytes > 0) {
128     while (num_bytes > (long) src->pub.bytes_in_buffer) {
129       num_bytes -= (long) src->pub.bytes_in_buffer;
130       (void) wiol_fill_input_buffer(cinfo);
131       /* note we assume that fill_input_buffer will never return FALSE,
132        * so suspension need not be handled.
133        */
134     }
135     src->pub.next_input_byte += (size_t) num_bytes;
136     src->pub.bytes_in_buffer -= (size_t) num_bytes;
137   }
138 }
139
140 static void
141 wiol_term_source (j_decompress_ptr cinfo) {
142   /* no work necessary here */ 
143   wiol_src_ptr src;
144   if (cinfo->src != NULL) {
145     src = (wiol_src_ptr) cinfo->src;
146     myfree(src->buffer);
147   }
148 }
149
150
151 /* Source manager Constructor */
152
153 static void
154 jpeg_wiol_src(j_decompress_ptr cinfo, io_glue *ig, int length) {
155   wiol_src_ptr src;
156   
157   if (cinfo->src == NULL) {     /* first time for this JPEG object? */
158     cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small) 
159       ((j_common_ptr) cinfo, JPOOL_PERMANENT,sizeof(wiol_source_mgr));
160     src = (wiol_src_ptr) cinfo->src;
161   }
162
163   /* put the request method call in here later */
164   
165   src         = (wiol_src_ptr) cinfo->src;
166   src->data   = ig;
167   src->buffer = mymalloc( JPGS );
168   src->length = length;
169
170   src->pub.init_source       = wiol_init_source;
171   src->pub.fill_input_buffer = wiol_fill_input_buffer;
172   src->pub.skip_input_data   = wiol_skip_input_data;
173   src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
174   src->pub.term_source       = wiol_term_source;
175   src->pub.bytes_in_buffer   = 0; /* forces fill_input_buffer on first read */
176   src->pub.next_input_byte   = NULL; /* until buffer loaded */
177 }
178
179
180
181
182 /*
183  * Methods for destination managers:
184  *
185  * init_destination    (j_compress_ptr cinfo);
186  * empty_output_buffer (j_compress_ptr cinfo);
187  * term_destination    (j_compress_ptr cinfo);
188  *
189  */
190
191 static void
192 wiol_init_destination (j_compress_ptr cinfo) {
193   wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
194   
195   /* We reset the empty-input-file flag for each image, but we don't clear
196    * the input buffer.   This is correct behavior for reading a series of
197    * images from one source.
198    */
199   dest->start_of_file = TRUE;
200 }
201
202 static boolean
203 wiol_empty_output_buffer(j_compress_ptr cinfo) {
204   wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
205   ssize_t rc;
206   /*
207     Previously this code was checking free_in_buffer to see how much 
208     needed to be written.  This does not follow the documentation:
209
210                        "In typical applications, it should write out the
211         *entire* buffer (use the saved start address and buffer length;
212         ignore the current state of next_output_byte and free_in_buffer)."
213
214   ssize_t nbytes     = JPGS - dest->pub.free_in_buffer;
215   */
216
217   mm_log((1,"wiol_empty_output_buffer(cinfo 0x%p)\n", cinfo));
218   rc = dest->data->writecb(dest->data, dest->buffer, JPGS);
219
220   if (rc != JPGS) { /* XXX: Should raise some jpeg error */
221     myfree(dest->buffer);
222     mm_log((1, "wiol_empty_output_buffer: Error: nbytes = %d != rc = %d\n", JPGS, rc));
223     ERREXIT(cinfo, JERR_FILE_WRITE);
224   }
225   dest->pub.free_in_buffer = JPGS;
226   dest->pub.next_output_byte = dest->buffer;
227   return TRUE;
228 }
229
230 static void
231 wiol_term_destination (j_compress_ptr cinfo) {
232   wiol_dest_ptr dest = (wiol_dest_ptr) cinfo->dest;
233   size_t nbytes = JPGS - dest->pub.free_in_buffer;
234   /* yes, this needs to flush the buffer */
235   /* needs error handling */
236
237   if (dest->data->writecb(dest->data, dest->buffer, nbytes) != nbytes) {
238     myfree(dest->buffer);
239     ERREXIT(cinfo, JERR_FILE_WRITE);
240   }
241   
242   if (dest != NULL) myfree(dest->buffer);
243 }
244
245
246 /* Destination manager Constructor */
247
248 static void
249 jpeg_wiol_dest(j_compress_ptr cinfo, io_glue *ig) {
250   wiol_dest_ptr dest;
251   
252   if (cinfo->dest == NULL) {    /* first time for this JPEG object? */
253     cinfo->dest = 
254       (struct jpeg_destination_mgr *)
255       (*cinfo->mem->alloc_small) 
256       ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(wiol_destination_mgr));
257   }
258   
259   dest = (wiol_dest_ptr) cinfo->dest;
260   dest->data                    = ig;
261   dest->buffer                  = mymalloc( JPGS );
262
263   dest->pub.init_destination    = wiol_init_destination;
264   dest->pub.empty_output_buffer = wiol_empty_output_buffer;
265   dest->pub.term_destination    = wiol_term_destination;
266   dest->pub.free_in_buffer      = JPGS;
267   dest->pub.next_output_byte    = dest->buffer;
268 }
269
270 LOCAL(unsigned int)
271 jpeg_getc (j_decompress_ptr cinfo)
272 /* Read next byte */
273 {
274   struct jpeg_source_mgr * datasrc = cinfo->src;
275
276   if (datasrc->bytes_in_buffer == 0) {
277     if (! (*datasrc->fill_input_buffer) (cinfo))
278       { fprintf(stderr,"Jpeglib: cant suspend.\n"); exit(3); }
279       /*      ERREXIT(cinfo, JERR_CANT_SUSPEND);*/
280   }
281   datasrc->bytes_in_buffer--;
282   return GETJOCTET(*datasrc->next_input_byte++);
283 }
284
285 METHODDEF(boolean)
286 APP13_handler (j_decompress_ptr cinfo) {
287   INT32 length;
288   unsigned int cnt=0;
289   
290   length = jpeg_getc(cinfo) << 8;
291   length += jpeg_getc(cinfo);
292   length -= 2;  /* discount the length word itself */
293   
294   tlength=length;
295
296   if ( ((*iptc_text)=mymalloc(length)) == NULL ) return FALSE;
297   while (--length >= 0) (*iptc_text)[cnt++] = jpeg_getc(cinfo); 
298  
299   return TRUE;
300 }
301
302 METHODDEF(void)
303 my_output_message (j_common_ptr cinfo) {
304   char buffer[JMSG_LENGTH_MAX];
305
306   /* Create the message */
307   (*cinfo->err->format_message) (cinfo, buffer);
308
309   i_push_error(0, buffer);
310
311   /* Send it to stderr, adding a newline */
312   mm_log((1, "%s\n", buffer));
313 }
314
315 struct my_error_mgr {
316   struct jpeg_error_mgr pub;    /* "public" fields */
317   jmp_buf setjmp_buffer;        /* for return to caller */
318 };
319
320 typedef struct my_error_mgr * my_error_ptr;
321
322 /* Here's the routine that will replace the standard error_exit method */
323
324 METHODDEF(void)
325 my_error_exit (j_common_ptr cinfo) {
326   /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
327   my_error_ptr myerr = (my_error_ptr) cinfo->err;
328   
329   /* Always display the message. */
330   /* We could postpone this until after returning, if we chose. */
331   (*cinfo->err->output_message) (cinfo);
332
333   /* Return control to the setjmp point */
334   longjmp(myerr->setjmp_buffer, 1);
335 }
336
337 static void 
338 transfer_cmyk_inverted(i_color *out, JSAMPARRAY in, int width) {
339   JSAMPROW inrow = *in;
340   while (width--) {
341     /* extract and convert to real CMYK */
342     /* horribly enough this is correct given cmyk values are inverted */
343     int c = *inrow++;
344     int m = *inrow++;
345     int y = *inrow++;
346     int k = *inrow++;
347     out->rgba.r = (c * k) / MAXJSAMPLE;
348     out->rgba.g = (m * k) / MAXJSAMPLE;
349     out->rgba.b = (y * k) / MAXJSAMPLE;
350     ++out;
351   }
352 }
353
354 static void
355 transfer_rgb(i_color *out, JSAMPARRAY in, int width) {
356   JSAMPROW inrow = *in;
357   while (width--) {
358     out->rgba.r = *inrow++;
359     out->rgba.g = *inrow++;
360     out->rgba.b = *inrow++;
361     ++out;
362   }
363 }
364
365 static void
366 transfer_gray(i_color *out, JSAMPARRAY in, int width) {
367   JSAMPROW inrow = *in;
368   while (width--) {
369     out->gray.gray_color = *inrow++;
370     ++out;
371   }
372 }
373
374 typedef void (*transfer_function_t)(i_color *out, JSAMPARRAY in, int width);
375
376 /*
377 =item i_readjpeg_wiol(data, length, iptc_itext, itlength)
378
379 =cut
380 */
381 i_img*
382 i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) {
383   i_img * volatile im = NULL;
384   int seen_exif = 0;
385   i_color * volatile line_buffer = NULL;
386   struct jpeg_decompress_struct cinfo;
387   struct my_error_mgr jerr;
388   JSAMPARRAY buffer;            /* Output row buffer */
389   int row_stride;               /* physical row width in output buffer */
390   jpeg_saved_marker_ptr markerp;
391   transfer_function_t transfer_f;
392   int channels;
393   volatile int src_set = 0;
394
395   mm_log((1,"i_readjpeg_wiol(data 0x%p, length %d,iptc_itext 0x%p)\n", data, length, iptc_itext));
396
397   i_clear_error();
398
399   iptc_text = iptc_itext;
400   cinfo.err = jpeg_std_error(&jerr.pub);
401   jerr.pub.error_exit     = my_error_exit;
402   jerr.pub.output_message = my_output_message;
403
404   /* Set error handler */
405   if (setjmp(jerr.setjmp_buffer)) {
406     if (src_set)
407       wiol_term_source(&cinfo);
408     jpeg_destroy_decompress(&cinfo); 
409     *iptc_itext=NULL;
410     *itlength=0;
411     if (line_buffer)
412       myfree(line_buffer);
413     if (im)
414       i_img_destroy(im);
415     return NULL;
416   }
417   
418   jpeg_create_decompress(&cinfo);
419   jpeg_set_marker_processor(&cinfo, JPEG_APP13, APP13_handler);
420   jpeg_save_markers(&cinfo, JPEG_APP1, 0xFFFF);
421   jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
422   jpeg_wiol_src(&cinfo, data, length);
423   src_set = 1;
424
425   (void) jpeg_read_header(&cinfo, TRUE);
426   (void) jpeg_start_decompress(&cinfo);
427
428   channels = cinfo.output_components;
429   switch (cinfo.out_color_space) {
430   case JCS_GRAYSCALE:
431     if (cinfo.output_components != 1) {
432       mm_log((1, "i_readjpeg: grayscale image with %d channels\n", cinfo.output_components));
433       i_push_errorf(0, "grayscale image with invalid components %d", cinfo.output_components);
434       wiol_term_source(&cinfo);
435       jpeg_destroy_decompress(&cinfo);
436       return NULL;
437     }
438     transfer_f = transfer_gray;
439     break;
440   
441   case JCS_RGB:
442     transfer_f = transfer_rgb;
443     if (cinfo.output_components != 3) {
444       mm_log((1, "i_readjpeg: RGB image with %d channels\n", cinfo.output_components));
445       i_push_errorf(0, "RGB image with invalid components %d", cinfo.output_components);
446       wiol_term_source(&cinfo);
447       jpeg_destroy_decompress(&cinfo);
448       return NULL;
449     }
450     break;
451
452   case JCS_CMYK:
453     if (cinfo.output_components == 4) {
454       /* we treat the CMYK values as inverted, because that's what that
455          buggy photoshop does, and everyone has to follow the gorilla.
456
457          Is there any app that still produces correct CMYK JPEGs?
458       */
459       transfer_f = transfer_cmyk_inverted;
460       channels = 3;
461     }
462     else {
463       mm_log((1, "i_readjpeg: cmyk image with %d channels\n", cinfo.output_components));
464       i_push_errorf(0, "CMYK image with invalid components %d", cinfo.output_components);
465       wiol_term_source(&cinfo);
466       jpeg_destroy_decompress(&cinfo);
467       return NULL;
468     }
469     break;
470
471   default:
472     mm_log((1, "i_readjpeg: unknown color space %d\n", cinfo.out_color_space));
473     i_push_errorf(0, "Unknown color space %d", cinfo.out_color_space);
474     wiol_term_source(&cinfo);
475     jpeg_destroy_decompress(&cinfo);
476     return NULL;
477   }
478
479   if (!i_int_check_image_file_limits(cinfo.output_width, cinfo.output_height,
480                                      channels, sizeof(i_sample_t))) {
481     mm_log((1, "i_readjpeg: image size exceeds limits\n"));
482     wiol_term_source(&cinfo);
483     jpeg_destroy_decompress(&cinfo);
484     return NULL;
485   }
486
487   im = i_img_8_new(cinfo.output_width, cinfo.output_height, channels);
488   if (!im) {
489     wiol_term_source(&cinfo);
490     jpeg_destroy_decompress(&cinfo);
491     return NULL;
492   }
493   row_stride = cinfo.output_width * cinfo.output_components;
494   buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
495   line_buffer = mymalloc(sizeof(i_color) * cinfo.output_width);
496   while (cinfo.output_scanline < cinfo.output_height) {
497     (void) jpeg_read_scanlines(&cinfo, buffer, 1);
498     transfer_f(line_buffer, buffer, cinfo.output_width);
499     i_plin(im, 0, cinfo.output_width, cinfo.output_scanline-1, line_buffer);
500   }
501   myfree(line_buffer);
502   line_buffer = NULL;
503
504   /* check for APP1 marker and save */
505   markerp = cinfo.marker_list;
506   while (markerp != NULL) {
507     if (markerp->marker == JPEG_COM) {
508       i_tags_set(&im->tags, "jpeg_comment", (const char *)markerp->data,
509                  markerp->data_length);
510     }
511     else if (markerp->marker == JPEG_APP1 && !seen_exif) {
512       seen_exif = i_int_decode_exif(im, markerp->data, markerp->data_length);
513     }
514
515     markerp = markerp->next;
516   }
517
518   i_tags_setn(&im->tags, "jpeg_out_color_space", cinfo.out_color_space);
519   i_tags_setn(&im->tags, "jpeg_color_space", cinfo.jpeg_color_space);
520
521   if (cinfo.saw_JFIF_marker) {
522     double xres = cinfo.X_density;
523     double yres = cinfo.Y_density;
524     
525     i_tags_setn(&im->tags, "jpeg_density_unit", cinfo.density_unit);
526     switch (cinfo.density_unit) {
527     case 0: /* values are just the aspect ratio */
528       i_tags_setn(&im->tags, "i_aspect_only", 1);
529       i_tags_set(&im->tags, "jpeg_density_unit_name", "none", -1);
530       break;
531
532     case 1: /* per inch */
533       i_tags_set(&im->tags, "jpeg_density_unit_name", "inch", -1);
534       break;
535
536     case 2: /* per cm */
537       i_tags_set(&im->tags, "jpeg_density_unit_name", "centimeter", -1);
538       xres *= 2.54;
539       yres *= 2.54;
540       break;
541     }
542     i_tags_set_float2(&im->tags, "i_xres", 0, xres, 6);
543     i_tags_set_float2(&im->tags, "i_yres", 0, yres, 6);
544   }
545
546   /* I originally used jpeg_has_multiple_scans() here, but that can
547    * return true for non-progressive files too.  The progressive_mode
548    * member is available at least as far back as 6b and does the right
549    * thing.
550    */
551   i_tags_setn(&im->tags, "jpeg_progressive", 
552               cinfo.progressive_mode ? 1 : 0);
553
554   (void) jpeg_finish_decompress(&cinfo);
555   jpeg_destroy_decompress(&cinfo);
556   *itlength=tlength;
557
558   i_tags_set(&im->tags, "i_format", "jpeg", 4);
559
560   mm_log((1,"i_readjpeg_wiol -> (0x%x)\n",im));
561   return im;
562 }
563
564 /*
565 =item i_writejpeg_wiol(im, ig, qfactor)
566
567 =cut
568 */
569
570 undef_int
571 i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) {
572   JSAMPLE *image_buffer;
573   int quality;
574   int got_xres, got_yres, aspect_only, resunit;
575   double xres, yres;
576   int comment_entry;
577   int want_channels = im->channels;
578   int progressive = 0;
579
580   struct jpeg_compress_struct cinfo;
581   struct my_error_mgr jerr;
582
583   JSAMPROW row_pointer[1];      /* pointer to JSAMPLE row[s] */
584   int row_stride;               /* physical row width in image buffer */
585   unsigned char * data = NULL;
586   i_color *line_buf = NULL;
587
588   mm_log((1,"i_writejpeg(im %p, ig %p, qfactor %d)\n", im, ig, qfactor));
589   
590   i_clear_error();
591
592   if (!(im->channels==1 || im->channels==3)) { 
593     want_channels = im->channels - 1;
594   }
595   quality = qfactor;
596
597   cinfo.err = jpeg_std_error(&jerr.pub);
598   jerr.pub.error_exit = my_error_exit;
599   jerr.pub.output_message = my_output_message;
600   
601   jpeg_create_compress(&cinfo);
602
603   if (setjmp(jerr.setjmp_buffer)) {
604     jpeg_destroy_compress(&cinfo);
605     if (data)
606       myfree(data);
607     if (line_buf)
608       myfree(line_buf);
609     return 0;
610   }
611
612   jpeg_wiol_dest(&cinfo, ig);
613
614   cinfo.image_width  = im -> xsize;     /* image width and height, in pixels */
615   cinfo.image_height = im -> ysize;
616
617   if (want_channels==3) {
618     cinfo.input_components = 3;         /* # of color components per pixel */
619     cinfo.in_color_space = JCS_RGB;     /* colorspace of input image */
620   }
621
622   if (want_channels==1) {
623     cinfo.input_components = 1;         /* # of color components per pixel */
624     cinfo.in_color_space = JCS_GRAYSCALE;       /* colorspace of input image */
625   }
626
627   jpeg_set_defaults(&cinfo);
628   jpeg_set_quality(&cinfo, quality, TRUE);  /* limit to baseline-JPEG values */
629
630   if (!i_tags_get_int(&im->tags, "jpeg_progressive", 0, &progressive))
631     progressive = 0;
632   if (progressive) {
633     jpeg_simple_progression(&cinfo);
634   }
635
636   got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres);
637   got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres);
638   if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only))
639     aspect_only = 0;
640   if (!i_tags_get_int(&im->tags, "jpeg_density_unit", 0, &resunit))
641     resunit = 1; /* per inch */
642   if (resunit < 0 || resunit > 2) /* default to inch if invalid */
643     resunit = 1;
644   if (got_xres || got_yres) {
645     if (!got_xres)
646       xres = yres;
647     else if (!got_yres)
648       yres = xres;
649     if (aspect_only)
650       resunit = 0; /* standard tags override format tags */
651     if (resunit == 2) {
652       /* convert to per cm */
653       xres /= 2.54;
654       yres /= 2.54;
655     }
656     cinfo.density_unit = resunit;
657     cinfo.X_density = (int)(xres + 0.5);
658     cinfo.Y_density = (int)(yres + 0.5);
659   }
660
661   jpeg_start_compress(&cinfo, TRUE);
662
663   if (i_tags_find(&im->tags, "jpeg_comment", 0, &comment_entry)) {
664     jpeg_write_marker(&cinfo, JPEG_COM, 
665                       (const JOCTET *)im->tags.tags[comment_entry].data,
666                       im->tags.tags[comment_entry].size);
667   }
668
669   row_stride = im->xsize * im->channels;        /* JSAMPLEs per row in image_buffer */
670
671   if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits
672       && im->channels == want_channels) {
673     image_buffer=im->idata;
674
675     while (cinfo.next_scanline < cinfo.image_height) {
676       /* jpeg_write_scanlines expects an array of pointers to scanlines.
677        * Here the array is only one element long, but you could pass
678        * more than one scanline at a time if that's more convenient.
679        */
680       row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
681       (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
682     }
683   }
684   else {
685     i_color bg;
686
687     i_get_file_background(im, &bg);
688     data = mymalloc(im->xsize * im->channels);
689     if (data) {
690       while (cinfo.next_scanline < cinfo.image_height) {
691         /* jpeg_write_scanlines expects an array of pointers to scanlines.
692          * Here the array is only one element long, but you could pass
693          * more than one scanline at a time if that's more convenient.
694          */
695         i_gsamp_bg(im, 0, im->xsize, cinfo.next_scanline, data, 
696                    want_channels, &bg);
697         row_pointer[0] = data;
698         (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
699       }
700       myfree(data);
701     }
702     else {
703       jpeg_destroy_compress(&cinfo);
704       i_push_error(0, "out of memory");
705       return 0; /* out of memory? */
706     }
707   }
708
709   /* Step 6: Finish compression */
710
711   jpeg_finish_compress(&cinfo);
712
713   jpeg_destroy_compress(&cinfo);
714
715   ig->closecb(ig);
716
717   return(1);
718 }
719
720 /*
721 =back
722
723 =head1 AUTHOR
724
725 Arnar M. Hrafnkelsson, addi@umich.edu
726
727 =head1 SEE ALSO
728
729 Imager(3)
730
731 =cut
732 */