🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

How To Load PNG files on a ddraw surface

Started by
0 comments, last by drago 24 years, 10 months ago
Greets,

Can anyone tell me how to load PNG files onto a ddraw surface, i use MSVC6. Any help would be appreciated

osu!
Advertisement
If you have any need to preserve the alpha component
of the pixel, you're best off skipping this and doing
it right the first time.

"This," being a test I did some time ago. I quickly
documented it and made sure it compiled, but other
than for loading a test PNG, I haven't tested it
further.

The code's pretty ugly in parts. Basically what
it does is load a PNG and make a DIB out of it with
MakeDIB(). Then, with MakeCompatibleSurface(), it
creates a DirectDraw surface and BitBlt()s that
DIB onto it.

A quick example of using it:

code:
MyPNG png;// I'll just pretend my code is// perfect and there's no need// for error processing.png.MakeDIB( "paddle.png" );// Oh no!  Global variables!png.MakeCompatibleSurface( g_lpDD, &g_lpPaddleSprite );png.DoCleanUp();png.MakeDIB( "ball.png" );png.MakeCompatibleSurface( g_lpDD,&g_lpBallSprite );PlayPong();


Using this, you don't have to worry about pixel format
conversion but, as I said, it might be better to
do that anyway. Try searching for information
on plotting 16bpp pixels in DirectDraw.

Note: this assumes you have libpng and zlib
already, so make sure you link to them.

code:
#ifndef __MY_PNG_INCLUDED__#define __MY_PNG_INCLUDED__#define WIN32_LEAN_AND_MEAN#include #include #include LIBPNG_HEADER_LOCATION// Number of bytes read by IsPNG() to// check for the file's signature.#define MYPNG_BYTES_TO_CHECK						4// The following specifies the RGB colours of the default// background.  The class doesn't preserve alpha// values.  Instead it tries to combine them with// the image's background or, if none is provided,// creates a default background from these// #defines.  If you define MYPNG_STRIP_ALPHA,// the alpha values are stripped instead of// combined.#undef MYPNG_STRIP_ALPHA/* Specifies a black background. */#define MYPNG_DEFAULT_BACKGROUND_RED			0#define MYPNG_DEFAULT_BACKGROUND_GREEN			0#define MYPNG_DEFAULT_BACKGROUND_BLUE			0enum MYPNG_STATUS{	MYPNG_OK,	MYPNGERR_FILECOULDNTBEACCESSED,	MYPNGERR_NOTAPNG,	MYPNGERR_NOTENOUGHMEMORY,	MYPNGERR_DIBNOTCREATED,	MYPNGERR_COULDNTCREATESURFACE,	MYPNGERR_COULDNTGETSRCCONTEXT,	MYPNGERR_COULDNTGETDESTCONTEXT,	MYPNGERR_COULDNTBLIT};class MyPNG{   public:      MyPNG();      ~MyPNG();		// Clears and resets all variables.		void DoCleanUp();      // Open a PNG file specified by 'filename', extract data      // from it and convert it into a device-independent      // bitmap.  Returns: MYPNG_OK on success, or a		// MYPNGERR_something.            MYPNG_STATUS MakeDIB( LPCSTR filename );            // Create an off-screen DirectDraw surface and BitBlt()      // the DIB onto it.  It takes as arguments a pointer      // to a DirectDraw object and a pointer to a DirectDraw      // surface pointer.  Returns: MYPNG_OK on success,		// else a MYPNGERR_something.            MYPNG_STATUS MakeCompatibleSurface( LPDIRECTDRAW4 lpdd, LPDIRECTDRAWSURFACE4         *surface );            private:      /*    Private function declarations.    */		BOOL IsPNG();				// Determines if the open										// file is a PNG.      void Initialize();      // Resets all variables.                  MYPNG_STATUS OpenFile( LPCSTR file ); // Opens a file and														  // extracts information														  // to the PNG structs.		BOOL ConvertToDIB();		// Sets up a DIB and writes										// image data into it.				BOOL CloseFile();       // Closes an open file and destroys                              // created PNG structs.            /*    Private variable declarations.    */            FILE       *m_pFile;    // Currently open file.      HBITMAP     m_hbm;      // Handle to a generated DIB.      png_structp m_pStruct;  // Currently open PNG file struct.      png_infop   m_pInfo;    // Currently open PNG info struct.      png_uint_32 m_Width;    // Width of image.      png_uint_32 m_Height;   // Height of image.};#endif#include "mypng.h"MyPNG::MyPNG(){	Initialize();} /* MyPNG() */MyPNG::~MyPNG(){	DoCleanUp();} /* MyPNG() */void MyPNG::Initialize(){	m_Width		= 0;	m_Height		= 0;	m_hbm			= NULL;	m_pFile		= NULL;	m_pInfo		= NULL;	m_pStruct	= NULL;} /* Initialize() */BOOL MyPNG::CloseFile(){			if( m_pStruct != NULL | | m_pInfo != NULL )	{		// Destroy created PNG structs.		png_destroy_read_struct( &m_pStruct, &m_pInfo, NULL );		m_pStruct = NULL;		m_pInfo = NULL;	}	if( m_pFile != NULL )	{		// Close open file.		fclose( m_pFile );		m_pFile = NULL;	}	else	{		return FALSE;	}	return TRUE;} /* CloseFile() */void MyPNG:    oCleanUp(){	CloseFile();			if( m_hbm )	{		// Does deleting an object also free the memory		// allocated to the bitmap pointer?  If not, this		// causes a serious memory leak because I'm too		// lazy to check it out and free it myself. :P		DeleteObject( m_hbm );		m_hbm = NULL;	}	Initialize();} /* DoCleanUp() */BOOL MyPNG::IsPNG(){	unsigned char buf[ MYPNG_BYTES_TO_CHECK ];	if( m_pFile == NULL )	{		// No file currently open.		return FALSE;	}	if( fread( buf, sizeof( unsigned char ), MYPNG_BYTES_TO_CHECK,		m_pFile ) != MYPNG_BYTES_TO_CHECK )	{		return FALSE;	}	if( png_sig_cmp( buf, ( png_size_t )0, MYPNG_BYTES_TO_CHECK ) == 1 )	{		// File signature match failed.  Not a PNG file.		return FALSE;	}	return TRUE;} /* IsPNG */MYPNG_STATUS MyPNG::MakeDIB( LPCSTR filename ){	MYPNG_STATUS retval;	// First open the file and extract header data.	retval = OpenFile( filename );	if( retval != MYPNG_OK )	{		DoCleanUp();		return retval;	}	if( !ConvertToDIB() )	{		DoCleanUp();		return MYPNGERR_NOTENOUGHMEMORY;	}	// The DIB can now be blitted onto a compatible	// surface.	return MYPNG_OK;} /* MakeDIB() */MYPNG_STATUS MyPNG::OpenFile( LPCSTR filename ){		/* Note that this is largely a copy of the		example file included in libpng. */#ifndef MYPNG_STRIP_ALPHA	png_color_16 my_background, *image_background;#endif	if( m_pFile != NULL | | m_hbm != NULL )	{		// File's already loaded.		DoCleanUp();		Initialize();	}	// Open the proposed file for reading.	m_pFile = fopen( filename, "rb" );	if( m_pFile == NULL )	{		// Couldn't access the file.		return MYPNGERR_FILECOULDNTBEACCESSED;	}	// Check if the file's a PNG file.	if( !this->IsPNG() )	{		return MYPNGERR_NOTAPNG;	}   // Create the PNG stuctures.	m_pStruct = png_create_read_struct( PNG_LIBPNG_VER_STRING,		NULL, NULL, NULL );	if ( m_pStruct == NULL )	{		// Oh dear.		return MYPNGERR_NOTENOUGHMEMORY;	}	m_pInfo = png_create_info_struct( m_pStruct );	if( m_pInfo == NULL )	{		// Oh dear oh dear.		return MYPNGERR_NOTENOUGHMEMORY;	}	// Used for default error handling.  Er, I think. :-/	if( setjmp( m_pStruct->jmpbuf ) )	{		return MYPNGERR_NOTENOUGHMEMORY;	}	// We've read some of the signature bytes, so let's let them	// know.	png_set_sig_bytes( m_pStruct, MYPNG_BYTES_TO_CHECK );	// Initialize default I/O routines.	png_init_io( m_pStruct, m_pFile );	// Read information into the info structure.	png_read_info( m_pStruct, m_pInfo );	// Strip 16 bit/channel files down to 8 bits/channel.	png_set_strip_16( m_pStruct );	// Expand palette and grayscale information to RGB triplets.	switch( m_pInfo->color_type )	{	case PNG_COLOR_TYPE_PALETTE:	case PNG_COLOR_TYPE_GRAY:		png_set_expand( m_pStruct );		break;	}	// Expand packed colour information to their own	// bytes.	switch( m_pInfo->bit_depth )	{	case 1:	case 2:	case 4:		png_set_packing( m_pStruct );		break;	}	// Swap the R and B bits.  This is required for Windows DIBs.	png_set_bgr( m_pStruct );	if( png_get_valid( m_pStruct, m_pInfo, PNG_INFO_tRNS ) )	{		png_set_expand( m_pStruct );	}#ifdef MYPNG_STRIP_ALPHA	// Strip alpha values (not recommended).	png_set_strip_alpha( png_ptr );#else	// Combine alpha values with the background.	if( png_get_bKGD( m_pStruct, m_pInfo, ℑ_background ) )	{		// Image had its own background.		png_set_background( m_pStruct, image_background,			PNG_BACKGROUND_GAMMA_FILE, 1, 1.0 );	}	else	{		// No background provided.  We'll make one.		my_background.red = MYPNG_DEFAULT_BACKGROUND_RED;		my_background.blue = MYPNG_DEFAULT_BACKGROUND_BLUE;		my_background.green = MYPNG_DEFAULT_BACKGROUND_GREEN;		png_set_background( m_pStruct, &my_background,			PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0 );	}#endif	// Finally update the info struct.	png_read_update_info( m_pStruct, m_pInfo );	return MYPNG_OK;} /* OpenFile() */BOOL MyPNG::ConvertToDIB(){	// PNG image's scanline length in bytes.	png_uint_32 rowsize;	// The future DIB's scanline length.  DIBs are	// LONG-aligned with the unused bytes set to 0.	png_uint_32 realrowsize;	// Counter.	png_uint_32 row;	BITMAPINFOHEADER bmih;	// Pointer returned by CreateDIBSection() where	// we can write our data.	png_bytep OurDIB;	if( m_pStruct == NULL | | m_pInfo == NULL | | m_pFile == NULL )	{		// No file open. :-/		return FALSE;	}	// Get the scanline length.	rowsize = png_get_rowbytes( m_pStruct, m_pInfo );	realrowsize = rowsize + ( rowsize % sizeof( LONG ) );	// Fill in the BITMAPINFOHEADER	ZeroMemory( &bmih, sizeof( bmih ) );	bmih.biSize				 = sizeof( bmih );	bmih.biWidth			 = m_pInfo->width;	// DIB only displays top->down if the height is negative.	bmih.biHeight			 = -( int )m_pInfo->height;	bmih.biPlanes			 = 1;	bmih.biBitCount		 = m_pInfo->bit_depth * m_pInfo->channels;	bmih.biCompression	 = BI_RGB;	bmih.biSizeImage		 = 0;	bmih.biXPelsPerMeter  = 0;	bmih.biYPelsPerMeter  = 0;	bmih.biClrUsed        = 0;	bmih.biClrImportant   = 0;	// Create a DIB we can write to from the info in the header.	m_hbm = CreateDIBSection( NULL, ( BITMAPINFO * )&bmih, DIB_RGB_COLORS,		( void ** )&OurDIB, NULL, 0 );	if ( m_hbm == NULL )	{		// Well we screwed up somewhere.		return FALSE;	}	// Initialize the bitmap with zeros.  Yeah, I did this	// just because unused bytes are set to zero,	// and this is a HIDEOUS way to do it.  I just don't	// feel like rewriting it for the sake of this post.     	ZeroMemory( OurDIB, m_pInfo->height * realrowsize );	m_Width = m_pInfo->width;	m_Height = m_pInfo->height;	// Read (write) one row at a time.	for( row = 0; row < m_pInfo->height; row ++ )	{		png_read_rows( m_pStruct, &OurDIB, NULL, 1 );		OurDIB += realrowsize;	}	// Finish reading.	png_read_end( m_pStruct, m_pInfo );	return TRUE;} /* MakeDIB() */MYPNG_STATUS MyPNG::MakeCompatibleSurface( LPDIRECTDRAW4 lp_dd, LPDIRECTDRAWSURFACE4 *lp_surface ){	HRESULT retval;	HDC hdc_src, hdc_dest;	DDSURFACEDESC2 ddsd;	LPDIRECTDRAWSURFACE4 surf;	if( m_hbm == NULL )	{		// Oops!  No DIB to create from.		return MYPNGERR_DIBNOTCREATED;	}	// Set up the surface parameters.	ZeroMemory( &ddsd, sizeof( ddsd ) );	ddsd.dwSize = sizeof( ddsd );	ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;	ddsd.dwWidth = m_Width;	ddsd.dwHeight = m_Height;	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;	retval = lp_dd->CreateSurface( &ddsd, &surf, NULL );	if( FAILED( retval ) )	{		return MYPNGERR_COULDNTCREATESURFACE;	}	// Create a device context to blit from.	hdc_src = CreateCompatibleDC( NULL );	if( !hdc_src ) 	{		surf->Release();		surf = NULL;		return MYPNGERR_COULDNTGETSRCCONTEXT;	}	SelectObject( hdc_src, m_hbm );	// Get the context to blit onto.	surf->GetDC( &hdc_dest );	if( !hdc_dest )	{		DeleteDC( hdc_src );		surf->Release();		surf = NULL;		return MYPNGERR_COULDNTGETDESTCONTEXT;	}	if( !BitBlt( hdc_dest, 0, 0, m_Width, m_Height,						  hdc_src, 0, 0, SRCCOPY ) )	{		DeleteDC( hdc_src );		surf->ReleaseDC( hdc_dest );		surf->Release();		surf = NULL;		return MYPNGERR_COULDNTBLIT;	}	DeleteDC( hdc_src );	surf->ReleaseDC( hdc_dest );	*lp_surface = surf;	return MYPNG_OK;} /* MakeCompatibleSurface() */


[This message has been edited by foo (edited August 08, 1999).]

[This message has been edited by foo (edited August 08, 1999).]

[This message has been edited by foo (edited August 08, 1999).]

This topic is closed to new replies.

Advertisement