package ika.utils; import java.io.IOException; import java.io.OutputStream; /** * Abstract super class for image writers for various file formats. Derived * image writers must be able to handle very large images that don't fit into * the available memory, i.e. passed pixel values must be immediately be written * to a stream. * @author Bernhard Jenny, Institute of Cartography, ETH Zurich. */ public abstract class ImageWriter { /** * The stream to write to. */ protected OutputStream out; /** * The number of columns in the image. */ protected int cols; /** * The number of rows in the image. */ protected int rows; /** * Counts the number of pixels written. */ private int pixelCounter = 0; /** * Creates a new instance of ImageWriter and writes the header. * */ public ImageWriter(OutputStream out, int cols, int rows) throws java.io.IOException { this.out = out; this.cols = cols; this.rows = rows; this.writeHeader(); } /** * Writes a color value to the image file. Does nothing if all possible * values have already been written. Automatically writes the "end of file * information" after the last pixel is written. * @param r Red in the range [0..255] * @param g Green in the range [0..255] * @param b Blue in the range [0..255] * @throws java.io.IOException */ final public void write(int r, int g, int b) throws IOException { final int maxPixels = this.cols * this.rows; if (this.pixelCounter < maxPixels) this.writeRGB(r, g, b); if (++this.pixelCounter == maxPixels) this.writeFooter(); } /** * Writes a color value to the image file. The r, g, and b values must be * premultiplied by the a value. Does nothing if all possible * values have already been written. Automatically writes the "end of file * information" after the last pixel is written. * @param r Red in the range [0..255] * @param g Green in the range [0..255] * @param b Blue in the range [0..255] * @param a Alpha in the range [0..255] * @throws java.io.IOException */ final public void write(int r, int g, int b, int a) throws IOException { final int maxPixels = this.cols * this.rows; if (this.pixelCounter < maxPixels) this.writeRGB(r, g, b, a); if (++this.pixelCounter == maxPixels) this.writeFooter(); } /** * Write an argb value to the file. This default implementation unpacks the * channels and calls write(r, g, b, a). The r, g, and b values must be * premultiplied by the a value. * @param color An rgba value packed in an integer * @throws java.io.IOException */ public void write(int argb) throws java.io.IOException { final int a = (argb >> 24) & 0xff; final int r = (argb >> 16) & 0xff; final int g = (argb >> 8) & 0xff; final int b = 0xff & argb; this.write(r, g, b, a); } /** * Writes the file header. This is called by the constructor. * @throws java.io.IOException */ protected abstract void writeHeader() throws java.io.IOException; /** * Writes the file footer at the end of the file. The standard implementation * does not write anything. * @throws java.io.IOException */ protected void writeFooter() throws java.io.IOException { } /** * Write an rgb value to the file. * @param r Red in the range [0..255] * @param g Green in the range [0..255] * @param b Blue in the range [0..255] * @throws java.io.IOException */ protected abstract void writeRGB(int r, int g, int b) throws java.io.IOException; /** * Write an rgba value to the file. The r, g, and b values must be * premultiplied by the a value. This default implementation ignores * the alpha channel. * @param r Red in the range [0..255] * @param g Green in the range [0..255] * @param b Blue in the range [0..255] * @param a Alpha in the range [0..255] * @throws java.io.IOException */ protected void writeRGB(int r, int g, int b, int a) throws java.io.IOException { this.write (r, g, b); } }