void image_jpeg_save_extended(const std::string &filename, unsigned pw, unsigned ph, unsigned pd, unsigned pb, uint8_t *pdata, int pquality) { if((0 >= pw) || (0 >= ph)) { std::stringstream sstr; sstr << "invalid image dimensions: " << pw << "x" << ph; BOOST_THROW_EXCEPTION(std::runtime_error(sstr.str())); } if(pd > 0) { ph *= pd; } JpegWriter writer(filename); boost::scoped_array<uint8_t*> row_pointers(new uint8_t*[ph]); { uint8_t *iter = pdata + pw * ph * pb / 8; unsigned iter_sub = (pw * pb / 8); // PNG and OpenGL scanlines are in different order for(unsigned ii = 0; (ii < ph); ++ii) { iter -= iter_sub; row_pointers[ii] = iter; } } writer.write(pw, ph, pd, pb, pquality, row_pointers.get()); }
void image_png_load_extended(unsigned &pw, unsigned &ph, unsigned &pd, unsigned &pb, uint8_t *&pdata, const std::string &filename, unsigned required_bpp) { FILE *fd = png_read_header(filename); PngReader reader(fd, 8); // error handling in libpng is still retarded if(setjmp(png_jmpbuf(reader.getPng()))) { std::stringstream sstr; sstr << "could not set longjmp"; BOOST_THROW_EXCEPTION(std::runtime_error(sstr.str())); } reader.readHeader(); unsigned width = reader.getWidth(), height = reader.getHeight(), depth = reader.getDepth(), bpp = reader.getBpp(); if(bpp != required_bpp) { std::stringstream sstr; sstr << '\'' << filename << "' has bit depth " << bpp << ", excepted " << required_bpp; BOOST_THROW_EXCEPTION(std::runtime_error(sstr.str())); } unsigned data_size = width * height * bpp / 8; reader.setBlock(new uint8_t[data_size]); boost::scoped_array<uint8_t*> row_pointers(new uint8_t*[height]); { uint8_t *iter = reader.getBlock() + data_size; unsigned iter_sub = (width * bpp / 8); // PNG and OpenGL scanlines are in different order for(unsigned ii = 0; (ii < height); ++ii) { iter -= iter_sub; row_pointers[ii] = iter; } } reader.read(row_pointers.get()); if(depth > 0) { height /= depth; } pw = width; ph = height; pd = depth; pb = bpp; // yield ownership of block pdata = reader.getBlock(); reader.setBlock(NULL); }
void image_png_save_extended(const std::string &filename, unsigned pw, unsigned ph, unsigned pd, unsigned pb, uint8_t *pdata) { if((0 >= pw) || (0 >= ph)) { std::stringstream sstr; sstr << "invalid image dimensions: " << pw << "x" << ph; BOOST_THROW_EXCEPTION(std::runtime_error(sstr.str())); } uint8_t color_type = bpp_to_png_color_type(pb); unsigned logical_height = ph; if(pd > 0) { logical_height *= ph; } FILE *fd = fopen(filename.c_str(), "wb"); if(!fd) { std::stringstream sstr; sstr << "could not open '" << filename << '\''; BOOST_THROW_EXCEPTION(std::runtime_error(sstr.str())); } PngWriter writer(fd); boost::scoped_array<uint8_t*> row_pointers(new uint8_t*[logical_height]); { uint8_t *iter = pdata + pw * logical_height * pb / 8; unsigned iter_sub = (pw * pb / 8); // PNG and OpenGL scanlines are in different order for(ptrdiff_t ii = 0; (static_cast<ptrdiff_t>(logical_height) > ii); ++ii) { iter -= iter_sub; row_pointers[ii] = iter; } } // error handling in libpng is still retarded if(setjmp(png_jmpbuf(writer.getPng()))) { std::stringstream sstr; sstr << "could not set longjmp"; BOOST_THROW_EXCEPTION(std::runtime_error(sstr.str())); } // Multiply ph again to prevent it being clobbered by longjmp. writer.write(pw, ph * ((pd <= 0) ? 1 : pd), pd, color_type, row_pointers.get()); }
void image_png_save_extended(const std::string &filename, unsigned pw, unsigned ph, unsigned pd, unsigned pb, uint8_t *pdata) { uint8_t color_type = bpp_to_png_color_type(pb); if((0 >= pw) || (0 >= ph)) { std::stringstream sstr; sstr << "invalid image dimensions: " << pw << "x" << ph; BOOST_THROW_EXCEPTION(std::runtime_error(sstr.str())); } if(pd > 0) { ph *= pd; } FILE *fd = fopen(filename.c_str(), "wb"); if(!fd) { std::stringstream sstr; sstr << "could not open '" << filename << '\''; BOOST_THROW_EXCEPTION(std::runtime_error(sstr.str())); } PngWriter writer(fd); boost::scoped_array<uint8_t*> row_pointers(new uint8_t*[ph]); { uint8_t *iter = pdata + pw * ph * pb / 8; unsigned iter_sub = (pw * pb / 8); // PNG and OpenGL scanlines are in different order for(unsigned ii = 0; (ii < ph); ++ii) { iter -= iter_sub; row_pointers[ii] = iter; } } // error handling in libpng is still retarded if(setjmp(png_jmpbuf(writer.getPng()))) { std::stringstream sstr; sstr << "could not set longjmp"; BOOST_THROW_EXCEPTION(std::runtime_error(sstr.str())); } writer.write(pw, ph, pd, color_type, row_pointers.get()); }
s32 pngDecodeData(PPUThread& ppu, PHandle handle, PStream stream, vm::ptr<u8> data, PDataControlParam data_control_param, PDataOutInfo data_out_info, PCbControlDisp cb_control_disp = vm::null, PDispParam disp_param = vm::null) { if (cb_control_disp || disp_param) { throw EXCEPTION("Partial image decoding is not supported"); } // Indicate, that the PNG decoding is stopped/failed. This is incase, we return an error code in the middle of decoding data_out_info->status = CELL_PNGDEC_DEC_STATUS_STOP; // Possibilities for decoding in different sizes aren't tested, so if anyone finds any of these cases, we'll know about it. if (stream->info.imageWidth != stream->out_param.outputWidth) { throw EXCEPTION("Image width doesn't match output width! (%d/%d)", stream->out_param.outputWidth, stream->info.imageWidth); } if (stream->info.imageHeight != stream->out_param.outputHeight) { throw EXCEPTION("Image width doesn't match output height! (%d/%d)", stream->out_param.outputHeight, stream->info.imageHeight); } // Get the amount of output bytes per line const u32 bytes_per_line = data_control_param->outputBytesPerLine; // Whether to recaculate bytes per row bool recalculate_bytes_per_row = false; // Check if the game is expecting the number of bytes per line to be lower, than the actual bytes per line on the image. (Arkedo Pixel for example) // In such case we strip the bit depth to be lower. if ((bytes_per_line < stream->out_param.outputWidthByte) && stream->out_param.outputBitDepth != 8) { // Check if the packing is really 1 byte per 1 pixel if (stream->packing != CELL_PNGDEC_1BYTE_PER_1PIXEL) { throw EXCEPTION("Unexpected packing value! (%d)", stream->packing); } // Scale 16 bit depth down to 8 bit depth. PS3 uses png_set_strip_16, since png_set_scale_16 wasn't available back then. png_set_strip_16(stream->png_ptr); recalculate_bytes_per_row = true; } // Check if the outputWidthByte is smaller than the intended output length of a line. For example an image might be in RGB, but we need to output 4 components, so we need to perform alpha padding. else if (stream->out_param.outputWidthByte < (stream->out_param.outputWidth * stream->out_param.outputComponents)) { // If fixed alpha is not specified in such a case, the default value for the alpha is 0xFF (255) if (!stream->fixed_alpha) { stream->fixed_alpha_colour = 0xFF; } // We need to fill alpha (before or after, depending on the output colour format) using the fixed alpha value passed by the game. png_set_add_alpha(stream->png_ptr, stream->fixed_alpha_colour, stream->out_param.outputColorSpace == CELL_PNGDEC_RGBA ? PNG_FILLER_AFTER : PNG_FILLER_BEFORE); recalculate_bytes_per_row = true; } // We decode as RGBA, so we need to swap the alpha else if (stream->out_param.outputColorSpace == CELL_PNGDEC_ARGB) { // Swap the alpha channel for the ARGB output format, if the padding isn't needed png_set_swap_alpha(stream->png_ptr); } // Sometimes games pass in a RBG/RGBA image and want it as grayscale else if ((stream->out_param.outputColorSpace == CELL_PNGDEC_GRAYSCALE_ALPHA || stream->out_param.outputColorSpace == CELL_PNGDEC_GRAYSCALE) && (stream->info.colorSpace == CELL_PNGDEC_RGB || stream->info.colorSpace == CELL_PNGDEC_RGBA)) { // Tell libpng to convert it to grayscale png_set_rgb_to_gray(stream->png_ptr, PNG_ERROR_ACTION_NONE, PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT); recalculate_bytes_per_row = true; } if (recalculate_bytes_per_row) { // Update the info structure png_read_update_info(stream->png_ptr, stream->info_ptr); // Recalculate the bytes per row stream->out_param.outputWidthByte = png_get_rowbytes(stream->png_ptr, stream->info_ptr); } // Calculate the image size u32 image_size = stream->out_param.outputWidthByte * stream->out_param.outputHeight; // Buffer for storing the image std::vector<u8> png(image_size); // Make an unique pointer for the row pointers std::vector<u8*> row_pointers(stream->out_param.outputHeight); // Allocate memory for rows for (u32 y = 0; y < stream->out_param.outputHeight; y++) { row_pointers[y] = &png[y * stream->out_param.outputWidthByte]; } // Decode the image png_read_image(stream->png_ptr, row_pointers.data()); // Check if the image needs to be flipped const bool flip = stream->out_param.outputMode == CELL_PNGDEC_BOTTOM_TO_TOP; // Copy the result to the output buffer switch (stream->out_param.outputColorSpace) { case CELL_PNGDEC_RGB: case CELL_PNGDEC_RGBA: case CELL_PNGDEC_ARGB: case CELL_PNGDEC_GRAYSCALE_ALPHA: { // Check if we need to flip the image or need to leave empty bytes at the end of a line if ((bytes_per_line > stream->out_param.outputWidthByte) || flip) { // Get how many bytes per line we need to output - bytesPerLine is total amount of bytes per line, rest is unused and the game can do as it pleases. const u32 line_size = std::min(bytes_per_line, stream->out_param.outputWidth * 4); // If the game wants more bytes per line to be output, than the image has, then we simply copy what we have for each line, // and continue on the next line, thus leaving empty bytes at the end of the line. for (u32 i = 0; i < stream->out_param.outputHeight; i++) { const u32 dst = i * bytes_per_line; const u32 src = stream->out_param.outputWidth * 4 * (flip ? stream->out_param.outputHeight - i - 1 : i); memcpy(&data[dst], &png[src], line_size); } } else { // We can simply copy the output to the data pointer specified by the game, since we already do alpha channel transformations in libpng, if needed memcpy(data.get_ptr(), png.data(), image_size); } break; } default: throw EXCEPTION("Unsupported color space (%d)", stream->out_param.outputColorSpace); } // Get the number of iTXt, tEXt and zTXt chunks s32 text_chunks = 0; png_get_text(stream->png_ptr, stream->info_ptr, nullptr, &text_chunks); // Set the chunk information and the previously obtained number of text chunks data_out_info->numText = (u32)text_chunks; data_out_info->chunkInformation = pngDecGetChunkInformation(stream, true); data_out_info->numUnknownChunk = 0; // TODO: Get this somehow. Does anything even use or need this? // Indicate that the decoding succeeded data_out_info->status = CELL_PNGDEC_DEC_STATUS_FINISH; return CELL_OK; }
bool DecodePNG ( Image& aImage, size_t aBufferSize, const void* aBuffer ) { if ( png_sig_cmp ( static_cast<uint8_t*> ( const_cast<void*> ( aBuffer ) ), 0, 8 ) != 0 ) { return false; } try { png_structp png_ptr = png_create_read_struct ( PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr ); if ( png_ptr == nullptr ) { throw std::runtime_error ( "png_create_read_struct failed." ); } png_infop info_ptr = png_create_info_struct ( png_ptr ); if ( info_ptr == nullptr ) { throw std::runtime_error ( "png_create_info_struct failed." ); } if ( setjmp ( png_jmpbuf ( png_ptr ) ) ) { throw std::runtime_error ( "Error during init_io." ); } png_read_memory_struct read_memory_struct = {static_cast<const uint8_t*> ( aBuffer ), static_cast<const uint8_t*> ( aBuffer ) + 8, static_cast<png_size_t> ( aBufferSize * sizeof ( uint8_t ) ) }; png_set_read_fn ( png_ptr, &read_memory_struct, png_read_memory_data ); png_set_sig_bytes ( png_ptr, 8 ); png_read_info ( png_ptr, info_ptr ); uint32_t width = png_get_image_width ( png_ptr, info_ptr ); uint32_t height = png_get_image_height ( png_ptr, info_ptr ); png_byte color_type = png_get_color_type ( png_ptr, info_ptr ); png_byte bit_depth = png_get_bit_depth ( png_ptr, info_ptr ); Image::ImageFormat format; Image::ImageType type; if ( ( color_type == PNG_COLOR_TYPE_RGB ) || ( color_type == PNG_COLOR_TYPE_RGBA ) ) { format = ( color_type == PNG_COLOR_TYPE_RGB ) ? Image::ImageFormat::RGB : Image::ImageFormat::RGBA; type = ( bit_depth == 8 ) ? Image::ImageType::UNSIGNED_BYTE : Image::ImageType::UNSIGNED_SHORT; } else { throw std::runtime_error ( "PNG image color type not supported...yet" ); } /*int number_of_passes =*/ png_set_interlace_handling ( png_ptr ); png_read_update_info ( png_ptr, info_ptr ); /* read file */ if ( setjmp ( png_jmpbuf ( png_ptr ) ) ) { throw std::runtime_error ( "Error during read_image." ); } // -------------------------------------- png_size_t rowbytes = png_get_rowbytes ( png_ptr, info_ptr ); std::vector<uint8_t*> row_pointers ( sizeof ( png_bytep ) * height ); std::vector<uint8_t> pixels ( width * height * GetPixelSize ( format, type ) ); for ( png_uint_32 y = 0; y < height; ++y ) { row_pointers[y] = pixels.data() + ( rowbytes * y ); } // -------------------------------------- png_read_image ( png_ptr, row_pointers.data() ); png_destroy_read_struct ( &png_ptr, &info_ptr, ( png_infopp ) nullptr ); aImage.Initialize ( width, height, format, type, pixels.data() ); } catch ( std::runtime_error& e ) { std::cout << e.what() << std::endl; return false; } return true; }
void save_as_png(T1 & file, T2 const& image, int compression = Z_DEFAULT_COMPRESSION, int strategy = Z_DEFAULT_STRATEGY, int trans_mode = -1, bool use_miniz = false) { if (use_miniz) { MiniZ::PNGWriter writer(compression,strategy); if (trans_mode == 0) { writer.writeIHDR(image.width(), image.height(), 24); writer.writeIDATStripAlpha(image); } else { writer.writeIHDR(image.width(), image.height(), 32); writer.writeIDAT(image); } writer.writeIEND(); writer.toStream(file); return; } png_voidp error_ptr=0; png_structp png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING, error_ptr,0, 0); if (!png_ptr) return; // switch on optimization only if supported #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) && defined(PNG_MMX_CODE_SUPPORTED) png_uint_32 mask, flags; flags = png_get_asm_flags(png_ptr); mask = png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE); png_set_asm_flags(png_ptr, flags | mask); #endif png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_NONE); png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr,(png_infopp)0); return; } jmp_buf* jmp_context = (jmp_buf*) png_get_error_ptr(png_ptr); if (jmp_context) { png_destroy_write_struct(&png_ptr, &info_ptr); return; } png_set_write_fn (png_ptr, &file, &write_data<T1>, &flush_data<T1>); png_set_compression_level(png_ptr, compression); png_set_compression_strategy(png_ptr, strategy); png_set_compression_buffer_size(png_ptr, 32768); png_set_IHDR(png_ptr, info_ptr,image.width(),image.height(),8, (trans_mode == 0) ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA,PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT); boost::scoped_array<png_byte*> row_pointers(new png_bytep[image.height()]); for (unsigned int i = 0; i < image.height(); i++) { row_pointers[i] = (png_bytep)image.getRow(i); } png_set_rows(png_ptr, info_ptr, row_pointers.get()); png_write_png(png_ptr, info_ptr, (trans_mode == 0) ? PNG_TRANSFORM_STRIP_FILLER_AFTER : PNG_TRANSFORM_IDENTITY, NULL); png_destroy_write_struct(&png_ptr, &info_ptr); }
bool ImageLoader::ReadPNG(ImageLoaderParams &ImgInfo) { // Set up libpng reading. png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, PNGErrorFn, PNGWarnFn); if (!png_ptr) return false; png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return false; } png_infop end_info = png_create_info_struct(png_ptr); if (!end_info) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return false; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return false; } // We read using a FILE*; this could be changed. File::IOFile file; if (!file.Open(ImgInfo.Path, "rb")) { return false; } png_init_io(png_ptr, file.GetHandle()); png_set_sig_bytes(png_ptr, 0); // Process PNG header, etc. png_read_info(png_ptr, info_ptr); png_uint_32 width, height; int bit_depth, color_type, interlace_type, compression_type, filter_method; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method); // Force RGB (8 or 16-bit). if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); // Force 8-bit RGB. if (bit_depth == 16) png_set_strip_16(png_ptr); // Force alpha channel (combined with the above, 8-bit RGBA). if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); else if ((color_type & PNG_COLOR_MASK_ALPHA) == 0) png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); png_read_update_info(png_ptr, info_ptr); ImgInfo.Width = width; ImgInfo.Height = height; ImgInfo.data_size = width * height * 4; ImgInfo.dst = ImgInfo.request_buffer_delegate(ImgInfo.data_size, false); std::vector<u8*> row_pointers(height); u8* row_pointer = ImgInfo.dst; for (unsigned i = 0; i < height; ++i) { row_pointers[i] = row_pointer; row_pointer += width * 4; } png_read_image(png_ptr, row_pointers.data()); png_read_end(png_ptr, end_info); png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return true; }
void write( Image& img, const char* filename ) { if ( !( img.get_width() > 0 && img.get_height() > 0 && img.get_num_channels() > 0 ) ) { throw std::exception( "This is not a valid image to write." ); } FILE* fp = fopen( filename, "wb" ); if ( !fp ) { throw std::exception( "Failed to open file." ); } boost::shared_ptr<void> fp_cleanup( static_cast<void*>(0), boost::bind( fclose, fp ) ); png_structp png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL ); if (!png_ptr) { throw std::exception( "png_create_write_struct failed." ); } png_infop info_ptr = 0; boost::shared_ptr<void> png_cleanup( static_cast<void*>(0), boost::bind( png_destroy_write_struct, &png_ptr, &info_ptr ) ); info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { throw std::exception( "png_create_info_struct failed." ); } if ( setjmp(png_jmpbuf(png_ptr)) ) { throw std::exception( "libpng returned an error." ); } png_init_io(png_ptr, fp); // optional: call png_set_filter to control compression // fill png_info structure typedef boost::scoped_array< Image::channel* > Channel_array; Channel_array row_pointers( new Image::channel* [ img.get_height() ] ); for ( int row = 0; row < img.get_height(); ++row ) { row_pointers[ row ] = img.get_row_ptr( row ); } png_set_rows( png_ptr, info_ptr, row_pointers.get() ); png_set_IHDR( png_ptr, info_ptr, img.get_width(), img.get_height(), 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); png_write_png( png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL ); }
static PyObject *Py_write_png(PyObject *self, PyObject *args, PyObject *kwds) { numpy::array_view<unsigned char, 3> buffer; PyObject *filein; double dpi = 0; const char *names[] = { "buffer", "file", "dpi", NULL }; // We don't need strict contiguity, just for each row to be // contiguous, and libpng has special handling for getting RGB out // of RGBA, ARGB or BGR. But the simplest thing to do is to // enforce contiguity using array_view::converter_contiguous. if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O|d:write_png", (char **)names, &buffer.converter_contiguous, &buffer, &filein, &dpi)) { return NULL; } png_uint_32 width = (png_uint_32)buffer.dim(1); png_uint_32 height = (png_uint_32)buffer.dim(0); int channels = buffer.dim(2); std::vector<png_bytep> row_pointers(height); for (png_uint_32 row = 0; row < (png_uint_32)height; ++row) { row_pointers[row] = (png_bytep)buffer[row].data(); } FILE *fp = NULL; mpl_off_t offset = 0; bool close_file = false; bool close_dup_file = false; PyObject *py_file = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; struct png_color_8_struct sig_bit; int png_color_type; switch (channels) { case 1: png_color_type = PNG_COLOR_TYPE_GRAY; break; case 3: png_color_type = PNG_COLOR_TYPE_RGB; break; case 4: png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; default: PyErr_SetString(PyExc_ValueError, "Buffer must be an NxMxD array with D in 1, 3, 4 " "(grayscale, RGB, RGBA)"); goto exit; } if (PyBytes_Check(filein) || PyUnicode_Check(filein)) { if ((py_file = mpl_PyFile_OpenFile(filein, (char *)"wb")) == NULL) { goto exit; } close_file = true; } else { py_file = filein; } if ((fp = mpl_PyFile_Dup(py_file, (char *)"wb", &offset))) { close_dup_file = true; } else { PyErr_Clear(); PyObject *write_method = PyObject_GetAttrString(py_file, "write"); if (!(write_method && PyCallable_Check(write_method))) { Py_XDECREF(write_method); PyErr_SetString(PyExc_TypeError, "Object does not appear to be a 8-bit string path or " "a Python file-like object"); goto exit; } Py_XDECREF(write_method); } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { PyErr_SetString(PyExc_RuntimeError, "Could not create write struct"); goto exit; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { PyErr_SetString(PyExc_RuntimeError, "Could not create info struct"); goto exit; } if (setjmp(png_jmpbuf(png_ptr))) { PyErr_SetString(PyExc_RuntimeError, "libpng signaled error"); goto exit; } if (fp) { png_init_io(png_ptr, fp); } else { png_set_write_fn(png_ptr, (void *)py_file, &write_png_data, &flush_png_data); } png_set_IHDR(png_ptr, info_ptr, width, height, 8, png_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); // Save the dpi of the image in the file if (dpi > 0.0) { png_uint_32 dots_per_meter = (png_uint_32)(dpi / (2.54 / 100.0)); png_set_pHYs(png_ptr, info_ptr, dots_per_meter, dots_per_meter, PNG_RESOLUTION_METER); } sig_bit.alpha = 0; switch (png_color_type) { case PNG_COLOR_TYPE_GRAY: sig_bit.gray = 8; sig_bit.red = 0; sig_bit.green = 0; sig_bit.blue = 0; break; case PNG_COLOR_TYPE_RGB_ALPHA: sig_bit.alpha = 8; // fall through case PNG_COLOR_TYPE_RGB: sig_bit.gray = 0; sig_bit.red = 8; sig_bit.green = 8; sig_bit.blue = 8; break; default: PyErr_SetString(PyExc_RuntimeError, "internal error, bad png_color_type"); goto exit; } png_set_sBIT(png_ptr, info_ptr, &sig_bit); png_write_info(png_ptr, info_ptr); png_write_image(png_ptr, &row_pointers[0]); png_write_end(png_ptr, info_ptr); exit: if (png_ptr && info_ptr) { png_destroy_write_struct(&png_ptr, &info_ptr); } if (close_dup_file) { mpl_PyFile_DupClose(py_file, fp, offset); } if (close_file) { mpl_PyFile_CloseFile(py_file); Py_DECREF(py_file); } if (PyErr_Occurred()) { return NULL; } else { Py_RETURN_NONE; } }
static PyObject *Py_write_png(PyObject *self, PyObject *args, PyObject *kwds) { numpy::array_view<unsigned char, 3> buffer; PyObject *filein; double dpi = 0; const char *names[] = { "buffer", "file", "dpi", NULL }; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O|d:write_png", (char **)names, &buffer.converter, &buffer, &filein, &dpi)) { return NULL; } png_uint_32 width = (png_uint_32)buffer.dim(1); png_uint_32 height = (png_uint_32)buffer.dim(0); std::vector<png_bytep> row_pointers(height); for (png_uint_32 row = 0; row < (png_uint_32)height; ++row) { row_pointers[row] = (png_bytep)buffer[row].data(); } FILE *fp = NULL; mpl_off_t offset = 0; bool close_file = false; bool close_dup_file = false; PyObject *py_file = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; struct png_color_8_struct sig_bit; if (buffer.dim(2) != 4) { PyErr_SetString(PyExc_ValueError, "Buffer must be RGBA NxMx4 array"); goto exit; } if (PyBytes_Check(filein) || PyUnicode_Check(filein)) { if ((py_file = mpl_PyFile_OpenFile(filein, (char *)"wb")) == NULL) { goto exit; } close_file = true; } else { py_file = filein; } if ((fp = mpl_PyFile_Dup(py_file, (char *)"wb", &offset))) { close_dup_file = true; } else { PyErr_Clear(); PyObject *write_method = PyObject_GetAttrString(py_file, "write"); if (!(write_method && PyCallable_Check(write_method))) { Py_XDECREF(write_method); PyErr_SetString(PyExc_TypeError, "Object does not appear to be a 8-bit string path or " "a Python file-like object"); goto exit; } Py_XDECREF(write_method); } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { PyErr_SetString(PyExc_RuntimeError, "Could not create write struct"); goto exit; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { PyErr_SetString(PyExc_RuntimeError, "Could not create info struct"); goto exit; } if (setjmp(png_jmpbuf(png_ptr))) { PyErr_SetString(PyExc_RuntimeError, "Error setting jumps"); goto exit; } if (fp) { png_init_io(png_ptr, fp); } else { png_set_write_fn(png_ptr, (void *)py_file, &write_png_data, &flush_png_data); } png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); // Save the dpi of the image in the file if (dpi > 0.0) { png_uint_32 dots_per_meter = (png_uint_32)(dpi / (2.54 / 100.0)); png_set_pHYs(png_ptr, info_ptr, dots_per_meter, dots_per_meter, PNG_RESOLUTION_METER); } // this a a color image! sig_bit.gray = 0; sig_bit.red = 8; sig_bit.green = 8; sig_bit.blue = 8; /* if the image has an alpha channel then */ sig_bit.alpha = 8; png_set_sBIT(png_ptr, info_ptr, &sig_bit); png_write_info(png_ptr, info_ptr); png_write_image(png_ptr, &row_pointers[0]); png_write_end(png_ptr, info_ptr); exit: if (png_ptr && info_ptr) { png_destroy_write_struct(&png_ptr, &info_ptr); } if (close_dup_file) { mpl_PyFile_DupClose(py_file, fp, offset); } if (close_file) { mpl_PyFile_CloseFile(py_file); Py_DECREF(py_file); } if (PyErr_Occurred()) { return NULL; } else { Py_RETURN_NONE; } }
Image::Image(const String &filename, const bool &tileable_) : m_tileable(tileable_) { // String memory; // ZENI_LOGD("Begin Image::Image(...)::File_Ops::get_asset_FILE(...)."); // File_Ops::load_asset(memory, filename); FILE * const file = File_Ops::get_asset_FILE(filename); class file_Destroyer { public: file_Destroyer(FILE * const &file_) : m_file(file_) {} ~file_Destroyer() {fclose(m_file);} private: FILE * m_file; } file_destroyer(file); // ZENI_LOGD("End Image::Image(...)::File_Ops::load_asset(...)."); // if(memory.length() < 8u || png_sig_cmp(reinterpret_cast<png_byte *>(const_cast<char *>(memory.c_str())), 0, 8)) { png_byte header[8]; if(!fread(header, 8u, 1u, file) || png_sig_cmp(header, 0, 8)) { ZENI_LOGE("PNG detection failure."); throw Texture_Init_Failure(); } // else // ZENI_LOGD("PNG detection success."); png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(!png_ptr) { ZENI_LOGE("png_create_read_struct(...) failed."); throw Texture_Init_Failure(); } // else // ZENI_LOGD("png_create_read_struct(...) success."); class png_structp_Destroyer { public: png_structp_Destroyer(const png_structp &ptr) : m_ptr(ptr) {} ~png_structp_Destroyer() {png_destroy_read_struct(&m_ptr, NULL, NULL);} private: png_structp m_ptr; } png_structp_destroyer(png_ptr); //create png info struct png_infop info_ptr = png_create_info_struct(png_ptr); if(!info_ptr) { ZENI_LOGE("png_create_info_struct(...) failed."); throw Texture_Init_Failure(); } // else // ZENI_LOGD("png_create_info_struct(...) success."); #ifdef _WINDOWS #pragma warning( push ) #pragma warning( disable : 4611 ) #endif //png error stuff, not sure libpng man suggests this. if(setjmp(png_jmpbuf(png_ptr))) { #ifdef _WINDOWS #pragma warning( pop ) #endif ZENI_LOGE("setjmp(png_jmpbuf(...)) failed."); throw Texture_Init_Failure(); } // else // ZENI_LOGD("setjmp(png_jmpbuf(...)) success."); //init png reading // png_bytep mem_ptr = reinterpret_cast<png_bytep>(const_cast<char *>(memory.c_str() + 8u)); // png_set_read_fn(png_ptr, &mem_ptr, png_read_from_memory); png_init_io(png_ptr, file); //let libpng know you already read the first 8 bytes png_set_sig_bytes(png_ptr, 8); // read all the info up to the image data png_read_info(png_ptr, info_ptr); //variables to pass to get info int bit_depth, color_type; png_uint_32 twidth, theight; // get info about png png_get_IHDR(png_ptr, info_ptr, &twidth, &theight, &bit_depth, &color_type, NULL, NULL, NULL); if(bit_depth != 8) { ZENI_LOGE("Image using bit depth other than 8."); throw Texture_Init_Failure(); } switch(color_type) { case PNG_COLOR_TYPE_GRAY: m_color_space = Luminance; m_bytes_per_pixel = 1; break; case PNG_COLOR_TYPE_GRAY_ALPHA: m_color_space = Luminance_Alpha; m_bytes_per_pixel = 2; break; case PNG_COLOR_TYPE_RGB: m_color_space = RGB; m_bytes_per_pixel = 3; break; case PNG_COLOR_TYPE_RGB_ALPHA: m_color_space = RGBA; m_bytes_per_pixel = 4; break; default: ZENI_LOGE("Image using must be Grayscale, RGB, or RGBA."); throw Texture_Init_Failure(); } //update width and height based on png info m_size.x = int(twidth); m_size.y = int(theight); // Update the png info struct. png_read_update_info(png_ptr, info_ptr); // Row size in bytes. m_row_size = int(png_get_rowbytes(png_ptr, info_ptr)); // Allocate the image_data as a big block, to be given to opengl m_data.resize(m_row_size * theight); //row_pointers is for pointing to image_data for reading the png with libpng std::vector<Uint8 *> row_pointers(theight); // set the individual row_pointers to point at the correct offsets of image_data for(int j = 0; j < m_size.y; ++j) row_pointers[j] = &m_data[0] + j * m_row_size; //read the png into image_data through row_pointers png_read_image(png_ptr, reinterpret_cast<png_bytep *>(&row_pointers[0])); // ZENI_LOGD(("Image: " + itoa(m_size.x) + "x" + itoa(m_size.y) + "x" + itoa(m_bytes_per_pixel) + " = " + itoa(m_row_size) + ", " + itoa(m_data.size())).c_str()); }
PNGImage::PNGImage(const char *full_file_path) { FILE *file = fopen(full_file_path, "rb"); if(file == nullptr) { throw ReadFileException("PNGImage", "PNG file specified is not valid."); } png_byte png_header[8]; fread(png_header, 1u, 8u, file); if(ferror(file) != 0 || feof(file) != 0) { fclose(file); throw ReadFileException("PNGImage", "Cannot read the PNG file."); } if(png_sig_cmp(png_header, 0, 8) != 0) { fclose(file); throw ReadFileException("PNGImage", "Please select a valid PNG file."); } png_structp png_read_struct = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if(png_read_struct == nullptr) { fclose(file); throw ReadFileException("PNGImage", "Cannot initialize PNG read struct."); } png_infop png_info_struct = png_create_info_struct(png_read_struct); if(png_info_struct == nullptr) { png_destroy_read_struct(&png_read_struct, nullptr, nullptr); fclose(file); throw ReadFileException("PNGImage", "Cannot initialize PNG info struct."); } png_infop png_end_struct = png_create_info_struct(png_read_struct); if(png_info_struct == nullptr) { png_destroy_read_struct(&png_read_struct, &png_info_struct, nullptr); fclose(file); throw ReadFileException("PNGImage", "Cannot initialize PNG info struct."); } if(setjmp(png_jmpbuf(png_read_struct))) { png_destroy_read_struct(&png_read_struct, &png_info_struct, &png_end_struct); fclose(file); throw ReadFileException("PNGImage", "Error in libpng."); } png_init_io(png_read_struct, file); png_set_sig_bytes(png_read_struct, 8); png_read_info(png_read_struct, png_info_struct); int bit_depth, color_type; png_get_IHDR(png_read_struct, png_info_struct, &width, &height, &bit_depth, &color_type, nullptr, nullptr, nullptr); if(bit_depth == 16) { png_set_scale_16(png_read_struct); bit_depth = 8; } switch(color_type) { case PNG_COLOR_TYPE_RGB: color_space = GL_RGB; break; case PNG_COLOR_TYPE_RGB_ALPHA: color_space = GL_RGBA; break; default: png_destroy_read_struct(&png_read_struct, &png_info_struct, &png_end_struct); fclose(file); throw ReadFileException("PNGImage", "Unsupported color space."); } int row_bytes = png_get_rowbytes(png_read_struct, png_info_struct); row_bytes += 3 - ((row_bytes-1) % 4); output_buffer.reset(new (std::nothrow)png_byte[row_bytes * height * sizeof(png_byte) + 15]); if(output_buffer.get() == nullptr) { png_destroy_read_struct(&png_read_struct, &png_info_struct, &png_end_struct); fclose(file); throw ReadFileException("PNGImage", "Cannot allocate required memory."); } std::unique_ptr<png_byte *> row_pointers(new png_byte *[height]); if(row_pointers.get() == nullptr) { png_destroy_read_struct(&png_read_struct, &png_info_struct, &png_end_struct); fclose(file); throw ReadFileException("PNGImage", "Cannot allocate required memory."); } png_byte **_row_pointers = row_pointers.get(); unsigned char *_output_buffer = output_buffer.get(); for(unsigned int i = 0; i < height; ++i) { _row_pointers[height - 1 - i] = _output_buffer + i * row_bytes; } png_read_image(png_read_struct, _row_pointers); png_destroy_read_struct(&png_read_struct, &png_info_struct, &png_end_struct); fclose(file); }