Can anyone tell me how to load PNG files onto a ddraw surface, i use MSVC6. Any help would be appreciated
How To Load PNG files on a ddraw surface
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).]