You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

187 lines
5.2 KiB

// SPDX-FileCopyrightText: 2020 Foundation Devices, Inc.
// <hello@foundationdevices.com> 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();
}