@@ -56,31 +56,39 @@ void common_hal_displayio_tilegrid_construct(displayio_tilegrid_t *self, mp_obj_
56
56
self -> bitmap_width_in_tiles = bitmap_width_in_tiles ;
57
57
self -> width_in_tiles = width ;
58
58
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 ;
61
63
self -> tile_width = tile_width ;
62
64
self -> tile_height = tile_height ;
63
65
self -> bitmap = bitmap ;
64
66
self -> pixel_shader = pixel_shader ;
65
- self -> x = x ;
66
- self -> y = y ;
67
67
}
68
68
69
69
70
70
mp_int_t common_hal_displayio_tilegrid_get_x (displayio_tilegrid_t * self ) {
71
- return self -> x ;
71
+ return self -> area . x1 ;
72
72
}
73
73
void 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 ;
76
80
}
77
81
mp_int_t common_hal_displayio_tilegrid_get_y (displayio_tilegrid_t * self ) {
78
- return self -> y ;
82
+ return self -> area . y1 ;
79
83
}
80
84
81
85
void 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 ;
84
92
}
85
93
86
94
mp_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
128
136
void common_hal_displayio_tilegrid_set_top_left (displayio_tilegrid_t * self , uint16_t x , uint16_t y ) {
129
137
self -> top_left_x = x ;
130
138
self -> top_left_y = y ;
139
+ self -> needs_refresh = true;
131
140
}
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
- }
138
141
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.
139
144
uint8_t * tiles = self -> tiles ;
140
145
if (self -> inline_tiles ) {
141
146
tiles = (uint8_t * ) & self -> tiles ;
142
147
}
143
148
if (tiles == NULL ) {
144
149
return false;
145
150
}
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 ;
150
151
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;
158
161
}
159
162
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 ;
167
177
}
168
178
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 ;
170
253
}
171
254
172
255
bool displayio_tilegrid_needs_refresh (displayio_tilegrid_t * self ) {
0 commit comments