From e62381a65edd3fee10f24e8bb62aaf7f509d9576 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 9 Jun 2016 18:45:41 -0400 Subject: [PATCH] Refactor sprite loading some, read alphas properly instead of this silly hack --- .../cache/definitions/SpriteDefinition.java | 13 +- .../definitions/loaders/SpriteLoader.java | 128 ++++++++++++------ .../runelite/cache/renderable/RGBSprite.java | 54 ++------ .../cache/loaders/SpriteLoaderTest.java | 8 +- 4 files changed, 120 insertions(+), 83 deletions(-) diff --git a/cache/src/main/java/net/runelite/cache/definitions/SpriteDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/SpriteDefinition.java index 4f0163ff5c..fc9dfe9a45 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/SpriteDefinition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/SpriteDefinition.java @@ -39,7 +39,8 @@ public class SpriteDefinition private int offsetY; private int width; private int height; - private byte[] pixels; + private byte[] pixels; // indexes into palette + private byte[] alphas; private int maxWidth; private int maxHeight; @@ -108,6 +109,16 @@ public class SpriteDefinition this.pixels = pixels; } + public byte[] getAlphas() + { + return alphas; + } + + public void setAlphas(byte[] alphas) + { + this.alphas = alphas; + } + public int getMaxWidth() { return maxWidth; diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/SpriteLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/SpriteLoader.java index 00c2ca5339..770f9aded3 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/SpriteLoader.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/SpriteLoader.java @@ -35,88 +35,140 @@ import net.runelite.cache.io.InputStream; public class SpriteLoader { + public static final int FLAG_VERTICAL = 0b01; + public static final int FLAG_ALPHA = 0b10; + private SpriteDefinition[] sprites; private int[] loadedPalette; - private int loadedSpriteMaxWidth; - private int loadedSpriteMaxHeight; + private int width; + private int height; public void load(InputStream stream) { stream.setOffset(stream.getLength() - 2); - int paletteChildCount = stream.readUnsignedShort(); - sprites = new SpriteDefinition[paletteChildCount]; - for (int i = 0; i < paletteChildCount; ++i) + int spriteCount = stream.readUnsignedShort(); + + sprites = new SpriteDefinition[spriteCount]; + for (int i = 0; i < spriteCount; ++i) { 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; - for (spriteIndex = 0; spriteIndex < paletteChildCount; ++spriteIndex) + // 2 for size + // 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); - loadedPalette = new int[var3]; + // same as above + 3 bytes for each palette entry, except for the first one (which is transparent) + 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(); - if (0 == loadedPalette[spriteIndex]) + loadedPalette[i] = stream.read24BitInt(); + + if (loadedPalette[i] == 0) { - loadedPalette[spriteIndex] = 1; + loadedPalette[i] = 1; } } 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 height = def.getHeight(); - int dimmension = width * height; - byte[] loadPixels = new byte[dimmension]; - int var4 = stream.readUnsignedByte(); - int var5; - if (var4 == 0) + // XXX OFFSETS + int dimension = width * height; + byte[] pixelPaletteIndicies = new byte[dimension]; + byte[] pixelAlphas = new byte[dimension]; + + 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); } } diff --git a/cache/src/main/java/net/runelite/cache/renderable/RGBSprite.java b/cache/src/main/java/net/runelite/cache/renderable/RGBSprite.java index 2c21d4bb3c..c6a984382d 100644 --- a/cache/src/main/java/net/runelite/cache/renderable/RGBSprite.java +++ b/cache/src/main/java/net/runelite/cache/renderable/RGBSprite.java @@ -30,8 +30,7 @@ package net.runelite.cache.renderable; -import java.awt.*; -import java.awt.image.*; +import java.awt.image.BufferedImage; import net.runelite.cache.definitions.SpriteDefinition; public class RGBSprite @@ -94,15 +93,19 @@ public class RGBSprite sprite.spriteWidth = def.getWidth(); sprite.spriteHeight = def.getHeight(); - int dimmension = sprite.spriteWidth * sprite.spriteHeight; + int dimension = sprite.spriteWidth * sprite.spriteHeight; byte[] pixels = def.getPixels(); + 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; @@ -110,43 +113,8 @@ public class RGBSprite 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); - Image img = makeColorTransparent(bi, new Color(0, 0, 0)); - 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; + return bi; } } diff --git a/cache/src/test/java/net/runelite/cache/loaders/SpriteLoaderTest.java b/cache/src/test/java/net/runelite/cache/loaders/SpriteLoaderTest.java index b8180359c2..ad84596159 100644 --- a/cache/src/test/java/net/runelite/cache/loaders/SpriteLoaderTest.java +++ b/cache/src/test/java/net/runelite/cache/loaders/SpriteLoaderTest.java @@ -64,6 +64,8 @@ public class SpriteLoaderTest java.io.File base = StoreLocation.LOCATION, outDir = folder.newFolder(); + int count = 0; + try (Store store = new Store(base)) { store.load(); @@ -96,10 +98,14 @@ public class SpriteLoaderTest java.io.File targ = new java.io.File(outDir, a.getArchiveId() + "-" + i + ".png"); targ.mkdirs(); ImageIO.write(image, "png", targ); + + ++count; } } } - logger.info("Dumped to {}", outDir); + Assert.assertTrue(count > 3000); + + logger.info("Dumped {} sprites to {}", count, outDir); } }