[AD] GDI+ image routines

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


Attached are implementations of al_(load/save)_gdiplus_bitmap(_f)
functions that could be used to handle PNG, JPEG, GIF, TIFF, and BMP
files. (BMP is probably best handled by Allegro natively.)

The GDI+ runtime is standard with XP and available for 98 and 2000.
Requires the the same SDK that is already required for D3D9.

There are reports that it works with some effort on MinGW, but I
haven't verified.

--
Matthew Leverton
#include <iostream>
#include <objidl.h>
#include <gdiplus.h>

#include "allegro5/allegro.h"

/* Source:
 *   http://msdn.microsoft.com/en-us/library/ms533843%28VS.85%29.aspx
 */
static int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
   UINT  num = 0;          // number of image encoders
   UINT  size = 0;         // size of the image encoder array in bytes

   Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;

   Gdiplus::GetImageEncodersSize(&num, &size);
   if(size == 0)
      return -1;  

   pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
   if(pImageCodecInfo == NULL)
      return -1;  

   GetImageEncoders(num, size, pImageCodecInfo);

   for(UINT j = 0; j < num; ++j)
   {
      if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
      {
         *pClsid = pImageCodecInfo[j].Clsid;
         free(pImageCodecInfo);
         return j;
      }    
   }

   free(pImageCodecInfo);
   return -1;  
}

/* A wrapper around an already opened ALLEGRO_FILE* pointer
 */
class AllegroWindowsStream : public IStream 
{
	long refCount;
	ALLEGRO_FILE *fp;

public:
	/* Create a stream from an open file handle */
	AllegroWindowsStream(ALLEGRO_FILE *fp) : fp(fp), refCount(1)
	{
	}

	// IUnknown
	virtual ULONG STDMETHODCALLTYPE AddRef(void)
	{
		return (ULONG) InterlockedIncrement(&refCount);  
	}

	virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject)
	{
		if (iid == __uuidof(IUnknown) || iid == __uuidof(ISequentialStream) || iid == __uuidof(IStream))
		{
			*ppvObject = static_cast<IStream*>(this);
			AddRef();
			return S_OK;
		}
		else
			return E_NOINTERFACE;
	}

	virtual ULONG STDMETHODCALLTYPE Release(void)
	{
		ULONG ret = InterlockedDecrement(&refCount);
		if( ret == 0) 
		{ 
			delete this; 
			return 0; 
		}
		return ret; 
	}

	// ISequentialStream
	virtual HRESULT STDMETHODCALLTYPE Read(void *pv, ULONG cb, ULONG *pcbRead)
	{
		size_t read = al_fread(fp, pv, cb);
		if (pcbRead) *pcbRead = read;
		return read == cb ? S_OK : S_FALSE;
	}

	virtual HRESULT STDMETHODCALLTYPE Write(const void *pv, ULONG cb, ULONG *pcbWritten)
	{
		size_t written = al_fwrite(fp, pv, cb);
		if (pcbWritten) *pcbWritten = written;
		return written == cb ? S_OK : STG_E_CANTSAVE; 
	}
    
	// IStream
	virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
	{
		ALLEGRO_SEEK o;
		if (dwOrigin == STREAM_SEEK_SET)
			o = ALLEGRO_SEEK_SET;
		else if (dwOrigin == STREAM_SEEK_CUR)
			o = ALLEGRO_SEEK_CUR; 
		else 
			o = ALLEGRO_SEEK_END;
		
		bool ret = al_fseek(fp, dlibMove.QuadPart, o);

		if (plibNewPosition)
		{
			int64_t pos = al_ftell(fp);
			if (pos == -1)
				return STG_E_INVALIDFUNCTION;

			plibNewPosition->QuadPart = pos;
		}

		return ret ? S_OK : STG_E_INVALIDFUNCTION;
	}

	/* The GDI+ image I/O methods need to know the file size */
	virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG *pstatstg, DWORD grfStatFlag)
	{		
		(void) grfStatFlag;
		memset(pstatstg, 0, sizeof(*pstatstg));
		pstatstg->type = STGTY_STREAM;
		pstatstg->cbSize.QuadPart = al_fsize(fp); 
		return S_OK;
	}

	/* The following IStream methods aren't needed */
	virtual HRESULT STDMETHODCALLTYPE Clone(IStream **ppstm)
	{
		(void) ppstm;
		return E_NOTIMPL;
	}

    virtual HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags)
	{
		(void) grfCommitFlags;
		return E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE CopyTo (IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
	{
		(void) pstm, cb, pcbRead, pcbWritten;
		return E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
	{
		(void) libOffset, cb, dwLockType;
		return E_NOTIMPL;
	}
    
    virtual HRESULT STDMETHODCALLTYPE Revert()
	{
		return E_NOTIMPL;
	}

	virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize)
	{ 
		(void) libNewSize;
		return E_NOTIMPL;
	}
    
    virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
	{
		(void) libOffset, cb, dwLockType;
		return E_NOTIMPL;
	}
};

/* Function: al_load_gdiplus_bitmap_f
 */
ALLEGRO_BITMAP *al_load_gdiplus_bitmap_f(ALLEGRO_FILE *fp)
{
	AllegroWindowsStream *s = new AllegroWindowsStream(fp);
	if (!s) return NULL;

	ALLEGRO_BITMAP *a_bmp = NULL;	

	Gdiplus::Bitmap *gdi_bmp = Gdiplus::Bitmap::FromStream(s, false);
	if (gdi_bmp)
	{
		const uint32_t w = gdi_bmp->GetWidth(), h = gdi_bmp->GetHeight();

		a_bmp = al_create_bitmap(w, h);
		if (a_bmp)
		{
			Gdiplus::Rect rect(0, 0, w, h);
			Gdiplus::BitmapData *gdi_lock = new Gdiplus::BitmapData();

			if (!gdi_bmp->LockBits(&rect, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, gdi_lock))
			{
				ALLEGRO_LOCKED_REGION *a_lock = al_lock_bitmap(a_bmp, ALLEGRO_PIXEL_FORMAT_ARGB_8888, ALLEGRO_LOCK_WRITEONLY);

				if (a_lock)
				{
					unsigned char *in = (unsigned char *)gdi_lock->Scan0;
					unsigned char *out = (unsigned char *)a_lock->data;
					uint32_t rows = h;
					while (rows--)
					{
						memcpy(out, in, w * 4);
						in += gdi_lock->Stride;
						out += a_lock->pitch;
					}

					al_unlock_bitmap(a_bmp);
				}

				gdi_bmp->UnlockBits(gdi_lock);
			}			
			delete gdi_lock;
		}
		delete gdi_bmp;
	}

	s->Release();

	return a_bmp;
}

/* Function: al_load_gdiplus_bitmap
 */
ALLEGRO_BITMAP *al_load_gdiplus_bitmap(const char *filename)
{
	ALLEGRO_BITMAP *bmp = NULL;
	ALLEGRO_FILE *fp;
	
	fp = al_fopen(filename, "rb");		
	if (fp)
	{
		bmp = al_load_gdiplus_bitmap_f(fp);
		al_fclose(fp);
	}

	return bmp;
}

/* Function: al_save_gdiplus_bitmap_f
 */
bool al_save_gdiplus_bitmap_f(ALLEGRO_FILE *fp, const char *ident, ALLEGRO_BITMAP *a_bmp)
{
	CLSID encoder;
	int encoder_status = -1;

	if (!strcmp(ident, ".bmp"))
		encoder_status = GetEncoderClsid(L"image/bmp", &encoder);
	else if (!strcmp(ident, ".jpg") || !strcmp(ident, ".jpeg"))
		encoder_status = GetEncoderClsid(L"image/jpeg", &encoder);
	else if (!strcmp(ident, ".gif"))
		encoder_status = GetEncoderClsid(L"image/gif", &encoder);
	else if (!strcmp(ident, ".tif") || !strcmp(ident, ".tiff"))
		encoder_status = GetEncoderClsid(L"image/tiff", &encoder);
	else if (!strcmp(ident, ".png"))
		encoder_status = GetEncoderClsid(L"image/png", &encoder);

	if (encoder_status == -1)
		return false;    

	AllegroWindowsStream *s = new AllegroWindowsStream(fp);
	if (!s) return false;

	const int w = al_get_bitmap_width(a_bmp), h = al_get_bitmap_height(a_bmp);
	bool ret = false;

	Gdiplus::Bitmap *gdi_bmp = new Gdiplus::Bitmap(w, h, PixelFormat32bppARGB);

	if (gdi_bmp)
	{
		Gdiplus::Rect rect(0, 0, w, h);
		Gdiplus::BitmapData *gdi_lock = new Gdiplus::BitmapData();

		if (!gdi_bmp->LockBits(&rect, Gdiplus::ImageLockModeWrite, PixelFormat32bppARGB, gdi_lock))
		{
			ALLEGRO_LOCKED_REGION *a_lock = al_lock_bitmap(a_bmp, ALLEGRO_PIXEL_FORMAT_ARGB_8888, ALLEGRO_LOCK_READONLY);

			if (a_lock)
			{
				unsigned char *in = (unsigned char *)a_lock->data;
				unsigned char *out = (unsigned char *)gdi_lock->Scan0;
				
				uint32_t rows = h;
				while (rows--)
				{
					memcpy(out, in, w * 4);
					in += a_lock->pitch;
					out += gdi_lock->Stride;				
				}

				al_unlock_bitmap(a_bmp);
			}
			gdi_bmp->UnlockBits(gdi_lock);
		}
				
		ret = (gdi_bmp->Save(s, &encoder) == 0);

		delete gdi_lock;		
		delete gdi_bmp;
	}

	s->Release();

	return ret;
}

/* Function: al_save_gdiplus_bitmap
 */
bool al_save_gdiplus_bitmap(const char *filename, ALLEGRO_BITMAP *bmp)
{
	ALLEGRO_FILE *fp;
	bool ret = false;

	fp = al_fopen(filename, "wb");
	if (fp)
	{
		ALLEGRO_PATH *path = al_create_path(filename);
		if (path)
		{
			ret = al_save_gdiplus_bitmap_f(fp, al_get_path_extension(path), bmp);
			al_destroy_path(path);
		}
		al_fclose(fp);
	}

	return ret;
}	



Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/