Custom rendering almost works

This commit is contained in:
Michal Moskal 2017-07-11 14:43:12 +02:00
parent 3bf3d07e4c
commit a3db891673
5 changed files with 161 additions and 65 deletions

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

@ -6,10 +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,
} }

View File

@ -6,12 +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,
}; };
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 { enum class ScreenFont {
Normal = FONTTYPE_NORMAL, Normal = FONTTYPE_NORMAL,
Small = FONTTYPE_SMALL, Small = FONTTYPE_SMALL,
@ -30,7 +40,6 @@ namespace screen {
static const uint8_t pixmap[] = {0x00, 0xE0, 0x1C, 0xFC, 0x03, 0xE3, 0x1F, 0xFF}; static const uint8_t pixmap[] = {0x00, 0xE0, 0x1C, 0xFC, 0x03, 0xE3, 0x1F, 0xFF};
static uint8_t bitBuffer[ROW_SIZE * LCD_HEIGHT]; static uint8_t bitBuffer[ROW_SIZE * LCD_HEIGHT];
static uint8_t *mappedFrameBuffer;
static bool dirty; static bool dirty;
static void bitBufferToFrameBuffer(uint8_t *bitBuffer, uint8_t *fb) { 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++ << 0;
pixels |= *bitBuffer++ << 8; pixels |= *bitBuffer++ << 8;
bitBuffer += ROW_SIZE - 26; bitBuffer += ROW_SIZE - 23;
int m = 4; int m = 4;
while (m--) { 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 OFF(x, y) (((y) << 5) + ((x) >> 3))
#define MASK(x, y) (1 << ((x)&7)) #define MASK(x, y) (1 << ((x)&7))
#define PIX2BYTES(x) (((x) + 7) >> 3)
static inline void applyMask(int off, int mask, Draw mode) { static inline void applyMask(int off, int mask, Draw mode) {
if (mode & Draw::Clear) 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) { 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; return;
if (x + w <= 0) if (x + w <= 0)
return; return;
if (x >= LMS.LCD_WIDTH) if (x >= LCD_WIDTH)
return; return;
int shift = x & 7; int shift = x & 7;
int off = OFF(x, y); int off = OFF(x, y);
int off0 = OFF(0, 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 x1 = x + w;
int prev = 0; int prev = 0;
while (x < x1 - 8) { while (x < x1 - 8) {
int curr = *data++ << shift; int curr = *data++ << shift;
if (off0 <= off && off <= off1) if (off0 <= off && off <= off1)
applyMask(off, curr | prev); applyMask(off, curr | prev, mode);
off++; off++;
prev = curr >> 8; prev = curr >> 8;
x += 8; x += 8;
@ -112,8 +118,10 @@ void blitLineCore(int x, int y, int w, uint8_t *data, Draw mode) {
if (left > 0) { if (left > 0) {
int curr = *data << shift; int curr = *data << shift;
if (off0 <= off && off <= off1) 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, 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. */ /** Draw an icon on the screen. */
//% //%
void drawIcon(int x, int y, Buffer buf, Draw mode) { void drawIcon(int x, int y, Buffer buf, Draw mode) {
if (buf->length < 2) if (!isValidIcon(buf))
return; return;
int pixwidth = buf->data[0]; if (mode & Draw::Double)
if (pixwidth > 100) buf = doubleIcon(buf);
return;
int ptr = 1; int pixwidth = buf->data[1];
int bytewidth = (pixwidth + 7) >> 3; int ptr = 2;
int bytewidth = PIX2BYTES(pixwidth);
pixwidth = min(pixwidth, LCD_WIDTH);
while (ptr + bytewidth <= buf->length) { 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, ones, Draw::Clear);
}
blitLineCore(x, y, pixwidth, &buf->data[ptr], mode); blitLineCore(x, y, pixwidth, &buf->data[ptr], mode);
y++; y++;
ptr += bytewidth; ptr += bytewidth;
} }
if (mode & Draw::Double)
decrRC(buf);
} }
/** Clear screen and reset font to normal. */ /** Clear screen and reset font to normal. */
@ -151,23 +205,62 @@ void clear() {
memset(bitBuffer, 0, sizeof(bitBuffer)); memset(bitBuffer, 0, sizeof(bitBuffer));
dirty = true; 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) { 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,9 +1,9 @@
namespace screen { namespace screen {
//% shim=screen::setPixelCore //% shim=screen::_setPixel
function setPixelCore(p0: uint32, p1: uint32, mode: Draw): void { } function _setPixel(p0: uint32, p1: uint32, mode: Draw): void { }
//% shim=screen::blitLine //% shim=screen::_blitLine
function blitLine(xw: uint32, y: uint32, buf: Buffer, mode: Draw): void { } function _blitLine(xw: uint32, y: uint32, buf: Buffer, 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)
@ -12,7 +12,7 @@ namespace screen {
const ones = hex`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff` const ones = hex`ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`
function setLineCore(x: number, x1: number, y: number, mode: Draw) { 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 { export interface Font {
@ -23,7 +23,7 @@ namespace screen {
} }
let currFont: Font let currFont: Font
export const heart = hex`07 367f7f3e1c08` export const heart = hex`f007 367f7f3e1c08`
export function defaultFont(): Font { export function defaultFont(): Font {
return { return {
@ -56,7 +56,7 @@ namespace screen {
x |= 0 x |= 0
y |= 0 y |= 0
if (0 <= x && x < LMS.LCD_WIDTH && 0 <= y && y < LMS.LCD_HEIGHT) 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) { export function drawText(x: number, y: number, text: string, mode = Draw.Normal) {
@ -66,21 +66,25 @@ namespace screen {
let x0 = x let x0 = x
let cp = 0 let cp = 0
let byteWidth = (currFont.charWidth + 7) >> 3 let byteWidth = (currFont.charWidth + 7) >> 3
let iconBuf = output.createBuffer(1 + byteWidth * currFont.charHeight) let charSize = byteWidth * currFont.charHeight
iconBuf[0] = currFont.charWidth let iconBuf = output.createBuffer(2 + charSize)
let double = (mode & Draw.Double) ? 2 : 1
iconBuf[0] = 0xf0
iconBuf[1] = currFont.charWidth
while (cp < text.length) { while (cp < text.length) {
let ch = text.charCodeAt(cp++) let ch = text.charCodeAt(cp++)
if (ch == 10) { if (ch == 10) {
y += currFont.charHeight + 2 y += double * currFont.charHeight + 2
x = x0 x = x0
} }
if (ch < 32) continue 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) if (idx < 0 || idx + iconBuf.length - 1 > currFont.data.length)
iconBuf.fill(0, 1) iconBuf.fill(0, 2)
else else
iconBuf.write(1, currFont.data.slice(idx, byteWidth)) iconBuf.write(2, currFont.data.slice(idx, charSize))
drawIcon(x, y, iconBuf, mode) drawIcon(x, y, iconBuf, mode)
x += double * currFont.charWidth
} }
} }
@ -105,7 +109,7 @@ namespace screen {
let y1 = Math.min(LMS.LCD_HEIGHT, y + h); let y1 = Math.min(LMS.LCD_HEIGHT, y + h);
if (w == 1) { if (w == 1) {
while (y < y1) while (y < y1)
setPixelCore(x, y++, mode); _setPixel(x, y++, mode);
return; return;
} }
@ -114,8 +118,8 @@ namespace screen {
if (mode & Draw.Fill) { if (mode & Draw.Fill) {
setLineCore(x, x1, y, mode); setLineCore(x, x1, y, mode);
} else { } else {
setPixelCore(x, y, mode); _setPixel(x, y, mode);
setPixelCore(x1 - 1, y, mode); _setPixel(x1 - 1, y, mode);
} }
y++; y++;
} }

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,30 @@
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.heart, Draw.Double)
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)
}) })
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! ")
}) })