Skip to content

Instantly share code, notes, and snippets.

@Hadyn
Created August 29, 2015 14:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Hadyn/0a445dff0961eac9c6f3 to your computer and use it in GitHub Desktop.
Save Hadyn/0a445dff0961eac9c6f3 to your computer and use it in GitHub Desktop.
package org.github.hadyn.api.tool;
import net.openrs.cache.Cache;
import net.openrs.cache.Container;
import net.openrs.cache.FileStore;
import net.openrs.cache.ReferenceTable;
import org.github.hadyn.api.cache.OverlayFloorDefinition;
import org.github.hadyn.api.cache.OverlayFloorDefinitions;
import org.github.hadyn.api.cache.UnderlayFloorConfig;
import org.github.hadyn.api.cache.UnderlayFloorDefinitions;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
/**
* @author Hadyn Fitzgerald
*/
public class MapRenderer {
/**
* The tile shapes as bit maps.
*/
private static int[][] TILE_SHAPES = new int[][]{
{
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
},
{
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1
},
{
1, 0, 0, 0,
1, 1, 0, 0,
1, 1, 1, 0,
1, 1, 1, 1
},
{
1, 1, 0, 0,
1, 1, 0, 0,
1, 0, 0, 0,
1, 0, 0, 0
},
{
0, 0, 1, 1,
0, 0, 1, 1,
0, 0, 0, 1,
0, 0, 0, 1},
{
0, 1, 1, 1,
0, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1
},
{
1, 1, 1, 0,
1, 1, 1, 0,
1, 1, 1, 1,
1, 1, 1, 1
},
{
1, 1, 0, 0,
1, 1, 0, 0,
1, 1, 0, 0,
1, 1, 0, 0
},
{
0, 0, 0, 0,
0, 0, 0, 0,
1, 0, 0, 0,
1, 1, 0, 0
},
{
1, 1, 1, 1,
1, 1, 1, 1,
0, 1, 1, 1,
0, 0, 1, 1
},
{
1, 1, 1, 1,
1, 1, 0, 0,
1, 0, 0, 0,
1, 0, 0, 0
},
{
0, 0, 0, 0,
0, 0, 1, 1,
0, 1, 1, 1,
0, 1, 1, 1
},
{
0, 0, 0, 0,
0, 0, 0, 0,
0, 1, 1, 0,
1, 1, 1, 1
}
};
/**
* The tile rotation matrices for each shape.
*/
private static int[][] TILE_ROTATIONS = new int[][]{
{
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15
},
{
12, 8, 4, 0,
13, 9, 5, 1,
14, 10, 6, 2,
15, 11, 7, 3
},
{
15, 14, 13, 12,
11, 10, 9, 8,
7, 6, 5, 4,
3, 2, 1, 0
},
{
3, 7, 11, 15,
2, 6, 10, 14,
1, 5, 9, 13,
0, 4, 8, 12
}
};
class RenderableTile {
private int x;
private int y;
private int shape;
private int rotation;
private int backgroundColor;
private int foregroundColor;
public RenderableTile(int x, int y, int shape, int rotation, int backgroundColor, int foregroundColor) {
this.x = x;
this.y = y;
this.shape = shape;
this.rotation = rotation;
this.backgroundColor = backgroundColor;
this.foregroundColor = foregroundColor;
}
/**
* Draws to a buffered image. Assumes that the image we are drawing to will accept a 4 x 4 pixel area to draw
* the tile to at the respected x and y coordinate for this tile. This method draws respective to the bottom
* left y axis.
*
* @param image the image to draw to.
*/
public void drawTo(BufferedImage image) {
if (shape > 0) {
int[] shapeMatrix = TILE_SHAPES[shape];
int[] rotationMatrix = TILE_ROTATIONS[rotation];
if (backgroundColor != 0) {
for (int oy = 0; oy < 4; oy++) {
for (int ox = 0; ox < 4; ox++) {
int drawx = x * 4 + ox;
int drawy = y * 4 + oy;
image.setRGB(drawx, drawy, shapeMatrix[rotationMatrix[ox + (3-oy) * 4]] == 0 ? backgroundColor : foregroundColor);
}
}
} else {
for (int oy = 0; oy < 4; oy++) {
for (int ox = 0; ox < 4; ox++) {
int drawx = x * 4 + ox;
int drawy = y * 4 + oy;
if (shapeMatrix[rotationMatrix[ox + (3-oy) * 4]] != 0) {
image.setRGB(drawx, drawy, foregroundColor);
}
}
}
}
} else {
for (int oy = 0; oy < 4; oy++) {
for (int ox = 0; ox < 4; ox++) {
int drawx = x * 4 + ox;
int drawy = y * 4 + oy;
image.setRGB(drawx, drawy, backgroundColor);
}
}
}
}
}
class MapBlock {
/**
* The width.
*/
int width;
/**
* The height.
*/
int height;
/**
* The underlay floor ids.
*/
int[][][] foregroundConfigIds;
/**
* The overlay floor ids, tile shapes, and rotations.
*/
int[][][] backgroundConfigIds;
int[][][] tileShapes;
int[][][] tileRotations;
/**
* The accumulated hue, saturation, luminosity, and intensity for each y coordinate.
*/
int[] accHue;
int[] accSaturation;
int[] accLuminosity;
int[] accIntensity;
int[] counter;
/**
* The tile flags.
*/
int[][][] flags;
MapBlock(int width, int height) {
this.width = width;
this.height = height;
foregroundConfigIds = new int[4][width][height];
backgroundConfigIds = new int[4][width][height];
tileShapes = new int[4][width][height];
tileRotations = new int[4][width][height];
accHue = new int[height];
accLuminosity = new int[height];
accSaturation = new int[height];
accIntensity = new int[height];
counter = new int[height];
flags = new int[4][width][height];
}
void decode(ByteBuffer buffer) {
for (int plane = 0; plane < 4; plane++) {
for (int x = 0; x < 64; x++) {
for (int y = 0; y < 64; y++) {
for (; ; ) {
int config = buffer.get() & 0xFF;
if (config == 0) {
break;
} else if (config == 1) {
buffer.get();
break;
} else if (config <= 49) {
foregroundConfigIds[plane][x][y] = buffer.get() & 0xff;
tileShapes[plane][x][y] = (config - 2) / 4;
tileRotations[plane][x][y] = (config - 2 & 3);
} else if (config <= 81) {
flags[plane][x][y] = config - 49;
} else {
backgroundConfigIds[plane][x][y] = config - 81;
}
}
}
}
}
}
List<RenderableTile> getRenderableTiles(int targetPlane) throws IOException {
List<RenderableTile> renderableTiles = new ArrayList<>();
for (int plane = 0; plane < 4; plane++) {
for (int x = -5; x < width + 5; x++) {
for (int y = 0; y < height; y++) {
int accSample = x + 5;
if (accSample >= 0 && accSample < width) {
int configId = backgroundConfigIds[plane][accSample][y];
if (configId != 0) {
UnderlayFloorConfig definition = UnderlayFloorDefinitions.forId(configId - 1);
definition.init();
accHue[y] += definition.getHue();
accSaturation[y] += definition.getSaturation();
accLuminosity[y] += definition.getLuminosity();
accIntensity[y] += definition.getIntensity();
counter[y]++;
}
}
int subSample = x - 5;
if (subSample >= 0 && subSample < width) {
int configId = backgroundConfigIds[plane][subSample][y];
if (configId != 0) {
UnderlayFloorConfig definition = UnderlayFloorDefinitions.forId(configId - 1);
definition.init();
accHue[y] -= definition.getHue();
accSaturation[y] -= definition.getSaturation();
accLuminosity[y] -= definition.getLuminosity();
accIntensity[y] -= definition.getIntensity();
counter[y]--;
}
}
}
if (x >= 1 && x < width - 1) {
int hue = 0;
int sat = 0;
int lum = 0;
int intensity = 0;
int total = 0;
for (int y = -5; y < height + 5; y++) {
int accSample = y + 5;
if (accSample >= 0 && accSample < height) {
hue += accHue[accSample];
sat += accSaturation[accSample];
lum += accLuminosity[accSample];
intensity += accIntensity[accSample];
total += counter[accSample];
}
int subSample = y - 5;
if (subSample >= 0 && subSample < height) {
hue -= accHue[subSample];
sat -= accSaturation[subSample];
lum -= accLuminosity[subSample];
intensity -= accIntensity[subSample];
total -= counter[subSample];
}
if (plane != targetPlane) {
continue;
}
if (y >= 1 && y < height - 1) {
int backgroundConfigId = backgroundConfigIds[plane][x][y];
int foregroundConfigId = foregroundConfigIds[plane][x][y];
if (backgroundConfigId > 0 || foregroundConfigId > 0) {
int packedBackgroundColor = -1;
if (backgroundConfigId > 0) {
int h = hue * 256 / intensity;
int s = sat / total;
int l = lum / total;
if (l < 0) {
l = 0;
} else if (l > 255) {
l = 255;
}
packedBackgroundColor = getHslToInt16(h, s, l);
}
int backgroundColor = 0;
if (packedBackgroundColor != -1) {
backgroundColor = int16RgbTable[method622(packedBackgroundColor, 96)];
}
if (foregroundConfigId == 0) {
renderableTiles.add(new RenderableTile(x, y, 0, 0, backgroundColor, 0));
} else {
int shape = tileShapes[plane][x][y] + 1;
int rotation = tileRotations[plane][x][y];
OverlayFloorDefinition floor = OverlayFloorDefinitions.forId(foregroundConfigId - 1);
floor.init();
int packedForegroundColor = -1;
if (false) {
// TODO(sinisoul): Textures
} else if (floor.getRgbColor() == 0xff00ff) {
packedForegroundColor = -2;
} else {
packedForegroundColor = getHslToInt16(floor.getHue(), floor.getSaturation(), floor.getLuminosity());
}
int foregroundColor = 0;
if (packedForegroundColor != -2) {
foregroundColor = int16RgbTable[method1927(packedForegroundColor, 96)];
}
renderableTiles.add(new RenderableTile(x, y, shape, rotation, backgroundColor, foregroundColor));
}
}
}
}
}
}
}
return renderableTiles;
}
}
static final int method622(int var0, int var1) {
if (-1 == var0) {
return 12345678;
} else {
var1 = (var0 & 127) * var1 / 128;
if (var1 >= 2) {
if (var1 > 126) {
var1 = 126;
}
} else {
var1 = 2;
}
return var1 + (var0 & '\uff80');
}
}
static final int method1927(int var0, int var1) {
if(-2 == var0) {
return 12345678;
} else if(var0 == -1) {
if(var1 < 2) {
var1 = 2;
} else if(var1 > 126) {
var1 = 126;
}
return var1;
} else {
var1 = var1 * (var0 & 127) / 128;
if(var1 < 2) {
var1 = 2;
} else if(var1 > 126) {
var1 = 126;
}
return (var0 & '\uff80') + var1;
}
}
/**
*
*/
private Cache cache;
/**
*
*/
private ReferenceTable table;
/**
* @param cache
* @throws IOException
*/
public MapRenderer(Cache cache) throws IOException {
this.cache = cache;
table = ReferenceTable.decode(Container.decode(cache.getStore().read(255, 5)).getData());
}
/**
* @param x
* @param y
*/
public BufferedImage render(int plane, int x, int y) throws IOException {
int landscapeEntryId = table.findEntry("l" + x + "_" + y);
int mapEntryId = table.findEntry("m" + x + "_" + y);
if (landscapeEntryId == -1 || mapEntryId == -1) {
return null;
}
MapBlock block = new MapBlock(64, 64);
block.decode(cache.read(5, mapEntryId).getData());
BufferedImage bufferedImage = new BufferedImage(64 * 4, 64 * 4, BufferedImage.TYPE_INT_RGB);
List<RenderableTile> tiles = block.getRenderableTiles(plane);
tiles.forEach(tile -> tile.drawTo(bufferedImage));
BufferedImage copy = new BufferedImage(64 * 4, 64 * 4, BufferedImage.TYPE_INT_RGB);
Graphics2D g = copy.createGraphics();
AffineTransform at = new AffineTransform();
at.concatenate(AffineTransform.getScaleInstance(1, -1));
at.concatenate(AffineTransform.getTranslateInstance(0, -bufferedImage.getHeight()));
g.transform(at);
g.drawImage(bufferedImage, 0, 0, null);
g.dispose();
return copy;
}
private static final int getHslToInt16(int hue, int saturation, int luminosity) {
if (luminosity > 179) {
saturation /= 2;
}
if (luminosity > 192) {
saturation /= 2;
}
if (luminosity > 217) {
saturation /= 2;
}
if (luminosity > 243) {
saturation /= 2;
}
int var4 = (hue / 4 << 10) + (saturation / 32 << 7) + luminosity / 2;
return var4;
}
private static int[] int16RgbTable = new int[65536];
public static final void calculatePalette(double var0, int var2, int var3) {
var0 += Math.random() * 0.03D - 0.015D;
int var21 = var2 * 128;
for (int var13 = var2; var13 < var3; ++var13) {
double var24 = (double) (var13 >> 3) / 64.0D + 0.0078125D;
double var22 = (double) (var13 & 7) / 8.0D + 0.0625D;
for (int var16 = 0; var16 < 128; ++var16) {
double var7 = (double) var16 / 128.0D;
double var19 = var7;
double var4 = var7;
double var14 = var7;
if (var22 != 0.0D) {
double var9;
if (var7 < 0.5D) {
var9 = var7 * (1.0D + var22);
} else {
var9 = var7 + var22 - var7 * var22;
}
double var11 = 2.0D * var7 - var9;
double var17 = var24 + 0.3333333333333333D;
if (var17 > 1.0D) {
--var17;
}
double var28 = var24 - 0.3333333333333333D;
if (var28 < 0.0D) {
++var28;
}
if (6.0D * var17 < 1.0D) {
var19 = var11 + (var9 - var11) * 6.0D * var17;
} else if (2.0D * var17 < 1.0D) {
var19 = var9;
} else if (3.0D * var17 < 2.0D) {
var19 = var11 + (var9 - var11) * (0.6666666666666666D - var17) * 6.0D;
} else {
var19 = var11;
}
if (6.0D * var24 < 1.0D) {
var4 = var11 + (var9 - var11) * 6.0D * var24;
} else if (2.0D * var24 < 1.0D) {
var4 = var9;
} else if (3.0D * var24 < 2.0D) {
var4 = var11 + (var9 - var11) * (0.6666666666666666D - var24) * 6.0D;
} else {
var4 = var11;
}
if (6.0D * var28 < 1.0D) {
var14 = var11 + (var9 - var11) * 6.0D * var28;
} else if (2.0D * var28 < 1.0D) {
var14 = var9;
} else if (3.0D * var28 < 2.0D) {
var14 = var11 + (var9 - var11) * (0.6666666666666666D - var28) * 6.0D;
} else {
var14 = var11;
}
}
int var31 = (int) (var19 * 256.0D);
int var6 = (int) (var4 * 256.0D);
int var32 = (int) (var14 * 256.0D);
int var30 = (var31 << 16) + (var6 << 8) + var32;
var30 = adjustBrightness(var30, var0);
if (var30 == 0) {
var30 = 1;
}
int16RgbTable[var21++] = var30;
}
}
}
private static int adjustBrightness(int rgb, double brightness) {
double r = (double) (rgb >> 16) / 256.0D;
double g = (double) (rgb >> 8 & 255) / 256.0D;
double b = (double) (rgb & 255) / 256.0D;
r = Math.pow(r, brightness);
g = Math.pow(g, brightness);
b = Math.pow(b, brightness);
int rBright = (int) (r * 256.0D);
int gBRight = (int) (g * 256.0D);
int bBright = (int) (b * 256.0D);
return (rBright << 16) + (gBRight << 8) + bBright;
}
/**
* Renders the runescape map out into region by region pictures.
*
* @param args the command line arguments.
*/
public static void main(String... args) throws IOException {
calculatePalette(0.5, 0, 512);
Cache cache = new Cache(FileStore.open("data/"));
UnderlayFloorDefinitions.init(cache);
OverlayFloorDefinitions.init(cache);
MapRenderer renderer = new MapRenderer(cache);
ImageIO.write(renderer.render(0, 50, 50), "png", new FileOutputStream("render.png"));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment