@@ -56,31 +56,40 @@ 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+ // -1 because areas are inclusive
62+ self -> area .x2 = x + width * tile_width - 1 ;
63+ self -> area .y2 = y + height * tile_height - 1 ;
6164 self -> tile_width = tile_width ;
6265 self -> tile_height = tile_height ;
6366 self -> bitmap = bitmap ;
6467 self -> pixel_shader = pixel_shader ;
65- self -> x = x ;
66- self -> y = y ;
6768}
6869
6970
7071mp_int_t common_hal_displayio_tilegrid_get_x (displayio_tilegrid_t * self ) {
71- return self -> x ;
72+ return self -> area . x1 ;
7273}
7374void 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 ;
75+ if (self -> area .x1 == x ) {
76+ return ;
77+ }
78+ self -> needs_refresh = true;
79+ self -> area .x2 += (self -> area .x1 - x );
80+ self -> area .x1 = x ;
7681}
7782mp_int_t common_hal_displayio_tilegrid_get_y (displayio_tilegrid_t * self ) {
78- return self -> y ;
83+ return self -> area . y1 ;
7984}
8085
8186void 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 ;
87+ if (self -> area .y1 == y ) {
88+ return ;
89+ }
90+ self -> needs_refresh = true;
91+ self -> area .y2 += (self -> area .y1 - y );
92+ self -> area .y1 = y ;
8493}
8594
8695mp_obj_t common_hal_displayio_tilegrid_get_pixel_shader (displayio_tilegrid_t * self ) {
@@ -128,45 +137,115 @@ void common_hal_displayio_tilegrid_set_tile(displayio_tilegrid_t *self, uint16_t
128137void common_hal_displayio_tilegrid_set_top_left (displayio_tilegrid_t * self , uint16_t x , uint16_t y ) {
129138 self -> top_left_x = x ;
130139 self -> top_left_y = y ;
140+ self -> needs_refresh = true;
131141}
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- }
138142
143+ bool displayio_tilegrid_get_area (displayio_tilegrid_t * self , displayio_buffer_transform_t * transform , displayio_area_t * area , uint32_t * mask , uint32_t * buffer ) {
144+ // If no tiles are present we have no impact.
139145 uint8_t * tiles = self -> tiles ;
140146 if (self -> inline_tiles ) {
141147 tiles = (uint8_t * ) & self -> tiles ;
142148 }
143149 if (tiles == NULL ) {
144150 return false;
145151 }
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 ;
150152
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 );
153+ displayio_area_t overlap ;
154+ displayio_area_t scaled_area = {
155+ .x1 = self -> area .x1 * transform -> scale ,
156+ .y1 = self -> area .y1 * transform -> scale ,
157+ .x2 = (self -> area .x2 + 1 ) * transform -> scale - 1 , // Second point is inclusive.
158+ .y2 = (self -> area .y2 + 1 ) * transform -> scale - 1
159+ };
160+ if (!displayio_area_compute_overlap (area , & scaled_area , & overlap )) {
161+ return false;
158162 }
159163
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;
164+ int16_t x_stride = 1 ;
165+ int16_t y_stride = displayio_area_width (area );
166+ if (transform -> transpose_xy ) {
167+ x_stride = displayio_area_height (area );
168+ y_stride = 1 ;
169+ }
170+ uint16_t start = 0 ;
171+ if (transform -> mirror_x ) {
172+ start += (area -> x2 - area -> x1 ) * x_stride ;
173+ x_stride *= -1 ;
174+ }
175+ if (transform -> mirror_y ) {
176+ start += (area -> y2 - area -> y1 ) * y_stride ;
177+ y_stride *= -1 ;
167178 }
168179
169- return false;
180+ bool full_coverage = displayio_area_equal (area , & overlap );
181+
182+ // TODO(tannewt): Set full coverage to true if all pixels outside the overlap have already been
183+ // set as well.
184+ bool always_full_coverage = false;
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+ // Check the mask first to see if the pixel has already been set.
206+ if ((mask [offset / 32 ] & (1 << (offset % 32 ))) != 0 ) {
207+ continue ;
208+ }
209+ int16_t local_x = x / transform -> scale ;
210+ 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 ;
211+ uint8_t tile = tiles [tile_location ];
212+ uint16_t tile_x = (tile % self -> bitmap_width_in_tiles ) * self -> tile_width + local_x % self -> tile_width ;
213+ uint16_t tile_y = (tile / self -> bitmap_width_in_tiles ) * self -> tile_height + local_y % self -> tile_height ;
214+
215+ uint32_t value = 0 ;
216+ // We always want to read bitmap pixels by row first and then transpose into the destination
217+ // buffer because most bitmaps are row associated.
218+ if (MP_OBJ_IS_TYPE (self -> bitmap , & displayio_bitmap_type )) {
219+ value = common_hal_displayio_bitmap_get_pixel (self -> bitmap , tile_x , tile_y );
220+ } else if (MP_OBJ_IS_TYPE (self -> bitmap , & displayio_shape_type )) {
221+ value = common_hal_displayio_shape_get_pixel (self -> bitmap , tile_x , tile_y );
222+ } else if (MP_OBJ_IS_TYPE (self -> bitmap , & displayio_ondiskbitmap_type )) {
223+ value = common_hal_displayio_ondiskbitmap_get_pixel (self -> bitmap , tile_x , tile_y );
224+ }
225+
226+ uint16_t * pixel = ((uint16_t * ) buffer ) + offset ;
227+ if (self -> pixel_shader == mp_const_none ) {
228+ * pixel = value ;
229+ return true;
230+ } else if (MP_OBJ_IS_TYPE (self -> pixel_shader , & displayio_palette_type )) {
231+ if (!displayio_palette_get_color (self -> pixel_shader , value , pixel )) {
232+ // mark the pixel as transparent
233+ full_coverage = false;
234+ } else if (!always_full_coverage ) {
235+ mask [offset / 32 ] |= 1 << (offset % 32 );
236+ }
237+ } else if (MP_OBJ_IS_TYPE (self -> pixel_shader , & displayio_colorconverter_type )) {
238+ if (!common_hal_displayio_colorconverter_convert (self -> pixel_shader , value , pixel )) {
239+ // mark the pixel as transparent
240+ full_coverage = false;
241+ } else if (!always_full_coverage ) {
242+ mask [offset / 32 ] |= 1 << (offset % 32 );
243+ }
244+ }
245+ }
246+ }
247+
248+ return full_coverage ;
170249}
171250
172251bool displayio_tilegrid_needs_refresh (displayio_tilegrid_t * self ) {
0 commit comments