TileMangler/src/tm/gfxlibs/PCXEncoder.java

147 lines
4.3 KiB
Java

/**
*
* @author Kent Hansen
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
**/
package tm.gfxlibs;
import java.awt.Image;
import java.awt.image.PixelGrabber;
import java.awt.image.ImageObserver;
import java.io.ByteArrayOutputStream;
public class PCXEncoder {
private static ByteArrayOutputStream baos = new ByteArrayOutputStream();
public static byte[] encode(Image image) {
// get width and height of image
int w = image.getWidth(null);
int h = image.getHeight(null);
// grab the pixels
int[] pixels = new int[w * h];
PixelGrabber pg = new PixelGrabber(image, 0, 0, w, h, pixels, 0, w);
try {
pg.grabPixels();
} catch (InterruptedException e) {
System.err.println("interrupted waiting for pixels!");
return null;
}
if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
System.err.println("image fetch aborted or errored");
return null;
}
// prepare the byte stream
baos.reset();
// write the header
emitByte(10); // Manufacturer = ZSoft
emitByte(5); // Version = 3.0+ (supports 24-bit PCX)
emitByte(1); // Encoding = RLE
emitByte(8); // BitsPerPixel (per plane)
emitInt(0); // Xmin
emitInt(0); // Ymin
emitInt(w-1); // Xmax
emitInt(h-1); // Ymax
emitInt(0); // HDpi (not used)
emitInt(0); // VDpi (not used)
emitZeroes(48); // Colormap (not used)
emitZeroes(1); // Reserved
emitByte(3); // NPlanes
emitInt(w); // BytesPerLine
emitInt(0); // PaletteInfo (not used)
emitInt(w); // HScreenSize (unsure what this is for)
emitInt(h); // VScreenSize (unsure what this is for)
emitZeroes(54); // Filler
// encode the pixels
int ofs=0;
int[] plane = new int[w];
for (int i=0; i<h; i++) {
// red plane
for (int j=0; j<w; j++) {
plane[j] = getRed(pixels[ofs+j]);
}
encodePlane(plane);
// green plane
for (int j=0; j<w; j++) {
plane[j] = getGreen(pixels[ofs+j]);
}
encodePlane(plane);
// blue plane
for (int j=0; j<w; j++) {
plane[j] = getBlue(pixels[ofs+j]);
}
encodePlane(plane);
ofs += w;
}
return baos.toByteArray();
}
private static void emitByte(int b) {
baos.write(b);
}
private static void emitInt(int i) {
baos.write(i & 0xFF);
baos.write((i >> 8) & 0xFF);
}
private static void emitZeroes(int c) {
for (int i=0; i<c; i++) {
emitByte(0x00);
}
}
private static int getRed(int rgb) {
return (rgb >> 16) & 0xFF;
}
private static int getGreen(int rgb) {
return (rgb >> 8) & 0xFF;
}
private static int getBlue(int rgb) {
return rgb & 0xFF;
}
private static void encodePlane(int[] plane) {
int i=0;
while (i < plane.length) {
int v = plane[i];
if ((i != plane.length-1) && (v == plane[i+1])) {
// run-length encode
int c = 1;
while ((c < 0x40) && (i+c < plane.length) && (v == plane[i+c])) {
c++;
}
i += c;
emitByte(c | 0xC0);
emitByte(v);
}
else {
// regular encode
if ((v & 0xC0) == 0xC0) {
emitByte(0xC1);
}
emitByte(v);
i++;
}
}
}
}