/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