kopia lustrzana https://github.com/pimoroni/pimoroni-pico
commit
a334899b61
|
@ -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) {};
|
||||
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)];
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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,0 +1,188 @@
|
|||
#include "pico_vector.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace pimoroni {
|
||||
void PicoVector::polygon(std::vector<pretty_poly::contour_t<picovector_point_type>> contours, Point origin, int scale) {
|
||||
pretty_poly::draw_polygon<picovector_point_type>(
|
||||
contours,
|
||||
pretty_poly::point_t<int>(origin.x, origin.y),
|
||||
scale);
|
||||
}
|
||||
|
||||
void PicoVector::rotate(std::vector<pretty_poly::contour_t<picovector_point_type>> &contours, Point origin, float angle) {
|
||||
pretty_poly::mat3_t t2 = pretty_poly::mat3_t::translation(origin.x, origin.y);
|
||||
pretty_poly::mat3_t t1 = pretty_poly::mat3_t::translation(-origin.x, -origin.y);
|
||||
angle = 2 * M_PI * (angle / 360.0f);
|
||||
pretty_poly::mat3_t r = pretty_poly::mat3_t::rotation(angle);
|
||||
for(auto &contour : contours) {
|
||||
for(auto i = 0u; i < contour.count; i++) {
|
||||
contour.points[i] *= t1;
|
||||
contour.points[i] *= r;
|
||||
contour.points[i] *= t2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PicoVector::translate(std::vector<pretty_poly::contour_t<picovector_point_type>> &contours, Point translation) {
|
||||
pretty_poly::mat3_t t = pretty_poly::mat3_t::translation(translation.x, translation.y);
|
||||
for(auto &contour : contours) {
|
||||
for(auto i = 0u; i < contour.count; i++) {
|
||||
contour.points[i] *= t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PicoVector::rotate(pretty_poly::contour_t<picovector_point_type> &contour, Point origin, float angle) {
|
||||
pretty_poly::mat3_t t2 = pretty_poly::mat3_t::translation(origin.x, origin.y);
|
||||
pretty_poly::mat3_t t1 = pretty_poly::mat3_t::translation(-origin.x, -origin.y);
|
||||
angle = 2 * M_PI * (angle / 360.0f);
|
||||
pretty_poly::mat3_t r = pretty_poly::mat3_t::rotation(angle);
|
||||
for(auto i = 0u; i < contour.count; i++) {
|
||||
contour.points[i] *= t1;
|
||||
contour.points[i] *= r;
|
||||
contour.points[i] *= t2;
|
||||
}
|
||||
}
|
||||
|
||||
void PicoVector::translate(pretty_poly::contour_t<picovector_point_type> &contour, Point translation) {
|
||||
pretty_poly::mat3_t t = pretty_poly::mat3_t::translation(translation.x, translation.y);
|
||||
for(auto i = 0u; i < contour.count; i++) {
|
||||
contour.points[i] *= t;
|
||||
}
|
||||
}
|
||||
|
||||
Point PicoVector::text(std::string_view text, Point origin) {
|
||||
// TODO: Normalize types somehow, so we're not converting?
|
||||
pretty_poly::point_t<int> caret = pretty_poly::point_t<int>(origin.x, origin.y);
|
||||
|
||||
// Align text from the bottom left
|
||||
caret.y += text_metrics.size;
|
||||
|
||||
int16_t space_width = alright_fonts::measure_character(text_metrics, ' ').w;
|
||||
if (space_width == 0) {
|
||||
space_width = text_metrics.word_spacing;
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
while(i < text.length()) {
|
||||
size_t next_space = text.find(' ', i + 1);
|
||||
|
||||
if(next_space == std::string::npos) {
|
||||
next_space = text.length();
|
||||
}
|
||||
|
||||
size_t next_linebreak = text.find('\n', i + 1);
|
||||
|
||||
if(next_linebreak == std::string::npos) {
|
||||
next_linebreak = text.length();
|
||||
}
|
||||
|
||||
size_t next_break = std::min(next_space, next_linebreak);
|
||||
|
||||
uint16_t word_width = 0;
|
||||
for(size_t j = i; j < next_break; j++) {
|
||||
word_width += alright_fonts::measure_character(text_metrics, text[j]).w;
|
||||
word_width += text_metrics.letter_spacing;
|
||||
}
|
||||
|
||||
if(caret.x != 0 && caret.x + word_width > graphics->clip.w) {
|
||||
caret.x = origin.x;
|
||||
caret.y += text_metrics.line_height;
|
||||
}
|
||||
|
||||
for(size_t j = i; j < std::min(next_break + 1, text.length()); j++) {
|
||||
if (text[j] == '\n') { // Linebreak
|
||||
caret.x = origin.x;
|
||||
caret.y += text_metrics.line_height;
|
||||
} else if (text[j] == ' ') { // Space
|
||||
caret.x += space_width;
|
||||
} else {
|
||||
alright_fonts::render_character(text_metrics, text[j], caret);
|
||||
}
|
||||
caret.x += alright_fonts::measure_character(text_metrics, text[j]).w;
|
||||
caret.x += text_metrics.letter_spacing;
|
||||
}
|
||||
|
||||
i = next_break + 1;
|
||||
}
|
||||
|
||||
return Point(caret.x, caret.y);
|
||||
}
|
||||
|
||||
Point PicoVector::text(std::string_view text, Point origin, float angle) {
|
||||
// TODO: Normalize types somehow, so we're not converting?
|
||||
pretty_poly::point_t<float> caret(0, 0);
|
||||
|
||||
// Prepare a transformation matrix for character and offset rotation
|
||||
angle = 2 * M_PI * (angle / 360.0f);
|
||||
pretty_poly::mat3_t transform = pretty_poly::mat3_t::rotation(angle);
|
||||
|
||||
// Align text from the bottom left
|
||||
caret.y += text_metrics.line_height;
|
||||
caret *= transform;
|
||||
|
||||
pretty_poly::point_t<float> space;
|
||||
pretty_poly::point_t<float> carriage_return(0, text_metrics.line_height);
|
||||
|
||||
space.x = alright_fonts::measure_character(text_metrics, ' ').w;
|
||||
if (space.x == 0) {
|
||||
space.x = text_metrics.word_spacing;
|
||||
}
|
||||
|
||||
space *= transform;
|
||||
carriage_return *= transform;
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
while(i < text.length()) {
|
||||
size_t next_space = text.find(' ', i + 1);
|
||||
|
||||
if(next_space == std::string::npos) {
|
||||
next_space = text.length();
|
||||
}
|
||||
|
||||
size_t next_linebreak = text.find('\n', i + 1);
|
||||
|
||||
if(next_linebreak == std::string::npos) {
|
||||
next_linebreak = text.length();
|
||||
}
|
||||
|
||||
size_t next_break = std::min(next_space, next_linebreak);
|
||||
|
||||
uint16_t word_width = 0;
|
||||
for(size_t j = i; j < next_break; j++) {
|
||||
word_width += alright_fonts::measure_character(text_metrics, text[j]).w;
|
||||
word_width += text_metrics.letter_spacing;
|
||||
}
|
||||
|
||||
if(caret.x != 0 && caret.x + word_width > graphics->clip.w) {
|
||||
caret -= carriage_return;
|
||||
carriage_return.x = 0;
|
||||
}
|
||||
|
||||
for(size_t j = i; j < std::min(next_break + 1, text.length()); j++) {
|
||||
if (text[j] == '\n') { // Linebreak
|
||||
caret -= carriage_return;
|
||||
carriage_return.x = 0;
|
||||
} else if (text[j] == ' ') { // Space
|
||||
caret += space;
|
||||
carriage_return += space;
|
||||
} else {
|
||||
alright_fonts::render_character(text_metrics, text[j], pretty_poly::point_t<int>(origin.x + caret.x, origin.y + caret.y), transform);
|
||||
}
|
||||
pretty_poly::point_t<float> advance(
|
||||
alright_fonts::measure_character(text_metrics, text[j]).w + text_metrics.letter_spacing,
|
||||
0
|
||||
);
|
||||
advance *= transform;
|
||||
caret += advance;
|
||||
carriage_return += advance;
|
||||
}
|
||||
|
||||
i = next_break + 1;
|
||||
}
|
||||
|
||||
return Point(caret.x, caret.y);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
#include "pretty_poly.hpp"
|
||||
#include "alright_fonts.hpp"
|
||||
#include "pico_graphics.hpp"
|
||||
|
||||
namespace pimoroni {
|
||||
|
||||
// Integer point types cause compound error in transformations
|
||||
typedef float picovector_point_type;
|
||||
|
||||
class PicoVector {
|
||||
private:
|
||||
PicoGraphics *graphics;
|
||||
alright_fonts::text_metrics_t text_metrics;
|
||||
const uint8_t alpha_map[4] {0, 128, 192, 255};
|
||||
|
||||
public:
|
||||
PicoVector(PicoGraphics *graphics, void *mem = nullptr) : graphics(graphics) {
|
||||
pretty_poly::init(mem);
|
||||
|
||||
set_options([this](const pretty_poly::tile_t &tile) -> void {
|
||||
uint8_t *tile_data = tile.data;
|
||||
|
||||
if(this->graphics->supports_alpha_blend() && pretty_poly::settings::antialias != pretty_poly::NONE) {
|
||||
for(auto y = 0; y < tile.bounds.h; y++) {
|
||||
for(auto x = 0; x < tile.bounds.w; x++) {
|
||||
uint8_t alpha = *tile_data++;
|
||||
if (alpha >= 4) {
|
||||
this->graphics->set_pixel({x + tile.bounds.x, y + tile.bounds.y});
|
||||
} else if (alpha > 0) {
|
||||
alpha = alpha_map[alpha];
|
||||
this->graphics->set_pixel_alpha({x + tile.bounds.x, y + tile.bounds.y}, alpha);
|
||||
}
|
||||
}
|
||||
tile_data += tile.stride - tile.bounds.w;
|
||||
}
|
||||
} else {
|
||||
for(auto y = 0; y < tile.bounds.h; y++) {
|
||||
for(auto x = 0; x < tile.bounds.w; x++) {
|
||||
uint8_t alpha = *tile_data++;
|
||||
if (alpha) {
|
||||
this->graphics->set_pixel({x + tile.bounds.x, y + tile.bounds.y});
|
||||
}
|
||||
}
|
||||
tile_data += tile.stride - tile.bounds.w;
|
||||
}
|
||||
}
|
||||
}, graphics->supports_alpha_blend() ? pretty_poly::X4 : pretty_poly::NONE, {0, 0, graphics->bounds.w, graphics->bounds.h});
|
||||
}
|
||||
|
||||
void set_antialiasing(pretty_poly::antialias_t antialias) {
|
||||
set_options(pretty_poly::settings::callback, antialias, pretty_poly::settings::clip);
|
||||
}
|
||||
|
||||
void set_font_size(unsigned int font_size) {
|
||||
text_metrics.set_size(font_size);
|
||||
}
|
||||
|
||||
bool set_font(std::string_view font_path, unsigned int font_size) {
|
||||
bool result = text_metrics.face.load(font_path);
|
||||
|
||||
set_font_size(font_size);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void rotate(std::vector<pretty_poly::contour_t<picovector_point_type>> &contours, Point origin, float angle);
|
||||
void translate(std::vector<pretty_poly::contour_t<picovector_point_type>> &contours, Point translation);
|
||||
|
||||
void rotate(pretty_poly::contour_t<picovector_point_type> &contour, Point origin, float angle);
|
||||
void translate(pretty_poly::contour_t<picovector_point_type> &contour, Point translation);
|
||||
|
||||
Point text(std::string_view text, Point origin);
|
||||
Point text(std::string_view text, Point origin, float angle);
|
||||
|
||||
void polygon(std::vector<pretty_poly::contour_t<picovector_point_type>> contours, Point origin = Point(0, 0), int scale=65536);
|
||||
|
||||
static constexpr size_t pretty_poly_buffer_size() {
|
||||
return pretty_poly::buffer_size();
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#include "pretty_poly.hpp"
|
||||
|
||||
|
||||
#ifdef PP_DEBUG
|
||||
#define debug(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define debug(...)
|
||||
#endif
|
||||
|
||||
namespace pretty_poly {
|
||||
|
||||
uint8_t *tile_buffer;
|
||||
|
||||
int (*nodes)[32];
|
||||
unsigned *node_counts;
|
||||
|
||||
// default tile bounds to X1 antialiasing
|
||||
rect_t tile_bounds(0, 0, tile_buffer_size / node_buffer_size, node_buffer_size);
|
||||
|
||||
// user settings
|
||||
namespace settings {
|
||||
rect_t clip(0, 0, 320, 240);
|
||||
tile_callback_t callback;
|
||||
antialias_t antialias = antialias_t::NONE;
|
||||
}
|
||||
|
||||
void init(void *memory) {
|
||||
uintptr_t m = (uintptr_t)memory;
|
||||
tile_buffer = new(memory) uint8_t[tile_buffer_size];
|
||||
node_counts = new((void *)(m + tile_buffer_size)) unsigned[node_buffer_size];
|
||||
nodes = new((void *)(m + tile_buffer_size + (node_buffer_size * sizeof(unsigned)))) int[node_buffer_size][32];
|
||||
}
|
||||
|
||||
void set_options(tile_callback_t callback, antialias_t antialias, rect_t clip) {
|
||||
settings::callback = callback;
|
||||
settings::antialias = antialias;
|
||||
settings::clip = clip;
|
||||
|
||||
// recalculate the tile size for rendering based on antialiasing level
|
||||
int tile_height = node_buffer_size >> antialias;
|
||||
tile_bounds = rect_t(0, 0, tile_buffer_size / tile_height, tile_height);
|
||||
}
|
||||
|
||||
// dy step (returns 1, 0, or -1 if the supplied value is > 0, == 0, < 0)
|
||||
inline constexpr int sign(int v) {
|
||||
// assumes 32-bit int/unsigned
|
||||
return ((unsigned)-v >> 31) - ((unsigned)v >> 31);
|
||||
}
|
||||
|
||||
// write out the tile bits
|
||||
void debug_tile(const tile_t &tile) {
|
||||
debug(" - tile %d, %d (%d x %d)\n", tile.bounds.x, tile.bounds.y, tile.bounds.w, tile.bounds.h);
|
||||
for(auto y = 0; y < tile.bounds.h; y++) {
|
||||
debug("[%3d]: ", y);
|
||||
for(auto x = 0; x < tile.bounds.w; x++) {
|
||||
debug("%d", tile.get_value(x, y));
|
||||
}
|
||||
debug("\n");
|
||||
}
|
||||
debug("-----------------------\n");
|
||||
}
|
||||
|
||||
void add_line_segment_to_nodes(const point_t<int> &start, const point_t<int> &end) {
|
||||
// swap endpoints if line "pointing up", we do this because we
|
||||
// alway skip the last scanline (so that polygons can but cleanly
|
||||
// up against each other without overlap)
|
||||
int sx = start.x, sy = start.y, ex = end.x, ey = end.y;
|
||||
|
||||
if(ey < sy) {
|
||||
std::swap(sy, ey);
|
||||
std::swap(sx, ex);
|
||||
}
|
||||
|
||||
/*sx <<= settings::antialias;
|
||||
ex <<= settings::antialias;
|
||||
sy <<= settings::antialias;
|
||||
ey <<= settings::antialias;*/
|
||||
|
||||
int x = sx;
|
||||
int y = sy;
|
||||
int e = 0;
|
||||
|
||||
int xinc = sign(ex - sx);
|
||||
int einc = abs(ex - sx) + 1;
|
||||
|
||||
// todo: preclamp sy and ey (and therefore count) no need to perform
|
||||
// that test inside the loop
|
||||
int dy = ey - sy;
|
||||
int count = dy;
|
||||
debug(" + line segment from %d, %d to %d, %d\n", sx, sy, ex, ey);
|
||||
// loop over scanlines
|
||||
while(count--) {
|
||||
// consume accumulated error
|
||||
while(e > dy) {e -= dy; x += xinc;}
|
||||
|
||||
if(y >= 0 && y < (int)node_buffer_size) {
|
||||
// clamp node x value to tile bounds
|
||||
int nx = std::max(std::min(x, (int)(tile_bounds.w << settings::antialias)), 0);
|
||||
debug(" + adding node at %d, %d\n", x, y);
|
||||
// add node to node list
|
||||
nodes[y][node_counts[y]++] = nx;
|
||||
}
|
||||
|
||||
// step to next scanline and accumulate error
|
||||
y++;
|
||||
e += einc;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void build_nodes(const contour_t<T> &contour, const tile_t &tile, point_t<int> origin, int scale) {
|
||||
int ox = (origin.x - tile.bounds.x) << settings::antialias;
|
||||
int oy = (origin.y - tile.bounds.y) << settings::antialias;
|
||||
|
||||
// start with the last point to close the loop
|
||||
point_t<int> last(
|
||||
(((int(contour.points[contour.count - 1].x) * scale) << settings::antialias) / 65536) + ox,
|
||||
(((int(contour.points[contour.count - 1].y) * scale) << settings::antialias) / 65536) + oy
|
||||
);
|
||||
|
||||
for(auto i = 0u; i < contour.count; i++) {
|
||||
point_t<int> point(
|
||||
(((int(contour.points[i].x) * scale) << settings::antialias) / 65536) + ox,
|
||||
(((int(contour.points[i].y) * scale) << settings::antialias) / 65536) + oy
|
||||
);
|
||||
|
||||
add_line_segment_to_nodes(last, point);
|
||||
|
||||
last = point;
|
||||
}
|
||||
}
|
||||
|
||||
void render_nodes(const tile_t &tile) {
|
||||
for(auto y = 0; y < (int)node_buffer_size; y++) {
|
||||
if(node_counts[y] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::sort(&nodes[y][0], &nodes[y][0] + node_counts[y]);
|
||||
|
||||
for(auto i = 0u; i < node_counts[y]; i += 2) {
|
||||
int sx = nodes[y][i + 0];
|
||||
int ex = nodes[y][i + 1];
|
||||
|
||||
if(sx == ex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
debug(" - render span at %d from %d to %d\n", y, sx, ex);
|
||||
|
||||
for(int x = sx; x < ex; x++) {
|
||||
tile.data[(x >> settings::antialias) + (y >> settings::antialias) * tile.stride]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void draw_polygon(T *points, unsigned count) {
|
||||
std::vector<contour_t<T>> contours;
|
||||
contour_t<T> c(points, count);
|
||||
contours.push_back(c);
|
||||
draw_polygon<T>(contours);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void draw_polygon(std::vector<contour_t<T>> contours, point_t<int> origin, int scale) {
|
||||
|
||||
debug("> draw polygon with %lu contours\n", contours.size());
|
||||
|
||||
if(contours.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// determine extreme bounds
|
||||
rect_t polygon_bounds = contours[0].bounds();
|
||||
for(auto &contour : contours) {
|
||||
polygon_bounds = polygon_bounds.merge(contour.bounds());
|
||||
}
|
||||
|
||||
polygon_bounds.x = ((polygon_bounds.x * scale) / 65536) + origin.x;
|
||||
polygon_bounds.y = ((polygon_bounds.y * scale) / 65536) + origin.y;
|
||||
polygon_bounds.w = ((polygon_bounds.w * scale) / 65536);
|
||||
polygon_bounds.h = ((polygon_bounds.h * scale) / 65536);
|
||||
|
||||
debug(" - bounds %d, %d (%d x %d)\n", polygon_bounds.x, polygon_bounds.y, polygon_bounds.w, polygon_bounds.h);
|
||||
debug(" - clip %d, %d (%d x %d)\n", settings::clip.x, settings::clip.y, settings::clip.w, settings::clip.h);
|
||||
|
||||
|
||||
memset(nodes, 0, node_buffer_size * sizeof(unsigned) * 32);
|
||||
|
||||
// iterate over tiles
|
||||
debug(" - processing tiles\n");
|
||||
for(auto y = polygon_bounds.y; y < polygon_bounds.y + polygon_bounds.h; y += tile_bounds.h) {
|
||||
for(auto x = polygon_bounds.x; x < polygon_bounds.x + polygon_bounds.w; x += tile_bounds.w) {
|
||||
tile_t tile;
|
||||
tile.bounds = rect_t(x, y, tile_bounds.w, tile_bounds.h).intersection(settings::clip);
|
||||
tile.stride = tile_bounds.w;
|
||||
tile.data = tile_buffer;
|
||||
debug(" : %d, %d (%d x %d)\n", tile.bounds.x, tile.bounds.y, tile.bounds.w, tile.bounds.h);
|
||||
|
||||
// if no intersection then skip tile
|
||||
if(tile.bounds.empty()) {
|
||||
debug(" : empty when clipped, skipping\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// clear existing tile data and nodes
|
||||
memset(node_counts, 0, node_buffer_size * sizeof(unsigned));
|
||||
memset(tile.data, 0, tile_buffer_size);
|
||||
|
||||
// build the nodes for each contour
|
||||
for(contour_t<T> &contour : contours) {
|
||||
debug(" : build nodes for contour\n");
|
||||
build_nodes(contour, tile, origin, scale);
|
||||
}
|
||||
|
||||
debug(" : render the tile\n");
|
||||
// render the tile
|
||||
render_nodes(tile);
|
||||
|
||||
settings::callback(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template void pretty_poly::draw_polygon<int>(std::vector<contour_t<int>> contours, point_t<int> origin, int scale);
|
||||
template void pretty_poly::draw_polygon<float>(std::vector<contour_t<float>> contours, point_t<int> origin, int scale);
|
||||
template void pretty_poly::draw_polygon<uint8_t>(std::vector<contour_t<uint8_t>> contours, point_t<int> origin, int scale);
|
||||
template void pretty_poly::draw_polygon<int8_t>(std::vector<contour_t<int8_t>> contours, point_t<int> origin, int scale);
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#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<void(const tile_t &tile)> tile_callback_t;
|
||||
|
||||
// user settings
|
||||
namespace settings {
|
||||
extern rect_t clip;
|
||||
extern tile_callback_t callback;
|
||||
extern antialias_t antialias;
|
||||
}
|
||||
|
||||
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<int> &start, const point_t<int> &end);
|
||||
|
||||
template<typename T>
|
||||
void build_nodes(const contour_t<T> &contour, const tile_t &tile, point_t<int> origin = point_t<int>(0, 0), int scale = 65536);
|
||||
|
||||
void render_nodes(const tile_t &tile);
|
||||
|
||||
template<typename T>
|
||||
void draw_polygon(T *points, unsigned count);
|
||||
|
||||
template<typename T>
|
||||
void draw_polygon(std::vector<contour_t<T>> contours, point_t<int> origin = point_t<int>(0, 0), int scale = 65536);
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <math.h>
|
||||
|
||||
#ifdef PP_DEBUG
|
||||
#define debug(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define debug(...)
|
||||
#endif
|
||||
|
||||
namespace pretty_poly {
|
||||
|
||||
enum antialias_t {NONE = 0, X4 = 1, X16 = 2};
|
||||
|
||||
// 3x3 matrix for coordinate transformations
|
||||
struct mat3_t {
|
||||
float v00, v10, v20, v01, v11, v21, v02, v12, v22 = 0.0f;
|
||||
mat3_t() = default;
|
||||
mat3_t(const mat3_t &m) = default;
|
||||
inline mat3_t& operator*= (const mat3_t &m) {
|
||||
float r00 = this->v00 * m.v00 + this->v01 * m.v10 + this->v02 * m.v20;
|
||||
float r01 = this->v00 * m.v01 + this->v01 * m.v11 + this->v02 * m.v21;
|
||||
float r02 = this->v00 * m.v02 + this->v01 * m.v12 + this->v02 * m.v22;
|
||||
float r10 = this->v10 * m.v00 + this->v11 * m.v10 + this->v12 * m.v20;
|
||||
float r11 = this->v10 * m.v01 + this->v11 * m.v11 + this->v12 * m.v21;
|
||||
float r12 = this->v10 * m.v02 + this->v11 * m.v12 + this->v12 * m.v22;
|
||||
float r20 = this->v20 * m.v00 + this->v21 * m.v10 + this->v22 * m.v20;
|
||||
float r21 = this->v20 * m.v01 + this->v21 * m.v11 + this->v22 * m.v21;
|
||||
float r22 = this->v20 * m.v02 + this->v21 * m.v12 + this->v22 * m.v22;
|
||||
this->v00 = r00; this->v01 = r01; this->v02 = r02;
|
||||
this->v10 = r10; this->v11 = r11; this->v12 = r12;
|
||||
this->v20 = r20; this->v21 = r21; this->v22 = r22;
|
||||
return *this;
|
||||
}
|
||||
|
||||
static mat3_t identity() {mat3_t m; m.v00 = m.v11 = m.v22 = 1.0f; return m;}
|
||||
static mat3_t rotation(float a) {
|
||||
float c = cosf(a), s = sinf(a); mat3_t r = mat3_t::identity();
|
||||
r.v00 = c; r.v01 = -s; r.v10 = s; r.v11 = c; return r;}
|
||||
static mat3_t translation(float x, float y) {
|
||||
mat3_t r = mat3_t::identity(); r.v02 = x; r.v12 = y; return r;}
|
||||
static mat3_t scale(float x, float y) {
|
||||
mat3_t r = mat3_t::identity(); r.v00 = x; r.v11 = y; return r;}
|
||||
};
|
||||
|
||||
// point type for contour points
|
||||
template<typename T = int>
|
||||
struct __attribute__ ((packed)) point_t {
|
||||
T x, y;
|
||||
point_t(T x, T y) : x(x), y(y) {}
|
||||
point_t() : x(0), y(0) {}
|
||||
inline point_t& operator-= (const point_t &a) {x -= a.x; y -= a.y; return *this;}
|
||||
inline point_t& operator+= (const point_t &a) {x += a.x; y += a.y; return *this;}
|
||||
inline point_t& operator*= (const float a) {x *= a; y *= a; return *this;}
|
||||
inline point_t& operator*= (const mat3_t &a) {this->transform(a); return *this;}
|
||||
inline point_t& operator/= (const float a) {x /= a; y /= a; return *this;}
|
||||
inline point_t& operator/= (const point_t &a) {x /= a.x; y /= a.y; return *this;}
|
||||
void transform(const mat3_t &m) {
|
||||
float tx = x, ty = y;
|
||||
this->x = (m.v00 * tx + m.v01 * ty + m.v02);
|
||||
this->y = (m.v10 * tx + m.v11 * ty + m.v12);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T> inline point_t<T> operator- (point_t<T> lhs, const point_t<T> &rhs) { lhs -= rhs; return lhs; }
|
||||
template<typename T> inline point_t<T> operator- (const point_t<T> &rhs) { return point_t<T>(-rhs.x, -rhs.y); }
|
||||
template<typename T> inline point_t<T> operator+ (point_t<T> lhs, const point_t<T> &rhs) { lhs += rhs; return lhs; }
|
||||
template<typename T> inline point_t<T> operator* (point_t<T> lhs, const float rhs) { lhs *= rhs; return lhs; }
|
||||
template<typename T> inline point_t<T> operator* (point_t<T> lhs, const point_t<T> &rhs) { lhs *= rhs; return lhs; }
|
||||
template<typename T> inline point_t<T> operator* (point_t<T> lhs, const mat3_t &rhs) { lhs *= rhs; return lhs; }
|
||||
template<typename T> inline point_t<T> operator/ (point_t<T> lhs, const float rhs) { lhs /= rhs; return lhs; }
|
||||
template<typename T> inline point_t<T> operator/ (point_t<T> lhs, const point_t<T> &rhs) { lhs.x /= rhs.x; lhs.y /= rhs.y; return lhs; }
|
||||
|
||||
|
||||
// rect type for bounds and clipping rectangles
|
||||
struct rect_t {
|
||||
int x, y, w, h;
|
||||
rect_t() : x(0), y(0), w(0), h(0) {}
|
||||
rect_t(int x, int y, int w, int h) : x(x), y(y), w(w), h(h) {}
|
||||
bool empty() const {return this->w == 0 && this->h == 0;}
|
||||
rect_t intersection(const rect_t &c) {
|
||||
return rect_t(std::max(this->x, c.x), std::max(this->y, c.y),
|
||||
std::max(0, std::min(this->x + this->w, c.x + c.w) - std::max(this->x, c.x)),
|
||||
std::max(0, std::min(this->y + this->h, c.y + c.h) - std::max(this->y, c.y)));
|
||||
}
|
||||
rect_t merge(const rect_t &c) {
|
||||
return rect_t(std::min(this->x, c.x), std::min(this->y, c.y),
|
||||
std::max(this->x + this->w, c.x + c.w) - std::min(this->x, c.x),
|
||||
std::max(this->y + this->h, c.y + c.h) - std::min(this->y, c.y));
|
||||
}
|
||||
};
|
||||
|
||||
struct tile_t {
|
||||
rect_t bounds;
|
||||
unsigned stride;
|
||||
uint8_t *data;
|
||||
|
||||
tile_t() {};
|
||||
|
||||
inline int get_value(int x, int y) const {
|
||||
return this->data[x + y * this->stride];
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct contour_t {
|
||||
point_t<T> *points;
|
||||
unsigned count;
|
||||
|
||||
contour_t() {}
|
||||
contour_t(std::vector<point_t<T>> v) : points(v.data()), count(v.size()) {};
|
||||
contour_t(point_t<T> *points, unsigned count) : points(points), count(count) {};
|
||||
|
||||
// TODO: Make this work, it's so much nicer to use auto point : contour
|
||||
//point_t<T> *begin() const { return points; };
|
||||
//point_t<T> *end() const { return points + count * sizeof(point_t<T>); };
|
||||
|
||||
rect_t bounds() {
|
||||
T minx = this->points[0].x, maxx = minx;
|
||||
T miny = this->points[0].y, maxy = miny;
|
||||
for(auto i = 1u; i < this->count; i++) {
|
||||
minx = std::min(minx, this->points[i].x);
|
||||
miny = std::min(miny, this->points[i].y);
|
||||
maxx = std::max(maxx, this->points[i].x);
|
||||
maxy = std::max(maxy, this->points[i].y);
|
||||
}
|
||||
return rect_t(minx, miny, maxx - minx, maxy - miny);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
import time
|
||||
import gc
|
||||
|
||||
from picographics import PicoGraphics, DISPLAY_PICO_W_EXPLORER, PEN_RGB332
|
||||
from picovector import PicoVector, Polygon, RegularPolygon, Rectangle, ANTIALIAS_X4
|
||||
|
||||
|
||||
display = PicoGraphics(DISPLAY_PICO_W_EXPLORER, pen_type=PEN_RGB332)
|
||||
|
||||
vector = PicoVector(display)
|
||||
vector.set_antialiasing(ANTIALIAS_X4)
|
||||
|
||||
RED = display.create_pen(200, 0, 0)
|
||||
BLACK = display.create_pen(0, 0, 0)
|
||||
GREY = display.create_pen(200, 200, 200)
|
||||
WHITE = display.create_pen(255, 255, 255)
|
||||
|
||||
"""
|
||||
# Redefine colours for a Blue clock
|
||||
RED = display.create_pen(200, 0, 0)
|
||||
BLACK = display.create_pen(135, 159, 169)
|
||||
GREY = display.create_pen(10, 40, 50)
|
||||
WHITE = display.create_pen(14, 60, 76)
|
||||
"""
|
||||
|
||||
WIDTH, HEIGHT = display.get_bounds()
|
||||
|
||||
hub = RegularPolygon(int(WIDTH / 2), int(HEIGHT / 2), 24, 5)
|
||||
|
||||
face = RegularPolygon(int(WIDTH / 2), int(HEIGHT / 2), 48, int(HEIGHT / 2))
|
||||
|
||||
print(time.localtime())
|
||||
|
||||
last_second = None
|
||||
|
||||
while True:
|
||||
t_start = time.ticks_ms()
|
||||
year, month, day, hour, minute, second, _, _ = time.localtime()
|
||||
|
||||
if last_second == second:
|
||||
continue
|
||||
|
||||
last_second = second
|
||||
|
||||
display.set_pen(0)
|
||||
display.clear()
|
||||
|
||||
display.set_pen(BLACK)
|
||||
display.circle(int(WIDTH / 2), int(HEIGHT / 2), int(HEIGHT / 2))
|
||||
display.set_pen(WHITE)
|
||||
display.circle(int(WIDTH / 2), int(HEIGHT / 2), int(HEIGHT / 2) - 4)
|
||||
|
||||
display.set_pen(GREY)
|
||||
|
||||
for a in range(60):
|
||||
tick_mark = Rectangle(int(WIDTH / 2) - 3, 10, 6, int(HEIGHT / 48))
|
||||
vector.rotate(tick_mark, 360 / 60.0 * a, int(WIDTH / 2), int(HEIGHT / 2))
|
||||
vector.translate(tick_mark, 0, 2)
|
||||
vector.draw(tick_mark)
|
||||
|
||||
for a in range(12):
|
||||
hour_mark = Rectangle(int(WIDTH / 2) - 5, 10, 10, int(HEIGHT / 10))
|
||||
vector.rotate(hour_mark, 360 / 12.0 * a, int(WIDTH / 2), int(HEIGHT / 2))
|
||||
vector.translate(hour_mark, 0, 2)
|
||||
vector.draw(hour_mark)
|
||||
|
||||
angle_second = second * 6
|
||||
second_hand_length = int(HEIGHT / 2) - int(HEIGHT / 8)
|
||||
second_hand = Polygon((-2, -second_hand_length), (-2, int(HEIGHT / 8)), (2, int(HEIGHT / 8)), (2, -second_hand_length))
|
||||
vector.rotate(second_hand, angle_second, 0, 0)
|
||||
vector.translate(second_hand, int(WIDTH / 2), int(HEIGHT / 2) + 5)
|
||||
|
||||
angle_minute = minute * 6
|
||||
angle_minute += second / 10.0
|
||||
minute_hand_length = int(HEIGHT / 2) - int(HEIGHT / 24)
|
||||
minute_hand = Polygon((-5, -minute_hand_length), (-10, int(HEIGHT / 16)), (10, int(HEIGHT / 16)), (5, -minute_hand_length))
|
||||
vector.rotate(minute_hand, angle_minute, 0, 0)
|
||||
vector.translate(minute_hand, int(WIDTH / 2), int(HEIGHT / 2) + 5)
|
||||
|
||||
angle_hour = (hour % 12) * 30
|
||||
angle_hour += minute / 2
|
||||
hour_hand_length = int(HEIGHT / 2) - int(HEIGHT / 8)
|
||||
hour_hand = Polygon((-5, -hour_hand_length), (-10, int(HEIGHT / 16)), (10, int(HEIGHT / 16)), (5, -hour_hand_length))
|
||||
vector.rotate(hour_hand, angle_hour, 0, 0)
|
||||
vector.translate(hour_hand, int(WIDTH / 2), int(HEIGHT / 2) + 5)
|
||||
|
||||
display.set_pen(GREY)
|
||||
|
||||
vector.draw(minute_hand)
|
||||
vector.draw(hour_hand)
|
||||
vector.draw(second_hand)
|
||||
|
||||
display.set_pen(BLACK)
|
||||
|
||||
for a in range(60):
|
||||
tick_mark = Rectangle(int(WIDTH / 2) - 3, 10, 6, int(HEIGHT / 48))
|
||||
vector.rotate(tick_mark, 360 / 60.0 * a, int(WIDTH / 2), int(HEIGHT / 2))
|
||||
vector.draw(tick_mark)
|
||||
|
||||
for a in range(12):
|
||||
hour_mark = Rectangle(int(WIDTH / 2) - 5, 10, 10, int(HEIGHT / 10))
|
||||
vector.rotate(hour_mark, 360 / 12.0 * a, int(WIDTH / 2), int(HEIGHT / 2))
|
||||
vector.draw(hour_mark)
|
||||
|
||||
vector.translate(minute_hand, 0, -5)
|
||||
vector.translate(hour_hand, 0, -5)
|
||||
vector.draw(minute_hand)
|
||||
vector.draw(hour_hand)
|
||||
|
||||
display.set_pen(RED)
|
||||
vector.translate(second_hand, 0, -5)
|
||||
vector.draw(second_hand)
|
||||
vector.draw(hub)
|
||||
|
||||
display.update()
|
||||
gc.collect()
|
||||
|
||||
t_end = time.ticks_ms()
|
||||
print(f"Took {t_end - t_start}ms")
|
|
@ -0,0 +1,119 @@
|
|||
import math
|
||||
import time
|
||||
from pimoroni_i2c import PimoroniI2C
|
||||
from breakout_as7262 import BreakoutAS7262
|
||||
from picographics import PicoGraphics, DISPLAY_PICO_W_EXPLORER, PEN_RGB332
|
||||
from picovector import PicoVector, Polygon, RegularPolygon, ANTIALIAS_X4
|
||||
|
||||
PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5}
|
||||
PINS_PICO_EXPLORER = {"sda": 20, "scl": 21}
|
||||
i2c = PimoroniI2C(**PINS_PICO_EXPLORER)
|
||||
|
||||
# Set up the AS7262 Spectrometer
|
||||
as7262 = BreakoutAS7262(i2c)
|
||||
as7262.set_gain(BreakoutAS7262.X16)
|
||||
as7262.set_measurement_mode(BreakoutAS7262.CONT_ROYGBR)
|
||||
as7262.set_illumination_current(BreakoutAS7262.MA12)
|
||||
as7262.set_indicator_current(BreakoutAS7262.MA4)
|
||||
as7262.set_leds(True, True)
|
||||
|
||||
# Set up the display
|
||||
display = PicoGraphics(DISPLAY_PICO_W_EXPLORER, pen_type=PEN_RGB332)
|
||||
display.set_backlight(0.8)
|
||||
|
||||
# Set up PicoVector
|
||||
vector = PicoVector(display)
|
||||
vector.set_antialiasing(ANTIALIAS_X4)
|
||||
|
||||
# Load an Alright Font
|
||||
result = vector.set_font("/AdvRe.af", 30)
|
||||
|
||||
WIDTH, HEIGHT = display.get_bounds()
|
||||
|
||||
CENTER_X = int(WIDTH / 2)
|
||||
CENTER_Y = int(HEIGHT / 2)
|
||||
|
||||
RADIUS = 90
|
||||
DEBUG = False
|
||||
|
||||
RED = display.create_pen(255, 0, 0)
|
||||
ORANGE = display.create_pen(255, 128, 0)
|
||||
YELLOW = display.create_pen(255, 255, 0)
|
||||
GREEN = display.create_pen(0, 255, 0)
|
||||
BLUE = display.create_pen(0, 0, 255)
|
||||
VIOLET = display.create_pen(255, 0, 255)
|
||||
|
||||
BLACK = display.create_pen(0, 0, 0)
|
||||
GREY = display.create_pen(128, 128, 128)
|
||||
WHITE = display.create_pen(255, 255, 255)
|
||||
|
||||
LABELS = ["R", "O", "Y", "G", "B", "V"]
|
||||
COLS = [RED, ORANGE, YELLOW, GREEN, BLUE, VIOLET]
|
||||
|
||||
|
||||
# Custom regular_polygon function to give each point its own "radius"
|
||||
def regular_polygon(o_x, o_y, radius, rotation):
|
||||
sides = 6
|
||||
angle = math.radians(360 / sides)
|
||||
rotation = math.radians(rotation)
|
||||
|
||||
points = []
|
||||
|
||||
for side in range(sides):
|
||||
current_angle = side * angle + rotation
|
||||
x = math.cos(current_angle) * radius[side]
|
||||
y = math.sin(current_angle) * radius[side]
|
||||
points.append((int(x) + o_x, int(y) + o_y))
|
||||
|
||||
return points
|
||||
|
||||
|
||||
lines = RegularPolygon(CENTER_X, CENTER_Y, 6, RADIUS)
|
||||
label_points = list(RegularPolygon(CENTER_X, CENTER_Y, 6, RADIUS * 0.7, -(360 / 12)))
|
||||
|
||||
|
||||
while True:
|
||||
# Clear to black
|
||||
display.set_pen(BLACK)
|
||||
display.clear()
|
||||
|
||||
# Add the title
|
||||
display.set_pen(WHITE)
|
||||
vector.text("Spectrograph", 5, -5)
|
||||
|
||||
# Get the spectrometer readings
|
||||
reading = list(as7262.read())
|
||||
|
||||
# Print out the readings
|
||||
if DEBUG:
|
||||
for i in range(6):
|
||||
print(f"{LABELS[i]}: {reading[i]:0.2f}", end=" ")
|
||||
print("")
|
||||
|
||||
# Draw the lines separating each section
|
||||
display.set_pen(GREY)
|
||||
for (x, y) in lines:
|
||||
display.line(CENTER_X, CENTER_Y, int(x), int(y))
|
||||
|
||||
# Scale readings for display
|
||||
for i in range(6):
|
||||
reading[i] = int(reading[i] / 3.0)
|
||||
reading[i] = min(reading[i], RADIUS)
|
||||
|
||||
# Create a 6 point polygon with each points distance from the center
|
||||
# scaled by the corresponding reading.
|
||||
points = regular_polygon(CENTER_X, CENTER_Y, reading, 0)
|
||||
|
||||
# Split the polygon into six triangles, one for each channel
|
||||
# draw each one, along with its corresponding label
|
||||
point_a = points[-1]
|
||||
for i in range(6):
|
||||
point_b = points[i]
|
||||
label_x, label_y = label_points[i]
|
||||
display.set_pen(COLS[i])
|
||||
vector.text(LABELS[i], int(label_x) - 5, int(label_y) - 20)
|
||||
vector.draw(Polygon(point_a, point_b, (CENTER_X, CENTER_Y)))
|
||||
point_a = point_b
|
||||
|
||||
display.update()
|
||||
time.sleep(1.0 / 60)
|
|
@ -0,0 +1,57 @@
|
|||
import time
|
||||
import math
|
||||
from picographics import PicoGraphics, DISPLAY_TUFTY_2040, PEN_RGB332
|
||||
|
||||
display = PicoGraphics(DISPLAY_TUFTY_2040, pen_type=PEN_RGB332)
|
||||
display.set_backlight(1.0)
|
||||
|
||||
WIDTH, HEIGHT = display.get_bounds()
|
||||
|
||||
BLACK = display.create_pen(0, 0, 0)
|
||||
|
||||
|
||||
def scaled_sine(start, finish, speed):
|
||||
s = math.sin(time.ticks_ms() / speed)
|
||||
s += 1 # -1 to +1 to 0 to 2
|
||||
s /= 2.0 # 0 to 2 to 0 to 1
|
||||
s *= finish - start
|
||||
s += start
|
||||
return s
|
||||
|
||||
|
||||
def regular_polygon(o_x, o_y, sides, radius, rotation):
|
||||
angle = math.radians(360 / sides)
|
||||
rotation = math.radians(rotation)
|
||||
|
||||
points = []
|
||||
|
||||
for side in range(sides):
|
||||
current_angle = side * angle + rotation
|
||||
x = math.cos(current_angle) * radius
|
||||
y = math.sin(current_angle) * radius
|
||||
points.append((int(x) + o_x, int(y) + o_y))
|
||||
|
||||
return points
|
||||
|
||||
|
||||
while True:
|
||||
sides = int(scaled_sine(3, 10, 500))
|
||||
rotation = time.ticks_ms() / 10
|
||||
display.set_pen(BLACK)
|
||||
display.clear()
|
||||
|
||||
for y in range(HEIGHT):
|
||||
p = display.create_pen_hsv(y / HEIGHT, 1.0, 1.0)
|
||||
display.set_pen(p)
|
||||
display.line(0, y, WIDTH, y)
|
||||
|
||||
display.set_pen(BLACK)
|
||||
|
||||
points_a = regular_polygon(int(WIDTH / 2), int(HEIGHT / 2), sides, 100, rotation)
|
||||
points_b = regular_polygon(int(WIDTH / 2), int(HEIGHT / 2), sides, 50, -rotation)
|
||||
|
||||
display.pretty_polygon(points_a, points_b)
|
||||
|
||||
display.update()
|
||||
|
||||
time.sleep(1.0 / 60)
|
|
@ -10,6 +10,7 @@ include(picographics/micropython)
|
|||
# Pico Graphics Extra
|
||||
include(pngdec/micropython)
|
||||
include(jpegdec/micropython)
|
||||
include(picovector/micropython)
|
||||
include(qrcode/micropython/micropython)
|
||||
|
||||
# Sensors & Breakouts
|
||||
|
|
|
@ -14,6 +14,7 @@ include(pimoroni_bus/micropython)
|
|||
# Pico Graphics Essential
|
||||
include(hershey_fonts/micropython)
|
||||
include(bitmap_fonts/micropython)
|
||||
include(picovector/micropython)
|
||||
include(picographics/micropython)
|
||||
|
||||
# Pico Graphics Extra
|
||||
|
|
|
@ -298,17 +298,17 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
|
|||
pimoroni::ParallelPins parallel_bus = {10, 11, 12, 13, 14, 2}; // Default for Tufty 2040 parallel
|
||||
pimoroni::I2C *i2c_bus = nullptr;
|
||||
|
||||
if (mp_obj_is_type(args[ARG_bus].u_obj, &SPIPins_type)) {
|
||||
if (mp_obj_is_exact_type(args[ARG_bus].u_obj, &SPIPins_type)) {
|
||||
if(bus_type != BUS_SPI) mp_raise_ValueError("unexpected SPI bus!");
|
||||
_PimoroniBus_obj_t *bus = (_PimoroniBus_obj_t *)MP_OBJ_TO_PTR(args[ARG_bus].u_obj);
|
||||
spi_bus = *(SPIPins *)(bus->pins);
|
||||
|
||||
} else if (mp_obj_is_type(args[ARG_bus].u_obj, &ParallelPins_type)) {
|
||||
} else if (mp_obj_is_exact_type(args[ARG_bus].u_obj, &ParallelPins_type)) {
|
||||
if(bus_type != BUS_PARALLEL) mp_raise_ValueError("unexpected Parallel bus!");
|
||||
_PimoroniBus_obj_t *bus = (_PimoroniBus_obj_t *)MP_OBJ_TO_PTR(args[ARG_bus].u_obj);
|
||||
parallel_bus = *(ParallelPins *)(bus->pins);
|
||||
|
||||
} else if (mp_obj_is_type(args[ARG_bus].u_obj, &PimoroniI2C_type) || MP_OBJ_IS_TYPE(args[ARG_bus].u_obj, &machine_i2c_type)) {
|
||||
} else if (mp_obj_is_exact_type(args[ARG_bus].u_obj, &PimoroniI2C_type) || mp_obj_is_exact_type(args[ARG_bus].u_obj, &machine_i2c_type)) {
|
||||
if(bus_type != BUS_I2C) mp_raise_ValueError("unexpected I2C bus!");
|
||||
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_bus].u_obj);
|
||||
i2c_bus = (pimoroni::I2C *)(self->i2c->i2c);
|
||||
|
@ -441,7 +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);
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
|
@ -807,7 +806,7 @@ mp_obj_t ModPicoGraphics_set_palette(size_t n_args, const mp_obj_t *pos_args, mp
|
|||
|
||||
// Check if there is only one argument, which might be a list
|
||||
if(n_args == 2) {
|
||||
if(mp_obj_is_type(pos_args[1], &mp_type_list)) {
|
||||
if(mp_obj_is_exact_type(pos_args[1], &mp_type_list)) {
|
||||
mp_obj_list_t *points = MP_OBJ_TO_PTR2(pos_args[1], mp_obj_list_t);
|
||||
|
||||
if(points->len <= 0) mp_raise_ValueError("set_palette(): cannot provide an empty list");
|
||||
|
@ -822,7 +821,7 @@ mp_obj_t ModPicoGraphics_set_palette(size_t n_args, const mp_obj_t *pos_args, mp
|
|||
|
||||
for(size_t i = 0; i < num_tuples; i++) {
|
||||
mp_obj_t obj = tuples[i];
|
||||
if(!mp_obj_is_type(obj, &mp_type_tuple)) mp_raise_ValueError("set_palette(): can't convert object to tuple");
|
||||
if(!mp_obj_is_exact_type(obj, &mp_type_tuple)) mp_raise_ValueError("set_palette(): can't convert object to tuple");
|
||||
|
||||
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR2(obj, mp_obj_tuple_t);
|
||||
|
||||
|
@ -1027,7 +1026,7 @@ mp_obj_t ModPicoGraphics_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map
|
|||
|
||||
// Check if there is only one argument, which might be a list
|
||||
if(n_args == 2) {
|
||||
if(mp_obj_is_type(pos_args[1], &mp_type_list)) {
|
||||
if(mp_obj_is_exact_type(pos_args[1], &mp_type_list)) {
|
||||
mp_obj_list_t *points = MP_OBJ_TO_PTR2(pos_args[1], mp_obj_list_t);
|
||||
|
||||
if(points->len <= 0) mp_raise_ValueError("poly(): cannot provide an empty list");
|
||||
|
@ -1044,7 +1043,7 @@ mp_obj_t ModPicoGraphics_polygon(size_t n_args, const mp_obj_t *pos_args, mp_map
|
|||
std::vector<Point> points;
|
||||
for(size_t i = 0; i < num_tuples; i++) {
|
||||
mp_obj_t obj = tuples[i];
|
||||
if(!mp_obj_is_type(obj, &mp_type_tuple)) mp_raise_ValueError("poly(): can't convert object to tuple");
|
||||
if(!mp_obj_is_exact_type(obj, &mp_type_tuple)) mp_raise_ValueError("poly(): can't convert object to tuple");
|
||||
|
||||
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR2(obj, mp_obj_tuple_t);
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
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
|
||||
MODULE_PICOVECTOR_ENABLED=1
|
||||
)
|
||||
|
||||
target_link_libraries(usermod INTERFACE usermod_picovector)
|
|
@ -0,0 +1,128 @@
|
|||
#include "picovector.h"
|
||||
|
||||
/* Polygon */
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(POLYGON__del__obj, POLYGON__del__);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(POLYGON_centroid_obj, POLYGON_centroid);
|
||||
|
||||
|
||||
STATIC const mp_rom_map_elem_t POLYGON_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&POLYGON__del__obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_centroid), MP_ROM_PTR(&POLYGON_centroid_obj) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(POLYGON_locals_dict, POLYGON_locals_dict_table);
|
||||
|
||||
#ifdef MP_DEFINE_CONST_OBJ_TYPE
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
POLYGON_type,
|
||||
MP_QSTR_polygon,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
make_new, POLYGON_make_new,
|
||||
print, POLYGON_print,
|
||||
iter, POLYGON_getiter,
|
||||
locals_dict, (mp_obj_dict_t*)&POLYGON_locals_dict
|
||||
);
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
REGULAR_POLYGON_type,
|
||||
MP_QSTR_regular_polygon,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
make_new, REGULAR_POLYGON_make_new,
|
||||
locals_dict, (mp_obj_dict_t*)&POLYGON_locals_dict
|
||||
);
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
RECTANGLE_type,
|
||||
MP_QSTR_rectangle,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
make_new, RECTANGLE_make_new,
|
||||
locals_dict, (mp_obj_dict_t*)&POLYGON_locals_dict
|
||||
);
|
||||
#else
|
||||
const mp_obj_type_t POLYGON_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_polygon,
|
||||
.make_new = POLYGON_make_new,
|
||||
.print = POLYGON_print,
|
||||
.iter = POLYGON_getiter,
|
||||
.locals_dict = (mp_obj_dict_t*)&POLYGON_locals_dict,
|
||||
};
|
||||
const mp_obj_type_t REGULAR_POLYGON_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_regular_polygon,
|
||||
.make_new = REGULAR_POLYGON_make_new,
|
||||
.locals_dict = (mp_obj_dict_t*)&POLYGON_locals_dict,
|
||||
};
|
||||
const mp_obj_type_t RECTANGLE_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_rectangle,
|
||||
.make_new = RECTANGLE_make_new,
|
||||
.locals_dict = (mp_obj_dict_t*)&POLYGON_locals_dict,
|
||||
};
|
||||
#endif
|
||||
|
||||
/* PicoVector */
|
||||
|
||||
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);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_set_font_size_obj, VECTOR_set_font_size);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_set_antialiasing_obj, VECTOR_set_antialiasing);
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(VECTOR_draw_obj, 2, VECTOR_draw);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(VECTOR_rotate_obj, 3, VECTOR_rotate);
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(VECTOR_translate_obj, 4, VECTOR_translate);
|
||||
|
||||
STATIC const mp_rom_map_elem_t VECTOR_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_set_font), MP_ROM_PTR(&VECTOR_set_font_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_set_font_size), MP_ROM_PTR(&VECTOR_set_font_size_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_set_antialiasing), MP_ROM_PTR(&VECTOR_set_antialiasing_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&VECTOR_text_obj) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_draw), MP_ROM_PTR(&VECTOR_draw_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_rotate), MP_ROM_PTR(&VECTOR_rotate_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_translate), MP_ROM_PTR(&VECTOR_translate_obj) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(VECTOR_locals_dict, VECTOR_locals_dict_table);
|
||||
|
||||
#ifdef MP_DEFINE_CONST_OBJ_TYPE
|
||||
MP_DEFINE_CONST_OBJ_TYPE(
|
||||
VECTOR_type,
|
||||
MP_QSTR_picovector,
|
||||
MP_TYPE_FLAG_NONE,
|
||||
make_new, VECTOR_make_new,
|
||||
locals_dict, (mp_obj_dict_t*)&VECTOR_locals_dict
|
||||
);
|
||||
#else
|
||||
const mp_obj_type_t VECTOR_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_picovector,
|
||||
.make_new = VECTOR_make_new,
|
||||
.locals_dict = (mp_obj_dict_t*)&VECTOR_locals_dict,
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Module */
|
||||
|
||||
STATIC const mp_map_elem_t VECTOR_globals_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_picovector) },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_PicoVector), (mp_obj_t)&VECTOR_type },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_Polygon), (mp_obj_t)&POLYGON_type },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_RegularPolygon), (mp_obj_t)®ULAR_POLYGON_type },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_Rectangle), (mp_obj_t)&RECTANGLE_type },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ANTIALIAS_NONE), MP_ROM_INT(0) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ANTIALIAS_X4), MP_ROM_INT(1) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ANTIALIAS_X16), MP_ROM_INT(2) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_VECTOR_globals, VECTOR_globals_table);
|
||||
|
||||
const mp_obj_module_t VECTOR_user_cmodule = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t*)&mp_module_VECTOR_globals,
|
||||
};
|
||||
|
||||
#if MICROPY_VERSION <= 70144
|
||||
MP_REGISTER_MODULE(MP_QSTR_picovector, VECTOR_user_cmodule, MODULE_PICOVECTOR_ENABLED);
|
||||
#else
|
||||
MP_REGISTER_MODULE(MP_QSTR_picovector, VECTOR_user_cmodule);
|
||||
#endif
|
|
@ -0,0 +1,459 @@
|
|||
#include "micropython/modules/util.hpp"
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "libraries/pico_vector/pico_vector.hpp"
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
extern "C" {
|
||||
#include "picovector.h"
|
||||
#include "micropython/modules/picographics/picographics.h"
|
||||
#include "py/stream.h"
|
||||
#include "py/reader.h"
|
||||
#include "extmod/vfs.h"
|
||||
|
||||
typedef struct _ModPicoGraphics_obj_t {
|
||||
mp_obj_base_t base;
|
||||
PicoGraphics *graphics;
|
||||
DisplayDriver *display;
|
||||
void *buffer;
|
||||
} ModPicoGraphics_obj_t;
|
||||
|
||||
typedef struct _VECTOR_obj_t {
|
||||
mp_obj_base_t base;
|
||||
void *mem;
|
||||
PicoVector *vector;
|
||||
} _VECTOR_obj_t;
|
||||
|
||||
typedef struct _POLYGON_obj_t {
|
||||
mp_obj_base_t base;
|
||||
pretty_poly::contour_t<picovector_point_type> contour;
|
||||
} _POLYGON_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");
|
||||
}
|
||||
|
||||
/* POLYGON */
|
||||
|
||||
mp_obj_t RECTANGLE_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||
enum { ARG_x, ARG_y, ARG_w, ARG_h };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT },
|
||||
{ MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT },
|
||||
{ MP_QSTR_w, MP_ARG_REQUIRED | MP_ARG_INT },
|
||||
{ MP_QSTR_h, MP_ARG_REQUIRED | MP_ARG_INT },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
_POLYGON_obj_t *self = m_new_obj_with_finaliser(_POLYGON_obj_t);
|
||||
self->base.type = &POLYGON_type;
|
||||
|
||||
int x = args[ARG_x].u_int;
|
||||
int y = args[ARG_y].u_int;
|
||||
int w = args[ARG_w].u_int;
|
||||
int h = args[ARG_h].u_int;
|
||||
|
||||
self->contour.points = m_new(pretty_poly::point_t<picovector_point_type>, 4);
|
||||
self->contour.count = 4;
|
||||
|
||||
self->contour.points[0] = {picovector_point_type(x), picovector_point_type(y)};
|
||||
self->contour.points[1] = {picovector_point_type(x + w), picovector_point_type(y)};
|
||||
self->contour.points[2] = {picovector_point_type(x + w), picovector_point_type(y + h)};
|
||||
self->contour.points[3] = {picovector_point_type(x), picovector_point_type(y + h)};
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
mp_obj_t REGULAR_POLYGON_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||
enum { ARG_x, ARG_y, ARG_sides, ARG_radius, ARG_rotation };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT },
|
||||
{ MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT },
|
||||
{ MP_QSTR_sides, MP_ARG_REQUIRED | MP_ARG_INT },
|
||||
{ MP_QSTR_radius, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
{ MP_QSTR_rotation, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||
};
|
||||
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
_POLYGON_obj_t *self = m_new_obj_with_finaliser(_POLYGON_obj_t);
|
||||
self->base.type = &POLYGON_type;
|
||||
|
||||
Point origin(args[ARG_x].u_int, args[ARG_y].u_int);
|
||||
unsigned int sides = args[ARG_sides].u_int;
|
||||
float radius = mp_obj_get_float(args[ARG_radius].u_obj);
|
||||
float rotation = 0.0f;
|
||||
if (args[ARG_rotation].u_obj != mp_const_none) {
|
||||
rotation = mp_obj_get_float(args[ARG_rotation].u_obj);
|
||||
rotation *= (M_PI / 180.0f);
|
||||
}
|
||||
int o_x = args[ARG_x].u_int;
|
||||
int o_y = args[ARG_y].u_int;
|
||||
|
||||
float angle = (360.0f / sides) * (M_PI / 180.0f);
|
||||
|
||||
self->contour.points = m_new(pretty_poly::point_t<picovector_point_type>, sides);
|
||||
self->contour.count = sides;
|
||||
|
||||
for(auto s = 0u; s < sides; s++) {
|
||||
float current_angle = angle * s + rotation;
|
||||
self->contour.points[s] = {
|
||||
(picovector_point_type)(cos(current_angle) * radius) + o_x,
|
||||
(picovector_point_type)(sin(current_angle) * radius) + o_y
|
||||
};
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
mp_obj_t POLYGON_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||
_POLYGON_obj_t *self = m_new_obj_with_finaliser(_POLYGON_obj_t);
|
||||
self->base.type = &POLYGON_type;
|
||||
|
||||
size_t num_points = n_args;
|
||||
const mp_obj_t *points = all_args;
|
||||
|
||||
if(num_points < 3) mp_raise_ValueError("Polygon: At least 3 points required.");
|
||||
|
||||
self->contour.points = m_new(pretty_poly::point_t<picovector_point_type>, num_points);
|
||||
self->contour.count = num_points;
|
||||
|
||||
for(auto i = 0u; i < num_points; i++) {
|
||||
mp_obj_t c_obj = points[i];
|
||||
|
||||
if(!mp_obj_is_exact_type(c_obj, &mp_type_tuple)) mp_raise_ValueError("Not a tuple");
|
||||
|
||||
mp_obj_tuple_t *t_point = MP_OBJ_TO_PTR2(c_obj, mp_obj_tuple_t);
|
||||
|
||||
if(t_point->len != 2) mp_raise_ValueError("Tuple must have X, Y");
|
||||
|
||||
self->contour.points[i] = {
|
||||
(picovector_point_type)mp_obj_get_int(t_point->items[0]),
|
||||
(picovector_point_type)mp_obj_get_int(t_point->items[1]),
|
||||
};
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
mp_obj_t POLYGON_centroid(mp_obj_t self_in) {
|
||||
_POLYGON_obj_t *self = MP_OBJ_TO_PTR2(self_in, _POLYGON_obj_t);
|
||||
|
||||
pretty_poly::point_t<picovector_point_type> sum(0, 0);
|
||||
|
||||
for(auto i = 0u; i < self->contour.count; i++) {
|
||||
sum += self->contour.points[i];
|
||||
}
|
||||
|
||||
sum /= (float)self->contour.count;
|
||||
|
||||
mp_obj_t tuple[2];
|
||||
tuple[0] = mp_obj_new_int((int)(sum.x));
|
||||
tuple[1] = mp_obj_new_int((int)(sum.y));
|
||||
|
||||
return mp_obj_new_tuple(2, tuple);
|
||||
}
|
||||
|
||||
void POLYGON_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
(void)kind;
|
||||
_POLYGON_obj_t *self = MP_OBJ_TO_PTR2(self_in, _POLYGON_obj_t);
|
||||
|
||||
mp_print_str(print, "Polygon(points = ");
|
||||
mp_obj_print_helper(print, mp_obj_new_int(self->contour.count), PRINT_REPR);
|
||||
mp_print_str(print, ", bounds = ");
|
||||
mp_obj_print_helper(print, mp_obj_new_int(self->contour.bounds().x), PRINT_REPR);
|
||||
mp_print_str(print, ", ");
|
||||
mp_obj_print_helper(print, mp_obj_new_int(self->contour.bounds().y), PRINT_REPR);
|
||||
mp_print_str(print, ", ");
|
||||
mp_obj_print_helper(print, mp_obj_new_int(self->contour.bounds().w), PRINT_REPR);
|
||||
mp_print_str(print, ", ");
|
||||
mp_obj_print_helper(print, mp_obj_new_int(self->contour.bounds().h), PRINT_REPR);
|
||||
mp_print_str(print, ")");
|
||||
}
|
||||
|
||||
mp_obj_t POLYGON__del__(mp_obj_t self_in) {
|
||||
_POLYGON_obj_t *self = MP_OBJ_TO_PTR2(self_in, _POLYGON_obj_t);
|
||||
(void)self;
|
||||
// TODO: Do we actually need to free anything here, if it's on GC heap it should get collected
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
typedef struct _mp_obj_polygon_it_t {
|
||||
mp_obj_base_t base;
|
||||
mp_fun_1_t iternext;
|
||||
mp_obj_t polygon;
|
||||
size_t cur;
|
||||
} mp_obj_polygon_it_t;
|
||||
|
||||
STATIC mp_obj_t py_image_it_iternext(mp_obj_t self_in) {
|
||||
mp_obj_polygon_it_t *self = MP_OBJ_TO_PTR2(self_in, mp_obj_polygon_it_t);
|
||||
_POLYGON_obj_t *polygon = MP_OBJ_TO_PTR2(self->polygon, _POLYGON_obj_t);
|
||||
|
||||
//mp_printf(&mp_plat_print, "points: %d, current: %d\n", polygon->contour.count, self->cur);
|
||||
|
||||
if(self->cur >= polygon->contour.count) return MP_OBJ_STOP_ITERATION;
|
||||
|
||||
mp_obj_t tuple[2];
|
||||
tuple[0] = mp_obj_new_int((int)(polygon->contour.points[self->cur].x));
|
||||
tuple[1] = mp_obj_new_int((int)(polygon->contour.points[self->cur].y));
|
||||
|
||||
self->cur++;
|
||||
return mp_obj_new_tuple(2, tuple);
|
||||
}
|
||||
|
||||
mp_obj_t POLYGON_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) {
|
||||
mp_obj_polygon_it_t *o = (mp_obj_polygon_it_t *)iter_buf;
|
||||
o->base.type = &mp_type_polymorph_iter;
|
||||
o->iternext = py_image_it_iternext;
|
||||
o->polygon = o_in;
|
||||
o->cur = 0;
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
|
||||
/* VECTOR */
|
||||
|
||||
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
|
||||
};
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_picographics, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
if(!MP_OBJ_IS_TYPE(args[ARG_picographics].u_obj, &ModPicoGraphics_type)) mp_raise_ValueError(MP_ERROR_TEXT("PicoGraphics Object Required"));
|
||||
|
||||
_VECTOR_obj_t *self = m_new_obj(_VECTOR_obj_t);
|
||||
self->base.type = &VECTOR_type;
|
||||
ModPicoGraphics_obj_t *graphics = (ModPicoGraphics_obj_t *)MP_OBJ_TO_PTR(args[ARG_picographics].u_obj);
|
||||
|
||||
// The PicoVector class calls `pretty_poly::init()` with the memory region
|
||||
// it does not store a pointer to this, so we need to store one ourselves
|
||||
self->mem = m_new(uint8_t, PicoVector::pretty_poly_buffer_size());
|
||||
|
||||
self->vector = m_new_class(PicoVector, graphics->graphics, self->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_set_font_size(mp_obj_t self_in, 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);
|
||||
self->vector->set_font_size(font_size);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t VECTOR_set_antialiasing(mp_obj_t self_in, mp_obj_t aa) {
|
||||
_VECTOR_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VECTOR_obj_t);
|
||||
|
||||
self->vector->set_antialiasing((pretty_poly::antialias_t)mp_obj_get_int(aa));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
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, ARG_angle };
|
||||
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_QSTR_angle, MP_ARG_OBJ, {.u_obj = mp_const_none} }
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
if(args[ARG_angle].u_obj == mp_const_none) {
|
||||
self->vector->text(t, Point(x, y));
|
||||
} else {
|
||||
self->vector->text(t, Point(x, y), mp_obj_get_float(args[ARG_angle].u_obj));
|
||||
}
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t VECTOR_rotate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_self, ARG_polygon, ARG_angle, ARG_origin_x, ARG_origin_y };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
{ MP_QSTR_polygon, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
{ MP_QSTR_angle, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
{ MP_QSTR_origin_x, MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_origin_y, MP_ARG_INT, {.u_int = 0} }
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
if(!MP_OBJ_IS_TYPE(args[ARG_polygon].u_obj, &POLYGON_type)) mp_raise_TypeError("rotate: polygon required");
|
||||
|
||||
_POLYGON_obj_t *poly = MP_OBJ_TO_PTR2(args[ARG_polygon].u_obj, _POLYGON_obj_t);
|
||||
|
||||
Point origin = Point(args[ARG_origin_x].u_int, args[ARG_origin_y].u_int);
|
||||
|
||||
float angle = mp_obj_get_float(args[ARG_angle].u_obj);
|
||||
|
||||
self->vector->rotate(poly->contour, origin, angle);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t VECTOR_translate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_self, ARG_polygon, ARG_x, ARG_y };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
{ MP_QSTR_polygon, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
{ MP_QSTR_x, MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_y, MP_ARG_INT, {.u_int = 0} }
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
if(!MP_OBJ_IS_TYPE(args[ARG_polygon].u_obj, &POLYGON_type)) mp_raise_TypeError("rotate: polygon required");
|
||||
|
||||
_POLYGON_obj_t *poly = MP_OBJ_TO_PTR2(args[ARG_polygon].u_obj, _POLYGON_obj_t);
|
||||
|
||||
Point translate = Point(args[ARG_x].u_int, args[ARG_y].u_int);
|
||||
|
||||
self->vector->translate(poly->contour, translate);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
mp_obj_t VECTOR_draw(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
|
||||
size_t num_polygons = n_args - 1;
|
||||
const mp_obj_t *polygons = pos_args + 1;
|
||||
|
||||
_VECTOR_obj_t *self = MP_OBJ_TO_PTR2(pos_args[0], _VECTOR_obj_t);
|
||||
|
||||
std::vector<pretty_poly::contour_t<picovector_point_type>> contours;
|
||||
|
||||
for(auto i = 0u; i < num_polygons; i++) {
|
||||
mp_obj_t poly_obj = polygons[i];
|
||||
|
||||
if(!MP_OBJ_IS_TYPE(poly_obj, &POLYGON_type)) mp_raise_TypeError("draw: Polygon required.");
|
||||
|
||||
_POLYGON_obj_t *poly = MP_OBJ_TO_PTR2(poly_obj, _POLYGON_obj_t);
|
||||
contours.emplace_back(poly->contour.points, poly->contour.count);
|
||||
}
|
||||
|
||||
self->vector->polygon(contours);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#include "py/runtime.h"
|
||||
#include "py/objstr.h"
|
||||
|
||||
extern const mp_obj_type_t VECTOR_type;
|
||||
extern const mp_obj_type_t POLYGON_type;
|
||||
extern const mp_obj_type_t REGULAR_POLYGON_type;
|
||||
extern const mp_obj_type_t RECTANGLE_type;
|
||||
|
||||
extern mp_obj_t POLYGON_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 REGULAR_POLYGON_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 RECTANGLE_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args);
|
||||
extern void POLYGON_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind);
|
||||
extern mp_obj_t POLYGON_centroid(mp_obj_t self_in);
|
||||
extern mp_obj_t POLYGON_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf);
|
||||
|
||||
extern mp_obj_t POLYGON__del__(mp_obj_t self_in);
|
||||
|
||||
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_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);
|
||||
extern mp_obj_t VECTOR_set_font_size(mp_obj_t self_in, mp_obj_t size);
|
||||
extern mp_obj_t VECTOR_set_antialiasing(mp_obj_t self_in, mp_obj_t aa);
|
||||
|
||||
extern mp_obj_t VECTOR_draw(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
|
||||
extern mp_obj_t VECTOR_rotate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
|
||||
extern mp_obj_t VECTOR_translate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
|
|
@ -96,7 +96,7 @@ int32_t pngdec_seek_callback(PNGFILE *png, int32_t p) {
|
|||
void pngdec_open_helper(_PNG_obj_t *self) {
|
||||
int result = -1;
|
||||
|
||||
if(mp_obj_is_str_or_bytes(self->file)){
|
||||
if(mp_obj_is_str(self->file)){
|
||||
GET_STR_DATA_LEN(self->file, str, str_len);
|
||||
|
||||
result = self->png->open(
|
||||
|
@ -188,10 +188,16 @@ MICROPY_EVENT_POLL_HOOK
|
|||
uint8_t i = 0;
|
||||
if(pDraw->iBpp == 8) {
|
||||
i = *pixel++;
|
||||
} else {
|
||||
i = pixel[x / 2];
|
||||
} else if (pDraw->iBpp == 4) {
|
||||
i = *pixel;
|
||||
i >>= (x & 0b1) ? 0 : 4;
|
||||
i &= 0xf;
|
||||
if (x & 1) pixel++;
|
||||
} else {
|
||||
i = *pixel;
|
||||
i >>= 6 - ((x & 0b11) << 1);
|
||||
i &= 0x3;
|
||||
if ((x & 0b11) == 0b11) pixel++;
|
||||
}
|
||||
if(x < target->source.x || x >= target->source.x + target->source.w) continue;
|
||||
// grab the colour from the palette
|
||||
|
@ -264,7 +270,7 @@ mp_obj_t _PNG_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, co
|
|||
self->base.type = &PNG_type;
|
||||
self->png = m_new_class(PNG);
|
||||
|
||||
mp_printf(&mp_plat_print, "PNG RAM %fK\n", sizeof(PNG) / 1024.0f);
|
||||
//mp_printf(&mp_plat_print, "PNG RAM %fK\n", sizeof(PNG) / 1024.0f);
|
||||
|
||||
ModPicoGraphics_obj_t *graphics = (ModPicoGraphics_obj_t *)MP_OBJ_TO_PTR(args[ARG_picographics].u_obj);
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue