Skip to content

Commit

Permalink
display: add BaseFont and introduce Font::draw methods (#4963)
Browse files Browse the repository at this point in the history
  • Loading branch information
ayufan committed Jun 18, 2023
1 parent 5a8e93e commit 8c9d63f
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 100 deletions.
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

0 comments on commit 8c9d63f

Please sign in to comment.