1 package org.freehep.graphicsio.ppm;
2
3 // ImageEncoder - abstract class for writing out an image
4 //
5 // Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 // 1. Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 // 2. Redistributions in binary form must reproduce the above copyright
13 // notice, this list of conditions and the following disclaimer in the
14 // documentation and/or other materials provided with the distribution.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 // SUCH DAMAGE.
27 //
28 // Visit the ACME Labs Java page for up-to-date versions of this and other
29 // fine Java utilities: http://www.acme.com/java/
30
31 //package Acme.JPM.Encoders;
32
33 import java.awt.Image;
34 import java.awt.image.ColorModel;
35 import java.awt.image.ImageConsumer;
36 import java.awt.image.ImageProducer;
37 import java.io.DataOutput;
38 import java.io.IOException;
39 import java.util.Hashtable;
40
41 /// Abstract class for writing out an image.
42 // <P>
43 // A framework for classes that encode and write out an image in
44 // a particular file format.
45 // <P>
46 // This provides a simplified rendition of the ImageConsumer interface.
47 // It always delivers the pixels as ints in the RGBdefault color model.
48 // It always provides them in top-down left-right order.
49 // If you want more flexibility you can always implement ImageConsumer
50 // directly.
51 // <P>
52 // <A HREF="/resources/classes/Acme/JPM/Encoders/ImageEncoder.java">Fetch the software.</A><BR>
53 // <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
54 // <P>
55 // @see GifEncoder
56 // @see PpmEncoder
57 // @see Acme.JPM.Decoders.ImageDecoder
58
59 public abstract class ImageEncoder implements ImageConsumer {
60
61 protected DataOutput out;
62
63 private ImageProducer producer;
64
65 private int width = -1;
66
67 private int height = -1;
68
69 private int hintflags = 0;
70
71 private boolean started = false;
72
73 private boolean encoding;
74
75 private IOException iox;
76
77 private static final ColorModel rgbModel = ColorModel.getRGBdefault();
78
79 protected Hashtable props = null;
80
81 // / Constructor.
82 // @param img The image to encode.
83 // @param out The stream to write the bytes to.
84 public ImageEncoder(Image img, DataOutput dos) throws IOException {
85 this(img.getSource(), dos);
86 }
87
88 // / Constructor.
89 // @param producer The ImageProducer to encode.
90 // @param out The stream to write the bytes to.
91 public ImageEncoder(ImageProducer producer, DataOutput dos)
92 throws IOException {
93 this.producer = producer;
94 this.out = dos;
95 }
96
97 // Methods that subclasses implement.
98
99 // / Subclasses implement this to initialize an encoding.
100 protected abstract void encodeStart(int w, int h) throws IOException;
101
102 // / Subclasses implement this to actually write out some bits. They
103 // are guaranteed to be delivered in top-down-left-right order.
104 // One int per pixel, index is row * scansize + off + col,
105 // RGBdefault (AARRGGBB) color model.
106 protected abstract void encodePixels(int x, int y, int w, int h,
107 int[] rgbPixels, int off, int scansize) throws IOException;
108
109 // / Subclasses implement this to finish an encoding.
110 protected abstract void encodeDone() throws IOException;
111
112 // Our own methods.
113
114 // / Call this after initialization to get things going.
115 public synchronized void encode() throws IOException {
116 encoding = true;
117 iox = null;
118 producer.startProduction(this);
119 while (encoding)
120 try {
121 wait();
122 } catch (InterruptedException e) {
123 }
124 if (iox != null)
125 throw iox;
126 }
127
128 private boolean accumulate = false;
129
130 private int[] accumulator;
131
132 private void encodePixelsWrapper(int x, int y, int w, int h,
133 int[] rgbPixels, int off, int scansize) throws IOException {
134 if (!started) {
135 started = true;
136 encodeStart(width, height);
137 if ((hintflags & TOPDOWNLEFTRIGHT) == 0) {
138 accumulate = true;
139 accumulator = new int[width * height];
140 }
141 }
142 if (accumulate)
143 for (int row = 0; row < h; ++row)
144 System.arraycopy(rgbPixels, row * scansize + off, accumulator,
145 (y + row) * width + x, 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 // Methods from ImageConsumer.
164
165 public void setDimensions(int width, int height) {
166 this.width = width;
167 this.height = height;
168 }
169
170 public void setProperties(Hashtable props) {
171 this.props = props;
172 }
173
174 public void setColorModel(ColorModel model) {
175 // Ignore.
176 }
177
178 public void setHints(int hintflags) {
179 this.hintflags = hintflags;
180 }
181
182 public void setPixels(int x, int y, int w, int h, ColorModel model,
183 byte[] pixels, int off, int scansize) {
184 int[] rgbPixels = new int[w];
185 for (int row = 0; row < h; ++row) {
186 int rowOff = off + row * scansize;
187 for (int col = 0; col < w; ++col)
188 rgbPixels[col] = model.getRGB(pixels[rowOff + col] & 0xff);
189 try {
190 encodePixelsWrapper(x, y + row, w, 1, rgbPixels, 0, w);
191 } catch (IOException e) {
192 iox = e;
193 stop();
194 return;
195 }
196 }
197 }
198
199 public void setPixels(int x, int y, int w, int h, ColorModel model,
200 int[] pixels, int off, int scansize) {
201 if (model == rgbModel) {
202 try {
203 encodePixelsWrapper(x, y, w, h, pixels, off, scansize);
204 } catch (IOException e) {
205 iox = e;
206 stop();
207 return;
208 }
209 } else {
210 int[] rgbPixels = new int[w];
211 for (int row = 0; row < h; ++row) {
212 int rowOff = off + row * scansize;
213 for (int col = 0; col < w; ++col)
214 rgbPixels[col] = model.getRGB(pixels[rowOff + col]);
215 try {
216 encodePixelsWrapper(x, y + row, w, 1, rgbPixels, 0, w);
217 } catch (IOException e) {
218 iox = e;
219 stop();
220 return;
221 }
222 }
223 }
224 }
225
226 public void imageComplete(int status) {
227 producer.removeConsumer(this);
228 if (status == ImageConsumer.IMAGEABORTED)
229 iox = new IOException("image aborted");
230 else {
231 try {
232 encodeFinish();
233 encodeDone();
234 } catch (IOException e) {
235 iox = e;
236 }
237 }
238 stop();
239 }
240
241 }