This commit is contained in:
2026-06-27 20:03:48 +02:00
commit 2fa69a20a1
4 changed files with 146 additions and 0 deletions

4
CMakeLists.txt Normal file
View File

@@ -0,0 +1,4 @@
idf_component_register(
SRCS "gfx.c"
INCLUDE_DIRS "."
)

7
README.md Normal file
View File

@@ -0,0 +1,7 @@
# Simple graphics library
Will draw Adafruit GFX compatible fonts, lines, squares, and it will also fill a square. It can draw or clear any of these. That's it, graphics as God intended.
Made for ST7920 with my own driver library, but it should be pretty universal, it just needs draw and clear functions taking x,y coords of a pixel + some user context.
Idk why I added a readme, it's pretty self documenting. Maybe i'll break this into it's own git repo and add it as submodule, along with the disp driver.

92
gfx.c Normal file
View File

@@ -0,0 +1,92 @@
#include "gfx.h"
#include <stdbool.h>
void gfx_draw_char(const gfx_canvas_t *canvas, const GFXfont *f, const bool inverted, const int16_t x, const int16_t y, const char c) {
void (*draw_cb)(void* ctx, uint8_t x, uint8_t y) = inverted ? canvas->clear_pixel_callback : canvas->draw_pixel_callback;
if (c < f->first || c > f->last) return;
const GFXglyph *g = &f->glyph[c - f->first];
const uint8_t *bm = f->bitmap + g->bitmapOffset;
uint16_t bit = 0; // current bit index into bitmap bytes
for (uint8_t row = 0; row < g->height; row++)
for (uint8_t col = 0; col < g->width; col++, bit++)
if (bm[bit / 8] & (0x80 >> (bit & 7)))
draw_cb(canvas->ctx, x + g->xOffset + col, y + g->yOffset + row);
}
int16_t gfx_string_width(const GFXfont *f, const char *s) {
int16_t w = 0;
while (*s) {
const char c = *s++;
if (c >= f->first && c <= f->last)
w += f->glyph[c - f->first].xAdvance;
}
return w;
}
/* returns x position of the end of the string */
int16_t gfx_draw_string(const gfx_canvas_t *canvas, const GFXfont *f, int16_t x, const int16_t y, const char *s, const bool inverted) {
while (*s) {
gfx_draw_char(canvas, f, inverted, x, y, *s);
const char c = *s++;
if (c >= f->first && c <= f->last)
x += f->glyph[c - f->first].xAdvance;
}
return x;
}
int16_t gfx_draw_string_centered(const gfx_canvas_t *canvas, const GFXfont *f, const int16_t centerpoint, const int16_t y, const char *s, const bool inverted) {
const uint8_t x = centerpoint - gfx_string_width(f, s) / 2;
return gfx_draw_string(canvas, f, x, y, s, inverted);
}
/* only draws top->bottom and left->right */
void gfx_draw_line(const gfx_canvas_t *canvas, const uint8_t start_x, const uint8_t start_y, const uint8_t end_x, const uint8_t end_y, const bool inverted) {
void (*draw_cb)(void* ctx, uint8_t x, uint8_t y) = inverted ? canvas->clear_pixel_callback : canvas->draw_pixel_callback;
const uint8_t dx = end_x - start_x;
const uint8_t dy = end_y - start_y;
// if it's a straight line just draw it
if (dx == 0) {
for (uint8_t y = start_y; y < end_y; y++)
draw_cb(canvas->ctx, start_x, y);
return;
}
if (dy == 0) {
for (uint8_t x = start_x; x < end_x; x++)
draw_cb(canvas->ctx, x, start_y);
return;
}
// if not, do bresenham's line algo
int16_t D = 2*dy - dx;
uint8_t y = start_y;
for (uint8_t x = start_x; x <= end_x; x++) {
draw_cb(canvas->ctx, x, y);
if (D > 0) {
y++;
D += (2 * (dy - dx));
} else {
D += 2*dy;
}
}
}
void gfx_draw_box(const gfx_canvas_t *canvas, const uint8_t pos_x, const uint8_t pos_y, const uint8_t size_x, const uint8_t size_y, const bool inverted) {
gfx_draw_line(canvas, pos_x, pos_y, pos_x + size_x - 1, pos_y, inverted);
gfx_draw_line(canvas, pos_x + size_x - 1, pos_y, pos_x + size_x - 1, pos_y + size_y - 1, inverted);
gfx_draw_line(canvas, pos_x, pos_y + size_y - 1, pos_x + size_x, pos_y + size_y - 1, inverted);
gfx_draw_line(canvas, pos_x, pos_y, pos_x, pos_y + size_y, inverted);
}
void gfx_fill_box(const gfx_canvas_t *canvas, const uint8_t pos_x, const uint8_t pos_y, const uint8_t size_x, const uint8_t size_y, const bool inverted) {
void (*draw_cb)(void* ctx, uint8_t x, uint8_t y) = inverted ? canvas->clear_pixel_callback : canvas->draw_pixel_callback;
for (uint8_t x = pos_x; x < pos_x + size_x; x++)
for (uint8_t y = pos_y; y < pos_y + size_y; y++)
draw_cb(canvas->ctx, x, y);
}

43
gfx.h Normal file
View File

@@ -0,0 +1,43 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
// compatibility for adafruit fonts
#ifndef PROGMEM
#define PROGMEM
#endif
typedef struct {
uint16_t bitmapOffset;
uint8_t width, height;
uint8_t xAdvance;
int8_t xOffset, yOffset;
} GFXglyph;
typedef struct {
const uint8_t *bitmap;
const GFXglyph *glyph;
uint8_t first, last;
uint8_t yAdvance;
} GFXfont;
/**
* @brief Canvas struct for the gfx library
* Functions take the context and x,y coordinates of a pixel
*
* @param ctx any context that the draw/clear functions might need, ie display device
* @param draw_pixel_callback function that gets called to draw a pixel
* @param clear_pixel_callback function that gets called to clear a pixel
*/
typedef struct {
void (*draw_pixel_callback)(void *ctx, uint8_t x, uint8_t y);
void (*clear_pixel_callback)(void *ctx, uint8_t x, uint8_t y);
void *ctx;
} gfx_canvas_t;
int16_t gfx_string_width(const GFXfont *f, const char *s);
int16_t gfx_draw_string(const gfx_canvas_t *canvas, const GFXfont* f, int16_t x, int16_t y, const char *s, bool inverted);
int16_t gfx_draw_string_centered(const gfx_canvas_t *canvas, const GFXfont *f, int16_t centerpoint, int16_t y, const char *s, bool inverted);
void gfx_draw_line(const gfx_canvas_t *canvas, uint8_t start_x, uint8_t start_y, uint8_t end_x, uint8_t end_y, bool inverted);
void gfx_draw_box(const gfx_canvas_t *canvas, uint8_t pos_x, uint8_t pos_y, uint8_t size_x, uint8_t size_y, bool inverted);
void gfx_fill_box(const gfx_canvas_t *canvas, uint8_t pos_x, uint8_t pos_y, uint8_t size_x, uint8_t size_y, bool inverted);