xref: /rk3399_rockchip-uboot/tools/rockchip/bmp2gray16.c (revision 894688431927c1b73c64860c8aa71463c2593ea2)
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