Skip to content

Commit

Permalink
Merge pull request #783 from pimoroni/feature/ppaf
Browse files Browse the repository at this point in the history
PicoVector.
  • Loading branch information
Gadgetoid committed Sep 6, 2023
2 parents 3d8f8c9 + cca2d56 commit a334899
Show file tree
Hide file tree
Showing 22 changed files with 1,944 additions and 13 deletions.
2 changes: 2 additions & 0 deletions libraries/pico_graphics/pico_graphics.cpp
Expand Up @@ -8,6 +8,7 @@ namespace pimoroni {
int PicoGraphics::reset_pen(uint8_t i) {return -1;};
int PicoGraphics::create_pen(uint8_t r, uint8_t g, uint8_t b) {return -1;};
int PicoGraphics::create_pen_hsv(float h, float s, float v){return -1;};
void PicoGraphics::set_pixel_alpha(const Point &p, const uint8_t a) {};
void PicoGraphics::set_pixel_dither(const Point &p, const RGB &c) {};
void PicoGraphics::set_pixel_dither(const Point &p, const RGB565 &c) {};
void PicoGraphics::set_pixel_dither(const Point &p, const uint8_t &c) {};
Expand All @@ -16,6 +17,7 @@ namespace pimoroni {

int PicoGraphics::get_palette_size() {return 0;}
RGB* PicoGraphics::get_palette() {return nullptr;}
bool PicoGraphics::supports_alpha_blend() {return false;}

void PicoGraphics::set_dimensions(int width, int height) {
bounds = clip = {0, 0, width, height};
Expand Down
19 changes: 18 additions & 1 deletion libraries/pico_graphics/pico_graphics.hpp
Expand Up @@ -47,7 +47,19 @@ namespace pimoroni {
g((c >> 8) & 0xff),
b(c & 0xff) {}
constexpr RGB(int16_t r, int16_t g, int16_t b) : r(r), g(g), b(b) {}


constexpr uint8_t blend(uint8_t s, uint8_t d, uint8_t a) {
return d + ((a * (s - d) + 127) >> 8);
}

constexpr RGB blend(RGB with, const uint8_t alpha) {
return RGB(
blend(with.r, r, alpha),
blend(with.g, g, alpha),
blend(with.b, b, alpha)
);
}

static RGB from_hsv(float h, float s, float v) {
float i = floor(h * 6.0f);
float f = h * 6.0f - i;
Expand Down Expand Up @@ -268,6 +280,7 @@ namespace pimoroni {

virtual int get_palette_size();
virtual RGB* get_palette();
virtual bool supports_alpha_blend();

virtual int create_pen(uint8_t r, uint8_t g, uint8_t b);
virtual int create_pen_hsv(float h, float s, float v);
Expand All @@ -276,6 +289,7 @@ namespace pimoroni {
virtual void set_pixel_dither(const Point &p, const RGB &c);
virtual void set_pixel_dither(const Point &p, const RGB565 &c);
virtual void set_pixel_dither(const Point &p, const uint8_t &c);
virtual void set_pixel_alpha(const Point &p, const uint8_t a);
virtual void frame_convert(PenType type, conversion_callback_func callback);
virtual void sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent);

Expand Down Expand Up @@ -471,6 +485,9 @@ namespace pimoroni {
void set_pixel_span(const Point &p, uint l) override;
void set_pixel_dither(const Point &p, const RGB &c) override;
void set_pixel_dither(const Point &p, const RGB565 &c) override;
void set_pixel_alpha(const Point &p, const uint8_t a) override;

bool supports_alpha_blend() override {return true;}

void sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent) override;

Expand Down
9 changes: 9 additions & 0 deletions libraries/pico_graphics/pico_graphics_pen_rgb332.cpp
Expand Up @@ -34,6 +34,15 @@ namespace pimoroni {
*buf++ = color;
}
}
void PicoGraphics_PenRGB332::set_pixel_alpha(const Point &p, const uint8_t a) {
if(!bounds.contains(p)) return;

uint8_t *buf = (uint8_t *)frame_buffer;

RGB332 blended = RGB(buf[p.y * bounds.w + p.x]).blend(RGB(color), a).to_rgb332();

buf[p.y * bounds.w + p.x] = blended;
};
void PicoGraphics_PenRGB332::set_pixel_dither(const Point &p, const RGB &c) {
if(!bounds.contains(p)) return;
uint8_t _dmv = dither16_pattern[(p.x & 0b11) | ((p.y & 0b11) << 2)];
Expand Down
171 changes: 171 additions & 0 deletions libraries/pico_vector/alright_fonts.cpp
@@ -0,0 +1,171 @@
#include <cstdint>
#include <math.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <optional>
#include <map>

#include "alright_fonts.hpp"

using namespace pretty_poly;

namespace alright_fonts {
/*
utility functions
*/
pretty_poly::rect_t measure_character(text_metrics_t &tm, uint16_t codepoint) {
if(tm.face.glyphs.count(codepoint) == 1) {
glyph_t glyph = tm.face.glyphs[codepoint];

return {0, 0, ((glyph.advance * tm.size) / 128), tm.size};
}

return {0, 0, 0, 0};
}

/*
render functions
*/

void render_character(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin) {
if(tm.face.glyphs.count(codepoint) == 1) {
glyph_t glyph = tm.face.glyphs[codepoint];

// scale is a fixed point 16:16 value, our font data is already scaled to
// -128..127 so to get the pixel size we want we can just shift the
// users requested size up one bit
unsigned scale = tm.size << 9;

pretty_poly::draw_polygon<int8_t>(glyph.contours, origin, scale);
}
}

void render_character(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin, pretty_poly::mat3_t transform) {
if(tm.face.glyphs.count(codepoint) == 1) {
glyph_t glyph = tm.face.glyphs[codepoint];

// scale is a fixed point 16:16 value, our font data is already scaled to
// -128..127 so to get the pixel size we want we can just shift the
// users requested size up one bit
unsigned scale = tm.size << 9;

std::vector<pretty_poly::contour_t<int8_t>> contours;

for(auto i = 0u; i < glyph.contours.size(); i++) {
unsigned int count = glyph.contours[i].count;
point_t<int8_t> *points = (point_t<int8_t> *)malloc(sizeof(point_t<int8_t>) * count);
for(auto j = 0u; j < count; j++) {
point_t<float> point(glyph.contours[i].points[j].x, glyph.contours[i].points[j].y);
point *= transform;
points[j] = point_t<int8_t>(point.x, point.y);
}
contours.emplace_back(points, count);
}

pretty_poly::draw_polygon<int8_t>(contours, origin, scale);

for(auto contour : contours) {
free(contour.points);
}
}
}

/*
load functions
*/

// big endian stream value helpers
uint16_t ru16(file_io &ifs) {uint8_t w[2]; ifs.read((char *)w, 2); return w[0] << 8 | w[1];}
int16_t rs16(file_io &ifs) {uint8_t w[2]; ifs.read((char *)w, 2); return w[0] << 8 | w[1];}
uint32_t ru32(file_io &ifs) {uint8_t dw[4]; ifs.read((char *)dw, 4); return dw[0] << 24 | dw[1] << 16 | dw[2] << 8 | dw[3];}
uint8_t ru8(file_io &ifs) {uint8_t w; ifs.read(&w, 1); return w;}
int8_t rs8(file_io &ifs) {int8_t w; ifs.read(&w, 1); return w;}

bool face_t::load(file_io &ifs) {
char marker[4];
ifs.read(marker, sizeof(marker));

// check header magic bytes are present
if(memcmp(marker, "af!?", 4) != 0) {
// doesn't start with magic marker
return false;
}

// number of glyphs embedded in font file
this->glyph_count = ru16(ifs);

// extract flags and ensure none set
this->flags = ru16(ifs);
if(this->flags != 0) {
// unknown flags set
return false;
}

// extract glyph dictionary
uint16_t glyph_entry_size = 9;
uint32_t contour_data_offset = 8 + this->glyph_count * glyph_entry_size;
for(auto i = 0; i < this->glyph_count; i++) {
glyph_t g;
g.codepoint = ru16(ifs);
g.bounds.x = rs8(ifs);
g.bounds.y = rs8(ifs);
g.bounds.w = ru8(ifs);
g.bounds.h = ru8(ifs);
g.advance = ru8(ifs);

if(ifs.fail()) {
// could not read glyph dictionary entry
return false;
}

// allocate space for the contour data and read it from the font file
uint16_t contour_data_length = ru16(ifs);

// remember where we are in the dictionary
int pos = ifs.tell();

// read contour data
ifs.seek(contour_data_offset);
while(true) {
// get number of points in contour
uint16_t count = ru16(ifs);

// if count is zero then this is the end of contour marker
if(count == 0) {
break;
}

// allocate space to store point data for contour and read
// from file
pretty_poly::point_t<int8_t> *points = new pretty_poly::point_t<int8_t>[count];
ifs.read((char *)points, count * 2);

g.contours.push_back({points, count});
}

// return back to position in dictionary
ifs.seek(pos);
contour_data_offset += contour_data_length;

if(ifs.fail()) {
// could not read glyph contour data
return false;
}

this->glyphs[g.codepoint] = g;
}

return true;
}

bool face_t::load(std::string_view path) {
file_io ifs(path);
if(ifs.fail()) {
// could not open file
return false;
}
return load(ifs);
}

}
74 changes: 74 additions & 0 deletions libraries/pico_vector/alright_fonts.hpp
@@ -0,0 +1,74 @@
#include <cstdint>
#include <math.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <optional>
#include <map>

#include "pretty_poly.hpp"

namespace alright_fonts {

struct glyph_t {
uint16_t codepoint;
pretty_poly::rect_t bounds;
uint8_t advance;
std::vector<pretty_poly::contour_t<int8_t>> contours;
};

struct face_t {
uint16_t glyph_count;
uint16_t flags;
std::map<uint16_t, glyph_t> glyphs;

face_t() {};
face_t(pretty_poly::file_io &ifs) {load(ifs);}
face_t(std::string_view path) {load(path);}

bool load(pretty_poly::file_io &ifs);
bool load(std::string_view path);
};

enum alignment_t {
left = 0,
center = 1,
right = 2,
justify = 4,
top = 8,
bottom = 16
};

struct text_metrics_t {
face_t face; // font to write in
int size; // text size in pixels
uint scroll; // vertical scroll offset
int line_height; // spacing between lines (%)
int letter_spacing; // spacing between characters
int word_spacing; // spacing between words
alignment_t align; // horizontal and vertical alignment
//optional<mat3_t> transform; // arbitrary transformation
pretty_poly::antialias_t antialiasing = pretty_poly::X4; // level of antialiasing to apply

void set_size(int s) {
size = s;
line_height = size;
letter_spacing = 0;
word_spacing = size / 2;
}

text_metrics_t() {};
};

/*
utility functions
*/
pretty_poly::rect_t measure_character(text_metrics_t &tm, uint16_t codepoint);

/*
render functions
*/

void render_character(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin);
void render_character(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin, pretty_poly::mat3_t transform);
}
9 changes: 9 additions & 0 deletions libraries/pico_vector/pico_vector.cmake
@@ -0,0 +1,9 @@
add_library(pico_vector
${CMAKE_CURRENT_LIST_DIR}/pico_vector.cpp
${CMAKE_CURRENT_LIST_DIR}/pretty_poly.cpp
${CMAKE_CURRENT_LIST_DIR}/alright_fonts.cpp
)

target_include_directories(pico_vector INTERFACE ${CMAKE_CURRENT_LIST_DIR})

target_link_libraries(pico_vector pico_stdlib)

0 comments on commit a334899

Please sign in to comment.