Merge branch 'newgfx'

This commit is contained in:
Michal Moskal 2017-07-11 15:42:15 +02:00
commit cb88fd7c2b
7 changed files with 384 additions and 92 deletions

View File

@ -2,7 +2,7 @@
* [x] implement serialPoll * [x] implement serialPoll
* [x] try some motors * [x] try some motors
* [x] add `control.interrupt(ms, () => { ... sync function ... })` - running outside regular JS thread * [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 * [ ] fix `@PXT@:` handling for ELF in PXT
* [x] parse Python field lists into offsets * [x] parse Python field lists into offsets

View File

@ -3,16 +3,16 @@
* Patterns for lights under the buttons. * Patterns for lights under the buttons.
*/ */
const enum LightsPattern { const enum LightsPattern {
Off = 0, // LED_BLACK Off = 0,
Green = 1, // LED_GREEN Green = 1,
Red = 2, // LED_RED Red = 2,
Orange = 3, // LED_ORANGE Orange = 3,
GreenFlash = 4, // LED_GREEN_FLASH GreenFlash = 4,
RedFlash = 5, // LED_RED_FLASH RedFlash = 5,
OrangeFlash = 6, // LED_ORANGE_FLASH OrangeFlash = 6,
GreenPulse = 7, // LED_GREEN_PULSE GreenPulse = 7,
RedPulse = 8, // LED_RED_PULSE RedPulse = 8,
OrangePulse = 9, // LED_ORANGE_PULSE OrangePulse = 9,
} }
/** /**

18
libs/core/enums.d.ts vendored
View File

@ -6,18 +6,12 @@
*/ */
declare enum Draw { declare enum Draw {
Normal = 0, Normal = 0x00,
Clear = (0x0004), // DRAW_OPT_CLEAR_PIXELS Clear = 0x01,
Xor = (0x0018), // DRAW_OPT_LOGICAL_XOR Xor = 0x02,
Fill = (0x0020), // DRAW_OPT_FILL_SHAPE Fill = 0x04,
} Transparent = 0x08,
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. // Auto-generated. Do not edit. Really.

View File

@ -6,18 +6,22 @@
* Drawing modes * Drawing modes
*/ */
enum class Draw { enum class Draw {
Normal = 0, // set pixels to black, no fill Normal = 0x00, // set pixels to black, no fill
Clear = DRAW_OPT_CLEAR_PIXELS, Clear = 0x01,
Xor = DRAW_OPT_LOGICAL_XOR, Xor = 0x02,
Fill = DRAW_OPT_FILL_SHAPE, Fill = 0x04,
Transparent = 0x08,
Double = 0x10,
}; };
enum class ScreenFont { inline bool operator&(Draw a, Draw b) {
Normal = FONTTYPE_NORMAL, return ((int)a & (int)b) != 0;
Small = FONTTYPE_SMALL, }
Large = FONTTYPE_LARGE,
Tiny = FONTTYPE_TINY, inline Draw operator|(Draw a, Draw b) {
}; return (Draw)((int)a | (int)b);
}
#define XX(v) ((uint32_t)(v)&0xffff) #define XX(v) ((uint32_t)(v)&0xffff)
#define YY(v) ((uint32_t)(v) >> 16) #define YY(v) ((uint32_t)(v) >> 16)
@ -25,63 +29,232 @@ enum class ScreenFont {
// We only support up to 4 arguments for C++ functions - need to pack them on the TS side // We only support up to 4 arguments for C++ functions - need to pack them on the TS side
namespace screen { 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};
void _drawLine(uint32_t p0, uint32_t p1, Draw mode) { static uint8_t bitBuffer[ROW_SIZE * LCD_HEIGHT];
LineOutEx(XX(p0), YY(p0), XX(p1), YY(p1), (uint32_t)mode); 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 - 23;
int m = 4;
while (m--) {
*fb++ = pixmap[pixels & 0x07];
pixels >>= 3;
}
}
}
#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)
bitBuffer[off] &= ~mask;
else if (mode & Draw::Xor)
bitBuffer[off] ^= mask;
else
bitBuffer[off] |= mask;
} }
//% //%
void _drawRect(uint32_t p0, uint32_t p1, Draw mode) { void _setPixel(int x, int y, Draw mode) {
RectOutEx(XX(p0), YY(p0), XX(p1), YY(p1), (uint32_t)mode); applyMask(OFF(x, y), MASK(x, y), mode);
}
void blitLineCore(int x, int y, int w, uint8_t *data, Draw mode) {
if (y < 0 || y >= LCD_HEIGHT)
return;
if (x + w <= 0)
return;
if (x >= LCD_WIDTH)
return;
int shift = x & 7;
int off = OFF(x, y);
int off0 = OFF(0, y);
int off1 = OFF(LCD_WIDTH - 1, y);
int x1 = x + w + shift;
int prev = 0;
while (x < x1 - 8) {
int curr = *data++ << shift;
if (off0 <= off && off <= off1)
applyMask(off, curr | prev, mode);
off++;
prev = curr >> 8;
x += 8;
}
int left = x1 - x;
if (left > 0) {
int curr = *data << shift;
if (off0 <= off && off <= off1)
applyMask(off, (curr | prev) & ((1 << left) - 1), mode);
}
dirty = true;
} }
//% //%
void _drawEllipse(uint32_t p0, uint32_t p1, Draw mode) { void _blitLine(int xw, int y, Buffer buf, Draw mode) {
EllipseOutEx(XX(p0), YY(p0), XX(p1), YY(p1), (uint32_t)mode); blitLineCore(XX(xw), y, YY(xw), buf->data, mode);
} }
/** Draw text. */ static uint8_t ones[] = {
//% mode.defl=0 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
void drawText(int x, int y, String text, Draw mode) { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
LcdText((int)mode & (int)Draw::Clear ? 0 : 1, x, y, text->data); };
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 (!isValidIcon(buf))
return;
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::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. */ /** Clear screen and reset font to normal. */
//% //%
void clear() { void clear() {
LcdClearDisplay(); memset(bitBuffer, 0, sizeof(bitBuffer));
dirty = true;
} }
/** Scroll screen vertically. */
//% //%
void scroll(int v) { void dump() {
LcdScroll(v); 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);
} }
/** Set font for drawText() */ static uint8_t *mappedFrameBuffer;
//% //%
void setFont(ScreenFont font) { void updateLCD() {
LcdSelectFont((uint8_t)font); if (dirty && mappedFrameBuffer != MAP_FAILED) {
dirty = false;
bitBufferToFrameBuffer(bitBuffer, mappedFrameBuffer);
} }
} }
namespace pxt {
void *screenRefresh(void *dummy) { void *screenRefresh(void *dummy) {
while (true) { while (true) {
sleep_core_us(30000); sleep_core_us(30000);
LcdUpdate(); updateLCD();
} }
} }
void screen_init() { void init() {
LcdInitNoAutoRefresh(); DMESG("init screen");
LcdClean(); 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_t pid;
pthread_create(&pid, NULL, screenRefresh, NULL); pthread_create(&pid, NULL, screenRefresh, NULL);
pthread_detach(pid); pthread_detach(pid);
} }
}
namespace pxt {
void screen_init() {
screen::init();
}
} }

View File

@ -1,30 +1,157 @@
namespace screen { namespace screen {
//% shim=screen::_drawLine //% shim=screen::_setPixel
function _drawLine(p0: uint32, p1: uint32, mode: Draw): void { } function _setPixel(p0: uint32, p1: uint32, mode: Draw): void { }
//% shim=screen::_drawRect //% shim=screen::_blitLine
function _drawRect(p0: uint32, p1: uint32, mode: Draw): void { } function _blitLine(xw: uint32, y: uint32, buf: Buffer, mode: Draw): void { }
//% shim=screen::_drawEllipse
function _drawEllipse(p0: uint32, p1: uint32, mode: Draw): void { }
function pack(x: number, y: number) { function pack(x: number, y: number) {
return Math.clamp(0, 512, x) | (Math.clamp(0, 512, y) << 16) 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) { const ones = hex`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
_drawLine(pack(x0, y0), pack(x1, y1), mode)
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) { export interface Font {
_drawRect(pack(x, y), pack(w, h), mode) charWidth: number;
charHeight: number;
firstChar: number;
data: Buffer;
}
let currFont: Font
export function setFont(f: Font) {
currFont = f
} }
export function drawEllipse(x: number, y: number, rx: number, ry: number, mode?: Draw) { export const heart = hex`f007 367f7f3e1c08`
_drawEllipse(pack(x, y), pack(rx, ry), mode)
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 drawCircle(x: number, y: number, r: number, mode?: Draw) { export function microbitFont() {
drawEllipse(x, y, r, r, mode) 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
if (0 <= x && x < LMS.LCD_WIDTH && 0 <= y && y < LMS.LCD_HEIGHT)
_setPixel(x, y, 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 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 += double * currFont.charHeight + 2
x = x0
}
if (ch < 32) continue
let idx = (ch - currFont.firstChar) * charSize
if (idx < 0 || idx + iconBuf.length - 1 > currFont.data.length)
iconBuf.fill(0, 2)
else
iconBuf.write(2, currFont.data.slice(idx, charSize))
drawIcon(x, y, iconBuf, mode)
x += double * currFont.charWidth
}
}
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)
_setPixel(x, y++, mode);
return;
}
setLineCore(x, x1, y++, mode);
while (y < y1 - 1) {
if (mode & Draw.Fill) {
setLineCore(x, x1, y, mode);
} else {
_setPixel(x, y, mode);
_setPixel(x1 - 1, y, mode);
}
y++;
}
if (y < y1)
setLineCore(x, x1, y, mode);
}
}

18
libs/core/shims.d.ts vendored
View File

@ -72,21 +72,17 @@ declare namespace serial {
} }
declare namespace screen { declare namespace screen {
/** Draw text. */ /** Double size of an icon. */
//% mode.defl=0 shim=screen::drawText //% shim=screen::doubleIcon
function drawText(x: int32, y: int32, text: string, mode?: Draw): void; 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. */ /** Clear screen and reset font to normal. */
//% shim=screen::clear //% shim=screen::clear
function clear(): void; 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 { declare namespace output {

View File

@ -1,29 +1,31 @@
screen.clear() screen.clear()
screen.setFont(ScreenFont.Large) screen.drawText(10, 30, "Welcome PXT!", Draw.Double)
screen.drawText(10, 30, "Welcome PXT!")
screen.drawEllipse(40, 40, 20, 10, Draw.Fill) screen.drawRect(40, 40, 20, 10, Draw.Fill)
output.setLights(LightsPattern.Orange) output.setLights(LightsPattern.Orange)
screen.drawIcon(100, 50, screen.doubleIcon(screen.heart), Draw.Double|Draw.Transparent)
input.buttonEnter.onEvent(ButtonEvent.Click, () => { input.buttonEnter.onEvent(ButtonEvent.Click, () => {
screen.clear() screen.clear()
}) })
input.buttonLeft.onEvent(ButtonEvent.Click, () => { input.buttonLeft.onEvent(ButtonEvent.Click, () => {
screen.drawRect(10, 70, 20, 10, Draw.Fill) screen.drawRect(10, 70, 20, 10, Draw.Fill)
output.setLights(LightsPattern.Red)
screen.setFont(screen.microbitFont())
}) })
input.buttonRight.onEvent(ButtonEvent.Click, () => { input.buttonRight.onEvent(ButtonEvent.Click, () => {
screen.setFont(ScreenFont.Normal) screen.drawText(10, 60, "Right!")
screen.drawText(10, 60, "Bang!")
}) })
input.buttonDown.onEvent(ButtonEvent.Click, () => { input.buttonDown.onEvent(ButtonEvent.Click, () => {
screen.scroll(-10) screen.drawText(10, 60, "Down! ")
}) })
input.buttonUp.onEvent(ButtonEvent.Click, () => { input.buttonUp.onEvent(ButtonEvent.Click, () => {
screen.scroll(10) screen.drawText(10, 60, "Up! ")
}) })