1
0
mirror of https://github.com/DxWnd/DxWnd.reloaded synced 2024-12-30 09:25:35 +01:00
gho tik 46fe2cb28f v2_02_76_src
Former-commit-id: e638d38fb9407ba82b1423e5596ccadd84aaecd3
2017-03-06 11:39:16 -05:00

553 lines
14 KiB
C++

#include "stdafx.h"
#include "bmpext.h"
//////////////////////////////////////////////////////////////////////////
// DIB support defines
#define BMIH_SIZE sizeof BITMAPINFOHEADER
#define BMIF_SIZE sizeof BITMAPFILEHEADER
/* DIB constants */
#define PALVERSION 0x300
/* Dib Header Marker - used in writing DIBs to files */
#define DIB_HEADER_MARKER ((WORD) ('M' << 8) | 'B')
// WIDTHBYTES performs DWORD-aligning of DIB scanlines. The "bits"
// parameter is the bit count for the scanline (biWidth * biBitCount),
// and this macro returns the number of DWORD-aligned bytes needed
// to hold those bits.
#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)
/////////////////////////////////////////////////////////////////////////////
//CDib implementation
CDib::CDib() : m_pBMI(0), m_pBits(0), m_pPalette(0)
{}
CDib::~CDib()
{
Free();
}
DWORD CDib::Width() const
{
if (!m_pBMI)
return 0;
/* return the DIB width */
return m_pBMI->bmiHeader.biWidth;
}
DWORD CDib::Height() const
{
if (!m_pBMI)
return 0;
/* return the DIB height */
return m_pBMI->bmiHeader.biHeight;
}
WORD CDib::NumColors( BITMAPINFOHEADER& bmiHeader ) const
{
if ( bmiHeader.biClrUsed != 0)
return (WORD)bmiHeader.biClrUsed;
switch ( bmiHeader.biBitCount )
{
case 1:
return 2;
case 4:
return 16;
case 8:
return 256;
default:
return 0;
}
}
BOOL CDib::IsValid() const
{
return (m_pBits != NULL);
}
DWORD CDib::PaletteSize() const
{
return NumColors( m_pBMI->bmiHeader ) * sizeof(RGBQUAD);
}
BOOL CDib::Draw(CDC* pDC, CRect& rectDC, CRect& rectDIB) const
{
if ( !IsValid() )
return FALSE;
CPalette* pOldPal = NULL; // Previous palette
// Get the DIB's palette, then select it into DC
if (m_pPalette != NULL)
{
// Select as background since we have
// already realized in forground if needed
pOldPal = pDC->SelectPalette( m_pPalette, TRUE);
}
/* Make sure to use the stretching mode best for color pictures */
pDC->SetStretchBltMode( COLORONCOLOR );
/* Determine whether to call StretchDIBits() or SetDIBitsToDevice() */
BOOL bSuccess;
if( ( rectDC.Width() == rectDIB.Width() ) &&
( rectDC.Height() == rectDIB.Height() ) )
bSuccess = ::SetDIBitsToDevice(pDC->m_hDC, // hDC
rectDC.left, // DestX
rectDC.top, // DestY
rectDC.Width(), // nDestWidth
rectDC.Height(), // nDestHeight
rectDIB.left, // SrcX
(int)Height() -
rectDIB.top -
rectDIB.Height(), // SrcY
0, // nStartScan
(WORD)Height(), // nNumScans
m_pBits, // lpBits
m_pBMI, // lpBitsInfo
DIB_RGB_COLORS); // wUsage
else
bSuccess = ::StretchDIBits(pDC->m_hDC, // hDC
rectDC.left, // DestX
rectDC.top, // DestY
rectDC.Width(), // nDestWidth
rectDC.Height(), // nDestHeight
rectDIB.left, // SrcX
rectDIB.top, // SrcY
rectDIB.Width(), // wSrcWidth
rectDIB.Height(), // wSrcHeight
m_pBits, // lpBits
m_pBMI, // lpBitsInfo
DIB_RGB_COLORS, // wUsage
SRCCOPY); // dwROP
/* Reselect old palette */
if (pOldPal != NULL)
{
pDC->SelectPalette( pOldPal, TRUE);
}
return bSuccess;
}
void CDib::AssertPosition(int iX, int iY)
{
if( (iX < 0) || (iX > m_pBMI->bmiHeader.biWidth - 1) ||
(iY < 0) || (iY > m_pBMI->bmiHeader.biHeight - 1) )
{
//invalid image pixel position
CDibException::Throw( CDibException::E_INVPOS );
}
}
RGBQUAD CDib::GetPixel(int iX, int iY)
{
RGBQUAD rgbResult;
WORD wDummy;
//takeinto account that DIBit raws are reversed vertically
iY = (m_pBMI->bmiHeader.biHeight - 1) - iY; // GHO fix
//iY = m_pBMI->bmiHeader.biHeight - iY;
//assert pixel position
AssertPosition( iX, iY );
//access the destination pixel
int nRowBytes = m_pBMI->bmiHeader.biWidth * m_pBMI->bmiHeader.biBitCount;
nRowBytes = ( (nRowBytes + 31) & (~31) ) / 8;
switch( m_pBMI->bmiHeader.biBitCount )
{
case 1: //Monochrome
rgbResult = m_pBMI->bmiColors[ *(m_pBits + nRowBytes*iY + iX/8) & (0x80 >> iX%8) ];
break;
case 4:
rgbResult = m_pBMI->bmiColors[ *(m_pBits + nRowBytes*iY + iX/2) & ((iX&1) ? 0x0f : 0xf0) ];
break;
case 8:
rgbResult = m_pBMI->bmiColors[ *(m_pBits + nRowBytes*iY + iX) ];
break;
case 16:
wDummy = *(LPWORD)(m_pBits + nRowBytes*iY + iX*2);
rgbResult.rgbBlue = (BYTE)(0x001F & wDummy);
rgbResult.rgbGreen = (BYTE)(0x001F & (wDummy >> 5));
rgbResult.rgbRed = (BYTE)(0x001F & wDummy >> 10 );
break;
case 24:
rgbResult = *(LPRGBQUAD)(m_pBits + nRowBytes*iY + iX*3);
break;
case 32:
rgbResult = *(LPRGBQUAD)(m_pBits + nRowBytes*iY + iX*4);
break;
}
return rgbResult;
}
void CDib::SetPixel(int iX, int iY, RGBQUAD &rgbPixel)
{
WORD wDummy;
//takeinto account that DIBit raws are reversed vertically
iY = (m_pBMI->bmiHeader.biHeight - 1) - iY; // GHO fix
//iY = m_pBMI->bmiHeader.biHeight - iY;
//assert pixel position
AssertPosition( iX, iY );
//access the destination pixel
int nRowBytes = m_pBMI->bmiHeader.biWidth * m_pBMI->bmiHeader.biBitCount;
nRowBytes = ( (nRowBytes + 31) & (~31) ) / 8;
switch( m_pBMI->bmiHeader.biBitCount )
{
case 1:
case 4:
case 8:
//do not support this operation;
CDibException::Throw( CDibException::E_NOTSUPP );
break;
case 16:
wDummy = rgbPixel.rgbRed;
wDummy = wDummy << 5;
wDummy |= rgbPixel.rgbGreen;
wDummy = wDummy << 5;
wDummy |= rgbPixel.rgbBlue;
*(LPWORD)(m_pBits + nRowBytes*iY + iX*2) = wDummy;
break;
case 24:
*(LPRGBQUAD)(m_pBits + nRowBytes*iY + iX*3) = rgbPixel;
break;
case 32:
*(LPRGBQUAD)(m_pBits + nRowBytes*iY + iX*4) = rgbPixel;
break;
}
}
DWORD CDib::Save(CFile& file) const
{
BITMAPFILEHEADER bmfHdr; // Header for Bitmap file
DWORD dwDIBSize;
if (m_pBMI == NULL)
return 0;
// Fill in the fields of the file header
// Fill in file type (first 2 bytes must be "BM" for a bitmap)
bmfHdr.bfType = DIB_HEADER_MARKER; // "BM"
// Calculating the size of the DIB is a bit tricky (if we want to
// do it right). The easiest way to do this is to call GlobalSize()
// on our global handle, but since the size of our global memory may have
// been padded a few bytes, we may end up writing out a few too
// many bytes to the file (which may cause problems with some apps).
//
// So, instead let's calculate the size manually (if we can)
//
// First, find size of header plus size of color table. Since the
// first DWORD in both BITMAPINFOHEADER and BITMAPCOREHEADER conains
// the size of the structure, let's use this.
dwDIBSize = m_pBMI->bmiHeader.biSize + PaletteSize(); // Partial Calculation
// Now calculate the size of the image
if ((m_pBMI->bmiHeader.biCompression == BI_RLE8) || (m_pBMI->bmiHeader.biCompression == BI_RLE4))
{
// It's an RLE bitmap, we can't calculate size, so trust the
// biSizeImage field
dwDIBSize += m_pBMI->bmiHeader.biSizeImage;
}
else
{
DWORD dwBmBitsSize; // Size of Bitmap Bits only
// It's not RLE, so size is Width (DWORD aligned) * Height
dwBmBitsSize = WIDTHBYTES((m_pBMI->bmiHeader.biWidth)*((DWORD)m_pBMI->bmiHeader.biBitCount)) * m_pBMI->bmiHeader.biHeight;
dwDIBSize += dwBmBitsSize;
// Now, since we have calculated the correct size, why don't we
// fill in the biSizeImage field (this will fix any .BMP files which
// have this field incorrect).
m_pBMI->bmiHeader.biSizeImage = dwBmBitsSize;
}
// Calculate the file size by adding the DIB size to sizeof(BITMAPFILEHEADER)
bmfHdr.bfSize = dwDIBSize + BMIF_SIZE;
bmfHdr.bfReserved1 = 0;
bmfHdr.bfReserved2 = 0;
/*
* Now, calculate the offset the actual bitmap bits will be in
* the file -- It's the Bitmap file header plus the DIB header,
* plus the size of the color table.
*/
bmfHdr.bfOffBits = BMIF_SIZE + m_pBMI->bmiHeader.biSize + PaletteSize();
// Write the file header
file.Write( (LPSTR)&bmfHdr, BMIF_SIZE );
DWORD dwBytesSaved = BMIF_SIZE;
// Write the DIB header
UINT nCount = sizeof(BITMAPINFO) + PaletteSize();
dwBytesSaved += nCount;
file.Write(m_pBMI, nCount);
// Write the DIB bits
DWORD dwBytes = m_pBMI->bmiHeader.biBitCount * Width();
// Calculate the number of bytes per line
if (dwBytes%32 == 0)
dwBytes /= 8;
else
dwBytes = dwBytes/8 + (32-dwBytes%32)/8 + (((32-dwBytes%32)%8 > 0) ? 1 : 0);
nCount = dwBytes * Height();
dwBytesSaved += nCount;
//file.WriteHuge(m_pBits, nCount);
file.Write(m_pBits, nCount);
return dwBytesSaved;
}
DWORD CDib::Read(CFile& file, BOOL bFromResource)
{
DWORD dwReadBytes = 0;
DWORD dwLength = (DWORD)file.GetLength();
// Ensures no memory leaks will occur
Free();
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bmiHeader;
if( !bFromResource )
{
// Go read the DIB file header and check if it's valid.
if( (dwReadBytes = file.Read((LPSTR)&bmfHeader, BMIF_SIZE)) != BMIF_SIZE)
return 0;
if(bmfHeader.bfType != DIB_HEADER_MARKER)
return 0;
}
// Read DIB header.
if( file.Read( &bmiHeader, BMIH_SIZE ) != BMIH_SIZE )
return 0;
dwReadBytes += BMIH_SIZE;
DWORD dwPalSize = NumColors( bmiHeader ) * sizeof RGBQUAD;
m_pBMI = (LPBITMAPINFO) new BYTE[BMIH_SIZE + dwPalSize];
memcpy( m_pBMI, &bmiHeader, BMIH_SIZE );
// read palette data
if( file.Read( m_pBMI->bmiColors, dwPalSize ) != dwPalSize )
return 0;
dwReadBytes += dwPalSize;
CreatePalette();
// Go read the bits.
m_pBits = new BYTE[ dwLength - dwReadBytes + 0x200]; // GHO fix: you need some more space who knows why...?
if (m_pBits == 0)
return 0;
if (file.Read( m_pBits, dwLength - dwReadBytes ) != (dwLength - dwReadBytes))
{
delete m_pBMI;
m_pBMI = NULL;
delete m_pBits;
m_pBits = NULL;
return 0;
}
dwReadBytes = dwLength;
return dwReadBytes;
}
DWORD CDib::ReadFromResource(UINT nResID)
{
DWORD dwResult = 0;
// Load from resource
HRSRC hbmres = FindResource( NULL, MAKEINTRESOURCE(nResID), RT_BITMAP );
CMemFile file;
HGLOBAL hGlob;
if (hbmres)
{
DWORD dwResSize = SizeofResource( NULL, hbmres );
file.Attach( (LPBYTE)LockResource( hGlob = LoadResource(NULL, hbmres) ), dwResSize );
dwResult = Read(file, TRUE);
file.Detach();
DeleteObject( hGlob );
}
return dwResult;
}
void CDib::Invalidate()
{
Free();
}
BOOL CDib::CreatePalette()
{
if ( !IsValid() )
return FALSE;
//get the number of colors in the DIB
WORD wNumColors = NumColors( m_pBMI->bmiHeader );
BOOL bResult = TRUE;
if (wNumColors != 0)
{
// allocate memory block for logical palette
LPLOGPALETTE pLogPal = (LPLOGPALETTE) new BYTE[ sizeof(LOGPALETTE) +
sizeof(PALETTEENTRY)*wNumColors ];
// if not enough memory, clean up and return NULL
if( pLogPal == 0 )
return FALSE;
// set version and number of palette entries
pLogPal->palVersion = PALVERSION;
pLogPal->palNumEntries = wNumColors;
for (WORD i = 0; i < wNumColors; i++)
{
pLogPal->palPalEntry[i].peRed = m_pBMI->bmiColors[i].rgbRed;
pLogPal->palPalEntry[i].peGreen = m_pBMI->bmiColors[i].rgbGreen;
pLogPal->palPalEntry[i].peBlue = m_pBMI->bmiColors[i].rgbBlue;
pLogPal->palPalEntry[i].peFlags = 0;
}
// create the palette and get handle to it
if (m_pPalette)
{
m_pPalette->DeleteObject();
delete m_pPalette;
}
m_pPalette = new CPalette;
bResult = m_pPalette->CreatePalette( pLogPal );
delete pLogPal;
}
return bResult;
}
void CDib::Free()
{
// Make sure all member data that might have been allocated is freed.
if(m_pBMI)
{
delete m_pBMI;
m_pBMI = NULL;
}
if(m_pBits)
{
delete m_pBits;
m_pBits = NULL;
}
if(m_pPalette)
{
m_pPalette->DeleteObject();
delete m_pPalette;
m_pPalette = NULL;
}
}
HBITMAP CDib::CreateDDBitmap( HDC hDC )
{
HBITMAP hBitmap = ::CreateDIBitmap( hDC, &m_pBMI->bmiHeader,
CBM_INIT, m_pBits, (LPBITMAPINFO)m_pBMI, DIB_RGB_COLORS);
ASSERT(hBitmap);
return hBitmap;
}
HBITMAP CDib::CreateDDBitmap(CDC* pDC)
{
return CreateDDBitmap( pDC->GetSafeHdc() );
}
BOOL CDib::Compress(CDC* pDC, BOOL bCompress )
{
// 1. makes GDI bitmap from existing DIB
// 2. makes a new DIB from GDI bitmap with compression
// 3. cleans up the original DIB
// 4. puts the new DIB in the object
if((m_pBMI->bmiHeader.biBitCount != 4) && (m_pBMI->bmiHeader.biBitCount != 8)) return FALSE;
// compression supported only for 4 bpp and 8 bpp DIBs
TRACE(_T("Compress: original palette size = %d\n"), NumColors(m_pBMI->bmiHeader) );
HDC hdc = pDC->GetSafeHdc();
CPalette* pOldPalette = pDC->SelectPalette( m_pPalette, TRUE);
HBITMAP hBitmap; // temporary
if((hBitmap = CreateDDBitmap(pDC)) == NULL) return FALSE;
int nSize = BMIF_SIZE + PaletteSize();
LPBITMAPINFO pBMI = (LPBITMAPINFO) new char[nSize];
memcpy(pBMI, &m_pBMI->bmiHeader, nSize); // new header
if(bCompress) {
switch (pBMI->bmiHeader.biBitCount) {
case 4:
pBMI->bmiHeader.biCompression = BI_RLE4;
break;
case 8:
pBMI->bmiHeader.biCompression = BI_RLE8;
break;
default:
ASSERT(FALSE);
}
// calls GetDIBits with null data pointer to get size of compressed DIB
if(!::GetDIBits(pDC->m_hDC, hBitmap, 0, (UINT) pBMI->bmiHeader.biHeight,
NULL, pBMI, DIB_RGB_COLORS)) {
AfxMessageBox(_T("Unable to compress this DIB"));
// probably a problem with the color table
::DeleteObject(hBitmap);
delete[] pBMI;
pDC->SelectPalette( pOldPalette, TRUE);
return FALSE;
}
if (pBMI->bmiHeader.biSizeImage == 0) {
AfxMessageBox(_T("Driver can't do compression"));
::DeleteObject(hBitmap);
delete[] pBMI;
pDC->SelectPalette( pOldPalette, TRUE);
return FALSE;
}
else {
m_pBMI->bmiHeader.biSizeImage = pBMI->bmiHeader.biSizeImage;
}
}
else {
pBMI->bmiHeader.biCompression = BI_RGB; // decompress
// figure the image size from the bitmap width and height
DWORD dwBytes = ((DWORD) pBMI->bmiHeader.biWidth * pBMI->bmiHeader.biBitCount) / 32;
if(((DWORD) pBMI->bmiHeader.biWidth * pBMI->bmiHeader.biBitCount) % 32) {
dwBytes++;
}
dwBytes *= 4;
m_pBMI->bmiHeader.biSizeImage = dwBytes * pBMI->bmiHeader.biHeight; // no compression
pBMI->bmiHeader.biSizeImage = m_pBMI->bmiHeader.biSizeImage;
}
// second GetDIBits call to make DIB
LPBYTE lpImage = (LPBYTE) new char[m_pBMI->bmiHeader.biSizeImage];
VERIFY(::GetDIBits(pDC->m_hDC, hBitmap, 0, (UINT) pBMI->bmiHeader.biHeight,
lpImage, pBMI, DIB_RGB_COLORS));
TRACE(_T("dib successfully created - height = %d\n"), pBMI->bmiHeader.biHeight);
::DeleteObject(hBitmap);
Free();
m_pBMI = pBMI;
m_pBits = lpImage;
CreatePalette();
pDC->SelectPalette( pOldPalette, TRUE );
TRACE(_T("Compress: new palette size = %d\n"), NumColors(m_pBMI->bmiHeader) );
return TRUE;
}