]> git.imager.perl.org - imager.git/blob - jpeg.c
Removed the query asking if GIF support should be disabled, since
[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 *im;
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
399   mm_log((1,"i_readjpeg_wiol(data 0x%p, length %d,iptc_itext 0x%p)\n", data, length, iptc_itext));
400
401   i_clear_error();
402
403   iptc_text = iptc_itext;
404   cinfo.err = jpeg_std_error(&jerr.pub);
405   jerr.pub.error_exit     = my_error_exit;
406   jerr.pub.output_message = my_output_message;
407
408   /* Set error handler */
409   if (setjmp(jerr.setjmp_buffer)) {
410     jpeg_destroy_decompress(&cinfo); 
411     *iptc_itext=NULL;
412     *itlength=0;
413     if (line_buffer)
414       myfree(line_buffer);
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
424   (void) jpeg_read_header(&cinfo, TRUE);
425   (void) jpeg_start_decompress(&cinfo);
426
427   channels = cinfo.output_components;
428   switch (cinfo.out_color_space) {
429   case JCS_GRAYSCALE:
430     if (cinfo.output_components != 1) {
431       mm_log((1, "i_readjpeg: grayscale image with %d channels\n", cinfo.output_components));
432       i_push_errorf(0, "grayscale image with invalid components %d", cinfo.output_components);
433       wiol_term_source(&cinfo);
434       jpeg_destroy_decompress(&cinfo);
435       return NULL;
436     }
437     transfer_f = transfer_gray;
438     break;
439   
440   case JCS_RGB:
441     transfer_f = transfer_rgb;
442     if (cinfo.output_components != 3) {
443       mm_log((1, "i_readjpeg: RGB image with %d channels\n", cinfo.output_components));
444       i_push_errorf(0, "RGB image with invalid components %d", cinfo.output_components);
445       wiol_term_source(&cinfo);
446       jpeg_destroy_decompress(&cinfo);
447       return NULL;
448     }
449     break;
450
451   case JCS_CMYK:
452     if (cinfo.output_components == 4) {
453       /* we treat the CMYK values as inverted, because that's what that
454          buggy photoshop does, and everyone has to follow the gorilla.
455
456          Is there any app that still produces correct CMYK JPEGs?
457       */
458       transfer_f = transfer_cmyk_inverted;
459       channels = 3;
460     }
461     else {
462       mm_log((1, "i_readjpeg: cmyk image with %d channels\n", cinfo.output_components));
463       i_push_errorf(0, "CMYK image with invalid components %d", cinfo.output_components);
464       wiol_term_source(&cinfo);
465       jpeg_destroy_decompress(&cinfo);
466       return NULL;
467     }
468     break;
469
470   default:
471     mm_log((1, "i_readjpeg: unknown color space %d\n", cinfo.out_color_space));
472     i_push_errorf(0, "Unknown color space %d", cinfo.out_color_space);
473     wiol_term_source(&cinfo);
474     jpeg_destroy_decompress(&cinfo);
475     return NULL;
476   }
477
478   if (!i_int_check_image_file_limits(cinfo.output_width, cinfo.output_height,
479                                      channels, sizeof(i_sample_t))) {
480     mm_log((1, "i_readjpeg: image size exceeds limits\n"));
481     wiol_term_source(&cinfo);
482     jpeg_destroy_decompress(&cinfo);
483     return NULL;
484   }
485
486   im=i_img_empty_ch(NULL, cinfo.output_width, cinfo.output_height, channels);
487   if (!im) {
488     wiol_term_source(&cinfo);
489     jpeg_destroy_decompress(&cinfo);
490     return NULL;
491   }
492   row_stride = cinfo.output_width * cinfo.output_components;
493   buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
494   line_buffer = mymalloc(sizeof(i_color) * cinfo.output_width);
495   while (cinfo.output_scanline < cinfo.output_height) {
496     (void) jpeg_read_scanlines(&cinfo, buffer, 1);
497     transfer_f(line_buffer, buffer, cinfo.output_width);
498     i_plin(im, 0, cinfo.output_width, cinfo.output_scanline-1, line_buffer);
499   }
500   myfree(line_buffer);
501
502   /* check for APP1 marker and save */
503   markerp = cinfo.marker_list;
504   while (markerp != NULL) {
505     if (markerp->marker == JPEG_COM) {
506       i_tags_add(&im->tags, "jpeg_comment", 0, (const char *)markerp->data,
507                  markerp->data_length, 0);
508     }
509 #ifdef IMEXIF_ENABLE
510     else if (markerp->marker == JPEG_APP1 && !seen_exif) {
511       seen_exif = i_int_decode_exif(im, markerp->data, markerp->data_length);
512     }
513 #endif      
514
515     markerp = markerp->next;
516   }
517
518   i_tags_addn(&im->tags, "jpeg_out_color_space", 0, cinfo.out_color_space);
519   i_tags_addn(&im->tags, "jpeg_color_space", 0, 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_addn(&im->tags, "jpeg_density_unit", 0, cinfo.density_unit);
526     switch (cinfo.density_unit) {
527     case 0: /* values are just the aspect ratio */
528       i_tags_addn(&im->tags, "i_aspect_only", 0, 1);
529       i_tags_add(&im->tags, "jpeg_density_unit_name", 0, "none", -1, 0);
530       break;
531
532     case 1: /* per inch */
533       i_tags_add(&im->tags, "jpeg_density_unit_name", 0, "inch", -1, 0);
534       break;
535
536     case 2: /* per cm */
537       i_tags_add(&im->tags, "jpeg_density_unit_name", 0, "centimeter", -1, 0);
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   (void) jpeg_finish_decompress(&cinfo);
547   jpeg_destroy_decompress(&cinfo);
548   *itlength=tlength;
549
550   i_tags_add(&im->tags, "i_format", 0, "jpeg", 4, 0);
551
552   mm_log((1,"i_readjpeg_wiol -> (0x%x)\n",im));
553   return im;
554 }
555
556 /*
557 =item i_writejpeg_wiol(im, ig, qfactor)
558
559 =cut
560 */
561
562 undef_int
563 i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) {
564   JSAMPLE *image_buffer;
565   int quality;
566   int got_xres, got_yres, aspect_only, resunit;
567   double xres, yres;
568   int comment_entry;
569
570   struct jpeg_compress_struct cinfo;
571   struct my_error_mgr jerr;
572
573   JSAMPROW row_pointer[1];      /* pointer to JSAMPLE row[s] */
574   int row_stride;               /* physical row width in image buffer */
575   unsigned char * data = NULL;
576
577   mm_log((1,"i_writejpeg(im %p, ig %p, qfactor %d)\n", im, ig, qfactor));
578   
579   i_clear_error();
580   io_glue_commit_types(ig);
581
582   if (!(im->channels==1 || im->channels==3)) { 
583     i_push_error(0, "only 1 or 3 channels images can be saved as JPEG");
584     return 0;
585   }
586   quality = qfactor;
587
588   cinfo.err = jpeg_std_error(&jerr.pub);
589   jerr.pub.error_exit = my_error_exit;
590   jerr.pub.output_message = my_output_message;
591   
592   jpeg_create_compress(&cinfo);
593
594   if (setjmp(jerr.setjmp_buffer)) {
595     jpeg_destroy_compress(&cinfo);
596     if (data)
597       myfree(data);
598     return 0;
599   }
600
601   jpeg_wiol_dest(&cinfo, ig);
602
603   cinfo.image_width  = im -> xsize;     /* image width and height, in pixels */
604   cinfo.image_height = im -> ysize;
605
606   if (im->channels==3) {
607     cinfo.input_components = 3;         /* # of color components per pixel */
608     cinfo.in_color_space = JCS_RGB;     /* colorspace of input image */
609   }
610
611   if (im->channels==1) {
612     cinfo.input_components = 1;         /* # of color components per pixel */
613     cinfo.in_color_space = JCS_GRAYSCALE;       /* colorspace of input image */
614   }
615
616   jpeg_set_defaults(&cinfo);
617   jpeg_set_quality(&cinfo, quality, TRUE);  /* limit to baseline-JPEG values */
618
619   got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres);
620   got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres);
621   if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only))
622     aspect_only = 0;
623   if (!i_tags_get_int(&im->tags, "jpeg_density_unit", 0, &resunit))
624     resunit = 1; /* per inch */
625   if (resunit < 0 || resunit > 2) /* default to inch if invalid */
626     resunit = 1;
627   if (got_xres || got_yres) {
628     if (!got_xres)
629       xres = yres;
630     else if (!got_yres)
631       yres = xres;
632     if (aspect_only)
633       resunit = 0; /* standard tags override format tags */
634     if (resunit == 2) {
635       /* convert to per cm */
636       xres /= 2.54;
637       yres /= 2.54;
638     }
639     cinfo.density_unit = resunit;
640     cinfo.X_density = (int)(xres + 0.5);
641     cinfo.Y_density = (int)(yres + 0.5);
642   }
643
644   jpeg_start_compress(&cinfo, TRUE);
645
646   if (i_tags_find(&im->tags, "jpeg_comment", 0, &comment_entry)) {
647     jpeg_write_marker(&cinfo, JPEG_COM, 
648                       (const JOCTET *)im->tags.tags[comment_entry].data,
649                       im->tags.tags[comment_entry].size);
650   }
651
652   row_stride = im->xsize * im->channels;        /* JSAMPLEs per row in image_buffer */
653
654   if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits) {
655     image_buffer=im->idata;
656
657     while (cinfo.next_scanline < cinfo.image_height) {
658       /* jpeg_write_scanlines expects an array of pointers to scanlines.
659        * Here the array is only one element long, but you could pass
660        * more than one scanline at a time if that's more convenient.
661        */
662       row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
663       (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
664     }
665   }
666   else {
667     data = mymalloc(im->xsize * im->channels);
668     if (data) {
669       while (cinfo.next_scanline < cinfo.image_height) {
670         /* jpeg_write_scanlines expects an array of pointers to scanlines.
671          * Here the array is only one element long, but you could pass
672          * more than one scanline at a time if that's more convenient.
673          */
674         i_gsamp(im, 0, im->xsize, cinfo.next_scanline, data, 
675                 NULL, im->channels);
676         row_pointer[0] = data;
677         (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
678       }
679       myfree(data);
680     }
681     else {
682       jpeg_destroy_compress(&cinfo);
683       i_push_error(0, "out of memory");
684       return 0; /* out of memory? */
685     }
686   }
687
688   /* Step 6: Finish compression */
689
690   jpeg_finish_compress(&cinfo);
691
692   jpeg_destroy_compress(&cinfo);
693
694   ig->closecb(ig);
695
696   return(1);
697 }
698
699 /*
700 =back
701
702 =head1 AUTHOR
703
704 Arnar M. Hrafnkelsson, addi@umich.edu
705
706 =head1 SEE ALSO
707
708 Imager(3)
709
710 =cut
711 */