404 lines
14 KiB
C
404 lines
14 KiB
C
/*
|
|
* Display_Image.c
|
|
*
|
|
* Created: Thu Nov 25 2021 13:15:42
|
|
* Author Chris
|
|
*/
|
|
#include "Display_Image.h"
|
|
|
|
#include <math.h>
|
|
#include "Display_Color.h"
|
|
#include "Display_Shapes.h"
|
|
|
|
|
|
// ============================================================================================
|
|
// Defines
|
|
|
|
|
|
// ============================================================================================
|
|
// Variables
|
|
static Display_Image_Buffer** _Current_Buffer;
|
|
static Display_Color _Transparent_Color;
|
|
|
|
static const uint8_t IMAGE_DEF_WIDTH = 0;
|
|
static const uint8_t IMAGE_DEF_HEIGHT = 1;
|
|
static const uint8_t IMAGE_DEF_DATA_SIZE = 2;
|
|
static const uint8_t IMAGE_DEF_DATA_OFFSET = 3;
|
|
|
|
|
|
// ============================================================================================
|
|
// Function Declarations
|
|
|
|
|
|
/*******************************************************************
|
|
Functions
|
|
*******************************************************************/
|
|
void Display_Image_Init(Display_Image_Buffer** current_buffer, Display_Color transparent_color)
|
|
{
|
|
_Current_Buffer = current_buffer;
|
|
_Transparent_Color = transparent_color;
|
|
}
|
|
|
|
void Display_Image_Draw_Color(int16_t x, int16_t y, Image_Color* image)
|
|
{
|
|
Display_Image_Draw_Color_Alpha(x, y, image, UINT8_MAX);
|
|
}
|
|
|
|
void Display_Image_Draw_Color_Alpha(int16_t x, int16_t y, Image_Color* image, uint8_t alpha)
|
|
{
|
|
if(image == NULL) {
|
|
return;
|
|
}
|
|
|
|
// Early exit for fully transparent
|
|
if(alpha == 0) {
|
|
return;
|
|
}
|
|
|
|
int16_t Width = image[IMAGE_DEF_WIDTH];
|
|
int16_t Height = image[IMAGE_DEF_HEIGHT];
|
|
|
|
// Early exit for completely off-screen images
|
|
if(x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT || x + Width <= 0 || y + Height <= 0) {
|
|
return;
|
|
}
|
|
|
|
// Calculate clipping bounds
|
|
int16_t Start_X = (x < 0) ? -x : 0;
|
|
int16_t Start_Y = (y < 0) ? -y : 0;
|
|
int16_t End_X = (x + Width > DISPLAY_WIDTH) ? DISPLAY_WIDTH - x : Width;
|
|
int16_t End_Y = (y + Height > DISPLAY_HEIGHT) ? DISPLAY_HEIGHT - y : Height;
|
|
|
|
// Optimized rendering based on alpha value
|
|
if(alpha == 255)
|
|
{
|
|
// Fully opaque - use direct copy for maximum performance
|
|
for(int16_t iy = Start_Y; iy < End_Y; iy++)
|
|
{
|
|
int16_t Screen_Y = y + iy;
|
|
uint32_t Src_Row_Offset = IMAGE_DEF_DATA_OFFSET + iy * Width;
|
|
|
|
for(int16_t ix = Start_X; ix < End_X; ix++)
|
|
{
|
|
Display_Color Pixel_Color = image[Src_Row_Offset + ix];
|
|
|
|
if(Pixel_Color != _Transparent_Color) {
|
|
(*_Current_Buffer)->Dim_2[Screen_Y][x + ix] = Pixel_Color;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Alpha blending required
|
|
for(int16_t iy = Start_Y; iy < End_Y; iy++)
|
|
{
|
|
int16_t Screen_Y = y + iy;
|
|
uint32_t Src_Row_Offset = IMAGE_DEF_DATA_OFFSET + iy * Width;
|
|
|
|
for(int16_t ix = Start_X; ix < End_X; ix++)
|
|
{
|
|
Display_Color Src_Pixel = image[Src_Row_Offset + ix];
|
|
|
|
if(Src_Pixel != _Transparent_Color) {
|
|
int16_t Screen_X = x + ix;
|
|
Display_Color Dst_Pixel = (*_Current_Buffer)->Dim_2[Screen_Y][Screen_X];
|
|
Display_Color Blended_Pixel = Display_Color_Blend_RGB565(Src_Pixel, Dst_Pixel, alpha);
|
|
(*_Current_Buffer)->Dim_2[Screen_Y][Screen_X] = Blended_Pixel;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Display_Image_Draw_Color_Scaled(int16_t x, int16_t y, Image_Color* image, float scale)
|
|
{
|
|
Display_Image_Draw_Color_Scaled_Alpha(x, y, image, scale, 255);
|
|
}
|
|
|
|
void Display_Image_Draw_Color_Scaled_Alpha(int16_t x, int16_t y, Image_Color* image, float scale, uint8_t alpha)
|
|
{
|
|
if(image == NULL || scale <= 0.0f) {
|
|
return;
|
|
}
|
|
|
|
// Early exit for fully transparent
|
|
if(alpha == 0) {
|
|
return;
|
|
}
|
|
|
|
int16_t Original_Width = image[IMAGE_DEF_WIDTH];
|
|
int16_t Original_Height = image[IMAGE_DEF_HEIGHT];
|
|
|
|
// Calculate scaled dimensions
|
|
int16_t Scaled_Width = (int16_t)(Original_Width * scale);
|
|
int16_t Scaled_Height = (int16_t)(Original_Height * scale);
|
|
|
|
// Early exit for completely off-screen images
|
|
if(x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT || x + Scaled_Width <= 0 || y + Scaled_Height <= 0) {
|
|
return;
|
|
}
|
|
|
|
// Calculate clipping bounds
|
|
int16_t Start_X = (x < 0) ? -x : 0;
|
|
int16_t Start_Y = (y < 0) ? -y : 0;
|
|
int16_t End_X = (x + Scaled_Width > DISPLAY_WIDTH) ? DISPLAY_WIDTH - x : Scaled_Width;
|
|
int16_t End_Y = (y + Scaled_Height > DISPLAY_HEIGHT) ? DISPLAY_HEIGHT - y : Scaled_Height;
|
|
|
|
// Pre-calculate fixed-point inverse scale factors
|
|
uint32_t Inv_Scale_X = (uint32_t)((65536.0f) / scale); // 16.16 fixed point
|
|
uint32_t Inv_Scale_Y = (uint32_t)((65536.0f) / scale);
|
|
|
|
// Optimized scaling with alpha blending
|
|
if(alpha == 255) {
|
|
// Fully opaque - direct buffer access for maximum performance
|
|
for(int16_t iy = Start_Y; iy < End_Y; iy++)
|
|
{
|
|
// Calculate source Y coordinate using fixed-point math
|
|
int16_t Orig_Y = (iy * Inv_Scale_Y) >> 16;
|
|
|
|
if(Orig_Y >= Original_Height) {
|
|
break;
|
|
}
|
|
|
|
// Pre-calculate source row offset
|
|
uint32_t Src_Row_Offset = IMAGE_DEF_DATA_OFFSET + Orig_Y * Original_Width;
|
|
int16_t Screen_Y = y + iy;
|
|
|
|
for(int16_t ix = Start_X; ix < End_X; ix++)
|
|
{
|
|
// Calculate source X coordinate using fixed-point math
|
|
int16_t Orig_X = (ix * Inv_Scale_X) >> 16;
|
|
|
|
if(Orig_X >= Original_Width) {
|
|
break;
|
|
}
|
|
|
|
Display_Color Pixel_Color = image[Src_Row_Offset + Orig_X];
|
|
|
|
if(Pixel_Color != _Transparent_Color) {
|
|
// Direct buffer write
|
|
(*_Current_Buffer)->Dim_2[Screen_Y][x + ix] = Pixel_Color;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Alpha blending required
|
|
for(int16_t iy = Start_Y; iy < End_Y; iy++)
|
|
{
|
|
// Calculate source Y coordinate using fixed-point math
|
|
int16_t Orig_Y = (iy * Inv_Scale_Y) >> 16;
|
|
|
|
if(Orig_Y >= Original_Height) {
|
|
break;
|
|
}
|
|
|
|
// Pre-calculate source row offset
|
|
uint32_t Src_Row_Offset = IMAGE_DEF_DATA_OFFSET + Orig_Y * Original_Width;
|
|
int16_t Screen_Y = y + iy;
|
|
|
|
for(int16_t ix = Start_X; ix < End_X; ix++)
|
|
{
|
|
// Calculate source X coordinate using fixed-point math
|
|
int16_t Orig_X = (ix * Inv_Scale_X) >> 16;
|
|
|
|
if(Orig_X >= Original_Width) {
|
|
break;
|
|
}
|
|
|
|
Display_Color Src_Pixel = image[Src_Row_Offset + Orig_X];
|
|
|
|
if(Src_Pixel != _Transparent_Color) {
|
|
int16_t Screen_X = x + ix;
|
|
Display_Color Dst_Pixel = (*_Current_Buffer)->Dim_2[Screen_Y][Screen_X];
|
|
Display_Color Blended_Pixel = Display_Color_Blend_RGB565(Src_Pixel, Dst_Pixel, alpha);
|
|
(*_Current_Buffer)->Dim_2[Screen_Y][Screen_X] = Blended_Pixel;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Display_Image_Draw_Color_Rotated(int16_t x, int16_t y, Image_Color* image, uint16_t angle)
|
|
{
|
|
Display_Image_Draw_Color_Rotated_Alpha(x, y, image, angle, UINT8_MAX);
|
|
}
|
|
|
|
void Display_Image_Draw_Color_Rotated_Alpha(int16_t x, int16_t y, Image_Color* image, uint16_t angle, uint8_t alpha)
|
|
{
|
|
if(image == NULL) {
|
|
return;
|
|
}
|
|
|
|
// Early exit for fully transparent
|
|
if(alpha == 0) {
|
|
return;
|
|
}
|
|
|
|
int16_t Height = image[IMAGE_DEF_HEIGHT];
|
|
int16_t Width = image[IMAGE_DEF_WIDTH];
|
|
|
|
// Adjust coordinates so rotation center is at (x, y)
|
|
x += (Width >> 1);
|
|
y += (Height >> 1);
|
|
|
|
// Convert angle to radians
|
|
float Angle_Rad = (angle % 360) * M_PI / 180.0f;
|
|
float Cos_Angle = cosf(Angle_Rad);
|
|
float Sin_Angle = sinf(Angle_Rad);
|
|
|
|
// Calculate image center
|
|
float Center_X = Width / 2.0f;
|
|
float Center_Y = Height / 2.0f;
|
|
|
|
// Calculate bounding box of rotated image
|
|
float Corners_X[4] = {0, Width-1, Width-1, 0};
|
|
float Corners_Y[4] = {0, 0, Height-1, Height-1};
|
|
|
|
int16_t Min_X = 0, Max_X = 0, Min_Y = 0, Max_Y = 0;
|
|
|
|
for(int i = 0; i < 4; i++) {
|
|
float rx = (Corners_X[i] - Center_X) * Cos_Angle - (Corners_Y[i] - Center_Y) * Sin_Angle;
|
|
float ry = (Corners_X[i] - Center_X) * Sin_Angle + (Corners_Y[i] - Center_Y) * Cos_Angle;
|
|
|
|
if(i == 0) {
|
|
Min_X = Max_X = (int16_t)roundf(rx);
|
|
Min_Y = Max_Y = (int16_t)roundf(ry);
|
|
} else {
|
|
if(rx < Min_X) Min_X = (int16_t)roundf(rx);
|
|
if(rx > Max_X) Max_X = (int16_t)roundf(rx);
|
|
if(ry < Min_Y) Min_Y = (int16_t)roundf(ry);
|
|
if(ry > Max_Y) Max_Y = (int16_t)roundf(ry);
|
|
}
|
|
}
|
|
|
|
// Optimized rendering based on alpha value
|
|
if(alpha == 255)
|
|
{
|
|
// Fully opaque - use direct pixel writes for maximum performance
|
|
for(int16_t dy = Min_Y; dy <= Max_Y; dy++)
|
|
{
|
|
for(int16_t dx = Min_X; dx <= Max_X; dx++)
|
|
{
|
|
// Reverse rotation to find source pixel
|
|
float Src_X = dx * Cos_Angle + dy * Sin_Angle + Center_X;
|
|
float Src_Y = -dx * Sin_Angle + dy * Cos_Angle + Center_Y;
|
|
|
|
// Check if source coordinates are within image bounds
|
|
int16_t ix = (int16_t)roundf(Src_X);
|
|
int16_t iy = (int16_t)roundf(Src_Y);
|
|
|
|
if(ix >= 0 && ix < Width && iy >= 0 && iy < Height)
|
|
{
|
|
Display_Color Pixel = image[IMAGE_DEF_DATA_OFFSET + iy * Width + ix];
|
|
if(Pixel != _Transparent_Color) {
|
|
// Calculate screen coordinates
|
|
int16_t Screen_X = x + dx;
|
|
int16_t Screen_Y = y + dy;
|
|
|
|
// Bounds check for screen buffer
|
|
if(Screen_X >= 0 && Screen_X < DISPLAY_WIDTH && Screen_Y >= 0 && Screen_Y < DISPLAY_HEIGHT) {
|
|
(*_Current_Buffer)->Dim_2[Screen_Y][Screen_X] = Pixel;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Alpha blending required
|
|
for(int16_t dy = Min_Y; dy <= Max_Y; dy++)
|
|
{
|
|
for(int16_t dx = Min_X; dx <= Max_X; dx++)
|
|
{
|
|
// Reverse rotation to find source pixel
|
|
float Src_X = dx * Cos_Angle + dy * Sin_Angle + Center_X;
|
|
float Src_Y = -dx * Sin_Angle + dy * Cos_Angle + Center_Y;
|
|
|
|
// Check if source coordinates are within image bounds
|
|
int16_t ix = (int16_t)roundf(Src_X);
|
|
int16_t iy = (int16_t)roundf(Src_Y);
|
|
|
|
if(ix >= 0 && ix < Width && iy >= 0 && iy < Height) {
|
|
Display_Color Src_Pixel = image[IMAGE_DEF_DATA_OFFSET + iy * Width + ix];
|
|
if(Src_Pixel != _Transparent_Color) {
|
|
// Calculate screen coordinates
|
|
int16_t Screen_X = x + dx;
|
|
int16_t Screen_Y = y + dy;
|
|
|
|
// Bounds check for screen buffer
|
|
if(Screen_X >= 0 && Screen_X < DISPLAY_WIDTH && Screen_Y >= 0 && Screen_Y < DISPLAY_HEIGHT) {
|
|
Display_Color Dst_Pixel = (*_Current_Buffer)->Dim_2[Screen_Y][Screen_X];
|
|
Display_Color Blended_Pixel = Display_Color_Blend_RGB565(Src_Pixel, Dst_Pixel, alpha);
|
|
(*_Current_Buffer)->Dim_2[Screen_Y][Screen_X] = Blended_Pixel;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Display_Image_Draw_Alpha(int16_t x, int16_t y, Image_Alpha* image, Display_Color color)
|
|
{
|
|
if(image == NULL) { return; }
|
|
|
|
int16_t Height = image[IMAGE_DEF_HEIGHT];
|
|
int16_t Width = image[IMAGE_DEF_WIDTH];
|
|
|
|
for(int16_t iy = 0; iy < Height ; iy++)
|
|
{
|
|
for (int16_t ix = 0; ix < Width; ix++)
|
|
{
|
|
if(image[IMAGE_DEF_DATA_OFFSET + iy * Width + ix] == UINT8_MAX) {
|
|
Display_Shapes_Draw_Pixel_Safe(x + ix, y + iy, color);
|
|
}
|
|
else if(image[IMAGE_DEF_DATA_OFFSET + iy * Width + ix] > 0) {
|
|
// This is only temporary here...
|
|
// Color needs to be scaled according to alpha value from image array
|
|
// Skipped for now, as only threshold images will be applied
|
|
Display_Shapes_Draw_Pixel_Safe(x + ix, y + iy, color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
uint16_t Display_Image_Get_Width(Image_Color* image)
|
|
{
|
|
if(image == NULL) { return 0; }
|
|
|
|
return image[IMAGE_DEF_WIDTH];
|
|
}
|
|
|
|
uint16_t Display_Image_Get_Height(Image_Color* image)
|
|
{
|
|
if(image == NULL) { return 0; }
|
|
|
|
return image[IMAGE_DEF_HEIGHT];
|
|
}
|
|
|
|
uint16_t Display_Image_Get_Scaled_Width(Image_Color* image, float scale)
|
|
{
|
|
if(image == NULL || scale <= 0.0f) { return 0; }
|
|
|
|
uint16_t Original_Width = image[IMAGE_DEF_WIDTH];
|
|
return (uint16_t)(Original_Width * scale);
|
|
}
|
|
|
|
uint16_t Display_Image_Get_Scaled_Height(Image_Color* image, float scale)
|
|
{
|
|
if(image == NULL || scale <= 0.0f) { return 0; }
|
|
|
|
uint16_t Original_Height = image[IMAGE_DEF_HEIGHT];
|
|
return (uint16_t)(Original_Height * scale);
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
Internal Functions
|
|
*******************************************************************/
|
|
|
|
|