From 1f51d137b39e153d3690b835248673bf2c2ff5a5 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Mon, 10 Jul 2017 15:11:21 +0100 Subject: [PATCH 1/7] Cleanup --- libs/core/buttons.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/libs/core/buttons.ts b/libs/core/buttons.ts index 8f16f02e..286c87da 100644 --- a/libs/core/buttons.ts +++ b/libs/core/buttons.ts @@ -3,16 +3,16 @@ * Patterns for lights under the buttons. */ const enum LightsPattern { - Off = 0, // LED_BLACK - Green = 1, // LED_GREEN - Red = 2, // LED_RED - Orange = 3, // LED_ORANGE - GreenFlash = 4, // LED_GREEN_FLASH - RedFlash = 5, // LED_RED_FLASH - OrangeFlash = 6, // LED_ORANGE_FLASH - GreenPulse = 7, // LED_GREEN_PULSE - RedPulse = 8, // LED_RED_PULSE - OrangePulse = 9, // LED_ORANGE_PULSE + Off = 0, + Green = 1, + Red = 2, + Orange = 3, + GreenFlash = 4, + RedFlash = 5, + OrangeFlash = 6, + GreenPulse = 7, + RedPulse = 8, + OrangePulse = 9, } /** From 17a01b46be2a8765a6e75de58050899d859eafe2 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Mon, 10 Jul 2017 21:37:29 +0100 Subject: [PATCH 2/7] Working on own LCD impl --- TODO.md | 2 +- libs/core/screen.cpp | 156 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 151 insertions(+), 7 deletions(-) diff --git a/TODO.md b/TODO.md index cfc211b2..33b1c642 100644 --- a/TODO.md +++ b/TODO.md @@ -2,7 +2,7 @@ * [x] implement serialPoll * [x] try some motors * [x] add `control.interrupt(ms, () => { ... sync function ... })` - running outside regular JS thread -* [ ] add `//% whenUsed` on global variable initializers in PXT +* [x] add `//% whenUsed` on global variable initializers in PXT * [ ] fix `@PXT@:` handling for ELF in PXT * [x] parse Python field lists into offsets diff --git a/libs/core/screen.cpp b/libs/core/screen.cpp index 2e112027..bd664609 100644 --- a/libs/core/screen.cpp +++ b/libs/core/screen.cpp @@ -25,20 +25,165 @@ enum class ScreenFont { // We only support up to 4 arguments for C++ functions - need to pack them on the TS side namespace screen { +#define ROW_SIZE 32 +#define FB_SIZE (60 * LCD_HEIGHT) +static const uint8_t pixmap[] = {0x00, 0xE0, 0x1C, 0xFC, 0x03, 0xE3, 0x1F, 0xFF}; +static uint8_t bitBuffer[ROW_SIZE * LCD_HEIGHT]; +static uint8_t *mappedFrameBuffer; +static bool dirty; + +static void bitBufferToFrameBuffer(uint8_t *bitBuffer, uint8_t *fb) { + uint32_t pixels; + + for (int line = 0; line < LCD_HEIGHT; line++) { + int n = 7; + while (n--) { + pixels = *bitBuffer++ << 0; + pixels |= *bitBuffer++ << 8; + pixels |= *bitBuffer++ << 16; + + int m = 8; + while (m--) { + *fb++ = pixmap[pixels & 0x07]; + pixels >>= 3; + } + } + + pixels = *bitBuffer++ << 0; + pixels |= *bitBuffer++ << 8; + + bitBuffer += ROW_SIZE - 26; + + int m = 4; + while (m--) { + *fb++ = pixmap[pixels & 0x07]; + pixels >>= 3; + } + } +} + +static void updateLCD() { + bitBufferToFrameBuffer(bitBuffer, mappedFrameBuffer); +} + +#define OFF(x, y) (((y) << 5) + ((x) >> 3)) +#define MASK(x, y) (1 << ((x)&7)) + +static inline void applyMask(int off, int mask, Draw mode) { + if (mode & Draw::Clear) + bitBuffer[off] &= ~mask; + else if (mode & Draw::Xor) + bitBuffer[off] ^= mask; + else + bitBuffer[off] |= mask; +} + +static inline void setPixelCore(int x, int y, Draw mode) { + applyMask(OFF(x, y), MASK(x, y), mode); +} + +static void setLineCore(int x0, int x1, int y, Draw mode) { + while (x0 < x1 && (x0 & 7)) { + setPixelCore(x0++, y, mode); + } + int off = OFF(x0, y); + // 8-aligned now + while (x0 < x1 - 8) { + applyMask(off++, 0xff, mode); + x0 += 8; + } + while (x0 < x1) { + setPixelCore(x0++, y, mode); + } + dirty = true; +} + +void drawRect(int x, int y, int w, int h, Draw mode) { + if (x < 0) { + w += x; + x = 0; + } + if (y < 0) { + h += y; + y = 0; + } + if (w <= 0) + return; + if (h <= 0) + return; + int x1 = min(LCD_WIDTH, x + w); + int y1 = min(LCD_HEIGHT, y + h); + if (w == 1) { + while (y < y1) + setPixelCore(x, y++, mode); + return; + } + + setLineCore(x, x1, y++, mode); + while (y < y1 - 1) { + if (mode & Draw::Fill) { + setLineCore(x, x1, y, mode); + } else { + setPixelCore(x, y, mode); + setPixelCore(x1 - 1, y, mode); + } + y++; + } + if (y < y1) + setLineCore(x, x1, y, mode); +} + +void blitLine(int x, int y, int w, uint8_t *data, Draw mode) { + int shift = x & 7; + int off = OFF(x, y); + int x1 = x + w; + int prev = 0; + + while (x < x1 - 8) { + int curr = *data++ << shift; + applyMask(off++, curr | prev); + prev = curr >> 8; + x += 8; + } + + int left = x1 - x; + if (left > 0) { + int curr = *data << shift; + applyMask(off, (curr | prev) & ((1 << left) - 1)); + } +} + +/** Update a pixel on the screen. */ //% -void _drawLine(uint32_t p0, uint32_t p1, Draw mode) { - LineOutEx(XX(p0), YY(p0), XX(p1), YY(p1), (uint32_t)mode); +void setPixel(int x, int y, Draw mode) { + if (0 <= x && x < LCD_WIDTH && 0 <= y && y <= LCD_HEIGHT) { + setPixelCore(x, y, mode); + dirty = true; + } } //% void _drawRect(uint32_t p0, uint32_t p1, Draw mode) { - RectOutEx(XX(p0), YY(p0), XX(p1), YY(p1), (uint32_t)mode); + drawRect(XX(p0), YY(p0), XX(p1), YY(p1), mode); } +static int fontFirst, fontWidth, fontHeight, fontByteWidth; +static uint8_t *fontChars; +static Buffer currFont; + +/** Set font. */ //% -void _drawEllipse(uint32_t p0, uint32_t p1, Draw mode) { - EllipseOutEx(XX(p0), YY(p0), XX(p1), YY(p1), (uint32_t)mode); +void setFont(Buffer font) { + if (!font || font->length < 100) + return; + if (font->data[0] != 0xf0 || font->data[1] != 0x42) + return; + fontFirst = font->data[2]; + fontWidth = font->data[3]; + fontHeight = font->data[4]; + fontByteWidth = (fontWidth + 7) / 8; + fontChars = &font->data[8]; } /** Draw text. */ @@ -83,5 +228,4 @@ void screen_init() { pthread_create(&pid, NULL, screenRefresh, NULL); pthread_detach(pid); } - } From ce758a091ea48bd106ba1bea0686b603f3c92f7b Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Tue, 11 Jul 2017 10:35:00 +0200 Subject: [PATCH 3/7] Moving drawing code into TS --- libs/core/screen.cpp | 128 ++++++++++++------------------------------- libs/core/screen.ts | 127 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 147 insertions(+), 108 deletions(-) diff --git a/libs/core/screen.cpp b/libs/core/screen.cpp index bd664609..c2a26e4a 100644 --- a/libs/core/screen.cpp +++ b/libs/core/screen.cpp @@ -79,70 +79,31 @@ static inline void applyMask(int off, int mask, Draw mode) { bitBuffer[off] |= mask; } -static inline void setPixelCore(int x, int y, Draw mode) { +//% +void _setPixel(int x, int y, Draw mode) { applyMask(OFF(x, y), MASK(x, y), mode); } -static void setLineCore(int x0, int x1, int y, Draw mode) { - while (x0 < x1 && (x0 & 7)) { - setPixelCore(x0++, y, mode); - } - int off = OFF(x0, y); - // 8-aligned now - while (x0 < x1 - 8) { - applyMask(off++, 0xff, mode); - x0 += 8; - } - while (x0 < x1) { - setPixelCore(x0++, y, mode); - } - dirty = true; -} - -void drawRect(int x, int y, int w, int h, Draw mode) { - if (x < 0) { - w += x; - x = 0; - } - if (y < 0) { - h += y; - y = 0; - } - if (w <= 0) +void blitLineCore(int x, int y, int w, uint8_t *data, Draw mode) { + if (y < 0 || y >= LMS.LCD_HEIGHT) return; - if (h <= 0) + if (x + w <= 0) return; - int x1 = min(LCD_WIDTH, x + w); - int y1 = min(LCD_HEIGHT, y + h); - if (w == 1) { - while (y < y1) - setPixelCore(x, y++, mode); + if (x >= LMS.LCD_WIDTH) return; - } - setLineCore(x, x1, y++, mode); - while (y < y1 - 1) { - if (mode & Draw::Fill) { - setLineCore(x, x1, y, mode); - } else { - setPixelCore(x, y, mode); - setPixelCore(x1 - 1, y, mode); - } - y++; - } - if (y < y1) - setLineCore(x, x1, y, mode); -} - -void blitLine(int x, int y, int w, uint8_t *data, Draw mode) { int shift = x & 7; int off = OFF(x, y); + int off0 = OFF(0, y); + int off1 = OFF(LMS.LCD_WIDTH - 1, y); int x1 = x + w; int prev = 0; while (x < x1 - 8) { int curr = *data++ << shift; - applyMask(off++, curr | prev); + if (off0 <= off && off <= off1) + applyMask(off, curr | prev); + off++; prev = curr >> 8; x += 8; } @@ -150,64 +111,45 @@ void blitLine(int x, int y, int w, uint8_t *data, Draw mode) { int left = x1 - x; if (left > 0) { int curr = *data << shift; - applyMask(off, (curr | prev) & ((1 << left) - 1)); - } -} - -/** Update a pixel on the screen. */ -//% -void setPixel(int x, int y, Draw mode) { - if (0 <= x && x < LCD_WIDTH && 0 <= y && y <= LCD_HEIGHT) { - setPixelCore(x, y, mode); - dirty = true; + if (off0 <= off && off <= off1) + applyMask(off, (curr | prev) & ((1 << left) - 1)); } } //% -void _drawRect(uint32_t p0, uint32_t p1, Draw mode) { - drawRect(XX(p0), YY(p0), XX(p1), YY(p1), mode); +void _blitLine(int xw, int y, Buffer buf, Draw mode) { + blitLineCore(XX(xw), y, YY(xw), buf->data, mode); } -static int fontFirst, fontWidth, fontHeight, fontByteWidth; -static uint8_t *fontChars; -static Buffer currFont; +static uint8_t ones[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; -/** Set font. */ +/** Draw an icon on the screen. */ //% -void setFont(Buffer font) { - if (!font || font->length < 100) +void drawIcon(int x, int y, Buffer buf, Draw mode) { + if (buf->length < 2) return; - if (font->data[0] != 0xf0 || font->data[1] != 0x42) + int pixwidth = buf->data[0]; + if (pixwidth > 100) return; - fontFirst = font->data[2]; - fontWidth = font->data[3]; - fontHeight = font->data[4]; - fontByteWidth = (fontWidth + 7) / 8; - fontChars = &font->data[8]; -} - -/** Draw text. */ -//% mode.defl=0 -void drawText(int x, int y, String text, Draw mode) { - LcdText((int)mode & (int)Draw::Clear ? 0 : 1, x, y, text->data); + int ptr = 1; + int bytewidth = (pixwidth + 7) >> 3; + while (ptr + bytewidth <= buf->length) { + if (mode == Draw::Normal) + blitLineCore(x, y, pixwidth, ones, Draw::Clear); + blitLineCore(x, y, pixwidth, &buf->data[ptr], mode); + y++; + ptr += bytewidth; + } } /** Clear screen and reset font to normal. */ //% void clear() { - LcdClearDisplay(); -} - -/** Scroll screen vertically. */ -//% -void scroll(int v) { - LcdScroll(v); -} - -/** Set font for drawText() */ -//% -void setFont(ScreenFont font) { - LcdSelectFont((uint8_t)font); + memset(bitBuffer, 0, sizeof(bitBuffer)); + dirty = true; } } diff --git a/libs/core/screen.ts b/libs/core/screen.ts index 11b89fca..0b6c4c7e 100644 --- a/libs/core/screen.ts +++ b/libs/core/screen.ts @@ -1,30 +1,127 @@ namespace screen { - //% shim=screen::_drawLine - function _drawLine(p0: uint32, p1: uint32, mode: Draw): void { } + //% shim=screen::setPixelCore + function setPixelCore(p0: uint32, p1: uint32, mode: Draw): void { } - //% shim=screen::_drawRect - function _drawRect(p0: uint32, p1: uint32, mode: Draw): void { } - - //% shim=screen::_drawEllipse - function _drawEllipse(p0: uint32, p1: uint32, mode: Draw): void { } + //% shim=screen::blitLine + function blitLine(xw: uint32, y: uint32, buf: Buffer, mode: Draw): void { } function pack(x: number, y: number) { return Math.clamp(0, 512, x) | (Math.clamp(0, 512, y) << 16) } - export function drawLine(x0: number, y0: number, x1: number, y1: number, mode?: Draw) { - _drawLine(pack(x0, y0), pack(x1, y1), mode) + const ones = hex`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff` + + function setLineCore(x: number, x1: number, y: number, mode: Draw) { + blitLine(pack(x, x1 - x), y, ones, mode) } - export function drawRect(x: number, y: number, w: number, h: number, mode?: Draw) { - _drawRect(pack(x, y), pack(w, h), mode) + export interface Font { + charWidth: number; + charHeight: number; + firstChar: number; + data: Buffer; + } + let currFont: Font + + export const heart = hex`07 367f7f3e1c08` + + export function defaultFont(): Font { + return { + charWidth: 8, + charHeight: 8, + firstChar: 32, + // source https://github.com/dhepper/font8x8 + data: hex` +0000000000000000 183C3C1818001800 3636000000000000 36367F367F363600 0C3E031E301F0C00 006333180C666300 +1C361C6E3B336E00 0606030000000000 180C0606060C1800 060C1818180C0600 00663CFF3C660000 000C0C3F0C0C0000 +00000000000C0C06 0000003F00000000 00000000000C0C00 6030180C06030100 3E63737B6F673E00 0C0E0C0C0C0C3F00 +1E33301C06333F00 1E33301C30331E00 383C36337F307800 3F031F3030331E00 1C06031F33331E00 3F3330180C0C0C00 +1E33331E33331E00 1E33333E30180E00 000C0C00000C0C00 000C0C00000C0C06 180C0603060C1800 00003F00003F0000 +060C1830180C0600 1E3330180C000C00 3E637B7B7B031E00 0C1E33333F333300 3F66663E66663F00 3C66030303663C00 +1F36666666361F00 7F46161E16467F00 7F46161E16060F00 3C66030373667C00 3333333F33333300 1E0C0C0C0C0C1E00 +7830303033331E00 6766361E36666700 0F06060646667F00 63777F7F6B636300 63676F7B73636300 1C36636363361C00 +3F66663E06060F00 1E3333333B1E3800 3F66663E36666700 1E33070E38331E00 3F2D0C0C0C0C1E00 3333333333333F00 +33333333331E0C00 6363636B7F776300 6363361C1C366300 3333331E0C0C1E00 7F6331184C667F00 1E06060606061E00 +03060C1830604000 1E18181818181E00 081C366300000000 00000000000000FF 0C0C180000000000 00001E303E336E00 +0706063E66663B00 00001E3303331E00 3830303e33336E00 00001E333f031E00 1C36060f06060F00 00006E33333E301F +0706366E66666700 0C000E0C0C0C1E00 300030303033331E 070666361E366700 0E0C0C0C0C0C1E00 0000337F7F6B6300 +00001F3333333300 00001E3333331E00 00003B66663E060F 00006E33333E3078 00003B6E66060F00 00003E031E301F00 +080C3E0C0C2C1800 0000333333336E00 00003333331E0C00 0000636B7F7F3600 000063361C366300 00003333333E301F +00003F190C263F00 380C0C070C0C3800 1818180018181800 070C0C380C0C0700 6E3B000000000000 0000000000000000 +` + } } - export function drawEllipse(x: number, y: number, rx: number, ry: number, mode?: Draw) { - _drawEllipse(pack(x, y), pack(rx, ry), mode) + export function setPixel(x: number, y: number, mode = Draw.Normal) { + x |= 0 + y |= 0 + if (0 <= x && x < LMS.LCD_WIDTH && 0 <= y && y < LMS.LCD_HEIGHT) + setPixelCore(x, y, mode) } - export function drawCircle(x: number, y: number, r: number, mode?: Draw) { - drawEllipse(x, y, r, r, mode) + export function drawText(x: number, y: number, text: string, mode = Draw.Normal) { + x |= 0 + y |= 0 + if (!currFont) currFont = defaultFont() + let x0 = x + let cp = 0 + let byteWidth = (currFont.charWidth + 7) >> 3 + let iconBuf = output.createBuffer(1 + byteWidth * currFont.charHeight) + iconBuf[0] = currFont.charWidth + while (cp < text.length) { + let ch = text.charCodeAt(cp++) + if (ch == 10) { + y += currFont.charHeight + 2 + x = x0 + } + if (ch < 32) continue + let idx = (ch - currFont.firstChar) * byteWidth + if (idx < 0 || idx + iconBuf.length - 1 > currFont.data.length) + iconBuf.fill(0, 1) + else + iconBuf.write(1, currFont.data.slice(idx, byteWidth)) + drawIcon(x, y, iconBuf, mode) + } } + + export function drawRect(x: number, y: number, w: number, h: number, mode = Draw.Normal) { + x |= 0; + y |= 0; + w |= 0; + h |= 0; + if (x < 0) { + w += x; + x = 0; + } + if (y < 0) { + h += y; + y = 0; + } + if (w <= 0) + return; + if (h <= 0) + return; + let x1 = Math.min(LMS.LCD_WIDTH, x + w); + let y1 = Math.min(LMS.LCD_HEIGHT, y + h); + if (w == 1) { + while (y < y1) + setPixelCore(x, y++, mode); + return; + } + + setLineCore(x, x1, y++, mode); + while (y < y1 - 1) { + if (mode & Draw.Fill) { + setLineCore(x, x1, y, mode); + } else { + setPixelCore(x, y, mode); + setPixelCore(x1 - 1, y, mode); + } + y++; + } + if (y < y1) + setLineCore(x, x1, y, mode); + } + + } \ No newline at end of file From a3db891673006a4b0187f433eb2e840fe1281d74 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Tue, 11 Jul 2017 14:43:12 +0200 Subject: [PATCH 4/7] Custom rendering almost works --- libs/core/enums.d.ts | 10 +-- libs/core/screen.cpp | 147 +++++++++++++++++++++++++++++++++++-------- libs/core/screen.ts | 36 ++++++----- libs/core/shims.d.ts | 18 +++--- libs/core/test.ts | 15 ++--- 5 files changed, 161 insertions(+), 65 deletions(-) diff --git a/libs/core/enums.d.ts b/libs/core/enums.d.ts index ea50d007..fe6627c2 100644 --- a/libs/core/enums.d.ts +++ b/libs/core/enums.d.ts @@ -6,10 +6,12 @@ */ declare enum Draw { - Normal = 0, - Clear = (0x0004), // DRAW_OPT_CLEAR_PIXELS - Xor = (0x0018), // DRAW_OPT_LOGICAL_XOR - Fill = (0x0020), // DRAW_OPT_FILL_SHAPE + Normal = 0x00, + Clear = 0x01, + Xor = 0x02, + Fill = 0x04, + Transparent = 0x08, + Double = 0x10, } diff --git a/libs/core/screen.cpp b/libs/core/screen.cpp index c2a26e4a..3279404c 100644 --- a/libs/core/screen.cpp +++ b/libs/core/screen.cpp @@ -6,12 +6,22 @@ * Drawing modes */ enum class Draw { - Normal = 0, // set pixels to black, no fill - Clear = DRAW_OPT_CLEAR_PIXELS, - Xor = DRAW_OPT_LOGICAL_XOR, - Fill = DRAW_OPT_FILL_SHAPE, + Normal = 0x00, // set pixels to black, no fill + Clear = 0x01, + Xor = 0x02, + Fill = 0x04, + Transparent = 0x08, + Double = 0x10, }; +inline bool operator&(Draw a, Draw b) { + return ((int)a & (int)b) != 0; +} + +inline Draw operator|(Draw a, Draw b) { + return (Draw)((int)a | (int)b); +} + enum class ScreenFont { Normal = FONTTYPE_NORMAL, Small = FONTTYPE_SMALL, @@ -30,7 +40,6 @@ namespace screen { static const uint8_t pixmap[] = {0x00, 0xE0, 0x1C, 0xFC, 0x03, 0xE3, 0x1F, 0xFF}; static uint8_t bitBuffer[ROW_SIZE * LCD_HEIGHT]; -static uint8_t *mappedFrameBuffer; static bool dirty; static void bitBufferToFrameBuffer(uint8_t *bitBuffer, uint8_t *fb) { @@ -53,7 +62,7 @@ static void bitBufferToFrameBuffer(uint8_t *bitBuffer, uint8_t *fb) { pixels = *bitBuffer++ << 0; pixels |= *bitBuffer++ << 8; - bitBuffer += ROW_SIZE - 26; + bitBuffer += ROW_SIZE - 23; int m = 4; while (m--) { @@ -63,12 +72,9 @@ static void bitBufferToFrameBuffer(uint8_t *bitBuffer, uint8_t *fb) { } } -static void updateLCD() { - bitBufferToFrameBuffer(bitBuffer, mappedFrameBuffer); -} - #define OFF(x, y) (((y) << 5) + ((x) >> 3)) #define MASK(x, y) (1 << ((x)&7)) +#define PIX2BYTES(x) (((x) + 7) >> 3) static inline void applyMask(int off, int mask, Draw mode) { if (mode & Draw::Clear) @@ -85,24 +91,24 @@ void _setPixel(int x, int y, Draw mode) { } void blitLineCore(int x, int y, int w, uint8_t *data, Draw mode) { - if (y < 0 || y >= LMS.LCD_HEIGHT) + if (y < 0 || y >= LCD_HEIGHT) return; if (x + w <= 0) return; - if (x >= LMS.LCD_WIDTH) + if (x >= LCD_WIDTH) return; int shift = x & 7; int off = OFF(x, y); int off0 = OFF(0, y); - int off1 = OFF(LMS.LCD_WIDTH - 1, y); + int off1 = OFF(LCD_WIDTH - 1, y); int x1 = x + w; int prev = 0; while (x < x1 - 8) { int curr = *data++ << shift; if (off0 <= off && off <= off1) - applyMask(off, curr | prev); + applyMask(off, curr | prev, mode); off++; prev = curr >> 8; x += 8; @@ -112,8 +118,10 @@ void blitLineCore(int x, int y, int w, uint8_t *data, Draw mode) { if (left > 0) { int curr = *data << shift; if (off0 <= off && off <= off1) - applyMask(off, (curr | prev) & ((1 << left) - 1)); + applyMask(off, (curr | prev) & ((1 << left) - 1), mode); } + + dirty = true; } //% @@ -126,23 +134,69 @@ static uint8_t ones[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; +bool isValidIcon(Buffer buf) { + return buf->length >= 3 && buf->data[0] == 0xf0; +} + +static const uint8_t bitdouble[] = { + 0x00, 0x03, 0x0c, 0x0f, 0x30, 0x33, 0x3c, 0x3f, 0xc0, 0xc3, 0xcc, 0xcf, 0xf0, 0xf3, 0xfc, 0xff, +}; + +/** Double size of an icon. */ +//% +Buffer doubleIcon(Buffer buf) { + if (!isValidIcon(buf)) + return NULL; + int w = buf->data[1]; + if (w > 126) + return NULL; + int bw = PIX2BYTES(w); + int h = (buf->length - 2) / bw; + int bw2 = PIX2BYTES(w * 2); + Buffer out = mkBuffer(NULL, 2 + bw2 * h * 2); + out->data[0] = 0xf0; + out->data[1] = w * 2; + uint8_t *src = buf->data + 2; + uint8_t *dst = out->data + 2; + for (int i = 0; i < h; ++i) { + for (int jj = 0; jj < 2; ++jj) { + auto p = src; + for (int j = 0; j < bw; ++j) { + *dst++ = bitdouble[*p & 0xf]; + *dst++ = bitdouble[*p >> 4]; + p++; + } + } + src += bw; + } + return out; +} + /** Draw an icon on the screen. */ //% void drawIcon(int x, int y, Buffer buf, Draw mode) { - if (buf->length < 2) + if (!isValidIcon(buf)) return; - int pixwidth = buf->data[0]; - if (pixwidth > 100) - return; - int ptr = 1; - int bytewidth = (pixwidth + 7) >> 3; + if (mode & Draw::Double) + buf = doubleIcon(buf); + + int pixwidth = buf->data[1]; + int ptr = 2; + int bytewidth = PIX2BYTES(pixwidth); + pixwidth = min(pixwidth, LCD_WIDTH); while (ptr + bytewidth <= buf->length) { - if (mode == Draw::Normal) + if (mode & (Draw::Clear | Draw::Xor | Draw::Transparent)) { + // no erase of background + } else { blitLineCore(x, y, pixwidth, ones, Draw::Clear); + } blitLineCore(x, y, pixwidth, &buf->data[ptr], mode); y++; ptr += bytewidth; } + + if (mode & Draw::Double) + decrRC(buf); } /** Clear screen and reset font to normal. */ @@ -151,23 +205,62 @@ void clear() { memset(bitBuffer, 0, sizeof(bitBuffer)); dirty = true; } + +//% +void dump() { + char buf[LCD_WIDTH + 1]; + FILE *f = fopen("/tmp/screen.txt", "w"); + for (int i = 0; i < LCD_HEIGHT; ++i) { + for (int j = 0; j < LCD_WIDTH; ++j) { + if (bitBuffer[OFF(j, i)] & MASK(j, i)) + buf[j] = '#'; + else + buf[j] = '.'; + } + buf[LCD_WIDTH] = 0; + fprintf(f, "%s\n", buf); + } + fclose(f); } -namespace pxt { +static uint8_t *mappedFrameBuffer; + +//% +void updateLCD() { + if (dirty && mappedFrameBuffer != MAP_FAILED) { + dirty = false; + bitBufferToFrameBuffer(bitBuffer, mappedFrameBuffer); + } +} void *screenRefresh(void *dummy) { while (true) { sleep_core_us(30000); - LcdUpdate(); + updateLCD(); } } -void screen_init() { - LcdInitNoAutoRefresh(); - LcdClean(); +void init() { + DMESG("init screen"); + if (mappedFrameBuffer) + return; + int fd = open("/dev/fb0", O_RDWR); + DMESG("init screen %d", fd); + mappedFrameBuffer = (uint8_t *)mmap(NULL, FB_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + DMESG("map %p", mappedFrameBuffer); + if (mappedFrameBuffer == MAP_FAILED) { + target_panic(111); + } + clear(); pthread_t pid; pthread_create(&pid, NULL, screenRefresh, NULL); pthread_detach(pid); } } + +namespace pxt { +void screen_init() { + screen::init(); +} +} diff --git a/libs/core/screen.ts b/libs/core/screen.ts index 0b6c4c7e..cb973b90 100644 --- a/libs/core/screen.ts +++ b/libs/core/screen.ts @@ -1,9 +1,9 @@ namespace screen { - //% shim=screen::setPixelCore - function setPixelCore(p0: uint32, p1: uint32, mode: Draw): void { } + //% shim=screen::_setPixel + function _setPixel(p0: uint32, p1: uint32, mode: Draw): void { } - //% shim=screen::blitLine - function blitLine(xw: uint32, y: uint32, buf: Buffer, mode: Draw): void { } + //% shim=screen::_blitLine + function _blitLine(xw: uint32, y: uint32, buf: Buffer, mode: Draw): void { } function pack(x: number, y: number) { return Math.clamp(0, 512, x) | (Math.clamp(0, 512, y) << 16) @@ -12,7 +12,7 @@ namespace screen { const ones = hex`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff` function setLineCore(x: number, x1: number, y: number, mode: Draw) { - blitLine(pack(x, x1 - x), y, ones, mode) + _blitLine(pack(x, x1 - x), y, ones, mode) } export interface Font { @@ -23,7 +23,7 @@ namespace screen { } let currFont: Font - export const heart = hex`07 367f7f3e1c08` + export const heart = hex`f007 367f7f3e1c08` export function defaultFont(): Font { return { @@ -56,7 +56,7 @@ namespace screen { x |= 0 y |= 0 if (0 <= x && x < LMS.LCD_WIDTH && 0 <= y && y < LMS.LCD_HEIGHT) - setPixelCore(x, y, mode) + _setPixel(x, y, mode) } export function drawText(x: number, y: number, text: string, mode = Draw.Normal) { @@ -66,21 +66,25 @@ namespace screen { let x0 = x let cp = 0 let byteWidth = (currFont.charWidth + 7) >> 3 - let iconBuf = output.createBuffer(1 + byteWidth * currFont.charHeight) - iconBuf[0] = currFont.charWidth + let charSize = byteWidth * currFont.charHeight + let iconBuf = output.createBuffer(2 + charSize) + let double = (mode & Draw.Double) ? 2 : 1 + iconBuf[0] = 0xf0 + iconBuf[1] = currFont.charWidth while (cp < text.length) { let ch = text.charCodeAt(cp++) if (ch == 10) { - y += currFont.charHeight + 2 + y += double * currFont.charHeight + 2 x = x0 } if (ch < 32) continue - let idx = (ch - currFont.firstChar) * byteWidth + let idx = (ch - currFont.firstChar) * charSize if (idx < 0 || idx + iconBuf.length - 1 > currFont.data.length) - iconBuf.fill(0, 1) + iconBuf.fill(0, 2) else - iconBuf.write(1, currFont.data.slice(idx, byteWidth)) + iconBuf.write(2, currFont.data.slice(idx, charSize)) drawIcon(x, y, iconBuf, mode) + x += double * currFont.charWidth } } @@ -105,7 +109,7 @@ namespace screen { let y1 = Math.min(LMS.LCD_HEIGHT, y + h); if (w == 1) { while (y < y1) - setPixelCore(x, y++, mode); + _setPixel(x, y++, mode); return; } @@ -114,8 +118,8 @@ namespace screen { if (mode & Draw.Fill) { setLineCore(x, x1, y, mode); } else { - setPixelCore(x, y, mode); - setPixelCore(x1 - 1, y, mode); + _setPixel(x, y, mode); + _setPixel(x1 - 1, y, mode); } y++; } diff --git a/libs/core/shims.d.ts b/libs/core/shims.d.ts index 406bf3f7..2e26ce3b 100644 --- a/libs/core/shims.d.ts +++ b/libs/core/shims.d.ts @@ -72,21 +72,17 @@ declare namespace serial { } declare namespace screen { - /** Draw text. */ - //% mode.defl=0 shim=screen::drawText - function drawText(x: int32, y: int32, text: string, mode?: Draw): void; + /** Double size of an icon. */ + //% shim=screen::doubleIcon + function doubleIcon(buf: Buffer): Buffer; + + /** Draw an icon on the screen. */ + //% shim=screen::drawIcon + function drawIcon(x: int32, y: int32, buf: Buffer, mode: Draw): void; /** Clear screen and reset font to normal. */ //% shim=screen::clear function clear(): void; - - /** Scroll screen vertically. */ - //% shim=screen::scroll - function scroll(v: int32): void; - - /** Set font for drawText() */ - //% shim=screen::setFont - function setFont(font: ScreenFont): void; } declare namespace output { diff --git a/libs/core/test.ts b/libs/core/test.ts index 4741275c..6273f726 100644 --- a/libs/core/test.ts +++ b/libs/core/test.ts @@ -1,29 +1,30 @@ screen.clear() -screen.setFont(ScreenFont.Large) -screen.drawText(10, 30, "Welcome PXT!") +screen.drawText(10, 30, "Welcome PXT!", Draw.Double) -screen.drawEllipse(40, 40, 20, 10, Draw.Fill) +screen.drawRect(40, 40, 20, 10, Draw.Fill) output.setLights(LightsPattern.Orange) +screen.drawIcon(100, 50, screen.heart, Draw.Double) + input.buttonEnter.onEvent(ButtonEvent.Click, () => { screen.clear() }) input.buttonLeft.onEvent(ButtonEvent.Click, () => { screen.drawRect(10, 70, 20, 10, Draw.Fill) + output.setLights(LightsPattern.Red) }) input.buttonRight.onEvent(ButtonEvent.Click, () => { - screen.setFont(ScreenFont.Normal) - screen.drawText(10, 60, "Bang!") + screen.drawText(10, 60, "Right!") }) input.buttonDown.onEvent(ButtonEvent.Click, () => { - screen.scroll(-10) + screen.drawText(10, 60, "Down! ") }) input.buttonUp.onEvent(ButtonEvent.Click, () => { - screen.scroll(10) + screen.drawText(10, 60, "Up! ") }) From 8c6b56fc395ced0f6a103052700e4403b7bae1bd Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Tue, 11 Jul 2017 15:25:14 +0200 Subject: [PATCH 5/7] Fix drawIcon --- libs/core/screen.cpp | 2 +- libs/core/test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/core/screen.cpp b/libs/core/screen.cpp index 3279404c..e722e9fb 100644 --- a/libs/core/screen.cpp +++ b/libs/core/screen.cpp @@ -102,7 +102,7 @@ void blitLineCore(int x, int y, int w, uint8_t *data, Draw mode) { int off = OFF(x, y); int off0 = OFF(0, y); int off1 = OFF(LCD_WIDTH - 1, y); - int x1 = x + w; + int x1 = x + w + shift; int prev = 0; while (x < x1 - 8) { diff --git a/libs/core/test.ts b/libs/core/test.ts index 6273f726..befee1b0 100644 --- a/libs/core/test.ts +++ b/libs/core/test.ts @@ -4,7 +4,7 @@ screen.drawText(10, 30, "Welcome PXT!", Draw.Double) screen.drawRect(40, 40, 20, 10, Draw.Fill) output.setLights(LightsPattern.Orange) -screen.drawIcon(100, 50, screen.heart, Draw.Double) +screen.drawIcon(100, 50, screen.doubleIcon(screen.heart), Draw.Double|Draw.Transparent) input.buttonEnter.onEvent(ButtonEvent.Click, () => { screen.clear() From 0d6e6a7c96ec63378acf3fac061555012236e002 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Tue, 11 Jul 2017 15:27:23 +0200 Subject: [PATCH 6/7] Remove unused enum --- libs/core/enums.d.ts | 8 -------- libs/core/screen.cpp | 6 ------ 2 files changed, 14 deletions(-) diff --git a/libs/core/enums.d.ts b/libs/core/enums.d.ts index fe6627c2..2faf5ed4 100644 --- a/libs/core/enums.d.ts +++ b/libs/core/enums.d.ts @@ -14,12 +14,4 @@ Double = 0x10, } - - declare enum ScreenFont { - Normal = 0, // FONTTYPE_NORMAL - Small = 1, // FONTTYPE_SMALL - Large = 2, // FONTTYPE_LARGE - Tiny = 3, // FONTTYPE_TINY - } - // Auto-generated. Do not edit. Really. diff --git a/libs/core/screen.cpp b/libs/core/screen.cpp index e722e9fb..9e4ce984 100644 --- a/libs/core/screen.cpp +++ b/libs/core/screen.cpp @@ -22,12 +22,6 @@ inline Draw operator|(Draw a, Draw b) { return (Draw)((int)a | (int)b); } -enum class ScreenFont { - Normal = FONTTYPE_NORMAL, - Small = FONTTYPE_SMALL, - Large = FONTTYPE_LARGE, - Tiny = FONTTYPE_TINY, -}; #define XX(v) ((uint32_t)(v)&0xffff) #define YY(v) ((uint32_t)(v) >> 16) From 574fdb3473a963af7c827a15dbf3d6bbda569d66 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Tue, 11 Jul 2017 15:40:27 +0200 Subject: [PATCH 7/7] Add microbit font --- libs/core/screen.ts | 26 ++++++++++++++++++++++++++ libs/core/test.ts | 1 + 2 files changed, 27 insertions(+) diff --git a/libs/core/screen.ts b/libs/core/screen.ts index cb973b90..ce0f1510 100644 --- a/libs/core/screen.ts +++ b/libs/core/screen.ts @@ -23,6 +23,10 @@ namespace screen { } let currFont: Font + export function setFont(f: Font) { + currFont = f + } + export const heart = hex`f007 367f7f3e1c08` export function defaultFont(): Font { @@ -52,6 +56,28 @@ namespace screen { } } + export function microbitFont() { + return { + charWidth: 6, + charHeight: 5, + firstChar: 32, + // source https://github.com/lancaster-university/microbit-dal/blob/master/source/core/MicroBitFont.cpp + data: hex` +0000000000 0202020002 0a0a000000 0a1f0a1f0a 0e130e190e 1309041219 0609060916 0202000000 0402020204 +0204040402 000a040a00 00040e0400 0000000402 00000e0000 0000000200 1008040201 0609090906 040604040e +070806010f 0f08040906 0c0a091f08 1f010f100f 08040e110e 1f08040201 0e110e110e 0e110e0402 0002000200 +0004000402 0804020408 000e000e00 0204080402 0e110c0004 0e11151906 06090f0909 0709070907 0e0101010e +0709090907 0f0107010f 0f01070101 0e0119110e 09090f0909 0702020207 1f08080906 0905030509 010101010f +111b151111 1113151911 0609090906 0709070101 060909060c 0709070911 0e01060807 1f04040404 0909090906 +1111110a04 1111151b11 0909060909 110a040404 0f0402010f 0e0202020e 0102040810 0e0808080e 040a000000 +000000001f 0204000000 000e09091e 0101070907 000e01010e 08080e090e 060907010e 0c02070202 0e090e0806 +0101070909 0200020202 0800080806 0105030509 020202020c 001b151111 0007090909 0006090906 0007090701 +000e090e08 000e010101 000c020403 02020e021c 000909091e 0011110a04 001111151b 0009060609 00110a0403 +000f04020f 0c0406040c 0202020202 0302060203 0000061800 +` + } + } + export function setPixel(x: number, y: number, mode = Draw.Normal) { x |= 0 y |= 0 diff --git a/libs/core/test.ts b/libs/core/test.ts index befee1b0..9ad44512 100644 --- a/libs/core/test.ts +++ b/libs/core/test.ts @@ -13,6 +13,7 @@ input.buttonEnter.onEvent(ButtonEvent.Click, () => { input.buttonLeft.onEvent(ButtonEvent.Click, () => { screen.drawRect(10, 70, 20, 10, Draw.Fill) output.setLights(LightsPattern.Red) + screen.setFont(screen.microbitFont()) }) input.buttonRight.onEvent(ButtonEvent.Click, () => {