001: // The UMLet source code is distributed under the terms of the GPL; see license.txt
002: package com.umlet.control.io;
003:
004: import java.awt.*;
005: import java.awt.image.*;
006: import java.io.*;
007: import java.util.*;
008:
009: /// Abstract class for writing out an image.
010: // <P>
011: // A framework for classes that encode and write out an image in
012: // a particular file format.
013: // <P>
014: // This provides a simplified rendition of the ImageConsumer interface.
015: // It always delivers the pixels as ints in the RGBdefault color model.
016: // It always provides them in top-down left-right order.
017: // If you want more flexibility you can always implement ImageConsumer
018: // directly.
019: // <P>
020: // <A HREF="/resources/classes/Acme/JPM/Encoders/ImageEncoder.java">Fetch the software.</A><BR>
021: // <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
022: // <P>
023: // @see GifEncoder
024: // @see PpmEncoder
025: // @see Acme.JPM.Decoders.ImageDecoder
026:
027: public abstract class ImageEncoder implements ImageConsumer {
028:
029: protected OutputStream out;
030:
031: private ImageProducer producer;
032: private int width = -1;
033: private int height = -1;
034: private int hintflags = 0;
035: private boolean started = false;
036: private boolean encoding;
037: private IOException iox;
038: private static final ColorModel rgbModel = ColorModel
039: .getRGBdefault();
040: private Hashtable props = null;
041:
042: /// Constructor.
043: // @param img The image to encode.
044: // @param out The stream to write the bytes to.
045: public ImageEncoder(Image img, OutputStream out) throws IOException {
046: this (img.getSource(), out);
047: }
048:
049: /// Constructor.
050: // @param producer The ImageProducer to encode.
051: // @param out The stream to write the bytes to.
052: public ImageEncoder(ImageProducer producer, OutputStream out)
053: throws IOException {
054: this .producer = producer;
055: this .out = out;
056: }
057:
058: // Methods that subclasses implement.
059:
060: /// Subclasses implement this to initialize an encoding.
061: abstract void encodeStart(int w, int h) throws IOException;
062:
063: /// Subclasses implement this to actually write out some bits. They
064: // are guaranteed to be delivered in top-down-left-right order.
065: // One int per pixel, index is row * scansize + off + col,
066: // RGBdefault (AARRGGBB) color model.
067: abstract void encodePixels(int x, int y, int w, int h,
068: int[] rgbPixels, int off, int scansize) throws IOException;
069:
070: /// Subclasses implement this to finish an encoding.
071: abstract void encodeDone() throws IOException;
072:
073: // Our own methods.
074:
075: /// Call this after initialization to get things going.
076: public synchronized void encode() throws IOException {
077: encoding = true;
078: iox = null;
079: producer.startProduction(this );
080: while (encoding)
081: try {
082: wait();
083: } catch (InterruptedException e) {
084: }
085: if (iox != null)
086: throw iox;
087: }
088:
089: private boolean accumulate = false;
090: private int[] accumulator;
091:
092: private void encodePixelsWrapper(int x, int y, int w, int h,
093: int[] rgbPixels, int off, int scansize) throws IOException {
094: if (!started) {
095: started = true;
096: encodeStart(width, height);
097: if ((hintflags & TOPDOWNLEFTRIGHT) == 0) {
098: accumulate = true;
099: accumulator = new int[width * height];
100: }
101: }
102: if (accumulate)
103: for (int row = 0; row < h; ++row)
104: System.arraycopy(rgbPixels, row * scansize + off,
105: accumulator, (y + row) * width + x, w);
106: else
107: encodePixels(x, y, w, h, rgbPixels, off, scansize);
108: }
109:
110: private void encodeFinish() throws IOException {
111: if (accumulate) {
112: encodePixels(0, 0, width, height, accumulator, 0, width);
113: accumulator = null;
114: accumulate = false;
115: }
116: }
117:
118: private synchronized void stop() {
119: encoding = false;
120: notifyAll();
121: }
122:
123: // Methods from ImageConsumer.
124:
125: public void setDimensions(int width, int height) {
126: this .width = width;
127: this .height = height;
128: }
129:
130: //[UB]: renamed method because setProperties alrady exists
131: //in the superclass with a different signature
132: public void setImageEncoderProperties(Hashtable props) {
133: this .props = props;
134: }
135:
136: public void setColorModel(ColorModel model) {
137: // Ignore.
138: }
139:
140: public void setHints(int hintflags) {
141: this .hintflags = hintflags;
142: }
143:
144: public void setPixels(int x, int y, int w, int h, ColorModel model,
145: byte[] pixels, int off, int scansize) {
146: int[] rgbPixels = new int[w];
147: for (int row = 0; row < h; ++row) {
148: int rowOff = off + row * scansize;
149: for (int col = 0; col < w; ++col)
150: rgbPixels[col] = model
151: .getRGB(pixels[rowOff + col] & 0xff);
152: try {
153: encodePixelsWrapper(x, y + row, w, 1, rgbPixels, 0, w);
154: } catch (IOException e) {
155: iox = e;
156: stop();
157: return;
158: }
159: }
160: }
161:
162: public void setPixels(int x, int y, int w, int h, ColorModel model,
163: int[] pixels, int off, int scansize) {
164: if (model == rgbModel) {
165: try {
166: encodePixelsWrapper(x, y, w, h, pixels, off, scansize);
167: } catch (IOException e) {
168: iox = e;
169: stop();
170: return;
171: }
172: } else {
173: int[] rgbPixels = new int[w];
174: for (int row = 0; row < h; ++row) {
175: int rowOff = off + row * scansize;
176: for (int col = 0; col < w; ++col)
177: rgbPixels[col] = model.getRGB(pixels[rowOff + col]);
178: try {
179: encodePixelsWrapper(x, y + row, w, 1, rgbPixels, 0,
180: w);
181: } catch (IOException e) {
182: iox = e;
183: stop();
184: return;
185: }
186: }
187: }
188: }
189:
190: public void imageComplete(int status) {
191: producer.removeConsumer(this );
192: if (status == ImageConsumer.IMAGEABORTED)
193: iox = new IOException("image aborted");
194: else {
195: try {
196: encodeFinish();
197: encodeDone();
198: } catch (IOException e) {
199: iox = e;
200: }
201: }
202: stop();
203: }
204:
205: }
|