eaglercraft/samples/ayunami2000/MapPacketCodec.java
2022-04-15 22:38:43 -07:00

232 lines
5.9 KiB
Java

package ayunami2000;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
public class MapPacketCodec {
public interface FragmentHandler {
void sendFragment(byte[] data, boolean isLastFragment);
}
public enum PixelFormat {
R5_G6_B5, R8_G8_B8
}
public final int mapId;
private Deflater deflate = null;
private PixelFormat pixelFormat = PixelFormat.R5_G6_B5;
private int[] pixels = null;
private int[] pallete = null;
private boolean palleteIsSet = false;
private boolean palleteIsDirty = false;
/**
* @param mapId the ID of the map item to write to
*/
public MapPacketCodec(int mapId) {
this.mapId = mapId;
}
/**
* @param enable enables java.util.zip deflate on packets encoded by this class
*/
public MapPacketCodec deflate(boolean enable) {
deflate(enable ? 5 : 0);
return this;
}
/**
* @param level sets or disables compression level (0-9)
*/
public MapPacketCodec deflate(int level) {
deflate = level > 0 ? new Deflater(level) : null;
return this;
}
/**
* @param pix sets if pixels should be encoded as 16 bits per pixel or 24 bits per pixel
*/
public MapPacketCodec pixelFormat(PixelFormat pix) {
pixelFormat = pix == null ? PixelFormat.R5_G6_B5 : pix;
return this;
}
/**
* @param pixels If pallete is disabled, array of 16384 integers each containing the RGB of a pixel. If
* pallete is enabled, array of 16384 integers each containing an index in the current pallete between 0 and 255
*/
public MapPacketCodec setPixels(int[] pixels) {
if(pixels != null && pixels.length != 16384) {
throw new IllegalArgumentException("Pixel array must be 16384 pixels long");
}
this.pixels = pixels;
return this;
}
/**
* @param pixels a 128x128 RGB java.awt.image.BufferedImage, this will disable the pallete
*/
public MapPacketCodec setPixels(BufferedImage pixels) {
if(pixels.getWidth() != 128 || pixels.getHeight() != 128) {
throw new IllegalArgumentException("BufferedImage must be 128x128 pixels");
}
palleteIsSet = false;
this.pallete = null;
palleteIsDirty = false;
int[] pxls = new int[16384];
pixels.getRGB(0, 0, 128, 128, pxls, 0, 128);
setPixels(pxls);
return this;
}
/**
* sets and enables the pallete, or disables it if 'pallete' is null
*
* @param pallete an array of any size between 1 and 256 containing integers each representing a single RGB color
*/
public MapPacketCodec setPallete(int[] pallete) {
boolean b = pallete != null;
if(b) {
palleteIsSet = true;
this.pallete = pallete;
palleteIsDirty = true;
}else {
if(pixels != null && this.pallete != null) {
int[] px = pixels;
pixels = new int[16384];
for(int i = 0; i < 16384; ++i) {
int j = px[i];
pixels[i] = this.pallete[j >= this.pallete.length ? 0 : j];
}
}
this.pallete = null;
palleteIsSet = false;
palleteIsDirty = false;
}
return this;
}
/**
* Disables the pallete
*/
public MapPacketCodec clearPallete() {
setPallete(null);
return this;
}
/*
* Operations:
*
* - 0: disable engine
* - 1: compressed data
*
* - 2: set pixels R8_G8_B8
* - 3: set pixels R5_G6_B5
* - 4: set pallete R8_G8_B8
* - 5: set pallete R5_G6_B5
* - 6: set pixels via pallete
* - 7: set pallete and pixels R8_G8_B8
* - 8: set pallete and pixels R5_G6_B5
*
*/
/**
* takes the current pixels array and writes it to a packet and returns it, or returns null if the current pixels array has not changed
*/
public byte[] getNextPacket() {
if(pixels == null) {
return null;
}
try {
ByteArrayOutputStream o = new ByteArrayOutputStream();
DataOutputStream s;
if(deflate != null) {
o.write(1);
s = new DataOutputStream(new DeflaterOutputStream(o, deflate));
}else {
s = new DataOutputStream(o);
}
if(!palleteIsSet || pallete == null) {
palleteIsSet = false;
if(pixelFormat == PixelFormat.R5_G6_B5) {
s.write(3);
for(int i = 0; i < 16384; ++i) {
int j = pixels[i];
int r = (j >> 19) & 0x1F;
int g = (j >> 10) & 0x3F;
int b = (j >> 3) & 0x1F;
s.writeShort((r << 11) | (g << 5) | b);
}
}else if(pixelFormat == PixelFormat.R8_G8_B8) {
s.write(2);
for(int i = 0; i < 16384; ++i) {
int j = pixels[i];
s.write((j >> 16) & 0xFF);
s.write((j >> 8) & 0xFF);
s.write(j & 0xFF);
}
}else {
return null; // ?
}
}else {
if(palleteIsDirty) {
if(pixelFormat == PixelFormat.R5_G6_B5) {
s.write(8);
s.write(pallete.length);
for(int i = 0; i < pallete.length; ++i) {
int j = pallete[i];
int r = (j >> 19) & 0x1F;
int g = (j >> 10) & 0x3F;
int b = (j >> 3) & 0x1F;
s.writeShort((r << 11) | (g << 5) | b);
}
}else if(pixelFormat == PixelFormat.R8_G8_B8) {
s.write(7);
s.write(pallete.length);
for(int i = 0; i < pallete.length; ++i) {
int j = pallete[i];
s.write((j >> 16) & 0xFF);
s.write((j >> 8) & 0xFF);
s.write(j & 0xFF);
}
}else {
return null; // ?
}
palleteIsDirty = false;
}else {
s.write(6);
}
for(int i = 0; i < 16384; ++i) {
s.write(pixels[i]);
}
}
pixels = null;
s.close();
return o.toByteArray();
}catch(IOException e) {
throw new RuntimeException("Failed to write ayunami map packet");
}
}
public byte[] getDisablePacket() {
try {
palleteIsSet = false;
palleteIsDirty = false;
pallete = null;
pixels = null;
ByteArrayOutputStream o = new ByteArrayOutputStream();
DataOutputStream s = new DataOutputStream(o);
s.writeShort(mapId);
s.write(0);
return o.toByteArray();
}catch(IOException e) {
throw new RuntimeException("Failed to write ayunami map packet");
}
}
}