blob: 8f82f98cee1378b2aea294c58cb1bc2bccc737ba [file] [log] [blame]
#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;
}