| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| extern "C"{ |
| #include "png.h" |
| } |
|
|
| #include "Image.h" |
| #include "Error.h" |
| #include <vector> |
|
|
| |
| static png_structp png_ptr = NULL; |
| static png_infop info_ptr = NULL; |
|
|
| static void pngfile_error(png_structp , png_const_charp msg) |
| { |
| throw CError(msg); |
| } |
|
|
| #define DEBUG_ImageIOpng 0 |
|
|
| |
| |
| |
| |
| CByteImage removeRedundantBands(CByteImage img) |
| { |
| CShape sh = img.Shape(); |
| int w = sh.width, h = sh.height, nB = sh.nBands; |
| int x, y; |
| if (nB < 3) |
| return img; |
|
|
| |
| bool fullAlpha = true; |
| if (nB == 4) { |
| for (y = 0; y < h && fullAlpha; y++) { |
| uchar *pix = &img.Pixel(0, y, 0); |
| for (x = 0; x < w; x++) { |
| if (pix[3] != 255) { |
| fullAlpha = false; |
| break; |
| } |
| pix += nB; |
| } |
| } |
| } |
| if (!fullAlpha) |
| return img; |
|
|
| |
| bool equalColors = true; |
| for (y = 0; y < h && equalColors; y++) { |
| uchar *pix = &img.Pixel(0, y, 0); |
| for (x = 0; x < w; x++) { |
| if (pix[0] != pix[1] || |
| pix[0] != pix[2] || |
| pix[1] != pix[2]) { |
| equalColors = false; |
| break; |
| } |
| pix += nB; |
| } |
| } |
| |
| |
| if (! equalColors && nB < 4) |
| return img; |
|
|
| int newNB = equalColors ? 1 : 3; |
|
|
| if (DEBUG_ImageIOpng) |
| fprintf(stderr, "reducing from %d to %d bands\n", nB, newNB); |
|
|
| CShape sh2(w, h, newNB); |
| CByteImage img2(sh2); |
| |
| for (y = 0; y < h; y++) { |
| uchar *pix = &img.Pixel(0, y, 0); |
| uchar *pix2 = &img2.Pixel(0, y, 0); |
| for (x = 0; x < w; x++) { |
| for (int b = 0; b < newNB; b++) { |
| pix2[b] = pix[b]; |
| } |
| pix += nB; |
| pix2 += newNB; |
| } |
| } |
|
|
| return img2; |
| } |
|
|
|
|
| void ReadFilePNG(CByteImage& img, const char* filename) |
| { |
| |
| FILE *stream = fopen(filename, "rb"); |
| if (stream == 0) |
| throw CError("ReadFilePNG: could not open %s", filename); |
|
|
| |
| png_byte pbSig[8]; |
| fread(pbSig, 1, 8, stream); |
| if (!png_check_sig(pbSig, 8)) { |
| fclose(stream); |
| throw CError("ReadFilePNG: invalid PNG signature"); |
| } |
|
|
| |
| png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, |
| (png_error_ptr)pngfile_error, (png_error_ptr)NULL); |
|
|
| if (!png_ptr) { |
| fclose(stream); |
| throw CError("ReadFilePNG: error creating png structure"); |
| } |
|
|
| info_ptr = png_create_info_struct(png_ptr); |
| if (!info_ptr) |
| { |
| png_destroy_read_struct(&png_ptr, NULL, NULL); |
| fclose(stream); |
| throw CError("ReadFilePNG: error creating png structure"); |
| } |
|
|
| png_init_io(png_ptr, stream); |
| png_set_sig_bytes(png_ptr, 8); |
|
|
| |
| png_read_info(png_ptr, info_ptr); |
|
|
| |
| int width, height, bits, colorType, nBands; |
|
|
| png_get_IHDR(png_ptr, info_ptr, |
| (png_uint_32 *)&width, (png_uint_32 *)&height, |
| &bits, &colorType, NULL, NULL, NULL); |
| nBands = (int)png_get_channels(png_ptr, info_ptr); |
|
|
| if (DEBUG_ImageIOpng) |
| fprintf(stderr, " w=%d, h=%d, %2d bits, %s, nB=%d", |
| width, height, bits, |
| colorType == PNG_COLOR_TYPE_GRAY ? "gray" : |
| colorType == PNG_COLOR_TYPE_PALETTE ? "plt " : |
| colorType == PNG_COLOR_TYPE_RGB ? "rgb " : |
| colorType == PNG_COLOR_TYPE_RGB_ALPHA ? "rgba" : |
| colorType == PNG_COLOR_TYPE_GRAY_ALPHA ? "gr-a" : "??? ", |
| nBands); |
|
|
|
|
| |
| |
| if (bits == 16) |
| png_set_strip_16(png_ptr); |
|
|
| |
| if (colorType == PNG_COLOR_TYPE_PALETTE) |
| png_set_expand(png_ptr); |
|
|
| |
| if (bits < 8) |
| png_set_expand(png_ptr); |
|
|
| |
| if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) |
| png_set_expand(png_ptr); |
|
|
| |
| if (colorType == PNG_COLOR_TYPE_GRAY_ALPHA) |
| |
| png_set_gray_to_rgb(png_ptr); |
|
|
| |
| |
| if (colorType == PNG_COLOR_TYPE_GRAY_ALPHA || |
| colorType == PNG_COLOR_TYPE_GRAY) { |
| png_color_16 *pBackground; |
| if (png_get_bKGD(png_ptr, info_ptr, &pBackground)) |
| png_set_background(png_ptr, pBackground, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| png_set_bgr(png_ptr); |
|
|
| |
| if (colorType == PNG_COLOR_TYPE_RGB) |
| png_set_add_alpha(png_ptr, 255, PNG_FILLER_AFTER); |
|
|
| |
| png_read_update_info(png_ptr, info_ptr); |
|
|
| |
| png_get_IHDR(png_ptr, info_ptr, |
| (png_uint_32 *)&width, (png_uint_32 *)&height, |
| &bits, &colorType, NULL, NULL, NULL); |
| nBands = (int)png_get_channels(png_ptr, info_ptr); |
|
|
| if (DEBUG_ImageIOpng) |
| fprintf(stderr, " -> w=%d, h=%d, %2d bits, %s, nB=%d\n", |
| width, height, bits, |
| colorType == PNG_COLOR_TYPE_GRAY ? "gray" : |
| colorType == PNG_COLOR_TYPE_PALETTE ? "plt " : |
| colorType == PNG_COLOR_TYPE_RGB ? "rgb " : |
| colorType == PNG_COLOR_TYPE_RGB_ALPHA ? "rgba" : |
| colorType == PNG_COLOR_TYPE_GRAY_ALPHA ? "gr-a" : "??? ", |
| nBands); |
| |
|
|
| if (! (nBands==1 || nBands==3 || nBands==4)) { |
| fclose(stream); |
| throw CError("ReadFilePNG: Can't handle nBands=%d", nBands); |
| } |
|
|
| |
| CShape sh(width, height, nBands); |
|
|
| |
| img.ReAllocate(sh); |
|
|
| |
| std::vector<uchar *> rowPtrs; |
| rowPtrs.resize(height); |
| for (int y = 0; y<height; y++) |
| rowPtrs[y] = &img.Pixel(0, y, 0); |
|
|
| |
| png_read_image(png_ptr, &rowPtrs[0]); |
|
|
| |
| png_read_end(png_ptr, NULL); |
|
|
| png_destroy_read_struct(&png_ptr, &info_ptr, NULL); |
|
|
| fclose(stream); |
| } |
|
|
|
|
| void WriteFilePNG(CByteImage img, const char* filename) |
| { |
| img = removeRedundantBands(img); |
|
|
| CShape sh = img.Shape(); |
| int width = sh.width, height = sh.height, nBands = sh.nBands; |
|
|
| |
| |
| |
|
|
| FILE *stream = fopen(filename, "wb"); |
| if (stream == 0) |
| throw CError("WriteFilePNG: could not open %s", filename); |
|
|
| png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, |
| (png_error_ptr)pngfile_error, (png_error_ptr)NULL); |
|
|
| if (!png_ptr) { |
| fclose(stream); |
| throw CError("WriteFilePNG: error creating png structure"); |
| } |
|
|
| info_ptr = png_create_info_struct(png_ptr); |
| if (!info_ptr) { |
| fclose(stream); |
| throw CError("WriteFilePNG: error creating png structure"); |
| } |
|
|
| png_init_io(png_ptr, stream); |
|
|
| int bits = 8; |
| int colortype = |
| nBands == 1 ? PNG_COLOR_TYPE_GRAY : |
| nBands == 3 ? PNG_COLOR_TYPE_RGB : |
| PNG_COLOR_TYPE_RGB_ALPHA; |
| png_set_IHDR(png_ptr, info_ptr, width, height, |
| bits, colortype, |
| PNG_INTERLACE_NONE, |
| PNG_COMPRESSION_TYPE_DEFAULT, |
| PNG_FILTER_TYPE_DEFAULT); |
|
|
| |
| png_write_info(png_ptr, info_ptr); |
|
|
| |
| png_set_bgr(png_ptr); |
|
|
| |
| std::vector<uchar *> rowPtrs; |
| rowPtrs.resize(height); |
| for (int y = 0; y<height; y++) |
| rowPtrs[y] = &img.Pixel(0, y, 0); |
|
|
| |
| png_write_image(png_ptr, &rowPtrs[0]); |
|
|
| |
| png_write_end(png_ptr, info_ptr); |
|
|
| png_destroy_write_struct(&png_ptr, (png_infopp) NULL); |
|
|
| fclose (stream); |
| } |
|
|