<commit_before>#include "cvd/internal/io/png.h"
#include "cvd/image_io.h"
#include "cvd/config.h"

#include <png.h>
#include <cstdlib>
#include <iostream>

using namespace CVD;
using namespace CVD::Exceptions;
using namespace CVD::Exceptions::Image_IO;
using namespace PNG;
using namespace std;

////////////////////////////////////////////////////////////////////////////////
//
// C++ istreams based I/O functions
// 
static void error_fn(png_structp png_ptr, png_const_charp error_msg)
{
	*(string*)(png_ptr->error_ptr) = error_msg;
}

static void warn_fn(png_structp, png_const_charp )
{
}

static void read_fn(png_structp png_ptr, unsigned char*  data, size_t numbytes)
{
	istream* i = (istream*)png_get_io_ptr(png_ptr);
	i->read((char*) data, numbytes);

	//What to do on stream failure?
	//There is no return value, and I do not know if longjmp is safe here.
	//Anyway, multiple rereads of the data will cause the PNG read
	//to fail because it has internal checksums
}

static void write_fn(png_structp png_ptr, unsigned char*  data, size_t numbytes)
{
	ostream* o = (ostream*)png_get_io_ptr(png_ptr);
	o->write((char*) data, numbytes);
}

static void flush_fn(png_structp png_ptr)
{
	ostream* o = (ostream*)png_get_io_ptr(png_ptr);
	(*o) << flush;
}

////////////////////////////////////////////////////////////////////////////////
//
// Implementation of PNGPimpl
//


class CVD::PNG::PNGPimpl
{
		
	public:
		template<class C> void read_pixels(C*);
		PNGPimpl(std::istream& in);
		~PNGPimpl();
		std::string datatype();
		std::string name();
		ImageRef size();
	
	private:
		std::istream& i;
		std::string type;
		unsigned long row;
		png_struct_def* png_ptr;
		png_info_struct* info_ptr, *end_info;

		std::string error_string;
		ImageRef my_size;
};

////////////////////////////////////////////////////////////////////////////////
//
// PNG reading functions
// 

string PNGPimpl::datatype()
{
	return type;
}

string PNGPimpl::name()
{
	return "PNG";
}

ImageRef PNGPimpl::size()
{
	return my_size;
}


#define LOG(X) do{ cerr << X; }while(0)

template<class P> void PNGPimpl::read_pixels(P* data)
{
	if(datatype() != PNM::type_name<P>::name())
		throw ReadTypeMismatch(datatype(), PNM::type_name<P>::name());

	if(row  > (unsigned long)my_size.y)
		throw InternalLibraryError("CVD", "Read past end of image.");


	if(setjmp(png_jmpbuf(png_ptr)))     
		throw Exceptions::Image_IO::MalformedImage(error_string);
	
	unsigned char* cptr = reinterpret_cast<unsigned char*>(data);
	unsigned char** row_ptr = &cptr;

	png_read_rows(png_ptr, row_ptr, NULL, 1);
}



PNGPimpl::PNGPimpl(std::istream& in)
:i(in),type(""),row(0),png_ptr(0),info_ptr(0),end_info(0)
{
	//Read the header and make sure it really is a PNG...
	unsigned char header[8];

	in.read((char*)header, 8);

	if(png_sig_cmp(header, 0, 8))
		throw Exceptions::Image_IO::MalformedImage("Not a PNG image");

	LOG("PNG header found\n");

	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);


	if(!png_ptr)
		throw Exceptions::OutOfMemory();
	
	info_ptr = png_create_info_struct(png_ptr);
	if(!info_ptr)
	{
		png_destroy_read_struct(&png_ptr, NULL, NULL);
		throw Exceptions::OutOfMemory();
	}

	end_info = png_create_info_struct(png_ptr);
	if(!info_ptr)
	{
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
		throw Exceptions::OutOfMemory();
	}

	if(setjmp(png_jmpbuf(png_ptr)))     
	{         
		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
		throw Exceptions::Image_IO::MalformedImage(error_string);
	}

	png_set_error_fn(png_ptr, &error_string, error_fn, warn_fn);

	//png_init_io(png_ptr, stdin);

	png_set_read_fn(png_ptr, &i, read_fn);


	png_set_sig_bytes(png_ptr, 8);
	
	png_read_info(png_ptr, info_ptr);

	png_uint_32 w, h;
	int colour, interlace, dummy, depth;

	png_get_IHDR(png_ptr, info_ptr, &w, &h, &depth, &colour, &interlace, &dummy, &dummy);
	
	my_size.x = w;
	my_size.y = h;

	//Figure out the type name, and what processing to to.
	if(depth == 1)
	{	
		//Unpack bools to bytes to ease loading.
		png_set_packing(png_ptr);
		type = PNM::type_name<bool>::name();
	}
	else if(depth <= 8)
	{
		//Expand nonbool colour depths up to 8bpp<commit_msg>Added macro based verbose mode to PNG loading.<commit_after>#include "cvd/internal/io/png.h"
#include "cvd/image_io.h"
#include "cvd/config.h"

#include <png.h>
#include <cstdlib>
#include <iostream>

using namespace CVD;
using namespace CVD::Exceptions;
using namespace CVD::Exceptions::Image_IO;
using namespace PNG;
using namespace std;

////////////////////////////////////////////////////////////////////////////////
//
// C++ istreams based I/O functions
// 
static void error_fn(png_structp png_ptr, png_const_charp error_msg)
{
	*(string*)(png_ptr->error_ptr) = error_msg;
}

static void warn_fn(png_structp, png_const_charp )
{
}

static void read_fn(png_structp png_ptr, unsigned char*  data, size_t numbytes)
{
	istream* i = (istream*)png_get_io_ptr(png_ptr);
	i->read((char*) data, numbytes);

	//What to do on stream failure?
	//There is no return value, and I do not know if longjmp is safe here.
	//Anyway, multiple rereads of the data will cause the PNG read
	//to fail because it has internal checksums
}

static void write_fn(png_structp png_ptr, unsigned char*  data, size_t numbytes)
{
	ostream* o = (ostream*)png_get_io_ptr(png_ptr);
	o->write((char*) data, numbytes);
}

static void flush_fn(png_structp png_ptr)
{
	ostream* o = (ostream*)png_get_io_ptr(png_ptr);
	(*o) << flush;
}

////////////////////////////////////////////////////////////////////////////////
//
// Implementation of PNGPimpl
//

class CVD::PNG::PNGPimpl
{
	public:
		template<class C> void read_pixels(C*);
		PNGPimpl(std::istream& in);
		~PNGPimpl();
		std::string datatype();
		std::string name();
		ImageRef size();
	
	private:
		std::istream& i;
		std::string type;
		unsigned long row;
		png_struct_def* png_ptr;
		png_info_struct* info_ptr, *end_info;

		std::string error_string;
		ImageRef my_size;
};

////////////////////////////////////////////////////////////////////////////////
//
// PNG reading functions
// 

string PNGPimpl::datatype()
{
	return type;
}

string PNGPimpl::name()
{
	return "PNG";
}

ImageRef PNGPimpl::size()
{
	return my_size;
}

#ifdef CVD_INTERNAL_VERBOSE_PNG
	#include <map>

	#define LOG(X) do{ cerr << X; }while(0)

	static string lookup_color_type(int i)
	{
		map<int, string> m;
		#define ADD(X) m[X] = #X
		ADD(PNG_COLOR_TYPE_GRAY);
		ADD(PNG_COLOR_TYPE_GRAY_ALPHA);
		ADD(PNG_COLOR_TYPE_PALETTE);
		ADD(PNG_COLOR_TYPE_RGB);
		ADD(PNG_COLOR_TYPE_RGB_ALPHA);
		ADD(PNG_COLOR_MASK_PALETTE);
		ADD(PNG_COLOR_MASK_COLOR);
		ADD(PNG_COLOR_MASK_ALPHA);

		return m[i];
	}
#else
	#define LOG(X)
#endif

template<class P> void PNGPimpl::read_pixels(P* data)
{
	if(datatype() != PNM::type_name<P>::name())
		throw ReadTypeMismatch(datatype(), PNM::type_name<P>::name());

	if(row  > (unsigned long)my_size.y)
		throw InternalLibraryError("CVD", "Read past end of image.");


	if(setjmp(png_jmpbuf(png_ptr)))     
		throw Exceptions::Image_IO::MalformedImage(error_string);
	
	unsigned char* cptr = reinterpret_cast<unsigned char*>(data);
	unsigned char** row_ptr = &cptr;

	png_read_rows(png_ptr, row_ptr, NULL, 1);
}



PNGPimpl::PNGPimpl(std::istream& in)
:i(in),type(""),row(0),png_ptr(0),info_ptr(0),end_info(0)
{
	//Read the header and make sure it really is a PNG...
	unsigned char header[8];

	in.read((char*)header, 8);

	if(png_sig_cmp(header, 0, 8))
		throw Exceptions::Image_IO::MalformedImage("Not a PNG image");

	LOG("PNG header found\n");

	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);


	if(!png_ptr)
		throw Exceptions::OutOfMemory();
	
	info_ptr = png_create_info_struct(png_ptr);
	if(!info_ptr)
	{
		png_destroy_read_struct(&png_ptr, NULL, NULL);
		throw Exceptions::OutOfMemory();
	}

	end_info = png_create_info_struct(png_ptr);
	if(!info_ptr)
	{
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
		throw Exceptions::OutOfMemory();
	}

	if(setjmp(png_jmpbuf(png_ptr)))     
	{         
		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
		throw Exceptions::Image_IO::MalformedImage(error_string);
	}

	png_set_error_fn(png_ptr, &error_string, error_fn, warn_fn);

	//png_init_io(png_ptr, stdin);

	png_set_read_fn(png_ptr, &i, read_fn);


	png_set_sig_bytes(png_ptr, 8);
	
	png_read_info(png_ptr, info_ptr);

	png_uint_32 w, h;
	int colour, interlace, dummy, depth;

	png_get_IHDR(png_ptr, info_ptr, &w, &h, &depth, &colour, &interlace, &dummy, &dummy);

	LOG("w         = " << w << endl);
	LOG("h         = " << h << endl);
	LOG("depth     = " << depth << endl);
	LOG("colour    = " << colour<< ": " << lookup_color_type(colour) << endl);
	LOG("  palette = " << (colour & PNG_COLOR_MASK_PALETTE) << endl);
	LOG("  color   = " << (colour & PNG_COLOR_MASK_COLOR) << endl);
	LOG("  alpha   = " << (colour & PNG_COLOR_MASK_ALPHA) << endl);
	LOG("interlace = " << interlace<< endl);
	LOG("channels  = " << (int)png_get_channels(png_ptr, info_ptr) << endl);
	
	my_size.x = w;
	my_size.y = h;

	//Figure out the type name, and what processing to to.
	if(depth == 1)
	{	
		//Unpack bools to bytes to ease loading.
		png_set_packing(png_ptr);
		type = PNM::type_name<bool>::name();
	}
	else if(depth <= 8)
	{
		//Expand nonbool colour depths up to 8bpp
{'fraction_non_alphanumeric': 0.1911259131929523, 'fraction_numerical': 0.0034379028792436614, 'mean_word_length': 3.7205882352941178, 'pattern_counts': {'":': 1, '<': 73, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5124677', 'n_tokens_mistral': 3696, 'n_tokens_neox': 3451, 'n_words': 752}