2025-05-10 21:49:39 +08:00

584 lines
14 KiB
C

/*
* (C) Copyright 2020 Rockchip Electronics Co., Ltd
*
* SPDX-License-Identifier: GPL-2.0+
* Author: Wenping Zhang <wenping.zhang@rock-chips.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rk_eink.h>
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;
}