/*************************************************************************** file : png2jpg.cpp created : Thu Dec 4 20:16:09 CET 2003 copyright : (C) 2003 by Eric Espié email : eric.espie@torcs.org version : $Id: png2jpg.c,v 1.1 2003/12/06 16:36:24 torcs Exp $ ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ /** @file PNG to JPEG converter @author Eric Espie @version $Id: png2jpg.c,v 1.1 2003/12/06 16:36:24 torcs Exp $ */ #ifdef WIN32 #include #endif #include #include #ifdef WIN32 #include #endif #include "png.h" #include "jpeglib.h" static char buf[1024]; #define PNG_BYTES_TO_CHECK 4 /** Load an image from disk to a buffer in RGBA mode. @ingroup img @param widthp width of the read image @param heightp height of the read image @param screen_gamma gamma correction value @return Pointer on the buffer containing the image
NULL Error */ unsigned char * ImgReadPng(FILE *fp, int *widthp, int *heightp, float screen_gamma) { unsigned char buf[PNG_BYTES_TO_CHECK]; png_structp png_ptr; png_infop info_ptr; png_uint_32 width, height; int bit_depth, color_type, interlace_type; /* png_color_16p image_background; */ double gamma; png_bytep *row_pointers; unsigned char *image_ptr, *cur_ptr; png_uint_32 rowbytes; png_uint_32 i; if (fread(buf, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK) { fprintf(stderr, "Can't read file\n"); fclose(fp); return (unsigned char *)NULL; } if (png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK) != 0) { fprintf(stderr, "File not in png format\n"); fclose(fp); return (unsigned char *)NULL; } png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, (png_error_ptr)NULL, (png_error_ptr)NULL); if (png_ptr == NULL) { fprintf(stderr, "Img Failed to create read_struct\n"); fclose(fp); return (unsigned char *)NULL; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { fclose(fp); png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return (unsigned char *)NULL; } if (setjmp(png_ptr->jmpbuf)) { /* Free all of the memory associated with the png_ptr and info_ptr */ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); fclose(fp); /* If we get here, we had a problem reading the file */ return (unsigned char *)NULL; } png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); /* png_set_invert_alpha(png_ptr); */ png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); *widthp = (int)width; *heightp = (int)height; if (bit_depth == 1 && color_type == PNG_COLOR_TYPE_GRAY) png_set_invert_mono(png_ptr); if (bit_depth == 16) { png_set_swap(png_ptr); png_set_strip_16(png_ptr); } if (bit_depth < 8) png_set_packing(png_ptr); 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 (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); /* if (png_get_bKGD(png_ptr, info_ptr, &image_background)) */ /* png_set_background(png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); */ if (bit_depth == 8 && color_type == PNG_COLOR_TYPE_RGB) png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); if (png_get_gAMA(png_ptr, info_ptr, &gamma)) png_set_gamma(png_ptr, screen_gamma, gamma); else png_set_gamma(png_ptr, screen_gamma, 0.50); png_read_update_info(png_ptr, info_ptr); rowbytes = png_get_rowbytes(png_ptr, info_ptr); /* RGB expected... */ if (rowbytes != (4 * width)) { fprintf(stderr, "input: bad byte count... %ld instead of %ld\n", rowbytes, 4 * width); fclose(fp); png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return (unsigned char *)NULL; } row_pointers = (png_bytep*)malloc(height * sizeof(png_bytep)); if (row_pointers == NULL) { fclose(fp); png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return (unsigned char *)NULL; } image_ptr = (unsigned char *)malloc(height * rowbytes); if (image_ptr == NULL) { fclose(fp); png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return (unsigned char *)NULL; } for (i = 0, cur_ptr = image_ptr ; i < height; i++, cur_ptr += rowbytes) { row_pointers[i] = cur_ptr; } png_read_image(png_ptr, row_pointers); png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); free(row_pointers); fclose(fp); return image_ptr; } /* * Sample routine for JPEG compression. We assume that the target file name * and a compression quality factor are passed in. */ void write_JPEG_file (FILE *outfile, unsigned char *image_buffer, int image_width, int image_height, int quality) { /* This struct contains the JPEG compression parameters and pointers to * working space (which is allocated as needed by the JPEG library). * It is possible to have several such structures, representing multiple * compression/decompression processes, in existence at once. We refer * to any one struct (and its associated working data) as a "JPEG object". */ struct jpeg_compress_struct cinfo; /* This struct represents a JPEG error handler. It is declared separately * because applications often want to supply a specialized error handler * (see the second half of this file for an example). But here we just * take the easy way out and use the standard error handler, which will * print a message on stderr and call exit() if compression fails. * Note that this struct must live as long as the main JPEG parameter * struct, to avoid dangling-pointer problems. */ struct jpeg_error_mgr jerr; /* More stuff */ JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ int row_stride; /* physical row width in image buffer */ /* Step 1: allocate and initialize JPEG compression object */ /* We have to set up the error handler first, in case the initialization * step fails. (Unlikely, but it could happen if you are out of memory.) * This routine fills in the contents of struct jerr, and returns jerr's * address which we place into the link field in cinfo. */ cinfo.err = jpeg_std_error(&jerr); /* Now we can initialize the JPEG compression object. */ jpeg_create_compress(&cinfo); /* Step 2: specify data destination (eg, a file) */ /* Note: steps 2 and 3 can be done in either order. */ jpeg_stdio_dest(&cinfo, outfile); /* Step 3: set parameters for compression */ /* First we supply a description of the input image. * Four fields of the cinfo struct must be filled in: */ cinfo.image_width = image_width; /* image width and height, in pixels */ cinfo.image_height = image_height; cinfo.input_components = 3; /* # of color components per pixel */ cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ /* Now use the library's routine to set default compression parameters. * (You must set at least cinfo.in_color_space before calling this, * since the defaults depend on the source color space.) */ jpeg_set_defaults(&cinfo); /* Now you can set any non-default parameters you wish to. * Here we just illustrate the use of quality (quantization table) scaling: */ jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); /* Step 4: Start compressor */ /* TRUE ensures that we will write a complete interchange-JPEG file. * Pass TRUE unless you are very sure of what you're doing. */ jpeg_start_compress(&cinfo, TRUE); /* Step 5: while (scan lines remain to be written) */ /* jpeg_write_scanlines(...); */ /* Here we use the library's state variable cinfo.next_scanline as the * loop counter, so that we don't have to keep track ourselves. * To keep things simple, we pass one scanline per call; you can pass * more if you wish, though. */ row_stride = image_width * 3; /* JSAMPLEs per row in image_buffer */ while (cinfo.next_scanline < cinfo.image_height) { /* jpeg_write_scanlines expects an array of pointers to scanlines. * Here the array is only one element long, but you could pass * more than one scanline at a time if that's more convenient. */ row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride]; (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); } /* Step 6: Finish compression */ jpeg_finish_compress(&cinfo); /* Step 7: release JPEG compression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_compress(&cinfo); /* And we're done! */ } static char *infile; static char *outfile; static int quality; static float gamma; static void usage(void) { fprintf(stderr, "PNG to JPEG image converter $Revision: 1.1 $ \n"); fprintf(stderr, "Usage: png2jpg [-i input.png] [-o output.jpg] [-q quality] [-g gamma]\n"); fprintf(stderr, " -i input.png : input PNG file (default stdin)\n"); fprintf(stderr, " -o output.jpg : output JPEG file (default input.jpg)\n"); fprintf(stderr, " -q quality : JPEG file quality [0..100] (default 90)\n"); fprintf(stderr, " -g gamma : PNG gamma value (default 0.5)\n"); } static void init_args(int argc, char **argv) { int i; infile = NULL; outfile = NULL; quality = 90; gamma = 0.5; i = 1; while (i < argc) { if ((strncmp(argv[i], "-h", 2) == 0) || (strncmp(argv[i], "-?", 2) == 0) || (strncmp(argv[i], "--help", 6) == 0)) { usage(); exit(0); } if (strncmp(argv[i], "-v", 2) == 0) { printf("PNG to JPEG image converter $Revision: 1.1 $ \n"); exit(0); } if (strncmp(argv[i], "-i", 2) == 0) { if (i + 1 < argc) { infile = strdup(argv[++i]); } else { usage(); exit(0); } } else if (strncmp(argv[i], "-o", 2) == 0) { if (i + 1 < argc) { outfile = strdup(argv[++i]); } else { usage(); exit(0); } } else if (strncmp(argv[i], "-q", 2) == 0) { if (i + 1 < argc) { quality = strtol(argv[++i], NULL, 0); } else { usage(); exit(0); } } else if (strncmp(argv[i], "-g", 2) == 0) { if (i + 1 < argc) { sscanf(argv[++i], "%g", &gamma); } else { usage(); exit(0); } } else { usage(); exit(0); } i++; } } int main (int argc, char **argv) { FILE *fin; FILE *fout; char *s; unsigned char *img1, *ip1; unsigned char *img2, *ip2; int width, height; int i, len; init_args (argc, argv); if (infile) { fin = fopen (infile, "rb"); if (!fin) { perror (infile); exit (1); } } else { fin = stdin; } if (outfile) { if ((fout = fopen(outfile, "wb")) == NULL) { perror (outfile); exit(1); } } else { if (infile) { outfile = strdup(infile); s = outfile + strlen(outfile) - 3; sprintf(s, "jpg"); if ((fout = fopen(outfile, "wb")) == NULL) { perror (outfile); exit(1); } } else { fout = stdout; } } img1 = ImgReadPng(fin, &width, &height, gamma); if (!img1) { fprintf(stderr, "Error while reading the file\n"); exit(1); } /* Strip the alpha channel * and revert the lines */ img2 = (unsigned char *)malloc(width * height * 3); ip1 = img1; ip2 = img2; len = width * height * 4; for (i = 1; i <= len; i++) { if (i % 4) { *ip2 = *ip1; ip2++; } ip1++; } write_JPEG_file (fout, img2, width, height, quality); return 0; }