1 /* STARTHEADER
2 *
3 * File : pcx.c
4 *
5 * Author : Paul Obermeier (paul@poSoft.de)
6 *
7 * Date : 2001 / 02 / 20
8 *
9 * Copyright : (C) 2001-2019 Paul Obermeier
10 *
11 * Description :
12 *
13 * A photo image handler for PaintBrush's PCX file format.
14 *
15 * The following image types are supported:
16 *
17 * 1-bit pixels: Black and White.
18 * 8-bit pixels: Grayscale or indexed.
19 * 24-bit pixels: True-color (RGB, each channel 8 bit).
20 *
21 * List of currently supported features:
22 *
23 * Type | Read | Write |
24 * | -file | -data | -file | -data |
25 * ----------------------------------------
26 * 1-bit | Yes | Yes | No | No |
27 * 8-bit | Yes | Yes | No | No |
28 * 24-bit | Yes | Yes | Yes | Yes |
29 *
30 * All images types may be either uncompressed or run-length encoded.
31 *
32 *
33 * The following format options are available:
34 *
35 * Read PCX image: "pcx -verbose <bool>"
36 * Write PCX image: "pcx -verbose <bool> -compression <type>"
37 *
38 * -verbose <bool>: If set to true, additional information about the file
39 * format is printed to stdout. Default is "false".
40 * -compression <type>: Set the compression mode to either "none" or "rle".
41 * Default is "rle".
42 *
43 * Notes:
44 *
45 * - Part of this code was taken from the "pcx" GIMP plugin:
46 *
47 * >> pcx.c GIMP plug-in for loading & saving PCX files
48 * >>
49 * >> This code is based in parts on code by Francisco Bustamante, but the
50 * >> largest portion of the code has been rewritten and is now maintained
51 * >> occasionally by Nick Lamb njl195@zepler.org.uk
52 *
53 * ENDHEADER
54 *
55 */
56
57 /*
58 * Generic initialization code, parameterized via CPACKAGE and PACKAGE.
59 */
60
61 #include "init.c"
62
63
64 /* #define DEBUG_LOCAL */
65
66 /* Some defines and typedefs. */
67 #define TRUE 1
68 #define FALSE 0
69 typedef unsigned char Boln; /* Boolean value: TRUE or FALSE */
70 typedef unsigned char UByte; /* Unsigned 8 bit integer */
71 typedef char Byte; /* Signed 8 bit integer */
72 typedef unsigned short UShort; /* Unsigned 16 bit integer */
73 typedef short Short; /* Signed 16 bit integer */
74 typedef int Int; /* Signed 32 bit integer */
75
76 typedef struct {
77 UByte manufacturer;
78 UByte version;
79 UByte compression;
80 UByte bpp;
81 Short x1;
82 Short y1;
83 Short x2;
84 Short y2;
85 Short hdpi;
86 Short vdpi;
87 UByte colormap[48];
88 UByte reserved;
89 UByte planes;
90 Short bytesperline;
91 Short color;
92 UByte filler[58];
93 } PCXHEADER;
94
95 /* Format options structure for use with ParseFormatOpts */
96 typedef struct {
97 Int compression;
98 Boln verbose;
99 } FMTOPT;
100
101 #define htoqs(x) qtohs(x)
qtohs(UShort x)102 static UShort qtohs (UShort x)
103 {
104 /* The PCX image format expects data to be in Intel (Little-endian) format. */
105 if (!tkimg_IsIntel ()) {
106 return ((UShort)((((UShort)(x) & 0x00ff) << 8) | \
107 (((UShort)(x) & 0xff00) >> 8)));
108 } else {
109 return x;
110 }
111 }
112
113 /* Read 1 byte, representing an unsigned integer number. */
114
115 #ifdef DEBUG_LOCAL
readUByte(tkimg_MFile * handle,UByte * b)116 static Boln readUByte (tkimg_MFile *handle, UByte *b)
117 {
118 char buf[1];
119 if (1 != tkimg_Read2(handle, (char *) buf, 1)) {
120 return FALSE;
121 }
122 *b = buf[0];
123 return TRUE;
124 }
125 #else
126 /* Use this macro for better performance, esp. when reading RLE files. */
127 # define readUByte(h,b) (1 == tkimg_Read2((h),(char *)(b),1))
128 #endif
129
130 /* Write 1 byte, representing an unsigned integer to a file. */
131
writeUByte(tkimg_MFile * handle,UByte b)132 static Boln writeUByte (tkimg_MFile *handle, UByte b)
133 {
134 UByte buf[1];
135 buf[0] = b;
136 if (1 != tkimg_Write2(handle, (const char *)buf, 1)) {
137 return FALSE;
138 }
139 return TRUE;
140 }
141
read_pcx_header(tkimg_MFile * ifp,PCXHEADER * pcxhdr)142 static Boln read_pcx_header (tkimg_MFile *ifp, PCXHEADER *pcxhdr)
143 {
144 if (tkimg_Read2(ifp, (char *)pcxhdr, 128) != 128) {
145 return FALSE;
146 }
147
148 if (pcxhdr->manufacturer != 10) {
149 return FALSE;
150 }
151 if (pcxhdr->bpp != 1 && pcxhdr->bpp != 8) {
152 return FALSE;
153 }
154 if (pcxhdr->planes != 1 && pcxhdr->planes != 3 && pcxhdr->planes != 4) {
155 return FALSE;
156 }
157 return TRUE;
158 }
159
160 #define OUT Tcl_WriteChars (outChan, str, -1)
printImgInfo(PCXHEADER * ph,const char * filename,const char * msg)161 static void printImgInfo (PCXHEADER *ph, const char *filename, const char *msg)
162 {
163 Tcl_Channel outChan;
164 char str[256];
165 Int width, height;
166
167 outChan = Tcl_GetStdChannel (TCL_STDOUT);
168 if (!outChan) {
169 return;
170 }
171 width = qtohs (ph->x2) - qtohs (ph->x1) + 1;
172 height = qtohs (ph->y2) - qtohs (ph->y1) + 1;
173
174 sprintf(str, "%s %s\n", msg, filename); OUT;
175 sprintf(str, "\tSize in pixel : %d x %d\n", width, height); OUT;
176 sprintf(str, "\tNo. of channels : %d\n", ph->planes); OUT;
177 sprintf(str, "\tBits per pixel : %d\n", ph->bpp); OUT;
178 sprintf(str, "\tBytes per line : %d\n", ph->bytesperline); OUT;
179 sprintf(str, "\tRLE compression : %s\n", ph->compression? "yes": "no"); OUT;
180 Tcl_Flush(outChan);
181 }
182 #undef OUT
183
readline(tkimg_MFile * handle,UByte * buffer,Int bytes,Int compr)184 static Boln readline (tkimg_MFile *handle, UByte *buffer, Int bytes, Int compr)
185 {
186 static UByte count = 0, value = 0;
187
188 if (compr) {
189 while (bytes--) {
190 if (count == 0) {
191 if (!readUByte (handle, &value)) {
192 return TRUE;
193 }
194 if (value < 0xc0) {
195 count = 1;
196 } else {
197 count = value - 0xc0;
198 if (!readUByte (handle, &value)) {
199 return TRUE;
200 }
201 }
202 }
203 count--;
204 *(buffer++) = value;
205 }
206 } else {
207 if (bytes != tkimg_Read2(handle, (char *)buffer, bytes)) {
208 return FALSE;
209 }
210 }
211 return TRUE;
212 }
213
writeline(tkimg_MFile * handle,UByte * buffer,Int bytes)214 static Boln writeline (tkimg_MFile *handle, UByte *buffer, Int bytes)
215 {
216 UByte value, count;
217 UByte *finish = buffer + bytes;
218
219 while (buffer < finish) {
220 value = *(buffer++);
221 count = 1;
222
223 while (buffer < finish && count < 63 && *buffer == value) {
224 count++;
225 buffer++;
226 }
227
228 if (value < 0xc0 && count == 1) {
229 if (!writeUByte (handle, value)) {
230 return FALSE;
231 }
232 } else {
233 if (!writeUByte (handle, 0xc0 + count)) {
234 return FALSE;
235 }
236 if (!writeUByte (handle, value)) {
237 return FALSE;
238 }
239 }
240 }
241 return TRUE;
242 }
243
load_8(Tcl_Interp * interp,tkimg_MFile * ifp,Tk_PhotoHandle imageHandle,int destX,int destY,int width,int height,int srcX,int srcY,int fileWidth,int fileHeight,int bytesPerLine,int compr)244 static Boln load_8 (Tcl_Interp *interp, tkimg_MFile *ifp,
245 Tk_PhotoHandle imageHandle, int destX, int destY,
246 int width, int height, int srcX, int srcY,
247 int fileWidth, int fileHeight, int bytesPerLine, int compr)
248 {
249 Int x, y;
250 Int stopY, outY;
251 Tk_PhotoImageBlock block;
252 UByte *line, *buffer, *indBuf, *indBufPtr;
253 UByte cmap[768], sepChar;
254 Boln haveColormap = FALSE;
255 Boln result = TRUE;
256 char errMsg[200];
257
258 line = (UByte *) ckalloc (bytesPerLine);
259 buffer = (UByte *) ckalloc (fileWidth * 3);
260 indBuf = (UByte *) ckalloc (fileWidth * fileHeight);
261 indBufPtr = indBuf;
262
263 block.pixelSize = 3;
264 block.pitch = fileWidth * 3;
265 block.width = width;
266 block.height = 1;
267 block.offset[0] = 0;
268 block.offset[1] = 1;
269 block.offset[2] = 2;
270 block.offset[3] = 0;
271
272 block.pixelPtr = buffer + srcX * 3;
273
274 stopY = srcY + height;
275 outY = destY;
276
277 /* Read in the whole image data as indices. */
278 for (y=0; y<stopY; y++) {
279 if (!readline (ifp, line, bytesPerLine, compr)) {
280 ckfree ((char *) line);
281 ckfree ((char *) buffer);
282 ckfree ((char *) indBuf);
283 sprintf(errMsg, "Unexpected end-of-file while scanline %d", y);
284 Tcl_AppendResult(interp, errMsg, (char *)NULL);
285 return FALSE;
286 }
287 memcpy (indBufPtr, line, fileWidth);
288 indBufPtr += fileWidth;
289 }
290 /* Read the colormap: 256 entries a 3 values for RGB */
291 if (tkimg_Read2(ifp, (char *)&sepChar, 1) == 1) {
292 if (sepChar == 12) {
293 /* A colormap is available, if sepChar equals 0x0C */
294 if (tkimg_Read2(ifp, (char *)&cmap, 768) != 768) {
295 ckfree ((char *) line);
296 ckfree ((char *) buffer);
297 ckfree ((char *) indBuf);
298 Tcl_AppendResult (interp, "Unexpected end-of-file while reading colormap",
299 (char *) NULL);
300 return FALSE;
301 }
302 haveColormap = TRUE;
303 }
304 }
305
306 for (y=srcY; y<stopY; y++) {
307 if (haveColormap) {
308 /* An indexed colormap image */
309 for (x=0; x<fileWidth; x++) {
310 buffer[x * 3 + 0] = cmap[indBuf[y*fileWidth + x]*3 + 0 ];
311 buffer[x * 3 + 1] = cmap[indBuf[y*fileWidth + x]*3 + 1 ];
312 buffer[x * 3 + 2] = cmap[indBuf[y*fileWidth + x]*3 + 2 ];
313 }
314 } else {
315 /* A grey-scale image */
316 for (x=0; x<fileWidth; x++) {
317 buffer[x * 3 + 0] = indBuf[y*fileWidth + x];
318 buffer[x * 3 + 1] = indBuf[y*fileWidth + x];
319 buffer[x * 3 + 2] = indBuf[y*fileWidth + x];
320 }
321 }
322 if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, width, 1,
323 TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
324 result = FALSE;
325 break;
326 }
327 outY++;
328 }
329 ckfree ((char *) line);
330 ckfree ((char *) buffer);
331 ckfree ((char *) indBuf);
332 return result;
333 }
334
load_24(Tcl_Interp * interp,tkimg_MFile * ifp,Tk_PhotoHandle imageHandle,int destX,int destY,int width,int height,int srcX,int srcY,int fileWidth,int fileHeight,int bytesPerLine,int compr)335 static Boln load_24 (Tcl_Interp *interp, tkimg_MFile *ifp,
336 Tk_PhotoHandle imageHandle, int destX, int destY,
337 int width, int height, int srcX, int srcY,
338 int fileWidth, int fileHeight, int bytesPerLine, int compr)
339 {
340 Int x, y, c;
341 Int stopY, outY;
342 Tk_PhotoImageBlock block;
343 UByte *line, *buffer;
344 Boln result = TRUE;
345
346 line = (UByte *) ckalloc (bytesPerLine);
347 buffer = (UByte *) ckalloc (fileWidth * 3);
348
349 block.pixelSize = 3;
350 block.pitch = fileWidth * 3;
351 block.width = width;
352 block.height = 1;
353 block.offset[0] = 0;
354 block.offset[1] = 1;
355 block.offset[2] = 2;
356 block.offset[3] = 0;
357
358 block.pixelPtr = buffer + srcX * 3;
359
360 stopY = srcY + height;
361 outY = destY;
362
363 for (y=0; y<stopY; y++) {
364 for (c=0; c<3; c++) {
365 if (!readline (ifp, line, bytesPerLine, compr)) {
366 ckfree ((char *) line);
367 ckfree ((char *) buffer);
368 return FALSE;
369 }
370 for (x=0; x<fileWidth; x++) {
371 buffer[x * 3 + c] = line[x];
372 }
373 }
374 if (y >= srcY) {
375 if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, width, 1,
376 TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
377 result = FALSE;
378 break;
379 }
380 outY++;
381 }
382 }
383 ckfree ((char *) line);
384 ckfree ((char *) buffer);
385 return result;
386 }
387
load_1(Tcl_Interp * interp,tkimg_MFile * ifp,Tk_PhotoHandle imageHandle,int destX,int destY,int width,int height,int srcX,int srcY,int fileWidth,int fileHeight,int bytesPerLine,int compr)388 static Boln load_1 (Tcl_Interp *interp, tkimg_MFile *ifp,
389 Tk_PhotoHandle imageHandle, int destX, int destY,
390 int width, int height, int srcX, int srcY,
391 int fileWidth, int fileHeight, int bytesPerLine, int compr)
392 {
393 Int x, y;
394 Int stopY, outY;
395 Tk_PhotoImageBlock block;
396 UByte *line, *buffer;
397 Boln result = TRUE;
398
399 line = (UByte *) ckalloc (fileWidth);
400 buffer = (UByte *) ckalloc (fileWidth * 1);
401
402 block.pixelSize = 1;
403 block.pitch = fileWidth * 1;
404 block.width = width;
405 block.height = 1;
406 block.offset[0] = 0;
407 block.offset[1] = 0;
408 block.offset[2] = 0;
409 block.offset[3] = 0;
410
411 block.pixelPtr = buffer + srcX * 1;
412
413 stopY = srcY + height;
414 outY = destY;
415
416 for (y=0; y<stopY; y++) {
417 if (!readline (ifp, line, bytesPerLine, compr)) {
418 ckfree ((char *) line);
419 ckfree ((char *) buffer);
420 return FALSE;
421 }
422 for (x=0; x<fileWidth; x++) {
423 if (line[x/8] & (128 >> (x%8))) {
424 buffer[x] = 255;
425 } else {
426 buffer[x] = 0;
427 }
428 }
429 if (y >= srcY) {
430 if (tkimg_PhotoPutBlock(interp, imageHandle, &block, destX, outY, width, 1,
431 TK_PHOTO_COMPOSITE_SET) == TCL_ERROR) {
432 result = FALSE;
433 break;
434 }
435 outY++;
436 }
437 }
438 ckfree ((char *) line);
439 ckfree ((char *) buffer);
440 return result;
441 }
442
443 /*
444 * Prototypes for local procedures defined in this file:
445 */
446
447 static int ParseFormatOpts(Tcl_Interp *interp, Tcl_Obj *format, FMTOPT *opts);
448 static int CommonMatch(tkimg_MFile *handle, int *widthPtr,
449 int *heightPtr, PCXHEADER *pcxHeaderPtr);
450 static int CommonRead(Tcl_Interp *interp, tkimg_MFile *handle,
451 const char *filename, Tcl_Obj *format,
452 Tk_PhotoHandle imageHandle, int destX, int destY,
453 int width, int height, int srcX, int srcY);
454 static int CommonWrite(Tcl_Interp *interp,
455 const char *filename, Tcl_Obj *format,
456 tkimg_MFile *handle, Tk_PhotoImageBlock *blockPtr);
457
ParseFormatOpts(Tcl_Interp * interp,Tcl_Obj * format,FMTOPT * opts)458 static int ParseFormatOpts(
459 Tcl_Interp *interp,
460 Tcl_Obj *format,
461 FMTOPT *opts
462 ) {
463 static const char *const pcxOptions[] = {
464 "-compression", "-verbose", NULL
465 };
466 int objc, i, index;
467 char *optionStr;
468 Tcl_Obj **objv;
469 int boolVal;
470
471 /* Initialize options with default values. */
472 opts->compression = 1;
473 opts->verbose = 0;
474
475 if (tkimg_ListObjGetElements(interp, format, &objc, &objv) != TCL_OK) {
476 return TCL_ERROR;
477 }
478 if (objc) {
479 for (i=1; i<objc; i++) {
480 if (Tcl_GetIndexFromObj(interp, objv[i], (const char *CONST86 *)pcxOptions,
481 "format option", 0, &index) != TCL_OK) {
482 return TCL_ERROR;
483 }
484 if (++i >= objc) {
485 Tcl_AppendResult(interp, "No value for option \"",
486 Tcl_GetStringFromObj (objv[--i], (int *) NULL),
487 "\"", (char *) NULL);
488 return TCL_ERROR;
489 }
490 optionStr = Tcl_GetStringFromObj(objv[i], (int *) NULL);
491 switch(index) {
492 case 0:
493 if (!strncmp (optionStr, "none", strlen ("none"))) {
494 opts->compression = 0;
495 } else if (!strncmp (optionStr, "rle", strlen ("rle"))) {
496 opts->compression = 1;
497 } else {
498 Tcl_AppendResult (interp, "Invalid compression mode \"", optionStr,
499 "\": Must be \"none\" or \"rle\"", (char *) NULL);
500 return TCL_ERROR;
501 }
502 break;
503 case 1:
504 if (Tcl_GetBoolean(interp, optionStr, &boolVal) == TCL_ERROR) {
505 Tcl_AppendResult (interp, "Invalid verbose mode \"", optionStr,
506 "\": should be 1 or 0, on or off, true or false",
507 (char *) NULL);
508 return TCL_ERROR;
509 }
510 opts->verbose = boolVal;
511 break;
512 }
513 }
514 }
515 return TCL_OK;
516 }
517
ChnMatch(Tcl_Channel chan,const char * filename,Tcl_Obj * format,int * widthPtr,int * heightPtr,Tcl_Interp * interp)518 static int ChnMatch(
519 Tcl_Channel chan,
520 const char *filename,
521 Tcl_Obj *format,
522 int *widthPtr,
523 int *heightPtr,
524 Tcl_Interp *interp
525 ) {
526 tkimg_MFile handle;
527
528 handle.data = (char *) chan;
529 handle.state = IMG_CHAN;
530
531 return CommonMatch(&handle, widthPtr, heightPtr, NULL);
532 }
533
ObjMatch(Tcl_Obj * data,Tcl_Obj * format,int * widthPtr,int * heightPtr,Tcl_Interp * interp)534 static int ObjMatch(
535 Tcl_Obj *data,
536 Tcl_Obj *format,
537 int *widthPtr,
538 int *heightPtr,
539 Tcl_Interp *interp
540 ) {
541 tkimg_MFile handle;
542
543 if (!tkimg_ReadInit(data, 10, &handle)) {
544 return 0;
545 }
546 return CommonMatch(&handle, widthPtr, heightPtr, NULL);
547 }
548
CommonMatch(tkimg_MFile * handle,int * widthPtr,int * heightPtr,PCXHEADER * pcxHeaderPtr)549 static int CommonMatch(
550 tkimg_MFile *handle,
551 int *widthPtr,
552 int *heightPtr,
553 PCXHEADER *pcxHeaderPtr
554 ) {
555 PCXHEADER ph;
556 Int offset_x, offset_y;
557
558 if (!read_pcx_header (handle, &ph)) {
559 return 0;
560 }
561
562 offset_x = qtohs (ph.x1);
563 offset_y = qtohs (ph.y1);
564
565 if (offset_x < 0 || offset_y < 0) {
566 return 0;
567 }
568
569 *widthPtr = qtohs (ph.x2) - offset_x + 1;
570 *heightPtr = qtohs (ph.y2) - offset_y + 1;
571
572 if (*widthPtr < 1 || *heightPtr < 1) {
573 return 0;
574 }
575
576 if (pcxHeaderPtr) {
577 *pcxHeaderPtr = ph;
578 }
579 return 1;
580 }
581
ChnRead(Tcl_Interp * interp,Tcl_Channel chan,const char * filename,Tcl_Obj * format,Tk_PhotoHandle imageHandle,int destX,int destY,int width,int height,int srcX,int srcY)582 static int ChnRead(
583 Tcl_Interp *interp, /* Interpreter to use for reporting errors. */
584 Tcl_Channel chan, /* The image channel, open for reading. */
585 const char *filename, /* The name of the image file. */
586 Tcl_Obj *format, /* User-specified format object, or NULL. */
587 Tk_PhotoHandle imageHandle, /* The photo image to write into. */
588 int destX, int destY, /* Coordinates of top-left pixel in
589 * photo image to be written to. */
590 int width, int height, /* Dimensions of block of photo image to
591 * be written to. */
592 int srcX, int srcY /* Coordinates of top-left pixel to be used
593 * in image being read. */
594 ) {
595 tkimg_MFile handle;
596
597 handle.data = (char *) chan;
598 handle.state = IMG_CHAN;
599
600 return CommonRead (interp, &handle, filename, format,
601 imageHandle, destX, destY,
602 width, height, srcX, srcY);
603 }
604
ObjRead(Tcl_Interp * interp,Tcl_Obj * data,Tcl_Obj * format,Tk_PhotoHandle imageHandle,int destX,int destY,int width,int height,int srcX,int srcY)605 static int ObjRead(
606 Tcl_Interp *interp,
607 Tcl_Obj *data,
608 Tcl_Obj *format,
609 Tk_PhotoHandle imageHandle,
610 int destX, int destY,
611 int width, int height,
612 int srcX, int srcY
613 ) {
614 tkimg_MFile handle;
615
616 tkimg_ReadInit (data, 10, &handle);
617 return CommonRead (interp, &handle, "InlineData", format, imageHandle,
618 destX, destY, width, height, srcX, srcY);
619 }
620
CommonRead(Tcl_Interp * interp,tkimg_MFile * handle,const char * filename,Tcl_Obj * format,Tk_PhotoHandle imageHandle,int destX,int destY,int width,int height,int srcX,int srcY)621 static int CommonRead(
622 Tcl_Interp *interp, /* Interpreter to use for reporting errors. */
623 tkimg_MFile *handle, /* The image file, open for reading. */
624 const char *filename, /* The name of the image file. */
625 Tcl_Obj *format, /* User-specified format object, or NULL. */
626 Tk_PhotoHandle imageHandle, /* The photo image to write into. */
627 int destX, int destY, /* Coordinates of top-left pixel in
628 * photo image to be written to. */
629 int width, int height, /* Dimensions of block of photo image to
630 * be written to. */
631 int srcX, int srcY /* Coordinates of top-left pixel to be used
632 * in image being read. */
633 ) {
634 int fileWidth, fileHeight;
635 int outWidth, outHeight;
636 int retCode = TCL_OK;
637 PCXHEADER ph;
638 FMTOPT opts;
639 char errMsg[200];
640
641 if (ParseFormatOpts(interp, format, &opts) != TCL_OK) {
642 return TCL_ERROR;
643 }
644
645 CommonMatch(handle, &fileWidth, &fileHeight, &ph);
646 if (opts.verbose) {
647 printImgInfo (&ph, filename, "Reading image:");
648 }
649
650 if ((srcX + width) > fileWidth) {
651 outWidth = fileWidth - srcX;
652 } else {
653 outWidth = width;
654 }
655 if ((srcY + height) > fileHeight) {
656 outHeight = fileHeight - srcY;
657 } else {
658 outHeight = height;
659 }
660 if ((outWidth <= 0) || (outHeight <= 0)
661 || (srcX >= fileWidth) || (srcY >= fileHeight)) {
662 return TCL_OK;
663 }
664
665 if (tkimg_PhotoExpand(interp, imageHandle, destX + outWidth, destY + outHeight) == TCL_ERROR) {
666 return TCL_ERROR;
667 }
668
669 if (ph.compression) {
670 tkimg_ReadBuffer (1);
671 }
672
673 if (ph.planes == 1 && ph.bpp == 1) {
674 if (!load_1 (interp, handle, imageHandle, destX, destY,
675 outWidth, outHeight, srcX, srcY, fileWidth, fileHeight,
676 qtohs (ph.bytesperline), ph.compression)) {
677 retCode = TCL_ERROR;
678 }
679 } else if (ph.planes == 4 && ph.bpp == 1) {
680 Tcl_AppendResult(interp, "Format (4 channels, 1 bit per channel) ",
681 "is not supported yet.", (char *)NULL);
682 retCode = TCL_ERROR;
683 } else if (ph.planes == 1 && ph.bpp == 8) {
684 if (!load_8 (interp, handle, imageHandle, destX, destY,
685 outWidth, outHeight, srcX, srcY, fileWidth, fileHeight,
686 qtohs (ph.bytesperline), ph.compression)) {
687 retCode = TCL_ERROR;
688 }
689 } else if (ph.planes == 3 && ph.bpp == 8) {
690 if (!load_24 (interp, handle, imageHandle, destX, destY,
691 outWidth, outHeight, srcX, srcY, fileWidth, fileHeight,
692 qtohs (ph.bytesperline), ph.compression)) {
693 retCode = TCL_ERROR;
694 }
695 } else {
696 sprintf(errMsg, "Image has invalid channel/bpp combination: (%d, %d)",
697 ph.planes, ph.bpp);
698 Tcl_AppendResult(interp, errMsg, (char *)NULL);
699 retCode = TCL_ERROR;
700 }
701 tkimg_ReadBuffer (0);
702 return retCode;
703 }
704
ChnWrite(interp,filename,format,blockPtr)705 static int ChnWrite (interp, filename, format, blockPtr)
706 Tcl_Interp *interp;
707 const char *filename;
708 Tcl_Obj *format;
709 Tk_PhotoImageBlock *blockPtr;
710 {
711 Tcl_Channel chan;
712 tkimg_MFile handle;
713 int result;
714
715 chan = tkimg_OpenFileChannel (interp, filename, 0644);
716 if (!chan) {
717 return TCL_ERROR;
718 }
719
720 handle.data = (char *) chan;
721 handle.state = IMG_CHAN;
722
723 result = CommonWrite (interp, filename, format, &handle, blockPtr);
724 if (Tcl_Close(interp, chan) == TCL_ERROR) {
725 return TCL_ERROR;
726 }
727 return result;
728 }
729
StringWrite(Tcl_Interp * interp,Tcl_Obj * format,Tk_PhotoImageBlock * blockPtr)730 static int StringWrite(
731 Tcl_Interp *interp,
732 Tcl_Obj *format,
733 Tk_PhotoImageBlock *blockPtr
734 ) {
735 tkimg_MFile handle;
736 int result;
737 Tcl_DString data;
738
739 Tcl_DStringInit(&data);
740 tkimg_WriteInit(&data, &handle);
741 result = CommonWrite (interp, "InlineData", format, &handle, blockPtr);
742 tkimg_Putc(IMG_DONE, &handle);
743
744 if (result == TCL_OK) {
745 Tcl_DStringResult(interp, &data);
746 } else {
747 Tcl_DStringFree(&data);
748 }
749 return result;
750 }
751
CommonWrite(Tcl_Interp * interp,const char * filename,Tcl_Obj * format,tkimg_MFile * handle,Tk_PhotoImageBlock * blockPtr)752 static int CommonWrite(
753 Tcl_Interp *interp,
754 const char *filename,
755 Tcl_Obj *format,
756 tkimg_MFile *handle,
757 Tk_PhotoImageBlock *blockPtr
758 ) {
759 int x, y, nchan, nBytes;
760 int redOffset, greenOffset, blueOffset, alphaOffset;
761 UByte *pixelPtr, *pixRowPtr;
762 PCXHEADER ph;
763 UByte *row;
764 FMTOPT opts;
765 char errMsg[200];
766
767 if (ParseFormatOpts(interp, format, &opts) != TCL_OK) {
768 return TCL_ERROR;
769 }
770
771 redOffset = 0;
772 greenOffset = blockPtr->offset[1] - blockPtr->offset[0];
773 blueOffset = blockPtr->offset[2] - blockPtr->offset[0];
774 alphaOffset = blockPtr->offset[0];
775
776 if (alphaOffset < blockPtr->offset[2]) {
777 alphaOffset = blockPtr->offset[2];
778 }
779 if (++alphaOffset < blockPtr->pixelSize) {
780 alphaOffset -= blockPtr->offset[0];
781 } else {
782 alphaOffset = 0;
783 }
784
785 nchan = 3;
786 nBytes = blockPtr->width * nchan;
787
788 /* Fill the PCX header struct and write the header to the channel. */
789 memset (&ph, 0, sizeof (PCXHEADER));
790 ph.manufacturer = 0x0a;
791 ph.version = 5;
792 ph.compression = opts.compression;
793 ph.bpp = 8;
794 ph.planes = 3;
795 ph.color = htoqs (1);
796 ph.bytesperline = htoqs (blockPtr->width);
797 ph.x1 = htoqs (0);
798 ph.y1 = htoqs (0);
799 ph.x2 = htoqs (blockPtr->width - 1);
800 ph.y2 = htoqs (blockPtr->height - 1);
801
802 ph.hdpi = htoqs (300);
803 ph.vdpi = htoqs (300);
804 ph.reserved = 0;
805
806 if (tkimg_Write2(handle, (const char *)&ph, 128) != 128) {
807 Tcl_AppendResult(interp, "Can't write PCX header.", (char *)NULL);
808 return TCL_ERROR;
809 }
810
811 row = (UByte *) ckalloc (nBytes);
812 /* Now write out the image data. */
813 pixRowPtr = blockPtr->pixelPtr + blockPtr->offset[0];
814 if (!opts.compression) {
815 for (y=0; y<blockPtr->height; y++) {
816 pixelPtr = pixRowPtr;
817 for (x=0; x<blockPtr->width; x++) {
818 row[x + 0*blockPtr->width] = pixelPtr[redOffset];
819 row[x + 1*blockPtr->width] = pixelPtr[greenOffset];
820 row[x + 2*blockPtr->width] = pixelPtr[blueOffset];
821 pixelPtr += blockPtr->pixelSize;
822 }
823 if (nBytes != tkimg_Write2(handle, (const char *)row, nBytes)) {
824 sprintf(errMsg, "Can't write %d bytes to image file.", nBytes);
825 Tcl_AppendResult(interp, errMsg, (char *)NULL);
826 ckfree ((char *)row);
827 return TCL_ERROR;
828 }
829 pixRowPtr += blockPtr->pitch;
830 }
831 } else { /* RLE compression */
832 for (y = 0; y < blockPtr->height; y++) {
833 pixelPtr = pixRowPtr;
834 for (x = 0; x < blockPtr->width; x++) {
835 row[x + 0*blockPtr->width] = pixelPtr[redOffset];
836 row[x + 1*blockPtr->width] = pixelPtr[greenOffset];
837 row[x + 2*blockPtr->width] = pixelPtr[blueOffset];
838 pixelPtr += blockPtr->pixelSize;
839 }
840 if (!writeline (handle, row, nBytes)) {
841 sprintf(errMsg, "Can't write %d bytes to image file.", nBytes);
842 Tcl_AppendResult(interp, errMsg, (char *)NULL);
843 ckfree ((char *)row);
844 return TCL_ERROR;
845 }
846 pixRowPtr += blockPtr->pitch;
847 }
848 }
849 if (opts.verbose) {
850 printImgInfo (&ph, filename, "Saving image:");
851 }
852 ckfree ((char *)row);
853 return TCL_OK;
854 }
855