roo_display
API Documentation for roo_display
Loading...
Searching...
No Matches
background_fill_optimizer.h
Go to the documentation of this file.
1#pragma once
2
3#include "roo_backport.h"
4#include "roo_backport/byte.h"
8
9namespace roo_display {
10
11// Display device filter which reduces the amount of redundant background
12// re-drawing, for a collection of up to 15 designated background colors.
13//
14// A natural, most efficient way to avoid needless redrawing of identically
15// colored pixel content is to use a frame buffer that 'remembers' the last
16// color written for each of the device pixel. With such framebuffer, redundant
17// redraws are completely eliminated - an attempt to write a pixel with the same
18// color as previously, are silently discarded. Unfortunately, such a frame
19// buffer requires significant RAM, which microcontrollers will usually not have
20// enough to spare. For example, to buffer the contents of an ILI9486 display,
21// we need 480 x 320 x 2 (width x height x bytes per pixel) = 300 KB of RAM.
22//
23// This device filter uses a compromise, capable of achieving a very significant
24// reduction in redundant redraws, at a fraction of the memory footprint.
25// Specifically, it focuses on use cases in which large areas are getting
26// rect-filled using colors from a small palette. This is common in GUIs, that
27// use solid backgrounds and some amount of solid area fills using a predefined
28// set of colors. The device maintains a reduced-resolution framebuffer, whose
29// each pixel corresponds to a rectangular area of the underlying device. Each
30// pixel of the framebuffer can take one of 16 values. Values 1-15 mean that
31// the corresponding rectangle in the underlying device is known to be entirely
32// filled with the specific color from a 15-color palette. Value 0 means that
33// this is not the case; i.e. that the corresponding rectangle either isn't
34// known to be mono-color, or that it contains pixels with colors outside of
35// that small palette. When 'Fill-rect' is called on this filter, using a color
36// from the pre-specified 15-color palette, the implementation omits writes
37// to the areas that are already known to be filled with the given color.
38//
39// With 4x resolution along both X and Y axes, i.e. when each pixel of the
40// framebuffer corresponds to a 4x4 rectangle, we achieve 64-fold reduction in
41// the RAM footprint. For example, the frame buffer for the aforementioned
42// ILI9486 display now only uses 4800 bytes.
43//
44// This filter is meant to be used directly on top of a physical device. In
45// particular, it expects the underlying display output to have zero offset.
46// That is, the (0, 0) nibble of the background mask corresponds to the output
47// rectangle (0, 0, kBgFillOptimizerWindowSize - 1, kBgFillOptimizerWindowSize -
48// 1).
49/// Display output filter that avoids redundant background fills.
51 public:
52 /// Size of the coarse background grid in pixels.
53 static constexpr const int kBlock = 4;
54
55 /// Minimum rectangle width for dynamic palette enrollment.
56 static constexpr const int kDynamicPaletteMinWidth = 8;
57
58 /// Minimum rectangle height for dynamic palette enrollment.
59 static constexpr const int kDynamicPaletteMinHeight = 8;
60
61 /// Backing buffer for the optimizer state.
63 public:
64 /// Return buffer size in bytes for a given device size.
65 ///
66 /// @param w Device width in pixels.
67 /// @param h Device height in pixels.
68 /// @return Required backing buffer size in bytes.
69 static constexpr size_t SizeForDimensions(int16_t w, int16_t h) {
70 return ((((w - 1) / kBlock + 1) + 1) / 2) *
71 ((((h - 1) / kBlock + 1) + 1) / 2) * 2;
72 }
73
74 /// Create a frame buffer with dynamic allocation.
75 ///
76 /// Buffer is zero-initialized. Call `setPalette()` before use.
77 FrameBuffer(int16_t width, int16_t height);
78
79 /// Create a frame buffer using caller-provided storage.
80 ///
81 /// The buffer is not cleared. It must be large enough for the device
82 /// dimensions (see `SizeForDimensions()`). Call `setPalette()` before use.
83 FrameBuffer(int16_t width, int16_t height, roo::byte* buffer);
84
85 /// Clear the entire background mask.
86 void invalidate();
87
88 /// Set whether the underlying display swaps x/y.
89 void setSwapXY(bool swap);
90
91 /// Clear the mask for the area covering `rect`.
92 void invalidateRect(const Box& rect);
93
94 /// Accessor for testing: retrieve the internal nibble mask.
95 const internal::NibbleRect& mask() const { return background_mask_; }
96
97 private:
100
101 FrameBuffer(int16_t width, int16_t height, roo::byte* buffer,
102 bool owns_buffer);
103
104 void prefilled(uint8_t idx_in_palette);
105
106 internal::NibbleRect background_mask_;
107 bool swap_xy_;
108 std::unique_ptr<roo::byte[]> owned_buffer_;
109 };
110
111 /// Create a background fill optimizer filter.
113
115
117
118 void setPalette(std::initializer_list<Color> palette);
119
120 void setAddress(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1,
121 BlendingMode mode) override;
122
123 void write(Color* color, uint32_t pixel_count) override;
124
125 void fill(Color color, uint32_t pixel_count) override;
126
127 void writeRects(BlendingMode mode, Color* color, int16_t* x0, int16_t* y0,
128 int16_t* x1, int16_t* y1, uint16_t count) override;
129
130 void fillRects(BlendingMode mode, Color color, int16_t* x0, int16_t* y0,
131 int16_t* x1, int16_t* y1, uint16_t count) override;
132
134 uint16_t pixel_count) override;
135
137 uint16_t pixel_count) override;
138
139 void drawDirectRect(const roo::byte* data, size_t row_width_bytes,
142
143 const ColorFormat& getColorFormat() const override {
144 return output_.getColorFormat();
145 }
146
147 private:
149
150 void setPrefilled(Color color);
151
152 uint8_t tryAddIdxInPaletteDynamic(Color color);
153
154 uint8_t tryAddIdxInPaletteOnSecondConsecutiveColor(Color color);
155
156 void resetPendingDynamicPaletteColor();
157
158 void updateMaskValue(int16_t x, int16_t y, uint8_t old_value,
160
161 void decrementPaletteUsage(uint8_t old_value);
162
163 void incrementPaletteUsage(uint8_t new_value, uint16_t count = 1);
164
165 void fillMaskRect(const Box& box, uint8_t new_value);
166
167 void resetMaskAndUsage(uint8_t mask_value);
168
169 void recountMaskUsage();
170
171 bool isReclaimablePaletteIdx(uint8_t idx) const;
172
173 void setWriteCursorOrd(uint32_t ord);
174
175 void advanceWriteCursor(uint32_t pixel_count);
176
177 uint32_t pixelsUntilStripeEnd() const;
178
179 void beginPassthroughIfNeeded();
180
181 void passthroughWrite(Color* color, uint32_t pixel_count);
182
183 void passthroughFill(Color color, uint32_t pixel_count);
184
185 void flushDeferredUniformRun();
186
187 void clearMaskForOrdRange(uint32_t start_ord, uint32_t pixel_count);
188
189 // Grid-aligned, block-based fast path (strict alignment required).
190 bool tryProcessGridAlignedBlockStripes(Color*& color, uint32_t& pixel_count);
191
192 void processAlignedFullStripeBlock(Color* color, int16_t bx, int16_t by,
194
195 void emitUniformScanRun(Color color, int16_t start_y, uint32_t count);
196
197 template <typename Filler>
198 void fillRectBg(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
200
201 DisplayOutput& output_;
202 internal::NibbleRect* background_mask_;
203 Color palette_[15];
204 uint8_t palette_size_;
205
206 Box address_window_;
207 uint32_t address_window_area_;
208 int16_t bx_min_;
209 int16_t bx_max_;
210 bool address_window_block_haligned_;
211 BlendingMode blending_mode_;
212 int16_t cursor_x_;
213 int16_t cursor_y_;
214 uint32_t cursor_ord_;
215 uint16_t palette_usage_count_[16];
216 uint8_t pinned_palette_size_;
217 bool palette_full_hint_;
218 bool has_pending_dynamic_palette_color_;
219 Color pending_dynamic_palette_color_;
220
221 enum class WriteScanState { kScan, kPassthrough };
222 WriteScanState write_scan_state_;
223 bool passthrough_address_set_;
224 bool scan_uniform_active_;
225 Color scan_uniform_color_;
226 int16_t scan_uniform_start_y_;
227 uint32_t scan_uniform_count_;
228};
229
230/// Display device wrapper that applies background fill optimization.
232 public:
233 /// Create a wrapper device.
235
236 /// Set the palette and optional prefilled color.
238 Color prefilled = color::Transparent);
239
240 /// Set palette using an initializer list.
241 void setPalette(std::initializer_list<Color> palette,
242 Color prefilled = color::Transparent);
243
244 void init() override { device_.init(); }
245
246 void orientationUpdated() override {
247 device_.orientationUpdated();
248 buffer_.setSwapXY(orientation().isXYswapped());
249 // Orientation swap reinterprets the same backing mask with transposed
250 // geometry, so rebuild usage counters from the current mask contents.
251 optimizer_.recountMaskUsage();
252 }
253
255 device_.setBgColorHint(bgcolor);
256 }
257
258 void begin() override { device_.begin(); }
259
260 void end() override { device_.end(); }
261
263 BlendingMode mode) override {
264 optimizer_.setAddress(x0, y0, x1, y1, mode);
265 }
266
267 void write(Color* color, uint32_t pixel_count) override {
268 optimizer_.write(color, pixel_count);
269 }
270
271 void fill(Color color, uint32_t pixel_count) override {
272 optimizer_.fill(color, pixel_count);
273 }
274
275 void writeRects(BlendingMode mode, Color* color, int16_t* x0, int16_t* y0,
276 int16_t* x1, int16_t* y1, uint16_t count) override {
277 optimizer_.writeRects(mode, color, x0, y0, x1, y1, count);
278 }
279
280 void fillRects(BlendingMode mode, Color color, int16_t* x0, int16_t* y0,
281 int16_t* x1, int16_t* y1, uint16_t count) override {
282 optimizer_.fillRects(mode, color, x0, y0, x1, y1, count);
283 }
284
285 void writePixels(BlendingMode mode, Color* color, int16_t* x, int16_t* y,
286 uint16_t pixel_count) override {
287 optimizer_.writePixels(mode, color, x, y, pixel_count);
288 }
289
290 void fillPixels(BlendingMode mode, Color color, int16_t* x, int16_t* y,
291 uint16_t pixel_count) override {
292 optimizer_.fillPixels(mode, color, x, y, pixel_count);
293 }
294
295 void drawDirectRect(const roo::byte* data, size_t row_width_bytes,
300 }
301
302 const ColorFormat& getColorFormat() const override {
303 return device_.getColorFormat();
304 }
305
306 /// Accessor for testing: retrieve the internal frame buffer.
307 const BackgroundFillOptimizer::FrameBuffer& buffer() const { return buffer_; }
308
309 private:
310 DisplayDevice& device_;
312 BackgroundFillOptimizer optimizer_;
313};
314
315} // namespace roo_display
Display device wrapper that applies background fill optimization.
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.
void begin() override
Enter a write transaction.
const ColorFormat & getColorFormat() const override
Return the native color format used by this device for direct drawing.
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 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.
void setPalette(const Color *palette, uint8_t palette_size, Color prefilled=color::Transparent)
Set the palette and optional prefilled color.
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.
const BackgroundFillOptimizer::FrameBuffer & buffer() const
Accessor for testing: retrieve the internal frame buffer.
void end() override
Finalize the previously entered write transaction, flushing any pending writes.
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 orientationUpdated() override
Invoked when orientation() is updated.
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().
void write(Color *color, uint32_t pixel_count) override
Write pixels into the current address window.
virtual void setBgColorHint(Color bgcolor)
Provide a background color hint for source-over blending.
void init() override
Initialize the display driver.
void fill(Color color, uint32_t pixel_count) override
Write pixel_count copies of the same color into the current address window.
static constexpr size_t SizeForDimensions(int16_t w, int16_t h)
Return buffer size in bytes for a given device size.
void setSwapXY(bool swap)
Set whether the underlying display swaps x/y.
const internal::NibbleRect & mask() const
Accessor for testing: retrieve the internal nibble mask.
void invalidateRect(const Box &rect)
Clear the mask for the area covering rect.
Display output filter that avoids redundant background fills.
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.
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.
Definition box.h:12
ARGB8888 color stored as a 32-bit unsigned integer.
Definition color.h:16
Base class for display device drivers.
Definition device.h:223
virtual void init()
Initialize the display driver.
Definition device.h:232
Orientation orientation() const
Return the current orientation of the display.
Definition device.h:250
virtual void orientationUpdated()
Invoked when orientation() is updated.
Definition device.h:245
virtual void setBgColorHint(Color bgcolor)
Provide a background color hint for source-over blending.
Definition device.h:280
The abstraction for drawing to a display.
Definition device.h:15
virtual void end()
Finalize the previously entered write transaction, flushing any pending writes.
Definition device.h:32
virtual const ColorFormat & getColorFormat() const =0
Return the native color format used by this device for direct drawing.
virtual void begin()
Enter a write transaction.
Definition device.h:25
Defines 140 opaque HTML named colors.
BlendingMode
Porter-Duff style blending modes.
Definition blending.h:17
const Palette * palette
Definition png.cpp:30
Color bgcolor
Definition smooth.cpp:889