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

PicoVector. #783

Merged
merged 20 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
cc7219b
PicoGraphics: Experimental Pretty Poly bring-up.
Gadgetoid Sep 16, 2022
09a58b2
PicoGraphics: Various compile warning fixes for Pretty Poly.
Gadgetoid May 25, 2023
e8dba75
PicoGraphics: Use std:: prefix, fix some type issues.
Gadgetoid May 25, 2023
38aaa04
Tufty 2040: Basic pretty polygon example.
Gadgetoid Aug 4, 2023
c9a8d5e
PicoVector: Move polygon drawing to a new home.
Gadgetoid Aug 8, 2023
9e430fd
PicoVector: Better separation of concerns, add Alright Fonts support.
Gadgetoid Aug 14, 2023
95ab839
PicoVector: Text wrap support.
Gadgetoid Aug 15, 2023
4671607
PicoVector: Vector anti-aliasing support.
Gadgetoid Aug 15, 2023
c7d9fe4
PicoVector: Bugfixes and font/aa options.
Gadgetoid Aug 15, 2023
61c9d7e
PicoVector: Experimental matrix transforms.
Gadgetoid Aug 15, 2023
7c5ebfc
PicoVector: Matrix transforms and polygon type.
Gadgetoid Aug 16, 2023
9d0501a
PicoVector: Polygon iter interface.
Gadgetoid Aug 16, 2023
cfe8b3c
PicoVector: Text rotation support.
Gadgetoid Aug 17, 2023
591058f
PicoVector: Store pointer to PP mem.
Gadgetoid Aug 18, 2023
c443f8d
PicoVector: Tweak polygon tile rendering loop.
Gadgetoid Aug 21, 2023
5a92a9c
PNGDEC: Support for 2bpp indexed PNGs, fix open_RAM.
Gadgetoid Sep 4, 2023
c9fd68e
PNGDEC: Remove PNG RAM debug text.
Gadgetoid Sep 6, 2023
231ceb7
PicoVector: Add basic polygon center of mass function.
Gadgetoid Sep 6, 2023
788f6c3
PicoVector: Add clock example for PicoW Explorer.
Gadgetoid Sep 6, 2023
cca2d56
PicoVector: Add vector spectrometer example for PicoW Explorer.
Gadgetoid Sep 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libraries/pico_graphics/pico_graphics.cpp
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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)
Loading