From 9e430fd68c9df0d9c2a71f12dac2ebccd8bc4e9e Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 14 Aug 2023 15:58:40 +0100 Subject: [PATCH] PicoVector: Better separation of concerns, add Alright Fonts support. --- libraries/pico_graphics/pico_graphics.cpp | 48 ------ libraries/pico_graphics/pico_graphics.hpp | 6 - libraries/pico_vector/alright_fonts.cpp | 128 ++++++++++++++ libraries/pico_vector/alright_fonts.hpp | 68 ++++++++ libraries/pico_vector/pico_vector.cmake | 9 + libraries/pico_vector/pico_vector.cpp | 30 ++++ libraries/pico_vector/pico_vector.hpp | 43 +++++ .../pretty_poly.cpp} | 30 ++-- libraries/pico_vector/pretty_poly.hpp | 65 +++++++ .../pretty_poly_types.hpp} | 0 .../modules/picographics/picographics.cpp | 4 - .../modules/picovector/micropython.cmake | 5 + micropython/modules/picovector/picovector.c | 5 + micropython/modules/picovector/picovector.cpp | 158 +++++++++++++++++- micropython/modules/picovector/picovector.h | 4 +- 15 files changed, 518 insertions(+), 85 deletions(-) create mode 100644 libraries/pico_vector/alright_fonts.cpp create mode 100644 libraries/pico_vector/alright_fonts.hpp create mode 100644 libraries/pico_vector/pico_vector.cmake create mode 100644 libraries/pico_vector/pico_vector.cpp create mode 100644 libraries/pico_vector/pico_vector.hpp rename libraries/{pico_graphics/pretty-poly.hpp => pico_vector/pretty_poly.cpp} (89%) create mode 100644 libraries/pico_vector/pretty_poly.hpp rename libraries/{pico_graphics/pretty-poly-types.hpp => pico_vector/pretty_poly_types.hpp} (100%) diff --git a/libraries/pico_graphics/pico_graphics.cpp b/libraries/pico_graphics/pico_graphics.cpp index 30cae7fd..557fc785 100644 --- a/libraries/pico_graphics/pico_graphics.cpp +++ b/libraries/pico_graphics/pico_graphics.cpp @@ -1,5 +1,4 @@ #include "pico_graphics.hpp" -#include "pretty-poly.hpp" namespace pimoroni { @@ -17,13 +16,6 @@ namespace pimoroni { int PicoGraphics::get_palette_size() {return 0;} RGB* PicoGraphics::get_palette() {return nullptr;} - size_t PicoGraphics::pretty_poly_buffer_size() { - return pretty_poly::buffer_size(); - } - - void PicoGraphics::pretty_poly_init(void *mem) { - pretty_poly::init(mem); - } void PicoGraphics::set_dimensions(int width, int height) { bounds = clip = {0, 0, width, height}; @@ -249,46 +241,7 @@ namespace pimoroni { } } - void PicoGraphics::polygon(std::vector> contours) { - set_options([this](const pretty_poly::tile_t &tile) -> void { - for(auto y = 0; y < tile.bounds.h; y++) { - for(auto x = 0; x < tile.bounds.w; x++) { - uint8_t alpha = tile.get_value(x, y); - if (alpha > 0) { - pixel({x + tile.bounds.x, y + tile.bounds.y}); - } - } - } - }, pretty_poly::NONE, {0, 0, bounds.w, bounds.h}); - - pretty_poly::draw_polygon(contours); - } - void PicoGraphics::polygon(const std::vector &points) { - - pretty_poly::contour_t contour(new pretty_poly::point_t[points.size()], points.size()); - - for(auto i = 0u; i < points.size(); i++) { - contour.points[i] = pretty_poly::point_t(points[i].x, points[i].y); - } - - set_options([this](const pretty_poly::tile_t &tile) -> void { - for(auto y = 0; y < tile.bounds.h; y++) { - for(auto x = 0; x < tile.bounds.w; x++) { - uint8_t alpha = tile.get_value(x, y); - if (alpha > 0) { - pixel({x + tile.bounds.x, y + tile.bounds.y}); - } - } - } - }, pretty_poly::NONE, {0, 0, bounds.w, bounds.h}); - - std::vector> contours; - contours.push_back(contour); - - pretty_poly::draw_polygon(contours); - - /* static int32_t nodes[64]; // maximum allowed number of nodes per scanline for polygon rendering int32_t miny = points[0].y, maxy = points[0].y; @@ -332,7 +285,6 @@ namespace pimoroni { pixel_span(Point(nodes[i], p.y), nodes[i + 1] - nodes[i] + 1); } } - */ } void PicoGraphics::thick_line(Point p1, Point p2, uint thickness) { diff --git a/libraries/pico_graphics/pico_graphics.hpp b/libraries/pico_graphics/pico_graphics.hpp index 758736ed..2dd7c83d 100644 --- a/libraries/pico_graphics/pico_graphics.hpp +++ b/libraries/pico_graphics/pico_graphics.hpp @@ -17,8 +17,6 @@ #include "common/pimoroni_common.hpp" -#include "pretty-poly-types.hpp" - // A tiny graphics library for our Pico products // supports: // - 16-bit (565) RGB @@ -262,9 +260,6 @@ namespace pimoroni { set_font(&font6); }; - size_t pretty_poly_buffer_size(); - void pretty_poly_init(void *mem); - virtual void set_pen(uint c) = 0; virtual void set_pen(uint8_t r, uint8_t g, uint8_t b) = 0; virtual void set_pixel(const Point &p) = 0; @@ -306,7 +301,6 @@ namespace pimoroni { void text(const std::string_view &t, const Point &p, int32_t wrap, float s = 2.0f, float a = 0.0f, uint8_t letter_spacing = 1, bool fixed_width = false); int32_t measure_text(const std::string_view &t, float s = 2.0f, uint8_t letter_spacing = 1, bool fixed_width = false); void polygon(const std::vector &points); - void polygon(std::vector> contours); void triangle(Point p1, Point p2, Point p3); void line(Point p1, Point p2); void thick_line(Point p1, Point p2, uint thickness); diff --git a/libraries/pico_vector/alright_fonts.cpp b/libraries/pico_vector/alright_fonts.cpp new file mode 100644 index 00000000..5289c50f --- /dev/null +++ b/libraries/pico_vector/alright_fonts.cpp @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "alright_fonts.hpp" + +using namespace pretty_poly; + +namespace alright_fonts { + /* + render functions + */ + + void render_character(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t 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(glyph.contours, origin, scale); + } + } + + /* + 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 *points = new pretty_poly::point_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); + } + +} \ No newline at end of file diff --git a/libraries/pico_vector/alright_fonts.hpp b/libraries/pico_vector/alright_fonts.hpp new file mode 100644 index 00000000..4bc7afff --- /dev/null +++ b/libraries/pico_vector/alright_fonts.hpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "pretty_poly.hpp" + +namespace alright_fonts { + + struct glyph_t { + uint16_t codepoint; + pretty_poly::rect_t bounds; + uint8_t advance; + std::vector> contours; + }; + + struct face_t { + uint16_t glyph_count; + uint16_t flags; + std::map 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 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 * 1.2; + letter_spacing = 0; + word_spacing = size; + } + + text_metrics_t() {}; + }; + + /* + render functions + */ + + void render_character(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t origin); +} \ No newline at end of file diff --git a/libraries/pico_vector/pico_vector.cmake b/libraries/pico_vector/pico_vector.cmake new file mode 100644 index 00000000..cecbff4f --- /dev/null +++ b/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) \ No newline at end of file diff --git a/libraries/pico_vector/pico_vector.cpp b/libraries/pico_vector/pico_vector.cpp new file mode 100644 index 00000000..e4f91971 --- /dev/null +++ b/libraries/pico_vector/pico_vector.cpp @@ -0,0 +1,30 @@ +#include "pico_vector.hpp" +#include + +namespace pimoroni { + void PicoVector::polygon(std::vector> contours, Point origin, int scale) { + pretty_poly::draw_polygon( + contours, + pretty_poly::point_t(origin.x, origin.y), + scale); + } + + Point PicoVector::text(std::string_view text, Point origin) { + // TODO: Normalize types somehow, so we're not converting? + pretty_poly::point_t caret = pretty_poly::point_t(origin.x, origin.y); + + for (auto c : text) { + alright_fonts::render_character(text_metrics, c, caret); + if(text_metrics.face.glyphs.count(c) == 1) { + alright_fonts::glyph_t glyph = text_metrics.face.glyphs[c]; + caret.x += ((glyph.advance * text_metrics.size) / 128); + if(caret.x > graphics->bounds.w) { + caret.y += text_metrics.size; + caret.x = origin.x; + } + } + } + + return Point(caret.x, caret.y); + } +} \ No newline at end of file diff --git a/libraries/pico_vector/pico_vector.hpp b/libraries/pico_vector/pico_vector.hpp new file mode 100644 index 00000000..b8132730 --- /dev/null +++ b/libraries/pico_vector/pico_vector.hpp @@ -0,0 +1,43 @@ +#include "pretty_poly.hpp" +#include "alright_fonts.hpp" +#include "pico_graphics.hpp" + +namespace pimoroni { + class PicoVector { + private: + PicoGraphics *graphics; + alright_fonts::text_metrics_t text_metrics; + + public: + PicoVector(PicoGraphics *graphics, void *mem = nullptr) : graphics(graphics) { + pretty_poly::init(mem); + + set_options([this](const pretty_poly::tile_t &tile) -> void { + for(auto y = 0; y < tile.bounds.h; y++) { + for(auto x = 0; x < tile.bounds.w; x++) { + uint8_t alpha = tile.get_value(x, y); + if (alpha > 0) { + this->graphics->pixel({x + tile.bounds.x, y + tile.bounds.y}); + } + } + } + }, pretty_poly::NONE, {0, 0, graphics->bounds.w, graphics->bounds.h}); + }; + + bool set_font(std::string_view font_path, unsigned int font_size) { + bool result = text_metrics.face.load(font_path); + + text_metrics.set_size(font_size); + + return result; + } + + Point text(std::string_view text, Point origin); + + void polygon(std::vector> contours, Point origin = Point(0, 0), int scale=65536); + + static constexpr size_t pretty_poly_buffer_size() { + return pretty_poly::buffer_size(); + }; + }; +} \ No newline at end of file diff --git a/libraries/pico_graphics/pretty-poly.hpp b/libraries/pico_vector/pretty_poly.cpp similarity index 89% rename from libraries/pico_graphics/pretty-poly.hpp rename to libraries/pico_vector/pretty_poly.cpp index f7ad3f05..3ff44c33 100644 --- a/libraries/pico_graphics/pretty-poly.hpp +++ b/libraries/pico_vector/pretty_poly.cpp @@ -3,6 +3,10 @@ #include #include #include +#include +#include + +#include "pretty_poly.hpp" #ifdef PP_DEBUG @@ -13,22 +17,8 @@ namespace pretty_poly { - //typedef void (*tile_callback_t)(const tile_t &tile); - - typedef std::function tile_callback_t; - - // buffer that each tile is rendered into before callback - constexpr unsigned tile_buffer_size = 1024; - //uint8_t tile_buffer[tile_buffer_size]; uint8_t *tile_buffer; - // polygon node buffer handles at most 16 line intersections per scanline - // is this enough for cjk/emoji? (requires a 2kB buffer) - constexpr unsigned node_buffer_size = 32; - - //int nodes[node_buffer_size][32]; - //unsigned node_counts[node_buffer_size]; - int (*nodes)[32]; unsigned *node_counts; @@ -42,10 +32,6 @@ namespace pretty_poly { antialias_t antialias = antialias_t::NONE; } - size_t buffer_size() { - return tile_buffer_size + (node_buffer_size * sizeof(unsigned)) + (node_buffer_size * 32 * sizeof(int)); - } - void init(void *memory) { uintptr_t m = (uintptr_t)memory; tile_buffer = new(memory) uint8_t[tile_buffer_size]; @@ -130,7 +116,7 @@ namespace pretty_poly { } template - void build_nodes(const contour_t &contour, const tile_t &tile, point_t origin = point_t(0, 0), int scale = 65536) { + void build_nodes(const contour_t &contour, const tile_t &tile, point_t origin, int scale) { int ox = (origin.x - tile.bounds.x) << settings::antialias; int oy = (origin.y - tile.bounds.y) << settings::antialias; @@ -186,7 +172,7 @@ namespace pretty_poly { } template - void draw_polygon(std::vector> contours, point_t origin = point_t(0, 0), int scale = 65536) { + void draw_polygon(std::vector> contours, point_t origin, int scale) { debug("> draw polygon with %lu contours\n", contours.size()); @@ -246,3 +232,7 @@ namespace pretty_poly { } } } + +template void pretty_poly::draw_polygon(std::vector> contours, point_t origin, int scale); +template void pretty_poly::draw_polygon(std::vector> contours, point_t origin, int scale); +template void pretty_poly::draw_polygon(std::vector> contours, point_t origin, int scale); \ No newline at end of file diff --git a/libraries/pico_vector/pretty_poly.hpp b/libraries/pico_vector/pretty_poly.hpp new file mode 100644 index 00000000..58c2e789 --- /dev/null +++ b/libraries/pico_vector/pretty_poly.hpp @@ -0,0 +1,65 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#include "pretty_poly_types.hpp" + +namespace pretty_poly { + + class file_io { + private: + void *state; + size_t filesize = 0; + + public: + file_io(std::string_view path); + ~file_io(); + size_t seek(size_t pos); + size_t read(void *buf, size_t len); + size_t tell(); + bool fail(); + }; + + // buffer that each tile is rendered into before callback + constexpr unsigned tile_buffer_size = 1024; + + // polygon node buffer handles at most 16 line intersections per scanline + // is this enough for cjk/emoji? (requires a 2kB buffer) + constexpr unsigned node_buffer_size = 32; + + typedef std::function tile_callback_t; + + constexpr size_t buffer_size() { + return tile_buffer_size + (node_buffer_size * sizeof(unsigned)) + (node_buffer_size * 32 * sizeof(int)); + } + + constexpr size_t buffer_size(); + + void init(void *memory); + + void set_options(tile_callback_t callback, antialias_t antialias, rect_t clip); + + // dy step (returns 1, 0, or -1 if the supplied value is > 0, == 0, < 0) + inline constexpr int sign(int v); + + // write out the tile bits + void debug_tile(const tile_t &tile); + + void add_line_segment_to_nodes(const point_t &start, const point_t &end); + + template + void build_nodes(const contour_t &contour, const tile_t &tile, point_t origin = point_t(0, 0), int scale = 65536); + + void render_nodes(const tile_t &tile); + + template + void draw_polygon(T *points, unsigned count); + + template + void draw_polygon(std::vector> contours, point_t origin = point_t(0, 0), int scale = 65536); +} \ No newline at end of file diff --git a/libraries/pico_graphics/pretty-poly-types.hpp b/libraries/pico_vector/pretty_poly_types.hpp similarity index 100% rename from libraries/pico_graphics/pretty-poly-types.hpp rename to libraries/pico_vector/pretty_poly_types.hpp diff --git a/micropython/modules/picographics/picographics.cpp b/micropython/modules/picographics/picographics.cpp index 3c8a791a..e10f78c8 100644 --- a/micropython/modules/picographics/picographics.cpp +++ b/micropython/modules/picographics/picographics.cpp @@ -7,7 +7,6 @@ #include "drivers/inky73/inky73.hpp" #include "drivers/psram_display/psram_display.hpp" #include "libraries/pico_graphics/pico_graphics.hpp" -#include "libraries/pico_graphics/pretty-poly-types.hpp" #include "common/pimoroni_common.hpp" #include "common/pimoroni_bus.hpp" #include "common/pimoroni_i2c.hpp" @@ -442,9 +441,6 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size if (display != DISPLAY_INKY_FRAME && display != DISPLAY_INKY_FRAME_4 && display != DISPLAY_INKY_PACK && display != DISPLAY_INKY_FRAME_7) { self->display->update(self->graphics); } - - self->graphics->pretty_poly_init(m_tracked_calloc(self->graphics->pretty_poly_buffer_size(), sizeof(uint8_t))); - return MP_OBJ_FROM_PTR(self); } diff --git a/micropython/modules/picovector/micropython.cmake b/micropython/modules/picovector/micropython.cmake index 5c054921..69624f20 100644 --- a/micropython/modules/picovector/micropython.cmake +++ b/micropython/modules/picovector/micropython.cmake @@ -1,12 +1,17 @@ add_library(usermod_picovector INTERFACE) target_sources(usermod_picovector INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_vector/pico_vector.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_vector/pretty_poly.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_vector/alright_fonts.cpp ${CMAKE_CURRENT_LIST_DIR}/picovector.c ${CMAKE_CURRENT_LIST_DIR}/picovector.cpp ) target_include_directories(usermod_picovector INTERFACE ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_vector/ + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_graphics/ ) target_compile_definitions(usermod_picovector INTERFACE diff --git a/micropython/modules/picovector/picovector.c b/micropython/modules/picovector/picovector.c index a716c737..56f374c2 100644 --- a/micropython/modules/picovector/picovector.c +++ b/micropython/modules/picovector/picovector.c @@ -2,11 +2,16 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(VECTOR_polygon_obj, 2, VECTOR_polygon); STATIC MP_DEFINE_CONST_FUN_OBJ_KW(VECTOR_regular_polygon_obj, 6, VECTOR_regular_polygon); +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(VECTOR_text_obj, 4, VECTOR_text); +STATIC MP_DEFINE_CONST_FUN_OBJ_3(VECTOR_set_font_obj, VECTOR_set_font); + // class STATIC const mp_rom_map_elem_t VECTOR_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_polygon), MP_ROM_PTR(&VECTOR_polygon_obj) }, { MP_ROM_QSTR(MP_QSTR_regular_polygon), MP_ROM_PTR(&VECTOR_regular_polygon_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_font), MP_ROM_PTR(&VECTOR_set_font_obj) }, + { MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&VECTOR_text_obj) }, }; STATIC MP_DEFINE_CONST_DICT(VECTOR_locals_dict, VECTOR_locals_dict_table); diff --git a/micropython/modules/picovector/picovector.cpp b/micropython/modules/picovector/picovector.cpp index eace364c..6c69be06 100644 --- a/micropython/modules/picovector/picovector.cpp +++ b/micropython/modules/picovector/picovector.cpp @@ -1,5 +1,6 @@ #include "micropython/modules/util.hpp" #include "libraries/pico_graphics/pico_graphics.hpp" +#include "libraries/pico_vector/pico_vector.hpp" using namespace pimoroni; @@ -19,9 +20,89 @@ typedef struct _ModPicoGraphics_obj_t { typedef struct _VECTOR_obj_t { mp_obj_base_t base; - ModPicoGraphics_obj_t *graphics; + PicoVector *vector; } _VECTOR_obj_t; + +pretty_poly::file_io::file_io(std::string_view filename) { + mp_obj_t fn = mp_obj_new_str(filename.data(), (mp_uint_t)filename.size()); + + //mp_printf(&mp_plat_print, "Opening file %s\n", filename.data()); + + mp_obj_t args[2] = { + fn, + MP_OBJ_NEW_QSTR(MP_QSTR_r), + }; + + // Stat the file to get its size + // example tuple response: (32768, 0, 0, 0, 0, 0, 5153, 1654709815, 1654709815, 1654709815) + mp_obj_t stat = mp_vfs_stat(fn); + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR2(stat, mp_obj_tuple_t); + filesize = mp_obj_get_int(tuple->items[6]); + + mp_obj_t fhandle = mp_vfs_open(MP_ARRAY_SIZE(args), &args[0], (mp_map_t *)&mp_const_empty_map); + + this->state = (void *)fhandle; +} + +pretty_poly::file_io::~file_io() { + mp_stream_close((mp_obj_t)this->state); +} + +size_t pretty_poly::file_io::read(void *buf, size_t len) { + //mp_printf(&mp_plat_print, "Reading %lu bytes\n", len); + mp_obj_t fhandle = this->state; + int error; + return mp_stream_read_exactly(fhandle, buf, len, &error); +} + +size_t pretty_poly::file_io::tell() { + mp_obj_t fhandle = this->state; + struct mp_stream_seek_t seek_s; + seek_s.offset = 0; + seek_s.whence = SEEK_CUR; + + const mp_stream_p_t *stream_p = mp_get_stream(fhandle); + + int error; + mp_uint_t res = stream_p->ioctl(fhandle, MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, &error); + if (res == MP_STREAM_ERROR) { + mp_raise_OSError(error); + } + + return seek_s.offset; +} + +bool pretty_poly::file_io::fail() { + return false; +} + +// Re-implementation of stream.c/STATIC mp_obj_t stream_seek(size_t n_args, const mp_obj_t *args) +size_t pretty_poly::file_io::seek(size_t pos) { + mp_obj_t fhandle = this->state; + struct mp_stream_seek_t seek_s; + seek_s.offset = pos; + seek_s.whence = SEEK_SET; + + const mp_stream_p_t *stream_p = mp_get_stream(fhandle); + + int error; + mp_uint_t res = stream_p->ioctl(fhandle, MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, &error); + if (res == MP_STREAM_ERROR) { + mp_raise_OSError(error); + } + + return seek_s.offset; +} + +static const std::string_view mp_obj_to_string_r(const mp_obj_t &obj) { + if(mp_obj_is_str_or_bytes(obj)) { + GET_STR_DATA_LEN(obj, str, str_len); + return std::string_view((const char*)str, str_len); + } + mp_raise_TypeError("can't convert object to str implicitly"); +} + mp_obj_t VECTOR_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { enum { ARG_picographics @@ -36,11 +117,60 @@ mp_obj_t VECTOR_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, _VECTOR_obj_t *self = m_new_obj(_VECTOR_obj_t); self->base.type = &VECTOR_type; - self->graphics = (ModPicoGraphics_obj_t *)MP_OBJ_TO_PTR(args[ARG_picographics].u_obj); + ModPicoGraphics_obj_t *graphics = (ModPicoGraphics_obj_t *)MP_OBJ_TO_PTR(args[ARG_picographics].u_obj); + + void *mem = m_tracked_calloc(PicoVector::pretty_poly_buffer_size(), sizeof(uint8_t)); + + self->vector = m_new_class(PicoVector, graphics->graphics, mem); return self; } +mp_obj_t VECTOR_set_font(mp_obj_t self_in, mp_obj_t font, mp_obj_t size) { + _VECTOR_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VECTOR_obj_t); + + int font_size = mp_obj_get_int(size); + bool result = false; + + if (mp_obj_is_str(font)) { + result = self->vector->set_font(mp_obj_to_string_r(font), font_size); + } + else { + + } + return result ? mp_const_true : mp_const_false; +} + +mp_obj_t VECTOR_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 }; + 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_x, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT } + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + _VECTOR_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _VECTOR_obj_t); + + mp_obj_t text_obj = args[ARG_text].u_obj; + + if(!mp_obj_is_str_or_bytes(text_obj)) mp_raise_TypeError("text: string required"); + + GET_STR_DATA_LEN(text_obj, str, str_len); + + const std::string_view t((const char*)str, str_len); + + int x = args[ARG_x].u_int; + int y = args[ARG_y].u_int; + + self->vector->text(t, Point(x, y)); + + return mp_const_none; +} + mp_obj_t VECTOR_regular_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_self, ARG_x, ARG_y, ARG_sides, ARG_radius, ARG_rotation }; static const mp_arg_t allowed_args[] = { @@ -71,14 +201,14 @@ mp_obj_t VECTOR_regular_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_ for(auto s = 0u; s < sides; s++) { float current_angle = angle * s + rotation; points[s] = { - int(cos(current_angle) * radius + o_x), - int(sin(current_angle) * radius + o_y) + int(cos(current_angle) * radius), + int(sin(current_angle) * radius) }; } std::vector> contours; contours.push_back({points, sides}); - self->graphics->graphics->polygon(contours); + self->vector->polygon(contours, Point(o_x, o_y)); delete points; @@ -86,11 +216,27 @@ mp_obj_t VECTOR_regular_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_ } mp_obj_t VECTOR_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + size_t num_tuples = n_args - 1; const mp_obj_t *lists = pos_args + 1; _VECTOR_obj_t *self = MP_OBJ_TO_PTR2(pos_args[0], _VECTOR_obj_t); + int offset_x = 0; + int offset_y = 0; + + if(mp_obj_is_int(pos_args[1])) { + offset_x = mp_obj_get_int(pos_args[1]); + lists++; + num_tuples--; + } + + if (mp_obj_is_int(pos_args[2])) { + offset_y = mp_obj_get_int(pos_args[2]); + lists++; + num_tuples--; + } + std::vector> contours; for(auto i = 0u; i < num_tuples; i++) { @@ -117,7 +263,7 @@ mp_obj_t VECTOR_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar contours.push_back({points, t_contour->len}); } - self->graphics->graphics->polygon(contours); + self->vector->polygon(contours, Point(offset_x, offset_y)); for(auto contour : contours) { delete contour.points; diff --git a/micropython/modules/picovector/picovector.h b/micropython/modules/picovector/picovector.h index 436196c5..05109b9b 100644 --- a/micropython/modules/picovector/picovector.h +++ b/micropython/modules/picovector/picovector.h @@ -6,4 +6,6 @@ extern const mp_obj_type_t VECTOR_type; extern mp_obj_t VECTOR_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); extern mp_obj_t VECTOR_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t VECTOR_regular_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); \ No newline at end of file +extern mp_obj_t VECTOR_regular_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t VECTOR_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t VECTOR_set_font(mp_obj_t self_in, mp_obj_t font, mp_obj_t size); \ No newline at end of file