blob: 8f82f98cee1378b2aea294c58cb1bc2bccc737ba [file] [log] [blame]
yuezonghe824eb0c2024-06-27 02:32:26 -07001#include "common.h"
2#include "bmphed.h"
3
4#define PNG2BMP_VERSION "1.62"
5#define PNG2BMP_COPYRIGHT "Copyright"
6
7#define ZLIB_VERSION "1.2.3"
8
9char outnam[FILENAME_MAX];
10char outdir[FILENAME_MAX];
11int deletesrc = 0;
12int copytime = 0;
13
14#define P2B_ALPHABMP_NONE 0
15#define P2B_ALPHABMP_ARGB 1
16#define P2B_ALPHABMP_BITFIELD 2
17
18int alpha_format = P2B_ALPHABMP_NONE;
19int expand_trans = 0;
20
21const char errlogfile[] = "./p2berror.log";
22
23
24const char wrn_mkdirfail[] =
25 "WARNING: Can not create directory - %s\n"
26 "WARNING: Output directory specified by '-%c' will be ignored.\n";
27
28const char err_ropenfail[] = "SKIPPED: No such file or directory - %s\n";
29const char err_wopenfail[] = "SKIPPED: Cannot create - %s\n";
30const char err_outofmemory[] = "SKIPPED: Out of memory - %s\n";
31
32const char err_writeerr[] = "SKIPPED: Write operation failed - %s\n";
33const char err_not_a_png[] = "SKIPPED: Not a PNG file - %s\n";
34
35static BOOL read_png(char *, IMAGE *);
36static int skip_macbinary(png_structp);
37static void to4bpp(png_structp, png_row_infop, png_bytep);
38static BOOL write_bmp(char *, IMAGE *);
39static const char *write_rgb_bits(IMAGE *, FILE *);
40static void mputdwl(void *, unsigned long);
41static void mputwl(void *, unsigned int);
42static void usage_exit(char *, int);
43
44#define ERROR_ABORT(s) do { errmsg = (s); goto error_abort; } while (0)
45
46static BOOL read_png(char *fn, IMAGE *img)
47{
48 png_structp png_ptr;
49 png_infop info_ptr, end_info;
50 png_uint_32 width, height;
51 int bit_depth, color_type;
52 int xbit_depth, xcolor_type, xchannels;
53 const char *errmsg;
54 FILE *fp;
55
56 imgbuf_init(img);
57
58 if (fn == NULL) {
59 fn = " (stdin)";
60 fp = binary_stdio(fileno(stdin));
61 } else {
62 fp = fopen(fn, "rb");
63 }
64 if (fp == NULL) ERROR_ABORT(err_ropenfail);
65
66 set_status("Reading %.80s", basname(fn));
67
68 /* ------------------------------------------------------ */
69
70 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, fn,
71 png_my_error, png_my_warning);
72 if (png_ptr == NULL) {
73 ERROR_ABORT(err_outofmemory);
74 }
75 info_ptr = png_create_info_struct(png_ptr);
76 end_info = png_create_info_struct(png_ptr);
77 if (info_ptr == NULL || end_info == NULL) {
78 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
79 ERROR_ABORT(err_outofmemory);
80 }
81 if (setjmp(png_jmpbuf(png_ptr))) {
82 /* If we get here, we had a problem writing the file */
83 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
84 ERROR_ABORT(NULL);
85 }
86 png_init_io(png_ptr, fp);
87 png_set_sig_bytes(png_ptr, skip_macbinary(png_ptr));
88
89 /* ------------------------------------------------------ */
90
91 png_read_info(png_ptr, info_ptr);
92
93 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
94 &color_type, NULL, NULL, NULL);
95
96 /* ------------------------------------------------------ */
97
98 if (color_type & PNG_COLOR_MASK_ALPHA) {
99 if (alpha_format == P2B_ALPHABMP_NONE) {
100 png_set_strip_alpha(png_ptr);
101 color_type &= ~PNG_COLOR_MASK_ALPHA;
102 }
103 } else if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
104 if (alpha_format != P2B_ALPHABMP_NONE && expand_trans) {
105 png_set_tRNS_to_alpha(png_ptr);
106 color_type |= PNG_COLOR_MASK_ALPHA;
107 color_type &= ~PNG_COLOR_MASK_PALETTE;
108 }
109 }
110 if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
111 png_set_gray_to_rgb(png_ptr);
112 }
113 if (color_type == PNG_COLOR_TYPE_RGB ||
114 color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
115 png_set_bgr(png_ptr);
116 }
117 if (!(color_type & PNG_COLOR_MASK_ALPHA) && bit_depth == 2) {
118 png_set_user_transform_info(png_ptr, NULL, 4, 1);
119 png_set_read_user_transform_fn(png_ptr, to4bpp);
120 }
121 if (bit_depth == 16)
122 png_set_strip_16(png_ptr);
123
124 png_read_update_info(png_ptr, info_ptr);
125
126 /* ------------------------------------------------------ */
127
128 png_get_IHDR(png_ptr, info_ptr, &width, &height, &xbit_depth,
129 &xcolor_type, NULL, NULL, NULL);
130 xchannels = png_get_channels(png_ptr, info_ptr);
131
132 img->width = (LONG)width;
133 img->height = (LONG)height;
134 img->pixdepth = (UINT)xbit_depth * xchannels;
135 img->palnum = (img->pixdepth <= 8) ? (1 << img->pixdepth) : 0;
136 img->topdown = FALSE;
137 img->alpha = (xcolor_type & PNG_COLOR_MASK_ALPHA) ? TRUE : FALSE;
138
139 if (!imgbuf_alloc(img)) {
140 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
141 ERROR_ABORT(err_outofmemory);
142 }
143
144 if (img->palnum > 0) {
145 if (xcolor_type == PNG_COLOR_TYPE_PALETTE) {
146 png_colorp palette;
147 int num_palette;
148 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
149 if (num_palette > (int)img->palnum) num_palette = img->palnum;
150 memset(img->palette, 0, img->palnum * sizeof(png_color));
151 memcpy(img->palette, palette, num_palette * sizeof(png_color));
152 } else {
153 int depth = (bit_depth == 16) ? 8 : bit_depth;
154 memset(img->palette, 0, img->palnum * sizeof(png_color));
155 png_build_grayscale_palette(depth, img->palette);
156 }
157 }
158
159 /* ------------------------------------------------------ */
160
161 png_set_read_status_fn(png_ptr, row_callback);
162 init_progress_meter(png_ptr, img->width, img->height);
163
164 png_read_image(png_ptr, img->rowptr);
165
166 png_read_end(png_ptr, end_info);
167 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
168
169 /* ------------------------------------------------------ */
170
171 set_status("Read OK %.80s", basname(fn));
172
173 if (fp != stdin) fclose(fp);
174
175 return TRUE;
176
177error_abort: /* error */
178 if (errmsg != NULL) xxprintf(errmsg, fn);
179 if (fp != stdin && fp != NULL) fclose(fp);
180 imgbuf_free(img);
181
182 return FALSE;
183}
184
185static int skip_macbinary(png_structp png_ptr)
186{
187 enum { PNG_BYTES_TO_CHECK = 8, MACBIN_SIZE = 128 };
188 png_byte buf[MACBIN_SIZE];
189 png_bytep sig;
190 png_size_t check;
191 png_FILE_p fp = (png_FILE_p)png_get_io_ptr(png_ptr);
192
193 check = (png_size_t)fread(buf, 1, PNG_BYTES_TO_CHECK, fp);
194 if (check != PNG_BYTES_TO_CHECK) png_error(png_ptr, "Read Error");
195
196 if (png_sig_cmp(buf, 0, PNG_BYTES_TO_CHECK) == 0)
197 return PNG_BYTES_TO_CHECK;
198
199 check = (png_size_t)fread(buf, 1, MACBIN_SIZE, fp);
200 if (check != MACBIN_SIZE) png_error(png_ptr, "Read Error");
201
202 sig = buf + MACBIN_SIZE - PNG_BYTES_TO_CHECK;
203 if (png_sig_cmp(sig, 0, PNG_BYTES_TO_CHECK) == 0)
204 return PNG_BYTES_TO_CHECK;
205
206 xxprintf(err_not_a_png, (char *)png_get_error_ptr(png_ptr));
207 longjmp(png_jmpbuf(png_ptr), 1);
208
209 return 0; /* to quiet compiler warnings */
210}
211
212static void to4bpp(png_structp png_ptr, png_row_infop row_info, png_bytep data)
213{
214 static const png_byte pix[] = {
215 0x00, 0x01, 0x02, 0x03, 0x10, 0x11, 0x12, 0x13,
216 0x20, 0x21, 0x22, 0x23, 0x30, 0x31, 0x32, 0x33,
217 };
218 png_uint_32 rowb;
219 png_bytep p, q;
220 png_byte c;
221
222 rowb = (row_info->width + 1) / 2;
223 q = data + rowb;
224 p = data + rowb / 2;
225
226 if (rowb % 2 == 1) {
227 c = *p;
228 *(--q) = pix[c >> 4];
229 }
230 while (p > data) {
231 c = *(--p);
232 *(--q) = pix[c & 0x0F];
233 *(--q) = pix[c >> 4];
234 }
235 row_info->bit_depth = 4;
236 row_info->pixel_depth = 4;
237 row_info->rowbytes = rowb;
238}
239
240static BOOL write_bmp(char *fn, IMAGE *img)
241{
242 BYTE bfh[FILEHED_SIZE + BMPV4HED_SIZE];
243 BYTE *const bih = bfh + FILEHED_SIZE;
244 BYTE rgbq[RGBQUAD_SIZE];
245 BOOL alpha_bitfield;
246 DWORD bihsize, offbits, filesize;
247 PALETTE *pal;
248 const char *errmsg;
249 FILE *fp;
250 UINT i;
251
252 if (fn == NULL) {
253 fn = " (stdout)";
254 fp = binary_stdio(fileno(stdout));
255 } else {
256 fp = fopen(fn, "wb");
257 }
258 if (fp == NULL) ERROR_ABORT(err_wopenfail);
259
260 set_status("Writing %.80s", basname(fn));
261
262 /* ------------------------------------------------------ */
263
264 alpha_bitfield = (img->alpha && alpha_format == P2B_ALPHABMP_BITFIELD);
265 bihsize = (alpha_bitfield) ? BMPV4HED_SIZE : INFOHED_SIZE;
266 offbits = FILEHED_SIZE + bihsize + RGBQUAD_SIZE * img->palnum;
267 filesize = offbits + img->imgbytes;
268
269 memset(bfh, 0, sizeof(bfh));
270
271 mputwl( bfh + BFH_WTYPE , BMP_SIGNATURE);
272 mputdwl(bfh + BFH_DSIZE , filesize);
273 mputdwl(bfh + BFH_DOFFBITS, offbits);
274
275 mputdwl(bih + BIH_DSIZE , bihsize);
276 mputdwl(bih + BIH_LWIDTH , (DWORD)img->width);
277 mputdwl(bih + BIH_LHEIGHT , (DWORD)img->height);
278 mputwl( bih + BIH_WPLANES , 1);
279 mputwl( bih + BIH_WBITCOUNT , img->pixdepth);
280 mputdwl(bih + BIH_DSIZEIMAGE, img->imgbytes);
281
282 if (alpha_bitfield) {
283 mputdwl(bih + BIH_DCOMPRESSION, BI_BITFIELDS);
284 mputdwl(bih + B4H_DALPHAMASK, 0xFF000000);
285 mputdwl(bih + B4H_DREDMASK , 0x00FF0000);
286 mputdwl(bih + B4H_DGREENMASK, 0x0000FF00);
287 mputdwl(bih + B4H_DBLUEMASK , 0x000000FF);
288 }
289
290 if (fwrite(bfh, (FILEHED_SIZE + bihsize), 1, fp) != 1)
291 ERROR_ABORT(err_writeerr);
292
293 /* ------------------------------------------------------ */
294
295 memset(rgbq, 0, sizeof(rgbq));
296
297 for (pal = img->palette, i = img->palnum; i > 0; i--, pal++) {
298 rgbq[RGBQ_RED] = pal->red;
299 rgbq[RGBQ_GREEN] = pal->green;
300 rgbq[RGBQ_BLUE] = pal->blue;
301 if (fwrite(rgbq, RGBQUAD_SIZE, 1, fp) != 1)
302 ERROR_ABORT(err_writeerr);
303 }
304
305 /* ------------------------------------------------------ */
306
307 if ((errmsg = write_rgb_bits(img, fp)) != NULL) ERROR_ABORT(errmsg);
308
309 /* ------------------------------------------------------ */
310
311 set_status("OK %.80s", basname(fn));
312 feed_line();
313
314 fflush(fp);
315 if (fp != stdout) fclose(fp);
316 imgbuf_free(img);
317
318 return TRUE;
319
320error_abort: /* error */
321 xxprintf(errmsg, fn);
322 if (fp != stdout && fp != NULL) fclose(fp);
323 imgbuf_free(img);
324
325 return FALSE;
326}
327
328static const char *write_rgb_bits(IMAGE *img, FILE *fp)
329{
330 DWORD wr = 16*1024*1024;
331 DWORD num = img->imgbytes;
332 BYTE *ptr = img->bmpbits;
333
334 while (num > 0) {
335 if (wr > num) wr = num;
336
337 if (fwrite(ptr, wr, 1, fp) != 1)
338 return err_writeerr;
339
340 ptr += wr; num -= wr;
341 }
342
343 return NULL;
344}
345
346static void mputdwl(void *ptr, unsigned long val)
347{
348 unsigned char *p = ptr;
349
350 p[0] = (unsigned char)(val & 0xFF);
351 p[1] = (unsigned char)(val >> 8 & 0xFF);
352 p[2] = (unsigned char)(val >> 16 & 0xFF);
353 p[3] = (unsigned char)(val >> 24 & 0xFF);
354}
355
356static void mputwl(void *ptr, unsigned int val)
357{
358 unsigned char *p = ptr;
359
360 p[0] = (unsigned char)(val & 0xFF);
361 p[1] = (unsigned char)(val >> 8 & 0xFF);
362}
363
364static void usage_exit(char *argv0, int status)
365{
366 static const char str_usage[] =
367 "png2bmp\n";
368
369 fprintf(stdout, str_usage, argv0, argv0, errlogfile);
370
371 exit(status);
372}
373
374int main(int argc, char *argv[])
375{
376 char outf[FILENAME_MAX];
377 IMAGE image;
378 int opt;
379 char *arg;
380 char *p, c;
381 int r_stdin, w_stdout;
382 int failure = 0, success = 0;
383
384 envargv(&argc, &argv, "B2P");
385
386 r_stdin = !isatty(fileno(stdin));
387 w_stdout = !isatty(fileno(stdout));
388
389 while (parsearg(&opt, &arg, argc, argv, "DdOoFfPp")) {
390 switch (toupper(opt)) {
391 case 'E': deletesrc ^= 1; break;
392 case 'T': copytime ^= 1; break;
393 case 'Q': quietmode ^= 1; break;
394 case 'L': errorlog ^= 1; break;
395
396 case 'X':
397 r_stdin = 0;
398 w_stdout = 0;
399 break;
400
401 case 'A':
402 alpha_format = (alpha_format == P2B_ALPHABMP_ARGB) ?
403 P2B_ALPHABMP_NONE : P2B_ALPHABMP_ARGB;
404 break;
405
406 case 'B':
407 alpha_format = (alpha_format == P2B_ALPHABMP_BITFIELD) ?
408 P2B_ALPHABMP_NONE : P2B_ALPHABMP_BITFIELD;
409 break;
410
411 case 'R':
412 expand_trans ^= 1;
413 break;
414
415 case 'F':
416 /* '-F' option of bmp2png (ignored on png2bmp) */
417 break;
418
419 case 'P':
420 /* '-P' option of bmp2png (ignored on png2bmp) */
421 break;
422
423 case 'D': /* output directory */
424 if (*arg == '-') arg = NULL;
425 if (arg == NULL) {
426 outdir[0] = '\0';
427 } else {
428 strcpy(outdir, arg);
429 addslash(outdir);
430 if (makedir(outdir) != 0) {
431 xxprintf(wrn_mkdirfail, outdir, 'D');
432 outdir[0] = '\0';
433 }
434 }
435 break;
436
437 case 'O': /* output filename */
438 if (arg == NULL) {
439 outnam[0] = '\0';
440 } else {
441 strcpy(outnam, arg);
442 p = basname(outnam);
443 c = *p; *p = '\0';
444 if (makedir(outnam) != 0) {
445 xxprintf(wrn_mkdirfail, outnam, 'O');
446 outnam[0] = '\0';
447 } else {
448 *p = c;
449 }
450 }
451 break;
452
453 case 0x00: /* input file spec */
454 if (outnam[0] != '\0') {
455 strcpy(outf, outnam);
456 outnam[0] = '\0';
457 } else if (w_stdout) {
458 if (!read_png(arg, &image)) return 1;
459 if (!write_bmp(NULL, &image)) return 1;
460 if (deletesrc) remove(arg);
461 return 0;
462 } else {
463 if (outdir[0] != '\0') {
464 strcat(strcpy(outf, outdir), basname(arg));
465 } else {
466 strcpy(outf, arg);
467 }
468
469 strcpy(suffix(outf), ".bmp");
470 }
471 /* ---------------------- */
472 printf("png2bmp read_png 2222222\n");
473 if (!read_png(arg, &image)) {
474 printf("png2bmp read_png arg image\n");
475 failure++;
476 break;
477 }
478 renbak(outf);
479 printf("png2bmp read_png 3333333\n");
480 if (!write_bmp(outf, &image)) {
481 printf("png2bmp read_png outf image\n");
482 failure++;
483 break;
484 }
485 /* ---------------------- */
486 if (copytime) cpyftime(arg, outf);
487 if (deletesrc) remove(arg);
488 /* ---------------------- */
489 success++;
490 break;
491
492 default:
493 ; /* Ignore unknown option */
494 }
495 }
496 printf("png2bmp failure is %d, success is %d, r_stdin is %d, w_stdout is %d \n", failure, success, r_stdin, w_stdout);
497 if (failure == 0 && success == 0) {
498 if (!r_stdin)
499 {
500 printf("png2bmp usage_exit\n");
501 usage_exit(argv[0], 255);
502 }
503 printf("png2bmp 111111111\n");
504 if (!read_png(NULL, &image))
505 {
506 printf("png2bmp read_png NULL\n");
507 return 1;
508 }
509 if (outnam[0] != '\0') {
510 printf("png2bmp write_bmp outnam is %s\n", outnam);
511 renbak(outnam);
512 return !write_bmp(outnam, &image);
513 } else if (w_stdout) {
514 printf("png2bmp write_bmp w_stdout\n");
515 return !write_bmp(NULL, &image);
516 } else {
517 printf("png2bmp write_bmp outf is %s\n", outf);
518 strcat(strcpy(outf, outdir), "___stdin.bmp");
519 renbak(outf);
520 return !write_bmp(outf, &image);
521 }
522 }
523 printf("png2bmp end failure is %d\n", failure);
524 return (failure > 255) ? 255 : failure;
525}
526