From fecadb382112ab0c1b8c1a02f10a8685e19fd88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 14 Nov 2022 01:02:52 +0100 Subject: [PATCH] import --- .gitignore | 4 + Makefile | 12 ++ framebuffer.c | 282 +++++++++++++++++++++++++++++++++++++++++++ framebuffer.h | 90 ++++++++++++++ framebuffer_config.h | 13 ++ main.c | 110 +++++++++++++++++ 6 files changed, 511 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 framebuffer.c create mode 100644 framebuffer.h create mode 100644 framebuffer_config.h create mode 100644 main.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8477f92 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.o +ufb-test +.idea/ + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1db62aa --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +.PHONY: build run + +build: ufb-test + +run: build + ./ufb-test + +clean: + rm -f ufb-test + +ufb-test: main.c framebuffer.c framebuffer.h framebuffer_config.h + cc -o $@ $^ -I. diff --git a/framebuffer.c b/framebuffer.c new file mode 100644 index 0000000..37bdb9b --- /dev/null +++ b/framebuffer.c @@ -0,0 +1,282 @@ +// +// Created by MightyPork on 2022/11/12. +// + +#include "framebuffer.h" + +#define MIN(a, b) ((a)>(b)?(b):(a)) +#define MAX(a, b) ((a)>(b)?(a):(b)) + +#include + +uint8_t fb[(FBH / 8) * FBW]; + +/** 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, 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); +} + +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, uint8_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, uint8_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, 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 (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, 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(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 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 = (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 = (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 = (map[i + mapc0] & (0xFF << (8 - rowrem))) >> (8 - rowrem); + draw_mask(cell + i, mask, color); + } + } + + if (h > 8) { + h -= 8; + } else { + break; + } + mapc0 += w0; + } + } +} + + +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; +} diff --git a/framebuffer.h b/framebuffer.h new file mode 100644 index 0000000..c0cb1f4 --- /dev/null +++ b/framebuffer.h @@ -0,0 +1,90 @@ +// +// Created by MightyPork on 2022/11/12. +// + +#ifndef FRAMEBUFFER_H +#define FRAMEBUFFER_H + +#include +#include + +#include "framebuffer_config.h" + +#define FBSET 0xFF +#define FBCLEAR 0x00 + +typedef uint16_t fbsize_t; +typedef uint8_t fbpos_t; + +/// Framebuffer backing array. +/// +/// The format is the native format for SSD1306: array of bytes representing pixels in 8 rows at once, LSB is the topmost row. +/// +/// a0 b0 ... til the display width +/// a1 b1 +/// a2 b2 +/// ... ... +/// a7 b7 +/// +/// and more bytes continue rows 8-15 and so on +extern uint8_t fb[(FBH / 8) * FBW]; + +/// Fill the entire screen with a byte pattern. +/// Use 0xFF or 0x00 for a solid fill. Other patterns may be used to create horizontal stripes. +void fb_fill(uint8_t pattern); + +/// Clear the display (fill with 0x00) +static inline void fb_clear(void) +{ + fb_fill(0); +} + +/// Set a single pixel +void fb_px(fbpos_t x, fbpos_t y, uint8_t color); + +/// Get pixel color +uint8_t fb_getpx(fbpos_t x, fbpos_t y); + +/// Draw a horizontal line +void fb_hline(fbpos_t x, fbpos_t y, fbpos_t w, uint8_t color); + +/// Draw a vertical line +void fb_vline(fbpos_t x, fbpos_t y, fbpos_t h, uint8_t color); + +/// Draw a filled rect +void fb_rect(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, uint8_t color); + +/// Draw a frame (unfilled rect) +void fb_frame(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t thickness, uint8_t color); + +/// Draw a bitmap +void fb_bitmap(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, const uint8_t *map, uint8_t color); + +/// Draw a 7-segment digit. Returns its width (without spacing) +/// +/// \param x - pos X (left top) +/// \param y - pos Y (left top) +/// \param w - full digit width +/// \param h - full digit height; will be adjusted down if needed +/// \param th - thickness +/// \param digit - digit 0-9 +/// \return width taken +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); + +/// Draw a 7-segment period. Returns its width (without spacing). +/// Digit height is (w * 2 - th) +/// +/// \param x - pos X (digit left top) +/// \param y - pos Y (digit left top) +/// \param w - full digit width +/// \param h - full digit height; will be adjusted down if needed +/// \param th - thickness +/// \return width taken +fbpos_t fb_7seg_period(fbpos_t x, fbpos_t y, fbpos_t w, fbpos_t h, fbpos_t th, uint8_t color); + +/// Output the framebuffer array `fb` to the display device. +/// +/// The user must implement this +extern void fb_blit(void); + +#endif //FRAMEBUFFER_H diff --git a/framebuffer_config.h b/framebuffer_config.h new file mode 100644 index 0000000..7055619 --- /dev/null +++ b/framebuffer_config.h @@ -0,0 +1,13 @@ +#ifndef FRAMEBUFFER_CONFIG_H +#define FRAMEBUFFER_CONFIG_H + +/* Tiny framebuffer */ +#define FBW 48 +#define FBH 32 + +// if built for AVR, uncommend the include and comment the fake defines: +// #include +#define PROGMEM +#define pgm_read_byte(adr) (*adr) + +#endif /* FRAMEBUFFER_CONFIG_H */ diff --git a/main.c b/main.c new file mode 100644 index 0000000..a00f003 --- /dev/null +++ b/main.c @@ -0,0 +1,110 @@ +#include +#include +#include "framebuffer.h" + +void main() { + fb_clear(); + + + const uint8_t ryba[14] = { + 0b11000011, + 0b01100110, + 0b01111110, + 0b00111100, + 0b00011000, + 0b00111100, + 0b01111110, + 0b11111111, + 0b11111111, + 0b11111011, + 0b11111111, + 0b01111110, + 0b00111100, + 0b00011000, + }; +#define ZIR_W 12 +#define ZIR_H 24 + const uint8_t zirafa[(ZIR_H/8) * ZIR_W] = { + // levo + 0b10000000, + 0b01000000, + 0b00100000, + 0b00010000, + 0b00001000, + 0b00000100, + 0b00000010, + 0b00011001, + 0b00011111, + 0b11111000, + 0b00011111, + 0b00001000, + // + 0b10000000, + 0b01000000, + 0b00100000, + 0b00010000, + 0b00001000, + 0b00000100, + 0b00000010, + 0b00000001, + 0b10000000, + 0b11111111, + 0b00100000, + 0b00010000, + // + 0b00110000, + 0b10001000, + 0b11111000, + 0b00011000, + 0b11111000, + 0b10011000, + 0b00011000, + 0b10011000, + 0b11111000, + 0b00011111, + 0b11111000, + 0b10000000, + }; + + //fb_bitmap(0, pos, 14, 8, ryba, 1); + +// fb_bitmap(0, 0, ZIR_W, ZIR_H, zirafa, 1); +// fb_bitmap(ZIR_W+2, 12, ZIR_W, ZIR_H, zirafa, 1); +// fb_bitmap((ZIR_W + 2) * 2, 2, ZIR_W, ZIR_H, zirafa, 1); + + fb_frame(0, 0, 48, 32, 2, 1); + + uint8_t x = 3; + uint8_t w = 7; + uint8_t h = 10; + uint8_t th = 1; + uint8_t sp = 3; + x += fb_7seg_dig(x, 3, w, h, th, /*val*/ 7, /*color*/ 1) + sp; + x += fb_7seg_dig(x, 3, w, h, th, /*val*/ 9, /*color*/ 1) + sp; + x += fb_7seg_period(x, 3, w, h, th, /*color*/ 1) + sp; + x += fb_7seg_dig(x, 3, w, h, th, /*val*/ 5, /*color*/ 1) + sp; + + //fb_frame(10, 7, 20, 20, 2, 1); + + fb_blit(); +} + + +void fb_blit() { + for(int y = 0; y < FBH; y++) { + for(int x = 0; x < FBW; x++) { + bool b = fb[(y/8)*FBW + x] & (1 << (y%8)); + if (b) { + printf("\x1b[1;32m#\x1b[m"); + } else { + if (y%8 == 0) { + printf("\x1b[34m.\x1b[m"); + } else { + printf("."); + } + } + } + printf("\r\n"); + } + printf("\r\n"); +}