/* * (C) Copyright 2020 Rockchip Electronics Co., Ltd * * SPDX-License-Identifier: GPL-2.0+ * Author: Wenping Zhang */ #include #include #include #include struct bmp_header { /* Header */ char signature[2]; uint32_t file_size; uint32_t reserved; uint32_t data_offset; /* InfoHeader */ uint32_t size; uint32_t width; uint32_t height; uint16_t planes; uint16_t bit_count; uint32_t compression; uint32_t image_size; uint32_t x_pixels_per_m; uint32_t y_pixels_per_m; uint32_t colors_used; uint32_t colors_important; /* ColorTable */ } __attribute__((packed)); struct bmp_image { struct bmp_header hdr; uint8_t color_table[0]; }; struct pixel_u16 { uint16_t blue : 4, green : 4, red : 4, alpha : 4; } __attribute__((packed)); struct pixel_u24 { uint8_t blue; uint8_t green; uint8_t red; } __attribute__((packed)); struct pixel_u32 { uint8_t blue; uint8_t green; uint8_t red; uint8_t alpha; } __attribute__((packed)); //logo partition Header, 64byte struct logo_part_header { char magic[4]; /* must be "RKEL" */ uint32_t totoal_size; uint32_t screen_width; uint32_t screen_height; uint32_t logo_count; char version[4]; uint32_t rsv[10]; } __attribute__((packed)); // logo image header,32 byte struct grayscale_header { char magic[4]; /* must be "GR04" */ uint16_t x; uint16_t y; uint16_t w; uint16_t h; uint32_t logo_type; uint32_t data_offset; /* image offset in byte */ uint32_t data_size; /* image size in byte */ uint32_t rsv[2]; } __attribute__((packed)); /* * The start address of logo image in logo.img must be aligned * in 512 bytes,so the header size must be times of 512 bytes. * Here we fix the size to 512 bytes, so the count of logo image * can only support up to 14. */ struct logo_info { struct logo_part_header part_hdr; struct grayscale_header img_hdr[14]; } __attribute__((packed)); struct input_img_info { char path[256]; int logo_type; }; /* * Every part of logo.img must be aligned in RK_BLK_SIZE, * use macro aligned_in_blk to calculate the the real size */ #define RK_BLK_SIZE 512 #define ALIGN(x, y) (((x) + (y) - 1) & ~((y) - 1)) struct input_img_info in_img_info[16]; uint32_t screen_w; uint32_t screen_h; static const char version[4] = "1.00"; static const char *PROG; static const char *fix_path(const char *path) { if (!memcmp(path, "./", 2)) return path + 2; return path; } static void print_version(void) { printf("Version %s (zwp@rock-chips.com)\n", version); } void usage(void) { printf("Usage: %s [options] [arguments]\n\n", PROG); print_version(); printf("\t --uboot-logo path"); printf("\t\t\t Pack uboot logo to logo.img from given path\n"); printf("\t --charge-logo path"); printf("\t\t\t Pack charge logo to logo.img from given path\n"); printf("\t --lowpower-logo path"); printf("\t\t\t Pack low power logo to logo.img from given path\n"); printf("\t --kernel-logo path"); printf("\t\t\t Pack low power logo to logo.img from given path\n"); printf("\t --poweroff-logo path"); printf("\t\t\t Pack power off logo to logo.img from given path\n"); printf("\t --output path"); printf("\t\t\t Output the grayscale image to path\n"); } static inline int size_of_header(void) { return ALIGN(sizeof(struct logo_info), RK_BLK_SIZE); } static inline int size_of_one_image(void) { return ALIGN((screen_w * screen_h) >> 1, RK_BLK_SIZE); } int get_logo_resolution(char *in_path, uint32_t *logo_width, uint32_t *logo_height) { struct bmp_header bmp_hdr; FILE *file; int size; int ret = 0; if (!in_path) fprintf(stderr, "Invalid bmp file path.\n"); file = fopen(in_path, "rb"); if (!file) { fprintf(stderr, "File %s open failed.\n", in_path); return -1; } size = sizeof(struct bmp_header); if (size != fread(&bmp_hdr, 1, size, file)) { fprintf(stderr, "Incomplete read of file %s.\n", in_path); ret = -1; goto out; } if (!(bmp_hdr.signature[0] == 'B' && bmp_hdr.signature[1] == 'M')) { printf("cat not find bmp file\n"); ret = -1; goto out; } *logo_width = bmp_hdr.width; *logo_height = bmp_hdr.height; fprintf(stderr, "logo resolution is %d x %d.\n", *logo_width, *logo_height); out: fclose(file); return ret; } /* * The bmp pixel is scan from left-bottom to right-top */ int convert_bmp_idx_to_gray_idx(int idx, uint32_t w, uint32_t h) { int row = h - (idx / w) - 1; return (row * w + idx % w) / 2; } int convert_one_image(char *in_path, void *out_buf, uint32_t offset, struct grayscale_header *gr_hdr, int type) { struct bmp_image *bmp; struct bmp_header *bmp_hdr; FILE *file; void *bmp_buf; int size; int ret = -1; uint8_t *gr16_data = (uint8_t *)out_buf; if (!out_buf || !in_path) { fprintf(stderr, "in_path or out_buf is NULL.\n"); return -1; } file = fopen(in_path, "rb"); if (!file) { fprintf(stderr, "File %s open failed.\n", in_path); return -1; } fseek(file, 0, SEEK_END); size = ftell(file); fseek(file, 0, SEEK_SET); bmp_buf = calloc(1, size); if (!bmp_buf) { fprintf(stderr, "Allocate memory of %d bytes failed.\n", size); fclose(file); return -1; } if (size != fread(bmp_buf, 1, size, file)) { fprintf(stderr, "Incomplete read of file %s.\n", in_path); goto out; } bmp = (struct bmp_image *)bmp_buf; bmp_hdr = &bmp->hdr; if (!(bmp_hdr->signature[0] == 'B' && bmp_hdr->signature[1] == 'M')) { printf("cat not find bmp file\n"); goto out; } if (size != le32_to_cpu(bmp_hdr->file_size)) { fprintf(stderr, "Invalid BMP file size %d.\n", le32_to_cpu(bmp_hdr->file_size)); goto out; } printf("bmp_hdr->width=%d, bmp_hdr->height=%d\n", bmp_hdr->width, bmp_hdr->height); printf("screen_w=%d, screen_h=%d\n", screen_w, screen_h); if (le32_to_cpu(bmp_hdr->width) != screen_w || le32_to_cpu(bmp_hdr->height) != screen_h) { fprintf(stderr, "The image size must same with others.\n"); goto out; } //write header gr_hdr->magic[0] = 'G'; gr_hdr->magic[1] = 'R'; gr_hdr->magic[2] = '0'; gr_hdr->magic[3] = '4'; gr_hdr->x = 0; gr_hdr->y = 0; gr_hdr->w = screen_w; gr_hdr->h = screen_h; gr_hdr->logo_type = type; gr_hdr->data_offset = offset; gr_hdr->data_size = screen_w * screen_h / 2; printf("bmp depth is %d\n", bmp_hdr->bit_count); //convert rgb to gray data, and write to output buffer // the used algorithm please refer to below url: // https://www.cnblogs.com/zhangjiansheng/p/6925722.html // we use below algorithm: // Gray = (R*19595 + G*38469 + B*7472) >> 16 switch (bmp_hdr->bit_count) { case 16:{ struct pixel_u16 *color_u16; int i; color_u16 = (struct pixel_u16 *)bmp->color_table; for (i = 0; i < screen_w * screen_h / 2; i++) { struct pixel_u16 *pix1 = &color_u16[2 * i]; struct pixel_u16 *pix2 = &color_u16[2 * i + 1]; int j = convert_bmp_idx_to_gray_idx(2 * i, screen_w, screen_h); /* * the rgb value of pixel_u16 is 4 bits, * so the counted grayscale value is 4bit */ uint32_t gray_px1 = (pix1->red * 19595 + pix1->green * 38469 + pix1->blue * 7472) >> 16; uint32_t gray_px2 = (pix2->red * 19595 + pix2->green * 38469 + pix2->blue * 7472) >> 16; gr16_data[j] = gray_px1 | (gray_px2 << 4); } } break; case 24: { struct pixel_u24 *color_u24; int i; color_u24 = (struct pixel_u24 *)bmp->color_table; for (i = 0; i < screen_w * screen_h / 2; i++) { struct pixel_u24 *pix1 = &color_u24[2 * i]; struct pixel_u24 *pix2 = &color_u24[2 * i + 1]; int j = convert_bmp_idx_to_gray_idx(2 * i, screen_w, screen_h); /* * The rgb value of pixel_u24 is 8 bits, * so the counted grayscale * value need to divide into 16 */ uint32_t gray_px1 = ((pix1->red * 19595 + pix1->green * 38469 + pix1->blue * 7472) >> 16) >> 4; uint32_t gray_px2 = ((pix2->red * 19595 + pix2->green * 38469 + pix2->blue * 7472) >> 16) >> 4; gr16_data[j] = gray_px1 | (gray_px2 << 4); } } break; case 32: { struct pixel_u32 *color_u32; int i; color_u32 = (struct pixel_u32 *)bmp->color_table; for (i = 0; i < screen_w * screen_h / 2; i++) { struct pixel_u32 *pix1 = &color_u32[2 * i]; struct pixel_u32 *pix2 = &color_u32[2 * i + 1]; int j = convert_bmp_idx_to_gray_idx(2 * i, screen_w, screen_h); /* * The rgb value of pixel_u32 is 8 bits, * so the counted grayscale * value need to divide into 16 */ uint32_t gray_px1 = ((pix1->red * 19595 + pix1->green * 38469 + pix1->blue * 7472) >> 16) >> 4; uint32_t gray_px2 = ((pix2->red * 19595 + pix2->green * 38469 + pix2->blue * 7472) >> 16) >> 4; gr16_data[j] = gray_px1 | (gray_px2 << 4); } } break; default: ret = -1; printf("Invalid bit count[%d],only support 16/24/32 bpp bmp\n", bmp_hdr->bit_count); break; } fprintf(stderr, "Convert image success\n"); ret = 0; out: free(bmp_buf); fclose(file); return ret; } void *init_grayscale_logo_buf(int logo_count, uint32_t size_one_image) { int size; void *out_buf; if (!logo_count) { fprintf(stderr, "No input logo!\n"); return NULL; } size = size_of_header(); fprintf(stderr, "size of header in logo.img is %d\n", size); //every pixel of the grayscale image is 4 bits size += logo_count * size_one_image; out_buf = calloc(1, size); return out_buf; } void deinit_grayscale_logo_buf(void *buf) { if (buf) { free(buf); buf = NULL; } } int main(int argc, char *argv[]) { char out_path[256] = {0}; void *out_buf; int logo_count = 0; int i; int hdr_size, one_img_size, total_size; int ret = -1; struct logo_info *logo_hdr; FILE *file; PROG = fix_path(argv[0]); argc--, argv++; while (argc > 0 && argv[0][0] == '-') { /* it's a opt arg. */ const char *arg = argv[0]; argc--, argv++; if (!strcmp("-h", arg)) { usage(); return 0; } else if (!strcmp("--charge-logo", arg)) { int len, i; /* * Charge logo are located in directory * u-boot/tools/images/eink/, there are 7 * pictures to tell user the battery capacity * during charging */ for (i = 0; i < 7; i++) { int logo_type = EINK_LOGO_CHARGING_0 << i; len = strlen(argv[0]); if (len > 256) { fprintf(stderr, "input charging logo path %s is too long.\n", argv[0]); return -1; } printf("charge logo path %s\n", argv[0]); memcpy(in_img_info[logo_count].path, argv[0], len); in_img_info[logo_count].logo_type = logo_type; logo_count++; argc--, argv++; } } else if (!strcmp("--uboot-logo", arg)) { int len = strlen(argv[0]); if (len > 256) { printf("Uboot logo path %s is too long.\n", argv[0]); return -1; } memcpy(in_img_info[logo_count].path, argv[0], len); in_img_info[logo_count].logo_type = EINK_LOGO_UBOOT; logo_count++; argc--, argv++; } else if (!strcmp("--kernel-logo", arg)) { int len = strlen(argv[0]); if (len > 256) { printf("Kernel logo path %s is too long\n", argv[0]); return -1; } memcpy(in_img_info[logo_count].path, argv[0], len); in_img_info[logo_count].logo_type = EINK_LOGO_KERNEL; logo_count++; argc--, argv++; } else if (!strcmp("--poweroff-logo", arg)) { int len = strlen(argv[0]); if (len > 256) { printf("Poweroff logo path %s is too long\n", argv[0]); return -1; } memcpy(in_img_info[logo_count].path, argv[0], len); in_img_info[logo_count].logo_type = EINK_LOGO_POWEROFF; logo_count++; argc--, argv++; } else if (!strcmp("--screen-width", arg)) { screen_w = strtoul(argv[0], NULL, 10); argc--, argv++; } else if (!strcmp("--screen-height", arg)) { screen_h = strtoul(argv[0], NULL, 10); argc--, argv++; } else if (!strcmp("--output", arg)) { int len = strlen(argv[0]); if (len > 256) { printf("input output path %s is too long.\n", argv[0]); return -1; } memcpy(out_path, argv[0], len); argc--, argv++; } else { fprintf(stderr, "Unknown opt:%s", arg); usage(); return -1; } } ret = get_logo_resolution(in_img_info[0].path, &screen_w, &screen_h); if (ret < 0) { fprintf(stderr, "Get height and width from logo image failed.\n"); usage(); return -1; } if (screen_w == 0 || screen_h == 0) { fprintf(stderr, "The screen weight and screen height must be set.\n"); usage(); return -1; } file = fopen(out_path, "wb+"); if (!file) { fprintf(stderr, "File %s open failed.\n", out_path); usage(); return -1; } hdr_size = size_of_header(); one_img_size = size_of_one_image(); out_buf = init_grayscale_logo_buf(logo_count, one_img_size); if (!out_buf) { fprintf(stderr, "Can't malloc buffer for grayscale image.\n"); fclose(file); return -1; } logo_hdr = (struct logo_info *)out_buf; fprintf(stderr, "logo count is %d,one_img_size=%d,size=%d.\n", logo_count, one_img_size, screen_w * screen_h / 2); for (i = 0; i < logo_count; i++) { char *in_path = in_img_info[i].path; int type = in_img_info[i].logo_type; void *img_buf; int offset = hdr_size + i * one_img_size; img_buf = out_buf + offset; printf("image[%d] start addr=0x%p\n", i, img_buf); ret = convert_one_image(in_path, img_buf, offset, &logo_hdr->img_hdr[i], type); if (ret < 0) { printf("Convert image[%d] failed, type is %d\n", i, type); break; } } if (ret == 0) { struct logo_part_header *part_hdr = &logo_hdr->part_hdr; total_size = hdr_size + (i - 1) * one_img_size + screen_h * screen_w / 2; //convert success, write header data. part_hdr->magic[0] = 'R'; part_hdr->magic[1] = 'K'; part_hdr->magic[2] = 'E'; part_hdr->magic[3] = 'L'; part_hdr->totoal_size = total_size; part_hdr->screen_width = screen_w; part_hdr->screen_height = screen_h; part_hdr->logo_count = i; printf("screen w=%d, h=%d, total_size=%d\n", screen_w, screen_h, total_size); memcpy(part_hdr->version, version, 4); // write to output file ret = fwrite(out_buf, total_size, 1, file); if (ret != 1) fprintf(stderr, "write image to file %s failed\n", out_path); } deinit_grayscale_logo_buf(out_buf); ret = fclose(file); if (ret != 0) printf("Close file[%s] failed, err=%d\n", out_path, ret); file = NULL; return ret; }