kopia lustrzana https://github.com/pimoroni/pimoroni-pico
Merge pull request #1019 from pimoroni/feature/picovector2-and-layers
PicoVector Enhancements & PicoGraphics Layerspull/1078/head v1.25.0
commit
21ec6a23ba
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -0,0 +1 @@
|
|||
include(bme69x.cmake)
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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, ®_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
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
include("${CMAKE_CURRENT_LIST_DIR}/bme690_forced.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/bme690_parallel.cmake")
|
|
@ -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})
|
|
@ -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;
|
||||
}
|
|
@ -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})
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
|
@ -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)
|
|
@ -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)
|
|
@ -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.
|
@ -0,0 +1 @@
|
|||
include(pico_w_explorer_vector.cmake)
|
|
@ -0,0 +1,10 @@
|
|||
add_executable(
|
||||
pico_w_explorer_vector
|
||||
pico_w_explorer_vector.cpp
|
||||
)
|
||||
|
||||
# Pull in pico libraries that we need
|
||||
target_link_libraries(pico_w_explorer_vector pico_stdlib pico_graphics pico_vector st7789)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(pico_w_explorer_vector)
|
|
@ -0,0 +1,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;
|
||||
}
|
|
@ -45,3 +45,4 @@ add_subdirectory(gfx_pack)
|
|||
add_subdirectory(interstate75)
|
||||
add_subdirectory(cosmic_unicorn)
|
||||
add_subdirectory(stellar_unicorn)
|
||||
add_subdirectory(pico_vector)
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)]);
|
||||
|
|
|
@ -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)]);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
include(pico_vector.cmake)
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
#include <string_view>
|
||||
#include <cstdint>
|
||||
|
||||
class file_io {
|
||||
private:
|
||||
void *state;
|
||||
size_t filesize = 0;
|
||||
|
||||
public:
|
||||
file_io(std::string_view path);
|
||||
~file_io();
|
||||
size_t seek(size_t pos);
|
||||
size_t read(void *buf, size_t len);
|
||||
size_t tell();
|
||||
bool fail();
|
||||
};
|
|
@ -1,9 +1,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"
|
||||
)
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -1,339 +0,0 @@
|
|||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#include "pretty_poly.hpp"
|
||||
|
||||
#include "hardware/interp.h"
|
||||
|
||||
#ifdef PP_DEBUG
|
||||
#define debug(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define debug(...)
|
||||
#endif
|
||||
|
||||
namespace pretty_poly {
|
||||
|
||||
uint8_t *tile_buffer;
|
||||
|
||||
int (*nodes)[32];
|
||||
unsigned *node_counts;
|
||||
|
||||
// default tile bounds to X1 antialiasing
|
||||
rect_t tile_bounds(0, 0, tile_buffer_size / node_buffer_size, node_buffer_size);
|
||||
|
||||
// user settings
|
||||
namespace settings {
|
||||
rect_t clip(0, 0, 320, 240);
|
||||
tile_callback_t callback;
|
||||
antialias_t antialias = antialias_t::NONE;
|
||||
}
|
||||
|
||||
void init(void *memory) {
|
||||
uintptr_t m = (uintptr_t)memory;
|
||||
tile_buffer = new(memory) uint8_t[tile_buffer_size];
|
||||
node_counts = new((void *)(m + tile_buffer_size)) unsigned[node_buffer_size];
|
||||
nodes = new((void *)(m + tile_buffer_size + (node_buffer_size * sizeof(unsigned)))) int[node_buffer_size][32];
|
||||
}
|
||||
|
||||
void set_options(tile_callback_t callback, antialias_t antialias, rect_t clip) {
|
||||
settings::callback = callback;
|
||||
settings::antialias = antialias;
|
||||
settings::clip = clip;
|
||||
|
||||
// recalculate the tile size for rendering based on antialiasing level
|
||||
int tile_height = node_buffer_size >> antialias;
|
||||
tile_bounds = rect_t(0, 0, tile_buffer_size / tile_height, tile_height);
|
||||
}
|
||||
|
||||
// dy step (returns 1, 0, or -1 if the supplied value is > 0, == 0, < 0)
|
||||
inline constexpr int sign(int v) {
|
||||
// assumes 32-bit int/unsigned
|
||||
return ((unsigned)-v >> 31) - ((unsigned)v >> 31);
|
||||
}
|
||||
|
||||
// write out the tile bits
|
||||
void debug_tile(const tile_t &tile) {
|
||||
debug(" - tile %d, %d (%d x %d)\n", tile.bounds.x, tile.bounds.y, tile.bounds.w, tile.bounds.h);
|
||||
for(auto y = 0; y < tile.bounds.h; y++) {
|
||||
debug("[%3d]: ", y);
|
||||
for(auto x = 0; x < tile.bounds.w; x++) {
|
||||
debug("%d", tile.get_value(x, y));
|
||||
}
|
||||
debug("\n");
|
||||
}
|
||||
debug("-----------------------\n");
|
||||
}
|
||||
|
||||
void add_line_segment_to_nodes(const point_t<int> &start, const point_t<int> &end) {
|
||||
// swap endpoints if line "pointing up", we do this because we
|
||||
// alway skip the last scanline (so that polygons can but cleanly
|
||||
// up against each other without overlap)
|
||||
int sx = start.x, sy = start.y, ex = end.x, ey = end.y;
|
||||
|
||||
if(ey < sy) {
|
||||
std::swap(sy, ey);
|
||||
std::swap(sx, ex);
|
||||
}
|
||||
|
||||
// Early out if line is completely outside the tile, or has no lines
|
||||
if (ey < 0 || sy >= (int)node_buffer_size || sy == ey) return;
|
||||
|
||||
debug(" + line segment from %d, %d to %d, %d\n", sx, sy, ex, ey);
|
||||
|
||||
// Determine how many in-bounds lines to render
|
||||
int y = std::max(0, sy);
|
||||
int count = std::min((int)node_buffer_size, ey) - y;
|
||||
|
||||
// Handle cases where x is completely off to one side or other
|
||||
if (std::max(sx, ex) <= 0) {
|
||||
while (count--) {
|
||||
nodes[y][node_counts[y]++] = 0;
|
||||
++y;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const int full_tile_width = (tile_bounds.w << settings::antialias);
|
||||
if (std::min(sx, ex) >= full_tile_width) {
|
||||
while (count--) {
|
||||
nodes[y][node_counts[y]++] = full_tile_width;
|
||||
++y;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal case
|
||||
int x = sx;
|
||||
int e = 0;
|
||||
|
||||
const int xinc = sign(ex - sx);
|
||||
const int einc = abs(ex - sx) + 1;
|
||||
const int dy = ey - sy;
|
||||
|
||||
// If sy < 0 jump to the start, note this does use a divide
|
||||
// but potentially saves many wasted loops below, so is likely worth it.
|
||||
if (sy < 0) {
|
||||
e = einc * -sy;
|
||||
int xjump = e / dy;
|
||||
e -= dy * xjump;
|
||||
x += xinc * xjump;
|
||||
}
|
||||
|
||||
interp1->base[1] = full_tile_width;
|
||||
interp1->accum[0] = x;
|
||||
|
||||
// loop over scanlines
|
||||
while(count--) {
|
||||
// consume accumulated error
|
||||
while(e > dy) {e -= dy; interp1->add_raw[0] = xinc;}
|
||||
|
||||
// clamp node x value to tile bounds
|
||||
const int nx = interp1->peek[0];
|
||||
debug(" + adding node at %d, %d\n", x, y);
|
||||
// add node to node list
|
||||
nodes[y][node_counts[y]++] = nx;
|
||||
|
||||
// step to next scanline and accumulate error
|
||||
y++;
|
||||
e += einc;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void build_nodes(const contour_t<T> &contour, const tile_t &tile, point_t<int> origin, int scale) {
|
||||
int ox = (origin.x - tile.bounds.x) << settings::antialias;
|
||||
int oy = (origin.y - tile.bounds.y) << settings::antialias;
|
||||
|
||||
// start with the last point to close the loop
|
||||
point_t<int> last(
|
||||
(((int(contour.points[contour.count - 1].x) * scale) << settings::antialias) / 65536) + ox,
|
||||
(((int(contour.points[contour.count - 1].y) * scale) << settings::antialias) / 65536) + oy
|
||||
);
|
||||
|
||||
for(auto i = 0u; i < contour.count; i++) {
|
||||
point_t<int> point(
|
||||
(((int(contour.points[i].x) * scale) << settings::antialias) / 65536) + ox,
|
||||
(((int(contour.points[i].y) * scale) << settings::antialias) / 65536) + oy
|
||||
);
|
||||
|
||||
add_line_segment_to_nodes(last, point);
|
||||
|
||||
last = point;
|
||||
}
|
||||
}
|
||||
|
||||
void render_nodes(const tile_t &tile, rect_t &bounds) {
|
||||
int maxy = -1;
|
||||
bounds.y = 0;
|
||||
bounds.x = tile.bounds.w;
|
||||
int maxx = 0;
|
||||
int anitialias_mask = (1 << settings::antialias) - 1;
|
||||
|
||||
for(auto y = 0; y < (int)node_buffer_size; y++) {
|
||||
if(node_counts[y] == 0) {
|
||||
if (y == bounds.y) ++bounds.y;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::sort(&nodes[y][0], &nodes[y][0] + node_counts[y]);
|
||||
|
||||
uint8_t* row_data = &tile.data[(y >> settings::antialias) * tile.stride];
|
||||
bool rendered_any = false;
|
||||
for(auto i = 0u; i < node_counts[y]; i += 2) {
|
||||
int sx = nodes[y][i + 0];
|
||||
int ex = nodes[y][i + 1];
|
||||
|
||||
if(sx == ex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rendered_any = true;
|
||||
|
||||
maxx = std::max((ex - 1) >> settings::antialias, maxx);
|
||||
|
||||
debug(" - render span at %d from %d to %d\n", y, sx, ex);
|
||||
|
||||
if (settings::antialias) {
|
||||
int ax = sx >> settings::antialias;
|
||||
const int aex = ex >> settings::antialias;
|
||||
|
||||
bounds.x = std::min(ax, bounds.x);
|
||||
|
||||
if (ax == aex) {
|
||||
row_data[ax] += ex - sx;
|
||||
continue;
|
||||
}
|
||||
|
||||
row_data[ax] += (1 << settings::antialias) - (sx & anitialias_mask);
|
||||
for(ax++; ax < aex; ax++) {
|
||||
row_data[ax] += (1 << settings::antialias);
|
||||
}
|
||||
|
||||
// This might add 0 to the byte after the end of the row, we pad the tile data
|
||||
// by 1 byte to ensure that is OK
|
||||
row_data[ax] += ex & anitialias_mask;
|
||||
}
|
||||
else {
|
||||
bounds.x = std::min(sx, bounds.x);
|
||||
for(int x = sx; x < ex; x++) {
|
||||
row_data[x]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rendered_any) {
|
||||
debug(" - rendered line %d\n", y);
|
||||
maxy = y;
|
||||
}
|
||||
else if (y == bounds.y) {
|
||||
debug(" - render nothing on line %d\n", y);
|
||||
++bounds.y;
|
||||
}
|
||||
}
|
||||
|
||||
bounds.y >>= settings::antialias;
|
||||
maxy >>= settings::antialias;
|
||||
bounds.w = (maxx >= bounds.x) ? maxx + 1 - bounds.x : 0;
|
||||
bounds.h = (maxy >= bounds.y) ? maxy + 1 - bounds.y : 0;
|
||||
debug(" - rendered tile bounds %d, %d (%d x %d)\n", bounds.x, bounds.y, bounds.w, bounds.h);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void draw_polygon(T *points, unsigned count) {
|
||||
std::vector<contour_t<T>> contours;
|
||||
contour_t<T> c(points, count);
|
||||
contours.push_back(c);
|
||||
draw_polygon<T>(contours);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void draw_polygon(const std::vector<contour_t<T>>& contours, point_t<int> origin, int scale) {
|
||||
|
||||
debug("> draw polygon with %lu contours\n", contours.size());
|
||||
|
||||
if(contours.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// determine extreme bounds
|
||||
rect_t polygon_bounds = contours[0].bounds();
|
||||
for(auto &contour : contours) {
|
||||
polygon_bounds = polygon_bounds.merge(contour.bounds());
|
||||
}
|
||||
|
||||
polygon_bounds.x = ((polygon_bounds.x * scale) / 65536) + origin.x;
|
||||
polygon_bounds.y = ((polygon_bounds.y * scale) / 65536) + origin.y;
|
||||
polygon_bounds.w = ((polygon_bounds.w * scale) / 65536);
|
||||
polygon_bounds.h = ((polygon_bounds.h * scale) / 65536);
|
||||
|
||||
debug(" - bounds %d, %d (%d x %d)\n", polygon_bounds.x, polygon_bounds.y, polygon_bounds.w, polygon_bounds.h);
|
||||
debug(" - clip %d, %d (%d x %d)\n", settings::clip.x, settings::clip.y, settings::clip.w, settings::clip.h);
|
||||
|
||||
interp_hw_save_t interp1_save;
|
||||
interp_save(interp1, &interp1_save);
|
||||
|
||||
interp_config cfg = interp_default_config();
|
||||
interp_config_set_clamp(&cfg, true);
|
||||
interp_config_set_signed(&cfg, true);
|
||||
interp_set_config(interp1, 0, &cfg);
|
||||
interp1->base[0] = 0;
|
||||
|
||||
//memset(nodes, 0, node_buffer_size * sizeof(unsigned) * 32);
|
||||
|
||||
// iterate over tiles
|
||||
debug(" - processing tiles\n");
|
||||
for(auto y = polygon_bounds.y; y < polygon_bounds.y + polygon_bounds.h; y += tile_bounds.h) {
|
||||
for(auto x = polygon_bounds.x; x < polygon_bounds.x + polygon_bounds.w; x += tile_bounds.w) {
|
||||
tile_t tile;
|
||||
tile.bounds = rect_t(x, y, tile_bounds.w, tile_bounds.h).intersection(settings::clip);
|
||||
tile.stride = tile_bounds.w;
|
||||
tile.data = tile_buffer;
|
||||
debug(" : %d, %d (%d x %d)\n", tile.bounds.x, tile.bounds.y, tile.bounds.w, tile.bounds.h);
|
||||
|
||||
// if no intersection then skip tile
|
||||
if(tile.bounds.empty()) {
|
||||
debug(" : empty when clipped, skipping\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// clear existing tile data and nodes
|
||||
memset(node_counts, 0, node_buffer_size * sizeof(unsigned));
|
||||
memset(tile.data, 0, tile_buffer_size);
|
||||
|
||||
// build the nodes for each contour
|
||||
for(const contour_t<T> &contour : contours) {
|
||||
debug(" : build nodes for contour\n");
|
||||
build_nodes(contour, tile, origin, scale);
|
||||
}
|
||||
|
||||
debug(" : render the tile\n");
|
||||
// render the tile
|
||||
rect_t bounds;
|
||||
render_nodes(tile, bounds);
|
||||
if (bounds.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tile.data += bounds.x + tile.stride * bounds.y;
|
||||
tile.bounds.x += bounds.x;
|
||||
tile.bounds.y += bounds.y;
|
||||
tile.bounds.w = bounds.w;
|
||||
tile.bounds.h = bounds.h;
|
||||
|
||||
settings::callback(tile);
|
||||
}
|
||||
}
|
||||
|
||||
interp_restore(interp1, &interp1_save);
|
||||
}
|
||||
}
|
||||
|
||||
template void pretty_poly::draw_polygon<int>(const std::vector<contour_t<int>>& contours, point_t<int> origin, int scale);
|
||||
template void pretty_poly::draw_polygon<float>(const std::vector<contour_t<float>>& contours, point_t<int> origin, int scale);
|
||||
template void pretty_poly::draw_polygon<uint8_t>(const std::vector<contour_t<uint8_t>>& contours, point_t<int> origin, int scale);
|
||||
template void pretty_poly::draw_polygon<int8_t>(const std::vector<contour_t<int8_t>>& contours, point_t<int> origin, int scale);
|
|
@ -1,73 +0,0 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
|
||||
#include "pretty_poly_types.hpp"
|
||||
|
||||
namespace pretty_poly {
|
||||
|
||||
class file_io {
|
||||
private:
|
||||
void *state;
|
||||
size_t filesize = 0;
|
||||
|
||||
public:
|
||||
file_io(std::string_view path);
|
||||
~file_io();
|
||||
size_t seek(size_t pos);
|
||||
size_t read(void *buf, size_t len);
|
||||
size_t tell();
|
||||
bool fail();
|
||||
};
|
||||
|
||||
// buffer that each tile is rendered into before callback
|
||||
constexpr unsigned tile_buffer_size = 1024;
|
||||
|
||||
// polygon node buffer handles at most 16 line intersections per scanline
|
||||
// is this enough for cjk/emoji? (requires a 2kB buffer)
|
||||
constexpr unsigned node_buffer_size = 32;
|
||||
|
||||
typedef std::function<void(const tile_t &tile)> tile_callback_t;
|
||||
|
||||
// user settings
|
||||
namespace settings {
|
||||
extern rect_t clip;
|
||||
extern tile_callback_t callback;
|
||||
extern antialias_t antialias;
|
||||
}
|
||||
|
||||
constexpr size_t buffer_size() {
|
||||
return tile_buffer_size + (node_buffer_size * sizeof(unsigned)) + (node_buffer_size * 32 * sizeof(int));
|
||||
}
|
||||
|
||||
constexpr size_t buffer_size();
|
||||
|
||||
void init(void *memory);
|
||||
|
||||
void set_options(tile_callback_t callback, antialias_t antialias, rect_t clip);
|
||||
|
||||
// dy step (returns 1, 0, or -1 if the supplied value is > 0, == 0, < 0)
|
||||
inline constexpr int sign(int v);
|
||||
|
||||
// write out the tile bits
|
||||
void debug_tile(const tile_t &tile);
|
||||
|
||||
void add_line_segment_to_nodes(const point_t<int> &start, const point_t<int> &end);
|
||||
|
||||
template<typename T>
|
||||
void build_nodes(const contour_t<T> &contour, const tile_t &tile, point_t<int> origin = point_t<int>(0, 0), int scale = 65536);
|
||||
|
||||
void render_nodes(const tile_t &tile, rect_t &bounds);
|
||||
|
||||
template<typename T>
|
||||
void draw_polygon(T *points, unsigned count);
|
||||
|
||||
template<typename T>
|
||||
void draw_polygon(const std::vector<contour_t<T>>& contours, point_t<int> origin = point_t<int>(0, 0), int scale = 65536);
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <math.h>
|
||||
#include <vector>
|
||||
|
||||
#ifdef PP_DEBUG
|
||||
#define debug(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define debug(...)
|
||||
#endif
|
||||
|
||||
namespace pretty_poly {
|
||||
|
||||
enum antialias_t {NONE = 0, X4 = 1, X16 = 2};
|
||||
|
||||
// 3x3 matrix for coordinate transformations
|
||||
struct mat3_t {
|
||||
float v00 = 0.0f, v10 = 0.0f, v20 = 0.0f, v01 = 0.0f, v11 = 0.0f, v21 = 0.0f, v02 = 0.0f, v12 = 0.0f, v22 = 0.0f;
|
||||
mat3_t() = default;
|
||||
mat3_t(const mat3_t &m) = default;
|
||||
inline mat3_t& operator*= (const mat3_t &m) {
|
||||
float r00 = this->v00 * m.v00 + this->v01 * m.v10 + this->v02 * m.v20;
|
||||
float r01 = this->v00 * m.v01 + this->v01 * m.v11 + this->v02 * m.v21;
|
||||
float r02 = this->v00 * m.v02 + this->v01 * m.v12 + this->v02 * m.v22;
|
||||
float r10 = this->v10 * m.v00 + this->v11 * m.v10 + this->v12 * m.v20;
|
||||
float r11 = this->v10 * m.v01 + this->v11 * m.v11 + this->v12 * m.v21;
|
||||
float r12 = this->v10 * m.v02 + this->v11 * m.v12 + this->v12 * m.v22;
|
||||
float r20 = this->v20 * m.v00 + this->v21 * m.v10 + this->v22 * m.v20;
|
||||
float r21 = this->v20 * m.v01 + this->v21 * m.v11 + this->v22 * m.v21;
|
||||
float r22 = this->v20 * m.v02 + this->v21 * m.v12 + this->v22 * m.v22;
|
||||
this->v00 = r00; this->v01 = r01; this->v02 = r02;
|
||||
this->v10 = r10; this->v11 = r11; this->v12 = r12;
|
||||
this->v20 = r20; this->v21 = r21; this->v22 = r22;
|
||||
return *this;
|
||||
}
|
||||
|
||||
static mat3_t identity() {mat3_t m; m.v00 = m.v11 = m.v22 = 1.0f; return m;}
|
||||
static mat3_t rotation(float a) {
|
||||
float c = cosf(a), s = sinf(a); mat3_t r = mat3_t::identity();
|
||||
r.v00 = c; r.v01 = -s; r.v10 = s; r.v11 = c; return r;}
|
||||
static mat3_t translation(float x, float y) {
|
||||
mat3_t r = mat3_t::identity(); r.v02 = x; r.v12 = y; return r;}
|
||||
static mat3_t scale(float x, float y) {
|
||||
mat3_t r = mat3_t::identity(); r.v00 = x; r.v11 = y; return r;}
|
||||
};
|
||||
|
||||
// 2x2 matrix for rotations and scales
|
||||
struct mat2_t {
|
||||
float v00 = 0.0f, v10 = 0.0f, v01 = 0.0f, v11 = 0.0f;
|
||||
mat2_t() = default;
|
||||
mat2_t(const mat2_t &m) = default;
|
||||
inline mat2_t& operator*= (const mat2_t &m) {
|
||||
float r00 = this->v00 * m.v00 + this->v01 * m.v10;
|
||||
float r01 = this->v00 * m.v01 + this->v01 * m.v11;
|
||||
float r10 = this->v10 * m.v00 + this->v11 * m.v10;
|
||||
float r11 = this->v10 * m.v01 + this->v11 * m.v11;
|
||||
this->v00 = r00; this->v01 = r01;
|
||||
this->v10 = r10; this->v11 = r11;
|
||||
return *this;
|
||||
}
|
||||
|
||||
static mat2_t identity() {mat2_t m; m.v00 = m.v11 = 1.0f; return m;}
|
||||
static mat2_t rotation(float a) {
|
||||
float c = cosf(a), s = sinf(a); mat2_t r;
|
||||
r.v00 = c; r.v01 = -s; r.v10 = s; r.v11 = c; return r;}
|
||||
static mat2_t scale(float x, float y) {
|
||||
mat2_t r; r.v00 = x; r.v11 = y; return r;}
|
||||
};
|
||||
|
||||
// point type for contour points
|
||||
template<typename T = int>
|
||||
struct __attribute__ ((packed)) point_t {
|
||||
T x, y;
|
||||
point_t(T x, T y) : x(x), y(y) {}
|
||||
point_t() : x(0), y(0) {}
|
||||
inline point_t& operator-= (const point_t &a) {x -= a.x; y -= a.y; return *this;}
|
||||
inline point_t& operator+= (const point_t &a) {x += a.x; y += a.y; return *this;}
|
||||
inline point_t& operator*= (const float a) {x *= a; y *= a; return *this;}
|
||||
inline point_t& operator*= (const mat2_t &a) {this->transform(a); return *this;}
|
||||
inline point_t& operator*= (const mat3_t &a) {this->transform(a); return *this;}
|
||||
inline point_t& operator/= (const float a) {x /= a; y /= a; return *this;}
|
||||
inline point_t& operator/= (const point_t &a) {x /= a.x; y /= a.y; return *this;}
|
||||
void transform(const mat3_t &m) {
|
||||
float tx = x, ty = y;
|
||||
this->x = (m.v00 * tx + m.v01 * ty + m.v02);
|
||||
this->y = (m.v10 * tx + m.v11 * ty + m.v12);
|
||||
}
|
||||
void transform(const mat2_t &m) {
|
||||
float tx = x, ty = y;
|
||||
this->x = (m.v00 * tx + m.v01 * ty);
|
||||
this->y = (m.v10 * tx + m.v11 * ty);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T> inline point_t<T> operator- (point_t<T> lhs, const point_t<T> &rhs) { lhs -= rhs; return lhs; }
|
||||
template<typename T> inline point_t<T> operator- (const point_t<T> &rhs) { return point_t<T>(-rhs.x, -rhs.y); }
|
||||
template<typename T> inline point_t<T> operator+ (point_t<T> lhs, const point_t<T> &rhs) { lhs += rhs; return lhs; }
|
||||
template<typename T> inline point_t<T> operator* (point_t<T> lhs, const float rhs) { lhs *= rhs; return lhs; }
|
||||
template<typename T> inline point_t<T> operator* (point_t<T> lhs, const point_t<T> &rhs) { lhs *= rhs; return lhs; }
|
||||
template<typename T> inline point_t<T> operator* (point_t<T> lhs, const mat3_t &rhs) { lhs *= rhs; return lhs; }
|
||||
template<typename T> inline point_t<T> operator/ (point_t<T> lhs, const float rhs) { lhs /= rhs; return lhs; }
|
||||
template<typename T> inline point_t<T> operator/ (point_t<T> lhs, const point_t<T> &rhs) { lhs.x /= rhs.x; lhs.y /= rhs.y; return lhs; }
|
||||
|
||||
|
||||
// rect type for bounds and clipping rectangles
|
||||
struct rect_t {
|
||||
int x, y, w, h;
|
||||
rect_t() : x(0), y(0), w(0), h(0) {}
|
||||
rect_t(int x, int y, int w, int h) : x(x), y(y), w(w), h(h) {}
|
||||
bool empty() const {return this->w == 0 || this->h == 0;}
|
||||
rect_t intersection(const rect_t &c) {
|
||||
return rect_t(std::max(this->x, c.x), std::max(this->y, c.y),
|
||||
std::max(0, std::min(this->x + this->w, c.x + c.w) - std::max(this->x, c.x)),
|
||||
std::max(0, std::min(this->y + this->h, c.y + c.h) - std::max(this->y, c.y)));
|
||||
}
|
||||
rect_t merge(const rect_t &c) {
|
||||
return rect_t(std::min(this->x, c.x), std::min(this->y, c.y),
|
||||
std::max(this->x + this->w, c.x + c.w) - std::min(this->x, c.x),
|
||||
std::max(this->y + this->h, c.y + c.h) - std::min(this->y, c.y));
|
||||
}
|
||||
};
|
||||
|
||||
struct tile_t {
|
||||
rect_t bounds;
|
||||
unsigned stride;
|
||||
uint8_t *data;
|
||||
|
||||
tile_t() {};
|
||||
|
||||
inline int get_value(int x, int y) const {
|
||||
return this->data[x + y * this->stride];
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct contour_t {
|
||||
point_t<T> *points;
|
||||
unsigned count;
|
||||
|
||||
contour_t() {}
|
||||
contour_t(const std::vector<point_t<T>>& v) : points(v.data()), count(v.size()) {};
|
||||
contour_t(point_t<T> *points, unsigned count) : points(points), count(count) {};
|
||||
|
||||
// TODO: Make this work, it's so much nicer to use auto point : contour
|
||||
//point_t<T> *begin() const { return points; };
|
||||
//point_t<T> *end() const { return points + count * sizeof(point_t<T>); };
|
||||
|
||||
rect_t bounds() const {
|
||||
T minx = this->points[0].x, maxx = minx;
|
||||
T miny = this->points[0].y, maxy = miny;
|
||||
for(auto i = 1u; i < this->count; i++) {
|
||||
minx = std::min(minx, this->points[i].x);
|
||||
miny = std::min(miny, this->points[i].y);
|
||||
maxx = std::max(maxx, this->points[i].x);
|
||||
maxy = std::max(maxy, this->points[i].y);
|
||||
}
|
||||
return rect_t(minx, miny, maxx - minx, maxy - miny);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
|
@ -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})
|
|
@ -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++
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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})
|
|
@ -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);
|
||||
|
|
|
@ -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) },
|
||||
|
|
|
@ -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
Ładowanie…
Reference in New Issue