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