1 /* 2 * (C) Copyright 2020 Rockchip Electronics Co., Ltd 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 * Author: Wenping Zhang <wenping.zhang@rock-chips.com> 6 */ 7 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <rk_eink.h> 12 13 struct bmp_header { 14 /* Header */ 15 char signature[2]; 16 uint32_t file_size; 17 uint32_t reserved; 18 uint32_t data_offset; 19 /* InfoHeader */ 20 uint32_t size; 21 uint32_t width; 22 uint32_t height; 23 uint16_t planes; 24 uint16_t bit_count; 25 uint32_t compression; 26 uint32_t image_size; 27 uint32_t x_pixels_per_m; 28 uint32_t y_pixels_per_m; 29 uint32_t colors_used; 30 uint32_t colors_important; 31 /* ColorTable */ 32 } __attribute__((packed)); 33 34 struct bmp_image { 35 struct bmp_header hdr; 36 uint8_t color_table[0]; 37 }; 38 39 struct pixel_u16 { 40 uint16_t blue : 4, 41 green : 4, 42 red : 4, 43 alpha : 4; 44 } __attribute__((packed)); 45 46 struct pixel_u24 { 47 uint8_t blue; 48 uint8_t green; 49 uint8_t red; 50 } __attribute__((packed)); 51 52 struct pixel_u32 { 53 uint8_t blue; 54 uint8_t green; 55 uint8_t red; 56 uint8_t alpha; 57 } __attribute__((packed)); 58 59 //logo partition Header, 64byte 60 struct logo_part_header { 61 char magic[4]; /* must be "RKEL" */ 62 uint32_t totoal_size; 63 uint32_t screen_width; 64 uint32_t screen_height; 65 uint32_t logo_count; 66 char version[4]; 67 uint32_t rsv[10]; 68 } __attribute__((packed)); 69 70 // logo image header,32 byte 71 struct grayscale_header { 72 char magic[4]; /* must be "GR04" */ 73 uint16_t x; 74 uint16_t y; 75 uint16_t w; 76 uint16_t h; 77 uint32_t logo_type; 78 uint32_t data_offset; /* image offset in byte */ 79 uint32_t data_size; /* image size in byte */ 80 uint32_t rsv[2]; 81 } __attribute__((packed)); 82 83 /* 84 * The start address of logo image in logo.img must be aligned 85 * in 512 bytes,so the header size must be times of 512 bytes. 86 * Here we fix the size to 512 bytes, so the count of logo image 87 * can only support up to 14. 88 */ 89 struct logo_info { 90 struct logo_part_header part_hdr; 91 struct grayscale_header img_hdr[14]; 92 } __attribute__((packed)); 93 94 struct input_img_info { 95 char path[256]; 96 int logo_type; 97 }; 98 99 /* 100 * Every part of logo.img must be aligned in RK_BLK_SIZE, 101 * use macro aligned_in_blk to calculate the the real size 102 */ 103 #define RK_BLK_SIZE 512 104 #define ALIGN(x, y) (((x) + (y) - 1) & ~((y) - 1)) 105 106 struct input_img_info in_img_info[16]; 107 uint32_t screen_w; 108 uint32_t screen_h; 109 static const char version[4] = "1.00"; 110 static const char *PROG; 111 112 static const char *fix_path(const char *path) 113 { 114 if (!memcmp(path, "./", 2)) 115 return path + 2; 116 return path; 117 } 118 119 static void print_version(void) 120 { 121 printf("Version %s (zwp@rock-chips.com)\n", version); 122 } 123 124 void usage(void) 125 { 126 printf("Usage: %s [options] [arguments]\n\n", PROG); 127 print_version(); 128 printf("\t --uboot-logo path"); 129 printf("\t\t\t Pack uboot logo to logo.img from given path\n"); 130 printf("\t --charge-logo path"); 131 printf("\t\t\t Pack charge logo to logo.img from given path\n"); 132 printf("\t --lowpower-logo path"); 133 printf("\t\t\t Pack low power logo to logo.img from given path\n"); 134 printf("\t --kernel-logo path"); 135 printf("\t\t\t Pack low power logo to logo.img from given path\n"); 136 printf("\t --poweroff-logo path"); 137 printf("\t\t\t Pack power off logo to logo.img from given path\n"); 138 printf("\t --output path"); 139 printf("\t\t\t Output the grayscale image to path\n"); 140 } 141 142 static inline int size_of_header(void) 143 { 144 return ALIGN(sizeof(struct logo_info), RK_BLK_SIZE); 145 } 146 147 static inline int size_of_one_image(void) 148 { 149 return ALIGN((screen_w * screen_h) >> 1, RK_BLK_SIZE); 150 } 151 152 int get_logo_resolution(char *in_path, uint32_t *logo_width, 153 uint32_t *logo_height) 154 { 155 struct bmp_header bmp_hdr; 156 FILE *file; 157 int size; 158 int ret = 0; 159 160 if (!in_path) 161 fprintf(stderr, "Invalid bmp file path.\n"); 162 163 file = fopen(in_path, "rb"); 164 if (!file) { 165 fprintf(stderr, "File %s open failed.\n", in_path); 166 return -1; 167 } 168 size = sizeof(struct bmp_header); 169 if (size != fread(&bmp_hdr, 1, size, file)) { 170 fprintf(stderr, "Incomplete read of file %s.\n", in_path); 171 ret = -1; 172 goto out; 173 } 174 if (!(bmp_hdr.signature[0] == 'B' && 175 bmp_hdr.signature[1] == 'M')) { 176 printf("cat not find bmp file\n"); 177 ret = -1; 178 goto out; 179 } 180 *logo_width = bmp_hdr.width; 181 *logo_height = bmp_hdr.height; 182 fprintf(stderr, "logo resolution is %d x %d.\n", 183 *logo_width, *logo_height); 184 out: 185 fclose(file); 186 return ret; 187 } 188 189 /* 190 * The bmp pixel is scan from left-bottom to right-top 191 */ 192 int convert_bmp_idx_to_gray_idx(int idx, uint32_t w, uint32_t h) 193 { 194 int row = h - (idx / w) - 1; 195 196 return (row * w + idx % w) / 2; 197 } 198 199 int convert_one_image(char *in_path, void *out_buf, uint32_t offset, 200 struct grayscale_header *gr_hdr, int type) 201 { 202 struct bmp_image *bmp; 203 struct bmp_header *bmp_hdr; 204 FILE *file; 205 void *bmp_buf; 206 int size; 207 int ret = -1; 208 uint8_t *gr16_data = (uint8_t *)out_buf; 209 210 if (!out_buf || !in_path) { 211 fprintf(stderr, "in_path or out_buf is NULL.\n"); 212 return -1; 213 } 214 215 file = fopen(in_path, "rb"); 216 if (!file) { 217 fprintf(stderr, "File %s open failed.\n", in_path); 218 return -1; 219 } 220 221 fseek(file, 0, SEEK_END); 222 size = ftell(file); 223 fseek(file, 0, SEEK_SET); 224 225 bmp_buf = calloc(1, size); 226 if (!bmp_buf) { 227 fprintf(stderr, "Allocate memory of %d bytes failed.\n", size); 228 fclose(file); 229 return -1; 230 } 231 if (size != fread(bmp_buf, 1, size, file)) { 232 fprintf(stderr, "Incomplete read of file %s.\n", in_path); 233 goto out; 234 } 235 236 bmp = (struct bmp_image *)bmp_buf; 237 bmp_hdr = &bmp->hdr; 238 if (!(bmp_hdr->signature[0] == 'B' && 239 bmp_hdr->signature[1] == 'M')) { 240 printf("cat not find bmp file\n"); 241 goto out; 242 } 243 244 if (size != le32_to_cpu(bmp_hdr->file_size)) { 245 fprintf(stderr, "Invalid BMP file size %d.\n", 246 le32_to_cpu(bmp_hdr->file_size)); 247 goto out; 248 } 249 printf("bmp_hdr->width=%d, bmp_hdr->height=%d\n", 250 bmp_hdr->width, bmp_hdr->height); 251 printf("screen_w=%d, screen_h=%d\n", screen_w, screen_h); 252 if (le32_to_cpu(bmp_hdr->width) != screen_w || 253 le32_to_cpu(bmp_hdr->height) != screen_h) { 254 fprintf(stderr, "The image size must same with others.\n"); 255 goto out; 256 } 257 //write header 258 gr_hdr->magic[0] = 'G'; 259 gr_hdr->magic[1] = 'R'; 260 gr_hdr->magic[2] = '0'; 261 gr_hdr->magic[3] = '4'; 262 gr_hdr->x = 0; 263 gr_hdr->y = 0; 264 gr_hdr->w = screen_w; 265 gr_hdr->h = screen_h; 266 gr_hdr->logo_type = type; 267 gr_hdr->data_offset = offset; 268 gr_hdr->data_size = screen_w * screen_h / 2; 269 270 printf("bmp depth is %d\n", bmp_hdr->bit_count); 271 //convert rgb to gray data, and write to output buffer 272 // the used algorithm please refer to below url: 273 // https://www.cnblogs.com/zhangjiansheng/p/6925722.html 274 // we use below algorithm: 275 // Gray = (R*19595 + G*38469 + B*7472) >> 16 276 switch (bmp_hdr->bit_count) { 277 case 16:{ 278 struct pixel_u16 *color_u16; 279 int i; 280 281 color_u16 = (struct pixel_u16 *)bmp->color_table; 282 for (i = 0; i < screen_w * screen_h / 2; i++) { 283 struct pixel_u16 *pix1 = &color_u16[2 * i]; 284 struct pixel_u16 *pix2 = &color_u16[2 * i + 1]; 285 int j = convert_bmp_idx_to_gray_idx(2 * i, screen_w, 286 screen_h); 287 /* 288 * the rgb value of pixel_u16 is 4 bits, 289 * so the counted grayscale value is 4bit 290 */ 291 uint32_t gray_px1 = (pix1->red * 19595 + 292 pix1->green * 38469 + 293 pix1->blue * 7472) >> 16; 294 uint32_t gray_px2 = (pix2->red * 19595 + 295 pix2->green * 38469 + 296 pix2->blue * 7472) >> 16; 297 gr16_data[j] = gray_px1 | (gray_px2 << 4); 298 } 299 } 300 break; 301 case 24: { 302 struct pixel_u24 *color_u24; 303 int i; 304 305 color_u24 = (struct pixel_u24 *)bmp->color_table; 306 for (i = 0; i < screen_w * screen_h / 2; i++) { 307 struct pixel_u24 *pix1 = &color_u24[2 * i]; 308 struct pixel_u24 *pix2 = &color_u24[2 * i + 1]; 309 int j = convert_bmp_idx_to_gray_idx(2 * i, screen_w, 310 screen_h); 311 /* 312 * The rgb value of pixel_u24 is 8 bits, 313 * so the counted grayscale 314 * value need to divide into 16 315 */ 316 uint32_t gray_px1 = ((pix1->red * 19595 + 317 pix1->green * 38469 + 318 pix1->blue * 7472) >> 16) >> 4; 319 uint32_t gray_px2 = ((pix2->red * 19595 + 320 pix2->green * 38469 + 321 pix2->blue * 7472) >> 16) >> 4; 322 323 gr16_data[j] = gray_px1 | (gray_px2 << 4); 324 } 325 } 326 break; 327 case 32: { 328 struct pixel_u32 *color_u32; 329 int i; 330 331 color_u32 = (struct pixel_u32 *)bmp->color_table; 332 for (i = 0; i < screen_w * screen_h / 2; i++) { 333 struct pixel_u32 *pix1 = &color_u32[2 * i]; 334 struct pixel_u32 *pix2 = &color_u32[2 * i + 1]; 335 int j = convert_bmp_idx_to_gray_idx(2 * i, screen_w, 336 screen_h); 337 /* 338 * The rgb value of pixel_u32 is 8 bits, 339 * so the counted grayscale 340 * value need to divide into 16 341 */ 342 uint32_t gray_px1 = ((pix1->red * 19595 + 343 pix1->green * 38469 + 344 pix1->blue * 7472) >> 16) >> 4; 345 uint32_t gray_px2 = ((pix2->red * 19595 + 346 pix2->green * 38469 + 347 pix2->blue * 7472) >> 16) >> 4; 348 gr16_data[j] = gray_px1 | (gray_px2 << 4); 349 } 350 } 351 break; 352 default: 353 ret = -1; 354 printf("Invalid bit count[%d],only support 16/24/32 bpp bmp\n", 355 bmp_hdr->bit_count); 356 break; 357 } 358 359 fprintf(stderr, "Convert image success\n"); 360 ret = 0; 361 out: 362 free(bmp_buf); 363 fclose(file); 364 return ret; 365 } 366 367 void *init_grayscale_logo_buf(int logo_count, uint32_t size_one_image) 368 { 369 int size; 370 void *out_buf; 371 372 if (!logo_count) { 373 fprintf(stderr, "No input logo!\n"); 374 return NULL; 375 } 376 size = size_of_header(); 377 fprintf(stderr, "size of header in logo.img is %d\n", size); 378 //every pixel of the grayscale image is 4 bits 379 size += logo_count * size_one_image; 380 out_buf = calloc(1, size); 381 382 return out_buf; 383 } 384 385 void deinit_grayscale_logo_buf(void *buf) 386 { 387 if (buf) { 388 free(buf); 389 buf = NULL; 390 } 391 } 392 393 int main(int argc, char *argv[]) 394 { 395 char out_path[256] = {0}; 396 void *out_buf; 397 int logo_count = 0; 398 int i; 399 int hdr_size, one_img_size, total_size; 400 int ret = -1; 401 struct logo_info *logo_hdr; 402 FILE *file; 403 404 PROG = fix_path(argv[0]); 405 406 argc--, argv++; 407 while (argc > 0 && argv[0][0] == '-') { 408 /* it's a opt arg. */ 409 const char *arg = argv[0]; 410 411 argc--, argv++; 412 if (!strcmp("-h", arg)) { 413 usage(); 414 return 0; 415 } else if (!strcmp("--charge-logo", arg)) { 416 int len, i; 417 /* 418 * Charge logo are located in directory 419 * u-boot/tools/images/eink/, there are 7 420 * pictures to tell user the battery capacity 421 * during charging 422 */ 423 for (i = 0; i < 7; i++) { 424 int logo_type = EINK_LOGO_CHARGING_0 << i; 425 426 len = strlen(argv[0]); 427 if (len > 256) { 428 fprintf(stderr, 429 "input charging logo path %s is too long.\n", 430 argv[0]); 431 return -1; 432 } 433 printf("charge logo path %s\n", argv[0]); 434 memcpy(in_img_info[logo_count].path, 435 argv[0], len); 436 in_img_info[logo_count].logo_type = logo_type; 437 logo_count++; 438 argc--, argv++; 439 } 440 } else if (!strcmp("--uboot-logo", arg)) { 441 int len = strlen(argv[0]); 442 443 if (len > 256) { 444 printf("Uboot logo path %s is too long.\n", 445 argv[0]); 446 return -1; 447 } 448 memcpy(in_img_info[logo_count].path, argv[0], len); 449 in_img_info[logo_count].logo_type = EINK_LOGO_UBOOT; 450 logo_count++; 451 argc--, argv++; 452 } else if (!strcmp("--kernel-logo", arg)) { 453 int len = strlen(argv[0]); 454 455 if (len > 256) { 456 printf("Kernel logo path %s is too long\n", 457 argv[0]); 458 return -1; 459 } 460 memcpy(in_img_info[logo_count].path, argv[0], len); 461 in_img_info[logo_count].logo_type = EINK_LOGO_KERNEL; 462 logo_count++; 463 argc--, argv++; 464 } else if (!strcmp("--poweroff-logo", arg)) { 465 int len = strlen(argv[0]); 466 467 if (len > 256) { 468 printf("Poweroff logo path %s is too long\n", 469 argv[0]); 470 return -1; 471 } 472 memcpy(in_img_info[logo_count].path, argv[0], len); 473 in_img_info[logo_count].logo_type = EINK_LOGO_POWEROFF; 474 logo_count++; 475 argc--, argv++; 476 } else if (!strcmp("--screen-width", arg)) { 477 screen_w = strtoul(argv[0], NULL, 10); 478 argc--, argv++; 479 } else if (!strcmp("--screen-height", arg)) { 480 screen_h = strtoul(argv[0], NULL, 10); 481 argc--, argv++; 482 } else if (!strcmp("--output", arg)) { 483 int len = strlen(argv[0]); 484 485 if (len > 256) { 486 printf("input output path %s is too long.\n", 487 argv[0]); 488 return -1; 489 } 490 memcpy(out_path, argv[0], len); 491 argc--, argv++; 492 } else { 493 fprintf(stderr, "Unknown opt:%s", arg); 494 usage(); 495 return -1; 496 } 497 } 498 499 ret = get_logo_resolution(in_img_info[0].path, &screen_w, &screen_h); 500 if (ret < 0) { 501 fprintf(stderr, 502 "Get height and width from logo image failed.\n"); 503 usage(); 504 return -1; 505 } 506 507 if (screen_w == 0 || screen_h == 0) { 508 fprintf(stderr, 509 "The screen weight and screen height must be set.\n"); 510 usage(); 511 return -1; 512 } 513 514 file = fopen(out_path, "wb+"); 515 if (!file) { 516 fprintf(stderr, "File %s open failed.\n", out_path); 517 usage(); 518 return -1; 519 } 520 hdr_size = size_of_header(); 521 one_img_size = size_of_one_image(); 522 523 out_buf = init_grayscale_logo_buf(logo_count, one_img_size); 524 if (!out_buf) { 525 fprintf(stderr, "Can't malloc buffer for grayscale image.\n"); 526 fclose(file); 527 return -1; 528 } 529 530 logo_hdr = (struct logo_info *)out_buf; 531 fprintf(stderr, "logo count is %d,one_img_size=%d,size=%d.\n", 532 logo_count, one_img_size, screen_w * screen_h / 2); 533 for (i = 0; i < logo_count; i++) { 534 char *in_path = in_img_info[i].path; 535 int type = in_img_info[i].logo_type; 536 void *img_buf; 537 int offset = hdr_size + i * one_img_size; 538 539 img_buf = out_buf + offset; 540 printf("image[%d] start addr=0x%p\n", i, img_buf); 541 ret = convert_one_image(in_path, img_buf, offset, 542 &logo_hdr->img_hdr[i], type); 543 if (ret < 0) { 544 printf("Convert image[%d] failed, type is %d\n", 545 i, type); 546 break; 547 } 548 } 549 550 if (ret == 0) { 551 struct logo_part_header *part_hdr = &logo_hdr->part_hdr; 552 553 total_size = hdr_size + (i - 1) * one_img_size + 554 screen_h * screen_w / 2; 555 556 //convert success, write header data. 557 part_hdr->magic[0] = 'R'; 558 part_hdr->magic[1] = 'K'; 559 part_hdr->magic[2] = 'E'; 560 part_hdr->magic[3] = 'L'; 561 part_hdr->totoal_size = total_size; 562 part_hdr->screen_width = screen_w; 563 part_hdr->screen_height = screen_h; 564 part_hdr->logo_count = i; 565 printf("screen w=%d, h=%d, total_size=%d\n", 566 screen_w, screen_h, total_size); 567 memcpy(part_hdr->version, version, 4); 568 569 // write to output file 570 ret = fwrite(out_buf, total_size, 1, file); 571 if (ret != 1) 572 fprintf(stderr, "write image to file %s failed\n", 573 out_path); 574 } 575 576 deinit_grayscale_logo_buf(out_buf); 577 ret = fclose(file); 578 if (ret != 0) 579 printf("Close file[%s] failed, err=%d\n", out_path, ret); 580 file = NULL; 581 return ret; 582 } 583 584