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