Better Sprite Packer

You should adapt jagex method of making sprites, packing the pixels instead of the image data.
 
Is there a drastic difference in compression doing it that way?
Using java to create image from byte[] creates a lot of unneeded objects. Just pack the pixels without the palette if you want "HD" sprites support, if you don't pack palette and pixels pointing to the palette. You'll see the difference when you do it.
 
Updates: October 8, 2016 (New format)
This is a fairly big update, first before I mention the updates. I had to change the encoding format Galkon originally had to support hd sprites. So if you use this version to read your past archives it won't work but I'll show you how to convert it takes 2 seconds.
  • Support for HD sprites
  • Pixels are now packed instead of the entire sprite data
  • Better compression
  • The program now ignores the color rgb (255, 0, 255) or hexadecimal 0xFF00FF which is the Pink color and makes it transparent by default.
    2oliCoO.png

    YQnVIjc.png

    Next update or so I'll let you pick your own transparency colors.
  • The program now creates only 1 image archive (it used to create a .dat and .idx file, but I merged the two for convenience so you don't have all of these files)
  • All image formats are supported (PNG, JPG, GIF, Tiff, Bmp, Webp, Bpg, Enif) They all get converted to PNG for transparency reasons when you dump the sprites.
  • You can now change the offsets of a sprite (x or y)
  • You don't even have to use this for RSPS anymore, if you wanted you can use this to create sprite archives for other games well or for general image compression.

Archives made pre version 1.45 will not work with this update follow these instructions to convert over.

To convert pre-1.45 archives
Use version 1.44 which can be found here, load up your archives and dump your sprites as images into a directory. Then use version 1.45 found here to load up the sprites then you can create new archives (you can now delete the sprites.idx since its no longer used)

To make the new format work with your client
Go to your SpriteLoader class and replace it with this.

Code:
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;

/**
 * The class that loads sprites from a custom sprites archive.
 * 
 * @author Vult-R
 */
public final class SpriteLoader {

	private static final Logger logger = Logger.getLogger(SpriteLoader.class.getName());
	
	public static SpriteLoader[] cache;
	public static Sprite[] sprites;
	
	public static int totalSprites;
	
	private int id = -1;
	private String name = "name";
	private int offsetX = 0;		
	private int offsetY = 0;

	private int width;	
	private int height;
	
	private int[] pixels;	

	public static void load() {
			Buffer data = new Buffer(FileUtility.readFile(SignLink.findcachedir() + "sprites.dat"));
			try(DataInputStream dataFile = new DataInputStream(new GZIPInputStream(new ByteArrayInputStream(data.buffer)))) {
				
				int totalSprites = dataFile.readInt();
				
				logger.info("Sprites Loaded: " + totalSprites);
				
				if (cache == null) {
					cache = new SpriteLoader[totalSprites];
					sprites = new Sprite[totalSprites];
				}
				
				for (int index = 0; index < totalSprites; index++) {					
					
					SpriteLoader loader = cache[index] = SpriteLoader.decode(dataFile);
					
					sprites[index] = new Sprite(loader);

				}
			} catch (IOException ex) {
				ex.printStackTrace();
			}
	}

    private static SpriteLoader decode(DataInputStream dat) throws IOException {
    	
    	SpriteLoader loader = new SpriteLoader();
    	
          while (true) {
                
                byte opcode = dat.readByte();
                
                if (opcode == 0) {
                      return loader;
                } else if (opcode == 1) {
                	loader.id = dat.readShort();
                } else if (opcode == 2) {
                	loader.name = dat.readUTF();
                } else if (opcode == 3) {
                	loader.width = dat.readShort();
                } else if (opcode == 4) {
                	loader.height = dat.readShort();
                } else if (opcode == 5) {
                	loader.offsetX = dat.readShort();
                } else if (opcode == 6) {
                	loader.offsetY = dat.readShort();
                } else if (opcode == 7) {
                	
                      int indexLength = dat.readInt();
                      
                      int[] pixels = new int[indexLength];                      
                      
                      for (int i = 0; i < pixels.length; i++) {
                    	  pixels[i] = dat.readInt();
                      }
                       
                      loader.pixels = pixels;
                }
          }
    }
    
	public static BufferedImage convert(BufferedImage src, int bufImgType) {
	    BufferedImage img= new BufferedImage(src.getWidth(), src.getHeight(), bufImgType);
	    Graphics2D g2d= img.createGraphics();
	    g2d.drawImage(src, 0, 0, null);
	    g2d.dispose();
	    return img;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getOffsetX() {
		return offsetX;
	}

	public void setOffsetX(int offsetX) {
		this.offsetX = offsetX;
	}

	public int getOffsetY() {
		return offsetY;
	}

	public void setOffsetY(int offsetY) {
		this.offsetY = offsetY;
	}

	public int getWidth() {
		return width;
	}

	public void setWidth(int width) {
		this.width = width;
	}

	public int getHeight() {
		return height;
	}

	public void setHeight(int height) {
		this.height = height;
	}

	public int[] getPixels() {
		return pixels;
	}

	public void setPixels(int[] pixels) {
		this.pixels = pixels;
	}

}

Go to your sprite class add this

Code:
	public Sprite(SpriteLoader loader) {
		this.width = loader.getWidth();
		this.height = loader.getHeight();
		this.drawOffsetX = loader.getOffsetX();
		this.drawOffsetY = loader.getOffsetY();
		this.raster = loader.getPixels();
		this.setTransparency(255, 0, 255);
	}

Change the variables to your naming if you have errors. For transparency to work make sure you did the HD Sprites tutorial in your client which can be found here. https://www.rune-server.org/runescape-development/rs2-client/tutorials/316016-client-hd-sprites-support.html (most clients have this nowadays)

I did test this a 100 times, before I released.

T9uYow0.png


nothing to special to show but most of the sprites used in this screenshot have pink backgrounds.

Download

version 1.45 (newer format)
bsp_1.45

version 1.44 (old format works with galkons format)
bsp_1.44
All cool features, can't wait to upgrade to 1.45, though;
Wouldn't a feature to mass add sprites? Instead of one single sprite at a time?
 
All cool features, can't wait to upgrade to 1.45, though;
Wouldn't a feature to mass add sprites? Instead of one single sprite at a time?

Yeah I can add that feature in 1.46 :) any other features you think would be helpful?

Features for next update
  • Can now add multiple sprites.
  • Multi-selection
  • Drag an drop features?
  • When removing a sprite, if the sprite is the last sprite the sprite now actually gets removed instead of creating an empty sprite.
 
Yeah I can add that feature in 1.46 :) any other features you think would be helpful?

Features for next update
  • Selecting multiple sprites to add (if the sprite is named with an index (some integer) then the image will replace the index in the list.
  • Selecting multiple sprites to dump
  • Drag an drop features?

Wouldn't it be better to actually ADD the sprite instead of replacing? Or make a confirmation box to check whether they'd want to replace or add the sprite.


Sent from my iPhone using Tapatalk
 
Patch 1.46 (October 9, 2016)
  • Can now add multiple sprites.
    5ig4Ab5.gif
  • Can now import all images in a directory (The program sorts them if they are named with an index)

    SRBg7tV.png


    becomes

    wWjEy3Q.gif



    nzvabmQ.png


    becomes
    WG0hYH8.gif

  • When removing the last sprite in the list, the sprite actually gets removed instead of creating an empty sprite.
    Ton40VF.gif
  • Fixed a bug with the current directory
  • When importing an image archive, you now select the file instead of the directory.
    sIAIEXQ.png

rippp
 
Takes an excessively long time to load images in the new format.

Sprites Loaded: 767
Loaded: 9149ms
 
Sprites.dat gets written but Sprites.idx isn't
 
Takes an excessively long time to load images in the new format.

Sprites Loaded: 767
Loaded: 9149ms

Yeah I noticed that too, I'll see what I can do to fix that issue.

Patch: 1.47 October, 14, 2016
  • Added a color picker so you have full control over what color is transparent. (The GIF distorts the color palette really bad but in the actual program the colors look great)
    lX6mIsY.png


    o5ZdOcb.png


    SHE2GFJ.gif
  • pure white is now transparent 0xFFFF
  • sprites now display centered when selected from the list instead of anchored towards the top.
  • titled pane content is now transparent (this is so you can easily see small sprites such as font more clearly)
    8pmPjhs.png
  • when importing sprites from a directory, the name of the image is now also the name of the sprite (this is so if you're looking for a sprite but don't know its index you can search the name of the image.)
    fLoX4UO.gif

rippp
 
leaving rune-server - admins can't even mind their own business and insist on ham-fisting their own practices onto their users as-if anyone asked their irrelevant, presumptuous opinions
then they mute when their repeated spamming of irrelevant responses, that are specifically not wanted, is called stupid
 
Last edited:
  • Like
Reactions: [object Object]
1.48
  • Compression uses LZMA2 over gzip which offers the highest compression possible with no loss in quality
  • Reworked the UI to look more professional

AZQao8c.png


I'll post later on how to use this with your client
 

Users who are viewing this thread (total: 1, members: 0, guests: 1)