/Users/lyon/j4p/src/ip/JPM/Decoders/ImageDecoder.java

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