11 void fillRect(int16_t x0, int16_t y0, int16_t x1, int16_t y1)
const {
16inline uint8_t getIdxInPalette(Color color,
const Color*
palette,
17 uint8_t palette_size) {
18 for (
int i = 0; i < palette_size; ++i) {
19 if (color ==
palette[i])
return i + 1;
40 : background_mask_(buffer, ((((width - 1) /
kBlock + 1) + 1) / 2),
41 ((((height - 1) /
kBlock + 1) + 1) / 2) * 2),
51 resetPendingDynamicPaletteColor();
59 pinned_palette_size_ =
palette.size();
60 resetPendingDynamicPaletteColor();
64void BackgroundFillOptimizer::setPrefilled(
Color color) {
67 idx = tryAddIdxInPaletteDynamic(color);
69 resetMaskAndUsage(idx);
76 Box(0, 0, background_mask_.
width() - 1, background_mask_.
height() - 1),
87 if (
swap == swap_xy_)
return;
90 background_mask_.
height() / 2,
91 background_mask_.
width());
98 background_mask_(&buffer.background_mask_),
100 address_window_(0, 0, 0, 0),
104 pinned_palette_size_(0),
105 palette_full_hint_(
false),
106 has_pending_dynamic_palette_color_(
false),
107 pending_dynamic_palette_color_(
color::Transparent),
108 write_scan_state_(WriteScanState::kScan),
109 passthrough_address_set_(
false),
110 scan_uniform_active_(
false),
111 scan_uniform_color_(
color::Transparent),
112 scan_uniform_start_y_(0),
113 scan_uniform_count_(0) {
114 resetMaskAndUsage(0);
121bool BackgroundFillOptimizer::isReclaimablePaletteIdx(
uint8_t idx)
const {
122 return idx > pinned_palette_size_ &&
idx <= palette_size_;
125void BackgroundFillOptimizer::recountMaskUsage() {
126 for (
int i = 0; i < 16; ++i) palette_usage_count_[i] = 0;
127 const int16_t w = background_mask_->
width();
128 const int16_t h = background_mask_->
height();
129 for (int16_t y = 0; y < h; ++y) {
130 for (int16_t x = 0; x < w; ++x) {
131 ++palette_usage_count_[background_mask_->
get(x, y)];
135 if (palette_size_ < 15) {
136 palette_full_hint_ =
false;
140 palette_full_hint_ =
true;
141 for (uint8_t idx = pinned_palette_size_ + 1; idx <= palette_size_; ++idx) {
142 if (palette_usage_count_[idx] == 0) {
143 palette_full_hint_ =
false;
149void BackgroundFillOptimizer::decrementPaletteUsage(uint8_t old_value) {
150 DCHECK_GT(palette_usage_count_[old_value], 0);
151 --palette_usage_count_[old_value];
152 if (palette_usage_count_[old_value] == 0 &&
153 isReclaimablePaletteIdx(old_value)) {
156 palette_full_hint_ =
false;
160void BackgroundFillOptimizer::incrementPaletteUsage(uint8_t new_value,
162 palette_usage_count_[new_value] += count;
165void BackgroundFillOptimizer::updateMaskValue(int16_t x, int16_t y,
168 DCHECK_NE(old_value, new_value);
169 decrementPaletteUsage(old_value);
170 incrementPaletteUsage(new_value);
171 background_mask_->
set(x, y, new_value);
174void BackgroundFillOptimizer::fillMaskRect(
const Box& box, uint8_t new_value) {
175 if (box.empty())
return;
176 const int16_t x0 = std::max<int16_t>(0, box.xMin());
177 const int16_t y0 = std::max<int16_t>(0, box.yMin());
179 std::min<int16_t>(background_mask_->
width() - 1, box.xMax());
181 std::min<int16_t>(background_mask_->
height() - 1, box.yMax());
182 if (x1 < x0 || y1 < y0)
return;
184 for (int16_t y = y0; y <= y1; ++y) {
185 for (int16_t x = x0; x <= x1; ++x) {
186 decrementPaletteUsage(background_mask_->
get(x, y));
190 background_mask_->
fillRect(Box(x0, y0, x1, y1), new_value);
191 incrementPaletteUsage(new_value,
192 static_cast<uint16_t
>((x1 - x0 + 1) * (y1 - y0 + 1)));
195void BackgroundFillOptimizer::resetMaskAndUsage(uint8_t mask_value) {
197 Box(0, 0, background_mask_->
width() - 1, background_mask_->
height() - 1),
199 for (
int i = 0; i < 16; ++i) palette_usage_count_[i] = 0;
200 const uint16_t cell_count =
static_cast<uint16_t
>(background_mask_->
width() *
201 background_mask_->
height());
202 palette_usage_count_[mask_value] = cell_count;
203 palette_full_hint_ =
false;
206uint8_t BackgroundFillOptimizer::tryAddIdxInPaletteDynamic(Color color) {
207 if (palette_size_ < 15) {
208 uint8_t reclaim_idx = palette_size_;
209 while (reclaim_idx > pinned_palette_size_ &&
210 palette_usage_count_[reclaim_idx] == 0) {
213 const bool append = (reclaim_idx == palette_size_);
222 palette_[reclaim_idx] =
color;
223 palette_full_hint_ =
false;
224 return reclaim_idx + 1;
227 if (palette_full_hint_)
return 0;
229 for (uint8_t idx = pinned_palette_size_ + 1; idx <= palette_size_; ++idx) {
230 if (palette_usage_count_[idx] == 0) {
231 palette_[idx - 1] =
color;
236 palette_full_hint_ =
true;
240uint8_t BackgroundFillOptimizer::tryAddIdxInPaletteOnSecondConsecutiveColor(
242 if (has_pending_dynamic_palette_color_ &&
243 pending_dynamic_palette_color_ == color) {
244 has_pending_dynamic_palette_color_ =
false;
245 return tryAddIdxInPaletteDynamic(color);
247 pending_dynamic_palette_color_ =
color;
248 has_pending_dynamic_palette_color_ =
true;
252void BackgroundFillOptimizer::resetPendingDynamicPaletteColor() {
253 has_pending_dynamic_palette_color_ =
false;
258 flushDeferredUniformRun();
259 address_window_ =
Box(x0, y0, x1, y1);
260 address_window_area_ = address_window_.
area();
263 address_window_block_haligned_ =
265 blending_mode_ = mode;
269 write_scan_state_ = WriteScanState::kScan;
270 passthrough_address_set_ =
false;
271 scan_uniform_active_ =
false;
272 scan_uniform_count_ = 0;
273 resetPendingDynamicPaletteColor();
276void BackgroundFillOptimizer::setWriteCursorOrd(
uint32_t ord) {
285void BackgroundFillOptimizer::advanceWriteCursor(uint32_t pixel_count) {
286 setWriteCursorOrd(cursor_ord_ + pixel_count);
289uint32_t BackgroundFillOptimizer::pixelsUntilStripeEnd()
const {
290 if (cursor_ord_ >= address_window_area_)
return 0;
291 const int16_t aw_width = address_window_.
width();
292 const int16_t aw_y0 = address_window_.
yMin();
293 const int16_t aw_y1 = address_window_.
yMax();
294 const int16_t aw_x1 = address_window_.
xMax();
296 const int16_t y = cursor_y_;
297 const int16_t mod =
static_cast<int16_t
>((y + 1) %
kBlock);
298 const int16_t rows_to_aligned_end = (mod == 0) ? 0 : (
kBlock - mod);
299 const int16_t stripe_end_y =
300 std::min<int16_t>(aw_y1, y + rows_to_aligned_end);
302 const uint32_t stripe_end_ord =
303 static_cast<uint32_t
>(stripe_end_y - aw_y0) *
304 static_cast<uint32_t
>(aw_width) +
305 static_cast<uint32_t
>(aw_x1 - address_window_.
xMin());
306 DCHECK_GE(stripe_end_ord, cursor_ord_);
307 return stripe_end_ord - cursor_ord_ + 1;
310void BackgroundFillOptimizer::beginPassthroughIfNeeded() {
311 if (passthrough_address_set_)
return;
313 address_window_.
yMax(), blending_mode_);
314 passthrough_address_set_ =
true;
317void BackgroundFillOptimizer::clearMaskForOrdRange(uint32_t start_ord,
318 uint32_t pixel_count) {
319 if (pixel_count == 0)
return;
320 const int16_t aw_width = address_window_.
width();
321 const int16_t aw_x0 = address_window_.
xMin();
322 const int16_t aw_y0 = address_window_.
yMin();
323 const uint32_t end_ord = start_ord + pixel_count - 1;
325 const int16_t start_y = aw_y0 +
static_cast<int16_t
>(start_ord / aw_width);
326 const int16_t end_y = aw_y0 +
static_cast<int16_t
>(end_ord / aw_width);
327 const int16_t start_x = aw_x0 +
static_cast<int16_t
>(start_ord % aw_width);
328 const int16_t end_x = aw_x0 +
static_cast<int16_t
>(end_ord % aw_width);
330 const int16_t start_by = start_y /
kBlock;
331 const int16_t start_bx = start_x /
kBlock;
332 const int16_t end_bx = end_x /
kBlock;
348 if (start_y == end_y) {
349 fillMaskRect(Box(start_bx, start_by, end_bx, start_by), 0);
353 const int16_t end_by = end_y /
kBlock;
355 int16_t full_by0 = start_by;
356 int16_t full_by1 = end_by;
360 if (((start_y + 1) %
kBlock) == 0) {
361 fillMaskRect(Box(start_bx, start_by, bx_max_, start_by), 0);
367 if ((end_y %
kBlock) == 0) {
368 fillMaskRect(Box(bx_min_, end_by, end_bx, end_by), 0);
372 if (full_by0 <= full_by1) {
373 fillMaskRect(Box(bx_min_, full_by0, bx_max_, full_by1), 0);
377void BackgroundFillOptimizer::passthroughWrite(Color* color,
378 uint32_t pixel_count) {
379 if (pixel_count == 0)
return;
380 beginPassthroughIfNeeded();
381 clearMaskForOrdRange(cursor_ord_, pixel_count);
382 output_.
write(color, pixel_count);
383 advanceWriteCursor(pixel_count);
386void BackgroundFillOptimizer::passthroughFill(Color color,
387 uint32_t pixel_count) {
388 if (pixel_count == 0)
return;
389 beginPassthroughIfNeeded();
390 clearMaskForOrdRange(cursor_ord_, pixel_count);
391 output_.
fill(color, pixel_count);
392 advanceWriteCursor(pixel_count);
395void BackgroundFillOptimizer::flushDeferredUniformRun() {
396 if (!scan_uniform_active_ || scan_uniform_count_ == 0)
return;
398 const int16_t aw_width = address_window_.
width();
399 cursor_ord_ = (scan_uniform_start_y_ - address_window_.
yMin()) * aw_width;
400 cursor_x_ = address_window_.
xMin();
401 cursor_y_ = scan_uniform_start_y_;
403 write_scan_state_ = WriteScanState::kPassthrough;
404 passthrough_address_set_ =
false;
405 passthroughFill(scan_uniform_color_, scan_uniform_count_);
406 scan_uniform_active_ =
false;
407 scan_uniform_count_ = 0;
410void BackgroundFillOptimizer::processAlignedFullStripeBlock(Color* colors,
415 bool all_same =
true;
416 Color first_color = *colors;
418 Color* row_base = colors;
419 for (int16_t dy = 0; dy <
kBlock && all_same; ++dy) {
420 for (int16_t dx = 0; dx <
kBlock; ++dx) {
421 if (row_base[dx] != first_color) {
426 row_base += aw_width;
430 const uint8_t current_mask_value = background_mask_->
get(bx, by);
433 resetPendingDynamicPaletteColor();
434 if (current_mask_value != 0) {
435 updateMaskValue(bx, by, current_mask_value, 0);
442 Color* row_base = colors;
443 for (int16_t dy = 0; dy <
kBlock; ++dy) {
445 row_base += aw_width;
452 uint8_t palette_idx = getIdxInPalette(first_color, palette_, palette_size_);
453 if (palette_idx == 0) {
454 palette_idx = tryAddIdxInPaletteOnSecondConsecutiveColor(first_color);
456 resetPendingDynamicPaletteColor();
459 if (palette_idx > 0 && current_mask_value == palette_idx) {
466 if (palette_idx != current_mask_value) {
467 updateMaskValue(bx, by, current_mask_value, palette_idx);
471bool BackgroundFillOptimizer::tryProcessGridAlignedBlockStripes(
472 Color*& color, uint32_t& pixel_count) {
473 if (!address_window_block_haligned_)
return false;
474 if (cursor_x_ != address_window_.
xMin())
return false;
475 if ((cursor_y_ %
kBlock) != 0)
return false;
476 const int16_t aw_width = address_window_.
width();
478 const uint32_t stripe_pixels =
static_cast<uint32_t
>(aw_width) *
kBlock;
479 bool consumed_any =
false;
480 while (pixel_count >= stripe_pixels &&
481 cursor_y_ +
kBlock - 1 <= address_window_.
yMax()) {
482 const int16_t by = cursor_y_ /
kBlock;
483 Color* block_start =
color;
484 for (int16_t block = bx_min_; block <= bx_max_; ++block) {
485 processAlignedFullStripeBlock(block_start, block, by, aw_width);
488 color += stripe_pixels;
489 pixel_count -= stripe_pixels;
490 cursor_ord_ += stripe_pixels;
493 scan_uniform_active_ =
false;
494 scan_uniform_count_ = 0;
499void BackgroundFillOptimizer::emitUniformScanRun(Color color, int16_t start_y,
501 if (count == 0)
return;
502 const int16_t aw_width = address_window_.
width();
503 DCHECK_EQ(0u, count % aw_width);
505 const int16_t y0 = start_y;
506 const int16_t y1 = y0 +
static_cast<int16_t
>(count / aw_width) - 1;
508 uint8_t palette_idx = getIdxInPalette(color, palette_, palette_size_);
509 const int16_t rect_h = y1 - y0 + 1;
512 palette_idx = tryAddIdxInPaletteDynamic(color);
518 BufferedRectWriter
writer(output_, blending_mode_);
519 if (palette_idx != 0) {
520 RectFillWriter adapter{.color =
color, .writer =
writer};
521 fillRectBg(address_window_.
xMin(), y0, address_window_.
xMax(), y1, adapter,
524 fillMaskRect(Box(bx_min_, y0 /
kBlock, bx_max_, y1 /
kBlock), 0);
525 writer.writeRect(address_window_.
xMin(), y0, address_window_.
xMax(), y1,
531 while (
pixel_count > 0 && !(cursor_ord_ >= address_window_area_)) {
532 if (write_scan_state_ == WriteScanState::kScan) {
544 if (!scan_uniform_active_) {
545 scan_uniform_active_ =
true;
546 scan_uniform_color_ =
color[0];
547 scan_uniform_start_y_ = cursor_y_;
548 scan_uniform_count_ = 0;
558 flushDeferredUniformRun();
559 write_scan_state_ = WriteScanState::kPassthrough;
569 emitUniformScanRun(scan_uniform_color_, scan_uniform_start_y_,
570 scan_uniform_count_);
571 scan_uniform_active_ =
false;
572 scan_uniform_count_ = 0;
586 write_scan_state_ = WriteScanState::kScan;
587 passthrough_address_set_ =
false;
588 scan_uniform_active_ =
false;
589 scan_uniform_count_ = 0;
595 while (
pixel_count > 0 && !(cursor_ord_ >= address_window_area_)) {
596 if (write_scan_state_ == WriteScanState::kScan) {
597 if (scan_uniform_active_ && scan_uniform_count_ > 0 &&
598 scan_uniform_color_ !=
color) {
599 flushDeferredUniformRun();
608 if (!scan_uniform_active_) {
609 scan_uniform_active_ =
true;
610 scan_uniform_color_ =
color;
611 scan_uniform_start_y_ = cursor_y_;
612 scan_uniform_count_ = 0;
620 emitUniformScanRun(scan_uniform_color_, scan_uniform_start_y_,
621 scan_uniform_count_);
622 scan_uniform_active_ =
false;
623 scan_uniform_count_ = 0;
636 write_scan_state_ = WriteScanState::kScan;
637 passthrough_address_set_ =
false;
638 scan_uniform_active_ =
false;
639 scan_uniform_count_ = 0;
647 flushDeferredUniformRun();
649 while (count-- > 0) {
686 flushDeferredUniformRun();
691 for (
int i = 0;
i < count; ++
i) {
722 flushDeferredUniformRun();
756 flushDeferredUniformRun();
802 flushDeferredUniformRun();
855 resetPendingDynamicPaletteColor();
858 resetPendingDynamicPaletteColor();
901template <
typename Filler>
923 fillMaskRect(
Box(
xstart, y, x - 1, y), 0);
925 if (x0 > xstart *
kBlock) {
926 const uint8_t old_value = background_mask_->
get(xstart, y);
927 if (old_value != 0) {
928 updateMaskValue(xstart, y, old_value, 0);
931 if (x1 < x *
kBlock - 1) {
932 const uint8_t old_value = background_mask_->
get(x - 1, y);
933 if (old_value != 0) {
934 updateMaskValue(x - 1, y, old_value, 0);
939 filler.fillRect(box.xMin(), box.yMin(), box.xMax(), box.yMax());
956 if (!inner_filter_box.empty()) {
957 fillMaskRect(inner_filter_box, palette_idx);
965 buffer_(device_.raw_width(), device_.raw_height()),
966 optimizer_(device_, buffer_) {
974 optimizer_.setPrefilled(prefilled);
978 std::initializer_list<Color>
palette,
Color prefilled) {
BufferedRectWriter & writer
BackgroundFillOptimizerDevice(DisplayDevice &device)
Create a wrapper device.
void setPalette(const Color *palette, uint8_t palette_size, Color prefilled=color::Transparent)
Set the palette and optional prefilled color.
Backing buffer for the optimizer state.
void invalidate()
Clear the entire background mask.
void setSwapXY(bool swap)
Set whether the underlying display swaps x/y.
void invalidateRect(const Box &rect)
Clear the mask for the area covering rect.
FrameBuffer(int16_t width, int16_t height)
Create a frame buffer with dynamic allocation.
void setAddress(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, BlendingMode mode) override
Set a rectangular window filled by subsequent calls to write().
static constexpr const int kDynamicPaletteMinWidth
Minimum rectangle width for dynamic palette enrollment.
void write(Color *color, uint32_t pixel_count) override
Write pixels into the current address window.
void drawDirectRect(const roo::byte *data, size_t row_width_bytes, int16_t src_x0, int16_t src_y0, int16_t src_x1, int16_t src_y1, int16_t dst_x0, int16_t dst_y0) override
Draw a rectangle represented in the device's native color format.
static constexpr const int kBlock
Size of the coarse background grid in pixels.
void writePixels(BlendingMode mode, Color *color, int16_t *x, int16_t *y, uint16_t pixel_count) override
Draw the specified pixels (per-pixel colors). Invalidates the address window.
void fillRects(BlendingMode mode, Color color, int16_t *x0, int16_t *y0, int16_t *x1, int16_t *y1, uint16_t count) override
Draw the specified rectangles using the same color. Invalidates the address window.
void fillPixels(BlendingMode mode, Color color, int16_t *x, int16_t *y, uint16_t pixel_count) override
Draw the specified pixels using the same color. Invalidates the address window.
void writeRects(BlendingMode mode, Color *color, int16_t *x0, int16_t *y0, int16_t *x1, int16_t *y1, uint16_t count) override
Draw the specified rectangles (per-rectangle colors). Invalidates the address window.
static constexpr const int kDynamicPaletteMinHeight
Minimum rectangle height for dynamic palette enrollment.
void fill(Color color, uint32_t pixel_count) override
Write pixel_count copies of the same color into the current address window.
BackgroundFillOptimizer(DisplayOutput &output, FrameBuffer &frame_buffer)
Create a background fill optimizer filter.
const ColorFormat & getColorFormat() const override
Return the native color format used by this device for direct drawing.
void setPalette(const Color *palette, uint8_t palette_size)
Axis-aligned integer rectangle.
int16_t width() const
Width in pixels (inclusive coordinates).
int16_t xMin() const
Minimum x (inclusive).
int16_t xMax() const
Maximum x (inclusive).
int16_t yMax() const
Maximum y (inclusive).
int32_t area() const
Area in pixels.
int16_t yMin() const
Minimum y (inclusive).
ARGB8888 color stored as a 32-bit unsigned integer.
Base class for display device drivers.
Orientation orientation() const
Return the current orientation of the display.
void setOrientation(Orientation orientation)
Set the orientation of the display.
The abstraction for drawing to a display.
virtual void write(Color *color, uint32_t pixel_count)=0
Write pixels into the current address window.
void fillRect(BlendingMode blending_mode, const Box &rect, Color color)
Fill a single rectangle. Invalidates the address window.
virtual void drawDirectRect(const roo::byte *data, size_t row_width_bytes, int16_t src_x0, int16_t src_y0, int16_t src_x1, int16_t src_y1, int16_t dst_x0, int16_t dst_y0)
Draw a rectangle represented in the device's native color format.
virtual void fill(Color color, uint32_t pixel_count)
Write pixel_count copies of the same color into the current address window.
virtual void fillPixels(BlendingMode blending_mode, Color color, int16_t *x, int16_t *y, uint16_t pixel_count)=0
Draw the specified pixels using the same color. Invalidates the address window.
virtual void writePixels(BlendingMode blending_mode, Color *color, int16_t *x, int16_t *y, uint16_t pixel_count)=0
Draw the specified pixels (per-pixel colors). Invalidates the address window.
void setAddress(const Box &bounds, BlendingMode blending_mode)
Convenience overload for setAddress() using a Box.
void fillRect(const Box &rect, uint8_t value)
uint8_t get(int16_t x, int16_t y) const
void set(int16_t x, int16_t y, uint8_t value)
Defines 140 opaque HTML named colors.
BlendingMode
Porter-Duff style blending modes.