TileMangler/src/tm/tilecodecs/DirectColorTileCodec.java

257 lines
6.7 KiB
Java

/*
*
* Copyright (C) 2003 Kent Hansen.
*
* This file is part of Tile Mangler.
*
* Tile Mangler 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.
*
* Tile Mangler 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.tilecodecs;
/**
*
* 16, 24, and 32-bit direct-color (ARGB) tile codec.
* # of bits per color component must not exceed 8.
* The color component masks must be directly adjacent.
*
**/
public class DirectColorTileCodec extends TileCodec {
public static final int LITTLE_ENDIAN=1;
public static final int BIG_ENDIAN=2;
private static int endianness; // LITTLE_ENDIAN | BIG_ENDIAN
private int rmask; // bitmask for Red component
private int gmask; // bitmask for Green component
private int bmask; // bitmask for Blue component
private int amask; // bitmask for Alpha component
// how much each component must be shifted to be transformed to a 32-bit ARGB int-packed Java pixel.
// these are pre-calculated in the constructor and used for decoding/encoding individual pixels.
private int rshift;
private int gshift;
private int bshift;
private int ashift;
// number of bytes that hold one compressed pixel.
private int bytesPerPixel;
private int startShift;
private int shiftStep;
/**
*
* Creates a direct-color tile codec.
*
**/
public DirectColorTileCodec(String id, int bpp, int rmask, int gmask, int bmask, int amask, String description) {
super(id, getByteIntegralBitCount(bpp), description);
bytesPerPixel = bitsPerPixel / 8; // 2, 3 or 4
this.rmask = rmask;
this.gmask = gmask;
this.bmask = bmask;
this.amask = amask;
// calculate the shifts
rshift = 23 - msb(rmask);
gshift = 15 - msb(gmask);
bshift = 7 - msb(bmask);
ashift = 31 - msb(amask);
setEndianness(LITTLE_ENDIAN); // default
}
/**
* Sets the endianness.
**/
public void setEndianness(int endianness) {
this.endianness = endianness;
if (endianness == LITTLE_ENDIAN) {
startShift = 0;
shiftStep = 8;
}
else {
// BIG_ENDIAN
startShift = (bytesPerPixel-1) * 8;
shiftStep = -8;
}
}
/**
* Gets the position of the most significant set bit in the given int.
**/
private static int msb(int mask) {
for (int i=31; i>=0; i--) {
if ((mask & 0x80000000) != 0) {
return i;
}
mask <<= 1;
}
return -1; // no bits set
}
/**
*
* Decodes a tile.
*
**/
public int[] decode(byte[] bits, int ofs, int stride) {
int v, r, g, b, a, s;
int pos=0;
stride *= bytesPerRow;
for (int i=0; i<8; i++) {
// do one row of pixels
for (int j=0; j<8; j++) {
// get encoded pixel
s = startShift;
v = 0;
for (int k=0; k<bytesPerPixel; k++) {
v |= (bits[ofs++] & 0xFF) << s;
s += shiftStep;
}
// decode R component
r = v & rmask;
if (rshift < 0) {
r >>= -rshift;
}
else {
r <<= rshift;
}
// decode G component
g = v & gmask;
if (gshift < 0) {
g >>= -gshift;
}
else {
g <<= gshift;
}
// decode B component
b = v & bmask;
if (bshift < 0) {
b >>= -bshift;
}
else {
b <<= bshift;
}
// decode A component
a = v & amask;
if (ashift < 0) {
a >>= -ashift;
}
else {
a <<= ashift;
}
// final pixel
pixels[pos++] = a | r | g | b;
}
ofs += stride;
}
return pixels;
}
/**
*
* Encodes a tile.
*
**/
public void encode(int[] pixels, byte[] bits, int ofs, int stride) {
int v, r, g, b, a, s, argb;
int pos=0;
stride *= bytesPerRow;
for (int i=0; i<8; i++) {
// do one row of pixels
for (int j=0; j<8; j++) {
// get decoded pixel
argb = pixels[pos++];
// encode R component
r = argb;
if (rshift < 0) {
r <<= -rshift;
}
else {
r >>= rshift;
}
r &= rmask;
// encode G component
g = argb;
if (gshift < 0) {
g <<= -gshift;
}
else {
g >>= gshift;
}
g &= gmask;
// encode B component
b = argb;
if (bshift < 0) {
b <<= -bshift;
}
else {
b >>= bshift;
}
b &= bmask;
// encode A component
a = argb;
if (ashift < 0) {
a <<= -ashift;
}
else {
a >>= ashift;
}
a &= amask;
// final value
s = startShift;
v = a | r | g | b;
for (int k=0; k<bytesPerPixel; k++) {
bits[ofs++] = (byte)(v >> s);
s += shiftStep;
}
}
ofs += stride;
}
}
/**
*
* Gets the least number of whole bytes that are required to store
* <code>bits</code> bits of information.
*
**/
private static int getByteIntegralBitCount(int bits) {
int bytes = bits / 8;
int extrabits = bits % 8;
if (extrabits != 0) bytes++;
return bytes * 8;
}
}