gotty/js/spice-web-client/lib/images/lz.js
Soren L. Hansen c66ae7b2e4 First, primitive stab at SPiCE integration
Launch an Xspice and run:
echo -ne "\033]844;127.0.0.1;9876\007"

This will launch a SPiCE client connecting to 127.0.0.1:9876.

Still need to add all the security stuff and generally be
more defensive in the implementation.
2021-04-16 06:50:05 -07:00

699 lines
18 KiB
JavaScript

wdi.LZSS = {
LZ_IMAGE_TYPE_INVALID: 0,
LZ_IMAGE_TYPE_PLT1_LE: 1,
LZ_IMAGE_TYPE_PLT1_BE: 2,
LZ_IMAGE_TYPE_PLT4_LE: 3,
LZ_IMAGE_TYPE_PLT4_BE: 4,
LZ_IMAGE_TYPE_PLT8: 5,
LZ_IMAGE_TYPE_RGB16: 6,
LZ_IMAGE_TYPE_RGB24: 7,
LZ_IMAGE_TYPE_RGB32: 8,
LZ_IMAGE_TYPE_RGBA: 9,
LZ_IMAGE_TYPE_XXXA: 10,
LZPALETTE_FLAG_PAL_CACHE_ME: 1,
LZPALETTE_FLAG_PAL_FROM_CACHE: 2,
LZPALETTE_FLAG_TOP_DOWN: 4,
PLT_PIXELS_PER_BYTE: [0, 8, 8, 2, 2, 1],
PLT1_MASK: [1, 2, 4, 8, 16, 32, 64, 128],
copy_pixel: function(op, color, out_buf) {
out_buf[(op) + 0] = color.r;
out_buf[(op) + 1] = color.g;
out_buf[(op) + 2] = color.b;
},
lz_rgb32_decompress_rgb: function(arr) {
//TODO: global alpha and uncouple code
var encoder = 0;
var op = 0;
var ctrl;
var in_buf = new Uint8Array(arr);
var format = in_buf[encoder++];
var opaque = in_buf[encoder++];
var type = in_buf[encoder++];
encoder++; //padding
var low = in_buf[encoder+1]*Math.pow(16, 2)+in_buf[encoder];
encoder += 2;
var high = in_buf[encoder+1]*Math.pow(16, 2)+in_buf[encoder];
encoder += 2;
var len = high*Math.pow(16, 4)+low;
var buf = new ArrayBuffer(len);
var buf8 = new Uint8Array(buf);
var data = new Uint32Array(buf);
var out_buf_len = len/4;
var code, ref, len, ofs, ref_4, b_4, b;
for (ctrl = in_buf[encoder++]; op < out_buf_len; ctrl = in_buf[encoder++])
{
ref = op;
len = ctrl >> 5;
ofs = ((ctrl & 31) << 8);
if (ctrl > 31) { //>=32
len--;
if (len === 6) {
do {
code = in_buf[encoder++];
len += code;
} while (code === 255);
}
code = in_buf[encoder++];
ofs += code;
if (code === 255) {
if ((ofs - code) === (31 << 8)) {
ofs = in_buf[encoder++] << 8;
ofs += in_buf[encoder++];
ofs += 8191;
}
}
len += 1;
ofs += 1;
ref -= ofs;
if (ref === (op - 1)) {//plt4/1 what?
b = ref;
b_4 = b*4;
for (; len; --len) {
data[op] =
(255 << 24) | // alpha
(buf8[(b_4)+2] << 16) | // blue
(buf8[(b_4)+1] << 8) | // green
buf8[(b_4)]; // red
op++;
}
} else {
for (; len; --len) {
//COPY_REF_PIXEL
ref_4 = ref*4;
data[op] =
(255 << 24) | // alpha
(buf8[(ref_4)+2] << 16) | // blue
(buf8[(ref_4)+1] << 8) | // green
buf8[(ref_4)]; // red
op++;ref++;
}
}
} else {
//COPY_COMP_PIXEL
ctrl++;
data[op] =
(255 << 24) | // alpha
(in_buf[encoder] << 16) | // blue
(in_buf[encoder + 1] << 8) | // green
in_buf[encoder + 2]; // red
encoder += 3;
op++;
for (--ctrl; ctrl; ctrl--) {
//COPY_COMP_PIXEL
data[op] =
(255 << 24) | // alpha
(in_buf[encoder] << 16) | // blue
(in_buf[encoder + 1] << 8) | // green
in_buf[encoder + 2]; // red
encoder += 3;
op++;
}
}
}
if (type === this.LZ_IMAGE_TYPE_RGBA && !opaque) {
op = 0;
ctrl = null;
encoder--;
for (ctrl = in_buf[encoder++]; op < out_buf_len; ctrl = in_buf[encoder++])
{
var ref = op;
var len = ctrl >> 5;
var ofs = ((ctrl & 31) << 8);
var op_4 = op*4;
if (ctrl >= 32) {
var code;
len--;
if (len === 7 - 1) {
do {
code = in_buf[encoder++];
len += code;
} while (code === 255);
}
code = in_buf[encoder++];
ofs += code;
if (code === 255) {
if ((ofs - code) === (31 << 8)) {
ofs = in_buf[encoder++] << 8;
ofs += in_buf[encoder++];
ofs += 8191;
}
}
len += 3;
ofs += 1;
ref -= ofs;
if (ref === (op - 1)) {//plt4/1 what?
var b = ref;
for (; len; --len) {
op_4 = op*4;
//COPY_PIXEL
buf8[(op_4) + 3] = buf8[(b*4)+3];
op++;
}
} else {
for (; len; --len) {
//COPY_REF_PIXEL
op_4 = op*4;
buf8[(op_4) + 3] = buf8[(ref*4)+3];
op++;ref++;
}
}
} else {
//COPY_COMP_PIXEL
ctrl++;
buf8[(op_4) + 3] = in_buf[encoder++];
op++;
for (--ctrl; ctrl; ctrl--) {
//COPY_COMP_PIXEL
op_4 = op*4; // faster?
buf8[(op_4) + 3] = in_buf[encoder++];
op++;
}
}
}
}
return buf;
},
lz_rgb32_decompress: function(in_buf, at, out_buf, type, default_alpha, palette, opaque) {
//TODO: global alpha and uncouple code
var encoder = at;
var op = 0;
var ctrl;
var out_buf_len = out_buf.length/4;
var is_rgba = type === this.LZ_IMAGE_TYPE_RGBA?true:false;
for (ctrl = in_buf[encoder++]; op < out_buf_len; ctrl = in_buf[encoder++])
{
var ref = op;
var len = ctrl >> 5;
var ofs = ((ctrl & 31) << 8);
var op_4 = op*4;
if (ctrl >= 32) {
var code;
len--;
if (len === 7 - 1) {
do {
code = in_buf[encoder++];
len += code;
} while (code === 255);
}
code = in_buf[encoder++];
ofs += code;
if (code === 255) {
if ((ofs - code) === (31 << 8)) {
ofs = in_buf[encoder++] << 8;
ofs += in_buf[encoder++];
ofs += 8191;
}
}
len += 1;
if (is_rgba || palette)
len += 2;
ofs += 1;
//CAST_PLT_DISTANCE ofs and len
if (type === this.LZ_IMAGE_TYPE_PLT4_LE || type === this.LZ_IMAGE_TYPE_PLT4_BE) {
ofs = ofs*2;
len = len*2;
} else if (type === this.LZ_IMAGE_TYPE_PLT1_BE || type === this.LZ_IMAGE_TYPE_PLT1_LE) {
ofs = ofs*8;
len = len*8;
}
ref -= ofs;
if (ref === (op - 1)) {//plt4/1 what?
var b = ref;
for (; len; --len) {
op_4 = op*4;
//COPY_PIXEL
if (is_rgba)
{
if(opaque) {
out_buf[(op_4) + 3] = 255;
} else {
out_buf[(op_4) + 3] = out_buf[(b*4)+3];
}
}
else
{
for (var i = 0; i < 4; i++)
out_buf[(op_4) + i] = out_buf[(b*4)+i];
}
op++;
}
} else {
for (; len; --len) {
//COPY_REF_PIXEL
op_4 = op*4;
if (is_rgba)
{
if(opaque) {
out_buf[(op_4) + 3] = 255;
} else {
out_buf[(op_4) + 3] = out_buf[(ref*4)+3];
}
}
else
{
for (i = 0; i < 4; i++)
out_buf[(op_4) + i] = out_buf[(ref*4)+i];
}
op++;ref++;
}
}
} else {
//COPY_COMP_PIXEL
ctrl++;
if (is_rgba) {
if(opaque) {
out_buf[(op_4) + 3] = 255;encoder++;
} else {
out_buf[(op_4) + 3] = in_buf[encoder++];
}
} else if (palette) {
if (type === this.LZ_IMAGE_TYPE_PLT1_LE) {
var oct = in_buf[encoder++];
var foreColor = palette[1];
var backColor = palette[0];
for (var i = 0; i < 8; i++) {
if (oct & this.PLT1_MASK[i]) {
this.copy_pixel(op_4, foreColor, out_buf);
} else {
this.copy_pixel(op_4, backColor, out_buf);
}
if (default_alpha)
out_buf[(op_4) + 3] = 255;
if (i < 7)
op++;op_4 = op*4;
}
} else if (type === this.LZ_IMAGE_TYPE_PLT1_BE) {
var oct = in_buf[encoder++];
var foreColor = palette[1];
var backColor = palette[0];
for (var i = 7; i >= 0; i--) {
if (oct & this.PLT1_MASK[i]) {
this.copy_pixel(op_4, foreColor, out_buf);
} else {
this.copy_pixel(op_4, backColor, out_buf);
}
if (default_alpha)
out_buf[(op_4) + 3] = 255;
if (i > 0)
op++;op_4 = op*4;
}
} else if (type === this.LZ_IMAGE_TYPE_PLT4_LE) {
var oct = in_buf[encoder++];
var spiceColor = palette[(oct & 0x0f)];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
op++;
op_4 = op*4;
var spiceColor = palette[((oct >>> 4) & 0x0f)];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
} else if (type === this.LZ_IMAGE_TYPE_PLT4_BE) {
var oct = in_buf[encoder++];
var bits1 = ((oct >>> 4) & 0x0f);
var bits2 = oct & 0x0f;
var spiceColor = palette[bits1];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
op++;
op_4 = op*4;
var spiceColor = palette[bits2];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
} else if (type === this.LZ_IMAGE_TYPE_PLT8) {
var posPal = in_buf[encoder++];
var spiceColor = palette[posPal];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
}
} else {
out_buf[(op_4) + 0] = in_buf[encoder + 2];
out_buf[(op_4) + 1] = in_buf[encoder + 1];
out_buf[(op_4) + 2] = in_buf[encoder + 0];
if (default_alpha)
out_buf[(op_4) + 3] = 255;
encoder += 3;
}
op++;
for (--ctrl; ctrl; ctrl--) {
//COPY_COMP_PIXEL
op_4 = op*4; // faster?
if (is_rgba) {
if(opaque) {
out_buf[(op_4) + 3] = 255;
} else {
out_buf[(op_4) + 3] = in_buf[encoder++];
}
} else if (palette) {
if (type === this.LZ_IMAGE_TYPE_PLT1_LE) {
var oct = in_buf[encoder++];
var foreColor = palette[1];
var backColor = palette[0];
for (var i = 0; i < 8; i++) {
if (oct & this.PLT1_MASK[i]) {
this.copy_pixel(op_4, foreColor, out_buf);
} else {
this.copy_pixel(op_4, backColor, out_buf);
}
if (default_alpha)
out_buf[(op_4) + 3] = 255;
if (i < 7)
op++;op_4 = op*4;
}
} else if (type === this.LZ_IMAGE_TYPE_PLT1_BE) {
var oct = in_buf[encoder++];
var foreColor = palette[1];
var backColor = palette[0];
for (var i = 7; i >=0; i--) {
if (oct & this.PLT1_MASK[i]) {
this.copy_pixel(op_4, foreColor, out_buf);
} else {
this.copy_pixel(op_4, backColor, out_buf);
}
if (default_alpha)
out_buf[(op_4) + 3] = 255;
if (i > 0)
op++;op_4 = op*4;
}
} else if (type === this.LZ_IMAGE_TYPE_PLT4_LE) {
var oct = in_buf[encoder++];
var spiceColor = palette[(oct & 0x0f)];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
op++;
op_4 = op*4;
var spiceColor = palette[((oct >>> 4) & 0x0f)];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
} else if (type === this.LZ_IMAGE_TYPE_PLT4_BE) {
var oct = in_buf[encoder++];
var spiceColor = palette[((oct >>> 4) & 0x0f)];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
op++;
op_4 = op*4;
var spiceColor = palette[(oct & 0x0f)];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
} else if (type === this.LZ_IMAGE_TYPE_PLT8) {
var posPal = in_buf[encoder++];
var spiceColor = palette[posPal];
this.copy_pixel(op_4, spiceColor, out_buf);
if (default_alpha)
out_buf[(op_4) + 3] = 255;
}
} else {
out_buf[(op_4) + 0] = in_buf[encoder + 2];
out_buf[(op_4) + 1] = in_buf[encoder + 1];
out_buf[(op_4) + 2] = in_buf[encoder + 0];
if (default_alpha)
out_buf[(op_4) + 3] = 255;
encoder += 3;
}
op++;
}
}
}
return encoder - 1;
},
convert_spice_lz_to_web: function(context, data, imageDescriptor, opaque) { //TODO: refactor this shit code
// var aux = data.toJSArray();
var format = imageDescriptor.type;
data = data.toJSArray(); //this old functions has no support for typed arrays...
if (format === wdi.SpiceImageType.SPICE_IMAGE_TYPE_LZ_PLT) {
var flags = wdi.SpiceObject.bytesToInt8(data.splice(0, 1));
if (flags === this.LZPALETTE_FLAG_PAL_FROM_CACHE) {
var header = data.splice(0, 12);
var length = wdi.SpiceObject.bytesToInt32(header.splice(0, 4));
var palette_id = wdi.SpiceObject.bytesToInt64(header.splice(0, 8));
header = data;
var magic = wdi.SpiceObject.bytesToStringBE(header.splice(0, 4));
var version = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var type = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var width = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var height = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var stride = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var top_down = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
} else if (flags === this.LZPALETTE_FLAG_PAL_CACHE_ME) {
var imageHeaders = imageDescriptor.offset + 1; //+1 because of the Flags byte
var currentHeaders = 36;
var header = data.splice(0, currentHeaders);
var length = wdi.SpiceObject.bytesToInt32(header.splice(0, 4));
var palette_offset = wdi.SpiceObject.bytesToInt32(header.splice(0, 4));
var spliceInit = palette_offset-imageHeaders-currentHeaders;
//LZ Compression headers with its magic
var magic = wdi.SpiceObject.bytesToStringBE(header.splice(0, 4));
var version = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var type = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var width = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var height = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var stride = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var top_down = wdi.SpiceObject.bytesToInt32BE(header.splice(0,4));
var palette_id = wdi.SpiceObject.bytesToInt64(data.splice(spliceInit, 8));
var num_palettes = wdi.SpiceObject.bytesToInt16(data.splice(spliceInit, 2));
var palette = [];
for (var i = 0; i < num_palettes; i++) {
var queue = new wdi.Queue();
queue.setData(data.splice(spliceInit, 4));
palette.push(new wdi.SpiceColor().demarshall(queue));
}
wdi.ImageCache.addPalette(palette_id, palette);
} else {
wdi.Debug.error('Unimplemented lz palette top down');
}
var palette = wdi.ImageCache.getPalette(palette_id);
}
if (type !== this.LZ_IMAGE_TYPE_RGB32 && type !== this.LZ_IMAGE_TYPE_RGBA &&
type !== this.LZ_IMAGE_TYPE_RGB24 && type !== this.LZ_IMAGE_TYPE_PLT8 &&
type !== this.LZ_IMAGE_TYPE_PLT1_LE && type !== this.LZ_IMAGE_TYPE_PLT1_BE &&
type !== this.LZ_IMAGE_TYPE_PLT4_LE && type !== this.LZ_IMAGE_TYPE_PLT4_BE) {
return false;
}
if (palette) {
var ret = context.createImageData(stride*this.PLT_PIXELS_PER_BYTE[type], height);
// if (type === this.LZ_IMAGE_TYPE_PLT1_BE) {
// this.lz_rgb32_plt1_be_decompress(data, ret.data, palette);
// } else {
this.lz_rgb32_decompress(data, 0, ret.data, type, type !== this.LZ_IMAGE_TYPE_RGBA, palette);
// }
var tmpCanvas = wdi.graphics.getNewTmpCanvas(width, height);
tmpCanvas.getContext('2d').putImageData(ret, 0, 0, 0, 0, width, height);
ret = tmpCanvas;
} else {
var arr = new ArrayBuffer(data.length+8);
var u8 = new Uint8Array(arr);
u8[0] = 1;
u8[1] = opaque;
u8[2] = type;
u8[3] = 0;
var number = ret.data.length;
for (var i = 0;i < 4;i++) {//iterations because of javascript number size
u8[4+i] = number & (255);//Get only the last byte
number = number >> 8;//Remove the last byte
}
u8.set(data, 8);
var result = new Uint8ClampedArray(this.lz_rgb32_decompress_rgb(arr));
ret = new ImageData(result, width, height);
ret = wdi.graphics.getImageFromData(ret);
}
if(!top_down) {
//TODO: PERFORMANCE:
ret = wdi.RasterOperation.flip(ret);
}
return ret;
},
demarshall_rgb: function(data) {
var header = data.splice(0, 32);
return {
length: wdi.SpiceObject.bytesToInt32(header.splice(0,4)),
magic: wdi.SpiceObject.bytesToStringBE(header.splice(0,4)),
version: wdi.SpiceObject.bytesToInt32BE(header.splice(0,4)),
type: wdi.SpiceObject.bytesToInt32BE(header.splice(0,4)),
width: wdi.SpiceObject.bytesToInt32BE(header.splice(0,4)),
height: wdi.SpiceObject.bytesToInt32BE(header.splice(0,4)),
stride: wdi.SpiceObject.bytesToInt32BE(header.splice(0,4)),
top_down: wdi.SpiceObject.bytesToInt32BE(header.splice(0,4))
}
},
lz_rgb32_plt1_be_decompress: function(in_buf, out_buf, palette) {
var encoder = 0;
var op = 0;
var ctrl;
var out_buf_len = out_buf.length/4;
var ref, len, ofs, next, ref_4, oct, foreColor, backColor, i;
var type = this.LZ_IMAGE_TYPE_PLT1_BE;
var pix_per_byte = this.PLT_PIXELS_PER_BYTE[type];
var pre_255_24 = 255 << 24;
var pre_31_8_plus255 = (31 << 8) + 255; //8191 === 13 bits to 1
for (ctrl = in_buf[encoder++]; op < out_buf_len; ctrl = in_buf[encoder++]) {
ref = op;
len = ctrl >> 5;
ofs = ((ctrl & 31) << 8);
if (ctrl > 31) {
if (len === 7) {
do {
next = in_buf[encoder++];
len += next;
} while (next === 255);
}
ofs += in_buf[encoder++];
if (ofs === pre_31_8_plus255) {
ofs += in_buf[encoder++] << 8 + in_buf[encoder++];
}
//CAST_PLT_DISTANCE ofs and len
len = (len + 2) * pix_per_byte;
ref -= (ofs + 1) * pix_per_byte;
if (ref === (op - 1)) {
ref_4 = ref * 4;
while (len-- !== 0) {
//COPY_PIXEL
op_4 = op * 4;
out_buf[op_4] = out_buf[ref_4];
out_buf[op_4 + 1] = out_buf[ref_4 + 1];
out_buf[op_4 + 2] = out_buf[ref_4 + 2];
out_buf[op_4 + 3] = out_buf[ref_4 + 3];
op++;
}
} else {
while (len-- !== 0) {
//COPY_REF_PIXEL
op_4 = op * 4;
ref_4 = ref * 4;
out_buf[op_4] = out_buf[ref_4];
out_buf[op_4 + 1] = out_buf[ref_4 + 1];
out_buf[op_4 + 2] = out_buf[ref_4 + 2];
out_buf[op_4 + 3] = out_buf[ref_4 + 3];
op++;ref++;
}
}
} else {
//COPY_COMP_PIXEL
while (ctrl-- !== -1) {
//COPY_COMP_PIXEL
op_4 = op * 4; // faster?
oct = in_buf[encoder++];
foreColor = palette[1];
backColor = palette[0];
for (i = 7; i >=0; i--) {
op_4 = op * 4;
if (oct & this.PLT1_MASK[i]) {
out_buf[op_4 + 0] = foreColor.r;
out_buf[op_4 + 1] = foreColor.g;
out_buf[op_4 + 2] = foreColor.b;
} else {
out_buf[op_4 + 0] = backColor.r;
out_buf[op_4 + 1] = backColor.g;
out_buf[op_4 + 2] = backColor.b;
}
out_buf[(op_4) + 3] = 255;
op++;
}
}
}
}
}
};