Skip to content

Commit bcda5e1

Browse files
authored
Merge pull request adafruit#1903 from tannewt/get_area
Rework the pixel computation to use areas
2 parents 63b253c + 5d0791c commit bcda5e1

11 files changed

Lines changed: 359 additions & 153 deletions

File tree

ports/atmel-samd/Makefile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ endif
9999

100100
#Debugging/Optimization
101101
ifeq ($(DEBUG), 1)
102-
# Turn on Python modules useful for debugging (e.g. uheap, ustack).
103102
CFLAGS += -ggdb
104103
# You may want to disable -flto if it interferes with debugging.
105104
CFLAGS += -flto

shared-bindings/displayio/Display.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ void common_hal_displayio_display_refresh_soon(displayio_display_obj_t* self);
5353
bool displayio_display_begin_transaction(displayio_display_obj_t* self);
5454
void displayio_display_end_transaction(displayio_display_obj_t* self);
5555

56+
// The second point of the region is exclusive.
5657
void displayio_display_set_region_to_update(displayio_display_obj_t* self, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
5758
bool displayio_display_frame_queued(displayio_display_obj_t* self);
5859

shared-module/displayio/Group.c

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -134,32 +134,30 @@ void displayio_group_construct(displayio_group_t* self, displayio_group_child_t*
134134
self->scale = scale;
135135
}
136136

137-
bool displayio_group_get_pixel(displayio_group_t *self, int16_t x, int16_t y, uint16_t* pixel) {
138-
x -= self->x;
139-
y -= self->y;
140-
// When we are scaled we need to substract all but one to ensure -scale to 0 divide down to -1.
141-
// Normally -scale to scale both divide down to 0 because 0 is unsigned.
142-
if (x < 0) {
143-
x -= self->scale - 1;
144-
}
145-
if (y < 0) {
146-
y -= self->scale - 1;
147-
}
148-
x /= self->scale;
149-
y /= self->scale;
137+
bool displayio_group_get_area(displayio_group_t *self, displayio_buffer_transform_t* transform, displayio_area_t* area, uint32_t* mask, uint32_t* buffer) {
138+
displayio_area_shift(area, -self->x * transform->scale, -self->y * transform->scale);
139+
transform->scale *= self->scale;
140+
141+
// Track if any of the layers finishes filling in the given area. We can ignore any remaining
142+
// layers at that point.
143+
bool full_coverage = false;
150144
for (int32_t i = self->size - 1; i >= 0 ; i--) {
151145
mp_obj_t layer = self->children[i].native;
152146
if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) {
153-
if (displayio_tilegrid_get_pixel(layer, x, y, pixel)) {
154-
return true;
147+
if (displayio_tilegrid_get_area(layer, transform, area, mask, buffer)) {
148+
full_coverage = true;
149+
break;
155150
}
156151
} else if (MP_OBJ_IS_TYPE(layer, &displayio_group_type)) {
157-
if (displayio_group_get_pixel(layer, x, y, pixel)) {
158-
return true;
152+
if (displayio_group_get_area(layer, transform, area, mask, buffer)) {
153+
full_coverage = true;
154+
break;
159155
}
160156
}
161157
}
162-
return false;
158+
transform->scale /= self->scale;
159+
displayio_area_shift(area, self->x * transform->scale, self->y * transform->scale);
160+
return full_coverage;
163161
}
164162

165163
bool displayio_group_needs_refresh(displayio_group_t *self) {

shared-module/displayio/Group.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <stdint.h>
3232

3333
#include "py/obj.h"
34+
#include "shared-module/displayio/area.h"
3435

3536
typedef struct {
3637
mp_obj_t native;
@@ -49,7 +50,7 @@ typedef struct {
4950
} displayio_group_t;
5051

5152
void displayio_group_construct(displayio_group_t* self, displayio_group_child_t* child_array, uint32_t max_size, uint32_t scale, mp_int_t x, mp_int_t y);
52-
bool displayio_group_get_pixel(displayio_group_t *group, int16_t x, int16_t y, uint16_t *pixel);
53+
bool displayio_group_get_area(displayio_group_t *group, displayio_buffer_transform_t* transform, displayio_area_t* area, uint32_t* mask, uint32_t *buffer);
5354
bool displayio_group_needs_refresh(displayio_group_t *self);
5455
void displayio_group_finish_refresh(displayio_group_t *self);
5556

shared-module/displayio/TileGrid.c

Lines changed: 118 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -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

7070
mp_int_t common_hal_displayio_tilegrid_get_x(displayio_tilegrid_t *self) {
71-
return self->x;
71+
return self->area.x1;
7272
}
7373
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;
7680
}
7781
mp_int_t common_hal_displayio_tilegrid_get_y(displayio_tilegrid_t *self) {
78-
return self->y;
82+
return self->area.y1;
7983
}
8084

8185
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;
8492
}
8593

8694
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
128136
void 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

172255
bool displayio_tilegrid_needs_refresh(displayio_tilegrid_t *self) {

shared-module/displayio/TileGrid.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,16 @@
3131
#include <stdint.h>
3232

3333
#include "py/obj.h"
34+
#include "shared-module/displayio/area.h"
3435

3536
typedef struct {
3637
mp_obj_base_t base;
3738
mp_obj_t bitmap;
3839
mp_obj_t pixel_shader;
39-
uint16_t x;
40-
uint16_t y;
40+
displayio_area_t area;
4141
uint16_t bitmap_width_in_tiles;
4242
uint16_t width_in_tiles;
4343
uint16_t height_in_tiles;
44-
uint16_t total_width;
45-
uint16_t total_height;
4644
uint16_t tile_width;
4745
uint16_t tile_height;
4846
uint16_t top_left_x;
@@ -52,7 +50,7 @@ typedef struct {
5250
bool inline_tiles;
5351
} displayio_tilegrid_t;
5452

55-
bool displayio_tilegrid_get_pixel(displayio_tilegrid_t *self, int16_t x, int16_t y, uint16_t *pixel);
53+
bool displayio_tilegrid_get_area(displayio_tilegrid_t *self, displayio_buffer_transform_t* transform, displayio_area_t* area, uint32_t* mask, uint32_t *buffer);
5654
bool displayio_tilegrid_needs_refresh(displayio_tilegrid_t *self);
5755
void displayio_tilegrid_finish_refresh(displayio_tilegrid_t *self);
5856

0 commit comments

Comments
 (0)