192 lines
5.9 KiB
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);
|
|
}
|