api.c 7.1 KB
Newer Older
1
2
3
4
#include "epicardium.h"
#include "drivers/display/lcd.h"
#include "drivers/display/epic_ctx.h"

Gerd's avatar
Gerd committed
5
#include "FreeRTOS.h"
6
#include "LCD_Driver.h"
7
8
9
10
#include "gpio.h"
#include "task.h"
#include "tmr.h"
#include "tmr_utils.h"
11
12

#include <machine/endian.h>
13
#include <string.h>
Gerd's avatar
Gerd committed
14
15
16
17
18
19
20
21
22
23
24
25
26

static TaskHandle_t lock = NULL;

static int check_lock()
{
	TaskHandle_t task = xTaskGetCurrentTaskHandle();
	if (task != lock) {
		return -EBUSY;
	} else {
		return 0;
	}
}

27
28
29
30
31
32
33
34
35
36
37
38
39
40
static uint16_t rgb888_to_rgb565(uint8_t *bytes)
{
	return ((bytes[0] & 0b11111000) << 8) | ((bytes[1] & 0b11111100) << 3) |
	       (bytes[2] >> 3);
}

static inline void
rgb565_to_rgb888(uint16_t pixel, uint8_t *red, uint8_t *green, uint8_t *blue)
{
	*blue  = (pixel & 31) << 3;
	*green = ((pixel >> 5) & 63) << 2;
	*red   = ((pixel >> 11) & 31) << 3;
}

Gerd's avatar
Gerd committed
41
int epic_disp_print(
42
43
	int16_t posx,
	int16_t posy,
Gerd's avatar
Gerd committed
44
45
46
	const char *pString,
	uint16_t fg,
	uint16_t bg
47
48
49
50
) {
	return epic_disp_print_adv(DISP_FONT20, posx, posy, pString, fg, bg);
}

51
52
53
static const float font_map[] = {
	[DISP_FONT8] = 8.0f,   [DISP_FONT12] = 12.0f, [DISP_FONT16] = 16.0f,
	[DISP_FONT20] = 20.0f, [DISP_FONT24] = 24.0f,
54
55
56
57
};

int epic_disp_print_adv(
	uint8_t font,
58
59
	int16_t posx,
	int16_t posy,
60
61
62
	const char *pString,
	uint16_t fg,
	uint16_t bg
Gerd's avatar
Gerd committed
63
) {
64
	uint8_t r, g, b;
Gerd's avatar
Gerd committed
65
66
67
68
	int cl = check_lock();
	if (cl < 0) {
		return cl;
	}
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

	if (font >= (sizeof(font_map) / sizeof(font_map[0]))) {
		return -EINVAL;
	}

	float font_size = font_map[font];
	ctx_font_size(epicardium_ctx, font_size);

	if (fg != bg) {
		/* non-transparent background */
		rgb565_to_rgb888(bg, &r, &g, &b);
		ctx_rgba8(epicardium_ctx, r, g, b, 255);
		float width = ctx_text_width(epicardium_ctx, pString);
		ctx_rectangle(epicardium_ctx, posx, posy, width, font_size);
		ctx_fill(epicardium_ctx);
	}

	rgb565_to_rgb888(fg, &r, &g, &b);
	ctx_rgba8(epicardium_ctx, r, g, b, 255);
	ctx_move_to(epicardium_ctx, posx, (float)posy + font_size * 0.8f);
	ctx_text(epicardium_ctx, pString);

	return 0;
Gerd's avatar
Gerd committed
92
93
94
95
96
97
98
99
}

int epic_disp_clear(uint16_t color)
{
	int cl = check_lock();
	if (cl < 0) {
		return cl;
	}
100
101
102
103
104
105
106
107
108
109
110

	/*
	 * We could use ctx for this but it's much easier to just clear the
	 * framebuffer directly.
	 */
	for (size_t i = 0; i < sizeof(epicardium_ctx_fb); i += 2) {
		epicardium_ctx_fb[i]     = color >> 8;
		epicardium_ctx_fb[i + 1] = color & 0xff;
	}

	return 0;
Gerd's avatar
Gerd committed
111
112
}

113
int epic_disp_pixel(int16_t x, int16_t y, uint16_t color)
114
115
116
117
118
{
	int cl = check_lock();
	if (cl < 0) {
		return cl;
	}
119
120
121
122
123
124

	uint8_t r, g, b;
	rgb565_to_rgb888(color, &r, &g, &b);
	ctx_set_pixel_u8(epicardium_ctx, x, y, r, g, b, 255);

	return 0;
125
126
}

127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
static uint16_t rgb565_pixel_from_buf(
	uint8_t *img,
	enum epic_rgb_format format,
	int16_t width,
	int16_t x,
	int16_t y,
	uint8_t *alpha
) {
	uint16_t tmp16;
	uint8_t rgba[4];

	switch (format) {
	case EPIC_RGB565:
		*alpha = 255;
		memcpy(&tmp16, &img[y * width * 2 + x * 2], 2);
		return tmp16;
	case EPIC_RGBA5551:
		memcpy(&tmp16, &img[y * width * 2 + x * 2], 2);
		*alpha = (tmp16 & 0x01) ? 255 : 0;
		return (tmp16 & 0xFFC0) | ((tmp16 & 0x3E) >> 1);
	case EPIC_RGB8:
		*alpha = 255;
		memcpy(rgba, &img[y * width * 3 + x * 3], 3);
		return rgb888_to_rgb565(rgba);
	case EPIC_RGBA8:
		memcpy(rgba, &img[y * width * 4 + x * 4], 4);
		*alpha = rgba[3];
		return rgb888_to_rgb565(rgba);
	default:
		return 0xFFFF;
	}
}

160
161
162
163
164
int epic_disp_blit(
	int16_t pos_x,
	int16_t pos_y,
	int16_t width,
	int16_t height,
165
166
	void *img,
	enum epic_rgb_format format
167
168
169
170
) {
	int cl = check_lock();
	if (cl < 0) {
		return cl;
171
	}
172

173
174
175
176
177
178
179
180
181
182
183
184
185
186
	for (int16_t xsrc = 0; xsrc < width; xsrc += 1) {
		for (int16_t ysrc = 0; ysrc < height; ysrc += 1) {
			int16_t xscreen = pos_x + xsrc;
			int16_t yscreen = pos_y + ysrc;
			size_t offset   = yscreen * 160 * 2 + xscreen * 2;
			if (xscreen < 0 || xscreen >= 160 || yscreen < 0 ||
			    yscreen >= 80) {
				continue;
			}

			uint8_t alpha  = 255;
			uint16_t pixel = rgb565_pixel_from_buf(
				img, format, width, xsrc, ysrc, &alpha
			);
187

188
189
190
			if (alpha == 0) {
				continue;
			}
191

192
193
			epicardium_ctx_fb[offset]     = (pixel & 0xFF00) >> 8;
			epicardium_ctx_fb[offset + 1] = pixel & 0xFF;
194
195
		}
	}
196
197

	return 0;
198
199
}

Gerd's avatar
Gerd committed
200
int epic_disp_line(
201
202
203
204
	int16_t xstart,
	int16_t ystart,
	int16_t xend,
	int16_t yend,
Gerd's avatar
Gerd committed
205
	uint16_t color,
Rahix's avatar
Rahix committed
206
	enum disp_linestyle linestyle,
Gerd's avatar
Gerd committed
207
208
209
210
211
212
	uint16_t pixelsize
) {
	int cl = check_lock();
	if (cl < 0) {
		return cl;
	}
213

214
215
216
217
218
219
220
221
222
223
224
225
226
	float xstartf = xstart, ystartf = ystart, xendf = xend, yendf = yend;

	/*
	 * For odd line widths, shift the line by half a pixel so it aligns
	 * perfectly with the pixel grid.
	 */
	if (pixelsize % 2 == 1) {
		xstartf += 0.5f;
		ystartf += 0.5f;
		yendf += 0.5f;
		xendf += 0.5f;
	}

227
228
229
230
231
	uint8_t r, g, b;
	rgb565_to_rgb888(color, &r, &g, &b);
	ctx_rgba8_stroke(epicardium_ctx, r, g, b, 255);

	ctx_line_width(epicardium_ctx, pixelsize);
232
233
	ctx_move_to(epicardium_ctx, xstartf, ystartf);
	ctx_line_to(epicardium_ctx, xendf, yendf);
234
235
236
	ctx_stroke(epicardium_ctx);

	return 0;
Gerd's avatar
Gerd committed
237
238
239
}

int epic_disp_rect(
240
241
242
243
	int16_t xstart,
	int16_t ystart,
	int16_t xend,
	int16_t yend,
Gerd's avatar
Gerd committed
244
	uint16_t color,
Rahix's avatar
Rahix committed
245
	enum disp_fillstyle fillstyle,
Gerd's avatar
Gerd committed
246
247
248
	uint16_t pixelsize
) {
	int cl = check_lock();
249
	if (cl < 0)
Gerd's avatar
Gerd committed
250
		return cl;
251

252
253
254
255
	uint8_t r, g, b;
	rgb565_to_rgb888(color, &r, &g, &b);

	ctx_rectangle(
256
257
258
259
260
		epicardium_ctx,
		xstart,
		ystart,
		xend - xstart + 1,
		yend - ystart + 1
261
262
	);

263
264
	switch (fillstyle) {
	case FILLSTYLE_EMPTY:
265
266
267
		ctx_rgba8_stroke(epicardium_ctx, r, g, b, 255);
		ctx_line_width(epicardium_ctx, pixelsize);
		ctx_stroke(epicardium_ctx);
268
269
		break;
	case FILLSTYLE_FILLED:
270
271
		ctx_rgba8(epicardium_ctx, r, g, b, 255);
		ctx_fill(epicardium_ctx);
272
		break;
Gerd's avatar
Gerd committed
273
	}
274

275
	return 0;
Gerd's avatar
Gerd committed
276
277
278
}

int epic_disp_circ(
279
280
	int16_t x,
	int16_t y,
Gerd's avatar
Gerd committed
281
282
	uint16_t rad,
	uint16_t color,
Rahix's avatar
Rahix committed
283
	enum disp_fillstyle fillstyle,
Gerd's avatar
Gerd committed
284
285
286
	uint16_t pixelsize
) {
	int cl = check_lock();
287
	if (cl < 0)
Gerd's avatar
Gerd committed
288
		return cl;
289

290
291
292
	uint8_t r, g, b;
	rgb565_to_rgb888(color, &r, &g, &b);

293
	ctx_arc(epicardium_ctx, x, y, rad, 0.0f, CTX_PI * 1.95, 0);
294

295
296
	switch (fillstyle) {
	case FILLSTYLE_EMPTY:
297
298
299
		ctx_rgba8_stroke(epicardium_ctx, r, g, b, 255);
		ctx_line_width(epicardium_ctx, pixelsize);
		ctx_stroke(epicardium_ctx);
300
301
		break;
	case FILLSTYLE_FILLED:
302
303
		ctx_rgba8(epicardium_ctx, r, g, b, 255);
		ctx_fill(epicardium_ctx);
304
		break;
Gerd's avatar
Gerd committed
305
	}
306
307

	return 0;
Gerd's avatar
Gerd committed
308
309
310
311
312
313
314
315
}

int epic_disp_update()
{
	int cl = check_lock();
	if (cl < 0) {
		return cl;
	}
316

317
318
	lcd_write_fb(epicardium_ctx_fb);

319
320
321
322
323
324
325
326
327
328
	return 0;
}

int epic_disp_framebuffer(union disp_framebuffer *fb)
{
	int cl = check_lock();
	if (cl < 0) {
		return cl;
	}

329
330
331
332
333
334
335
	/*
	 * Flip the screen because that's what this API call historically
	 * expects.
	 */
	lcd_set_screenflip(true);
	lcd_write_fb(fb->raw);
	lcd_set_screenflip(false);
336
	return 0;
Gerd's avatar
Gerd committed
337
338
}

339
340
int epic_disp_backlight(uint16_t brightness)
{
341
	/* TODO: lock? */
342
343
344
345
346
	if (brightness == 0) {
		lcd_set_sleep(true);
	} else {
		lcd_set_sleep(false);
	}
347
348
349
350
	LCD_SetBacklight(brightness);
	return 0;
}

Gerd's avatar
Gerd committed
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
int epic_disp_open()
{
	TaskHandle_t task = xTaskGetCurrentTaskHandle();
	if (lock == task) {
		return 0;
	} else if (lock == NULL) {
		lock = task;
		return 0;
	} else {
		return -EBUSY;
	}
}

int epic_disp_close()
{
	if (check_lock() < 0 && lock != NULL) {
		return -EBUSY;
	} else {
		lock = NULL;
		return 0;
	}
}

374
375
376
377
378
void disp_update_backlight_clock(void)
{
	LCD_UpdateBacklightClock();
}

Gerd's avatar
Gerd committed
379
380
381
382
383
void disp_forcelock()
{
	TaskHandle_t task = xTaskGetCurrentTaskHandle();
	lock              = task;
}