aboutsummaryrefslogtreecommitdiff
path: root/draw.c
blob: 297f5d0b04cd75ae00e48e87a8cba6a75c5c0c40 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/* draw.c - software rendering onto an ARGB8888 mb_buffer. */
#include "draw.h"
#include "font.h"

#include <stddef.h>

/* wl_shm ARGB8888 and Win32 layered windows both hold premultiplied alpha. */
static uint32_t premul(uint32_t argb)
{
	uint32_t a = argb >> 24;
	uint32_t r = (argb >> 16) & 0xff;
	uint32_t g = (argb >> 8) & 0xff;
	uint32_t b = argb & 0xff;
	r = r * a / 255;
	g = g * a / 255;
	b = b * a / 255;
	return (a << 24) | (r << 16) | (g << 8) | b;
}

/* Fill a logical rectangle, expanding each logical pixel to a scale*scale block
 * of device pixels. Clipping is in logical space; scale=1 is the identity. */
static void fill_premul(struct mb_buffer *b, int x, int y, int w, int h,
			uint32_t pm)
{
	int s = b->scale;
	int x1 = x + w, y1 = y + h;
	if (x < 0)
		x = 0;
	if (y < 0)
		y = 0;
	if (x1 > b->w)
		x1 = b->w;
	if (y1 > b->h)
		y1 = b->h;
	int n = (x1 - x) * s;
	for (int row = y; row < y1; row++) {
		for (int sy = 0; sy < s; sy++) {
			uint32_t *p = b->px +
				      (size_t)(row * s + sy) * b->stride +
				      (size_t)x * s;
			for (int i = 0; i < n; i++)
				p[i] = pm;
		}
	}
}

void draw_fill(struct mb_buffer *b, int x, int y, int w, int h, uint32_t argb)
{
	fill_premul(b, x, y, w, h, premul(argb));
}

void draw_hline(struct mb_buffer *b, int x, int y, int len, int thick,
		uint32_t argb)
{
	fill_premul(b, x, y, len, thick, premul(argb));
}

void draw_vline(struct mb_buffer *b, int x, int y, int len, int thick,
		uint32_t argb)
{
	fill_premul(b, x, y, thick, len, premul(argb));
}

void draw_rect(struct mb_buffer *b, int x, int y, int w, int h, int thick,
	       uint32_t argb)
{
	uint32_t pm = premul(argb);
	fill_premul(b, x, y, w, thick, pm);		/* top */
	fill_premul(b, x, y + h - thick, w, thick, pm); /* bottom */
	fill_premul(b, x, y, thick, h, pm);		/* left */
	fill_premul(b, x + w - thick, y, thick, h, pm); /* right */
}

int text_height(int scale)
{
	return FONT_H * scale;
}

int text_width(const char *s, int scale)
{
	int n = 0;
	while (s[n])
		n++;
	if (n == 0)
		return 0;
	return n * (FONT_W * scale) + (n - 1) * scale; /* scale-px gaps */
}

int draw_text(struct mb_buffer *b, int x, int y, const char *s, int scale,
	      uint32_t fg)
{
	int advance = FONT_W * scale + scale;
	int w = text_width(s, scale);

	uint32_t fpm = premul(fg);
	int pen = x;
	for (const char *c = s; *c; c++) {
		const unsigned char *g = font_glyph(*c);
		for (int gy = 0; gy < FONT_H; gy++) {
			unsigned char bits = g[gy];
			for (int gx = 0; gx < FONT_W; gx++) {
				if (!(bits & (1u << gx)))
					continue;
				fill_premul(b, pen + gx * scale,
					    y + gy * scale, scale, scale, fpm);
			}
		}
		pen += advance;
	}
	return w;
}