- writing a 2 or 4 channel image to a JPEG will now write that image as
[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 *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
508   /* check for APP1 marker and save */
509   markerp = cinfo.marker_list;
510   while (markerp != NULL) {
511     if (markerp->marker == JPEG_COM) {
512       i_tags_add(&im->tags, "jpeg_comment", 0, (const char *)markerp->data,
513                  markerp->data_length, 0);
514     }
515 #ifdef IMEXIF_ENABLE
516     else if (markerp->marker == JPEG_APP1 && !seen_exif) {
517       seen_exif = i_int_decode_exif(im, markerp->data, markerp->data_length);
518     }
519 #endif      
520
521     markerp = markerp->next;
522   }
523
524   i_tags_addn(&im->tags, "jpeg_out_color_space", 0, cinfo.out_color_space);
525   i_tags_addn(&im->tags, "jpeg_color_space", 0, cinfo.jpeg_color_space);
526
527   if (cinfo.saw_JFIF_marker) {
528     double xres = cinfo.X_density;
529     double yres = cinfo.Y_density;
530     
531     i_tags_addn(&im->tags, "jpeg_density_unit", 0, cinfo.density_unit);
532     switch (cinfo.density_unit) {
533     case 0: /* values are just the aspect ratio */
534       i_tags_addn(&im->tags, "i_aspect_only", 0, 1);
535       i_tags_add(&im->tags, "jpeg_density_unit_name", 0, "none", -1, 0);
536       break;
537
538     case 1: /* per inch */
539       i_tags_add(&im->tags, "jpeg_density_unit_name", 0, "inch", -1, 0);
540       break;
541
542     case 2: /* per cm */
543       i_tags_add(&im->tags, "jpeg_density_unit_name", 0, "centimeter", -1, 0);
544       xres *= 2.54;
545       yres *= 2.54;
546       break;
547     }
548     i_tags_set_float2(&im->tags, "i_xres", 0, xres, 6);
549     i_tags_set_float2(&im->tags, "i_yres", 0, yres, 6);
550   }
551
552   (void) jpeg_finish_decompress(&cinfo);
553   jpeg_destroy_decompress(&cinfo);
554   *itlength=tlength;
555
556   i_tags_add(&im->tags, "i_format", 0, "jpeg", 4, 0);
557
558   mm_log((1,"i_readjpeg_wiol -> (0x%x)\n",im));
559   return im;
560 }
561
562 /*
563 =item i_writejpeg_wiol(im, ig, qfactor)
564
565 =cut
566 */
567
568 undef_int
569 i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) {
570   JSAMPLE *image_buffer;
571   int quality;
572   int got_xres, got_yres, aspect_only, resunit;
573   double xres, yres;
574   int comment_entry;
575   int want_channels = im->channels;
576
577   struct jpeg_compress_struct cinfo;
578   struct my_error_mgr jerr;
579
580   JSAMPROW row_pointer[1];      /* pointer to JSAMPLE row[s] */
581   int row_stride;               /* physical row width in image buffer */
582   unsigned char * data = NULL;
583   i_color *line_buf = NULL;
584
585   mm_log((1,"i_writejpeg(im %p, ig %p, qfactor %d)\n", im, ig, qfactor));
586   
587   i_clear_error();
588   io_glue_commit_types(ig);
589
590   if (!(im->channels==1 || im->channels==3)) { 
591     want_channels = im->channels - 1;
592   }
593   quality = qfactor;
594
595   cinfo.err = jpeg_std_error(&jerr.pub);
596   jerr.pub.error_exit = my_error_exit;
597   jerr.pub.output_message = my_output_message;
598   
599   jpeg_create_compress(&cinfo);
600
601   if (setjmp(jerr.setjmp_buffer)) {
602     jpeg_destroy_compress(&cinfo);
603     if (data)
604       myfree(data);
605     if (line_buf)
606       myfree(line_buf);
607     return 0;
608   }
609
610   jpeg_wiol_dest(&cinfo, ig);
611
612   cinfo.image_width  = im -> xsize;     /* image width and height, in pixels */
613   cinfo.image_height = im -> ysize;
614
615   if (want_channels==3) {
616     cinfo.input_components = 3;         /* # of color components per pixel */
617     cinfo.in_color_space = JCS_RGB;     /* colorspace of input image */
618   }
619
620   if (want_channels==1) {
621     cinfo.input_components = 1;         /* # of color components per pixel */
622     cinfo.in_color_space = JCS_GRAYSCALE;       /* colorspace of input image */
623   }
624
625   jpeg_set_defaults(&cinfo);
626   jpeg_set_quality(&cinfo, quality, TRUE);  /* limit to baseline-JPEG values */
627
628   got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres);
629   got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres);
630   if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only))
631     aspect_only = 0;
632   if (!i_tags_get_int(&im->tags, "jpeg_density_unit", 0, &resunit))
633     resunit = 1; /* per inch */
634   if (resunit < 0 || resunit > 2) /* default to inch if invalid */
635     resunit = 1;
636   if (got_xres || got_yres) {
637     if (!got_xres)
638       xres = yres;
639     else if (!got_yres)
640       yres = xres;
641     if (aspect_only)
642       resunit = 0; /* standard tags override format tags */
643     if (resunit == 2) {
644       /* convert to per cm */
645       xres /= 2.54;
646       yres /= 2.54;
647     }
648     cinfo.density_unit = resunit;
649     cinfo.X_density = (int)(xres + 0.5);
650     cinfo.Y_density = (int)(yres + 0.5);
651   }
652
653   jpeg_start_compress(&cinfo, TRUE);
654
655   if (i_tags_find(&im->tags, "jpeg_comment", 0, &comment_entry)) {
656     jpeg_write_marker(&cinfo, JPEG_COM, 
657                       (const JOCTET *)im->tags.tags[comment_entry].data,
658                       im->tags.tags[comment_entry].size);
659   }
660
661   row_stride = im->xsize * im->channels;        /* JSAMPLEs per row in image_buffer */
662
663   if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits
664       && im->channels == want_channels) {
665     image_buffer=im->idata;
666
667     while (cinfo.next_scanline < cinfo.image_height) {
668       /* jpeg_write_scanlines expects an array of pointers to scanlines.
669        * Here the array is only one element long, but you could pass
670        * more than one scanline at a time if that's more convenient.
671        */
672       row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
673       (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
674     }
675   }
676   else if (im->channels == want_channels) {
677     data = mymalloc(im->xsize * im->channels);
678     if (data) {
679       while (cinfo.next_scanline < cinfo.image_height) {
680         /* jpeg_write_scanlines expects an array of pointers to scanlines.
681          * Here the array is only one element long, but you could pass
682          * more than one scanline at a time if that's more convenient.
683          */
684         i_gsamp(im, 0, im->xsize, cinfo.next_scanline, data, 
685                 NULL, im->channels);
686         row_pointer[0] = data;
687         (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
688       }
689       myfree(data);
690     }
691     else {
692       jpeg_destroy_compress(&cinfo);
693       i_push_error(0, "out of memory");
694       return 0; /* out of memory? */
695     }
696   }
697   else {
698     i_color bg;
699     int x;
700     int ch;
701     i_color const *linep;
702     unsigned char * datap;    
703     
704     line_buf = mymalloc(sizeof(i_color) * im->xsize);
705
706     i_get_file_background(im, &bg);
707
708     data = mymalloc(im->xsize * want_channels);
709     while (cinfo.next_scanline < cinfo.image_height) {
710       i_glin(im, 0, im->xsize, cinfo.next_scanline, line_buf);
711       i_adapt_colors_bg(want_channels, im->channels, line_buf, im->xsize, &bg);
712       datap = data;
713       linep = line_buf;
714       for (x = 0; x < im->xsize; ++x) {
715         for (ch = 0; ch < want_channels; ++ch) {
716           *datap++ = linep->channel[ch];
717         }
718         ++linep;
719       }
720       row_pointer[0] = data;
721       (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
722     }
723     myfree(line_buf);
724     myfree(data);
725   }
726
727   /* Step 6: Finish compression */
728
729   jpeg_finish_compress(&cinfo);
730
731   jpeg_destroy_compress(&cinfo);
732
733   ig->closecb(ig);
734
735   return(1);
736 }
737
738 /*
739 =back
740
741 =head1 AUTHOR
742
743 Arnar M. Hrafnkelsson, addi@umich.edu
744
745 =head1 SEE ALSO
746
747 Imager(3)
748
749 =cut
750 */