/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