/Users/lyon/j4p/src/ip/JPM/Encoders/ImageEncoder.java

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