Skip to content
Snippets Groups Projects
Forked from card10 / firmware
1417 commits behind the upstream repository.
gfx.c 5.19 KiB
#include "gfx.h"
#include "framebuffer.h"
#include <stddef.h>
#include <stdlib.h>
#include <math.h>

const struct gfx_color_rgb gfx_colors_rgb[COLORS] = {
	{ 255, 255, 255 }, /* WHITE */
	{ 0, 0, 0 },       /* BLACK */
	{ 255, 0, 0 },     /* RED */
	{ 0, 255, 0 },     /* GREEN */
	{ 0, 0, 255 },     /* BLUE */
	{ 255, 255, 0 }    /* YELLOW */
};

void gfx_setpixel(struct gfx_region *r, int x, int y, Color c)
{
	if (x < 0 || y < 0)
		return;
	if (x >= r->width || y >= r->height)
		return;

	fb_setpixel(r->fb, r->x + x, r->y + y, c);
}

struct gfx_region gfx_screen(struct framebuffer *fb)
{
	struct gfx_region r = { .fb     = fb,
				.x      = 0,
				.y      = 0,
				.width  = fb->width,
				.height = fb->height };
	return r;
}

static inline int letter_bit(sFONT *font, char c, int x, int y)
{
	if (x < 0 || y < 0)
		return 0;
	if (x >= font->Width || y >= font->Height)
		return 0;
	if (c < ' ' || c > '~')
		return 0;

	size_t bytes_per_row      = font->Width / 8 + 1;
	size_t bytes_per_letter   = bytes_per_row * font->Height;
	int letter                = c - ' ';
	const uint8_t *letter_ptr = font->table + bytes_per_letter * letter;
	int horz_byte             = x / 8;
	int horz_bit              = 7 - x % 8;

	return (*(letter_ptr + y * bytes_per_row + horz_byte) >> horz_bit) & 1;
}

void gfx_putchar(
	sFONT *font,
	struct gfx_region *r,
	int x,
	int y,
	char ch,
	Color fg,
	Color bg
) {
	for (int yo = 0; yo < font->Height; yo++) {
		for (int xo = 0; xo < font->Width; xo++) {
			int lb = letter_bit(font, ch, xo, yo);

			if (fg != bg) {
				Color c = lb ? fg : bg;
				gfx_setpixel(r, x + xo, y + yo, c);
			} else {
				if (lb) {
					gfx_setpixel(r, x + xo, y + yo, fg);
				}
			}
		}
	}
}

void gfx_puts(
	sFONT *font,
	struct gfx_region *r,
	int x,
	int y,
	const char *str,
	Color fg,
	Color bg
) {
	while (*str) {
		gfx_putchar(font, r, x, y, *str, fg, bg);
		str++;

		x += font->Width;
		if (x >= r->width) {
			x = 0;
			y += font->Height;
		}
		if (y >= r->height)
			return;
	}
}

Color gfx_color_rgb_f(struct gfx_region *reg, float r, float g, float b)
{
	return fb_encode_color_rgb_f(reg->fb, r, g, b);
}

Color gfx_color_rgb(struct gfx_region *reg, uint8_t r, uint8_t g, uint8_t b)
{
	return fb_encode_color_rgb(reg->fb, r, g, b);
}

void gfx_update(struct gfx_region *reg)
{
	reg->fb->update(reg->fb);
}

void gfx_clear_to_color(struct gfx_region *reg, Color c)
{
	fb_clear_to_color(reg->fb, c);
}

void gfx_clear(struct gfx_region *reg)
{
	gfx_clear_to_color(reg, gfx_color(reg, BLACK));
}

void gfx_circle(struct gfx_region *reg, int x, int y, int r, int t, Color c)
{
	for (int y_ = y - r; y_ <= y + r; y_++) {
		for (int x_ = x - r; x_ <= x + r; x_++) {
			int dx    = (x_ - x) * (x_ - x);
			int dy    = (y_ - y) * (y_ - y);
			int outer = (r + t) * (r + t);
			int inner = r * r;
			int edge = ((dx + dy) >= inner) && ((dx + dy) <= outer);
			if (edge)
				gfx_setpixel(reg, x_, y_, c);
		}
	}
}

void gfx_circle_fill(struct gfx_region *reg, int x, int y, int r, Color c)
{
	for (int y_ = y - r; y_ <= y + r; y_++) {
		for (int x_ = x - r; x_ <= x + r; x_++) {
			int dx   = (x_ - x) * (x_ - x);
			int dy   = (y_ - y) * (y_ - y);
			int edge = r * r;
			int fill = (dx + dy) <= edge;
			if (fill)
				gfx_setpixel(reg, x_, y_, c);
		}
	}
}

void gfx_rectangle(
	struct gfx_region *reg, int x, int y, int w, int h, int t, Color c
) {
	if (t > 1) {
		gfx_thick_line(reg, x, y, x + w, y, t, c);
		gfx_thick_line(reg, x, y + h, x + w, y + h, t, c);
		gfx_thick_line(reg, x, y, x, y + h, t, c);
		gfx_thick_line(reg, x + w, y, x + w, y + h, t, c);
	} else {
		gfx_line(reg, x, y, x + w, y, c);
		gfx_line(reg, x, y + h, x + w, y + h, c);
		gfx_line(reg, x, y, x, y + h, c);
		gfx_line(reg, x + w, y, x + w, y + h, c);
	}
}

void gfx_rectangle_fill(
	struct gfx_region *reg, int x, int y, int w, int h, Color c
) {
	for (int y_ = y; y_ < y + h; y_++) {
		for (int x_ = x; x_ < x + w; x_++)
			gfx_setpixel(reg, x_, y_, c);
	}
}

void gfx_line(struct gfx_region *reg, int x1, int y1, int x2, int y2, Color c)
{
	float dx = x2 - x1;
	float dy = y2 - y1;
	float de = fabs(dy / dx);
	float e  = .0f;
	int y    = y1;
	for (int x = x1; x < x2; x++) {
		gfx_setpixel(reg, x, y, c);
		e += de;
		if (e >= .5f) {
			y += dy >= .0f ? 1 : -1;
			e -= 1.0f;
		}
	}
}

void gfx_thick_line(
	struct gfx_region *reg, int x1, int y1, int x2, int y2, int t, Color c
) {
	float dx = x2 - x1;
	float dy = y2 - y1;
	float de = fabs(dy / dx);
	float e  = .0f;
	int y    = y1;
	for (int x = x1; x < x2; x++) {
		gfx_circle_fill(reg, x, y, t, c);
		e += de;
		if (e >= .5f) {
			y += dy >= .0f ? 1 : -1;
			e -= 1.0f;
		}
	}
}

Color gfx_color(struct gfx_region *reg, enum gfx_color color)
{
	if ((int)(color) >= COLORS)
		return 0;

	const struct gfx_color_rgb *c = &gfx_colors_rgb[color];
	return gfx_color_rgb(reg, c->r, c->g, c->b);
}

void gfx_copy_region_raw(
	struct gfx_region *reg,
	int x,
	int y,
	int w,
	int h,
	size_t bpp,
	const void *p
) {
	for (int y_ = 0; y_ < h; y_++) {
		for (int x_ = 0; x_ < w; x_++) {
			Color c;

			switch (bpp) {
			default:
			case 2:
				c = *(const uint16_t *)(p);
				break;
			}

			gfx_setpixel(reg, x + x_, y + y_, c);
			p += bpp;
		}
	}
}

void gfx_copy_raw(struct gfx_region *reg, const void *p, size_t size)
{
	fb_copy_raw(reg->fb, p, size);
}