Skip to content

Commit 944956e

Browse files
committed
displayio: composite common TileGrid rows without per-pixel calls
For the common case - an 8 or 16 bpp Bitmap, a Palette without dithering, a 16 bpp display, no scaling, no x flip and no transpose - fill_area now handles a row in one function: it reads the bitmap row directly and takes the colour from the palette cache. The per-pixel calls to get_pixel and palette_get_color, and the type checks, are gone. Everything else takes the existing loop. Full-screen repaint, before -> after (both commits): PicoPad (RP2040) 320x240 297 -> 90 ms PyBadge (SAMD51) 160x128 61 -> 30 ms Feather ESP32-S3 TFT 240x135 72 -> 53 ms Flash +0.8 to +0.9 KB depending on the port. Output is unchanged: 16 test scenes checksum identically via BusDisplay.fill_row on all three boards, including overlapping 8 bpp layers with transparent colours, a 16 bpp bitmap with indices past the end of its palette, and a TileGrid with more than 255 tiles.
1 parent e2d97af commit 944956e

1 file changed

Lines changed: 87 additions & 0 deletions

File tree

‎shared-module/displayio/TileGrid.c‎

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,70 @@ void common_hal_displayio_tilegrid_set_top_left(displayio_tilegrid_t *self, uint
425425
self->full_change = true;
426426
}
427427

428+
// One row of the common case: an 8 or 16 bpp Bitmap, a Palette without dithering, a 16 bpp
429+
// display, no scaling, no x flip and no transpose. Same result as the per-pixel loop in
430+
// displayio_tilegrid_fill_area, without the per-pixel calls. Returns false if a pixel was
431+
// transparent.
432+
static bool _fill_row_fast(displayio_tilegrid_t *self, const void *tiles,
433+
displayio_bitmap_t *bitmap, displayio_palette_t *palette,
434+
const _displayio_colorspace_t *colorspace, uint32_t *mask, uint16_t *buffer,
435+
int32_t offset, uint16_t x, uint16_t y, int16_t count, int16_t x_in_tile,
436+
uint16_t x_tile_index, uint16_t row_tile_location, int16_t y_in_tile) {
437+
bool covered = true;
438+
uint16_t tile_width = self->tile_width;
439+
while (count > 0) {
440+
uint16_t tile_location = row_tile_location + x_tile_index;
441+
// uint8_t like displayio_input_pixel_t.tile, which the per-pixel loop goes through.
442+
uint8_t tile = (self->tiles_in_bitmap > 255) ?
443+
((const uint16_t *)tiles)[tile_location] : ((const uint8_t *)tiles)[tile_location];
444+
int16_t bx = (tile % self->bitmap_width_in_tiles) * tile_width + x_in_tile;
445+
int16_t by = (tile / self->bitmap_width_in_tiles) * self->tile_height + y_in_tile;
446+
int16_t run = tile_width - x_in_tile;
447+
if (run > count) {
448+
run = count;
449+
}
450+
bool row_inside = by >= 0 && by < bitmap->height;
451+
const uint32_t *row = bitmap->data + by * bitmap->stride;
452+
for (int16_t i = 0; i < run; i++, offset++, bx++, x++) {
453+
uint32_t bit = 1u << (offset & 31);
454+
if (mask[offset >> 5] & bit) {
455+
continue;
456+
}
457+
uint32_t index = 0; // get_pixel returns 0 outside the bitmap
458+
if (row_inside && bx >= 0 && bx < bitmap->width) {
459+
index = (bitmap->bits_per_value == 8) ?
460+
((const uint8_t *)row)[bx] : ((const uint16_t *)row)[bx];
461+
}
462+
if (index > palette->color_count || palette->colors[index].transparent) {
463+
covered = false;
464+
continue;
465+
}
466+
const _displayio_color_t *color = &palette->colors[index];
467+
uint16_t pixel;
468+
if (color->cached_colorspace == colorspace &&
469+
color->cached_colorspace_grayscale_bit == colorspace->grayscale_bit &&
470+
color->cached_colorspace_grayscale == colorspace->grayscale) {
471+
pixel = color->cached_color;
472+
} else {
473+
displayio_input_pixel_t input_pixel = {
474+
.pixel = index, .x = x, .y = y, .tile = tile, .tile_x = bx, .tile_y = by,
475+
};
476+
displayio_output_pixel_t output_pixel = { .pixel = 0, .opaque = true };
477+
displayio_palette_get_color(palette, colorspace, &input_pixel, &output_pixel);
478+
pixel = output_pixel.pixel;
479+
}
480+
mask[offset >> 5] |= bit;
481+
buffer[offset] = pixel;
482+
}
483+
count -= run;
484+
x_in_tile = 0;
485+
if (++x_tile_index == self->width_in_tiles) {
486+
x_tile_index = 0;
487+
}
488+
}
489+
return covered;
490+
}
491+
428492
bool displayio_tilegrid_fill_area(displayio_tilegrid_t *self,
429493
const _displayio_colorspace_t *colorspace, const displayio_area_t *area,
430494
uint32_t *mask, uint32_t *buffer) {
@@ -519,6 +583,20 @@ bool displayio_tilegrid_fill_area(displayio_tilegrid_t *self,
519583
uint16_t tile_width = self->tile_width;
520584
uint16_t width_in_tiles = self->width_in_tiles;
521585

586+
displayio_bitmap_t *row_bitmap = NULL;
587+
displayio_palette_t *row_palette = NULL;
588+
if (scale == 1 && x_stride == 1 && colorspace->depth == 16 &&
589+
self->transpose_xy == self->absolute_transform->transpose_xy &&
590+
mp_obj_is_type(self->bitmap, &displayio_bitmap_type) &&
591+
mp_obj_is_type(self->pixel_shader, &displayio_palette_type)) {
592+
displayio_bitmap_t *bitmap = self->bitmap;
593+
displayio_palette_t *palette = self->pixel_shader;
594+
if ((bitmap->bits_per_value == 8 || bitmap->bits_per_value == 16) && !palette->dither) {
595+
row_bitmap = bitmap;
596+
row_palette = palette;
597+
}
598+
}
599+
522600
for (input_pixel.y = start_y; input_pixel.y < end_y; ++input_pixel.y) {
523601
int16_t row_start = start + (input_pixel.y - start_y + y_shift) * y_stride; // in pixels
524602
int16_t local_y = input_pixel.y / scale;
@@ -535,6 +613,15 @@ bool displayio_tilegrid_fill_area(displayio_tilegrid_t *self,
535613
int16_t tile_base_x = 0;
536614
int16_t tile_base_y = 0;
537615

616+
if (row_bitmap != NULL) {
617+
if (!_fill_row_fast(self, tiles, row_bitmap, row_palette, colorspace, mask,
618+
(uint16_t *)buffer, row_start + x_shift, start_x, input_pixel.y,
619+
end_x - start_x, x_in_tile, x_tile_index, row_tile_location, y_in_tile)) {
620+
full_coverage = false;
621+
}
622+
continue;
623+
}
624+
538625
for (input_pixel.x = start_x; input_pixel.x < end_x; ++input_pixel.x) {
539626
if (input_pixel.x != start_x && ++x_in_scale == scale) {
540627
x_in_scale = 0;

0 commit comments

Comments
 (0)