Previous parts
- Simple cross-platform game engine - Introduction
- Universal Box2D debug draw for OpenGL ES 1.x and OpenGL ES 2.0
Today I will describe how I managed loading of images for Win platform in my small cross-platform mobile engine. If you wonder why I support desktop Win in mobile engine read this: Simple cross-platform game engine - Introduction. But shortly: Win with OpenGL ES emulation is not primary target for me. It is convenient way how to get the game run fast, easy way how to share progress with team mates, comfortable debugging, ...
Before you can create texture in OpenGL you have to get the texture data somehow. As Android platform in my engine can handle both .png and .jpg I wanted to have the same in Win. Finally I got 2 ways working on my PC through:
- Microsoft WIC (Windows Imaging Component),
- FreeImage Open Source library
Microsoft WIC
WIC was the first way. The following routine will load image (.png, .jpg, ...) and return unsigned char* to raw RGBA data. You can take this data and create texture from it. You will also have to add library windowscodecs.lib.
1. convert char* file name to WCHAR
u8* AssetLoader::loadImage(s32 aIdx, u32& aWidth, u32& aHeight) { // convert string to wchar int len = strlen(mActualFileName); if (len + 1 > 256) LOGE("ERROR loading file - filename is loner than 256 characters"); // long enough WCHAR wFileName[256]; MultiByteToWideChar(0, 0, mActualFileName, -1, wFileName, 256);
2. prepare variables and initialize WIC Imaging Factory
HRESULT hr = S_OK; IWICImagingFactory* pImagingFactory = NULL; IWICBitmapDecoder* pIDecoder = NULL; IWICBitmapFrameDecode* pIDecoderFrame = NULL; IWICFormatConverter* pIFormatConverter = NULL; hr = CoCreateInstance( CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID*) &pImagingFactory);
3. create decoder and decode first frame of the image
// Create the decoder. if (SUCCEEDED(hr)) hr = pImagingFactory->CreateDecoderFromFilename( wFileName, // previously created filename NULL, // Do not prefer a particular vendor GENERIC_READ, // Desired read access to the file WICDecodeMetadataCacheOnDemand, // Cache metadata when needed &pIDecoder // pointer to created decoder ); // Retrieve the first bitmap frame. if (SUCCEEDED(hr)) hr = pIDecoder->GetFrame(0, &pIDecoderFrame);
4. create and initialize the convertor. We will use it to convert the image into RGBA format
// Create convertor. if (SUCCEEDED(hr)) hr = pImagingFactory->CreateFormatConverter(&pIFormatConverter); // Initialize the format converter. if (SUCCEEDED(hr)) hr = pIFormatConverter->Initialize( pIDecoderFrame, // Input source to convert GUID_WICPixelFormat32bppPRGBA, // Destination pixel format WICBitmapDitherTypeNone, // Specified dither pattern NULL, // Specify a particular palette 0.f, // Alpha threshold WICBitmapPaletteTypeCustom // Palette translation type );
5. convert the image and copy its pixels
// get pixels in RGBA format u8* buffer = NULL; if (SUCCEEDED(hr)) { pIFormatConverter->GetSize(&aWidth, &aHeight); buffer = new u8[aWidth * aHeight * 4]; pIFormatConverter->CopyPixels(0, aWidth * 4, aWidth * aHeight * 4, buffer); }
6. clean and return
pIFormatConverter->Release(); pIDecoderFrame->Release(); pIDecoder->Release(); return buffer; }
In this way the loading of images worked fine for me. But our graphician got only black screen. And you will agree that black screen for someone whou should see how his graphics looks in game like is not good at all. It seems that WIC is supported from Windows XP SP3. And "something missing" is probably the reason why it returned only black screen. As I had no opportunity to test on his computer I started to look for something that is less dependant on windows versions. I found open source library FreeImage.
FreeImage
FreeImage has very clearly and well written documentation with lot of examples. The library is capable of many things but my only target was to load image (regardless the format at the best). So, to make FreeImage work download the header, .lib file and .dll. Set path to header and to FreeImage.lib file.
1. add header and create variables
#include <FreeImage.h> u8* AssetLoader::loadImage(s32 aIdx, u32& aWidth, u32& aHeight) { FIBITMAP* bitmap = NULL; FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
2. load the image. This part was directly taken from documentation with copy&paste (just very slightly adjusted). You can see it is very simple to load image while checking lot of situations
// check the file signature and deduce its format // (the second argument is currently not used by FreeImage) fif = FreeImage_GetFileType(mActualFileName, 0); if(fif == FIF_UNKNOWN) { // no signature ? // try to guess the file format from the file extension fif = FreeImage_GetFIFFromFilename(mActualFileName); } // check that the plugin has reading capabilities ... if((fif != FIF_UNKNOWN) && FreeImage_FIFSupportsReading(fif)) { // ok, let's load the file bitmap = FreeImage_Load(fif, mActualFileName, 0); // unless a bad file format, we are done ! if (!bitmap) { LOGE("loading bitmap %s failed", mActualFileName); return NULL; } }
3. convert image into 32bpp RGBA format
// convert to 32bpp FIBITMAP* bitmap32 = FreeImage_ConvertTo32Bits(bitmap); // FreeImage bitmaps are always upside down FreeImage_FlipVertical(bitmap32); //retrieve the image data and get the image width and height RGBQUAD* bits = (RGBQUAD*) FreeImage_GetBits(bitmap32); aWidth = FreeImage_GetWidth(bitmap32); aHeight = FreeImage_GetHeight(bitmap32); // check results if((bits == NULL) || (aWidth <= 0) || (aHeight <= 0)) { LOGE("bitmap is somehow corrupted (width=%i, height=%i, bits=%i)", aWidth, aHeight, bits); return NULL; }
4. switch red and blue color
u8* buffer = new u8[aWidth * aHeight * 4]; RGBQUAD* dest = (RGBQUAD*) buffer; for(u32 i = 0; i < aHeight; i++) { for (u32 j = 0; j < aWidth; j++) { RGBQUAD colorQuad = *(bits ++); // swap red and blue BYTE rgbTemp = colorQuad.rgbBlue; colorQuad.rgbBlue = colorQuad.rgbRed; colorQuad.rgbRed = rgbTemp; *(dest ++) = colorQuad; } }
5. clean and return data
// clean FreeImage_Unload(bitmap); FreeImage_Unload(bitmap32); return buffer; }
When first compiling I had problem saying that RGBQUAD is not defined. RGBQUAD is structure defined in wingdi.h which is included in windows.h. As I have NOGDI defined in my project (for some reasons) this structure was undefined. I had to alter slightly the FreeImage.h header file to overcome this. This part ...
:
:
typedef unsigned __int64 UINT64; #endif // _MSC_VER #if (defined(_WIN32) || defined(__WIN32__)) #pragma pack(push, 1) #else #pragma pack(1) #endif // WIN32 typedef struct tagRGBQUAD { #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
:
:
... was changed to this:
: : typedef unsigned __int64 UINT64; #endif // _MSC_VER #endif // _WINDOWS_ ******* #if (defined(_WIN32) || defined(__WIN32__)) #pragma pack(push, 1) #else #pragma pack(1) #endif // WIN32 #if !defined(_WINDOWS_) || (defined(_WINDOWS_) && defined(NOGDI)) ******* typedef struct tagRGBQUAD { #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR : :
So, here are two ways how you can get image data for your textures. Next time I will write how is this achieved for Android platform in my engine using NDK and JNI.
No comments:
Post a Comment