#include "stdafx.h"
#include "GDIUtil.h"

// DDBToDIB      - Creates a DIB from a DDB
// hbmp        - Device dependent bitmap
// dwCompression - Type of compression, default BI_RGB
// hpal          - Logical palette, default NULL

HANDLE GDIUtil::DDBToDIB( HBITMAP hbmp, DWORD dwCompression, HPALETTE hpal )
{
	if( hbmp == NULL )
		return NULL;

    // The function has no arg for bitfields
    if( dwCompression == BI_BITFIELDS )
        return NULL;

    // If a palette has not been supplied use defaul palette
	if( hpal == NULL )
        hpal = (HPALETTE) GetStockObject(DEFAULT_PALETTE);

    // Get bitmap information
    BITMAP				bm;
	::GetObject( hbmp, sizeof( bm ), &bm );
	
    BITMAPINFOHEADER	bi;
    // Initialize the bitmapinfoheader
	memset( &bi, 0, sizeof( bi ) );
    bi.biSize        = sizeof(BITMAPINFOHEADER);
    bi.biWidth       = bm.bmWidth;
    bi.biHeight      = bm.bmHeight;
    bi.biPlanes      = 1;
    bi.biBitCount    = (unsigned short)(bm.bmPlanes * bm.bmBitsPixel) ;
    bi.biCompression = dwCompression;
    bi.biSizeImage   = 0;
    bi.biXPelsPerMeter  = 0;
    bi.biYPelsPerMeter  = 0;
    bi.biClrUsed        = 0;
    bi.biClrImportant   = 0;
	
    // Compute the size of the  infoheader and the color table
    int nColors = 0;
    if(bi.biBitCount <= 8)
    {
        nColors = (1 << bi.biBitCount);
    }
    DWORD dwLen  = bi.biSize + nColors * sizeof(RGBQUAD);
	
    // We need a device context to get the DIB from
	CClientDC dc( NULL );
	GHandle hDIB;

	{
		LocalHPalette localPal( dc, hpal );
		dc.RealizePalette( );
	
		// Allocate enough memory to hold bitmapinfoheader and color table
	    HANDLE handle = GlobalAlloc(GMEM_MOVEABLE, dwLen);
		if( handle == NULL )
			return NULL;

		hDIB.Attach( handle );

		{
			GLock lock( hDIB );
			LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)(LPVOID)lock;

			//copy from it
			*lpbi = bi;
		
			// Call GetDIBits with a NULL lpBits param, so the device driver 
			// will calculate the biSizeImage field 
			GetDIBits( dc, 
				hbmp,
				0L, (DWORD)bi.biHeight,
				(LPBYTE)NULL, 
				(LPBITMAPINFO)lpbi, 
				(DWORD)DIB_RGB_COLORS );
		
			bi = *lpbi;
		
			// If the driver did not fill in the biSizeImage field, then compute it
			// Each scan line of the image is aligned on a DWORD (32bit) boundary
			if (bi.biSizeImage == 0)
			{
				bi.biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) 
				* bi.biHeight;
			
				// If a compression scheme is used the result may infact be larger
				// Increase the size to account for this.
				if (dwCompression != BI_RGB )
					bi.biSizeImage = (bi.biSizeImage * 3) / 2;
			}
		
			// Realloc the buffer so that it can hold all the bits
			dwLen += bi.biSizeImage;

			lock.Release( );

			if( !hDIB.ReAlloc( dwLen, GMEM_MOVEABLE ) )
				return NULL;

			lock.Attach( hDIB );
			lpbi = (LPBITMAPINFOHEADER)(LPVOID)lock;
			
			// FINALLY get the DIB
			BOOL bGotBits = GetDIBits( dc,
				hbmp,
				0L,                      // Start scan line
				(DWORD)bi.biHeight,      // # of scan lines
				(LPBYTE)lpbi             // address for bitmap bits
				+ (bi.biSize + nColors * sizeof(RGBQUAD)),
				(LPBITMAPINFO)lpbi,      // address of bitmapinfo
				(DWORD)DIB_RGB_COLORS);  // Use RGB for color table
			if( !bGotBits )
				return NULL;
		}
	}
	return hDIB.Detach( );
}

HANDLE GDIUtil::GrabDIB( CDC* pDC, CRect &rect )
{
	CBitmap bmp;
	CDC  memDC;
	memDC.CreateCompatibleDC( pDC );

	bmp.CreateCompatibleBitmap( pDC, rect.Width(), rect.Height() );

	CBitmap* old_bmp = memDC.SelectObject( &bmp );
	memDC.BitBlt( 0, 0, rect.Width(), rect.Height(), 
		pDC, 
		rect.left, rect.top, 
		SRCCOPY);

	memDC.SelectObject( old_bmp );
	return DDBToDIB( bmp, BI_RGB, NULL );
}
