#include #include #include #include #include #include #include #include typedef unsigned char uch; typedef unsigned short ush; typedef unsigned long ulg; #ifndef png_jmpbuf #define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) #endif png_structp png_ptr = NULL; png_infop info_ptr = NULL; png_uint_32 width, height; int bit_depth, color_type; uch *image_data = NULL; /* returns 0 on success, -1 on error */ int readpng_init(const char *fname, FILE *infile, ulg *pWidth, ulg *pHeight) { uch sig[8]; fread(sig, 1, 8, infile); if (!png_check_sig(sig, 8)) { fprintf(stderr, "%s: invalid PNG file signature\n", fname); return -1; } png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { fprintf(stderr, "%s: out of memory\n", fname); return -1; } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, NULL, NULL); fprintf(stderr, "%s: out of memory\n", fname); return -1; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); fprintf(stderr, "%s: bad header in PNG image\n", fname); return -1; } png_init_io(png_ptr, infile); png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); *pWidth = width; *pHeight = height; return 0; } /* returns -1 on error, 0 otherwise (out vars not touched if we can't find background */ int readpng_get_bgcolor(const char *fname, uch *red, uch *green, uch *blue) { png_color_16p pBackground; if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); fprintf(stderr, "%s: unspecified error in background reading\n", fname); return -1; } if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) return 0; png_get_bKGD(png_ptr, info_ptr, &pBackground); if (bit_depth == 16) { *red = pBackground->red >> 8; *green = pBackground->green >> 8; *blue = pBackground->blue >> 8; } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { if (bit_depth == 1) *red = *green = *blue = pBackground->gray ? 255 : 0; else if (bit_depth == 2) *red = *green = *blue = (255/3) * pBackground->gray; else /* bit_depth == 4 */ *red = *green = *blue = (255/15) * pBackground->gray; } else { *red = (uch)pBackground->red; *green = (uch)pBackground->green; *blue = (uch)pBackground->blue; } return 0; } /* display_exponent == LUT_exponent * CRT_exponent */ uch *readpng_get_image(const char *fname, double display_exponent, int *pChannels, ulg *pRowbytes) { double gamma; png_uint_32 i, rowbytes; png_bytepp row_pointers = NULL; if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); fprintf(stderr, "%s: unspecified error in image data reading\n", fname); return NULL; } if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand(png_ptr); if (bit_depth == 16) png_set_strip_16(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); if (png_get_gAMA(png_ptr, info_ptr, &gamma)) png_set_gamma(png_ptr, display_exponent, gamma); png_read_update_info(png_ptr, info_ptr); *pRowbytes = rowbytes = png_get_rowbytes(png_ptr, info_ptr); *pChannels = (int)png_get_channels(png_ptr, info_ptr); if ((image_data = (uch *)malloc(rowbytes*height)) == NULL) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); fprintf(stderr, "%s: could not allocate image buffer\n", fname); return NULL; } if ((row_pointers = (png_bytepp)malloc(height*sizeof(png_bytep))) == NULL) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(image_data); image_data = NULL; fprintf(stderr, "%s: could not allocate image row buffer\n", fname); return NULL; } fprintf(stderr, "image %s: chans=%d rowbytes=%u height=%u\n", fname, *pChannels, rowbytes, height); for (i = 0; i < height; ++i) row_pointers[i] = image_data + i * rowbytes; png_read_image(png_ptr, row_pointers); free(row_pointers); row_pointers = NULL; png_read_end(png_ptr, NULL); return image_data; } void readpng_cleanup(int free_image_data) { if (free_image_data && image_data) { free(image_data); image_data = NULL; } if (png_ptr && info_ptr) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); png_ptr = NULL; info_ptr = NULL; } } int do_convert(const char *infilename, const char *outfilename) { ulg image_width, image_height, image_rowbytes, row, i; FILE *fpin; uch *image_data, *src; uint16_t bred = 0, bgreen = 0, bblue = 0, buf = 0; int image_channels, fdout; uch bg_red=0, bg_green=0, bg_blue=0, br, bg, bb, ba; double display_exponent = 2.2; /* a guesstimate */ if (!(fpin = fopen(infilename, "rb"))) { fprintf(stderr, "%s: could not open file\n", infilename); return -1; } if (readpng_init(infilename, fpin, &image_width, &image_height)) { readpng_cleanup(1); fclose(fpin); return -1; } if (readpng_get_bgcolor(infilename, &bg_red, &bg_green, &bg_blue)) { readpng_cleanup(1); fclose(fpin); return -1; } image_data = readpng_get_image(infilename, display_exponent, &image_channels, &image_rowbytes); readpng_cleanup(0); fclose(fpin); if (!image_data) return -1; if ((fdout = creat(outfilename, S_IREAD|S_IWRITE)) < 0) { free(image_data); fprintf(stderr, "%s: could not open file\n", outfilename); return -1; } for (row = 0; row < image_height; ++row) { src = image_data + (row * image_rowbytes); for (i = image_width; i > 0; --i) { if (image_channels == 3) { bred = *src++; bgreen = *src++; bblue = *src++; } else if (image_channels == 4) { br = *src++; bg = *src++; bb = *src++; ba = *src++; if (ba == 255) { bred = br; bgreen = bg; bblue = bb; } else if (ba == 0) { bred = bg_red; bgreen = bg_green; bblue = bg_blue; } else { png_composite(bred, br, ba, bg_red); png_composite(bgreen, bg, ba, bg_green); png_composite(bblue, bb, ba, bg_blue); } } bred = (bred >> 3) & 0x1F; bgreen = (bgreen >> 2) & 0x3F; bblue = (bblue >> 3) & 0x1F; buf = (bred << 11) | (bgreen << 5) | bblue; if (write(fdout, &buf, sizeof(uint16_t)) < 0) { close(fdout); free(image_data); fprintf(stderr, "%s: error writing image data\n", outfilename); return -1; } } } close(fdout); free(image_data); return 0; } int main(int argc, char *argv[]) { if (argc < 3) { fprintf(stderr, "usage: mksplash inputfile outputfile\n"); return EXIT_FAILURE; } if (do_convert(argv[1], argv[2])) return EXIT_FAILURE; return EXIT_SUCCESS; }