kopia lustrzana https://github.com/pimoroni/pimoroni-pico
Graphics Enhancements (#8)
* typedef pen and constexpr create_pen for #6 * Prevent out of bounds clip permitting write outside buffer * camelcase rect, point, and pen types * added triangle(p1, p2, p3) and polygon(std::vector<Point> points) methods to graphics library * change all uses of pen that were uint16_t into Pen type * updated micropython modules to use new rect, point, pen naming * added line(p1, p2) method to pico graphics library with faster vertical/horizontal special cases Co-authored-by: Jonathan Williamson <jon@pimoroni.com>pull/13/head
rodzic
51d95db154
commit
7d4ca4a86b
|
@ -122,13 +122,44 @@ int main() {
|
|||
if(shape.y >= pico_display.bounds.h) shape.dy *= -1;
|
||||
|
||||
pico_display.set_pen(shape.pen);
|
||||
pico_display.circle(point(shape.x, shape.y), shape.r);
|
||||
pico_display.circle(Point(shape.x, shape.y), shape.r);
|
||||
}
|
||||
|
||||
float led_step = fmod(i / 20.0f, M_PI * 2.0f);
|
||||
int r = (sin(led_step) * 25.0f) + 25.0f;
|
||||
pico_display.set_led(r, r / 1.2f, r);
|
||||
|
||||
|
||||
std::vector<Point> poly;
|
||||
poly.push_back(Point(30, 30));
|
||||
poly.push_back(Point(50, 35));
|
||||
poly.push_back(Point(70, 25));
|
||||
poly.push_back(Point(80, 65));
|
||||
poly.push_back(Point(50, 85));
|
||||
poly.push_back(Point(30, 45));
|
||||
|
||||
pico_display.set_pen(255, 255, 0);
|
||||
pico_display.polygon(poly);
|
||||
|
||||
|
||||
pico_display.set_pen(0, 255, 255);
|
||||
pico_display.triangle(Point(50, 50), Point(130, 80), Point(80, 110));
|
||||
|
||||
pico_display.set_pen(255, 255, 255);
|
||||
pico_display.line(Point(50, 50), Point(120, 80));
|
||||
pico_display.line(Point(20, 20), Point(120, 20));
|
||||
pico_display.line(Point(20, 20), Point(20, 120));
|
||||
|
||||
for(int r = 0; r < 30; r++) {
|
||||
for(int j = 0; j < 10; j++) {
|
||||
float rads = ((M_PI * 2) / 30.0f) * float(r);
|
||||
rads += (float(i) / 100.0f);
|
||||
rads += (float(j) / 100.0f);
|
||||
float cx = sin(rads) * 300.0f;
|
||||
float cy = cos(rads) * 300.0f;
|
||||
pico_display.line(Point(120, 67), Point(cx + 120, cy + 67));
|
||||
}
|
||||
}
|
||||
/*
|
||||
if(pico_display.is_pressed(pico_display.A)) {
|
||||
pico_display.rectangle(0, 0, 18, 18);
|
||||
|
|
|
@ -100,26 +100,26 @@ int main() {
|
|||
if(shape.y >= pico_explorer.bounds.h) shape.dy *= -1;
|
||||
|
||||
pico_explorer.set_pen(shape.pen);
|
||||
pico_explorer.circle(point(shape.x, shape.y), shape.r);
|
||||
pico_explorer.circle(Point(shape.x, shape.y), shape.r);
|
||||
}
|
||||
|
||||
float rv = pico_explorer.get_adc(pico_explorer.ADC0);
|
||||
pico_explorer.set_pen(255, 255, 255);
|
||||
pico_explorer.circle(point(rv * 140 + 50, 110), 20);
|
||||
pico_explorer.circle(Point(rv * 140 + 50, 110), 20);
|
||||
pico_explorer.set_pen(rv * 255, 0, 0);
|
||||
pico_explorer.circle(point(rv * 140 + 50, 110), 15);
|
||||
pico_explorer.circle(Point(rv * 140 + 50, 110), 15);
|
||||
|
||||
float gv = pico_explorer.get_adc(pico_explorer.ADC1);
|
||||
pico_explorer.set_pen(255, 255, 255);
|
||||
pico_explorer.circle(point(gv * 140 + 50, 160), 20);
|
||||
pico_explorer.circle(Point(gv * 140 + 50, 160), 20);
|
||||
pico_explorer.set_pen(0, gv * 255, 0);
|
||||
pico_explorer.circle(point(gv * 140 + 50, 160), 15);
|
||||
pico_explorer.circle(Point(gv * 140 + 50, 160), 15);
|
||||
|
||||
float bv = pico_explorer.get_adc(pico_explorer.ADC2);
|
||||
pico_explorer.set_pen(255, 255, 255);
|
||||
pico_explorer.circle(point(bv * 140 + 50, 210), 20);
|
||||
pico_explorer.circle(Point(bv * 140 + 50, 210), 20);
|
||||
pico_explorer.set_pen(0, 0, bv * 255);
|
||||
pico_explorer.circle(point(bv * 140 + 50, 210), 15);
|
||||
pico_explorer.circle(Point(bv * 140 + 50, 210), 15);
|
||||
|
||||
pico_explorer.set_motor(pico_explorer.MOTOR1, pico_explorer.FORWARD, bv);
|
||||
pico_explorer.set_motor(pico_explorer.MOTOR2, pico_explorer.FORWARD, rv);
|
||||
|
@ -128,32 +128,32 @@ int main() {
|
|||
|
||||
if(pico_explorer.is_pressed(pico_explorer.A)) {
|
||||
pico_explorer.set_pen(255, 255, 255);
|
||||
pico_explorer.character('A', point(120, 180), 5);
|
||||
pico_explorer.character('A', Point(120, 180), 5);
|
||||
}
|
||||
|
||||
if(pico_explorer.is_pressed(pico_explorer.B)) {
|
||||
pico_explorer.set_pen(255, 255, 255);
|
||||
pico_explorer.character('B', point(120, 180), 5);
|
||||
pico_explorer.character('B', Point(120, 180), 5);
|
||||
}
|
||||
|
||||
if(pico_explorer.is_pressed(pico_explorer.X)) {
|
||||
pico_explorer.set_pen(255, 255, 255);
|
||||
pico_explorer.character('X', point(120, 180), 5);
|
||||
pico_explorer.character('X', Point(120, 180), 5);
|
||||
}
|
||||
|
||||
if(pico_explorer.is_pressed(pico_explorer.Y)) {
|
||||
pico_explorer.set_pen(255, 255, 255);
|
||||
pico_explorer.character('Y', point(120, 180), 5);
|
||||
pico_explorer.character('Y', Point(120, 180), 5);
|
||||
}
|
||||
|
||||
float tyoff = cos(i / 20.0f) * 50.0f - 50.0f;
|
||||
rect text_box(10, 10, 150, 150);
|
||||
Rect text_box(10, 10, 150, 150);
|
||||
pico_explorer.set_pen(55, 65, 75);
|
||||
pico_explorer.rectangle(text_box);
|
||||
text_box.deflate(10);
|
||||
pico_explorer.set_clip(text_box);
|
||||
pico_explorer.set_pen(255, 255, 255);
|
||||
pico_explorer.text("This is a test of some text data that should wrap nicely onto multiple lines which is dead useful like.", point(text_box.x, text_box.y + tyoff), 100);
|
||||
pico_explorer.text("This is a test of some text data that should wrap nicely onto multiple lines which is dead useful like.", Point(text_box.x, text_box.y + tyoff), 100);
|
||||
|
||||
float xoff = sin(i / 20.0f) * 50.0f;
|
||||
xoff += 120 - (81 / 2);
|
||||
|
@ -168,23 +168,23 @@ int main() {
|
|||
uint8_t b = *src++;
|
||||
|
||||
pico_explorer.set_pen(r, g, b);
|
||||
pico_explorer.pixel(point(x + xoff, 68 - y + yoff));
|
||||
pico_explorer.pixel(Point(x + xoff, 68 - y + yoff));
|
||||
}
|
||||
}
|
||||
|
||||
pico_explorer.remove_clip();
|
||||
|
||||
pico_explorer.set_pen(255, 255, 255);
|
||||
pico_explorer.text("x: " + std::to_string(int(msa301.get_axis(msa301.X, 16) * 100)), point(10, 190), 100);
|
||||
pico_explorer.text("y: " + std::to_string(int(msa301.get_axis(msa301.Y, 16) * 100)), point(10, 205), 100);
|
||||
pico_explorer.text("z: " + std::to_string(int(msa301.get_axis(msa301.Z, 16) * 100)), point(10, 220), 100);
|
||||
pico_explorer.text("x: " + std::to_string(int(msa301.get_axis(msa301.X, 16) * 100)), Point(10, 190), 100);
|
||||
pico_explorer.text("y: " + std::to_string(int(msa301.get_axis(msa301.Y, 16) * 100)), Point(10, 205), 100);
|
||||
pico_explorer.text("z: " + std::to_string(int(msa301.get_axis(msa301.Z, 16) * 100)), Point(10, 220), 100);
|
||||
|
||||
uint16_t xpos = (msa301.get_axis(msa301.X, 16) * 120) + 120;
|
||||
uint16_t ypos = (msa301.get_axis(msa301.Z, 16) * 120) + 120;
|
||||
pico_explorer.set_pen(255, 255, 255);
|
||||
pico_explorer.circle(point(xpos, ypos), 20);
|
||||
pico_explorer.circle(Point(xpos, ypos), 20);
|
||||
pico_explorer.set_pen(255, 0, 255);
|
||||
pico_explorer.circle(point(xpos, ypos), 15);
|
||||
pico_explorer.circle(Point(xpos, ypos), 15);
|
||||
|
||||
|
||||
/*
|
||||
|
|
|
@ -9,35 +9,27 @@ namespace pimoroni {
|
|||
pen = create_pen(r, g, b);
|
||||
}
|
||||
|
||||
void PicoGraphics::set_pen(uint16_t p) {
|
||||
void PicoGraphics::set_pen(Pen p) {
|
||||
pen = p;
|
||||
}
|
||||
|
||||
uint16_t PicoGraphics::create_pen(uint8_t r, uint8_t g, uint8_t b) {
|
||||
uint16_t p = ((r & 0b11111000) << 8) |
|
||||
((g & 0b11111100) << 3) |
|
||||
((b & 0b11111000) >> 3);
|
||||
|
||||
return __builtin_bswap16(p);
|
||||
}
|
||||
|
||||
void PicoGraphics::set_clip(const rect &r) {
|
||||
clip = r;
|
||||
void PicoGraphics::set_clip(const Rect &r) {
|
||||
clip = bounds.intersection(r);
|
||||
}
|
||||
|
||||
void PicoGraphics::remove_clip() {
|
||||
clip = bounds;
|
||||
}
|
||||
|
||||
uint16_t* PicoGraphics::ptr(const rect &r) {
|
||||
Pen* PicoGraphics::ptr(const Rect &r) {
|
||||
return frame_buffer + r.x + r.y * bounds.w;
|
||||
}
|
||||
|
||||
uint16_t* PicoGraphics::ptr(const point &p) {
|
||||
Pen* PicoGraphics::ptr(const Point &p) {
|
||||
return frame_buffer + p.x + p.y * bounds.w;
|
||||
}
|
||||
|
||||
uint16_t* PicoGraphics::ptr(int32_t x, int32_t y) {
|
||||
Pen* PicoGraphics::ptr(int32_t x, int32_t y) {
|
||||
return frame_buffer + x + y * bounds.w;
|
||||
}
|
||||
|
||||
|
@ -45,34 +37,34 @@ namespace pimoroni {
|
|||
rectangle(clip);
|
||||
}
|
||||
|
||||
void PicoGraphics::pixel(const point &p) {
|
||||
void PicoGraphics::pixel(const Point &p) {
|
||||
if(!clip.contains(p)) return;
|
||||
*ptr(p) = pen;
|
||||
}
|
||||
|
||||
void PicoGraphics::pixel_span(const point &p, int32_t l) {
|
||||
void PicoGraphics::pixel_span(const Point &p, int32_t l) {
|
||||
// check if span in bounds
|
||||
if( p.x + l < clip.x || p.x >= clip.x + clip.w ||
|
||||
p.y < clip.y || p.y >= clip.y + clip.h) return;
|
||||
|
||||
// clamp span horizontally
|
||||
point clipped = p;
|
||||
Point clipped = p;
|
||||
if(clipped.x < clip.x) {l += clipped.x - clip.x; clipped.x = clip.x;}
|
||||
if(clipped.x + l >= clip.x + clip.w) {l = clip.x + clip.w - clipped.x;}
|
||||
|
||||
uint16_t *dest = ptr(clipped);
|
||||
Pen *dest = ptr(clipped);
|
||||
while(l--) {
|
||||
*dest++ = pen;
|
||||
}
|
||||
}
|
||||
|
||||
void PicoGraphics::rectangle(const rect &r) {
|
||||
void PicoGraphics::rectangle(const Rect &r) {
|
||||
// clip and/or discard depending on rectangle visibility
|
||||
rect clipped = r.intersection(clip);
|
||||
Rect clipped = r.intersection(clip);
|
||||
|
||||
if(clipped.empty()) return;
|
||||
|
||||
uint16_t *dest = ptr(clipped);
|
||||
Pen *dest = ptr(clipped);
|
||||
while(clipped.h--) {
|
||||
// draw span of pixels for this row
|
||||
for(uint32_t i = 0; i < clipped.w; i++) {
|
||||
|
@ -84,9 +76,9 @@ namespace pimoroni {
|
|||
}
|
||||
}
|
||||
|
||||
void PicoGraphics::circle(const point &p, int32_t radius) {
|
||||
void PicoGraphics::circle(const Point &p, int32_t radius) {
|
||||
// circle in screen bounds?
|
||||
rect bounds = rect(p.x - radius, p.y - radius, radius * 2, radius * 2);
|
||||
Rect bounds = Rect(p.x - radius, p.y - radius, radius * 2, radius * 2);
|
||||
if(!bounds.intersects(clip)) return;
|
||||
|
||||
int ox = radius, oy = 0, err = -radius;
|
||||
|
@ -96,15 +88,15 @@ namespace pimoroni {
|
|||
|
||||
err += oy; oy++; err += oy;
|
||||
|
||||
pixel_span(point(p.x - ox, p.y + last_oy), ox * 2 + 1);
|
||||
pixel_span(Point(p.x - ox, p.y + last_oy), ox * 2 + 1);
|
||||
if (last_oy != 0) {
|
||||
pixel_span(point(p.x - ox, p.y - last_oy), ox * 2 + 1);
|
||||
pixel_span(Point(p.x - ox, p.y - last_oy), ox * 2 + 1);
|
||||
}
|
||||
|
||||
if(err >= 0 && ox != last_oy) {
|
||||
pixel_span(point(p.x - last_oy, p.y + ox), last_oy * 2 + 1);
|
||||
pixel_span(Point(p.x - last_oy, p.y + ox), last_oy * 2 + 1);
|
||||
if (ox != 0) {
|
||||
pixel_span(point(p.x - last_oy, p.y - ox), last_oy * 2 + 1);
|
||||
pixel_span(Point(p.x - last_oy, p.y - ox), last_oy * 2 + 1);
|
||||
}
|
||||
|
||||
err -= ox; ox--; err -= ox;
|
||||
|
@ -112,9 +104,9 @@ namespace pimoroni {
|
|||
}
|
||||
}
|
||||
|
||||
void PicoGraphics::character(const char c, const point &p, uint8_t scale) {
|
||||
void PicoGraphics::character(const char c, const Point &p, uint8_t scale) {
|
||||
uint8_t char_index = c - 32;
|
||||
rect char_bounds(p.x, p.y, character_widths[char_index] * scale, 6 * scale);
|
||||
Rect char_bounds(p.x, p.y, character_widths[char_index] * scale, 6 * scale);
|
||||
|
||||
if(!clip.intersects(char_bounds)) return;
|
||||
|
||||
|
@ -122,7 +114,7 @@ namespace pimoroni {
|
|||
for(uint8_t cx = 0; cx < character_widths[char_index]; cx++) {
|
||||
for(uint8_t cy = 0; cy < 6; cy++) {
|
||||
if((1U << cy) & *d) {
|
||||
rectangle(rect(p.x + (cx * scale), p.y + (cy * scale), scale, scale));
|
||||
rectangle(Rect(p.x + (cx * scale), p.y + (cy * scale), scale, scale));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,7 +122,7 @@ namespace pimoroni {
|
|||
}
|
||||
}
|
||||
|
||||
void PicoGraphics::text(const std::string &t, const point &p, int32_t wrap, uint8_t scale) {
|
||||
void PicoGraphics::text(const std::string &t, const Point &p, int32_t wrap, uint8_t scale) {
|
||||
uint32_t co = 0, lo = 0; // character and line (if wrapping) offset
|
||||
|
||||
size_t i = 0;
|
||||
|
@ -156,7 +148,7 @@ namespace pimoroni {
|
|||
|
||||
// draw word
|
||||
for(size_t j = i; j < next_space; j++) {
|
||||
character(t[j], point(p.x + co, p.y + lo), scale);
|
||||
character(t[j], Point(p.x + co, p.y + lo), scale);
|
||||
co += character_widths[t[j] - 32] * scale;
|
||||
}
|
||||
|
||||
|
@ -166,4 +158,172 @@ namespace pimoroni {
|
|||
}
|
||||
}
|
||||
|
||||
int32_t orient2d(Point p1, Point p2, Point p3) {
|
||||
return (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x);
|
||||
}
|
||||
|
||||
bool is_top_left(const Point &p1, const Point &p2) {
|
||||
return (p1.y == p2.y && p1.x > p2.x) || (p1.y < p2.y);
|
||||
}
|
||||
|
||||
void PicoGraphics::triangle(Point p1, Point p2, Point p3) {
|
||||
Rect triangle_bounds(
|
||||
Point(std::min(p1.x, std::min(p2.x, p3.x)), std::min(p1.y, std::min(p2.y, p3.y))),
|
||||
Point(std::max(p1.x, std::max(p2.x, p3.x)), std::max(p1.y, std::max(p2.y, p3.y))));
|
||||
|
||||
// clip extremes to frame buffer size
|
||||
triangle_bounds = clip.intersection(triangle_bounds);
|
||||
|
||||
// if triangle completely out of bounds then don't bother!
|
||||
if (triangle_bounds.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// fix "winding" of vertices if needed
|
||||
int32_t winding = orient2d(p1, p2, p3);
|
||||
if (winding < 0) {
|
||||
Point t;
|
||||
t = p1; p1 = p3; p3 = t;
|
||||
}
|
||||
|
||||
// bias ensures no overdraw between neighbouring triangles
|
||||
int8_t bias0 = is_top_left(p2, p3) ? 0 : -1;
|
||||
int8_t bias1 = is_top_left(p3, p1) ? 0 : -1;
|
||||
int8_t bias2 = is_top_left(p1, p2) ? 0 : -1;
|
||||
|
||||
int32_t a01 = p1.y - p2.y;
|
||||
int32_t b01 = p2.x - p1.x;
|
||||
int32_t a12 = p2.y - p3.y;
|
||||
int32_t b12 = p3.x - p2.x;
|
||||
int32_t a20 = p3.y - p1.y;
|
||||
int32_t b20 = p1.x - p3.x;
|
||||
|
||||
Point tl(triangle_bounds.x, triangle_bounds.y);
|
||||
int32_t w0row = orient2d(p2, p3, tl) + bias0;
|
||||
int32_t w1row = orient2d(p3, p1, tl) + bias1;
|
||||
int32_t w2row = orient2d(p1, p2, tl) + bias2;
|
||||
|
||||
for (uint32_t y = 0; y < triangle_bounds.h; y++) {
|
||||
int32_t w0 = w0row;
|
||||
int32_t w1 = w1row;
|
||||
int32_t w2 = w2row;
|
||||
|
||||
Pen *dest = ptr(triangle_bounds.x, triangle_bounds.y + y);
|
||||
for (uint32_t x = 0; x < triangle_bounds.w; x++) {
|
||||
if ((w0 | w1 | w2) >= 0) {
|
||||
*dest = pen;
|
||||
}
|
||||
|
||||
dest++;
|
||||
|
||||
w0 += a12;
|
||||
w1 += a20;
|
||||
w2 += a01;
|
||||
}
|
||||
|
||||
w0row += b12;
|
||||
w1row += b20;
|
||||
w2row += b01;
|
||||
}
|
||||
}
|
||||
|
||||
void PicoGraphics::polygon(const std::vector<Point> &points) {
|
||||
static int32_t nodes[64]; // maximum allowed number of nodes per scanline for polygon rendering
|
||||
|
||||
int32_t miny = points[0].y, maxy = points[0].y;
|
||||
|
||||
for (uint16_t i = 1; i < points.size(); i++) {
|
||||
miny = std::min(miny, points[i].y);
|
||||
maxy = std::max(maxy, points[i].y);
|
||||
}
|
||||
|
||||
// for each scanline within the polygon bounds (clipped to clip rect)
|
||||
Point p;
|
||||
|
||||
for (p.y = std::max(clip.y, miny); p.y <= std::min(clip.y + clip.h, maxy); p.y++) {
|
||||
uint8_t n = 0;
|
||||
for (uint16_t i = 0; i < points.size(); i++) {
|
||||
uint16_t j = (i + 1) % points.size();
|
||||
int32_t sy = points[i].y;
|
||||
int32_t ey = points[j].y;
|
||||
int32_t fy = p.y;
|
||||
if ((sy < fy && ey >= fy) || (ey < fy && sy >= fy)) {
|
||||
int32_t sx = points[i].x;
|
||||
int32_t ex = points[j].x;
|
||||
int32_t px = int32_t(sx + float(fy - sy) / float(ey - sy) * float(ex - sx));
|
||||
|
||||
nodes[n++] = px < clip.x ? clip.x : (px >= clip.x + clip.w ? clip.x + clip.w - 1 : px);// clamp(int32_t(sx + float(fy - sy) / float(ey - sy) * float(ex - sx)), clip.x, clip.x + clip.w);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t i = 0;
|
||||
while (i < n - 1) {
|
||||
if (nodes[i] > nodes[i + 1]) {
|
||||
int32_t s = nodes[i]; nodes[i] = nodes[i + 1]; nodes[i + 1] = s;
|
||||
if (i) i--;
|
||||
}
|
||||
else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < n; i += 2) {
|
||||
pixel_span(Point(nodes[i], p.y), nodes[i + 1] - nodes[i] + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PicoGraphics::line(Point p1, Point p2) {
|
||||
// fast horizontal line
|
||||
if(p1.y == p2.y) {
|
||||
int32_t start = std::max(clip.x, std::min(p1.x, p2.x));
|
||||
int32_t end = std::min(clip.x + clip.w, std::max(p1.x, p2.x));
|
||||
pixel_span(Point(start, p1.y), end - start);
|
||||
return;
|
||||
}
|
||||
|
||||
// fast vertical line
|
||||
if(p1.x == p2.x) {
|
||||
int32_t start = std::max(clip.y, std::min(p1.y, p2.y));
|
||||
int32_t length = std::min(clip.y + clip.h, std::max(p1.y, p2.y)) - start;
|
||||
Pen *dest = ptr(p1.x, start);
|
||||
while(length--) {
|
||||
*dest = pen;
|
||||
dest += bounds.w;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// general purpose line
|
||||
// lines are either "shallow" or "steep" based on whether the x delta
|
||||
// is greater than the y delta
|
||||
int32_t dx = p2.x - p1.x;
|
||||
int32_t dy = p2.y - p1.y;
|
||||
bool shallow = std::abs(dx) > std::abs(dy);
|
||||
if(shallow) {
|
||||
// shallow version
|
||||
int32_t s = std::abs(dx); // number of steps
|
||||
int32_t sx = dx < 0 ? -1 : 1; // x step value
|
||||
int32_t sy = (dy << 16) / s; // y step value in fixed 16:16
|
||||
int32_t x = p1.x;
|
||||
int32_t y = p1.y << 16;
|
||||
while(s--) {
|
||||
pixel(Point(x, y >> 16));
|
||||
y += sy;
|
||||
x += sx;
|
||||
}
|
||||
}else{
|
||||
// steep version
|
||||
int32_t s = std::abs(dy); // number of steps
|
||||
int32_t sy = dy < 0 ? -1 : 1; // x step value
|
||||
int32_t sx = (dx << 16) / s; // y step value in fixed 16:16
|
||||
int32_t y = p1.y;
|
||||
int32_t x = p1.x << 16;
|
||||
while(s--) {
|
||||
pixel(Point(x >> 16, y));
|
||||
y += sy;
|
||||
x += sx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,36 +3,40 @@
|
|||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
// a tiny little graphics library for our Pico products
|
||||
// supports only 16-bit (565) RGB framebuffers
|
||||
namespace pimoroni {
|
||||
|
||||
struct rect;
|
||||
typedef uint16_t Pen;
|
||||
|
||||
struct point {
|
||||
struct Rect;
|
||||
|
||||
struct Point {
|
||||
int32_t x = 0, y = 0;
|
||||
|
||||
point() = default;
|
||||
point(int32_t x, int32_t y) : x(x), y(y) {}
|
||||
Point() = default;
|
||||
Point(int32_t x, int32_t y) : x(x), y(y) {}
|
||||
|
||||
inline point& operator-= (const point &a) { x -= a.x; y -= a.y; return *this; }
|
||||
inline point& operator+= (const point &a) { x += a.x; y += a.y; return *this; }
|
||||
inline Point& operator-= (const Point &a) { x -= a.x; y -= a.y; return *this; }
|
||||
inline Point& operator+= (const Point &a) { x += a.x; y += a.y; return *this; }
|
||||
|
||||
point clamp(const rect &r) const;
|
||||
Point clamp(const Rect &r) const;
|
||||
};
|
||||
|
||||
struct rect {
|
||||
struct Rect {
|
||||
int32_t x = 0, y = 0, w = 0, h = 0;
|
||||
|
||||
rect() = default;
|
||||
rect(int32_t x, int32_t y, int32_t w, int32_t h) : x(x), y(y), w(w), h(h) {}
|
||||
Rect() = default;
|
||||
Rect(int32_t x, int32_t y, int32_t w, int32_t h) : x(x), y(y), w(w), h(h) {}
|
||||
Rect(const Point &tl, const Point &br) : x(tl.x), y(tl.y), w(br.x - tl.x), h(br.y - tl.y) {}
|
||||
|
||||
bool empty() const;
|
||||
bool contains(const point &p) const;
|
||||
bool contains(const rect &p) const;
|
||||
bool intersects(const rect &r) const;
|
||||
rect intersection(const rect &r) const;
|
||||
bool contains(const Point &p) const;
|
||||
bool contains(const Rect &p) const;
|
||||
bool intersects(const Rect &r) const;
|
||||
Rect intersection(const Rect &r) const;
|
||||
|
||||
void inflate(int32_t v);
|
||||
void deflate(int32_t v);
|
||||
|
@ -42,34 +46,43 @@ namespace pimoroni {
|
|||
public:
|
||||
uint16_t *frame_buffer;
|
||||
|
||||
rect bounds;
|
||||
rect clip;
|
||||
Rect bounds;
|
||||
Rect clip;
|
||||
|
||||
uint16_t pen;
|
||||
Pen pen;
|
||||
|
||||
public:
|
||||
PicoGraphics(uint16_t width, uint16_t height, uint16_t *frame_buffer)
|
||||
: frame_buffer(frame_buffer), bounds(0, 0, width, height), clip(0, 0, width, height) {}
|
||||
|
||||
void set_pen(uint8_t r, uint8_t g, uint8_t b);
|
||||
void set_pen(uint16_t p);
|
||||
uint16_t create_pen(uint8_t r, uint8_t g, uint8_t b);
|
||||
void set_pen(Pen p);
|
||||
|
||||
void set_clip(const rect &r);
|
||||
constexpr Pen create_pen(uint8_t r, uint8_t g, uint8_t b) {
|
||||
uint16_t p = ((r & 0b11111000) << 8) |
|
||||
((g & 0b11111100) << 3) |
|
||||
((b & 0b11111000) >> 3);
|
||||
|
||||
return __builtin_bswap16(p);
|
||||
};
|
||||
|
||||
void set_clip(const Rect &r);
|
||||
void remove_clip();
|
||||
|
||||
uint16_t* ptr(const point &p);
|
||||
uint16_t* ptr(const rect &r);
|
||||
uint16_t* ptr(int32_t x, int32_t y);
|
||||
Pen* ptr(const Point &p);
|
||||
Pen* ptr(const Rect &r);
|
||||
Pen* ptr(int32_t x, int32_t y);
|
||||
|
||||
void clear();
|
||||
void pixel(const point &p);
|
||||
void pixel_span(const point &p, int32_t l);
|
||||
void rectangle(const rect &r);
|
||||
void circle(const point &p, int32_t r);
|
||||
void character(const char c, const point &p, uint8_t scale = 2);
|
||||
void text(const std::string &t, const point &p, int32_t wrap, uint8_t scale = 2);
|
||||
//void polygon(std::vector);
|
||||
void pixel(const Point &p);
|
||||
void pixel_span(const Point &p, int32_t l);
|
||||
void rectangle(const Rect &r);
|
||||
void circle(const Point &p, int32_t r);
|
||||
void character(const char c, const Point &p, uint8_t scale = 2);
|
||||
void text(const std::string &t, const Point &p, int32_t wrap, uint8_t scale = 2);
|
||||
void polygon(const std::vector<Point> &points);
|
||||
void triangle(Point p1, Point p2, Point p3);
|
||||
void line(Point p1, Point p2);
|
||||
};
|
||||
|
||||
}
|
|
@ -5,55 +5,55 @@
|
|||
|
||||
namespace pimoroni {
|
||||
|
||||
point point::clamp(const rect &r) const {
|
||||
return point(
|
||||
Point Point::clamp(const Rect &r) const {
|
||||
return Point(
|
||||
std::min(std::max(x, r.x), r.x + r.w),
|
||||
std::min(std::max(y, r.y), r.y + r.h)
|
||||
);
|
||||
}
|
||||
|
||||
point operator- (point lhs, const point &rhs) {
|
||||
Point operator- (Point lhs, const Point &rhs) {
|
||||
lhs -= rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
point operator- (const point &rhs) {
|
||||
return point(-rhs.x, -rhs.y);
|
||||
Point operator- (const Point &rhs) {
|
||||
return Point(-rhs.x, -rhs.y);
|
||||
}
|
||||
|
||||
point operator+ (point lhs, const point &rhs) {
|
||||
Point operator+ (Point lhs, const Point &rhs) {
|
||||
lhs += rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
bool rect::empty() const {
|
||||
bool Rect::empty() const {
|
||||
return w <= 0 || h <= 0;
|
||||
}
|
||||
|
||||
bool rect::contains(const point &p) const {
|
||||
bool Rect::contains(const Point &p) const {
|
||||
return p.x >= x && p.y >= y && p.x < x + w && p.y < y + h;
|
||||
}
|
||||
|
||||
bool rect::contains(const rect &p) const {
|
||||
bool Rect::contains(const Rect &p) const {
|
||||
return p.x >= x && p.y >= y && p.x + p.w < x + w && p.y + p.h < y + h;
|
||||
}
|
||||
|
||||
bool rect::intersects(const rect &r) const {
|
||||
bool Rect::intersects(const Rect &r) const {
|
||||
return !(x > r.x + r.w || x + w < r.x || y > r.y + r.h || y + h < r.y);
|
||||
}
|
||||
|
||||
rect rect::intersection(const rect &r) const {
|
||||
return rect(std::max(x, r.x),
|
||||
Rect Rect::intersection(const Rect &r) const {
|
||||
return Rect(std::max(x, r.x),
|
||||
std::max(y, r.y),
|
||||
std::min(x + w, r.x + r.w) - std::max(x, r.x),
|
||||
std::min(y + h, r.y + r.h) - std::max(y, r.y));
|
||||
}
|
||||
|
||||
void rect::inflate(int32_t v) {
|
||||
void Rect::inflate(int32_t v) {
|
||||
x -= v; y -= v; w += v * 2; h += v * 2;
|
||||
}
|
||||
|
||||
void rect::deflate(int32_t v) {
|
||||
void Rect::deflate(int32_t v) {
|
||||
x += v; y += v; w -= v * 2; h -= v * 2;
|
||||
}
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ mp_obj_t picodisplay_create_pen(mp_obj_t r_obj, mp_obj_t g_obj, mp_obj_t b_obj)
|
|||
mp_raise_ValueError("b out of range. Expected 0 to 255");
|
||||
else
|
||||
pen = display->create_pen(r, g, b);
|
||||
|
||||
|
||||
return mp_obj_new_int(pen);
|
||||
}
|
||||
|
||||
|
@ -158,7 +158,7 @@ mp_obj_t picodisplay_set_clip(mp_uint_t n_args, const mp_obj_t *args) {
|
|||
int w = mp_obj_get_int(args[2]);
|
||||
int h = mp_obj_get_int(args[3]);
|
||||
|
||||
rect r(x, y, w, h);
|
||||
Rect r(x, y, w, h);
|
||||
display->set_clip(r);
|
||||
|
||||
return mp_const_none;
|
||||
|
@ -178,9 +178,9 @@ mp_obj_t picodisplay_pixel(mp_obj_t x_obj, mp_obj_t y_obj) {
|
|||
int x = mp_obj_get_int(x_obj);
|
||||
int y = mp_obj_get_int(y_obj);
|
||||
|
||||
point p(x, y);
|
||||
Point p(x, y);
|
||||
display->pixel(p);
|
||||
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
|
@ -189,7 +189,7 @@ mp_obj_t picodisplay_pixel_span(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t l_obj)
|
|||
int y = mp_obj_get_int(y_obj);
|
||||
int l = mp_obj_get_int(l_obj);
|
||||
|
||||
point p(x, y);
|
||||
Point p(x, y);
|
||||
display->pixel_span(p, l);
|
||||
|
||||
return mp_const_none;
|
||||
|
@ -203,7 +203,7 @@ mp_obj_t picodisplay_rectangle(mp_uint_t n_args, const mp_obj_t *args) {
|
|||
int w = mp_obj_get_int(args[2]);
|
||||
int h = mp_obj_get_int(args[3]);
|
||||
|
||||
rect r(x, y, w, h);
|
||||
Rect r(x, y, w, h);
|
||||
display->rectangle(r);
|
||||
|
||||
return mp_const_none;
|
||||
|
@ -214,7 +214,7 @@ mp_obj_t picodisplay_circle(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t r_obj) {
|
|||
int y = mp_obj_get_int(y_obj);
|
||||
int r = mp_obj_get_int(r_obj);
|
||||
|
||||
point p(x, y);
|
||||
Point p(x, y);
|
||||
display->circle(p, r);
|
||||
|
||||
return mp_const_none;
|
||||
|
@ -225,7 +225,7 @@ mp_obj_t picodisplay_character(mp_uint_t n_args, const mp_obj_t *args) {
|
|||
int x = mp_obj_get_int(args[1]);
|
||||
int y = mp_obj_get_int(args[2]);
|
||||
|
||||
point p(x, y);
|
||||
Point p(x, y);
|
||||
if(n_args == 4) {
|
||||
int scale = mp_obj_get_int(args[3]);
|
||||
display->character((char)c, p, scale);
|
||||
|
@ -246,7 +246,7 @@ mp_obj_t picodisplay_text(mp_uint_t n_args, const mp_obj_t *args) {
|
|||
int y = mp_obj_get_int(args[2]);
|
||||
int wrap = mp_obj_get_int(args[3]);
|
||||
|
||||
point p(x, y);
|
||||
Point p(x, y);
|
||||
if(n_args == 5) {
|
||||
int scale = mp_obj_get_int(args[4]);
|
||||
display->text(t, p, wrap, scale);
|
||||
|
|
Ładowanie…
Reference in New Issue