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

1    /* 
2     * @author Douglas A. Lyon 
3     * @version  Oct 12, 2002.10:59:59 AM 
4     */ 
5    package ip.ppm; 
6     
7     
8    // ImageEncoder - abstract class for writing out an 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.awt.*; 
38   import java.awt.image.ColorModel; 
39   import java.awt.image.ImageConsumer; 
40   import java.awt.image.ImageProducer; 
41   import java.io.IOException; 
42   import java.io.OutputStream; 
43   import java.util.Hashtable; 
44    
45   /// Abstract class for writing out an image. 
46   // <P> 
47   // A framework for classes that encode and write out an image in 
48   // a particular file format. 
49   // <P> 
50   // This provides a simplified rendition of the ImageConsumer interface. 
51   // It always delivers the pixels as ints in the RGBdefault color model. 
52   // It always provides them in top-down left-right order. 
53   // If you want more flexibility you can always implement ImageConsumer 
54   // directly. 
55   // <P> 
56   // <A HREF="/resources/classes/Acme/JPM/Encoders/ImageEncoder.java">Fetch the software.</A><BR> 
57   // <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A> 
58   // <P> 
59   // @see GifEncoder 
60   // @see PpmEncoder 
61   // @see Acme.JPM.Decoders.ImageDecoder 
62    
63   public abstract class ImageEncoder implements ImageConsumer { 
64    
65       protected OutputStream out; 
66    
67       private ImageProducer producer; 
68       private int width = -1; 
69       private int height = -1; 
70       private int hintflags = 0; 
71       private boolean started = false; 
72       private boolean encoding; 
73       private IOException iox; 
74       private static final ColorModel rgbModel = ColorModel.getRGBdefault(); 
75       private Hashtable props = null; 
76    
77       /// Constructor. 
78       // @param img The image to encode. 
79       // @param out The stream to write the bytes to. 
80       public ImageEncoder(Image img, OutputStream out) throws IOException { 
81           this(img.getSource(), out); 
82       } 
83    
84       /// Constructor. 
85       // @param producer The ImageProducer to encode. 
86       // @param out The stream to write the bytes to. 
87       public ImageEncoder(ImageProducer producer, OutputStream out) throws IOException { 
88           this.producer = producer; 
89           this.out = out; 
90       } 
91    
92    
93       // Methods that subclasses implement. 
94    
95       /// Subclasses implement this to initialize an encoding. 
96       abstract void encodeStart(int w, int h) throws IOException; 
97    
98       /// Subclasses implement this to actually write out some bits.  They 
99       // are guaranteed to be delivered in top-down-left-right order. 
100      // One int per pixel, index is row * scansize + off + col, 
101      // RGBdefault (AARRGGBB) color model. 
102      abstract void encodePixels( 
103              int x, int y, int w, int h, int[] rgbPixels, int off, int scansize) 
104              throws IOException; 
105   
106      /// Subclasses implement this to finish an encoding. 
107      abstract void encodeDone() throws IOException; 
108   
109   
110      // Our own methods. 
111   
112      /// Call this after initialization to get things going. 
113      public synchronized void encode() throws IOException { 
114          encoding = true; 
115          iox = null; 
116          producer.startProduction(this); 
117          while (encoding) 
118              try { 
119                  wait(); 
120              } catch (InterruptedException e) { 
121              } 
122          if (iox != null) 
123              throw iox; 
124      } 
125   
126      private boolean accumulate = false; 
127      private int[] accumulator; 
128   
129      private void encodePixelsWrapper( 
130              int x, int y, int w, int h, int[] rgbPixels, int off, int scansize) 
131              throws IOException { 
132          if (!started) { 
133              started = true; 
134              encodeStart(width, height); 
135              if ((hintflags & TOPDOWNLEFTRIGHT) == 0) { 
136                  accumulate = true; 
137                  accumulator = new int[width * height]; 
138              } 
139          } 
140          if (accumulate) 
141              for (int row = 0; row < h; ++row) 
142                  System.arraycopy( 
143                          rgbPixels, row * scansize + off, 
144                          accumulator, (y + row) * width + x, 
145                          w); 
146          else 
147              encodePixels(x, y, w, h, rgbPixels, off, scansize); 
148      } 
149   
150      private void encodeFinish() throws IOException { 
151          if (accumulate) { 
152              encodePixels(0, 0, width, height, accumulator, 0, width); 
153              accumulator = null; 
154              accumulate = false; 
155          } 
156      } 
157   
158      private synchronized void stop() { 
159          encoding = false; 
160          notifyAll(); 
161      } 
162   
163   
164      // Methods from ImageConsumer. 
165   
166      public void setDimensions(int width, int height) { 
167          this.width = width; 
168          this.height = height; 
169      } 
170   
171      public void setProperties(Hashtable props) { 
172          this.props = props; 
173      } 
174   
175      public void setColorModel(ColorModel model) { 
176          // Ignore. 
177      } 
178   
179      public void setHints(int hintflags) { 
180          this.hintflags = hintflags; 
181      } 
182   
183      public void setPixels( 
184              int x, int y, int w, int h, ColorModel model, byte[] pixels, 
185              int off, int scansize) { 
186          int[] rgbPixels = new int[w]; 
187          for (int row = 0; row < h; ++row) { 
188              int rowOff = off + row * scansize; 
189              for (int col = 0; col < w; ++col) 
190                  rgbPixels[col] = model.getRGB(pixels[rowOff + col] & 0xff); 
191              try { 
192                  encodePixelsWrapper(x, y + row, w, 1, rgbPixels, 0, w); 
193              } catch (IOException e) { 
194                  iox = e; 
195                  stop(); 
196                  return; 
197              } 
198          } 
199      } 
200   
201      public void setPixels( 
202              int x, int y, int w, int h, ColorModel model, int[] pixels, 
203              int off, int scansize) { 
204          if (model == rgbModel) { 
205              try { 
206                  encodePixelsWrapper(x, y, w, h, pixels, off, scansize); 
207              } catch (IOException e) { 
208                  iox = e; 
209                  stop(); 
210                  return; 
211              } 
212          } else { 
213              int[] rgbPixels = new int[w]; 
214              for (int row = 0; row < h; ++row) { 
215                  int rowOff = off + row * scansize; 
216                  for (int col = 0; col < w; ++col) 
217                      rgbPixels[col] = model.getRGB(pixels[rowOff + col]); 
218                  try { 
219                      encodePixelsWrapper(x, y + row, w, 1, rgbPixels, 0, w); 
220                  } catch (IOException e) { 
221                      iox = e; 
222                      stop(); 
223                      return; 
224                  } 
225              } 
226          } 
227      } 
228   
229      public void imageComplete(int status) { 
230          producer.removeConsumer(this); 
231          if (status == ImageConsumer.IMAGEABORTED) 
232              iox = new IOException("image aborted"); 
233          else { 
234              try { 
235                  encodeFinish(); 
236                  encodeDone(); 
237              } catch (IOException e) { 
238                  iox = e; 
239              } 
240          } 
241          stop(); 
242      } 
243   
244  } 
245   
246