Refactor sprite loading some, read alphas properly instead of this silly hack

This commit is contained in:
Adam
2016-06-09 18:45:41 -04:00
parent 3110c52bbf
commit e62381a65e
4 changed files with 120 additions and 83 deletions

View File

@@ -39,7 +39,8 @@ public class SpriteDefinition
private int offsetY; private int offsetY;
private int width; private int width;
private int height; private int height;
private byte[] pixels; private byte[] pixels; // indexes into palette
private byte[] alphas;
private int maxWidth; private int maxWidth;
private int maxHeight; private int maxHeight;
@@ -108,6 +109,16 @@ public class SpriteDefinition
this.pixels = pixels; this.pixels = pixels;
} }
public byte[] getAlphas()
{
return alphas;
}
public void setAlphas(byte[] alphas)
{
this.alphas = alphas;
}
public int getMaxWidth() public int getMaxWidth()
{ {
return maxWidth; return maxWidth;

View File

@@ -35,88 +35,140 @@ import net.runelite.cache.io.InputStream;
public class SpriteLoader public class SpriteLoader
{ {
public static final int FLAG_VERTICAL = 0b01;
public static final int FLAG_ALPHA = 0b10;
private SpriteDefinition[] sprites; private SpriteDefinition[] sprites;
private int[] loadedPalette; private int[] loadedPalette;
private int loadedSpriteMaxWidth; private int width;
private int loadedSpriteMaxHeight; private int height;
public void load(InputStream stream) public void load(InputStream stream)
{ {
stream.setOffset(stream.getLength() - 2); stream.setOffset(stream.getLength() - 2);
int paletteChildCount = stream.readUnsignedShort(); int spriteCount = stream.readUnsignedShort();
sprites = new SpriteDefinition[paletteChildCount];
for (int i = 0; i < paletteChildCount; ++i) sprites = new SpriteDefinition[spriteCount];
for (int i = 0; i < spriteCount; ++i)
{ {
sprites[i] = new SpriteDefinition(this); sprites[i] = new SpriteDefinition(this);
} }
stream.setOffset(stream.getLength() - 7 - paletteChildCount * 8);
loadedSpriteMaxWidth = stream.readUnsignedShort();
loadedSpriteMaxHeight = stream.readUnsignedShort();
int var3 = (stream.readUnsignedByte() & 255) + 1;
int spriteIndex; // 2 for size
for (spriteIndex = 0; spriteIndex < paletteChildCount; ++spriteIndex) // 5 for width, height, palette length
// + 8 bytes per sprite for offset x/y, width, and height
stream.setOffset(stream.getLength() - 7 - spriteCount * 8);
width = stream.readUnsignedShort();
height = stream.readUnsignedShort();
int paletteLength = (stream.readUnsignedByte() & 255) + 1;
for (int i = 0; i < spriteCount; ++i)
{ {
sprites[spriteIndex].setOffsetX(stream.readUnsignedShort()); sprites[i].setOffsetX(stream.readUnsignedShort());
} }
for (spriteIndex = 0; spriteIndex < paletteChildCount; ++spriteIndex) for (int i = 0; i < spriteCount; ++i)
{ {
sprites[spriteIndex].setOffsetY(stream.readUnsignedShort()); sprites[i].setOffsetY(stream.readUnsignedShort());
} }
for (spriteIndex = 0; spriteIndex < paletteChildCount; ++spriteIndex) for (int i = 0; i < spriteCount; ++i)
{ {
sprites[spriteIndex].setWidth(stream.readUnsignedShort()); sprites[i].setWidth(stream.readUnsignedShort());
} }
for (spriteIndex = 0; spriteIndex < paletteChildCount; ++spriteIndex) for (int i = 0; i < spriteCount; ++i)
{ {
sprites[spriteIndex].setHeight(stream.readUnsignedShort()); sprites[i].setHeight(stream.readUnsignedShort());
} }
stream.setOffset(stream.getLength() - 7 - paletteChildCount * 8 - (var3 - 1) * 3); // same as above + 3 bytes for each palette entry, except for the first one (which is transparent)
loadedPalette = new int[var3]; stream.setOffset(stream.getLength() - 7 - spriteCount * 8 - (paletteLength - 1) * 3);
loadedPalette = new int[paletteLength];
for (spriteIndex = 1; spriteIndex < var3; ++spriteIndex) for (int i = 1; i < paletteLength; ++i)
{ {
loadedPalette[spriteIndex] = stream.read24BitInt(); loadedPalette[i] = stream.read24BitInt();
if (0 == loadedPalette[spriteIndex])
if (loadedPalette[i] == 0)
{ {
loadedPalette[spriteIndex] = 1; loadedPalette[i] = 1;
} }
} }
stream.setOffset(0); stream.setOffset(0);
for (spriteIndex = 0; spriteIndex < paletteChildCount; ++spriteIndex) for (int i = 0; i < spriteCount; ++i)
{ {
SpriteDefinition def = sprites[spriteIndex]; SpriteDefinition def = sprites[i];
int width = def.getWidth(); int width = def.getWidth();
int height = def.getHeight(); int height = def.getHeight();
int dimmension = width * height; // XXX OFFSETS
byte[] loadPixels = new byte[dimmension]; int dimension = width * height;
int var4 = stream.readUnsignedByte(); byte[] pixelPaletteIndicies = new byte[dimension];
int var5; byte[] pixelAlphas = new byte[dimension];
if (var4 == 0)
int flags = stream.readUnsignedByte();
if ((flags & FLAG_VERTICAL) == 0)
{ {
for (var5 = 0; var5 < dimmension; ++var5) // read horizontally
for (int j = 0; j < dimension; ++j)
{ {
loadPixels[var5] = (byte) stream.readByte(); pixelPaletteIndicies[j] = stream.readByte();
} }
} }
else if (1 == var4) else
{ {
for (var5 = 0; var5 < width; ++var5) // read vertically
for (int j = 0; j < width; ++j)
{ {
for (int var8 = 0; var8 < height; ++var8) for (int k = 0; k < height; ++k)
{ {
loadPixels[width * var8 + var5] = (byte) stream.readByte(); pixelPaletteIndicies[width * k + j] = stream.readByte();
} }
} }
} }
def.setPixels(loadPixels);
// read alphas
if ((flags & FLAG_ALPHA) != 0)
{
if ((flags & FLAG_VERTICAL) == 0)
{
// read horizontally
for (int j = 0; j < dimension; ++j)
{
pixelAlphas[j] = stream.readByte();
}
}
else
{
// read vertically
for (int j = 0; j < width; ++j)
{
for (int k = 0; k < height; ++k)
{
pixelAlphas[width * k + j] = stream.readByte();
}
}
}
}
else
{
// everything non-zero is opaque
for (int j = 0; j < dimension; ++j)
{
int index = pixelPaletteIndicies[j];
if (index != 0)
pixelAlphas[j] = (byte) 0xFF;
}
}
def.setPixels(pixelPaletteIndicies);
def.setAlphas(pixelAlphas);
} }
} }

View File

@@ -30,8 +30,7 @@
package net.runelite.cache.renderable; package net.runelite.cache.renderable;
import java.awt.*; import java.awt.image.BufferedImage;
import java.awt.image.*;
import net.runelite.cache.definitions.SpriteDefinition; import net.runelite.cache.definitions.SpriteDefinition;
public class RGBSprite public class RGBSprite
@@ -94,15 +93,19 @@ public class RGBSprite
sprite.spriteWidth = def.getWidth(); sprite.spriteWidth = def.getWidth();
sprite.spriteHeight = def.getHeight(); sprite.spriteHeight = def.getHeight();
int dimmension = sprite.spriteWidth * sprite.spriteHeight; int dimension = sprite.spriteWidth * sprite.spriteHeight;
byte[] pixels = def.getPixels(); byte[] pixels = def.getPixels();
int[] palette = def.getLoader().getLoadedPalette(); int[] palette = def.getLoader().getLoadedPalette();
byte[] alphas = def.getAlphas();
sprite.pixels = new int[dimmension]; sprite.pixels = new int[dimension];
for (int pos = 0; pos < dimmension; ++pos) for (int pos = 0; pos < dimension; ++pos)
{ {
sprite.pixels[pos] = palette[pixels[pos] & 255]; int index = pixels[pos] & 0xff;
sprite.pixels[pos] = palette[index] | (alphas[pos] << 24);
} }
return sprite; return sprite;
@@ -110,43 +113,8 @@ public class RGBSprite
public BufferedImage getBufferedImage() public BufferedImage getBufferedImage()
{ {
BufferedImage bi = new BufferedImage(spriteWidth, spriteHeight, BufferedImage.TYPE_INT_RGB); BufferedImage bi = new BufferedImage(spriteWidth, spriteHeight, BufferedImage.TYPE_INT_ARGB);
bi.setRGB(0, 0, spriteWidth, spriteHeight, pixels, 0, spriteWidth); bi.setRGB(0, 0, spriteWidth, spriteHeight, pixels, 0, spriteWidth);
Image img = makeColorTransparent(bi, new Color(0, 0, 0)); return bi;
BufferedImage trans = imageToBufferedImage(img);
return trans;
}
private static Image makeColorTransparent(BufferedImage im, final Color color)
{
final int markerRGB = color.getRGB() | 0xFF000000;
RGBImageFilter filter = new RGBImageFilter()
{
@Override
public final int filterRGB(int x, int y, int rgb)
{
if ((rgb | 0xFF000000) == markerRGB)
{
return 0x00FFFFFF & rgb;
}
else
{
return rgb;
}
}
};
ImageProducer ip = new FilteredImageSource(im.getSource(), filter);
return Toolkit.getDefaultToolkit().createImage(ip);
}
private static BufferedImage imageToBufferedImage(Image image)
{
BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bufferedImage.createGraphics();
g2.drawImage(image, 0, 0, null);
g2.dispose();
return bufferedImage;
} }
} }

View File

@@ -64,6 +64,8 @@ public class SpriteLoaderTest
java.io.File base = StoreLocation.LOCATION, java.io.File base = StoreLocation.LOCATION,
outDir = folder.newFolder(); outDir = folder.newFolder();
int count = 0;
try (Store store = new Store(base)) try (Store store = new Store(base))
{ {
store.load(); store.load();
@@ -96,10 +98,14 @@ public class SpriteLoaderTest
java.io.File targ = new java.io.File(outDir, a.getArchiveId() + "-" + i + ".png"); java.io.File targ = new java.io.File(outDir, a.getArchiveId() + "-" + i + ".png");
targ.mkdirs(); targ.mkdirs();
ImageIO.write(image, "png", targ); ImageIO.write(image, "png", targ);
++count;
} }
} }
} }
logger.info("Dumped to {}", outDir); Assert.assertTrue(count > 3000);
logger.info("Dumped {} sprites to {}", count, outDir);
} }
} }