Skip to content

Commit bcda5e1

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

File tree

11 files changed

+359
-153
lines changed

11 files changed

+359
-153
lines changed

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy