2018-02-27 00:16:17 +01:00
|
|
|
namespace pxsim.image {
|
2017-10-30 15:42:08 +01:00
|
|
|
function DMESG(msg: string) {
|
|
|
|
control.dmesg(msg)
|
|
|
|
}
|
|
|
|
const NULL: RefBuffer = null;
|
|
|
|
|
|
|
|
export function unpackPNG(png: RefBuffer) {
|
|
|
|
function memcmp(off: number, mark: string) {
|
|
|
|
for (let i = 0; i < mark.length; ++i) {
|
|
|
|
if (mark.charCodeAt(i) != png.data[off + i])
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
function readInt(off: number) {
|
|
|
|
return ((png.data[off] << 24) | (png.data[off + 1] << 16) |
|
|
|
|
(png.data[off + 2] << 8) | (png.data[off + 3])) >>> 0
|
|
|
|
}
|
|
|
|
if (!png) {
|
|
|
|
DMESG("PNG: Missing image");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (png.data.length < 45) {
|
|
|
|
DMESG("PNG: File too small");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (memcmp(0, "\x89PNG\r\n\x1A\n") != 0) {
|
|
|
|
DMESG("PNG: Invalid header");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (memcmp(12, "IHDR") != 0) {
|
|
|
|
DMESG("PNG: missing IHDR");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
const lenIHDR = readInt(8);
|
|
|
|
const width = readInt(16);
|
|
|
|
const height = readInt(20);
|
|
|
|
const lenIDAT = readInt(33);
|
|
|
|
const sizeOfHD = 41;
|
|
|
|
|
|
|
|
if (lenIHDR != 13) {
|
|
|
|
DMESG("PNG: bad IHDR len");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (memcmp(24, "\x01\x00\x00\x00\x00") != 0) {
|
|
|
|
DMESG("PNG: not 1-bit grayscale");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (memcmp(37, "IDAT") != 0) {
|
|
|
|
DMESG("PNG: missing IDAT");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (lenIDAT + sizeOfHD >= png.data.length) {
|
|
|
|
DMESG("PNG: buffer too short");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (width > 300 || height > 300) {
|
|
|
|
DMESG("PNG: too big");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
const byteW = (width + 7) >> 3;
|
|
|
|
const sz = (byteW + 1) * height;
|
|
|
|
const tmp = new Uint8Array(sz + 1);
|
|
|
|
// uncompress doesn't take the zlib header, hence + 2
|
|
|
|
const two = tinf.uncompress(png.data.slice(sizeOfHD + 2, sizeOfHD + lenIDAT), tmp);
|
|
|
|
if (two.length != sz) {
|
|
|
|
DMESG("PNG: invalid compressed size");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-02-27 00:16:17 +01:00
|
|
|
const res = output.createBuffer(3 + byteW * height);
|
|
|
|
res.data[0] = 0xf1;
|
2017-10-30 15:42:08 +01:00
|
|
|
res.data[1] = width;
|
2018-02-27 00:16:17 +01:00
|
|
|
res.data[2] = height;
|
|
|
|
let dst = 3
|
2017-10-30 15:42:08 +01:00
|
|
|
let src = 0
|
|
|
|
let lastMask = (1 << (width & 7)) - 1;
|
|
|
|
if (lastMask == 0)
|
|
|
|
lastMask = 0xff;
|
|
|
|
for (let i = 0; i < height; ++i) {
|
|
|
|
if (two[src++] != 0) {
|
|
|
|
DMESG("PNG: unsupported filter");
|
|
|
|
decr(res);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
for (let j = 0; j < byteW; ++j) {
|
2018-02-27 00:16:17 +01:00
|
|
|
res.data[dst] = ~(two[src++]);
|
2017-10-30 15:42:08 +01:00
|
|
|
if (j == byteW - 1) {
|
|
|
|
res.data[dst] &= lastMask;
|
|
|
|
}
|
|
|
|
dst++;
|
|
|
|
}
|
|
|
|
}
|
2018-02-27 00:16:17 +01:00
|
|
|
return image.ofBuffer(res)
|
2017-07-13 22:01:39 +02:00
|
|
|
}
|
2017-10-30 15:42:08 +01:00
|
|
|
}
|
2018-02-27 06:25:04 +01:00
|
|
|
|
|
|
|
namespace pxsim.pxtcore {
|
|
|
|
export function updateStats(str: string) {
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
}
|