// // 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 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; } } }