Skip to content

Commit cd6e018

Browse files
committed
Bangle.js2: Much faster rendering of overlays
1 parent 0204e8e commit cd6e018

File tree

4 files changed

+50
-39
lines changed

4 files changed

+50
-39
lines changed

ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
Swap to using locks for watch&timer arrays (uses 4b more RAM, but faster and no defrag issues)
1616
Bangle.js2: Remove buzz when going back in a menu using the button (not the widget)
1717
Bangle.js2: Ensure overlays don't use the current Graphics colors
18+
Bangle.js2: Much faster rendering of overlays
1819

1920
2v28 : Add `E.internal` as a way to access the 'hidden root' containing Espruino internal variables that previously needed `global["\xff"]`
2021
Bangle.js: Fix back handler not removed when using E.setUI with a back button but without widgets (#2636)

libs/graphics/jswrap_graphics.c

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,30 @@ NO_INLINE void _jswrap_drawImageLayerNextY(GfxDrawImageLayer *l) {
405405
}
406406
}
407407

408+
// Called by _jswrap_drawImageSimple to blit out a row
409+
NO_INLINE void _jswrap_drawImageSimpleRow(JsGraphics *gfx, int xPos, int y, GfxDrawImageInfo *img, JsvStringIterator *it, JsGraphicsSetPixelFn setPixel, int *_bits, uint32_t *_colData) {
410+
int bits = *_bits;
411+
uint32_t colData = *_colData;
412+
for (int x=xPos;x<xPos+img->width;x++) {
413+
// Get the data we need...
414+
while (bits < img->bpp) {
415+
colData = (colData<<8) | ((unsigned char)jsvStringIteratorGetUTF8CharAndNext(it));
416+
bits += 8;
417+
}
418+
// extract just the bits we want
419+
unsigned int col = (colData>>(bits-img->bpp))&img->bitMask;
420+
bits -= img->bpp;
421+
// Try and write pixel!
422+
if (img->transparentCol!=col) {
423+
if (img->palettePtr) col = img->palettePtr[col&img->paletteMask];
424+
setPixel(gfx, x, y, col);
425+
}
426+
}
427+
*_bits = bits;
428+
*_colData = colData;
429+
}
430+
431+
408432
/* Draw an image 1:1 at xPos,yPos. If parseFullImage=true we ensure
409433
we leave the StringIterator pointing right at the end of the image. If not
410434
we can optimise if the image is clipped/offscreen. */
@@ -438,21 +462,7 @@ NO_INLINE void _jswrap_drawImageSimple(JsGraphics *gfx, int xPos, int yPos, GfxD
438462
#endif
439463
JsGraphicsSetPixelFn setPixel = graphicsGetSetPixelUnclippedFn(gfx, xPos, y1, xPos+img->width-1, y2, true);
440464
for (int y=y1;y<=y2;y++) {
441-
for (int x=xPos;x<xPos+img->width;x++) {
442-
// Get the data we need...
443-
while (bits < img->bpp) {
444-
colData = (colData<<8) | ((unsigned char)jsvStringIteratorGetUTF8CharAndNext(it));
445-
bits += 8;
446-
}
447-
// extract just the bits we want
448-
unsigned int col = (colData>>(bits-img->bpp))&img->bitMask;
449-
bits -= img->bpp;
450-
// Try and write pixel!
451-
if (img->transparentCol!=col) {
452-
if (img->palettePtr) col = img->palettePtr[col&img->paletteMask];
453-
setPixel(gfx, x, y, col);
454-
}
455-
}
465+
_jswrap_drawImageSimpleRow(gfx, xPos, y, img, it, setPixel, &bits, &colData);
456466
}
457467
#ifndef SAVE_ON_FLASH
458468
if (parseFullImage) {

libs/graphics/jswrap_graphics.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,6 @@ void _jswrap_drawImageLayerStartX(GfxDrawImageLayer *l);
153153
void _jswrap_drawImageLayerNextX(GfxDrawImageLayer *l);
154154
void _jswrap_drawImageLayerNextXRepeat(GfxDrawImageLayer *l);
155155
void _jswrap_drawImageLayerNextY(GfxDrawImageLayer *l);
156+
// Called by _jswrap_drawImageSimple to blit out a row
157+
void _jswrap_drawImageSimpleRow(JsGraphics *gfx, int xPos, int y, GfxDrawImageInfo *img, JsvStringIterator *it, JsGraphicsSetPixelFn setPixel, int *_bits, uint32_t *_colData);
156158
void _jswrap_drawImageSimple(JsGraphics *gfx, int xPos, int yPos, GfxDrawImageInfo *img, JsvStringIterator *it, bool parseFullImage);

libs/graphics/lcd_memlcd.c

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,10 @@ void lcdMemLCD_flip_spi_callback() {
277277
jshPinSetValue(LCD_SPI_CS, 0);
278278
lcdIsBusy = false;
279279
}
280+
// Mirror X - use when doing overlays when screen is rotated 180
281+
static void _lcdMemLCD_setPixel_mirrored(JsGraphics *gfx, int x, int y, unsigned int col) {
282+
lcdMemLCD_setPixel(gfx, (LCD_WIDTH-1)-x, y, col);
283+
}
280284
// send the data to the screen
281285
void lcdMemLCD_flip(JsGraphics *gfx) {
282286
if (gfx->data.modMinY > gfx->data.modMaxY) return; // nothing to do!
@@ -320,44 +324,38 @@ void lcdMemLCD_flip(JsGraphics *gfx) {
320324
// Take account of rotation - only check for a full 180 rotation - doing 90 is too hard
321325
bool isRotated180 = (graphicsInternal.data.flags & (JSGRAPHICSFLAGS_SWAP_XY | JSGRAPHICSFLAGS_INVERT_X | JSGRAPHICSFLAGS_INVERT_Y)) ==
322326
(JSGRAPHICSFLAGS_INVERT_X | JSGRAPHICSFLAGS_INVERT_Y);
323-
int ovY = isRotated180 ? (LCD_HEIGHT-(lcdOverlayY+overlayImg.height)) : lcdOverlayY;
324-
// initialise image layer
325-
GfxDrawImageLayer l;
326-
l.x1 = 0;
327-
l.y1 = ovY * 256;
328-
l.img = overlayImg;
329-
l.rotate = isRotated180 ? 3.141592 : 0;
330-
l.scale = 1;
331-
l.center = false;
332-
l.repeat = false;
333-
jsvStringIteratorNew(&l.it, l.img.buffer, (size_t)l.img.bitmapOffset);
334-
_jswrap_drawImageLayerInit(&l);
335-
_jswrap_drawImageLayerSetStart(&l, 0, y1);
336-
for (int y=y1;y<=y2;y++) {
327+
int ovY = lcdOverlayY;
328+
int yd = 1;
329+
JsGraphicsSetPixelFn setPixel = lcdMemLCD_setPixel;
330+
int bits = (ovY<y1) ? (ovY-y1)*overlayImg.bpp*overlayImg.width : 0;
331+
uint32_t colData;
332+
if (isRotated180) {
333+
yd = -1;
334+
int y = y1;
335+
y1 = y2;
336+
y2 = y-1;
337+
ovY = LCD_HEIGHT-(lcdOverlayY+overlayImg.height);
338+
setPixel = _lcdMemLCD_setPixel_mirrored;
339+
} else y2++;
340+
JsvStringIterator it;
341+
jsvStringIteratorNew(&it, overlayImg.buffer, (size_t)overlayImg.bitmapOffset);
342+
for (int y=y1;y!=y2;y+=yd) {
337343
int bufferLine = LCD_HEIGHT + (y&1); // alternate lines so we still get dither AND we can send while calculating next line
338344
unsigned char *buf = &lcdBuffer[LCD_STRIDE*bufferLine]; // point to line right on the end of gfx
339345
// copy original line in
340346
memcpy(buf, &lcdBuffer[LCD_STRIDE*y], LCD_STRIDE);
341347
// overwrite areas with overlay image
342348
if (y>=ovY && y<ovY+overlayImg.height) {
343-
_jswrap_drawImageLayerStartX(&l);
344-
for (int x=0;x<overlayImg.width;x++) {
345-
uint32_t c;
346-
int ox = x+lcdOverlayX;
347-
if (_jswrap_drawImageLayerGetPixel(&l, &c) && (ox < LCD_WIDTH) && (ox >= 0))
348-
lcdMemLCD_setPixel(NULL, ox, bufferLine, c);
349-
_jswrap_drawImageLayerNextX(&l);
350-
}
349+
_jswrap_drawImageSimpleRow(gfx, lcdOverlayX, bufferLine, &overlayImg, &it, lcdMemLCD_setPixel, &bits, &colData);
351350
}
352-
_jswrap_drawImageLayerNextY(&l);
353351
// send the line
354352
#ifdef EMULATED
355353
memcpy(&fakeLCDBuffer[LCD_STRIDE*y], buf, LCD_STRIDE);
356354
#else
357355
jshSPISendMany(LCD_SPI, buf, NULL, LCD_STRIDE, lcdMemLCD_flip_spi_ovr_callback);
358356
#endif
359357
}
360-
jsvStringIteratorFree(&l.it);
358+
jsvStringIteratorFree(&it);
361359
_jswrap_graphics_freeImageInfo(&overlayImg);
362360
// and 2 final bytes to finish the transfer
363361
#ifndef EMULATED

0 commit comments

Comments
 (0)