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:
committed by
Peli de Halleux
parent
95ab3be26e
commit
8ed79e7133
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -14,7 +14,6 @@
|
||||
"timer.ts",
|
||||
"serialnumber.cpp",
|
||||
"buttons.ts",
|
||||
"png.cpp",
|
||||
"screen.cpp",
|
||||
"battery.ts",
|
||||
"output.cpp",
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
6
libs/core/shims.d.ts
vendored
6
libs/core/shims.d.ts
vendored
@ -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 {
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
})
|
||||
*/
|
||||
|
Reference in New Issue
Block a user