1 /*
2  * xvjpeg.c - i/o routines for 'jpeg' format pictures
3  */
4 
5 /* based in part on 'example.c' from the IJG JPEG distribution */
6 
7 
8 #include "copyright.h"
9 #include "xv.h"
10 
11 #ifdef HAVE_JPEG
12 
13 #include <setjmp.h>
14 
15 #include "jpeglib.h"   /* currently defines JPEG_APP0 but not JPEG_APP1 */
16 #include "jerror.h"
17 
18 #ifndef JPEG_APP1
19 #  define JPEG_APP1 (JPEG_APP0 + 1)   /* EXIF marker */
20 #endif
21 
22 #define CREATOR_STR "CREATOR: "
23 
24 #if BITS_IN_JSAMPLE != 8
25   Sorry, this code copes only with 8-bit JSAMPLEs. /* deliberate syntax err */
26 #endif
27 
28 
29 /*** Stuff for JPEG Dialog box ***/
30 #define JWIDE 400
31 #define JHIGH 200
32 #define J_NBUTTS 2
33 #define J_BOK    0
34 #define J_BCANC  1
35 #define BUTTH    24
36 
37 /* Minimum size compression when doing a 'quick' image load.  (Of course, if
38    the image *is* smaller than this, you'll get whatever size it actually is.)
39    This is currently hardcoded to be twice the size of a schnauzer icon, as
40    the schnauzer's the only thing that does a quick load... */
41 
42 #define QUICKWIDE 160
43 #define QUICKHIGH 120
44 
45 struct my_error_mgr {
46   struct jpeg_error_mgr pub;
47   jmp_buf               setjmp_buffer;
48 };
49 
50 typedef struct my_error_mgr *my_error_ptr;
51 
52 
53 /*** local functions ***/
54 static    void         drawJD             PARM((int, int, int, int));
55 static    void         clickJD            PARM((int, int));
56 static    void         doCmd              PARM((int));
57 static    void         writeJPEG          PARM((void));
58 #if JPEG_LIB_VERSION > 60
59 METHODDEF(void)        xv_error_exit      PARM((j_common_ptr));
60 METHODDEF(void)        xv_error_output    PARM((j_common_ptr));
61 METHODDEF(void)        xv_prog_meter      PARM((j_common_ptr));
62 #else
63 METHODDEF void         xv_error_exit      PARM((j_common_ptr));
64 METHODDEF void         xv_error_output    PARM((j_common_ptr));
65 METHODDEF void         xv_prog_meter      PARM((j_common_ptr));
66 #endif
67 static    unsigned int j_getc             PARM((j_decompress_ptr));
68 #if JPEG_LIB_VERSION > 60
69 METHODDEF(boolean)     xv_process_comment PARM((j_decompress_ptr));
70 METHODDEF(boolean)     xv_process_app1    PARM((j_decompress_ptr));
71 #else
72 METHODDEF boolean      xv_process_comment PARM((j_decompress_ptr));
73 METHODDEF boolean      xv_process_app1    PARM((j_decompress_ptr));
74 #endif
75 static    int          writeJFIF          PARM((FILE *, byte *, int,int,int));
76 
77 
78 
79 /*** local variables ***/
80 static char *filename;
81 static const char *fbasename;
82 static char *comment;
83 static byte *exifInfo;
84 static int   exifInfoSize;   /* not a string => must track size explicitly */
85 static int   colorType;
86 
87 static DIAL  qDial, smDial;
88 static BUTT  jbut[J_NBUTTS];
89 
90 
91 
92 
93 /***************************************************************************/
94 /* JPEG SAVE DIALOG ROUTINES ***********************************************/
95 /***************************************************************************/
96 
97 
98 /***************************************************/
CreateJPEGW()99 void CreateJPEGW()
100 {
101   jpegW = CreateWindow("xv jpeg","XVjpeg",NULL,JWIDE,JHIGH,infofg,infobg,0);
102   if (!jpegW) FatalError("can't create jpeg window!");
103 
104   XSelectInput(theDisp, jpegW, ExposureMask | ButtonPressMask | KeyPressMask);
105 
106   DCreate(&qDial, jpegW, 10, 10, 80, 100, 1.0, 100.0, 75.0, 1.0, 5.0,
107 	  infofg, infobg, hicol, locol, "Quality", "%");
108 
109   DCreate(&smDial, jpegW, 120, 10, 80, 100, 0.0, 100.0, 0.0, 1.0, 5.0,
110 	  infofg, infobg, hicol, locol, "Smoothing", "%");
111 
112   BTCreate(&jbut[J_BOK], jpegW, JWIDE-180-1, JHIGH-10-BUTTH-1, 80, BUTTH,
113 	   "Ok", infofg, infobg, hicol, locol);
114 
115   BTCreate(&jbut[J_BCANC], jpegW, JWIDE-90-1, JHIGH-10-BUTTH-1, 80, BUTTH,
116 	   "Cancel", infofg, infobg, hicol, locol);
117 
118   XMapSubwindows(theDisp, jpegW);
119 }
120 
121 
122 /***************************************************/
JPEGDialog(vis)123 void JPEGDialog(vis)
124      int vis;
125 {
126   if (vis) {
127     CenterMapWindow(jpegW, jbut[J_BOK].x + (int) jbut[J_BOK].w/2,
128 		    jbut[J_BOK].y + (int) jbut[J_BOK].h/2, JWIDE, JHIGH);
129   }
130   else     XUnmapWindow(theDisp, jpegW);
131   jpegUp = vis;
132 }
133 
134 
135 /***************************************************/
JPEGCheckEvent(xev)136 int JPEGCheckEvent(xev)
137      XEvent *xev;
138 {
139   /* check event to see if it's for one of our subwindows.  If it is,
140      deal accordingly, and return '1'.  Otherwise, return '0' */
141 
142   int rv;
143   rv = 1;
144 
145   if (!jpegUp) return 0;
146 
147   if (xev->type == Expose) {
148     int x,y,w,h;
149     XExposeEvent *e = (XExposeEvent *) xev;
150     x = e->x;  y = e->y;  w = e->width;  h = e->height;
151 
152     /* throw away excess expose events for 'dumb' windows */
153     if (e->count > 0 && (e->window == qDial.win ||
154 			 e->window == smDial.win)) {}
155 
156     else if (e->window == jpegW)       drawJD(x, y, w, h);
157     else if (e->window == qDial.win)   DRedraw(&qDial);
158     else if (e->window == smDial.win)  DRedraw(&smDial);
159     else rv = 0;
160   }
161 
162   else if (xev->type == ButtonPress) {
163     XButtonEvent *e = (XButtonEvent *) xev;
164     int x,y;
165     x = e->x;  y = e->y;
166 
167     if (e->button == Button1) {
168       if      (e->window == jpegW)      clickJD(x,y);
169       else if (e->window == qDial.win)  DTrack(&qDial,  x,y);
170       else if (e->window == smDial.win) DTrack(&smDial, x,y);
171       else rv = 0;
172     }  /* button1 */
173     else rv = 0;
174   }  /* button press */
175 
176 
177   else if (xev->type == KeyPress) {
178     XKeyEvent *e = (XKeyEvent *) xev;
179     char buf[128];  KeySym ks;
180     int stlen;
181 
182     stlen = XLookupString(e,buf,128,&ks,(XComposeStatus *) NULL);
183     buf[stlen] = '\0';
184 
185     RemapKeyCheck(ks, buf, &stlen);
186 
187     if (e->window == jpegW) {
188       if (stlen) {
189 	if (buf[0] == '\r' || buf[0] == '\n') { /* enter */
190 	  FakeButtonPress(&jbut[J_BOK]);
191 	}
192 	else if (buf[0] == '\033') {            /* ESC */
193 	  FakeButtonPress(&jbut[J_BCANC]);
194 	}
195       }
196     }
197     else rv = 0;
198   }
199   else rv = 0;
200 
201   if (rv==0 && (xev->type == ButtonPress || xev->type == KeyPress)) {
202     XBell(theDisp, 50);
203     rv = 1;   /* eat it */
204   }
205 
206   return rv;
207 }
208 
209 
210 /***************************************************/
JPEGSaveParams(fname,col)211 void JPEGSaveParams(fname, col)
212      char *fname;
213      int col;
214 {
215   filename = fname;
216   colorType = col;
217 }
218 
219 
220 /***************************************************/
drawJD(x,y,w,h)221 static void drawJD(x,y,w,h)
222      int x,y,w,h;
223 {
224   const char *title  = "Save JPEG file...";
225   const char *title1 = "Quality value determines";
226   const char *title2 = "compression rate: higher";
227   const char *title3 = "quality = bigger file.";
228   const char *title4 = "Use smoothing if saving";
229   const char *title5 = "an 8-bit image (eg, a GIF).";
230 
231   const char *qtitle1 = "Default = 75.";
232   const char *qtitle2 = "Useful range";
233   const char *qtitle3 = "is 5-95.";
234 
235   const char *smtitle1 = "Default = 0 (none).";
236   const char *smtitle2 = "10-30 is enough";
237   const char *smtitle3 = "for typical GIFs.";
238 
239   int  i;
240   XRectangle xr;
241 
242   xr.x = x;  xr.y = y;  xr.width = w;  xr.height = h;
243   XSetClipRectangles(theDisp, theGC, 0,0, &xr, 1, Unsorted);
244 
245   XSetForeground(theDisp, theGC, infofg);
246   XSetBackground(theDisp, theGC, infobg);
247 
248   for (i=0; i<J_NBUTTS; i++) BTRedraw(&jbut[i]);
249 
250   DrawString(jpegW, 220, 10+ASCENT,                   title);
251   DrawString(jpegW, 230, 10+ASCENT+LINEHIGH*1,        title1);
252   DrawString(jpegW, 230, 10+ASCENT+LINEHIGH*2,        title2);
253   DrawString(jpegW, 230, 10+ASCENT+LINEHIGH*3,        title3);
254   DrawString(jpegW, 230, 10+ASCENT+LINEHIGH*4,        title4);
255   DrawString(jpegW, 230, 10+ASCENT+LINEHIGH*5,        title5);
256 
257   DrawString(jpegW,  15, 10+100+10+ASCENT,            qtitle1);
258   DrawString(jpegW,  15, 10+100+10+ASCENT+LINEHIGH,   qtitle2);
259   DrawString(jpegW,  15, 10+100+10+ASCENT+LINEHIGH*2, qtitle3);
260 
261   DrawString(jpegW, 115, 10+100+10+ASCENT+LINEHIGH*0, smtitle1);
262   DrawString(jpegW, 115, 10+100+10+ASCENT+LINEHIGH*1, smtitle2);
263   DrawString(jpegW, 115, 10+100+10+ASCENT+LINEHIGH*2, smtitle3);
264 
265   XSetClipMask(theDisp, theGC, None);
266 }
267 
268 
269 /***************************************************/
clickJD(x,y)270 static void clickJD(x,y)
271      int x,y;
272 {
273   int i;
274   BUTT *bp;
275 
276   /* check BUTTs */
277 
278   for (i=0; i<J_NBUTTS; i++) {
279     bp = &jbut[i];
280     if (PTINRECT(x, y, bp->x, bp->y, bp->w, bp->h)) break;
281   }
282 
283   if (i<J_NBUTTS) {  /* found one */
284     if (BTTrack(bp)) doCmd(i);
285   }
286 }
287 
288 
289 
290 /***************************************************/
doCmd(cmd)291 static void doCmd(cmd)
292      int cmd;
293 {
294 
295   switch (cmd) {
296   case J_BOK: {
297     char *fullname;
298 
299     writeJPEG();
300     JPEGDialog(0);
301 
302     fullname = GetDirFullName();
303     if (!ISPIPE(fullname[0])) {
304       XVCreatedFile(fullname);
305       StickInCtrlList(0);
306     }
307   }
308     break;
309 
310   case J_BCANC:  JPEGDialog(0);  break;
311 
312   default:        break;
313   }
314 }
315 
316 
317 
318 
319 
320 /*******************************************/
writeJPEG()321 static void writeJPEG()
322 {
323   FILE          *fp;
324   int            i, nc, rv, w, h, npixels, ptype, pfree;
325   register byte *ip, *ep;
326   byte          *inpix, *rmap, *gmap, *bmap;
327   byte          *image8, *image24;
328 
329   /* get the XV image into a format that the JPEG software can grok on.
330      Also, open the output file, so we don't waste time doing this format
331      conversion if we won't be able to write it out */
332 
333 
334   fp = OpenOutFile(filename);
335   if (!fp) return;
336 
337   fbasename = BaseName(filename);
338 
339   WaitCursor();
340   inpix = GenSavePic(&ptype, &w, &h, &pfree, &nc, &rmap, &gmap, &bmap);
341 
342   /* this case may not be possible to trigger, but not totally clear, so... */
343   npixels = w*h;
344   if (w <= 0 || h <= 0 || npixels/w < h) {
345     SetISTR(ISTR_WARNING, "%s:  image dimensions too large (%dx%d)",
346             fbasename, w, h);
347     return;
348   }
349 
350   image8 = image24 = (byte *) NULL;
351 
352 
353   /* monocity: see if the image is mono, save it that way to save space */
354   if (colorType != F_GREYSCALE) {
355     if (ptype == PIC8) {
356       for (i=0; i<nc && rmap[i]==gmap[i] && rmap[i]==bmap[i]; i++);
357       if (i==nc) colorType = F_GREYSCALE;    /* made it all the way through */
358     }
359     else {  /* PIC24 */
360       for (i=0,ip=inpix; i<npixels && ip[0]==ip[1] && ip[1]==ip[2]; i++,ip+=3);
361       if (i==npixels) colorType = F_GREYSCALE;  /* all the way through */
362     }
363   }
364 
365 
366   /* first thing to do is build an 8/24-bit Greyscale/TrueColor image
367      (meaning: non-colormapped) */
368 
369   if (colorType == F_GREYSCALE) {   /* build an 8-bit Greyscale image */
370     image8 = (byte *) malloc((size_t) npixels);
371     if (!image8) FatalError("writeJPEG: unable to malloc image8\n");
372 
373     if (ptype == PIC8) {
374       for (i=0,ip=image8,ep=inpix; i<npixels; i++, ip++, ep++)
375 	*ip = MONO(rmap[*ep], gmap[*ep], bmap[*ep]);
376     }
377     else {  /* PIC24 */
378       for (i=0,ip=image8,ep=inpix; i<npixels; i++, ip++, ep+=3)
379 	*ip = MONO(ep[0],ep[1],ep[2]);
380     }
381   }
382 
383   else {    /* *not* F_GREYSCALE */
384     if (ptype == PIC8) {
385       int count = 3*npixels;
386 
387       /* already know npixels > 0 (above) */
388       if (count/3 < npixels) {
389         SetISTR(ISTR_WARNING, "%s:  image dimensions too large (%dx%d)",
390                 fbasename, w, h);
391         return;
392       }
393 
394       image24 = (byte *) malloc((size_t) count);
395       if (!image24) {  /* this simply isn't going to work */
396 	FatalError("writeJPEG: unable to malloc image24\n");
397       }
398 
399       for (i=0, ip=image24, ep=inpix; i<npixels; i++, ep++) {
400 	*ip++ = rmap[*ep];
401 	*ip++ = gmap[*ep];
402 	*ip++ = bmap[*ep];
403       }
404     }
405 
406     else {  /* PIC24 */
407       image24 = inpix;
408     }
409   }
410 
411 
412   /* in any event, we've got some valid image.  Do the JPEG Thing */
413   rv = writeJFIF(fp, (colorType==F_GREYSCALE) ? image8 : image24,
414 		 w, h, colorType);
415 
416   if      (colorType == F_GREYSCALE) free(image8);
417   else if (ptype == PIC8)            free(image24);
418 
419   if (pfree) free(inpix);
420 
421   if (CloseOutFile(fp, filename, rv) == 0) DirBox(0);
422   SetCursors(-1);
423 }
424 
425 
426 
427 
428 
429 
430 /***************************************************************************/
431 /* JPEG INTERFACE ROUTINES *************************************************/
432 /***************************************************************************/
433 
434 
435 
436 /**************************************************/
437 #if JPEG_LIB_VERSION > 60
438 METHODDEF(void) xv_error_exit(cinfo)
439 #else
440 METHODDEF void  xv_error_exit(cinfo)
441 #endif
442      j_common_ptr cinfo;
443 {
444   my_error_ptr myerr;
445 
446   myerr = (my_error_ptr) cinfo->err;
447   (*cinfo->err->output_message)(cinfo);     /* display error message */
448   longjmp(myerr->setjmp_buffer, 1);         /* return from error */
449 }
450 
451 
452 /**************************************************/
453 #if JPEG_LIB_VERSION > 60
454 METHODDEF(void) xv_error_output(cinfo)
455 #else
456 METHODDEF void  xv_error_output(cinfo)
457 #endif
458      j_common_ptr cinfo;
459 {
460   my_error_ptr myerr;
461   char         buffer[JMSG_LENGTH_MAX];
462 
463   myerr = (my_error_ptr) cinfo->err;
464   (*cinfo->err->format_message)(cinfo, buffer);
465 
466   SetISTR(ISTR_WARNING, "%s: %s", fbasename, buffer);   /* send it to XV */
467 }
468 
469 
470 /**************************************************/
471 #if JPEG_LIB_VERSION > 60
472 METHODDEF(void) xv_prog_meter(cinfo)
473 #else
474 METHODDEF void  xv_prog_meter(cinfo)
475 #endif
476      j_common_ptr cinfo;
477 {
478   struct jpeg_progress_mgr *prog;
479 
480   prog = cinfo->progress;
481 
482   if ((prog->pass_counter & 0x3f)==0) WaitCursor();
483 
484 #ifdef FOO
485   fprintf(stderr,"xv_prog_meter: cnt=%ld, maxcnt=%ld, pass=%d, maxpass=%d\n",
486 	  prog->pass_counter, prog->pass_limit, prog->completed_passes,
487 	  prog->total_passes);
488 #endif
489 }
490 
491 
492 
493 /***************************************************************************/
494 /* LOAD ROUTINES ***********************************************************/
495 /***************************************************************************/
496 
497 
498 /*******************************************/
LoadJFIF(fname,pinfo,quick)499 int LoadJFIF(fname, pinfo, quick)
500      char    *fname;
501      PICINFO *pinfo;
502      int      quick;
503 {
504   /* returns '1' on success, '0' on failure */
505 
506   struct jpeg_decompress_struct    cinfo;
507   struct jpeg_progress_mgr         prog;
508   struct my_error_mgr              jerr;
509   JSAMPROW                         rowptr[1];
510   FILE                            *fp;
511   const char                      *colorspace_name = "Color";
512   byte                            *pic, *pic_end;
513   long                             filesize;
514   int                              i,w,h,bperpix,bperline,count;
515 
516 
517   fbasename = BaseName(fname);
518   pic       = (byte *) NULL;
519   comment   = (char *) NULL;
520   exifInfo  = (byte *) NULL;
521 
522   pinfo->type  = PIC8;
523 
524   if ((fp = xv_fopen(fname, "r")) == NULL) return 0;
525 
526   fseek(fp, 0L, 2);
527   filesize = ftell(fp);
528   fseek(fp, 0L, 0);
529 
530 
531   cinfo.err = jpeg_std_error(&jerr.pub);
532   jerr.pub.error_exit     = xv_error_exit;
533   jerr.pub.output_message = xv_error_output;
534 
535   if (setjmp(jerr.setjmp_buffer)) {
536 L1:
537     /* if we're here, it blowed up... */
538     jpeg_destroy_decompress(&cinfo);
539     fclose(fp);
540     if (pic)      free(pic);
541     if (comment)  free(comment);
542     if (exifInfo) free(exifInfo);
543 
544     pinfo->pic      = (byte *) NULL;
545     pinfo->comment  = (char *) NULL;
546     pinfo->exifInfo = (byte *) NULL;
547     pinfo->exifInfoSize = 0;
548 
549     comment  = (char *) NULL;
550     exifInfo = (byte *) NULL;
551     exifInfoSize = 0;
552 
553     return 0;
554   }
555 
556 
557   jpeg_create_decompress(&cinfo);
558   jpeg_set_marker_processor(&cinfo, JPEG_COM, xv_process_comment);
559   jpeg_set_marker_processor(&cinfo, JPEG_APP1, xv_process_app1);
560 
561   /* hook up progress meter */
562   prog.progress_monitor = xv_prog_meter;
563   cinfo.progress = &prog;
564 
565   jpeg_stdio_src(&cinfo, fp);
566   (void) jpeg_read_header(&cinfo, TRUE);
567 
568 
569 
570   /* do various cleverness regarding decompression parameters & such... */
571 
572 
573 
574   jpeg_calc_output_dimensions(&cinfo);
575   pinfo->normw = w = cinfo.output_width;
576   pinfo->normh = h = cinfo.output_height;
577 
578   if (quick) {
579     int wfac, hfac, fac;
580     wfac = w / QUICKWIDE;
581     hfac = h / QUICKHIGH;
582 
583     fac = wfac;  if (fac > hfac) fac = hfac;
584     if      (fac > 8) fac = 8;
585     else if (fac > 4) fac = 4;
586     else if (fac > 2) fac = 2;
587     else fac = 1;
588 
589     cinfo.scale_num   = 1;
590     cinfo.scale_denom = fac;
591     cinfo.dct_method = JDCT_FASTEST;
592     cinfo.do_fancy_upsampling = FALSE;
593 
594     pinfo->normw = w;  pinfo->normh = h;
595 
596     jpeg_calc_output_dimensions(&cinfo);
597     w = cinfo.output_width;
598     h = cinfo.output_height;
599   }
600 
601 
602   cinfo.quantize_colors = FALSE;     /* default: give 24-bit image to XV */
603   switch (cinfo.num_components) {
604     case 1:
605       cinfo.out_color_space = JCS_GRAYSCALE;
606       colorspace_name = "Greyscale";
607       for (i=0; i<256; i++) pinfo->r[i] = pinfo->g[i] = pinfo->b[i] = i;
608       break;
609 
610     case 3:
611       cinfo.out_color_space = JCS_RGB;
612       goto L2;
613 
614     case 4:
615       cinfo.out_color_space = JCS_CMYK;
616       colorspace_name = "4-Plane Color";
617 L2:
618       if (!quick && picType == PIC8 && conv24MB.flags[CONV24_LOCK] == 1) {
619         /*
620          * we're locked into 8-bit mode:
621          *   if CONV24_FAST, use JPEG's one-pass quantizer
622          *   if CONV24_SLOW, use JPEG's two-pass quantizer
623          *   if CONV24_BEST, or other, ask for 24-bit image and hand it to XV
624          */
625         cinfo.desired_number_of_colors = 256;
626 
627         if (conv24 == CONV24_FAST || conv24 == CONV24_SLOW) {
628           cinfo.quantize_colors = TRUE;
629           state824 = 1; /* image was converted from 24 to 8 bits */
630           cinfo.two_pass_quantize = (conv24 == CONV24_SLOW);
631         }
632       }
633       break;
634 
635     default:
636       SetISTR(ISTR_WARNING, "%s:  can't read %d-plane JPEG file!",
637               fbasename, cinfo.output_components);
638       goto L1;
639   }
640   SetISTR(ISTR_INFO, "Loading %dx%d %s JPEG (%ld bytes)...", w, h,
641           colorspace_name, filesize);
642 
643   jpeg_calc_output_dimensions(&cinfo);   /* note colorspace changes... */
644 
645 
646   bperpix = cinfo.output_components;
647   pinfo->type = (bperpix == 1) ? PIC8 : PIC24;
648 
649   bperline = w * bperpix;
650   count = h * bperline;
651   if (w <= 0 || h <= 0 || bperline/w < bperpix || count/h < bperline) {
652     SetISTR(ISTR_WARNING, "%s:  image dimensions too large (%dx%d)",
653             fbasename, w, h);
654     goto L1;
655   }
656 
657   pic = (byte *) malloc((size_t) count);
658   if (!pic) {
659     SetISTR(ISTR_WARNING, "%s:  can't read JPEG file - out of memory",
660 	    fbasename);
661     goto L1;
662   }
663   pic_end = pic + count;
664 
665   jpeg_start_decompress(&cinfo);
666 
667   while (cinfo.output_scanline < cinfo.output_height) {
668     if (cinfo.output_scanline < 0) {   /* should never happen, but... */
669       SetISTR(ISTR_WARNING, "%s:  invalid negative scanline (%d)",
670               fbasename, cinfo.output_scanline);
671       goto L1;
672     }
673     rowptr[0] = (JSAMPROW) &pic[cinfo.output_scanline * w * bperpix];
674     (void) jpeg_read_scanlines(&cinfo, rowptr, (JDIMENSION) 1);
675   }
676 
677 
678   /* Convert CMYK to RGB color space */
679 
680   if (bperpix > 3) {
681     register byte *p = pic;
682 
683     /* According to documentation accompanying the IJG JPEG Library, it appears
684      * that some versions of Adobe Systems' "Photoshop" write inverted CMYK
685      * data, where Byte 0 represents 100% ink coverage instead of 0% ink as
686      * you'd expect.  The JPEG Library's implementors made a policy decision
687      * not to correct for this in the Library, but instead force applications
688      * to deal with it; so we try to do that here:
689      */
690     if (cinfo.saw_Adobe_marker) { /* assume inverted data */
691       register byte *q = pic;
692 
693       do {
694         register int cmy, k = 255 - q[3];
695 
696         if ((cmy = *q++ - k) < 0) cmy = 0; *p++ = cmy; /* R */
697         if ((cmy = *q++ - k) < 0) cmy = 0; *p++ = cmy; /* G */
698         if ((cmy = *q++ - k) < 0) cmy = 0; *p++ = cmy; /* B */
699       } while (++q < pic_end);
700     }
701     else { /* assume normal data */
702       register byte *q = pic;
703 
704       do {
705         register int cmy, k = 255 - q[3];
706 
707         if ((cmy = k - *q++) < 0) cmy = 0; *p++ = cmy; /* R */
708         if ((cmy = k - *q++) < 0) cmy = 0; *p++ = cmy; /* G */
709         if ((cmy = k - *q++) < 0) cmy = 0; *p++ = cmy; /* B */
710       } while (++q < pic_end);
711     }
712     pic = realloc(pic,p-pic); /* Release extra storage */
713   }
714 
715 
716 
717   /* return 'PICINFO' structure to XV */
718 
719   pinfo->pic = pic;
720   pinfo->w = w;
721   pinfo->h = h;
722   pinfo->frmType = F_JPEG;
723 
724   if (cinfo.out_color_space == JCS_GRAYSCALE) {
725     pinfo->colType = F_GREYSCALE;
726 
727     for (i=0; i<256; i++) pinfo->r[i] = pinfo->g[i] = pinfo->b[i] = i;
728   }
729   else {
730     pinfo->colType = F_FULLCOLOR;
731 
732     if (cinfo.quantize_colors) {
733       switch (bperpix) {
734         case 3:
735           for (i = 0; i < cinfo.actual_number_of_colors; i++) {
736             pinfo->r[i] = cinfo.colormap[0][i];
737             pinfo->g[i] = cinfo.colormap[1][i];
738             pinfo->b[i] = cinfo.colormap[2][i];
739           }
740           break;
741 
742         case 4:
743           for (i = 0; i < cinfo.actual_number_of_colors; i++) {
744             register int cmy, k = cinfo.colormap[3][i];
745 
746             if ((cmy = 255 - cinfo.colormap[0][i] - k) < 0) cmy = 0;
747             pinfo->r[i] = cmy;
748             if ((cmy = 255 - cinfo.colormap[1][i] - k) < 0) cmy = 0;
749             pinfo->g[i] = cmy;
750             if ((cmy = 255 - cinfo.colormap[2][i] - k) < 0) cmy = 0;
751             pinfo->b[i] = cmy;
752           }
753           break;
754       }
755     }
756   }
757 
758   sprintf(pinfo->fullInfo, "%s JPEG. (%ld bytes)", colorspace_name, filesize);
759   sprintf(pinfo->shrtInfo, "%dx%d %s JPEG. ", w, h, colorspace_name);
760 
761   pinfo->comment      = comment;
762   pinfo->exifInfo     = exifInfo;
763   pinfo->exifInfoSize = exifInfoSize;
764 
765   jpeg_finish_decompress(&cinfo);
766   jpeg_destroy_decompress(&cinfo);
767   fclose(fp);
768 
769   /* ownership transferred to pinfo */
770   comment  = (char *) NULL;
771   exifInfo = (byte *) NULL;
772   exifInfoSize = 0;
773 
774   return 1;
775 }
776 
777 
778 
779 
780 /**************************************************/
j_getc(cinfo)781 static unsigned int j_getc(cinfo)
782      j_decompress_ptr cinfo;
783 {
784   struct jpeg_source_mgr *datasrc = cinfo->src;
785 
786   if (datasrc->bytes_in_buffer == 0) {
787     if (! (*datasrc->fill_input_buffer) (cinfo))
788       ERREXIT(cinfo, JERR_CANT_SUSPEND);
789   }
790   datasrc->bytes_in_buffer--;
791   return GETJOCTET(*datasrc->next_input_byte++);
792 }
793 
794 
795 /**************************************************/
796 #if JPEG_LIB_VERSION > 60
797 METHODDEF(boolean) xv_process_comment(cinfo)
798 #else
799 METHODDEF boolean  xv_process_comment(cinfo)
800 #endif
801      j_decompress_ptr cinfo;
802 {
803   int          length, hasnull;
804   unsigned int ch;
805   char         *oldsp, *sp;
806 
807   length  = j_getc(cinfo) << 8;
808   length += j_getc(cinfo);
809   length -= 2;                  /* discount the length word itself */
810 
811   if (!comment) {
812     comment = (char *) malloc((size_t) length + 1);
813     if (comment) comment[0] = '\0';
814   }
815   else comment = (char *) realloc(comment, strlen(comment) + length + 1);
816   if (!comment) FatalError("out of memory in xv_process_comment");
817 
818   oldsp = sp = comment + strlen(comment);
819   hasnull = 0;
820 
821   while (length-- > 0) {
822     ch = j_getc(cinfo);
823     *sp++ = (char) ch;
824     if (ch == 0) hasnull = 1;
825   }
826 
827   if (hasnull) sp = oldsp;       /* swallow comment blocks that have nulls */
828   *sp++ = '\0';
829 
830   return TRUE;
831 }
832 
833 
834 /**************************************************/
835 #if JPEG_LIB_VERSION > 60
836 METHODDEF(boolean) xv_process_app1(cinfo)   /* Geoff H. Kuenning 20030331 */
837 #else
838 METHODDEF boolean  xv_process_app1(cinfo)
839 #endif
840      j_decompress_ptr cinfo;
841 {
842   int          length;
843   unsigned int ch;
844   byte         *sp;
845 
846   length  = j_getc(cinfo) << 8;
847   length += j_getc(cinfo);
848   length -= 2;                  /* discount the length word itself */
849 
850   if (!exifInfo) {
851     exifInfo = (byte *) malloc((size_t) length);
852     exifInfoSize = 0;
853   }
854   else exifInfo = (byte *) realloc(exifInfo, exifInfoSize + length);
855   if (!exifInfo) FatalError("out of memory in xv_process_app1 (EXIF info)");
856 
857   sp = exifInfo + exifInfoSize;
858   exifInfoSize += length;
859 
860   while (length-- > 0) {
861     ch = j_getc(cinfo);
862     *sp++ = (byte) ch;
863   }
864 
865   return TRUE;
866 }
867 
868 
869 
870 
871 /***************************************************************************/
872 /* WRITE ROUTINES **********************************************************/
873 /***************************************************************************/
874 
writeJFIF(fp,pic,w,h,coltype)875 static int writeJFIF(fp, pic, w,h, coltype)
876      FILE *fp;
877      byte *pic;
878      int   w,h,coltype;
879 {
880   struct     jpeg_compress_struct cinfo;
881   struct     jpeg_progress_mgr    prog;
882   struct     my_error_mgr         jerr;
883   JSAMPROW                        rowptr[1];
884   int                             i, bperpix;
885   char                            xvcmt[256];
886 
887   comment = (char *) NULL;
888 
889   cinfo.err               = jpeg_std_error(&jerr.pub);
890   jerr.pub.error_exit     = xv_error_exit;
891   jerr.pub.output_message = xv_error_output;
892 
893   if (setjmp(jerr.setjmp_buffer)) {
894     /* if we're here, it blowed up... */
895     jpeg_destroy_compress(&cinfo);
896     if (picComments && comment) free(comment);
897     return 1;
898   }
899 
900 
901   jpeg_create_compress(&cinfo);
902   jpeg_stdio_dest(&cinfo, fp);
903 
904   cinfo.image_width  = w;
905   cinfo.image_height = h;
906   if (coltype == F_GREYSCALE) {
907     cinfo.input_components = 1;
908     cinfo.in_color_space = JCS_GRAYSCALE;
909   }
910   else {
911     cinfo.input_components = 3;
912     cinfo.in_color_space = JCS_RGB;
913   }
914 
915   bperpix = cinfo.input_components;
916 
917 
918   prog.progress_monitor = xv_prog_meter;
919   cinfo.progress = &prog;
920 
921 
922   jpeg_set_defaults(&cinfo);
923   jpeg_set_quality(&cinfo, (int)qDial.val, TRUE);
924   cinfo.smoothing_factor = (int)smDial.val;
925 
926 
927   jpeg_start_compress(&cinfo, TRUE);
928 
929 
930   /*** COMMENT HANDLING ***/
931 
932   sprintf(xvcmt, "%sXV %s  Quality = %d, Smoothing = %d\n",
933 	  CREATOR_STR, REVDATE, (int)qDial.val, (int)smDial.val);
934 
935   if (picComments) {   /* append XV comment */
936     char *sp, *sp1;  int done;
937 
938     i   = strlen(picComments);
939     comment = (char *) malloc(i + strlen(xvcmt) + 2 + 1);
940     if (!comment) FatalError("out of memory in writeJFIF()");
941 
942     strcpy(comment, picComments);
943 
944     /* see if there's a line that starts with 'CREATOR: ' in the
945        comments.  If there is, rip it out. */
946 
947     sp = comment;  done = 0;
948     while (!done && *sp) {
949       if (strncmp(sp, CREATOR_STR, strlen(CREATOR_STR)) == 0) {
950 	sp1 = sp;
951 	while (*sp1 && *sp1 != '\n') sp1++;    /* find end of this line */
952 	if (*sp1 == '\n') sp1++;               /* move past \n */
953 
954 	/* move comments from sp1 and on down to sp */
955 	xvbcopy(sp1, sp, strlen(sp1) + 1);   /* +1 to copy the trailing \0 */
956 
957 	done = 1;
958       }
959       else {   /* skip ahead to next line */
960 	while (*sp && *sp != '\n') sp++;
961 	if (*sp == '\n') sp++;
962       }
963     }
964 
965     /* count # of \n's at end of comment.
966        If none, add 2.   If one, add 1.  If two or more, add none. */
967 
968     sp = comment + strlen(comment);
969     for (i=0; i<3 && ((size_t) i < strlen(comment)); i++) {
970       sp--;
971       if (*sp != '\n') break;
972     }
973 
974     for ( ; i<2; i++) strcat(comment, "\n");
975     strcat(comment, xvcmt);
976   }
977   else comment = xvcmt;
978 
979 
980   jpeg_write_marker(&cinfo, JPEG_COM, (byte *)comment, (u_int)strlen(comment));
981 
982   if (picExifInfo) jpeg_write_marker(&cinfo, JPEG_APP1, (byte *)picExifInfo,
983                                      (u_int)picExifInfoSize);
984 
985   while (cinfo.next_scanline < cinfo.image_height) {
986     rowptr[0] = (JSAMPROW) &pic[cinfo.next_scanline * w * bperpix];
987     (void) jpeg_write_scanlines(&cinfo, rowptr, (JDIMENSION) 1);
988   }
989 
990   jpeg_finish_compress(&cinfo);
991   jpeg_destroy_compress(&cinfo);
992   return 0;
993 }
994 
995 
996 
997 
998 
999 /*******************************************/
1000 void
VersionInfoJPEG()1001 VersionInfoJPEG()	/* GRR 19980605, 19980607 */
1002 {
1003   int major = JPEG_LIB_VERSION / 10;
1004   int minor = JPEG_LIB_VERSION % 10;
1005   char minoralpha[2];
1006 
1007   if (minor) {
1008     minoralpha[0] = (char)(minor - 1 + 'a');
1009     minoralpha[1] = '\0';
1010   } else
1011     minoralpha[0] = '\0';
1012 
1013 /* fprintf(stderr, "   Compiled with libjpeg %d.%d.\n", major, minor); */
1014   fprintf(stderr, "   Compiled with libjpeg %d%s.\n", major, minoralpha);
1015 }
1016 
1017 
1018 
1019 
1020 
1021 #endif  /* HAVE_JPEG */
1022