|
|
|
//
|
|
|
|
// Created by MightyPork on 2022/11/12.
|
|
|
|
//
|
|
|
|
|
|
|
|
#include "framebuffer.h"
|
|
|
|
#include "progmem.h"
|
|
|
|
|
|
|
|
#define MIN(a, b) ((a)>(b)?(b):(a))
|
|
|
|
#define MAX(a, b) ((a)>(b)?(a):(b))
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
uint8_t fb[FB_LEN];
|
|
|
|
|
|
|
|
/** Fill with a vertical pattern, 1 byte */
|
|
|
|
void fb_fill(uint8_t pattern)
|
|
|
|
{
|
|
|
|
memset(fb, pattern, sizeof(fb));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void draw_mask(fbsize_t idx, uint8_t mask, fbcolor_t color)
|
|
|
|
{
|
|
|
|
if (color != 0) {
|
|
|
|
fb[idx] |= mask;
|
|
|
|
} else {
|
|
|
|
fb[idx] &= ~mask;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void fb_fill_pattern(const uint8_t *pattern, uint8_t pattern_len, fbcolor_t color)
|
|
|
|
{
|
|
|
|
uint8_t p = 0;
|
|
|
|
for (fbsize_t i = 0; i < FB_LEN; i++) {
|
|
|
|
draw_mask(i, pattern[p], color);
|
|
|
|
p++;
|
|
|
|
if (p >= pattern_len) {
|
|
|
|
p = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void fb_invert()
|
|
|
|
{
|
|
|
|
for (fbsize_t i = 0; i < FB_LEN; i++) {
|
|
|
|
fb[i] ^= 0xFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void fb_px(fbpos_t x, fbpos_t y, fbcolor_t color)
|
|
|
|
{
|
|
|
|
if (x >= FBW || y >= FBH) { return; }
|
|
|
|
const fbpos_t row = y / 8;
|
|
|
|
const fbpos_t rowrem = y % 8;
|
|
|
|
const fbsize_t cell = (fbsize_t) x + (fbsize_t) row * FBW;
|
|
|
|
draw_mask(cell, 1 << rowrem, color);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t fb_getpx(fbpos_t x, fbpos_t y)
|
|
|
|
{
|
|
|
|
if (x >= FBW || y >= FBH) { return 0; }
|
|
|
|
const fbpos_t row = y / 8;
|
|
|
|
const fbpos_t rowrem = y % 8;
|
|
|
|
const fbsize_t cell = (fbsize_t) x + (fbsize_t) row * FBW;
|
|
|
|
if (fb[cell] & (1 << rowrem)) {
|
|
|
|
return 0xFF;
|
|
|
|
} else {
|
|
|
|
return 0x00;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void fb_hline(fbpos_t x, fbpos_t y, fbpos_t w, fbcolor_t color)
|
|
|
|
{
|
|
|
|
if (x >= FBW || y >= FBH) { return; }
|
|
|
|
w = MIN(FBW - x, w);
|
|
|
|
const fbpos_t row = y / 8;
|
|
|
|
const fbpos_t rowrem = y % 8;
|
|
|
|
fbsize_t cell = (fbsize_t) x + (fbsize_t) row * FBW;
|
|
|
|
for (uint8_t i = 0; i < w; i++) {
|
|
|
|
draw_mask(cell, 1 << rowrem, color);
|
|
|
|
cell++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void fb_vline(fbpos_t x, fbpos_t y, fbpos_t h, fbcolor_t color)
|
|
|
|
{
|
|
|
|
if (x >= FBW || y >= FBH) { return; }
|
|
|
|
h = MIN(FBH - y - 1, h);
|
|
|
|
const fbpos_t row = y / 8;
|
|
|
|
const fbpos_t rowrem = y % 8;
|
|
|
|
uint16_t cell = (uint16_t) x + (uint16_t) row * FBW;
|
|
|
|
|
|
|
|
if (rowrem + h < 8) {
|
|
|
|
// all within one cell
|
|
|
|
const uint8_t mask = (0xFF << rowrem) & (0xFF >> (8 - h - rowrem));
|
|
|
|
draw_mask(cell, mask, color);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
// First
|
|
|
|
draw_mask(cell, 0xFF << rowrem, color);
|
|
|
|
h -= (rowrem == 0 ? 8 : (8 - rowrem));
|
|
|
|
|
|
|
|
const fbpos_t whole_cells = h / 8;
|
|
|
|
if (whole_cells > 0) {
|
|
|
|
h -= whole_cells * 8;
|
|
|
|
for (fbpos_t i = 0; i < whole_cells; i++) {
|
|
|
|
cell += FBW;
|
|
|
|
draw_mask(cell, 0xFF, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// last
|
|
|
|
cell += FBW;
|
|
|
|
draw_mask(cell, 0xFF >> (8 - h), color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void fb_rect(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbcolor_t color)
|
|
|
|
{
|
|
|
|
if (x >= FBW || y >= FBH) { return; }
|
|
|
|
w = MIN(FBW - x, w);
|
|
|
|
h = MIN(FBH - y, h);
|
|
|
|
const fbpos_t row = y / 8;
|
|
|
|
const fbpos_t rowrem = y % 8;
|
|
|
|
fbsize_t cell = (fbsize_t) x + (fbsize_t) row * FBW;
|
|
|
|
|
|
|
|
if (w == 1 && h == 1) {
|
|
|
|
fb_px(x, y, color);
|
|
|
|
} else if (w == 1) {
|
|
|
|
fb_vline(x, y, h, color);
|
|
|
|
} else if (h == 1) {
|
|
|
|
fb_hline(x, y, w, color);
|
|
|
|
} else if (rowrem + h <= 8) {
|
|
|
|
// all within one cell
|
|
|
|
uint8_t mask = (0xFF << rowrem) & (0xFF >> (8 - h - rowrem));
|
|
|
|
|
|
|
|
for (fbpos_t i = 0; i < w; i++) {
|
|
|
|
draw_mask(cell + i, mask, color);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
// First
|
|
|
|
uint8_t mask = (0xFF << rowrem);
|
|
|
|
for (fbpos_t i = 0; i < w; i++) {
|
|
|
|
draw_mask(cell + i, mask, color);
|
|
|
|
}
|
|
|
|
h -= 8 - rowrem;
|
|
|
|
|
|
|
|
const fbpos_t whole_cells = h / 8;
|
|
|
|
if (whole_cells > 0) {
|
|
|
|
h -= whole_cells * 8;
|
|
|
|
for (fbpos_t j = 0; j < whole_cells; j++) {
|
|
|
|
cell += FBW;
|
|
|
|
for (fbpos_t i = 0; i < w; i++) {
|
|
|
|
draw_mask(cell + i, 0xFF, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cell += FBW;
|
|
|
|
|
|
|
|
// last
|
|
|
|
mask = (0xFF >> (8 - h));
|
|
|
|
for (fbpos_t i = 0; i < w; i++) {
|
|
|
|
draw_mask(cell + i, mask, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void fb_frame(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t thickness, fbcolor_t color)
|
|
|
|
{
|
|
|
|
if (thickness == 0) {
|
|
|
|
return;
|
|
|
|
} else if (thickness == 1) {
|
|
|
|
fb_hline(x, y, w, color);
|
|
|
|
fb_vline(x, y, h, color);
|
|
|
|
fb_hline(x, y + h - 1, w, color);
|
|
|
|
fb_vline(x + w - 1, y, h, color);
|
|
|
|
} else {
|
|
|
|
fb_rect(x, y, w, thickness, color);
|
|
|
|
fb_rect(x, y + h - thickness, w, thickness, color);
|
|
|
|
fb_rect(x, y + thickness, thickness, h - 2 * thickness, color);
|
|
|
|
fb_rect(x + w - thickness, y + thickness, thickness, h - 2 * thickness, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// setCircle draws a circle centered around x0,y0 with a defined
|
|
|
|
// radius. The circle can be black or white. And have a line
|
|
|
|
// thickness ranging from 1 to the radius of the circle.
|
|
|
|
// This function was grabbed from the SparkFun ColorLCDShield
|
|
|
|
// library.
|
|
|
|
void fb_circle(fbpos_t x0, fbpos_t y0, fbpos_t radius, uint8_t thickness, fbcolor_t color)
|
|
|
|
{
|
|
|
|
for (uint8_t r = 0; r < thickness; r++) {
|
|
|
|
int8_t f = 1 - radius;
|
|
|
|
fbpos_t ddF_x = 0;
|
|
|
|
int8_t ddF_y = -2 * radius;
|
|
|
|
fbpos_t x = 0;
|
|
|
|
fbpos_t y = radius;
|
|
|
|
|
|
|
|
fb_px(x0, y0 + radius, color);
|
|
|
|
fb_px(x0 + radius, y0, color);
|
|
|
|
if (y0 >= radius) {
|
|
|
|
fb_px(x0, y0 - radius, color);
|
|
|
|
}
|
|
|
|
if (x0 >= radius) {
|
|
|
|
fb_px(x0 - radius, y0, color);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (x < y) {
|
|
|
|
if (f >= 0) {
|
|
|
|
y--;
|
|
|
|
ddF_y += 2;
|
|
|
|
f += ddF_y;
|
|
|
|
}
|
|
|
|
x++;
|
|
|
|
ddF_x += 2;
|
|
|
|
f += ddF_x + 1;
|
|
|
|
|
|
|
|
fb_px(x0 + x, y0 + y, color);
|
|
|
|
fb_px(x0 - x, y0 + y, color);
|
|
|
|
fb_px(x0 + x, y0 - y, color);
|
|
|
|
fb_px(x0 - x, y0 - y, color);
|
|
|
|
fb_px(x0 + y, y0 + x, color);
|
|
|
|
fb_px(x0 - y, y0 + x, color);
|
|
|
|
fb_px(x0 + y, y0 - x, color);
|
|
|
|
fb_px(x0 - y, y0 - x, color);
|
|
|
|
}
|
|
|
|
radius--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void fb_bitmap_P(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, fbcolor_t color)
|
|
|
|
{
|
|
|
|
if (x >= FBW || y >= FBH) { return; }
|
|
|
|
const fbpos_t w0 = w;
|
|
|
|
w = MIN(FBW - x - 1, w);
|
|
|
|
h = MIN(FBH - y - 1, h);
|
|
|
|
const fbpos_t row = y / 8;
|
|
|
|
const fbpos_t rowrem = y % 8;
|
|
|
|
fbsize_t cell = (fbsize_t) x + (fbsize_t) row * FBW;
|
|
|
|
|
|
|
|
if (rowrem + h <= 8) {
|
|
|
|
for (fbpos_t i = 0; i < w; i++) {
|
|
|
|
// all within one cell
|
|
|
|
const uint8_t mask = (pgm_read_byte(&map[i]) & (0xFF >> (8 - h))) << rowrem;
|
|
|
|
draw_mask(cell + i, mask, color);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
fbsize_t mapc0 = 0;
|
|
|
|
|
|
|
|
// Draw the bitmap slice-by-slice based on how rows of the bitmap intersect with rows of the canvas.
|
|
|
|
// This could be optimized to walk each row of the canvas only once, but the code would get bigger.
|
|
|
|
while (h > 0) {
|
|
|
|
for (fbpos_t i = 0; i < w; i++) {
|
|
|
|
const uint8_t mask = (pgm_read_byte(&map[i + mapc0]) & (0xFF >> rowrem)) << rowrem;
|
|
|
|
draw_mask(cell + i, mask, color);
|
|
|
|
}
|
|
|
|
|
|
|
|
cell += FBW;
|
|
|
|
|
|
|
|
if (rowrem != 0) {
|
|
|
|
for (fbpos_t i = 0; i < w; i++) {
|
|
|
|
const uint8_t mask = (pgm_read_byte(&map[i + mapc0]) & (0xFF << (8 - rowrem))) >> (8 - rowrem);
|
|
|
|
draw_mask(cell + i, mask, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (h > 8) {
|
|
|
|
h -= 8;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
mapc0 += w0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|