Files
capacitance-lcd-driver/main/write.c

192 lines
5.9 KiB
C

#include "write.h"
#include "eye_image.h"
#include "icon_image.h"
#include "lcd_reg.h"
typedef struct {
uint16_t icon_id;
uint16_t x;
uint16_t y;
} icon_draw_t;
static uint16_t lcd_swap_color(uint16_t color)
{
return (uint16_t)((color >> 8) | (color << 8));
}
static const icon_image_source_t *write_get_icon(uint16_t icon_id)
{
#if ICON_IMAGE_SOURCE_COUNT == 0
(void)icon_id;
return 0;
#else
if (icon_id >= ICON_IMAGE_SOURCE_COUNT) {
return 0;
}
return &icon_image_sources[icon_id];
#endif
}
static const eye_image_frame_t *write_get_frame(uint16_t image_id)
{
if (image_id >= EYE_IMAGE_SOURCE_COUNT) {
return 0;
}
const eye_image_source_t *source = &eye_image_sources[image_id];
if (source->frame_count == 0) {
return 0;
}
return &source->frames[0];
}
static void lcd_overlay_logic(uint16_t *pixel_buffer, uint16_t row_start, uint16_t row_count, const icon_draw_t *icons, uint32_t icon_count)
{
for (uint32_t icon_index = 0; icon_index < icon_count; icon_index++) {
const icon_draw_t *draw = &icons[icon_index];
const icon_image_source_t *icon = write_get_icon(draw->icon_id);
if (icon == 0 || icon->width == 0 || icon->height == 0) {
continue;
}
if (icon->frame_count == 0) {
continue;
}
if (draw->x >= LCD_WIDTH || draw->y >= LCD_HEIGHT) {
continue;
}
const eye_image_frame_t *frame = &icon->frames[0];
if (frame->runs == 0 || frame->run_count == 0) {
continue;
}
uint16_t draw_width = icon->width;
uint16_t draw_height = icon->height;
if (draw_width > LCD_WIDTH - draw->x) {
draw_width = LCD_WIDTH - draw->x;
}
if (draw_height > LCD_HEIGHT - draw->y) {
draw_height = LCD_HEIGHT - draw->y;
}
uint16_t row_end = row_start + row_count;
uint16_t overlay_y0 = draw->y;
uint16_t overlay_y1 = draw->y + draw_height;
if (overlay_y1 <= row_start || overlay_y0 >= row_end) {
continue;
}
uint16_t copy_y0 = overlay_y0 > row_start ? overlay_y0 : row_start;
uint16_t copy_y1 = overlay_y1 < row_end ? overlay_y1 : row_end;
for (uint16_t y = copy_y0; y < copy_y1; y++) {
uint16_t local_y = y - row_start;
uint16_t image_y = y - draw->y;
uint32_t icon_pixel_start = (uint32_t)image_y * icon->width;
uint32_t icon_pixel_end = icon_pixel_start + draw_width;
uint32_t icon_pixel_pos = 0;
for (uint32_t run_index = 0; run_index < frame->run_count && icon_pixel_pos < icon_pixel_end; run_index++) {
uint32_t run_start = icon_pixel_pos;
uint32_t run_end = run_start + frame->runs[run_index].count;
if (run_end > icon_pixel_start && run_start < icon_pixel_end) {
uint32_t copy_start = run_start > icon_pixel_start ? run_start : icon_pixel_start;
uint32_t copy_end = run_end < icon_pixel_end ? run_end : icon_pixel_end;
uint16_t color = lcd_swap_color(frame->runs[run_index].color);
for (uint32_t x = copy_start; x < copy_end; x++) {
pixel_buffer[(uint32_t)local_y * LCD_WIDTH + draw->x + (x - icon_pixel_start)] = color;
}
}
icon_pixel_pos = run_end;
}
}
}
}
static void lcd_draw_image(const rle_structure_t *runs, uint32_t run_count, const icon_draw_t *icons, uint32_t icon_count)
{
enum { ROWS_PER_TRANSFER = 8 };
enum { PIXELS_PER_TRANSFER = LCD_WIDTH * ROWS_PER_TRANSFER };
static uint16_t pixel_buffer[PIXELS_PER_TRANSFER];
uint32_t run_index = 0;
uint32_t run_remaining = 0;
uint16_t run_color = 0;
if (runs == 0) {
return;
}
lcd_set_window(0, 0, LCD_WIDTH - 1, LCD_HEIGHT - 1);
for (uint16_t row_start = 0; row_start < LCD_HEIGHT; row_start += ROWS_PER_TRANSFER) {
uint16_t row_count = LCD_HEIGHT - row_start;
uint32_t pixels_needed;
uint32_t buffered_pixels = 0;
if (row_count > ROWS_PER_TRANSFER) {
row_count = ROWS_PER_TRANSFER;
}
pixels_needed = (uint32_t)LCD_WIDTH * row_count;
while (buffered_pixels < pixels_needed) {
if (run_remaining == 0) {
if (run_index >= run_count) {
break;
}
run_color = lcd_swap_color(runs[run_index].color);
run_remaining = runs[run_index].count;
run_index++;
}
uint32_t pixels_to_copy = run_remaining;
uint32_t pixels_left = pixels_needed - buffered_pixels;
if (pixels_to_copy > pixels_left) {
pixels_to_copy = pixels_left;
}
for (uint32_t i = 0; i < pixels_to_copy; i++) {
pixel_buffer[buffered_pixels + i] = run_color;
}
buffered_pixels += pixels_to_copy;
run_remaining -= pixels_to_copy;
}
while (buffered_pixels < pixels_needed) {
pixel_buffer[buffered_pixels++] = 0;
}
if (icons != 0 && icon_count > 0) {
lcd_overlay_logic(pixel_buffer, row_start, row_count, icons, icon_count);
}
lcd_write_pixels(pixel_buffer, pixels_needed * 2);
}
}
void lcd_play_image(uint16_t image_id, uint16_t icon_id, uint16_t x, uint16_t y, uint32_t delay_ms)
{
const icon_draw_t icon = {icon_id, x, y};
const eye_image_frame_t *frame = write_get_frame(image_id);
const icon_draw_t *icons = icon_id == LCD_NO_ICON ? 0 : &icon;
uint32_t icon_count = icon_id == LCD_NO_ICON ? 0 : 1;
if (frame != 0 && frame->runs != 0) {
lcd_draw_image(frame->runs, frame->run_count, icons, icon_count);
}
lcd_delay_ms(delay_ms);
}