#include "pch.hpp"
#ifdef USEPNG

//===========================================================================//
// File:	 Loader_PNG.cpp													 //
// Project:  GameOS					                                         //
// Contents: Used to load textures from .PNG files							 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 08/31/99  AG     Created													 //
//---------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
#include "Texture Manager.hpp"
#include "DXRasterizer.hpp"
#include "DirectX.hpp"
#include "MemoryManager.hpp"
#include "PNG\png.h"







png_bytep	m_pbtImage;
int			m_nSize;				// The number of bytes in m_pbtImage.

png_bytep	m_pbtFileData;			// Buffered raw data from the PNG file
int			m_nBytesRead;			// Number of bytes read from m_pbtFileData from the read callback function


png_structp	m_png;					// Our PNG ADT
png_infop	m_pPngInfo;				// Info retrieved from the PNG data up to IDAT chunk

int			m_nColorType;
int			m_nInterlaceType;
int			m_nBitDepth;

HBITMAP		m_hBitmap;

png_voidp myMalloc(png_structp png_ptr, size_t size)
{
	return gos_Malloc(size);
}
void myFree(png_structp png_ptr, void* Addr)
{
	gos_Free(Addr);
}
static void myError( png_structp png_ptr, png_const_charp message )
{
	STOP(( "PNG Error - %s",message ));
}

/**************************************************************************************
 DESC:	Read callback function.  (called back by the real callback function
		CPNGFileHelpers::_PngReadCallback)
 IN:	png -		The PNG handle that generated this callback
		pbtData -	The buffer to write the file contents to.
		nLength -	The number of bytes to copy to pbtData.
 [public]
**************************************************************************************/
void PngReadCallback(png_structp png, png_bytep pbtData, png_size_t nLength)
{
	memcpy((LPVOID)pbtData, LPVOID(m_pbtFileData + m_nBytesRead), nLength);
	m_nBytesRead += nLength;
}


void DecodePNGDimensions( const char* FileName, BYTE *Data, DWORD DataSize, DWORD* pTextureWidth, DWORD* pTextureHeight )
{
	LOG_BLOCK("Gos3D::Texture.DecodePNGDimensions");
	
	m_png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, myError, myError, 0, &myMalloc, &myFree );

	m_pPngInfo = png_create_info_struct(m_png);
	if( m_pPngInfo == NULL )
		STOP(( "Error Reading file '%s'",FileName ));

	m_pbtFileData=Data;
	m_nBytesRead = 0;
		
	png_set_read_fn( m_png, 0, PngReadCallback );

	png_read_info(m_png, m_pPngInfo);

	png_uint_32	nDummy;
	int	nCompressionType, nFilterType;
	png_get_IHDR(m_png, m_pPngInfo, &nDummy, &nDummy, &m_nBitDepth, &m_nColorType, &m_nInterlaceType, &nCompressionType, &nFilterType);

	if (m_nColorType == PNG_COLOR_TYPE_PALETTE)
		png_set_expand(m_png);

	if (m_nColorType == PNG_COLOR_TYPE_GRAY && m_nBitDepth < 8)
		png_set_expand(m_png);

	if (m_nColorType == PNG_COLOR_TYPE_GRAY)
		png_set_gray_to_rgb(m_png);

	if (png_get_valid(m_png, m_pPngInfo, PNG_INFO_tRNS))
		png_set_expand(m_png);

	png_set_bgr(m_png);

	png_set_filler(m_png, 0xff, PNG_FILLER_AFTER);

	*pTextureWidth=m_pPngInfo->width;
	*pTextureHeight=m_pPngInfo->height; 

	png_destroy_read_struct(&m_png, (png_infopp)(m_pPngInfo ? &m_pPngInfo : NULL), (png_infopp)NULL );
}




//
// Read in a PNG file and return a 32bit memory surface containing it.
//
//
// pBitmap contains the BMP file
// Size is the length of the BMP file
//
//
void* DecodePNG( const char* FileName, BYTE* Data, DWORD DataSize, DWORD* TextureWidth, DWORD* TextureHeight, bool TextureLoad, void *pDestSurf )
{
	LOG_BLOCK("Gos3D::Texture.DecodePNG");
	IDirectDrawSurface7* DWSurface7=0;
	DDSURFACEDESC2 ddsd;
	DWORD* Memory=0;
	DWORD Pitch=0;
	DWORD* Dest=0;
	memset( &ddsd, 0, sizeof(ddsd) );
//
// Reading PNG files
//
	m_png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, myError, myError, 0, &myMalloc, &myFree );

	m_pPngInfo = png_create_info_struct(m_png);
	if( m_pPngInfo == NULL )
		STOP(( "Error Reading file '%s'",FileName ));

	m_pbtFileData=Data;
	m_nBytesRead = 0;
		
	png_set_read_fn( m_png, 0, PngReadCallback );

	png_read_info(m_png, m_pPngInfo);

	png_uint_32	nDummy;
	int	nCompressionType, nFilterType;
	png_get_IHDR(m_png, m_pPngInfo, &nDummy, &nDummy, &m_nBitDepth, &m_nColorType, &m_nInterlaceType, &nCompressionType, &nFilterType);

	if (m_nColorType == PNG_COLOR_TYPE_PALETTE)
		png_set_expand(m_png);

	if (m_nColorType == PNG_COLOR_TYPE_GRAY && m_nBitDepth < 8)
		png_set_expand(m_png);

	if (m_nColorType == PNG_COLOR_TYPE_GRAY)
		png_set_gray_to_rgb(m_png);

	if (png_get_valid(m_png, m_pPngInfo, PNG_INFO_tRNS))
		png_set_expand(m_png);

	png_set_bgr(m_png);

	png_set_filler(m_png, 0xff, PNG_FILLER_AFTER);

	DWORD Width=m_pPngInfo->width;
	DWORD Height=m_pPngInfo->height; 
//
// Now check size of the image
//
	if( TextureLoad )
	{
		if( ((Width|Height)&(15|~2047L)) || (Width&(Width-1)) || (Height&(Height-1)) )
			STOP(( "Only power of 2 sized PNG files supported up to 1024*1024 '%s' (%d,%d)",FileName,m_pPngInfo->width,m_pPngInfo->height ));
//
// Setup format of the DWORD version of the system memory image
//
		if( pDestSurf )
		{
			DWSurface7 = (IDirectDrawSurface7*)pDestSurf;
		}
		else
		{
			ddsd.dwSize								= sizeof(ddsd);
			ddsd.dwFlags							= DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
			ddsd.ddsCaps.dwCaps						= DDSCAPS_SYSTEMMEMORY | DDSCAPS_TEXTURE;
			ddsd.dwWidth							= Width;
			ddsd.dwHeight							= Height;
			ddsd.ddpfPixelFormat.dwSize				= sizeof(DDPIXELFORMAT);
			ddsd.ddpfPixelFormat.dwFlags			= DDPF_RGB;
			ddsd.ddpfPixelFormat.dwRGBBitCount		= 32;
			ddsd.ddpfPixelFormat.dwRGBAlphaBitMask	= 0xff000000;
			ddsd.ddpfPixelFormat.dwRBitMask			= 0x00ff0000;
			ddsd.ddpfPixelFormat.dwGBitMask			= 0x0000ff00;
			ddsd.ddpfPixelFormat.dwBBitMask			= 0x000000ff;
//
// Create system memory surface
//
			wCreateSurface( DDobject, &ddsd, &DWSurface7, NULL );
//
// Calculate memory useage
//
			gos_ChangeHeapSize( Heap_TextureOther, Width*Height*4, 1 );
		}
//
// Lock the surface and get pointer to bottom left (BMP's are stored upside down)
//
		ddsd.dwSize = sizeof(ddsd);
		wLock( DWSurface7, NULL, &ddsd, DDLOCK_NOSYSLOCK | DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL );
		Pitch=ddsd.lPitch;
		Dest=(DWORD*)ddsd.lpSurface;
	}
	else
//
// Loading into a memory surface
//
	{
		Memory=Dest=(DWORD*)gos_Malloc(Width*Height*4, Heap_TextureOther);
		Pitch=Width*4;
	}
//
// Now read the PNG file, line by line
//
	for( int y=0; y<Height; y++ )
	{
		png_read_row(m_png, (BYTE*)Dest+Pitch*y, NULL);
	}
//
// Clean up PNG reading
//
	png_read_end(m_png, m_pPngInfo);
	png_destroy_read_struct(&m_png, (png_infopp)(m_pPngInfo ? &m_pPngInfo : NULL), (png_infopp)NULL );
//
// Update structure with information about surface
//
	*TextureHeight=Height;
	*TextureWidth=Width;
//
// Loading into a texture?
//
	if( TextureLoad )
	{
		wUnlock( DWSurface7, NULL );
		return (void*)DWSurface7;
	}
	else
	{
		return (void*)Memory;
	}
}






#endif