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