| #include "common.h" |
| #include "bmphed.h" |
| |
| #define PNG2BMP_VERSION "1.62" |
| #define PNG2BMP_COPYRIGHT "Copyright" |
| |
| #define ZLIB_VERSION "1.2.3" |
| |
| char outnam[FILENAME_MAX]; |
| char outdir[FILENAME_MAX]; |
| int deletesrc = 0; |
| int copytime = 0; |
| |
| #define P2B_ALPHABMP_NONE 0 |
| #define P2B_ALPHABMP_ARGB 1 |
| #define P2B_ALPHABMP_BITFIELD 2 |
| |
| int alpha_format = P2B_ALPHABMP_NONE; |
| int expand_trans = 0; |
| |
| const char errlogfile[] = "./p2berror.log"; |
| |
| |
| const char wrn_mkdirfail[] = |
| "WARNING: Can not create directory - %s\n" |
| "WARNING: Output directory specified by '-%c' will be ignored.\n"; |
| |
| const char err_ropenfail[] = "SKIPPED: No such file or directory - %s\n"; |
| const char err_wopenfail[] = "SKIPPED: Cannot create - %s\n"; |
| const char err_outofmemory[] = "SKIPPED: Out of memory - %s\n"; |
| |
| const char err_writeerr[] = "SKIPPED: Write operation failed - %s\n"; |
| const char err_not_a_png[] = "SKIPPED: Not a PNG file - %s\n"; |
| |
| static BOOL read_png(char *, IMAGE *); |
| static int skip_macbinary(png_structp); |
| static void to4bpp(png_structp, png_row_infop, png_bytep); |
| static BOOL write_bmp(char *, IMAGE *); |
| static const char *write_rgb_bits(IMAGE *, FILE *); |
| static void mputdwl(void *, unsigned long); |
| static void mputwl(void *, unsigned int); |
| static void usage_exit(char *, int); |
| |
| #define ERROR_ABORT(s) do { errmsg = (s); goto error_abort; } while (0) |
| |
| static BOOL read_png(char *fn, IMAGE *img) |
| { |
| png_structp png_ptr; |
| png_infop info_ptr, end_info; |
| png_uint_32 width, height; |
| int bit_depth, color_type; |
| int xbit_depth, xcolor_type, xchannels; |
| const char *errmsg; |
| FILE *fp; |
| |
| imgbuf_init(img); |
| |
| if (fn == NULL) { |
| fn = " (stdin)"; |
| fp = binary_stdio(fileno(stdin)); |
| } else { |
| fp = fopen(fn, "rb"); |
| } |
| if (fp == NULL) ERROR_ABORT(err_ropenfail); |
| |
| set_status("Reading %.80s", basname(fn)); |
| |
| /* ------------------------------------------------------ */ |
| |
| png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, fn, |
| png_my_error, png_my_warning); |
| if (png_ptr == NULL) { |
| ERROR_ABORT(err_outofmemory); |
| } |
| info_ptr = png_create_info_struct(png_ptr); |
| end_info = png_create_info_struct(png_ptr); |
| if (info_ptr == NULL || end_info == NULL) { |
| png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); |
| ERROR_ABORT(err_outofmemory); |
| } |
| if (setjmp(png_jmpbuf(png_ptr))) { |
| /* If we get here, we had a problem writing the file */ |
| png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); |
| ERROR_ABORT(NULL); |
| } |
| png_init_io(png_ptr, fp); |
| png_set_sig_bytes(png_ptr, skip_macbinary(png_ptr)); |
| |
| /* ------------------------------------------------------ */ |
| |
| png_read_info(png_ptr, info_ptr); |
| |
| png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, |
| &color_type, NULL, NULL, NULL); |
| |
| /* ------------------------------------------------------ */ |
| |
| if (color_type & PNG_COLOR_MASK_ALPHA) { |
| if (alpha_format == P2B_ALPHABMP_NONE) { |
| png_set_strip_alpha(png_ptr); |
| color_type &= ~PNG_COLOR_MASK_ALPHA; |
| } |
| } else if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { |
| if (alpha_format != P2B_ALPHABMP_NONE && expand_trans) { |
| png_set_tRNS_to_alpha(png_ptr); |
| color_type |= PNG_COLOR_MASK_ALPHA; |
| color_type &= ~PNG_COLOR_MASK_PALETTE; |
| } |
| } |
| if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { |
| png_set_gray_to_rgb(png_ptr); |
| } |
| if (color_type == PNG_COLOR_TYPE_RGB || |
| color_type == PNG_COLOR_TYPE_RGB_ALPHA) { |
| png_set_bgr(png_ptr); |
| } |
| if (!(color_type & PNG_COLOR_MASK_ALPHA) && bit_depth == 2) { |
| png_set_user_transform_info(png_ptr, NULL, 4, 1); |
| png_set_read_user_transform_fn(png_ptr, to4bpp); |
| } |
| if (bit_depth == 16) |
| png_set_strip_16(png_ptr); |
| |
| png_read_update_info(png_ptr, info_ptr); |
| |
| /* ------------------------------------------------------ */ |
| |
| png_get_IHDR(png_ptr, info_ptr, &width, &height, &xbit_depth, |
| &xcolor_type, NULL, NULL, NULL); |
| xchannels = png_get_channels(png_ptr, info_ptr); |
| |
| img->width = (LONG)width; |
| img->height = (LONG)height; |
| img->pixdepth = (UINT)xbit_depth * xchannels; |
| img->palnum = (img->pixdepth <= 8) ? (1 << img->pixdepth) : 0; |
| img->topdown = FALSE; |
| img->alpha = (xcolor_type & PNG_COLOR_MASK_ALPHA) ? TRUE : FALSE; |
| |
| if (!imgbuf_alloc(img)) { |
| png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); |
| ERROR_ABORT(err_outofmemory); |
| } |
| |
| if (img->palnum > 0) { |
| if (xcolor_type == PNG_COLOR_TYPE_PALETTE) { |
| png_colorp palette; |
| int num_palette; |
| png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); |
| if (num_palette > (int)img->palnum) num_palette = img->palnum; |
| memset(img->palette, 0, img->palnum * sizeof(png_color)); |
| memcpy(img->palette, palette, num_palette * sizeof(png_color)); |
| } else { |
| int depth = (bit_depth == 16) ? 8 : bit_depth; |
| memset(img->palette, 0, img->palnum * sizeof(png_color)); |
| png_build_grayscale_palette(depth, img->palette); |
| } |
| } |
| |
| /* ------------------------------------------------------ */ |
| |
| png_set_read_status_fn(png_ptr, row_callback); |
| init_progress_meter(png_ptr, img->width, img->height); |
| |
| png_read_image(png_ptr, img->rowptr); |
| |
| png_read_end(png_ptr, end_info); |
| png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); |
| |
| /* ------------------------------------------------------ */ |
| |
| set_status("Read OK %.80s", basname(fn)); |
| |
| if (fp != stdin) fclose(fp); |
| |
| return TRUE; |
| |
| error_abort: /* error */ |
| if (errmsg != NULL) xxprintf(errmsg, fn); |
| if (fp != stdin && fp != NULL) fclose(fp); |
| imgbuf_free(img); |
| |
| return FALSE; |
| } |
| |
| static int skip_macbinary(png_structp png_ptr) |
| { |
| enum { PNG_BYTES_TO_CHECK = 8, MACBIN_SIZE = 128 }; |
| png_byte buf[MACBIN_SIZE]; |
| png_bytep sig; |
| png_size_t check; |
| png_FILE_p fp = (png_FILE_p)png_get_io_ptr(png_ptr); |
| |
| check = (png_size_t)fread(buf, 1, PNG_BYTES_TO_CHECK, fp); |
| if (check != PNG_BYTES_TO_CHECK) png_error(png_ptr, "Read Error"); |
| |
| if (png_sig_cmp(buf, 0, PNG_BYTES_TO_CHECK) == 0) |
| return PNG_BYTES_TO_CHECK; |
| |
| check = (png_size_t)fread(buf, 1, MACBIN_SIZE, fp); |
| if (check != MACBIN_SIZE) png_error(png_ptr, "Read Error"); |
| |
| sig = buf + MACBIN_SIZE - PNG_BYTES_TO_CHECK; |
| if (png_sig_cmp(sig, 0, PNG_BYTES_TO_CHECK) == 0) |
| return PNG_BYTES_TO_CHECK; |
| |
| xxprintf(err_not_a_png, (char *)png_get_error_ptr(png_ptr)); |
| longjmp(png_jmpbuf(png_ptr), 1); |
| |
| return 0; /* to quiet compiler warnings */ |
| } |
| |
| static void to4bpp(png_structp png_ptr, png_row_infop row_info, png_bytep data) |
| { |
| static const png_byte pix[] = { |
| 0x00, 0x01, 0x02, 0x03, 0x10, 0x11, 0x12, 0x13, |
| 0x20, 0x21, 0x22, 0x23, 0x30, 0x31, 0x32, 0x33, |
| }; |
| png_uint_32 rowb; |
| png_bytep p, q; |
| png_byte c; |
| |
| rowb = (row_info->width + 1) / 2; |
| q = data + rowb; |
| p = data + rowb / 2; |
| |
| if (rowb % 2 == 1) { |
| c = *p; |
| *(--q) = pix[c >> 4]; |
| } |
| while (p > data) { |
| c = *(--p); |
| *(--q) = pix[c & 0x0F]; |
| *(--q) = pix[c >> 4]; |
| } |
| row_info->bit_depth = 4; |
| row_info->pixel_depth = 4; |
| row_info->rowbytes = rowb; |
| } |
| |
| static BOOL write_bmp(char *fn, IMAGE *img) |
| { |
| BYTE bfh[FILEHED_SIZE + BMPV4HED_SIZE]; |
| BYTE *const bih = bfh + FILEHED_SIZE; |
| BYTE rgbq[RGBQUAD_SIZE]; |
| BOOL alpha_bitfield; |
| DWORD bihsize, offbits, filesize; |
| PALETTE *pal; |
| const char *errmsg; |
| FILE *fp; |
| UINT i; |
| |
| if (fn == NULL) { |
| fn = " (stdout)"; |
| fp = binary_stdio(fileno(stdout)); |
| } else { |
| fp = fopen(fn, "wb"); |
| } |
| if (fp == NULL) ERROR_ABORT(err_wopenfail); |
| |
| set_status("Writing %.80s", basname(fn)); |
| |
| /* ------------------------------------------------------ */ |
| |
| alpha_bitfield = (img->alpha && alpha_format == P2B_ALPHABMP_BITFIELD); |
| bihsize = (alpha_bitfield) ? BMPV4HED_SIZE : INFOHED_SIZE; |
| offbits = FILEHED_SIZE + bihsize + RGBQUAD_SIZE * img->palnum; |
| filesize = offbits + img->imgbytes; |
| |
| memset(bfh, 0, sizeof(bfh)); |
| |
| mputwl( bfh + BFH_WTYPE , BMP_SIGNATURE); |
| mputdwl(bfh + BFH_DSIZE , filesize); |
| mputdwl(bfh + BFH_DOFFBITS, offbits); |
| |
| mputdwl(bih + BIH_DSIZE , bihsize); |
| mputdwl(bih + BIH_LWIDTH , (DWORD)img->width); |
| mputdwl(bih + BIH_LHEIGHT , (DWORD)img->height); |
| mputwl( bih + BIH_WPLANES , 1); |
| mputwl( bih + BIH_WBITCOUNT , img->pixdepth); |
| mputdwl(bih + BIH_DSIZEIMAGE, img->imgbytes); |
| |
| if (alpha_bitfield) { |
| mputdwl(bih + BIH_DCOMPRESSION, BI_BITFIELDS); |
| mputdwl(bih + B4H_DALPHAMASK, 0xFF000000); |
| mputdwl(bih + B4H_DREDMASK , 0x00FF0000); |
| mputdwl(bih + B4H_DGREENMASK, 0x0000FF00); |
| mputdwl(bih + B4H_DBLUEMASK , 0x000000FF); |
| } |
| |
| if (fwrite(bfh, (FILEHED_SIZE + bihsize), 1, fp) != 1) |
| ERROR_ABORT(err_writeerr); |
| |
| /* ------------------------------------------------------ */ |
| |
| memset(rgbq, 0, sizeof(rgbq)); |
| |
| for (pal = img->palette, i = img->palnum; i > 0; i--, pal++) { |
| rgbq[RGBQ_RED] = pal->red; |
| rgbq[RGBQ_GREEN] = pal->green; |
| rgbq[RGBQ_BLUE] = pal->blue; |
| if (fwrite(rgbq, RGBQUAD_SIZE, 1, fp) != 1) |
| ERROR_ABORT(err_writeerr); |
| } |
| |
| /* ------------------------------------------------------ */ |
| |
| if ((errmsg = write_rgb_bits(img, fp)) != NULL) ERROR_ABORT(errmsg); |
| |
| /* ------------------------------------------------------ */ |
| |
| set_status("OK %.80s", basname(fn)); |
| feed_line(); |
| |
| fflush(fp); |
| if (fp != stdout) fclose(fp); |
| imgbuf_free(img); |
| |
| return TRUE; |
| |
| error_abort: /* error */ |
| xxprintf(errmsg, fn); |
| if (fp != stdout && fp != NULL) fclose(fp); |
| imgbuf_free(img); |
| |
| return FALSE; |
| } |
| |
| static const char *write_rgb_bits(IMAGE *img, FILE *fp) |
| { |
| DWORD wr = 16*1024*1024; |
| DWORD num = img->imgbytes; |
| BYTE *ptr = img->bmpbits; |
| |
| while (num > 0) { |
| if (wr > num) wr = num; |
| |
| if (fwrite(ptr, wr, 1, fp) != 1) |
| return err_writeerr; |
| |
| ptr += wr; num -= wr; |
| } |
| |
| return NULL; |
| } |
| |
| static void mputdwl(void *ptr, unsigned long val) |
| { |
| unsigned char *p = ptr; |
| |
| p[0] = (unsigned char)(val & 0xFF); |
| p[1] = (unsigned char)(val >> 8 & 0xFF); |
| p[2] = (unsigned char)(val >> 16 & 0xFF); |
| p[3] = (unsigned char)(val >> 24 & 0xFF); |
| } |
| |
| static void mputwl(void *ptr, unsigned int val) |
| { |
| unsigned char *p = ptr; |
| |
| p[0] = (unsigned char)(val & 0xFF); |
| p[1] = (unsigned char)(val >> 8 & 0xFF); |
| } |
| |
| static void usage_exit(char *argv0, int status) |
| { |
| static const char str_usage[] = |
| "png2bmp\n"; |
| |
| fprintf(stdout, str_usage, argv0, argv0, errlogfile); |
| |
| exit(status); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| char outf[FILENAME_MAX]; |
| IMAGE image; |
| int opt; |
| char *arg; |
| char *p, c; |
| int r_stdin, w_stdout; |
| int failure = 0, success = 0; |
| |
| envargv(&argc, &argv, "B2P"); |
| |
| r_stdin = !isatty(fileno(stdin)); |
| w_stdout = !isatty(fileno(stdout)); |
| |
| while (parsearg(&opt, &arg, argc, argv, "DdOoFfPp")) { |
| switch (toupper(opt)) { |
| case 'E': deletesrc ^= 1; break; |
| case 'T': copytime ^= 1; break; |
| case 'Q': quietmode ^= 1; break; |
| case 'L': errorlog ^= 1; break; |
| |
| case 'X': |
| r_stdin = 0; |
| w_stdout = 0; |
| break; |
| |
| case 'A': |
| alpha_format = (alpha_format == P2B_ALPHABMP_ARGB) ? |
| P2B_ALPHABMP_NONE : P2B_ALPHABMP_ARGB; |
| break; |
| |
| case 'B': |
| alpha_format = (alpha_format == P2B_ALPHABMP_BITFIELD) ? |
| P2B_ALPHABMP_NONE : P2B_ALPHABMP_BITFIELD; |
| break; |
| |
| case 'R': |
| expand_trans ^= 1; |
| break; |
| |
| case 'F': |
| /* '-F' option of bmp2png (ignored on png2bmp) */ |
| break; |
| |
| case 'P': |
| /* '-P' option of bmp2png (ignored on png2bmp) */ |
| break; |
| |
| case 'D': /* output directory */ |
| if (*arg == '-') arg = NULL; |
| if (arg == NULL) { |
| outdir[0] = '\0'; |
| } else { |
| strcpy(outdir, arg); |
| addslash(outdir); |
| if (makedir(outdir) != 0) { |
| xxprintf(wrn_mkdirfail, outdir, 'D'); |
| outdir[0] = '\0'; |
| } |
| } |
| break; |
| |
| case 'O': /* output filename */ |
| if (arg == NULL) { |
| outnam[0] = '\0'; |
| } else { |
| strcpy(outnam, arg); |
| p = basname(outnam); |
| c = *p; *p = '\0'; |
| if (makedir(outnam) != 0) { |
| xxprintf(wrn_mkdirfail, outnam, 'O'); |
| outnam[0] = '\0'; |
| } else { |
| *p = c; |
| } |
| } |
| break; |
| |
| case 0x00: /* input file spec */ |
| if (outnam[0] != '\0') { |
| strcpy(outf, outnam); |
| outnam[0] = '\0'; |
| } else if (w_stdout) { |
| if (!read_png(arg, &image)) return 1; |
| if (!write_bmp(NULL, &image)) return 1; |
| if (deletesrc) remove(arg); |
| return 0; |
| } else { |
| if (outdir[0] != '\0') { |
| strcat(strcpy(outf, outdir), basname(arg)); |
| } else { |
| strcpy(outf, arg); |
| } |
| |
| strcpy(suffix(outf), ".bmp"); |
| } |
| /* ---------------------- */ |
| printf("png2bmp read_png 2222222\n"); |
| if (!read_png(arg, &image)) { |
| printf("png2bmp read_png arg image\n"); |
| failure++; |
| break; |
| } |
| renbak(outf); |
| printf("png2bmp read_png 3333333\n"); |
| if (!write_bmp(outf, &image)) { |
| printf("png2bmp read_png outf image\n"); |
| failure++; |
| break; |
| } |
| /* ---------------------- */ |
| if (copytime) cpyftime(arg, outf); |
| if (deletesrc) remove(arg); |
| /* ---------------------- */ |
| success++; |
| break; |
| |
| default: |
| ; /* Ignore unknown option */ |
| } |
| } |
| printf("png2bmp failure is %d, success is %d, r_stdin is %d, w_stdout is %d \n", failure, success, r_stdin, w_stdout); |
| if (failure == 0 && success == 0) { |
| if (!r_stdin) |
| { |
| printf("png2bmp usage_exit\n"); |
| usage_exit(argv[0], 255); |
| } |
| printf("png2bmp 111111111\n"); |
| if (!read_png(NULL, &image)) |
| { |
| printf("png2bmp read_png NULL\n"); |
| return 1; |
| } |
| if (outnam[0] != '\0') { |
| printf("png2bmp write_bmp outnam is %s\n", outnam); |
| renbak(outnam); |
| return !write_bmp(outnam, &image); |
| } else if (w_stdout) { |
| printf("png2bmp write_bmp w_stdout\n"); |
| return !write_bmp(NULL, &image); |
| } else { |
| printf("png2bmp write_bmp outf is %s\n", outf); |
| strcat(strcpy(outf, outdir), "___stdin.bmp"); |
| renbak(outf); |
| return !write_bmp(outf, &image); |
| } |
| } |
| printf("png2bmp end failure is %d\n", failure); |
| return (failure > 255) ? 255 : failure; |
| } |
| |