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