display: add BaseFont and introduce Font::draw methods (#4963)

This commit is contained in:
Kamil Trzciński 2023-06-19 01:04:19 +02:00 committed by GitHub
parent 5a8e93ed0a
commit 8c9d63f48f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 106 additions and 100 deletions

View file

@ -7,10 +7,6 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "animation.h"
#include "image.h"
#include "font.h"
namespace esphome {
namespace display {
@ -256,54 +252,14 @@ void DisplayBuffer::filled_circle(int center_x, int center_y, int radius, Color
} while (dx <= 0);
}
void DisplayBuffer::print(int x, int y, Font *font, Color color, TextAlign align, const char *text) {
void DisplayBuffer::print(int x, int y, BaseFont *font, Color color, TextAlign align, const char *text) {
int x_start, y_start;
int width, height;
this->get_text_bounds(x, y, text, font, align, &x_start, &y_start, &width, &height);
int i = 0;
int x_at = x_start;
while (text[i] != '\0') {
int match_length;
int glyph_n = font->match_next_glyph(text + i, &match_length);
if (glyph_n < 0) {
// Unknown char, skip
ESP_LOGW(TAG, "Encountered character without representation in font: '%c'", text[i]);
if (!font->get_glyphs().empty()) {
uint8_t glyph_width = font->get_glyphs()[0].glyph_data_->width;
for (int glyph_x = 0; glyph_x < glyph_width; glyph_x++) {
for (int glyph_y = 0; glyph_y < height; glyph_y++)
this->draw_pixel_at(glyph_x + x_at, glyph_y + y_start, color);
}
x_at += glyph_width;
}
i++;
continue;
}
const Glyph &glyph = font->get_glyphs()[glyph_n];
int scan_x1, scan_y1, scan_width, scan_height;
glyph.scan_area(&scan_x1, &scan_y1, &scan_width, &scan_height);
{
const int glyph_x_max = scan_x1 + scan_width;
const int glyph_y_max = scan_y1 + scan_height;
for (int glyph_x = scan_x1; glyph_x < glyph_x_max; glyph_x++) {
for (int glyph_y = scan_y1; glyph_y < glyph_y_max; glyph_y++) {
if (glyph.get_pixel(glyph_x, glyph_y)) {
this->draw_pixel_at(glyph_x + x_at, glyph_y + y_start, color);
}
}
}
}
x_at += glyph.glyph_data_->width + glyph.glyph_data_->offset_x;
i += match_length;
}
font->print(x_start, y_start, this, color, text);
}
void DisplayBuffer::vprintf_(int x, int y, Font *font, Color color, TextAlign align, const char *format, va_list arg) {
void DisplayBuffer::vprintf_(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format,
va_list arg) {
char buffer[256];
int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
if (ret > 0)
@ -358,7 +314,7 @@ void DisplayBuffer::qr_code(int x, int y, qr_code::QrCode *qr_code, Color color_
}
#endif // USE_QR_CODE
void DisplayBuffer::get_text_bounds(int x, int y, const char *text, Font *font, TextAlign align, int *x1, int *y1,
void DisplayBuffer::get_text_bounds(int x, int y, const char *text, BaseFont *font, TextAlign align, int *x1, int *y1,
int *width, int *height) {
int x_offset, baseline;
font->measure(text, width, &x_offset, &baseline, height);
@ -396,34 +352,34 @@ void DisplayBuffer::get_text_bounds(int x, int y, const char *text, Font *font,
break;
}
}
void DisplayBuffer::print(int x, int y, Font *font, Color color, const char *text) {
void DisplayBuffer::print(int x, int y, BaseFont *font, Color color, const char *text) {
this->print(x, y, font, color, TextAlign::TOP_LEFT, text);
}
void DisplayBuffer::print(int x, int y, Font *font, TextAlign align, const char *text) {
void DisplayBuffer::print(int x, int y, BaseFont *font, TextAlign align, const char *text) {
this->print(x, y, font, COLOR_ON, align, text);
}
void DisplayBuffer::print(int x, int y, Font *font, const char *text) {
void DisplayBuffer::print(int x, int y, BaseFont *font, const char *text) {
this->print(x, y, font, COLOR_ON, TextAlign::TOP_LEFT, text);
}
void DisplayBuffer::printf(int x, int y, Font *font, Color color, TextAlign align, const char *format, ...) {
void DisplayBuffer::printf(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ...) {
va_list arg;
va_start(arg, format);
this->vprintf_(x, y, font, color, align, format, arg);
va_end(arg);
}
void DisplayBuffer::printf(int x, int y, Font *font, Color color, const char *format, ...) {
void DisplayBuffer::printf(int x, int y, BaseFont *font, Color color, const char *format, ...) {
va_list arg;
va_start(arg, format);
this->vprintf_(x, y, font, color, TextAlign::TOP_LEFT, format, arg);
va_end(arg);
}
void DisplayBuffer::printf(int x, int y, Font *font, TextAlign align, const char *format, ...) {
void DisplayBuffer::printf(int x, int y, BaseFont *font, TextAlign align, const char *format, ...) {
va_list arg;
va_start(arg, format);
this->vprintf_(x, y, font, COLOR_ON, align, format, arg);
va_end(arg);
}
void DisplayBuffer::printf(int x, int y, Font *font, const char *format, ...) {
void DisplayBuffer::printf(int x, int y, BaseFont *font, const char *format, ...) {
va_list arg;
va_start(arg, format);
this->vprintf_(x, y, font, COLOR_ON, TextAlign::TOP_LEFT, format, arg);
@ -470,19 +426,20 @@ void DisplayOnPageChangeTrigger::process(DisplayPage *from, DisplayPage *to) {
if ((this->from_ == nullptr || this->from_ == from) && (this->to_ == nullptr || this->to_ == to))
this->trigger(from, to);
}
void DisplayBuffer::strftime(int x, int y, Font *font, Color color, TextAlign align, const char *format, ESPTime time) {
void DisplayBuffer::strftime(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format,
ESPTime time) {
char buffer[64];
size_t ret = time.strftime(buffer, sizeof(buffer), format);
if (ret > 0)
this->print(x, y, font, color, align, buffer);
}
void DisplayBuffer::strftime(int x, int y, Font *font, Color color, const char *format, ESPTime time) {
void DisplayBuffer::strftime(int x, int y, BaseFont *font, Color color, const char *format, ESPTime time) {
this->strftime(x, y, font, color, TextAlign::TOP_LEFT, format, time);
}
void DisplayBuffer::strftime(int x, int y, Font *font, TextAlign align, const char *format, ESPTime time) {
void DisplayBuffer::strftime(int x, int y, BaseFont *font, TextAlign align, const char *format, ESPTime time) {
this->strftime(x, y, font, COLOR_ON, align, format, time);
}
void DisplayBuffer::strftime(int x, int y, Font *font, const char *format, ESPTime time) {
void DisplayBuffer::strftime(int x, int y, BaseFont *font, const char *format, ESPTime time) {
this->strftime(x, y, font, COLOR_ON, TextAlign::TOP_LEFT, format, time);
}

View file

@ -16,10 +16,6 @@
#include "esphome/components/qr_code/qr_code.h"
#endif
#include "animation.h"
#include "font.h"
#include "image.h"
namespace esphome {
namespace display {
@ -175,6 +171,24 @@ using display_writer_t = std::function<void(DisplayBuffer &)>;
ESP_LOGCONFIG(TAG, "%s Dimensions: %dpx x %dpx", prefix, (obj)->get_width(), (obj)->get_height()); \
}
/// Turn the pixel OFF.
extern const Color COLOR_OFF;
/// Turn the pixel ON.
extern const Color COLOR_ON;
class BaseImage {
public:
virtual void draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) = 0;
virtual int get_width() const = 0;
virtual int get_height() const = 0;
};
class BaseFont {
public:
virtual void print(int x, int y, DisplayBuffer *display, Color color, const char *text) = 0;
virtual void measure(const char *str, int *width, int *x_offset, int *baseline, int *height) = 0;
};
class DisplayBuffer {
public:
/// Fill the entire screen with the given color.
@ -221,7 +235,7 @@ class DisplayBuffer {
* @param align The alignment of the text.
* @param text The text to draw.
*/
void print(int x, int y, Font *font, Color color, TextAlign align, const char *text);
void print(int x, int y, BaseFont *font, Color color, TextAlign align, const char *text);
/** Print `text` with the top left at [x,y] with `font`.
*
@ -231,7 +245,7 @@ class DisplayBuffer {
* @param color The color to draw the text with.
* @param text The text to draw.
*/
void print(int x, int y, Font *font, Color color, const char *text);
void print(int x, int y, BaseFont *font, Color color, const char *text);
/** Print `text` with the anchor point at [x,y] with `font`.
*
@ -241,7 +255,7 @@ class DisplayBuffer {
* @param align The alignment of the text.
* @param text The text to draw.
*/
void print(int x, int y, Font *font, TextAlign align, const char *text);
void print(int x, int y, BaseFont *font, TextAlign align, const char *text);
/** Print `text` with the top left at [x,y] with `font`.
*
@ -250,7 +264,7 @@ class DisplayBuffer {
* @param font The font to draw the text with.
* @param text The text to draw.
*/
void print(int x, int y, Font *font, const char *text);
void print(int x, int y, BaseFont *font, const char *text);
/** Evaluate the printf-format `format` and print the result with the anchor point at [x,y] with `font`.
*
@ -262,7 +276,7 @@ class DisplayBuffer {
* @param format The format to use.
* @param ... The arguments to use for the text formatting.
*/
void printf(int x, int y, Font *font, Color color, TextAlign align, const char *format, ...)
void printf(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ...)
__attribute__((format(printf, 7, 8)));
/** Evaluate the printf-format `format` and print the result with the top left at [x,y] with `font`.
@ -274,7 +288,7 @@ class DisplayBuffer {
* @param format The format to use.
* @param ... The arguments to use for the text formatting.
*/
void printf(int x, int y, Font *font, Color color, const char *format, ...) __attribute__((format(printf, 6, 7)));
void printf(int x, int y, BaseFont *font, Color color, const char *format, ...) __attribute__((format(printf, 6, 7)));
/** Evaluate the printf-format `format` and print the result with the anchor point at [x,y] with `font`.
*
@ -285,7 +299,8 @@ class DisplayBuffer {
* @param format The format to use.
* @param ... The arguments to use for the text formatting.
*/
void printf(int x, int y, Font *font, TextAlign align, const char *format, ...) __attribute__((format(printf, 6, 7)));
void printf(int x, int y, BaseFont *font, TextAlign align, const char *format, ...)
__attribute__((format(printf, 6, 7)));
/** Evaluate the printf-format `format` and print the result with the top left at [x,y] with `font`.
*
@ -295,7 +310,7 @@ class DisplayBuffer {
* @param format The format to use.
* @param ... The arguments to use for the text formatting.
*/
void printf(int x, int y, Font *font, const char *format, ...) __attribute__((format(printf, 5, 6)));
void printf(int x, int y, BaseFont *font, const char *format, ...) __attribute__((format(printf, 5, 6)));
/** Evaluate the strftime-format `format` and print the result with the anchor point at [x,y] with `font`.
*
@ -307,7 +322,7 @@ class DisplayBuffer {
* @param format The strftime format to use.
* @param time The time to format.
*/
void strftime(int x, int y, Font *font, Color color, TextAlign align, const char *format, ESPTime time)
void strftime(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ESPTime time)
__attribute__((format(strftime, 7, 0)));
/** Evaluate the strftime-format `format` and print the result with the top left at [x,y] with `font`.
@ -319,7 +334,7 @@ class DisplayBuffer {
* @param format The strftime format to use.
* @param time The time to format.
*/
void strftime(int x, int y, Font *font, Color color, const char *format, ESPTime time)
void strftime(int x, int y, BaseFont *font, Color color, const char *format, ESPTime time)
__attribute__((format(strftime, 6, 0)));
/** Evaluate the strftime-format `format` and print the result with the anchor point at [x,y] with `font`.
@ -331,7 +346,7 @@ class DisplayBuffer {
* @param format The strftime format to use.
* @param time The time to format.
*/
void strftime(int x, int y, Font *font, TextAlign align, const char *format, ESPTime time)
void strftime(int x, int y, BaseFont *font, TextAlign align, const char *format, ESPTime time)
__attribute__((format(strftime, 6, 0)));
/** Evaluate the strftime-format `format` and print the result with the top left at [x,y] with `font`.
@ -342,7 +357,7 @@ class DisplayBuffer {
* @param format The strftime format to use.
* @param time The time to format.
*/
void strftime(int x, int y, Font *font, const char *format, ESPTime time) __attribute__((format(strftime, 5, 0)));
void strftime(int x, int y, BaseFont *font, const char *format, ESPTime time) __attribute__((format(strftime, 5, 0)));
/** Draw the `image` with the top-left corner at [x,y] to the screen.
*
@ -412,7 +427,7 @@ class DisplayBuffer {
* @param width A pointer to store the returned text width in.
* @param height A pointer to store the returned text height in.
*/
void get_text_bounds(int x, int y, const char *text, Font *font, TextAlign align, int *x1, int *y1, int *width,
void get_text_bounds(int x, int y, const char *text, BaseFont *font, TextAlign align, int *x1, int *y1, int *width,
int *height);
/// Internal method to set the display writer lambda.
@ -487,7 +502,7 @@ class DisplayBuffer {
bool is_clipping() const { return !this->clipping_rectangle_.empty(); }
protected:
void vprintf_(int x, int y, Font *font, Color color, TextAlign align, const char *format, va_list arg);
void vprintf_(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, va_list arg);
virtual void draw_absolute_pixel_internal(int x, int y, Color color) = 0;

View file

@ -1,10 +1,13 @@
#include "font.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
namespace esphome {
namespace display {
static const char *const TAG = "display";
bool Glyph::get_pixel(int x, int y) const {
const int x_data = x - this->glyph_data_->offset_x;
const int y_data = y - this->glyph_data_->offset_y;
@ -14,6 +17,20 @@ bool Glyph::get_pixel(int x, int y) const {
const uint32_t pos = x_data + y_data * width_8;
return progmem_read_byte(this->glyph_data_->data + (pos / 8u)) & (0x80 >> (pos % 8u));
}
void Glyph::draw(int x_at, int y_start, DisplayBuffer *display, Color color) const {
int scan_x1, scan_y1, scan_width, scan_height;
this->scan_area(&scan_x1, &scan_y1, &scan_width, &scan_height);
const int glyph_x_max = scan_x1 + scan_width;
const int glyph_y_max = scan_y1 + scan_height;
for (int glyph_x = scan_x1; glyph_x < glyph_x_max; glyph_x++) {
for (int glyph_y = scan_y1; glyph_y < glyph_y_max; glyph_y++) {
if (this->get_pixel(glyph_x, glyph_y)) {
display->draw_pixel_at(glyph_x + x_at, glyph_y + y_start, color);
}
}
}
}
const char *Glyph::get_char() const { return this->glyph_data_->a_char; }
bool Glyph::compare_to(const char *str) const {
// 1 -> this->char_
@ -47,6 +64,12 @@ void Glyph::scan_area(int *x1, int *y1, int *width, int *height) const {
*width = this->glyph_data_->width;
*height = this->glyph_data_->height;
}
Font::Font(const GlyphData *data, int data_nr, int baseline, int height) : baseline_(baseline), height_(height) {
glyphs_.reserve(data_nr);
for (int i = 0; i < data_nr; ++i)
glyphs_.emplace_back(&data[i]);
}
int Font::match_next_glyph(const char *str, int *match_length) {
int lo = 0;
int hi = this->glyphs_.size() - 1;
@ -95,10 +118,31 @@ void Font::measure(const char *str, int *width, int *x_offset, int *baseline, in
*x_offset = min_x;
*width = x - min_x;
}
Font::Font(const GlyphData *data, int data_nr, int baseline, int height) : baseline_(baseline), height_(height) {
glyphs_.reserve(data_nr);
for (int i = 0; i < data_nr; ++i)
glyphs_.emplace_back(&data[i]);
void Font::print(int x_start, int y_start, DisplayBuffer *display, Color color, const char *text) {
int i = 0;
int x_at = x_start;
while (text[i] != '\0') {
int match_length;
int glyph_n = this->match_next_glyph(text + i, &match_length);
if (glyph_n < 0) {
// Unknown char, skip
ESP_LOGW(TAG, "Encountered character without representation in font: '%c'", text[i]);
if (!this->get_glyphs().empty()) {
uint8_t glyph_width = this->get_glyphs()[0].glyph_data_->width;
display->filled_rectangle(x_at, y_start, glyph_width, this->height_, color);
x_at += glyph_width;
}
i++;
continue;
}
const Glyph &glyph = this->get_glyphs()[glyph_n];
glyph.draw(x_at, y_start, display, color);
x_at += glyph.glyph_data_->width + glyph.glyph_data_->offset_x;
i += match_length;
}
}
} // namespace display

View file

@ -1,6 +1,7 @@
#pragma once
#include "esphome/core/datatypes.h"
#include "display_buffer.h"
namespace esphome {
namespace display {
@ -23,6 +24,8 @@ class Glyph {
bool get_pixel(int x, int y) const;
void draw(int x, int y, DisplayBuffer *display, Color color) const;
const char *get_char() const;
bool compare_to(const char *str) const;
@ -33,12 +36,11 @@ class Glyph {
protected:
friend Font;
friend DisplayBuffer;
const GlyphData *glyph_data_;
};
class Font {
class Font : public BaseFont {
public:
/** Construct the font with the given glyphs.
*
@ -50,7 +52,8 @@ class Font {
int match_next_glyph(const char *str, int *match_length);
void measure(const char *str, int *width, int *x_offset, int *baseline, int *height);
void print(int x_start, int y_start, DisplayBuffer *display, Color color, const char *text) override;
void measure(const char *str, int *width, int *x_offset, int *baseline, int *height) override;
inline int get_baseline() { return this->baseline_; }
inline int get_height() { return this->height_; }

View file

@ -1,7 +1,6 @@
#include "image.h"
#include "esphome/core/hal.h"
#include "display_buffer.h"
namespace esphome {
namespace display {

View file

@ -1,5 +1,6 @@
#pragma once
#include "esphome/core/color.h"
#include "display_buffer.h"
namespace esphome {
namespace display {
@ -30,20 +31,6 @@ inline int image_type_to_bpp(ImageType type) {
inline int image_type_to_width_stride(int width, ImageType type) { return (width * image_type_to_bpp(type) + 7u) / 8u; }
/// Turn the pixel OFF.
extern const Color COLOR_OFF;
/// Turn the pixel ON.
extern const Color COLOR_ON;
class DisplayBuffer;
class BaseImage {
public:
virtual void draw(int x, int y, DisplayBuffer *display, Color color_on, Color color_off) = 0;
virtual int get_width() const = 0;
virtual int get_height() const = 0;
};
class Image : public BaseImage {
public:
Image(const uint8_t *data_start, int width, int height, ImageType type);

View file

@ -1,5 +1,6 @@
#include "graph.h"
#include "esphome/components/display/display_buffer.h"
#include "esphome/components/display/font.h"
#include "esphome/core/color.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"