Merge pull request #1019 from pimoroni/feature/picovector2-and-layers

PicoVector Enhancements & PicoGraphics Layers
pull/1078/head v1.25.0
Philip Howard 2025-05-12 13:54:50 +01:00 zatwierdzone przez GitHub
commit 21ec6a23ba
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
137 zmienionych plików z 5416 dodań i 2370 usunięć

Wyświetl plik

@ -7,7 +7,7 @@ on:
types: [created]
env:
MICROPYTHON_VERSION: v1.24.0
MICROPYTHON_VERSION: v1.25.0
MICROPYTHON_FLAVOUR: micropython
jobs:
@ -36,12 +36,6 @@ jobs:
board: PIMORONI_TUFTY2040
- name: enviro
board: PICO_W_ENVIRO
- name: galactic_unicorn
board: RPI_PICO_W
- name: cosmic_unicorn
board: RPI_PICO_W
- name: stellar_unicorn
board: RPI_PICO_W
env:
# MicroPython version will be contained in github.event.release.tag_name for releases

3
.gitmodules vendored
Wyświetl plik

@ -25,3 +25,6 @@
[submodule "drivers/mlx90640/src"]
path = drivers/mlx90640/src
url = https://github.com/melexis/mlx90640-library
[submodule "drivers/bme69x/src"]
path = drivers/bme69x/src
url = https://github.com/boschsensortec/BME690_SensorAPI

Wyświetl plik

@ -44,4 +44,5 @@ add_subdirectory(st7567)
add_subdirectory(psram_display)
add_subdirectory(shiftregister)
add_subdirectory(inky73)
add_subdirectory(mlx90640)
add_subdirectory(mlx90640)
add_subdirectory(bme69x)

Wyświetl plik

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

Wyświetl plik

@ -0,0 +1,15 @@
set(DRIVER_NAME bme69x)
add_library(${DRIVER_NAME} INTERFACE)
target_sources(${DRIVER_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/src/bme69x.c
${CMAKE_CURRENT_LIST_DIR}/bme69x.cpp
)
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}/src)
# We can't control the uninitialized result variables in the BME68X API
# so demote unitialized to a warning for this target.
target_compile_options(${DRIVER_NAME} INTERFACE -Wno-error=uninitialized)

Wyświetl plik

@ -0,0 +1,121 @@
#include "bme69x.hpp"
#include "pico/stdlib.h"
namespace pimoroni {
bool BME69X::init() {
int8_t result = 0;
if(interrupt != PIN_UNUSED) {
gpio_set_function(interrupt, GPIO_FUNC_SIO);
gpio_set_dir(interrupt, GPIO_IN);
gpio_pull_up(interrupt);
}
i2c_interface.i2c = i2c;
i2c_interface.address = address;
device.intf_ptr = &i2c_interface;
device.intf = bme69x_intf::BME69X_I2C_INTF;
device.read = (bme69x_read_fptr_t)&read_bytes;
device.write = (bme69x_write_fptr_t)&write_bytes;
device.delay_us = (bme69x_delay_us_fptr_t)&delay_us;
device.amb_temp = 20;
result = bme69x_init(&device);
bme69x_check_rslt("bme69x_init", result);
if(result != BME69X_OK) return false;
result = bme69x_get_conf(&conf, &device);
bme69x_check_rslt("bme69x_get_conf", result);
if(result != BME69X_OK) return false;
configure(BME69X_FILTER_OFF, BME69X_ODR_NONE, BME69X_OS_16X, BME69X_OS_1X, BME69X_OS_2X);
return true;
}
bool BME69X::configure(uint8_t filter, uint8_t odr, uint8_t os_humidity, uint8_t os_pressure, uint8_t os_temp) {
int8_t result = 0;
conf.filter = filter;
conf.odr = odr;
conf.os_hum = os_humidity;
conf.os_pres = os_pressure;
conf.os_temp = os_temp;
bme69x_set_conf(&conf, &device);
bme69x_check_rslt("bme69x_set_conf", result);
if(result != BME69X_OK) return false;
return true;
}
bool BME69X::read_forced(bme69x_data *data, uint16_t heater_temp, uint16_t heater_duration) {
int8_t result = 0;
uint8_t n_fields;
uint32_t delay_period;
heatr_conf.enable = BME69X_ENABLE;
heatr_conf.heatr_temp = heater_temp;
heatr_conf.heatr_dur = heater_duration;
result = bme69x_set_heatr_conf(BME69X_FORCED_MODE, &heatr_conf, &device);
bme69x_check_rslt("bme69x_set_heatr_conf", result);
if(result != BME69X_OK) return false;
result = bme69x_set_op_mode(BME69X_FORCED_MODE, &device);
bme69x_check_rslt("bme69x_set_op_mode", result);
if(result != BME69X_OK) return false;
delay_period = bme69x_get_meas_dur(BME69X_FORCED_MODE, &conf, &device) + (heatr_conf.heatr_dur * 1000);
// Could probably just call sleep_us here directly, I guess the API uses this internally
device.delay_us(delay_period, device.intf_ptr);
result = bme69x_get_data(BME69X_FORCED_MODE, data, &n_fields, &device);
bme69x_check_rslt("bme69x_get_data", result);
if(result != BME69X_OK) return false;
return true;
}
/*
Will read profile_length results with the given temperatures and duration multipliers into the results array.
Blocks until it has a valid result for each temp/duration, and returns the entire set in the given order.
*/
bool BME69X::read_parallel(bme69x_data *results, uint16_t *profile_temps, uint16_t *profile_durations, size_t profile_length) {
int8_t result;
bme69x_data data[3]; // Parallel & Sequential mode read 3 simultaneous fields
uint8_t n_fields;
uint32_t delay_period;
heatr_conf.enable = BME69X_ENABLE;
heatr_conf.heatr_temp_prof = profile_temps;
heatr_conf.heatr_dur_prof = profile_durations;
heatr_conf.profile_len = profile_length;
heatr_conf.shared_heatr_dur = 140 - (bme69x_get_meas_dur(BME69X_PARALLEL_MODE, &conf, &device) / 1000);
result = bme69x_set_heatr_conf(BME69X_PARALLEL_MODE, &heatr_conf, &device);
bme69x_check_rslt("bme69x_set_heatr_conf", result);
if(result != BME69X_OK) return false;
result = bme69x_set_op_mode(BME69X_PARALLEL_MODE, &device);
bme69x_check_rslt("bme69x_set_op_mode", result);
if(result != BME69X_OK) return false;
while (1) {
delay_period = bme69x_get_meas_dur(BME69X_PARALLEL_MODE, &conf, &device) + (heatr_conf.shared_heatr_dur * 1000);
device.delay_us(delay_period, device.intf_ptr);
result = bme69x_get_data(BME69X_PARALLEL_MODE, data, &n_fields, &device);
if(result == BME69X_W_NO_NEW_DATA) continue;
bme69x_check_rslt("bme69x_get_data", result);
if(result != BME69X_OK) return false;
for(auto i = 0u; i < n_fields; i++) {
results[data[i].gas_index] = data[i];
if(data[i].gas_index == profile_length - 1) return true;
}
}
return true;
}
}

Wyświetl plik

@ -0,0 +1,108 @@
#pragma once
#include "hardware/i2c.h"
#include "hardware/gpio.h"
#include "bme69x.h"
#include "bme69x_defs.h"
#include "common/pimoroni_i2c.hpp"
#include "stdio.h"
namespace pimoroni {
class BME69X {
public:
static const uint8_t DEFAULT_I2C_ADDRESS = 0x76;
static const uint8_t ALTERNATE_I2C_ADDRESS = 0x77;
struct i2c_intf_ptr {
I2C *i2c;
int8_t address;
};
i2c_intf_ptr i2c_interface;
bool debug = true;
bool init();
bool configure(uint8_t filter, uint8_t odr, uint8_t os_humidity, uint8_t os_pressure, uint8_t os_temp);
bool read_forced(bme69x_data *data, uint16_t heater_temp=300, uint16_t heater_duration=100);
bool read_parallel(bme69x_data *results, uint16_t *profile_temps, uint16_t *profile_durations, size_t profile_length);
BME69X() : BME69X(new I2C()) {}
BME69X(uint8_t address, uint interrupt = PIN_UNUSED) : BME69X(new I2C(), address, interrupt) {}
BME69X(I2C *i2c, uint8_t address = DEFAULT_I2C_ADDRESS, uint interrupt = PIN_UNUSED) : i2c(i2c), address(address), interrupt(interrupt) {}
I2C *get_i2c() {return i2c;}
uint get_int() {return PIN_UNUSED;}
// Bindings for bme69x_dev
static int write_bytes(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, void *intf_ptr) {
BME69X::i2c_intf_ptr* i2c = (BME69X::i2c_intf_ptr *)intf_ptr;
uint8_t buffer[length + 1];
buffer[0] = reg_addr;
for(auto x = 0u; x < length; x++) {
buffer[x + 1] = reg_data[x];
}
int result = i2c->i2c->write_blocking(i2c->address, buffer, length + 1, false);
return result == PICO_ERROR_GENERIC ? 1 : 0;
};
static int read_bytes(uint8_t reg_addr, uint8_t *reg_data, uint32_t length, void *intf_ptr) {
BME69X::i2c_intf_ptr* i2c = (BME69X::i2c_intf_ptr *)intf_ptr;
int result = i2c->i2c->write_blocking(i2c->address, &reg_addr, 1, true);
result = i2c->i2c->read_blocking(i2c->address, reg_data, length, false);
return result == PICO_ERROR_GENERIC ? 1 : 0;
};
static void delay_us(uint32_t period, void *intf_ptr) {
sleep_us(period);
}
/* From BME69X API examples/common/common.c */
void bme69x_check_rslt(const char api_name[], int8_t rslt)
{
if(!debug) return;
switch (rslt)
{
case BME69X_OK:
/* Do nothing */
break;
case BME69X_E_NULL_PTR:
printf("%s: Error [%d] : Null pointer\r\n", api_name, rslt);
break;
case BME69X_E_COM_FAIL:
printf("%s: Error [%d] : Communication failure\r\n", api_name, rslt);
break;
case BME69X_E_INVALID_LENGTH:
printf("%s: Error [%d] : Incorrect length parameter\r\n", api_name, rslt);
break;
case BME69X_E_DEV_NOT_FOUND:
printf("%s: Error [%d] : Device not found\r\n", api_name, rslt);
break;
case BME69X_E_SELF_TEST:
printf("%s: Error [%d] : Self test error\r\n", api_name, rslt);
break;
case BME69X_W_NO_NEW_DATA:
printf("%s: Warning [%d] : No new data found\r\n", api_name, rslt);
break;
default:
printf("%s: Error [%d] : Unknown error code\r\n", api_name, rslt);
break;
}
}
private:
bme69x_dev device;
bme69x_conf conf;
bme69x_heatr_conf heatr_conf;
I2C *i2c;
int8_t address = DEFAULT_I2C_ADDRESS;
uint interrupt = I2C_DEFAULT_INT;
};
}

@ -0,0 +1 @@
Subproject commit 4381e37a6dceacf0dcb79df2143793062f8eab56

Wyświetl plik

@ -2,6 +2,8 @@
#include <algorithm>
#include <cmath>
#include "hardware/clocks.h"
#include "hub75.hpp"
namespace pimoroni {
@ -37,10 +39,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 +98,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) {
@ -117,6 +147,8 @@ void Hub75::start(irq_handler_t handler) {
FM6126A_setup();
}
uint latch_cycles = clock_get_hz(clk_sys) / 4000000;
// Claim the PIO so we can clean it upon soft restart
pio_sm_claim(pio, sm_data);
pio_sm_claim(pio, sm_row);
@ -128,7 +160,7 @@ void Hub75::start(irq_handler_t handler) {
row_prog_offs = pio_add_program(pio, &hub75_row_program);
}
hub75_data_rgb888_program_init(pio, sm_data, data_prog_offs, DATA_BASE_PIN, pin_clk);
hub75_row_program_init(pio, sm_row, row_prog_offs, ROWSEL_BASE_PIN, ROWSEL_N_PINS, pin_stb);
hub75_row_program_init(pio, sm_row, row_prog_offs, ROWSEL_BASE_PIN, ROWSEL_N_PINS, pin_stb, latch_cycles);
// Prevent flicker in Python caused by the smaller dataset just blasting through the PIO too quickly
pio_sm_set_clkdiv(pio, sm_data, width <= 32 ? 2.0f : 1.0f);
@ -245,32 +277,115 @@ void Hub75::dma_complete() {
}
}
void Hub75::copy_to_back_buffer(void *data, size_t len, int start_x, int start_y, int g_width, int g_height) {
uint8_t *p = (uint8_t *)data;
if(g_width == int32_t(width / 2) && g_height == int32_t(height * 2)) {
for(int y = start_y; y < g_height; 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;
}
// 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 = start_x; x < g_width; 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++;
len -= 4;
if(len == 0) {
return;
}
}
}
} else {
for(uint y = start_y; y < height; y++) {
for(uint x = start_x; 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++;
len -= 4;
if(len == 0) {
return;
}
}
}
}
}
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++;
copy_to_back_buffer(graphics->frame_buffer, width * height * sizeof(RGB888), 0, 0, graphics->bounds.w, graphics->bounds.h);
} else {
unsigned int offset = 0;
graphics->frame_convert(PicoGraphics::PEN_RGB888, [this, &offset, &graphics](void *data, size_t length) {
if (length > 0) {
int offset_y = offset / graphics->bounds.w;
int offset_x = offset - (offset_y * graphics->bounds.w);
copy_to_back_buffer(data, length, offset_x, offset_y, graphics->bounds.w, graphics->bounds.h);
offset += length / sizeof(RGB888);
}
}
}
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++;
}
}
});
}
}
}

Wyświetl plik

@ -17,25 +17,38 @@ const uint ROWSEL_BASE_PIN = 6;
const uint ROWSEL_N_PINS = 5;
const uint BIT_DEPTH = 10;
// This gamma table is used to correct our 8-bit (0-255) colours up to 11-bit,
// allowing us to gamma correct without losing dynamic range.
/*
10-bit gamma table, allowing us to gamma correct our 8-bit colour values up
to 10-bit without losing dynamic range.
Calculated with the following Python code:
gamma_lut = [int(round(1024 * (x / (256 - 1)) ** 2.2)) for x in range(256)]
Post-processed to enforce a minimum difference of 1 between adjacent values,
and no leading zeros:
for i in range(1, len(gamma_lut)):
if gamma_lut[i] <= gamma_lut[i - 1]:
gamma_lut[i] = gamma_lut[i - 1] + 1
*/
constexpr uint16_t GAMMA_10BIT[256] = {
0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8,
8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16,
16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 25,
26, 27, 29, 30, 31, 33, 34, 35, 37, 38, 40, 41, 43, 44, 46, 47,
49, 51, 53, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78,
80, 82, 85, 87, 89, 92, 94, 96, 99, 101, 104, 106, 109, 112, 114, 117,
120, 122, 125, 128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 161, 164,
168, 171, 174, 178, 181, 185, 188, 192, 195, 199, 202, 206, 210, 214, 217, 221,
225, 229, 233, 237, 241, 245, 249, 253, 257, 261, 265, 270, 274, 278, 283, 287,
291, 296, 300, 305, 309, 314, 319, 323, 328, 333, 338, 343, 347, 352, 357, 362,
367, 372, 378, 383, 388, 393, 398, 404, 409, 414, 420, 425, 431, 436, 442, 447,
453, 459, 464, 470, 476, 482, 488, 494, 499, 505, 511, 518, 524, 530, 536, 542,
548, 555, 561, 568, 574, 580, 587, 593, 600, 607, 613, 620, 627, 633, 640, 647,
654, 661, 668, 675, 682, 689, 696, 703, 711, 718, 725, 733, 740, 747, 755, 762,
770, 777, 785, 793, 800, 808, 816, 824, 832, 839, 847, 855, 863, 872, 880, 888,
896, 904, 912, 921, 929, 938, 946, 954, 963, 972, 980, 989, 997, 1006, 1015, 1023
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 82, 84, 87, 89, 91, 94, 96, 98, 101, 103, 106, 109, 111, 114, 117,
119, 122, 125, 128, 130, 133, 136, 139, 142, 145, 148, 151, 155, 158, 161, 164,
167, 171, 174, 177, 181, 184, 188, 191, 195, 198, 202, 206, 209, 213, 217, 221,
225, 228, 232, 236, 240, 244, 248, 252, 257, 261, 265, 269, 274, 278, 282, 287,
291, 295, 300, 304, 309, 314, 318, 323, 328, 333, 337, 342, 347, 352, 357, 362,
367, 372, 377, 382, 387, 393, 398, 403, 408, 414, 419, 425, 430, 436, 441, 447,
452, 458, 464, 470, 475, 481, 487, 493, 499, 505, 511, 517, 523, 529, 535, 542,
548, 554, 561, 567, 573, 580, 586, 593, 599, 606, 613, 619, 626, 633, 640, 647,
653, 660, 667, 674, 681, 689, 696, 703, 710, 717, 725, 732, 739, 747, 754, 762,
769, 777, 784, 792, 800, 807, 815, 823, 831, 839, 847, 855, 863, 871, 879, 887,
895, 903, 912, 920, 928, 937, 945, 954, 962, 971, 979, 988, 997, 1005, 1014, 1023
};
@ -65,6 +78,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;
@ -132,6 +148,7 @@ class Hub75 {
void set_color(uint x, uint y, Pixel c);
void set_pixel(uint x, uint y, uint8_t r, uint8_t g, uint8_t b);
void copy_to_back_buffer(void *data, size_t len, int start_x, int start_y, int g_width, int g_height);
void display_update();
void clear();
void start(irq_handler_t handler);

Wyświetl plik

@ -21,7 +21,10 @@
.wrap_target
out pins, 5 [1] side 0x2 ; Deassert OEn, output row select
out x, 27 [7] side 0x3 ; Pulse LATCH, get OEn pulse width
mov x, y [3] side 0x3 ; Pulse LATCH
wait_loop:
jmp x-- wait_loop side 0x2 ; Wait for row to latch
out x, 27 side 0x2 ; Get OEn pulse width
pulse_loop:
jmp x-- pulse_loop side 0x0 ; Assert OEn for x+1 cycles
.wrap
@ -43,13 +46,16 @@ pulse_loop:
.wrap_target
out pins, 5 [1] side 0x3 ; Deassert OEn, output row select
out x, 27 [7] side 0x2 ; Pulse LATCH, get OEn pulse width
mov x, y [3] side 0x2 ; Pulse LATCH
wait_loop:
jmp x-- wait_loop side 0x3 ; Wait for row to latch
out x, 27 side 0x3 ; Get OEn pulse width
pulse_loop:
jmp x-- pulse_loop side 0x1 ; Assert OEn for x+1 cycles
.wrap
% c-sdk {
static inline void hub75_row_program_init(PIO pio, uint sm, uint offset, uint row_base_pin, uint n_row_pins, uint latch_base_pin) {
static inline void hub75_row_program_init(PIO pio, uint sm, uint offset, uint row_base_pin, uint n_row_pins, uint latch_base_pin, uint latch_cycles) {
pio_sm_set_consecutive_pindirs(pio, sm, row_base_pin, n_row_pins, true);
pio_sm_set_consecutive_pindirs(pio, sm, latch_base_pin, 2, true);
for (uint i = row_base_pin; i < row_base_pin + n_row_pins; ++i)
@ -62,6 +68,8 @@ static inline void hub75_row_program_init(PIO pio, uint sm, uint offset, uint ro
sm_config_set_sideset_pins(&c, latch_base_pin);
sm_config_set_out_shift(&c, true, true, 32);
pio_sm_init(pio, sm, offset, &c);
pio_sm_exec(pio, sm, pio_encode_out(pio_y, 32));
pio_sm_put(pio, sm, latch_cycles);
pio_sm_set_enabled(pio, sm, true);
}

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);
@ -44,14 +60,18 @@ bool APA102::dma_timer_callback(struct repeating_timer *t) {
return true;
}
bool APA102::is_busy() {
return dma_channel_is_busy(dma_channel);
}
void APA102::update(bool blocking) {
if(dma_channel_is_busy(dma_channel) && !blocking) return;
while(dma_channel_is_busy(dma_channel)) {}; // Block waiting for DMA finish
if(is_busy() && !blocking) return;
while(is_busy()) {}; // Block waiting for DMA finish
pio->txf[sm] = 0x00000000; // Output the APA102 start-of-frame bytes
dma_channel_set_trans_count(dma_channel, num_leds, false);
dma_channel_set_read_addr(dma_channel, buffer, true);
if (!blocking) return;
while(dma_channel_is_busy(dma_channel)) {}; // Block waiting for DMA finish
while(is_busy()) {}; // Block waiting for DMA finish
// This is necessary to prevent a single LED remaining lit when clearing and updating.
// This code will only run in *blocking* mode since it's assumed non-blocking will be continuously updating anyway.
// Yes this will slow down LED updates... don't use blocking mode unless you're clearing LEDs before shutdown,

Wyświetl plik

@ -76,6 +76,7 @@ namespace plasma {
bool start(uint fps=60);
bool stop();
void update(bool blocking=false);
bool is_busy();
void clear();
void set_hsv(uint32_t index, float h, float s, float v);
void set_rgb(uint32_t index, uint8_t r, uint8_t g, uint8_t b, bool gamma=true);

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);
@ -43,13 +46,17 @@ bool WS2812::dma_timer_callback(struct repeating_timer *t) {
return true;
}
bool WS2812::is_busy() {
return dma_channel_is_busy(dma_channel);
}
void WS2812::update(bool blocking) {
if(dma_channel_is_busy(dma_channel) && !blocking) return;
while(dma_channel_is_busy(dma_channel)) {}; // Block waiting for DMA finish
if(is_busy() && !blocking) return;
while(is_busy()) {}; // Block waiting for DMA finish
dma_channel_set_trans_count(dma_channel, num_leds, false);
dma_channel_set_read_addr(dma_channel, buffer, true);
if (!blocking) return;
while(dma_channel_is_busy(dma_channel)) {}; // Block waiting for DMA finish
while(is_busy()) {}; // Block waiting for DMA finish
}
bool WS2812::start(uint fps) {

Wyświetl plik

@ -85,6 +85,7 @@ namespace plasma {
bool start(uint fps=60);
bool stop();
void update(bool blocking=false);
bool is_busy();
void clear();
void set_hsv(uint32_t index, float h, float s, float v, uint8_t w=0);
void set_rgb(uint32_t index, uint8_t r, uint8_t g, uint8_t b, uint8_t w=0, bool gamma=true);

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

@ -23,6 +23,7 @@ namespace pimoroni {
TEON = 0x35,
MADCTL = 0x36,
COLMOD = 0x3A,
RAMCTRL = 0xB0,
GCTRL = 0xB7,
VCOMS = 0xBB,
LCMCTRL = 0xC0,
@ -79,6 +80,12 @@ namespace pimoroni {
command(reg::PWCTRL1, 2, "\xa4\xa1");
command(reg::FRCTRL2, 1, "\x0f");
// As noted in https://github.com/pimoroni/pimoroni-pico/issues/1040
// this is required to avoid a weird light grey banding issue with low brightness green.
// The banding is not visible without tweaking gamma settings (GMCTRP1 & GMCTRN1) but
// it makes sense to fix it anyway.
command(reg::RAMCTRL, 2, "\x00\xc0");
if(width == 240 && height == 240) {
command(reg::GCTRL, 1, "\x14");
command(reg::VCOMS, 1, "\x37");
@ -282,7 +289,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

@ -15,6 +15,7 @@ add_subdirectory(breakout_trackball)
add_subdirectory(breakout_sgp30)
add_subdirectory(breakout_colourlcd240x240)
add_subdirectory(breakout_msa301)
add_subdirectory(breakout_bme690)
add_subdirectory(breakout_bme688)
add_subdirectory(breakout_bmp280)
add_subdirectory(breakout_bme280)
@ -64,3 +65,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

@ -0,0 +1,2 @@
include("${CMAKE_CURRENT_LIST_DIR}/bme690_forced.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/bme690_parallel.cmake")

Wyświetl plik

@ -0,0 +1,12 @@
set(OUTPUT_NAME bme690_forced)
add_executable(
${OUTPUT_NAME}
${OUTPUT_NAME}.cpp
)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib hardware_i2c pimoroni_i2c bme69x)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -0,0 +1,49 @@
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "bme69x.hpp"
#include "common/pimoroni_i2c.hpp"
/*
Read a single reading from the BME690
*/
using namespace pimoroni;
I2C i2c(BOARD::BREAKOUT_GARDEN);
BME69X bme69x(&i2c);
int main() {
#ifdef PICO_DEFAULT_LED_PIN
gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
#endif
stdio_init_all();
bme69x.init();
while (1) {
sleep_ms(1000);
auto time_abs = get_absolute_time();
auto time_ms = to_ms_since_boot(time_abs);
bme69x_data data;
auto result = bme69x.read_forced(&data);
(void)result;
printf("%lu, %.2f, %.2f, %.2f, %.2f, 0x%x, %d, %d\n",
(long unsigned int)time_ms,
data.temperature,
data.pressure,
data.humidity,
data.gas_resistance,
data.status,
data.gas_index,
data.meas_index);
}
return 0;
}

Wyświetl plik

@ -0,0 +1,12 @@
set(OUTPUT_NAME bme690_parallel)
add_executable(
${OUTPUT_NAME}
${OUTPUT_NAME}.cpp
)
# Pull in pico libraries that we need
target_link_libraries(${OUTPUT_NAME} pico_stdlib hardware_i2c pimoroni_i2c bme69x)
# create map/bin/hex file etc.
pico_add_extra_outputs(${OUTPUT_NAME})

Wyświetl plik

@ -0,0 +1,68 @@
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "bme69x.hpp"
#include "common/pimoroni_i2c.hpp"
/*
Read a sequence of readings from the BME690 with given heat/duration profiles
Reading the full batch of readings will take some time. This seems to take ~10sec.
*/
using namespace pimoroni;
I2C i2c(BOARD::BREAKOUT_GARDEN);
BME69X bme69x(&i2c);
constexpr uint16_t profile_length = 10;
// Space for <profile_length> results
bme69x_data data[profile_length];
/* Heater temperature in degree Celsius */
uint16_t temps[profile_length] = { 320, 100, 100, 100, 200, 200, 200, 320, 320, 320 };
/* Multiplier to the shared heater duration */
uint16_t durations[profile_length] = { 5, 2, 10, 30, 5, 5, 5, 5, 5, 5 };
int main() {
#ifdef PICO_DEFAULT_LED_PIN
gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
#endif
stdio_init_all();
bme69x.init();
while (1) {
sleep_ms(1000);
auto time_start = get_absolute_time();
printf("Fetching %u readings, please wait...\n", profile_length);
auto result = bme69x.read_parallel(data, temps, durations, profile_length);
(void)result;
auto time_end = get_absolute_time();
auto duration = absolute_time_diff_us(time_start, time_end);
auto time_ms = to_ms_since_boot(time_start);
printf("Done at %lu in %lluus\n", (long unsigned int)time_ms, (long long unsigned int)duration);
for(auto i = 0u; i < 10u; i++){
printf("%d, %d: %.2f, %.2f, %.2f, %.2f, 0x%x\n",
data[i].gas_index,
data[i].meas_index,
data[i].temperature,
data[i].pressure,
data[i].humidity,
data[i].gas_resistance,
data[i].status);
}
}
return 0;
}

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

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

Wyświetl plik

@ -6,4 +6,14 @@ target_sources(${LIB_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${LIB_NAME}_data.cpp
)
if(NOT HERSHEY_FONTS)
# TODO: Swap this to disabled by default when we're ready to deprecate
# Hershey has all but been replaced by PicoVector's "alright fonts."
set(HERSHEY_FONTS 1)
endif()
target_compile_definitions(${LIB_NAME} INTERFACE
HERSHEY_FONTS=${HERSHEY_FONTS}
)
target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})

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};
}
@ -29,13 +37,17 @@ namespace pimoroni {
void PicoGraphics::set_font(const bitmap::font_t *font){
this->bitmap_font = font;
#ifdef HERSHEY_FONTS
this->hershey_font = nullptr;
#endif
}
#ifdef HERSHEY_FONTS
void PicoGraphics::set_font(const hershey::font_t *font){
this->bitmap_font = nullptr;
this->hershey_font = font;
}
#endif
void PicoGraphics::set_font(std::string_view name){
if (name == "bitmap6") {
@ -45,10 +57,12 @@ namespace pimoroni {
} else if (name == "bitmap14_outline") {
set_font(&font14_outline);
} else {
#ifdef HERSHEY_FONTS
// check that font exists and assign it
if(hershey::has_font(name)) {
set_font(hershey::font(name));
}
#endif
}
}
@ -138,12 +152,14 @@ namespace pimoroni {
return;
}
#ifdef HERSHEY_FONTS
if (hershey_font) {
hershey::glyph(hershey_font, [this](int32_t x1, int32_t y1, int32_t x2, int32_t y2) {
line(Point(x1, y1), Point(x2, y2));
}, c, p.x, p.y, s, a);
return;
}
#endif
}
void PicoGraphics::text(const std::string_view &t, const Point &p, int32_t wrap, float s, float a, uint8_t letter_spacing, bool fixed_width) {
@ -154,6 +170,7 @@ namespace pimoroni {
return;
}
#ifdef HERSHEY_FONTS
if (hershey_font) {
if(thickness == 1) {
hershey::text(hershey_font, [this](int32_t x1, int32_t y1, int32_t x2, int32_t y2) {
@ -166,11 +183,14 @@ namespace pimoroni {
}
return;
}
#endif
}
int32_t PicoGraphics::measure_text(const std::string_view &t, float s, uint8_t letter_spacing, bool fixed_width) {
if (bitmap_font) return bitmap::measure_text(bitmap_font, t, std::max(1.0f, s), letter_spacing, fixed_width);
#ifdef HERSHEY_FONTS
if (hershey_font) return hershey::measure_text(hershey_font, t, s);
#endif
return 0;
}

Wyświetl plik

@ -9,7 +9,9 @@
#include <functional>
#include <math.h>
#ifdef HERSHEY_FONTS
#include "libraries/hershey_fonts/hershey_fonts.hpp"
#endif
#include "libraries/bitmap_fonts/bitmap_fonts.hpp"
#include "libraries/bitmap_fonts/font6_data.hpp"
#include "libraries/bitmap_fonts/font8_data.hpp"
@ -79,6 +81,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 +145,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 +235,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;
@ -234,7 +247,10 @@ namespace pimoroni {
//scanline_interrupt_func scanline_interrupt = nullptr;
const bitmap::font_t *bitmap_font;
#ifdef HERSHEY_FONTS
const hershey::font_t *hershey_font;
#endif
static constexpr RGB332 rgb_to_rgb332(uint8_t r, uint8_t g, uint8_t b) {
return RGB(r, g, b).to_rgb332();
@ -270,6 +286,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 +300,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,10 +318,12 @@ 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);
#ifdef HERSHEY_FONTS
void set_font(const hershey::font_t *font);
#endif
void set_font(std::string_view name);
void set_dimensions(int width, int height);
@ -330,7 +357,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 +373,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 +414,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 +447,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 +467,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 +482,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 +502,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 +528,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 +549,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 +572,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 +642,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
@ -78,6 +79,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
@ -148,16 +150,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,54 @@ 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) {
// Unpack our pen colour
uint32_t sr = (color >> 16) & 0xff;
uint32_t sg = (color >> 8) & 0xff;
uint32_t sb = (color >> 0) & 0xff;
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++) {
uint32_t dest = *p_dest;
uint8_t alpha = *p_alpha;
// TODO: Alpha blending
if(alpha == 255) {
*p_dest = color;
}else if(alpha == 0) {
} else {
// blend the pixel
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,36 @@
#include "af-memory.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
void *af_tracked_malloc(size_t size) {
return malloc(size);
}
void *af_tracked_realloc(void *p, size_t size) {
return realloc(p, size);
}
void af_tracked_free(void *p) {
free(p);
}
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,16 @@
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
void *af_tracked_malloc(size_t size);
void *af_tracked_realloc(void *p, size_t size);
void af_tracked_free(void *p);
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,512 @@
/*
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>
#define FLAG_16BIT_POINT_COUNT 0b00000001
#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)
#define PP_TRACKED_MALLOC(size) AF_TRACKED_MALLOC(size)
#define PP_TRACKED_REALLOC(p, size) AF_TRACKED_REALLOC(p, size)
#define PP_TRACKED_FREE(p) AF_TRACKED_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 {
uint16_t point_count;
af_point_t *points;
} af_path_t;
typedef struct {
uint16_t 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, uint16_t 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) {
bool font16 = false;
// 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 == FLAG_16BIT_POINT_COUNT) {
font16 = true;
} else 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_TRACKED_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 = font16 ? ru16(file) : 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;
}
uint16_t get_utf8_char(const char *text, const char *end) {
uint16_t codepoint;
if((*text & 0x80) == 0x00) {
codepoint = *text; // ASCII, codepoints U+0000...U007F
}
else if( ((*text & 0xE0) == 0xC0) && (text+1 <= end) && ((*(text+1) & 0xC0) == 0x80) ) {
codepoint = ((uint16_t)(*text & 0x1F) << 6) + (*(text+1) & 0x3F); //codepoints U+0080...U+07FF
}
else if( ((*text & 0xF0) == 0xE0) && (text+2 <= end) && ((*(text+1) & 0xC0) == 0x80) && ((*(text+2) & 0xC0) == 0x80) ) {
codepoint = ((uint16_t)(*text & 0x0F) << 12) + ((uint16_t)(*(text+1) & 0x3F) << 6) + (*(text+2) & 0x3F); // codepoints U+0800...U+FFFF
}
else {
codepoint = 0xFFFF; // malformed UTF-8 sequences or unsupported codepoints starting at U+10000
}
return codepoint;
}
uint8_t num_of_utf8_continuation_bytes(const char *text, const char *end) {
uint8_t cont = 0;
for(char c = *text; text < end; text++, c = *text) {
if((c & 0xC0) == 0x80) {
cont++;
}
}
return cont;
}
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, glyph->paths[i].point_count);
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, uint16_t 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;
uint16_t utf8_char;
while(text < end) {
utf8_char = get_utf8_char(text, end);
text++;
if(utf8_char > 0x7F) {
text++;
}
if(utf8_char > 0x7FF) {
text++;
}
af_glyph_t *glyph = find_glyph(face, utf8_char);
if(!glyph) {
continue;
}
float char_width;
if(utf8_char == 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;
uint16_t utf8_char;
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;
while(line < end) {
utf8_char = get_utf8_char(line, end);
line++;
if(utf8_char > 0x7F) {
line++;
}
if(utf8_char > 0x7FF) {
line++;
}
af_glyph_t *glyph = find_glyph(face, utf8_char);
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 ? *old : pp_mat3_identity();
pp_mat3_mul(&final_transform, &caret_transform);
pp_transform(&final_transform);
af_render_glyph(glyph, tm);
if(utf8_char == 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,36 @@
#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 AF_TRACKED_MALLOC(size) af_tracked_malloc(size)
#define AF_TRACKED_REALLOC(p, size) af_tracked_realloc(p, size)
#define AF_TRACKED_FREE(p) af_tracked_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 PP_TRACKED_MALLOC(size) af_tracked_malloc(size)
#define PP_TRACKED_REALLOC(p, size) af_tracked_realloc(p, size)
#define PP_TRACKED_FREE(p) af_tracked_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 +38,142 @@ 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_transform(NULL);
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;
text_metrics.face = NULL;
text_metrics.transform = NULL;
// 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_tracked_free(text_metrics.face->glyphs);
af_tracked_free(text_metrics.face);
}
text_metrics.face = (af_face_t *)af_tracked_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_tracked_free(text_metrics.face->glyphs);
af_tracked_free(text_metrics.face);
}
text_metrics.face = (af_face_t *)af_tracked_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 + d.h}) : _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 = d.s != 0.0f ? pp_path_new() : NULL;
// 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);
pp_path_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,738 @@
/*
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, int32_t points = -1);
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
#ifndef PP_TRACKED_MALLOC
#define PP_TRACKED_MALLOC(size) malloc(size)
#define PP_TRACKED_REALLOC(p, size) realloc(p, size)
#define PP_TRACKED_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;
}
pp_path_t *pp_path_new(int32_t points = -1) {
pp_path_t *path = (pp_path_t *)PP_MALLOC(sizeof(pp_path_t));
memset(path, 0, sizeof(pp_path_t));
path->storage = points > -1 ? points : 8;
path->points = (pp_point_t *)PP_MALLOC(sizeof(pp_point_t) * path->storage);
return path;
}
void pp_path_free(pp_path_t *path) {
PP_FREE(path->points);
PP_FREE(path);
}
void pp_poly_free(pp_poly_t *poly) {
if(poly->paths) {
pp_path_t *path = poly->paths;
while(path) {
pp_path_t *free_path = path;
path = path->next;
pp_path_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, int32_t points) {
pp_path_t *path = pp_path_new(points);
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_TRACKED_MALLOC(PP_TILE_BUFFER_SIZE * 4 * max_nodes_per_scanline * 2 * sizeof(int32_t));
pp_node_counts = (uint32_t *)PP_TRACKED_MALLOC(PP_TILE_BUFFER_SIZE * 4 * sizeof(uint32_t));
}
void pp_deinit() {
PP_TRACKED_FREE(pp_nodes);
PP_TRACKED_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;
}
int32_t *pp_scanline_nodes = &pp_nodes[y * _pp_max_nodes_per_scanline * 2];
const int32_t nodes_step = _pp_max_nodes_per_scanline * 2;
const int full_tile_width = (tb->w << _pp_antialias);
if (false && _pp_min(x, ex) >= 0 && _pp_max(x, ex) <= full_tile_width) {
// loop over scanlines
while(count--) {
// consume accumulated error
while(e > dy) {e -= dy; x += xinc;}
//debug(" + adding node at %d, %d\n", x, y);
// add node to node list
pp_scanline_nodes[pp_node_counts[y]++] = x;
// step to next scanline and accumulate error
y++;
e += einc;
pp_scanline_nodes += nodes_step;
}
}
else {
// loop over scanlines
while(count--) {
// consume accumulated error
while(e > dy) {e -= dy; x += xinc;}
// clamp node x value to tile bounds
int nx = _pp_max(_pp_min(x, full_tile_width), 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;
pp_scanline_nodes += nodes_step;
}
}
}
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, 0, 0, 0}; // render bounds
int maxx = 0, maxy = -1;
const int antialias_mask = (1 << _pp_antialias) - 1;
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];
bool rendered_any = false;
rb.x = _pp_min(rb.x, *pp_scanline_nodes);
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
rendered_any = true;
if (_pp_antialias) {
int ax = sx >> _pp_antialias;
const int aex = ex >> _pp_antialias;
if (ax == aex) {
row_data[ax] += ex - sx;
continue;
}
row_data[ax] += (1 << _pp_antialias) - (sx & antialias_mask);
for(ax++; ax < aex; ax++) {
row_data[ax] += (1 << _pp_antialias);
}
// This might add 0 to the byte after the end of the row,
// TODO check that's OK
row_data[ax] += ex & antialias_mask;
}
else {
for(int x = sx; x < ex; x++) {
row_data[x]++;
}
}
}
maxx = _pp_max(*(pp_scanline_nodes-1) - 1, maxx);
if (rendered_any) {
maxy = y;
}
else if (y == rb.y) {
++rb.y;
}
}
rb.w = (maxx >= rb.x) ? maxx + 1 - rb.x : 0;
rb.h = (maxy >= rb.y) ? maxy + 1 - rb.y : 0;
// 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

@ -20,4 +20,5 @@
#define MICROPY_HW_PIN_EXT_COUNT CYW43_WL_GPIO_COUNT
#define MICROPY_HW_PIN_RESERVED(i) ((i) == CYW43_PIN_WL_HOST_WAKE || (i) == CYW43_PIN_WL_REG_ON)
int mp_hal_is_pin_reserved(int n);
#define MICROPY_HW_PIN_RESERVED(i) mp_hal_is_pin_reserved(i)

Wyświetl plik

@ -20,4 +20,5 @@
#define MICROPY_HW_PIN_EXT_COUNT CYW43_WL_GPIO_COUNT
#define MICROPY_HW_PIN_RESERVED(i) ((i) == CYW43_PIN_WL_HOST_WAKE || (i) == CYW43_PIN_WL_REG_ON)
int mp_hal_is_pin_reserved(int n);
#define MICROPY_HW_PIN_RESERVED(i) mp_hal_is_pin_reserved(i)

Wyświetl plik

@ -34,13 +34,13 @@ std::string mp_obj_to_string_r(const mp_obj_t &obj) {
return (const char*)str;
}
else if(mp_obj_is_float(obj))
mp_raise_TypeError("can't convert 'float' object to str implicitly");
mp_raise_TypeError(MP_ERROR_TEXT("can't convert 'float' object to str implicitly"));
else if(mp_obj_is_int(obj))
mp_raise_TypeError("can't convert 'int' object to str implicitly");
mp_raise_TypeError(MP_ERROR_TEXT("can't convert 'int' object to str implicitly"));
else if(mp_obj_is_bool(obj))
mp_raise_TypeError("can't convert 'bool' object to str implicitly");
mp_raise_TypeError(MP_ERROR_TEXT("can't convert 'bool' object to str implicitly"));
else
mp_raise_TypeError("can't convert object to str implicitly");
mp_raise_TypeError(MP_ERROR_TEXT("can't convert object to str implicitly"));
}
typedef struct _mp_obj_float_t {
@ -92,7 +92,7 @@ mp_obj_t Badger2040_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_
mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW);
buffer = (uint8_t *)bufinfo.buf;
if(bufinfo.len < (size_t)(width * height / 8)) {
mp_raise_ValueError("Supplied buffer is too small!");
mp_raise_ValueError(MP_ERROR_TEXT("Supplied buffer is too small!"));
}
} else {
buffer = m_new(uint8_t, width * height / 8);
@ -354,7 +354,7 @@ mp_obj_t Badger2040_image(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[ARG_data].u_obj, &bufinfo, MP_BUFFER_RW);
if(bufinfo.len < (size_t)(dw * dh / 8)) {
mp_raise_ValueError("image: Supplied buffer is too small!");
mp_raise_ValueError(MP_ERROR_TEXT("image: Supplied buffer is too small!"));
}
_Badger2040_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _Badger2040_obj_t);
@ -389,7 +389,7 @@ mp_obj_t Badger2040_icon(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_a
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[ARG_data].u_obj, &bufinfo, MP_BUFFER_RW);
if(bufinfo.len < (size_t)(ssize * isize / 8)) {
mp_raise_ValueError("icon: Supplied buffer is too small!");
mp_raise_ValueError(MP_ERROR_TEXT("icon: Supplied buffer is too small!"));
}
_Badger2040_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _Badger2040_obj_t);
@ -471,7 +471,7 @@ mp_obj_t Badger2040_glyph(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_
std::string message = mp_obj_to_string_r(args[ARG_char].u_obj);
self->badger2040->text(message, x, y, scale, rotation);
} else {
mp_raise_TypeError("glyph: expected char or string.");
mp_raise_TypeError(MP_ERROR_TEXT("glyph: expected char or string."));
}
return mp_const_none;

Wyświetl plik

@ -32,15 +32,14 @@ mp_obj_t BreakoutAS7262_make_new(const mp_obj_type_t *type, size_t n_args, size_
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);
self = m_new_obj(breakout_as7262_BreakoutAS7262_obj_t);
self->base.type = &breakout_as7262_BreakoutAS7262_type;
self = mp_obj_malloc(breakout_as7262_BreakoutAS7262_obj_t, &breakout_as7262_BreakoutAS7262_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BreakoutAS7262, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_int].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutAS7262: breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutAS7262: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);

Wyświetl plik

@ -32,15 +32,14 @@ mp_obj_t BreakoutAS7343_make_new(const mp_obj_type_t *type, size_t n_args, size_
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);
self = m_new_obj(breakout_as7343_BreakoutAS7343_obj_t);
self->base.type = &breakout_as7343_BreakoutAS7343_type;
self = mp_obj_malloc(breakout_as7343_BreakoutAS7343_obj_t, &breakout_as7343_BreakoutAS7343_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(AS7343, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_int].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutAS7343: breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutAS7343: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);

Wyświetl plik

@ -30,15 +30,14 @@ mp_obj_t BreakoutBH1745_make_new(const mp_obj_type_t *type, size_t n_args, size_
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);
self = m_new_obj(breakout_bh1745_BreakoutBH1745_obj_t);
self->base.type = &breakout_bh1745_BreakoutBH1745_type;
self = mp_obj_malloc(breakout_bh1745_BreakoutBH1745_obj_t, &breakout_bh1745_BreakoutBH1745_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BreakoutBH1745, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_address].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutBH1745: breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutBH1745: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);
@ -113,9 +112,9 @@ mp_obj_t BreakoutBH1745_threshold(size_t n_args, const mp_obj_t *pos_args, mp_ma
int upper = args[ARG_upper].u_int;
if(lower < 0 || lower > 65535) {
mp_raise_ValueError("lower out of range. Expected 0 to 65535");
mp_raise_ValueError(MP_ERROR_TEXT("lower out of range. Expected 0 to 65535"));
} else if(upper < 0 || upper > 65535) {
mp_raise_ValueError("upper out of range. Expected 0 to 65535");
mp_raise_ValueError(MP_ERROR_TEXT("upper out of range. Expected 0 to 65535"));
} else {
self->breakout->set_threshold_low(lower);
self->breakout->set_threshold_high(upper);
@ -139,7 +138,7 @@ mp_obj_t BreakoutBH1745_measurement_time_ms(size_t n_args, const mp_obj_t *pos_a
int measurement_time = args[ARG_time].u_int;
if(measurement_time < 0 || measurement_time > 65535) {
mp_raise_ValueError("Time out of range. Expected 0 to 65535");
mp_raise_ValueError(MP_ERROR_TEXT("Time out of range. Expected 0 to 65535"));
} else {
self->breakout->set_measurement_time_ms(measurement_time);
}

Wyświetl plik

@ -30,15 +30,14 @@ mp_obj_t BreakoutBME280_make_new(const mp_obj_type_t *type, size_t n_args, size_
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);
self = m_new_obj(breakout_bme280_BreakoutBME280_obj_t);
self->base.type = &breakout_bme280_BreakoutBME280_type;
self = mp_obj_malloc(breakout_bme280_BreakoutBME280_obj_t, &breakout_bme280_BreakoutBME280_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BME280, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_address].u_int, args[ARG_int].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutBME280: breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutBME280: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);
@ -56,7 +55,7 @@ mp_obj_t BreakoutBME280_read(mp_obj_t self_in) {
return mp_obj_new_tuple(3, tuple);
}
mp_raise_msg(&mp_type_RuntimeError, "BME280: read failed.");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BME280: read failed."));
}
mp_obj_t BreakoutBME280_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {

Wyświetl plik

@ -31,15 +31,14 @@ mp_obj_t BreakoutBME68X_make_new(const mp_obj_type_t *type, size_t n_args, size_
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);
self = m_new_obj(breakout_bme68x_BreakoutBME68X_obj_t);
self->base.type = &breakout_bme68x_BreakoutBME68X_type;
self = mp_obj_malloc(breakout_bme68x_BreakoutBME68X_obj_t, &breakout_bme68x_BreakoutBME68X_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BME68X, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_address].u_int, args[ARG_int].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutBME68X: breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutBME68X: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);
@ -71,7 +70,7 @@ mp_obj_t BreakoutBME68X_read(size_t n_args, const mp_obj_t *pos_args, mp_map_t *
return mp_obj_new_tuple(7, tuple);
}
else {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutBME68X: failed read_forced");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutBME68X: failed read_forced"));
return mp_const_none;
}
}

Wyświetl plik

@ -0,0 +1,99 @@
#include "breakout_bme69x.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// BreakoutBME69X Class
////////////////////////////////////////////////////////////////////////////////////////////////////
/***** Methods *****/
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutBME69X_read_obj, 1, BreakoutBME69X_read);
MP_DEFINE_CONST_FUN_OBJ_KW(BreakoutBME69X_configure_obj, 1, BreakoutBME69X_configure);
/***** Binding of Methods *****/
static const mp_rom_map_elem_t BreakoutBME69X_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&BreakoutBME69X_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_configure), MP_ROM_PTR(&BreakoutBME69X_configure_obj) },
};
static MP_DEFINE_CONST_DICT(BreakoutBME69X_locals_dict, BreakoutBME69X_locals_dict_table);
/***** Class Definition *****/
#ifdef MP_DEFINE_CONST_OBJ_TYPE
MP_DEFINE_CONST_OBJ_TYPE(
breakout_bme69x_BreakoutBME69X_type,
MP_QSTR_BreakoutBME69X,
MP_TYPE_FLAG_NONE,
make_new, BreakoutBME69X_make_new,
locals_dict, (mp_obj_dict_t*)&BreakoutBME69X_locals_dict
);
#else
const mp_obj_type_t breakout_bme69x_BreakoutBME69X_type = {
{ &mp_type_type },
.name = MP_QSTR_BreakoutBME69X,
.make_new = BreakoutBME69X_make_new,
.locals_dict = (mp_obj_dict_t*)&BreakoutBME69X_locals_dict,
};
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////
// breakout_bme69x Module
////////////////////////////////////////////////////////////////////////////////////////////////////
/***** Globals Table *****/
static const mp_map_elem_t breakout_bme69x_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_breakout_bme69x) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_BreakoutBME69X), (mp_obj_t)&breakout_bme69x_BreakoutBME69X_type },
{ MP_ROM_QSTR(MP_QSTR_FILTER_COEFF_OFF), MP_ROM_INT(BME69X_FILTER_OFF) },
{ MP_ROM_QSTR(MP_QSTR_FILTER_COEFF_1), MP_ROM_INT(BME69X_FILTER_SIZE_1) },
{ MP_ROM_QSTR(MP_QSTR_FILTER_COEFF_3), MP_ROM_INT(BME69X_FILTER_SIZE_3) },
{ MP_ROM_QSTR(MP_QSTR_FILTER_COEFF_7), MP_ROM_INT(BME69X_FILTER_SIZE_7) },
{ MP_ROM_QSTR(MP_QSTR_FILTER_COEFF_15), MP_ROM_INT(BME69X_FILTER_SIZE_15) },
{ MP_ROM_QSTR(MP_QSTR_FILTER_COEFF_31), MP_ROM_INT(BME69X_FILTER_SIZE_31) },
{ MP_ROM_QSTR(MP_QSTR_FILTER_COEFF_63), MP_ROM_INT(BME69X_FILTER_SIZE_63) },
{ MP_ROM_QSTR(MP_QSTR_FILTER_COEFF_127), MP_ROM_INT(BME69X_FILTER_SIZE_127) },
{ MP_ROM_QSTR(MP_QSTR_NO_OVERSAMPLING), MP_ROM_INT(BME69X_OS_NONE) },
{ MP_ROM_QSTR(MP_QSTR_OVERSAMPLING_1X), MP_ROM_INT(BME69X_OS_1X) },
{ MP_ROM_QSTR(MP_QSTR_OVERSAMPLING_2X), MP_ROM_INT(BME69X_OS_2X) },
{ MP_ROM_QSTR(MP_QSTR_OVERSAMPLING_4X), MP_ROM_INT(BME69X_OS_4X) },
{ MP_ROM_QSTR(MP_QSTR_OVERSAMPLING_8X), MP_ROM_INT(BME69X_OS_8X) },
{ MP_ROM_QSTR(MP_QSTR_OVERSAMPLING_16X), MP_ROM_INT(BME69X_OS_16X) },
/* TODO add MicroPython support for alternate reading modes?
{ MP_ROM_QSTR(MP_QSTR_SLEEP_MODE), MP_ROM_INT(BME69X_SLEEP_MODE) },
{ MP_ROM_QSTR(MP_QSTR_FORCED_MODE), MP_ROM_INT(BME69X_FORCED_MODE) },
{ MP_ROM_QSTR(MP_QSTR_PARALLEL_MODE), MP_ROM_INT(BME69X_PARALLEL_MODE) },
{ MP_ROM_QSTR(MP_QSTR_SEQUENTIAL_MODE), MP_ROM_INT(BME69X_SEQUENTIAL_MODE) },
*/
{ MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_OFF), MP_ROM_INT(BME69X_ODR_NONE) },
{ MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_0_59_MS), MP_ROM_INT(BME69X_ODR_0_59_MS) },
{ MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_62_5_MS), MP_ROM_INT(BME69X_ODR_62_5_MS) },
{ MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_125_MS), MP_ROM_INT(BME69X_ODR_125_MS) },
{ MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_250_MS), MP_ROM_INT(BME69X_ODR_250_MS) },
{ MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_500_MS), MP_ROM_INT(BME69X_ODR_500_MS) },
{ MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_1000_MS), MP_ROM_INT(BME69X_ODR_1000_MS) },
{ MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_10_MS), MP_ROM_INT(BME69X_ODR_10_MS) },
{ MP_ROM_QSTR(MP_QSTR_STANDBY_TIME_20_MS), MP_ROM_INT(BME69X_ODR_20_MS) },
{ MP_ROM_QSTR(MP_QSTR_STATUS_GAS_VALID), MP_ROM_INT(BME69X_GASM_VALID_MSK) },
{ MP_ROM_QSTR(MP_QSTR_STATUS_HEATER_STABLE), MP_ROM_INT(BME69X_HEAT_STAB_MSK) },
{ MP_ROM_QSTR(MP_QSTR_I2C_ADDRESS_DEFAULT), MP_ROM_INT(BME69X_I2C_ADDR_LOW) },
{ MP_ROM_QSTR(MP_QSTR_I2C_ADDRESS_ALT), MP_ROM_INT(BME69X_I2C_ADDR_HIGH) },
};
static MP_DEFINE_CONST_DICT(mp_module_breakout_bme69x_globals, breakout_bme69x_globals_table);
/***** Module Definition *****/
const mp_obj_module_t breakout_bme69x_user_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_breakout_bme69x_globals,
};
////////////////////////////////////////////////////////////////////////////////////////////////////
#if MICROPY_VERSION <= 70144
MP_REGISTER_MODULE(MP_QSTR_breakout_bme69x, breakout_bme69x_user_cmodule, MODULE_BREAKOUT_BME69X_ENABLED);
#else
MP_REGISTER_MODULE(MP_QSTR_breakout_bme69x, breakout_bme69x_user_cmodule);
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

Wyświetl plik

@ -0,0 +1,105 @@
#include "drivers/bme69x/bme69x.hpp"
#include "micropython/modules/util.hpp"
using namespace pimoroni;
extern "C" {
#include "breakout_bme69x.h"
#include "pimoroni_i2c.h"
/***** Variables Struct *****/
typedef struct _breakout_bme69x_BreakoutBME69X_obj_t {
mp_obj_base_t base;
BME69X *breakout;
_PimoroniI2C_obj_t *i2c;
} breakout_bme69x_BreakoutBME69X_obj_t;
/***** Constructor *****/
mp_obj_t BreakoutBME69X_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
breakout_bme69x_BreakoutBME69X_obj_t *self = nullptr;
enum { ARG_i2c, ARG_address, ARG_int };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_i2c, MP_ARG_OBJ, {.u_obj = nullptr} },
{ MP_QSTR_address, MP_ARG_INT, {.u_int = BME69X::DEFAULT_I2C_ADDRESS} },
{ MP_QSTR_interrupt, MP_ARG_INT, {.u_int = PIN_UNUSED} },
};
// Parse args.
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);
self = m_new_obj(breakout_bme69x_BreakoutBME69X_obj_t);
self->base.type = &breakout_bme69x_BreakoutBME69X_type;
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BME69X, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_address].u_int, args[ARG_int].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutBME69X: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);
}
mp_obj_t BreakoutBME69X_read(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_temp, ARG_duration };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_heater_temp, MP_ARG_INT, { .u_int=300 } },
{ MP_QSTR_heater_duration, MP_ARG_INT, { .u_int=100 } },
};
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);
breakout_bme69x_BreakoutBME69X_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_bme69x_BreakoutBME69X_obj_t);
bme69x_data result;
if(self->breakout->read_forced(&result, args[ARG_temp].u_int, args[ARG_duration].u_int)){
mp_obj_t tuple[7];
tuple[0] = mp_obj_new_float(result.temperature);
tuple[1] = mp_obj_new_float(result.pressure);
tuple[2] = mp_obj_new_float(result.humidity);
tuple[3] = mp_obj_new_float(result.gas_resistance);
tuple[4] = mp_obj_new_int(result.status);
tuple[5] = mp_obj_new_int(result.gas_index);
tuple[6] = mp_obj_new_int(result.meas_index);
return mp_obj_new_tuple(7, tuple);
}
else {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutBME69X: failed read_forced"));
return mp_const_none;
}
}
mp_obj_t BreakoutBME69X_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_self, ARG_filter, ARG_standby_time, ARG_os_pressure, ARG_os_temp, ARG_os_humidity };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_filter, MP_ARG_INT, { .u_int=BME69X_FILTER_SIZE_3 } },
{ MP_QSTR_standby_time, MP_ARG_INT, { .u_int=BME69X_ODR_0_59_MS } },
{ MP_QSTR_os_pressure, MP_ARG_INT, { .u_int=BME69X_OS_16X } },
{ MP_QSTR_os_temp, MP_ARG_INT, { .u_int=BME69X_OS_2X } },
{ MP_QSTR_os_humidity, MP_ARG_INT, { .u_int=BME69X_OS_1X } }
};
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);
breakout_bme69x_BreakoutBME69X_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_bme69x_BreakoutBME69X_obj_t);
self->breakout->configure(
args[ARG_filter].u_int,
args[ARG_standby_time].u_int,
args[ARG_os_humidity].u_int,
args[ARG_os_pressure].u_int,
args[ARG_os_temp].u_int
);
return mp_const_none;
}
}

Wyświetl plik

@ -0,0 +1,13 @@
// Include MicroPython API.
#include "py/runtime.h"
#include "drivers/bme69x/src/bme69x_defs.h"
/***** Constants *****/
/***** Extern of Class Definition *****/
extern const mp_obj_type_t breakout_bme69x_BreakoutBME69X_type;
/***** Extern of Class Methods *****/
extern mp_obj_t BreakoutBME69X_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 BreakoutBME69X_read(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
extern mp_obj_t BreakoutBME69X_configure(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);

Wyświetl plik

@ -0,0 +1,21 @@
set(MOD_NAME breakout_bme69x)
string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER)
add_library(usermod_${MOD_NAME} INTERFACE)
target_sources(usermod_${MOD_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.c
${CMAKE_CURRENT_LIST_DIR}/${MOD_NAME}.cpp
)
include(drivers/bme69x/bme69x)
target_link_libraries(usermod_${MOD_NAME} INTERFACE bme69x)
target_include_directories(usermod_${MOD_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}
)
target_compile_definitions(usermod_${MOD_NAME} INTERFACE
MODULE_${MOD_NAME_UPPER}_ENABLED=1
)
target_link_libraries(usermod INTERFACE usermod_${MOD_NAME})

Wyświetl plik

@ -0,0 +1,13 @@
set(MOD_NAME breakout_bme69x)
BREAKOUT_MOD_DIR := $(USERMOD_DIR)
# Add our source files to the respective variables.
SRC_USERMOD += $(BREAKOUT_MOD_DIR)/${MOD_NAME}.c
SRC_USERMOD_CXX += $(BREAKOUT_MOD_DIR)/${MOD_NAME}.cpp
# Add our module directory to the include path.
CFLAGS_USERMOD += -I$(BREAKOUT_MOD_DIR)
CXXFLAGS_USERMOD += -I$(BREAKOUT_MOD_DIR)
# We use C++ features so have to link against the standard library.
LDFLAGS_USERMOD += -lstdc++

Wyświetl plik

@ -31,15 +31,14 @@ mp_obj_t BreakoutBMP280_make_new(const mp_obj_type_t *type, size_t n_args, size_
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);
self = m_new_obj(breakout_bmp280_BreakoutBMP280_obj_t);
self->base.type = &breakout_bmp280_BreakoutBMP280_type;
self = mp_obj_malloc(breakout_bmp280_BreakoutBMP280_obj_t, &breakout_bmp280_BreakoutBMP280_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BMP280, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_address].u_int, args[ARG_int].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutBMP280: breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutBMP280: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);

Wyświetl plik

@ -32,15 +32,14 @@ mp_obj_t BreakoutDotMatrix_make_new(const mp_obj_type_t *type, size_t n_args, si
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);
self = m_new_obj(breakout_dotmatrix_BreakoutDotMatrix_obj_t);
self->base.type = &breakout_dotmatrix_BreakoutDotMatrix_type;
self = mp_obj_malloc(breakout_dotmatrix_BreakoutDotMatrix_obj_t, &breakout_dotmatrix_BreakoutDotMatrix_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BreakoutDotMatrix, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_address].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "DotMatrix breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("DotMatrix breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);
@ -61,7 +60,7 @@ mp_obj_t BreakoutDotMatrix_set_brightness(size_t n_args, const mp_obj_t *pos_arg
float brightness = mp_obj_get_float(args[ARG_brightness].u_obj);
if(brightness < 0 || brightness > 1.0f)
mp_raise_ValueError("brightness out of range. Expected 0.0 to 1.0");
mp_raise_ValueError(MP_ERROR_TEXT("brightness out of range. Expected 0.0 to 1.0"));
else
self->breakout->set_brightness((uint8_t)(brightness * BreakoutDotMatrix::MAX_BRIGHTNESS), args[ARG_update].u_bool);
@ -103,7 +102,7 @@ mp_obj_t BreakoutDotMatrix_set_pixel(size_t n_args, const mp_obj_t *pos_args, mp
int y = args[ARG_y].u_int;
if(x < 0 || x >= BreakoutDotMatrix::WIDTH || y < 0 || y >= BreakoutDotMatrix::HEIGHT)
mp_raise_ValueError("x or y out of range.");
mp_raise_ValueError(MP_ERROR_TEXT("x or y out of range."));
else
self->breakout->set_pixel(x, y, args[ARG_on].u_bool);
@ -167,13 +166,13 @@ mp_obj_t BreakoutDotMatrix_set_image(size_t n_args, const mp_obj_t *pos_args, mp
int padding = args[ARG_padding].u_int;
if(width <= 0 || height <= 0)
mp_raise_ValueError("width or height less than or equal to zero.");
mp_raise_ValueError(MP_ERROR_TEXT("width or height less than or equal to zero."));
else if(offset_x < 0 || offset_y < 0)
mp_raise_ValueError("offset_x or offset_y less than zero.");
mp_raise_ValueError(MP_ERROR_TEXT("offset_x or offset_y less than zero."));
else if(on_level < 0 || offset_y > 255)
mp_raise_ValueError("on_level out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("on_level out of range. Expected 0 to 255"));
else if(padding < 0 || padding > 255)
mp_raise_ValueError("padding out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("padding out of range. Expected 0 to 255"));
else {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[ARG_image].u_obj, &bufinfo, MP_BUFFER_READ);

Wyświetl plik

@ -32,15 +32,14 @@ mp_obj_t BreakoutEncoder_make_new(const mp_obj_type_t *type, size_t n_args, size
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);
self = m_new_obj(breakout_encoder_BreakoutEncoder_obj_t);
self->base.type = &breakout_encoder_BreakoutEncoder_type;
self = mp_obj_malloc(breakout_encoder_BreakoutEncoder_obj_t, &breakout_encoder_BreakoutEncoder_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BreakoutEncoder, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_address].u_int, args[ARG_interrupt].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutEncoder: breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutEncoder: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);
@ -112,7 +111,7 @@ mp_obj_t BreakoutEncoder_set_brightness(size_t n_args, const mp_obj_t *pos_args,
float brightness = mp_obj_get_float(args[ARG_brightness].u_obj);
if(brightness < 0 || brightness > 1.0f)
mp_raise_ValueError("brightness out of range. Expected 0.0 to 1.0");
mp_raise_ValueError(MP_ERROR_TEXT("brightness out of range. Expected 0.0 to 1.0"));
else
self->breakout->set_brightness(brightness);
@ -138,11 +137,11 @@ mp_obj_t BreakoutEncoder_set_led(size_t n_args, const mp_obj_t *pos_args, mp_map
int b = args[ARG_b].u_int;
if(r < 0 || r > 255)
mp_raise_ValueError("r out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("r out of range. Expected 0 to 255"));
else if(g < 0 || g > 255)
mp_raise_ValueError("g out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("g out of range. Expected 0 to 255"));
else if(b < 0 || b > 255)
mp_raise_ValueError("b out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("b out of range. Expected 0 to 255"));
else
self->breakout->set_led(r, g, b);

Wyświetl plik

@ -34,15 +34,14 @@ mp_obj_t BreakoutEncoderWheel_make_new(const mp_obj_type_t *type, size_t n_args,
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);
self = m_new_obj(breakout_encoder_wheel_BreakoutEncoderWheel_obj_t);
self->base.type = &breakout_encoder_wheel_BreakoutEncoderWheel_type;
self = mp_obj_malloc(breakout_encoder_wheel_BreakoutEncoderWheel_obj_t, &breakout_encoder_wheel_BreakoutEncoderWheel_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BreakoutEncoderWheel, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_ioe_address].u_int, args[ARG_led_address].u_int, args[ARG_interrupt].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutEncoderWheel: breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutEncoderWheel: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);
@ -83,7 +82,7 @@ extern mp_obj_t BreakoutEncoderWheel_pressed(mp_obj_t self_in, mp_obj_t button_i
int button = mp_obj_get_int(button_in);
if(button < 0 || button >= 5) {
mp_raise_ValueError("button out of range. Expected 0 to 4");
mp_raise_ValueError(MP_ERROR_TEXT("button out of range. Expected 0 to 4"));
}
return mp_obj_new_bool(self->breakout->pressed(button));
@ -150,7 +149,7 @@ extern mp_obj_t BreakoutEncoderWheel_direction(size_t n_args, const mp_obj_t *po
else {
int direction = mp_obj_get_int(args[ARG_direction].u_obj);
if(direction < 0 || direction > 1) {
mp_raise_ValueError("direction out of range. Expected NORMAL_DIR (0) or REVERSED_DIR (1)");
mp_raise_ValueError(MP_ERROR_TEXT("direction out of range. Expected NORMAL_DIR (0) or REVERSED_DIR (1)"));
}
self->breakout->direction((Direction)direction);
return mp_const_none;
@ -179,13 +178,13 @@ extern mp_obj_t BreakoutEncoderWheel_set_rgb(size_t n_args, const mp_obj_t *pos_
int b = args[ARG_b].u_int;
if(index < 0 || index >= 24)
mp_raise_ValueError("index out of range. Expected 0 to 23");
mp_raise_ValueError(MP_ERROR_TEXT("index out of range. Expected 0 to 23"));
else if(r < 0 || r > 255)
mp_raise_ValueError("r out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("r out of range. Expected 0 to 255"));
else if(g < 0 || g > 255)
mp_raise_ValueError("g out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("g out of range. Expected 0 to 255"));
else if(b < 0 || b > 255)
mp_raise_ValueError("b out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("b out of range. Expected 0 to 255"));
else
self->breakout->set_rgb(index, r, g, b);
@ -222,7 +221,7 @@ extern mp_obj_t BreakoutEncoderWheel_set_hsv(size_t n_args, const mp_obj_t *pos_
}
if(index < 0 || index >= 24)
mp_raise_ValueError("index out of range. Expected 0 to 23");
mp_raise_ValueError(MP_ERROR_TEXT("index out of range. Expected 0 to 23"));
else
self->breakout->set_hsv(index, h, s, v);
@ -258,7 +257,7 @@ enum { ARG_self, ARG_gpio, ARG_mode };
breakout_encoder_wheel_BreakoutEncoderWheel_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_encoder_wheel_BreakoutEncoderWheel_obj_t);
int gpio = args[ARG_gpio].u_int;
if(gpio < 7 || gpio > 9) {
mp_raise_ValueError("gpio out of range. Expected GP7 (7), GP8 (8), or GP9 (9)");
mp_raise_ValueError(MP_ERROR_TEXT("gpio out of range. Expected GP7 (7), GP8 (8), or GP9 (9)"));
}
if(args[ARG_mode].u_obj == mp_const_none) {
@ -289,7 +288,7 @@ extern mp_obj_t BreakoutEncoderWheel_gpio_pin_value(size_t n_args, const mp_obj_
breakout_encoder_wheel_BreakoutEncoderWheel_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, breakout_encoder_wheel_BreakoutEncoderWheel_obj_t);
int gpio = args[ARG_gpio].u_int;
if(gpio < 7 || gpio > 9) {
mp_raise_ValueError("gpio out of range. Expected GP7 (7), GP8 (8), or GP9 (9)");
mp_raise_ValueError(MP_ERROR_TEXT("gpio out of range. Expected GP7 (7), GP8 (8), or GP9 (9)"));
}
if(args[ARG_value].u_obj == mp_const_none) {
@ -346,10 +345,10 @@ extern mp_obj_t BreakoutEncoderWheel_gpio_pwm_frequency(size_t n_args, const mp_
float frequency = mp_obj_get_float(args[ARG_frequency].u_obj);
uint32_t period = (uint32_t)(IOExpander::CLOCK_FREQ / frequency);
if (period / 128 > IOExpander::MAX_PERIOD) {
mp_raise_ValueError("The provided frequency is too low");
mp_raise_ValueError(MP_ERROR_TEXT("The provided frequency is too low"));
}
if (period < 2) {
mp_raise_ValueError("The provided frequency is too high");
mp_raise_ValueError(MP_ERROR_TEXT("The provided frequency is too high"));
}
bool load = args[ARG_load].u_bool;

Wyświetl plik

@ -29,15 +29,14 @@ mp_obj_t BreakoutICP10125_make_new(const mp_obj_type_t *type, size_t n_args, siz
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);
self = m_new_obj(breakout_icp10125_BreakoutICP10125_obj_t);
self->base.type = &breakout_icp10125_BreakoutICP10125_type;
self = mp_obj_malloc(breakout_icp10125_BreakoutICP10125_obj_t, &breakout_icp10125_BreakoutICP10125_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(ICP10125, (pimoroni::I2C *)(self->i2c->i2c));
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutICP10125: breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutICP10125: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);

Wyświetl plik

@ -32,15 +32,14 @@ mp_obj_t BreakoutIOExpander_make_new(const mp_obj_type_t *type, size_t n_args, s
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);
self = m_new_obj(breakout_ioexpander_BreakoutIOExpander_obj_t);
self->base.type = &breakout_ioexpander_BreakoutIOExpander_type;
self = mp_obj_malloc(breakout_ioexpander_BreakoutIOExpander_obj_t, &breakout_ioexpander_BreakoutIOExpander_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BreakoutIOExpander, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_address].u_int, args[ARG_interrupt].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutIOExpander: breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutIOExpander: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);
@ -145,7 +144,7 @@ mp_obj_t BreakoutIOExpander_set_pin_interrupt(size_t n_args, const mp_obj_t *pos
int pin = args[ARG_pin].u_int;
bool enabled = args[ARG_enabled].u_bool;
if(!self->breakout->set_pin_interrupt(pin, enabled)) {
mp_raise_ValueError("pin out of range. Expected 1 to 14");
mp_raise_ValueError(MP_ERROR_TEXT("pin out of range. Expected 1 to 14"));
}
return mp_const_none;
@ -211,7 +210,7 @@ mp_obj_t BreakoutIOExpander_set_pwm_control(size_t n_args, const mp_obj_t *pos_a
int divider = args[ARG_divider].u_int;
if(!self->breakout->set_pwm_control(divider)) {
mp_raise_ValueError("divider not valid. Available options are: 1, 2, 4, 8, 16, 32, 64, 128");
mp_raise_ValueError(MP_ERROR_TEXT("divider not valid. Available options are: 1, 2, 4, 8, 16, 32, 64, 128"));
}
return mp_const_none;
@ -233,7 +232,7 @@ mp_obj_t BreakoutIOExpander_set_pwm_period(size_t n_args, const mp_obj_t *pos_ar
int value = args[ARG_value].u_int;
bool load = args[ARG_load].u_bool;
if(value < 0 || value > 65535)
mp_raise_ValueError("value out of range. Expected 0 to 65535");
mp_raise_ValueError(MP_ERROR_TEXT("value out of range. Expected 0 to 65535"));
else
self->breakout->set_pwm_period(value, load);
@ -255,7 +254,7 @@ mp_obj_t BreakoutIOExpander_get_mode(size_t n_args, const mp_obj_t *pos_args, mp
int pin = args[ARG_pin].u_int;
uint8_t mode = self->breakout->get_mode(pin);
if(mode == UINT8_MAX)
mp_raise_ValueError("pin out of range. Expected 1 to 14");
mp_raise_ValueError(MP_ERROR_TEXT("pin out of range. Expected 1 to 14"));
return mp_obj_new_int(mode);
}
@ -281,7 +280,7 @@ mp_obj_t BreakoutIOExpander_set_mode(size_t n_args, const mp_obj_t *pos_args, mp
bool invert = args[ARG_invert].u_bool;
if(pin < 1 || pin > IOExpander::NUM_PINS)
mp_raise_ValueError("pin out of range. Expected 1 to 14");
mp_raise_ValueError(MP_ERROR_TEXT("pin out of range. Expected 1 to 14"));
else
self->breakout->set_mode(pin, mode, schmitt_trigger, invert);
@ -303,7 +302,7 @@ mp_obj_t BreakoutIOExpander_input(size_t n_args, const mp_obj_t *pos_args, mp_ma
int pin = args[ARG_pin].u_int;
if(pin < 1 || pin > IOExpander::NUM_PINS)
mp_raise_ValueError("pin out of range. Expected 1 to 14");
mp_raise_ValueError(MP_ERROR_TEXT("pin out of range. Expected 1 to 14"));
else
return mp_obj_new_int(self->breakout->input(pin));
@ -325,7 +324,7 @@ mp_obj_t BreakoutIOExpander_input_as_voltage(size_t n_args, const mp_obj_t *pos_
int pin = args[ARG_pin].u_int;
if(pin < 1 || pin > IOExpander::NUM_PINS)
mp_raise_ValueError("pin out of range. Expected 1 to 14");
mp_raise_ValueError(MP_ERROR_TEXT("pin out of range. Expected 1 to 14"));
else
return mp_obj_new_float(self->breakout->input_as_voltage(pin));
@ -351,9 +350,9 @@ mp_obj_t BreakoutIOExpander_output(size_t n_args, const mp_obj_t *pos_args, mp_m
bool load = args[ARG_load].u_bool;
if(pin < 1 || pin > IOExpander::NUM_PINS)
mp_raise_ValueError("pin out of range. Expected 1 to 14");
mp_raise_ValueError(MP_ERROR_TEXT("pin out of range. Expected 1 to 14"));
else if(value < 0 || value > 65535)
mp_raise_ValueError("value out of range. Expected 0 to 65535");
mp_raise_ValueError(MP_ERROR_TEXT("value out of range. Expected 0 to 65535"));
else
self->breakout->output(pin, value, load);
@ -383,13 +382,13 @@ mp_obj_t BreakoutIOExpander_setup_rotary_encoder(size_t n_args, const mp_obj_t *
bool count_microsteps = args[ARG_count_microsteps].u_bool;
if(channel < 1 || channel > 4)
mp_raise_ValueError("channel out of range. Expected 1 to 4");
mp_raise_ValueError(MP_ERROR_TEXT("channel out of range. Expected 1 to 4"));
else if(pin_a < 1 || pin_a > 14)
mp_raise_ValueError("pin_a out of range. Expected 1 to 14");
mp_raise_ValueError(MP_ERROR_TEXT("pin_a out of range. Expected 1 to 14"));
else if(pin_b < 1 || pin_b > 14)
mp_raise_ValueError("pin_b out of range. Expected 1 to 14");
mp_raise_ValueError(MP_ERROR_TEXT("pin_b out of range. Expected 1 to 14"));
else if(pin_c < 1 || pin_c > 14)
mp_raise_ValueError("pin_c out of range. Expected 1 to 14");
mp_raise_ValueError(MP_ERROR_TEXT("pin_c out of range. Expected 1 to 14"));
else
self->breakout->setup_rotary_encoder(channel, pin_a, pin_b, pin_c, count_microsteps);
@ -410,7 +409,7 @@ mp_obj_t BreakoutIOExpander_read_rotary_encoder(size_t n_args, const mp_obj_t *p
int channel = args[ARG_channel].u_int;
if(channel < 1 || channel > 4)
mp_raise_ValueError("channel out of range. Expected 1 to 4");
mp_raise_ValueError(MP_ERROR_TEXT("channel out of range. Expected 1 to 4"));
else
return mp_obj_new_int(self->breakout->read_rotary_encoder(channel));

Wyświetl plik

@ -31,15 +31,14 @@ mp_obj_t BreakoutLTR559_make_new(const mp_obj_type_t *type, size_t n_args, size_
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);
self = m_new_obj(breakout_ltr559_BreakoutLTR559_obj_t);
self->base.type = &breakout_ltr559_BreakoutLTR559_type;
self = mp_obj_malloc(breakout_ltr559_BreakoutLTR559_obj_t, &breakout_ltr559_BreakoutLTR559_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BreakoutLTR559, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_interrupt].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutLTR559: breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutLTR559: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);
@ -118,13 +117,13 @@ mp_obj_t BreakoutLTR559_proximity_led(size_t n_args, const mp_obj_t *pos_args, m
int num_pulses = args[ARG_num_pulses].u_int;
if(current < 0 || current > 255)
mp_raise_ValueError("current out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("current out of range. Expected 0 to 255"));
else if(duty_cycle < 0 || duty_cycle > 255)
mp_raise_ValueError("duty_cycle out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("duty_cycle out of range. Expected 0 to 255"));
else if(pulse_freq < 0 || pulse_freq > 255)
mp_raise_ValueError("pulse_freq out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("pulse_freq out of range. Expected 0 to 255"));
else if(num_pulses < 0 || num_pulses > 255)
mp_raise_ValueError("num_pulses out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("num_pulses out of range. Expected 0 to 255"));
else
self->breakout->proximity_led(current, duty_cycle, pulse_freq, num_pulses);
@ -147,7 +146,7 @@ mp_obj_t BreakoutLTR559_light_control(size_t n_args, const mp_obj_t *pos_args, m
int gain = args[ARG_gain].u_int;
if(gain < 0 || gain > 255)
mp_raise_ValueError("gain out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("gain out of range. Expected 0 to 255"));
else
self->breakout->light_control(args[ARG_active].u_bool, gain);
@ -188,9 +187,9 @@ mp_obj_t BreakoutLTR559_light_threshold(size_t n_args, const mp_obj_t *pos_args,
int upper = args[ARG_upper].u_int;
if(lower < 0 || lower > 65535)
mp_raise_ValueError("lower out of range. Expected 0 to 65535");
mp_raise_ValueError(MP_ERROR_TEXT("lower out of range. Expected 0 to 65535"));
else if(upper < 0 || upper > 65535)
mp_raise_ValueError("upper out of range. Expected 0 to 65535");
mp_raise_ValueError(MP_ERROR_TEXT("upper out of range. Expected 0 to 65535"));
else
self->breakout->light_threshold(lower, upper);
@ -214,9 +213,9 @@ mp_obj_t BreakoutLTR559_proximity_threshold(size_t n_args, const mp_obj_t *pos_a
int upper = args[ARG_upper].u_int;
if(lower < 0 || lower > 65535)
mp_raise_ValueError("lower out of range. Expected 0 to 65535");
mp_raise_ValueError(MP_ERROR_TEXT("lower out of range. Expected 0 to 65535"));
else if(upper < 0 || upper > 65535)
mp_raise_ValueError("upper out of range. Expected 0 to 65535");
mp_raise_ValueError(MP_ERROR_TEXT("upper out of range. Expected 0 to 65535"));
else
self->breakout->proximity_threshold(lower, upper);
@ -240,9 +239,9 @@ mp_obj_t BreakoutLTR559_light_measurement_rate(size_t n_args, const mp_obj_t *po
int rate = args[ARG_rate].u_int;
if(integration_time < 0 || integration_time > 65535)
mp_raise_ValueError("integration_time out of range. Expected 0 to 65535");
mp_raise_ValueError(MP_ERROR_TEXT("integration_time out of range. Expected 0 to 65535"));
else if(rate < 0 || rate > 65535)
mp_raise_ValueError("rate out of range. Expected 0 to 65535");
mp_raise_ValueError(MP_ERROR_TEXT("rate out of range. Expected 0 to 65535"));
else
self->breakout->light_measurement_rate(integration_time, rate);
@ -264,7 +263,7 @@ mp_obj_t BreakoutLTR559_proximity_measurement_rate(size_t n_args, const mp_obj_t
int rate = args[ARG_rate].u_int;
if(rate < 0 || rate > 65535)
mp_raise_ValueError("rate out of range. Expected 0 to 65535");
mp_raise_ValueError(MP_ERROR_TEXT("rate out of range. Expected 0 to 65535"));
else
self->breakout->proximity_measurement_rate(rate);
@ -286,7 +285,7 @@ mp_obj_t BreakoutLTR559_proximity_offset(size_t n_args, const mp_obj_t *pos_args
int offset = args[ARG_offset].u_int;
if(offset < 0 || offset > 65535)
mp_raise_ValueError("offset out of range. Expected 0 to 65535");
mp_raise_ValueError(MP_ERROR_TEXT("offset out of range. Expected 0 to 65535"));
else
self->breakout->proximity_offset(offset);

Wyświetl plik

@ -30,15 +30,14 @@ mp_obj_t BreakoutMatrix11x7_make_new(const mp_obj_type_t *type, size_t n_args, s
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);
self = m_new_obj(breakout_matrix11x7_BreakoutMatrix11x7_obj_t);
self->base.type = &breakout_matrix11x7_BreakoutMatrix11x7_type;
self = mp_obj_malloc(breakout_matrix11x7_BreakoutMatrix11x7_obj_t, &breakout_matrix11x7_BreakoutMatrix11x7_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BreakoutMatrix11x7, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_address].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutMatrix11x7: breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutMatrix11x7: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);
@ -64,10 +63,10 @@ mp_obj_t BreakoutMatrix11x7_set_pixel(size_t n_args, const mp_obj_t *pos_args, m
int val = args[ARG_val].u_int;
if(x < 0 || x >= BreakoutMatrix11x7::WIDTH || y < 0 || y >= BreakoutMatrix11x7::HEIGHT)
mp_raise_ValueError("x or y out of range.");
mp_raise_ValueError(MP_ERROR_TEXT("x or y out of range."));
else {
if(val < 0 || val > 255)
mp_raise_ValueError("val out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("val out of range. Expected 0 to 255"));
else
self->breakout->set_pixel(x, y, val);
}

Wyświetl plik

@ -32,15 +32,14 @@ mp_obj_t BreakoutMICS6814_make_new(const mp_obj_type_t *type, size_t n_args, siz
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);
self = m_new_obj(breakout_mics6814_BreakoutMICS6814_obj_t);
self->base.type = &breakout_mics6814_BreakoutMICS6814_type;
self = mp_obj_malloc(breakout_mics6814_BreakoutMICS6814_obj_t, &breakout_mics6814_BreakoutMICS6814_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BreakoutMICS6814, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_address].u_int, args[ARG_interrupt].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutMICS6814: breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutMICS6814: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);
@ -101,7 +100,7 @@ mp_obj_t BreakoutMICS6814_set_brightness(size_t n_args, const mp_obj_t *pos_args
float brightness = mp_obj_get_float(args[ARG_brightness].u_obj);
if(brightness < 0 || brightness > 1.0f)
mp_raise_ValueError("brightness out of range. Expected 0.0 to 1.0");
mp_raise_ValueError(MP_ERROR_TEXT("brightness out of range. Expected 0.0 to 1.0"));
else
self->breakout->set_brightness(brightness);
@ -127,11 +126,11 @@ mp_obj_t BreakoutMICS6814_set_led(size_t n_args, const mp_obj_t *pos_args, mp_ma
int b = args[ARG_b].u_int;
if(r < 0 || r > 255)
mp_raise_ValueError("r out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("r out of range. Expected 0 to 255"));
else if(g < 0 || g > 255)
mp_raise_ValueError("g out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("g out of range. Expected 0 to 255"));
else if(b < 0 || b > 255)
mp_raise_ValueError("b out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("b out of range. Expected 0 to 255"));
else
self->breakout->set_led(r, g, b);

Wyświetl plik

@ -29,15 +29,14 @@ mp_obj_t BreakoutMSA301_make_new(const mp_obj_type_t *type, size_t n_args, size_
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);
self = m_new_obj(breakout_msa301_BreakoutMSA301_obj_t);
self->base.type = &breakout_msa301_BreakoutMSA301_type;
self = mp_obj_malloc(breakout_msa301_BreakoutMSA301_obj_t, &breakout_msa301_BreakoutMSA301_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BreakoutMSA301, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_interrupt].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutMSA301: breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutMSA301: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);
@ -64,7 +63,7 @@ mp_obj_t BreakoutMSA301_get_axis(size_t n_args, const mp_obj_t *pos_args, mp_map
int sample_count = args[ARG_sample_count].u_int;
if(sample_count < 0 || sample_count > 255)
mp_raise_ValueError("sample_count out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("sample_count out of range. Expected 0 to 255"));
else {
float value = 0.0f;
switch(args[ARG_axis].u_int) {
@ -78,7 +77,7 @@ mp_obj_t BreakoutMSA301_get_axis(size_t n_args, const mp_obj_t *pos_args, mp_map
value = self->breakout->get_axis(BreakoutMSA301::Z, sample_count);
break;
default:
mp_raise_ValueError("axis out of range. Expected 0 to 2 (X, Y, Z)");
mp_raise_ValueError(MP_ERROR_TEXT("axis out of range. Expected 0 to 2 (X, Y, Z)"));
break;
}
@ -102,7 +101,7 @@ mp_obj_t BreakoutMSA301_get_x_axis(size_t n_args, const mp_obj_t *pos_args, mp_m
int sample_count = args[ARG_sample_count].u_int;
if(sample_count < 0 || sample_count > 255)
mp_raise_ValueError("sample_count out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("sample_count out of range. Expected 0 to 255"));
else
return mp_obj_new_float(self->breakout->get_x_axis(sample_count));
@ -123,7 +122,7 @@ mp_obj_t BreakoutMSA301_get_y_axis(size_t n_args, const mp_obj_t *pos_args, mp_m
int sample_count = args[ARG_sample_count].u_int;
if(sample_count < 0 || sample_count > 255)
mp_raise_ValueError("sample_count out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("sample_count out of range. Expected 0 to 255"));
else
return mp_obj_new_float(self->breakout->get_y_axis(sample_count));
@ -144,7 +143,7 @@ mp_obj_t BreakoutMSA301_get_z_axis(size_t n_args, const mp_obj_t *pos_args, mp_m
int sample_count = args[ARG_sample_count].u_int;
if(sample_count < 0 || sample_count > 255)
mp_raise_ValueError("sample_count out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("sample_count out of range. Expected 0 to 255"));
else
return mp_obj_new_float(self->breakout->get_z_axis(sample_count));
@ -170,7 +169,7 @@ mp_obj_t BreakoutMSA301_set_power_mode(size_t n_args, const mp_obj_t *pos_args,
int power_mode = args[ARG_power_mode].u_int;
if(power_mode < 0 || power_mode > 255)
mp_raise_ValueError("power_mode out of range. Expected 0 to 2 (NORMAL, LOW, SUSPEND)");
mp_raise_ValueError(MP_ERROR_TEXT("power_mode out of range. Expected 0 to 2 (NORMAL, LOW, SUSPEND)"));
else
self->breakout->set_power_mode((BreakoutMSA301::PowerMode)power_mode);
@ -193,9 +192,9 @@ mp_obj_t BreakoutMSA301_set_range_and_resolution(size_t n_args, const mp_obj_t *
int range = args[ARG_range].u_int;
int resolution = args[ARG_resolution].u_int;
if(range < 0 || range > 3)
mp_raise_ValueError("range out of range. Expected 0 to 3 (G_2, G_4, G_8, G_16)");
mp_raise_ValueError(MP_ERROR_TEXT("range out of range. Expected 0 to 3 (G_2, G_4, G_8, G_16)"));
if(resolution < 0 || resolution > 3)
mp_raise_ValueError("resolution out of range. Expected 0 to 3 (BITS_14, BITS_12, BITS_10, BITS_8)");
mp_raise_ValueError(MP_ERROR_TEXT("resolution out of range. Expected 0 to 3 (BITS_14, BITS_12, BITS_10, BITS_8)"));
else
self->breakout->set_range_and_resolution((BreakoutMSA301::Range)range, (BreakoutMSA301::Resolution)(resolution << 2));
@ -216,7 +215,7 @@ mp_obj_t BreakoutMSA301_set_axis_polarity(size_t n_args, const mp_obj_t *pos_arg
int polarity = args[ARG_polarity].u_int;
if(polarity < 0 || polarity > 15)
mp_raise_ValueError("polarity out of range. Expected 0 or the bitwise combination of 1 (INVERT_X), 2 (INVERT_Y), 4 (INVERT_Z), or 8 (XY_SWAP)");
mp_raise_ValueError(MP_ERROR_TEXT("polarity out of range. Expected 0 or the bitwise combination of 1 (INVERT_X), 2 (INVERT_Y), 4 (INVERT_Z), or 8 (XY_SWAP)"));
else
self->breakout->set_axis_polarity(polarity);
@ -251,7 +250,7 @@ mp_obj_t BreakoutMSA301_enable_interrupts(size_t n_args, const mp_obj_t *pos_arg
BreakoutMSA301::SINGLE_TAP |
BreakoutMSA301::DOUBLE_TAP;
if(interrupts < 0 || (interrupts & mask) == 0)
mp_raise_ValueError("interrupts out of range. Expected 0 or the bitwise combination of 1 (X_ACTIVE), 2 (Y_ACTIVE), 4 (Z_ACTIVE), 16 (DOUBLE_TAP), 32 (SINGLE_TAP), 64 (ORIENTATION), 2048 (FREEFALL), 4096 (NEW_DATA)");
mp_raise_ValueError(MP_ERROR_TEXT("interrupts out of range. Expected 0 or the bitwise combination of 1 (X_ACTIVE), 2 (Y_ACTIVE), 4 (Z_ACTIVE), 16 (DOUBLE_TAP), 32 (SINGLE_TAP), 64 (ORIENTATION), 2048 (FREEFALL), 4096 (NEW_DATA)"));
else
self->breakout->enable_interrupts(interrupts);
@ -287,7 +286,7 @@ mp_obj_t BreakoutMSA301_set_interrupt_latch(size_t n_args, const mp_obj_t *pos_a
case MSA_LATCH_4S: period = BreakoutMSA301::LATCH_4S; break;
case MSA_LATCH_8S: period = BreakoutMSA301::LATCH_8S; break;
default:
mp_raise_ValueError("latch_period out of range. Expected 0 to 10 (LATCH_1MS, LATCH_2MS, LATCH_25MS, LATCH_50MS, LATCH_100MS, LATCH_250MS, LATCH_500MS, LATCH_1S, LATCH_2S, LATCH_4S, or LATCH_8S)");
mp_raise_ValueError(MP_ERROR_TEXT("latch_period out of range. Expected 0 to 10 (LATCH_1MS, LATCH_2MS, LATCH_25MS, LATCH_50MS, LATCH_100MS, LATCH_250MS, LATCH_500MS, LATCH_1S, LATCH_2S, LATCH_4S, or LATCH_8S)"));
break;
}
self->breakout->set_interrupt_latch(period, reset_latched);
@ -317,7 +316,7 @@ mp_obj_t BreakoutMSA301_read_interrupt(size_t n_args, const mp_obj_t *pos_args,
case BreakoutMSA301::ORIENTATION:
return mp_obj_new_bool(self->breakout->read_interrupt((BreakoutMSA301::Interrupt)interrupt));
default:
mp_raise_ValueError("interrupt not valid. Expected 7 (ACTIVE), 16 (DOUBLE_TAP), 32 (SINGLE_TAP), 64 (ORIENTATION), 2048 (FREEFALL), 4096 (NEW_DATA)");
mp_raise_ValueError(MP_ERROR_TEXT("interrupt not valid. Expected 7 (ACTIVE), 16 (DOUBLE_TAP), 32 (SINGLE_TAP), 64 (ORIENTATION), 2048 (FREEFALL), 4096 (NEW_DATA)"));
break;
}

Wyświetl plik

@ -80,20 +80,20 @@ mp_obj_t make_new(enum ChipType chip, const mp_obj_type_t *type, size_t n_args,
BreakoutPMW3901 *breakout = m_new_class(BreakoutPMW3901, (BG_SPI_SLOT)slot);
if (!breakout->init()) {
m_del_class(BreakoutPMW3901, breakout);
mp_raise_msg(&mp_type_RuntimeError, "BreakoutPMW3901: Init failed");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutPMW3901: Init failed"));
}
self->breakout = breakout;
} else {
BreakoutPAA5100 *breakout = m_new_class(BreakoutPAA5100, (BG_SPI_SLOT)slot);
if (!breakout->init()) {
m_del_class(BreakoutPAA5100, breakout);
mp_raise_msg(&mp_type_RuntimeError, "BreakoutPAA5100: Init failed");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutPAA5100: Init failed"));
}
self->breakout = breakout;
}
}
else {
mp_raise_ValueError("slot not a valid value. Expected 0 to 1");
mp_raise_ValueError(MP_ERROR_TEXT("slot not a valid value. Expected 0 to 1"));
}
}
else {
@ -143,14 +143,14 @@ mp_obj_t make_new(enum ChipType chip, const mp_obj_type_t *type, size_t n_args,
BreakoutPMW3901 *breakout = m_new_class(BreakoutPMW3901, spi, args[ARG_cs].u_int, sck, mosi, miso, args[ARG_interrupt].u_int);
if (!breakout->init()) {
m_del_class(BreakoutPMW3901, breakout);
mp_raise_msg(&mp_type_RuntimeError, "BreakoutPMW3901: Init failed");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutPMW3901: Init failed"));
}
self->breakout = breakout;
} else {
BreakoutPAA5100 *breakout = m_new_class(BreakoutPAA5100, spi, args[ARG_cs].u_int, sck, mosi, miso, args[ARG_interrupt].u_int);
if (!breakout->init()) {
m_del_class(BreakoutPAA5100, breakout);
mp_raise_msg(&mp_type_RuntimeError, "BreakoutPAA5100: Init failed");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutPAA5100: Init failed"));
}
self->breakout = breakout;
}
@ -193,7 +193,7 @@ mp_obj_t BreakoutPMW3901_set_rotation(size_t n_args, const mp_obj_t *pos_args, m
int degrees = args[ARG_degrees].u_int;
if(degrees < 0 || degrees > 3)
mp_raise_ValueError("degrees out of range. Expected 0 (0), 1 (90), 2 (180) or 3 (270)");
mp_raise_ValueError(MP_ERROR_TEXT("degrees out of range. Expected 0 (0), 1 (90), 2 (180) or 3 (270)"));
else
self->breakout->set_rotation((BreakoutPMW3901::Degrees)degrees);
@ -299,7 +299,7 @@ mp_obj_t BreakoutPMW3901_frame_capture(size_t n_args, const mp_obj_t *pos_args,
mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW);
uint8_t *buffer = (uint8_t *)bufinfo.buf;
if(bufinfo.len != (size_t)(BreakoutPMW3901::FRAME_BYTES)) {
mp_raise_ValueError("Supplied buffer is the wrong size for frame capture. Needs to be 1225.");
mp_raise_ValueError(MP_ERROR_TEXT("Supplied buffer is the wrong size for frame capture. Needs to be 1225."));
}
float timeout = (float)BreakoutPMW3901::DEFAULT_MOTION_TIMEOUT_MS / 1000.0f;

Wyświetl plik

@ -32,15 +32,14 @@ mp_obj_t BreakoutPotentiometer_make_new(const mp_obj_type_t *type, size_t n_args
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);
self = m_new_obj(breakout_potentiometer_BreakoutPotentiometer_obj_t);
self->base.type = &breakout_potentiometer_BreakoutPotentiometer_type;
self = mp_obj_malloc(breakout_potentiometer_BreakoutPotentiometer_obj_t, &breakout_potentiometer_BreakoutPotentiometer_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BreakoutPotentiometer, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_interrupt].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutPotentiometer: breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutPotentiometer: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);
@ -123,7 +122,7 @@ mp_obj_t BreakoutPotentiometer_set_brightness(size_t n_args, const mp_obj_t *pos
float brightness = mp_obj_get_float(args[ARG_brightness].u_obj);
if(brightness < 0 || brightness > 1.0f)
mp_raise_ValueError("brightness out of range. Expected 0.0 to 1.0");
mp_raise_ValueError(MP_ERROR_TEXT("brightness out of range. Expected 0.0 to 1.0"));
else
self->breakout->set_brightness(brightness);
@ -149,11 +148,11 @@ mp_obj_t BreakoutPotentiometer_set_led(size_t n_args, const mp_obj_t *pos_args,
int b = args[ARG_b].u_int;
if(r < 0 || r > 255)
mp_raise_ValueError("r out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("r out of range. Expected 0 to 255"));
else if(g < 0 || g > 255)
mp_raise_ValueError("g out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("g out of range. Expected 0 to 255"));
else if(b < 0 || b > 255)
mp_raise_ValueError("b out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("b out of range. Expected 0 to 255"));
else
self->breakout->set_led(r, g, b);

Wyświetl plik

@ -31,15 +31,14 @@ mp_obj_t BreakoutRGBMatrix5x5_make_new(const mp_obj_type_t *type, size_t n_args,
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);
self = m_new_obj(breakout_rgbmatrix5x5_BreakoutRGBMatrix5x5_obj_t);
self->base.type = &breakout_rgbmatrix5x5_BreakoutRGBMatrix5x5_type;
self = mp_obj_malloc(breakout_rgbmatrix5x5_BreakoutRGBMatrix5x5_obj_t, &breakout_rgbmatrix5x5_BreakoutRGBMatrix5x5_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BreakoutRGBMatrix5x5, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_address].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutRGBMatrix5x5: breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutRGBMatrix5x5: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);
@ -69,14 +68,14 @@ mp_obj_t BreakoutRGBMatrix5x5_set_pixel(size_t n_args, const mp_obj_t *pos_args,
int b = args[ARG_b].u_int;
if(x < 0 || x >= BreakoutRGBMatrix5x5::WIDTH || y < 0 || y >= BreakoutRGBMatrix5x5::HEIGHT)
mp_raise_ValueError("x or y out of range.");
mp_raise_ValueError(MP_ERROR_TEXT("x or y out of range."));
else {
if(r < 0 || r > 255)
mp_raise_ValueError("r out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("r out of range. Expected 0 to 255"));
else if(g < 0 || g > 255)
mp_raise_ValueError("g out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("g out of range. Expected 0 to 255"));
else if(b < 0 || b > 255)
mp_raise_ValueError("b out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("b out of range. Expected 0 to 255"));
else
self->breakout->set_pixel(x, y, r, g, b);
}

Wyświetl plik

@ -33,15 +33,14 @@ mp_obj_t BreakoutRTC_make_new(const mp_obj_type_t *type, size_t n_args, size_t n
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);
self = m_new_obj(breakout_rtc_BreakoutRTC_obj_t);
self->base.type = &breakout_rtc_BreakoutRTC_type;
self = mp_obj_malloc(breakout_rtc_BreakoutRTC_obj_t, &breakout_rtc_BreakoutRTC_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BreakoutRTC, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_interrupt].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutRTC: breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutRTC: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);
@ -87,13 +86,13 @@ mp_obj_t BreakoutRTC_set_time(size_t n_args, const mp_obj_t *pos_args, mp_map_t
int month = args[ARG_month].u_int;
int year = args[ARG_year].u_int;
if(sec < 0 || sec > 59) mp_raise_ValueError("sec out of range. Expected 0 to 59");
if(min < 0 || min > 59) mp_raise_ValueError("min out of range. Expected 0 to 59");
if(hour < 0 || hour > 23) mp_raise_ValueError("hour out of range. Expected 0 to 23");
if(weekday < 0 || weekday > 6) mp_raise_ValueError("weekday out of range. Expected 0 to 6");
if(date < 1 || date > 31) mp_raise_ValueError("date out of range. Expected 1 to 31");
if(month < 1 || month > 12) mp_raise_ValueError("month out of range. Expected 1 to 12");
if(year < 2000 || year > 2099) mp_raise_ValueError("year out of range. Expected 2000 to 2099");
if(sec < 0 || sec > 59) mp_raise_ValueError(MP_ERROR_TEXT("sec out of range. Expected 0 to 59"));
if(min < 0 || min > 59) mp_raise_ValueError(MP_ERROR_TEXT("min out of range. Expected 0 to 59"));
if(hour < 0 || hour > 23) mp_raise_ValueError(MP_ERROR_TEXT("hour out of range. Expected 0 to 23"));
if(weekday < 0 || weekday > 6) mp_raise_ValueError(MP_ERROR_TEXT("weekday out of range. Expected 0 to 6"));
if(date < 1 || date > 31) mp_raise_ValueError(MP_ERROR_TEXT("date out of range. Expected 1 to 31"));
if(month < 1 || month > 12) mp_raise_ValueError(MP_ERROR_TEXT("month out of range. Expected 1 to 12"));
if(year < 2000 || year > 2099) mp_raise_ValueError(MP_ERROR_TEXT("year out of range. Expected 2000 to 2099"));
return mp_obj_new_bool(self->breakout->set_time(sec, min, hour, weekday, date, month, year));
}
@ -103,7 +102,7 @@ mp_obj_t BreakoutRTC_set_seconds(mp_obj_t self_in, mp_obj_t sec_in) {
int sec = mp_obj_get_int(sec_in);
if(sec < 0 || sec > 59) mp_raise_ValueError("sec out of range. Expected 0 to 59");
if(sec < 0 || sec > 59) mp_raise_ValueError(MP_ERROR_TEXT("sec out of range. Expected 0 to 59"));
return mp_obj_new_bool(self->breakout->set_seconds(sec));
}
@ -113,7 +112,7 @@ mp_obj_t BreakoutRTC_set_minutes(mp_obj_t self_in, mp_obj_t min_in) {
int min = mp_obj_get_int(min_in);
if(min < 0 || min > 59) mp_raise_ValueError("min out of range. Expected 0 to 59");
if(min < 0 || min > 59) mp_raise_ValueError(MP_ERROR_TEXT("min out of range. Expected 0 to 59"));
return mp_obj_new_bool(self->breakout->set_minutes(min));
}
@ -123,7 +122,7 @@ mp_obj_t BreakoutRTC_set_hours(mp_obj_t self_in, mp_obj_t hour_in) {
int hour = mp_obj_get_int(hour_in);
if(hour < 0 || hour > 23) mp_raise_ValueError("hour out of range. Expected 0 to 23");
if(hour < 0 || hour > 23) mp_raise_ValueError(MP_ERROR_TEXT("hour out of range. Expected 0 to 23"));
return mp_obj_new_bool(self->breakout->set_hours(hour));
}
@ -133,7 +132,7 @@ mp_obj_t BreakoutRTC_set_weekday(mp_obj_t self_in, mp_obj_t weekday_in) {
int weekday = mp_obj_get_int(weekday_in);
if(weekday < 0 || weekday > 6) mp_raise_ValueError("weekday out of range. Expected 0 to 6");
if(weekday < 0 || weekday > 6) mp_raise_ValueError(MP_ERROR_TEXT("weekday out of range. Expected 0 to 6"));
return mp_obj_new_bool(self->breakout->set_weekday(weekday));
}
@ -143,7 +142,7 @@ mp_obj_t BreakoutRTC_set_date(mp_obj_t self_in, mp_obj_t date_in) {
int date = mp_obj_get_int(date_in);
if(date < 1 || date > 31) mp_raise_ValueError("date out of range. Expected 1 to 31");
if(date < 1 || date > 31) mp_raise_ValueError(MP_ERROR_TEXT("date out of range. Expected 1 to 31"));
return mp_obj_new_bool(self->breakout->set_date(date));
}
@ -153,7 +152,7 @@ mp_obj_t BreakoutRTC_set_month(mp_obj_t self_in, mp_obj_t month_in) {
int month = mp_obj_get_int(month_in);
if(month < 1 || month > 12) mp_raise_ValueError("month out of range. Expected 1 to 12");
if(month < 1 || month > 12) mp_raise_ValueError(MP_ERROR_TEXT("month out of range. Expected 1 to 12"));
return mp_obj_new_bool(self->breakout->set_month(month));
}
@ -163,7 +162,7 @@ mp_obj_t BreakoutRTC_set_year(mp_obj_t self_in, mp_obj_t year_in) {
int year = mp_obj_get_int(year_in);
if(year < 0 || year > 99) mp_raise_ValueError("year out of range. Expected 0 to 99");
if(year < 0 || year > 99) mp_raise_ValueError(MP_ERROR_TEXT("year out of range. Expected 0 to 99"));
return mp_obj_new_bool(self->breakout->set_year(year));
}
@ -321,17 +320,17 @@ mp_obj_t BreakoutRTC_enable_alarm_interrupt(size_t n_args, const mp_obj_t *pos_a
bool enable_clock_output = args[ARG_enable_clock_output].u_bool;
if(min < 0 || min > 59)
mp_raise_ValueError("min out of range. Expected 0 to 59");
mp_raise_ValueError(MP_ERROR_TEXT("min out of range. Expected 0 to 59"));
else if(hour < 0 || hour > 23)
mp_raise_ValueError("hour out of range. Expected 0 to 23");
mp_raise_ValueError(MP_ERROR_TEXT("hour out of range. Expected 0 to 23"));
else {
if(set_weekday_alarm_not_date) {
if(date_or_weekday < 0 || date_or_weekday > 6)
mp_raise_ValueError("date_or_weekday out of range. Expected 0 to 6");
mp_raise_ValueError(MP_ERROR_TEXT("date_or_weekday out of range. Expected 0 to 6"));
}
else {
if(date_or_weekday < 1 || date_or_weekday > 31)
mp_raise_ValueError("date_or_weekday out of range. Expected 1 to 31");
mp_raise_ValueError(MP_ERROR_TEXT("date_or_weekday out of range. Expected 1 to 31"));
}
self->breakout->enable_alarm_interrupt(min, hour, date_or_weekday, set_weekday_alarm_not_date, mode, enable_clock_output);
}
@ -384,7 +383,7 @@ mp_obj_t BreakoutRTC_set_timer(size_t n_args, const mp_obj_t *pos_args, mp_map_t
bool enable_clock_output = args[ARG_enable_clock_output].u_bool;
if(timer_value < 0 || timer_value > 4065) {
mp_raise_ValueError("timer_value out of range. Expected 0 to 4095");
mp_raise_ValueError(MP_ERROR_TEXT("timer_value out of range. Expected 0 to 4095"));
}
else {
switch(timer_frequency) {
@ -396,7 +395,7 @@ mp_obj_t BreakoutRTC_set_timer(size_t n_args, const mp_obj_t *pos_args, mp_map_t
break;
default:
mp_raise_ValueError("timer_frequency not valid. Expected, 4096, 64, 1, or 60000");
mp_raise_ValueError(MP_ERROR_TEXT("timer_frequency not valid. Expected, 4096, 64, 1, or 60000"));
break;
}
}
@ -502,7 +501,7 @@ mp_obj_t BreakoutRTC_enable_trickle_charge(size_t n_args, const mp_obj_t *pos_ar
int tcr = args[ARG_tcr].u_int;
if(tcr < 0 || tcr > 3) mp_raise_ValueError("tcr out of range. Expected 0 to 3 (TCR_3K, TCR_5K, TCR_9K, TCR_15K)");
if(tcr < 0 || tcr > 3) mp_raise_ValueError(MP_ERROR_TEXT("tcr out of range. Expected 0 to 3 (TCR_3K, TCR_5K, TCR_9K, TCR_15K)"));
self->breakout->enable_trickle_charge(tcr);
@ -521,7 +520,7 @@ mp_obj_t BreakoutRTC_set_backup_switchover_mode(mp_obj_t self_in, mp_obj_t val_i
int val = mp_obj_get_int(val_in);
if(val < 0 || val > 3) mp_raise_ValueError("tcr out of range. Expected 0 to 3");
if(val < 0 || val > 3) mp_raise_ValueError(MP_ERROR_TEXT("tcr out of range. Expected 0 to 3"));
self->breakout->set_backup_switchover_mode(val);
@ -533,7 +532,7 @@ mp_obj_t BreakoutRTC_enable_clock_out(mp_obj_t self_in, mp_obj_t freq_in) {
int freq = mp_obj_get_int(freq_in);
if(freq < 0 || freq > 7) mp_raise_ValueError("freq out of range. Expected 0 to 7");
if(freq < 0 || freq > 7) mp_raise_ValueError(MP_ERROR_TEXT("freq out of range. Expected 0 to 7"));
self->breakout->enable_clock_out(freq);
@ -545,7 +544,7 @@ mp_obj_t BreakoutRTC_enable_interrupt_controlled_clockout(mp_obj_t self_in, mp_o
int freq = mp_obj_get_int(freq_in);
if(freq < 0 || freq > 7) mp_raise_ValueError("freq out of range. Expected 0 to 7");
if(freq < 0 || freq > 7) mp_raise_ValueError(MP_ERROR_TEXT("freq out of range. Expected 0 to 7"));
self->breakout->enable_interrupt_controlled_clockout(freq);

Wyświetl plik

@ -16,10 +16,10 @@ extern "C" {
#include "breakout_scd41.h"
#include "pimoroni_i2c.h"
#define NOT_INITIALISED_MSG "SCD41: Not initialised. Call scd41.init(<i2c instance>) first."
#define READ_FAIL_MSG "SCD41: Reading failed."
#define FAIL_MSG "SCD41: Error."
#define SAMPLE_FAIL_MSG "SCD41: Read invalid sample."
#define NOT_INITIALISED_MSG MP_ERROR_TEXT("SCD41: Not initialised. Call scd41.init(<i2c instance>) first.")
#define READ_FAIL_MSG MP_ERROR_TEXT("SCD41: Reading failed.")
#define FAIL_MSG MP_ERROR_TEXT("SCD41: Error.")
#define SAMPLE_FAIL_MSG MP_ERROR_TEXT("SCD41: Read invalid sample.")
mp_obj_t scd41_init(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {

Wyświetl plik

@ -29,15 +29,14 @@ mp_obj_t BreakoutSGP30_make_new(const mp_obj_type_t *type, size_t n_args, size_t
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);
self = m_new_obj(breakout_sgp30_BreakoutSGP30_obj_t);
self->base.type = &breakout_sgp30_BreakoutSGP30_type;
self = mp_obj_malloc(breakout_sgp30_BreakoutSGP30_obj_t, &breakout_sgp30_BreakoutSGP30_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BreakoutSGP30, (pimoroni::I2C *)(self->i2c->i2c));
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "BreakoutSGP30: breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("BreakoutSGP30: breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);

Wyświetl plik

@ -32,15 +32,14 @@ mp_obj_t BreakoutTrackball_make_new(const mp_obj_type_t *type, size_t n_args, si
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);
self = m_new_obj(breakout_trackball_BreakoutTrackball_obj_t);
self->base.type = &breakout_trackball_BreakoutTrackball_type;
self = mp_obj_malloc(breakout_trackball_BreakoutTrackball_obj_t, &breakout_trackball_BreakoutTrackball_type);
self->i2c = PimoroniI2C_from_machine_i2c_or_native(args[ARG_i2c].u_obj);
self->breakout = m_new_class(BreakoutTrackball, (pimoroni::I2C *)(self->i2c->i2c), args[ARG_address].u_int, args[ARG_interrupt].u_int);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "Trackball breakout not found when initialising");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Trackball breakout not found when initialising"));
}
return MP_OBJ_FROM_PTR(self);
@ -108,13 +107,13 @@ mp_obj_t BreakoutTrackball_set_rgbw(size_t n_args, const mp_obj_t *pos_args, mp_
int w = args[ARG_w].u_int;
if(r < 0 || r > 255)
mp_raise_ValueError("r out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("r out of range. Expected 0 to 255"));
else if(g < 0 || g > 255)
mp_raise_ValueError("g out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("g out of range. Expected 0 to 255"));
else if(b < 0 || b > 255)
mp_raise_ValueError("b out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("b out of range. Expected 0 to 255"));
else if(w < 0 || w > 255)
mp_raise_ValueError("w out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("w out of range. Expected 0 to 255"));
else
self->breakout->set_rgbw(r, g, b, w);
@ -136,7 +135,7 @@ mp_obj_t BreakoutTrackball_set_red(size_t n_args, const mp_obj_t *pos_args, mp_m
int value = args[ARG_value].u_int;
if(value < 0 || value > 255)
mp_raise_ValueError("value out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("value out of range. Expected 0 to 255"));
else
self->breakout->set_red(value);
@ -158,7 +157,7 @@ mp_obj_t BreakoutTrackball_set_green(size_t n_args, const mp_obj_t *pos_args, mp
int value = args[ARG_value].u_int;
if(value < 0 || value > 255)
mp_raise_ValueError("value out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("value out of range. Expected 0 to 255"));
else
self->breakout->set_green(value);
@ -180,7 +179,7 @@ mp_obj_t BreakoutTrackball_set_blue(size_t n_args, const mp_obj_t *pos_args, mp_
int value = args[ARG_value].u_int;
if(value < 0 || value > 255)
mp_raise_ValueError("value out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("value out of range. Expected 0 to 255"));
else
self->breakout->set_blue(value);
@ -202,7 +201,7 @@ mp_obj_t BreakoutTrackball_set_white(size_t n_args, const mp_obj_t *pos_args, mp
int value = args[ARG_value].u_int;
if(value < 0 || value > 255)
mp_raise_ValueError("value out of range. Expected 0 to 255");
mp_raise_ValueError(MP_ERROR_TEXT("value out of range. Expected 0 to 255"));
else
self->breakout->set_white(value);

Wyświetl plik

@ -88,13 +88,13 @@ mp_obj_t VL53L5CX_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw
}
if(bufinfo.len != (size_t)(firmware_size)) { // firmware blob is always 84K
mp_raise_ValueError("Firmware must be 84k bytes!");
mp_raise_ValueError(MP_ERROR_TEXT("Firmware must be 84k bytes!"));
}
self->breakout = m_new_class(pimoroni::VL53L5CX, (pimoroni::I2C*)self->i2c->i2c, (uint8_t *)bufinfo.buf, addr, self->configuration, self->motion_configuration);
if(!self->breakout->init()) {
mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: init error");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("VL53L5CX: init error"));
}
if(args[ARG_firmware].u_obj == nullptr) {
@ -108,7 +108,7 @@ mp_obj_t VL53L5CX_start_ranging(mp_obj_t self_in) {
_VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t);
bool status = self->breakout->start_ranging();
if(!status) {
mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: start_ranging error");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("VL53L5CX: start_ranging error"));
}
return mp_const_none;
}
@ -117,7 +117,7 @@ mp_obj_t VL53L5CX_stop_ranging(mp_obj_t self_in) {
_VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t);
bool status = self->breakout->stop_ranging();
if(!status) {
mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: stop_ranging error");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("VL53L5CX: stop_ranging error"));
}
return mp_const_none;
}
@ -126,7 +126,7 @@ mp_obj_t VL53L5CX_enable_motion_indicator(mp_obj_t self_in, mp_obj_t value) {
_VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t);
bool status = self->breakout->enable_motion_indicator((pimoroni::VL53L5CX::Resolution)mp_obj_get_int(value));
if(!status) {
mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: enable_motion_indicator error");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("VL53L5CX: enable_motion_indicator error"));
}
return mp_const_none;
}
@ -135,7 +135,7 @@ mp_obj_t VL53L5CX_set_motion_distance(mp_obj_t self_in, mp_obj_t distance_min, m
_VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t);
bool status = self->breakout->set_motion_distance(mp_obj_get_int(distance_min), mp_obj_get_int(distance_max));
if(!status) {
mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: set_motion_distance error");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("VL53L5CX: set_motion_distance error"));
}
return mp_const_none;
}
@ -144,7 +144,7 @@ mp_obj_t VL53L5CX_set_i2c_address(mp_obj_t self_in, mp_obj_t value) {
_VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t);
bool status = self->breakout->set_i2c_address(mp_obj_get_int(value));
if(!status) {
mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: set_i2c_address error");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("VL53L5CX: set_i2c_address error"));
}
return mp_const_none;
}
@ -153,7 +153,7 @@ mp_obj_t VL53L5CX_set_ranging_mode(mp_obj_t self_in, mp_obj_t value) {
_VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t);
bool status = self->breakout->set_ranging_mode((pimoroni::VL53L5CX::RangingMode)mp_obj_get_int(value));
if(!status) {
mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: set_ranging_mode error");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("VL53L5CX: set_ranging_mode error"));
}
return mp_const_none;
}
@ -162,7 +162,7 @@ mp_obj_t VL53L5CX_set_ranging_frequency_hz(mp_obj_t self_in, mp_obj_t value) {
_VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t);
bool status = self->breakout->set_ranging_frequency_hz(mp_obj_get_int(value));
if(!status) {
mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: set_ranging_frequency_hz error");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("VL53L5CX: set_ranging_frequency_hz error"));
}
return mp_const_none;
}
@ -171,7 +171,7 @@ mp_obj_t VL53L5CX_set_resolution(mp_obj_t self_in, mp_obj_t value) {
_VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t);
bool status = self->breakout->set_resolution((pimoroni::VL53L5CX::Resolution)mp_obj_get_int(value));
if(!status) {
mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: set_resolution error");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("VL53L5CX: set_resolution error"));
}
return mp_const_none;
}
@ -180,7 +180,7 @@ mp_obj_t VL53L5CX_set_integration_time_ms(mp_obj_t self_in, mp_obj_t value) {
_VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t);
bool status = self->breakout->set_integration_time_ms(mp_obj_get_int(value));
if(!status) {
mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: set_integration_time_ms error");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("VL53L5CX: set_integration_time_ms error"));
}
return mp_const_none;
}
@ -189,7 +189,7 @@ mp_obj_t VL53L5CX_set_sharpener_percent(mp_obj_t self_in, mp_obj_t value) {
_VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t);
bool status = self->breakout->set_sharpener_percent(mp_obj_get_int(value));
if(!status) {
mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: set_sharpener_percent error");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("VL53L5CX: set_sharpener_percent error"));
}
return mp_const_none;
}
@ -198,7 +198,7 @@ mp_obj_t VL53L5CX_set_target_order(mp_obj_t self_in, mp_obj_t value) {
_VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t);
bool status = self->breakout->set_target_order((pimoroni::VL53L5CX::TargetOrder)mp_obj_get_int(value));
if(!status) {
mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: set_target_order error");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("VL53L5CX: set_target_order error"));
}
return mp_const_none;
}
@ -207,7 +207,7 @@ mp_obj_t VL53L5CX_set_power_mode(mp_obj_t self_in, mp_obj_t value) {
_VL53L5CX_obj_t *self = MP_OBJ_TO_PTR2(self_in, _VL53L5CX_obj_t);
bool status = self->breakout->set_power_mode((pimoroni::VL53L5CX::PowerMode)mp_obj_get_int(value));
if(!status) {
mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: set_power_mode error");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("VL53L5CX: set_power_mode error"));
}
return mp_const_none;
}
@ -222,7 +222,7 @@ mp_obj_t VL53L5CX_get_data(mp_obj_t self_in) {
pimoroni::VL53L5CX::ResultsData *results = (pimoroni::VL53L5CX::ResultsData *)self->results;
bool status = self->breakout->get_data(results);
if(!status) {
mp_raise_msg(&mp_type_RuntimeError, "VL53L5CX: get_data error");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("VL53L5CX: get_data error"));
}
// Get the current resolution so we only look at valid results.

Wyświetl plik

@ -34,7 +34,7 @@ void Channel_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t ki
/***** Constructor *****/
mp_obj_t Channel_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
mp_raise_msg(&mp_type_RuntimeError, "Cannot create Channel objects. They can only be accessed from CosmicUnicorn.synth_channel()");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Cannot create Channel objects. They can only be accessed from CosmicUnicorn.synth_channel()"));
return mp_const_none;
}
@ -50,7 +50,7 @@ void set_channel_waveforms(AudioChannel& channel, mp_obj_t in) {
int waveforms = mp_obj_get_int(in);
const int mask = (NOISE | SQUARE | SAW | TRIANGLE | SINE | WAVE);
if(waveforms < 0 || (waveforms & mask) == 0) {
mp_raise_ValueError("waveforms invalid. Expected a combination of NOISE, SQUARE, SAW, TRIANGLE, SINE, or WAVE");
mp_raise_ValueError(MP_ERROR_TEXT("waveforms invalid. Expected a combination of NOISE, SQUARE, SAW, TRIANGLE, SINE, or WAVE"));
}
channel.waveforms = (uint8_t)waveforms;
}
@ -58,7 +58,7 @@ void set_channel_waveforms(AudioChannel& channel, mp_obj_t in) {
void set_channel_frequency(AudioChannel& channel, mp_obj_t in) {
int freq = mp_obj_get_int(in);
if(freq <= 0 || freq > UINT16_MAX) {
mp_raise_ValueError("frequency out of range. Expected greater than 0Hz to 65535Hz");
mp_raise_ValueError(MP_ERROR_TEXT("frequency out of range. Expected greater than 0Hz to 65535Hz"));
}
channel.frequency = (uint16_t)freq;
}
@ -66,7 +66,7 @@ void set_channel_frequency(AudioChannel& channel, mp_obj_t in) {
void set_channel_volume(AudioChannel& channel, mp_obj_t in) {
float volume = mp_obj_get_float(in);
if(volume < 0.0f || volume > 1.0f) {
mp_raise_ValueError("volume out of range. Expected 0.0 to 1.0");
mp_raise_ValueError(MP_ERROR_TEXT("volume out of range. Expected 0.0 to 1.0"));
}
channel.volume = (uint16_t)(volume * UINT16_MAX);
}
@ -74,7 +74,7 @@ void set_channel_volume(AudioChannel& channel, mp_obj_t in) {
void set_channel_attack(AudioChannel& channel, mp_obj_t in) {
int attack_ms = (int)(mp_obj_get_float(in) * 1000.0f);
if(attack_ms < 0 || attack_ms > UINT16_MAX) {
mp_raise_ValueError("attack out of range. Expected 0.0s to 65.5s");
mp_raise_ValueError(MP_ERROR_TEXT("attack out of range. Expected 0.0s to 65.5s"));
}
channel.attack_ms = MAX(attack_ms, 1);
}
@ -82,7 +82,7 @@ void set_channel_attack(AudioChannel& channel, mp_obj_t in) {
void set_channel_decay(AudioChannel& channel, mp_obj_t in) {
int decay_ms = (int)(mp_obj_get_float(in) * 1000.0f);
if(decay_ms < 0 || decay_ms > UINT16_MAX) {
mp_raise_ValueError("decay out of range. Expected 0.0s to 65.5s");
mp_raise_ValueError(MP_ERROR_TEXT("decay out of range. Expected 0.0s to 65.5s"));
}
channel.decay_ms = MAX(decay_ms, 1);
}
@ -90,7 +90,7 @@ void set_channel_decay(AudioChannel& channel, mp_obj_t in) {
void set_channel_sustain(AudioChannel& channel, mp_obj_t in) {
float sustain = mp_obj_get_float(in);
if(sustain < 0.0f || sustain > 1.0f) {
mp_raise_ValueError("sustain out of range. Expected 0.0 to 1.0");
mp_raise_ValueError(MP_ERROR_TEXT("sustain out of range. Expected 0.0 to 1.0"));
}
channel.sustain = (uint16_t)(sustain * UINT16_MAX);
}
@ -98,7 +98,7 @@ void set_channel_sustain(AudioChannel& channel, mp_obj_t in) {
void set_channel_release(AudioChannel& channel, mp_obj_t in) {
int release_ms = (int)(mp_obj_get_float(in) * 1000.0f);
if(release_ms < 0 || release_ms > UINT16_MAX) {
mp_raise_ValueError("release out of range. Expected 0.0s to 65.5s");
mp_raise_ValueError(MP_ERROR_TEXT("release out of range. Expected 0.0s to 65.5s"));
}
channel.release_ms = MAX(release_ms, 1);
}
@ -106,7 +106,7 @@ void set_channel_release(AudioChannel& channel, mp_obj_t in) {
void set_channel_pulse_width(AudioChannel& channel, mp_obj_t in) {
float pulse_width = mp_obj_get_float(in);
if(pulse_width < 0.0f || pulse_width > 1.0f) {
mp_raise_ValueError("pulse_width out of range. Expected 0.0 to 1.0");
mp_raise_ValueError(MP_ERROR_TEXT("pulse_width out of range. Expected 0.0 to 1.0"));
}
channel.pulse_width = (uint16_t)(pulse_width * UINT16_MAX);
}
@ -378,13 +378,13 @@ mp_obj_t CosmicUnicorn_make_new(const mp_obj_type_t *type, size_t n_args, size_t
int pio_int = args[ARG_pio].u_int;
if(pio_int < 0 || pio_int > (int)NUM_PIOS) {
mp_raise_ValueError("pio out of range. Expected 0 to 1");
mp_raise_ValueError(MP_ERROR_TEXT("pio out of range. Expected 0 to 1"));
}
//PIO pio = pio_int == 0 ? pio0 : pio1;
int sm = args[ARG_sm].u_int;
if(sm < 0 || sm > (int)NUM_PIO_STATE_MACHINES) {
mp_raise_ValueError("sm out of range. Expected 0 to 3");
mp_raise_ValueError(MP_ERROR_TEXT("sm out of range. Expected 0 to 3"));
}
@ -473,7 +473,7 @@ extern mp_obj_t CosmicUnicorn_play_sample(mp_obj_t self_in, mp_obj_t data) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_RW);
if(bufinfo.len < 1) {
mp_raise_ValueError("Supplied buffer is too small!");
mp_raise_ValueError(MP_ERROR_TEXT("Supplied buffer is too small!"));
}
self->cosmic->play_sample((uint8_t *)bufinfo.buf, bufinfo.len);

Wyświetl plik

@ -21,7 +21,6 @@ static allocator_mode mode = FIXED_HEAP;
static constexpr size_t cpp_heap_size = CPP_FIXED_HEAP_SIZE / 4;
static uint32_t cpp_heap[cpp_heap_size];
static uint32_t ptr = 0;
static char cpp_err_buf[128] = {0};
extern "C" {
#include "cppmem.h"
@ -69,8 +68,7 @@ void* stat_new(std::size_t n) {
}
std::size_t s = alloc_size(n);
if(ptr + s > cpp_heap_size) {
snprintf(cpp_err_buf, sizeof(cpp_err_buf), "Failed to allocate %d bytes.", s * 4);
mp_raise_msg(&mp_type_RuntimeError, cpp_err_buf);
mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("Failed to allocate %d bytes."), s * 4);
return nullptr;
}
alloc_bytes += s * 4;

Wyświetl plik

@ -73,13 +73,13 @@ mp_obj_t Encoder_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
int pio_int = args[ARG_pio].u_int;
if(pio_int < 0 || pio_int > (int)NUM_PIOS) {
mp_raise_ValueError("pio out of range. Expected 0 to 1");
mp_raise_ValueError(MP_ERROR_TEXT("pio out of range. Expected 0 to 1"));
}
PIO pio = pio_int == 0 ? pio0 : pio1;
int sm = args[ARG_sm].u_int;
if(sm < 0 || sm > (int)NUM_PIO_STATE_MACHINES) {
mp_raise_ValueError("sm out of range. Expected 0 to 3");
mp_raise_ValueError(MP_ERROR_TEXT("sm out of range. Expected 0 to 3"));
}
size_t pin_count = 0;
@ -100,18 +100,18 @@ mp_obj_t Encoder_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
}
if(items == nullptr)
mp_raise_TypeError("cannot convert object to a list or tuple of pins");
mp_raise_TypeError(MP_ERROR_TEXT("cannot convert object to a list or tuple of pins"));
else if(pin_count != 2)
mp_raise_TypeError("list or tuple must only contain two integers");
mp_raise_TypeError(MP_ERROR_TEXT("list or tuple must only contain two integers"));
else {
int a = mp_obj_get_int(items[0]);
int b = mp_obj_get_int(items[1]);
if((a < 0 || a >= (int)NUM_BANK0_GPIOS) ||
(b < 0 || b >= (int)NUM_BANK0_GPIOS)) {
mp_raise_ValueError("a pin in the list or tuple is out of range. Expected 0 to 29");
mp_raise_ValueError(MP_ERROR_TEXT("a pin in the list or tuple is out of range. Expected 0 to 29"));
}
else if(a == b) {
mp_raise_ValueError("cannot use the same pin for encoder A and B");
mp_raise_ValueError(MP_ERROR_TEXT("cannot use the same pin for encoder A and B"));
}
pins.a = (uint8_t)a;
@ -120,14 +120,14 @@ mp_obj_t Encoder_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
int direction = args[ARG_direction].u_int;
if(direction < 0 || direction > 1) {
mp_raise_ValueError("direction out of range. Expected NORMAL (0) or REVERSED_DIR (1)");
mp_raise_ValueError(MP_ERROR_TEXT("direction out of range. Expected NORMAL (0) or REVERSED_DIR (1)"));
}
float counts_per_rev = Encoder::DEFAULT_COUNTS_PER_REV;
if(args[ARG_counts_per_rev].u_obj != mp_const_none) {
counts_per_rev = mp_obj_get_float(args[ARG_counts_per_rev].u_obj);
if(counts_per_rev < FLT_EPSILON) {
mp_raise_ValueError("counts_per_rev out of range. Expected greater than 0.0");
mp_raise_ValueError(MP_ERROR_TEXT("counts_per_rev out of range. Expected greater than 0.0"));
}
}
@ -141,7 +141,7 @@ mp_obj_t Encoder_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
Encoder *encoder = m_new_class(Encoder, pio, sm, pins, args[ARG_common_pin].u_int, (Direction)direction, counts_per_rev, count_microsteps, freq_divider);
if(!encoder->init()) {
m_del_class(Encoder, encoder);
mp_raise_msg(&mp_type_RuntimeError, "unable to allocate the hardware resources needed to initialise this Encoder. Try running `import gc` followed by `gc.collect()` before creating it");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("unable to allocate the hardware resources needed to initialise this Encoder. Try running `import gc` followed by `gc.collect()` before creating it"));
}
self = mp_obj_malloc_with_finaliser(_Encoder_obj_t, &Encoder_type);
@ -245,7 +245,7 @@ extern mp_obj_t Encoder_direction(size_t n_args, const mp_obj_t *pos_args, mp_ma
else {
int direction = mp_obj_get_int(args[ARG_direction].u_obj);
if(direction < 0 || direction > 1) {
mp_raise_ValueError("direction out of range. Expected NORMAL (0) or REVERSED_DIR (1)");
mp_raise_ValueError(MP_ERROR_TEXT("direction out of range. Expected NORMAL (0) or REVERSED_DIR (1)"));
}
self->encoder->direction((Direction)direction);
return mp_const_none;
@ -271,7 +271,7 @@ extern mp_obj_t Encoder_counts_per_rev(size_t n_args, const mp_obj_t *pos_args,
else {
float counts_per_rev = mp_obj_get_float(args[ARG_counts_per_rev].u_obj);
if(counts_per_rev < FLT_EPSILON) {
mp_raise_ValueError("counts_per_rev out of range. Expected greater than 0.0");
mp_raise_ValueError(MP_ERROR_TEXT("counts_per_rev out of range. Expected greater than 0.0"));
}
self->encoder->counts_per_rev(counts_per_rev);
return mp_const_none;

Wyświetl plik

@ -34,7 +34,7 @@ void Channel_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t ki
/***** Constructor *****/
mp_obj_t Channel_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
mp_raise_msg(&mp_type_RuntimeError, "Cannot create Channel objects. They can only be accessed from GalacticUnicorn.synth_channel()");
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("Cannot create Channel objects. They can only be accessed from GalacticUnicorn.synth_channel()"));
return mp_const_none;
}
@ -50,7 +50,7 @@ void set_channel_waveforms(AudioChannel& channel, mp_obj_t in) {
int waveforms = mp_obj_get_int(in);
const int mask = (NOISE | SQUARE | SAW | TRIANGLE | SINE | WAVE);
if(waveforms < 0 || (waveforms & mask) == 0) {
mp_raise_ValueError("waveforms invalid. Expected a combination of NOISE, SQUARE, SAW, TRIANGLE, SINE, or WAVE");
mp_raise_ValueError(MP_ERROR_TEXT("waveforms invalid. Expected a combination of NOISE, SQUARE, SAW, TRIANGLE, SINE, or WAVE"));
}
channel.waveforms = (uint8_t)waveforms;
}
@ -58,7 +58,7 @@ void set_channel_waveforms(AudioChannel& channel, mp_obj_t in) {
void set_channel_frequency(AudioChannel& channel, mp_obj_t in) {
int freq = mp_obj_get_int(in);
if(freq <= 0 || freq > UINT16_MAX) {
mp_raise_ValueError("frequency out of range. Expected greater than 0Hz to 65535Hz");
mp_raise_ValueError(MP_ERROR_TEXT("frequency out of range. Expected greater than 0Hz to 65535Hz"));
}
channel.frequency = (uint16_t)freq;
}
@ -66,7 +66,7 @@ void set_channel_frequency(AudioChannel& channel, mp_obj_t in) {
void set_channel_volume(AudioChannel& channel, mp_obj_t in) {
float volume = mp_obj_get_float(in);
if(volume < 0.0f || volume > 1.0f) {
mp_raise_ValueError("volume out of range. Expected 0.0 to 1.0");
mp_raise_ValueError(MP_ERROR_TEXT("volume out of range. Expected 0.0 to 1.0"));
}
channel.volume = (uint16_t)(volume * UINT16_MAX);
}
@ -74,7 +74,7 @@ void set_channel_volume(AudioChannel& channel, mp_obj_t in) {
void set_channel_attack(AudioChannel& channel, mp_obj_t in) {
int attack_ms = (int)(mp_obj_get_float(in) * 1000.0f);
if(attack_ms < 0 || attack_ms > UINT16_MAX) {
mp_raise_ValueError("attack out of range. Expected 0.0s to 65.5s");
mp_raise_ValueError(MP_ERROR_TEXT("attack out of range. Expected 0.0s to 65.5s"));
}
channel.attack_ms = MAX(attack_ms, 1);
}
@ -82,7 +82,7 @@ void set_channel_attack(AudioChannel& channel, mp_obj_t in) {
void set_channel_decay(AudioChannel& channel, mp_obj_t in) {
int decay_ms = (int)(mp_obj_get_float(in) * 1000.0f);
if(decay_ms < 0 || decay_ms > UINT16_MAX) {
mp_raise_ValueError("decay out of range. Expected 0.0s to 65.5s");
mp_raise_ValueError(MP_ERROR_TEXT("decay out of range. Expected 0.0s to 65.5s"));
}
channel.decay_ms = MAX(decay_ms, 1);
}
@ -90,7 +90,7 @@ void set_channel_decay(AudioChannel& channel, mp_obj_t in) {
void set_channel_sustain(AudioChannel& channel, mp_obj_t in) {
float sustain = mp_obj_get_float(in);
if(sustain < 0.0f || sustain > 1.0f) {
mp_raise_ValueError("sustain out of range. Expected 0.0 to 1.0");
mp_raise_ValueError(MP_ERROR_TEXT("sustain out of range. Expected 0.0 to 1.0"));
}
channel.sustain = (uint16_t)(sustain * UINT16_MAX);
}
@ -98,7 +98,7 @@ void set_channel_sustain(AudioChannel& channel, mp_obj_t in) {
void set_channel_release(AudioChannel& channel, mp_obj_t in) {
int release_ms = (int)(mp_obj_get_float(in) * 1000.0f);
if(release_ms < 0 || release_ms > UINT16_MAX) {
mp_raise_ValueError("release out of range. Expected 0.0s to 65.5s");
mp_raise_ValueError(MP_ERROR_TEXT("release out of range. Expected 0.0s to 65.5s"));
}
channel.release_ms = MAX(release_ms, 1);
}
@ -106,7 +106,7 @@ void set_channel_release(AudioChannel& channel, mp_obj_t in) {
void set_channel_pulse_width(AudioChannel& channel, mp_obj_t in) {
float pulse_width = mp_obj_get_float(in);
if(pulse_width < 0.0f || pulse_width > 1.0f) {
mp_raise_ValueError("pulse_width out of range. Expected 0.0 to 1.0");
mp_raise_ValueError(MP_ERROR_TEXT("pulse_width out of range. Expected 0.0 to 1.0"));
}
channel.pulse_width = (uint16_t)(pulse_width * UINT16_MAX);
}
@ -378,13 +378,13 @@ mp_obj_t GalacticUnicorn_make_new(const mp_obj_type_t *type, size_t n_args, size
int pio_int = args[ARG_pio].u_int;
if(pio_int < 0 || pio_int > (int)NUM_PIOS) {
mp_raise_ValueError("pio out of range. Expected 0 to 1");
mp_raise_ValueError(MP_ERROR_TEXT("pio out of range. Expected 0 to 1"));
}
//PIO pio = pio_int == 0 ? pio0 : pio1;
int sm = args[ARG_sm].u_int;
if(sm < 0 || sm > (int)NUM_PIO_STATE_MACHINES) {
mp_raise_ValueError("sm out of range. Expected 0 to 3");
mp_raise_ValueError(MP_ERROR_TEXT("sm out of range. Expected 0 to 3"));
}
@ -473,7 +473,7 @@ extern mp_obj_t GalacticUnicorn_play_sample(mp_obj_t self_in, mp_obj_t data) {
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(data, &bufinfo, MP_BUFFER_RW);
if(bufinfo.len < 1) {
mp_raise_ValueError("Supplied buffer is too small!");
mp_raise_ValueError(MP_ERROR_TEXT("Supplied buffer is too small!"));
}
self->galactic->play_sample((uint8_t *)bufinfo.buf, bufinfo.len);

Wyświetl plik

@ -12,4 +12,10 @@ target_include_directories(usermod_${MOD_NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/../../../libraries/hershey_fonts
)
# Hershey has all but been replaced by PicoVector's "alright fonts."
# This definition ensures that PicoGraphics compiles in Hershey support.
target_compile_definitions(usermod_${MOD_NAME} INTERFACE
HERSHEY_FONTS=1
)
target_link_libraries(usermod INTERFACE usermod_${MOD_NAME})

Wyświetl plik

@ -114,7 +114,7 @@ mp_obj_t Hub75_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, c
mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_RW);
buffer = (Pixel *)bufinfo.buf;
if(bufinfo.len < (size_t)(width * height * sizeof(Pixel))) {
mp_raise_ValueError("Supplied buffer is too small!");
mp_raise_ValueError(MP_ERROR_TEXT("Supplied buffer is too small!"));
}
} else {
buffer = m_new(Pixel, width * height);

Wyświetl plik

@ -1,6 +1,5 @@
#include "jpegdec.h"
static MP_DEFINE_CONST_FUN_OBJ_1(JPEG_del_obj, _JPEG_del);
static MP_DEFINE_CONST_FUN_OBJ_2(JPEG_openRAM_obj, _JPEG_openRAM);
static MP_DEFINE_CONST_FUN_OBJ_2(JPEG_openFILE_obj, _JPEG_openFILE);
static MP_DEFINE_CONST_FUN_OBJ_KW(JPEG_decode_obj, 1, _JPEG_decode);
@ -9,7 +8,6 @@ static MP_DEFINE_CONST_FUN_OBJ_1(JPEG_getHeight_obj, _JPEG_getHeight);
// class
static const mp_rom_map_elem_t JPEG_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&JPEG_del_obj) },
{ MP_ROM_QSTR(MP_QSTR_open_RAM), MP_ROM_PTR(&JPEG_openRAM_obj) },
{ MP_ROM_QSTR(MP_QSTR_open_file), MP_ROM_PTR(&JPEG_openFILE_obj) },
{ MP_ROM_QSTR(MP_QSTR_decode), MP_ROM_PTR(&JPEG_decode_obj) },

Wyświetl plik

@ -43,7 +43,7 @@ void *jpegdec_open_callback(const char *filename, int32_t *size) {
mp_obj_t args[2] = {
fn,
MP_OBJ_NEW_QSTR(MP_QSTR_r),
MP_ROM_QSTR(MP_QSTR_r),
};
// Stat the file to get its size
@ -79,7 +79,7 @@ int32_t jpegdec_seek_callback(JPEGFILE *jpeg, int32_t p) {
int error;
mp_uint_t res = stream_p->ioctl(fhandle, MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, &error);
if (res == MP_STREAM_ERROR) {
mp_raise_OSError(error);
mp_raise_msg_varg(&mp_type_OSError, MP_ERROR_TEXT("JPEG: seek failed with %d"), error);
}
return seek_s.offset;
@ -197,7 +197,7 @@ void jpegdec_open_helper(_JPEG_obj_t *self) {
result = self->jpeg->openRAM((uint8_t *)self->buf.buf, self->buf.len, JPEGDraw);
}
if(result != 1) mp_raise_msg(&mp_type_RuntimeError, "JPEG: could not read file/buffer.");
if(result != 1) mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("JPEG: could not read file/buffer."));
}
mp_obj_t _JPEG_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
@ -212,19 +212,13 @@ mp_obj_t _JPEG_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, c
if(!MP_OBJ_IS_TYPE(args[ARG_picographics].u_obj, &ModPicoGraphics_type)) mp_raise_ValueError(MP_ERROR_TEXT("PicoGraphics Object Required"));
_JPEG_obj_t *self = mp_obj_malloc_with_finaliser(_JPEG_obj_t, &JPEG_type);
_JPEG_obj_t *self = mp_obj_malloc(_JPEG_obj_t, &JPEG_type);
self->jpeg = m_new_class(JPEGDEC);
self->graphics = (ModPicoGraphics_obj_t *)MP_OBJ_TO_PTR(args[ARG_picographics].u_obj);
return self;
}
mp_obj_t _JPEG_del(mp_obj_t self_in) {
_JPEG_obj_t *self = MP_OBJ_TO_PTR2(self_in, _JPEG_obj_t);
self->jpeg->close();
return mp_const_none;
}
// open_FILE
mp_obj_t _JPEG_openFILE(mp_obj_t self_in, mp_obj_t filename) {
_JPEG_obj_t *self = MP_OBJ_TO_PTR2(self_in, _JPEG_obj_t);

Some files were not shown because too many files have changed in this diff Show More