diff --git a/scripts/README.rockchip b/scripts/README.rockchip index 4bbe819e3e..c1f75ff7b6 100644 --- a/scripts/README.rockchip +++ b/scripts/README.rockchip @@ -51,3 +51,5 @@ Example usage of scripts/tools for Rockchip ./tools/loaderimage --pack --trustos ./bin/rk32/rk322x_tee_v2.00.bin trust.img 0x80000000 --size 1024 2 ./tools/loaderimage --unpack --trustos trust.img tee.bin + # bmp2gray16 + ./tools/bmp2gray16 --uboot-logo uboot.bmp --charge-logo charging.bmp --lowpower-logo lowpower.bmp --kernel-logo kernel.bmp --output ./logo.img diff --git a/tools/Makefile b/tools/Makefile index 1482a9f22b..2bc097f57e 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -63,11 +63,13 @@ hostprogs-y += boot_merger hostprogs-y += trust_merger hostprogs-y += loaderimage hostprogs-y += resource_tool +hostprogs-y += bmp2gray16 boot_merger-objs := rockchip/boot_merger.o rockchip/sha2.o lib/sha256.o trust_merger-objs := rockchip/trust_merger.o rockchip/sha2.o lib/sha256.o loaderimage-objs := rockchip/loaderimage.o rockchip/sha.o lib/sha256.o rockchip/crc32_rk.o resource_tool-objs := rockchip/resource_tool.o +bmp2gray16-objs := rockchip/bmp2gray16.o endif FIT_SIG_OBJS-$(CONFIG_FIT_SIGNATURE) := common/image-sig.o diff --git a/tools/rockchip/bmp2gray16.c b/tools/rockchip/bmp2gray16.c new file mode 100644 index 0000000000..27627c812f --- /dev/null +++ b/tools/rockchip/bmp2gray16.c @@ -0,0 +1,570 @@ +/* + * (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 --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 screen_w, + uint32_t screen_h) +{ + 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 * screen_w * screen_h / 2; + 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("--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; + } + + out_buf = init_grayscale_logo_buf(logo_count, screen_w, screen_h); + if (!out_buf) { + fprintf(stderr, "Can't malloc buffer for grayscale image.\n"); + fclose(file); + return -1; + } + + hdr_size = size_of_header(); + one_img_size = size_of_one_image(); + 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; +} +