PicoGraphicS: Hershey support, bugfixes, tidyup.

This commit is contained in:
Phil Howard 2022-06-08 16:37:09 +01:00
parent fc1561e54b
commit 49b62515c2
5 changed files with 121 additions and 64 deletions

View File

@ -5,4 +5,4 @@ add_library(pico_graphics
target_include_directories(pico_graphics INTERFACE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(pico_graphics bitmap_fonts pico_stdlib)
target_link_libraries(pico_graphics bitmap_fonts hershey_fonts pico_stdlib)

View File

@ -4,10 +4,10 @@ namespace pimoroni {
void PicoGraphics::set_pen(uint c) {};
void PicoGraphics::set_pen(uint8_t r, uint8_t g, uint8_t b) {};
void PicoGraphics::update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) {};
void PicoGraphics::reset_pen(uint8_t i) {};
int PicoGraphics::update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) {return -1;};
int PicoGraphics::reset_pen(uint8_t i) {return -1;};
int PicoGraphics::create_pen(uint8_t r, uint8_t g, uint8_t b) {return -1;};
void PicoGraphics::set_pixel(void *frame_buffer, uint x, uint y, uint stride) {};
void PicoGraphics::set_pixel(const Point &p) {};
void PicoGraphics::palette_lookup(void *frame_buffer, void *result, uint offset, uint length) {};
void PicoGraphics::set_dimensions(int width, int height) {
@ -27,16 +27,27 @@ namespace pimoroni {
}
void PicoGraphics::set_font(const bitmap::font_t *font){
this->font = font;
this->bitmap_font = font;
this->hershey_font = nullptr;
}
void PicoGraphics::set_font(std::string font){
if (font == "bitmap6") {
this->font = &font6;
} else if (font == "bitmap8") {
this->font = &font8;
} else if (font == "bitmap14_outline") {
this->font = &font14_outline;
void PicoGraphics::set_font(const hershey::font_t *font){
this->bitmap_font = nullptr;
this->hershey_font = font;
}
void PicoGraphics::set_font(std::string name){
if (name == "bitmap6") {
set_font(&font6);
} else if (name == "bitmap8") {
set_font(&font8);
} else if (name == "bitmap14_outline") {
set_font(&font14_outline);
} else {
// check that font exists and assign it
if(hershey::fonts.find(name) != hershey::fonts.end()) {
set_font(hershey::fonts[name]);
}
}
}
@ -54,7 +65,7 @@ namespace pimoroni {
void PicoGraphics::pixel(const Point &p) {
if(!clip.contains(p)) return;
set_pixel(frame_buffer, p.x, p.y, bounds.w);
set_pixel(p);
}
void PicoGraphics::pixel_span(const Point &p, int32_t l) {
@ -69,7 +80,7 @@ namespace pimoroni {
Point dest(clipped.x, clipped.y);
while(l--) {
set_pixel(frame_buffer, dest.x, dest.y, bounds.w);
set_pixel(dest);
dest.x++;
}
}
@ -117,20 +128,42 @@ namespace pimoroni {
}
}
void PicoGraphics::character(const char c, const Point &p, uint8_t scale) {
bitmap::character(font, [this](int32_t x, int32_t y, int32_t w, int32_t h){
rectangle(Rect(x, y, w, h));
}, c, p.x, p.y, scale);
void PicoGraphics::character(const char c, const Point &p, float s, float a) {
if (bitmap_font) {
bitmap::character(bitmap_font, [this](int32_t x, int32_t y, int32_t w, int32_t h) {
rectangle(Rect(x, y, w, h));
}, c, p.x, p.y, std::max(1.0f, s));
return;
}
if (hershey_font) {
hershey::glyph(hershey_font, [this](int32_t x1, int32_t y1, int32_t x2, int32_t y2) {
line(Point(x1, y1), Point(x2, y2));
}, c, p.x, p.y, s, a);
return;
}
}
void PicoGraphics::text(const std::string &t, const Point &p, int32_t wrap, uint8_t scale) {
bitmap::text(font, [this](int32_t x, int32_t y, int32_t w, int32_t h){
rectangle(Rect(x, y, w, h));
}, t, p.x, p.y, wrap, scale);
void PicoGraphics::text(const std::string &t, const Point &p, int32_t wrap, float s, float a, uint8_t letter_spacing) {
if (bitmap_font) {
bitmap::text(bitmap_font, [this](int32_t x, int32_t y, int32_t w, int32_t h) {
rectangle(Rect(x, y, w, h));
}, t, p.x, p.y, wrap, std::max(1.0f, s), letter_spacing);
return;
}
if (hershey_font) {
hershey::text(hershey_font, [this](int32_t x1, int32_t y1, int32_t x2, int32_t y2) {
line(Point(x1, y1), Point(x2, y2));
}, t, p.x, p.y, s, a);
return;
}
}
int32_t PicoGraphics::measure_text(const std::string &t, uint8_t scale) {
return bitmap::measure_text(font, t, scale);
int32_t PicoGraphics::measure_text(const std::string &t, float s, uint8_t letter_spacing) {
if (bitmap_font) return bitmap::measure_text(bitmap_font, t, std::max(1.0f, s), letter_spacing);
if (hershey_font) return hershey::measure_text(hershey_font, t, s);
return 0;
}
int32_t orient2d(Point p1, Point p2, Point p3) {
@ -186,7 +219,7 @@ namespace pimoroni {
Point dest = Point(triangle_bounds.x, triangle_bounds.y + y);
for (int32_t x = 0; x < triangle_bounds.w; x++) {
if ((w0 | w1 | w2) >= 0) {
set_pixel(frame_buffer, dest.x, dest.y, bounds.w);
set_pixel(dest);
}
dest.x++;
@ -251,24 +284,29 @@ namespace pimoroni {
void PicoGraphics::line(Point p1, Point p2) {
// fast horizontal line
if(p1.y == p2.y) {
int32_t start = std::max(clip.x, std::min(p1.x, p2.x));
int32_t end = std::min(clip.x + clip.w, std::max(p1.x, p2.x));
p1 = p1.clamp(clip);
p2 = p2.clamp(clip);
int32_t start = std::min(p1.x, p2.x);
int32_t end = std::max(p1.x, p2.x);
pixel_span(Point(start, p1.y), end - start);
return;
}
// fast vertical line
if(p1.x == p2.x) {
int32_t start = std::max(clip.y, std::min(p1.y, p2.y));
int32_t length = std::min(clip.y + clip.h, std::max(p1.y, p2.y)) - start;
p1 = p1.clamp(clip);
p2 = p2.clamp(clip);
int32_t start = std::min(p1.y, p2.y);
int32_t length = std::max(p1.y, p2.y) - start;
Point dest(p1.x, start);
while(length--) {
set_pixel(frame_buffer, dest.x, dest.y, bounds.w);
set_pixel(dest);
dest.y++;
}
return;
}
// general purpose line
// lines are either "shallow" or "steep" based on whether the x delta
// is greater than the y delta
@ -283,7 +321,8 @@ namespace pimoroni {
int32_t x = p1.x;
int32_t y = p1.y << 16;
while(s--) {
set_pixel(frame_buffer, x, y >> 16, bounds.w);
Point p(x, y >> 16);
if(clip.contains(p)) set_pixel(p);
y += sy;
x += sx;
}
@ -295,7 +334,8 @@ namespace pimoroni {
int32_t y = p1.y;
int32_t x = p1.x << 16;
while(s--) {
set_pixel(frame_buffer, x >> 16, y, bounds.w);
Point p(x >> 16, y);
if(clip.contains(p)) set_pixel(p);
y += sy;
x += sx;
}

View File

@ -4,9 +4,13 @@
#include <cstdint>
#include <algorithm>
#include <vector>
#include "libraries/hershey_fonts/hershey_fonts.hpp"
#include "libraries/bitmap_fonts/bitmap_fonts.hpp"
#include "libraries/bitmap_fonts/font6_data.hpp"
#include "libraries/bitmap_fonts/font8_data.hpp"
#include "libraries/bitmap_fonts/font14_outline_data.hpp"
#include "common/pimoroni_common.hpp"
// A tiny graphics library for our Pico products
@ -72,7 +76,8 @@ namespace pimoroni {
Rect bounds;
Rect clip;
const bitmap::font_t *font;
const bitmap::font_t *bitmap_font;
const hershey::font_t *hershey_font;
static constexpr RGB332 rgb_to_rgb332(uint8_t r, uint8_t g, uint8_t b) {
return (r & 0b11100000) | ((g & 0b11100000) >> 3) | ((b & 0b11000000) >> 6);
@ -100,13 +105,14 @@ namespace pimoroni {
virtual void set_pen(uint c);
virtual void set_pen(uint8_t r, uint8_t g, uint8_t b);
virtual void update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b);
virtual void reset_pen(uint8_t i);
virtual int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b);
virtual int reset_pen(uint8_t i);
virtual int create_pen(uint8_t r, uint8_t g, uint8_t b);
virtual void set_pixel(void *frame_buffer, uint x, uint y, uint stride);
virtual void set_pixel(const Point &p);
virtual void palette_lookup(void *frame_buffer, void *result, uint offset, uint length);
void set_font(const bitmap::font_t *font);
void set_font(const hershey::font_t *font);
void set_font(std::string font);
void set_dimensions(int width, int height);
@ -123,9 +129,9 @@ namespace pimoroni {
void pixel_span(const Point &p, int32_t l);
void rectangle(const Rect &r);
void circle(const Point &p, int32_t r);
void character(const char c, const Point &p, uint8_t scale = 2);
void text(const std::string &t, const Point &p, int32_t wrap, uint8_t scale = 2);
int32_t measure_text(const std::string &t, uint8_t scale = 2);
void character(const char c, const Point &p, float s = 2.0f, float a = 0.0f);
void text(const std::string &t, const Point &p, int32_t wrap, float s = 2.0f, float a = 0.0f, uint8_t letter_spacing = 1);
int32_t measure_text(const std::string &t, float s = 2.0f, uint8_t letter_spacing = 1);
void polygon(const std::vector<Point> &points);
void triangle(Point p1, Point p2, Point p3);
void line(Point p1, Point p2);
@ -163,26 +169,28 @@ namespace pimoroni {
void set_pen(uint8_t r, uint8_t g, uint8_t b) override {
// TODO look up closest palette colour, or just NOOP?
}
void update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override {
int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override {
i &= 0xf;
palette[i].color = rgb_to_rgb565(r, g, b);
palette[i].used = true;
return i;
}
void reset_pen(uint8_t i) override {
int reset_pen(uint8_t i) override {
i &= 0xf;
palette[i].color = default_palette[i];
return i;
}
void set_pixel(void *frame_buffer, uint x, uint y, uint stride) override {
void set_pixel(const Point &p) override {
// pointer to byte in framebuffer that contains this pixel
uint8_t *buf = (uint8_t *)frame_buffer;
uint8_t *p = &buf[(x / 2) + (y * stride / 2)];
uint8_t *f = &buf[(p.x / 2) + (p.y * bounds.w / 2)];
uint8_t o = (~x & 0b1) * 4; // bit offset within byte
uint8_t m = ~(0b1111 << o); // bit mask for byte
uint8_t b = color << o; // bit value shifted to position
uint8_t o = (~p.x & 0b1) * 4; // bit offset within byte
uint8_t m = ~(0b1111 << o); // bit mask for byte
uint8_t b = color << o; // bit value shifted to position
*p &= m; // clear bits
*p |= b; // set value
*f &= m; // clear bits
*f |= b; // set value
}
void palette_lookup(void *frame_buffer, void *result, uint offset, uint length) override {
uint8_t *src = (uint8_t *)frame_buffer;
@ -219,15 +227,17 @@ namespace pimoroni {
void set_pen(uint8_t r, uint8_t g, uint8_t b) override {
// TODO look up closest palette colour, or just NOOP?
}
void update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override {
int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override {
i &= 0xff;
palette[i].color = rgb_to_rgb565(r, g, b);
palette[i].used = true;
return i;
}
void reset_pen(uint8_t i) override {
int reset_pen(uint8_t i) override {
i &= 0xff;
palette[i].color = 0;
palette[i].used = false;
return i;
}
int create_pen(uint8_t r, uint8_t g, uint8_t b) override {
// Create a colour and place it in the palette if there's space
@ -241,9 +251,9 @@ namespace pimoroni {
}
return -1;
}
void set_pixel(void *frame_buffer, uint x, uint y, uint stride) override {
void set_pixel(const Point &p) override {
uint8_t *buf = (uint8_t *)frame_buffer;
buf[y * stride + x] = color;
buf[p.y * bounds.w + p.x] = color;
}
void palette_lookup(void *frame_buffer, void *result, uint offset, uint length) override {
uint8_t *src = (uint8_t *)frame_buffer;
@ -281,9 +291,9 @@ namespace pimoroni {
int create_pen(uint8_t r, uint8_t g, uint8_t b) override {
return rgb_to_rgb332(r, g, b);
}
void set_pixel(void *frame_buffer, uint x, uint y, uint stride) override {
void set_pixel(const Point &p) override {
uint8_t *buf = (uint8_t *)frame_buffer;
buf[y * stride + x] = color;
buf[p.y * bounds.w + p.x] = color;
}
void palette_lookup(void *frame_buffer, void *result, uint offset, uint length) override {
uint8_t *src = (uint8_t *)frame_buffer;
@ -316,9 +326,9 @@ namespace pimoroni {
int create_pen(uint8_t r, uint8_t g, uint8_t b) override {
return rgb_to_rgb565(r, g, b);
}
void set_pixel(void *frame_buffer, uint x, uint y, uint stride) override {
void set_pixel(const Point &p) override {
uint16_t *buf = (uint16_t *)frame_buffer;
buf[y * stride + x] = color;
buf[p.y * bounds.w + p.x] = color;
}
static size_t buffer_size(uint w, uint h) {
return w * h * sizeof(RGB565);

View File

@ -29,6 +29,7 @@ include(pico_unicorn/micropython)
include(pico_wireless/micropython)
include(pico_explorer/micropython)
include(hershey_fonts/micropython)
include(bitmap_fonts/micropython)
include(plasma/micropython)

View File

@ -443,14 +443,16 @@ mp_obj_t ModPicoGraphics_character(size_t n_args, const mp_obj_t *pos_args, mp_m
}
mp_obj_t ModPicoGraphics_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_text, ARG_x, ARG_y, ARG_wrap, ARG_scale };
enum { ARG_self, ARG_text, ARG_x, ARG_y, ARG_wrap, ARG_scale, ARG_angle, ARG_spacing };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_text, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_wordwrap, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_scale, MP_ARG_INT, {.u_int = 2} },
{ MP_QSTR_scale, MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_angle, MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_spacing, MP_ARG_INT, {.u_int = 1} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@ -469,19 +471,22 @@ mp_obj_t ModPicoGraphics_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t
int x = args[ARG_x].u_int;
int y = args[ARG_y].u_int;
int wrap = args[ARG_wrap].u_int;
int scale = args[ARG_scale].u_int;
float scale = args[ARG_scale].u_obj == mp_const_none ? 2.0f : mp_obj_get_float(args[ARG_scale].u_obj);
int angle = args[ARG_angle].u_int;
int letter_spacing = args[ARG_spacing].u_int;
self->graphics->text(t, Point(x, y), wrap, scale);
self->graphics->text(t, Point(x, y), wrap, scale, angle, letter_spacing);
return mp_const_none;
}
mp_obj_t ModPicoGraphics_measure_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_text, ARG_scale };
enum { ARG_self, ARG_text, ARG_scale, ARG_spacing };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_text, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_scale, MP_ARG_INT, {.u_int = 2} },
{ MP_QSTR_scale, MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_spacing, MP_ARG_INT, {.u_int = 1} },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@ -497,9 +502,10 @@ mp_obj_t ModPicoGraphics_measure_text(size_t n_args, const mp_obj_t *pos_args, m
std::string t((const char*)str);
int scale = args[ARG_scale].u_int;
float scale = args[ARG_scale].u_obj == mp_const_none ? 2.0f : mp_obj_get_float(args[ARG_scale].u_obj);
int letter_spacing = args[ARG_spacing].u_int;
int width = self->graphics->measure_text(t, scale);
int width = self->graphics->measure_text(t, scale, letter_spacing);
return mp_obj_new_int(width);
}