/* * The copyright in this software is being made available under the 2-clauses * BSD License, included below. This software may be subject to other third * party and contributor rights, including patent rights, and no such rights * are granted under this license. * * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium * Copyright (c) 2002-2014, Professor Benoit Macq * Copyright (c) 2001-2003, David Janssens * Copyright (c) 2002-2003, Yannick Verschueren * Copyright (c) 2003-2007, Francois-Olivier Devaux * Copyright (c) 2003-2014, Antonin Descampe * Copyright (c) 2005, Herve Drolon, FreeImage Team * Copyright (c) 2006-2007, Parvatha Elangovan * Copyright (c) 2015, Matthieu Darbois * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "opj_apps_config.h" #include #include #include #include #include #include #include "openjpeg.h" #include "convert.h" #define PNG_MAGIC "\x89PNG\x0d\x0a\x1a\x0a" #define MAGIC_SIZE 8 /* PNG allows bits per sample: 1, 2, 4, 8, 16 */ static void convert_16u32s_C1R(const OPJ_BYTE* pSrc, OPJ_INT32* pDst, OPJ_SIZE_T length) { OPJ_SIZE_T i; for (i = 0; i < length; i++) { OPJ_INT32 val0 = *pSrc++; OPJ_INT32 val1 = *pSrc++; pDst[i] = val0 << 8 | val1; } } static opj_image_t * pngtoimage_internal(opj_cparameters_t * params, FILE *reader, png_structp png, png_infop info, png_uint_32* pheight, OPJ_BYTE*** prows, OPJ_INT32** prow32s) { *pheight = 0; *prows = NULL; *prow32s = NULL; if (setjmp(png_jmpbuf(png))) { return NULL; } { opj_image_t *image = NULL; opj_image_cmptparm_t cmptparm[4]; OPJ_UINT32 nr_comp; convert_XXx32s_C1R cvtXXTo32s = NULL; convert_32s_CXPX cvtCxToPx = NULL; OPJ_INT32* planes[4]; double gamma; int bit_depth, interlace_type, compression_type, filter_type; OPJ_UINT32 i; png_uint_32 width, height = 0U; int color_type; OPJ_BYTE** rows = NULL; OPJ_INT32* row32s = NULL; png_init_io(png, reader); png_set_sig_bytes(png, MAGIC_SIZE); png_read_info(png, info); if (png_get_IHDR(png, info, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type) == 0) { return image; } *pheight = height; /* png_set_expand(): * expand paletted images to RGB, expand grayscale images of * less than 8-bit depth to 8-bit depth, and expand tRNS chunks * to alpha channels. */ if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_expand(png); } if (png_get_valid(png, info, PNG_INFO_tRNS)) { png_set_expand(png); } /* We might wan't to expand background */ /* if(png_get_valid(png, info, PNG_INFO_bKGD)) { png_color_16p bgnd; png_get_bKGD(png, info, &bgnd); png_set_background(png, bgnd, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); } */ if (!png_get_gAMA(png, info, &gamma)) { gamma = 1.0; } /* we're not displaying but converting, screen gamma == 1.0 */ png_set_gamma(png, 1.0, gamma); png_read_update_info(png, info); color_type = png_get_color_type(png, info); switch (color_type) { case PNG_COLOR_TYPE_GRAY: nr_comp = 1; break; case PNG_COLOR_TYPE_GRAY_ALPHA: nr_comp = 2; break; case PNG_COLOR_TYPE_RGB: nr_comp = 3; break; case PNG_COLOR_TYPE_RGB_ALPHA: nr_comp = 4; break; default: fprintf(stderr, "pngtoimage: colortype %d is not supported\n", color_type); return image; } cvtCxToPx = convert_32s_CXPX_LUT[nr_comp]; bit_depth = png_get_bit_depth(png, info); switch (bit_depth) { case 1: case 2: case 4: case 8: cvtXXTo32s = convert_XXu32s_C1R_LUT[bit_depth]; break; case 16: /* 16 bpp is specific to PNG */ cvtXXTo32s = convert_16u32s_C1R; break; default: fprintf(stderr, "pngtoimage: bit depth %d is not supported\n", bit_depth); return image; } rows = (OPJ_BYTE**)calloc(height + 1, sizeof(OPJ_BYTE*)); if (rows == NULL) { fprintf(stderr, "pngtoimage: memory out\n"); return image; } *prows = rows; for (i = 0; i < height; ++i) { rows[i] = (OPJ_BYTE*)malloc(png_get_rowbytes(png, info)); if (rows[i] == NULL) { fprintf(stderr, "pngtoimage: memory out\n"); return image; } } png_read_image(png, rows); /* Create image */ memset(cmptparm, 0, sizeof(cmptparm)); for (i = 0; i < nr_comp; ++i) { /* bits_per_pixel: 8 or 16 */ cmptparm[i].prec = (OPJ_UINT32)bit_depth; cmptparm[i].sgnd = 0; cmptparm[i].dx = (OPJ_UINT32)params->subsampling_dx; cmptparm[i].dy = (OPJ_UINT32)params->subsampling_dy; cmptparm[i].w = (OPJ_UINT32)width; cmptparm[i].h = (OPJ_UINT32)height; } image = opj_image_create(nr_comp, &cmptparm[0], (nr_comp > 2U) ? OPJ_CLRSPC_SRGB : OPJ_CLRSPC_GRAY); if (image == NULL) { return image; } image->x0 = (OPJ_UINT32)params->image_offset_x0; image->y0 = (OPJ_UINT32)params->image_offset_y0; image->x1 = (OPJ_UINT32)(image->x0 + (width - 1) * (OPJ_UINT32) params->subsampling_dx + 1); image->y1 = (OPJ_UINT32)(image->y0 + (height - 1) * (OPJ_UINT32) params->subsampling_dy + 1); row32s = (OPJ_INT32 *)malloc((size_t)width * nr_comp * sizeof(OPJ_INT32)); if (row32s == NULL) { return image; } *prow32s = row32s; /* Set alpha channel */ image->comps[nr_comp - 1U].alpha = 1U - (nr_comp & 1U); for (i = 0; i < nr_comp; i++) { planes[i] = image->comps[i].data; } for (i = 0; i < height; ++i) { cvtXXTo32s(rows[i], row32s, (OPJ_SIZE_T)width * nr_comp); cvtCxToPx(row32s, planes, width); planes[0] += width; planes[1] += width; planes[2] += width; planes[3] += width; } return image; } } opj_image_t *pngtoimage(const char *read_idf, opj_cparameters_t * params) { png_structp png = NULL; png_infop info = NULL; OPJ_UINT32 i; png_uint_32 height = 0U; FILE *reader = NULL; OPJ_BYTE** rows = NULL; OPJ_INT32* row32s = NULL; OPJ_BYTE sigbuf[8]; opj_image_t *image = NULL; if ((reader = fopen(read_idf, "rb")) == NULL) { fprintf(stderr, "pngtoimage: can not open %s\n", read_idf); return NULL; } if (fread(sigbuf, 1, MAGIC_SIZE, reader) != MAGIC_SIZE || memcmp(sigbuf, PNG_MAGIC, MAGIC_SIZE) != 0) { fprintf(stderr, "pngtoimage: %s is no valid PNG file\n", read_idf); goto fin; } if ((png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) == NULL) { goto fin; } if ((info = png_create_info_struct(png)) == NULL) { goto fin; } image = pngtoimage_internal(params, reader, png, info, &height, &rows, &row32s); fin: if (rows) { for (i = 0; i < height; ++i) if (rows[i]) { free(rows[i]); } free(rows); } if (row32s) { free(row32s); } if (png) { png_destroy_read_struct(&png, &info, NULL); } fclose(reader); return image; }/* pngtoimage() */ static void convert_32s16u_C1R(const OPJ_INT32* pSrc, OPJ_BYTE* pDst, OPJ_SIZE_T length) { OPJ_SIZE_T i; for (i = 0; i < length; i++) { OPJ_UINT32 val = (OPJ_UINT32)pSrc[i]; *pDst++ = (OPJ_BYTE)(val >> 8); *pDst++ = (OPJ_BYTE)val; } } int imagetopng(opj_image_t * image, const char *write_idf) { FILE * volatile writer = NULL; png_structp png = NULL; png_infop info = NULL; png_bytep volatile row_buf = NULL; int nr_comp, color_type; volatile int prec; png_color_8 sig_bit; OPJ_INT32 const* planes[4]; int i; OPJ_INT32* volatile buffer32s = NULL; volatile int fails = 1; memset(&sig_bit, 0, sizeof(sig_bit)); prec = (int)image->comps[0].prec; planes[0] = image->comps[0].data; if (planes[0] == NULL) { fprintf(stderr, "imagetopng: planes[%d] == NULL.\n", 0); fprintf(stderr, "\tAborting\n"); return 1; } nr_comp = (int)image->numcomps; if (nr_comp > 4) { nr_comp = 4; } for (i = 1; i < nr_comp; ++i) { if (image->comps[0].dx != image->comps[i].dx) { break; } if (image->comps[0].dy != image->comps[i].dy) { break; } if (image->comps[0].prec != image->comps[i].prec) { break; } if (image->comps[0].sgnd != image->comps[i].sgnd) { break; } planes[i] = image->comps[i].data; if (planes[i] == NULL) { fprintf(stderr, "imagetopng: planes[%d] == NULL.\n", i); fprintf(stderr, "\tAborting\n"); return 1; } } if (i != nr_comp) { fprintf(stderr, "imagetopng: All components shall have the same subsampling, same bit depth, same sign.\n"); fprintf(stderr, "\tAborting\n"); return 1; } for (i = 0; i < nr_comp; ++i) { clip_component(&(image->comps[i]), image->comps[0].prec); } if (prec > 8 && prec < 16) { for (i = 0; i < nr_comp; ++i) { scale_component(&(image->comps[i]), 16); } prec = 16; } else if (prec < 8 && nr_comp > 1) { /* GRAY_ALPHA, RGB, RGB_ALPHA */ for (i = 0; i < nr_comp; ++i) { scale_component(&(image->comps[i]), 8); } prec = 8; } else if ((prec > 1) && (prec < 8) && ((prec == 6) || ((prec & 1) == 1))) { /* GRAY with non native precision */ if ((prec == 5) || (prec == 6)) { prec = 8; } else { prec++; } for (i = 0; i < nr_comp; ++i) { scale_component(&(image->comps[i]), (OPJ_UINT32)prec); } } if (prec != 1 && prec != 2 && prec != 4 && prec != 8 && prec != 16) { fprintf(stderr, "imagetopng: can not create %s\n\twrong bit_depth %d\n", write_idf, prec); return fails; } writer = fopen(write_idf, "wb"); if (writer == NULL) { return fails; } /* Create and initialize the png_struct with the desired error handler * functions. If you want to use the default stderr and longjump method, * you can supply NULL for the last three parameters. We also check that * the library version is compatible with the one used at compile time, * in case we are using dynamically linked libraries. REQUIRED. */ png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); /*png_voidp user_error_ptr, user_error_fn, user_warning_fn); */ if (png == NULL) { goto fin; } /* Allocate/initialize the image information data. REQUIRED */ info = png_create_info_struct(png); if (info == NULL) { goto fin; } /* Set error handling. REQUIRED if you are not supplying your own * error handling functions in the png_create_write_struct() call. */ if (setjmp(png_jmpbuf(png))) { goto fin; } /* I/O initialization functions is REQUIRED */ png_init_io(png, writer); /* Set the image information here. Width and height are up to 2^31, * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. * REQUIRED * * ERRORS: * * color_type == PNG_COLOR_TYPE_PALETTE && bit_depth > 8 * color_type == PNG_COLOR_TYPE_RGB && bit_depth < 8 * color_type == PNG_COLOR_TYPE_GRAY_ALPHA && bit_depth < 8 * color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8 * */ png_set_compression_level(png, Z_BEST_COMPRESSION); if (nr_comp >= 3) { /* RGB(A) */ color_type = PNG_COLOR_TYPE_RGB; sig_bit.red = sig_bit.green = sig_bit.blue = (png_byte)prec; } else { /* GRAY(A) */ color_type = PNG_COLOR_TYPE_GRAY; sig_bit.gray = (png_byte)prec; } if ((nr_comp & 1) == 0) { /* ALPHA */ color_type |= PNG_COLOR_MASK_ALPHA; sig_bit.alpha = (png_byte)prec; } png_set_IHDR(png, info, image->comps[0].w, image->comps[0].h, prec, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_set_sBIT(png, info, &sig_bit); /* png_set_gamma(png, 2.2, 1./2.2); */ /* png_set_sRGB(png, info, PNG_sRGB_INTENT_PERCEPTUAL); */ png_write_info(png, info); /* setup conversion */ { OPJ_SIZE_T rowStride; png_size_t png_row_size; png_row_size = png_get_rowbytes(png, info); rowStride = ((OPJ_SIZE_T)image->comps[0].w * (OPJ_SIZE_T)nr_comp * (OPJ_SIZE_T)prec + 7U) / 8U; if (rowStride != (OPJ_SIZE_T)png_row_size) { fprintf(stderr, "Invalid PNG row size\n"); goto fin; } row_buf = (png_bytep)malloc(png_row_size); if (row_buf == NULL) { fprintf(stderr, "Can't allocate memory for PNG row\n"); goto fin; } buffer32s = (OPJ_INT32*)malloc((OPJ_SIZE_T)image->comps[0].w * (OPJ_SIZE_T)nr_comp * sizeof(OPJ_INT32)); if (buffer32s == NULL) { fprintf(stderr, "Can't allocate memory for interleaved 32s row\n"); goto fin; } } /* convert */ { OPJ_SIZE_T width = image->comps[0].w; OPJ_UINT32 y; convert_32s_PXCX cvtPxToCx = convert_32s_PXCX_LUT[nr_comp]; convert_32sXXx_C1R cvt32sToPack = NULL; OPJ_INT32 adjust = image->comps[0].sgnd ? 1 << (prec - 1) : 0; png_bytep row_buf_cpy = row_buf; OPJ_INT32* buffer32s_cpy = buffer32s; switch (prec) { case 1: case 2: case 4: case 8: cvt32sToPack = convert_32sXXu_C1R_LUT[prec]; break; case 16: cvt32sToPack = convert_32s16u_C1R; break; default: /* never here */ break; } for (y = 0; y < image->comps[0].h; ++y) { cvtPxToCx(planes, buffer32s_cpy, width, adjust); cvt32sToPack(buffer32s_cpy, row_buf_cpy, width * (OPJ_SIZE_T)nr_comp); png_write_row(png, row_buf_cpy); planes[0] += width; planes[1] += width; planes[2] += width; planes[3] += width; } } png_write_end(png, info); fails = 0; fin: if (png) { png_destroy_write_struct(&png, &info); } if (row_buf) { free(row_buf); } if (buffer32s) { free(buffer32s); } fclose(writer); if (fails) { (void)remove(write_idf); /* ignore return value */ } return fails; }/* imagetopng() */