Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 1 commit into from
Jun 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
77 changes: 17 additions & 60 deletions esphome/components/display/display_buffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}

Expand Down
51 changes: 33 additions & 18 deletions esphome/components/display/display_buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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`.
*
Expand All @@ -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`.
*
Expand All @@ -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`.
*
Expand All @@ -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`.
*
Expand All @@ -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`.
Expand All @@ -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`.
*
Expand All @@ -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`.
*
Expand All @@ -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`.
*
Expand All @@ -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`.
Expand All @@ -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`.
Expand All @@ -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`.
Expand All @@ -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.
*
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;

Expand Down
52 changes: 48 additions & 4 deletions esphome/components/display/font.cpp
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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_
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down