1 /*
2  * imgJPEG.c --
3  *
4  *	A photo image file handler for JPEG files.
5  *
6  * This Tk image format handler reads and writes JPEG files in the standard
7  * JFIF file format.  ("JPEG" should be the format name.)  It can also read
8  * and write strings containing base64-encoded JPEG data.
9  *
10  * Several options can be provided in the format string, for example:
11  *
12  *	imageObject read input.jpg -shrink -format "jpeg -grayscale"
13  *	imageObject write output.jpg -format "jpeg -quality 50 -progressive"
14  *
15  * The supported options for reading are:
16  *	-fast:        Fast, low-quality processing
17  *	-grayscale:   Force incoming image to grayscale
18  * The supported options for writing are:
19  *	-quality N:   Compression quality (0..100; 5-95 is useful range)
20  *	              Default value: 75
21  *	-smooth N:    Perform smoothing (10-30 is enough for most GIF's)
22  *		      Default value: 0
23  *	-grayscale:   Create monochrome JPEG file
24  *	-optimize:    Optimize Huffman table
25  *	-progressive: Create progressive JPEG file
26  *
27  *
28  * Copyright (c) 1996-1997 Thomas G. Lane.
29  * This file is based on tkImgPPM.c from the Tk 4.2 distribution.
30  * That file is
31  *	Copyright (c) 1994 The Australian National University.
32  *	Copyright (c) 1994-1996 Sun Microsystems, Inc.
33  *
34  * See the file "license.terms" for information on usage and redistribution
35  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
36  *
37  * You will need a copy of the IJG JPEG library, version 5 or later,
38  * to use this file.  If you didn't receive it with this package, see
39  *	ftp://ftp.uu.net/graphics/jpeg/
40  *
41  * Author: Tom Lane (tgl@sss.pgh.pa.us)
42  *
43  * Modified for dynamical loading, reading from channels and Tcl_Obj's by:
44  *	Jan Nijtmans (j.nijtmans@chello.nl)
45  *
46  * SCCS: @(#) imgJPEG.c
47  */
48 
49 /* system includes */
50 #include <stdlib.h>
51 #include <string.h>
52 #include <setjmp.h>
53 
54 
55 
56 /* Tk */
57 #include <pTk/imgInt.h>
58 #include <pTk/tkImgPhoto.h>
59 #include <pTk/tkVMacro.h>
60 
61 /* undef Tcl macros that conflict with libjpeg stuff (sigh) */
62 #undef EXTERN
63 
64 #if defined(WIN32) && defined(__GNUC__)
65 /* Mingw headers define INT32 unless this is set */
66 #define INT32 jpegINT32
67 #endif
68 
69 
70 /* libjpeg */
71 #ifdef MAC_TCL
72 #  include "libjpeg:jpeglib.h"
73 #  include "libjpeg:jerror.h"
74 #else
75 #  include <sys/types.h>
76 #ifdef HAVE_JPEGLIB_H
77 #  include <jpeglib.h>
78 #  include <jerror.h>
79 #else
80 #  include "jpeg/jpeglib.h"
81 #  include "jpeg/jerror.h"
82 #endif
83 #endif
84 
85 #ifdef __WIN32__
86 #define JPEG_LIB_NAME "jpeg62.dll"
87 #endif
88 
89 #ifndef JPEG_LIB_NAME
90 #define JPEG_LIB_NAME "libjpeg.so"
91 #endif
92 
93 /*
94  * The format record for the JPEG file format:
95  */
96 #if TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION == 0
97 #define OPTCONST
98 static int	ChnMatchJPEG _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Channel chan,
99 		    Tcl_Obj *fileName,
100 		    Tcl_Obj *format, int *widthPtr, int *heightPtr));
101 static int	ObjMatchJPEG _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *data,
102 		    Tcl_Obj *format, int *widthPtr, int *heightPtr));
103 #else
104 #define OPTCONST const
105 static int	ChnMatchJPEG _ANSI_ARGS_((Tcl_Channel chan,  Tcl_Obj *fileName,
106 		    Tcl_Obj *format, int *widthPtr, int *heightPtr, Tcl_Interp *interp));
107 static int	ObjMatchJPEG _ANSI_ARGS_((Tcl_Obj *data,  Tcl_Obj *format,
108 		    int *widthPtr, int *heightPtr, Tcl_Interp *interp));
109 #endif
110 static int	ChnReadJPEG _ANSI_ARGS_((Tcl_Interp *interp,
111 		    Tcl_Channel chan, Tcl_Obj *fileName, Tcl_Obj *format,
112 		    Tk_PhotoHandle imageHandle, int destX, int destY,
113 		    int width, int height, int srcX, int srcY));
114 static int	ObjReadJPEG _ANSI_ARGS_((Tcl_Interp *interp,
115 		    Tcl_Obj *data, Tcl_Obj *format,
116 		    Tk_PhotoHandle imageHandle, int destX, int destY,
117 		    int width, int height, int srcX, int srcY));
118 static int	ChnWriteJPEG _ANSI_ARGS_((Tcl_Interp *interp,
119 		    char *fileName, Tcl_Obj *format,
120 		    Tk_PhotoImageBlock *blockPtr));
121 static int	StringWriteJPEG _ANSI_ARGS_((Tcl_Interp *interp,
122 		    Tcl_Obj *format,
123 		    Tk_PhotoImageBlock *blockPtr));
124 
125 Tk_PhotoImageFormat imgFmtJPEG = {
126     "jpeg",					/* name */
127     ChnMatchJPEG,	/* fileMatchProc */
128     ObjMatchJPEG,	/* stringMatchProc */
129     ChnReadJPEG,	/* fileReadProc */
130     ObjReadJPEG,	/* stringReadProc */
131     ChnWriteJPEG,	/* fileWriteProc */
132     StringWriteJPEG,    /* stringWriteProc */
133 };
134 
135 /*
136  * Declarations for libjpeg source and destination managers to handle
137  * reading and writing base64-encoded strings and Tcl_Channel's.
138  */
139 
140 #define STRING_BUF_SIZE  4096	/* choose any convenient size */
141 
142 typedef struct source_mgr {	/* Source manager for reading from string */
143   struct jpeg_source_mgr pub;	/* public fields */
144 
145   MFile handle;			/* base64 stream */
146   JOCTET buffer[STRING_BUF_SIZE]; /* buffer for a chunk of decoded data */
147 } *src_ptr;
148 
149 typedef struct destination_mgr { /* Manager for string output */
150   struct jpeg_destination_mgr pub; /* public fields */
151 
152   MFile handle;			/* base64 stream */
153   JOCTET buffer[STRING_BUF_SIZE]; /* buffer for a chunk of decoded data */
154 } *dest_ptr;
155 
156 /*
157  * Other declarations
158  */
159 
160 struct my_error_mgr {		/* Extended libjpeg error manager */
161   struct jpeg_error_mgr pub;	/* public fields */
162   jmp_buf setjmp_buffer;	/* for return to caller from error exit */
163 };
164 
165 /*
166  * Prototypes for local procedures defined in this file:
167  */
168 
169 static int	CommonMatchJPEG _ANSI_ARGS_((MFile *handle,
170 		    int *widthPtr, int *heightPtr));
171 static int	CommonReadJPEG _ANSI_ARGS_((Tcl_Interp *interp,
172 		    j_decompress_ptr cinfo, Tcl_Obj *format,
173 		    Tk_PhotoHandle imageHandle, int destX, int destY,
174 		    int width, int height, int srcX, int srcY));
175 static int	CommonWriteJPEG _ANSI_ARGS_((Tcl_Interp *interp,
176 		    j_compress_ptr cinfo, Tcl_Obj *format,
177 		    Tk_PhotoImageBlock *blockPtr));
178 static void	jpeg_obj_src _ANSI_ARGS_((j_decompress_ptr, Tcl_Obj *));
179 static void	jpeg_channel_src _ANSI_ARGS_((j_decompress_ptr, Tcl_Channel));
180 static boolean	fill_input_buffer _ANSI_ARGS_((j_decompress_ptr));
181 static void	skip_input_data _ANSI_ARGS_((j_decompress_ptr, long));
182 static void	dummy_source _ANSI_ARGS_((j_decompress_ptr));
183 static void	jpeg_string_dest _ANSI_ARGS_((j_compress_ptr, Tcl_DString*));
184 static void	jpeg_channel_dest _ANSI_ARGS_((j_compress_ptr, Tcl_Channel));
185 static void	my_init_destination _ANSI_ARGS_((j_compress_ptr));
186 static boolean	my_empty_output_buffer _ANSI_ARGS_((j_compress_ptr));
187 static void	my_term_destination _ANSI_ARGS_((j_compress_ptr));
188 static void	my_error_exit _ANSI_ARGS_((j_common_ptr cinfo));
189 static void	my_output_message _ANSI_ARGS_((j_common_ptr cinfo));
190 static void	append_jpeg_message _ANSI_ARGS_((Tcl_Interp *interp,
191 		    j_common_ptr cinfo));
192 static int	load_jpeg_library _ANSI_ARGS_((Tcl_Interp *interp));
193 static int	CreateCompress _ANSI_ARGS_((j_compress_ptr, int, size_t));
194 static int	CreateDecompress _ANSI_ARGS_((j_decompress_ptr, int, size_t));
195 
196 /*
197  * Stuff to support dynamic loading of libjpeg
198  */
199 #ifndef _LANG
200 
201 static struct JpegFunctions {
202     VOID *handle;
203     int (* abort_decompress) _ANSI_ARGS_((j_decompress_ptr));
204     int (* destroy_compress) _ANSI_ARGS_((j_compress_ptr));
205     int (* destroy_decompress) _ANSI_ARGS_((j_decompress_ptr));
206     int (* finish_compress) _ANSI_ARGS_((j_compress_ptr));
207     int (* finish_decompress) _ANSI_ARGS_((j_decompress_ptr));
208     int (* read_header) _ANSI_ARGS_((j_decompress_ptr, int));
209     JDIMENSION (* read_scanlines) _ANSI_ARGS_((j_decompress_ptr,
210 			JSAMPARRAY, JDIMENSION));
211     boolean (* resync_to_restart) _ANSI_ARGS_((j_decompress_ptr, int));
212     int (* set_defaults) _ANSI_ARGS_((j_compress_ptr));
213     int (* start_compress) _ANSI_ARGS_((j_compress_ptr, int));
214     int (* start_decompress) _ANSI_ARGS_((j_decompress_ptr));
215     struct jpeg_error_mgr *(* std_error) _ANSI_ARGS_((struct jpeg_error_mgr *));
216     JDIMENSION (* write_scanlines) _ANSI_ARGS_((j_compress_ptr,
217 			JSAMPARRAY, JDIMENSION));
218     int (* set_colorspace) _ANSI_ARGS_((j_compress_ptr, J_COLOR_SPACE));
219     int (* set_quality) _ANSI_ARGS_((j_compress_ptr, int, int));
220     int (* simple_progression) _ANSI_ARGS_((j_compress_ptr));
221     int (* CreateCompress) _ANSI_ARGS_((j_compress_ptr, int, size_t));
222     int (* CreateDecompress) _ANSI_ARGS_((j_decompress_ptr, int, size_t));
223     int (* create_compress) _ANSI_ARGS_((j_compress_ptr));
224     int (* create_decompress) _ANSI_ARGS_((j_decompress_ptr));
225     void (* destroy) _ANSI_ARGS_((j_common_ptr));
226     JDIMENSION (* write_raw_data) _ANSI_ARGS_((j_compress_ptr,
227 	    JSAMPIMAGE, JDIMENSION));
228     void (* suppress_tables) _ANSI_ARGS_((j_compress_ptr, boolean));
229     void (* write_tables) _ANSI_ARGS_((j_compress_ptr));
230     JDIMENSION (* read_raw_data) _ANSI_ARGS_((j_decompress_ptr,
231 	    JSAMPIMAGE, JDIMENSION));
232     void (* abort) _ANSI_ARGS_((j_common_ptr));
233 } jpeg = {0};
234 
235 static char *symbols[] = {
236 #ifdef NEED_SHORT_EXTERNAL_NAMES
237     "jAbrtDecompress",
238     "jDestCompress",
239     "jDestDecompress",
240     "jFinCompress",
241     "jFinDecompress",
242     "jReadHeader",
243     "jReadScanlines",
244     "jResyncRestart",
245     "jSetDefaults",
246     "jStrtCompress",
247     "jStrtDecompress",
248     "jStdError",
249     "jWrtScanlines",
250     /*	the following 3 symbols are not crucial. They implement
251 	resp. the "-grayscale", "-quality" and "-progressive"
252 	options. If any of the symbols are missing from the
253 	library, the corresponding option just has no effect. */
254     "jSetColorspace",
255     "jSetQuality",
256     "jSimProgress",
257     "jCreaCompress",
258     "jCreaDecompress",
259     "jCreaCompress",
260     "jCreaDecompress",
261     "jDestroy",
262     "jWrtRawData",
263     "jSuppressTables",
264     "jWrtTables",
265     "jReadRawData",
266     "jAbort",
267 #else
268     "jpeg_abort_decompress",
269     "jpeg_destroy_compress",
270     "jpeg_destroy_decompress",
271     "jpeg_finish_compress",
272     "jpeg_finish_decompress",
273     "jpeg_read_header",
274     "jpeg_read_scanlines",
275     "jpeg_resync_to_restart",
276     "jpeg_set_defaults",
277     "jpeg_start_compress",
278     "jpeg_start_decompress",
279     "jpeg_std_error",
280     "jpeg_write_scanlines",
281     /*	the following 3 symbols are not crucial. They implement
282 	resp. the "-grayscale", "-quality" and "-progressive"
283 	options. If any of the symbols are missing from the
284 	library, the corresponding option just has no effect. */
285     "jpeg_set_colorspace",
286     "jpeg_set_quality",
287     "jpeg_simple_progression",
288     /* In later versions, jpeg_create_compress and jpeg_create_decompress
289        are macros pointing to jpeg_CreateCompress and jpeg_CreateDecompres.
290        Which one is found depends on the version. */
291     "jpeg_CreateCompress",
292     "jpeg_CreateDecompress",
293     "jpeg_create_compress",
294     "jpeg_create_decompress",
295     "jpeg_destroy",
296     "jpeg_write_raw_data",
297     "jpeg_suppress_tables",
298     "jpeg_write_tables",
299     "jpeg_read_raw_data",
300     "jpeg_abort",
301 #endif
302     (char *) NULL
303 };
304 
305 
306 
307 static int
load_jpeg_library(interp)308 load_jpeg_library(interp)
309     Tcl_Interp *interp;
310 {
311     struct jpeg_compress_struct *cinfo; /* libjpeg's parameter structure */
312     struct my_error_mgr jerror;	/* for controlling libjpeg error handling */
313     int i;
314 
315     if (ImgLoadLib(interp, JPEG_LIB_NAME, &jpeg_handle, symbols, 13)
316 	    != TCL_OK) {
317 	return TCL_ERROR;
318     }
319     if (jpeg_CreateCompress == NULL) {
320 	if (jpeg_create_compress == NULL) {
321 	    goto load_failed;
322 	}
323 	jpeg_CreateCompress = CreateCompress;
324     }
325     if (jpeg_CreateDecompress == NULL) {
326 	if (jpeg_create_decompress == NULL) {
327 	    goto load_failed;
328 	}
329 	jpeg_CreateDecompress = CreateDecompress;
330     }
331 
332     /* The followin code tries to determine if the JPEG library is
333        valid at all. The library might be configured differently,
334        which will produce core dumps. Also it might be that
335        fields appear in different places in jpeg_compress_struct
336        or jpeg_decompress_struct. This will make the library totally
337        unusable. In stead of a core-dump, we better have a proper
338        error message */
339 
340     /* overallocat size, so we don't get a core-dump if the library
341        thinks that the structure is much larger */
342     cinfo = (struct jpeg_compress_struct *)
343 	    ckalloc(8*sizeof(struct jpeg_compress_struct));
344     cinfo->err = jpeg_std_error(&jerror.pub);
345     jerror.pub.error_exit = my_error_exit;
346     jerror.pub.output_message = my_output_message;
347     /* Establish the setjmp return context for my_error_exit to use. */
348     if (setjmp(jerror.setjmp_buffer)) {
349       /* If we get here, the JPEG library is invalid. */
350       jpeg_destroy_compress(cinfo);
351       ckfree((char *)cinfo);
352 load_failed:
353       if (interp) {
354 	Tcl_AppendResult(interp, "couldn't load \"", JPEG_LIB_NAME,
355 		"\": please upgrade to at least version 6a", (char *) NULL);
356       }
357       ImgLoadFailed(&jpeg_handle);
358       return TCL_ERROR;
359     }
360 
361     /* Now we can initialize libjpeg. */
362     ((char *) cinfo)[sizeof(struct jpeg_compress_struct)] = 53;
363     jpeg_CreateCompress(cinfo, JPEG_LIB_VERSION,
364 			(size_t) sizeof(struct jpeg_compress_struct));
365     if (((char *) cinfo)[sizeof(struct jpeg_compress_struct)] != 53) {
366 	/* Oops. The library changed this value, which is outside the
367 	 * structure. Definitely, the library is invalid!!!! */
368 	ERREXIT(cinfo, JMSG_NOMESSAGE);
369     }
370 
371     /* Set up JPEG compression parameters. */
372     cinfo->image_width = 16;
373     cinfo->image_height = 16;
374     cinfo->input_components = 3;
375     cinfo->in_color_space = JCS_RGB;
376     cinfo->data_precision = -1;
377     cinfo->optimize_coding = TRUE;
378     cinfo->dct_method = -1;
379     cinfo->X_density = 0;
380     cinfo->Y_density = 0;
381     jpeg_set_defaults(cinfo);
382 
383     if ((cinfo->data_precision != BITS_IN_JSAMPLE) ||
384 	    (cinfo->optimize_coding != FALSE) ||
385 	    (cinfo->dct_method != JDCT_DEFAULT) ||
386 	    (cinfo->X_density != 1) ||
387 	    (cinfo->Y_density != 1)) {
388 	ERREXIT(cinfo, JMSG_NOMESSAGE);
389     }
390     for (i = 0; i < NUM_ARITH_TBLS; i++) {
391 	if ((cinfo->arith_dc_L[i] != 0) ||
392 		(cinfo->arith_dc_U[i] != 1) ||
393 		(cinfo->arith_ac_K[i] != 5)) {
394 	    ERREXIT(cinfo, JMSG_NOMESSAGE);
395 	}
396     }
397     jpeg_destroy_compress(cinfo);
398     ckfree((char *) cinfo);
399     return TCL_OK;
400 }
401 
402 #else
403 
404 static int
load_jpeg_library(interp)405 load_jpeg_library(interp)
406     Tcl_Interp *interp;
407 {
408  return TCL_OK;
409 }
410 
411 #endif
412 
413 int
ImgLoadJpegLibrary()414 ImgLoadJpegLibrary()
415 {
416     if ((load_jpeg_library(NULL) == TCL_OK)
417 	    && jpeg_destroy
418 	    && jpeg_write_raw_data
419 	    && jpeg_suppress_tables
420 	    && jpeg_write_tables
421 	    && jpeg_read_raw_data
422 	    && jpeg_abort) {
423 	return TCL_OK;
424     }
425     return TCL_ERROR;
426 }
427 
428 boolean Imgjpeg_resync_to_restart _ANSI_ARGS_((j_decompress_ptr, int));
429 JDIMENSION Imgjpeg_read_scanlines _ANSI_ARGS_((j_decompress_ptr,
430 			JSAMPARRAY, JDIMENSION));
431 int Imgjpeg_set_colorspace _ANSI_ARGS_((j_compress_ptr, J_COLOR_SPACE));
432 int Imgjpeg_set_defaults _ANSI_ARGS_((j_compress_ptr));
433 int Imgjpeg_start_decompress _ANSI_ARGS_((j_decompress_ptr));
434 void Imgjpeg_destroy _ANSI_ARGS_((j_common_ptr));
435 struct jpeg_error_mgr * Imgjpeg_std_error _ANSI_ARGS_((struct jpeg_error_mgr *));
436 int Imgjpeg_CreateDecompress _ANSI_ARGS_((j_decompress_ptr, int, size_t));
437 JDIMENSION Imgjpeg_write_raw_data _ANSI_ARGS_((j_compress_ptr,
438 	    JSAMPIMAGE, JDIMENSION));
439 void Imgjpeg_suppress_tables _ANSI_ARGS_((j_compress_ptr, boolean));
440 void Imgjpeg_abort _ANSI_ARGS_((j_common_ptr));
441 int Imgjpeg_read_header _ANSI_ARGS_((j_decompress_ptr, int));
442 int Imgjpeg_start_compress _ANSI_ARGS_((j_compress_ptr, int));
443 void Imgjpeg_write_tables _ANSI_ARGS_((j_compress_ptr));
444 int Imgjpeg_finish_decompress _ANSI_ARGS_((j_decompress_ptr));
445 int Imgjpeg_CreateCompress _ANSI_ARGS_((j_compress_ptr, int, size_t));
446 int Imgjpeg_finish_compress _ANSI_ARGS_((j_compress_ptr));
447 JDIMENSION Imgjpeg_read_raw_data _ANSI_ARGS_((j_decompress_ptr,
448 	    JSAMPIMAGE, JDIMENSION));
449 int Imgjpeg_set_quality _ANSI_ARGS_((j_compress_ptr, int, int));
450 JDIMENSION Imgjpeg_write_scanlines _ANSI_ARGS_((j_compress_ptr,
451 			JSAMPARRAY, JDIMENSION));
452 
453 static int
CreateCompress(cinfo,version,size)454 CreateCompress(cinfo, version, size)
455     j_compress_ptr cinfo;
456     int version;
457     size_t size;
458 {
459     jpeg_create_compress(cinfo);
460     return TCL_OK;
461 }
462 
463 static int
CreateDecompress(cinfo,version,size)464 CreateDecompress(cinfo, version, size)
465     j_decompress_ptr cinfo;
466     int version;
467     size_t size;
468 {
469     jpeg_create_decompress(cinfo);
470     return TCL_OK;
471 }
472 
473 int
Imgjpeg_CreateCompress(cinfo,version,size)474 Imgjpeg_CreateCompress(cinfo, version, size)
475     j_compress_ptr cinfo;
476     int version;
477     size_t size;
478 {
479     return CreateCompress(cinfo, version, size);
480 }
481 
482 int
Imgjpeg_CreateDecompress(cinfo,version,size)483 Imgjpeg_CreateDecompress(cinfo, version, size)
484     j_decompress_ptr cinfo;
485     int version;
486     size_t size;
487 {
488     return CreateDecompress(cinfo, version, size);
489 }
490 
491 boolean
Imgjpeg_resync_to_restart(a,b)492 Imgjpeg_resync_to_restart(a,b)
493     j_decompress_ptr a;
494     int b;
495 {
496     return jpeg_resync_to_restart(a,b);
497 }
498 
499 JDIMENSION
Imgjpeg_read_scanlines(a,b,c)500 Imgjpeg_read_scanlines(a,b,c)
501     j_decompress_ptr a;
502     JSAMPARRAY b;
503     JDIMENSION c;
504 {
505     return jpeg_read_scanlines(a,b,c);
506 }
507 
508 int
Imgjpeg_set_colorspace(a,b)509 Imgjpeg_set_colorspace(a,b)
510     j_compress_ptr a;
511     J_COLOR_SPACE b;
512 {
513     jpeg_set_colorspace(a,b);
514     return TCL_OK;
515 }
516 
517 int
Imgjpeg_set_defaults(a)518 Imgjpeg_set_defaults(a)
519     j_compress_ptr a;
520 {
521     jpeg_set_defaults(a);
522     return TCL_OK;
523 }
524 
525 int
Imgjpeg_start_decompress(a)526 Imgjpeg_start_decompress(a)
527     j_decompress_ptr a;
528 {
529     return jpeg_start_decompress(a);
530 }
531 
532 void
Imgjpeg_destroy(a)533 Imgjpeg_destroy(a)
534     j_common_ptr a;
535 {
536     jpeg_destroy(a);
537 }
538 
539 struct jpeg_error_mgr *
Imgjpeg_std_error(a)540 Imgjpeg_std_error(a)
541     struct jpeg_error_mgr *a;
542 {
543     return jpeg_std_error(a);
544 }
545 
Imgjpeg_write_raw_data(a,b,c)546 JDIMENSION Imgjpeg_write_raw_data(a,b,c)
547     j_compress_ptr a;
548     JSAMPIMAGE b;
549     JDIMENSION c;
550 {
551     return jpeg_write_raw_data(a,b,c);
552 }
553 
554 void
Imgjpeg_suppress_tables(a,b)555 Imgjpeg_suppress_tables(a,b)
556     j_compress_ptr a;
557     boolean b;
558 {
559     jpeg_suppress_tables(a,b);
560 }
561 
562 void
Imgjpeg_abort(a)563 Imgjpeg_abort(a)
564     j_common_ptr a;
565 {
566     jpeg_abort(a);
567 }
568 
569 int
Imgjpeg_read_header(a,b)570 Imgjpeg_read_header(a,b)
571     j_decompress_ptr a;
572     int b;
573 {
574     return jpeg_read_header(a,b);
575 }
576 
577 int
Imgjpeg_start_compress(a,b)578 Imgjpeg_start_compress(a,b)
579     j_compress_ptr a;
580     int b;
581 {
582     jpeg_start_compress(a,b);
583     return TCL_OK;
584 }
585 
586 void
Imgjpeg_write_tables(a)587 Imgjpeg_write_tables(a)
588     j_compress_ptr a;
589 {
590     jpeg_write_tables(a);
591 }
592 
593 int
Imgjpeg_finish_decompress(a)594 Imgjpeg_finish_decompress(a)
595     j_decompress_ptr a;
596 {
597     return jpeg_finish_decompress(a);
598 }
599 
600 int
Imgjpeg_finish_compress(a)601 Imgjpeg_finish_compress(a)
602     j_compress_ptr a;
603 {
604     jpeg_finish_compress(a);
605     return TCL_OK;
606 }
607 
608 JDIMENSION
Imgjpeg_read_raw_data(a,b,c)609 Imgjpeg_read_raw_data(a,b,c)
610     j_decompress_ptr a;
611     JSAMPIMAGE b;
612     JDIMENSION c;
613 {
614     return jpeg_read_raw_data(a,b,c);
615 }
616 
617 int
Imgjpeg_set_quality(a,b,c)618 Imgjpeg_set_quality(a,b,c)
619     j_compress_ptr a;
620     int b;
621     int c;
622 {
623     jpeg_set_quality(a,b,c);
624     return TCL_OK;
625 }
626 
627 JDIMENSION
Imgjpeg_write_scanlines(a,b,c)628 Imgjpeg_write_scanlines(a,b,c)
629     j_compress_ptr a;
630     JSAMPARRAY b;
631     JDIMENSION c;
632 {
633     return jpeg_write_scanlines(a,b,c);
634 }
635 
636 
637 /*
638  *----------------------------------------------------------------------
639  *
640  * ChnMatchJPEG --
641  *
642  *	This procedure is invoked by the photo image type to see if
643  *	a channel contains image data in JPEG format.
644  *
645  * Results:
646  *	The return value is >0 if the first characters in channel "chan"
647  *	look like JPEG data, and 0 otherwise.  For a valid file, the
648  *	image dimensions are determined.
649  *
650  * Side effects:
651  *	The access position in f may change.
652  *
653  *----------------------------------------------------------------------
654  */
655 
656 #if TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION == 0
657 static int
ChnMatchJPEG(interp,chan,fileName,format,widthPtr,heightPtr)658 ChnMatchJPEG(interp, chan, fileName, format, widthPtr, heightPtr)
659 #else
660 static int
661 ChnMatchJPEG(chan, fileName, format, widthPtr, heightPtr, interp)
662 #endif
663     Tcl_Interp *interp;
664     Tcl_Channel chan;		/* The image channel, open for reading. */
665     Tcl_Obj *fileName;		/* The name of the image file. */
666     Tcl_Obj *format;		/* User-specified format string, or NULL. */
667     int *widthPtr, *heightPtr;	/* The dimensions of the image are
668 				 * returned here if the file is a valid
669 				 * JPEG file. */
670 {
671     MFile handle;
672 
673     ImgFixChanMatchProc(&interp, &chan, &fileName, &format, &widthPtr, &heightPtr);
674 
675     handle.data = (char *) chan;
676     handle.state = IMG_CHAN;
677     return CommonMatchJPEG(&handle, widthPtr, heightPtr);
678 }
679 
680 /*
681  *----------------------------------------------------------------------
682  *
683  * ObjMatchJPEG --
684  *
685  *	This procedure is invoked by the photo image type to see if
686  *	a string contains image data in JPEG format.
687  *
688  * Results:
689  *	The return value is >0 if the first characters in the string look
690  *	like JPEG data, and 0 otherwise.  For a valid image, the image
691  *	dimensions are determined.
692  *
693  * Side effects:
694  *  the size of the image is placed in widthPtr and heightPtr.
695  *
696  *----------------------------------------------------------------------
697  */
698 #if TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION == 0
699 static int
ObjMatchJPEG(interp,data,format,widthPtr,heightPtr)700 ObjMatchJPEG(interp, data, format, widthPtr, heightPtr)
701 #else
702 static int
703 ObjMatchJPEG(data, format, widthPtr, heightPtr, interp)
704 #endif
705     Tcl_Interp *interp;
706     Tcl_Obj *data;		/* the object containing the image data */
707     Tcl_Obj *format;		/* User-specified format object, or NULL. */
708     int *widthPtr, *heightPtr;	/* The dimensions of the image are
709 				 * returned here if the string is a valid
710 				 * JPEG image. */
711 {
712     MFile handle;
713 
714     ImgFixObjMatchProc(&interp, &data, &format, &widthPtr, &heightPtr);
715 
716     ImgReadInit(data, '\377', &handle);
717     return CommonMatchJPEG(&handle, widthPtr, heightPtr);
718 }
719 
720 /*
721  *----------------------------------------------------------------------
722  *
723  * CommonMatchJPEG --
724  *
725  *	This procedure is invoked by the photo image type to see if
726  *	a string contains image data in JPEG format.
727  *
728  * Results:
729  *	The return value is >0 if the first characters in the string look
730  *	like JPEG data, and 0 otherwise.  For a valid image, the image
731  *	dimensions are determined.
732  *
733  * Side effects:
734  *  the size of the image is placed in widthPtr and heightPtr.
735  *
736  *----------------------------------------------------------------------
737  */
738 
739 static int
CommonMatchJPEG(handle,widthPtr,heightPtr)740 CommonMatchJPEG(handle, widthPtr, heightPtr)
741     MFile *handle;		/* the "file" handle */
742     int *widthPtr, *heightPtr;	/* The dimensions of the image are
743 				 * returned here if the string is a valid
744 				 * JPEG image. */
745 {
746     char buf[256];
747     int i;
748 
749     i = ImgRead(handle, buf, 3);
750     if ((i != 3)||strncmp(buf,"\377\330\377", 3)) {
751 	return 0;
752     }
753 
754     buf[0] = buf[2];
755     /* at top of loop: have just read first FF of a marker into buf[0] */
756     for (;;) {
757 	/* get marker type byte, skipping any padding FFs */
758 	while (buf[0] == (char) 0xff) {
759 	    if (ImgRead(handle, buf,1) != 1) {
760 		return 0;
761 	    }
762 	}
763 	/* look for SOF0, SOF1, or SOF2, which are the only JPEG variants
764 	 * currently accepted by libjpeg.
765 	 */
766 	if (buf[0] == (char) 0xc0 || buf[0] == (char) 0xc1
767 		|| buf[0] == (char) 0xc2)
768 	    break;
769 	/* nope, skip the marker parameters */
770 	if (ImgRead(handle, buf, 2) != 2) {
771 	    return 0;
772 	}
773 	i = ((buf[0] & 0x0ff)<<8) + (buf[1] & 0x0ff) - 1;
774 	while (i>256) {
775 	    ImgRead(handle, buf, 256);
776 	    i -= 256;
777 	}
778 	if ((i<1) || (ImgRead(handle, buf, i)) != i) {
779 	    return 0;
780 	}
781 	buf[0] = buf[i-1];
782 	/* skip any inter-marker junk (there shouldn't be any, really) */
783 	while (buf[0] != (char) 0xff) {
784 	    if (ImgRead(handle, buf,1) != 1) {
785 		return 0;
786 	    }
787 	}
788     }
789     /* Found the SOFn marker, get image dimensions */
790     if (ImgRead(handle, buf, 7) != 7) {
791 	return 0;
792     }
793     *heightPtr = ((buf[3] & 0x0ff)<<8) + (buf[4] & 0x0ff);
794     *widthPtr = ((buf[5] & 0x0ff)<<8) + (buf[6] & 0x0ff);
795 
796     return 1;
797 }
798 
799 /*
800  *----------------------------------------------------------------------
801  *
802  * ChnReadJPEG --
803  *
804  *	This procedure is called by the photo image type to read
805  *	JPEG format data from a channel, and give it to
806  *	the photo image.
807  *
808  * Results:
809  *	A standard TCL completion code.  If TCL_ERROR is returned
810  *	then an error message is left in interp->result.
811  *
812  * Side effects:
813  *	New data is added to the image given by imageHandle.
814  *
815  *----------------------------------------------------------------------
816  */
817 
818 static int
ChnReadJPEG(interp,chan,fileName,format,imageHandle,destX,destY,width,height,srcX,srcY)819 ChnReadJPEG(interp, chan, fileName, format, imageHandle, destX, destY,
820 	width, height, srcX, srcY)
821     Tcl_Interp *interp;		/* Interpreter to use for reporting errors. */
822     Tcl_Channel chan;		/* The image channel, open for reading. */
823     Tcl_Obj *fileName;		/* The name of the image file. */
824     Tcl_Obj *format;		/* User-specified format string, or NULL. */
825     Tk_PhotoHandle imageHandle;	/* The photo image to write into. */
826     int destX, destY;		/* Coordinates of top-left pixel in
827 				 * photo image to be written to. */
828     int width, height;		/* Dimensions of block of photo image to
829 				 * be written to. */
830     int srcX, srcY;		/* Coordinates of top-left pixel to be used
831 				 * in image being read. */
832 {
833     struct jpeg_decompress_struct cinfo; /* libjpeg's parameter structure */
834     struct my_error_mgr jerror;	/* for controlling libjpeg error handling */
835     int result;
836 
837     if (load_jpeg_library(interp) != TCL_OK) {
838 	return TCL_ERROR;
839     }
840 
841     /* Initialize JPEG error handler */
842     /* We set up the normal JPEG error routines, then override error_exit. */
843     cinfo.err = jpeg_std_error(&jerror.pub);
844     jerror.pub.error_exit = my_error_exit;
845     jerror.pub.output_message = my_output_message;
846 
847     /* Establish the setjmp return context for my_error_exit to use. */
848     if (setjmp(jerror.setjmp_buffer)) {
849       /* If we get here, the JPEG code has signaled an error. */
850       Tcl_AppendResult(interp, "couldn't read JPEG string: ", (char *) NULL);
851       append_jpeg_message(interp, (j_common_ptr) &cinfo);
852       jpeg_destroy_decompress(&cinfo);
853       return TCL_ERROR;
854     }
855 
856     /* Now we can initialize libjpeg. */
857     CreateDecompress(&cinfo, JPEG_LIB_VERSION,
858 			(size_t) sizeof(struct jpeg_decompress_struct));
859     jpeg_channel_src(&cinfo, chan);
860 
861     /* Share code with ObjReadJPEG. */
862     result = CommonReadJPEG(interp, &cinfo, format, imageHandle,
863 			    destX, destY, width, height, srcX, srcY);
864 
865     /* Reclaim libjpeg's internal resources. */
866     jpeg_destroy_decompress(&cinfo);
867 
868     return result;
869 }
870 
871 /*
872  *----------------------------------------------------------------------
873  *
874  * ObjReadJPEG --
875  *
876  *	This procedure is called by the photo image type to read
877  *	JPEG format data from a base64 encoded string, and give it to
878  *	the photo image.
879  *
880  * Results:
881  *	A standard TCL completion code.  If TCL_ERROR is returned
882  *	then an error message is left in interp->result.
883  *
884  * Side effects:
885  *	New data is added to the image given by imageHandle.
886  *
887  *----------------------------------------------------------------------
888  */
889 
890 static int
ObjReadJPEG(interp,data,format,imageHandle,destX,destY,width,height,srcX,srcY)891 ObjReadJPEG(interp, data, format, imageHandle, destX, destY,
892 	width, height, srcX, srcY)
893     Tcl_Interp *interp;		/* Interpreter to use for reporting errors. */
894     Tcl_Obj *data;		/* Object containing the image data. */
895     Tcl_Obj *format;		/* User-specified format object, or NULL. */
896     Tk_PhotoHandle imageHandle;	/* The photo image to write into. */
897     int destX, destY;		/* Coordinates of top-left pixel in
898 				 * photo image to be written to. */
899     int width, height;		/* Dimensions of block of photo image to
900 				 * be written to. */
901     int srcX, srcY;		/* Coordinates of top-left pixel to be used
902 				 * in image being read. */
903 {
904     struct jpeg_decompress_struct cinfo; /* libjpeg's parameter structure */
905     struct my_error_mgr jerror;	/* for controlling libjpeg error handling */
906     int result;
907 
908     if (load_jpeg_library(interp) != TCL_OK) {
909 	return TCL_ERROR;
910     }
911 
912     /* Initialize JPEG error handler */
913     /* We set up the normal JPEG error routines, then override error_exit. */
914     cinfo.err = jpeg_std_error(&jerror.pub);
915     jerror.pub.error_exit = my_error_exit;
916     jerror.pub.output_message = my_output_message;
917 
918     /* Establish the setjmp return context for my_error_exit to use. */
919     if (setjmp(jerror.setjmp_buffer)) {
920       /* If we get here, the JPEG code has signaled an error. */
921       Tcl_AppendResult(interp, "couldn't read JPEG string: ", (char *) NULL);
922       append_jpeg_message(interp, (j_common_ptr) &cinfo);
923       jpeg_destroy_decompress(&cinfo);
924       return TCL_ERROR;
925     }
926 
927     /* Now we can initialize libjpeg. */
928     CreateDecompress(&cinfo, JPEG_LIB_VERSION,
929 			(size_t) sizeof(struct jpeg_decompress_struct));
930     jpeg_obj_src(&cinfo, data);
931 
932     /* Share code with ChnReadJPEG. */
933     result = CommonReadJPEG(interp, &cinfo, format, imageHandle,
934 			    destX, destY, width, height, srcX, srcY);
935 
936     /* Reclaim libjpeg's internal resources. */
937     jpeg_destroy_decompress(&cinfo);
938 
939     return result;
940 }
941 
942 /*
943  *----------------------------------------------------------------------
944  *
945  * CommonReadJPEG --
946  *
947  *	The common guts of ChnReadJPEG and ObjReadJPEG.
948  *	The decompress struct has already been set up and the
949  *	appropriate data source manager initialized.
950  *	The caller should do jpeg_destroy_decompress upon return.
951  *
952  *----------------------------------------------------------------------
953  */
954 
955 typedef struct myblock {
956     Tk_PhotoImageBlock ck;
957     int dummy; /* extra space for offset[3], if not included already
958 		  in Tk_PhotoImageBlock */
959 } myblock;
960 
961 static int
CommonReadJPEG(interp,cinfo,format,imageHandle,destX,destY,width,height,srcX,srcY)962 CommonReadJPEG(interp, cinfo, format, imageHandle, destX, destY,
963 	width, height, srcX, srcY)
964     Tcl_Interp *interp;		/* Interpreter to use for reporting errors. */
965     j_decompress_ptr cinfo;	/* Already-constructed decompress struct. */
966     Tcl_Obj *format;		/* User-specified format string, or NULL. */
967     Tk_PhotoHandle imageHandle;	/* The photo image to write into. */
968     int destX, destY;		/* Coordinates of top-left pixel in
969 				 * photo image to be written to. */
970     int width, height;		/* Dimensions of block of photo image to
971 				 * be written to. */
972     int srcX, srcY;		/* Coordinates of top-left pixel to be used
973 				 * in image being read. */
974 {
975     static OPTCONST char *jpegReadOptions[] = {"-fast", "-grayscale", NULL};
976     int fileWidth, fileHeight, stopY, curY, outY, outWidth, outHeight;
977     myblock bl;
978 #define block bl.ck
979     JSAMPARRAY buffer;		/* Output row buffer */
980     int objc, i, index;
981     Tcl_Obj **objv = (Tcl_Obj **) NULL;
982 
983     /* Ready to read header data. */
984     jpeg_read_header(cinfo, TRUE);
985 
986     /* This code only supports 8-bit-precision JPEG files. */
987     if ((cinfo->data_precision != 8) ||
988 	    (sizeof(JSAMPLE) != sizeof(unsigned char))) {
989 	Tcl_AppendResult(interp, "Unsupported JPEG precision", (char *) NULL);
990 	return TCL_ERROR;
991     }
992 
993     /* Process format parameters. */
994     if (ImgListObjGetElements(interp, format, &objc, &objv) != TCL_OK) {
995 	return TCL_ERROR;
996     }
997     if (objc) {
998 	for (i=1; i<objc; i++) {
999 	    if (Tcl_GetIndexFromObj(interp, objv[i], jpegReadOptions, "format option",
1000 		    0, &index)!=TCL_OK) {
1001 		return TCL_ERROR;
1002 	    }
1003 	    switch (index) {
1004 		case 0: {
1005 		    /* Select fast processing mode. */
1006 		    cinfo->two_pass_quantize = FALSE;
1007 		    cinfo->dither_mode = JDITHER_ORDERED;
1008 		    cinfo->dct_method = JDCT_FASTEST;
1009 		    cinfo->do_fancy_upsampling = FALSE;
1010 		    break;
1011 		}
1012 		case 1: {
1013 		    /* Force monochrome output. */
1014 		    cinfo->out_color_space = JCS_GRAYSCALE;
1015 		    break;
1016 		}
1017 	    }
1018 	}
1019     }
1020 
1021     jpeg_start_decompress(cinfo);
1022 
1023     /* Check dimensions. */
1024     fileWidth = (int) cinfo->output_width;
1025     fileHeight = (int) cinfo->output_height;
1026     if ((srcX + width) > fileWidth) {
1027 	outWidth = fileWidth - srcX;
1028     } else {
1029 	outWidth = width;
1030     }
1031     if ((srcY + height) > fileHeight) {
1032 	outHeight = fileHeight - srcY;
1033     } else {
1034 	outHeight = height;
1035     }
1036     if ((outWidth <= 0) || (outHeight <= 0)
1037 	|| (srcX >= fileWidth) || (srcY >= fileHeight)) {
1038 	return TCL_OK;
1039     }
1040 
1041     /* Check colorspace. */
1042     switch (cinfo->out_color_space) {
1043     case JCS_GRAYSCALE:
1044 	/* a single-sample grayscale pixel is expanded into equal R,G,B values */
1045 	block.pixelSize = 1;
1046 	block.offset[0] = 0;
1047 	block.offset[1] = 0;
1048 	block.offset[2] = 0;
1049 	break;
1050     case JCS_RGB:
1051 	/* note: this pixel layout assumes default configuration of libjpeg. */
1052 	block.pixelSize = 3;
1053 	block.offset[0] = 0;
1054 	block.offset[1] = 1;
1055 	block.offset[2] = 2;
1056 	break;
1057     default:
1058 	Tcl_AppendResult(interp, "Unsupported JPEG color space", (char *) NULL);
1059 	return TCL_ERROR;
1060     }
1061     block.width = outWidth;
1062     block.height = 1;
1063     block.pitch = block.pixelSize * fileWidth;
1064     block.offset[3] = block.offset[0];
1065 
1066     Tk_PhotoExpand(imageHandle, destX + outWidth, destY + outHeight);
1067 
1068     /* Make a temporary one-row-high sample array */
1069     buffer = (*cinfo->mem->alloc_sarray)
1070 		((j_common_ptr) cinfo, JPOOL_IMAGE,
1071 		 cinfo->output_width * cinfo->output_components, 1);
1072     block.pixelPtr = (unsigned char *) buffer[0] + srcX * block.pixelSize;
1073 
1074     /* Read as much of the data as we need to */
1075     stopY = srcY + outHeight;
1076     outY = destY;
1077     for (curY = 0; curY < stopY; curY++) {
1078       jpeg_read_scanlines(cinfo, buffer, 1);
1079       if (curY >= srcY) {
1080 #if TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION == 0
1081 	Tk_PhotoPutBlock(imageHandle, &block, destX, outY, outWidth, 1);
1082 #else
1083         /* This is like Tk_PhotoPutZoomedBlock_NoComposite
1084            but reminds us that we could add support for compose
1085          */
1086 	Tk_PhotoPutBlock(imageHandle, &block, destX, outY, outWidth, 1,
1087           TK_PHOTO_COMPOSITE_OVERLAY);
1088 #endif
1089 	outY++;
1090       }
1091     }
1092 
1093     /* Do normal cleanup if we read the whole image; else early abort */
1094     if (cinfo->output_scanline == cinfo->output_height)
1095 	jpeg_finish_decompress(cinfo);
1096     else
1097 	jpeg_abort_decompress(cinfo);
1098 
1099     return TCL_OK;
1100 }
1101 
1102 /*
1103  *----------------------------------------------------------------------
1104  *
1105  * ChnWriteJPEG --
1106  *
1107  *	This procedure is invoked to write image data to a file in JPEG
1108  *	format.
1109  *
1110  * Results:
1111  *	A standard TCL completion code.  If TCL_ERROR is returned
1112  *	then an error message is left in interp->result.
1113  *
1114  * Side effects:
1115  *	Data is written to the file given by "fileName".
1116  *
1117  *----------------------------------------------------------------------
1118  */
1119 
1120 static int
ChnWriteJPEG(interp,fileName,format,blockPtr)1121 ChnWriteJPEG(interp, fileName, format, blockPtr)
1122     Tcl_Interp *interp;
1123     char *fileName;
1124     Tcl_Obj *format;
1125     Tk_PhotoImageBlock *blockPtr;
1126 {
1127     struct jpeg_compress_struct cinfo; /* libjpeg's parameter structure */
1128     struct my_error_mgr jerror;	/* for controlling libjpeg error handling */
1129     Tcl_Channel chan;
1130     int result;
1131 
1132     if (load_jpeg_library(interp) != TCL_OK) {
1133 	return TCL_ERROR;
1134     }
1135 
1136     chan = ImgOpenFileChannel(interp, fileName, 0644);
1137     if (!chan) {
1138 	return TCL_ERROR;
1139     }
1140 
1141     /* Initialize JPEG error handler */
1142     /* We set up the normal JPEG error routines, then override error_exit. */
1143     cinfo.err = jpeg_std_error(&jerror.pub);
1144     jerror.pub.error_exit = my_error_exit;
1145     jerror.pub.output_message = my_output_message;
1146 
1147     /* Establish the setjmp return context for my_error_exit to use. */
1148     if (setjmp(jerror.setjmp_buffer)) {
1149       /* If we get here, the JPEG code has signaled an error. */
1150       Tcl_AppendResult(interp, "couldn't write JPEG file \"", fileName,
1151 		       "\": ", (char *) NULL);
1152       append_jpeg_message(interp, (j_common_ptr) &cinfo);
1153       jpeg_destroy_compress(&cinfo);
1154       Tcl_Close(interp, chan);
1155       return TCL_ERROR;
1156     }
1157 
1158     /* Now we can initialize libjpeg. */
1159     jpeg_CreateCompress(&cinfo, JPEG_LIB_VERSION,
1160 			(size_t) sizeof(struct jpeg_compress_struct));
1161     jpeg_channel_dest(&cinfo, chan);
1162 
1163     /* Share code with StringWriteJPEG. */
1164     result = CommonWriteJPEG(interp, &cinfo, format, blockPtr);
1165 
1166     jpeg_destroy_compress(&cinfo);
1167     if (Tcl_Close(interp, chan) == TCL_ERROR) {
1168 	return TCL_ERROR;
1169     }
1170     return result;
1171 }
1172 
1173 /*
1174  *----------------------------------------------------------------------
1175  *
1176  * StringWriteJPEG --
1177  *
1178  *	This procedure is called by the photo image type to write
1179  *	JPEG format data to a base-64 encoded string from the photo block.
1180  *
1181  * Results:
1182  *	A standard TCL completion code.  If TCL_ERROR is returned
1183  *	then an error message is left in interp->result.
1184  *
1185  * Side effects:
1186  *	None.
1187  *
1188  *----------------------------------------------------------------------
1189  */
1190 
1191 static int
StringWriteJPEG(interp,format,blockPtr)1192 StringWriteJPEG(interp, format, blockPtr)
1193     Tcl_Interp *interp;
1194     Tcl_Obj *format;
1195     Tk_PhotoImageBlock *blockPtr;
1196 {
1197     struct jpeg_compress_struct cinfo; /* libjpeg's parameter structure */
1198     struct my_error_mgr jerror;	/* for controlling libjpeg error handling */
1199     int result;
1200     Tcl_DString data;
1201     Tcl_DString *dataPtr;
1202 
1203     if (load_jpeg_library(interp) != TCL_OK) {
1204 	return TCL_ERROR;
1205     }
1206 
1207     ImgFixStringWriteProc(&data, &interp, &dataPtr, &format, &blockPtr);
1208 
1209     /* Initialize JPEG error handler */
1210     /* We set up the normal JPEG error routines, then override error_exit. */
1211     cinfo.err = jpeg_std_error(&jerror.pub);
1212     jerror.pub.error_exit = my_error_exit;
1213     jerror.pub.output_message = my_output_message;
1214 
1215     /* Establish the setjmp return context for my_error_exit to use. */
1216     if (setjmp(jerror.setjmp_buffer)) {
1217       /* If we get here, the JPEG code has signaled an error. */
1218       Tcl_AppendResult(interp, "couldn't write JPEG string: ", (char *) NULL);
1219       append_jpeg_message(interp, (j_common_ptr) &cinfo);
1220       result = TCL_ERROR;
1221       goto writeend;
1222     }
1223 
1224     /* Now we can initialize libjpeg. */
1225     jpeg_CreateCompress(&cinfo, JPEG_LIB_VERSION,
1226 	    (size_t) sizeof(struct jpeg_compress_struct));
1227     jpeg_string_dest(&cinfo, dataPtr);
1228 
1229     /* Share code with ChnWriteJPEG. */
1230     result = CommonWriteJPEG(interp, &cinfo, format, blockPtr);
1231 
1232 writeend:
1233 
1234     jpeg_destroy_compress(&cinfo);
1235     if (dataPtr == &data) {
1236 	if (result == TCL_OK) {
1237 	    Tcl_DStringResult(interp, dataPtr);
1238 	} else {
1239 	    Tcl_DStringFree(dataPtr);
1240 	}
1241     }
1242 
1243     return result;
1244 }
1245 
1246 /*
1247  *----------------------------------------------------------------------
1248  *
1249  * CommonWriteJPEG --
1250  *
1251  *	The common guts of ChnWriteJPEG and StringWriteJPEG.
1252  *	The compress struct has already been set up and the
1253  *	appropriate data destination manager initialized.
1254  *	The caller should do jpeg_destroy_compress upon return,
1255  *	and also close the destination as necessary.
1256  *
1257  *----------------------------------------------------------------------
1258  */
1259 
1260 static int
CommonWriteJPEG(interp,cinfo,format,blockPtr)1261 CommonWriteJPEG(interp, cinfo, format, blockPtr)
1262     Tcl_Interp *interp;
1263     j_compress_ptr cinfo;
1264     Tcl_Obj *format;
1265     Tk_PhotoImageBlock *blockPtr;
1266 {
1267     static OPTCONST char *jpegWriteOptions[] = {"-grayscale", "-optimize",
1268 	"-progressive", "-quality", "-smooth", NULL};
1269     JSAMPROW row_pointer[1];	/* pointer to original data scanlines */
1270     JSAMPARRAY buffer;		/* Intermediate row buffer */
1271     JSAMPROW bufferPtr;
1272     int w, h;
1273     int greenOffset, blueOffset, alphaOffset;
1274     unsigned char *pixelPtr, *pixLinePtr;
1275     int objc, i, index, grayscale = 0;
1276     Tcl_Obj **objv = (Tcl_Obj **) NULL;
1277 
1278     greenOffset = blockPtr->offset[1] - blockPtr->offset[0];
1279     blueOffset = blockPtr->offset[2] - blockPtr->offset[0];
1280     alphaOffset = blockPtr->offset[0];
1281     if (alphaOffset < blockPtr->offset[2]) {
1282         alphaOffset = blockPtr->offset[2];
1283     }
1284     if (++alphaOffset < blockPtr->pixelSize) {
1285 	alphaOffset -= blockPtr->offset[0];
1286     } else {
1287 	alphaOffset = 0;
1288     }
1289 
1290     /* Set up JPEG compression parameters. */
1291     cinfo->image_width = blockPtr->width;
1292     cinfo->image_height = blockPtr->height;
1293     cinfo->input_components = 3;
1294     cinfo->in_color_space = JCS_RGB;
1295 
1296     jpeg_set_defaults(cinfo);
1297 
1298     /* Parse options, if any, and alter default parameters */
1299 
1300     if (ImgListObjGetElements(interp, format, &objc, &objv) != TCL_OK) {
1301 	return TCL_ERROR;
1302     }
1303     if (objc) {
1304 	for (i=1; i<objc; i++) {
1305 	    if (Tcl_GetIndexFromObj(interp, objv[i], jpegWriteOptions,
1306 		    "format option", 0, &index)!=TCL_OK) {
1307 		return TCL_ERROR;
1308 	    }
1309 	    switch (index) {
1310 		case 0: {
1311 		    grayscale = 1;
1312 		    break;
1313 		}
1314 		case 1: {
1315 		    cinfo->optimize_coding = TRUE;
1316 		    break;
1317 		}
1318 		case 2: {
1319 		    if (jpeg_simple_progression != NULL) {
1320 			/* Select simple progressive mode. */
1321 			jpeg_simple_progression(cinfo);
1322 		    }
1323 		    break;
1324 		}
1325 		case 3: {
1326 		    int quality = 0;
1327 		    if (++i >= objc) {
1328 			Tcl_AppendResult(interp, "No value for option \"",
1329 				Tcl_GetStringFromObj(objv[--i], (int *) NULL), "\"", (char *) NULL);
1330 			return TCL_ERROR;
1331 		    }
1332 		    if (Tcl_GetIntFromObj(interp, objv[i], &quality) != TCL_OK) {
1333 			return TCL_ERROR;
1334 		    }
1335 		    jpeg_set_quality(cinfo, quality, FALSE);
1336 		    break;
1337 		}
1338 		case 4: {
1339 		    int smooth = 0;
1340 		    if (++i >= objc) {
1341 			Tcl_AppendResult(interp, "No value for option \"",
1342 				Tcl_GetStringFromObj(objv[--i], (int *) NULL), "\"", (char *) NULL);
1343 			return TCL_ERROR;
1344 		    }
1345 		    if (Tcl_GetIntFromObj(interp, objv[i], &smooth) != TCL_OK) {
1346 			return TCL_ERROR;
1347 		    }
1348 		    cinfo->smoothing_factor = smooth;
1349 		    break;
1350 		}
1351 	    }
1352 	}
1353     }
1354 
1355     pixLinePtr = blockPtr->pixelPtr + blockPtr->offset[0];
1356     greenOffset = blockPtr->offset[1] - blockPtr->offset[0];
1357     blueOffset = blockPtr->offset[2] - blockPtr->offset[0];
1358     if ((jpeg_set_colorspace != NULL) &&
1359 	    (grayscale || (!greenOffset && !blueOffset))) {
1360 	/* Generate monochrome JPEG file if source block is grayscale. */
1361 	jpeg_set_colorspace(cinfo, JCS_GRAYSCALE);
1362     }
1363 
1364     jpeg_start_compress(cinfo, TRUE);
1365 
1366     /* note: we assume libjpeg is configured for standard RGB pixel order. */
1367     if ((greenOffset == 1) && (blueOffset == 2)
1368 	&& (blockPtr->pixelSize == 3)) {
1369 	/* No need to reformat pixels before passing data to libjpeg */
1370 	for (h = blockPtr->height; h > 0; h--) {
1371 	    row_pointer[0] = (JSAMPROW) pixLinePtr;
1372 	    jpeg_write_scanlines(cinfo, row_pointer, 1);
1373 	    pixLinePtr += blockPtr->pitch;
1374 	}
1375     } else {
1376 	/* Must convert data format.  Create a one-scanline work buffer. */
1377 	buffer = (*cinfo->mem->alloc_sarray)
1378 	  ((j_common_ptr) cinfo, JPOOL_IMAGE,
1379 	   cinfo->image_width * cinfo->input_components, 1);
1380 	for (h = blockPtr->height; h > 0; h--) {
1381 	    pixelPtr = pixLinePtr;
1382 	    bufferPtr = buffer[0];
1383 	    for (w = blockPtr->width; w > 0; w--) {
1384 		if (alphaOffset && !pixelPtr[alphaOffset]) {
1385 		    /* if pixel is transparant, better use gray
1386 		     * than the default black.
1387 		     */
1388 		    *bufferPtr++ = 0xd9;
1389 		    *bufferPtr++ = 0xd9;
1390 		    *bufferPtr++ = 0xd9;
1391 		} else {
1392 		    *bufferPtr++ = pixelPtr[0];
1393 		    *bufferPtr++ = pixelPtr[greenOffset];
1394 		    *bufferPtr++ = pixelPtr[blueOffset];
1395 		}
1396 		pixelPtr += blockPtr->pixelSize;
1397 	    }
1398 	    jpeg_write_scanlines(cinfo, buffer, 1);
1399 	    pixLinePtr += blockPtr->pitch;
1400 	}
1401     }
1402 
1403     jpeg_finish_compress(cinfo);
1404     return TCL_OK;
1405 }
1406 
1407 /*
1408  * libjpeg source manager for reading from base64-encoded strings
1409  * and from Tcl_Channels.
1410  */
1411 
1412 static void
jpeg_obj_src(cinfo,dataObj)1413 jpeg_obj_src (cinfo, dataObj)
1414     j_decompress_ptr cinfo;
1415     Tcl_Obj *dataObj;
1416 {
1417   src_ptr src;
1418 
1419   src = (src_ptr)
1420       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
1421 				  sizeof(struct source_mgr));
1422   cinfo->src = (struct jpeg_source_mgr *) src;
1423 
1424   src->pub.init_source = dummy_source;
1425   src->pub.fill_input_buffer = fill_input_buffer;
1426   src->pub.skip_input_data = skip_input_data;
1427   src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
1428   src->pub.term_source = dummy_source;
1429 
1430   ImgReadInit(dataObj, '\377', &src->handle);
1431 
1432   src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
1433   src->pub.next_input_byte = NULL; /* until buffer loaded */
1434 }
1435 
1436 static boolean
fill_input_buffer(cinfo)1437 fill_input_buffer(cinfo)
1438     j_decompress_ptr cinfo;
1439 {
1440   src_ptr src = (src_ptr) cinfo->src;
1441   int nbytes;
1442 
1443   nbytes = ImgRead(&src->handle, src->buffer, STRING_BUF_SIZE);
1444 
1445   if (nbytes <= 0) {
1446     /* Insert a fake EOI marker */
1447     src->buffer[0] = (JOCTET) 0xFF;
1448     src->buffer[1] = (JOCTET) JPEG_EOI;
1449     nbytes = 2;
1450   }
1451 
1452   src->pub.next_input_byte = src->buffer;
1453   src->pub.bytes_in_buffer = nbytes;
1454 
1455   return TRUE;
1456 }
1457 
1458 static void
skip_input_data(cinfo,num_bytes)1459 skip_input_data(cinfo, num_bytes)
1460     j_decompress_ptr cinfo;
1461     long num_bytes;
1462 {
1463   src_ptr src = (src_ptr) cinfo->src;
1464 
1465   if (num_bytes > 0) {
1466     while (num_bytes > (long) src->pub.bytes_in_buffer) {
1467       num_bytes -= (long) src->pub.bytes_in_buffer;
1468       fill_input_buffer(cinfo);
1469     }
1470     src->pub.next_input_byte += (size_t) num_bytes;
1471     src->pub.bytes_in_buffer -= (size_t) num_bytes;
1472   }
1473 }
1474 
1475 static void
dummy_source(cinfo)1476 dummy_source(cinfo)
1477     j_decompress_ptr cinfo;
1478 {
1479   /* no work necessary here */
1480 }
1481 
1482 /*
1483  * libjpeg source manager for reading from channels.
1484  */
1485 static void
jpeg_channel_src(cinfo,chan)1486 jpeg_channel_src (cinfo, chan)
1487     j_decompress_ptr cinfo;
1488     Tcl_Channel chan;
1489 {
1490   src_ptr src;
1491 
1492   src = (src_ptr)
1493       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
1494 				  sizeof(struct source_mgr));
1495   cinfo->src = (struct jpeg_source_mgr *) src;
1496 
1497   src->pub.init_source = dummy_source;
1498   src->pub.fill_input_buffer = fill_input_buffer;
1499   src->pub.skip_input_data = skip_input_data;
1500   src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
1501   src->pub.term_source = dummy_source;
1502 
1503   src->handle.data = (char *) chan;
1504   src->handle.state = IMG_CHAN;
1505 
1506   src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
1507   src->pub.next_input_byte = NULL; /* until buffer loaded */
1508 }
1509 
1510 
1511 /*
1512  * libjpeg destination manager for writing to base64-encoded strings
1513  * and Tcl_Channel's.
1514  */
1515 
1516 static void
jpeg_string_dest(cinfo,dstring)1517 jpeg_string_dest (cinfo, dstring)
1518     j_compress_ptr cinfo;
1519     Tcl_DString* dstring;
1520 {
1521   dest_ptr dest;
1522 
1523   if (cinfo->dest == NULL) {	/* first time for this JPEG object? */
1524     cinfo->dest = (struct jpeg_destination_mgr *)
1525       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
1526 				  sizeof(struct destination_mgr));
1527   }
1528 
1529   dest = (dest_ptr) cinfo->dest;
1530   dest->pub.init_destination = my_init_destination;
1531   dest->pub.empty_output_buffer = my_empty_output_buffer;
1532   dest->pub.term_destination = my_term_destination;
1533   Tcl_DStringSetLength(dstring, 200);
1534   dest->handle.buffer = dstring;
1535   dest->handle.data = Tcl_DStringValue(dstring);
1536   dest->handle.state = 0;
1537   dest->handle.length = 0;
1538 }
1539 
1540 static void
jpeg_channel_dest(cinfo,chan)1541 jpeg_channel_dest (cinfo, chan)
1542     j_compress_ptr cinfo;
1543     Tcl_Channel chan;
1544 {
1545   dest_ptr dest;
1546 
1547   if (cinfo->dest == NULL) {	/* first time for this JPEG object? */
1548     cinfo->dest = (struct jpeg_destination_mgr *)
1549       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
1550 				  sizeof(struct destination_mgr));
1551   }
1552 
1553   dest = (dest_ptr) cinfo->dest;
1554   dest->pub.init_destination = my_init_destination;
1555   dest->pub.empty_output_buffer = my_empty_output_buffer;
1556   dest->pub.term_destination = my_term_destination;
1557   dest->handle.data = (char *) chan;
1558   dest->handle.state = IMG_CHAN;
1559 }
1560 
1561 static void
my_init_destination(cinfo)1562 my_init_destination (cinfo)
1563     j_compress_ptr cinfo;
1564 {
1565   dest_ptr dest = (dest_ptr) cinfo->dest;
1566   dest->pub.next_output_byte = dest->buffer;
1567   dest->pub.free_in_buffer = STRING_BUF_SIZE;
1568 }
1569 
1570 static boolean
my_empty_output_buffer(cinfo)1571 my_empty_output_buffer (cinfo)
1572     j_compress_ptr cinfo;
1573 {
1574   dest_ptr dest = (dest_ptr) cinfo->dest;
1575   if (ImgWrite(&dest->handle, (char *) dest->buffer, STRING_BUF_SIZE)
1576   	!= STRING_BUF_SIZE)
1577     ERREXIT(cinfo, JERR_FILE_WRITE);
1578 
1579   dest->pub.next_output_byte = dest->buffer;
1580   dest->pub.free_in_buffer = STRING_BUF_SIZE;
1581 
1582   return TRUE;
1583 }
1584 
1585 static void
my_term_destination(cinfo)1586 my_term_destination (cinfo)
1587     j_compress_ptr cinfo;
1588 {
1589   dest_ptr dest = (dest_ptr) cinfo->dest;
1590   int datacount = STRING_BUF_SIZE - (int) dest->pub.free_in_buffer;
1591 
1592   /* Write any data remaining in the buffer */
1593   if (datacount > 0) {
1594     if (ImgWrite(&dest->handle, (char *) dest->buffer, datacount)
1595 	!= datacount)
1596       ERREXIT(cinfo, JERR_FILE_WRITE);
1597   }
1598   /* Empty any partial-byte from the base64 encoder */
1599   ImgPutc(IMG_DONE, &dest->handle);
1600 }
1601 
1602 
1603 /*
1604  * Error handler to replace (or extend, really) libjpeg's default handler
1605  */
1606 
1607 static void
my_error_exit(cinfo)1608 my_error_exit (cinfo)
1609     j_common_ptr cinfo;
1610 {
1611   struct my_error_mgr *myerr = (struct my_error_mgr *) cinfo->err;
1612   /* Exit back to outer level */
1613   longjmp(myerr->setjmp_buffer, 1);
1614 }
1615 
1616 static void
append_jpeg_message(interp,cinfo)1617 append_jpeg_message (interp, cinfo)
1618     Tcl_Interp *interp;
1619     j_common_ptr cinfo;
1620 {
1621   /* Append libjpeg error message to interp->result */
1622   char buffer[JMSG_LENGTH_MAX];
1623   (*cinfo->err->format_message) (cinfo, buffer);
1624   Tcl_AppendResult(interp, buffer, (char *) NULL);
1625 }
1626 
1627 static void
my_output_message(cinfo)1628 my_output_message (cinfo)
1629     j_common_ptr cinfo;
1630 {
1631   /* Override libjpeg's output_message to do nothing.
1632    * This ensures that warning messages will not appear on stderr,
1633    * even for a corrupted JPEG file.  Too bad there's no way
1634    * to report a "warning" message to the calling Tcl script.
1635    */
1636 }
1637