Skip to content

Commit c4decc5

Browse files
PicoGraphics: HSV Pen (#665)
Co-authored-by: Gee Bartlett <[email protected]>
1 parent cd2f45d commit c4decc5

File tree

11 files changed

+88
-69
lines changed

11 files changed

+88
-69
lines changed

examples/interstate75/interstate75_rainbow.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ target_link_libraries(${OUTPUT_NAME}
1111
pico_multicore
1212
hardware_vreg
1313
hardware_pwm
14+
pico_graphics
1415
hub75
1516
interstate75
1617
)

examples/interstate75/interstate75_rainbow.cpp

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,33 +25,12 @@ Hub75 hub75(32, 32, nullptr, PANEL_GENERIC, false);
2525
//Hub75 hub75(128, 32, nullptr, PANEL_GENERIC, false);
2626

2727
//Works with our 64x64 panel https://shop.pimoroni.com/products/rgb-led-matrix-panel?variant=3029531983882
28-
//Hub75 hub75(64, 64, nullptr, PANEL_GENERIC, false);
28+
//Hub75 hub75(64, 64, nullptr, PANEL_GENERIC, true);
2929
//or using 2 of these panels
3030
//Hub75 hub75(128, 64, nullptr, PANEL_GENERIC, false);
3131

3232
PicoGraphics_PenRGB888 graphics(hub75.width, hub75.height, nullptr);
3333

34-
// HSV Conversion expects float inputs in the range of 0.00-1.00 for each channel
35-
// Outputs are rgb in the range 0-255 for each channel
36-
void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) {
37-
float i = floor(h * 6.0f);
38-
float f = h * 6.0f - i;
39-
v *= 255.0f;
40-
uint8_t p = v * (1.0f - s);
41-
uint8_t q = v * (1.0f - f * s);
42-
uint8_t t = v * (1.0f - (1.0f - f) * s);
43-
44-
switch (int(i) % 6) {
45-
case 0: r = v; g = t; b = p; break;
46-
case 1: r = q; g = v; b = p; break;
47-
case 2: r = p; g = v; b = t; break;
48-
case 3: r = p; g = q; b = v; break;
49-
case 4: r = t; g = p; b = v; break;
50-
case 5: r = v; g = p; b = q; break;
51-
}
52-
}
53-
54-
5534
// Interrupt callback required function
5635
void __isr dma_complete() {
5736
hub75.dma_complete();
@@ -63,7 +42,13 @@ int main() {
6342

6443
uint8_t hue_map[hub75.width][3];
6544
for(uint i = 0; i < hub75.width; i++) {
66-
from_hsv(i / (float) hub75.width, 1.0f, 1.0f, hue_map[i][0], hue_map[i][1], hue_map[i][2]);
45+
uint8_t r=0;
46+
uint8_t g=0;
47+
uint8_t b=0;
48+
graphics.from_hsv(i / (float) hub75.width, 1.0f, 0.7f, r, g, b);
49+
hue_map[i][0] = r;
50+
hue_map[i][1] = g;
51+
hue_map[i][2] = b;
6752
}
6853

6954
hub75.start(dma_complete);
@@ -83,12 +68,12 @@ int main() {
8368
}
8469

8570
if(button_a.raw()) {
86-
speed += 0.05f;
71+
speed += 0.1f;
8772
speed = speed >= 10.0f ? 10.0f : speed;
8873
animate = true;
8974
}
9075
if(button_b.raw()) {
91-
speed -= 0.05f;
76+
speed -= 0.1f;
9277
speed = speed <= 0.0f ? 0.0f : speed;
9378
animate = true;
9479
}
@@ -103,18 +88,17 @@ int main() {
10388

10489
graphics.set_pen(r, g, b);
10590
graphics.pixel(Point(x, y));
106-
}
91+
}
10792
}
10893
hub75.update(&graphics);
10994

11095
led_rgb.set_hsv(led_h, 1.0f, 1.0f);
11196
led_h += 0.01;
11297

11398

114-
sleep_ms(20);
99+
//sleep_ms(5);
115100
}
116101

117-
printf("done\n");
118102

119-
return 0;
120103
}
104+

libraries/interstate75/interstate75.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ add_library(${LIB_NAME} INTERFACE)
44
target_include_directories(${LIB_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR})
55

66
# Pull in pico libraries that we need
7-
target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib hub75 button rgbled hardware_pwm)
7+
target_link_libraries(${LIB_NAME} INTERFACE pico_stdlib hub75 button rgbled hardware_pwm pico_graphics)

libraries/pico_graphics/pico_graphics.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,28 @@ namespace pimoroni {
44

55
const uint8_t dither16_pattern[16] = {0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5};
66

7+
void PicoGraphics::from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) {
8+
float i = floor(h * 6.0f);
9+
float f = h * 6.0f - i;
10+
v *= 255.0f;
11+
uint8_t p = v * (1.0f - s);
12+
uint8_t q = v * (1.0f - f * s);
13+
uint8_t t = v * (1.0f - (1.0f - f) * s);
14+
15+
switch (int(i) % 6) {
16+
case 0: r = v; g = t; b = p; break;
17+
case 1: r = q; g = v; b = p; break;
18+
case 2: r = p; g = v; b = t; break;
19+
case 3: r = p; g = q; b = v; break;
20+
case 4: r = t; g = p; b = v; break;
21+
case 5: r = v; g = p; b = q; break;
22+
}
23+
}
24+
725
int PicoGraphics::update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) {return -1;};
826
int PicoGraphics::reset_pen(uint8_t i) {return -1;};
927
int PicoGraphics::create_pen(uint8_t r, uint8_t g, uint8_t b) {return -1;};
28+
int PicoGraphics::create_pen_hsv(float h, float s, float v){return -1;};
1029
void PicoGraphics::set_pixel_dither(const Point &p, const RGB &c) {};
1130
void PicoGraphics::set_pixel_dither(const Point &p, const RGB565 &c) {};
1231
void PicoGraphics::set_pixel_dither(const Point &p, const uint8_t &c) {};

libraries/pico_graphics/pico_graphics.hpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <algorithm>
77
#include <vector>
88
#include <functional>
9+
#include <math.h>
910

1011
#include "libraries/hershey_fonts/hershey_fonts.hpp"
1112
#include "libraries/bitmap_fonts/bitmap_fonts.hpp"
@@ -25,6 +26,8 @@ namespace pimoroni {
2526
typedef uint8_t RGB332;
2627
typedef uint16_t RGB565;
2728
typedef uint32_t RGB888;
29+
30+
2831
struct RGB {
2932
int16_t r, g, b;
3033

@@ -90,6 +93,8 @@ namespace pimoroni {
9093
}
9194
};
9295

96+
97+
9398
typedef int Pen;
9499

95100
struct Rect;
@@ -171,6 +176,9 @@ namespace pimoroni {
171176
Rect bounds;
172177
Rect clip;
173178

179+
180+
181+
174182
typedef std::function<void(void *data, size_t length)> conversion_callback_func;
175183
typedef std::function<RGB565()> next_pixel_func;
176184
//typedef std::function<void(int y)> scanline_interrupt_func;
@@ -184,6 +192,7 @@ namespace pimoroni {
184192
return RGB(r, g, b).to_rgb332();
185193
}
186194

195+
187196
static constexpr RGB565 rgb332_to_rgb565(RGB332 c) {
188197
uint16_t p = ((c & 0b11100000) << 8) |
189198
((c & 0b00011100) << 6) |
@@ -221,6 +230,7 @@ namespace pimoroni {
221230
virtual void set_pixel_span(const Point &p, uint l) = 0;
222231

223232
virtual int create_pen(uint8_t r, uint8_t g, uint8_t b);
233+
virtual int create_pen_hsv(float h, float s, float v);
224234
virtual int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b);
225235
virtual int reset_pen(uint8_t i);
226236
virtual void set_pixel_dither(const Point &p, const RGB &c);
@@ -253,6 +263,7 @@ namespace pimoroni {
253263
void polygon(const std::vector<Point> &points);
254264
void triangle(Point p1, Point p2, Point p3);
255265
void line(Point p1, Point p2);
266+
void from_hsv(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b);
256267

257268
protected:
258269
void frame_convert_rgb565(conversion_callback_func callback, next_pixel_func get_next_pixel);
@@ -400,7 +411,6 @@ namespace pimoroni {
400411
void set_pen(uint c) override;
401412
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
402413
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
403-
404414
void set_pixel(const Point &p) override;
405415
void set_pixel_span(const Point &p, uint l) override;
406416
void set_pixel_dither(const Point &p, const RGB &c) override;
@@ -422,14 +432,14 @@ namespace pimoroni {
422432
void set_pen(uint c) override;
423433
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
424434
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
435+
int create_pen_hsv(float h, float s, float v) override;
425436
void set_pixel(const Point &p) override;
426437
void set_pixel_span(const Point &p, uint l) override;
427438
static size_t buffer_size(uint w, uint h) {
428439
return w * h * sizeof(RGB565);
429440
}
430441
};
431442

432-
433443
class PicoGraphics_PenRGB888 : public PicoGraphics {
434444
public:
435445
RGB src_color;
@@ -438,6 +448,7 @@ namespace pimoroni {
438448
void set_pen(uint c) override;
439449
void set_pen(uint8_t r, uint8_t g, uint8_t b) override;
440450
int create_pen(uint8_t r, uint8_t g, uint8_t b) override;
451+
int create_pen_hsv(float h, float s, float v) override;
441452
void set_pixel(const Point &p) override;
442453
void set_pixel_span(const Point &p, uint l) override;
443454
static size_t buffer_size(uint w, uint h) {

libraries/pico_graphics/pico_graphics_pen_rgb565.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,18 @@ namespace pimoroni {
1313
}
1414
void PicoGraphics_PenRGB565::set_pen(uint8_t r, uint8_t g, uint8_t b) {
1515
src_color = {r, g, b};
16-
color = src_color.to_rgb565();
16+
color = src_color.to_rgb565();
1717
}
1818
int PicoGraphics_PenRGB565::create_pen(uint8_t r, uint8_t g, uint8_t b) {
1919
return RGB(r, g, b).to_rgb565();
2020
}
21+
int PicoGraphics_PenRGB565::create_pen_hsv(float h, float s, float v) {
22+
uint8_t r;
23+
uint8_t g;
24+
uint8_t b;
25+
from_hsv(h, s, v, r, g, b);
26+
return RGB(r, g, b).to_rgb565();
27+
}
2128
void PicoGraphics_PenRGB565::set_pixel(const Point &p) {
2229
uint16_t *buf = (uint16_t *)frame_buffer;
2330
buf[p.y * bounds.w + p.x] = color;

libraries/pico_graphics/pico_graphics_pen_rgb888.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ namespace pimoroni {
1818
int PicoGraphics_PenRGB888::create_pen(uint8_t r, uint8_t g, uint8_t b) {
1919
return RGB(r, g, b).to_rgb888();
2020
}
21+
int PicoGraphics_PenRGB888::create_pen_hsv(float h, float s, float v) {
22+
uint8_t r;
23+
uint8_t g;
24+
uint8_t b;
25+
from_hsv(h, s, v, r, g, b);
26+
return RGB(r, g, b).to_rgb888();
27+
}
2128
void PicoGraphics_PenRGB888::set_pixel(const Point &p) {
2229
uint32_t *buf = (uint32_t *)frame_buffer;
2330
buf[p.y * bounds.w + p.x] = color;
Lines changed: 7 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,30 @@
11
import time
2-
import math
32
import interstate75
43

5-
i75 = interstate75.Interstate75(display=interstate75.DISPLAY_INTERSTATE75_32X32)
4+
i75 = interstate75.Interstate75(display=interstate75.DISPLAY_INTERSTATE75_64X64, stb_invert=True)
65
graphics = i75.display
76

87
width = i75.width
98
height = i75.height
109

11-
12-
@micropython.native # noqa: F821
13-
def from_hsv(h, s, v):
14-
i = math.floor(h * 6.0)
15-
f = h * 6.0 - i
16-
v *= 255.0
17-
p = v * (1.0 - s)
18-
q = v * (1.0 - f * s)
19-
t = v * (1.0 - (1.0 - f) * s)
20-
21-
i = int(i) % 6
22-
if i == 0:
23-
return int(v), int(t), int(p)
24-
if i == 1:
25-
return int(q), int(v), int(p)
26-
if i == 2:
27-
return int(p), int(v), int(t)
28-
if i == 3:
29-
return int(p), int(q), int(v)
30-
if i == 4:
31-
return int(t), int(p), int(v)
32-
if i == 5:
33-
return int(v), int(p), int(q)
10+
devs = 1.0 / height
3411

3512

3613
@micropython.native # noqa: F821
37-
def draw():
38-
global hue_offset, phase
39-
phase_percent = phase / 15
14+
def draw(offset):
4015
for x in range(width):
41-
colour = hue_map[int((x + (hue_offset * width)) % width)]
16+
graphics.set_pen(graphics.create_pen_hsv(devs * x + offset, 1.0, 0.5))
4217
for y in range(height):
43-
v = ((math.sin((x + y) / stripe_width + phase_percent) + 1.5) / 2.5)
4418

45-
graphics.set_pen(graphics.create_pen(int(colour[0] * v), int(colour[1] * v), int(colour[2] * v)))
4619
graphics.pixel(x, y)
4720

4821
i75.update(graphics)
4922

5023

51-
hue_map = [from_hsv(x / width, 1.0, 0.5) for x in range(width)]
52-
hue_offset = 0.0
53-
5424
animate = True
5525
stripe_width = 3.0
5626
speed = 5.0
57-
27+
offset = 0.0
5828

5929
phase = 0
6030
while True:
@@ -63,6 +33,7 @@ def draw():
6333
phase += speed
6434

6535
start = time.ticks_ms()
66-
draw()
36+
offset += 0.05
37+
draw(offset)
6738

6839
print("total took: {} ms".format(time.ticks_ms() - start))

micropython/modules/picographics/picographics.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(ModPicoGraphics_set_palette_obj, 2, ModPicoGraphics_s
2121
// Pen
2222
MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_pen_obj, ModPicoGraphics_set_pen);
2323
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_create_pen_obj, 4, 4, ModPicoGraphics_create_pen);
24+
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_create_pen_hsv_obj, 4, 4, ModPicoGraphics_create_pen_hsv);
2425

2526
// Primitives
2627
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_set_clip_obj, 5, 5, ModPicoGraphics_set_clip);
@@ -77,6 +78,7 @@ STATIC const mp_rom_map_elem_t ModPicoGraphics_locals_dict_table[] = {
7778
{ MP_ROM_QSTR(MP_QSTR_sprite), MP_ROM_PTR(&ModPicoGraphics_sprite_obj) },
7879

7980
{ MP_ROM_QSTR(MP_QSTR_create_pen), MP_ROM_PTR(&ModPicoGraphics_create_pen_obj) },
81+
{ MP_ROM_QSTR(MP_QSTR_create_pen_hsv), MP_ROM_PTR(&ModPicoGraphics_create_pen_hsv_obj) },
8082
{ MP_ROM_QSTR(MP_QSTR_update_pen), MP_ROM_PTR(&ModPicoGraphics_update_pen_obj) },
8183
{ MP_ROM_QSTR(MP_QSTR_reset_pen), MP_ROM_PTR(&ModPicoGraphics_reset_pen_obj) },
8284
{ MP_ROM_QSTR(MP_QSTR_set_palette), MP_ROM_PTR(&ModPicoGraphics_set_palette_obj) },

micropython/modules/picographics/picographics.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,21 @@ mp_obj_t ModPicoGraphics_create_pen(size_t n_args, const mp_obj_t *args) {
696696
return mp_obj_new_int(result);
697697
}
698698

699+
mp_obj_t ModPicoGraphics_create_pen_hsv(size_t n_args, const mp_obj_t *args) {
700+
enum { ARG_self, ARG_h, ARG_s, ARG_v };
701+
702+
ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], ModPicoGraphics_obj_t);
703+
int result = self->graphics->create_pen_hsv(
704+
mp_obj_get_float(args[ARG_h]),
705+
mp_obj_get_float(args[ARG_s]),
706+
mp_obj_get_float(args[ARG_v])
707+
);
708+
709+
if (result == -1) mp_raise_ValueError("create_pen failed. No matching colour or space in palette!");
710+
711+
return mp_obj_new_int(result);
712+
}
713+
699714
mp_obj_t ModPicoGraphics_set_palette(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
700715
size_t num_tuples = n_args - 1;
701716
const mp_obj_t *tuples = pos_args + 1;

0 commit comments

Comments
 (0)