|
/* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tk_base/tkimgppm.c,v 1.1.1.1 2001/06/13 05:03:30 dtashley Exp $ */ |
|
|
|
|
|
/* |
|
|
* tkImgPPM.c -- |
|
|
* |
|
|
* A photo image file handler for PPM (Portable PixMap) files. |
|
|
* |
|
|
* Copyright (c) 1994 The Australian National University. |
|
|
* Copyright (c) 1994-1997 Sun Microsystems, Inc. |
|
|
* |
|
|
* See the file "license.terms" for information on usage and redistribution |
|
|
* of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
|
|
* |
|
|
* Author: Paul Mackerras (paulus@cs.anu.edu.au), |
|
|
* Department of Computer Science, |
|
|
* Australian National University. |
|
|
* |
|
|
* RCS: @(#) $Id: tkimgppm.c,v 1.1.1.1 2001/06/13 05:03:30 dtashley Exp $ |
|
|
*/ |
|
|
|
|
|
#define USE_OLD_IMAGE |
|
|
|
|
|
#include "tkInt.h" |
|
|
#include "tkPort.h" |
|
|
|
|
|
/* |
|
|
* The maximum amount of memory to allocate for data read from the |
|
|
* file. If we need more than this, we do it in pieces. |
|
|
*/ |
|
|
|
|
|
#define MAX_MEMORY 10000 /* don't allocate > 10KB */ |
|
|
|
|
|
/* |
|
|
* Define PGM and PPM, i.e. gray images and color images. |
|
|
*/ |
|
|
|
|
|
#define PGM 1 |
|
|
#define PPM 2 |
|
|
|
|
|
/* |
|
|
* The format record for the PPM file format: |
|
|
*/ |
|
|
|
|
|
static int FileMatchPPM _ANSI_ARGS_((Tcl_Channel chan, |
|
|
char *fileName, char *formatString, |
|
|
int *widthPtr, int *heightPtr)); |
|
|
static int FileReadPPM _ANSI_ARGS_((Tcl_Interp *interp, |
|
|
Tcl_Channel chan, char *fileName, |
|
|
char *formatString, Tk_PhotoHandle imageHandle, |
|
|
int destX, int destY, int width, int height, |
|
|
int srcX, int srcY)); |
|
|
static int FileWritePPM _ANSI_ARGS_((Tcl_Interp *interp, |
|
|
char *fileName, char *formatString, |
|
|
Tk_PhotoImageBlock *blockPtr)); |
|
|
|
|
|
Tk_PhotoImageFormat tkImgFmtPPM = { |
|
|
"PPM", /* name */ |
|
|
FileMatchPPM, /* fileMatchProc */ |
|
|
NULL, /* stringMatchProc */ |
|
|
FileReadPPM, /* fileReadProc */ |
|
|
NULL, /* stringReadProc */ |
|
|
FileWritePPM, /* fileWriteProc */ |
|
|
NULL, /* stringWriteProc */ |
|
|
}; |
|
|
|
|
|
/* |
|
|
* Prototypes for local procedures defined in this file: |
|
|
*/ |
|
|
|
|
|
static int ReadPPMFileHeader _ANSI_ARGS_((Tcl_Channel chan, |
|
|
int *widthPtr, int *heightPtr, |
|
|
int *maxIntensityPtr)); |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* FileMatchPPM -- |
|
|
* |
|
|
* This procedure is invoked by the photo image type to see if |
|
|
* a file contains image data in PPM format. |
|
|
* |
|
|
* Results: |
|
|
* The return value is >0 if the first characters in file "f" look |
|
|
* like PPM data, and 0 otherwise. |
|
|
* |
|
|
* Side effects: |
|
|
* The access position in f may change. |
|
|
* |
|
|
*---------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
static int |
|
|
FileMatchPPM(chan, fileName, formatString, widthPtr, heightPtr) |
|
|
Tcl_Channel chan; /* The image file, open for reading. */ |
|
|
char *fileName; /* The name of the image file. */ |
|
|
char *formatString; /* User-specified format string, or NULL. */ |
|
|
int *widthPtr, *heightPtr; /* The dimensions of the image are |
|
|
* returned here if the file is a valid |
|
|
* raw PPM file. */ |
|
|
{ |
|
|
int dummy; |
|
|
|
|
|
return ReadPPMFileHeader(chan, widthPtr, heightPtr, &dummy); |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* FileReadPPM -- |
|
|
* |
|
|
* This procedure is called by the photo image type to read |
|
|
* PPM format data from a file and write it into a given |
|
|
* photo image. |
|
|
* |
|
|
* Results: |
|
|
* A standard TCL completion code. If TCL_ERROR is returned |
|
|
* then an error message is left in the interp's result. |
|
|
* |
|
|
* Side effects: |
|
|
* The access position in file f is changed, and new data is |
|
|
* added to the image given by imageHandle. |
|
|
* |
|
|
*---------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
static int |
|
|
FileReadPPM(interp, chan, fileName, formatString, imageHandle, destX, destY, |
|
|
width, height, srcX, srcY) |
|
|
Tcl_Interp *interp; /* Interpreter to use for reporting errors. */ |
|
|
Tcl_Channel chan; /* The image file, open for reading. */ |
|
|
char *fileName; /* The name of the image file. */ |
|
|
char *formatString; /* User-specified format string, or NULL. */ |
|
|
Tk_PhotoHandle imageHandle; /* The photo image to write into. */ |
|
|
int destX, destY; /* Coordinates of top-left pixel in |
|
|
* photo image to be written to. */ |
|
|
int width, height; /* Dimensions of block of photo image to |
|
|
* be written to. */ |
|
|
int srcX, srcY; /* Coordinates of top-left pixel to be used |
|
|
* in image being read. */ |
|
|
{ |
|
|
int fileWidth, fileHeight, maxIntensity; |
|
|
int nLines, nBytes, h, type, count; |
|
|
unsigned char *pixelPtr; |
|
|
Tk_PhotoImageBlock block; |
|
|
|
|
|
type = ReadPPMFileHeader(chan, &fileWidth, &fileHeight, &maxIntensity); |
|
|
if (type == 0) { |
|
|
Tcl_AppendResult(interp, "couldn't read raw PPM header from file \"", |
|
|
fileName, "\"", NULL); |
|
|
return TCL_ERROR; |
|
|
} |
|
|
if ((fileWidth <= 0) || (fileHeight <= 0)) { |
|
|
Tcl_AppendResult(interp, "PPM image file \"", fileName, |
|
|
"\" has dimension(s) <= 0", (char *) NULL); |
|
|
return TCL_ERROR; |
|
|
} |
|
|
if ((maxIntensity <= 0) || (maxIntensity >= 256)) { |
|
|
char buffer[TCL_INTEGER_SPACE]; |
|
|
|
|
|
sprintf(buffer, "%d", maxIntensity); |
|
|
Tcl_AppendResult(interp, "PPM image file \"", fileName, |
|
|
"\" has bad maximum intensity value ", buffer, |
|
|
(char *) NULL); |
|
|
return TCL_ERROR; |
|
|
} |
|
|
|
|
|
if ((srcX + width) > fileWidth) { |
|
|
width = fileWidth - srcX; |
|
|
} |
|
|
if ((srcY + height) > fileHeight) { |
|
|
height = fileHeight - srcY; |
|
|
} |
|
|
if ((width <= 0) || (height <= 0) |
|
|
|| (srcX >= fileWidth) || (srcY >= fileHeight)) { |
|
|
return TCL_OK; |
|
|
} |
|
|
|
|
|
if (type == PGM) { |
|
|
block.pixelSize = 1; |
|
|
block.offset[0] = 0; |
|
|
block.offset[1] = 0; |
|
|
block.offset[2] = 0; |
|
|
} |
|
|
else { |
|
|
block.pixelSize = 3; |
|
|
block.offset[0] = 0; |
|
|
block.offset[1] = 1; |
|
|
block.offset[2] = 2; |
|
|
} |
|
|
block.offset[3] = 0; |
|
|
block.width = width; |
|
|
block.pitch = block.pixelSize * fileWidth; |
|
|
|
|
|
Tk_PhotoExpand(imageHandle, destX + width, destY + height); |
|
|
|
|
|
if (srcY > 0) { |
|
|
Tcl_Seek(chan, (srcY * block.pitch), SEEK_CUR); |
|
|
} |
|
|
|
|
|
nLines = (MAX_MEMORY + block.pitch - 1) / block.pitch; |
|
|
if (nLines > height) { |
|
|
nLines = height; |
|
|
} |
|
|
if (nLines <= 0) { |
|
|
nLines = 1; |
|
|
} |
|
|
nBytes = nLines * block.pitch; |
|
|
pixelPtr = (unsigned char *) ckalloc((unsigned) nBytes); |
|
|
block.pixelPtr = pixelPtr + srcX * block.pixelSize; |
|
|
|
|
|
for (h = height; h > 0; h -= nLines) { |
|
|
if (nLines > h) { |
|
|
nLines = h; |
|
|
nBytes = nLines * block.pitch; |
|
|
} |
|
|
count = Tcl_Read(chan, (char *) pixelPtr, nBytes); |
|
|
if (count != nBytes) { |
|
|
Tcl_AppendResult(interp, "error reading PPM image file \"", |
|
|
fileName, "\": ", |
|
|
Tcl_Eof(chan) ? "not enough data" : Tcl_PosixError(interp), |
|
|
(char *) NULL); |
|
|
ckfree((char *) pixelPtr); |
|
|
return TCL_ERROR; |
|
|
} |
|
|
if (maxIntensity != 255) { |
|
|
unsigned char *p; |
|
|
|
|
|
for (p = pixelPtr; count > 0; count--, p++) { |
|
|
*p = (((int) *p) * 255)/maxIntensity; |
|
|
} |
|
|
} |
|
|
block.height = nLines; |
|
|
Tk_PhotoPutBlock(imageHandle, &block, destX, destY, width, nLines); |
|
|
destY += nLines; |
|
|
} |
|
|
|
|
|
ckfree((char *) pixelPtr); |
|
|
return TCL_OK; |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* FileWritePPM -- |
|
|
* |
|
|
* This procedure is invoked to write image data to a file in PPM |
|
|
* format. |
|
|
* |
|
|
* Results: |
|
|
* A standard TCL completion code. If TCL_ERROR is returned |
|
|
* then an error message is left in the interp's result. |
|
|
* |
|
|
* Side effects: |
|
|
* Data is written to the file given by "fileName". |
|
|
* |
|
|
*---------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
static int |
|
|
FileWritePPM(interp, fileName, formatString, blockPtr) |
|
|
Tcl_Interp *interp; |
|
|
char *fileName; |
|
|
char *formatString; |
|
|
Tk_PhotoImageBlock *blockPtr; |
|
|
{ |
|
|
Tcl_Channel chan; |
|
|
int w, h; |
|
|
int greenOffset, blueOffset, nBytes; |
|
|
unsigned char *pixelPtr, *pixLinePtr; |
|
|
char header[16 + TCL_INTEGER_SPACE * 2]; |
|
|
|
|
|
chan = Tcl_OpenFileChannel(interp, fileName, "w", 0666); |
|
|
if (chan == NULL) { |
|
|
return TCL_ERROR; |
|
|
} |
|
|
|
|
|
if (Tcl_SetChannelOption(interp, chan, "-translation", "binary") |
|
|
!= TCL_OK) { |
|
|
return TCL_ERROR; |
|
|
} |
|
|
if (Tcl_SetChannelOption(interp, chan, "-encoding", "binary") |
|
|
!= TCL_OK) { |
|
|
return TCL_ERROR; |
|
|
} |
|
|
|
|
|
sprintf(header, "P6\n%d %d\n255\n", blockPtr->width, blockPtr->height); |
|
|
Tcl_Write(chan, header, -1); |
|
|
|
|
|
pixLinePtr = blockPtr->pixelPtr + blockPtr->offset[0]; |
|
|
greenOffset = blockPtr->offset[1] - blockPtr->offset[0]; |
|
|
blueOffset = blockPtr->offset[2] - blockPtr->offset[0]; |
|
|
|
|
|
if ((greenOffset == 1) && (blueOffset == 2) && (blockPtr->pixelSize == 3) |
|
|
&& (blockPtr->pitch == (blockPtr->width * 3))) { |
|
|
nBytes = blockPtr->height * blockPtr->pitch; |
|
|
if (Tcl_Write(chan, (char *) pixLinePtr, nBytes) != nBytes) { |
|
|
goto writeerror; |
|
|
} |
|
|
} else { |
|
|
for (h = blockPtr->height; h > 0; h--) { |
|
|
pixelPtr = pixLinePtr; |
|
|
for (w = blockPtr->width; w > 0; w--) { |
|
|
if ((Tcl_Write(chan, (char *) &pixelPtr[0], 1) == -1) |
|
|
|| (Tcl_Write(chan, (char *) &pixelPtr[greenOffset], 1) == -1) |
|
|
|| (Tcl_Write(chan, (char *) &pixelPtr[blueOffset], 1) == -1)) { |
|
|
goto writeerror; |
|
|
} |
|
|
pixelPtr += blockPtr->pixelSize; |
|
|
} |
|
|
pixLinePtr += blockPtr->pitch; |
|
|
} |
|
|
} |
|
|
|
|
|
if (Tcl_Close(NULL, chan) == 0) { |
|
|
return TCL_OK; |
|
|
} |
|
|
chan = NULL; |
|
|
|
|
|
writeerror: |
|
|
Tcl_AppendResult(interp, "error writing \"", fileName, "\": ", |
|
|
Tcl_PosixError(interp), (char *) NULL); |
|
|
if (chan != NULL) { |
|
|
Tcl_Close(NULL, chan); |
|
|
} |
|
|
return TCL_ERROR; |
|
|
} |
|
|
|
|
|
/* |
|
|
*---------------------------------------------------------------------- |
|
|
* |
|
|
* ReadPPMFileHeader -- |
|
|
* |
|
|
* This procedure reads the PPM header from the beginning of a |
|
|
* PPM file and returns information from the header. |
|
|
* |
|
|
* Results: |
|
|
* The return value is PGM if file "f" appears to start with |
|
|
* a valid PGM header, PPM if "f" appears to start with a valid |
|
|
* PPM header, and 0 otherwise. If the header is valid, |
|
|
* then *widthPtr and *heightPtr are modified to hold the |
|
|
* dimensions of the image and *maxIntensityPtr is modified to |
|
|
* hold the value of a "fully on" intensity value. |
|
|
* |
|
|
* Side effects: |
|
|
* The access position in f advances. |
|
|
* |
|
|
*---------------------------------------------------------------------- |
|
|
*/ |
|
|
|
|
|
static int |
|
|
ReadPPMFileHeader(chan, widthPtr, heightPtr, maxIntensityPtr) |
|
|
Tcl_Channel chan; /* Image file to read the header from */ |
|
|
int *widthPtr, *heightPtr; /* The dimensions of the image are |
|
|
* returned here. */ |
|
|
int *maxIntensityPtr; /* The maximum intensity value for |
|
|
* the image is stored here. */ |
|
|
{ |
|
|
#define BUFFER_SIZE 1000 |
|
|
char buffer[BUFFER_SIZE]; |
|
|
int i, numFields; |
|
|
int type = 0; |
|
|
char c; |
|
|
|
|
|
/* |
|
|
* Read 4 space-separated fields from the file, ignoring |
|
|
* comments (any line that starts with "#"). |
|
|
*/ |
|
|
|
|
|
if (Tcl_Read(chan, &c, 1) != 1) { |
|
|
return 0; |
|
|
} |
|
|
i = 0; |
|
|
for (numFields = 0; numFields < 4; numFields++) { |
|
|
/* |
|
|
* Skip comments and white space. |
|
|
*/ |
|
|
|
|
|
while (1) { |
|
|
while (isspace(UCHAR(c))) { |
|
|
if (Tcl_Read(chan, &c, 1) != 1) { |
|
|
return 0; |
|
|
} |
|
|
} |
|
|
if (c != '#') { |
|
|
break; |
|
|
} |
|
|
do { |
|
|
if (Tcl_Read(chan, &c, 1) != 1) { |
|
|
return 0; |
|
|
} |
|
|
} while (c != '\n'); |
|
|
} |
|
|
|
|
|
/* |
|
|
* Read a field (everything up to the next white space). |
|
|
*/ |
|
|
|
|
|
while (!isspace(UCHAR(c))) { |
|
|
if (i < (BUFFER_SIZE-2)) { |
|
|
buffer[i] = c; |
|
|
i++; |
|
|
} |
|
|
if (Tcl_Read(chan, &c, 1) != 1) { |
|
|
goto done; |
|
|
} |
|
|
} |
|
|
if (i < (BUFFER_SIZE-1)) { |
|
|
buffer[i] = ' '; |
|
|
i++; |
|
|
} |
|
|
} |
|
|
done: |
|
|
buffer[i] = 0; |
|
|
|
|
|
/* |
|
|
* Parse the fields, which are: id, width, height, maxIntensity. |
|
|
*/ |
|
|
|
|
|
if (strncmp(buffer, "P6 ", 3) == 0) { |
|
|
type = PPM; |
|
|
} else if (strncmp(buffer, "P5 ", 3) == 0) { |
|
|
type = PGM; |
|
|
} else { |
|
|
return 0; |
|
|
} |
|
|
if (sscanf(buffer+3, "%d %d %d", widthPtr, heightPtr, maxIntensityPtr) |
|
|
!= 3) { |
|
|
return 0; |
|
|
} |
|
|
return type; |
|
|
} |
|
|
|
|
|
|
|
|
/* $History: tkImgPPM.c $ |
|
|
* |
|
|
* ***************** Version 1 ***************** |
|
|
* User: Dtashley Date: 1/02/01 Time: 2:53a |
|
|
* Created in $/IjuScripter, IjuConsole/Source/Tk Base |
|
|
* Initial check-in. |
|
|
*/ |
|
|
|
|
|
/* End of TKIMGPPM.C */ |
|
1 |
|
/* $Header$ */ |
2 |
|
|
3 |
|
/* |
4 |
|
* tkImgPPM.c -- |
5 |
|
* |
6 |
|
* A photo image file handler for PPM (Portable PixMap) files. |
7 |
|
* |
8 |
|
* Copyright (c) 1994 The Australian National University. |
9 |
|
* Copyright (c) 1994-1997 Sun Microsystems, Inc. |
10 |
|
* |
11 |
|
* See the file "license.terms" for information on usage and redistribution |
12 |
|
* of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
13 |
|
* |
14 |
|
* Author: Paul Mackerras (paulus@cs.anu.edu.au), |
15 |
|
* Department of Computer Science, |
16 |
|
* Australian National University. |
17 |
|
* |
18 |
|
* RCS: @(#) $Id: tkimgppm.c,v 1.1.1.1 2001/06/13 05:03:30 dtashley Exp $ |
19 |
|
*/ |
20 |
|
|
21 |
|
#define USE_OLD_IMAGE |
22 |
|
|
23 |
|
#include "tkInt.h" |
24 |
|
#include "tkPort.h" |
25 |
|
|
26 |
|
/* |
27 |
|
* The maximum amount of memory to allocate for data read from the |
28 |
|
* file. If we need more than this, we do it in pieces. |
29 |
|
*/ |
30 |
|
|
31 |
|
#define MAX_MEMORY 10000 /* don't allocate > 10KB */ |
32 |
|
|
33 |
|
/* |
34 |
|
* Define PGM and PPM, i.e. gray images and color images. |
35 |
|
*/ |
36 |
|
|
37 |
|
#define PGM 1 |
38 |
|
#define PPM 2 |
39 |
|
|
40 |
|
/* |
41 |
|
* The format record for the PPM file format: |
42 |
|
*/ |
43 |
|
|
44 |
|
static int FileMatchPPM _ANSI_ARGS_((Tcl_Channel chan, |
45 |
|
char *fileName, char *formatString, |
46 |
|
int *widthPtr, int *heightPtr)); |
47 |
|
static int FileReadPPM _ANSI_ARGS_((Tcl_Interp *interp, |
48 |
|
Tcl_Channel chan, char *fileName, |
49 |
|
char *formatString, Tk_PhotoHandle imageHandle, |
50 |
|
int destX, int destY, int width, int height, |
51 |
|
int srcX, int srcY)); |
52 |
|
static int FileWritePPM _ANSI_ARGS_((Tcl_Interp *interp, |
53 |
|
char *fileName, char *formatString, |
54 |
|
Tk_PhotoImageBlock *blockPtr)); |
55 |
|
|
56 |
|
Tk_PhotoImageFormat tkImgFmtPPM = { |
57 |
|
"PPM", /* name */ |
58 |
|
FileMatchPPM, /* fileMatchProc */ |
59 |
|
NULL, /* stringMatchProc */ |
60 |
|
FileReadPPM, /* fileReadProc */ |
61 |
|
NULL, /* stringReadProc */ |
62 |
|
FileWritePPM, /* fileWriteProc */ |
63 |
|
NULL, /* stringWriteProc */ |
64 |
|
}; |
65 |
|
|
66 |
|
/* |
67 |
|
* Prototypes for local procedures defined in this file: |
68 |
|
*/ |
69 |
|
|
70 |
|
static int ReadPPMFileHeader _ANSI_ARGS_((Tcl_Channel chan, |
71 |
|
int *widthPtr, int *heightPtr, |
72 |
|
int *maxIntensityPtr)); |
73 |
|
|
74 |
|
/* |
75 |
|
*---------------------------------------------------------------------- |
76 |
|
* |
77 |
|
* FileMatchPPM -- |
78 |
|
* |
79 |
|
* This procedure is invoked by the photo image type to see if |
80 |
|
* a file contains image data in PPM format. |
81 |
|
* |
82 |
|
* Results: |
83 |
|
* The return value is >0 if the first characters in file "f" look |
84 |
|
* like PPM data, and 0 otherwise. |
85 |
|
* |
86 |
|
* Side effects: |
87 |
|
* The access position in f may change. |
88 |
|
* |
89 |
|
*---------------------------------------------------------------------- |
90 |
|
*/ |
91 |
|
|
92 |
|
static int |
93 |
|
FileMatchPPM(chan, fileName, formatString, widthPtr, heightPtr) |
94 |
|
Tcl_Channel chan; /* The image file, open for reading. */ |
95 |
|
char *fileName; /* The name of the image file. */ |
96 |
|
char *formatString; /* User-specified format string, or NULL. */ |
97 |
|
int *widthPtr, *heightPtr; /* The dimensions of the image are |
98 |
|
* returned here if the file is a valid |
99 |
|
* raw PPM file. */ |
100 |
|
{ |
101 |
|
int dummy; |
102 |
|
|
103 |
|
return ReadPPMFileHeader(chan, widthPtr, heightPtr, &dummy); |
104 |
|
} |
105 |
|
|
106 |
|
/* |
107 |
|
*---------------------------------------------------------------------- |
108 |
|
* |
109 |
|
* FileReadPPM -- |
110 |
|
* |
111 |
|
* This procedure is called by the photo image type to read |
112 |
|
* PPM format data from a file and write it into a given |
113 |
|
* photo image. |
114 |
|
* |
115 |
|
* Results: |
116 |
|
* A standard TCL completion code. If TCL_ERROR is returned |
117 |
|
* then an error message is left in the interp's result. |
118 |
|
* |
119 |
|
* Side effects: |
120 |
|
* The access position in file f is changed, and new data is |
121 |
|
* added to the image given by imageHandle. |
122 |
|
* |
123 |
|
*---------------------------------------------------------------------- |
124 |
|
*/ |
125 |
|
|
126 |
|
static int |
127 |
|
FileReadPPM(interp, chan, fileName, formatString, imageHandle, destX, destY, |
128 |
|
width, height, srcX, srcY) |
129 |
|
Tcl_Interp *interp; /* Interpreter to use for reporting errors. */ |
130 |
|
Tcl_Channel chan; /* The image file, open for reading. */ |
131 |
|
char *fileName; /* The name of the image file. */ |
132 |
|
char *formatString; /* User-specified format string, or NULL. */ |
133 |
|
Tk_PhotoHandle imageHandle; /* The photo image to write into. */ |
134 |
|
int destX, destY; /* Coordinates of top-left pixel in |
135 |
|
* photo image to be written to. */ |
136 |
|
int width, height; /* Dimensions of block of photo image to |
137 |
|
* be written to. */ |
138 |
|
int srcX, srcY; /* Coordinates of top-left pixel to be used |
139 |
|
* in image being read. */ |
140 |
|
{ |
141 |
|
int fileWidth, fileHeight, maxIntensity; |
142 |
|
int nLines, nBytes, h, type, count; |
143 |
|
unsigned char *pixelPtr; |
144 |
|
Tk_PhotoImageBlock block; |
145 |
|
|
146 |
|
type = ReadPPMFileHeader(chan, &fileWidth, &fileHeight, &maxIntensity); |
147 |
|
if (type == 0) { |
148 |
|
Tcl_AppendResult(interp, "couldn't read raw PPM header from file \"", |
149 |
|
fileName, "\"", NULL); |
150 |
|
return TCL_ERROR; |
151 |
|
} |
152 |
|
if ((fileWidth <= 0) || (fileHeight <= 0)) { |
153 |
|
Tcl_AppendResult(interp, "PPM image file \"", fileName, |
154 |
|
"\" has dimension(s) <= 0", (char *) NULL); |
155 |
|
return TCL_ERROR; |
156 |
|
} |
157 |
|
if ((maxIntensity <= 0) || (maxIntensity >= 256)) { |
158 |
|
char buffer[TCL_INTEGER_SPACE]; |
159 |
|
|
160 |
|
sprintf(buffer, "%d", maxIntensity); |
161 |
|
Tcl_AppendResult(interp, "PPM image file \"", fileName, |
162 |
|
"\" has bad maximum intensity value ", buffer, |
163 |
|
(char *) NULL); |
164 |
|
return TCL_ERROR; |
165 |
|
} |
166 |
|
|
167 |
|
if ((srcX + width) > fileWidth) { |
168 |
|
width = fileWidth - srcX; |
169 |
|
} |
170 |
|
if ((srcY + height) > fileHeight) { |
171 |
|
height = fileHeight - srcY; |
172 |
|
} |
173 |
|
if ((width <= 0) || (height <= 0) |
174 |
|
|| (srcX >= fileWidth) || (srcY >= fileHeight)) { |
175 |
|
return TCL_OK; |
176 |
|
} |
177 |
|
|
178 |
|
if (type == PGM) { |
179 |
|
block.pixelSize = 1; |
180 |
|
block.offset[0] = 0; |
181 |
|
block.offset[1] = 0; |
182 |
|
block.offset[2] = 0; |
183 |
|
} |
184 |
|
else { |
185 |
|
block.pixelSize = 3; |
186 |
|
block.offset[0] = 0; |
187 |
|
block.offset[1] = 1; |
188 |
|
block.offset[2] = 2; |
189 |
|
} |
190 |
|
block.offset[3] = 0; |
191 |
|
block.width = width; |
192 |
|
block.pitch = block.pixelSize * fileWidth; |
193 |
|
|
194 |
|
Tk_PhotoExpand(imageHandle, destX + width, destY + height); |
195 |
|
|
196 |
|
if (srcY > 0) { |
197 |
|
Tcl_Seek(chan, (srcY * block.pitch), SEEK_CUR); |
198 |
|
} |
199 |
|
|
200 |
|
nLines = (MAX_MEMORY + block.pitch - 1) / block.pitch; |
201 |
|
if (nLines > height) { |
202 |
|
nLines = height; |
203 |
|
} |
204 |
|
if (nLines <= 0) { |
205 |
|
nLines = 1; |
206 |
|
} |
207 |
|
nBytes = nLines * block.pitch; |
208 |
|
pixelPtr = (unsigned char *) ckalloc((unsigned) nBytes); |
209 |
|
block.pixelPtr = pixelPtr + srcX * block.pixelSize; |
210 |
|
|
211 |
|
for (h = height; h > 0; h -= nLines) { |
212 |
|
if (nLines > h) { |
213 |
|
nLines = h; |
214 |
|
nBytes = nLines * block.pitch; |
215 |
|
} |
216 |
|
count = Tcl_Read(chan, (char *) pixelPtr, nBytes); |
217 |
|
if (count != nBytes) { |
218 |
|
Tcl_AppendResult(interp, "error reading PPM image file \"", |
219 |
|
fileName, "\": ", |
220 |
|
Tcl_Eof(chan) ? "not enough data" : Tcl_PosixError(interp), |
221 |
|
(char *) NULL); |
222 |
|
ckfree((char *) pixelPtr); |
223 |
|
return TCL_ERROR; |
224 |
|
} |
225 |
|
if (maxIntensity != 255) { |
226 |
|
unsigned char *p; |
227 |
|
|
228 |
|
for (p = pixelPtr; count > 0; count--, p++) { |
229 |
|
*p = (((int) *p) * 255)/maxIntensity; |
230 |
|
} |
231 |
|
} |
232 |
|
block.height = nLines; |
233 |
|
Tk_PhotoPutBlock(imageHandle, &block, destX, destY, width, nLines); |
234 |
|
destY += nLines; |
235 |
|
} |
236 |
|
|
237 |
|
ckfree((char *) pixelPtr); |
238 |
|
return TCL_OK; |
239 |
|
} |
240 |
|
|
241 |
|
/* |
242 |
|
*---------------------------------------------------------------------- |
243 |
|
* |
244 |
|
* FileWritePPM -- |
245 |
|
* |
246 |
|
* This procedure is invoked to write image data to a file in PPM |
247 |
|
* format. |
248 |
|
* |
249 |
|
* Results: |
250 |
|
* A standard TCL completion code. If TCL_ERROR is returned |
251 |
|
* then an error message is left in the interp's result. |
252 |
|
* |
253 |
|
* Side effects: |
254 |
|
* Data is written to the file given by "fileName". |
255 |
|
* |
256 |
|
*---------------------------------------------------------------------- |
257 |
|
*/ |
258 |
|
|
259 |
|
static int |
260 |
|
FileWritePPM(interp, fileName, formatString, blockPtr) |
261 |
|
Tcl_Interp *interp; |
262 |
|
char *fileName; |
263 |
|
char *formatString; |
264 |
|
Tk_PhotoImageBlock *blockPtr; |
265 |
|
{ |
266 |
|
Tcl_Channel chan; |
267 |
|
int w, h; |
268 |
|
int greenOffset, blueOffset, nBytes; |
269 |
|
unsigned char *pixelPtr, *pixLinePtr; |
270 |
|
char header[16 + TCL_INTEGER_SPACE * 2]; |
271 |
|
|
272 |
|
chan = Tcl_OpenFileChannel(interp, fileName, "w", 0666); |
273 |
|
if (chan == NULL) { |
274 |
|
return TCL_ERROR; |
275 |
|
} |
276 |
|
|
277 |
|
if (Tcl_SetChannelOption(interp, chan, "-translation", "binary") |
278 |
|
!= TCL_OK) { |
279 |
|
return TCL_ERROR; |
280 |
|
} |
281 |
|
if (Tcl_SetChannelOption(interp, chan, "-encoding", "binary") |
282 |
|
!= TCL_OK) { |
283 |
|
return TCL_ERROR; |
284 |
|
} |
285 |
|
|
286 |
|
sprintf(header, "P6\n%d %d\n255\n", blockPtr->width, blockPtr->height); |
287 |
|
Tcl_Write(chan, header, -1); |
288 |
|
|
289 |
|
pixLinePtr = blockPtr->pixelPtr + blockPtr->offset[0]; |
290 |
|
greenOffset = blockPtr->offset[1] - blockPtr->offset[0]; |
291 |
|
blueOffset = blockPtr->offset[2] - blockPtr->offset[0]; |
292 |
|
|
293 |
|
if ((greenOffset == 1) && (blueOffset == 2) && (blockPtr->pixelSize == 3) |
294 |
|
&& (blockPtr->pitch == (blockPtr->width * 3))) { |
295 |
|
nBytes = blockPtr->height * blockPtr->pitch; |
296 |
|
if (Tcl_Write(chan, (char *) pixLinePtr, nBytes) != nBytes) { |
297 |
|
goto writeerror; |
298 |
|
} |
299 |
|
} else { |
300 |
|
for (h = blockPtr->height; h > 0; h--) { |
301 |
|
pixelPtr = pixLinePtr; |
302 |
|
for (w = blockPtr->width; w > 0; w--) { |
303 |
|
if ((Tcl_Write(chan, (char *) &pixelPtr[0], 1) == -1) |
304 |
|
|| (Tcl_Write(chan, (char *) &pixelPtr[greenOffset], 1) == -1) |
305 |
|
|| (Tcl_Write(chan, (char *) &pixelPtr[blueOffset], 1) == -1)) { |
306 |
|
goto writeerror; |
307 |
|
} |
308 |
|
pixelPtr += blockPtr->pixelSize; |
309 |
|
} |
310 |
|
pixLinePtr += blockPtr->pitch; |
311 |
|
} |
312 |
|
} |
313 |
|
|
314 |
|
if (Tcl_Close(NULL, chan) == 0) { |
315 |
|
return TCL_OK; |
316 |
|
} |
317 |
|
chan = NULL; |
318 |
|
|
319 |
|
writeerror: |
320 |
|
Tcl_AppendResult(interp, "error writing \"", fileName, "\": ", |
321 |
|
Tcl_PosixError(interp), (char *) NULL); |
322 |
|
if (chan != NULL) { |
323 |
|
Tcl_Close(NULL, chan); |
324 |
|
} |
325 |
|
return TCL_ERROR; |
326 |
|
} |
327 |
|
|
328 |
|
/* |
329 |
|
*---------------------------------------------------------------------- |
330 |
|
* |
331 |
|
* ReadPPMFileHeader -- |
332 |
|
* |
333 |
|
* This procedure reads the PPM header from the beginning of a |
334 |
|
* PPM file and returns information from the header. |
335 |
|
* |
336 |
|
* Results: |
337 |
|
* The return value is PGM if file "f" appears to start with |
338 |
|
* a valid PGM header, PPM if "f" appears to start with a valid |
339 |
|
* PPM header, and 0 otherwise. If the header is valid, |
340 |
|
* then *widthPtr and *heightPtr are modified to hold the |
341 |
|
* dimensions of the image and *maxIntensityPtr is modified to |
342 |
|
* hold the value of a "fully on" intensity value. |
343 |
|
* |
344 |
|
* Side effects: |
345 |
|
* The access position in f advances. |
346 |
|
* |
347 |
|
*---------------------------------------------------------------------- |
348 |
|
*/ |
349 |
|
|
350 |
|
static int |
351 |
|
ReadPPMFileHeader(chan, widthPtr, heightPtr, maxIntensityPtr) |
352 |
|
Tcl_Channel chan; /* Image file to read the header from */ |
353 |
|
int *widthPtr, *heightPtr; /* The dimensions of the image are |
354 |
|
* returned here. */ |
355 |
|
int *maxIntensityPtr; /* The maximum intensity value for |
356 |
|
* the image is stored here. */ |
357 |
|
{ |
358 |
|
#define BUFFER_SIZE 1000 |
359 |
|
char buffer[BUFFER_SIZE]; |
360 |
|
int i, numFields; |
361 |
|
int type = 0; |
362 |
|
char c; |
363 |
|
|
364 |
|
/* |
365 |
|
* Read 4 space-separated fields from the file, ignoring |
366 |
|
* comments (any line that starts with "#"). |
367 |
|
*/ |
368 |
|
|
369 |
|
if (Tcl_Read(chan, &c, 1) != 1) { |
370 |
|
return 0; |
371 |
|
} |
372 |
|
i = 0; |
373 |
|
for (numFields = 0; numFields < 4; numFields++) { |
374 |
|
/* |
375 |
|
* Skip comments and white space. |
376 |
|
*/ |
377 |
|
|
378 |
|
while (1) { |
379 |
|
while (isspace(UCHAR(c))) { |
380 |
|
if (Tcl_Read(chan, &c, 1) != 1) { |
381 |
|
return 0; |
382 |
|
} |
383 |
|
} |
384 |
|
if (c != '#') { |
385 |
|
break; |
386 |
|
} |
387 |
|
do { |
388 |
|
if (Tcl_Read(chan, &c, 1) != 1) { |
389 |
|
return 0; |
390 |
|
} |
391 |
|
} while (c != '\n'); |
392 |
|
} |
393 |
|
|
394 |
|
/* |
395 |
|
* Read a field (everything up to the next white space). |
396 |
|
*/ |
397 |
|
|
398 |
|
while (!isspace(UCHAR(c))) { |
399 |
|
if (i < (BUFFER_SIZE-2)) { |
400 |
|
buffer[i] = c; |
401 |
|
i++; |
402 |
|
} |
403 |
|
if (Tcl_Read(chan, &c, 1) != 1) { |
404 |
|
goto done; |
405 |
|
} |
406 |
|
} |
407 |
|
if (i < (BUFFER_SIZE-1)) { |
408 |
|
buffer[i] = ' '; |
409 |
|
i++; |
410 |
|
} |
411 |
|
} |
412 |
|
done: |
413 |
|
buffer[i] = 0; |
414 |
|
|
415 |
|
/* |
416 |
|
* Parse the fields, which are: id, width, height, maxIntensity. |
417 |
|
*/ |
418 |
|
|
419 |
|
if (strncmp(buffer, "P6 ", 3) == 0) { |
420 |
|
type = PPM; |
421 |
|
} else if (strncmp(buffer, "P5 ", 3) == 0) { |
422 |
|
type = PGM; |
423 |
|
} else { |
424 |
|
return 0; |
425 |
|
} |
426 |
|
if (sscanf(buffer+3, "%d %d %d", widthPtr, heightPtr, maxIntensityPtr) |
427 |
|
!= 3) { |
428 |
|
return 0; |
429 |
|
} |
430 |
|
return type; |
431 |
|
} |
432 |
|
|
433 |
|
/* End of tkimgppm.c */ |