diff --git a/os/drivers/lcd/lcd_logo.h b/os/drivers/lcd/lcd_logo.h index f7bc20ddcc..c06ee6f3e3 100644 --- a/os/drivers/lcd/lcd_logo.h +++ b/os/drivers/lcd/lcd_logo.h @@ -29,6 +29,28 @@ #define LOGO_XRES 0 #define LOGO_YRES 0 #endif /* CONFIG_LCD_LOGO_52_340 */ +#pragma pack(push, 1) +typedef struct { + uint16_t signature; + uint32_t file_size; + uint16_t reserved1; + uint16_t reserved2; + uint32_t data_offset; +} bmp_header_t; +typedef struct { + uint32_t header_size; + int32_t width; + int32_t height; + uint16_t planes; + uint16_t bits_per_pixel; + uint32_t compression; + uint32_t image_size; + int32_t x_pixels_per_meter; + int32_t y_pixels_per_meter; + uint32_t colors_used; + uint32_t colors_important; +} bmp_info_header_t; +#pragma pack(pop) #endif /* __DRIVER_LCD_LOGO_H */ diff --git a/os/drivers/lcd/mipi_lcd.c b/os/drivers/lcd/mipi_lcd.c index 2a97345c05..04fa015e83 100644 --- a/os/drivers/lcd/mipi_lcd.c +++ b/os/drivers/lcd/mipi_lcd.c @@ -28,6 +28,7 @@ #include #include #include +#include #define LCD_XRES CONFIG_LCD_XRES #define LCD_YRES CONFIG_LCD_YRES @@ -149,6 +150,64 @@ static int send_to_mipi_dsi(struct mipi_lcd_dev_s *priv, struct mipi_dsi_msg* ms return transfer_status; } +int count_trailing_zeros(uint32_t mask) +{ + if (mask == 0) + return 0; + int count = 0; + while ((mask & 1) == 0) { + mask >>= 1; + count++; + } + return count; +} + +int count_set_bits(uint32_t mask) +{ + int count = 0; + while (mask) { + count += mask & 1; + mask >>= 1; + } + return count; +} + +static uint16_t convert_pixel_to_rgb565(uint16_t bmp_pixel, uint32_t r_mask, uint32_t g_mask, uint32_t b_mask) +{ + uint8_t r, g, b; + // Extract red channel + if (r_mask == 0) { + r = 0; + } else { + uint8_t r_bits = count_set_bits(r_mask); + uint8_t r_shift = count_trailing_zeros(r_mask); + r = ((bmp_pixel & r_mask) >> r_shift) << (5 - r_bits); // Scale to 5 bits + if (r_bits > 5) + r >>= (r_bits - 5); // Scale down if source has more bits + } + // Extract green channel + if (g_mask == 0) { + g = 0; + } else { + uint8_t g_bits = count_set_bits(g_mask); + uint8_t g_shift = count_trailing_zeros(g_mask); + g = ((bmp_pixel & g_mask) >> g_shift) << (6 - g_bits); // Scale to 6 bits + if (g_bits > 6) + g >>= (g_bits - 6); // Scale down if source has more bits + } + // Extract blue channel + if (b_mask == 0) { + b = 0; + } else { + uint8_t b_bits = count_set_bits(b_mask); + uint8_t b_shift = count_trailing_zeros(b_mask); + b = ((bmp_pixel & b_mask) >> b_shift) << (5 - b_bits); // Scale to 5 bits + if (b_bits > 5) + b >>= (b_bits - 5); // Scale down if source has more bits + } + return (r << 11) | (g << 5) | b; +} + static int send_cmd(struct mipi_lcd_dev_s *priv, lcm_setting_table_t command) { int transfer_status = OK; @@ -563,6 +622,121 @@ static int lcd_setcontrast(FAR struct lcd_dev_s *dev, unsigned int contrast) return OK; } +static int lcd_render_bmp(FAR struct lcd_dev_s *dev, const char *bmp_filename) +{ + int xres = LCD_XRES; + int yres = LCD_YRES; + uint8_t *fullscreen_buffer = NULL; + FAR struct mipi_lcd_dev_s *priv = (FAR struct mipi_lcd_dev_s *)dev; + FILE *bmp_file = fopen(bmp_filename, "rb"); + if (!bmp_file) { + lcddbg("Failed to open BMP file"); + return ERROR; + } + bmp_header_t header; + bmp_info_header_t info_header; + if (fread(&header, sizeof(bmp_header_t), 1, bmp_file) != 1) { + lcddbg("Failed to read BMP header"); + goto errout; + } + if (header.signature != 0x4D42) { + lcddbg("Invalid BMP file: signature mismatch\n"); + goto errout; + } + if (fread(&info_header, sizeof(bmp_info_header_t), 1, bmp_file) != 1) { + lcddbg("Failed to read BMP info header"); + goto errout; + } + + if (info_header.bits_per_pixel != 16) { + lcddbg("Unsupported BMP format: only 16-bit color is supported (found %d-bit)\n", info_header.bits_per_pixel); + goto errout; + } + + uint32_t r_mask = 0, g_mask = 0, b_mask = 0; + if (info_header.compression == 3) { // BI_BITFIELDS + if (fread(&r_mask, sizeof(uint32_t), 1, bmp_file) != 1 || + fread(&g_mask, sizeof(uint32_t), 1, bmp_file) != 1 || + fread(&b_mask, sizeof(uint32_t), 1, bmp_file) != 1) { + lcddbg("Failed to read BMP bitmasks"); + goto errout; + } + } else if (info_header.compression == 0) { // BI_RGB + r_mask = 0x00007C00; // 0111110000000000 (5 bits for red) + g_mask = 0x000003E0; // 0000001111100000 (5 bits for green) + b_mask = 0x0000001F; // 0000000000011111 (5 bits for blue) + } else { + lcddbg("Unsupported BMP compression: %d. Only BI_RGB (0) and BI_BITFIELDS (3) are supported for 16-bit.\n", info_header.compression); + goto errout; + } + + int img_width = info_header.width; + int img_height = info_header.height; + if (img_width > yres || img_height > xres) { + lcddbg("Error: Image too large for screen\n"); + goto errout; + } + // For 16-bit, each pixel is 2 bytes. Padding is to the next 4-byte boundary. + int padding = (4 - (img_width * 2) % 4) % 4; +#if defined(CONFIG_LCD_SW_ROTATION) + fullscreen_buffer = lcd_buffer[lcd_buffer_index]; + lcd_buffer_index = (1 - lcd_buffer_index); +#else + fullscreen_buffer = (uint8_t *)kmm_malloc(xres * yres * 2 + 1); + if (!fullscreen_buffer) { + lcddbg("Failed to allocate memory for fullscreen buffer\n"); + goto errout; + } +#endif + memset(fullscreen_buffer, 0, xres * yres * 2); + + // Calculate offset to center the rotated image + int x_offset = (xres - img_height) / 2; + int y_offset = (yres - img_width) / 2; + if (x_offset < 0) { + x_offset = 0; + } + if (y_offset < 0) { + y_offset = 0; + } + + // BMP stores pixels bottom-to-top. We read row by row. + for (int src_y = 0; src_y < img_height; src_y++) { + int bmp_file_y = img_height - 1 - src_y; + long file_offset_for_row = header.data_offset + (long)bmp_file_y * (img_width * 2 + padding); + if (fseek(bmp_file, file_offset_for_row, SEEK_SET) != 0) { + lcddbg("Failed to seek to BMP row"); + goto errout; + } + // Read one row of pixel data (as 16-bit words) + uint16_t temp_row_buffer[img_width]; + if (fread(temp_row_buffer, sizeof(uint16_t), img_width, bmp_file) != img_width) { + lcddbg("Failed to read BMP pixel row"); + goto errout; + } + for (int src_x = 0; src_x < img_width; src_x++) { + int dest_x_in_rotated = src_y; + int dest_y_in_rotated = (img_width - 1) - src_x; + int target_buffer_index = ((y_offset + dest_y_in_rotated) * xres + (x_offset + dest_x_in_rotated)) * 2; + uint16_t bmp_pixel = temp_row_buffer[src_x]; + uint16_t rgb565_pixel = convert_pixel_to_rgb565(bmp_pixel, r_mask, g_mask, b_mask); + fullscreen_buffer[target_buffer_index] = (uint8_t)(rgb565_pixel & 0xFF); + fullscreen_buffer[target_buffer_index + 1] = (uint8_t)((rgb565_pixel >> 8) & 0xFF); + } + } + priv->config->lcd_put_area((u8 *)fullscreen_buffer, 1, 1, CONFIG_LCD_XRES, CONFIG_LCD_YRES); + fclose(bmp_file); + return OK; +errout: + fclose(bmp_file); +#if !defined(CONFIG_LCD_SW_ROTATION) + if (fullscreen_buffer) { + kmm_free(fullscreen_buffer); + } +#endif + return ERROR; +} + FAR void lcd_init_put_image(FAR struct lcd_dev_s *dev) { int logo_arr_index = 0; @@ -609,6 +783,7 @@ FAR void lcd_init_put_image(FAR struct lcd_dev_s *dev) priv->config->lcd_put_area((u8 *)lcd_init_fullscreen_image, 1, 1, CONFIG_LCD_XRES, CONFIG_LCD_YRES); // 1, 1 -> Start index of the frame buffer priv->config->mipi_mode_switch(VIDEO_MODE); priv->lcdonoff = LCD_ON; + lcd_render_bmp(dev, "/res/kernel/audio/img_logo_samsung.bmp"); // Displaying BMP image on LCD }