|
|
|
@ -4,138 +4,134 @@ |
|
|
|
|
|
|
|
|
|
#include "framebuffer.h" |
|
|
|
|
|
|
|
|
|
#define MIN(a,b) ((a)>(b)?(b):(a)) |
|
|
|
|
#define MAX(a,b) ((a)>(b)?(a):(b)) |
|
|
|
|
#define MIN(a, b) ((a)>(b)?(b):(a)) |
|
|
|
|
#define MAX(a, b) ((a)>(b)?(a):(b)) |
|
|
|
|
|
|
|
|
|
#include <string.h> |
|
|
|
|
|
|
|
|
|
uint8_t fb[(FBH / 8) * FBW]; |
|
|
|
|
|
|
|
|
|
/** Fill with a vertical pattern, 1 byte */ |
|
|
|
|
void fb_fill(uint8_t pattern) { |
|
|
|
|
void fb_fill(uint8_t pattern) |
|
|
|
|
{ |
|
|
|
|
memset(fb, pattern, sizeof(fb)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void fb_clear() { |
|
|
|
|
fb_fill(0); |
|
|
|
|
static void draw_mask(fbsize_t idx, uint8_t mask, uint8_t color) |
|
|
|
|
{ |
|
|
|
|
if (color != 0) { |
|
|
|
|
fb[idx] |= mask; |
|
|
|
|
} else { |
|
|
|
|
fb[idx] &= ~mask; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void fb_px(fbpos_t x, fbpos_t y, uint8_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); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void fb_px(uint8_t x, uint8_t y, uint8_t color) { |
|
|
|
|
if (x >= FBW || y >= FBH) return; |
|
|
|
|
const uint8_t row = y / 8; |
|
|
|
|
const uint8_t rowrem = y % 8; |
|
|
|
|
const uint16_t cell = (uint16_t) x + (uint16_t) row * FBW; |
|
|
|
|
if (color) { |
|
|
|
|
fb[cell] |= (1 << rowrem); |
|
|
|
|
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 { |
|
|
|
|
fb[cell] &= ~(1 << rowrem); |
|
|
|
|
return 0x00; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void fb_hline(uint8_t x, uint8_t y, uint8_t w, uint8_t color) { |
|
|
|
|
if (x >= FBW || y >= FBH) return; |
|
|
|
|
void fb_hline(fbpos_t x, fbpos_t y, fbpos_t w, uint8_t color) |
|
|
|
|
{ |
|
|
|
|
if (x >= FBW || y >= FBH) { return; } |
|
|
|
|
w = MIN(FBW - x, w); |
|
|
|
|
const uint8_t row = y / 8; |
|
|
|
|
const uint8_t rowrem = y % 8; |
|
|
|
|
uint16_t cell = (uint16_t) x + (uint16_t) row * FBW; |
|
|
|
|
for(uint8_t i = 0; i < w; i++) { |
|
|
|
|
if (color) { |
|
|
|
|
fb[cell] |= (1 << rowrem); |
|
|
|
|
} else { |
|
|
|
|
fb[cell] &= ~(1 << rowrem); |
|
|
|
|
} |
|
|
|
|
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(uint8_t x, uint8_t y, uint8_t h, uint8_t color) { |
|
|
|
|
if (x >= FBW || y >= FBH) return; |
|
|
|
|
void fb_vline(fbpos_t x, fbpos_t y, fbpos_t h, uint8_t color) |
|
|
|
|
{ |
|
|
|
|
if (x >= FBW || y >= FBH) { return; } |
|
|
|
|
h = MIN(FBH - y - 1, h); |
|
|
|
|
const uint8_t row = y / 8; |
|
|
|
|
const uint8_t rowrem = y % 8; |
|
|
|
|
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
|
|
|
|
|
uint8_t mask = (0xFF << rowrem) & (0xFF >> (h-rowrem)); |
|
|
|
|
if (color) { |
|
|
|
|
fb[cell] |= mask; |
|
|
|
|
} else { |
|
|
|
|
fb[cell] &= ~mask; |
|
|
|
|
} |
|
|
|
|
const uint8_t mask = (0xFF << rowrem) & (0xFF >> (8 - h - rowrem)); |
|
|
|
|
draw_mask(cell, mask, color); |
|
|
|
|
return; |
|
|
|
|
} else { |
|
|
|
|
// First
|
|
|
|
|
uint8_t mask = (0xFF << rowrem); |
|
|
|
|
if (color) { |
|
|
|
|
fb[cell] |= mask; |
|
|
|
|
} else { |
|
|
|
|
fb[cell] &= ~mask; |
|
|
|
|
} |
|
|
|
|
h -= rowrem; |
|
|
|
|
draw_mask(cell, 0xFF << rowrem, color); |
|
|
|
|
h -= (rowrem == 0 ? 8 : (8 - rowrem)); |
|
|
|
|
|
|
|
|
|
uint8_t whole_cells = h / 8; |
|
|
|
|
const fbpos_t whole_cells = h / 8; |
|
|
|
|
if (whole_cells > 0) { |
|
|
|
|
h -= whole_cells * 8; |
|
|
|
|
for(uint8_t i = 0; i < whole_cells; i++) { |
|
|
|
|
for (fbpos_t i = 0; i < whole_cells; i++) { |
|
|
|
|
cell += FBW; |
|
|
|
|
if (color) { |
|
|
|
|
fb[cell] = 0xFF; |
|
|
|
|
} else { |
|
|
|
|
fb[cell] = 0; |
|
|
|
|
draw_mask(cell, 0xFF, color); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// last
|
|
|
|
|
mask = (0xFF >> (8-h)); |
|
|
|
|
if (color) { |
|
|
|
|
fb[cell] |= mask; |
|
|
|
|
} else { |
|
|
|
|
fb[cell] &= ~mask; |
|
|
|
|
} |
|
|
|
|
cell += FBW; |
|
|
|
|
draw_mask(cell, 0xFF >> (8 - h), color); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void fb_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) { |
|
|
|
|
if (x >= FBW || y >= FBH) return; |
|
|
|
|
w = MIN(FBW - x - 1, w); |
|
|
|
|
h = MIN(FBH - y - 1, h); |
|
|
|
|
const uint8_t row = y / 8; |
|
|
|
|
const uint8_t rowrem = y % 8; |
|
|
|
|
uint16_t cell = (uint16_t) x + (uint16_t) row * FBW; |
|
|
|
|
void fb_rect(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, uint8_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 (rowrem + h <= 8) { |
|
|
|
|
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))); |
|
|
|
|
uint8_t mask = (0xFF << rowrem) & (0xFF >> (8 - h - rowrem)); |
|
|
|
|
|
|
|
|
|
for(uint8_t i = 0; i < w; i++) { |
|
|
|
|
if (color) { |
|
|
|
|
fb[cell + i] |= mask; |
|
|
|
|
} else { |
|
|
|
|
fb[cell + i] &= ~mask; |
|
|
|
|
} |
|
|
|
|
for (fbpos_t i = 0; i < w; i++) { |
|
|
|
|
draw_mask(cell + i, mask, color); |
|
|
|
|
} |
|
|
|
|
return; |
|
|
|
|
} else { |
|
|
|
|
// First
|
|
|
|
|
uint8_t mask = (0xFF << rowrem); |
|
|
|
|
for(uint8_t i = 0; i < w; i++) { |
|
|
|
|
if (color) { |
|
|
|
|
fb[cell + i] |= mask; |
|
|
|
|
} else { |
|
|
|
|
fb[cell + i] &= ~mask; |
|
|
|
|
} |
|
|
|
|
for (fbpos_t i = 0; i < w; i++) { |
|
|
|
|
draw_mask(cell + i, mask, color); |
|
|
|
|
} |
|
|
|
|
h -= 8 - rowrem; |
|
|
|
|
|
|
|
|
|
uint8_t whole_cells = h / 8; |
|
|
|
|
const fbpos_t whole_cells = h / 8; |
|
|
|
|
if (whole_cells > 0) { |
|
|
|
|
h -= whole_cells * 8; |
|
|
|
|
for(uint8_t j = 0; j < whole_cells; j++) { |
|
|
|
|
for (fbpos_t j = 0; j < whole_cells; j++) { |
|
|
|
|
cell += FBW; |
|
|
|
|
for(uint8_t i = 0; i < w; i++) { |
|
|
|
|
if (color) { |
|
|
|
|
fb[cell + i] = 0xFF; |
|
|
|
|
} else { |
|
|
|
|
fb[cell + i] = 0; |
|
|
|
|
for (fbpos_t i = 0; i < w; i++) { |
|
|
|
|
draw_mask(cell + i, 0xFF, color); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -143,109 +139,64 @@ void fb_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) { |
|
|
|
|
cell += FBW; |
|
|
|
|
|
|
|
|
|
// last
|
|
|
|
|
mask = (0xFF >> (8-h)); |
|
|
|
|
for(uint8_t i = 0; i < w; i++) { |
|
|
|
|
if (color) { |
|
|
|
|
fb[cell + i] |= mask; |
|
|
|
|
} else { |
|
|
|
|
fb[cell + i] &= ~mask; |
|
|
|
|
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, uint8_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); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void fb_bitmap(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *map, uint8_t color) { |
|
|
|
|
if (x >= FBW || y >= FBH) return; |
|
|
|
|
const uint8_t w0 = w; |
|
|
|
|
const uint8_t h0 = h; |
|
|
|
|
void fb_bitmap(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, uint8_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 uint8_t row = y / 8; |
|
|
|
|
const uint8_t rowrem = y % 8; |
|
|
|
|
uint16_t cell = (uint16_t) x + (uint16_t) row * FBW; |
|
|
|
|
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(uint8_t i = 0; i < w; i++) { |
|
|
|
|
for (fbpos_t i = 0; i < w; i++) { |
|
|
|
|
// all within one cell
|
|
|
|
|
uint8_t mask = (map[i] & (0xFF >> (8-h))) << rowrem; |
|
|
|
|
if (color) { |
|
|
|
|
fb[cell + i] |= mask; |
|
|
|
|
} else { |
|
|
|
|
fb[cell + i] &= ~mask; |
|
|
|
|
} |
|
|
|
|
const uint8_t mask = (map[i] & (0xFF >> (8 - h))) << rowrem; |
|
|
|
|
draw_mask(cell + i, mask, color); |
|
|
|
|
} |
|
|
|
|
return; |
|
|
|
|
} else if (rowrem == 0) { |
|
|
|
|
// Optimization
|
|
|
|
|
uint8_t mapc0 = 0; |
|
|
|
|
|
|
|
|
|
// First
|
|
|
|
|
for(uint8_t i = 0; i < w; i++) { |
|
|
|
|
uint8_t mask = map[i]; |
|
|
|
|
if (color) { |
|
|
|
|
fb[cell + i] |= mask; |
|
|
|
|
} else { |
|
|
|
|
fb[cell + i] &= ~mask; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
h -= 8; |
|
|
|
|
|
|
|
|
|
// Middle
|
|
|
|
|
uint8_t whole_cells = h / 8; |
|
|
|
|
h -= whole_cells * 8; |
|
|
|
|
for(uint8_t j = 0; j < whole_cells; j++) { |
|
|
|
|
cell += FBW; |
|
|
|
|
mapc0 += w0; |
|
|
|
|
for(uint8_t i = 0; i < w; i++) { |
|
|
|
|
uint8_t mask = map[i + mapc0]; |
|
|
|
|
if (color) { |
|
|
|
|
fb[cell + i] |= mask; |
|
|
|
|
} else { |
|
|
|
|
fb[cell + i] &= ~mask; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// last
|
|
|
|
|
mapc0 += w0; |
|
|
|
|
cell += FBW; |
|
|
|
|
|
|
|
|
|
// last
|
|
|
|
|
for(uint8_t i = 0; i < w; i++) { |
|
|
|
|
uint8_t mask = map[i + mapc0] & (0xFF >> (8-h)); |
|
|
|
|
if (color) { |
|
|
|
|
fb[cell + i] |= mask; |
|
|
|
|
} else { |
|
|
|
|
fb[cell + i] &= ~mask; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
uint8_t mapc0 = 0; |
|
|
|
|
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) { |
|
|
|
|
// First
|
|
|
|
|
for (uint8_t i = 0; i < w; i++) { |
|
|
|
|
uint8_t mask = (map[i + mapc0] & (0xFF >> rowrem)) << rowrem; |
|
|
|
|
if (color) { |
|
|
|
|
fb[cell + i] |= mask; |
|
|
|
|
} else { |
|
|
|
|
fb[cell + i] &= ~mask; |
|
|
|
|
} |
|
|
|
|
for (fbpos_t i = 0; i < w; i++) { |
|
|
|
|
const uint8_t mask = (map[i + mapc0] & (0xFF >> rowrem)) << rowrem; |
|
|
|
|
draw_mask(cell + i, mask, color); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//mapc0 += w0;
|
|
|
|
|
cell += FBW; |
|
|
|
|
|
|
|
|
|
// leftover of the first row in map
|
|
|
|
|
|
|
|
|
|
// last
|
|
|
|
|
for (uint8_t i = 0; i < w; i++) { |
|
|
|
|
uint8_t mask = (map[i + mapc0] & (0xFF << (8 - rowrem))) >> (8 - rowrem); |
|
|
|
|
if (color) { |
|
|
|
|
fb[cell + i] |= mask; |
|
|
|
|
} else { |
|
|
|
|
fb[cell + i] &= ~mask; |
|
|
|
|
if (rowrem != 0) { |
|
|
|
|
for (fbpos_t i = 0; i < w; i++) { |
|
|
|
|
const uint8_t mask = (map[i + mapc0] & (0xFF << (8 - rowrem))) >> (8 - rowrem); |
|
|
|
|
draw_mask(cell + i, mask, color); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -259,3 +210,73 @@ void fb_bitmap(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *map, u |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
enum SevenSegBars { |
|
|
|
|
T = 1, RT = 2, RB = 4, B = 8, LB = 16, LT = 32, M = 64 |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static const uint8_t PROGMEM seven[] = { |
|
|
|
|
[0] = T | RT | RB | B | LB | LT, |
|
|
|
|
[1] = RT | RB, |
|
|
|
|
[2] = T | RT | M | LB | B, |
|
|
|
|
[3] = T | RT | M | RB | B, |
|
|
|
|
[4] = RT | RB | M | LT, |
|
|
|
|
[5] = T | LT | M | RB | B, |
|
|
|
|
[6] = T | LT | LB | B | RB | M, |
|
|
|
|
[7] = T | RT | RB, |
|
|
|
|
[8] = T | LT | RT | LB | RB | B | M, |
|
|
|
|
[9] = T | LT | RT | RB | B | M, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
fbpos_t fb_7seg_dig(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t th, uint8_t digit, uint8_t color) |
|
|
|
|
{ |
|
|
|
|
const uint8_t mask = digit > 9 ? 0 : pgm_read_byte(&seven[digit]); |
|
|
|
|
const fbpos_t wi = w - th * 2; |
|
|
|
|
const fbpos_t hi = (h - th * 3) / 2; |
|
|
|
|
|
|
|
|
|
bool bcolor = !color; // changed for XOR
|
|
|
|
|
|
|
|
|
|
fb_rect(x + th, |
|
|
|
|
y, |
|
|
|
|
wi, |
|
|
|
|
th, bcolor ^ (bool) (mask & T)); |
|
|
|
|
|
|
|
|
|
fb_rect(x + th, |
|
|
|
|
y + th + hi, |
|
|
|
|
wi, |
|
|
|
|
th, bcolor ^ (bool) (mask & M)); |
|
|
|
|
|
|
|
|
|
fb_rect(x + th, |
|
|
|
|
y + th * 2 + hi * 2, |
|
|
|
|
wi, |
|
|
|
|
th, bcolor ^ (bool) (mask & B)); |
|
|
|
|
|
|
|
|
|
fb_rect(x, |
|
|
|
|
y + th, |
|
|
|
|
th, |
|
|
|
|
hi, bcolor ^ (bool) (mask & LT)); |
|
|
|
|
|
|
|
|
|
fb_rect(x + th + wi, |
|
|
|
|
y + hi + th, |
|
|
|
|
th, |
|
|
|
|
hi, bcolor ^ (bool) (mask & LB)); |
|
|
|
|
|
|
|
|
|
fb_rect(x + th + wi, |
|
|
|
|
y + th, |
|
|
|
|
th, |
|
|
|
|
hi, bcolor ^ (bool) (mask & RT)); |
|
|
|
|
|
|
|
|
|
fb_rect(x + th + wi, |
|
|
|
|
y + th * 2 + hi, |
|
|
|
|
th, |
|
|
|
|
hi, bcolor ^ (bool) (mask & RB)); |
|
|
|
|
|
|
|
|
|
return w; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fbpos_t fb_7seg_period(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t th, uint8_t color) |
|
|
|
|
{ |
|
|
|
|
const fbpos_t hi = (h - th * 3) / 2; |
|
|
|
|
fb_rect(x, y + hi * 2 + th * 2, th, th, color); |
|
|
|
|
return th; |
|
|
|
|
} |
|
|
|
|