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);
  }
Example #3
0
  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());
  }
Example #5
0
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;
}
Example #6
0
    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;
    }
Example #7
0
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);
}
Example #8
0
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;
}
Example #9
0
	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 );
	}
Example #10
0
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;
    }
}
Example #11
0
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;
    }
}
Example #12
0
  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());
  }
Example #13
0
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);
}