#include "stdafx.h"
#include "png.h"

static Mix::Plugin::INFORMATION g_Info = { Mix::Plugin::TEXTURE_LOADER, L"PNG", L"1.0.0.0" };

////////////////////////////////////////////////////////////////////////////////////////////////////
// プラグイン情報の取得
////////////////////////////////////////////////////////////////////////////////////////////////////

const Mix::Plugin::INFORMATION& __stdcall GetInformation( void )
{
	return g_Info;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// ロード
////////////////////////////////////////////////////////////////////////////////////////////////////

void PNG_Read( png_structp png_sp, png_bytep buff, png_size_t size )
{
	Mix::Plugin::IReader* pSrc = reinterpret_cast<Mix::Plugin::IReader*>( png_get_io_ptr( png_sp ) );

	if( size > INT_MAX )
	{
		png_error( png_sp, "Failed : PNG_Read : LargeSize!" );
		return;
	}

	if( pSrc->Read( buff, static_cast<unsigned int>( size ) ) != size )
	{
		png_error( png_sp, "Failed : PNG_Read : Overflow!" );
	}
}

int __stdcall Load( Mix::Plugin::IReader* pSrc, Mix::Plugin::Graphics::ITextureKit* pDst )
{
	//////////////////////////////////////////////////////////////////////////////////////////
	// シグネチャのチェック
	//////////////////////////////////////////////////////////////////////////////////////////

	png_byte png_header[8];

	if( pSrc->Read( png_header, sizeof( png_byte[8] ) ) != sizeof( png_byte[8] ) )
	{
		return Mix::Plugin::Graphics::TP_NOT_SUPPORTED;
	}

	if( png_sig_cmp( png_header, 0, 8 ) != 0 )
	{
		return Mix::Plugin::Graphics::TP_NOT_SUPPORTED;
	}

	pSrc->Seek( Mix::Plugin::IReader::BEGIN, 0 );

	//////////////////////////////////////////////////////////////////////////////////////////

	png_structp png_sp;
	png_infop png_ip;

	png_uint_32 width;
	png_uint_32 height;
	int bitCount;
	int colorType;
	int interlaceType;

	unsigned int memPitch;
	unsigned int memSize;
	void* pMem;

	png_bytep row;
	png_size_t rowPitch;

	unsigned int i;

	//////////////////////////////////////////////////////////////////////////////////////////

	png_sp = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );
	png_ip = png_create_info_struct( png_sp );

	png_set_read_fn( png_sp, pSrc, PNG_Read );
	png_read_info( png_sp, png_ip );
	png_get_IHDR( png_sp, png_ip, &width, &height, &bitCount, &colorType, &interlaceType, NULL, NULL );

	if( bitCount != 8 )
	{
		png_read_end( png_sp, png_ip );
		return Mix::Plugin::Graphics::TP_NOT_SUPPORTED;
	}

	//////////////////////////////////////////////////////////////////////////////////////////

	memPitch = width * 4;
	memSize = memPitch * height;

	pMem = pDst->AddSubResourceData( memSize, memPitch );
	if( pMem == NULL )
	{
		png_read_end( png_sp, png_ip );
		return Mix::Plugin::Graphics::TP_OUT_OF_MEMORY;
	}

	rowPitch = png_get_rowbytes( png_sp, png_ip );
	if( rowPitch > INT_MAX )
	{
		png_read_end( png_sp, png_ip );
		return Mix::Plugin::Graphics::TP_FATAL_ERROR;
	}

	row = reinterpret_cast<png_bytep>( png_malloc( png_sp, rowPitch ) );
	if( row == NULL )
	{
		png_read_end( png_sp, png_ip );
		return Mix::Plugin::Graphics::TP_OUT_OF_MEMORY;
	}

	//////////////////////////////////////////////////////////////////////////////////////////

	Mix::Plugin::Graphics::TEXTURE_PLUGIN_RESULT result;

	pDst->SetType( Mix::Plugin::Graphics::ITextureKit::PLANE );
	pDst->SetFormat( Mix::Plugin::Graphics::ITextureKit::FORMAT::R8G8B8A8 );
	pDst->SetSize( width, height );
	pDst->SetDepth( 1 );
	pDst->SetMipLevels( 1 );
	pDst->SetFlags( 0 );

	if( colorType == PNG_COLOR_TYPE_GRAY )
	{
		result = Mix::Plugin::Graphics::TP_NOT_SUPPORTED;
	}
	else if( colorType == PNG_COLOR_TYPE_PALETTE )
	{
		result = Mix::Plugin::Graphics::TP_NOT_SUPPORTED;
	}
	else if( colorType == PNG_COLOR_TYPE_RGB )
	{
		png_int_32p pTexDst = reinterpret_cast<png_int_32p>( pMem );

		png_bytep pTexSrc;
		png_bytep pTexSrcEnd;

		png_int_32 r;
		png_int_32 b;
		png_int_32 g;

		for( i = 0; i < height; i++ )
		{
			png_read_row( png_sp, row, NULL );

			pTexSrc = &( row[0] );
			pTexSrcEnd = pTexSrc + rowPitch;

			while( pTexSrc != pTexSrcEnd )
			{
				r = *pTexSrc++;
				g = *pTexSrc++;
				b = *pTexSrc++;

				*pTexDst++ = 0xFF000000 | ( r << 16 ) | ( g << 8 ) | b;
			}
		}

		result = Mix::Plugin::Graphics::TP_OK;
	}
	else if( colorType == PNG_COLOR_TYPE_RGB_ALPHA )
	{
		png_int_32p pTexDst = reinterpret_cast<png_int_32p>( pMem );

		png_bytep pTexSrc;
		png_bytep pTexSrcEnd;

		png_int_32 r;
		png_int_32 g;
		png_int_32 b;
		png_int_32 a;

		for( i = 0; i < height; i++ )
		{
			png_read_row( png_sp, row, NULL );

			pTexSrc = &( row[0] );
			pTexSrcEnd = pTexSrc + rowPitch;

			while( pTexSrc != pTexSrcEnd )
			{
				r = *pTexSrc++;
				g = *pTexSrc++;
				b = *pTexSrc++;
				a = *pTexSrc++;

				*pTexDst++ = ( a << 24 ) | ( r << 16 ) | ( g << 8 ) | b;
			}
		}

		result = Mix::Plugin::Graphics::TP_OK;
	}
	else if( colorType == PNG_COLOR_TYPE_GRAY_ALPHA )
	{
		result = Mix::Plugin::Graphics::TP_NOT_SUPPORTED;
	}
	else
	{
		result = Mix::Plugin::Graphics::TP_NOT_SUPPORTED;
	}

	//////////////////////////////////////////////////////////////////////////////////////////

	png_free( png_sp, row );
	png_read_end( png_sp, png_ip );

	//////////////////////////////////////////////////////////////////////////////////////////

	return result;
}
