forked from Foundation-Devices/passport-firmware
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdisplay.c
187 lines (159 loc) · 5.22 KB
/
display.c
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
112
113
114
115
116
117
118
119
120
121
122
123
124
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// SPDX-FileCopyrightText: 2020 Foundation Devices, Inc.
// <[email protected]> SPDX-License-Identifier: GPL-3.0-or-later
//
// display.c - Display rendering functions for the Passport bootloader
#include <string.h>
#include "display.h"
#include "keypad-adp-5587.h"
#include "gpio.h"
static uint8_t disp_buf[SCREEN_BYTES_PER_LINE * SCREEN_HEIGHT];
static uint8_t get_image_pixel(int16_t x, int16_t y, uint16_t w, uint16_t h, uint8_t* image, uint8_t default_color)
{
if (x < 0 || x >= w || y < 0 || y >= h) {
return default_color;
}
uint16_t w_bytes = (w + 7) / 8;
uint16_t offset = (y * w_bytes) + x / 8;
uint8_t bit = 1 << (7 - x % 8);
return ((image[offset] & bit) == 0) ? 0 : 1;
}
static void set_pixel(int16_t x, int16_t y, uint8_t c)
{
if (x < 0 || x >= SCREEN_WIDTH || y < 0 || y >= SCREEN_HEIGHT) {
return;
}
uint16_t offset = (y * SCREEN_BYTES_PER_LINE) + x / 8;
uint8_t bit = 1 << (7 - x % 8);
if (c == 1) {
disp_buf[offset] |= bit;
} else {
disp_buf[offset] &= ~bit;
}
}
uint16_t display_measure_text(char* text, Font* font)
{
uint16_t width = 0;
uint16_t slen = strlen(text);
for (int i=0; i<slen; i++){
GlyphInfo glyphInfo;
glyph_lookup(font, text[i], &glyphInfo);
width += glyphInfo.advance;
}
return width;
}
void display_fill_rect(int16_t x, int16_t y, int16_t w, int16_t h, uint8_t color)
{
for (int dy = y; dy < y + h; dy++) {
for (int dx = x; dx < x + w; dx++) {
set_pixel(dx, dy, color);
}
}
}
void display_text(char* text, int16_t x, int16_t y, Font* font, bool invert)
{
if (x == CENTER_X) {
uint16_t text_width = display_measure_text(text, font);
x = SCREEN_WIDTH/2 - text_width/2;
}
uint16_t slen = strlen(text);
for (int i=0; i<slen; i++) {
GlyphInfo glyphInfo;
glyph_lookup(font, text[i], &glyphInfo);
// y + font.ascent - fn.h - fn.y
display_image(x + glyphInfo.x, y + font->ascent - glyphInfo.h - glyphInfo.y, glyphInfo.w, glyphInfo.h, glyphInfo.bitmap,
invert ? DRAW_MODE_WHITE_ONLY | DRAW_MODE_INVERT : DRAW_MODE_WHITE_ONLY);
x += glyphInfo.advance;
}
}
uint16_t display_get_char_width(char ch, Font* font)
{
GlyphInfo glyphInfo;
glyph_lookup(font, ch, &glyphInfo);
return glyphInfo.advance;
}
void display_rect(int16_t x, int16_t y, int16_t w, int16_t h, u_int8_t color)
{
// Draw the top and bottom
int16_t y_bottom = y + h - 1;
for (int dx = x; dx < x + w; dx++) {
set_pixel(dx, y, color);
set_pixel(dx, y_bottom, color);
}
// Draw the sides - repeats the top and bottom pixels to avoid special case
// code for short rectangles
int16_t x_right = x + w - 1;
for (int dy = y; dy < y + w; dy++) {
set_pixel(x, dy, color);
set_pixel(x_right, dy, color);
}
}
// Very simple and inefficient image drawing, but should be fast enough for our
// limited use.
void display_image(uint16_t x, uint16_t y, uint16_t image_w, uint16_t image_h, uint8_t* image, uint8_t mode)
{
// Iterate over the image bounds
for (int dy = 0; dy < image_h; dy++) {
for (int dx = 0; dx < image_w; dx++) {
uint8_t color = get_image_pixel(dx, dy, image_w, image_h, image, 0);
if (((mode & DRAW_MODE_BLACK_ONLY) && color == 1) || ((mode & DRAW_MODE_WHITE_ONLY) && color == 0)) {
// Skip this pixel if we are not supposed to draw it
continue;
}
if (mode & DRAW_MODE_INVERT) {
color = !color;
}
set_pixel(x + dx, y + dy, color);
}
}
}
// Assumes it's the only thing on these lines, so it does not retain any other
// image that might have been rendered there.
void display_progress_bar(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t percent)
{
// Clear whole line first
display_fill_rect(0, y, SCREEN_WIDTH-1, h, 0);
display_fill_rect(x, y, w, h, 1);
display_fill_rect(x + 2, y + 2, w - 4, h - 4, 0);
display_fill_rect(x + 3, y + 3, (w * percent) / 100 - 6, h - 6, 1);
}
void display_show(void)
{
// Disable IRQs so keypad events don't interrupt display drawing
__disable_irq();
lcd_update(disp_buf, true);
__enable_irq();
#ifndef DEBUG
// Clear the keypad interrupt so that it will retrigger if it had any events while
// interrupts were disabled, else it will hang the controller since it's waiting
// for the previous interrupt to be acknowledged.
keypad_write(KBD_ADDR, KBD_REG_INT_STAT, 0xFF);
#endif /* DEBUG */
}
void display_show_lines(uint16_t y_start, uint16_t y_end)
{
if (y_start >= SCREEN_HEIGHT) {
return;
}
if (y_end >= SCREEN_HEIGHT) {
y_end = SCREEN_HEIGHT - 1;
}
for (uint16_t y=y_start; y<=y_end; y++) {
lcd_prebuffer_line(y, &disp_buf[y * SCREEN_BYTES_PER_LINE], true);
}
lcd_update_line_range(y_start, y_end);
}
void display_clear(uint8_t color)
{
memset(disp_buf, color == 0 ? 0x00 : 0xFF, SCREEN_BYTES_PER_LINE * SCREEN_HEIGHT);
}
void display_init(bool clear)
{
lcd_init(clear);
}
// Clear the memory display and then shutdown
void display_clean_shutdown()
{
display_clear(0);
display_show();
passport_shutdown();
}