/Users/lyon/j4p/src/ip/ppm/ImageDecoder.java

1    /* 
2     * @author Douglas A. Lyon 
3     * @version  Oct 12, 2002.10:57:23 AM 
4     */ 
5    package ip.ppm; 
6     
7    import java.awt.image.ColorModel; 
8    import java.awt.image.ImageConsumer; 
9    import java.awt.image.ImageProducer; 
10   import java.io.IOException; 
11   import java.io.InputStream; 
12   import java.util.Vector; 
13    
14    
15   // ImageDecoder - abstract class for reading in an image 
16   // 
17   // Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>.  All rights reserved. 
18   // 
19   // Redistribution and use in source and binary forms, with or without 
20   // modification, are permitted provided that the following conditions 
21   // are met: 
22   // 1. Redistributions of source code must retain the above copyright 
23   //    notice, this list of conditions and the following disclaimer. 
24   // 2. Redistributions in binary form must reproduce the above copyright 
25   //    notice, this list of conditions and the following disclaimer in the 
26   //    documentation and/or other materials provided with the distribution. 
27   // 
28   // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 
29   // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
30   // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
31   // ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 
32   // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
33   // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
34   // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
35   // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
36   // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
37   // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
38   // SUCH DAMAGE. 
39   // 
40   // Visit the ACME Labs Java page for up-to-date versions of this and other 
41   // fine Java utilities: http://www.acme.com/java/ 
42    
43    
44    
45   /// Abstract class for reading in an image. 
46   // <P> 
47   // A framework for classes that read in and decode an image in 
48   // a particular file format. 
49   // <P> 
50   // This provides a very simplified rendition of the ImageProducer interface. 
51   // It requires the decoder to read the image a row at a time.  It requires 
52   // use of the RGBdefault color model. 
53   // If you want more flexibility you can always implement ImageProducer 
54   // directly. 
55   // <P> 
56   // <A HREF="/resources/classes/Acme/JPM/Decoders/ImageDecoder.java">Fetch the software.</A><BR> 
57   // <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A> 
58   // <P> 
59   // @see PpmDecoder 
60   // @see Acme.JPM.Encoders.ImageEncoder 
61    
62   public abstract class ImageDecoder implements ImageProducer { 
63    
64       private InputStream in; 
65       private int width, height; 
66       private boolean[] rowsRead; 
67       private int[][] rgbPixels; 
68       private boolean startedRead = false; 
69       private boolean gotSize = false; 
70       private boolean err = false; 
71       private boolean producing = false; 
72       private Vector consumers = new Vector(); 
73       private static final ColorModel model = ColorModel.getRGBdefault(); 
74    
75    
76       /// Constructor. 
77       // @param in The stream to read the bytes from. 
78       public ImageDecoder(InputStream in) { 
79           this.in = in; 
80       } 
81    
82    
83       // Methods that subclasses implement. 
84    
85       /// Subclasses implement this to read in enough of the image stream 
86       // to figure out the width and height. 
87       abstract void readHeader(InputStream in) throws IOException; 
88    
89       /// Subclasses implement this to return the width, or -1 if not known. 
90       abstract int getWidth(); 
91    
92       /// Subclasses implement this to return the height, or -1 if not known. 
93       abstract int getHeight(); 
94    
95       /// Subclasses implement this to read pixel data into the rgbRow 
96       // array, an int[width].  One int per pixel, no offsets or padding, 
97       // RGBdefault (AARRGGBB) color model. 
98       abstract void readRow(InputStream in, int row, int[] rgbRow) throws IOException; 
99    
100   
101      // Our own methods. 
102   
103      void readImage() { 
104          try { 
105              readHeader(in); 
106              width = getWidth(); 
107              height = getHeight(); 
108              if (width == -1 || height == -1) 
109                  err = true; 
110              else { 
111                  rowsRead = new boolean[height]; 
112                  for (int row = 0; row < height; ++row) 
113                      rowsRead[row] = false; 
114                  gotSize = true; 
115                  notifyThem(); 
116                  rgbPixels = new int[height][width]; 
117                  for (int row = 0; row < height; ++row) { 
118                      readRow(in, row, rgbPixels[row]); 
119                      rowsRead[row] = true; 
120                      notifyThem(); 
121                  } 
122              } 
123          } catch (IOException e) { 
124              err = true; 
125              width = -1; 
126              height = -1; 
127              rowsRead = null; 
128              rgbPixels = null; 
129          } 
130      } 
131   
132      private synchronized void notifyThem() { 
133          notifyAll(); 
134      } 
135   
136      void sendImage() { 
137          // Grab the list of consumers, in case it changes while we're sending. 
138          ImageConsumer[] c = new ImageConsumer[consumers.size()]; 
139          int i; 
140          for (i = 0; i < c.length; ++i) 
141              c[i] = (ImageConsumer) consumers.elementAt(i); 
142          // Try to be as parallel as possible. 
143          waitForSize(); 
144          for (i = 0; i < c.length; ++i) 
145              sendHead(c[i]); 
146          for (int row = 0; row < height; ++row) 
147              for (i = 0; i < c.length; ++i) 
148                  sendPixelRow(c[i], row); 
149          for (i = 0; i < c.length; ++i) 
150              sendTail(c[i]); 
151          producing = false; 
152      } 
153   
154      private synchronized void waitForSize() { 
155          while ((!err) && (!gotSize)) { 
156              try { 
157                  wait(); 
158              } catch (InterruptedException ignore) { 
159              } 
160          } 
161      } 
162   
163      private synchronized void waitForRow(int row) { 
164          while ((!err) && (!rowsRead[row])) { 
165              try { 
166                  wait(); 
167              } catch (InterruptedException ignore) { 
168              } 
169          } 
170      } 
171   
172      private void sendHead(ImageConsumer ic) { 
173          if (err) 
174              return; 
175          ic.setDimensions(width, height); 
176          ic.setColorModel(model); 
177          ic.setHints( 
178                  ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES | 
179                  ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME); 
180      } 
181   
182      private void sendPixelRow(ImageConsumer ic, int row) { 
183          if (err) 
184              return; 
185          waitForRow(row); 
186          if (err) 
187              return; 
188          ic.setPixels(0, row, width, 1, model, rgbPixels[row], 0, width); 
189      } 
190   
191      private void sendTail(ImageConsumer ic) { 
192          if (err) 
193              ic.imageComplete(ImageConsumer.IMAGEERROR); 
194          else 
195              ic.imageComplete(ImageConsumer.STATICIMAGEDONE); 
196   
197      } 
198   
199   
200      // Methods from ImageProducer. 
201   
202      /// This method is used to register an ImageConsumer with the 
203      // ImageProducer for access to the image data during a later 
204      // reconstruction of the Image.  The ImageProducer may, at its 
205      // discretion, start delivering the image data to the consumer 
206      // using the ImageConsumer interface immediately, or when the 
207      // next available image reconstruction is triggered by a call 
208      // to the startProduction method. 
209      // @see #startProduction 
210      public void addConsumer(ImageConsumer ic) { 
211          if (ic != null && !isConsumer(ic)) 
212              consumers.addElement(ic); 
213      } 
214   
215      /// This method determines if a given ImageConsumer object 
216      // is currently registered with this ImageProducer as one 
217      // of its consumers. 
218      public boolean isConsumer(ImageConsumer ic) { 
219          return consumers.contains(ic); 
220      } 
221   
222      /// This method removes the given ImageConsumer object 
223      // from the list of consumers currently registered to 
224      // receive image data.  It is not considered an error 
225      // to remove a consumer that is not currently registered. 
226      // The ImageProducer should stop sending data to this 
227      // consumer as soon as is feasible. 
228      public void removeConsumer(ImageConsumer ic) { 
229          consumers.removeElement(ic); 
230      } 
231   
232      /// This method both registers the given ImageConsumer object 
233      // as a consumer and starts an immediate reconstruction of 
234      // the image data which will then be delivered to this 
235      // consumer and any other consumer which may have already 
236      // been registered with the producer.  This method differs 
237      // from the addConsumer method in that a reproduction of 
238      // the image data should be triggered as soon as possible. 
239      // @see #addConsumer 
240      public void startProduction(ImageConsumer ic) { 
241          addConsumer(ic); 
242          if (!startedRead) { 
243              startedRead = true; 
244              new ImageDecoderRead(this); 
245          } 
246          if (!producing) { 
247              producing = true; 
248              sendImage(); 
249          } 
250      } 
251   
252      /// This method is used by an ImageConsumer to request that 
253      // the ImageProducer attempt to resend the image data one 
254      // more time in TOPDOWNLEFTRIGHT order so that higher 
255      // quality conversion algorithms which depend on receiving 
256      // pixels in order can be used to produce a better output 
257      // version of the image.  The ImageProducer is free to 
258      // ignore this call if it cannot resend the data in that 
259      // order.  If the data can be resent, then the ImageProducer 
260      // should respond by executing the following minimum set of 
261      // ImageConsumer method calls: 
262      // <PRE> 
263      //     ic.setHints( TOPDOWNLEFTRIGHT | [otherhints] ); 
264      //     ic.setPixels( [...] );    // as many times as needed 
265      //     ic.imageComplete( [status] ); 
266      // </PRE> 
267      // @see ImageConsumer#setHints 
268      public void requestTopDownLeftRightResend(ImageConsumer ic) { 
269          addConsumer(ic); 
270          waitForSize(); 
271          sendHead(ic); 
272          for (int row = 0; row < height; ++row) 
273              sendPixelRow(ic, row); 
274          sendTail(ic); 
275      } 
276   
277  } 
278   
279   
280  class ImageDecoderRead extends Thread { 
281   
282      private ImageDecoder parent; 
283   
284      public ImageDecoderRead(ImageDecoder parent) { 
285          this.parent = parent; 
286          start(); 
287      } 
288   
289      // Methods from Runnable. 
290   
291      public void run() { 
292          parent.readImage(); 
293      } 
294   
295  } 
296