/Users/lyon/j4p/src/ip/ppm/PpmDecoder.java
|
1 /*
2 * @author Douglas A. Lyon
3 * @version Oct 12, 2002.11:04:30 AM
4 */
5 package ip.ppm;
6
7
8 // PpmDecoder - read in a PPM image
9 //
10 // Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
11 //
12 // Redistribution and use in source and binary forms, with or without
13 // modification, are permitted provided that the following conditions
14 // are met:
15 // 1. Redistributions of source code must retain the above copyright
16 // notice, this list of conditions and the following disclaimer.
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 // SUCH DAMAGE.
32 //
33 // Visit the ACME Labs Java page for up-to-date versions of this and other
34 // fine Java utilities: http://www.acme.com/java/
35
36
37 import java.io.EOFException;
38 import java.io.IOException;
39 import java.io.InputStream;
40
41 /// Read in a PPM image.
42 // <P>
43 // <A HREF="/resources/classes/Acme/JPM/Decoders/PpmDecoder.java">Fetch the software.</A><BR>
44 // <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
45 // <P>
46 // @see Acme.JPM.Encoders.PpmEncoder
47
48 public class PpmDecoder extends ImageDecoder {
49
50 /// Constructor.
51 // @param in The stream to read the bytes from.
52 public PpmDecoder(InputStream in) {
53 super(in);
54 }
55
56
57 private int type;
58 private static final int PBM_ASCII = 1;
59 private static final int PGM_ASCII = 2;
60 private static final int PPM_ASCII = 3;
61 private static final int PBM_RAW = 4;
62 private static final int PGM_RAW = 5;
63 private static final int PPM_RAW = 6;
64
65 private int width = -1, height = -1;
66 private int maxval;
67
68 /// Subclasses implement this to read in enough of the image stream
69 // to figure out the width and height.
70 void readHeader(InputStream in) throws IOException {
71 char c1, c2;
72
73 c1 = (char) readByte(in);
74 c2 = (char) readByte(in);
75
76 if (c1 != 'P')
77 throw new IOException("not a PBM/PGM/PPM file");
78 switch (c2) {
79 case '1':
80 type = PBM_ASCII;
81 break;
82 case '2':
83 type = PGM_ASCII;
84 break;
85 case '3':
86 type = PPM_ASCII;
87 break;
88 case '4':
89 type = PBM_RAW;
90 break;
91 case '5':
92 type = PGM_RAW;
93 break;
94 case '6':
95 type = PPM_RAW;
96 break;
97 default:
98 throw new IOException("not a standard PBM/PGM/PPM file");
99 }
100 width = readInt(in);
101 height = readInt(in);
102 if (type != PBM_ASCII && type != PBM_RAW)
103 maxval = readInt(in);
104 }
105
106 /// Subclasses implement this to return the width, or -1 if not known.
107 int getWidth() {
108 return width;
109 }
110
111 /// Subclasses implement this to return the height, or -1 if not known.
112 int getHeight() {
113 return height;
114 }
115
116 /// Subclasses implement this to read pixel data into the rgbRow
117 // array, an int[width]. One int per pixel, no offsets or padding,
118 // RGBdefault (AARRGGBB) color model
119 void readRow(InputStream in, int row, int[] rgbRow) throws IOException {
120 int col, r, g, b;
121 int rgb = 0;
122 char c;
123
124 for (col = 0; col < width; ++col) {
125 switch (type) {
126 case PBM_ASCII:
127 c = readChar(in);
128 if (c == '1')
129 rgb = 0xff000000;
130 else if (c == '0')
131 rgb = 0xffffffff;
132 else
133 throw new IOException("illegal PBM bit");
134 break;
135 case PGM_ASCII:
136 g = readInt(in);
137 rgb = makeRgb(g, g, g);
138 break;
139 case PPM_ASCII:
140 r = readInt(in);
141 g = readInt(in);
142 b = readInt(in);
143 rgb = makeRgb(r, g, b);
144 break;
145 case PBM_RAW:
146 if (readBit(in))
147 rgb = 0xff000000;
148 else
149 rgb = 0xffffffff;
150 break;
151 case PGM_RAW:
152 g = readByte(in);
153 if (maxval != 255)
154 g = fixDepth(g);
155 rgb = makeRgb(g, g, g);
156 break;
157 case PPM_RAW:
158 r = readByte(in);
159 g = readByte(in);
160 b = readByte(in);
161 if (maxval != 255) {
162 r = fixDepth(r);
163 g = fixDepth(g);
164 b = fixDepth(b);
165 }
166 rgb = makeRgb(r, g, b);
167 break;
168 }
169 rgbRow[col] = rgb;
170 }
171 }
172
173 /// Utility routine to read a byte. Instead of returning -1 on
174 // EOF, it throws an exception.
175 private static int readByte(InputStream in) throws IOException {
176 int b = in.read();
177 if (b == -1)
178 throw new EOFException();
179 return b;
180 }
181
182 private int bitshift = -1;
183 private int bits;
184
185 /// Utility routine to read a bit, packed eight to a byte, big-endian.
186 private boolean readBit(InputStream in) throws IOException {
187 if (bitshift == -1) {
188 bits = readByte(in);
189 bitshift = 7;
190 }
191 boolean bit = (((bits >> bitshift) & 1) != 0);
192 --bitshift;
193 return bit;
194 }
195
196 /// Utility routine to read a character, ignoring comments.
197 private static char readChar(InputStream in) throws IOException {
198 char c;
199
200 c = (char) readByte(in);
201 if (c == '#') {
202 do {
203 c = (char) readByte(in);
204 } while (c != '\n' && c != '\r');
205 }
206
207 return c;
208 }
209
210 /// Utility routine to read the first non-whitespace character.
211 private static char readNonwhiteChar(InputStream in) throws IOException {
212 char c;
213
214 do {
215 c = readChar(in);
216 } while (c == ' ' || c == '\t' || c == '\n' || c == '\r');
217
218 return c;
219 }
220
221 /// Utility routine to read an ASCII integer, ignoring comments.
222 private static int readInt(InputStream in) throws IOException {
223 char c;
224 int i;
225
226 c = readNonwhiteChar(in);
227 if (c < '0' || c > '9')
228 throw new IOException("junk in file where integer should be");
229
230 i = 0;
231 do {
232 i = i * 10 + c - '0';
233 c = readChar(in);
234 } while (c >= '0' && c <= '9');
235
236 return i;
237 }
238
239 /// Utility routine to rescale a pixel value from a non-eight-bit maxval.
240 private int fixDepth(int p) {
241 return (p * 255 + maxval / 2) / maxval;
242 }
243
244 /// Utility routine make an RGBdefault pixel from three color values.
245 private static int makeRgb(int r, int g, int b) {
246 return 0xff000000 | (r << 16) | (g << 8) | b;
247 }
248
249 }
250
251