libepaper 2.0.0
A C++23 library for controlling Waveshare e-paper displays on Raspberry Pi, featuring transparent sleep/wake management and a fluent builder API.
Loading...
Searching...
No Matches
display.hpp
Go to the documentation of this file.
1#pragma once
2
15#include <cstddef>
16#include <expected>
17#include <span>
18#include <string_view>
19#include <vector>
20
21namespace epaper {
22
23// Forward declarations used in drawing
24struct LineCommand;
25struct RectangleCommand;
26struct CircleCommand;
27struct PointCommand;
28struct TextCommand;
29class LineBuilder;
30class RectangleBuilder;
31class CircleBuilder;
32class PointBuilder;
33class TextBuilder;
34class Font;
35
89template <Driver DriverT, FramebufferLike FramebufferT> class Display {
90public:
92 : driver_(std::move(driver)), framebuffer_(driver_.width(), driver_.height(), mode), orientation_(orientation),
93 auto_sleep_enabled_(auto_sleep), physical_width_(driver_.width()), physical_height_(driver_.height()),
94 display_mode_(mode) {}
95
96 // Non-copyable, movable
97 Display(const Display &) = delete;
98 auto operator=(const Display &) -> Display & = delete;
99 Display(Display &&) noexcept = default;
100 auto operator=(Display &&) noexcept -> Display & = default;
101 ~Display() = default;
102
103 // --- Core Functionality (formerly DisplayCore) ---
104
105 [[nodiscard]] auto width() const noexcept -> std::size_t {
106 return (orientation_ == Orientation::Portrait0 || orientation_ == Orientation::Portrait180) ? physical_width_
107 : physical_height_;
108 }
109
110 [[nodiscard]] auto height() const noexcept -> std::size_t {
111 return (orientation_ == Orientation::Portrait0 || orientation_ == Orientation::Portrait180) ? physical_height_
112 : physical_width_;
113 }
114
115 [[nodiscard]] auto effective_width() const noexcept -> std::size_t { return width(); }
116 [[nodiscard]] auto effective_height() const noexcept -> std::size_t { return height(); }
117 [[nodiscard]] auto mode() const noexcept -> DisplayMode { return display_mode_; }
118
119 [[nodiscard]] auto orientation() const noexcept -> Orientation { return orientation_; }
120 [[nodiscard]] auto auto_sleep_enabled() const noexcept -> bool { return auto_sleep_enabled_; }
121 auto set_auto_sleep(bool enabled) noexcept -> void { auto_sleep_enabled_ = enabled; }
122
123 [[nodiscard]] auto is_color() const noexcept -> bool { return epaper::is_color_mode(mode()); }
124 [[nodiscard]] auto get_num_planes() const noexcept -> std::size_t { return epaper::num_planes(mode()); }
125
126 [[nodiscard]] auto available_colors() const -> std::vector<Color> {
127 const auto m = mode();
128 if (!is_color()) {
129 return {Color::Black, Color::White};
130 }
131 if (m == DisplayMode::BWR) {
133 }
134 if (m == DisplayMode::BWY) {
136 }
137 if (m == DisplayMode::Spectra6) {
139 }
141 }
142
143 auto set_pixel(std::size_t x, std::size_t y, Color color) -> void {
144 framebuffer_.set_pixel(x, y, color, orientation_);
145 }
146
147 [[nodiscard]] auto get_pixel(std::size_t x, std::size_t y) const -> Color {
148 return framebuffer_.get_pixel(x, y, orientation_);
149 }
150
151 auto clear(Color color = Color::White) -> void { framebuffer_.clear(color); }
152
153 // --- Builder Factories ---
154 [[nodiscard]] auto line() -> LineBuilder { return {}; }
155 [[nodiscard]] auto rectangle() -> RectangleBuilder { return {}; }
156 [[nodiscard]] auto circle() -> CircleBuilder { return {}; }
157 [[nodiscard]] auto point() -> PointBuilder { return {}; }
158 [[nodiscard]] auto text(std::string_view content = "") -> TextBuilder { return TextBuilder(std::string(content)); }
159
160 // --- Drawing Commands ---
161 auto draw(const LineCommand &cmd) -> void {
162 Graphics::draw_line(framebuffer_, cmd.from, cmd.to, cmd.style, cmd.color, orientation_);
163 }
164
165 auto draw(const RectangleCommand &cmd) -> void {
166 Graphics::draw_rectangle(framebuffer_, cmd.top_left, cmd.bottom_right, LineStyle::Solid, cmd.color, cmd.fill,
167 orientation_);
168 }
169
170 auto draw(const CircleCommand &cmd) -> void {
171 Graphics::draw_circle(framebuffer_, cmd.center, cmd.radius, LineStyle::Solid, cmd.color, cmd.fill, orientation_);
172 }
173
174 auto draw(const PointCommand &cmd) -> void {
175 framebuffer_.set_pixel(cmd.position.x, cmd.position.y, cmd.color, orientation_);
176 }
177
178 auto draw(const TextCommand &cmd) -> void {
179 Graphics::draw_text(framebuffer_, cmd.position, cmd.text, *cmd.font, cmd.foreground, cmd.background, orientation_);
180 }
181
182 // --- Bitmap Drawing ---
183 auto draw_bitmap(std::size_t x, std::size_t y, std::span<const std::uint8_t> data, std::size_t w, std::size_t h,
184 std::size_t target_w = 0, std::size_t target_h = 0) -> void {
185 Graphics::draw_bitmap(framebuffer_, {x, y}, data, w, h, target_w, target_h, orientation_);
186 }
187
188 auto draw_bitmap(std::size_t x, std::size_t y, std::span<const Color> data, std::size_t w, std::size_t h,
189 std::size_t target_w = 0, std::size_t target_h = 0) -> void {
190 auto bytes = std::span<const std::uint8_t>(reinterpret_cast<const std::uint8_t *>(data.data()), data.size());
191 Graphics::draw_bitmap(framebuffer_, {x, y}, bytes, w, h, target_w, target_h, orientation_);
192 }
193
194 template <typename T>
195 auto draw_bitmap(std::size_t x, std::size_t y, const std::vector<T> &data, std::size_t w, std::size_t h,
196 std::size_t target_w = 0, std::size_t target_h = 0) -> void {
197 draw_bitmap(x, y, std::span(data), w, h, target_w, target_h);
198 }
199
200 [[nodiscard]] auto draw_bitmap_from_file(std::size_t x, std::size_t y, std::string_view file_path,
201 std::size_t target_width = 0, std::size_t target_height = 0)
202 -> std::expected<void, Error> {
203 auto image_res = ImageIO::load_image(file_path, 0);
204 if (!image_res) {
205 return std::unexpected(image_res.error());
206 }
207
208 const auto &img = *image_res;
209 std::size_t draw_w = (target_width > 0) ? target_width : img.width;
210 std::size_t draw_h = (target_height > 0) ? target_height : img.height;
211
212 for (std::size_t dy = 0; dy < draw_h; ++dy) {
213 for (std::size_t dx = 0; dx < draw_w; ++dx) {
214 std::size_t sx = dx * img.width / draw_w;
215 std::size_t sy = dy * img.height / draw_h;
216 std::size_t src_idx = (sy * img.width + sx) * img.channels;
217
218 std::uint8_t r = 0;
219 std::uint8_t g = 0;
220 std::uint8_t b = 0;
221 if (img.channels >= 3) {
222 r = img.data[src_idx];
223 g = img.data[src_idx + 1];
224 b = img.data[src_idx + 2];
225 } else if (img.channels == 1) {
226 r = g = b = img.data[src_idx];
227 }
228
229 // Simple color quantization for generic display
230 Color color = Color::White;
231 if (r < 128 && g < 128 && b < 128) {
232 color = Color::Black;
233 } else if (r > 200 && g < 100 && b < 100) {
234 color = Color::Red;
235 } else if (r > 200 && g > 200 && b < 100) {
236 color = Color::Yellow;
237 } else if (r < 100 && g < 100 && b > 200) {
238 color = Color::Blue;
239 } else if (r < 100 && g > 200 && b < 100) {
240 color = Color::Green;
241 }
242
243 set_pixel(x + dx, y + dy, color);
244 }
245 }
246 return {};
247 }
248
249 [[nodiscard]] auto save_framebuffer_to_png(std::string_view filename) const -> std::expected<void, Error> {
250 auto rgb_data = ImageIO::framebuffer_to_rgb(framebuffer_);
251 return ImageIO::save_png(filename, framebuffer_.width(), framebuffer_.height(), 3, rgb_data);
252 }
253
271 auto refresh() -> std::expected<void, Error> {
272 // Auto-Sleep State Machine:
273 // State 1: ASLEEP (low power) → wake() → State 2: AWAKE (active)
274 // State 2: AWAKE → display() → AWAKE (still active)
275 // State 2: AWAKE → sleep() → State 1: ASLEEP (low power)
276 //
277 // This implements automatic power management to minimize energy consumption
278 // on battery-powered devices. The display wakes only during refresh operations.
279
280 if (auto_sleep_enabled_) {
281 // Wake from sleep mode before display transfer
282 // Driver may be in deep sleep (VCOM off, high-impedance outputs)
283 // wake() re-initializes power rails and prepares for SPI communication
284 if (auto res = driver_.wake(); !res && res.error().code != ErrorCode::InvalidMode) {
285 // InvalidMode indicates driver doesn't support wake (always active)
286 // Other errors (GPIO failures, timeout) are fatal
287 return std::unexpected(res.error());
288 }
289 }
290
291 // Transfer framebuffer to display controller
292 // Multi-plane displays (BWR, BWY) require separate plane buffers
293 // Single-plane displays use contiguous framebuffer
294 if (get_num_planes() > 1) {
295 if (auto res = driver_.display_planes(get_planes()); !res) {
296 return std::unexpected(res.error());
297 }
298 } else {
299 if (auto res = driver_.display(get_buffer()); !res) {
300 return std::unexpected(res.error());
301 }
302 }
303
304 if (auto_sleep_enabled_) {
305 // Return to sleep mode after refresh completes
306 // sleep() powers down VCOM, enters deep sleep, reduces current draw
307 // Typical power: Active ~20mA, Sleep <1µA
308 if (auto res = driver_.sleep(); !res) {
309 return std::unexpected(res.error());
310 }
311 }
312 return {};
313 }
314
315 auto sleep() -> std::expected<void, Error> { return driver_.sleep(); }
316 auto wake() -> std::expected<void, Error> { return driver_.wake(); }
317 auto power_off() -> std::expected<void, Error> { return driver_.power_off(); }
318 auto power_on() -> std::expected<void, Error> { return driver_.power_on(); }
319
320 [[nodiscard]] auto supports_wake() const noexcept -> bool { return driver_.supports_wake(); }
321 [[nodiscard]] auto supports_power_control() const noexcept -> bool { return driver_.supports_power_control(); }
322
323 auto driver() -> DriverT & { return driver_; }
324 auto driver() const -> const DriverT & { return driver_; }
325
326 // Accessors mainly for testing or advanced usage
327 auto framebuffer() -> FramebufferT & { return framebuffer_; }
328 auto framebuffer() const -> const FramebufferT & { return framebuffer_; }
329
330private:
331 [[nodiscard]] auto get_planes() const -> std::vector<std::span<const std::byte>> { return framebuffer_.get_planes(); }
332
333 [[nodiscard]] auto get_buffer() const -> std::span<const std::byte> { return framebuffer_.data(); }
334
335 DriverT driver_;
336 FramebufferT framebuffer_;
337 Orientation orientation_;
338 bool auto_sleep_enabled_;
339 std::size_t physical_width_;
340 std::size_t physical_height_;
341 DisplayMode display_mode_;
342};
343
394template <typename DriverType>
395 requires Driver<DriverType> && DriverTraits<DriverType>
396[[nodiscard]] auto create_display(Device &device, DisplayMode mode, Orientation orientation = Orientation::Portrait0,
397 bool auto_sleep = true)
398 -> std::expected<Display<DriverType, MonoFramebuffer>, Error> {
399 return create_display<DriverType, MonoFramebuffer>(device, mode, orientation, auto_sleep);
400}
401
413template <typename DriverType, FramebufferLike FramebufferT>
414 requires Driver<DriverType> && DriverTraits<DriverType>
415[[nodiscard]] auto create_display(Device &device, DisplayMode mode, Orientation orientation = Orientation::Portrait0,
416 bool auto_sleep = true) -> std::expected<Display<DriverType, FramebufferT>, Error> {
417 // Validate capabilities
419 return std::unexpected(Error(ErrorCode::InvalidMode, "Grayscale not supported by this driver"));
420 }
421
423 return std::unexpected(Error(ErrorCode::InvalidMode, "Display mode exceeds driver capabilities"));
424 }
425
426 if (!FramebufferT::supports_mode(mode)) {
427 return std::unexpected(Error(ErrorCode::InvalidMode, "Display mode not supported by framebuffer"));
428 }
429
430 auto driver = DriverType(device);
431 if (auto result = driver.init(mode); !result) {
432 return std::unexpected(result.error());
433 }
434
435 return Display<DriverType, FramebufferT>(std::move(driver), mode, orientation, auto_sleep);
436}
437
438} // namespace epaper
439
440// Also include command builders and image I/O to maintain API compatibility
Fluent builder pattern for constructing drawing commands.
Compile-time driver capability traits system.
Definition builders.hpp:493
Definition device.hpp:158
Definition display.hpp:89
auto draw(const LineCommand &cmd) -> void
Definition display.hpp:161
auto text(std::string_view content="") -> TextBuilder
Definition display.hpp:158
auto set_auto_sleep(bool enabled) noexcept -> void
Definition display.hpp:121
Display(Display &&) noexcept=default
Display(DriverT driver, DisplayMode mode, Orientation orientation=Orientation::Portrait0, bool auto_sleep=true)
Definition display.hpp:91
Display(const Display &)=delete
auto circle() -> CircleBuilder
Definition display.hpp:156
auto draw(const CircleCommand &cmd) -> void
Definition display.hpp:170
auto power_off() -> std::expected< void, Error >
Definition display.hpp:317
auto draw(const RectangleCommand &cmd) -> void
Definition display.hpp:165
auto orientation() const noexcept -> Orientation
Definition display.hpp:119
auto supports_wake() const noexcept -> bool
Definition display.hpp:320
auto clear(Color color=Color::White) -> void
Definition display.hpp:151
auto draw_bitmap(std::size_t x, std::size_t y, std::span< const Color > data, std::size_t w, std::size_t h, std::size_t target_w=0, std::size_t target_h=0) -> void
Definition display.hpp:188
auto set_pixel(std::size_t x, std::size_t y, Color color) -> void
Definition display.hpp:143
auto power_on() -> std::expected< void, Error >
Definition display.hpp:318
auto get_num_planes() const noexcept -> std::size_t
Definition display.hpp:124
auto supports_power_control() const noexcept -> bool
Definition display.hpp:321
auto draw(const TextCommand &cmd) -> void
Definition display.hpp:178
auto is_color() const noexcept -> bool
Definition display.hpp:123
auto wake() -> std::expected< void, Error >
Definition display.hpp:316
auto draw_bitmap(std::size_t x, std::size_t y, std::span< const std::uint8_t > data, std::size_t w, std::size_t h, std::size_t target_w=0, std::size_t target_h=0) -> void
Definition display.hpp:183
auto save_framebuffer_to_png(std::string_view filename) const -> std::expected< void, Error >
Definition display.hpp:249
auto get_pixel(std::size_t x, std::size_t y) const -> Color
Definition display.hpp:147
auto auto_sleep_enabled() const noexcept -> bool
Definition display.hpp:120
auto height() const noexcept -> std::size_t
Definition display.hpp:110
auto driver() const -> const DriverT &
Definition display.hpp:324
auto available_colors() const -> std::vector< Color >
Definition display.hpp:126
auto effective_width() const noexcept -> std::size_t
Definition display.hpp:115
auto draw(const PointCommand &cmd) -> void
Definition display.hpp:174
auto framebuffer() -> FramebufferT &
Definition display.hpp:327
auto framebuffer() const -> const FramebufferT &
Definition display.hpp:328
auto rectangle() -> RectangleBuilder
Definition display.hpp:155
auto draw_bitmap_from_file(std::size_t x, std::size_t y, std::string_view file_path, std::size_t target_width=0, std::size_t target_height=0) -> std::expected< void, Error >
Definition display.hpp:200
auto point() -> PointBuilder
Definition display.hpp:157
auto effective_height() const noexcept -> std::size_t
Definition display.hpp:116
auto refresh() -> std::expected< void, Error >
Refresh the display by transferring framebuffer to hardware.
Definition display.hpp:271
auto sleep() -> std::expected< void, Error >
Definition display.hpp:315
auto width() const noexcept -> std::size_t
Definition display.hpp:105
auto mode() const noexcept -> DisplayMode
Definition display.hpp:117
auto operator=(const Display &) -> Display &=delete
auto driver() -> DriverT &
Definition display.hpp:323
auto line() -> LineBuilder
Definition display.hpp:154
auto draw_bitmap(std::size_t x, std::size_t y, const std::vector< T > &data, std::size_t w, std::size_t h, std::size_t target_w=0, std::size_t target_h=0) -> void
Definition display.hpp:195
static auto draw_line(FB &fb, Point start, Point end, LineStyle style, Color color, Orientation orientation) -> void
Draw a line on the framebuffer.
Definition graphics.hpp:171
static auto draw_circle(FB &fb, Point center, std::size_t radius, LineStyle style, Color color, DrawFill fill, Orientation orientation) -> void
Draw a circle on the framebuffer.
Definition graphics.hpp:265
static auto draw_rectangle(FB &fb, Point top_left, Point bottom_right, LineStyle style, Color color, DrawFill fill, Orientation orientation) -> void
Draw a rectangle on the framebuffer.
Definition graphics.hpp:226
static auto draw_bitmap(FB &fb, Point pos, std::span< const std::uint8_t > data, std::size_t w, std::size_t h, std::size_t target_w, std::size_t target_h, Orientation orientation) -> void
Draw a bitmap on the framebuffer.
Definition graphics.hpp:376
static auto draw_text(FB &fb, Point pos, std::string_view text, const Font &font, Color foreground, Color background, Orientation orientation) -> void
Draw text string on the framebuffer.
Definition graphics.hpp:328
static auto load_image(std::string_view path, int desired_channels=0) -> std::expected< ImageResult, Error >
Definition image_io.cpp:36
static auto save_png(std::string_view path, std::size_t width, std::size_t height, int channels, std::span< const std::uint8_t > data) -> std::expected< void, Error >
Definition image_io.cpp:82
static auto framebuffer_to_rgb(const FB &fb) -> std::vector< std::uint8_t >
Definition image_io.hpp:328
Definition builders.hpp:113
Definition builders.hpp:643
Definition builders.hpp:283
Definition builders.hpp:779
Immutable command structures for drawing operations.
C++20 concept defining the Driver interface contract.
Image loading/saving utilities using stb_image library.
Definition color.hpp:5
auto create_display(Device &device, DisplayMode mode, Orientation orientation=Orientation::Portrait0, bool auto_sleep=true) -> std::expected< Display< DriverType, MonoFramebuffer >, Error >
Convenience factory for mono-plane displays.
Definition display.hpp:396
constexpr auto num_planes(DisplayMode mode) noexcept -> std::size_t
Get number of color planes required for a display mode.
Definition driver.hpp:97
constexpr auto is_color_mode(DisplayMode mode) noexcept -> bool
Check if mode supports color (non-grayscale).
Definition driver.hpp:80
@ InvalidMode
Invalid display mode specified.
Orientation
Definition types.hpp:66
@ Portrait180
180° rotation (upside down portrait)
@ Portrait0
Default portrait orientation (0° rotation)
Color
Definition types.hpp:32
@ White
White (or lightest gray in grayscale modes)
@ Yellow
Yellow (BWY and Spectra6 modes)
@ Blue
Blue (Spectra6 mode only)
@ Green
Green (Spectra6 mode only)
@ Black
Black (or darkest gray in grayscale modes)
@ Red
Red (BWR and Spectra6 modes)
@ Solid
Continuous solid line.
DisplayMode
Definition driver.hpp:46
@ BWY
Black, White, Yellow (3 colors, typically 2-bit)
@ Spectra6
6-color: Black, White, Red, Yellow, Blue, Green (3-bit)
@ Grayscale4
2-bit 4-level grayscale
@ BWR
Black, White, Red (3 colors, typically 2-bit)
Command for drawing a circle.
Definition commands.hpp:174
Definition errors.hpp:140
Command for drawing a line between two points.
Definition commands.hpp:84
Command for drawing a point (pixel or multi-pixel dot).
Definition commands.hpp:218
Command for drawing a rectangle.
Definition commands.hpp:129
Command for drawing text.
Definition commands.hpp:281
Driver capabilities trait template.
Definition capabilities.hpp:110