kopia lustrzana https://github.com/pimoroni/pimoroni-pico
PicoVector: Rewrite around new C pretty-poly.h.
rodzic
fd4eb165f8
commit
0717c57fe7
|
@ -25,3 +25,6 @@
|
|||
[submodule "drivers/mlx90640/src"]
|
||||
path = drivers/mlx90640/src
|
||||
url = https://github.com/melexis/mlx90640-library
|
||||
[submodule "libraries/pico_vector/pretty_poly"]
|
||||
path = libraries/pico_vector/pretty_poly
|
||||
url = https://github.com/lowfatcode/pretty-poly/
|
||||
|
|
|
@ -62,3 +62,4 @@ add_subdirectory(galactic_unicorn)
|
|||
add_subdirectory(gfx_pack)
|
||||
add_subdirectory(cosmic_unicorn)
|
||||
add_subdirectory(stellar_unicorn)
|
||||
add_subdirectory(pico_w_explorer)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
include(pico_w_explorer_vector.cmake)
|
|
@ -0,0 +1,10 @@
|
|||
add_executable(
|
||||
pico_w_explorer_vector
|
||||
pico_w_explorer_vector.cpp
|
||||
)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(pico_w_explorer_vector pico_stdlib pico_graphics pico_vector st7789)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(pico_w_explorer_vector)
|
|
@ -0,0 +1,51 @@
|
|||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "drivers/st7789/st7789.hpp"
|
||||
#include "libraries/pico_graphics/pico_graphics.hpp"
|
||||
#include "libraries/pico_vector/pico_vector.hpp"
|
||||
|
||||
using namespace pimoroni;
|
||||
|
||||
|
||||
ST7789 st7789(320, 240, ROTATE_0, false, {PIMORONI_SPI_DEFAULT_INSTANCE, 17, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, SPI_DEFAULT_MISO, 9});
|
||||
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
|
||||
PicoVector vector(&graphics);
|
||||
|
||||
int main() {
|
||||
st7789.set_backlight(255);
|
||||
|
||||
Pen WHITE = graphics.create_pen(255, 255, 255);
|
||||
Pen BLACK = graphics.create_pen(0, 0, 0);
|
||||
|
||||
while(true) {
|
||||
graphics.set_pen(BLACK);
|
||||
graphics.clear();
|
||||
|
||||
graphics.set_pen(WHITE);
|
||||
graphics.text("Hello World", Point(0, 0), 320);
|
||||
|
||||
pp_point_t outline[] = {{-128, -128}, {128, -128}, {128, 128}, {-128, 128}};
|
||||
pp_point_t hole[] = {{ -64, 64}, { 64, 64}, { 64, -64}, { -64, -64}};
|
||||
pp_path_t paths[] = {
|
||||
{.points = outline, .count = 4},
|
||||
{.points = hole, .count = 4}
|
||||
};
|
||||
pp_poly_t poly = {.paths = paths, .count = 2};
|
||||
|
||||
vector.rotate(&poly, {0, 0}, 45.0f);
|
||||
vector.translate(&poly, {128, 128});
|
||||
|
||||
vector.draw(&poly);
|
||||
|
||||
pp_mat3_t t = pp_mat3_identity();
|
||||
vector.text("Hello World", {0, 0}, &t);
|
||||
|
||||
// update screen
|
||||
st7789.update(&graphics);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -44,3 +44,4 @@ add_subdirectory(gfx_pack)
|
|||
add_subdirectory(interstate75)
|
||||
add_subdirectory(cosmic_unicorn)
|
||||
add_subdirectory(stellar_unicorn)
|
||||
add_subdirectory(pico_vector)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
include(pico_vector.cmake)
|
|
@ -8,13 +8,11 @@
|
|||
|
||||
#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) {
|
||||
pp_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];
|
||||
|
||||
|
@ -28,59 +26,13 @@ namespace alright_fonts {
|
|||
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, pp_point_t origin, pp_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;
|
||||
|
||||
pretty_poly::draw_polygon<int8_t>(glyph.contours, origin, scale);
|
||||
pp_transform(transform);
|
||||
pp_render(&glyph.contours);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename mat_t>
|
||||
void render_character(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin, mat_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;
|
||||
contours.reserve(glyph.contours.size());
|
||||
|
||||
unsigned int total_points = 0;
|
||||
for(auto i = 0u; i < glyph.contours.size(); i++) {
|
||||
total_points += glyph.contours[i].count;;
|
||||
}
|
||||
|
||||
point_t<int8_t> *points = (point_t<int8_t> *)malloc(sizeof(point_t<int8_t>) * total_points);
|
||||
|
||||
for(auto i = 0u; i < glyph.contours.size(); i++) {
|
||||
const unsigned int count = glyph.contours[i].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);
|
||||
points += count;
|
||||
}
|
||||
|
||||
pretty_poly::draw_polygon<int8_t>(contours, origin, scale);
|
||||
|
||||
free(contours[0].points);
|
||||
}
|
||||
}
|
||||
|
||||
template void render_character<pretty_poly::mat3_t>(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin, pretty_poly::mat3_t transform);
|
||||
template void render_character<pretty_poly::mat2_t>(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin, pretty_poly::mat2_t transform);
|
||||
|
||||
/*
|
||||
load functions
|
||||
*/
|
||||
|
@ -124,6 +76,8 @@ namespace alright_fonts {
|
|||
g.bounds.h = ru8(ifs);
|
||||
g.advance = ru8(ifs);
|
||||
|
||||
g.contours.paths = (pp_path_t *)malloc(sizeof(pp_path_t) * 10);
|
||||
|
||||
if(ifs.fail()) {
|
||||
// could not read glyph dictionary entry
|
||||
return false;
|
||||
|
@ -148,10 +102,11 @@ namespace alright_fonts {
|
|||
|
||||
// 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.paths[g.contours.count].points = (pp_point_t *)malloc(sizeof(pp_point_t) * count);
|
||||
|
||||
g.contours.push_back({points, count});
|
||||
ifs.read((char *)g.contours.paths[g.contours.count].points, sizeof(pp_point_t) * count);
|
||||
|
||||
g.contours.count ++;
|
||||
}
|
||||
|
||||
// return back to position in dictionary
|
||||
|
|
|
@ -6,15 +6,16 @@
|
|||
#include <optional>
|
||||
#include <map>
|
||||
|
||||
#include "pretty_poly.hpp"
|
||||
#include "pretty_poly/pretty-poly.h"
|
||||
#include "file_io.hpp"
|
||||
|
||||
namespace alright_fonts {
|
||||
|
||||
struct glyph_t {
|
||||
uint16_t codepoint;
|
||||
pretty_poly::rect_t bounds;
|
||||
pp_rect_t bounds;
|
||||
uint8_t advance;
|
||||
std::vector<pretty_poly::contour_t<int8_t>> contours;
|
||||
pp_poly_t contours;
|
||||
};
|
||||
|
||||
struct face_t {
|
||||
|
@ -23,10 +24,10 @@ namespace alright_fonts {
|
|||
std::map<uint16_t, glyph_t> glyphs;
|
||||
|
||||
face_t() {};
|
||||
face_t(pretty_poly::file_io &ifs) {load(ifs);}
|
||||
face_t(file_io &ifs) {load(ifs);}
|
||||
face_t(std::string_view path) {load(path);}
|
||||
|
||||
bool load(pretty_poly::file_io &ifs);
|
||||
bool load(file_io &ifs);
|
||||
bool load(std::string_view path);
|
||||
};
|
||||
|
||||
|
@ -48,7 +49,7 @@ namespace alright_fonts {
|
|||
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
|
||||
pp_antialias_t antialiasing = PP_AA_X4; // level of antialiasing to apply
|
||||
|
||||
void set_size(int s) {
|
||||
size = s;
|
||||
|
@ -63,13 +64,11 @@ namespace alright_fonts {
|
|||
/*
|
||||
utility functions
|
||||
*/
|
||||
pretty_poly::rect_t measure_character(text_metrics_t &tm, uint16_t codepoint);
|
||||
pp_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);
|
||||
template<typename mat_t>
|
||||
void render_character(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin, mat_t transform);
|
||||
void render_character(text_metrics_t &tm, uint16_t codepoint, pp_point_t origin, pp_mat3_t *transform);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
#include <string_view>
|
||||
#include <cstdint>
|
||||
|
||||
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();
|
||||
};
|
|
@ -1,9 +1,12 @@
|
|||
if(NOT TARGET pico_graphics)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../pico_graphics/pico_graphics.cmake)
|
||||
endif()
|
||||
|
||||
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 hardware_interp)
|
||||
target_link_libraries(pico_vector pico_graphics pico_stdlib hardware_interp)
|
|
@ -1,141 +1,82 @@
|
|||
#define PP_IMPLEMENTATION
|
||||
#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::settings::clip = {graphics->clip.x, graphics->clip.y, graphics->clip.w, graphics->clip.h};
|
||||
pretty_poly::draw_polygon<picovector_point_type>(
|
||||
contours,
|
||||
pretty_poly::point_t<int>(origin.x, origin.y),
|
||||
scale);
|
||||
PicoGraphics *PicoVector::graphics = nullptr;
|
||||
|
||||
void PicoVector::draw(pp_poly_t *poly) {
|
||||
pp_transform(NULL);
|
||||
pp_render(poly);
|
||||
}
|
||||
|
||||
void PicoVector::rotate(std::vector<pretty_poly::contour_t<picovector_point_type>> &contours, Point origin, float angle) {
|
||||
pretty_poly::point_t<picovector_point_type> t{(picovector_point_type)origin.x, (picovector_point_type)origin.y};
|
||||
angle = (2 * (float)M_PI / 360.f) * angle;
|
||||
pretty_poly::mat2_t r = pretty_poly::mat2_t::rotation(angle);
|
||||
for(auto &contour : contours) {
|
||||
for(auto i = 0u; i < contour.count; i++) {
|
||||
contour.points[i] -= t;
|
||||
contour.points[i] *= r;
|
||||
contour.points[i] += t;
|
||||
}
|
||||
void PicoVector::draw(pp_poly_t *poly, pp_mat3_t *t) {
|
||||
pp_transform(t);
|
||||
pp_render(poly);
|
||||
}
|
||||
|
||||
void PicoVector::rotate(pp_path_t *path, pp_point_t origin, float angle) {
|
||||
pp_mat3_t t = pp_mat3_identity();
|
||||
pp_mat3_translate(&t, -origin.x, -origin.y);
|
||||
pp_mat3_rotate(&t, angle);
|
||||
pp_mat3_translate(&t, origin.x, origin.y);
|
||||
transform(path, &t);
|
||||
}
|
||||
|
||||
void PicoVector::translate(pp_path_t *path, pp_point_t translation) {
|
||||
pp_mat3_t t = pp_mat3_identity();
|
||||
pp_mat3_translate(&t, translation.x, translation.y);
|
||||
transform(path, &t);
|
||||
}
|
||||
|
||||
void PicoVector::transform(pp_path_t *path, pp_mat3_t *t) {
|
||||
for (auto j = 0u; j < path->count; j++) {
|
||||
path->points[j] = pp_point_transform(&path->points[j], t);
|
||||
}
|
||||
}
|
||||
|
||||
void PicoVector::translate(std::vector<pretty_poly::contour_t<picovector_point_type>> &contours, Point translation) {
|
||||
pretty_poly::point_t<picovector_point_type> t{(picovector_point_type)translation.x, (picovector_point_type)translation.y};
|
||||
for(auto &contour : contours) {
|
||||
for(auto i = 0u; i < contour.count; i++) {
|
||||
contour.points[i] += t;
|
||||
}
|
||||
void PicoVector::rotate(pp_poly_t *poly, pp_point_t origin, float angle) {
|
||||
pp_mat3_t t = pp_mat3_identity();
|
||||
pp_mat3_translate(&t, -origin.x, -origin.y);
|
||||
pp_mat3_rotate(&t, angle);
|
||||
pp_mat3_translate(&t, origin.x, origin.y);
|
||||
transform(poly, &t);
|
||||
}
|
||||
|
||||
void PicoVector::translate(pp_poly_t *poly, pp_point_t translation) {
|
||||
pp_mat3_t t = pp_mat3_identity();
|
||||
pp_mat3_translate(&t, translation.x, translation.y);
|
||||
transform(poly, &t);
|
||||
}
|
||||
|
||||
void PicoVector::transform(pp_poly_t *poly, pp_mat3_t *t) {
|
||||
for (auto i = 0u; i < poly->count; i++) {
|
||||
transform(&poly->paths[i], t);
|
||||
}
|
||||
}
|
||||
|
||||
void PicoVector::rotate(pretty_poly::contour_t<picovector_point_type> &contour, Point origin, float angle) {
|
||||
pretty_poly::point_t<picovector_point_type> t{(picovector_point_type)origin.x, (picovector_point_type)origin.y};
|
||||
angle = (2 * (float)M_PI / 360.f) * angle;
|
||||
pretty_poly::mat2_t r = pretty_poly::mat2_t::rotation(angle);
|
||||
for(auto i = 0u; i < contour.count; i++) {
|
||||
contour.points[i] -= t;
|
||||
contour.points[i] *= r;
|
||||
contour.points[i] += t;
|
||||
}
|
||||
}
|
||||
|
||||
void PicoVector::translate(pretty_poly::contour_t<picovector_point_type> &contour, Point translation) {
|
||||
pretty_poly::point_t<picovector_point_type> t{(picovector_point_type)translation.x, (picovector_point_type)translation.y};
|
||||
for(auto i = 0u; i < contour.count; i++) {
|
||||
contour.points[i] += t;
|
||||
}
|
||||
}
|
||||
|
||||
Point PicoVector::text(std::string_view text, Point origin) {
|
||||
// Copy clipping bounds from the PicoGraphics instance
|
||||
pretty_poly::settings::clip = {graphics->clip.x, graphics->clip.y, graphics->clip.w, graphics->clip.h};
|
||||
// TODO: Normalize types somehow, so we're not converting?
|
||||
pretty_poly::point_t<int> caret = pretty_poly::point_t<int>(origin.x, origin.y);
|
||||
pp_point_t PicoVector::text(std::string_view text, pp_point_t offset, pp_mat3_t *t) {
|
||||
pp_point_t caret = {0, 0};
|
||||
|
||||
// Align text from the bottom left
|
||||
caret.y += text_metrics.size;
|
||||
caret.y += (PP_COORD_TYPE)text_metrics.line_height;
|
||||
|
||||
int16_t space_width = alright_fonts::measure_character(text_metrics, ' ').w;
|
||||
if (space_width == 0) {
|
||||
space_width = text_metrics.word_spacing;
|
||||
}
|
||||
caret = pp_point_transform(&caret, t);
|
||||
caret.x += offset.x;
|
||||
caret.y += offset.y;
|
||||
|
||||
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) {
|
||||
// Copy clipping bounds from the PicoGraphics instance
|
||||
pretty_poly::settings::clip = {graphics->clip.x, graphics->clip.y, graphics->clip.w, graphics->clip.h};
|
||||
// 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 * (float)M_PI / 360.f) * angle;
|
||||
pretty_poly::mat2_t transform = pretty_poly::mat2_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);
|
||||
pp_point_t space;
|
||||
pp_point_t carriage_return = {0, -(PP_COORD_TYPE)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;
|
||||
const pretty_poly::point_t<float> initial_carriage_return = carriage_return;
|
||||
space = pp_point_transform(&space, t);
|
||||
carriage_return = pp_point_transform(&carriage_return, t);
|
||||
|
||||
pp_point_t initial_carriage_return = carriage_return;
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
|
@ -161,32 +102,32 @@ namespace pimoroni {
|
|||
}
|
||||
|
||||
if(caret.x != 0 && caret.x + word_width > graphics->clip.w) {
|
||||
caret -= carriage_return;
|
||||
caret = pp_point_sub(&caret, &carriage_return);
|
||||
carriage_return = initial_carriage_return;
|
||||
}
|
||||
|
||||
for(size_t j = i; j < std::min(next_break + 1, text.length()); j++) {
|
||||
if (text[j] == '\n') { // Linebreak
|
||||
caret -= carriage_return;
|
||||
caret = pp_point_sub(&caret, &carriage_return);
|
||||
carriage_return = initial_carriage_return;
|
||||
} else if (text[j] == ' ') { // Space
|
||||
caret += space;
|
||||
carriage_return += space;
|
||||
caret = pp_point_add(&caret, &space);
|
||||
carriage_return = pp_point_add(&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);
|
||||
alright_fonts::render_character(text_metrics, text[j], caret, t);
|
||||
}
|
||||
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;
|
||||
pp_point_t advance = {
|
||||
(PP_COORD_TYPE)alright_fonts::measure_character(text_metrics, text[j]).w + text_metrics.letter_spacing,
|
||||
(PP_COORD_TYPE)0
|
||||
};
|
||||
advance = pp_point_transform(&advance, t);
|
||||
caret = pp_point_add(&caret, &advance);
|
||||
carriage_return = pp_point_add(&carriage_return, &advance);
|
||||
}
|
||||
|
||||
i = next_break + 1;
|
||||
}
|
||||
|
||||
return Point(caret.x, caret.y);
|
||||
return {caret.x, caret.y};
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
#include "pretty_poly.hpp"
|
||||
#include "pretty_poly/pretty-poly.h"
|
||||
#include "alright_fonts.hpp"
|
||||
#include "pico_graphics.hpp"
|
||||
|
||||
|
@ -9,52 +9,58 @@ namespace pimoroni {
|
|||
|
||||
class PicoVector {
|
||||
private:
|
||||
PicoGraphics *graphics;
|
||||
static PicoGraphics *graphics;
|
||||
alright_fonts::text_metrics_t text_metrics;
|
||||
const uint8_t alpha_map[4] {0, 128, 192, 255};
|
||||
static constexpr uint8_t alpha_map[4] {0, 128, 192, 255};
|
||||
|
||||
public:
|
||||
PicoVector(PicoGraphics *graphics, void *mem = nullptr) : graphics(graphics) {
|
||||
pretty_poly::init(mem);
|
||||
PicoVector(PicoGraphics *graphics, void *mem = nullptr) {
|
||||
PicoVector::graphics = graphics;
|
||||
|
||||
set_options([this](const pretty_poly::tile_t &tile) -> void {
|
||||
uint8_t *tile_data = tile.data;
|
||||
pp_tile_callback(PicoVector::tile_callback);
|
||||
|
||||
if(this->graphics->supports_alpha_blend() && pretty_poly::settings::antialias != pretty_poly::NONE) {
|
||||
if (this->graphics->render_pico_vector_tile({tile.bounds.x, tile.bounds.y, tile.bounds.w, tile.bounds.h},
|
||||
tile.data,
|
||||
tile.stride,
|
||||
(uint8_t)pretty_poly::settings::antialias)) {
|
||||
return;
|
||||
}
|
||||
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, {graphics->clip.x, graphics->clip.y, graphics->clip.w, graphics->clip.h});
|
||||
pp_antialias(graphics->supports_alpha_blend() ? PP_AA_X4 : PP_AA_NONE);
|
||||
|
||||
pp_clip(graphics->clip.x, graphics->clip.y, graphics->clip.w, graphics->clip.h);
|
||||
}
|
||||
|
||||
void set_antialiasing(pretty_poly::antialias_t antialias) {
|
||||
set_options(pretty_poly::settings::callback, antialias, pretty_poly::settings::clip);
|
||||
static void tile_callback(const pp_tile_t *tile) {
|
||||
uint8_t *tile_data = tile->data;
|
||||
|
||||
if(PicoVector::graphics->supports_alpha_blend() && _pp_antialias != PP_AA_NONE) {
|
||||
if (PicoVector::graphics->render_pico_vector_tile({tile->x, tile->y, tile->w, tile->h},
|
||||
tile->data,
|
||||
tile->stride,
|
||||
(uint8_t)_pp_antialias)) {
|
||||
return;
|
||||
}
|
||||
for(auto y = 0; y < tile->h; y++) {
|
||||
for(auto x = 0; x < tile->w; x++) {
|
||||
uint8_t alpha = *tile_data++;
|
||||
if (alpha >= 4) {
|
||||
PicoVector::graphics->set_pixel({x + tile->x, y + tile->y});
|
||||
} else if (alpha > 0) {
|
||||
alpha = alpha_map[alpha];
|
||||
PicoVector::graphics->set_pixel_alpha({x + tile->x, y + tile->y}, alpha);
|
||||
}
|
||||
}
|
||||
tile_data += tile->stride - tile->w;
|
||||
}
|
||||
} else {
|
||||
for(auto y = 0; y < tile->h; y++) {
|
||||
for(auto x = 0; x < tile->w; x++) {
|
||||
uint8_t alpha = *tile_data++;
|
||||
if (alpha) {
|
||||
PicoVector::graphics->set_pixel({x + tile->x, y + tile->y});
|
||||
}
|
||||
}
|
||||
tile_data += tile->stride - tile->w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void set_antialiasing(pp_antialias_t antialias) {
|
||||
pp_antialias(antialias);
|
||||
}
|
||||
|
||||
void set_font_size(unsigned int font_size) {
|
||||
|
@ -69,19 +75,32 @@ namespace pimoroni {
|
|||
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);
|
||||
pp_point_t text(std::string_view text, pp_point_t origin, pp_mat3_t *t);
|
||||
|
||||
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);
|
||||
void transform(pp_path_t *path, pp_mat3_t *t);
|
||||
void transform(pp_poly_t *poly, pp_mat3_t *t);
|
||||
|
||||
Point text(std::string_view text, Point origin);
|
||||
Point text(std::string_view text, Point origin, float angle);
|
||||
void rotate(pp_path_t *path, pp_point_t origin, float angle);
|
||||
void rotate(pp_poly_t *poly, pp_point_t origin, float angle);
|
||||
|
||||
void polygon(std::vector<pretty_poly::contour_t<picovector_point_type>> contours, Point origin = Point(0, 0), int scale=65536);
|
||||
void translate(pp_path_t *path, pp_point_t translation);
|
||||
void translate(pp_poly_t *poly, pp_point_t translation);
|
||||
|
||||
void draw(pp_poly_t *poly);
|
||||
void draw(pp_poly_t *poly, pp_mat3_t *t);
|
||||
|
||||
void draw(pp_path_t *path) {
|
||||
pp_poly_t poly = {.paths = path, .count = 1};
|
||||
draw(&poly);
|
||||
};
|
||||
|
||||
void draw(pp_path_t *path, pp_mat3_t *t) {
|
||||
pp_poly_t poly = {.paths = path, .count = 1};
|
||||
draw(&poly, t);
|
||||
};
|
||||
|
||||
static constexpr size_t pretty_poly_buffer_size() {
|
||||
return pretty_poly::buffer_size();
|
||||
return 0;
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 193967fcc98d78f594f516bc7e4ddb08d7977ad7
|
|
@ -1,339 +0,0 @@
|
|||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#include "pretty_poly.hpp"
|
||||
|
||||
#include "hardware/interp.h"
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
// Early out if line is completely outside the tile, or has no lines
|
||||
if (ey < 0 || sy >= (int)node_buffer_size || sy == ey) return;
|
||||
|
||||
debug(" + line segment from %d, %d to %d, %d\n", sx, sy, ex, ey);
|
||||
|
||||
// Determine how many in-bounds lines to render
|
||||
int y = std::max(0, sy);
|
||||
int count = std::min((int)node_buffer_size, ey) - y;
|
||||
|
||||
// Handle cases where x is completely off to one side or other
|
||||
if (std::max(sx, ex) <= 0) {
|
||||
while (count--) {
|
||||
nodes[y][node_counts[y]++] = 0;
|
||||
++y;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const int full_tile_width = (tile_bounds.w << settings::antialias);
|
||||
if (std::min(sx, ex) >= full_tile_width) {
|
||||
while (count--) {
|
||||
nodes[y][node_counts[y]++] = full_tile_width;
|
||||
++y;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal case
|
||||
int x = sx;
|
||||
int e = 0;
|
||||
|
||||
const int xinc = sign(ex - sx);
|
||||
const int einc = abs(ex - sx) + 1;
|
||||
const int dy = ey - sy;
|
||||
|
||||
// If sy < 0 jump to the start, note this does use a divide
|
||||
// but potentially saves many wasted loops below, so is likely worth it.
|
||||
if (sy < 0) {
|
||||
e = einc * -sy;
|
||||
int xjump = e / dy;
|
||||
e -= dy * xjump;
|
||||
x += xinc * xjump;
|
||||
}
|
||||
|
||||
interp1->base[1] = full_tile_width;
|
||||
interp1->accum[0] = x;
|
||||
|
||||
// loop over scanlines
|
||||
while(count--) {
|
||||
// consume accumulated error
|
||||
while(e > dy) {e -= dy; interp1->add_raw[0] = xinc;}
|
||||
|
||||
// clamp node x value to tile bounds
|
||||
const int nx = interp1->peek[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, rect_t &bounds) {
|
||||
int maxy = -1;
|
||||
bounds.y = 0;
|
||||
bounds.x = tile.bounds.w;
|
||||
int maxx = 0;
|
||||
int anitialias_mask = (1 << settings::antialias) - 1;
|
||||
|
||||
for(auto y = 0; y < (int)node_buffer_size; y++) {
|
||||
if(node_counts[y] == 0) {
|
||||
if (y == bounds.y) ++bounds.y;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::sort(&nodes[y][0], &nodes[y][0] + node_counts[y]);
|
||||
|
||||
uint8_t* row_data = &tile.data[(y >> settings::antialias) * tile.stride];
|
||||
bool rendered_any = false;
|
||||
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;
|
||||
}
|
||||
|
||||
rendered_any = true;
|
||||
|
||||
maxx = std::max((ex - 1) >> settings::antialias, maxx);
|
||||
|
||||
debug(" - render span at %d from %d to %d\n", y, sx, ex);
|
||||
|
||||
if (settings::antialias) {
|
||||
int ax = sx >> settings::antialias;
|
||||
const int aex = ex >> settings::antialias;
|
||||
|
||||
bounds.x = std::min(ax, bounds.x);
|
||||
|
||||
if (ax == aex) {
|
||||
row_data[ax] += ex - sx;
|
||||
continue;
|
||||
}
|
||||
|
||||
row_data[ax] += (1 << settings::antialias) - (sx & anitialias_mask);
|
||||
for(ax++; ax < aex; ax++) {
|
||||
row_data[ax] += (1 << settings::antialias);
|
||||
}
|
||||
|
||||
// This might add 0 to the byte after the end of the row, we pad the tile data
|
||||
// by 1 byte to ensure that is OK
|
||||
row_data[ax] += ex & anitialias_mask;
|
||||
}
|
||||
else {
|
||||
bounds.x = std::min(sx, bounds.x);
|
||||
for(int x = sx; x < ex; x++) {
|
||||
row_data[x]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rendered_any) {
|
||||
debug(" - rendered line %d\n", y);
|
||||
maxy = y;
|
||||
}
|
||||
else if (y == bounds.y) {
|
||||
debug(" - render nothing on line %d\n", y);
|
||||
++bounds.y;
|
||||
}
|
||||
}
|
||||
|
||||
bounds.y >>= settings::antialias;
|
||||
maxy >>= settings::antialias;
|
||||
bounds.w = (maxx >= bounds.x) ? maxx + 1 - bounds.x : 0;
|
||||
bounds.h = (maxy >= bounds.y) ? maxy + 1 - bounds.y : 0;
|
||||
debug(" - rendered tile bounds %d, %d (%d x %d)\n", bounds.x, bounds.y, bounds.w, bounds.h);
|
||||
}
|
||||
|
||||
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(const 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);
|
||||
|
||||
interp_hw_save_t interp1_save;
|
||||
interp_save(interp1, &interp1_save);
|
||||
|
||||
interp_config cfg = interp_default_config();
|
||||
interp_config_set_clamp(&cfg, true);
|
||||
interp_config_set_signed(&cfg, true);
|
||||
interp_set_config(interp1, 0, &cfg);
|
||||
interp1->base[0] = 0;
|
||||
|
||||
//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(const 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
|
||||
rect_t bounds;
|
||||
render_nodes(tile, bounds);
|
||||
if (bounds.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tile.data += bounds.x + tile.stride * bounds.y;
|
||||
tile.bounds.x += bounds.x;
|
||||
tile.bounds.y += bounds.y;
|
||||
tile.bounds.w = bounds.w;
|
||||
tile.bounds.h = bounds.h;
|
||||
|
||||
settings::callback(tile);
|
||||
}
|
||||
}
|
||||
|
||||
interp_restore(interp1, &interp1_save);
|
||||
}
|
||||
}
|
||||
|
||||
template void pretty_poly::draw_polygon<int>(const std::vector<contour_t<int>>& contours, point_t<int> origin, int scale);
|
||||
template void pretty_poly::draw_polygon<float>(const std::vector<contour_t<float>>& contours, point_t<int> origin, int scale);
|
||||
template void pretty_poly::draw_polygon<uint8_t>(const std::vector<contour_t<uint8_t>>& contours, point_t<int> origin, int scale);
|
||||
template void pretty_poly::draw_polygon<int8_t>(const std::vector<contour_t<int8_t>>& contours, point_t<int> origin, int scale);
|
|
@ -1,73 +0,0 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
|
||||
#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, rect_t &bounds);
|
||||
|
||||
template<typename T>
|
||||
void draw_polygon(T *points, unsigned count);
|
||||
|
||||
template<typename T>
|
||||
void draw_polygon(const std::vector<contour_t<T>>& contours, point_t<int> origin = point_t<int>(0, 0), int scale = 65536);
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <math.h>
|
||||
#include <vector>
|
||||
|
||||
#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 = 0.0f, v10 = 0.0f, v20 = 0.0f, v01 = 0.0f, v11 = 0.0f, v21 = 0.0f, v02 = 0.0f, v12 = 0.0f, 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;}
|
||||
};
|
||||
|
||||
// 2x2 matrix for rotations and scales
|
||||
struct mat2_t {
|
||||
float v00 = 0.0f, v10 = 0.0f, v01 = 0.0f, v11 = 0.0f;
|
||||
mat2_t() = default;
|
||||
mat2_t(const mat2_t &m) = default;
|
||||
inline mat2_t& operator*= (const mat2_t &m) {
|
||||
float r00 = this->v00 * m.v00 + this->v01 * m.v10;
|
||||
float r01 = this->v00 * m.v01 + this->v01 * m.v11;
|
||||
float r10 = this->v10 * m.v00 + this->v11 * m.v10;
|
||||
float r11 = this->v10 * m.v01 + this->v11 * m.v11;
|
||||
this->v00 = r00; this->v01 = r01;
|
||||
this->v10 = r10; this->v11 = r11;
|
||||
return *this;
|
||||
}
|
||||
|
||||
static mat2_t identity() {mat2_t m; m.v00 = m.v11 = 1.0f; return m;}
|
||||
static mat2_t rotation(float a) {
|
||||
float c = cosf(a), s = sinf(a); mat2_t r;
|
||||
r.v00 = c; r.v01 = -s; r.v10 = s; r.v11 = c; return r;}
|
||||
static mat2_t scale(float x, float y) {
|
||||
mat2_t r; 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 mat2_t &a) {this->transform(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);
|
||||
}
|
||||
void transform(const mat2_t &m) {
|
||||
float tx = x, ty = y;
|
||||
this->x = (m.v00 * tx + m.v01 * ty);
|
||||
this->y = (m.v10 * tx + m.v11 * ty);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
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(const 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() const {
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
Ładowanie…
Reference in New Issue