
Prefer not to have edgy references to child sexual assault in our tools. Consider that victims of such violence may well grow up to become ROM hackers just as often as the rest of us. Let's not be rude to them!
257 lines
No EOL
6.7 KiB
Java
257 lines
No EOL
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;
|
|
}
|
|
|
|
} |