void PNGFile::save(std::ostream &stream) { if (pixels.empty()) { throw std::runtime_error("Trying to save an empty PNG"); } // Initializations needed by libpng png_structp PngPointer = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (!PngPointer) { throw std::runtime_error("Cannot allocate memory"); } png_infop InfoPointer = png_create_info_struct(PngPointer); if (!InfoPointer) { png_destroy_write_struct(&PngPointer, nullptr); throw std::runtime_error("Cannot allocate memory"); } if (setjmp(png_jmpbuf(PngPointer))) { png_destroy_write_struct(&PngPointer, &InfoPointer); throw std::runtime_error("Cannot set jump pointer"); } // Set PNG parameters png_set_IHDR(PngPointer, InfoPointer, params.width, params.height, params.BitDepth, params.BitsPerPixel == 24 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGBA, params.InterlaceType, params.CompressionType, params.FilterType); /* Instead of storing the image in a 2D-array, I store it in a 1D-array. Since png_set_rows() accepts a pointer to a pointer as an argument, I need to create a temporary std::vector storing pointers to addresses of 1st pixels for each row. */ std::vector<unsigned char*> RowPointers(params.height); size_t BytesPerLine = params.width << 2; // (x << 2) == (x * 4). 4 channels: RGB and Alpha. unsigned char *ptr = reinterpret_cast<unsigned char*>(pixels.data()); for (size_t i = 0; i < params.height; ++i, ptr += BytesPerLine) RowPointers[i] = ptr; // Write data to file png_set_bgr(PngPointer); png_set_write_fn(PngPointer, reinterpret_cast<void*>(&stream), WriteToStream, nullptr); // png_set_rows() takes a pointer to a non-const data as its // 3rd argument, making it not possible to declare save() as const // without using const_cast on pixels.data(), I'd rather not do that. png_set_rows(PngPointer, InfoPointer, RowPointers.data()); png_write_png(PngPointer, InfoPointer, params.BitsPerPixel == 24 ? PNG_TRANSFORM_STRIP_FILLER_AFTER : PNG_TRANSFORM_IDENTITY, NULL); png_destroy_write_struct(&PngPointer, &InfoPointer); }
void PngFile::SetFromImage(const class Image2D &image, const class ColorMap &colorMap, long double normalizeFactor, long double zeroLevel) throw(IOException) { png_bytep *row_pointers = RowPointers(); for(unsigned long y=0;y<image.Height();y++) { for(unsigned long x=0;x<image.Width();x++) { int xa = x * PixelSize(); row_pointers[y][xa]=colorMap.ValueToColorR((image.Value(x, y) - zeroLevel) * normalizeFactor); row_pointers[y][xa+1]=colorMap.ValueToColorG((image.Value(x, y) - zeroLevel) * normalizeFactor); row_pointers[y][xa+2]=colorMap.ValueToColorB((image.Value(x, y) - zeroLevel) * normalizeFactor); row_pointers[y][xa+3]=colorMap.ValueToColorA((image.Value(x, y) - zeroLevel) * normalizeFactor); } } }
void PngFile::Save(const Image2D &image, const ColorMap &colorMap) throw(IOException) { long double normalizeFactor = image.GetMaxMinNormalizationFactor(); png_bytep *row_pointers = RowPointers(); for(unsigned long y=0;y<image.Height();++y) { for(unsigned long x=0;x<image.Width();++x) { int xa = x * PixelSize(); row_pointers[y][xa]=colorMap.ValueToColorR(image.Value(x, y) * normalizeFactor); row_pointers[y][xa+1]=colorMap.ValueToColorG(image.Value(x, y) * normalizeFactor); row_pointers[y][xa+2]=colorMap.ValueToColorB(image.Value(x, y) * normalizeFactor); row_pointers[y][xa+3]=colorMap.ValueToColorA(image.Value(x, y) * normalizeFactor); } } }
void PNGFile::load(std::istream &stream) { const int signatureLength = 8; uint8_t header[signatureLength]; // Check the file's signature stream.read(reinterpret_cast<char*>(&header), signatureLength); if (png_sig_cmp(header, 0, signatureLength)) { throw std::invalid_argument("Invalid file format"); } // Initializations needed by libpng png_structp PngPointer = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (!PngPointer) { throw std::runtime_error("Cannot allocate memory"); } png_infop InfoPointer = png_create_info_struct(PngPointer); if (!InfoPointer) { png_destroy_read_struct(&PngPointer, nullptr, nullptr); throw std::runtime_error("Cannot allocate memory"); } if (setjmp(png_jmpbuf(PngPointer))) { png_destroy_read_struct(&PngPointer, &InfoPointer, nullptr); throw std::runtime_error("Cannot set jump pointer"); } png_set_sig_bytes(PngPointer, sizeof(header)); png_set_read_fn(PngPointer, reinterpret_cast<void*>(&stream), ReadFromStream); // Get the image's parameters png_read_info(PngPointer, InfoPointer); params.Channels = png_get_channels(PngPointer, InfoPointer); png_get_IHDR(PngPointer, InfoPointer, ¶ms.width, ¶ms.height, ¶ms.BitDepth, ¶ms.ColorType, ¶ms.InterlaceType, ¶ms.CompressionType, ¶ms.FilterType); // Convert to 32-bits if needed png_set_strip_16(PngPointer); png_set_packing(PngPointer); switch (params.ColorType) { case PNG_COLOR_TYPE_GRAY: { png_set_gray_to_rgb(PngPointer); png_set_filler(PngPointer, 0xFF, PNG_FILLER_AFTER); png_set_bgr(PngPointer); params.BitsPerPixel = 24; break; } case PNG_COLOR_TYPE_PALETTE: { // Check whether there's a tRNS chunk png_bytep transparency_vals = nullptr; png_get_tRNS(PngPointer, InfoPointer, &transparency_vals, nullptr, nullptr); // Convert to RGB png_set_palette_to_rgb(PngPointer); png_set_bgr(PngPointer); // If there was no tRNS chunk, // add an alpha channel if (transparency_vals != nullptr) { params.BitsPerPixel = 32; } else { png_set_filler(PngPointer, 0xFF, PNG_FILLER_AFTER); params.BitsPerPixel = 24; } break; } case PNG_COLOR_TYPE_RGB: { png_set_filler(PngPointer, 0xFF, PNG_FILLER_AFTER); png_set_bgr(PngPointer); params.BitsPerPixel = 24; break; } case PNG_COLOR_TYPE_GRAY_ALPHA: { png_set_gray_to_rgb(PngPointer); png_set_bgr(PngPointer); params.BitsPerPixel = 32; break; } case PNG_COLOR_TYPE_RGBA: { png_set_bgr(PngPointer); params.BitsPerPixel = 32; break; } default: png_destroy_read_struct(&PngPointer, &InfoPointer, nullptr); throw std::runtime_error("Not supported PNG Type"); break; } // Update the image's parameters png_read_update_info(PngPointer, InfoPointer); params.Channels = png_get_channels(PngPointer, InfoPointer); png_get_IHDR(PngPointer, InfoPointer, ¶ms.width, ¶ms.height, ¶ms.BitDepth, ¶ms.ColorType, ¶ms.InterlaceType, ¶ms.CompressionType, ¶ms.FilterType); /* Instead of storing the image in a 2D-array, I store it in a 1D-array. Since png_read_image() accepts a pointer to a pointer as an argument, I need to create a temporary std::vector storing pointers to addresses of 1st pixels for each row. */ pixels.resize(params.width * params.height); std::vector<unsigned char*> RowPointers(params.height); size_t BytesPerLine = params.width << 2; // (x << 2) == (x * 4). 4 channels: RGB and Alpha. unsigned char *ptr = reinterpret_cast<unsigned char*>(pixels.data()); for (size_t i = 0; i < params.height; ++i, ptr += BytesPerLine) RowPointers[i] = ptr; // Read pixels png_read_image(PngPointer, RowPointers.data()); png_destroy_read_struct(&PngPointer, &InfoPointer, nullptr); // Read cryptographic stuff this->ReadIV(); this->ReadSalt(); }
// load in the image data Picture PictureLoaderPng::load( io::NFile file ) const { if(!file.isOpen()) { Logger::warning( "LOAD PNG: can't open file %s", file.getFileName().toString().c_str() ); return Picture::getInvalid(); } png_byte buffer[8]; // Read the first few bytes of the PNG file if( file.read(buffer, 8) != 8 ) { Logger::warning( "LOAD PNG: can't read file %s", file.getFileName().toString().c_str() ); return Picture::getInvalid(); } // Check if it really is a PNG file if( png_sig_cmp(buffer, 0, 8) ) { Logger::warning( "LOAD PNG: not really a png %s", file.getFileName().toString().c_str() ); return Picture::getInvalid(); } // Allocate the png read struct png_structp png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, (png_error_ptr)png_cpexcept_error, (png_error_ptr)png_cpexcept_warn); if( !png_ptr ) { Logger::warning( "LOAD PNG: Internal PNG create read struct failure %s", file.getFileName().toString().c_str() ); return Picture::getInvalid(); } // Allocate the png info struct png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { Logger::warning( "LOAD PNG: Internal PNG create info struct failure 5s", file.getFileName().toString().c_str() ); png_destroy_read_struct(&png_ptr, NULL, NULL); return Picture::getInvalid(); } // for proper error handling if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); /* if( RowPointers ) delete [] RowPointers; */ return Picture::getInvalid(); } // changed by zola so we don't need to have public FILE pointers png_set_read_fn(png_ptr, &file, user_read_data_fcn); png_set_sig_bytes(png_ptr, 8); // Tell png that we read the signature png_read_info(png_ptr, info_ptr); // Read the info section of the png file unsigned int Width; unsigned int Height; int BitDepth; int ColorType; { // Use temporary variables to avoid passing casted pointers png_uint_32 w,h; // Extract info png_get_IHDR(png_ptr, info_ptr, &w, &h, &BitDepth, &ColorType, NULL, NULL, NULL); Width=w; Height=h; } // Convert palette color to true color if (ColorType==PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr); // Convert low bit colors to 8 bit colors if (BitDepth < 8) { if (ColorType==PNG_COLOR_TYPE_GRAY || ColorType==PNG_COLOR_TYPE_GRAY_ALPHA) png_set_expand_gray_1_2_4_to_8(png_ptr); else png_set_packing(png_ptr); } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); // Convert high bit colors to 8 bit colors if (BitDepth == 16) png_set_strip_16(png_ptr); // Convert gray color to true color if (ColorType==PNG_COLOR_TYPE_GRAY || ColorType==PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); int intent; const double screen_gamma = 2.2; if (png_get_sRGB(png_ptr, info_ptr, &intent)) png_set_gamma(png_ptr, screen_gamma, 0.45455); else { double image_gamma; if (png_get_gAMA(png_ptr, info_ptr, &image_gamma)) png_set_gamma(png_ptr, screen_gamma, image_gamma); else png_set_gamma(png_ptr, screen_gamma, 0.45455); } // Update the changes in between, as we need to get the new color type // for proper processing of the RGBA type png_read_update_info(png_ptr, info_ptr); { // Use temporary variables to avoid passing casted pointers png_uint_32 w,h; // Extract info png_get_IHDR(png_ptr, info_ptr, &w, &h, &BitDepth, &ColorType, NULL, NULL, NULL); Width=w; Height=h; } // Convert RGBA to BGRA if (ColorType==PNG_COLOR_TYPE_RGB_ALPHA) { png_set_bgr(png_ptr); } // Create the image structure to be filled by png data Picture* pic = GfxEngine::instance().createPicture( Size( Width, Height ) ); GfxEngine::instance().loadPicture( *pic ); if( pic->getSize().getArea() == 0 ) { Logger::warning( "LOAD PNG: Internal PNG create image struct failure %s", file.getFileName().toString().c_str() ); png_destroy_read_struct(&png_ptr, NULL, NULL); return Picture::getInvalid(); } if( !Height ) { Logger::warning( "LOAD PNG: Internal PNG create row pointers failure %s", file.getFileName().toString().c_str() ); png_destroy_read_struct(&png_ptr, NULL, NULL); return Picture::getInvalid(); } // Create array of pointers to rows in image data ScopedPtr<unsigned char*> RowPointers( (unsigned char**)new png_bytep[ Height ] ); // Fill array of pointers to rows in image data SDL_LockSurface( pic->getSurface() ); unsigned char* data = (unsigned char*)pic->getSurface()->pixels; for(unsigned int i=0; i<Height; ++i) { RowPointers.data()[i] = data; data += pic->getSurface()->pitch; } // for proper error handling if( setjmp( png_jmpbuf( png_ptr ) ) ) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); GfxEngine::instance().deletePicture( pic ); return Picture::getInvalid(); } // Read data using the library function that handles all transformations including interlacing png_read_image( png_ptr, RowPointers.data() ); png_read_end( png_ptr, NULL ); png_destroy_read_struct( &png_ptr, &info_ptr, 0 ); // Clean up memory SDL_UnlockSurface(pic->getSurface()); return *pic; }