@@ -56,31 +56,39 @@ void common_hal_displayio_tilegrid_construct(displayio_tilegrid_t *self, mp_obj_
5656 self -> bitmap_width_in_tiles = bitmap_width_in_tiles ;
5757 self -> width_in_tiles = width ;
5858 self -> height_in_tiles = height ;
59- self -> total_width = width * tile_width ;
60- self -> total_height = height * tile_height ;
59+ self -> area .x1 = x ;
60+ self -> area .y1 = y ;
61+ self -> area .x2 = x + width * tile_width ;
62+ self -> area .y2 = y + height * tile_height ;
6163 self -> tile_width = tile_width ;
6264 self -> tile_height = tile_height ;
6365 self -> bitmap = bitmap ;
6466 self -> pixel_shader = pixel_shader ;
65- self -> x = x ;
66- self -> y = y ;
6767}
6868
6969
7070mp_int_t common_hal_displayio_tilegrid_get_x (displayio_tilegrid_t * self ) {
71- return self -> x ;
71+ return self -> area . x1 ;
7272}
7373void common_hal_displayio_tilegrid_set_x (displayio_tilegrid_t * self , mp_int_t x ) {
74- self -> needs_refresh = self -> x != x ;
75- self -> x = x ;
74+ if (self -> area .x1 == x ) {
75+ return ;
76+ }
77+ self -> needs_refresh = true;
78+ self -> area .x2 += (self -> area .x1 - x );
79+ self -> area .x1 = x ;
7680}
7781mp_int_t common_hal_displayio_tilegrid_get_y (displayio_tilegrid_t * self ) {
78- return self -> y ;
82+ return self -> area . y1 ;
7983}
8084
8185void common_hal_displayio_tilegrid_set_y (displayio_tilegrid_t * self , mp_int_t y ) {
82- self -> needs_refresh = self -> y != y ;
83- self -> y = y ;
86+ if (self -> area .y1 == y ) {
87+ return ;
88+ }
89+ self -> needs_refresh = true;
90+ self -> area .y2 += (self -> area .y1 - y );
91+ self -> area .y1 = y ;
8492}
8593
8694mp_obj_t common_hal_displayio_tilegrid_get_pixel_shader (displayio_tilegrid_t * self ) {
@@ -128,45 +136,120 @@ void common_hal_displayio_tilegrid_set_tile(displayio_tilegrid_t *self, uint16_t
128136void common_hal_displayio_tilegrid_set_top_left (displayio_tilegrid_t * self , uint16_t x , uint16_t y ) {
129137 self -> top_left_x = x ;
130138 self -> top_left_y = y ;
139+ self -> needs_refresh = true;
131140}
132- bool displayio_tilegrid_get_pixel (displayio_tilegrid_t * self , int16_t x , int16_t y , uint16_t * pixel ) {
133- x -= self -> x ;
134- y -= self -> y ;
135- if (y < 0 || y >= self -> total_height || x >= self -> total_width || x < 0 ) {
136- return false;
137- }
138141
142+ bool displayio_tilegrid_get_area (displayio_tilegrid_t * self , displayio_buffer_transform_t * transform , displayio_area_t * area , uint32_t * mask , uint32_t * buffer ) {
143+ // If no tiles are present we have no impact.
139144 uint8_t * tiles = self -> tiles ;
140145 if (self -> inline_tiles ) {
141146 tiles = (uint8_t * ) & self -> tiles ;
142147 }
143148 if (tiles == NULL ) {
144149 return false;
145150 }
146- uint16_t tile_location = ((y / self -> tile_height + self -> top_left_y ) % self -> height_in_tiles ) * self -> width_in_tiles + (x / self -> tile_width + self -> top_left_x ) % self -> width_in_tiles ;
147- uint8_t tile = tiles [tile_location ];
148- uint16_t tile_x = tile_x = (tile % self -> bitmap_width_in_tiles ) * self -> tile_width + x % self -> tile_width ;
149- uint16_t tile_y = tile_y = (tile / self -> bitmap_width_in_tiles ) * self -> tile_height + y % self -> tile_height ;
150151
151- uint32_t value = 0 ;
152- if (MP_OBJ_IS_TYPE (self -> bitmap , & displayio_bitmap_type )) {
153- value = common_hal_displayio_bitmap_get_pixel (self -> bitmap , tile_x , tile_y );
154- } else if (MP_OBJ_IS_TYPE (self -> bitmap , & displayio_shape_type )) {
155- value = common_hal_displayio_shape_get_pixel (self -> bitmap , tile_x , tile_y );
156- } else if (MP_OBJ_IS_TYPE (self -> bitmap , & displayio_ondiskbitmap_type )) {
157- value = common_hal_displayio_ondiskbitmap_get_pixel (self -> bitmap , tile_x , tile_y );
152+ displayio_area_t overlap ;
153+ displayio_area_t scaled_area = {
154+ .x1 = self -> area .x1 * transform -> scale ,
155+ .y1 = self -> area .y1 * transform -> scale ,
156+ .x2 = self -> area .x2 * transform -> scale ,
157+ .y2 = self -> area .y2 * transform -> scale
158+ };
159+ if (!displayio_area_compute_overlap (area , & scaled_area , & overlap )) {
160+ return false;
158161 }
159162
160- if (self -> pixel_shader == mp_const_none ) {
161- * pixel = value ;
162- return true;
163- } else if (MP_OBJ_IS_TYPE (self -> pixel_shader , & displayio_palette_type ) && displayio_palette_get_color (self -> pixel_shader , value , pixel )) {
164- return true;
165- } else if (MP_OBJ_IS_TYPE (self -> pixel_shader , & displayio_colorconverter_type ) && common_hal_displayio_colorconverter_convert (self -> pixel_shader , value , pixel )) {
166- return true;
163+ int16_t x_stride = 1 ;
164+ int16_t y_stride = displayio_area_width (area );
165+ if (transform -> transpose_xy ) {
166+ x_stride = displayio_area_height (area );
167+ y_stride = 1 ;
168+ }
169+ uint16_t start = 0 ;
170+ if (transform -> mirror_x ) {
171+ start += (area -> x2 - area -> x1 - 1 ) * x_stride ;
172+ x_stride *= -1 ;
173+ }
174+ if (transform -> mirror_y ) {
175+ start += (area -> y2 - area -> y1 - 1 ) * y_stride ;
176+ y_stride *= -1 ;
167177 }
168178
169- return false;
179+ // Track if this layer finishes filling in the given area. We can ignore any remaining
180+ // layers at that point.
181+ bool full_coverage = displayio_area_equal (area , & overlap );
182+
183+ // TODO(tannewt): Skip coverage tracking if all pixels outside the overlap have already been
184+ // set and our palette is all opaque.
185+
186+ // TODO(tannewt): Check to see if the pixel_shader has any transparency. If it doesn't then we
187+ // can either return full coverage or bulk update the mask.
188+ int16_t y = overlap .y1 - scaled_area .y1 ;
189+ if (y < 0 ) {
190+ y = 0 ;
191+ }
192+ int16_t x_shift = area -> x1 - scaled_area .x1 ;
193+ int16_t y_shift = area -> y1 - scaled_area .y1 ;
194+ for (; y < overlap .y2 - scaled_area .y1 ; y ++ ) {
195+ int16_t x = overlap .x1 - scaled_area .x1 ;
196+ if (x < 0 ) {
197+ x = 0 ;
198+ }
199+ int16_t row_start = start + (y - y_shift ) * y_stride ;
200+ int16_t local_y = y / transform -> scale ;
201+ for (; x < overlap .x2 - scaled_area .x1 ; x ++ ) {
202+ // Compute the destination pixel in the buffer and mask based on the transformations.
203+ uint16_t offset = row_start + (x - x_shift ) * x_stride ;
204+
205+ // This is super useful for debugging out range accesses. Uncomment to use.
206+ // if (offset < 0 || offset >= displayio_area_size(area)) {
207+ // asm("bkpt");
208+ // }
209+
210+ // Check the mask first to see if the pixel has already been set.
211+ if ((mask [offset / 32 ] & (1 << (offset % 32 ))) != 0 ) {
212+ continue ;
213+ }
214+ int16_t local_x = x / transform -> scale ;
215+ uint16_t tile_location = ((local_y / self -> tile_height + self -> top_left_y ) % self -> height_in_tiles ) * self -> width_in_tiles + (local_x / self -> tile_width + self -> top_left_x ) % self -> width_in_tiles ;
216+ uint8_t tile = tiles [tile_location ];
217+ uint16_t tile_x = (tile % self -> bitmap_width_in_tiles ) * self -> tile_width + local_x % self -> tile_width ;
218+ uint16_t tile_y = (tile / self -> bitmap_width_in_tiles ) * self -> tile_height + local_y % self -> tile_height ;
219+
220+ uint32_t value = 0 ;
221+ // We always want to read bitmap pixels by row first and then transpose into the destination
222+ // buffer because most bitmaps are row associated.
223+ if (MP_OBJ_IS_TYPE (self -> bitmap , & displayio_bitmap_type )) {
224+ value = common_hal_displayio_bitmap_get_pixel (self -> bitmap , tile_x , tile_y );
225+ } else if (MP_OBJ_IS_TYPE (self -> bitmap , & displayio_shape_type )) {
226+ value = common_hal_displayio_shape_get_pixel (self -> bitmap , tile_x , tile_y );
227+ } else if (MP_OBJ_IS_TYPE (self -> bitmap , & displayio_ondiskbitmap_type )) {
228+ value = common_hal_displayio_ondiskbitmap_get_pixel (self -> bitmap , tile_x , tile_y );
229+ }
230+
231+ uint16_t * pixel = ((uint16_t * ) buffer ) + offset ;
232+ if (self -> pixel_shader == mp_const_none ) {
233+ * pixel = value ;
234+ return true;
235+ } else if (MP_OBJ_IS_TYPE (self -> pixel_shader , & displayio_palette_type )) {
236+ if (!displayio_palette_get_color (self -> pixel_shader , value , pixel )) {
237+ // A pixel is transparent so we haven't fully covered the area ourselves.
238+ full_coverage = false;
239+ } else {
240+ mask [offset / 32 ] |= 1 << (offset % 32 );
241+ }
242+ } else if (MP_OBJ_IS_TYPE (self -> pixel_shader , & displayio_colorconverter_type )) {
243+ if (!common_hal_displayio_colorconverter_convert (self -> pixel_shader , value , pixel )) {
244+ // A pixel is transparent so we haven't fully covered the area ourselves.
245+ full_coverage = false;
246+ } else {
247+ mask [offset / 32 ] |= 1 << (offset % 32 );
248+ }
249+ }
250+ }
251+ }
252+ return full_coverage ;
170253}
171254
172255bool displayio_tilegrid_needs_refresh (displayio_tilegrid_t * self ) {
0 commit comments