001: // ImageEncoder - abstract class for writing out an image
002: //
003: // Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
004: //
005: // Redistribution and use in source and binary forms, with or without
006: // modification, are permitted provided that the following conditions
007: // are met:
008: // 1. Redistributions of source code must retain the above copyright
009: // notice, this list of conditions and the following disclaimer.
010: // 2. Redistributions in binary form must reproduce the above copyright
011: // notice, this list of conditions and the following disclaimer in the
012: // documentation and/or other materials provided with the distribution.
013: //
014: // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
015: // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
016: // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
017: // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
018: // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
019: // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
020: // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
021: // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
022: // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
023: // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
024: // SUCH DAMAGE.
025: //
026: // Visit the ACME Labs Java page for up-to-date versions of this and other
027: // fine Java utilities: http://www.acme.com/java/
028:
029: package Acme.JPM.Encoders;
030:
031: import java.awt.*;
032: import java.awt.image.ColorModel;
033: import java.awt.image.ImageConsumer;
034: import java.awt.image.ImageProducer;
035: import java.io.IOException;
036: import java.io.OutputStream;
037: import java.util.Hashtable;
038:
039: /// Abstract class for writing out an image.
040: // <P>
041: // A framework for classes that encode and write out an image in
042: // a particular file format.
043: // <P>
044: // This provides a simplified rendition of the ImageConsumer interface.
045: // It always delivers the pixels as ints in the RGBdefault color model.
046: // It always provides them in top-down left-right order.
047: // If you want more flexibility you can always implement ImageConsumer
048: // directly.
049: // <P>
050: // <A HREF="/resources/classes/Acme/JPM/Encoders/ImageEncoder.java">Fetch the software.</A><BR>
051: // <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
052: // <P>
053: // @see GifEncoder
054: // @see PpmEncoder
055: // @see Acme.JPM.Decoders.ImageDecoder
056:
057: public abstract class ImageEncoder implements ImageConsumer {
058:
059: protected OutputStream out;
060:
061: private ImageProducer producer;
062: private int width = -1;
063: private int height = -1;
064: private int hintflags = 0;
065: private boolean started = false;
066: private boolean encoding;
067: private IOException iox;
068: private static final ColorModel rgbModel = ColorModel
069: .getRGBdefault();
070: private Hashtable props = null;
071:
072: /// Constructor.
073: // @param img The image to encode.
074: // @param out The stream to write the bytes to.
075: public ImageEncoder(Image img, OutputStream out) throws IOException {
076: this (img.getSource(), out);
077: }
078:
079: /// Constructor.
080: // @param producer The ImageProducer to encode.
081: // @param out The stream to write the bytes to.
082: public ImageEncoder(ImageProducer producer, OutputStream out)
083: throws IOException {
084: this .producer = producer;
085: this .out = out;
086: }
087:
088: // Methods that subclasses implement.
089:
090: /// Subclasses implement this to initialize an encoding.
091: abstract void encodeStart(int w, int h) throws IOException;
092:
093: /// Subclasses implement this to actually write out some bits. They
094: // are guaranteed to be delivered in top-down-left-right order.
095: // One int per pixel, index is row * scansize + off + col,
096: // RGBdefault (AARRGGBB) color model.
097: abstract void encodePixels(int x, int y, int w, int h,
098: int[] rgbPixels, int off, int scansize) throws IOException;
099:
100: /// Subclasses implement this to finish an encoding.
101: abstract void encodeDone() throws IOException;
102:
103: // Our own methods.
104:
105: /// Call this after initialization to get things going.
106: public synchronized void encode() throws IOException {
107: encoding = true;
108: iox = null;
109: producer.startProduction(this );
110: while (encoding)
111: try {
112: wait();
113: } catch (InterruptedException e) {
114: }
115: if (iox != null)
116: throw iox;
117: }
118:
119: private boolean accumulate = false;
120: private int[] accumulator;
121:
122: private void encodePixelsWrapper(int x, int y, int w, int h,
123: int[] rgbPixels, int off, int scansize) throws IOException {
124: if (!started) {
125: started = true;
126: encodeStart(width, height);
127: if ((hintflags & TOPDOWNLEFTRIGHT) == 0) {
128: accumulate = true;
129: accumulator = new int[width * height];
130: }
131: }
132: if (accumulate)
133: for (int row = 0; row < h; ++row)
134: System.arraycopy(rgbPixels, row * scansize + off,
135: accumulator, (y + row) * width + x, w);
136: else
137: encodePixels(x, y, w, h, rgbPixels, off, scansize);
138: }
139:
140: private void encodeFinish() throws IOException {
141: if (accumulate) {
142: encodePixels(0, 0, width, height, accumulator, 0, width);
143: accumulator = null;
144: accumulate = false;
145: }
146: }
147:
148: private synchronized void stop() {
149: encoding = false;
150: notifyAll();
151: }
152:
153: // Methods from ImageConsumer.
154:
155: public void setDimensions(int width, int height) {
156: this .width = width;
157: this .height = height;
158: }
159:
160: public void setProperties(Hashtable props) {
161: this .props = props;
162: }
163:
164: public void setColorModel(ColorModel model) {
165: // Ignore.
166: }
167:
168: public void setHints(int hintflags) {
169: this .hintflags = hintflags;
170: }
171:
172: public void setPixels(int x, int y, int w, int h, ColorModel model,
173: byte[] pixels, int off, int scansize) {
174: int[] rgbPixels = new int[w];
175: for (int row = 0; row < h; ++row) {
176: int rowOff = off + row * scansize;
177: for (int col = 0; col < w; ++col)
178: rgbPixels[col] = model
179: .getRGB(pixels[rowOff + col] & 0xff);
180: try {
181: encodePixelsWrapper(x, y + row, w, 1, rgbPixels, 0, w);
182: } catch (IOException e) {
183: iox = e;
184: stop();
185: return;
186: }
187: }
188: }
189:
190: public void setPixels(int x, int y, int w, int h, ColorModel model,
191: int[] pixels, int off, int scansize) {
192: if (model == rgbModel) {
193: try {
194: encodePixelsWrapper(x, y, w, h, pixels, off, scansize);
195: } catch (IOException e) {
196: iox = e;
197: stop();
198: return;
199: }
200: } else {
201: int[] rgbPixels = new int[w];
202: for (int row = 0; row < h; ++row) {
203: int rowOff = off + row * scansize;
204: for (int col = 0; col < w; ++col)
205: rgbPixels[col] = model.getRGB(pixels[rowOff + col]);
206: try {
207: encodePixelsWrapper(x, y + row, w, 1, rgbPixels, 0,
208: w);
209: } catch (IOException e) {
210: iox = e;
211: stop();
212: return;
213: }
214: }
215: }
216: }
217:
218: public void imageComplete(int status) {
219: producer.removeConsumer(this );
220: if (status == ImageConsumer.IMAGEABORTED)
221: iox = new IOException("image aborted");
222: else {
223: try {
224: encodeFinish();
225: encodeDone();
226: } catch (IOException e) {
227: iox = e;
228: }
229: }
230: stop();
231: }
232:
233: }
|