/*
* com/mmbreakfast/unlod/app/handler/PCXDecoder.java
*
* Copyright (C) 2000 Sil Veritas (sil_the_follower_of_dark@hotmail.com)
*/
/* This file is part of Unlod.
*
* Unlod 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.
*
* Unlod 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.
*
* You should have received a copy of the GNU General Public License
* along with Unlod; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Unlod
*
* Copyright (C) 2000 Sil Veritas. All Rights Reserved. This work is
* distributed under the W3C(R) Software License [1] 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.
* [1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
*/
//import com.mmbreakfast.unlod.lod.RandomAccessFileInputStream;
package com.mmbreakfast.unlod.app.handler;
import java.io.*;
import java.awt.image.*;
public class PCXDecoder {
protected static final int Xmin_OFFSET = 4;
protected static final int Ymin_OFFSET = 6;
protected static final int Xmax_OFFSET = 8;
protected static final int Ymax_OFFSET = 10;
protected static final int NPlanes_OFFSET = 65;
protected static final int BytesPerLine_OFFSET = 66;
protected static final int Data_OFFSET = 128;
protected static final int TOP_TWO_BITS = 0xC0;
protected static final int LO_SIX_BITS = ~TOP_TWO_BITS;
public ImageProducer decode(ByteArrayInputStream in) throws IOException, InvalidPCXFileException {
int pos = 0;
int xMin = this.readNibble(in, Xmin_OFFSET);
pos = Xmin_OFFSET + 2;
int yMin = this.readNibble(in, Ymin_OFFSET - pos);
pos = Ymin_OFFSET + 2;
int xMax = this.readNibble(in, Xmax_OFFSET - pos);
pos = Xmax_OFFSET + 2;
int yMax = this.readNibble(in, Ymax_OFFSET - pos);
pos = Ymax_OFFSET + 2;
int nPlanes = this.readByte(in, NPlanes_OFFSET - pos);
pos = NPlanes_OFFSET + 1;
int bytesPerLine = this.readNibble(in, BytesPerLine_OFFSET - pos);
pos = BytesPerLine_OFFSET + 2;
int xSize = xMax - xMin + 1;
int ySize = yMax - yMin + 1;
int totalBytesPerLine = nPlanes * bytesPerLine;
int totalBytes = xSize * ySize;
in.skip(Data_OFFSET - pos);
int[] scanLine = new int[totalBytesPerLine];
int[] image = new int[totalBytes];
int r = 0;
int bytesWritten = 0;
int repeats = 0;
int totalRepeats = 0;
int imageBytesWritten = 0;
int totalImageBytesWritten = 0;
int plane = 0;
int srgb = 0;
for (int y = 0; y < ySize; y++) {
// scan a line
bytesWritten = 0;
while ((bytesWritten < totalBytesPerLine) && ((r = in.read()) != -1) ) {
if ((r & TOP_TWO_BITS) == TOP_TWO_BITS) {
for (repeats = 0, totalRepeats = (r & LO_SIX_BITS), r = in.read(); repeats < totalRepeats; repeats++) {
if (r == -1) {
throw new InvalidPCXFileException();
} else {
scanLine[bytesWritten++] = r;
//System.out.println(r);
}
}
} else {
scanLine[bytesWritten++] = r;
//System.out.println(r);
}
}
if (bytesWritten < totalBytesPerLine) {
throw new InvalidPCXFileException();
}
// write the line
if (nPlanes == 4) {
for (imageBytesWritten = 0; imageBytesWritten < xSize; imageBytesWritten++, totalImageBytesWritten++) {
srgb = (scanLine[imageBytesWritten + 3 * bytesPerLine] << 24)
| (scanLine[imageBytesWritten] << 16)
| (scanLine[imageBytesWritten + bytesPerLine] << 8)
| (scanLine[imageBytesWritten + 2 * bytesPerLine]);
image[totalImageBytesWritten] = srgb;
}
} else if (nPlanes == 3) {
for (imageBytesWritten = 0; imageBytesWritten < xSize; imageBytesWritten++, totalImageBytesWritten++) {
srgb = (0xFF << 24)
| (scanLine[imageBytesWritten] << 16)
| (scanLine[imageBytesWritten + bytesPerLine] << 8)
| (scanLine[imageBytesWritten + 2 * bytesPerLine]);
image[totalImageBytesWritten] = srgb;
}
}
}
return new MemoryImageSource(xSize, ySize, image, 0, xSize);
}
public ImageProducer decode(RandomAccessFile in) throws IOException, InvalidPCXFileException {
int xMin = this.readNibble(in, Xmin_OFFSET);
int yMin = this.readNibble(in, Ymin_OFFSET);
int xMax = this.readNibble(in, Xmax_OFFSET);
int yMax = this.readNibble(in, Ymax_OFFSET);
int nPlanes = this.readByte(in, NPlanes_OFFSET);
int bytesPerLine = this.readNibble(in, BytesPerLine_OFFSET);
int xSize = xMax - xMin + 1;
int ySize = yMax - yMin + 1;
int totalBytesPerLine = nPlanes * bytesPerLine;
//int totalBytes = nPlanes * bytesPerLine * ySize;
int totalBytes = xSize * ySize;
in.seek(Data_OFFSET);
int[] scanLine = new int[totalBytesPerLine];
int[] image = new int[totalBytes];
int r = 0;
int bytesWritten = 0;
int repeats = 0;
int totalRepeats = 0;
int imageBytesWritten = 0;
int totalImageBytesWritten = 0;
int plane = 0;
int srgb = 0;
for (int y = 0; y < ySize; y++) {
// scan a line
bytesWritten = 0;
while ((bytesWritten < totalBytesPerLine) && ((r = in.read()) != -1) ) {
if ((r & TOP_TWO_BITS) == TOP_TWO_BITS) {
for (repeats = 0, totalRepeats = (r & LO_SIX_BITS), r = in.read(); repeats < totalRepeats; repeats++) {
if (r == -1) {
throw new InvalidPCXFileException();
} else {
scanLine[bytesWritten++] = r;
//System.out.println(r);
}
}
} else {
scanLine[bytesWritten++] = r;
//System.out.println(r);
}
}
if (bytesWritten < totalBytesPerLine) {
throw new InvalidPCXFileException();
}
if (nPlanes == 4) {
for (imageBytesWritten = 0; imageBytesWritten < xSize; imageBytesWritten++, totalImageBytesWritten++) {
srgb = (scanLine[imageBytesWritten + 3 * bytesPerLine] << 24)
| (scanLine[imageBytesWritten] << 16)
| (scanLine[imageBytesWritten + bytesPerLine] << 8)
| (scanLine[imageBytesWritten + 2 * bytesPerLine]);
image[totalImageBytesWritten] = srgb;
}
} else if (nPlanes == 3) {
for (imageBytesWritten = 0; imageBytesWritten < xSize; imageBytesWritten++, totalImageBytesWritten++) {
srgb = (0xFF << 24)
| (scanLine[imageBytesWritten] << 16)
| (scanLine[imageBytesWritten + bytesPerLine] << 8)
| (scanLine[imageBytesWritten + 2 * bytesPerLine]);
image[totalImageBytesWritten] = srgb;
}
}
}
return new MemoryImageSource(xSize, ySize, image, 0, xSize);
}
private int readNibble(RandomAccessFile in, long offset) throws IOException, InvalidPCXFileException {
// Fixme: Since the readNibble() methods now read 2 bytes they really should be called readWord()
in.seek(offset);
int r = 0;
int nibble = 0;
for (int i = 0; (i < 2) && ((r = in.read()) != -1); i++) {
nibble += (r << (8 * i));
}
if (r == -1) {
throw new InvalidPCXFileException();
}
return nibble;
}
private int readByte(RandomAccessFile in, long offset) throws IOException, InvalidPCXFileException {
in.seek(offset);
int r = in.read();
if (r == -1) {
throw new InvalidPCXFileException();
}
return r;
}
private int readNibble(ByteArrayInputStream in, int skip) throws InvalidPCXFileException {
in.skip(skip);
int r = 0;
int nibble = 0;
for (int i = 0; (i < 2) && ((r = in.read()) != -1); i++) {
nibble += (r << (8 * i));
}
if (r == -1) {
throw new InvalidPCXFileException();
}
return nibble;
}
private int readByte(ByteArrayInputStream in, int skip) throws InvalidPCXFileException {
in.skip(skip);
int r = in.read();
if (r == -1) {
throw new InvalidPCXFileException();
}
return r;
}
public class InvalidPCXFileException extends Exception {}
}