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
fix_path(const char * path)112 static const char *fix_path(const char *path)
113 {
114 if (!memcmp(path, "./", 2))
115 return path + 2;
116 return path;
117 }
118
print_version(void)119 static void print_version(void)
120 {
121 printf("Version %s (zwp@rock-chips.com)\n", version);
122 }
123
usage(void)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
size_of_header(void)142 static inline int size_of_header(void)
143 {
144 return ALIGN(sizeof(struct logo_info), RK_BLK_SIZE);
145 }
146
size_of_one_image(void)147 static inline int size_of_one_image(void)
148 {
149 return ALIGN((screen_w * screen_h) >> 1, RK_BLK_SIZE);
150 }
151
get_logo_resolution(char * in_path,uint32_t * logo_width,uint32_t * logo_height)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 */
convert_bmp_idx_to_gray_idx(int idx,uint32_t w,uint32_t h)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
convert_one_image(char * in_path,void * out_buf,uint32_t offset,struct grayscale_header * gr_hdr,int type)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
init_grayscale_logo_buf(int logo_count,uint32_t size_one_image)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
deinit_grayscale_logo_buf(void * buf)385 void deinit_grayscale_logo_buf(void *buf)
386 {
387 if (buf) {
388 free(buf);
389 buf = NULL;
390 }
391 }
392
main(int argc,char * argv[])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