/* * Display_Image.c * * Created: Thu Nov 25 2021 13:15:42 * Author Chris */ #include "Display_Image.h" #include #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 *******************************************************************/