Swap X/y in images (#528)

* Swap X/Y in image encoding

* Fix PNG decoder

* Now really fix png

* reducing deps

* moving png.cpp into ev3

* bumped pxt

* updated shims

* fixing c++ compilation

* updated shims

* Fix pixel order

* update pxt

* Fix C++ image decoding

* Add expanded PNG images

* Generate E1 format images (in libs/ev3 run 'pxt buildsprites images')

* Go back to white background

* Remove PNG support
This commit is contained in:
Michał Moskal
2018-04-19 12:08:16 -07:00
committed by Peli de Halleux
parent 95ab3be26e
commit 8ed79e7133
130 changed files with 363 additions and 945 deletions

View File

@ -45,64 +45,4 @@ namespace console {
if (!listener) return;
listeners.push(listener);
}
/**
* Sends the log messages to the brick screen and uses the brick up and down buttons to scroll.
*/
//% blockId=logsendtostreen block="send console to screen"
//% weight=1
//% help=console/send-to-screen
export function sendToScreen(): void {
console._screen.attach();
}
}
namespace console._screen {
const maxLines = 100;
let lines: string[];
let scrollPosition = 0;
export function attach() {
if (!lines) {
lines = [];
console.addListener(log);
brick.buttonUp.onEvent(ButtonEvent.Bumped, () => scroll(-3))
brick.buttonDown.onEvent(ButtonEvent.Bumped, () => scroll(3))
}
}
function printLog() {
brick.clearScreen()
if (!lines) return;
const screenLines = brick.lineCount();
for (let i = 0; i < screenLines; ++i) {
const line = lines[i + scrollPosition];
if (line)
screen.print(line, 0, 4 + i * brick.lineHeight(), 1, brick.font)
}
}
function scroll(pos: number) {
if (!pos) return;
scrollPosition += pos >> 0;
if (scrollPosition >= lines.length) scrollPosition = lines.length - 1;
if (scrollPosition < 0) scrollPosition = 0;
printLog();
}
function log(msg: string): void {
lines.push(msg);
if (lines.length + 5 > maxLines) {
lines.splice(0, maxLines - lines.length);
scrollPosition = Math.min(scrollPosition, lines.length - 1)
}
// move down scroll once it gets large than the screen
const screenLines = brick.lineCount();
if (lines.length > screenLines
&& lines.length >= scrollPosition + screenLines) {
scrollPosition++;
}
printLog();
}
}

View File

@ -1,122 +0,0 @@
#include "pxt.h"
#include "ev3const.h"
#include <zlib.h>
#include <stdlib.h>
struct PNGHeader {
uint8_t pngHeader[8];
uint32_t lenIHDR;
uint8_t IHDR[4];
uint32_t width;
uint32_t height;
uint8_t bitDepth;
uint8_t colorType;
uint8_t compressionMethod;
uint8_t filterMethod;
uint8_t interlaceMethod;
uint32_t hdCRC;
uint32_t lenIDAT;
uint8_t IDAT[4];
} __attribute__((packed));
namespace image {
static uint32_t swap(uint32_t num) {
return ((num >> 24) & 0xff) | ((num << 8) & 0xff0000) | ((num >> 8) & 0xff00) |
((num << 24) & 0xff000000);
}
/** Decompresses a 1-bit gray scale PNG image to image format. */
//%
Image_ unpackPNG(Buffer png) {
if (!png) {
DMESG("PNG: Missing image");
return NULL;
}
if (png->length < sizeof(PNGHeader) + 4) {
DMESG("PNG: File too small");
return NULL;
}
if (memcmp(png->data, "\x89PNG\r\n\x1A\n", 8) != 0) {
DMESG("PNG: Invalid header");
return NULL;
}
struct PNGHeader hd;
memcpy(&hd, png->data, sizeof(hd));
if (memcmp(hd.IHDR, "IHDR", 4) != 0) {
DMESG("PNG: missing IHDR");
return NULL;
}
hd.lenIHDR = swap(hd.lenIHDR);
hd.width = swap(hd.width);
hd.height = swap(hd.height);
hd.lenIDAT = swap(hd.lenIDAT);
if (hd.lenIHDR != 13) {
DMESG("PNG: bad IHDR len");
return NULL;
}
if (hd.bitDepth != 1 || hd.colorType != 0 || hd.compressionMethod != 0 ||
hd.filterMethod != 0 || hd.interlaceMethod != 0) {
DMESG("PNG: not 1-bit grayscale");
return NULL;
}
if (memcmp(hd.IDAT, "IDAT", 4) != 0) {
DMESG("PNG: missing IDAT");
return NULL;
}
if (hd.lenIDAT + sizeof(hd) >= png->length) {
DMESG("PNG: buffer too short");
return NULL;
}
if (hd.width > 300 || hd.height > 300) {
DMESG("PNG: too big");
return NULL;
}
uint32_t byteW = (hd.width + 7) >> 3;
uint32_t expSize = (byteW + 1) * hd.height;
unsigned long sz = expSize;
uint8_t *tmp = (uint8_t *)xmalloc(sz);
int code = uncompress(tmp, &sz, png->data + sizeof(hd), hd.lenIDAT);
if (code != 0) {
DMESG("PNG: zlib failed: %d", code);
free(tmp);
return NULL;
}
if (sz != expSize) {
DMESG("PNG: invalid compressed size");
free(tmp);
return NULL;
}
auto res = mkImage(hd.width, hd.height, 1);
uint8_t *dst = res->pix();
uint8_t *src = tmp;
uint8_t lastMask = 0xff << (8 - (hd.width & 7));
if (lastMask == 0)
lastMask = 0xff;
for (uint32_t i = 0; i < hd.height; ++i) {
if (*src++ != 0) {
DMESG("PNG: unsupported filter");
free(tmp);
decrRC(res);
return NULL;
}
for (uint32_t j = 0; j < byteW; ++j) {
*dst = ~*src++;
if (j == byteW - 1) {
*dst &= lastMask;
}
dst++;
}
}
free(tmp);
return res;
}
}

View File

@ -14,7 +14,6 @@
"timer.ts",
"serialnumber.cpp",
"buttons.ts",
"png.cpp",
"screen.cpp",
"battery.ts",
"output.cpp",

View File

@ -16,6 +16,36 @@ namespace pxt {
static const uint8_t pixmap[] = {0x00, 0x03, 0x1C, 0x1F, 0xE0, 0xE3, 0xFC, 0xFF};
static void bitBufferToFrameBufferSwap(uint8_t *bitBuffer, uint8_t *fb) {
int mask = 0x01;
uint8_t *currLine = bitBuffer;
for (int line = 0; line < LCD_HEIGHT; line++) {
uint8_t *ptr = currLine;
int n = 60;
while (n--) {
uint8_t v = 0;
if (*ptr & mask)
v |= 0xE0;
ptr += LCD_HEIGHT / 8;
if (*ptr & mask)
v |= 0x1C;
ptr += LCD_HEIGHT / 8;
if (*ptr & mask)
v |= 0x03;
ptr += LCD_HEIGHT / 8;
*fb++ = v;
}
mask <<= 1;
if (mask == 0x100) {
mask = 0x01;
currLine++;
}
}
}
static void bitBufferToFrameBuffer(uint8_t *bitBuffer, uint8_t *fb) {
uint32_t pixels;
@ -59,7 +89,7 @@ void updateScreen(Image_ img) {
if (lastImg->bpp() != 1 || lastImg->width() != LCD_WIDTH || lastImg->height() != LCD_HEIGHT)
target_panic(906);
lastImg->clearDirty();
bitBufferToFrameBuffer(lastImg->pix(), mappedFrameBuffer);
bitBufferToFrameBufferSwap(lastImg->pix(), mappedFrameBuffer);
}
}

View File

@ -75,12 +75,6 @@ declare namespace serial {
//% shim=serial::writeDmesg
function writeDmesg(): void;
}
declare namespace image {
/** Decompresses a 1-bit gray scale PNG image to image format. */
//% shim=image::unpackPNG
function unpackPNG(png: Buffer): Image;
}
declare namespace output {
/**

View File

@ -1,45 +1 @@
screen.clear()
brick.print("PXT!", 10, 30, Draw.Quad)
brick.drawRect(40, 40, 20, 10, Draw.Fill)
brick.setStatusLight(StatusLight.Orange)
brick.heart.doubled().draw(100, 50, Draw.Double | Draw.Transparent)
brick.buttonEnter.onEvent(ButtonEvent.Bumped, () => {
screen.clear()
})
brick.buttonLeft.onEvent(ButtonEvent.Bumped, () => {
brick.drawRect(10, 70, 20, 10, Draw.Fill)
brick.setStatusLight(StatusLight.Red)
brick.setFont(brick.microbitFont())
})
brick.buttonRight.onEvent(ButtonEvent.Bumped, () => {
brick.print("Right!", 10, 60)
})
brick.buttonDown.onEvent(ButtonEvent.Bumped, () => {
brick.print("Down! ", 10, 60)
})
brick.buttonUp.onEvent(ButtonEvent.Bumped, () => {
brick.print("Up! ", 10, 60)
})
let num = 0
forever(() => {
serial.writeDmesg()
pause(100)
})
/*
forever(() => {
let v = input.color.getColor()
screen.print(10, 60, v + " ")
pause(200)
})
*/