| /* |
| * 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 <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <ctype.h> |
| |
| #include <zlib.h> |
| #include <png.h> |
| |
| #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; |
| } |
| } |
| |
| opj_image_t *pngtoimage(const char *read_idf, opj_cparameters_t * params) |
| { |
| png_structp png = NULL; |
| png_infop info = NULL; |
| double gamma; |
| int bit_depth, interlace_type,compression_type, filter_type; |
| OPJ_UINT32 i; |
| png_uint_32 width, height = 0U; |
| int color_type; |
| FILE *reader = NULL; |
| OPJ_BYTE** rows = NULL; |
| OPJ_INT32* row32s = NULL; |
| /* j2k: */ |
| opj_image_t *image = NULL; |
| opj_image_cmptparm_t cmptparm[4]; |
| OPJ_UINT32 nr_comp; |
| OPJ_BYTE sigbuf[8]; |
| convert_XXx32s_C1R cvtXXTo32s = NULL; |
| convert_32s_CXPX cvtCxToPx = NULL; |
| OPJ_INT32* planes[4]; |
| |
| 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; |
| |
| if(setjmp(png_jmpbuf(png))) |
| goto fin; |
| |
| 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) |
| goto fin; |
| |
| /* 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); |
| goto fin; |
| } |
| 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); |
| goto fin; |
| } |
| |
| |
| rows = (OPJ_BYTE**)calloc(height+1, sizeof(OPJ_BYTE*)); |
| for(i = 0; i < height; ++i) |
| rows[i] = (OPJ_BYTE*)malloc(png_get_rowbytes(png,info)); |
| |
| png_read_image(png, rows); |
| |
| /* Create image */ |
| memset(cmptparm, 0, sizeof(cmptparm)); |
| for(i = 0; i < nr_comp; ++i) { |
| cmptparm[i].prec = (OPJ_UINT32)bit_depth; |
| /* bits_per_pixel: 8 or 16 */ |
| cmptparm[i].bpp = (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) goto fin; |
| 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->x0); |
| image->y1 = (OPJ_UINT32)(image->y0 + (height - 1) * (OPJ_UINT32)params->subsampling_dy + 1 + image->y0); |
| |
| row32s = (OPJ_INT32 *)malloc((size_t)width * nr_comp * sizeof(OPJ_INT32)); |
| if(row32s == NULL) goto fin; |
| |
| /* 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; |
| } |
| fin: |
| if(rows) { |
| for(i = 0; i < height; ++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; |
| 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 (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() */ |