1/*
2 * jpeg.c --
3 *
4 *  JPEG photo image type, Tcl/Tk package
5 *
6 * Copyright (c) 2002 Andreas Kupries <andreas_kupries@users.sourceforge.net>
7 *
8 * This Tk image format handler reads and writes JPEG files in the standard
9 * JFIF file format.  ("JPEG" should be the format name.)  It can also read
10 * and write strings containing base64-encoded JPEG data.
11 *
12 * Several options can be provided in the format string, for example:
13 *
14 *	imageObject read input.jpg -shrink -format "jpeg -grayscale"
15 *	imageObject write output.jpg -format "jpeg -quality 50 -progressive"
16 *
17 * The supported options for reading are:
18 *	-fast:        Fast, low-quality processing
19 *	-grayscale:   Force incoming image to grayscale
20 * The supported options for writing are:
21 *	-quality N:   Compression quality (0..100; 5-95 is useful range)
22 *	              Default value: 75
23 *	-smooth N:    Perform smoothing (10-30 is enough for most GIF's)
24 *		      Default value: 0
25 *	-grayscale:   Create monochrome JPEG file
26 *	-optimize:    Optimize Huffman table
27 *	-progressive: Create progressive JPEG file
28 *
29 *
30 * Copyright (c) 1996-1997 Thomas G. Lane.
31 * This file is based on tkImgPPM.c from the Tk 4.2 distribution.
32 * That file is
33 *	Copyright (c) 1994 The Australian National University.
34 *	Copyright (c) 1994-1996 Sun Microsystems, Inc.
35 *
36 * See the file "license.terms" for information on usage and redistribution
37 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
38 *
39 * You will need a copy of the IJG JPEG library, version 5 or later,
40 * to use this file.  If you didn't receive it with this package, see
41 *	ftp://ftp.uu.net/graphics/jpeg/
42 *
43 * Author: Tom Lane (tgl@sss.pgh.pa.us)
44 *
45 * Modified for dynamical loading, reading from channels and Tcl_Obj's by:
46 *	Jan Nijtmans (nijtmans@users.sourceforge.net)
47 *
48 * $Id: jpeg.c 271 2010-06-17 13:40:24Z nijtmans $
49 */
50
51/*
52 * Generic initialization code, parameterized via CPACKAGE and PACKAGE.
53 */
54
55#include "tkimg.h"
56#include "jpegtcl.h"
57
58static int SetupJPegLibrary(Tcl_Interp *interp);
59
60#define MORE_INITIALIZATION \
61    if (SetupJPegLibrary (interp) != TCL_OK) { return TCL_ERROR; }
62
63#include "init.c"
64
65/* system includes */
66#include <stdlib.h>
67#include <string.h>
68#include <setjmp.h>
69
70/*
71 * The format record for the JPEG file format:
72 */
73
74
75/*
76 * Declarations for libjpeg source and destination managers to handle
77 * reading and writing base64-encoded strings and Tcl_Channel's.
78 */
79
80#define STRING_BUF_SIZE  4096	/* choose any convenient size */
81
82typedef struct source_mgr {	/* Source manager for reading from string */
83  struct jpeg_source_mgr pub;	/* public fields */
84
85  tkimg_MFile handle;			/* base64 stream */
86  JOCTET buffer[STRING_BUF_SIZE]; /* buffer for a chunk of decoded data */
87} *src_ptr;
88
89typedef struct destination_mgr { /* Manager for string output */
90  struct jpeg_destination_mgr pub; /* public fields */
91
92  tkimg_MFile handle;			/* base64 stream */
93  JOCTET buffer[STRING_BUF_SIZE]; /* buffer for a chunk of decoded data */
94} *dest_ptr;
95
96/*
97 * Other declarations
98 */
99
100struct my_error_mgr {		/* Extended libjpeg error manager */
101  struct jpeg_error_mgr pub;	/* public fields */
102  jmp_buf setjmp_buffer;	/* for return to caller from error exit */
103};
104
105/*
106 * Prototypes for local procedures defined in this file:
107 */
108
109static int CommonMatch(tkimg_MFile *handle,
110	int *widthPtr, int *heightPtr);
111
112static int CommonRead(Tcl_Interp *interp,
113	j_decompress_ptr cinfo, Tcl_Obj *format,
114	Tk_PhotoHandle imageHandle, int destX, int destY,
115	int width, int height, int srcX, int srcY);
116
117static int CommonWrite(Tcl_Interp *interp,
118	j_compress_ptr cinfo, Tcl_Obj *format,
119	Tk_PhotoImageBlock *blockPtr);
120
121static void	my_jpeg_obj_src(j_decompress_ptr, Tcl_Obj *);
122static void	my_jpeg_channel_src(j_decompress_ptr, Tcl_Channel);
123static boolean	fill_input_buffer(j_decompress_ptr);
124static void	skip_input_data(j_decompress_ptr, long);
125static void	dummy_source(j_decompress_ptr);
126static void	my_jpeg_string_dest(j_compress_ptr, Tcl_DString*);
127static void	my_jpeg_channel_dest(j_compress_ptr, Tcl_Channel);
128static void	my_init_destination(j_compress_ptr);
129static boolean	my_empty_output_buffer(j_compress_ptr);
130static void	my_term_destination(j_compress_ptr);
131static void	my_error_exit(j_common_ptr cinfo);
132static void	my_output_message(j_common_ptr cinfo);
133static void	append_jpeg_message(Tcl_Interp *interp,
134		    j_common_ptr cinfo);
135
136
137
138static int
139SetupJPegLibrary (interp)
140    Tcl_Interp *interp;
141{
142    struct jpeg_compress_struct *cinfo; /* libjpeg's parameter structure */
143    struct my_error_mgr jerror;	/* for controlling libjpeg error handling */
144    int i;
145
146    if (Jpegtcl_InitStubs(interp, JPEGTCL_VERSION, 0) == NULL) {
147        return TCL_ERROR;
148    }
149
150    /* The followin code tries to determine if the JPEG library is
151       valid at all. The library might be configured differently,
152       which will produce core dumps. Also it might be that
153       fields appear in different places in jpeg_compress_struct
154       or jpeg_decompress_struct. This will make the library totally
155       unusable. In stead of a core-dump, we better have a proper
156       error message */
157
158    /* overallocat size, so we don't get a core-dump if the library
159       thinks that the structure is much larger */
160
161    cinfo = (struct jpeg_compress_struct *) ckalloc(8*sizeof(struct jpeg_compress_struct));
162    cinfo->err = jpeg_std_error(&jerror.pub);
163    jerror.pub.error_exit = my_error_exit;
164    jerror.pub.output_message = my_output_message;
165    /* Establish the setjmp return context for my_error_exit to use. */
166    if (setjmp(jerror.setjmp_buffer)) {
167      /* If we get here, the JPEG library is invalid. */
168      jpeg_destroy_compress(cinfo);
169      ckfree((char *)cinfo);
170
171      if (interp) {
172	Tcl_AppendResult(interp, "couldn't use \"", "jpegtcl",
173		"\": please upgrade to at least version 6a", (char *) NULL);
174      }
175      return TCL_ERROR;
176    }
177
178    /* Now we can initialize libjpeg. */
179    ((char *) cinfo)[sizeof(struct jpeg_compress_struct)] = 53;
180    jpeg_create_compress(cinfo);
181    if (((char *) cinfo)[sizeof(struct jpeg_compress_struct)] != 53) {
182	/* Oops. The library changed this value, which is outside the
183	 * structure. Definitely, the library is invalid!!!! */
184	ERREXIT(cinfo, JMSG_NOMESSAGE);
185    }
186
187    /* Set up JPEG compression parameters. */
188    cinfo->image_width = 16;
189    cinfo->image_height = 16;
190    cinfo->input_components = 3;
191    cinfo->in_color_space = JCS_RGB;
192    cinfo->data_precision = -1;
193    cinfo->optimize_coding = TRUE;
194    cinfo->dct_method = -1;
195    cinfo->X_density = 0;
196    cinfo->Y_density = 0;
197    jpeg_set_defaults(cinfo);
198
199    if ((cinfo->data_precision != BITS_IN_JSAMPLE) ||
200	    (cinfo->optimize_coding != FALSE) ||
201	    (cinfo->dct_method != JDCT_DEFAULT) ||
202	    (cinfo->X_density != 1) ||
203	    (cinfo->Y_density != 1)) {
204	ERREXIT(cinfo, JMSG_NOMESSAGE);
205    }
206    for (i = 0; i < NUM_ARITH_TBLS; i++) {
207	if ((cinfo->arith_dc_L[i] != 0) ||
208		(cinfo->arith_dc_U[i] != 1) ||
209		(cinfo->arith_ac_K[i] != 5)) {
210	    ERREXIT(cinfo, JMSG_NOMESSAGE);
211	}
212    }
213    jpeg_destroy_compress(cinfo);
214    ckfree((char *) cinfo);
215    return TCL_OK;
216}
217
218
219/*
220 *----------------------------------------------------------------------
221 *
222 * ChnMatch --
223 *
224 *	This procedure is invoked by the photo image type to see if
225 *	a channel contains image data in JPEG format.
226 *
227 * Results:
228 *	The return value is >0 if the first characters in channel "chan"
229 *	look like JPEG data, and 0 otherwise.  For a valid file, the
230 *	image dimensions are determined.
231 *
232 * Side effects:
233 *	The access position in f may change.
234 *
235 *----------------------------------------------------------------------
236 */
237
238static int ChnMatch(
239    Tcl_Channel chan,		/* The image channel, open for reading. */
240    const char *fileName,	/* The name of the image file. */
241    Tcl_Obj *format,		/* User-specified format string, or NULL. */
242    int *widthPtr,			/* The dimensions of the image are */
243	int *heightPtr,			/* returned here if the file is a valid
244				 * JPEG file. */
245    Tcl_Interp *interp
246) {
247    tkimg_MFile handle;
248
249    handle.data = (char *) chan;
250    handle.state = IMG_CHAN;
251    return CommonMatch(&handle, widthPtr, heightPtr);
252}
253
254/*
255 *----------------------------------------------------------------------
256 *
257 * ObjMatch --
258 *
259 *	This procedure is invoked by the photo image type to see if
260 *	a string contains image data in JPEG format.
261 *
262 * Results:
263 *	The return value is >0 if the first characters in the string look
264 *	like JPEG data, and 0 otherwise.  For a valid image, the image
265 *	dimensions are determined.
266 *
267 * Side effects:
268 *  the size of the image is placed in widthPtr and heightPtr.
269 *
270 *----------------------------------------------------------------------
271 */
272
273static int ObjMatch(
274    Tcl_Obj *data,		/* the object containing the image data */
275    Tcl_Obj *format,		/* User-specified format object, or NULL. */
276    int *widthPtr,		/* The dimensions of the image are */
277	int *heightPtr,		/* returned here if the string is a valid
278				 * JPEG image. */
279    Tcl_Interp *interp
280) {
281    tkimg_MFile handle;
282
283    tkimg_ReadInit(data, '\377', &handle);
284    return CommonMatch(&handle, widthPtr, heightPtr);
285}
286
287/*
288 *----------------------------------------------------------------------
289 *
290 * CommonMatch --
291 *
292 *	This procedure is invoked by the photo image type to see if
293 *	a string contains image data in JPEG format.
294 *
295 * Results:
296 *	The return value is >0 if the first characters in the string look
297 *	like JPEG data, and 0 otherwise.  For a valid image, the image
298 *	dimensions are determined.
299 *
300 * Side effects:
301 *  the size of the image is placed in widthPtr and heightPtr.
302 *
303 *----------------------------------------------------------------------
304 */
305
306static int
307CommonMatch(handle, widthPtr, heightPtr)
308    tkimg_MFile *handle;		/* the "file" handle */
309    int *widthPtr, *heightPtr;	/* The dimensions of the image are
310				 * returned here if the string is a valid
311				 * JPEG image. */
312{
313    char buf[256];
314    int i;
315
316    i = tkimg_Read(handle, buf, 3);
317    if ((i != 3)||strncmp(buf,"\377\330\377", 3)) {
318	return 0;
319    }
320
321    buf[0] = buf[2];
322    /* at top of loop: have just read first FF of a marker into buf[0] */
323    for (;;) {
324	/* get marker type byte, skipping any padding FFs */
325	while (buf[0] == (char) 0xff) {
326	    if (tkimg_Read(handle, buf,1) != 1) {
327		return 0;
328	    }
329	}
330	/* look for SOF0, SOF1, or SOF2, which are the only JPEG variants
331	 * currently accepted by libjpeg.
332	 */
333	if (buf[0] == (char) 0xc0 || buf[0] == (char) 0xc1
334		|| buf[0] == (char) 0xc2)
335	    break;
336	/* nope, skip the marker parameters */
337	if (tkimg_Read(handle, buf, 2) != 2) {
338	    return 0;
339	}
340	i = ((buf[0] & 0x0ff)<<8) + (buf[1] & 0x0ff) - 1;
341	while (i>256) {
342	    tkimg_Read(handle, buf, 256);
343	    i -= 256;
344	}
345	if ((i<1) || (tkimg_Read(handle, buf, i)) != i) {
346	    return 0;
347	}
348	buf[0] = buf[i-1];
349	/* skip any inter-marker junk (there shouldn't be any, really) */
350	while (buf[0] != (char) 0xff) {
351	    if (tkimg_Read(handle, buf,1) != 1) {
352		return 0;
353	    }
354	}
355    }
356    /* Found the SOFn marker, get image dimensions */
357    if (tkimg_Read(handle, buf, 7) != 7) {
358	return 0;
359    }
360    *heightPtr = ((buf[3] & 0x0ff)<<8) + (buf[4] & 0x0ff);
361    *widthPtr = ((buf[5] & 0x0ff)<<8) + (buf[6] & 0x0ff);
362
363    return 1;
364}
365
366/*
367 *----------------------------------------------------------------------
368 *
369 * ChnRead --
370 *
371 *	This procedure is called by the photo image type to read
372 *	JPEG format data from a channel, and give it to
373 *	the photo image.
374 *
375 * Results:
376 *	A standard TCL completion code.  If TCL_ERROR is returned
377 *	then an error message is left in interp->result.
378 *
379 * Side effects:
380 *	New data is added to the image given by imageHandle.
381 *
382 *----------------------------------------------------------------------
383 */
384
385static int
386ChnRead(interp, chan, fileName, format, imageHandle, destX, destY,
387	width, height, srcX, srcY)
388    Tcl_Interp *interp;		/* Interpreter to use for reporting errors. */
389    Tcl_Channel chan;		/* The image channel, open for reading. */
390    const char *fileName;	/* The name of the image file. */
391    Tcl_Obj *format;		/* User-specified format string, or NULL. */
392    Tk_PhotoHandle imageHandle;	/* The photo image to write into. */
393    int destX, destY;		/* Coordinates of top-left pixel in
394				 * photo image to be written to. */
395    int width, height;		/* Dimensions of block of photo image to
396				 * be written to. */
397    int srcX, srcY;		/* Coordinates of top-left pixel to be used
398				 * in image being read. */
399{
400    struct jpeg_decompress_struct cinfo; /* libjpeg's parameter structure */
401    struct my_error_mgr jerror;	/* for controlling libjpeg error handling */
402    int result;
403
404    /* Initialize JPEG error handler */
405    /* We set up the normal JPEG error routines, then override error_exit. */
406    cinfo.err = jpeg_std_error(&jerror.pub);
407    jerror.pub.error_exit = my_error_exit;
408    jerror.pub.output_message = my_output_message;
409
410    /* Establish the setjmp return context for my_error_exit to use. */
411    if (setjmp(jerror.setjmp_buffer)) {
412      /* If we get here, the JPEG code has signaled an error. */
413      Tcl_AppendResult(interp, "couldn't read JPEG string: ", (char *) NULL);
414      append_jpeg_message(interp, (j_common_ptr) &cinfo);
415      jpeg_destroy_decompress(&cinfo);
416      return TCL_ERROR;
417    }
418
419    /* Now we can initialize libjpeg. */
420    jpeg_CreateDecompress(&cinfo, JPEG_LIB_VERSION,
421			(size_t) sizeof(struct jpeg_decompress_struct));
422    my_jpeg_channel_src(&cinfo, chan);
423
424    /* Share code with ObjRead. */
425    result = CommonRead(interp, &cinfo, format, imageHandle,
426			    destX, destY, width, height, srcX, srcY);
427
428    /* Reclaim libjpeg's internal resources. */
429    jpeg_destroy_decompress(&cinfo);
430
431    return result;
432}
433
434/*
435 *----------------------------------------------------------------------
436 *
437 * ObjRead --
438 *
439 *	This procedure is called by the photo image type to read
440 *	JPEG format data from a base64 encoded string, and give it to
441 *	the photo image.
442 *
443 * Results:
444 *	A standard TCL completion code.  If TCL_ERROR is returned
445 *	then an error message is left in interp->result.
446 *
447 * Side effects:
448 *	New data is added to the image given by imageHandle.
449 *
450 *----------------------------------------------------------------------
451 */
452
453static int
454ObjRead(interp, data, format, imageHandle, destX, destY,
455	width, height, srcX, srcY)
456    Tcl_Interp *interp;		/* Interpreter to use for reporting errors. */
457    Tcl_Obj *data;		/* Object containing the image data. */
458    Tcl_Obj *format;		/* User-specified format object, or NULL. */
459    Tk_PhotoHandle imageHandle;	/* The photo image to write into. */
460    int destX, destY;		/* Coordinates of top-left pixel in
461				 * photo image to be written to. */
462    int width, height;		/* Dimensions of block of photo image to
463				 * be written to. */
464    int srcX, srcY;		/* Coordinates of top-left pixel to be used
465				 * in image being read. */
466{
467    struct jpeg_decompress_struct cinfo; /* libjpeg's parameter structure */
468    struct my_error_mgr jerror;	/* for controlling libjpeg error handling */
469    int result;
470
471    /* Initialize JPEG error handler */
472    /* We set up the normal JPEG error routines, then override error_exit. */
473    cinfo.err = jpeg_std_error(&jerror.pub);
474    jerror.pub.error_exit = my_error_exit;
475    jerror.pub.output_message = my_output_message;
476
477    /* Establish the setjmp return context for my_error_exit to use. */
478    if (setjmp(jerror.setjmp_buffer)) {
479      /* If we get here, the JPEG code has signaled an error. */
480      Tcl_AppendResult(interp, "couldn't read JPEG string: ", (char *) NULL);
481      append_jpeg_message(interp, (j_common_ptr) &cinfo);
482      jpeg_destroy_decompress(&cinfo);
483      return TCL_ERROR;
484    }
485
486    /* Now we can initialize libjpeg. */
487    jpeg_CreateDecompress(&cinfo, JPEG_LIB_VERSION,
488			(size_t) sizeof(struct jpeg_decompress_struct));
489    my_jpeg_obj_src(&cinfo, data);
490
491    /* Share code with ChnRead. */
492    result = CommonRead(interp, &cinfo, format, imageHandle,
493			    destX, destY, width, height, srcX, srcY);
494
495    /* Reclaim libjpeg's internal resources. */
496    jpeg_destroy_decompress(&cinfo);
497
498    return result;
499}
500
501/*
502 *----------------------------------------------------------------------
503 *
504 * CommonRead --
505 *
506 *	The common guts of ChnRead and ObjRead.
507 *	The decompress struct has already been set up and the
508 *	appropriate data source manager initialized.
509 *	The caller should do jpeg_destroy_decompress upon return.
510 *
511 *----------------------------------------------------------------------
512 */
513static int
514CommonRead(interp, cinfo, format, imageHandle, destX, destY,
515	width, height, srcX, srcY)
516    Tcl_Interp *interp;		/* Interpreter to use for reporting errors. */
517    j_decompress_ptr cinfo;	/* Already-constructed decompress struct. */
518    Tcl_Obj *format;		/* User-specified format string, or NULL. */
519    Tk_PhotoHandle imageHandle;	/* The photo image to write into. */
520    int destX, destY;		/* Coordinates of top-left pixel in
521				 * photo image to be written to. */
522    int width, height;		/* Dimensions of block of photo image to
523				 * be written to. */
524    int srcX, srcY;		/* Coordinates of top-left pixel to be used
525				 * in image being read. */
526{
527    static const char *const jpegReadOptions[] = {"-fast", "-grayscale", NULL};
528    int fileWidth, fileHeight, stopY, curY, outY, outWidth, outHeight;
529    Tk_PhotoImageBlock block;
530    JSAMPARRAY buffer;		/* Output row buffer */
531    int objc, i, index;
532    Tcl_Obj **objv = (Tcl_Obj **) NULL;
533
534    /* Ready to read header data. */
535    jpeg_read_header(cinfo, TRUE);
536
537    /* This code only supports 8-bit-precision JPEG files. */
538    if ((cinfo->data_precision != 8) ||
539	    (sizeof(JSAMPLE) != sizeof(unsigned char))) {
540	Tcl_AppendResult(interp, "Unsupported JPEG precision", (char *) NULL);
541	return TCL_ERROR;
542    }
543
544    /* Process format parameters. */
545    if (tkimg_ListObjGetElements(interp, format, &objc, &objv) != TCL_OK) {
546	return TCL_ERROR;
547    }
548    if (objc) {
549	for (i=1; i<objc; i++) {
550	    if (Tcl_GetIndexFromObj(interp, objv[i], (CONST84 char *CONST86 *)jpegReadOptions,
551		    "format option", 0, &index)!=TCL_OK) {
552		return TCL_ERROR;
553	    }
554	    switch (index) {
555		case 0: {
556		    /* Select fast processing mode. */
557		    cinfo->two_pass_quantize = FALSE;
558		    cinfo->dither_mode = JDITHER_ORDERED;
559		    cinfo->dct_method = JDCT_FASTEST;
560		    cinfo->do_fancy_upsampling = FALSE;
561		    break;
562		}
563		case 1: {
564		    /* Force monochrome output. */
565		    cinfo->out_color_space = JCS_GRAYSCALE;
566		    break;
567		}
568	    }
569	}
570    }
571
572    jpeg_start_decompress(cinfo);
573
574    /* Check dimensions. */
575    fileWidth = (int) cinfo->output_width;
576    fileHeight = (int) cinfo->output_height;
577    if ((srcX + width) > fileWidth) {
578	outWidth = fileWidth - srcX;
579    } else {
580	outWidth = width;
581    }
582    if ((srcY + height) > fileHeight) {
583	outHeight = fileHeight - srcY;
584    } else {
585	outHeight = height;
586    }
587    if ((outWidth <= 0) || (outHeight <= 0)
588	|| (srcX >= fileWidth) || (srcY >= fileHeight)) {
589	return TCL_OK;
590    }
591
592    /* Check colorspace. */
593    switch (cinfo->out_color_space) {
594    case JCS_GRAYSCALE:
595	/* a single-sample grayscale pixel is expanded into equal R,G,B values */
596	block.pixelSize = 1;
597	block.offset[0] = 0;
598	block.offset[1] = 0;
599	block.offset[2] = 0;
600	break;
601    case JCS_RGB:
602	/* note: this pixel layout assumes default configuration of libjpeg. */
603	block.pixelSize = 3;
604	block.offset[0] = 0;
605	block.offset[1] = 1;
606	block.offset[2] = 2;
607	break;
608    default:
609	Tcl_AppendResult(interp, "Unsupported JPEG color space", (char *) NULL);
610	return TCL_ERROR;
611    }
612    block.width = outWidth;
613    block.height = 1;
614    block.pitch = block.pixelSize * fileWidth;
615    block.offset[3] = block.offset[0];
616
617    if (tkimg_PhotoExpand(interp, imageHandle, destX + outWidth, destY + outHeight) == TCL_ERROR) {
618	jpeg_abort_decompress(cinfo);
619	return TCL_ERROR;
620    }
621
622    /* Make a temporary one-row-high sample array */
623    buffer = (*cinfo->mem->alloc_sarray)
624		((j_common_ptr) cinfo, JPOOL_IMAGE,
625		 cinfo->output_width * cinfo->output_components, 1);
626    block.pixelPtr = (unsigned char *) buffer[0] + srcX * block.pixelSize;
627
628    /* Read as much of the data as we need to */
629    stopY = srcY + outHeight;
630    outY = destY;
631    for (curY = 0; curY < stopY; curY++) {
632      jpeg_read_scanlines(cinfo, buffer, 1);
633      if (curY >= srcY) {
634	if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, outWidth, 1, TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
635	    jpeg_abort_decompress(cinfo);
636	    return TCL_ERROR;
637	}
638	outY++;
639      }
640    }
641
642    /* Do normal cleanup if we read the whole image; else early abort */
643    if (cinfo->output_scanline == cinfo->output_height)
644	jpeg_finish_decompress(cinfo);
645    else
646	jpeg_abort_decompress(cinfo);
647
648    return TCL_OK;
649}
650
651/*
652 *----------------------------------------------------------------------
653 *
654 * ChnWrite --
655 *
656 *	This procedure is invoked to write image data to a file in JPEG
657 *	format.
658 *
659 * Results:
660 *	A standard TCL completion code.  If TCL_ERROR is returned
661 *	then an error message is left in interp->result.
662 *
663 * Side effects:
664 *	Data is written to the file given by "fileName".
665 *
666 *----------------------------------------------------------------------
667 */
668
669static int
670ChnWrite(interp, fileName, format, blockPtr)
671    Tcl_Interp *interp;
672    const char *fileName;
673    Tcl_Obj *format;
674    Tk_PhotoImageBlock *blockPtr;
675{
676    struct jpeg_compress_struct cinfo; /* libjpeg's parameter structure */
677    struct my_error_mgr jerror;	/* for controlling libjpeg error handling */
678    Tcl_Channel chan;
679    int result;
680
681    chan = tkimg_OpenFileChannel(interp, fileName, 0644);
682    if (!chan) {
683	return TCL_ERROR;
684    }
685
686    /* Initialize JPEG error handler */
687    /* We set up the normal JPEG error routines, then override error_exit. */
688    cinfo.err = jpeg_std_error(&jerror.pub);
689    jerror.pub.error_exit = my_error_exit;
690    jerror.pub.output_message = my_output_message;
691
692    /* Establish the setjmp return context for my_error_exit to use. */
693    if (setjmp(jerror.setjmp_buffer)) {
694      /* If we get here, the JPEG code has signaled an error. */
695      Tcl_AppendResult(interp, "couldn't write JPEG file \"", fileName,
696		       "\": ", (char *) NULL);
697      append_jpeg_message(interp, (j_common_ptr) &cinfo);
698      jpeg_destroy_compress(&cinfo);
699      Tcl_Close(interp, chan);
700      return TCL_ERROR;
701    }
702
703    /* Now we can initialize libjpeg. */
704    jpeg_create_compress(&cinfo);
705    my_jpeg_channel_dest(&cinfo, chan);
706
707    /* Share code with StringWrite. */
708    result = CommonWrite(interp, &cinfo, format, blockPtr);
709
710    jpeg_destroy_compress(&cinfo);
711    if (Tcl_Close(interp, chan) == TCL_ERROR) {
712	return TCL_ERROR;
713    }
714    return result;
715}
716
717/*
718 *----------------------------------------------------------------------
719 *
720 * StringWrite --
721 *
722 *	This procedure is called by the photo image type to write
723 *	JPEG format data to a base-64 encoded string from the photo block.
724 *
725 * Results:
726 *	A standard TCL completion code.  If TCL_ERROR is returned
727 *	then an error message is left in interp->result.
728 *
729 * Side effects:
730 *	None.
731 *
732 *----------------------------------------------------------------------
733 */
734
735static int StringWrite(
736    Tcl_Interp *interp,
737    Tcl_Obj *format,
738    Tk_PhotoImageBlock *blockPtr
739) {
740    struct jpeg_compress_struct cinfo; /* libjpeg's parameter structure */
741    struct my_error_mgr jerror;	/* for controlling libjpeg error handling */
742    int result;
743    Tcl_DString data;
744
745    Tcl_DStringInit(&data);
746
747    /* Initialize JPEG error handler */
748    /* We set up the normal JPEG error routines, then override error_exit. */
749    cinfo.err = jpeg_std_error(&jerror.pub);
750    jerror.pub.error_exit = my_error_exit;
751    jerror.pub.output_message = my_output_message;
752
753    /* Establish the setjmp return context for my_error_exit to use. */
754    if (setjmp(jerror.setjmp_buffer)) {
755      /* If we get here, the JPEG code has signaled an error. */
756      Tcl_AppendResult(interp, "couldn't write JPEG string: ", (char *) NULL);
757      append_jpeg_message(interp, (j_common_ptr) &cinfo);
758      result = TCL_ERROR;
759      goto writeend;
760    }
761
762    /* Now we can initialize libjpeg. */
763    jpeg_create_compress(&cinfo);
764    my_jpeg_string_dest(&cinfo, &data);
765
766    /* Share code with ChnWrite. */
767    result = CommonWrite(interp, &cinfo, format, blockPtr);
768
769writeend:
770
771    jpeg_destroy_compress(&cinfo);
772    if (result == TCL_OK) {
773	Tcl_DStringResult(interp, &data);
774    } else {
775	Tcl_DStringFree(&data);
776    }
777
778    return result;
779}
780
781/*
782 *----------------------------------------------------------------------
783 *
784 * CommonWrite --
785 *
786 *	The common guts of ChnWrite and StringWrite.
787 *	The compress struct has already been set up and the
788 *	appropriate data destination manager initialized.
789 *	The caller should do jpeg_destroy_compress upon return,
790 *	and also close the destination as necessary.
791 *
792 *----------------------------------------------------------------------
793 */
794
795static int
796CommonWrite(interp, cinfo, format, blockPtr)
797    Tcl_Interp *interp;
798    j_compress_ptr cinfo;
799    Tcl_Obj *format;
800    Tk_PhotoImageBlock *blockPtr;
801{
802    static const char *const jpegWriteOptions[] = {"-grayscale", "-optimize",
803	"-progressive", "-quality", "-smooth", NULL};
804    JSAMPROW row_pointer[1];	/* pointer to original data scanlines */
805    JSAMPARRAY buffer;		/* Intermediate row buffer */
806    JSAMPROW bufferPtr;
807    int w, h;
808    int greenOffset, blueOffset, alphaOffset;
809    unsigned char *pixelPtr, *pixLinePtr;
810    int objc, i, index, grayscale = 0;
811    Tcl_Obj **objv = (Tcl_Obj **) NULL;
812
813    greenOffset = blockPtr->offset[1] - blockPtr->offset[0];
814    blueOffset = blockPtr->offset[2] - blockPtr->offset[0];
815    alphaOffset = blockPtr->offset[0];
816    if (alphaOffset < blockPtr->offset[2]) {
817        alphaOffset = blockPtr->offset[2];
818    }
819    if (++alphaOffset < blockPtr->pixelSize) {
820	alphaOffset -= blockPtr->offset[0];
821    } else {
822	alphaOffset = 0;
823    }
824
825    /* Set up JPEG compression parameters. */
826    cinfo->image_width = blockPtr->width;
827    cinfo->image_height = blockPtr->height;
828    cinfo->input_components = 3;
829    cinfo->in_color_space = JCS_RGB;
830
831    jpeg_set_defaults(cinfo);
832
833    /* Parse options, if any, and alter default parameters */
834
835    if (tkimg_ListObjGetElements(interp, format, &objc, &objv) != TCL_OK) {
836	return TCL_ERROR;
837    }
838    if (objc) {
839	for (i=1; i<objc; i++) {
840	    if (Tcl_GetIndexFromObj(interp, objv[i], (CONST84 char *CONST86 *)jpegWriteOptions,
841		    "format option", 0, &index)!=TCL_OK) {
842		return TCL_ERROR;
843	    }
844	    switch (index) {
845		case 0: {
846		    grayscale = 1;
847		    break;
848		}
849		case 1: {
850		    cinfo->optimize_coding = TRUE;
851		    break;
852		}
853		case 2: {
854		    if (jpeg_simple_progression != NULL) {
855			/* Select simple progressive mode. */
856			jpeg_simple_progression(cinfo);
857		    }
858		    break;
859		}
860		case 3: {
861		    int quality = 0;
862		    if (++i >= objc) {
863			Tcl_AppendResult(interp, "No value for option \"",
864				Tcl_GetStringFromObj(objv[--i], (int *) NULL), "\"", (char *) NULL);
865			return TCL_ERROR;
866		    }
867		    if (Tcl_GetIntFromObj(interp, objv[i], &quality) != TCL_OK) {
868			return TCL_ERROR;
869		    }
870		    jpeg_set_quality(cinfo, quality, FALSE);
871		    break;
872		}
873		case 4: {
874		    int smooth = 0;
875		    if (++i >= objc) {
876			Tcl_AppendResult(interp, "No value for option \"",
877				Tcl_GetStringFromObj(objv[--i], (int *) NULL), "\"", (char *) NULL);
878			return TCL_ERROR;
879		    }
880		    if (Tcl_GetIntFromObj(interp, objv[i], &smooth) != TCL_OK) {
881			return TCL_ERROR;
882		    }
883		    cinfo->smoothing_factor = smooth;
884		    break;
885		}
886	    }
887	}
888    }
889
890    pixLinePtr = blockPtr->pixelPtr + blockPtr->offset[0];
891    greenOffset = blockPtr->offset[1] - blockPtr->offset[0];
892    blueOffset = blockPtr->offset[2] - blockPtr->offset[0];
893    if ((jpeg_set_colorspace != NULL) &&
894	    (grayscale || (!greenOffset && !blueOffset))) {
895	/* Generate monochrome JPEG file if source block is grayscale. */
896	jpeg_set_colorspace(cinfo, JCS_GRAYSCALE);
897    }
898
899    jpeg_start_compress(cinfo, TRUE);
900
901    /* note: we assume libjpeg is configured for standard RGB pixel order. */
902    if ((greenOffset == 1) && (blueOffset == 2)
903	&& (blockPtr->pixelSize == 3)) {
904	/* No need to reformat pixels before passing data to libjpeg */
905	for (h = blockPtr->height; h > 0; h--) {
906	    row_pointer[0] = (JSAMPROW) pixLinePtr;
907	    jpeg_write_scanlines(cinfo, row_pointer, 1);
908	    pixLinePtr += blockPtr->pitch;
909	}
910    } else {
911	/* Must convert data format.  Create a one-scanline work buffer. */
912	buffer = (*cinfo->mem->alloc_sarray)
913	  ((j_common_ptr) cinfo, JPOOL_IMAGE,
914	   cinfo->image_width * cinfo->input_components, 1);
915	for (h = blockPtr->height; h > 0; h--) {
916	    pixelPtr = pixLinePtr;
917	    bufferPtr = buffer[0];
918	    for (w = blockPtr->width; w > 0; w--) {
919		if (alphaOffset && !pixelPtr[alphaOffset]) {
920		    /* if pixel is transparant, better use gray
921		     * than the default black.
922		     */
923		    *bufferPtr++ = 0xd9;
924		    *bufferPtr++ = 0xd9;
925		    *bufferPtr++ = 0xd9;
926		} else {
927		    *bufferPtr++ = pixelPtr[0];
928		    *bufferPtr++ = pixelPtr[greenOffset];
929		    *bufferPtr++ = pixelPtr[blueOffset];
930		}
931		pixelPtr += blockPtr->pixelSize;
932	    }
933	    jpeg_write_scanlines(cinfo, buffer, 1);
934	    pixLinePtr += blockPtr->pitch;
935	}
936    }
937
938    jpeg_finish_compress(cinfo);
939    return TCL_OK;
940}
941
942/*
943 * libjpeg source manager for reading from base64-encoded strings
944 * and from Tcl_Channels.
945 */
946
947static void
948my_jpeg_obj_src (cinfo, dataObj)
949    j_decompress_ptr cinfo;
950    Tcl_Obj *dataObj;
951{
952  src_ptr src;
953
954  src = (src_ptr)
955      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
956				  sizeof(struct source_mgr));
957  cinfo->src = (struct jpeg_source_mgr *) src;
958
959  src->pub.init_source = dummy_source;
960  src->pub.fill_input_buffer = fill_input_buffer;
961  src->pub.skip_input_data = skip_input_data;
962  src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
963  src->pub.term_source = dummy_source;
964
965  tkimg_ReadInit(dataObj, '\377', &src->handle);
966
967  src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
968  src->pub.next_input_byte = NULL; /* until buffer loaded */
969}
970
971static boolean
972fill_input_buffer(cinfo)
973    j_decompress_ptr cinfo;
974{
975  src_ptr src = (src_ptr) cinfo->src;
976  int nbytes;
977
978  nbytes = tkimg_Read(&src->handle, (char *) src->buffer, STRING_BUF_SIZE);
979
980  if (nbytes <= 0) {
981    /* Insert a fake EOI marker */
982    src->buffer[0] = (JOCTET) 0xFF;
983    src->buffer[1] = (JOCTET) JPEG_EOI;
984    nbytes = 2;
985  }
986
987  src->pub.next_input_byte = src->buffer;
988  src->pub.bytes_in_buffer = nbytes;
989
990  return TRUE;
991}
992
993static void
994skip_input_data(cinfo, num_bytes)
995    j_decompress_ptr cinfo;
996    long num_bytes;
997{
998  src_ptr src = (src_ptr) cinfo->src;
999
1000  if (num_bytes > 0) {
1001    while (num_bytes > (long) src->pub.bytes_in_buffer) {
1002      num_bytes -= (long) src->pub.bytes_in_buffer;
1003      fill_input_buffer(cinfo);
1004    }
1005    src->pub.next_input_byte += (size_t) num_bytes;
1006    src->pub.bytes_in_buffer -= (size_t) num_bytes;
1007  }
1008}
1009
1010static void
1011dummy_source(cinfo)
1012    j_decompress_ptr cinfo;
1013{
1014  /* no work necessary here */
1015}
1016
1017/*
1018 * libjpeg source manager for reading from channels.
1019 */
1020static void
1021my_jpeg_channel_src (cinfo, chan)
1022    j_decompress_ptr cinfo;
1023    Tcl_Channel chan;
1024{
1025  src_ptr src;
1026
1027  src = (src_ptr)
1028      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
1029				  sizeof(struct source_mgr));
1030  cinfo->src = (struct jpeg_source_mgr *) src;
1031
1032  src->pub.init_source = dummy_source;
1033  src->pub.fill_input_buffer = fill_input_buffer;
1034  src->pub.skip_input_data = skip_input_data;
1035  src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
1036  src->pub.term_source = dummy_source;
1037
1038  src->handle.data = (char *) chan;
1039  src->handle.state = IMG_CHAN;
1040
1041  src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
1042  src->pub.next_input_byte = NULL; /* until buffer loaded */
1043}
1044
1045
1046/*
1047 * libjpeg destination manager for writing to base64-encoded strings
1048 * and Tcl_Channel's.
1049 */
1050
1051static void
1052my_jpeg_string_dest (cinfo, dstring)
1053    j_compress_ptr cinfo;
1054    Tcl_DString* dstring;
1055{
1056  dest_ptr dest;
1057
1058  if (cinfo->dest == NULL) {	/* first time for this JPEG object? */
1059    cinfo->dest = (struct jpeg_destination_mgr *)
1060      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
1061				  sizeof(struct destination_mgr));
1062  }
1063
1064  dest = (dest_ptr) cinfo->dest;
1065  dest->pub.init_destination = my_init_destination;
1066  dest->pub.empty_output_buffer = my_empty_output_buffer;
1067  dest->pub.term_destination = my_term_destination;
1068  Tcl_DStringSetLength(dstring, dstring->spaceAvl);
1069  dest->handle.buffer = dstring;
1070  dest->handle.data = Tcl_DStringValue(dstring);
1071  dest->handle.state = 0;
1072  dest->handle.length = 0;
1073}
1074
1075static void
1076my_jpeg_channel_dest (cinfo, chan)
1077    j_compress_ptr cinfo;
1078    Tcl_Channel chan;
1079{
1080  dest_ptr dest;
1081
1082  if (cinfo->dest == NULL) {	/* first time for this JPEG object? */
1083    cinfo->dest = (struct jpeg_destination_mgr *)
1084      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
1085				  sizeof(struct destination_mgr));
1086  }
1087
1088  dest = (dest_ptr) cinfo->dest;
1089  dest->pub.init_destination = my_init_destination;
1090  dest->pub.empty_output_buffer = my_empty_output_buffer;
1091  dest->pub.term_destination = my_term_destination;
1092  dest->handle.data = (char *) chan;
1093  dest->handle.state = IMG_CHAN;
1094}
1095
1096static void
1097my_init_destination (cinfo)
1098    j_compress_ptr cinfo;
1099{
1100  dest_ptr dest = (dest_ptr) cinfo->dest;
1101  dest->pub.next_output_byte = dest->buffer;
1102  dest->pub.free_in_buffer = STRING_BUF_SIZE;
1103}
1104
1105static boolean
1106my_empty_output_buffer (cinfo)
1107    j_compress_ptr cinfo;
1108{
1109  dest_ptr dest = (dest_ptr) cinfo->dest;
1110  if (tkimg_Write(&dest->handle, (char *) dest->buffer, STRING_BUF_SIZE)
1111  	!= STRING_BUF_SIZE)
1112    ERREXIT(cinfo, JERR_FILE_WRITE);
1113
1114  dest->pub.next_output_byte = dest->buffer;
1115  dest->pub.free_in_buffer = STRING_BUF_SIZE;
1116
1117  return TRUE;
1118}
1119
1120static void
1121my_term_destination (cinfo)
1122    j_compress_ptr cinfo;
1123{
1124  dest_ptr dest = (dest_ptr) cinfo->dest;
1125  int datacount = STRING_BUF_SIZE - (int) dest->pub.free_in_buffer;
1126
1127  /* Write any data remaining in the buffer */
1128  if (datacount > 0) {
1129    if (tkimg_Write(&dest->handle, (char *) dest->buffer, datacount)
1130	!= datacount)
1131      ERREXIT(cinfo, JERR_FILE_WRITE);
1132  }
1133  /* Empty any partial-byte from the base64 encoder */
1134  tkimg_Putc(IMG_DONE, &dest->handle);
1135}
1136
1137
1138/*
1139 * Error handler to replace (or extend, really) libjpeg's default handler
1140 */
1141
1142static void
1143my_error_exit (cinfo)
1144    j_common_ptr cinfo;
1145{
1146  struct my_error_mgr *myerr = (struct my_error_mgr *) cinfo->err;
1147  /* Exit back to outer level */
1148  longjmp(myerr->setjmp_buffer, 1);
1149}
1150
1151static void
1152append_jpeg_message (interp, cinfo)
1153    Tcl_Interp *interp;
1154    j_common_ptr cinfo;
1155{
1156  /* Append libjpeg error message to interp->result */
1157  char buffer[JMSG_LENGTH_MAX];
1158  (*cinfo->err->format_message) (cinfo, buffer);
1159  Tcl_AppendResult(interp, buffer, (char *) NULL);
1160}
1161
1162static void
1163my_output_message (cinfo)
1164    j_common_ptr cinfo;
1165{
1166  /* Override libjpeg's output_message to do nothing.
1167   * This ensures that warning messages will not appear on stderr,
1168   * even for a corrupted JPEG file.  Too bad there's no way
1169   * to report a "warning" message to the calling Tcl script.
1170   */
1171}
1172