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 --output path"); 137 printf("\t\t\t Output the grayscale image to path\n"); 138 } 139 140 static inline int size_of_header(void) 141 { 142 return ALIGN(sizeof(struct logo_info), RK_BLK_SIZE); 143 } 144 145 static inline int size_of_one_image(void) 146 { 147 return ALIGN((screen_w * screen_h) >> 1, RK_BLK_SIZE); 148 } 149 150 int get_logo_resolution(char *in_path, uint32_t *logo_width, 151 uint32_t *logo_height) 152 { 153 struct bmp_header bmp_hdr; 154 FILE *file; 155 int size; 156 int ret = 0; 157 158 if (!in_path) 159 fprintf(stderr, "Invalid bmp file path.\n"); 160 161 file = fopen(in_path, "rb"); 162 if (!file) { 163 fprintf(stderr, "File %s open failed.\n", in_path); 164 return -1; 165 } 166 size = sizeof(struct bmp_header); 167 if (size != fread(&bmp_hdr, 1, size, file)) { 168 fprintf(stderr, "Incomplete read of file %s.\n", in_path); 169 ret = -1; 170 goto out; 171 } 172 if (!(bmp_hdr.signature[0] == 'B' && 173 bmp_hdr.signature[1] == 'M')) { 174 printf("cat not find bmp file\n"); 175 ret = -1; 176 goto out; 177 } 178 *logo_width = bmp_hdr.width; 179 *logo_height = bmp_hdr.height; 180 fprintf(stderr, "logo resolution is %d x %d.\n", 181 *logo_width, *logo_height); 182 out: 183 fclose(file); 184 return ret; 185 } 186 187 /* 188 * The bmp pixel is scan from left-bottom to right-top 189 */ 190 int convert_bmp_idx_to_gray_idx(int idx, uint32_t w, uint32_t h) 191 { 192 int row = h - (idx / w) - 1; 193 194 return (row * w + idx % w) / 2; 195 } 196 197 int convert_one_image(char *in_path, void *out_buf, uint32_t offset, 198 struct grayscale_header *gr_hdr, int type) 199 { 200 struct bmp_image *bmp; 201 struct bmp_header *bmp_hdr; 202 FILE *file; 203 void *bmp_buf; 204 int size; 205 int ret = -1; 206 uint8_t *gr16_data = (uint8_t *)out_buf; 207 208 if (!out_buf || !in_path) { 209 fprintf(stderr, "in_path or out_buf is NULL.\n"); 210 return -1; 211 } 212 213 file = fopen(in_path, "rb"); 214 if (!file) { 215 fprintf(stderr, "File %s open failed.\n", in_path); 216 return -1; 217 } 218 219 fseek(file, 0, SEEK_END); 220 size = ftell(file); 221 fseek(file, 0, SEEK_SET); 222 223 bmp_buf = calloc(1, size); 224 if (!bmp_buf) { 225 fprintf(stderr, "Allocate memory of %d bytes failed.\n", size); 226 fclose(file); 227 return -1; 228 } 229 if (size != fread(bmp_buf, 1, size, file)) { 230 fprintf(stderr, "Incomplete read of file %s.\n", in_path); 231 goto out; 232 } 233 234 bmp = (struct bmp_image *)bmp_buf; 235 bmp_hdr = &bmp->hdr; 236 if (!(bmp_hdr->signature[0] == 'B' && 237 bmp_hdr->signature[1] == 'M')) { 238 printf("cat not find bmp file\n"); 239 goto out; 240 } 241 242 if (size != le32_to_cpu(bmp_hdr->file_size)) { 243 fprintf(stderr, "Invalid BMP file size %d.\n", 244 le32_to_cpu(bmp_hdr->file_size)); 245 goto out; 246 } 247 printf("bmp_hdr->width=%d, bmp_hdr->height=%d\n", 248 bmp_hdr->width, bmp_hdr->height); 249 printf("screen_w=%d, screen_h=%d\n", screen_w, screen_h); 250 if (le32_to_cpu(bmp_hdr->width) != screen_w || 251 le32_to_cpu(bmp_hdr->height) != screen_h) { 252 fprintf(stderr, "The image size must same with others.\n"); 253 goto out; 254 } 255 //write header 256 gr_hdr->magic[0] = 'G'; 257 gr_hdr->magic[1] = 'R'; 258 gr_hdr->magic[2] = '0'; 259 gr_hdr->magic[3] = '4'; 260 gr_hdr->x = 0; 261 gr_hdr->y = 0; 262 gr_hdr->w = screen_w; 263 gr_hdr->h = screen_h; 264 gr_hdr->logo_type = type; 265 gr_hdr->data_offset = offset; 266 gr_hdr->data_size = screen_w * screen_h / 2; 267 268 printf("bmp depth is %d\n", bmp_hdr->bit_count); 269 //convert rgb to gray data, and write to output buffer 270 // the used algorithm please refer to below url: 271 // https://www.cnblogs.com/zhangjiansheng/p/6925722.html 272 // we use below algorithm: 273 // Gray = (R*19595 + G*38469 + B*7472) >> 16 274 switch (bmp_hdr->bit_count) { 275 case 16:{ 276 struct pixel_u16 *color_u16; 277 int i; 278 279 color_u16 = (struct pixel_u16 *)bmp->color_table; 280 for (i = 0; i < screen_w * screen_h / 2; i++) { 281 struct pixel_u16 *pix1 = &color_u16[2 * i]; 282 struct pixel_u16 *pix2 = &color_u16[2 * i + 1]; 283 int j = convert_bmp_idx_to_gray_idx(2 * i, screen_w, 284 screen_h); 285 /* 286 * the rgb value of pixel_u16 is 4 bits, 287 * so the counted grayscale value is 4bit 288 */ 289 uint32_t gray_px1 = (pix1->red * 19595 + 290 pix1->green * 38469 + 291 pix1->blue * 7472) >> 16; 292 uint32_t gray_px2 = (pix2->red * 19595 + 293 pix2->green * 38469 + 294 pix2->blue * 7472) >> 16; 295 gr16_data[j] = gray_px1 | (gray_px2 << 4); 296 } 297 } 298 break; 299 case 24: { 300 struct pixel_u24 *color_u24; 301 int i; 302 303 color_u24 = (struct pixel_u24 *)bmp->color_table; 304 for (i = 0; i < screen_w * screen_h / 2; i++) { 305 struct pixel_u24 *pix1 = &color_u24[2 * i]; 306 struct pixel_u24 *pix2 = &color_u24[2 * i + 1]; 307 int j = convert_bmp_idx_to_gray_idx(2 * i, screen_w, 308 screen_h); 309 /* 310 * The rgb value of pixel_u24 is 8 bits, 311 * so the counted grayscale 312 * value need to divide into 16 313 */ 314 uint32_t gray_px1 = ((pix1->red * 19595 + 315 pix1->green * 38469 + 316 pix1->blue * 7472) >> 16) >> 4; 317 uint32_t gray_px2 = ((pix2->red * 19595 + 318 pix2->green * 38469 + 319 pix2->blue * 7472) >> 16) >> 4; 320 321 gr16_data[j] = gray_px1 | (gray_px2 << 4); 322 } 323 } 324 break; 325 case 32: { 326 struct pixel_u32 *color_u32; 327 int i; 328 329 color_u32 = (struct pixel_u32 *)bmp->color_table; 330 for (i = 0; i < screen_w * screen_h / 2; i++) { 331 struct pixel_u32 *pix1 = &color_u32[2 * i]; 332 struct pixel_u32 *pix2 = &color_u32[2 * i + 1]; 333 int j = convert_bmp_idx_to_gray_idx(2 * i, screen_w, 334 screen_h); 335 /* 336 * The rgb value of pixel_u32 is 8 bits, 337 * so the counted grayscale 338 * value need to divide into 16 339 */ 340 uint32_t gray_px1 = ((pix1->red * 19595 + 341 pix1->green * 38469 + 342 pix1->blue * 7472) >> 16) >> 4; 343 uint32_t gray_px2 = ((pix2->red * 19595 + 344 pix2->green * 38469 + 345 pix2->blue * 7472) >> 16) >> 4; 346 gr16_data[j] = gray_px1 | (gray_px2 << 4); 347 } 348 } 349 break; 350 default: 351 ret = -1; 352 printf("Invalid bit count[%d],only support 16/24/32 bpp bmp\n", 353 bmp_hdr->bit_count); 354 break; 355 } 356 357 fprintf(stderr, "Convert image success\n"); 358 ret = 0; 359 out: 360 free(bmp_buf); 361 fclose(file); 362 return ret; 363 } 364 365 void *init_grayscale_logo_buf(int logo_count, uint32_t screen_w, 366 uint32_t screen_h) 367 { 368 int size; 369 void *out_buf; 370 371 if (!logo_count) { 372 fprintf(stderr, "No input logo!\n"); 373 return NULL; 374 } 375 size = size_of_header(); 376 fprintf(stderr, "size of header in logo.img is %d\n", size); 377 //every pixel of the grayscale image is 4 bits 378 size += logo_count * screen_w * screen_h / 2; 379 out_buf = calloc(1, size); 380 381 return out_buf; 382 } 383 384 void deinit_grayscale_logo_buf(void *buf) 385 { 386 if (buf) { 387 free(buf); 388 buf = NULL; 389 } 390 } 391 392 int main(int argc, char *argv[]) 393 { 394 char out_path[256] = {0}; 395 void *out_buf; 396 int logo_count = 0; 397 int i; 398 int hdr_size, one_img_size, total_size; 399 int ret = -1; 400 struct logo_info *logo_hdr; 401 FILE *file; 402 403 PROG = fix_path(argv[0]); 404 405 argc--, argv++; 406 while (argc > 0 && argv[0][0] == '-') { 407 /* it's a opt arg. */ 408 const char *arg = argv[0]; 409 410 argc--, argv++; 411 if (!strcmp("-h", arg)) { 412 usage(); 413 return 0; 414 } else if (!strcmp("--charge-logo", arg)) { 415 int len, i; 416 /* 417 * Charge logo are located in directory 418 * u-boot/tools/images/eink/, there are 7 419 * pictures to tell user the battery capacity 420 * during charging 421 */ 422 for (i = 0; i < 7; i++) { 423 int logo_type = EINK_LOGO_CHARGING_0 << i; 424 425 len = strlen(argv[0]); 426 if (len > 256) { 427 fprintf(stderr, 428 "input charging logo path %s is too long.\n", 429 argv[0]); 430 return -1; 431 } 432 printf("charge logo path %s\n", argv[0]); 433 memcpy(in_img_info[logo_count].path, 434 argv[0], len); 435 in_img_info[logo_count].logo_type = logo_type; 436 logo_count++; 437 argc--, argv++; 438 } 439 } else if (!strcmp("--uboot-logo", arg)) { 440 int len = strlen(argv[0]); 441 442 if (len > 256) { 443 printf("Uboot logo path %s is too long.\n", 444 argv[0]); 445 return -1; 446 } 447 memcpy(in_img_info[logo_count].path, argv[0], len); 448 in_img_info[logo_count].logo_type = EINK_LOGO_UBOOT; 449 logo_count++; 450 argc--, argv++; 451 } else if (!strcmp("--kernel-logo", arg)) { 452 int len = strlen(argv[0]); 453 454 if (len > 256) { 455 printf("Kernel logo path %s is too long\n", 456 argv[0]); 457 return -1; 458 } 459 memcpy(in_img_info[logo_count].path, argv[0], len); 460 in_img_info[logo_count].logo_type = EINK_LOGO_KERNEL; 461 logo_count++; 462 argc--, argv++; 463 } else if (!strcmp("--screen-width", arg)) { 464 screen_w = strtoul(argv[0], NULL, 10); 465 argc--, argv++; 466 } else if (!strcmp("--screen-height", arg)) { 467 screen_h = strtoul(argv[0], NULL, 10); 468 argc--, argv++; 469 } else if (!strcmp("--output", arg)) { 470 int len = strlen(argv[0]); 471 472 if (len > 256) { 473 printf("input output path %s is too long.\n", 474 argv[0]); 475 return -1; 476 } 477 memcpy(out_path, argv[0], len); 478 argc--, argv++; 479 } else { 480 fprintf(stderr, "Unknown opt:%s", arg); 481 usage(); 482 return -1; 483 } 484 } 485 486 ret = get_logo_resolution(in_img_info[0].path, &screen_w, &screen_h); 487 if (ret < 0) { 488 fprintf(stderr, 489 "Get height and width from logo image failed.\n"); 490 usage(); 491 return -1; 492 } 493 494 if (screen_w == 0 || screen_h == 0) { 495 fprintf(stderr, 496 "The screen weight and screen height must be set.\n"); 497 usage(); 498 return -1; 499 } 500 501 file = fopen(out_path, "wb+"); 502 if (!file) { 503 fprintf(stderr, "File %s open failed.\n", out_path); 504 usage(); 505 return -1; 506 } 507 508 out_buf = init_grayscale_logo_buf(logo_count, screen_w, screen_h); 509 if (!out_buf) { 510 fprintf(stderr, "Can't malloc buffer for grayscale image.\n"); 511 fclose(file); 512 return -1; 513 } 514 515 hdr_size = size_of_header(); 516 one_img_size = size_of_one_image(); 517 logo_hdr = (struct logo_info *)out_buf; 518 fprintf(stderr, "logo count is %d,one_img_size=%d,size=%d.\n", 519 logo_count, one_img_size, screen_w * screen_h / 2); 520 for (i = 0; i < logo_count; i++) { 521 char *in_path = in_img_info[i].path; 522 int type = in_img_info[i].logo_type; 523 void *img_buf; 524 int offset = hdr_size + i * one_img_size; 525 526 img_buf = out_buf + offset; 527 printf("image[%d] start addr=0x%p\n", i, img_buf); 528 ret = convert_one_image(in_path, img_buf, offset, 529 &logo_hdr->img_hdr[i], type); 530 if (ret < 0) { 531 printf("Convert image[%d] failed, type is %d\n", 532 i, type); 533 break; 534 } 535 } 536 537 if (ret == 0) { 538 struct logo_part_header *part_hdr = &logo_hdr->part_hdr; 539 540 total_size = hdr_size + (i - 1) * one_img_size + 541 screen_h * screen_w / 2; 542 543 //convert success, write header data. 544 part_hdr->magic[0] = 'R'; 545 part_hdr->magic[1] = 'K'; 546 part_hdr->magic[2] = 'E'; 547 part_hdr->magic[3] = 'L'; 548 part_hdr->totoal_size = total_size; 549 part_hdr->screen_width = screen_w; 550 part_hdr->screen_height = screen_h; 551 part_hdr->logo_count = i; 552 printf("screen w=%d, h=%d, total_size=%d\n", 553 screen_w, screen_h, total_size); 554 memcpy(part_hdr->version, version, 4); 555 556 // write to output file 557 ret = fwrite(out_buf, total_size, 1, file); 558 if (ret != 1) 559 fprintf(stderr, "write image to file %s failed\n", 560 out_path); 561 } 562 563 deinit_grayscale_logo_buf(out_buf); 564 ret = fclose(file); 565 if (ret != 0) 566 printf("Close file[%s] failed, err=%d\n", out_path, ret); 567 file = NULL; 568 return ret; 569 } 570 571