/* Copyright (c), 2001-2022, Shenshu Tech. Co., Ltd. */ #include "loadbmp.h" #include #include #include #include "securec.h" #include #include "sample_comm.h" #define BITS_NUM_PER_BYTE 8 #define BYTE_PER_PIX_1555 2 #define BYTE_PER_PIX_8888 4 #define PIX_PER_BYTE_CLUT2 4 #define PIX_PER_BYTE_CLUT4 2 #define STRIDE_ALIGN 4 #define MAX_BIT_COUNT 32 #define MAX_OFF_BITS 118 #define MAX_WIDTH 16384 #define MAX_HEIGHT 8192 #ifndef align_up #define align_up(x, a) ((((x) + ((a) - 1)) / (a)) * (a)) #endif osd_component_info g_osd_comp_info[OSD_COLOR_FORMAT_BUTT] = { { 0, 4, 4, 4 }, /* RGB444 */ { 4, 4, 4, 4 }, /* ARGB4444 */ { 0, 5, 5, 5 }, /* RGB555 */ { 0, 5, 6, 5 }, /* RGB565 */ { 1, 5, 5, 5 }, /* ARGB1555 */ { 0, 0, 0, 0 }, /* RESERVED */ { 0, 8, 8, 8 }, /* RGB888 */ { 8, 8, 8, 8 } /* ARGB8888 */ }; td_u16 osd_make_color_u16(td_u8 r, td_u8 g, td_u8 b, osd_component_info comp_info) { td_u8 r1, g1, b1; td_u16 pixel = 0; td_u32 tmp = 15; /* 15bit color data */ r1 = r >> (BITS_NUM_PER_BYTE - comp_info.r_len); g1 = g >> (BITS_NUM_PER_BYTE - comp_info.g_len); b1 = b >> (BITS_NUM_PER_BYTE - comp_info.b_len); while (comp_info.a_len) { pixel |= (1 << tmp); tmp--; comp_info.a_len--; } pixel |= (r1 | (g1 << comp_info.b_len) | (b1 << (comp_info.b_len + comp_info.g_len))); return pixel; } td_s32 get_bmp_info(const td_char *filename, osd_bit_map_file_header *bmp_file_header, osd_bit_map_info *bmp_info) { FILE *file = TD_NULL; td_u16 bf_type; td_char *path = TD_NULL; check_null_ptr_return(filename); check_null_ptr_return(bmp_file_header); check_null_ptr_return(bmp_info); if (strlen(filename) > PATH_MAX - 1) { printf("file name Extra long\n"); return TD_FAILURE; } path = realpath(filename, TD_NULL); if (path == TD_NULL) { return TD_FAILURE; } file = fopen(path, "rb"); if (file == TD_NULL) { printf("Open file failed:%s!\n", filename); goto read_bmp_failed; } (td_void)fread(&bf_type, 1, sizeof(bf_type), file); if (bf_type != 0x4d42) { /* BM */ printf("not bitmap file\n"); fclose(file); goto read_bmp_failed; } (td_void)fread(bmp_file_header, 1, sizeof(osd_bit_map_file_header), file); (td_void)fread(bmp_info, 1, sizeof(osd_bit_map_info), file); fclose(file); free(path); return TD_SUCCESS; read_bmp_failed: free(path); return TD_FAILURE; } static td_bool is_support_bmp_file(const osd_bit_map_info *bmp_info, td_u16 bpp_threshold) { td_u16 bpp; bpp = bmp_info->bmp_header.bi_bit_count / BITS_NUM_PER_BYTE; if (bpp_threshold != 0 && bpp < bpp_threshold) { printf("bitmap format not supported!\n"); return TD_FALSE; } if (bmp_info->bmp_header.bi_compression != 0) { printf("not support compressed bitmap file!\n"); return TD_FALSE; } if (bmp_info->bmp_header.bi_height < 0) { printf("bmp_info.bmp_header.bi_height < 0\n"); return TD_FALSE; } return TD_TRUE; } static td_s32 read_bmp_data(const osd_bit_map_file_header *bmp_file_header, const osd_bit_map_info *bmp_info, FILE *file, osd_logo *video_logo) { td_u8 *rgb_buf = video_logo->rgb_buf; td_u8 *orig_bmp_buf = NULL; td_u32 bmp_data_stride; td_u32 bmp_data_size; td_u16 bpp, dst_bpp; td_u16 i, j; td_s32 ret; bpp = bmp_info->bmp_header.bi_bit_count / BITS_NUM_PER_BYTE; dst_bpp = (bpp > 2) ? 4 : 2; /* RGB1555: 2byte, RGB8888: 4byte */ video_logo->width = bmp_info->bmp_header.bi_width; video_logo->height = bmp_info->bmp_header.bi_height; if (video_logo->stride == 0) { video_logo->stride = video_logo->width * dst_bpp; } bmp_data_stride = video_logo->width * bpp; bmp_data_stride = align_up(bmp_data_stride, STRIDE_ALIGN); bmp_data_size = video_logo->height * bmp_data_stride; /* RGB8888 or RGB1555 */ orig_bmp_buf = (td_u8 *)malloc(bmp_data_size); if (orig_bmp_buf == NULL) { printf("not enough memory to malloc!\n"); return TD_FAILURE; } fseek(file, bmp_file_header->bf_off_bits, 0); if (fread(orig_bmp_buf, 1, bmp_data_size, file) != bmp_data_size) { printf("fread error!line:%d\n", __LINE__); perror("fread:"); } for (i = 0; i < video_logo->height; i++) { for (j = 0; j < video_logo->width; ++j) { ret = memcpy_s(rgb_buf + i * video_logo->stride + j * dst_bpp, bpp, orig_bmp_buf + ((video_logo->height - 1) - i) * bmp_data_stride + j * bpp, bpp); if (ret != EOK) { free(orig_bmp_buf); printf("copy bmp failed!line:%d\n", __LINE__); return TD_FAILURE; } if (dst_bpp == 4) { /* 4: RGB8888 */ *(rgb_buf + i * video_logo->stride + j * dst_bpp + 3) = 0x80; /* 3: alpha offset */ } } } free(orig_bmp_buf); return TD_SUCCESS; } td_s32 load_bmp(const td_char *filename, osd_logo *video_logo) { osd_bit_map_file_header bmp_file_header; osd_bit_map_info bmp_info; FILE *file = TD_NULL; td_char *path = TD_NULL; if (filename == TD_NULL || video_logo == TD_NULL) { printf("load_bmp: null ptr args!\n"); return TD_FAILURE; } if (strlen(filename) > PATH_MAX - 1) { printf("file name Extra long\n"); return TD_FAILURE; } path = realpath(filename, TD_NULL); if (path == TD_NULL) { return TD_FAILURE; } if (get_bmp_info(path, &bmp_file_header, &bmp_info) < 0) { goto read_bmp_failed; } if (bmp_info.bmp_header.bi_bit_count > MAX_BIT_COUNT || bmp_info.bmp_header.bi_width > MAX_WIDTH || bmp_info.bmp_header.bi_height > MAX_HEIGHT || bmp_file_header.bf_off_bits > MAX_OFF_BITS) { printf("bmp info error!"); goto read_bmp_failed; } if (is_support_bmp_file(&bmp_info, 2) != TD_TRUE) { /* each pixel should takes 2 (or more) bytes */ goto read_bmp_failed; } file = fopen(path, "rb"); if (file == TD_NULL) { printf("Open file failed:%s!\n", filename); goto read_bmp_failed; } if (read_bmp_data(&bmp_file_header, &bmp_info, file, video_logo) != TD_SUCCESS) { fclose(file); goto read_bmp_failed; } fclose(file); free(path); return TD_SUCCESS; read_bmp_failed: free(path); return TD_FAILURE; } static td_void updata_osd_logo_size_info(const osd_bit_map_info *bmp_info, osd_color_format fmt, osd_logo *video_logo) { video_logo->width = bmp_info->bmp_header.bi_width; video_logo->height = bmp_info->bmp_header.bi_height; switch (fmt) { case OSD_COLOR_FORMAT_RGB444: case OSD_COLOR_FORMAT_RGB555: case OSD_COLOR_FORMAT_RGB565: case OSD_COLOR_FORMAT_RGB1555: case OSD_COLOR_FORMAT_RGB4444: video_logo->stride = video_logo->width * BYTE_PER_PIX_1555; return; case OSD_COLOR_FORMAT_RGB888: case OSD_COLOR_FORMAT_RGB8888: video_logo->stride = video_logo->width * BYTE_PER_PIX_8888; return; case OSD_COLOR_FORMAT_CLUT2: video_logo->stride = video_logo->width / PIX_PER_BYTE_CLUT2; return; case OSD_COLOR_FORMAT_CLUT4: video_logo->stride = video_logo->width / PIX_PER_BYTE_CLUT4; return; default: printf("file(%s), line(%d), no such format!\n", __FILE__, __LINE__); return; } } static td_s32 copy_original_bmp_data(td_u16 bpp, const td_u8 *data, const bmp_data_size_info *data_size_info, osd_logo *video_logo) { td_u32 i, j; td_s32 ret; td_u8 *rgb_buf = video_logo->rgb_buf; for (i = 0; i < data_size_info->height; ++i) { for (j = 0; j < data_size_info->width; ++j) { ret = memcpy_s(rgb_buf + i * video_logo->stride + j * bpp, bpp, data + ((data_size_info->height - 1) - i) * data_size_info->stride + j * bpp, bpp); if (ret != EOK) { printf("copy bmp failed!line:%d\n", __LINE__); return TD_FAILURE; } } } return TD_SUCCESS; } static td_s32 copy_clut_bmp_data(td_u16 bpp, osd_color_format fmt, const td_u8 *data, const bmp_data_size_info *data_size_info, osd_logo *video_logo) { td_u32 i, j; td_s32 ret; td_u8 *rgb_buf = video_logo->rgb_buf; td_u32 width = data_size_info->width; if (fmt == OSD_COLOR_FORMAT_CLUT4) { width /= PIX_PER_BYTE_CLUT4; } else if (fmt == OSD_COLOR_FORMAT_CLUT2) { width /= PIX_PER_BYTE_CLUT2; } else { } for (i = 0; i < data_size_info->height; ++i) { for (j = 0; j < width; ++j) { ret = memcpy_s(rgb_buf + i * video_logo->stride + j, bpp, data + ((data_size_info->height - 1) - i) * data_size_info->stride + j, bpp); if (ret != EOK) { printf("copy bmp failed!line:%d\n", __LINE__); return TD_FAILURE; } } } return TD_SUCCESS; } static td_s32 copy_2byte_bmp(td_u16 bpp, osd_color_format fmt, const td_u8 *data, const bmp_data_size_info *data_size_info, osd_logo *video_logo) { td_u32 i, j; td_u8 *rgb_buf = video_logo->rgb_buf; td_u8 r, g, b; td_u8 *start = TD_NULL; td_u16 *dst = TD_NULL; /* start color convert */ for (i = 0; i < data_size_info->height; ++i) { for (j = 0; j < data_size_info->width; ++j) { start = (td_u8 *)(data + ((data_size_info->height - 1) - i) * data_size_info->stride + j * bpp); dst = (td_u16 *)(rgb_buf + i * video_logo->stride + j * 2); /* 2 bytes */ r = *(start); g = *(start + 1); b = *(start + 2); /* 2 bytes offset */ *dst = osd_make_color_u16(r, g, b, g_osd_comp_info[fmt]); } } return TD_SUCCESS; } static td_s32 copy_4byte_bmp(td_u16 bpp, const td_u8 *data, const bmp_data_size_info *data_size_info, osd_logo *video_logo) { td_u32 i, j; td_s32 ret; td_u8 *rgb_buf = video_logo->rgb_buf; for (i = 0; i < data_size_info->height; ++i) { for (j = 0; j < data_size_info->width; ++j) { ret = memcpy_s(rgb_buf + i * video_logo->stride + j * 4, bpp, /* offset 4 bytes */ data + ((data_size_info->height - 1) - i) * data_size_info->stride + j * bpp, bpp); if (ret != EOK) { printf("copy bmp failed!line:%d\n", __LINE__); return TD_FAILURE; } *(rgb_buf + i * video_logo->stride + j * 4 + 3) = 0xff; /* 4 bytes data, alpha offset is 3 */ } } return TD_SUCCESS; } static td_s32 copy_original_bmp_data_and_fill_alpha(td_u16 bpp, osd_color_format fmt, const td_u8 *data, const bmp_data_size_info *data_size_info, osd_logo *video_logo) { switch (fmt) { case OSD_COLOR_FORMAT_RGB444: case OSD_COLOR_FORMAT_RGB555: case OSD_COLOR_FORMAT_RGB565: case OSD_COLOR_FORMAT_RGB1555: case OSD_COLOR_FORMAT_RGB4444: return copy_2byte_bmp(bpp, fmt, data, data_size_info, video_logo); case OSD_COLOR_FORMAT_RGB888: case OSD_COLOR_FORMAT_RGB8888: return copy_4byte_bmp(bpp, data, data_size_info, video_logo); default: printf("file(%s), line(%d), no such format!\n", __FILE__, __LINE__); return TD_FAILURE; } } static td_s32 read_bmp_data_ex(const osd_bit_map_file_header *bmp_file_header, const osd_bit_map_info *bmp_info, FILE *file, osd_logo *video_logo, osd_color_format fmt) { td_u8 *orig_bmp_buf = NULL; td_u16 bpp = bmp_info->bmp_header.bi_bit_count / BITS_NUM_PER_BYTE; td_u32 bmp_data_stride; td_u32 bmp_data_size; td_s32 ret = TD_SUCCESS; bmp_data_size_info data_size_info; updata_osd_logo_size_info(bmp_info, fmt, video_logo); bmp_data_stride = (bpp == 0) ? video_logo->stride : (video_logo->width * bpp); bmp_data_stride = align_up(bmp_data_stride, STRIDE_ALIGN); bmp_data_size = video_logo->height * bmp_data_stride; data_size_info.width = video_logo->width; data_size_info.height = video_logo->height; data_size_info.stride = bmp_data_stride; orig_bmp_buf = (td_u8 *)malloc(bmp_data_size); if (orig_bmp_buf == NULL) { printf("not enough memory to malloc!\n"); return TD_FAILURE; } fseek(file, bmp_file_header->bf_off_bits, 0); if (fread(orig_bmp_buf, 1, bmp_data_size, file) != bmp_data_size) { printf("fread error!line:%d\n", __LINE__); perror("fread:"); } /* copy bmp data to rgb_buf according bpp (and fmt) */ if (bpp == 2 || bpp == 4) { /* each pixel takes 2 (or 4) bytes */ ret = copy_original_bmp_data(bpp, orig_bmp_buf, &data_size_info, video_logo); goto copy_over_exit; } if (bpp <= 1) { /* such as clut2 or clut 4, or 2BPP... */ ret = copy_clut_bmp_data(bpp, fmt, orig_bmp_buf, &data_size_info, video_logo); goto copy_over_exit; } /* bpp should equal to 3 here */ ret = copy_original_bmp_data_and_fill_alpha(bpp, fmt, orig_bmp_buf, &data_size_info, video_logo); copy_over_exit: free(orig_bmp_buf); return ret; } td_s32 load_bmp_ex(const td_char *filename, osd_logo *video_logo, osd_color_format fmt) { osd_bit_map_file_header bmp_file_header; osd_bit_map_info bmp_info; FILE *file = TD_NULL; td_char *path = TD_NULL; if (filename == TD_NULL || video_logo == TD_NULL) { printf("load_bmp_ex: null ptr args!\n"); return TD_FAILURE; } if (strlen(filename) > PATH_MAX - 1) { printf("file name Extra long\n"); return TD_FAILURE; } path = realpath(filename, TD_NULL); if (path == TD_NULL) { return TD_FAILURE; } if (get_bmp_info(path, &bmp_file_header, &bmp_info) < 0) { goto read_bmp_failed; } if (bmp_info.bmp_header.bi_bit_count > MAX_BIT_COUNT || bmp_info.bmp_header.bi_width > MAX_WIDTH || bmp_info.bmp_header.bi_height > MAX_HEIGHT || bmp_file_header.bf_off_bits > MAX_OFF_BITS) { printf("bmp info error!"); goto read_bmp_failed; } if (is_support_bmp_file(&bmp_info, 0) != TD_TRUE) { goto read_bmp_failed; } file = fopen(path, "rb"); if (file == TD_NULL) { printf("Open file failed:%s!\n", filename); goto read_bmp_failed; } if (read_bmp_data_ex(&bmp_file_header, &bmp_info, file, video_logo, fmt) != TD_SUCCESS) { fclose(file); goto read_bmp_failed; } fclose(file); free(path); return TD_SUCCESS; read_bmp_failed: free(path); return TD_FAILURE; } static td_s32 read_bmp_canvas(const osd_bit_map_file_header *bmp_file_header, const osd_bit_map_info *bmp_info, FILE *file, osd_logo *video_logo, osd_color_format fmt) { td_u8 *orig_bmp_buf = NULL; td_u16 bpp = bmp_info->bmp_header.bi_bit_count / BITS_NUM_PER_BYTE; td_u32 bmp_data_size; td_s32 ret = TD_SUCCESS; bmp_data_size_info data_size_info; data_size_info.width = bmp_info->bmp_header.bi_width; data_size_info.height = bmp_info->bmp_header.bi_height; if (bpp == 0) { if (fmt == OSD_COLOR_FORMAT_CLUT2) { data_size_info.stride = data_size_info.width / PIX_PER_BYTE_CLUT2; } else if (fmt == OSD_COLOR_FORMAT_CLUT4) { data_size_info.stride = data_size_info.width / PIX_PER_BYTE_CLUT4; } else { data_size_info.stride = video_logo->stride; } } else { data_size_info.stride = data_size_info.width * bpp; data_size_info.stride = align_up(data_size_info.stride, STRIDE_ALIGN); } bmp_data_size = data_size_info.height * data_size_info.stride; orig_bmp_buf = (td_u8 *)malloc(bmp_data_size); if (orig_bmp_buf == NULL) { printf("not enough memory to malloc!\n"); return TD_FAILURE; } fseek(file, bmp_file_header->bf_off_bits, 0); if (fread(orig_bmp_buf, 1, bmp_data_size, file) != bmp_data_size) { printf("fread error!line:%d\n", __LINE__); perror("fread:"); } /* copy bmp data to rgb_buf according bpp (and fmt) */ if (bpp == 2 || bpp == 4) { /* each pixel takes 2 (or 4) bytes */ ret = copy_original_bmp_data(bpp, orig_bmp_buf, &data_size_info, video_logo); goto copy_over_exit; } if (bpp <= 1) { /* such as clut2 or clut 4, or 2BPP... */ ret = copy_clut_bmp_data(bpp, fmt, orig_bmp_buf, &data_size_info, video_logo); goto copy_over_exit; } /* bpp should equal to 3 here */ ret = copy_original_bmp_data_and_fill_alpha(bpp, fmt, orig_bmp_buf, &data_size_info, video_logo); copy_over_exit: free(orig_bmp_buf); return ret; } td_s32 load_bmp_canvas(const td_char *filename, osd_logo *video_logo, osd_color_format fmt) { osd_bit_map_file_header bmp_file_header; osd_bit_map_info bmp_info; FILE *file = TD_NULL; td_char *path = TD_NULL; if (filename == TD_NULL || video_logo == TD_NULL) { printf("load_bmp_canvas: null ptr args!\n"); return TD_FAILURE; } if (strlen(filename) > PATH_MAX - 1) { printf("file name Extra long\n"); return TD_FAILURE; } path = realpath(filename, TD_NULL); if (path == TD_NULL) { return TD_FAILURE; } if (get_bmp_info(path, &bmp_file_header, &bmp_info) < 0) { goto read_bmp_failed; } if (bmp_info.bmp_header.bi_bit_count > MAX_BIT_COUNT || bmp_info.bmp_header.bi_width > MAX_WIDTH || bmp_info.bmp_header.bi_height > MAX_HEIGHT || bmp_file_header.bf_off_bits > MAX_OFF_BITS) { printf("bmp info error!"); goto read_bmp_failed; } if (is_support_bmp_file(&bmp_info, 0) != TD_TRUE) { goto read_bmp_failed; } file = fopen(path, "rb"); if (file == TD_NULL) { printf("Open file failed:%s!\n", filename); goto read_bmp_failed; } if (read_bmp_canvas(&bmp_file_header, &bmp_info, file, video_logo, fmt) != TD_SUCCESS) { fclose(file); goto read_bmp_failed; } fclose(file); free(path); return TD_SUCCESS; read_bmp_failed: free(path); return TD_FAILURE; } td_char *get_ext_name(const td_char *filename) { td_char *pret = TD_NULL; size_t len; if (filename == TD_NULL) { printf("filename can't be null!"); return TD_NULL; } len = strlen(filename); while (len) { pret = (td_char *)(filename + len); if (*pret == '.') { return (pret + 1); } len--; } return pret; } td_s32 load_image(const td_char *filename, osd_logo *video_logo) { td_char *ext = get_ext_name(filename); if (ext == TD_NULL) { printf("get_ext_name error!\n"); return -1; } if (strcmp(ext, "bmp") == 0) { if (load_bmp(filename, video_logo) != 0) { printf("load_bmp error!\n"); return -1; } } else { printf("not supported image file!\n"); return -1; } return 0; } td_s32 load_image_ex(const td_char *filename, osd_logo *video_logo, osd_color_format fmt) { td_char *ext = get_ext_name(filename); if (ext == TD_NULL) { printf("load_image_ex error!\n"); return -1; } if (strcmp(ext, "bmp") == 0) { if (load_bmp_ex(filename, video_logo, fmt) != 0) { printf("load_bmp_ex error!\n"); return -1; } } else { printf("not supported image file!\n"); return -1; } return 0; } td_s32 load_canvas_ex(const td_char *filename, osd_logo *video_logo, osd_color_format fmt) { td_char *ext = get_ext_name(filename); if (ext == TD_NULL) { printf("load_canvas_ex error!\n"); return -1; } if (strcmp(ext, "bmp") == 0) { if (load_bmp_canvas(filename, video_logo, fmt) != 0) { printf("OSD_LoadBMP error!\n"); return -1; } } else { printf("not supported image file!\n"); return -1; } return 0; } td_s32 load_bit_map_to_surface(const td_char *file_name, const osd_surface *surface, td_u8 *virt) { osd_logo logo; check_null_ptr_return(file_name); check_null_ptr_return(surface); logo.stride = surface->stride; logo.rgb_buf = virt; return load_image(file_name, &logo); } td_s32 create_surface_by_bit_map(const td_char *file_name, osd_surface *surface, td_u8 *virt) { osd_logo logo; check_null_ptr_return(file_name); check_null_ptr_return(surface); logo.rgb_buf = virt; if (load_image_ex(file_name, &logo, surface->color_format) < 0) { printf("load bmp error!\n"); return -1; } surface->height = logo.height; surface->width = logo.width; surface->stride = logo.stride; return 0; } td_s32 create_surface_by_canvas(const td_char *file_name, osd_surface *surface, td_u8 *virt, const canvas_size_info *canvas_size) { osd_logo logo; check_null_ptr_return(file_name); check_null_ptr_return(surface); check_null_ptr_return(canvas_size); logo.rgb_buf = virt; logo.width = canvas_size->width; logo.height = canvas_size->height; logo.stride = canvas_size->stride; if (load_canvas_ex(file_name, &logo, surface->color_format) < 0) { printf("load bmp error!\n"); return -1; } surface->height = canvas_size->height; surface->width = canvas_size->width; surface->stride = canvas_size->stride; return 0; }