diff --git a/shared-module/displayio/ColorConverter.c b/shared-module/displayio/ColorConverter.c index d4e64ee286e..2ee5f6cf0f9 100644 --- a/shared-module/displayio/ColorConverter.c +++ b/shared-module/displayio/ColorConverter.c @@ -9,8 +9,6 @@ #include "py/misc.h" #include "py/runtime.h" -#define NO_TRANSPARENT_COLOR (0x1000000) - uint32_t displayio_colorconverter_dither_noise_1(uint32_t n) { n = (n >> 13) ^ n; int nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff; diff --git a/shared-module/displayio/ColorConverter.h b/shared-module/displayio/ColorConverter.h index 304004d566a..8cab1f4b2b4 100644 --- a/shared-module/displayio/ColorConverter.h +++ b/shared-module/displayio/ColorConverter.h @@ -12,6 +12,9 @@ #include "py/obj.h" #include "shared-module/displayio/Palette.h" +// Stored in transparent_color when no color is transparent. It is outside the 24 bit color range. +#define NO_TRANSPARENT_COLOR (0x1000000) + typedef struct displayio_colorconverter { mp_obj_base_t base; bool dither; diff --git a/shared-module/displayio/TileGrid.c b/shared-module/displayio/TileGrid.c index 88eb0f694fa..c70c9327a5e 100644 --- a/shared-module/displayio/TileGrid.c +++ b/shared-module/displayio/TileGrid.c @@ -425,17 +425,25 @@ void common_hal_displayio_tilegrid_set_top_left(displayio_tilegrid_t *self, uint self->full_change = true; } +// Any transparent color above this value can never match a 16 bit pixel. +#define MAX_16BIT_PIXEL_VALUE (0xffff) + // One row of the common case: an 8 or 16 bpp Bitmap or a mapped 8 bpp OnDiskBitmap, a Palette -// without dithering, a 16 bpp display, no scaling, no x flip and no transpose. Same result as the -// per-pixel loop in displayio_tilegrid_fill_area, without the per-pixel calls. Returns false if a -// pixel was transparent. +// without dithering, a 16 bpp display, no scaling, no x flip and no transpose. Also a 16 bpp +// Bitmap with an RGB565 ColorConverter without dithering, whose pixels reach a 16 bpp display +// unchanged apart from byte order. Same result as the per-pixel loop in +// displayio_tilegrid_fill_area, without the per-pixel calls. Returns false if a pixel was +// transparent. static bool _fill_row_fast(displayio_tilegrid_t *self, const void *tiles, displayio_bitmap_t *bitmap, displayio_ondiskbitmap_t *odb, displayio_palette_t *palette, + displayio_colorconverter_t *converter, const _displayio_colorspace_t *colorspace, uint32_t *mask, uint16_t *buffer, int32_t offset, uint16_t x, uint16_t y, int16_t count, int16_t x_in_tile, uint16_t x_tile_index, uint16_t row_tile_location, int16_t y_in_tile) { bool covered = true; uint16_t tile_width = self->tile_width; + bool swap = converter != NULL && + (converter->input_colorspace == DISPLAYIO_COLORSPACE_RGB565_SWAPPED) != colorspace->reverse_bytes_in_word; while (count > 0) { uint16_t tile_location = row_tile_location + x_tile_index; // uint8_t like displayio_input_pixel_t.tile, which the per-pixel loop goes through. @@ -466,36 +474,78 @@ static bool _fill_row_fast(displayio_tilegrid_t *self, const void *tiles, row = bitmap->data + by * bitmap->stride; } bool row_inside = by >= 0 && by < bitmap_height; - for (int16_t i = 0; i < run; i++, offset++, bx++, x++) { - uint32_t bit = 1u << (offset & 31); - if (mask[offset >> 5] & bit) { - continue; - } - uint32_t index = 0; // get_pixel returns 0 outside the bitmap - if (row_inside && bx >= 0 && bx < bitmap_width) { - index = (bits_per_value == 8) ? - ((const uint8_t *)row)[bx] : ((const uint16_t *)row)[bx]; - } - if (index >= palette->color_count || palette->colors[index].transparent) { - covered = false; - continue; + if (converter != NULL) { + // A separate loop, so the Palette loop below stays as fast as before. With no + // transparent color, copy 32 pixels at a time where none of them is set yet. + bool blocks = converter->transparent_color > MAX_16BIT_PIXEL_VALUE && + row_inside && bx >= 0 && bx + run <= bitmap_width; + for (int16_t i = 0; i < run; i++, offset++, bx++) { + while (blocks && (offset & 31) == 0 && run - i >= 32 && mask[offset >> 5] == 0) { + const uint16_t *in = (const uint16_t *)row + bx; + uint16_t *out = buffer + offset; + if (swap) { + for (int k = 0; k < 32; k++) { + out[k] = __builtin_bswap16(in[k]); + } + } else { + memcpy(out, in, 32 * sizeof(uint16_t)); + } + mask[offset >> 5] = 0xffffffff; + i += 32; + offset += 32; + bx += 32; + } + if (i >= run) { + break; + } + uint32_t bit = 1u << (offset & 31); + if (mask[offset >> 5] & bit) { + continue; + } + uint32_t pixel = 0; // get_pixel returns 0 outside the bitmap + if (row_inside && bx >= 0 && bx < bitmap_width) { + pixel = ((const uint16_t *)row)[bx]; + } + if (pixel == converter->transparent_color) { + covered = false; + continue; + } + mask[offset >> 5] |= bit; + buffer[offset] = swap ? __builtin_bswap16(pixel) : pixel; } - const _displayio_color_t *color = &palette->colors[index]; - uint16_t pixel; - if (color->cached_colorspace == colorspace && - color->cached_colorspace_grayscale_bit == colorspace->grayscale_bit && - color->cached_colorspace_grayscale == colorspace->grayscale) { - pixel = color->cached_color; - } else { - displayio_input_pixel_t input_pixel = { - .pixel = index, .x = x, .y = y, .tile = tile, .tile_x = bx, .tile_y = by, - }; - displayio_output_pixel_t output_pixel = { .pixel = 0, .opaque = true }; - displayio_palette_get_color(palette, colorspace, &input_pixel, &output_pixel); - pixel = output_pixel.pixel; + x += run; + } else { + for (int16_t i = 0; i < run; i++, offset++, bx++, x++) { + uint32_t bit = 1u << (offset & 31); + if (mask[offset >> 5] & bit) { + continue; + } + uint32_t index = 0; // get_pixel returns 0 outside the bitmap + if (row_inside && bx >= 0 && bx < bitmap_width) { + index = (bits_per_value == 8) ? + ((const uint8_t *)row)[bx] : ((const uint16_t *)row)[bx]; + } + if (index >= palette->color_count || palette->colors[index].transparent) { + covered = false; + continue; + } + const _displayio_color_t *color = &palette->colors[index]; + uint16_t pixel; + if (color->cached_colorspace == colorspace && + color->cached_colorspace_grayscale_bit == colorspace->grayscale_bit && + color->cached_colorspace_grayscale == colorspace->grayscale) { + pixel = color->cached_color; + } else { + displayio_input_pixel_t input_pixel = { + .pixel = index, .x = x, .y = y, .tile = tile, .tile_x = bx, .tile_y = by, + }; + displayio_output_pixel_t output_pixel = { .pixel = 0, .opaque = true }; + displayio_palette_get_color(palette, colorspace, &input_pixel, &output_pixel); + pixel = output_pixel.pixel; + } + mask[offset >> 5] |= bit; + buffer[offset] = pixel; } - mask[offset >> 5] |= bit; - buffer[offset] = pixel; } count -= run; x_in_tile = 0; @@ -617,15 +667,25 @@ bool displayio_tilegrid_fill_area(displayio_tilegrid_t *self, displayio_bitmap_t *row_bitmap = NULL; displayio_ondiskbitmap_t *row_odb = NULL; displayio_palette_t *row_palette = NULL; + displayio_colorconverter_t *row_converter = NULL; if (scale == 1 && x_stride == 1 && colorspace->depth == 16 && self->transpose_xy == self->absolute_transform->transpose_xy && - mp_obj_is_type(self->bitmap, &displayio_bitmap_type) && - mp_obj_is_type(self->pixel_shader, &displayio_palette_type)) { + mp_obj_is_type(self->bitmap, &displayio_bitmap_type)) { displayio_bitmap_t *bitmap = self->bitmap; - displayio_palette_t *palette = self->pixel_shader; - if ((bitmap->bits_per_value == 8 || bitmap->bits_per_value == 16) && !palette->dither) { - row_bitmap = bitmap; - row_palette = palette; + if (mp_obj_is_type(self->pixel_shader, &displayio_palette_type)) { + displayio_palette_t *palette = self->pixel_shader; + if ((bitmap->bits_per_value == 8 || bitmap->bits_per_value == 16) && !palette->dither) { + row_bitmap = bitmap; + row_palette = palette; + } + } else if (mp_obj_is_type(self->pixel_shader, &displayio_colorconverter_type)) { + displayio_colorconverter_t *converter = self->pixel_shader; + if (bitmap->bits_per_value == 16 && !converter->dither && + (converter->input_colorspace == DISPLAYIO_COLORSPACE_RGB565 || + converter->input_colorspace == DISPLAYIO_COLORSPACE_RGB565_SWAPPED)) { + row_bitmap = bitmap; + row_converter = converter; + } } } #if CIRCUITPY_STORAGE_MAP_FILE @@ -655,8 +715,8 @@ bool displayio_tilegrid_fill_area(displayio_tilegrid_t *self, int16_t tile_base_y = 0; if (row_bitmap != NULL || row_odb != NULL) { - if (!_fill_row_fast(self, tiles, row_bitmap, row_odb, row_palette, colorspace, mask, - (uint16_t *)buffer, row_start + x_shift, start_x, input_pixel.y, + if (!_fill_row_fast(self, tiles, row_bitmap, row_odb, row_palette, row_converter, + colorspace, mask, (uint16_t *)buffer, row_start + x_shift, start_x, input_pixel.y, end_x - start_x, x_in_tile, x_tile_index, row_tile_location, y_in_tile)) { full_coverage = false; }