257 lines
6.7 KiB
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;
|
|
}
|
|
|
|
} |