pull/1019/merge
Philip Howard 2024-11-26 11:55:57 +00:00 zatwierdzone przez GitHub
commit 1a0a83ebd1
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
53 zmienionych plików z 3378 dodań i 1570 usunięć

Wyświetl plik

@ -37,10 +37,48 @@ Hub75::Hub75(uint width, uint height, Pixel *buffer, PanelType panel_type, bool
}
if (brightness == 0) {
#if PICO_RP2350
brightness = 6;
#else
if (width >= 64) brightness = 6;
if (width >= 96) brightness = 3;
if (width >= 128) brightness = 2;
if (width >= 160) brightness = 1;
#endif
}
switch (color_order) {
case COLOR_ORDER::RGB:
r_shift = 0;
g_shift = 10;
b_shift = 20;
break;
case COLOR_ORDER::RBG:
r_shift = 0;
g_shift = 20;
b_shift = 10;
break;
case COLOR_ORDER::GRB:
r_shift = 20;
g_shift = 0;
b_shift = 10;
break;
case COLOR_ORDER::GBR:
r_shift = 10;
g_shift = 20;
b_shift = 0;
break;
case COLOR_ORDER::BRG:
r_shift = 10;
g_shift = 00;
b_shift = 20;
break;
case COLOR_ORDER::BGR:
r_shift = 20;
g_shift = 10;
b_shift = 0;
break;
}
}
@ -58,26 +96,16 @@ void Hub75::set_color(uint x, uint y, Pixel c) {
}
void Hub75::set_pixel(uint x, uint y, uint8_t r, uint8_t g, uint8_t b) {
switch(color_order) {
case COLOR_ORDER::RGB:
set_color(x, y, Pixel(r, g, b));
break;
case COLOR_ORDER::RBG:
set_color(x, y, Pixel(r, b, g));
break;
case COLOR_ORDER::GRB:
set_color(x, y, Pixel(g, r, b));
break;
case COLOR_ORDER::GBR:
set_color(x, y, Pixel(g, b, r));
break;
case COLOR_ORDER::BRG:
set_color(x, y, Pixel(b, r, g));
break;
case COLOR_ORDER::BGR:
set_color(x, y, Pixel(b, g, r));
break;
int offset = 0;
if(x >= width || y >= height) return;
if(y >= height / 2) {
y -= height / 2;
offset = (y * width + x) * 2;
offset += 1;
} else {
offset = (y * width + x) * 2;
}
back_buffer[offset] = (GAMMA_10BIT[b] << b_shift) | (GAMMA_10BIT[g] << g_shift) | (GAMMA_10BIT[r] << r_shift);
}
void Hub75::FM6126A_write_register(uint16_t value, uint8_t position) {
@ -249,28 +277,88 @@ void Hub75::dma_complete() {
void Hub75::update(PicoGraphics *graphics) {
if(graphics->pen_type == PicoGraphics::PEN_RGB888) {
uint32_t *p = (uint32_t *)graphics->frame_buffer;
for(uint y = 0; y < height; y++) {
for(uint x = 0; x < width; x++) {
uint32_t col = *p;
uint8_t r = (col & 0xff0000) >> 16;
uint8_t g = (col & 0x00ff00) >> 8;
uint8_t b = (col & 0x0000ff) >> 0;
set_pixel(x, y, r, g, b);
p++;
uint8_t *p = (uint8_t *)graphics->frame_buffer;
if(graphics->bounds.w == int32_t(width / 2) && graphics->bounds.h == int32_t(height * 2)) {
for(int y = 0; y < graphics->bounds.h; y++) {
int offsety = 0;
int sy = y;
int basex = 0;
// Assuming our canvas is 128x128 and our display is 256x64,
// consisting of 2x128x64 panels, remap the bottom half
// of the canvas to the right-half of the display,
// This gives us an optional square arrangement.
if (sy >= int(height)) {
sy -= height;
basex = width / 2;
} else {
// Awkward hack to *TEMPORARILY* rotate the top panel
sy = height - 1 - sy;
basex = (width / 2) - 1;
}
// Interlace the top and bottom halves of the panel.
// Since these are scanned out simultaneously to two chains
// of shift registers we need each pair of rows
// (N and N + height / 2) to be adjacent in the buffer.
offsety = width * 2;
if(sy >= int(height / 2)) {
sy -= height / 2;
offsety *= sy;
offsety += 1;
} else {
offsety *= sy;
}
for(int x = 0; x < graphics->bounds.w; x++) {
int sx = x;
uint8_t b = *p++;
uint8_t g = *p++;
uint8_t r = *p++;
// Assumes width / 2 is even.
if (basex & 1) {
sx = basex - sx;
} else {
sx += basex;
}
int offset = offsety + sx * 2;
back_buffer[offset] = (GAMMA_10BIT[b] << b_shift) | (GAMMA_10BIT[g] << g_shift) | (GAMMA_10BIT[r] << r_shift);
// Skip the empty byte in out 32-bit aligned 24-bit colour.
p++;
}
}
}
}
else if(graphics->pen_type == PicoGraphics::PEN_RGB565) {
uint16_t *p = (uint16_t *)graphics->frame_buffer;
for(uint y = 0; y < height; y++) {
for(uint x = 0; x < width; x++) {
uint16_t col = __builtin_bswap16(*p);
uint8_t r = (col & 0b1111100000000000) >> 8;
uint8_t g = (col & 0b0000011111100000) >> 3;
uint8_t b = (col & 0b0000000000011111) << 3;
set_pixel(x, y, r, g, b);
p++;
} else {
for(uint y = 0; y < height; y++) {
for(uint x = 0; x < width; x++) {
int offset = 0;
int sy = y;
int sx = x;
uint8_t b = *p++;
uint8_t g = *p++;
uint8_t r = *p++;
// Interlace the top and bottom halves of the panel.
// Since these are scanned out simultaneously to two chains
// of shift registers we need each pair of rows
// (N and N + height / 2) to be adjacent in the buffer.
offset = width * 2;
if(sy >= int(height / 2)) {
sy -= height / 2;
offset *= sy;
offset += 1;
} else {
offset *= sy;
}
offset += sx * 2;
back_buffer[offset] = (GAMMA_10BIT[b] << b_shift) | (GAMMA_10BIT[g] << g_shift) | (GAMMA_10BIT[r] << r_shift);
// Skip the empty byte in out 32-bit aligned 24-bit colour.
p++;
}
}
}
}

Wyświetl plik

@ -65,6 +65,9 @@ class Hub75 {
};
uint width;
uint height;
uint r_shift = 0;
uint g_shift = 10;
uint b_shift = 20;
Pixel *back_buffer;
bool managed_buffer = false;
PanelType panel_type;

Wyświetl plik

@ -4,10 +4,26 @@
namespace plasma {
APA102::APA102(uint num_leds, PIO pio, uint sm, uint pin_dat, uint pin_clk, uint freq, RGB* buffer) : buffer(buffer), num_leds(num_leds), pio(pio), sm(sm) {
// NOTE: This sets the gpio_base for *the entire PIO* not just this state machine
uint range_max = std::max(pin_dat, pin_clk);
uint range_min = std::min(pin_dat, pin_clk);
// Both pins in 16-48 range
if(range_max >= 32 && range_min >= 16) {
pio_set_gpio_base(pio, 16);
// Both pins in 0-31 range
} else if(range_max <= 31) {
pio_set_gpio_base(pio, 0);
// Pins in different ranges: invalid combo!
} else {
// TODO: Need some means to notify the caller
pio_set_gpio_base(pio, 0);
}
pio_program_offset = pio_add_program(pio, &apa102_program);
pio_sm_set_pins_with_mask(pio, sm, 0, (1u << pin_clk) | (1u << pin_dat));
pio_sm_set_pindirs_with_mask(pio, sm, ~0u, (1u << pin_clk) | (1u << pin_dat));
pio_sm_set_pins_with_mask(pio, sm, 0, (1u << (pin_clk - pio_get_gpio_base(pio))) | (1u << (pin_dat - pio_get_gpio_base(pio))));
pio_sm_set_pindirs_with_mask(pio, sm, ~0u, (1u << (pin_clk - pio_get_gpio_base(pio))) | (1u << (pin_dat - pio_get_gpio_base(pio))));
pio_gpio_init(pio, pin_clk);
pio_gpio_init(pio, pin_dat);

Wyświetl plik

@ -4,6 +4,9 @@
namespace plasma {
WS2812::WS2812(uint num_leds, PIO pio, uint sm, uint pin, uint freq, bool rgbw, COLOR_ORDER color_order, RGB* buffer) : buffer(buffer), num_leds(num_leds), color_order(color_order), pio(pio), sm(sm) {
// NOTE: This sets the gpio_base for *the entire PIO* not just this state machine
pio_set_gpio_base(pio, pin >= 32 ? 16 : 0);
pio_program_offset = pio_add_program(pio, &ws2812_program);
pio_gpio_init(pio, pin);

Wyświetl plik

@ -165,7 +165,7 @@ namespace pimoroni {
// Native 16-bit framebuffer update
void ST7735::update(PicoGraphics *graphics) {
if(graphics->pen_type == PicoGraphics::PEN_RGB565) {
if(graphics->pen_type == PicoGraphics::PEN_RGB565 && graphics->layers == 1) {
command(reg::RAMWR, width * height * sizeof(uint16_t), (const char*)graphics->frame_buffer);
} else {
command(reg::RAMWR);

Wyświetl plik

@ -282,7 +282,7 @@ namespace pimoroni {
void ST7789::update(PicoGraphics *graphics) {
uint8_t cmd = reg::RAMWR;
if(graphics->pen_type == PicoGraphics::PEN_RGB565) { // Display buffer is screen native
if(graphics->pen_type == PicoGraphics::PEN_RGB565 && graphics->layers == 1) { // Display buffer is screen native
command(cmd, width * height * sizeof(uint16_t), (const char*)graphics->frame_buffer);
} else {
gpio_put(dc, 0); // command mode

Wyświetl plik

@ -63,3 +63,4 @@ add_subdirectory(galactic_unicorn)
add_subdirectory(gfx_pack)
add_subdirectory(cosmic_unicorn)
add_subdirectory(stellar_unicorn)
add_subdirectory(pico_w_explorer)

Wyświetl plik

@ -1,14 +1,3 @@
add_subdirectory(mandelbrot)
set(OUTPUT_NAME pico_display2_demo)
add_executable(
${OUTPUT_NAME}
pico_display_2_demo.cpp
)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib hardware_spi hardware_pwm hardware_dma rgbled button pico_display_2 st7789 pico_graphics)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})
include(pico_display_2_demo.cmake)
include(pico_display_2_vector.cmake)

Wyświetl plik

@ -0,0 +1,10 @@
add_executable(
pico_display_2_demo
pico_display_2_demo.cpp
)
# Pull in pico libraries that we need
target_link_libraries(pico_display_2_demo pico_stdlib hardware_spi hardware_pwm hardware_dma rgbled button pico_display_2 st7789 pico_graphics)
# create map/bin/hex file etc.
pico_add_extra_outputs(pico_display_2_demo)

Wyświetl plik

@ -0,0 +1,41 @@
function(static_asset NAME PATH)
get_filename_component(PATH ${PATH} ABSOLUTE)
get_filename_component(ASSET ${PATH} NAME)
get_filename_component(PATH ${PATH} DIRECTORY)
set(OBJNAME ${ASSET}.o)
add_custom_command(OUTPUT ${OBJNAME}
DEPENDS ${PATH}/${ASSET}
COMMENT "Building ${OBJNAME}"
WORKING_DIRECTORY "${PATH}"
COMMAND ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/${OBJNAME} ${ASSET}
COMMAND ${CMAKE_OBJDUMP} -t ${CMAKE_CURRENT_BINARY_DIR}/${OBJNAME}
)
# TODO figure out how to make static resources work
## COMMAND ${CMAKE_OBJCOPY} --rename-section .data=.rodata,alloc,load,readonly,data,contents ${CMAKE_CURRENT_BINARY_DIR}/${OBJNAME} ${CMAKE_CURRENT_BINARY_DIR}/${OBJNAME})
target_sources(${NAME} PRIVATE ${OBJNAME})
endfunction()
add_executable(
pico_display_2_vector
pico_display_2_vector.cpp
)
# Pull in pico libraries that we need
target_link_libraries(pico_display_2_vector
pico_stdlib
hardware_spi
hardware_pwm
hardware_dma
pico_display_2
st7789
pico_graphics
pico_vector
)
static_asset(pico_display_2_vector ${CMAKE_CURRENT_LIST_DIR}/vector/DynaPuff-Medium.af)
pico_enable_stdio_usb(pico_display_2_vector 0)
pico_enable_stdio_uart(pico_display_2_vector 1)
# create map/bin/hex file etc.
pico_add_extra_outputs(pico_display_2_vector)

Wyświetl plik

@ -0,0 +1,67 @@
#include <string.h>
#include <math.h>
#include <vector>
#include <cstdlib>
#include "libraries/pico_display_2/pico_display_2.hpp"
#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_180, false, get_spi_pins(BG_SPI_FRONT));
PicoGraphics_PenRGB332 graphics(st7789.width, st7789.height, nullptr);
uint8_t vector_mem[PicoVector::pretty_poly_buffer_size()];
PicoVector vector(&graphics);
extern char _binary_DynaPuff_Medium_af_start[];
extern size_t _binary_DynaPuff_Medium_af_size;
int main() {
stdio_init_all();
Pen BG = graphics.create_pen(120, 40, 60);
Pen TEXT = graphics.create_pen(255, 255, 255);
st7789.set_backlight(255);
vector.set_font(_binary_DynaPuff_Medium_af_start, 30);
unsigned int a = 0;
while (true) {
Point text_location(0, 0);
graphics.set_pen(BG);
graphics.clear();
graphics.set_pen(TEXT);
graphics.text("Hello World", text_location, 320);
pp_point_t outline[] = {{-64, -64}, {64, -64}, {64, 64}, {-64, 64}};
pp_point_t hole[] = {{ -32, 32}, { 32, 32}, { 32, -32}, { -32, -32}};
pp_poly_t *poly = pp_poly_new();
pp_path_add_points(pp_poly_add_path(poly), outline, sizeof(outline) / sizeof(pp_point_t));
pp_path_add_points(pp_poly_add_path(poly), hole, sizeof(hole) / sizeof(pp_point_t));
pp_mat3_t pos = pp_mat3_identity();
pp_mat3_translate(&pos, 50, 50);
pp_mat3_rotate(&pos, a);
vector.draw(poly);
vector.text("Hello World", 320, 240, &pos);
// update screen
st7789.update(&graphics);
a += 1;
if (a > 359) {
a = 0;
}
pp_poly_free(poly);
}
return 0;
}

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -0,0 +1 @@
include(pico_w_explorer_vector.cmake)

Wyświetl plik

@ -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)

Wyświetl plik

@ -0,0 +1,56 @@
#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);
float angle = 0.0f;
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_poly_t *poly = pp_poly_new();
pp_path_add_points(pp_poly_add_path(poly), outline, sizeof(outline) / sizeof(pp_point_t));
pp_path_add_points(pp_poly_add_path(poly), hole, sizeof(hole) / sizeof(pp_point_t));
vector.rotate(poly, {0, 0}, angle);
vector.translate(poly, {160, 120});
vector.draw(poly);
//pp_mat3_t t = pp_mat3_identity();
//vector.text("Hello World", {0, 0}, &t);
// update screen
st7789.update(&graphics);
angle += 1.0f;
pp_poly_free(poly);
}
return 0;
}

Wyświetl plik

@ -44,3 +44,4 @@ add_subdirectory(gfx_pack)
add_subdirectory(interstate75)
add_subdirectory(cosmic_unicorn)
add_subdirectory(stellar_unicorn)
add_subdirectory(pico_vector)

Wyświetl plik

@ -19,6 +19,14 @@ namespace pimoroni {
RGB* PicoGraphics::get_palette() {return nullptr;}
bool PicoGraphics::supports_alpha_blend() {return false;}
void PicoGraphics::set_layer(uint l) {
this->layer = l;
this->layer_offset = this->bounds.w * this->bounds.h * l;
};
uint PicoGraphics::get_layer() {
return this->layer;
};
void PicoGraphics::set_dimensions(int width, int height) {
bounds = clip = {0, 0, width, height};
}

Wyświetl plik

@ -79,6 +79,7 @@ namespace pimoroni {
}
}
constexpr operator bool() {return r || g || b;};
constexpr RGB operator+ (const RGB& c) const {return RGB(r + c.r, g + c.g, b + c.b);}
constexpr RGB& operator+=(const RGB& c) {r += c.r; g += c.g; b += c.b; return *this;}
constexpr RGB& operator-=(const RGB& c) {r -= c.r; g -= c.g; b -= c.b; return *this;}
@ -142,6 +143,12 @@ namespace pimoroni {
typedef int Pen;
struct Tile {
int32_t x, y, w, h;
uint32_t stride;
uint8_t *data;
};
struct Rect;
struct Point {
@ -226,6 +233,10 @@ namespace pimoroni {
Rect clip;
uint thickness = 1;
uint layers = 1;
uint layer = 0;
uint layer_offset = 0;
typedef std::function<void(void *data, size_t length)> conversion_callback_func;
typedef std::function<RGB565()> next_pixel_func;
typedef std::function<RGB888()> next_pixel_func_rgb888;
@ -270,6 +281,12 @@ namespace pimoroni {
PicoGraphics(uint16_t width, uint16_t height, void *frame_buffer)
: frame_buffer(frame_buffer), bounds(0, 0, width, height), clip(0, 0, width, height) {
set_font(&font6);
layers = 1;
};
PicoGraphics(uint16_t width, uint16_t height, uint16_t layers, void *frame_buffer)
: frame_buffer(frame_buffer), bounds(0, 0, width, height), clip(0, 0, width, height), layers(layers) {
set_font(&font6);
};
virtual void set_pen(uint c) = 0;
@ -278,6 +295,9 @@ namespace pimoroni {
virtual void set_pixel_span(const Point &p, uint l) = 0;
void set_thickness(uint t);
void set_layer(uint l);
uint get_layer();
virtual int get_palette_size();
virtual RGB* get_palette();
virtual bool supports_alpha_blend();
@ -293,7 +313,7 @@ namespace pimoroni {
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);
virtual bool render_pico_vector_tile(const Rect &bounds, uint8_t* alpha_data, uint32_t stride, uint8_t alpha_type) { return false; }
virtual bool render_tile(const Tile *tile) { return false; }
void set_font(const bitmap::font_t *font);
void set_font(const hershey::font_t *font);
@ -330,7 +350,7 @@ namespace pimoroni {
public:
uint8_t color;
PicoGraphics_Pen1Bit(uint16_t width, uint16_t height, void *frame_buffer);
PicoGraphics_Pen1Bit(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
@ -346,7 +366,7 @@ namespace pimoroni {
public:
uint8_t color;
PicoGraphics_Pen1BitY(uint16_t width, uint16_t height, void *frame_buffer);
PicoGraphics_Pen1BitY(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
@ -387,7 +407,7 @@ namespace pimoroni {
bool cache_built = false;
std::array<uint8_t, 16> candidates;
PicoGraphics_Pen3Bit(uint16_t width, uint16_t height, void *frame_buffer);
PicoGraphics_Pen3Bit(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
@ -420,7 +440,7 @@ namespace pimoroni {
bool cache_built = false;
std::array<uint8_t, 16> candidates;
PicoGraphics_PenP4(uint16_t width, uint16_t height, void *frame_buffer);
PicoGraphics_PenP4(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override;
@ -440,6 +460,8 @@ namespace pimoroni {
static size_t buffer_size(uint w, uint h) {
return w * h / 2;
}
bool render_tile(const Tile *tile);
};
class PicoGraphics_PenP8 : public PicoGraphics {
@ -453,7 +475,7 @@ namespace pimoroni {
bool cache_built = false;
std::array<uint8_t, 16> candidates;
PicoGraphics_PenP8(uint16_t width, uint16_t height, void *frame_buffer);
PicoGraphics_PenP8(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override;
@ -473,12 +495,14 @@ namespace pimoroni {
static size_t buffer_size(uint w, uint h) {
return w * h;
}
bool render_tile(const Tile *tile);
};
class PicoGraphics_PenRGB332 : public PicoGraphics {
public:
RGB332 color;
PicoGraphics_PenRGB332(uint16_t width, uint16_t height, void *frame_buffer);
PicoGraphics_PenRGB332(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
@ -497,13 +521,15 @@ namespace pimoroni {
static size_t buffer_size(uint w, uint h) {
return w * h;
}
bool render_tile(const Tile *tile);
};
class PicoGraphics_PenRGB565 : public PicoGraphics {
public:
RGB src_color;
RGB565 color;
PicoGraphics_PenRGB565(uint16_t width, uint16_t height, void *frame_buffer);
PicoGraphics_PenRGB565(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
@ -516,13 +542,20 @@ namespace pimoroni {
static size_t buffer_size(uint w, uint h) {
return w * h * sizeof(RGB565);
}
void frame_convert(PenType type, conversion_callback_func callback) override;
void set_pixel_alpha(const Point &p, const uint8_t a) override;
bool supports_alpha_blend() override {return true;}
bool render_tile(const Tile *tile);
};
class PicoGraphics_PenRGB888 : public PicoGraphics {
public:
RGB src_color;
RGB888 color;
PicoGraphics_PenRGB888(uint16_t width, uint16_t height, void *frame_buffer);
PicoGraphics_PenRGB888(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
@ -532,6 +565,8 @@ namespace pimoroni {
static size_t buffer_size(uint w, uint h) {
return w * h * sizeof(uint32_t);
}
bool render_tile(const Tile *tile);
};
@ -600,7 +635,7 @@ namespace pimoroni {
uint color;
IDirectDisplayDriver<uint8_t> &driver;
PicoGraphics_PenInky7(uint16_t width, uint16_t height, IDirectDisplayDriver<uint8_t> &direct_display_driver);
PicoGraphics_PenInky7(uint16_t width, uint16_t height, IDirectDisplayDriver<uint8_t> &direct_display_driver, uint16_t layers = 1);
void set_pen(uint c) override;
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;

Wyświetl plik

@ -2,11 +2,11 @@
namespace pimoroni {
PicoGraphics_Pen1Bit::PicoGraphics_Pen1Bit(uint16_t width, uint16_t height, void *frame_buffer)
: PicoGraphics(width, height, frame_buffer) {
PicoGraphics_Pen1Bit::PicoGraphics_Pen1Bit(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers)
: PicoGraphics(width, height, layers, frame_buffer) {
this->pen_type = PEN_1BIT;
if(this->frame_buffer == nullptr) {
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height) * layers]);
}
}

Wyświetl plik

@ -2,8 +2,8 @@
namespace pimoroni {
PicoGraphics_Pen1BitY::PicoGraphics_Pen1BitY(uint16_t width, uint16_t height, void *frame_buffer)
: PicoGraphics(width, height, frame_buffer) {
PicoGraphics_Pen1BitY::PicoGraphics_Pen1BitY(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers)
: PicoGraphics(width, height, layers, frame_buffer) {
this->pen_type = PEN_1BIT;
if(this->frame_buffer == nullptr) {
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);

Wyświetl plik

@ -2,8 +2,8 @@
namespace pimoroni {
PicoGraphics_Pen3Bit::PicoGraphics_Pen3Bit(uint16_t width, uint16_t height, void *frame_buffer)
: PicoGraphics(width, height, frame_buffer) {
PicoGraphics_Pen3Bit::PicoGraphics_Pen3Bit(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers)
: PicoGraphics(width, height, layers, frame_buffer) {
this->pen_type = PEN_3BIT;
if(this->frame_buffer == nullptr) {
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);

Wyświetl plik

@ -1,8 +1,8 @@
#include "pico_graphics.hpp"
namespace pimoroni {
PicoGraphics_PenInky7::PicoGraphics_PenInky7(uint16_t width, uint16_t height, IDirectDisplayDriver<uint8_t> &direct_display_driver)
: PicoGraphics(width, height, nullptr),
PicoGraphics_PenInky7::PicoGraphics_PenInky7(uint16_t width, uint16_t height, IDirectDisplayDriver<uint8_t> &direct_display_driver, uint16_t layers)
: PicoGraphics(width, height, layers, nullptr),
driver(direct_display_driver) {
this->pen_type = PEN_INKY7;
}

Wyświetl plik

@ -2,8 +2,8 @@
namespace pimoroni {
PicoGraphics_PenP4::PicoGraphics_PenP4(uint16_t width, uint16_t height, void *frame_buffer)
: PicoGraphics(width, height, frame_buffer) {
PicoGraphics_PenP4::PicoGraphics_PenP4(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers)
: PicoGraphics(width, height, layers, frame_buffer) {
this->pen_type = PEN_P4;
if(this->frame_buffer == nullptr) {
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
@ -59,6 +59,7 @@ namespace pimoroni {
// pointer to byte in framebuffer that contains this pixel
uint8_t *buf = (uint8_t *)frame_buffer;
buf += this->layer_offset / 2;
uint8_t *f = &buf[i / 2];
uint8_t o = (~i & 0b1) * 4; // bit offset within byte
@ -74,6 +75,7 @@ namespace pimoroni {
// pointer to byte in framebuffer that contains this pixel
uint8_t *buf = (uint8_t *)frame_buffer;
buf += this->layer_offset / 2;
uint8_t *f = &buf[i / 2];
// doubled up color value, so the color is stored in both nibbles
@ -144,16 +146,62 @@ namespace pimoroni {
uint8_t *src = (uint8_t *)frame_buffer;
uint8_t o = 4;
frame_convert_rgb565(callback, [&]() {
uint8_t c = *src;
uint8_t b = (c >> o) & 0xf; // bit value shifted to position
// Increment to next 4-bit entry
o ^= 4;
if (o != 0) ++src;
if(this->layers > 1) {
return cache[b];
});
uint offset = this->bounds.w * this->bounds.h / 2;
frame_convert_rgb565(callback, [&]() {
uint8_t b = 0;
// Iterate through layers in reverse order
// Return the first nonzero (not transparent) pixel
for(auto layer = this->layers; layer > 0; layer--) {
uint8_t c = *(src + offset * (layer - 1));
b = (c >> o) & 0xf; // bit value shifted to position
if (b) break;
}
// Increment to next 4-bit entry
o ^= 4;
if (o != 0) src++;
return cache[b];
});
} else {
frame_convert_rgb565(callback, [&]() {
uint8_t c = *src;
uint8_t b = (c >> o) & 0xf; // bit value shifted to position
// Increment to next 4-bit entry
o ^= 4;
if (o != 0) ++src;
return cache[b];
});
}
}
}
bool PicoGraphics_PenP4::render_tile(const Tile *tile) {
for(int y = 0; y < tile->h; y++) {
uint8_t *palpha = &tile->data[(y * tile->stride)];
uint8_t *pdest = &((uint8_t *)frame_buffer)[(tile->x / 2) + ((tile->y + y) * (bounds.w / 2))];
for(int x = 0; x < tile->w; x++) {
uint8_t shift = (x & 1) ? 0 : 4;
uint8_t alpha = *palpha;
if(alpha == 0) {
} else {
*pdest &= shift ? 0x0f : 0xf0;
*pdest |= color << shift;
}
if(x & 1) {
pdest++;
}
palpha++;
}
}
return true;
}
}

Wyświetl plik

@ -1,8 +1,8 @@
#include "pico_graphics.hpp"
namespace pimoroni {
PicoGraphics_PenP8::PicoGraphics_PenP8(uint16_t width, uint16_t height, void *frame_buffer)
: PicoGraphics(width, height, frame_buffer) {
PicoGraphics_PenP8::PicoGraphics_PenP8(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers)
: PicoGraphics(width, height, layers, frame_buffer) {
this->pen_type = PEN_P8;
if(this->frame_buffer == nullptr) {
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
@ -51,12 +51,14 @@ namespace pimoroni {
}
void PicoGraphics_PenP8::set_pixel(const Point &p) {
uint8_t *buf = (uint8_t *)frame_buffer;
buf += this->layer_offset;
buf[p.y * bounds.w + p.x] = color;
}
void PicoGraphics_PenP8::set_pixel_span(const Point &p, uint l) {
// pointer to byte in framebuffer that contains this pixel
uint8_t *buf = (uint8_t *)frame_buffer;
buf += this->layer_offset;
buf = &buf[p.y * bounds.w + p.x];
while(l--) {
@ -103,26 +105,90 @@ namespace pimoroni {
}
void PicoGraphics_PenP8::frame_convert(PenType type, conversion_callback_func callback) {
if(type == PEN_RGB565) {
// Cache the RGB888 palette as RGB565
RGB565 cache[palette_size];
for(auto i = 0u; i < palette_size; i++) {
cache[i] = palette[i].to_rgb565();
// Treat our void* frame_buffer as uint8_t
uint8_t *src = (uint8_t *)frame_buffer;
if(layers > 1) {
// The size of a single layer
uint offset = this->bounds.w * this->bounds.h;
if(type == PEN_RGB565) {
// Cache the RGB888 palette as RGB565
RGB565 cache[palette_size];
for(auto i = 0u; i < palette_size; i++) {
cache[i] = palette[i].to_rgb565();
}
frame_convert_rgb565(callback, [&]() {
// Check the *palette* index, rather than the colour
// Thus palette entry 0 is *always* transparent
uint8_t c = 0;
// Iterate through layers in reverse order
// Return the first nonzero (not transparent) pixel
for(auto layer = this->layers; layer > 0; layer--) {
c = *(src + offset * (layer - 1));
if (c) break;
}
src++;
return cache[c];
});
} else if (type == PEN_RGB888) {
frame_convert_rgb888(callback, [&]() {
// Check the *palette* index, rather than the colour
// Thus palette entry 0 is *always* transparent
uint8_t c = 0;
// Iterate through layers in reverse order
// Return the first nonzero (not transparent) pixel
for(auto layer = this->layers; layer > 0; layer--) {
c = *(src + offset * (layer - 1));
if (c) break;
}
src++;
return palette[c].to_rgb888();
});
}
} else {
if(type == PEN_RGB565) {
// Cache the RGB888 palette as RGB565
RGB565 cache[palette_size];
for(auto i = 0u; i < palette_size; i++) {
cache[i] = palette[i].to_rgb565();
}
// Treat our void* frame_buffer as uint8_t
uint8_t *src = (uint8_t *)frame_buffer;
frame_convert_rgb565(callback, [&]() {
return cache[*src++];
});
} else if (type == PEN_RGB888) {
// Treat our void* frame_buffer as uint8_t
uint8_t *src = (uint8_t *)frame_buffer;
frame_convert_rgb888(callback, [&]() {
return palette[*src++].to_rgb888();
});
frame_convert_rgb565(callback, [&]() {
return cache[*src++];
});
} else if (type == PEN_RGB888) {
frame_convert_rgb888(callback, [&]() {
return palette[*src++].to_rgb888();
});
}
}
}
bool PicoGraphics_PenP8::render_tile(const Tile *tile) {
for(int y = 0; y < tile->h; y++) {
uint8_t *palpha = &tile->data[(y * tile->stride)];
uint8_t *pdest = &((uint8_t *)frame_buffer)[tile->x + ((tile->y + y) * bounds.w)];
for(int x = 0; x < tile->w; x++) {
uint8_t alpha = *palpha;
if(alpha == 0) {
} else {
*pdest = color;
}
pdest++;
palpha++;
}
}
return true;
}
}

Wyświetl plik

@ -2,11 +2,11 @@
#include <string.h>
namespace pimoroni {
PicoGraphics_PenRGB332::PicoGraphics_PenRGB332(uint16_t width, uint16_t height, void *frame_buffer)
: PicoGraphics(width, height, frame_buffer) {
PicoGraphics_PenRGB332::PicoGraphics_PenRGB332(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers)
: PicoGraphics(width, height, layers, frame_buffer) {
this->pen_type = PEN_RGB332;
if(this->frame_buffer == nullptr) {
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height) * layers]);
}
}
void PicoGraphics_PenRGB332::set_pen(uint c) {
@ -23,12 +23,14 @@ namespace pimoroni {
}
void PicoGraphics_PenRGB332::set_pixel(const Point &p) {
uint8_t *buf = (uint8_t *)frame_buffer;
buf += this->layer_offset;
buf[p.y * bounds.w + p.x] = color;
}
void PicoGraphics_PenRGB332::set_pixel_span(const Point &p, uint l) {
// pointer to byte in framebuffer that contains this pixel
uint8_t *buf = (uint8_t *)frame_buffer;
buf = &buf[p.y * bounds.w + p.x];
buf += this->layer_offset;
buf += p.y * bounds.w + p.x;
while(l--) {
*buf++ = color;
@ -38,6 +40,7 @@ namespace pimoroni {
if(!bounds.contains(p)) return;
uint8_t *buf = (uint8_t *)frame_buffer;
buf += this->layer_offset;
RGB332 blended = RGB(buf[p.y * bounds.w + p.x]).blend(RGB(color), a).to_rgb332();
@ -96,9 +99,29 @@ namespace pimoroni {
// Treat our void* frame_buffer as uint8_t
uint8_t *src = (uint8_t *)frame_buffer;
frame_convert_rgb565(callback, [&]() {
return rgb332_to_rgb565_lut[*src++];
});
if(this->layers > 1) {
// The size of a single layer
uint offset = this->bounds.w * this->bounds.h;
frame_convert_rgb565(callback, [&]() {
uint8_t c = 0;
// Iterate through layers in reverse order
// Return the first nonzero (not transparent) pixel
for(auto layer = this->layers; layer > 0; layer--) {
c = *(src + offset * (layer - 1));
if (c) break;
}
src++;
return rgb332_to_rgb565_lut[c];
});
} else {
frame_convert_rgb565(callback, [&]() {
return rgb332_to_rgb565_lut[*src++];
});
}
}
}
void PicoGraphics_PenRGB332::sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent) {
@ -122,4 +145,50 @@ namespace pimoroni {
}
}
}
bool PicoGraphics_PenRGB332::render_tile(const Tile *tile) {
for(int y = 0; y < tile->h; y++) {
uint8_t *palpha = &tile->data[(y * tile->stride)];
uint8_t *p_dest = &((uint8_t *)frame_buffer)[tile->x + ((tile->y + y) * bounds.w)];
p_dest += this->layer_offset;
uint8_t *p_layer0 = &((uint8_t *)frame_buffer)[tile->x + ((tile->y + y) * bounds.w)];
for(int x = 0; x < tile->w; x++) {
uint8_t alpha = *palpha;
uint8_t dest = *p_dest;
if(dest == 0) {
dest = *p_layer0;
}
// TODO: Try to alpha blend RGB332... somewhat?
if(alpha == 255) {
*p_dest = color;
}else if(alpha == 0) {
}else{
// blend tha pixel
uint16_t sr = (color & 0b11100000) >> 5;
uint16_t sg = (color & 0b00011100) >> 2;
uint16_t sb = (color & 0b00000011);
uint16_t dr = (dest & 0b11100000) >> 5;
uint16_t dg = (dest & 0b00011100) >> 2;
uint16_t db = (dest & 0b00000011);
uint8_t r = ((sr * alpha) + (dr * (255 - alpha))) >> 8;
uint8_t g = ((sg * alpha) + (dg * (255 - alpha))) >> 8;
uint8_t b = ((sb * alpha) + (db * (255 - alpha))) >> 8;
// recombine the channels
*p_dest = (r << 5) | (g << 2) | (b);
}
p_dest++;
palpha++;
}
}
return true;
}
}

Wyświetl plik

@ -1,8 +1,8 @@
#include "pico_graphics.hpp"
namespace pimoroni {
PicoGraphics_PenRGB565::PicoGraphics_PenRGB565(uint16_t width, uint16_t height, void *frame_buffer)
: PicoGraphics(width, height, frame_buffer) {
PicoGraphics_PenRGB565::PicoGraphics_PenRGB565(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers)
: PicoGraphics(width, height, layers, frame_buffer) {
this->pen_type = PEN_RGB565;
if(this->frame_buffer == nullptr) {
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
@ -23,11 +23,15 @@ namespace pimoroni {
}
void PicoGraphics_PenRGB565::set_pixel(const Point &p) {
uint16_t *buf = (uint16_t *)frame_buffer;
// We can't use buffer_size because our pointer is uint16_t
buf += this->layer_offset;
buf[p.y * bounds.w + p.x] = color;
}
void PicoGraphics_PenRGB565::set_pixel_span(const Point &p, uint l) {
// pointer to byte in framebuffer that contains this pixel
uint16_t *buf = (uint16_t *)frame_buffer;
// We can't use buffer_size because our pointer is uint16_t
buf += this->layer_offset;
buf = &buf[p.y * bounds.w + p.x];
while(l--) {
@ -35,26 +39,105 @@ namespace pimoroni {
}
}
void PicoGraphics_PenRGB565::frame_convert(PenType type, conversion_callback_func callback) {
if(type == PEN_RGB565) {
// Treat our void* frame_buffer as uint8_t
uint16_t *src = (uint16_t *)frame_buffer;
void PicoGraphics_PenRGB565::sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent) {
//int sprite_x = (sprite & 0x0f) << 3;
//int sprite_y = (sprite & 0xf0) >> 1;
Point s {
sprite.x << 3,
sprite.y << 3
};
RGB565 *ptr = (RGB565 *)data;
Point o = {0, 0};
for(o.y = 0; o.y < 8 * scale; o.y++) {
Point so = {
0,
o.y / scale
};
for(o.x = 0; o.x < 8 * scale; o.x++) {
so.x = o.x / scale;
color = ptr[(s.y + so.y) * 128 + (s.x + so.x)];
if(color != transparent) pixel(dest + o);
}
}
}
if(layers > 1) {
// Assume only two layers for now
// We can't use buffer_size because our pointer is uint16_t
uint16_t *src_layer2 = src + this->bounds.w * this->bounds.h;
frame_convert_rgb565(callback, [&]() {
RGB565 c1 = *src++;
RGB565 c2 = *src_layer2++;
return c2 ? c2 : c1;
});
} else {
frame_convert_rgb565(callback, [&]() {
return *src++;
});
}
}
}
void PicoGraphics_PenRGB565::set_pixel_alpha(const Point &p, const uint8_t a) {
if(!bounds.contains(p)) return;
uint16_t *buf = (uint16_t *)frame_buffer;
buf += this->layer_offset;
RGB565 blended = RGB(buf[p.y * bounds.w + p.x]).blend(RGB(color), a).to_rgb565();
buf[p.y * bounds.w + p.x] = blended;
};
void PicoGraphics_PenRGB565::sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent) {
//int sprite_x = (sprite & 0x0f) << 3;
//int sprite_y = (sprite & 0xf0) >> 1;
Point s {
sprite.x << 3,
sprite.y << 3
};
RGB565 *ptr = (RGB565 *)data;
Point o = {0, 0};
for(o.y = 0; o.y < 8 * scale; o.y++) {
Point so = {
0,
o.y / scale
};
for(o.x = 0; o.x < 8 * scale; o.x++) {
so.x = o.x / scale;
color = ptr[(s.y + so.y) * 128 + (s.x + so.x)];
if(color != transparent) pixel(dest + o);
}
}
}
bool PicoGraphics_PenRGB565::render_tile(const Tile *tile) {
for(int y = 0; y < tile->h; y++) {
uint8_t *p_alpha = &tile->data[(y * tile->stride)];
uint16_t *p_dest = &((uint16_t *)frame_buffer)[tile->x + ((tile->y + y) * bounds.w)];
p_dest += this->layer_offset;
uint16_t *p_layer0 = &((uint16_t *)frame_buffer)[tile->x + ((tile->y + y) * bounds.w)];
for(int x = 0; x < tile->w; x++) {
uint16_t dest = *p_dest;
if(dest == 0 && this->layers > 1) {
dest = *p_layer0;
}
uint8_t alpha = *p_alpha;
if(alpha == 255) {
*p_dest = color;
}else if(alpha == 0) {
}else{
// blend tha pixel
uint16_t sr = (__builtin_bswap16(color) & 0b1111100000000000) >> 11;
uint16_t sg = (__builtin_bswap16(color) & 0b0000011111100000) >> 5;
uint16_t sb = (__builtin_bswap16(color) & 0b0000000000011111);
uint16_t dr = (__builtin_bswap16(dest) & 0b1111100000000000) >> 11;
uint16_t dg = (__builtin_bswap16(dest) & 0b0000011111100000) >> 5;
uint16_t db = (__builtin_bswap16(dest) & 0b0000000000011111);
uint8_t r = ((sr * alpha) + (dr * (255 - alpha))) >> 8;
uint8_t g = ((sg * alpha) + (dg * (255 - alpha))) >> 8;
uint8_t b = ((sb * alpha) + (db * (255 - alpha))) >> 8;
// recombine the channels
*p_dest = __builtin_bswap16((r << 11) | (g << 5) | (b));
}
p_layer0++;
p_dest++;
p_alpha++;
}
}
return true;
}
}

Wyświetl plik

@ -1,8 +1,8 @@
#include "pico_graphics.hpp"
namespace pimoroni {
PicoGraphics_PenRGB888::PicoGraphics_PenRGB888(uint16_t width, uint16_t height, void *frame_buffer)
: PicoGraphics(width, height, frame_buffer) {
PicoGraphics_PenRGB888::PicoGraphics_PenRGB888(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers)
: PicoGraphics(width, height, layers, frame_buffer) {
this->pen_type = PEN_RGB888;
if(this->frame_buffer == nullptr) {
this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]);
@ -23,15 +23,53 @@ namespace pimoroni {
}
void PicoGraphics_PenRGB888::set_pixel(const Point &p) {
uint32_t *buf = (uint32_t *)frame_buffer;
buf += this->layer_offset;
buf[p.y * bounds.w + p.x] = color;
}
void PicoGraphics_PenRGB888::set_pixel_span(const Point &p, uint l) {
// pointer to byte in framebuffer that contains this pixel
uint32_t *buf = (uint32_t *)frame_buffer;
buf += this->layer_offset;
buf = &buf[p.y * bounds.w + p.x];
while(l--) {
*buf++ = color;
}
}
bool PicoGraphics_PenRGB888::render_tile(const Tile *tile) {
for(int y = 0; y < tile->h; y++) {
uint8_t *p_alpha = &tile->data[(y * tile->stride)];
uint32_t *p_dest = &((uint32_t *)frame_buffer)[tile->x + ((tile->y + y) * bounds.w)];
for(int x = 0; x < tile->w; x++) {
uint16_t dest = *p_dest;
uint8_t alpha = *p_alpha;
// TODO: Alpha blending
if(alpha == 255) {
*p_dest = color;
}else if(alpha == 0) {
} else {
// blend tha pixel
uint32_t sr = (color >> 16) & 0xff;
uint32_t sg = (color >> 8) & 0xff;
uint32_t sb = (color >> 0) & 0xff;
uint32_t dr = (dest >> 16) & 0xff;
uint32_t dg = (dest >> 8) & 0xff;
uint32_t db = (dest >> 0) & 0xff;
uint8_t r = ((sr * alpha) + (dr * (255 - alpha))) >> 8;
uint8_t g = ((sg * alpha) + (dg * (255 - alpha))) >> 8;
uint8_t b = ((sb * alpha) + (db * (255 - alpha))) >> 8;
*p_dest = (r << 16) | (g << 8) | b;
}
p_dest++;
p_alpha++;
}
}
return true;
}
}

Wyświetl plik

@ -0,0 +1 @@
include(pico_vector.cmake)

Wyświetl plik

@ -0,0 +1,36 @@
#include "af-file-io.h"
#include "string.h"
static size_t ptr = 0;
void* fileio_open(const char* filename) {
ptr = 0;
return NULL;
}
void fileio_close(void* fhandle) {
ptr = 0;
return;
}
size_t fileio_read(void* fhandle, void *buf, size_t len) {
memcpy(buf, fhandle + ptr, len);
ptr += len;
return len;
}
int fileio_getc(void* fhandle) {
uint8_t *f = fhandle;
int c = f[ptr];
ptr += 1;
return c;
}
size_t fileio_tell(void* fhandle) {
return ptr;
}
size_t fileio_seek(void* fhandle, size_t pos) {
ptr = pos;
return ptr;
}

Wyświetl plik

@ -0,0 +1,20 @@
#include <stdlib.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
void* fileio_open(const char* filename);
void fileio_close(void* fhandle);
size_t fileio_read(void* fhandle, void *buf, size_t len);
int fileio_getc(void* fhandle);
size_t fileio_tell(void* fhandle);
size_t fileio_seek(void* fhandle, size_t pos);
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -0,0 +1,24 @@
#include "af-memory.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
void *af_malloc(size_t size) {
return malloc(size);
}
void *af_realloc(void *p, size_t size) {
return realloc(p, size);
}
void af_free(void *p) {
free(p);
}
void af_debug(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
int ret = printf(fmt, ap);
va_end(ap);
(void)ret;
}

Wyświetl plik

@ -0,0 +1,12 @@
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
void *af_malloc(size_t size);
void *af_realloc(void *p, size_t size);
void af_free(void *p);
void af_debug(const char *fmt, ...);
#ifdef __cplusplus
}
#endif

Wyświetl plik

@ -0,0 +1,458 @@
/*
Alright Fonts 🖍 - a font format for embedded and low resource platforms.
Jonathan Williamson, August 2022
Examples, source, and more: https://github.com/lowfatcode/pretty-poly
MIT License https://github.com/lowfatcode/pretty-poly/blob/main/LICENSE
An easy way to render high quality text in embedded applications running
on resource constrained microcontrollers such as the Cortex M0 and up.
- OTF and TTF support: generate efficient packed fonts easily
- Minimal data: ~4kB (40 bytes per char) for printable ASCII set (Roboto)
- Tunable: trade off file size, contour complexity, and visual quality
- Metrics: advance and bounding box for fast layout
- UTF-8 or ASCII: support for non ASCII like Kanji or Cyrillic
- Fixed scale: coords scaled to ^2 bounds for fast scaling (no divide)
- C17 header only library: simply copy the header file into your project
- Customised font packs: include only the characters you need
- Simple outlines: all paths are simply polylines for easy rendering
- Easy antialiasing: combine with Pretty Poly for quick results!
*/
#ifndef AF_INCLUDE_H
#define AF_INCLUDE_H
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <stdbool.h>
#include <wchar.h>
#include <float.h>
#ifdef AF_MALLOC
#ifndef PP_MALLOC
#define PP_MALLOC(size) AF_MALLOC(size)
#define PP_REALLOC(p, size) AF_REALLOC(p, size)
#define PP_FREE(p) AF_FREE(p)
#endif // PP_MALLOC
#endif // AF_MALLOC
#ifndef AF_MALLOC
#define AF_MALLOC(size) malloc(size)
#define AF_REALLOC(p, size) realloc(p, size)
#define AF_FREE(p) free(p)
#endif // AF_MALLOC
#ifndef AF_FILE
#define AF_FILE FILE*
#define AF_FREAD(p, size, nmemb, stream) fread(p, size, nmemb, stream)
#define AF_FGETC(stream) fgetc(stream)
#endif
#ifndef AF_DEBUG
#define AF_DEBUG(...)
#endif
#include "pretty-poly.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
int8_t x, y;
} af_point_t;
pp_point_t af_point_transform(pp_point_t *p, pp_mat3_t *m);
typedef struct {
uint8_t point_count;
af_point_t *points;
} af_path_t;
typedef struct {
char codepoint;
int8_t x, y, w, h;
int8_t advance;
uint8_t path_count;
af_path_t *paths;
} af_glyph_t;
typedef struct {
uint16_t flags;
uint16_t glyph_count;
af_glyph_t *glyphs;
} af_face_t;
typedef enum {
AF_H_ALIGN_LEFT = 0, AF_H_ALIGN_CENTER = 1, AF_H_ALIGN_RIGHT = 2,
AF_H_ALIGN_JUSTIFY = 4,
AF_V_ALIGN_TOP = 8, AF_V_ALIGN_MIDDLE = 16, AF_V_ALIGN_BOTTOM = 32
} af_align_t;
typedef struct {
af_face_t *face; // font
float size; // text size in pixels
float line_height; // spacing between lines (%)
float letter_spacing; // spacing between characters (%)
float word_spacing; // spacing between words (%)
unsigned int align; // horizontal and vertical alignment
pp_mat3_t *transform; // arbitrary transformation
} af_text_metrics_t;
bool af_load_font_file(AF_FILE file, af_face_t *face);
void af_render_character(af_face_t *face, const char codepoint, af_text_metrics_t *tm);
void af_render(af_face_t *face, const char *text, size_t tlen, float max_line_width, float max_height, af_text_metrics_t *tm);
pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, float max_line_width, af_text_metrics_t *tm);
#ifdef AF_USE_PRETTY_POLY
#endif
#ifdef __cplusplus
}
#endif
#ifdef AF_IMPLEMENTATION
/*
helper functions
*/
// big endian file reading helpers
uint16_t ru16(AF_FILE file) {uint8_t w[2]; AF_FREAD((char *) w, 1, 2, file); return (uint16_t)w[0] << 8 | w[1];}
int16_t rs16(AF_FILE file) {uint8_t w[2]; AF_FREAD((char *) w, 1, 2, file); return (uint16_t)w[0] << 8 | w[1];}
uint32_t ru32(AF_FILE file) {uint8_t dw[4]; AF_FREAD((char *)dw, 1, 4, file); return (uint32_t)dw[0] << 24 | (uint32_t)dw[1] << 16 | (uint32_t)dw[2] << 8 | dw[3];}
uint8_t ru8(AF_FILE file) {return AF_FGETC(file);}
int8_t rs8(AF_FILE file) {return AF_FGETC(file);}
bool af_load_font_file(AF_FILE file, af_face_t *face) {
// check header magic bytes are present
char marker[4]; AF_FREAD(marker, 1, 4, file);
if(memcmp(marker, "af!?", 4) != 0) {
return false; // doesn't start with magic marker
}
// extract flags and ensure none set
face->flags = ru16(file);
if(face->flags != 0) {
return false; // unknown flags set
}
// number of glyphs, paths, and points in font
uint16_t glyph_count = ru16(file);
uint16_t path_count = ru16(file);
uint16_t point_count = ru16(file);
size_t glyph_buffer_size = sizeof(af_glyph_t) * glyph_count;
size_t path_buffer_size = sizeof(af_path_t) * path_count;
size_t point_buffer_size = sizeof(af_point_t) * point_count;
// allocate buffer to store font glyph, path, and point data
uint8_t *buffer = (uint8_t *)AF_MALLOC(glyph_buffer_size + path_buffer_size + point_buffer_size);
if(!buffer) {
return false; // failed memory allocation
}
af_glyph_t *glyphs = (af_glyph_t *) buffer;
af_path_t *paths = ( af_path_t *)(buffer + glyph_buffer_size);
af_point_t *points = (af_point_t *)(buffer + glyph_buffer_size + path_buffer_size);
// load glyph dictionary
face->glyph_count = glyph_count;
face->glyphs = glyphs;
for(int i = 0; i < glyph_count; i++) {
af_glyph_t *glyph = &face->glyphs[i];
glyph->codepoint = ru16(file);
glyph->x = rs8(file);
glyph->y = rs8(file);
glyph->w = ru8(file);
glyph->h = ru8(file);
glyph->advance = ru8(file);
glyph->path_count = ru8(file);
glyph->paths = paths;
paths += glyph->path_count;
}
// load the glyph paths
for(int i = 0; i < glyph_count; i++) {
af_glyph_t *glyph = &face->glyphs[i];
for(int j = 0; j < glyph->path_count; j++) {
af_path_t *path = &glyph->paths[j];
path->point_count = ru8(file);
path->points = points;
points += path->point_count;
}
}
// load the glyph points
for(int i = 0; i < glyph_count; i++) {
af_glyph_t *glyph = &face->glyphs[i];
for(int j = 0; j < glyph->path_count; j++) {
af_path_t *path = &glyph->paths[j];
for(int k = 0; k < path->point_count; k++) {
af_point_t *point = &path->points[k];
point->x = ru8(file);
point->y = ru8(file);
}
}
}
return true;
}
af_glyph_t *find_glyph(af_face_t *face, char c) {
for(int i = 0; i < face->glyph_count; i++) {
if(face->glyphs[i].codepoint == c) {
return &face->glyphs[i];
}
}
return NULL;
}
void af_render_glyph(af_glyph_t* glyph, af_text_metrics_t *tm) {
assert(glyph != NULL);
pp_poly_t *poly = pp_poly_new();
for(uint32_t i = 0; i < glyph->path_count; i++) {
pp_path_t *path = pp_poly_add_path(poly);
for(uint32_t j = 0; j < glyph->paths[i].point_count; j++) {
pp_path_add_point(path, {
glyph->paths[i].points[j].x,
glyph->paths[i].points[j].y
});
}
}
pp_render(poly);
pp_poly_free(poly);
}
void af_render_character(af_face_t *face, const char c, af_text_metrics_t *tm) {
af_glyph_t *glyph = find_glyph(face, c);
if(!glyph) {
return;
}
af_render_glyph(glyph, tm);
}
float get_line_width(af_face_t *face, const char *text, size_t *tlen, float max_line_width, af_text_metrics_t *tm) {
float line_width = 0;
const char *start = text;
const char *end = text + *tlen;
const char *last_space = nullptr;
for(char c = *text; text < end; text++, c = *text) {
af_glyph_t *glyph = find_glyph(face, c);
if(!glyph) {
continue;
}
float char_width;
if(c == L' ') {
char_width = (glyph->advance * tm->word_spacing) / 100.0f;
last_space = text;
} else {
char_width = (glyph->advance * tm->letter_spacing) / 100.0f;
}
if (max_line_width > 0 && line_width + char_width > max_line_width && last_space) {
*tlen = last_space - start;
break;
}
line_width += char_width;
}
return line_width;
}
size_t line_length(const char *text, const char *end) {
if(text >= end) return 0;
char *line_ending = (char *)memchr(text, '\n', end - text);
if(line_ending == NULL || line_ending > end) {
line_ending = (char *)end;
}
return line_ending - text;
}
float get_max_line_width(af_face_t *face, const char *text, size_t tlen, af_text_metrics_t *tm) {
float max_width = 0;
char *line = (char *)text;
char *tend = line + tlen;
size_t line_len = line_length(line, tend);
while(line_len) {
float width = get_line_width(face, line, &line_len, 0, tm);
max_width = max_width < width ? width : max_width;
line += line_len + 1;
line_len = line_length(line, tend);
}
return max_width;
}
void af_render(af_face_t *face, const char *text, size_t tlen, float max_line_width, float max_height, af_text_metrics_t *tm) {
char *line = (char *)text;
char *tend = line + tlen;
size_t line_len = 0;
pp_mat3_t *old = pp_transform(NULL);
float line_height = (tm->line_height * 128.0f) / 100.0f;
float scale = tm->size / 128.0f;
struct {
float x, y;
} caret;
caret.x = 0;
caret.y = 0;
// find maximum line length
if (max_line_width == 0.f) {
max_line_width = get_max_line_width(face, text, tlen, tm);
} else {
max_line_width /= scale;
}
if (max_height == 0.f) {
max_height = FLT_MAX;
} else {
max_height /= scale;
}
line_len = line_length(line, tend);
while(line_len && caret.y + line_height <= max_height) {
int line_width = get_line_width(face, line, &line_len, max_line_width, tm);
char *end = line + line_len;
for(char c = *line; line < end; line++, c = *line) {
af_glyph_t *glyph = find_glyph(face, c);
if(!glyph) {
continue;
}
pp_mat3_t caret_transform = *tm->transform;
pp_mat3_scale(&caret_transform, scale, scale);
pp_mat3_translate(&caret_transform, caret.x, caret.y);
if(tm->align & AF_H_ALIGN_CENTER) {
pp_mat3_translate(&caret_transform, (max_line_width - line_width) / 2, 0);
}
if(tm->align & AF_H_ALIGN_RIGHT) {
pp_mat3_translate(&caret_transform, (max_line_width - line_width), 0);
}
pp_mat3_t final_transform = *old;
pp_mat3_mul(&final_transform, &caret_transform);
pp_transform(&final_transform);
af_render_glyph(glyph, tm);
if(c == L' ') {
caret.x += (glyph->advance * tm->word_spacing) / 100.0f;
} else {
caret.x += (glyph->advance * tm->letter_spacing) / 100.0f;
}
}
line += 1; // Skip over \n
line_len = line_length(line, tend);
caret.x = 0;
caret.y += line_height;
}
pp_transform(old);
}
void _af_render(af_face_t *face, const char *text, af_text_metrics_t *tm) {
af_render(face, text, strlen(text), 0, 0, tm);
}
pp_rect_t af_measure(af_face_t *face, const char *text, size_t tlen, float max_line_width, af_text_metrics_t *tm) {
pp_rect_t result;
bool first = true;
char *line = (char *)text;
char *tend = line + tlen;
size_t line_len = 0;
float line_height = (tm->line_height * 128.0f) / 100.0f;
float scale = tm->size / 128.0f;
struct {
float x, y;
} caret;
caret.x = 0;
caret.y = 0;
// find maximum line length
if (max_line_width == 0.f) max_line_width = get_max_line_width(face, text, tlen, tm);
line_len = line_length(line, tend);
while(line_len) {
int line_width = get_line_width(face, line, &line_len, max_line_width, tm);
char *end = line + line_len;
for(char c = *line; line < end; line++, c = *line) {
af_glyph_t *glyph = find_glyph(face, c);
if(!glyph) {
continue;
}
pp_mat3_t caret_transform = *tm->transform;
pp_mat3_scale(&caret_transform, scale, scale);
pp_mat3_translate(&caret_transform, caret.x, caret.y);
if(tm->align & AF_H_ALIGN_CENTER) {
pp_mat3_translate(&caret_transform, (max_line_width - line_width) / 2, 0);
}
if(tm->align & AF_H_ALIGN_RIGHT) {
pp_mat3_translate(&caret_transform, (max_line_width - line_width), 0);
}
pp_rect_t r = {glyph->x, glyph->y, glyph->x + glyph->w, glyph->y + glyph->h};
//pp_rect_t r = af_glyph_bounds(glyph, tm);
r = pp_rect_transform(&r, &caret_transform);
if(first) {
result = r;
first = false;
} else {
result = pp_rect_merge(&result, &r);
}
if(c == L' ') {
caret.x += (glyph->advance * tm->word_spacing) / 100.0f;
} else {
caret.x += (glyph->advance * tm->letter_spacing) / 100.0f;
}
}
line += 1; // Skip over \n
line_len = line_length(line, tend);
caret.x = 0;
caret.y += line_height;
}
return result;
}
#endif // AF_IMPLEMENTATION
#endif // AF_INCLUDE_H

Wyświetl plik

@ -1,181 +0,0 @@
#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);
}
}
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
*/
// 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);
}
}

Wyświetl plik

@ -1,75 +0,0 @@
#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);
template<typename mat_t>
void render_character(text_metrics_t &tm, uint16_t codepoint, pretty_poly::point_t<int> origin, mat_t transform);
}

Wyświetl plik

@ -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();
};

Wyświetl plik

@ -1,9 +1,19 @@
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
${CMAKE_CURRENT_LIST_DIR}/af-file-io.c
${CMAKE_CURRENT_LIST_DIR}/af-memory.c
)
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)
set_source_files_properties(
${CMAKE_CURRENT_LIST_DIR}/pico_vector.cpp
PROPERTIES COMPILE_FLAGS
"-Wno-narrowing"
)

Wyświetl plik

@ -1,192 +1,72 @@
#define PP_IMPLEMENTATION
#define AF_IMPLEMENTATION
#define PPP_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 = 0; 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) {
pp_path_t *path = poly->paths;
while(path) {
transform(path, t);
path = path->next;
}
}
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;
}
}
pp_point_t PicoVector::text(std::string_view text, int max_width, int max_height, pp_mat3_t *t) {
pp_point_t caret = {0, 0};
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;
}
}
text_metrics.transform = 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);
af_render(text_metrics.face, text.data(), text.size(), max_width, max_height, &text_metrics);
// 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) {
// 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);
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;
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 = 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;
carriage_return = initial_carriage_return;
} 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);
return caret;
}
}

Wyświetl plik

@ -1,7 +1,28 @@
#include "pretty_poly.hpp"
#include "alright_fonts.hpp"
#include "af-file-io.h"
#include "af-memory.h"
#define AF_FILE void*
#define AF_FREAD(p, size, nmemb, stream) fileio_read(stream, p, nmemb)
#define AF_FGETC(stream) fileio_getc(stream)
#define AF_MALLOC(size) af_malloc(size)
#define AF_REALLOC(p, size) af_realloc(p, size)
#define AF_FREE(p) af_free(p)
#define PP_MALLOC(size) af_malloc(size)
#define PP_REALLOC(p, size) af_realloc(p, size)
#define PP_FREE(p) af_free(p)
#define AF_DEBUG(...) af_debug(__VA_ARGS__)
#include "pretty-poly.h"
#include "pretty-poly-primitives.h"
#include "alright-fonts.h"
#include "pico_graphics.hpp"
pp_rect_t pp_contour_bounds(const pp_path_t *c);
namespace pimoroni {
// Integer point types cause compound error in transformations
@ -9,79 +30,138 @@ namespace pimoroni {
class PicoVector {
private:
PicoGraphics *graphics;
alright_fonts::text_metrics_t text_metrics;
const uint8_t alpha_map[4] {0, 128, 192, 255};
af_text_metrics_t text_metrics;
// Hold copies of pretty-poly's pointers
// so MicroPython does not garbage collect them!
void *_pp_nodes;
void *_pp_node_counts;
public:
PicoVector(PicoGraphics *graphics, void *mem = nullptr) : graphics(graphics) {
pretty_poly::init(mem);
static PicoGraphics *graphics;
PicoVector(PicoGraphics *graphics) {
PicoVector::graphics = graphics;
set_options([this](const pretty_poly::tile_t &tile) -> void {
uint8_t *tile_data = tile.data;
// TODO: Make these configurable?
// Tile buffer size, Max nodes per scanline
pp_init(16);
_pp_nodes = pp_nodes;
_pp_node_counts = pp_node_counts;
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_tile_callback(PicoVector::tile_callback);
set_antialiasing(graphics->supports_alpha_blend() ? PP_AA_X4 : PP_AA_NONE);
pp_clip(graphics->clip.x, graphics->clip.y, graphics->clip.w, graphics->clip.h);
text_metrics.align = AF_H_ALIGN_LEFT;
text_metrics.line_height = 110;
text_metrics.letter_spacing = 95;
text_metrics.word_spacing = 200;
text_metrics.size = 48;
// Should be set before rendering chars
//text_metrics.transform = (pp_mat3_t *)af_malloc(sizeof(pp_mat3_t));
//*text_metrics.transform = pp_mat3_identity();
}
void set_antialiasing(pretty_poly::antialias_t antialias) {
set_options(pretty_poly::settings::callback, antialias, pretty_poly::settings::clip);
~PicoVector() {
pp_deinit();
}
static void tile_callback(const pp_tile_t *tile) {
// TODO: we're using a cast here to avoid a hard dependency link between
// PicoGraphics and PicoVector. These types might subtly mismatch, though...
PicoVector::graphics->render_tile((pimoroni::Tile *)tile);
}
void set_antialiasing(pp_antialias_t antialias) {
pp_antialias(antialias);
}
void set_font_size(unsigned int font_size) {
text_metrics.set_size(font_size);
text_metrics.size = font_size;
}
void set_font_word_spacing(unsigned int font_wordspacing) {
text_metrics.word_spacing = font_wordspacing;
}
void set_font_letter_spacing(unsigned int font_letterspacing) {
text_metrics.letter_spacing = font_letterspacing;
}
void set_font_line_height(unsigned int font_line_height) {
text_metrics.line_height = font_line_height;
}
void set_font_align(unsigned int font_align) {
text_metrics.align = font_align;
}
pp_rect_t measure_text(std::string_view text, pp_mat3_t *t) {
text_metrics.transform = t;
return af_measure(text_metrics.face, text.data(), text.size(), 0, &text_metrics);
}
bool set_font(std::string_view font_path, unsigned int font_size) {
bool result = text_metrics.face.load(font_path);
if(text_metrics.face) {
af_free(text_metrics.face->glyphs);
af_free(text_metrics.face);
}
text_metrics.face = (af_face_t *)af_malloc(sizeof(af_face_t));
//bool result = text_metrics.face.load(font_path);
void* font = fileio_open(font_path.data());
bool result = af_load_font_file(font, text_metrics.face);
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);
bool set_font(void* font, unsigned int font_size) {
if(text_metrics.face) {
af_free(text_metrics.face->glyphs);
af_free(text_metrics.face);
}
text_metrics.face = (af_face_t *)af_malloc(sizeof(af_face_t));
bool result = af_load_font_file(font, text_metrics.face);
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);
set_font_size(font_size);
Point text(std::string_view text, Point origin);
Point text(std::string_view text, Point origin, float angle);
return result;
}
void polygon(std::vector<pretty_poly::contour_t<picovector_point_type>> contours, Point origin = Point(0, 0), int scale=65536);
pp_point_t text(std::string_view text, int max_width, int max_height, pp_mat3_t *t=nullptr);
void transform(pp_path_t *path, pp_mat3_t *t);
void transform(pp_poly_t *poly, pp_mat3_t *t);
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 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 = pp_poly_new();
poly->paths = path;
draw(poly);
poly->paths = NULL; // Don't free our non-owned path
pp_poly_free(poly);
};
void draw(pp_path_t *path, pp_mat3_t *t) {
pp_poly_t *poly = pp_poly_new();
poly->paths = path;
draw(poly, t);
poly->paths = NULL; // Don't free our non-owned path
pp_poly_free(poly);
};
static constexpr size_t pretty_poly_buffer_size() {
return pretty_poly::buffer_size();
return 0;
};
};
}

Wyświetl plik

@ -0,0 +1,226 @@
/*
Pretty Poly 🦜 - super-sampling polygon renderer for low resource platforms.
Jonathan Williamson, August 2022
Examples, source, and more: https://github.com/lowfatcode/pretty-poly
MIT License https://github.com/lowfatcode/pretty-poly/blob/main/LICENSE
An easy way to render high quality graphics in embedded applications running
on resource constrained microcontrollers such as the Cortex M0 and up.
- Renders polygons: concave, self-intersecting, multi contour, holes, etc.
- C11 header only library: simply copy the header file into your project
- Tile based renderer: low memory footprint, cache coherency
- Low memory usage: ~4kB of heap memory required
- High speed on low resource platforms: optionally no floating point
- Antialiasing modes: X1 (none), X4 and X16 super sampling
- Bounds clipping: all results clipped to supplied clip rectangle
- Pixel format agnostic: renders a "tile" to blend into your framebuffer
- Support for hardware interpolators on rp2040 (thanks @MichaelBell!)
Contributor bwaaaaaarks! 🦜
@MichaelBell - lots of bug fixes, performance boosts, and suggestions.
@gadgetoid - integrating into the PicoVector library and testing.
*/
#ifndef PPP_INCLUDE_H
#define PPP_INCLUDE_H
#include "pretty-poly.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
PP_COORD_TYPE x, y, w, h; // coordinates
PP_COORD_TYPE s; // stroke thickness (0 == filled)
PP_COORD_TYPE r1, r2, r3, r4; // corner radii (r1 = top left then clockwise)
} ppp_rect_def;
typedef struct {
PP_COORD_TYPE x, y; // coordinates
PP_COORD_TYPE r; // radius
int e; // edge count
PP_COORD_TYPE s; // stroke thickness (0 == filled)
} ppp_regular_def;
typedef struct {
PP_COORD_TYPE x, y; // coordinates
PP_COORD_TYPE r; // radius
PP_COORD_TYPE s; // stroke thickness (0 == filled)
} ppp_circle_def;
typedef struct {
PP_COORD_TYPE x, y; // coordinates
PP_COORD_TYPE r; // radius
PP_COORD_TYPE s; // stroke thickness (0 == filled)
PP_COORD_TYPE f, t; // angle from and to
} ppp_arc_def;
typedef struct {
PP_COORD_TYPE x, y; // coordinates
int c; // number of points on star
PP_COORD_TYPE ro, ri; // outer and inner radius for points
PP_COORD_TYPE s; // stroke thickness (0 == filled)
} ppp_star_def;
typedef struct {
PP_COORD_TYPE x1, y1; // start point
PP_COORD_TYPE x2, y2; // end point
PP_COORD_TYPE s; // thickness
} ppp_line_def;
pp_poly_t* ppp_rect(ppp_rect_def d);
pp_poly_t* ppp_regular(ppp_regular_def d);
pp_poly_t* ppp_circle(ppp_circle_def d);
pp_poly_t* ppp_arc(ppp_arc_def d);
pp_poly_t* ppp_star(ppp_star_def d);
pp_poly_t* ppp_line(ppp_line_def d);
#ifdef __cplusplus
}
#endif
#ifdef PPP_IMPLEMENTATION
void _pp_round_rect_corner_points(pp_path_t *path, PP_COORD_TYPE cx, PP_COORD_TYPE cy, PP_COORD_TYPE r, int q) {
float quality = 5; // higher the number, lower the quality - selected by experiment
int steps = ceil(r / quality) + 2; // + 2 to include start and end
float delta = -(M_PI / 2) / steps;
float theta = (M_PI / 2) * q; // select start theta for this quadrant
for(int i = 0; i <= steps; i++) {
PP_COORD_TYPE xo = sin(theta) * r, yo = cos(theta) * r;
pp_path_add_point(path, (pp_point_t){cx + xo, cy + yo});
theta += delta;
}
}
void _ppp_rrect_corner(pp_path_t *path, PP_COORD_TYPE cx, PP_COORD_TYPE cy, PP_COORD_TYPE r, int q) {
float quality = 5; // higher the number, lower the quality - selected by experiment
int steps = ceil(r / quality) + 2; // + 2 to include start and end
float delta = -(M_PI / 2) / steps;
float theta = (M_PI / 2) * q; // select start theta for this quadrant
for(int i = 0; i <= steps; i++) {
PP_COORD_TYPE xo = sin(theta) * r, yo = cos(theta) * r;
pp_path_add_point(path, (pp_point_t){cx + xo, cy + yo});
theta += delta;
}
}
void _ppp_rrect_path(pp_path_t *path, ppp_rect_def d) {
d.r1 == 0 ? pp_path_add_point(path, (pp_point_t){d.x, d.y}) : _ppp_rrect_corner(path, d.x + d.r1, d.y + d.r1, d.r1, 3);
d.r2 == 0 ? pp_path_add_point(path, (pp_point_t){d.x + d.w, d.y}) : _ppp_rrect_corner(path, d.x + d.w - d.r2, d.y + d.r2, d.r2, 2);
d.r3 == 0 ? pp_path_add_point(path, (pp_point_t){d.x + d.w, d.y + d.h}) : _ppp_rrect_corner(path, d.x + d.w - d.r3, d.y + d.h - d.r3, d.r3, 1);
d.r4 == 0 ? pp_path_add_point(path, (pp_point_t){d.x, d.y}) : _ppp_rrect_corner(path, d.x + d.r4, d.y + d.h - d.r4, d.r4, 0);
}
pp_poly_t* ppp_rect(ppp_rect_def d) {
pp_poly_t *poly = pp_poly_new();
pp_path_t *path = pp_poly_add_path(poly);
if(d.r1 == 0.0f && d.r2 == 0.0f && d.r3 == 0.0f && d.r4 == 0.0f) { // non rounded rect
pp_point_t points[] = {{d.x, d.y}, {d.x + d.w, d.y}, {d.x + d.w, d.y + d.h}, {d.x, d.y + d.h}};
pp_path_add_points(path, points, 4);
if(d.s != 0) { // stroked, not filled
d.x += d.s; d.y += d.s; d.w -= 2 * d.s; d.h -= 2 * d.s;
pp_path_t *inner = pp_poly_add_path(poly);
pp_point_t points[] = {{d.x, d.y}, {d.x + d.w, d.y}, {d.x + d.w, d.y + d.h}, {d.x, d.y + d.h}};
pp_path_add_points(inner, points, 4);
}
}else{ // rounded rect
_ppp_rrect_path(path, d);
if(d.s != 0) { // stroked, not filled
d.x += d.s; d.y += d.s; d.w -= 2 * d.s; d.h -= 2 * d.s;
d.r1 = _pp_max(0, d.r1 - d.s);
d.r2 = _pp_max(0, d.r2 - d.s);
d.r3 = _pp_max(0, d.r3 - d.s);
d.r4 = _pp_max(0, d.r4 - d.s);
pp_path_t *inner = pp_poly_add_path(poly);
_ppp_rrect_path(inner, d);
}
}
return poly;
}
pp_poly_t* ppp_regular(ppp_regular_def d) {
pp_poly_t *poly = pp_poly_new();
pp_path_t *path = pp_poly_add_path(poly);
pp_path_t *inner = d.s != 0.0f ? pp_poly_add_path(poly) : NULL;
for(int i = 0; i < d.e; i++) {
float theta = ((M_PI * 2.0f) / (float)d.e) * (float)i;
pp_path_add_point(path, (pp_point_t){sin(theta) * d.r + d.x, cos(theta) * d.r + d.y});
if(inner) {
pp_path_add_point(inner, (pp_point_t){sin(theta) * (d.r - d.s) + d.x, cos(theta) * (d.r - d.s) + d.y});
}
}
return poly;
}
pp_poly_t* ppp_circle(ppp_circle_def d) {
int e = _pp_max(8, d.r); // edge count
ppp_regular_def r = {d.x, d.y, d.r, e, d.s};
return ppp_regular(r);
}
pp_poly_t* ppp_arc(ppp_arc_def d) {
pp_poly_t *poly = pp_poly_new();
pp_path_t *path = pp_poly_add_path(poly);
pp_path_t *inner = (pp_path_t *)(d.s == 0.0f ? NULL : calloc(1, sizeof(pp_path_t)));
// no thickness, so add centre point to make pie shape
if(!inner) pp_path_add_point(path, (pp_point_t){d.x, d.y});
d.f = d.f * (M_PI / 180.0f); d.t = d.t * (M_PI / 180.0f); // to radians
int s = _pp_max(8, d.r); float astep = (d.t - d.f) / s; float a = d.f;
for(int i = 0; i <= s; i++) {
pp_path_add_point(path, (pp_point_t){sin(a) * d.r + d.x, cos(a) * d.r + d.y});
if(inner) {
pp_path_add_point(inner, (pp_point_t){sin(d.t - (a - d.f)) * (d.r - d.s) + d.x, cos(d.t - (a - d.f)) * (d.r - d.s) + d.y});
}
a += astep;
}
if(inner) { // append the inner path
pp_path_add_points(path, inner->points, inner->count);
free(inner->points); free(inner);
}
return poly;
}
pp_poly_t* ppp_star(ppp_star_def d) {
pp_poly_t *poly = pp_poly_new();
pp_path_t *path = pp_poly_add_path(poly);
pp_path_t *inner = d.s != 0.0f ? pp_poly_add_path(poly) : NULL;
for(int i = 0; i < d.c * 2; i++) {
float step = ((M_PI * 2) / (float)(d.c * 2)) * (float)i;
PP_COORD_TYPE r = i % 2 == 0 ? d.ro : d.ri;
pp_path_add_point(path, (pp_point_t){sin(step) * r + d.x, cos(step) * r + d.y});
if(inner) { // append the inner path
PP_COORD_TYPE ior = d.ro - (d.s * d.ro / d.ri);
PP_COORD_TYPE iir = d.ri - d.s;
PP_COORD_TYPE ir = i % 2 == 0 ? ior : iir;
pp_path_add_point(inner, (pp_point_t){sin(step) * ir + d.x, cos(step) * ir + d.y});
}
}
return poly;
}
pp_poly_t* ppp_line(ppp_line_def d) {
pp_poly_t *poly = pp_poly_new();
pp_path_t *path = pp_poly_add_path(poly);
// create a normalised perpendicular vector
pp_point_t v = {d.y2 - d.y1, d.x2 - d.x1};
float mag = sqrt(v.x * v.x + v.y * v.y);
v.x /= mag; v.y /= mag; v.x *= -(d.s / 2.0f); v.y *= (d.s / 2.0f);
pp_point_t points[] = {{d.x1 + v.x, d.y1 + v.y}, {d.x2 + v.x, d.y2 + v.y}, {d.x2 - v.x, d.y2 - v.y}, {d.x1 - v.x, d.y1 - v.y}};
pp_path_add_points(path, points, 4);
return poly;
}
#endif // PPP_IMPLEMENTATION
#endif // PPP_INCLUDE_H

Wyświetl plik

@ -0,0 +1,698 @@
/*
Pretty Poly 🦜 - super-sampling polygon renderer for low resource platforms.
Jonathan Williamson, August 2022
Examples, source, and more: https://github.com/lowfatcode/pretty-poly
MIT License https://github.com/lowfatcode/pretty-poly/blob/main/LICENSE
An easy way to render high quality graphics in embedded applications running
on resource constrained microcontrollers such as the Cortex M0 and up.
- Renders polygons: concave, self-intersecting, multi contour, holes, etc.
- C11 header only library: simply copy the header file into your project
- Tile based renderer: low memory footprint, cache coherency
- Low memory usage: ~4kB of heap memory required
- High speed on low resource platforms: optionally no floating point
- Antialiasing modes: X1 (none), X4 and X16 super sampling
- Bounds clipping: all results clipped to supplied clip rectangle
- Pixel format agnostic: renders a "tile" to blend into your framebuffer
- Support for hardware interpolators on rp2040 (thanks @MichaelBell!)
Contributor bwaaaaaarks! 🦜
@MichaelBell - lots of bug fixes, performance boosts, and suggestions.
@gadgetoid - integrating into the PicoVector library and testing.
*/
#ifndef PP_INCLUDE_H
#define PP_INCLUDE_H
#include <stdlib.h>
#include <stdint.h>
#include <limits.h>
#include <string.h>
#include <math.h>
#include <stdbool.h>
#ifndef PP_COORD_TYPE
#define PP_COORD_TYPE float
#endif
#ifndef PP_TILE_BUFFER_SIZE
#define PP_TILE_BUFFER_SIZE 64
#endif
#ifndef PP_SCALE_TO_ALPHA
#define PP_SCALE_TO_ALPHA 1
#endif
#if defined(PICO_ON_DEVICE) && PICO_ON_DEVICE
#define USE_RP2040_INTERP
#include "hardware/interp.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
// 3x3 matrix type allows for optional transformation of polygon during render
typedef struct {
float v00, v10, v20, v01, v11, v21, v02, v12, v22;
} pp_mat3_t;
pp_mat3_t pp_mat3_identity();
void pp_mat3_rotate(pp_mat3_t *m, float a);
void pp_mat3_rotate_rad(pp_mat3_t *m, float a);
void pp_mat3_translate(pp_mat3_t *m, float x, float y);
void pp_mat3_scale(pp_mat3_t *m, float x, float y);
void pp_mat3_mul(pp_mat3_t *m1, pp_mat3_t *m2);
// point type used to hold polygon vertex coordinates
typedef struct __attribute__((__packed__)) pp_point_t {
PP_COORD_TYPE x, y;
} pp_point_t;
pp_point_t pp_point_add(pp_point_t *p1, pp_point_t *p2);
pp_point_t pp_point_sub(pp_point_t *p1, pp_point_t *p2);
pp_point_t pp_point_mul(pp_point_t *p1, pp_point_t *p2);
pp_point_t pp_point_div(pp_point_t *p1, pp_point_t *p2);
pp_point_t pp_point_transform(pp_point_t *p, pp_mat3_t *m);
// rect type
typedef struct {
int32_t x, y, w, h;
} pp_rect_t;
bool pp_rect_empty(pp_rect_t *r);
pp_rect_t pp_rect_intersection(pp_rect_t *r1, pp_rect_t *r2);
pp_rect_t pp_rect_merge(pp_rect_t *r1, pp_rect_t *r2);
pp_rect_t pp_rect_transform(pp_rect_t *r, pp_mat3_t *m);
// antialias levels
typedef enum {PP_AA_NONE = 0, PP_AA_FAST = 1, PP_AA_X4 = 1, PP_AA_BEST = 2, PP_AA_X16 = 2} pp_antialias_t;
typedef struct {
int32_t x, y, w, h;
uint32_t stride;
uint8_t *data;
} pp_tile_t;
typedef struct _pp_path_t {
pp_point_t *points;
int count; // number of points currently stored in points buffer
int storage; // size of *points buffer
struct _pp_path_t *next; // next path in the linked list
} pp_path_t;
void pp_path_add_point(pp_path_t *path, pp_point_t p);
void pp_path_add_points(pp_path_t *path, pp_point_t *p, int count);
void pp_path_add_path(pp_path_t *path, pp_path_t *other);
void pp_path_union(pp_path_t *path, pp_path_t *other);
pp_rect_t pp_path_bounds(const pp_path_t *c);
typedef struct {
pp_path_t *paths;
} pp_poly_t;
pp_poly_t *pp_poly_new();
void pp_poly_free(pp_poly_t *poly);
pp_path_t* pp_poly_tail_path(pp_poly_t *p);
pp_path_t* pp_poly_add_path(pp_poly_t *p);
pp_rect_t pp_poly_bounds(pp_poly_t *p);
int pp_poly_path_count(pp_poly_t *p);
void pp_poly_merge(pp_poly_t *p, pp_poly_t *m);
// user settings
typedef void (*pp_tile_callback_t)(const pp_tile_t *tile);
extern pp_rect_t _pp_clip;
extern pp_tile_callback_t _pp_tile_callback;
extern pp_antialias_t _pp_antialias;
extern pp_mat3_t *_pp_transform;
// Our parent scope might want to hold a pointer to these
// ie: MicroPython to avoid garbage collection
extern int32_t *pp_nodes;
extern uint32_t *pp_node_counts;
void pp_clip(int32_t x, int32_t y, int32_t w, int32_t h);
void pp_tile_callback(pp_tile_callback_t callback);
void pp_antialias(pp_antialias_t antialias);
pp_mat3_t *pp_transform(pp_mat3_t *transform);
void pp_render(pp_poly_t *polygon);
void pp_init(uint32_t max_nodes_per_scanline);
void pp_deinit();
#ifdef __cplusplus
}
#endif
#ifdef PP_IMPLEMENTATION
#ifndef PP_MALLOC
#define PP_MALLOC(size) malloc(size)
#define PP_REALLOC(p, size) realloc(p, size)
#define PP_FREE(p) free(p)
#endif
#ifdef PP_DEBUG
#define debug(...) printf(__VA_ARGS__)
#else
#define debug(...)
#endif
pp_rect_t _pp_clip = (pp_rect_t){-INT_MAX, -INT_MAX, INT_MAX, INT_MAX};
pp_tile_callback_t _pp_tile_callback = NULL;
pp_antialias_t _pp_antialias = PP_AA_X4;
pp_mat3_t *_pp_transform = NULL;
int _pp_max(int a, int b) { return a > b ? a : b; }
int _pp_min(int a, int b) { return a < b ? a : b; }
int _pp_sign(int v) {return (v > 0) - (v < 0);}
void _pp_swap(int *a, int *b) {int t = *a; *a = *b; *b = t;}
// pp_mat3_t implementation
pp_mat3_t pp_mat3_identity() {
pp_mat3_t m; memset(&m, 0, sizeof(pp_mat3_t)); m.v00 = m.v11 = m.v22 = 1.0f; return m;}
void pp_mat3_rotate(pp_mat3_t *m, float a) {
pp_mat3_rotate_rad(m, a * M_PI / 180.0f);}
void pp_mat3_rotate_rad(pp_mat3_t *m, float a) {
float c = cosf(a), s = sinf(a); pp_mat3_t r = pp_mat3_identity();
r.v00 = c; r.v01 = -s; r.v10 = s; r.v11 = c; pp_mat3_mul(m, &r); }
void pp_mat3_translate(pp_mat3_t *m, float x, float y) {
pp_mat3_t r = pp_mat3_identity(); r.v02 = x; r.v12 = y; pp_mat3_mul(m, &r);}
void pp_mat3_scale(pp_mat3_t *m, float x, float y) {
pp_mat3_t r = pp_mat3_identity(); r.v00 = x; r.v11 = y; pp_mat3_mul(m, &r);}
void pp_mat3_mul(pp_mat3_t *m1, pp_mat3_t *m2) {
pp_mat3_t r;
r.v00 = m1->v00 * m2->v00 + m1->v01 * m2->v10 + m1->v02 * m2->v20;
r.v01 = m1->v00 * m2->v01 + m1->v01 * m2->v11 + m1->v02 * m2->v21;
r.v02 = m1->v00 * m2->v02 + m1->v01 * m2->v12 + m1->v02 * m2->v22;
r.v10 = m1->v10 * m2->v00 + m1->v11 * m2->v10 + m1->v12 * m2->v20;
r.v11 = m1->v10 * m2->v01 + m1->v11 * m2->v11 + m1->v12 * m2->v21;
r.v12 = m1->v10 * m2->v02 + m1->v11 * m2->v12 + m1->v12 * m2->v22;
r.v20 = m1->v20 * m2->v00 + m1->v21 * m2->v10 + m1->v22 * m2->v20;
r.v21 = m1->v20 * m2->v01 + m1->v21 * m2->v11 + m1->v22 * m2->v21;
r.v22 = m1->v20 * m2->v02 + m1->v21 * m2->v12 + m1->v22 * m2->v22;
*m1 = r;
}
// pp_point_t implementation
pp_point_t pp_point_add(pp_point_t *p1, pp_point_t *p2) {
return (pp_point_t){.x = p1->x + p2->x, .y = p1->y + p2->y};
}
pp_point_t pp_point_sub(pp_point_t *p1, pp_point_t *p2) {
return (pp_point_t){.x = p1->x - p2->x, .y = p1->y - p2->y};
}
pp_point_t pp_point_mul(pp_point_t *p1, pp_point_t *p2) {
return (pp_point_t){.x = p1->x * p2->x, .y = p1->y * p2->y};
}
pp_point_t pp_point_div(pp_point_t *p1, pp_point_t *p2) {
return (pp_point_t){.x = p1->x / p2->x, .y = p1->y / p2->y};
}
pp_point_t pp_point_transform(pp_point_t *p, pp_mat3_t *m) {
return (pp_point_t){
.x = (m->v00 * p->x + m->v01 * p->y + m->v02),
.y = (m->v10 * p->x + m->v11 * p->y + m->v12)
};
}
// pp_rect_t implementation
bool pp_rect_empty(pp_rect_t *r) {
return r->w == 0 || r->h == 0;
}
pp_rect_t pp_rect_intersection(pp_rect_t *r1, pp_rect_t *r2) {
return (pp_rect_t){
.x = _pp_max(r1->x, r2->x), .y = _pp_max(r1->y, r2->y),
.w = _pp_max(0, _pp_min(r1->x + r1->w, r2->x + r2->w) - _pp_max(r1->x, r2->x)),
.h = _pp_max(0, _pp_min(r1->y + r1->h, r2->y + r2->h) - _pp_max(r1->y, r2->y))
};
}
pp_rect_t pp_rect_merge(pp_rect_t *r1, pp_rect_t *r2) {
return (pp_rect_t){
.x = _pp_min(r1->x, r2->x),
.y = _pp_min(r1->y, r2->y),
.w = _pp_max(r1->x + r1->w, r2->x + r2->w) - _pp_min(r1->x, r2->x),
.h = _pp_max(r1->y + r1->h, r2->y + r2->h) - _pp_min(r1->y, r2->y)
};
}
pp_rect_t pp_rect_transform(pp_rect_t *r, pp_mat3_t *m) {
pp_point_t tl = {.x = (PP_COORD_TYPE)r->x, .y = (PP_COORD_TYPE)r->y};
pp_point_t tr = {.x = (PP_COORD_TYPE)r->x + (PP_COORD_TYPE)r->w, .y = (PP_COORD_TYPE)r->y};
pp_point_t bl = {.x = (PP_COORD_TYPE)r->x, .y = (PP_COORD_TYPE)r->y + (PP_COORD_TYPE)r->h};
pp_point_t br = {.x = (PP_COORD_TYPE)r->x + (PP_COORD_TYPE)r->w, .y = (PP_COORD_TYPE)r->y + (PP_COORD_TYPE)r->h};
tl = pp_point_transform(&tl, m);
tr = pp_point_transform(&tr, m);
bl = pp_point_transform(&bl, m);
br = pp_point_transform(&br, m);
PP_COORD_TYPE minx = _pp_min(tl.x, _pp_min(tr.x, _pp_min(bl.x, br.x)));
PP_COORD_TYPE miny = _pp_min(tl.y, _pp_min(tr.y, _pp_min(bl.y, br.y)));
PP_COORD_TYPE maxx = _pp_max(tl.x, _pp_max(tr.x, _pp_max(bl.x, br.x)));
PP_COORD_TYPE maxy = _pp_max(tl.y, _pp_max(tr.y, _pp_max(bl.y, br.y)));
return (pp_rect_t){
.x = (int32_t)minx,
.y = (int32_t)miny,
.w = (int32_t)(maxx - minx),
.h = (int32_t)(maxy - miny)
};
}
// pp_tile_t implementation
uint8_t pp_tile_get(const pp_tile_t *tile, const int32_t x, const int32_t y) {
return tile->data[(x - tile->x) + (y - tile->y) * PP_TILE_BUFFER_SIZE];
}
pp_poly_t *pp_poly_new() {
pp_poly_t *poly = (pp_poly_t *)PP_MALLOC(sizeof(pp_poly_t));
poly->paths = NULL;
return poly;
}
void pp_poly_free(pp_poly_t *poly) {
if(poly->paths) {
pp_path_t *path = poly->paths;
while(path) {
PP_FREE(path->points);
pp_path_t *free_path = path;
path = path->next;
PP_FREE(free_path);
}
}
PP_FREE(poly);
}
// polygon and path implementation
pp_path_t* pp_poly_tail_path(pp_poly_t *poly) {
pp_path_t *path = poly->paths;
while(path->next) path = path->next;
return path;
}
int pp_poly_path_count(pp_poly_t *poly) {
if(!poly->paths) return 0;
pp_path_t *path = poly->paths;
int i = 0;
while(path->next) {
i++;
path = path->next;
}
return i;
}
pp_path_t* pp_poly_add_path(pp_poly_t *poly) {
pp_path_t *path = (pp_path_t *)PP_MALLOC(sizeof(pp_path_t));
memset(path, 0, sizeof(pp_path_t));
path->storage = 8;
path->points = (pp_point_t *)PP_MALLOC(sizeof(pp_point_t) * path->storage);
if(!poly->paths) {
poly->paths = path;
}else{
pp_path_t *tail = pp_poly_tail_path(poly);
tail->next = path;
}
return path;
}
void pp_poly_merge(pp_poly_t *p, pp_poly_t *m) {
if(!p->paths) {
p->paths = m->paths;
}else{
pp_poly_tail_path(p)->next = m->paths;
}
m->paths = NULL;
pp_poly_free(m);
}
pp_point_t* pp_path_tail_point(pp_path_t *path) {
return (path->count > 0) ? &path->points[path->count -1] : NULL;
}
void pp_path_add_point(pp_path_t *path, pp_point_t p) {
if(path->count == path->storage) { // no storage left, double buffer size
if(path->points) {
path->storage *= 2;
path->points = (pp_point_t *)PP_REALLOC(path->points, sizeof(pp_point_t) * (path->storage));
}else{
path->storage = 8;
path->points = (pp_point_t *)PP_MALLOC(sizeof(pp_point_t) * (path->storage));
}
}
path->points[path->count] = p;
path->count++;
}
void pp_path_add_points(pp_path_t *path, pp_point_t *points, int count) {
if(count + path->count > path->storage) { // not enough storage, allocate
path->storage = path->count + count;
path->points = (pp_point_t *)PP_REALLOC(path->points, sizeof(pp_point_t) * (path->storage));
}
memcpy(&path->points[path->count], points, sizeof(pp_point_t) * count);
path->count += count;
}
// pp_contour_t implementation
pp_rect_t pp_path_bounds(const pp_path_t *path) {
int minx = INT_MAX, maxx = -INT_MAX, miny = INT_MAX, maxy = -INT_MAX;
for(int i = 0; i < path->count; i++) {
minx = _pp_min(minx, path->points[i].x);
miny = _pp_min(miny, path->points[i].y);
maxx = _pp_max(maxx, path->points[i].x);
maxy = _pp_max(maxy, path->points[i].y);
}
return (pp_rect_t){minx, miny, maxx - minx, maxy - miny};
}
void pp_path_union(pp_path_t *path, pp_path_t *other) {
}
pp_rect_t pp_poly_bounds(pp_poly_t *p) {
pp_path_t *path = p->paths;
if(!path) return (pp_rect_t){};
pp_rect_t b = pp_path_bounds(path);
path = path->next;
while(path) {
pp_rect_t cb = pp_path_bounds(path);
b = pp_rect_merge(&b, &cb);
path = path->next;
}
return b;
}
uint32_t _pp_max_nodes_per_scanline = 0;
// buffer that each tile is rendered into before callback
// This allocates 4k up-front to ensure it's stored in Pico's RAM
// Rather than potentially allocating into PSRAM at runtime and trashing perf
uint8_t pp_tile_buffer[PP_TILE_BUFFER_SIZE * PP_TILE_BUFFER_SIZE];
// polygon node buffer handles at most 16 line intersections per scanline
// is this enough for cjk/emoji? (requires a 2kB buffer)
int32_t *pp_nodes;
uint32_t *pp_node_counts;
uint8_t _pp_alpha_map_none[2] = {0, 255};
uint8_t _pp_alpha_map_x4[5] = {0, 63, 127, 190, 255};
uint8_t _pp_alpha_map_x16[17] = {0, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 255};
void pp_init(uint32_t max_nodes_per_scanline) {
_pp_max_nodes_per_scanline = max_nodes_per_scanline;
pp_nodes = (int32_t *)PP_MALLOC(PP_TILE_BUFFER_SIZE * 4 * max_nodes_per_scanline * 2 * sizeof(int32_t));
pp_node_counts = (uint32_t *)PP_MALLOC(PP_TILE_BUFFER_SIZE * 4 * sizeof(uint32_t));
}
void pp_deinit() {
PP_FREE(pp_nodes);
PP_FREE(pp_node_counts);
}
void pp_clip(int32_t x, int32_t y, int32_t w, int32_t h) {
_pp_clip = (pp_rect_t){.x = x, .y = y, .w = w, .h = h};
}
void pp_tile_callback(pp_tile_callback_t callback) {
_pp_tile_callback = callback;
}
// maximum tile bounds determined by antialias level
void pp_antialias(pp_antialias_t antialias) {
_pp_antialias = antialias;
}
pp_mat3_t *pp_transform(pp_mat3_t *transform) {
pp_mat3_t *old = _pp_transform;
_pp_transform = transform;
return old;
}
// write out the tile bits
void debug_tile(const pp_tile_t *tile) {
debug(" - tile %d, %d (%d x %d)\n", tile->x, tile->y, tile->w, tile->h);
for(int32_t y = 0; y < tile->h; y++) {
debug("[%3d]: ", y);
for(int32_t x = 0; x < tile->w; x++) {
debug("%02x", pp_tile_get(tile, x, y));
}
debug("\n");
}
debug("-----------------------\n");
}
void add_line_segment_to_nodes(const pp_point_t start, const pp_point_t end, pp_rect_t *tb) {
int32_t sx = start.x, sy = start.y, ex = end.x, ey = end.y;
if(ey < sy) {
// 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)
int32_t ty = sy; sy = ey; ey = ty;
int32_t tx = sx; sx = ex; ex = tx;
}
// early out if line is completely outside the tile, or has no gradient
if (ey < 0 || sy >= (int)(tb->h << _pp_antialias) || 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 = _pp_max(0, sy);
int count = _pp_min((int)(tb->h << _pp_antialias), ey) - y;
int x = sx;
int e = 0;
const int xinc = _pp_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;
}
// #ifdef USE_RP2040_INTERP
// 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
// pp_nodes[y][pp_node_counts[y]++] = nx;
// // step to next scanline and accumulate error
// y++;
// e += einc;
// }
// #else
// loop over scanlines
while(count--) {
int32_t *pp_scanline_nodes = &pp_nodes[y * _pp_max_nodes_per_scanline * 2];
// consume accumulated error
while(e > dy) {e -= dy; x += xinc;}
// clamp node x value to tile bounds
int nx = _pp_max(_pp_min(x, (tb->w << _pp_antialias)), 0);
//debug(" + adding node at %d, %d\n", x, y);
// add node to node list
pp_scanline_nodes[pp_node_counts[y]++] = nx;
// step to next scanline and accumulate error
y++;
e += einc;
}
//#endif
}
void build_nodes(pp_path_t *path, pp_rect_t *tb) {
PP_COORD_TYPE aa_scale = (PP_COORD_TYPE)(1 << _pp_antialias);
pp_point_t tile_origin = (pp_point_t){tb->x * aa_scale, tb->y * aa_scale};
// start with the last point to close the loop, transform it, scale for antialiasing, and offset to tile origin
pp_point_t last = path->points[path->count - 1];
if(_pp_transform) last = pp_point_transform(&last, _pp_transform);
last.x *= aa_scale; last.y *= aa_scale;
last = pp_point_sub(&last, &tile_origin);
for(int i = 0; i < path->count; i++) {
pp_point_t next = path->points[i];
if(_pp_transform) next = pp_point_transform(&next, _pp_transform);
next.x *= aa_scale; next.y *= aa_scale;
next = pp_point_sub(&next, &tile_origin);
add_line_segment_to_nodes(last, next, tb);
last = next;
}
}
int compare_nodes(const void* a, const void* b) {
return *((int*)a) - *((int*)b);
}
pp_rect_t render_nodes(pp_rect_t *tb) {
pp_rect_t rb = {PP_TILE_BUFFER_SIZE << _pp_antialias, PP_TILE_BUFFER_SIZE << _pp_antialias, 0, 0}; // render bounds
int maxx = 0, minx = PP_TILE_BUFFER_SIZE << _pp_antialias;
debug(" + render tile %d, %d - %d, %d\n", tb->x, tb->y, tb->w, tb->h);
for(int y = 0; y < ((int)PP_TILE_BUFFER_SIZE << _pp_antialias); y++) {
int32_t *pp_scanline_nodes = &pp_nodes[y * _pp_max_nodes_per_scanline * 2];
// debug(" : row %d node count %d\n", y, pp_node_counts[y]);
if(pp_node_counts[y] == 0) continue; // no nodes on this raster line
qsort(pp_scanline_nodes, pp_node_counts[y], sizeof(int), compare_nodes);
unsigned char* row_data = &pp_tile_buffer[(y >> _pp_antialias) * PP_TILE_BUFFER_SIZE];
for(uint32_t i = 0; i < pp_node_counts[y]; i += 2) {
int sx = *pp_scanline_nodes++;
int ex = *pp_scanline_nodes++;
if(sx == ex) { // empty span, nothing to do
continue;
}
// update render bounds
rb.x = _pp_min(rb.x, sx);
rb.y = _pp_min(rb.y, y);
minx = _pp_min(_pp_min(sx, ex), minx);
maxx = _pp_max(_pp_max(sx, ex), maxx);
rb.h = y - rb.y + 1;
//debug(" - render span at %d from %d to %d\n", y, sx, ex);
// rasterise the span into the tile buffer
do {
row_data[sx >> _pp_antialias]++;
} while(++sx < ex);
}
}
rb.w = maxx - minx;
// shifting the width and height effectively "floors" the result which can
// mean we lose a pixel off the right or bottom edge of the tile. by adding
// either 1 (at x4) or 3 (at x16) we change that to a "ceil" instead ensuring
// the full tile bounds are returned
if(_pp_antialias) {
int maxx = rb.x + rb.w + (_pp_antialias | 0b1);
int maxy = rb.y + rb.h + (_pp_antialias | 0b1);
rb.x >>= _pp_antialias;
rb.y >>= _pp_antialias;
rb.w = (maxx >> _pp_antialias) - rb.x;
rb.h = (maxy >> _pp_antialias) - rb.y;
}
uint8_t *p_alpha_map = _pp_alpha_map_none;
if(_pp_antialias == 1) p_alpha_map = _pp_alpha_map_x4;
if(_pp_antialias == 2) p_alpha_map = _pp_alpha_map_x16;
#if PP_SCALE_TO_ALPHA == 1
for(int y = rb.y; y < rb.y + rb.h; y++) {
unsigned char* row_data = &pp_tile_buffer[y * PP_TILE_BUFFER_SIZE + rb.x];
for(int x = rb.x; x < rb.x + rb.w; x++) {
*row_data = p_alpha_map[*row_data];
row_data++;
}
}
#endif
debug(" : rendered tile bounds %d, %d (%d x %d)\n", rb.x, rb.y, rb.w, rb.h);
return rb;
}
void pp_render(pp_poly_t *polygon) {
debug("> draw polygon with %u contours\n", pp_poly_path_count(polygon));
if(!polygon->paths) return;
// determine extreme bounds
pp_rect_t pb = pp_poly_bounds(polygon);
if(_pp_transform) {
pb = pp_rect_transform(&pb, _pp_transform);
}
debug(" - polygon bounds %d, %d (%d x %d)\n", pb.x, pb.y, pb.w, pb.h);
debug(" - clip %d, %d (%d x %d)\n", _pp_clip.x, _pp_clip.y, _pp_clip.w, _pp_clip.h);
#ifdef USE_RP2040_INTERP
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;
#endif
// iterate over tiles
debug(" - processing tiles\n");
for(int32_t y = pb.y; y < pb.y + pb.h; y += PP_TILE_BUFFER_SIZE) {
for(int32_t x = pb.x; x < pb.x + pb.w; x += PP_TILE_BUFFER_SIZE) {
pp_rect_t tb = (pp_rect_t){.x = x, .y = y, .w = PP_TILE_BUFFER_SIZE, .h = PP_TILE_BUFFER_SIZE};
tb = pp_rect_intersection(&tb, &_pp_clip);
debug(" : %d, %d (%d x %d)\n", tb.x, tb.y, tb.w, tb.h);
// if no intersection then skip tile
if(pp_rect_empty(&tb)) { debug(" : empty when clipped, skipping\n"); continue; }
// clear existing tile data and nodes
memset(pp_node_counts, 0, PP_TILE_BUFFER_SIZE * 4 * sizeof(uint32_t));
memset(pp_tile_buffer, 0, sizeof(pp_tile_buffer));
// build the nodes for each pp_path_t
pp_path_t *path = polygon->paths;
if(!path) return;
do {
debug(" : build nodes for path (%d points)\n", path->count);
build_nodes(path, &tb);
path = path->next;
} while(path);
debug(" : render the tile\n");
// render the tile
pp_rect_t rb = render_nodes(&tb);
tb.x += rb.x; tb.y += rb.y; tb.w = rb.w; tb.h = rb.h;
if(pp_rect_empty(&tb)) { debug(" : empty after rendering, skipping\n"); continue; }
pp_tile_t tile = {
.x = tb.x, .y = tb.y, .w = tb.w, .h = tb.h,
.stride = PP_TILE_BUFFER_SIZE,
.data = pp_tile_buffer + rb.x + (PP_TILE_BUFFER_SIZE * rb.y)
};
_pp_tile_callback(&tile);
}
}
#ifdef USE_RP2040_INTERP
interp_restore(interp1, &interp1_save);
#endif
}
#endif // PP_IMPLEMENTATION
#endif // PP_INCLUDE_H

Wyświetl plik

@ -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);

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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);
}
};
}

Wyświetl plik

@ -1,11 +1,11 @@
import time
import gc
from picographics import PicoGraphics, DISPLAY_TUFTY_2040, PEN_RGB332
from picovector import PicoVector, Polygon, RegularPolygon, Rectangle, ANTIALIAS_X4
from picographics import PicoGraphics, DISPLAY_TUFTY_2040, PEN_RGB565 as PEN
from picovector import PicoVector, Transform, Polygon, ANTIALIAS_X4
display = PicoGraphics(DISPLAY_TUFTY_2040, pen_type=PEN_RGB332)
display = PicoGraphics(DISPLAY_TUFTY_2040, pen_type=PEN)
vector = PicoVector(display)
vector.set_antialiasing(ANTIALIAS_X4)
@ -25,15 +25,39 @@ WHITE = display.create_pen(14, 60, 76)
WIDTH, HEIGHT = display.get_bounds()
hub = RegularPolygon(int(WIDTH / 2), int(HEIGHT / 2), 24, 5)
t = Transform()
face = RegularPolygon(int(WIDTH / 2), int(HEIGHT / 2), 48, int(HEIGHT / 2))
hub = Polygon()
hub.circle(int(WIDTH / 2), int(HEIGHT / 2), 5)
face = Polygon()
face.circle(int(WIDTH / 2), int(HEIGHT / 2), int(HEIGHT / 2))
tick_mark = Polygon()
tick_mark.rectangle(int(WIDTH / 2) - 3, 10, 6, int(HEIGHT / 48))
hour_mark = Polygon()
hour_mark.rectangle(int(WIDTH / 2) - 5, 10, 10, int(HEIGHT / 10))
second_hand_length = int(HEIGHT / 2) - int(HEIGHT / 8)
second_hand = Polygon()
second_hand.path((-2, -second_hand_length), (-2, int(HEIGHT / 8)), (2, int(HEIGHT / 8)), (2, -second_hand_length))
minute_hand_length = int(HEIGHT / 2) - int(HEIGHT / 24)
minute_hand = Polygon()
minute_hand.path((-5, -minute_hand_length), (-10, int(HEIGHT / 16)), (10, int(HEIGHT / 16)), (5, -minute_hand_length))
hour_hand_length = int(HEIGHT / 2) - int(HEIGHT / 8)
hour_hand = Polygon()
hour_hand.path((-5, -hour_hand_length), (-10, int(HEIGHT / 16)), (10, int(HEIGHT / 16)), (5, -hour_hand_length))
print(time.localtime())
last_second = None
vector.set_transform(None)
while True:
t.reset()
t_start = time.ticks_ms()
year, month, day, hour, minute, second, _, _ = time.localtime()
@ -48,72 +72,79 @@ while True:
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.circle(int(WIDTH / 2), int(HEIGHT / 2), int(HEIGHT / 2) - 4)
vector.draw(face)
display.set_pen(GREY)
vector.set_transform(t)
t.translate(0, 2)
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)
t.rotate(360 / 60.0, (WIDTH / 2, HEIGHT / 2))
if a % 5 == 0:
vector.draw(hour_mark)
else:
vector.draw(tick_mark)
t.reset()
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)
t.translate(WIDTH / 2, HEIGHT / 2 + 5)
t.rotate(angle_minute, (0, 0))
vector.draw(minute_hand)
t.reset()
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)
t.translate(WIDTH / 2, HEIGHT / 2 + 5)
t.rotate(angle_hour, (0, 0))
vector.draw(hour_hand)
t.reset()
t.translate(WIDTH / 2, HEIGHT / 2 + 5)
t.rotate(second * 6, (0, 0))
vector.draw(second_hand)
display.set_pen(BLACK)
t.reset()
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)
t.rotate(360 / 60.0, (WIDTH / 2, HEIGHT / 2))
if a % 5 == 0:
vector.draw(hour_mark)
else:
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)
t.reset()
angle_minute = minute * 6
angle_minute += second / 10.0
t.translate(WIDTH / 2, HEIGHT / 2)
t.rotate(angle_minute, (0, 0))
vector.draw(minute_hand)
t.reset()
angle_hour = (hour % 12) * 30
angle_hour += minute / 2
t.translate(WIDTH / 2, HEIGHT / 2)
t.rotate(angle_hour, (0, 0))
vector.draw(hour_hand)
display.set_pen(RED)
vector.translate(second_hand, 0, -5)
t.reset()
t.translate(WIDTH / 2, HEIGHT / 2)
t.rotate(second * 6, (0, 0))
vector.draw(second_hand)
vector.set_transform(None)
vector.draw(hub)
display.update()
mem = gc.mem_free()
gc.collect()
used = gc.mem_free() - mem
t_end = time.ticks_ms()
print(f"Took {t_end - t_start}ms")
print(f"Took {t_end - t_start}ms, mem free: {gc.mem_free()} {used}")

Wyświetl plik

@ -12,6 +12,8 @@ MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics_update_obj, ModPicoGraphics_update);
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_partial_update_obj, 5, 5, ModPicoGraphics_partial_update);
MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_backlight_obj, ModPicoGraphics_set_backlight);
MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_update_speed_obj, ModPicoGraphics_set_update_speed);
MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_blocking_obj, ModPicoGraphics_set_blocking);
MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics_is_busy_obj, ModPicoGraphics_is_busy);
// Palette management
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_update_pen_obj, 5, 5, ModPicoGraphics_update_pen);
@ -24,8 +26,12 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_create_pen_obj, 4, 4, ModPic
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_create_pen_hsv_obj, 4, 4, ModPicoGraphics_create_pen_hsv);
MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_thickness_obj, ModPicoGraphics_set_thickness);
// Layers
MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_layer_obj, ModPicoGraphics_set_layer);
// Primitives
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_set_clip_obj, 5, 5, ModPicoGraphics_set_clip);
MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics_get_clip_obj, ModPicoGraphics_get_clip);
MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics_remove_clip_obj, ModPicoGraphics_remove_clip);
MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics_clear_obj, ModPicoGraphics_clear);
MP_DEFINE_CONST_FUN_OBJ_3(ModPicoGraphics_pixel_obj, ModPicoGraphics_pixel);
@ -60,10 +66,17 @@ static const mp_rom_map_elem_t ModPicoGraphics_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_set_thickness), MP_ROM_PTR(&ModPicoGraphics_set_thickness_obj) },
{ MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&ModPicoGraphics_clear_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_layer), MP_ROM_PTR(&ModPicoGraphics_set_layer_obj) },
{ MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&ModPicoGraphics_update_obj) },
{ MP_ROM_QSTR(MP_QSTR_partial_update), MP_ROM_PTR(&ModPicoGraphics_partial_update_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_update_speed), MP_ROM_PTR(&ModPicoGraphics_set_update_speed_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_blocking), MP_ROM_PTR(&ModPicoGraphics_set_blocking_obj) },
{ MP_ROM_QSTR(MP_QSTR_is_busy), MP_ROM_PTR(&ModPicoGraphics_is_busy_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_clip), MP_ROM_PTR(&ModPicoGraphics_set_clip_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_clip), MP_ROM_PTR(&ModPicoGraphics_get_clip_obj) },
{ MP_ROM_QSTR(MP_QSTR_remove_clip), MP_ROM_PTR(&ModPicoGraphics_remove_clip_obj) },
{ MP_ROM_QSTR(MP_QSTR_pixel_span), MP_ROM_PTR(&ModPicoGraphics_pixel_span_obj) },
{ MP_ROM_QSTR(MP_QSTR_rectangle), MP_ROM_PTR(&ModPicoGraphics_rectangle_obj) },
@ -151,6 +164,7 @@ static const mp_map_elem_t picographics_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_128X64), MP_ROM_INT(DISPLAY_INTERSTATE75_128X64) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_192X64), MP_ROM_INT(DISPLAY_INTERSTATE75_192X64) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_256X64), MP_ROM_INT(DISPLAY_INTERSTATE75_256X64) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INTERSTATE75_128X128), MP_ROM_INT(DISPLAY_INTERSTATE75_128X128) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_INKY_FRAME_7), MP_ROM_INT(DISPLAY_INKY_FRAME_7) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_COSMIC_UNICORN), MP_ROM_INT(DISPLAY_COSMIC_UNICORN) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_STELLAR_UNICORN), MP_ROM_INT(DISPLAY_STELLAR_UNICORN) },
@ -158,6 +172,8 @@ static const mp_map_elem_t picographics_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_SCROLL_PACK), MP_ROM_INT(DISPLAY_SCROLL_PACK) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_PICO_W_EXPLORER), MP_ROM_INT(DISPLAY_PICO_W_EXPLORER) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_EXPLORER), MP_ROM_INT(DISPLAY_EXPLORER) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_PRESTO), MP_ROM_INT(DISPLAY_PRESTO) },
{ MP_ROM_QSTR(MP_QSTR_DISPLAY_PRESTO_FULL_RES), MP_ROM_INT(DISPLAY_PRESTO_FULL_RES) },
{ MP_ROM_QSTR(MP_QSTR_PEN_1BIT), MP_ROM_INT(PEN_1BIT) },
{ MP_ROM_QSTR(MP_QSTR_PEN_P4), MP_ROM_INT(PEN_P4) },

Wyświetl plik

@ -39,6 +39,7 @@ typedef struct _ModPicoGraphics_obj_t {
void *buffer;
void *fontdata;
_PimoroniI2C_obj_t *i2c;
bool blocking = true;
//mp_obj_t scanline_callback; // Not really feasible in MicroPython
} ModPicoGraphics_obj_t;
@ -210,6 +211,14 @@ bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height,
if(rotate == -1) rotate = (int)Rotation::ROTATE_0;
if(pen_type == -1) pen_type = PEN_RGB888;
break;
case DISPLAY_INTERSTATE75_128X128:
width = 128;
height = 128;
bus_type = BUS_PIO;
// Portrait to match labelling
if(rotate == -1) rotate = (int)Rotation::ROTATE_0;
if(pen_type == -1) pen_type = PEN_RGB888;
break;
case DISPLAY_INKY_FRAME_7:
width = 800;
height = 480;
@ -248,30 +257,44 @@ bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height,
if(rotate == -1) rotate = (int)Rotation::ROTATE_0;
if(pen_type == -1) pen_type = PEN_RGB888;
break;
case DISPLAY_PRESTO:
width = 240;
height = 240;
bus_type = BUS_PIO;
rotate = (int)Rotation::ROTATE_0;
if(pen_type == -1) pen_type = PEN_RGB565;
break;
case DISPLAY_PRESTO_FULL_RES:
width = 480;
height = 480;
bus_type = BUS_PIO;
rotate = (int)Rotation::ROTATE_0;
if(pen_type == -1) pen_type = PEN_RGB565;
break;
default:
return false;
}
return true;
}
size_t get_required_buffer_size(PicoGraphicsPenType pen_type, uint width, uint height) {
size_t get_required_buffer_size(PicoGraphicsPenType pen_type, uint width, uint height, uint layers) {
switch(pen_type) {
case PEN_1BIT:
return PicoGraphics_Pen1Bit::buffer_size(width, height);
return PicoGraphics_Pen1Bit::buffer_size(width, height) * layers;
case PEN_3BIT:
return PicoGraphics_Pen3Bit::buffer_size(width, height);
return PicoGraphics_Pen3Bit::buffer_size(width, height) * layers;
case PEN_P4:
return PicoGraphics_PenP4::buffer_size(width, height);
return PicoGraphics_PenP4::buffer_size(width, height) * layers;
case PEN_P8:
return PicoGraphics_PenP8::buffer_size(width, height);
return PicoGraphics_PenP8::buffer_size(width, height) * layers;
case PEN_RGB332:
return PicoGraphics_PenRGB332::buffer_size(width, height);
return PicoGraphics_PenRGB332::buffer_size(width, height) * layers;
case PEN_RGB565:
return PicoGraphics_PenRGB565::buffer_size(width, height);
return PicoGraphics_PenRGB565::buffer_size(width, height) * layers;
case PEN_RGB888:
return PicoGraphics_PenRGB888::buffer_size(width, height);
return PicoGraphics_PenRGB888::buffer_size(width, height) * layers;
case PEN_INKY7:
return PicoGraphics_PenInky7::buffer_size(width, height);
return PicoGraphics_PenInky7::buffer_size(width, height) * layers;
default:
return 0;
}
@ -280,7 +303,7 @@ size_t get_required_buffer_size(PicoGraphicsPenType pen_type, uint width, uint h
mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
ModPicoGraphics_obj_t *self = nullptr;
enum { ARG_display, ARG_rotate, ARG_bus, ARG_buffer, ARG_pen_type, ARG_extra_pins, ARG_i2c_address };
enum { ARG_display, ARG_rotate, ARG_bus, ARG_buffer, ARG_pen_type, ARG_extra_pins, ARG_i2c_address, ARG_layers };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_display, MP_ARG_INT | MP_ARG_REQUIRED },
{ MP_QSTR_rotate, MP_ARG_INT, { .u_int = -1 } },
@ -289,6 +312,7 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
{ MP_QSTR_pen_type, MP_ARG_INT, { .u_int = -1 } },
{ MP_QSTR_extra_pins, MP_ARG_OBJ, { .u_obj = mp_const_none } },
{ MP_QSTR_i2c_address, MP_ARG_INT, { .u_int = -1 } },
{ MP_QSTR_layers, MP_ARG_INT, { .u_int = 1 } },
};
// Parse args.
@ -304,6 +328,7 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
int height = 0;
int pen_type = args[ARG_pen_type].u_int;
int rotate = args[ARG_rotate].u_int;
int layers = args[ARG_layers].u_int;
PicoGraphicsBusType bus_type = BUS_SPI;
if(!get_display_settings(display, width, height, rotate, pen_type, bus_type)) mp_raise_ValueError("Unsupported display!");
if(rotate == -1) rotate = (int)Rotation::ROTATE_0;
@ -386,7 +411,9 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
|| display == DISPLAY_COSMIC_UNICORN
|| display == DISPLAY_STELLAR_UNICORN
|| display == DISPLAY_UNICORN_PACK
|| display == DISPLAY_SCROLL_PACK) {
|| display == DISPLAY_SCROLL_PACK
|| display == DISPLAY_PRESTO
|| display == DISPLAY_PRESTO_FULL_RES) {
// Create a dummy display driver
self->display = m_new_class(DisplayDriver, width, height, (Rotation)rotate);
@ -395,7 +422,7 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
}
// Create or fetch buffer
size_t required_size = get_required_buffer_size((PicoGraphicsPenType)pen_type, width, height);
size_t required_size = get_required_buffer_size((PicoGraphicsPenType)pen_type, width, height, layers);
if(required_size == 0) mp_raise_ValueError("Unsupported pen type!");
if(pen_type == PEN_INKY7) {
@ -418,31 +445,31 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
switch((PicoGraphicsPenType)pen_type) {
case PEN_1BIT:
if (display == DISPLAY_INKY_PACK) {
self->graphics = m_new_class(PicoGraphics_Pen1BitY, self->display->width, self->display->height, self->buffer);
self->graphics = m_new_class(PicoGraphics_Pen1BitY, self->display->width, self->display->height, self->buffer, layers);
} else {
self->graphics = m_new_class(PicoGraphics_Pen1Bit, self->display->width, self->display->height, self->buffer);
self->graphics = m_new_class(PicoGraphics_Pen1Bit, self->display->width, self->display->height, self->buffer, layers);
}
break;
case PEN_3BIT:
self->graphics = m_new_class(PicoGraphics_Pen3Bit, self->display->width, self->display->height, self->buffer);
self->graphics = m_new_class(PicoGraphics_Pen3Bit, self->display->width, self->display->height, self->buffer, layers);
break;
case PEN_P4:
self->graphics = m_new_class(PicoGraphics_PenP4, self->display->width, self->display->height, self->buffer);
self->graphics = m_new_class(PicoGraphics_PenP4, self->display->width, self->display->height, self->buffer, layers);
break;
case PEN_P8:
self->graphics = m_new_class(PicoGraphics_PenP8, self->display->width, self->display->height, self->buffer);
self->graphics = m_new_class(PicoGraphics_PenP8, self->display->width, self->display->height, self->buffer, layers);
break;
case PEN_RGB332:
self->graphics = m_new_class(PicoGraphics_PenRGB332, self->display->width, self->display->height, self->buffer);
self->graphics = m_new_class(PicoGraphics_PenRGB332, self->display->width, self->display->height, self->buffer, layers);
break;
case PEN_RGB565:
self->graphics = m_new_class(PicoGraphics_PenRGB565, self->display->width, self->display->height, self->buffer);
self->graphics = m_new_class(PicoGraphics_PenRGB565, self->display->width, self->display->height, self->buffer, layers);
break;
case PEN_RGB888:
self->graphics = m_new_class(PicoGraphics_PenRGB888, self->display->width, self->display->height, self->buffer);
self->graphics = m_new_class(PicoGraphics_PenRGB888, self->display->width, self->display->height, self->buffer, layers);
break;
case PEN_INKY7:
self->graphics = m_new_class(PicoGraphics_PenInky7, self->display->width, self->display->height, *(IDirectDisplayDriver<uint8_t> *)self->buffer);
self->graphics = m_new_class(PicoGraphics_PenInky7, self->display->width, self->display->height, *(IDirectDisplayDriver<uint8_t> *)self->buffer, layers);
break;
default:
break;
@ -453,8 +480,15 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size
self->spritedata = nullptr;
// Clear the buffer
self->graphics->set_layer(0);
self->graphics->set_pen(0);
self->graphics->clear();
if(layers > 1) {
self->graphics->set_layer(1);
self->graphics->set_pen(0);
self->graphics->clear();
self->graphics->set_layer(0);
}
// Update the LCD from the graphics library
if (display != DISPLAY_INKY_FRAME && display != DISPLAY_INKY_FRAME_4 && display != DISPLAY_INKY_PACK && display != DISPLAY_INKY_FRAME_7) {
@ -477,7 +511,7 @@ mp_obj_t ModPicoGraphics_set_spritesheet(mp_obj_t self_in, mp_obj_t spritedata)
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(spritedata, &bufinfo, MP_BUFFER_RW);
int required_size = get_required_buffer_size((PicoGraphicsPenType)self->graphics->pen_type, 128, 128);
int required_size = get_required_buffer_size((PicoGraphicsPenType)self->graphics->pen_type, 128, 128, 1);
if(bufinfo.len != (size_t)(required_size)) {
mp_raise_ValueError("Spritesheet the wrong size!");
@ -562,7 +596,7 @@ mp_int_t ModPicoGraphics_get_framebuffer(mp_obj_t self_in, mp_buffer_info_t *buf
mp_raise_ValueError("No local framebuffer.");
}
bufinfo->buf = self->graphics->frame_buffer;
bufinfo->len = get_required_buffer_size((PicoGraphicsPenType)self->graphics->pen_type, self->graphics->bounds.w, self->graphics->bounds.h);
bufinfo->len = get_required_buffer_size((PicoGraphicsPenType)self->graphics->pen_type, self->graphics->bounds.w, self->graphics->bounds.h, 1);
bufinfo->typecode = 'B';
return 0;
}
@ -603,7 +637,7 @@ mp_obj_t ModPicoGraphics_get_required_buffer_size(mp_obj_t display_in, mp_obj_t
int pen_type = mp_obj_get_int(pen_type_in);
PicoGraphicsBusType bus_type = BUS_SPI;
if(!get_display_settings(display, width, height, rotation, pen_type, bus_type)) mp_raise_ValueError("Unsupported display!");
size_t required_size = get_required_buffer_size((PicoGraphicsPenType)pen_type, width, height);
size_t required_size = get_required_buffer_size((PicoGraphicsPenType)pen_type, width, height, 1);
if(required_size == 0) mp_raise_ValueError("Unsupported pen type!");
return mp_obj_new_int(required_size);
@ -626,6 +660,17 @@ mp_obj_t ModPicoGraphics_set_scanline_callback(mp_obj_t self_in, mp_obj_t cb_in)
}
*/
mp_obj_t ModPicoGraphics_set_blocking(mp_obj_t self_in, mp_obj_t blocking_in) {
ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t);
self->blocking = blocking_in == mp_const_true;
return mp_const_none;
}
mp_obj_t ModPicoGraphics_is_busy(mp_obj_t self_in) {
ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t);
return self->display->is_busy() ? mp_const_true : mp_const_false;
}
mp_obj_t ModPicoGraphics_update(mp_obj_t self_in) {
ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t);
/*
@ -649,13 +694,15 @@ mp_obj_t ModPicoGraphics_update(mp_obj_t self_in) {
self->display->update(self->graphics);
while(self->display->is_busy()) {
#ifdef mp_event_handle_nowait
mp_event_handle_nowait();
#endif
}
if(self->blocking) {
while(self->display->is_busy()) {
#ifdef mp_event_handle_nowait
mp_event_handle_nowait();
#endif
}
self->display->power_off();
self->display->power_off();
}
return mp_const_none;
}
@ -678,10 +725,12 @@ mp_obj_t ModPicoGraphics_partial_update(size_t n_args, const mp_obj_t *args) {
mp_obj_get_int(args[ARG_h])
});
while(self->display->is_busy()) {
#ifdef mp_event_handle_nowait
mp_event_handle_nowait();
#endif
if(self->blocking) {
while(self->display->is_busy()) {
#ifdef mp_event_handle_nowait
mp_event_handle_nowait();
#endif
}
}
return mp_const_none;
@ -755,6 +804,14 @@ mp_obj_t ModPicoGraphics_set_pen(mp_obj_t self_in, mp_obj_t pen) {
return mp_const_none;
}
mp_obj_t ModPicoGraphics_set_layer(mp_obj_t self_in, mp_obj_t layer) {
ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t);
self->graphics->set_layer(mp_obj_get_int(layer));
return mp_const_none;
}
mp_obj_t ModPicoGraphics_reset_pen(mp_obj_t self_in, mp_obj_t pen) {
ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t);
@ -872,6 +929,18 @@ mp_obj_t ModPicoGraphics_set_clip(size_t n_args, const mp_obj_t *args) {
return mp_const_none;
}
mp_obj_t ModPicoGraphics_get_clip(mp_obj_t self_in) {
ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t);
mp_obj_t tuple[4] = {
mp_obj_new_int(self->graphics->clip.x),
mp_obj_new_int(self->graphics->clip.y),
mp_obj_new_int(self->graphics->clip.w),
mp_obj_new_int(self->graphics->clip.h)
};
return mp_obj_new_tuple(4, tuple);
}
mp_obj_t ModPicoGraphics_remove_clip(mp_obj_t self_in) {
ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t);

Wyświetl plik

@ -25,13 +25,16 @@ enum PicoGraphicsDisplay {
DISPLAY_INTERSTATE75_128X64,
DISPLAY_INTERSTATE75_192X64,
DISPLAY_INTERSTATE75_256X64,
DISPLAY_INTERSTATE75_128X128,
DISPLAY_INKY_FRAME_7,
DISPLAY_COSMIC_UNICORN,
DISPLAY_STELLAR_UNICORN,
DISPLAY_UNICORN_PACK,
DISPLAY_SCROLL_PACK,
DISPLAY_PICO_W_EXPLORER,
DISPLAY_EXPLORER
DISPLAY_EXPLORER,
DISPLAY_PRESTO,
DISPLAY_PRESTO_FULL_RES
};
enum PicoGraphicsPenType {
@ -71,12 +74,18 @@ extern mp_obj_t ModPicoGraphics_partial_update(size_t n_args, const mp_obj_t *ar
extern mp_obj_t ModPicoGraphics_set_backlight(mp_obj_t self_in, mp_obj_t brightness);
extern mp_obj_t ModPicoGraphics_set_update_speed(mp_obj_t self_in, mp_obj_t update_speed);
extern mp_obj_t ModPicoGraphics_set_blocking(mp_obj_t self_in, mp_obj_t blocking_in);
extern mp_obj_t ModPicoGraphics_is_busy(mp_obj_t self_in);
// Palette management
extern mp_obj_t ModPicoGraphics_update_pen(size_t n_args, const mp_obj_t *args);
extern mp_obj_t ModPicoGraphics_reset_pen(mp_obj_t self_in, mp_obj_t pen);
extern mp_obj_t ModPicoGraphics_set_palette(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t ModPicoGraphics_hsv_to_rgb(size_t n_args, const mp_obj_t *args);
// Layers
extern mp_obj_t ModPicoGraphics_set_layer(mp_obj_t self_in, mp_obj_t layer);
// Pen
extern mp_obj_t ModPicoGraphics_set_pen(mp_obj_t self_in, mp_obj_t pen);
extern mp_obj_t ModPicoGraphics_create_pen(size_t n_args, const mp_obj_t *args);
@ -85,6 +94,7 @@ extern mp_obj_t ModPicoGraphics_set_thickness(mp_obj_t self_in, mp_obj_t thickne
// Primitives
extern mp_obj_t ModPicoGraphics_set_clip(size_t n_args, const mp_obj_t *args);
extern mp_obj_t ModPicoGraphics_get_clip(mp_obj_t self_in);
extern mp_obj_t ModPicoGraphics_remove_clip(mp_obj_t self_in);
extern mp_obj_t ModPicoGraphics_clear(mp_obj_t self_in);
extern mp_obj_t ModPicoGraphics_pixel(mp_obj_t self_in, mp_obj_t x, mp_obj_t y);

Wyświetl plik

@ -2,8 +2,6 @@ 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
)
@ -21,3 +19,9 @@ target_compile_definitions(usermod_picovector INTERFACE
target_link_libraries(usermod_picovector INTERFACE hardware_interp)
target_link_libraries(usermod INTERFACE usermod_picovector)
set_source_files_properties(
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/pico_vector/pico_vector.cpp
PROPERTIES COMPILE_FLAGS
"-Wno-narrowing"
)

Wyświetl plik

@ -3,19 +3,50 @@
/* Polygon */
static MP_DEFINE_CONST_FUN_OBJ_1(POLYGON__del__obj, POLYGON__del__);
// Transformations
//static MP_DEFINE_CONST_FUN_OBJ_KW(POLYGON_rotate_obj, 3, POLYGON_rotate);
//static MP_DEFINE_CONST_FUN_OBJ_KW(POLYGON_translate_obj, 4, POLYGON_translate);
// Utility functions
static MP_DEFINE_CONST_FUN_OBJ_1(POLYGON_centroid_obj, POLYGON_centroid);
static MP_DEFINE_CONST_FUN_OBJ_1(POLYGON_bounds_obj, POLYGON_bounds);
static MP_DEFINE_CONST_FUN_OBJ_2(POLYGON_transform_obj, POLYGON_transform);
// Primitives
static MP_DEFINE_CONST_FUN_OBJ_KW(POLYGON_rectangle_obj, 5, POLYGON_rectangle);
static MP_DEFINE_CONST_FUN_OBJ_VAR(POLYGON_path_obj, 4, POLYGON_path);
static MP_DEFINE_CONST_FUN_OBJ_KW(POLYGON_regular_obj, 5, POLYGON_regular);
static MP_DEFINE_CONST_FUN_OBJ_KW(POLYGON_circle_obj, 4, POLYGON_circle);
static MP_DEFINE_CONST_FUN_OBJ_KW(POLYGON_arc_obj, 6, POLYGON_arc);
static MP_DEFINE_CONST_FUN_OBJ_KW(POLYGON_star_obj, 6, POLYGON_star);
static MP_DEFINE_CONST_FUN_OBJ_KW(POLYGON_line_obj, 5, POLYGON_line);
static const mp_rom_map_elem_t POLYGON_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&POLYGON__del__obj) },
// Transformations
//{ MP_ROM_QSTR(MP_QSTR_rotate), MP_ROM_PTR(&POLYGON_rotate_obj) },
//{ MP_ROM_QSTR(MP_QSTR_translate), MP_ROM_PTR(&POLYGON_translate_obj) },
// Utility functions
{ MP_ROM_QSTR(MP_QSTR_centroid), MP_ROM_PTR(&POLYGON_centroid_obj) },
{ MP_ROM_QSTR(MP_QSTR_bounds), MP_ROM_PTR(&POLYGON_bounds_obj) },
{ MP_ROM_QSTR(MP_QSTR_transform), MP_ROM_PTR(&POLYGON_transform_obj) },
// Primitives
{ MP_ROM_QSTR(MP_QSTR_rectangle), MP_ROM_PTR(&POLYGON_rectangle_obj) },
{ MP_ROM_QSTR(MP_QSTR_regular), MP_ROM_PTR(&POLYGON_regular_obj) },
{ MP_ROM_QSTR(MP_QSTR_path), MP_ROM_PTR(&POLYGON_path_obj) },
{ MP_ROM_QSTR(MP_QSTR_circle), MP_ROM_PTR(&POLYGON_circle_obj) },
{ MP_ROM_QSTR(MP_QSTR_arc), MP_ROM_PTR(&POLYGON_arc_obj) },
{ MP_ROM_QSTR(MP_QSTR_star), MP_ROM_PTR(&POLYGON_star_obj) },
{ MP_ROM_QSTR(MP_QSTR_line), MP_ROM_PTR(&POLYGON_line_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,
@ -25,68 +56,66 @@ MP_DEFINE_CONST_OBJ_TYPE(
iter, POLYGON_getiter,
locals_dict, (mp_obj_dict_t*)&POLYGON_locals_dict
);
/* Transform */
static MP_DEFINE_CONST_FUN_OBJ_3(TRANSFORM_rotate_obj, TRANSFORM_rotate);
static MP_DEFINE_CONST_FUN_OBJ_3(TRANSFORM_translate_obj, TRANSFORM_translate);
static MP_DEFINE_CONST_FUN_OBJ_3(TRANSFORM_scale_obj, TRANSFORM_scale);
static MP_DEFINE_CONST_FUN_OBJ_1(TRANSFORM_reset_obj, TRANSFORM_reset);
static const mp_rom_map_elem_t TRANSFORM_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_rotate), MP_ROM_PTR(&TRANSFORM_rotate_obj) },
{ MP_ROM_QSTR(MP_QSTR_translate), MP_ROM_PTR(&TRANSFORM_translate_obj) },
{ MP_ROM_QSTR(MP_QSTR_scale), MP_ROM_PTR(&TRANSFORM_scale_obj) },
{ MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&TRANSFORM_reset_obj) },
};
static MP_DEFINE_CONST_DICT(TRANSFORM_locals_dict, TRANSFORM_locals_dict_table);
MP_DEFINE_CONST_OBJ_TYPE(
REGULAR_POLYGON_type,
MP_QSTR_regular_polygon,
TRANSFORM_type,
MP_QSTR_Transform,
MP_TYPE_FLAG_NONE,
make_new, REGULAR_POLYGON_make_new,
locals_dict, (mp_obj_dict_t*)&POLYGON_locals_dict
make_new, TRANSFORM_make_new,
locals_dict, (mp_obj_dict_t*)&TRANSFORM_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_KW(VECTOR_measure_text_obj, 2, VECTOR_measure_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_font_word_spacing_obj, VECTOR_set_font_word_spacing);
static MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_set_font_letter_spacing_obj, VECTOR_set_font_letter_spacing);
static MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_set_font_line_height_obj, VECTOR_set_font_line_height);
static MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_set_font_align_obj, VECTOR_set_font_align);
static MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_set_antialiasing_obj, VECTOR_set_antialiasing);
static MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_set_transform_obj, VECTOR_set_transform);
static MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_set_clip_obj, VECTOR_set_clip);
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 MP_DEFINE_CONST_FUN_OBJ_2(VECTOR_draw_obj, VECTOR_draw);
static const mp_rom_map_elem_t VECTOR_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_text), MP_ROM_PTR(&VECTOR_text_obj) },
{ MP_ROM_QSTR(MP_QSTR_measure_text), MP_ROM_PTR(&VECTOR_measure_text_obj) },
{ 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_font_word_spacing), MP_ROM_PTR(&VECTOR_set_font_word_spacing_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_font_letter_spacing), MP_ROM_PTR(&VECTOR_set_font_letter_spacing_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_font_line_height), MP_ROM_PTR(&VECTOR_set_font_line_height_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_font_align), MP_ROM_PTR(&VECTOR_set_font_align_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_set_transform), MP_ROM_PTR(&VECTOR_set_transform_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_clip), MP_ROM_PTR(&VECTOR_set_clip_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,
@ -94,14 +123,6 @@ MP_DEFINE_CONST_OBJ_TYPE(
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 */
@ -109,11 +130,19 @@ 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)&REGULAR_POLYGON_type },
{ MP_OBJ_NEW_QSTR(MP_QSTR_Rectangle), (mp_obj_t)&RECTANGLE_type },
{ MP_OBJ_NEW_QSTR(MP_QSTR_Transform), (mp_obj_t)&TRANSFORM_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) },
{ MP_ROM_QSTR(MP_QSTR_ANTIALIAS_FAST), MP_ROM_INT(1) },
{ MP_ROM_QSTR(MP_QSTR_ANTIALIAS_BEST), MP_ROM_INT(2) },
{ MP_ROM_QSTR(MP_QSTR_HALIGN_LEFT), MP_ROM_INT(0) },
{ MP_ROM_QSTR(MP_QSTR_HALIGN_CENTER), MP_ROM_INT(1) },
{ MP_ROM_QSTR(MP_QSTR_HALIGN_RIGHT), MP_ROM_INT(2) },
{ MP_ROM_QSTR(MP_QSTR_VALIGN_TOP), MP_ROM_INT(8) },
{ MP_ROM_QSTR(MP_QSTR_VALIGN_MIDDLE), MP_ROM_INT(16) },
{ MP_ROM_QSTR(MP_QSTR_VALIGN_BOTTOM), MP_ROM_INT(32) },
};
static MP_DEFINE_CONST_DICT(mp_module_VECTOR_globals, VECTOR_globals_table);
@ -123,8 +152,4 @@ const mp_obj_module_t VECTOR_user_cmodule = {
.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
MP_REGISTER_MODULE(MP_QSTR_picovector, VECTOR_user_cmodule);

Wyświetl plik

@ -10,6 +10,7 @@ extern "C" {
#include "py/stream.h"
#include "py/reader.h"
#include "extmod/vfs.h"
#include <stdarg.h>
typedef struct _ModPicoGraphics_obj_t {
mp_obj_base_t base;
@ -20,19 +21,69 @@ typedef struct _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 {
typedef struct _TRANSFORM_obj_t {
mp_obj_base_t base;
pretty_poly::contour_t<picovector_point_type> contour;
} _POLYGON_obj_t;
pp_mat3_t transform;
} _TRANSFORM_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());
typedef struct _POLY_obj_t {
mp_obj_base_t base;
pp_poly_t *poly;
} _POLY_obj_t;
//mp_printf(&mp_plat_print, "Opening file %s\n", filename.data());
#if DEBUG
void __printf_debug_flush() {
for(auto i = 0u; i < 10; i++) {
sleep_ms(1);
mp_event_handle_nowait();
}
}
int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args);
void af_debug(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
int ret = mp_vprintf(&mp_plat_print, fmt, ap);
va_end(ap);
__printf_debug_flush();
(void)ret;
}
#endif
#define mp_picovector_get_point_type mp_obj_get_float
#define mp_picovector_set_point_type mp_obj_new_float
void *af_malloc(size_t size) {
//mp_printf(&mp_plat_print, "af_malloc %lu\n", size);
//__printf_debug_flush();
//void *addr = m_tracked_calloc(sizeof(uint8_t), size);
void *addr = m_malloc(size);
//mp_printf(&mp_plat_print, "addr %lu\n", addr);
//__printf_debug_flush();
return addr;
}
void *af_realloc(void *p, size_t size) {
return m_realloc(p, size);
}
void af_free(void *p) {
//mp_printf(&mp_plat_print, "af_free\n");
//__printf_debug_flush();
//m_tracked_free(p);
m_free(p);
}
void* fileio_open(const char *filename) {
mp_obj_t fn = mp_obj_new_str(filename, (mp_uint_t)strlen(filename));
//mp_printf(&mp_plat_print, "Opening file %s\n", filename);
//__printf_debug_flush();
mp_obj_t args[2] = {
fn,
@ -41,36 +92,44 @@ pretty_poly::file_io::file_io(std::string_view filename) {
// 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 stat = mp_vfs_stat(fn);
//mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR2(stat, mp_obj_tuple_t);
//int filesize = mp_obj_get_int(tuple->items[6]);
//mp_printf(&mp_plat_print, "Size %lu\n", filesize);
mp_obj_t fhandle = mp_vfs_open(MP_ARRAY_SIZE(args), &args[0], (mp_map_t *)&mp_const_empty_map);
this->state = (void *)fhandle;
return (void*)fhandle;
}
pretty_poly::file_io::~file_io() {
mp_stream_close((mp_obj_t)this->state);
void fileio_close(void* fhandle) {
mp_stream_close((mp_obj_t)fhandle);
}
size_t pretty_poly::file_io::read(void *buf, size_t len) {
size_t fileio_read(void* fhandle, void *buf, size_t len) {
//mp_printf(&mp_plat_print, "Reading %lu bytes\n", len);
mp_obj_t fhandle = this->state;
//__printf_debug_flush();
int error;
return mp_stream_read_exactly(fhandle, buf, len, &error);
return mp_stream_read_exactly((mp_obj_t)fhandle, buf, len, &error);
}
size_t pretty_poly::file_io::tell() {
mp_obj_t fhandle = this->state;
int fileio_getc(void* fhandle) {
unsigned char buf;
//mp_printf(&mp_plat_print, "Reading char\n");
//__printf_debug_flush();
fileio_read(fhandle, (void *)&buf, 1);
return (int)buf;
}
size_t fileio_tell(void* fhandle) {
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);
const mp_stream_p_t *stream_p = mp_get_stream((mp_obj_t)fhandle);
int error;
mp_uint_t res = stream_p->ioctl(fhandle, MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, &error);
mp_uint_t res = stream_p->ioctl((mp_obj_t)fhandle, MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, &error);
if (res == MP_STREAM_ERROR) {
mp_raise_OSError(error);
}
@ -78,21 +137,16 @@ size_t pretty_poly::file_io::tell() {
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;
// Re-implementation of stream.c/STATIC mp_obj_t stream_seek(size_t n_args, const mp_obj_t *args)
size_t fileio_seek(void* fhandle, size_t pos) {
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);
const mp_stream_p_t *stream_p = mp_get_stream((mp_obj_t)fhandle);
int error;
mp_uint_t res = stream_p->ioctl(fhandle, MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, &error);
mp_uint_t res = stream_p->ioctl((mp_obj_t)fhandle, MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, &error);
if (res == MP_STREAM_ERROR) {
mp_raise_OSError(error);
}
@ -100,6 +154,7 @@ size_t pretty_poly::file_io::seek(size_t pos) {
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);
@ -107,92 +162,32 @@ static const std::string_view mp_obj_to_string_r(const mp_obj_t &obj) {
}
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 = mp_obj_malloc_with_finaliser(_POLYGON_obj_t, &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 = mp_obj_malloc_with_finaliser(_POLYGON_obj_t, &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 = mp_obj_malloc_with_finaliser(_POLYGON_obj_t, &POLYGON_type);
_POLY_obj_t *self = mp_obj_malloc_with_finaliser(_POLY_obj_t, &POLYGON_type);
self->poly = pp_poly_new();
return self;
}
size_t num_points = n_args;
const mp_obj_t *points = all_args;
mp_obj_t POLYGON__del__(mp_obj_t self_in) {
_POLY_obj_t *self = MP_OBJ_TO_PTR2(self_in, _POLY_obj_t);
pp_poly_free(self->poly);
return mp_const_none;
}
mp_obj_t POLYGON_path(size_t n_args, const mp_obj_t *all_args) {
_POLY_obj_t *self = MP_OBJ_TO_PTR2(all_args[0], _POLY_obj_t);
size_t num_points = n_args - 1;
const mp_obj_t *points = all_args + 1;
pp_path_t *path = pp_poly_add_path(self->poly);
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];
@ -202,101 +197,401 @@ mp_obj_t POLYGON_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
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]),
};
pp_path_add_point(path, {
(picovector_point_type)mp_picovector_get_point_type(t_point->items[0]),
(picovector_point_type)mp_picovector_get_point_type(t_point->items[1]),
});
}
return mp_const_none;
}
mp_obj_t POLYGON_rectangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_x, ARG_y, ARG_w, ARG_h, ARG_corners, ARG_stroke };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_w, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_h, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_corners, MP_ARG_OBJ, { .u_obj = mp_const_none }},
{ MP_QSTR_stroke, 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);
_POLY_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _POLY_obj_t);
picovector_point_type x = mp_picovector_get_point_type(args[ARG_x].u_obj);
picovector_point_type y = mp_picovector_get_point_type(args[ARG_y].u_obj);
picovector_point_type w = mp_picovector_get_point_type(args[ARG_w].u_obj);
picovector_point_type h = mp_picovector_get_point_type(args[ARG_h].u_obj);
picovector_point_type s = args[ARG_stroke].u_obj == mp_const_none ? 0 : mp_picovector_get_point_type(args[ARG_stroke].u_obj);
picovector_point_type r1 = 0;
picovector_point_type r2 = 0;
picovector_point_type r3 = 0;
picovector_point_type r4 = 0;
if(mp_obj_is_exact_type(args[ARG_corners].u_obj, &mp_type_tuple)){
mp_obj_tuple_t *t_corners = MP_OBJ_TO_PTR2(args[ARG_corners].u_obj, mp_obj_tuple_t);
if(t_corners->len != 4) mp_raise_ValueError("Corners must have r1, r2, r3, r4");
r1 = mp_picovector_get_point_type(t_corners->items[0]);
r2 = mp_picovector_get_point_type(t_corners->items[1]);
r3 = mp_picovector_get_point_type(t_corners->items[2]);
r4 = mp_picovector_get_point_type(t_corners->items[3]);
}
pp_poly_merge(self->poly, ppp_rect({
x, y, w, h,
s,
r1, r2, r3, r4
}));
return self;
}
mp_obj_t POLYGON_regular(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_x, ARG_y, ARG_sides, ARG_radius, ARG_stroke };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_radius, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_sides, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_stroke, 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);
_POLY_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _POLY_obj_t);
picovector_point_type x = mp_picovector_get_point_type(args[ARG_x].u_obj);
picovector_point_type y = mp_picovector_get_point_type(args[ARG_y].u_obj);
picovector_point_type r = mp_picovector_get_point_type(args[ARG_radius].u_obj);
int e = args[ARG_sides].u_int;
picovector_point_type s = args[ARG_stroke].u_obj == mp_const_none ? 0 : mp_picovector_get_point_type(args[ARG_stroke].u_obj);
pp_poly_merge(self->poly, ppp_regular({
x, y,
r,
e,
s
}));
return self;
}
mp_obj_t POLYGON_circle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_x, ARG_y, ARG_radius, ARG_stroke };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_radius, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_stroke, 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);
_POLY_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _POLY_obj_t);
picovector_point_type x = mp_picovector_get_point_type(args[ARG_x].u_obj);
picovector_point_type y = mp_picovector_get_point_type(args[ARG_y].u_obj);
picovector_point_type r = mp_picovector_get_point_type(args[ARG_radius].u_obj);
picovector_point_type s = args[ARG_stroke].u_obj == mp_const_none ? 0 : mp_picovector_get_point_type(args[ARG_stroke].u_obj);
pp_poly_merge(self->poly, ppp_circle({
x, y,
r,
s
}));
return self;
}
mp_obj_t POLYGON_arc(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_x, ARG_y, ARG_radius, ARG_from, ARG_to, ARG_stroke };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_radius, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_from, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_to, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_stroke, 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);
_POLY_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _POLY_obj_t);
picovector_point_type x = mp_picovector_get_point_type(args[ARG_x].u_obj);
picovector_point_type y = mp_picovector_get_point_type(args[ARG_y].u_obj);
picovector_point_type r = mp_picovector_get_point_type(args[ARG_radius].u_obj);
picovector_point_type f = mp_picovector_get_point_type(args[ARG_from].u_obj);
picovector_point_type t = mp_picovector_get_point_type(args[ARG_to].u_obj);
picovector_point_type s = args[ARG_stroke].u_obj == mp_const_none ? 0 : mp_picovector_get_point_type(args[ARG_stroke].u_obj);
pp_poly_merge(self->poly, ppp_arc({
x, y,
r,
s,
f,
t
}));
return self;
}
mp_obj_t POLYGON_star(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_x, ARG_y, ARG_points, ARG_inner_radius, ARG_outer_radius, ARG_stroke };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_points, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_inner_radius, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_outer_radius, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_stroke, 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);
_POLY_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _POLY_obj_t);
picovector_point_type x = mp_picovector_get_point_type(args[ARG_x].u_obj);
picovector_point_type y = mp_picovector_get_point_type(args[ARG_y].u_obj);
int p = args[ARG_points].u_int;
picovector_point_type r1 = mp_picovector_get_point_type(args[ARG_inner_radius].u_obj);
picovector_point_type r2 = mp_picovector_get_point_type(args[ARG_outer_radius].u_obj);
picovector_point_type s = args[ARG_stroke].u_obj == mp_const_none ? 0 : mp_picovector_get_point_type(args[ARG_stroke].u_obj);
pp_poly_merge(self->poly, ppp_star({
x, y,
p,
r1,
r2,
s
}));
return self;
}
mp_obj_t POLYGON_line(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_x, ARG_y, ARG_x2, ARG_y2, ARG_thickness };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_x2, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_y2, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_thickness, 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);
_POLY_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _POLY_obj_t);
picovector_point_type x = mp_picovector_get_point_type(args[ARG_x].u_obj);
picovector_point_type y = mp_picovector_get_point_type(args[ARG_y].u_obj);
picovector_point_type x2 = mp_picovector_get_point_type(args[ARG_x2].u_obj);
picovector_point_type y2 = mp_picovector_get_point_type(args[ARG_y2].u_obj);
picovector_point_type t = args[ARG_thickness].u_obj == mp_const_none ? 0 : mp_picovector_get_point_type(args[ARG_thickness].u_obj);
pp_poly_merge(self->poly, ppp_line({
x, y,
x2, y2,
t
}));
return self;
}
// Utility functions
mp_obj_t POLYGON_centroid(mp_obj_t self_in) {
_POLYGON_obj_t *self = MP_OBJ_TO_PTR2(self_in, _POLYGON_obj_t);
_POLY_obj_t *self = MP_OBJ_TO_PTR2(self_in, _POLY_obj_t);
pretty_poly::point_t<picovector_point_type> sum(0, 0);
PP_COORD_TYPE sum_x = (PP_COORD_TYPE)0;
PP_COORD_TYPE sum_y = (PP_COORD_TYPE)0;
for(auto i = 0u; i < self->contour.count; i++) {
sum += self->contour.points[i];
// TODO: Maybe include in pretty-poly?
// Might need to handle multiple paths
pp_path_t *path = self->poly->paths;
for(auto i = 0; i < path->count; i++) {
sum_x += path->points[i].x;
sum_y += path->points[i].y;
}
sum /= (float)self->contour.count;
sum_x /= (float)path->count;
sum_y /= (float)path->count;
mp_obj_t tuple[2];
tuple[0] = mp_obj_new_int((int)(sum.x));
tuple[1] = mp_obj_new_int((int)(sum.y));
tuple[0] = mp_picovector_set_point_type((int)(sum_x));
tuple[1] = mp_picovector_set_point_type((int)(sum_y));
return mp_obj_new_tuple(2, tuple);
}
mp_obj_t POLYGON_bounds(mp_obj_t self_in) {
_POLYGON_obj_t *self = MP_OBJ_TO_PTR2(self_in, _POLYGON_obj_t);
_POLY_obj_t *self = MP_OBJ_TO_PTR2(self_in, _POLY_obj_t);
pp_rect_t bounds = pp_poly_bounds(self->poly);
mp_obj_t tuple[4];
tuple[0] = mp_obj_new_int((int)(self->contour.bounds().x));
tuple[1] = mp_obj_new_int((int)(self->contour.bounds().y));
tuple[2] = mp_obj_new_int((int)(self->contour.bounds().w));
tuple[3] = mp_obj_new_int((int)(self->contour.bounds().h));
tuple[0] = mp_picovector_set_point_type((int)(bounds.x));
tuple[1] = mp_picovector_set_point_type((int)(bounds.y));
tuple[2] = mp_picovector_set_point_type((int)(bounds.w));
tuple[3] = mp_picovector_set_point_type((int)(bounds.h));
return mp_obj_new_tuple(4, 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, ")");
void _pp_path_transform(pp_path_t *path, pp_mat3_t *transform) {
for (int i = 0; i < path->count; i++) {
path->points[i] = pp_point_transform(&path->points[i], transform);
}
}
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
void _pp_poly_transform(pp_poly_t *poly, pp_mat3_t *transform) {
pp_path_t *path = poly->paths;
while(path) {
_pp_path_transform(path, transform);
path = path->next;
}
}
mp_obj_t POLYGON_transform(mp_obj_t self_in, mp_obj_t transform_in) {
_POLY_obj_t *self = MP_OBJ_TO_PTR2(self_in, _POLY_obj_t);
if (!MP_OBJ_IS_TYPE(transform_in, &TRANSFORM_type)) mp_raise_ValueError("Transform required");
_TRANSFORM_obj_t *transform = (_TRANSFORM_obj_t *)MP_OBJ_TO_PTR(transform_in);
_pp_poly_transform(self->poly, &transform->transform);
return mp_const_none;
}
void POLYGON_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
_POLY_obj_t *self = MP_OBJ_TO_PTR2(self_in, _POLY_obj_t);
(void)self;
// TODO: Make print better
mp_print_str(print, "Polygon();");
}
typedef struct _mp_obj_polygon_it_t {
mp_obj_base_t base;
mp_fun_1_t iternext;
mp_obj_t polygon;
size_t cur;
pp_path_t *cur;
} mp_obj_polygon_it_t;
static mp_obj_t py_image_it_iternext(mp_obj_t self_in) {
static mp_obj_t POLYGON_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);
//_POLY_obj_t *poly = MP_OBJ_TO_PTR2(self->polygon, _POLY_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;
if(!self->cur) 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));
mp_obj_t tuple[self->cur->count];
for (auto i = 0; i < self->cur->count; i++) {
mp_obj_t t_point[2] = {
mp_picovector_set_point_type((int)(self->cur->points[i].x)),
mp_picovector_set_point_type((int)(self->cur->points[i].y))
};
tuple[i] = mp_obj_new_tuple(2, t_point);
}
self->cur++;
return mp_obj_new_tuple(2, tuple);
self->cur = self->cur->next;
return mp_obj_new_tuple(self->cur->count, 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->iternext = POLYGON_it_iternext;
o->polygon = o_in;
o->cur = 0;
o->cur = MP_OBJ_TO_PTR2(o_in, _POLY_obj_t)->poly->paths;
return MP_OBJ_FROM_PTR(o);
}
/* TRANSFORM */
mp_obj_t TRANSFORM_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
_TRANSFORM_obj_t *self = m_new_obj(_TRANSFORM_obj_t);
self->base.type = &TRANSFORM_type;
self->transform = pp_mat3_identity();
return self;
}
mp_obj_t TRANSFORM_rotate(mp_obj_t self_in, mp_obj_t angle_in, mp_obj_t origin_in) {
_TRANSFORM_obj_t *transform = MP_OBJ_TO_PTR2(self_in, _TRANSFORM_obj_t);
float angle = mp_obj_get_float(angle_in);
if(mp_obj_is_exact_type(origin_in, &mp_type_tuple)) {
mp_obj_tuple_t *t_origin = MP_OBJ_TO_PTR2(origin_in, mp_obj_tuple_t);
if(t_origin->len != 2) mp_raise_ValueError("Origin Tuple must have X, Y");
picovector_point_type x = mp_picovector_get_point_type(t_origin->items[0]);
picovector_point_type y = mp_picovector_get_point_type(t_origin->items[1]);
pp_mat3_translate(&transform->transform, x, y);
pp_mat3_rotate(&transform->transform, angle);
pp_mat3_translate(&transform->transform, -x, -y);
} else {
pp_mat3_rotate(&transform->transform, angle);
}
return mp_const_none;
}
mp_obj_t TRANSFORM_translate(mp_obj_t self_in, mp_obj_t x_in, mp_obj_t y_in) {
_TRANSFORM_obj_t *transform = MP_OBJ_TO_PTR2(self_in, _TRANSFORM_obj_t);
picovector_point_type o_x = mp_picovector_get_point_type(x_in);
picovector_point_type o_y = mp_picovector_get_point_type(y_in);
pp_mat3_translate(&transform->transform, o_x, o_y);
return mp_const_none;
}
mp_obj_t TRANSFORM_scale(mp_obj_t self_in, mp_obj_t x_in, mp_obj_t y_in) {
_TRANSFORM_obj_t *transform = MP_OBJ_TO_PTR2(self_in, _TRANSFORM_obj_t);
picovector_point_type o_x = mp_picovector_get_point_type(x_in);
picovector_point_type o_y = mp_picovector_get_point_type(y_in);
pp_mat3_scale(&transform->transform, o_x, o_y);
return mp_const_none;
}
mp_obj_t TRANSFORM_reset(mp_obj_t self_in) {
_TRANSFORM_obj_t *transform = MP_OBJ_TO_PTR2(self_in, _TRANSFORM_obj_t);
transform->transform = pp_mat3_identity();
return mp_const_none;
}
/* 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) {
@ -315,23 +610,40 @@ mp_obj_t VECTOR_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
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);
self->vector = m_new_class(PicoVector, graphics->graphics);
return self;
}
mp_obj_t VECTOR_set_transform(mp_obj_t self_in, mp_obj_t transform_in) {
_VECTOR_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VECTOR_obj_t);
(void)self;
if(transform_in == mp_const_none) {
pp_mat3_t* old = pp_transform(NULL);
(void)old; // TODO: Return old transform?
} else if MP_OBJ_IS_TYPE(transform_in, &TRANSFORM_type) {
_TRANSFORM_obj_t *transform = (_TRANSFORM_obj_t *)MP_OBJ_TO_PTR(transform_in);
pp_mat3_t* old = pp_transform(&transform->transform);
(void)old;
} else {
// TODO: ValueError?
}
return mp_const_none;
}
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);
(void)font_size;
bool result = false;
if (mp_obj_is_str(font)) {
result = self->vector->set_font(mp_obj_to_string_r(font), font_size);
// TODO: Implement when Alright Fonts rewrite is ready
GET_STR_DATA_LEN(font, str, str_len);
result = self->vector->set_font((const char*)str, font_size);
}
else {
@ -347,20 +659,41 @@ mp_obj_t VECTOR_set_font_size(mp_obj_t self_in, mp_obj_t size) {
return mp_const_none;
}
mp_obj_t VECTOR_set_antialiasing(mp_obj_t self_in, mp_obj_t aa) {
mp_obj_t VECTOR_set_font_word_spacing(mp_obj_t self_in, mp_obj_t spacing) {
_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));
self->vector->set_font_word_spacing(mp_obj_get_int(spacing));
return mp_const_none;
}
mp_obj_t VECTOR_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
mp_obj_t VECTOR_set_font_letter_spacing(mp_obj_t self_in, mp_obj_t spacing) {
_VECTOR_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VECTOR_obj_t);
self->vector->set_font_letter_spacing(mp_obj_get_int(spacing));
return mp_const_none;
}
mp_obj_t VECTOR_set_font_line_height(mp_obj_t self_in, mp_obj_t spacing) {
_VECTOR_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VECTOR_obj_t);
self->vector->set_font_line_height(mp_obj_get_int(spacing));
return mp_const_none;
}
mp_obj_t VECTOR_set_font_align(mp_obj_t self_in, mp_obj_t align) {
_VECTOR_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VECTOR_obj_t);
self->vector->set_font_align(mp_obj_get_int(align));
return mp_const_none;
}
mp_obj_t VECTOR_measure_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_x, MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_y, MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_angle, MP_ARG_OBJ, {.u_obj = mp_const_none} }
};
@ -375,28 +708,72 @@ mp_obj_t VECTOR_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
GET_STR_DATA_LEN(text_obj, str, str_len);
const std::string_view t((const char*)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));
pp_mat3_t tt = pp_mat3_identity();
if(args[ARG_angle].u_obj != mp_const_none) {
pp_mat3_rotate(&tt, mp_obj_get_float(args[ARG_angle].u_obj));
}
pp_mat3_translate(&tt, (float)x, (float)y);
pp_rect_t bounds = self->vector->measure_text(t, &tt);
// TODO: Should probably add the transformations available to text here?
mp_obj_t tuple[4];
tuple[0] = mp_picovector_set_point_type((int)(bounds.x));
tuple[1] = mp_picovector_set_point_type((int)(bounds.y));
tuple[2] = mp_picovector_set_point_type((int)(bounds.w));
tuple[3] = mp_picovector_set_point_type((int)(bounds.h));
return mp_obj_new_tuple(4, tuple);
}
mp_obj_t VECTOR_set_clip(mp_obj_t self_in, mp_obj_t clip_in) {
_VECTOR_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VECTOR_obj_t);
picovector_point_type x = self->vector->graphics->bounds.x;
picovector_point_type y = self->vector->graphics->bounds.y;
picovector_point_type w = self->vector->graphics->bounds.w;
picovector_point_type h = self->vector->graphics->bounds.h;
if(mp_obj_is_exact_type(clip_in, &mp_type_tuple)){
mp_obj_tuple_t *t_clip = MP_OBJ_TO_PTR2(clip_in, mp_obj_tuple_t);
if(t_clip->len != 4) mp_raise_ValueError("Clip must have x, y, w, h");
x = mp_picovector_get_point_type(t_clip->items[0]);
y = mp_picovector_get_point_type(t_clip->items[1]);
w = mp_picovector_get_point_type(t_clip->items[2]);
h = mp_picovector_get_point_type(t_clip->items[3]);
}
pp_clip(x, y, w, h);
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 };
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((pp_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, ARG_max_width, ARG_max_height };
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_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_QSTR_max_width, MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_max_height, MP_ARG_INT, {.u_int = 0} }
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@ -404,63 +781,41 @@ mp_obj_t VECTOR_rotate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_arg
_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");
mp_obj_t text_obj = args[ARG_text].u_obj;
_POLYGON_obj_t *poly = MP_OBJ_TO_PTR2(args[ARG_polygon].u_obj, _POLYGON_obj_t);
if(!mp_obj_is_str_or_bytes(text_obj)) mp_raise_TypeError("text: string required");
Point origin = Point(args[ARG_origin_x].u_int, args[ARG_origin_y].u_int);
GET_STR_DATA_LEN(text_obj, str, str_len);
float angle = mp_obj_get_float(args[ARG_angle].u_obj);
const std::string_view t((const char *)str, str_len);
self->vector->rotate(poly->contour, origin, angle);
int x = args[ARG_x].u_int;
int y = args[ARG_y].u_int;
int max_width = args[ARG_max_width].u_int;
int max_height = args[ARG_max_height].u_int;
return mp_const_none;
}
pp_mat3_t tt = pp_mat3_identity();
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);
if(args[ARG_angle].u_obj != mp_const_none) {
pp_mat3_rotate(&tt, mp_obj_get_float(args[ARG_angle].u_obj));
}
self->vector->polygon(contours);
pp_mat3_translate(&tt, (float)x, (float)y);
self->vector->text(t, max_width, max_height, &tt);
return mp_const_none;
}
mp_obj_t VECTOR_draw(mp_obj_t self_in, mp_obj_t poly_in) {
_VECTOR_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VECTOR_obj_t);
(void)self;
if(!MP_OBJ_IS_TYPE(poly_in, &POLYGON_type)) mp_raise_TypeError("draw: Polygon required.");
_POLY_obj_t *poly = MP_OBJ_TO_PTR2(poly_in, _POLY_obj_t);
pp_render(poly->poly);
return mp_const_none;
}

Wyświetl plik

@ -3,26 +3,50 @@
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 const mp_obj_type_t TRANSFORM_type;
/* Polygon */
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 mp_obj_t POLYGON_path(size_t n_args, const mp_obj_t *all_args);
extern mp_obj_t POLYGON_regular(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t POLYGON_rectangle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t POLYGON_circle(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t POLYGON_arc(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t POLYGON_star(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t POLYGON_line(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_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_bounds(mp_obj_t self_in);
extern mp_obj_t POLYGON_transform(mp_obj_t self_in, mp_obj_t transform_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);
/* Transform */
extern mp_obj_t TRANSFORM_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 TRANSFORM_rotate(mp_obj_t self_in, mp_obj_t angle_in, mp_obj_t origin_in);
extern mp_obj_t TRANSFORM_translate(mp_obj_t self_in, mp_obj_t x_in, mp_obj_t y_in);
extern mp_obj_t TRANSFORM_scale(mp_obj_t self_in, mp_obj_t x_in, mp_obj_t y_in);
extern mp_obj_t TRANSFORM_reset(mp_obj_t self_in);
/* Vector */
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_measure_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_font_word_spacing(mp_obj_t self_in, mp_obj_t size);
extern mp_obj_t VECTOR_set_font_letter_spacing(mp_obj_t self_in, mp_obj_t size);
extern mp_obj_t VECTOR_set_font_line_height(mp_obj_t self_in, mp_obj_t size);
extern mp_obj_t VECTOR_set_font_align(mp_obj_t self_in, mp_obj_t align);
extern mp_obj_t VECTOR_set_antialiasing(mp_obj_t self_in, mp_obj_t aa);
extern mp_obj_t VECTOR_set_transform(mp_obj_t self_in, mp_obj_t transform_in);
extern mp_obj_t VECTOR_set_clip(mp_obj_t self_in, mp_obj_t clip_in);
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_draw(mp_obj_t self_in, mp_obj_t poly_in);
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);

Wyświetl plik

@ -1,5 +1,5 @@
from pimoroni import RGBLED, Button
from picographics import PicoGraphics, DISPLAY_INTERSTATE75_32X32, DISPLAY_INTERSTATE75_64X32, DISPLAY_INTERSTATE75_96X32, DISPLAY_INTERSTATE75_96X48, DISPLAY_INTERSTATE75_128X32, DISPLAY_INTERSTATE75_64X64, DISPLAY_INTERSTATE75_128X64, DISPLAY_INTERSTATE75_192X64, DISPLAY_INTERSTATE75_256X64
from picographics import PicoGraphics, DISPLAY_INTERSTATE75_32X32, DISPLAY_INTERSTATE75_64X32, DISPLAY_INTERSTATE75_96X32, DISPLAY_INTERSTATE75_96X48, DISPLAY_INTERSTATE75_128X32, DISPLAY_INTERSTATE75_64X64, DISPLAY_INTERSTATE75_128X64, DISPLAY_INTERSTATE75_192X64, DISPLAY_INTERSTATE75_256X64, DISPLAY_INTERSTATE75_128X128
from pimoroni_i2c import PimoroniI2C
import hub75
import sys
@ -29,6 +29,7 @@ class Interstate75:
DISPLAY_INTERSTATE75_128X64 = DISPLAY_INTERSTATE75_128X64
DISPLAY_INTERSTATE75_192X64 = DISPLAY_INTERSTATE75_192X64
DISPLAY_INTERSTATE75_256X64 = DISPLAY_INTERSTATE75_256X64
DISPLAY_INTERSTATE75_128X128 = DISPLAY_INTERSTATE75_128X128
PANEL_GENERIC = hub75.PANEL_GENERIC
PANEL_FM6126A = hub75.PANEL_FM6126A
@ -46,7 +47,15 @@ class Interstate75:
self.interstate75w = "Pico W" in sys.implementation._machine
self.display = PicoGraphics(display=display)
self.width, self.height = self.display.get_bounds()
self.hub75 = hub75.Hub75(self.width, self.height, panel_type=panel_type, stb_invert=stb_invert, color_order=color_order)
out_width = self.width
out_height = self.height
if display == DISPLAY_INTERSTATE75_128X128:
out_width = 256
out_height = 64
self.hub75 = hub75.Hub75(out_width, out_height, panel_type=panel_type, stb_invert=stb_invert, color_order=color_order)
self.hub75.start()
if self.interstate75w:
self._switch_pins = self.SWITCH_PINS_W