// Near Infinity - An Infinity Engine Browser and Editor
// Copyright (C) 2001 - 2005 Jon Olav Hauglid
// See LICENSE.txt for license information
package org.infinity.resource.graphics;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.nio.ByteBuffer;
import org.infinity.resource.key.ResourceEntry;
/**
* Handles legacy TIS resources (using palettized tiles).
*/
public class TisV1Decoder extends TisDecoder
{
private static final int HeaderSize = 24; // Size of the TIS header
private static final Color TransparentColor = new Color(0, true);
private ByteBuffer tisBuffer;
private int tileCount, tileSize;
private int[] workingPalette;
private BufferedImage workingCanvas;
public TisV1Decoder(ResourceEntry tisEntry)
{
super(tisEntry);
init();
}
/**
* Returns the palette of the specified tile.
* @param tileIdx The tile index
* @return The palette as int array of 256 entries (Format: ARGB).
* Returns {@code null} on error.
*/
public int[] getTilePalette(int tileIdx)
{
if (tileIdx >= 0 && tileIdx < getTileCount()) {
int[] palette = new int[256];
getTilePalette(tileIdx, palette);
return palette;
} else {
return null;
}
}
/**
* Writes the palette entries of the specified tile into the buffer.
* @param tileIdx The tile index
* @param buffer The buffer to write the palette data into.
*/
public void getTilePalette(int tileIdx, int[] buffer)
{
if (buffer != null) {
int ofs = getTileOffset(tileIdx);
if (ofs > 0) {
int maxLen = (buffer.length > 256) ? 256 : buffer.length;
for (int i = 0; i < maxLen; i++) {
buffer[i] = tisBuffer.getInt(ofs);
if (i == 0 && (buffer[i] & 0x00ffffff) == 0x0000ff00) {
buffer[i] &= 0xff000000;
} else {
buffer[i] |= 0xff000000;
}
ofs += 4;
}
}
}
}
/**
* Returns the unprocessed tile data (without palette).
* @param tileIdx The tile index
* @return Unprocessed data of the specified tile without the palette block.
* Returns {@code null} on error.
*/
public byte[] getRawTileData(int tileIdx)
{
if (tileIdx >= 0 && tileIdx < getTileCount()) {
byte[] buffer = new byte[TileDimension*TileDimension];
getRawTileData(tileIdx, buffer);
return buffer;
} else {
return null;
}
}
/**
* Writes unprocessed tile data into the specified buffer (without palette data).
* @param tileIdx The tile index
* @param buffer The buffer to write the tile data into.
*/
public void getRawTileData(int tileIdx, byte[] buffer)
{
if (buffer != null) {
int ofs = getTileOffset(tileIdx);
if (ofs > 0) {
int maxSize = (buffer.length > TileDimension*TileDimension) ? TileDimension*TileDimension : buffer.length;
tisBuffer.position(ofs);
tisBuffer.get(buffer, 0, maxSize);
}
}
}
@Override
public void close()
{
tisBuffer = null;
tileCount = 0;
tileSize = 0;
workingPalette = null;
if (workingCanvas != null) {
workingCanvas.flush();
workingCanvas = null;
}
}
@Override
public void reload()
{
init();
}
@Override
public ByteBuffer getResourceBuffer()
{
return tisBuffer;
}
@Override
public int getTileWidth()
{
return TileDimension;
}
@Override
public int getTileHeight()
{
return TileDimension;
}
@Override
public int getTileCount()
{
return tileCount;
}
@Override
public Image getTile(int tileIdx)
{
BufferedImage image = ColorConvert.createCompatibleImage(TileDimension, TileDimension, true);
renderTile(tileIdx, image);
return image;
}
@Override
public boolean getTile(int tileIdx, Image canvas)
{
return renderTile(tileIdx, canvas);
}
@Override
public int[] getTileData(int tileIdx)
{
int[] buffer = new int[TileDimension*TileDimension];
renderTile(tileIdx, buffer);
return buffer;
}
@Override
public boolean getTileData(int tileIdx, int[] buffer)
{
return renderTile(tileIdx, buffer);
}
private void init()
{
close();
if (getResourceEntry() != null) {
try {
int[] info = getResourceEntry().getResourceInfo();
if (info == null || info.length < 2) {
throw new Exception("Error reading TIS header");
}
tileCount = info[0];
if (tileCount <= 0) {
throw new Exception("Invalid tile count: " + tileCount);
}
tileSize = info[1];
if (tileSize != 1024 + TileDimension*TileDimension) {
throw new Exception("Invalid tile size: " + tileSize);
}
tisBuffer = getResourceEntry().getResourceBuffer();
setType(Type.PALETTE);
workingPalette = new int[256];
workingCanvas = new BufferedImage(TileDimension, TileDimension, BufferedImage.BITMASK);
} catch (Exception e) {
e.printStackTrace();
close();
}
}
}
// Returns the start offset of the specified tile. Returns -1 on error.
private int getTileOffset(int tileIdx)
{
if (tileIdx >= 0 && tileIdx < getTileCount()) {
return HeaderSize + tileIdx*tileSize;
} else {
return -1;
}
}
// Paints the specified tile onto the canvas
private boolean renderTile(int tileIdx, Image canvas)
{
if (canvas != null && canvas.getWidth(null) >= TileDimension && canvas.getHeight(null) >= TileDimension) {
int[] buffer = ((DataBufferInt)workingCanvas.getRaster().getDataBuffer()).getData();
if (renderTile(tileIdx, buffer)) {
buffer = null;
Graphics2D g = (Graphics2D)canvas.getGraphics();
try {
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));
g.setColor(TransparentColor);
g.fillRect(0, 0, TileDimension, TileDimension);
g.drawImage(workingCanvas, 0, 0, null);
} finally {
g.dispose();
g = null;
}
return true;
}
buffer = null;
}
return false;
}
// Writes the specified tile data into the buffer
private boolean renderTile(int tileIdx, int[] buffer)
{
int size = TileDimension*TileDimension;
if (buffer != null && buffer.length >= size) {
int ofs = getTileOffset(tileIdx);
if (ofs > 0) {
ofs += 1024; // skipping palette data
getTilePalette(tileIdx, workingPalette);
for (int i = 0; i < size; i++, ofs++) {
buffer[i] = workingPalette[tisBuffer.get(ofs) & 0xff];
}
return true;
}
}
return false;
}
}