#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); }