// CRGBSurface.cpp - a wrapper to DIB section GDI object
//
// (c) W.Weyna 'Voytec'
#include "stdafx.h"
#include "OGLT.h"
#include "CRGBSurface.h"
#define ENABLE_JPEG
// JPEG compressor/decompressor
#ifdef ENABLE_JPEG
#include "jpeglib.h"
#include <setjmp.h>
#endif
#define DIB_MAGIC 0x4d42
#define SIZEOF_BITMAPFILEHEADER_PACKED ( \
sizeof(WORD) + /* bfType */ \
sizeof(DWORD) + /* bfSize */ \
sizeof(WORD) + /* bfReserved1 */ \
sizeof(WORD) + /* bfReserved2 */ \
sizeof(DWORD)) /* bfOffBits */
//******************************************************
//
// CRGBSurface()
//
//******************************************************
CRGBSurface::CRGBSurface()
{
m_hDIBSection = NULL;
m_pDIBits = NULL;
m_hDIBSectionOld = NULL;
m_pDC = NULL;
m_bSaveFile_FileExists = false;
}
//******************************************************
//
// ~CRGBSurface()
//
//******************************************************
CRGBSurface::~CRGBSurface()
{
DeleteDC();
Delete();
}
//******************************************************
//
// Delete()
//
//******************************************************
void CRGBSurface::Delete()
{
if(m_hDIBSectionOld && m_pDC)
{
::SelectObject(m_pDC->m_hDC, m_hDIBSectionOld);
m_hDIBSectionOld = NULL;
}
if(m_hDIBSection)
::DeleteObject(m_hDIBSection);
m_hDIBSection = NULL;
m_pDIBits = NULL;
}
//******************************************************
//
// DeleteDC()
//
//******************************************************
void CRGBSurface::DeleteDC()
{
if(m_pDC)
{
if(m_hDIBSectionOld)
{
::SelectObject(m_pDC->m_hDC, m_hDIBSectionOld);
m_hDIBSectionOld = NULL;
}
delete m_pDC;
}
m_pDC = NULL;
}
//******************************************************
//
// RecreateDC()
//
//******************************************************
bool CRGBSurface::RecreateDC()
{
if(m_pDC != NULL)
return true;
if(!m_hDIBSection)
{
ASSERT(0);
return false;
}
// If programme asserts here it means that
// you called DeleteDC() in your code and
// you probably shouldn't have done it, because
// we need a DC now. Remove a DeleteDC() from your code
// or remove an ASSERT(0) here if you want a DC to
// be recreated.
ASSERT(0);
// Recreate DC.
m_pDC = new CDC;
if(!m_pDC->CreateCompatibleDC(NULL))
{
ASSERT(0);
return false;
}
m_hDIBSectionOld = (HBITMAP)::SelectObject(m_pDC->m_hDC, m_hDIBSection);
return true;
}
//******************************************************
//
// Create()
//
//******************************************************
bool CRGBSurface::Create(int cx, int cy, int nBitsPerPixel /* = 24 */)
{
ASSERT(nBitsPerPixel == 24 || nBitsPerPixel == 32);
ASSERT(cx > 0);
ASSERT(cy > 0);
// If same sizes as previous surface,
// don't create a new surface,
// only make sure that all members are valid,
// and that DIB section is selected into DC.
if(m_pDC != NULL && m_bih.biWidth == cx && m_bih.biHeight == cy &&
m_bih.biBitCount == nBitsPerPixel && m_hDIBSection && m_pDIBits)
{
SelectObject(m_pDC->m_hDC, m_hDIBSection);
return true;
}
// Destroy previous surface if we are recreating.
Delete();
// Create surface DC if not created yet.
if(m_pDC == NULL)
{
m_pDC = new CDC;
if(!m_pDC->CreateCompatibleDC(NULL))
{
ASSERT(0);
return false;
}
}
// Fill BITMAPINFOHEADER.
int nSize = sizeof(BITMAPINFOHEADER);
ZeroMemory(&m_bih, nSize);
m_bih.biSize = nSize;
m_bih.biWidth = cx;
m_bih.biHeight = cy;
m_bih.biPlanes = 1;
m_bih.biBitCount = nBitsPerPixel;
m_bih.biCompression = BI_RGB;
// Create the DIB section.
m_hDIBSection = CreateDIBSection(m_pDC->m_hDC,
(BITMAPINFO*)&m_bih,
DIB_RGB_COLORS,
(void**)&m_pDIBits,
NULL,
0);
ASSERT(m_hDIBSection);
ASSERT(m_pDIBits);
if(!m_hDIBSection || !m_pDIBits)
return false;
// Select the new DIB section into the surface DC
if(m_hDIBSection)
m_hDIBSectionOld = (HBITMAP)::SelectObject(m_pDC->m_hDC, m_hDIBSection);
return true;
}
//*******************************************************
//
// Create()
//
//*******************************************************
bool CRGBSurface::Create(HBITMAP hBitmap, int nBitsPerPixel /* = 24 */, CDC* pBitmapDC /* = NULL */)
{
ASSERT(nBitsPerPixel == 24 || nBitsPerPixel == 32);
// Get bitmap sizes
BITMAP bm;
if(!GetObject(hBitmap, sizeof(BITMAP), &bm))
{
ASSERT(0);
return false;
}
// Create new DIB section
Create(bm.bmWidth, bm.bmHeight, nBitsPerPixel);
// Create a DC for pBitmap DDB if pBitmapDC not supplied
bool bWeCreatedDC = false;
if(pBitmapDC == NULL)
{
pBitmapDC = new CDC;
VERIFY(pBitmapDC->CreateCompatibleDC(NULL));
bWeCreatedDC = true;
}
HGDIOBJ hOld = ::SelectObject(pBitmapDC->m_hDC, hBitmap);
// Convert DDB to surface DIB section
m_pDC->BitBlt(0, 0, bm.bmWidth, bm.bmHeight, pBitmapDC,
0, 0, SRCCOPY);
::SelectObject(pBitmapDC->m_hDC, hOld);
if(bWeCreatedDC)
delete pBitmapDC;
return true;
}
//*******************************************************
//
// Create()
//
//*******************************************************
bool CRGBSurface::Create(const char* szPath, int nBitsPerPixel /* = 24 */)
{
ASSERT(nBitsPerPixel == 24 || nBitsPerPixel == 32);
CString strPath = szPath;
CString strExt = strPath.Right(3);
#ifdef ENABLE_JPEG
// JPEG
if(!strExt.CompareNoCase(_T("jpg")))
return CreateFromJPEGFile(strPath, nBitsPerPixel);
// Stereoscopic JPEG
if(!strExt.CompareNoCase(_T("jps")))
return CreateFromJPEGFile(strPath, nBitsPerPixel);
#endif
// DIB
if(!strExt.CompareNoCase(_T("bmp")))
return CreateFromDIBFile(strPath, nBitsPerPixel);
if(!strExt.CompareNoCase(_T("dib")))
return CreateFromDIBFile(strPath, nBitsPerPixel);
// Unsupported file type
ASSERT(0);
return false;
}
bool CRGBSurface::CreateFromDIBFile(const CString& strPath, int nBitsPerPixel /* = 24 */)
{
ASSERT(nBitsPerPixel == 24 || nBitsPerPixel == 32);
// Load DIB from file to memory.
BYTE* pDIB = LoadDIB(strPath);
if(pDIB == NULL)
return false;
// Convert from memory DIB to a pure RGB DIB section;
bool bRetVal = Create(pDIB, nBitsPerPixel);
delete pDIB;
return bRetVal;
}
//*******************************************************
//
// Create()
//
//*******************************************************
bool CRGBSurface::Create(HANDLE hFile, int nBitsPerPixel /* = 24 */)
{
ASSERT(nBitsPerPixel == 24 || nBitsPerPixel == 32);
// Load DIB from file to memory.
BYTE* pDIB = LoadDIBFromOpenedFile(hFile);
if(pDIB == NULL)
return false;
// Convert from memory DIB to a pure RGB DIB section;
bool bRetVal = Create(pDIB, nBitsPerPixel);
delete pDIB;
return bRetVal;
}
//*******************************************************
//
// Create()
//
//*******************************************************
bool CRGBSurface::Create(CRGBSurface* const pSourceSurface)
{
if(!Create(pSourceSurface->GetWidth(),
pSourceSurface->GetHeight(),
pSourceSurface->m_bih.biBitCount))
{
ASSERT(0);
return false;
}
return pSourceSurface->CopyTo(this);
}
//*******************************************************
//
// Create()
//
//*******************************************************
bool CRGBSurface::Create(BYTE* pDIB, int nBitsPerPixel /* = 24 */)
{
ASSERT(nBitsPerPixel == 24 || nBitsPerPixel == 32);
if(pDIB == NULL)
{
ASSERT(0);
return false;
}
// Create new DIB section
Create(((BITMAPINFOHEADER*)pDIB)->biWidth,
((BITMAPINFOHEADER*)pDIB)->biHeight, nBitsPerPixel);
// Initialize created DIB section using memory DIB.
// A convertion from a memory DIB with a color table or
// different number of bits per pixel
// to a 24 bit or 32 bit RGB DIB section
// is done here by SetDIBits().
if(!SetDIBits(m_pDC->m_hDC,
m_hDIBSection,
0,
m_bih.biHeight,
GetPointerToDIBits(pDIB),
(BITMAPINFO*)pDIB,
DIB_RGB_COLORS))
{
ASSERT(0);
return false;
}
return true;
}
//******************************************************
//
// Draw()
//
//******************************************************
void CRGBSurface::Draw(CDC* pDestDC, int x, int y, int destWidth /* = -1 */, int destHeight /* = -1 */) const
{
GdiFlush();
if(destWidth < 0)
destWidth = m_bih.biWidth;
if(destHeight < 0)
destHeight = m_bih.biHeight;
CSize bitmapDestSize(destWidth, destHeight);
CPoint ptDestPosition(x, y);
if(pDestDC->GetMapMode() != MM_TEXT)
{
// Convert DIB sizes to currently used logical units,
pDestDC->DPtoLP(&bitmapDestSize);
pDestDC->DPtoLP(&ptDestPosition);
}
pDestDC->SetStretchBltMode(COLORONCOLOR);
if(!StretchDIBits(pDestDC->m_hDC,
ptDestPosition.x, ptDestPosition.y,
bitmapDestSize.cx, bitmapDestSize.cy,
0, 0,
m_bih.biWidth, m_bih.biHeight,
m_pDIBits,
(BITMAPINFO*)&m_bih,
DIB_RGB_COLORS,
SRCCOPY))
{
ASSERT(0);
}
GdiFlush();
}
void CRGBSurface::Draw(CDC* pDestDC, int srcx, int srcy, int srcWidth, int srcHeight,
int destx, int desty, int destWidth /* =-1 */, int destHeight /* =-1 */) const
{
GdiFlush();
ASSERT(srcx+srcWidth<=m_bih.biWidth);
ASSERT(srcy+srcHeight<=m_bih.biHeight);
if(destWidth < 0)
destWidth = m_bih.biWidth;
if(destHeight < 0)
destHeight = m_bih.biHeight;
CSize bitmapDestSize(destWidth, destHeight);
CSize bitmapSrcSize(srcWidth, srcHeight);
CPoint ptDestPosition(destx, desty);
CPoint ptSrcPosition(srcx, srcy);
if(pDestDC->GetMapMode() != MM_TEXT)
{
// Convert DIB sizes to currently used logical units,
pDestDC->DPtoLP(&bitmapDestSize);
pDestDC->DPtoLP(&ptDestPosition);
}
pDestDC->SetStretchBltMode(COLORONCOLOR);
if(!StretchDIBits(pDestDC->m_hDC,
ptDestPosition.x, ptDestPosition.y,
bitmapDestSize.cx, bitmapDestSize.cy,
ptSrcPosition.x, ptSrcPosition.y,
bitmapSrcSize.cx, bitmapSrcSize.cy,
m_pDIBits,
(BITMAPINFO*)&m_bih,
DIB_RGB_COLORS,
SRCCOPY))
{
ASSERT(0);
}
GdiFlush();
}
//******************************************************
//
// Blt()
//
//******************************************************
void CRGBSurface::Blt(CDC* pDestDC, int destWidth, int destHeight) const
{
GdiFlush();
if (destWidth == -1)
destWidth = m_bih.biWidth;
if (destHeight == -1)
destHeight = m_bih.biHeight;
if(!SetDIBitsToDevice(pDestDC->m_hDC,
0, 0,
destWidth, destHeight,
0, 0,
0,
m_bih.biHeight,
m_pDIBits,
(BITMAPINFO*)&m_bih,
DIB_RGB_COLORS ))
{
ASSERT(0);
}
GdiFlush();
}
void CRGBSurface::Blt(CDC* pDestDC, int srcX, int srcY, int destX, int destY, int destWidth, int destHeight ) const
{
GdiFlush();
if (destWidth == -1)
destWidth = m_bih.biWidth;
if (destHeight == -1)
destHeight = m_bih.biHeight;
ASSERT(srcX+destWidth <= m_bih.biWidth);
ASSERT(destX+destWidth <= m_bih.biWidth);
ASSERT(srcY+destHeight <= m_bih.biHeight);
ASSERT(destY+destHeight <= m_bih.biHeight);
if(!SetDIBitsToDevice(pDestDC->m_hDC,
destX, destY,
destWidth, destHeight,
srcX, srcY,
0,
m_bih.biHeight,
m_pDIBits,
(BITMAPINFO*)&m_bih,
DIB_RGB_COLORS ))
{
ASSERT(0);
}
GdiFlush();
}
bool CRGBSurface::CopyToClipboard(bool bNoCopy /* = false */)
{
if(!m_hDIBSection)
return false;
if(!::OpenClipboard(NULL))
return false;
if(!EmptyClipboard())
return false;
GdiFlush();
if(bNoCopy)
{
if(!SetClipboardData(CF_BITMAP, m_hDIBSection))
return false;
}
else
{
if(!SetClipboardData(CF_DIB, CreateDIB_Win16()))
return false;
}
if(bNoCopy)
m_hDIBSection = NULL; // We no longer own this object
if(!CloseClipboard())
return false;
return true;
}
//*******************************************************
//
// CreateDIB_Win16()
//
//*******************************************************
HGLOBAL CRGBSurface::CreateDIB_Win16()
{
if(!m_hDIBSection || !m_pDIBits)
{
// Call Create() first.
ASSERT(0);
return NULL;
}
RecreateDC(); // check if DC is valid and if not, recreate
GdiFlush();
ASSERT(GetSize_Bytes());
HGLOBAL hDIB = GlobalAlloc(GMEM_MOVEABLE, sizeof(BITMAPINFOHEADER) + GetSize24_Bytes());
BYTE* pDIB = (BYTE*)GlobalLock(hDIB);
CopyMemory(pDIB, &m_bih, sizeof(BITMAPINFOHEADER));
((BITMAPINFOHEADER*)pDIB)->biBitCount = 24;
BYTE* pDIBits = pDIB + sizeof(BITMAPINFOHEADER);
if(!GetDIBits(m_pDC->m_hDC,
m_hDIBSection,
0,
m_bih.biHeight,
(LPVOID)pDIBits,
(LPBITMAPINFO)pDIB,
DIB_RGB_COLORS))
{
GlobalFree(hDIB);
return NULL;
}
GlobalUnlock(hDIB);
ASSERT(hDIB);
return hDIB;
}
//*******************************************************
//
// CreateDIB()
//
//*******************************************************
BYTE* CRGBSurface::CreateDIB()
{
if(!m_hDIBSection || !m_pDIBits)
{
// Call Create() first.
ASSERT(0);
return NULL;
}
RecreateDC(); // check if DC is valid and if not, recreate
GdiFlush();
ASSERT(GetSize_Bytes());
BYTE* pDIB = (BYTE*)malloc(sizeof(BITMAPINFOHEADER) + GetSize24_Bytes());
CopyMemory(pDIB, &m_bih, sizeof(BITMAPINFOHEADER));
((BITMAPINFOHEADER*)pDIB)->biBitCount = 24;
BYTE* pDIBits = pDIB + sizeof(BITMAPINFOHEADER);
if(!GetDIBits(m_pDC->m_hDC,
m_hDIBSection,
0,
m_bih.biHeight,
(LPVOID)pDIBits,
(LPBITMAPINFO)pDIB,
DIB_RGB_COLORS))
{
free(pDIB);
return NULL;
}
return pDIB;
}
//*******************************************************
//
// CopyTo()
//
//*******************************************************
bool CRGBSurface::CopyTo(CRGBSurface* pDestSurface, DWORD dwX /* = 0 */, DWORD dwY /* = 0 */) const
{
pDestSurface->AssertValid();
if(*pDestSurface < *this)
{
ASSERT(0);
return false;
}
if(dwX >= (DWORD)pDestSurface->GetWidth())
{
ASSERT(0);
return false;
}
if(dwY >= (DWORD)pDestSurface->GetHeight())
{
ASSERT(0);
return false;
}
if(!pDestSurface->GetDC()->BitBlt(dwX, dwY, GetWidth(), GetHeight(), m_pDC, 0, 0, SRCCOPY))
{
ASSERT(0);
return false;
}
return true;
}
////////////////////////////////////////////////////////
// Functions that directly manipulate DIB section bits
void CRGBSurface::Clear(DWORD dwBGRA /* = BGRA(255, 255, 255, 255) */)
{
GdiFlush();
switch(m_bih.biBitCount)
{
case 32:
{
// Fast DWORD copy.
for(DWORD* pdwPixel = (DWORD*)m_pDIBits; pdwPixel < (DWORD*)(m_pDIBits + GetSize_Bytes()); pdwPixel++)
*pdwPixel = dwBGRA;
}
break;
case 24:
{
// Check if same value in R, G and B,
// so we could use fast FillMemory().
BYTE bGValue = GetGValue(dwBGRA);
if(GetRValue(dwBGRA) == bGValue && bGValue == GetBValue(dwBGRA))
{
FillMemory(m_pDIBits, GetSize_Bytes(), bGValue);
return;
}
// Fill the DIB, take into acount a correct row alignment.
for(BYTE* pbNextRow = m_pDIBits;
pbNextRow < m_pDIBits + GetSize_Bytes();
pbNextRow += GetAlignedRowSize_Bytes())
{
for(BYTE* pbPtr = pbNextRow;
pbPtr < pbNextRow + GetRowSize_Bytes();
pbPtr += 3)
{
*(DWORD*)pbPtr = dwBGRA;
}
}
break;
}
default:
ASSERT(0);
}
}
//******************************************************
//
// ClearCR()
//
//******************************************************
void CRGBSurface::ClearCR(DWORD dwRGBA /* = RGBA(255, 255, 255, 255) */)
{
Clear(COLORREFtoBGRA(dwRGBA));
}
//******************************************************
//
// Brightness()
//
//******************************************************
void CRGBSurface::Brightness(double dBrightness)
{
DWORD dwByte;
BYTE* pNextLine = GetPixels();
for(int y = 0; y < m_bih.biHeight; y++, pNextLine += GetAlignedRowSize_Bytes())
{
BYTE* pNextPixel = pNextLine;
for(int x = 0; x < m_bih.biWidth; x++, pNextPixel += GetPixelSize_Bytes())
{
dwByte = DWORD(*pNextPixel * dBrightness);
if(dwByte > 255)
dwByte = 255;
*pNextPixel = (BYTE)dwByte;
dwByte = DWORD(*(pNextPixel + 1) * dBrightness);
if(dwByte > 255)
dwByte = 255;
*(pNextPixel + 1) = (BYTE)dwByte;
dwByte = DWORD(*(pNextPixel + 2) * dBrightness);
if(dwByte > 255)
dwByte = 255;
*(pNextPixel + 2) = (BYTE)dwByte;
}
}
}
////////////////////////////////////////////////////////
// IO functions
//*******************************************************
//
// LoadDIB()
//
//*******************************************************
BYTE* CRGBSurface::LoadDIB(const CString& strPath)
{
HANDLE hFile;
hFile = CreateFile(strPath,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if(hFile == INVALID_HANDLE_VALUE)
return NULL;
BYTE* pDIB = LoadDIBFromOpenedFile(hFile);
CloseHandle(hFile);
return pDIB;
}
//*******************************************************
//
// LoadDIBFromOpenedFile()
//
//*******************************************************
BYTE* CRGBSurface::LoadDIBFromOpenedFile(HANDLE hFile)
{
if(hFile == INVALID_HANDLE_VALUE)
{
ASSERT(0);
return NULL;
}
// Get current file pointer.
int nOrigin = SetFilePointer(hFile, 0, 0, FILE_CURRENT);
if(nOrigin < 0)
return NULL;
// Read BITMAPFILEHEADER.
BITMAPFILEHEADER bitmapFileHeader;
DWORD dwBytesRead;
if(!ReadFile(hFile, &bitmapFileHeader, sizeof (BITMAPFILEHEADER), &dwBytesRead, NULL))
return NULL;
// Make sure this is a Microsoft DIB file.
if((dwBytesRead != sizeof (BITMAPFILEHEADER)) ||
(bitmapFileHeader.bfType != DIB_MAGIC))
{
return NULL;
}
// Allocate memory for BITMAPINFOHEADER.
DWORD dwSize = bitmapFileHeader.bfSize - sizeof (BITMAPFILEHEADER);
BYTE* pDIB = (BYTE*) malloc(sizeof (BITMAPINFOHEADER));
// Read BITMAPINFOHEADER.
if(!ReadFile(hFile, pDIB, sizeof (BITMAPINFOHEADER), &dwBytesRead, NULL))
{
free(pDIB);
return NULL;
}
if(dwBytesRead != sizeof (BITMAPINFOHEADER))
{
free(pDIB);
return NULL;
}
BITMAPINFOHEADER* pBitmapInfo = (BITMAPINFOHEADER*)pDIB;
// Fill empty fields.
// DWORD alligned image size in bytes.
if(pBitmapInfo->biSizeImage == 0)
{
pBitmapInfo->biSizeImage =
ALIGN(pBitmapInfo->biWidth * pBitmapInfo->biBitCount / 8, 4) * pBitmapInfo->biHeight;
}
// Number of entries in the color table
if(pBitmapInfo->biClrUsed == 0)
{
switch(pBitmapInfo->biBitCount)
{
case 1:
pBitmapInfo->biClrUsed = 2;
break;
case 4:
pBitmapInfo->biClrUsed = 16;
break;
case 8:
pBitmapInfo->biClrUsed = 256;
}
}
DWORD dwColorTableSize = pBitmapInfo->biClrUsed * sizeof(RGBQUAD);
// Change allocated BITMAPINFOHEADER memory block
// to a memory block for a complete DIB.
pDIB = (BYTE*)realloc(pDIB, sizeof (BITMAPINFOHEADER) + dwColorTableSize + pBitmapInfo->biSizeImage);
pBitmapInfo = (BITMAPINFOHEADER*)pDIB;
// Load the color table.
if(pBitmapInfo->biClrUsed)
{
RGBQUAD* pColorTable = (RGBQUAD*)(pDIB + pBitmapInfo->biSize);
if(!ReadFile(hFile, pColorTable, dwColorTableSize, &dwBytesRead, NULL))
{
free(pDIB);
return NULL;
}
if(dwBytesRead != dwColorTableSize)
{
free(pDIB);
return NULL;
}
}
// Set file pointer where DIB bits are located, and read them.
if(SetFilePointer(hFile, nOrigin + bitmapFileHeader.bfOffBits, 0, FILE_BEGIN) <0)
{
free(pDIB);
return NULL;
}
BYTE* pBitmapBits = pDIB + sizeof(BITMAPINFOHEADER) + dwColorTableSize;
if(!ReadFile(hFile, pBitmapBits, pBitmapInfo->biSizeImage, &dwBytesRead, NULL))
{
free(pDIB);
return NULL;
}
if(dwBytesRead != pBitmapInfo->biSizeImage)
{
free(pDIB);
return NULL;
}
return pDIB;
}
//*******************************************************
//
// SaveDIB()
//
//*******************************************************
bool CRGBSurface::SaveDIB(const CString& strPath, bool bAllowOverwrite /* = false */)
{
HANDLE hFile;
DWORD dwCreationDisposition = CREATE_NEW;
if(bAllowOverwrite)
dwCreationDisposition = CREATE_ALWAYS;
m_bSaveFile_FileExists = false;
hFile = CreateFile(strPath,
GENERIC_WRITE,
NULL,
NULL,
dwCreationDisposition,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
if(GetLastError() == ERROR_FILE_EXISTS)
m_bSaveFile_FileExists = true;
return false;
}
bool bRet = SaveDIBToOpenedFile(hFile);
CloseHandle(hFile);
return bRet;
}
//*******************************************************
//
// SaveDIBToOpenedFile()
//
//*******************************************************
bool CRGBSurface::SaveDIBToOpenedFile(HANDLE hFile)
{
if(hFile == INVALID_HANDLE_VALUE)
{
ASSERT(0);
return false;
}
// Get current file pointer.
int nOrigin = SetFilePointer(hFile, 0, 0, FILE_CURRENT);
if(nOrigin < 0)
return false;
// Construct and write BITMAPFILEHEADER.
BITMAPFILEHEADER bitmapFileHeader;
bitmapFileHeader.bfType = DIB_MAGIC;
bitmapFileHeader.bfSize = SIZEOF_BITMAPFILEHEADER_PACKED +
sizeof(BITMAPINFOHEADER) +
GetSize_Bytes();
bitmapFileHeader.bfReserved1 = 0;
bitmapFileHeader.bfReserved2 = 0;
bitmapFileHeader.bfOffBits = SIZEOF_BITMAPFILEHEADER_PACKED +
sizeof(BITMAPINFOHEADER);
DWORD dwBytesWritten;
if(!WriteFile(hFile, &bitmapFileHeader, SIZEOF_BITMAPFILEHEADER_PACKED, &dwBytesWritten, NULL))
return false;
if(dwBytesWritten != SIZEOF_BITMAPFILEHEADER_PACKED)
return false;
// Construct a 24bit memory DIB from this DIB section,
// and write it to the file.
DWORD dwDIBSize = sizeof (BITMAPINFOHEADER) + GetSize24_Bytes();
BYTE* pDIB = CreateDIB();
if(pDIB == NULL)
return false;
int nResult = WriteFile(hFile, pDIB, dwDIBSize, &dwBytesWritten, NULL);
free(pDIB);
if(!nResult || dwBytesWritten != dwDIBSize)
return false;
return true;
}
//*******************************************************
//
// GetPointerToDIBits()
//
//*******************************************************
BYTE* CRGBSurface::GetPointerToDIBits(BYTE* pDIB)
{
BITMAPINFOHEADER* pBitmapInfo = (BITMAPINFOHEADER*)pDIB;
DWORD dwColorTableSize = pBitmapInfo->biClrUsed * sizeof(RGBQUAD);
return pDIB + sizeof(BITMAPINFOHEADER) + dwColorTableSize;
}
//*******************************************************
//
// SaveToFile()
//
//*******************************************************
bool CRGBSurface::SaveToFile(const CString& strPath, bool bAllowOverwrite /* = false */, int nQuality /* = -1 */)
{
CString strExt = strPath.Right(3);
#ifdef ENABLE_JPEG
// JPEG
if(!strExt.CompareNoCase(_T("jpg")) || !strExt.CompareNoCase(_T("jps")))
return SaveAsJPEGFile(strPath, nQuality, bAllowOverwrite);
#endif
if(!strExt.CompareNoCase(_T("bmp")) || !strExt.CompareNoCase(_T("dib")))
return SaveDIB(strPath, bAllowOverwrite);
ASSERT(0);
return false;
}
#ifdef ENABLE_JPEG
#pragma message ("Compiling JPEG routines...")
bool CRGBSurface::CreateFromJPEGFile(const CString& strPath, int nBitsPerPixel /* = 24 */)
{
// Open stdio file.
FILE* infile;
if((infile = fopen(strPath, "rb"))==NULL)
return false;
if(!CreateFromOpenedStdioJPEGFile(infile, nBitsPerPixel))
{
fclose(infile);
return false;
}
fclose(infile);
return true;
}
//*******************************************************
//
// CreateFromOpenedStdioJPEGFile()
//
//*******************************************************
// Needed for JPEG error handling
struct my_error_mgr {
struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer; };
typedef struct my_error_mgr * my_error_ptr;
METHODDEF(void) my_error_exit (j_common_ptr cinfo)
{
my_error_ptr myerr = (my_error_ptr) cinfo->err;
longjmp(myerr->setjmp_buffer, 1);
}
bool CRGBSurface::CreateFromOpenedStdioJPEGFile(FILE* infile, int nBitsPerPixel /* = 24 */)
{
JSAMPARRAY ppbBuffer;
struct my_error_mgr jerr;
jpeg_decompress_struct cinfo;
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
// Set return jump point for longjmp()
if(setjmp(jerr.setjmp_buffer))
{
// If we get here, it means that
// there was an error during decompression.
jpeg_destroy_decompress(&cinfo);
#ifdef _DEBUG
char buffer[1024];
cinfo.err->format_message((jpeg_common_struct*)&cinfo, buffer);
AfxMessageBox(buffer);
#endif
return false;
}
// Prepeare decompresor object
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, infile);
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo);
// Create DIB section
if(!Create(cinfo.output_width, cinfo.output_height, nBitsPerPixel))
return false;
DWORD dwByteWidth = GetRowSize_Bytes();
DWORD dwAlignedRowSize = GetAlignedRowSize_Bytes();
BYTE* pbTemp;
ppbBuffer = &pbTemp; // pbTemp to avoid alloc_sarray
*ppbBuffer = m_pDIBits + GetSize_Bytes() - dwAlignedRowSize;
switch(nBitsPerPixel)
{
case 32:
{
// Decompresion to 32 bit DIB section.
while(cinfo.output_scanline < cinfo.output_height)
{
jpeg_read_scanlines(&cinfo, ppbBuffer, 1);
// Swap R and B components and insert A component
BYTE* pbBuf = *ppbBuffer + cinfo.output_width * 3 - 1;
for(BYTE* pbBuf32 = *ppbBuffer + dwAlignedRowSize - 1;
pbBuf32 > *ppbBuffer;
pbBuf32 -= 4, pbBuf -= 3)
{
*pbBuf32 = 255;
*(pbBuf32 - 3) = *pbBuf;
*(pbBuf32 - 2) = *(pbBuf - 1);
*(pbBuf32 - 1) = *(pbBuf - 2);
}
*ppbBuffer -= dwAlignedRowSize;
}
}
break;
case 24:
{
// Decompresion to 24 bit DIB section
BYTE bByte;
while(cinfo.output_scanline < cinfo.output_height)
{
jpeg_read_scanlines(&cinfo, ppbBuffer, 1);
// swap R and B components
for(BYTE* pbBuf = *ppbBuffer; pbBuf < *ppbBuffer + dwByteWidth; pbBuf += 3)
{
bByte = pbBuf[2];
pbBuf[2] = *pbBuf;
*pbBuf = bByte;
}
*ppbBuffer -= dwAlignedRowSize;
}
}
break;
default:
ASSERT(0);
return false;
}
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return true;
}
//*******************************************************
//
// SaveAsJPEGFile()
//
//*******************************************************
bool CRGBSurface::SaveAsJPEGFile(const CString& strPath, int nQuality /* = -1 */, bool bAllowOverwrite /* = false */)
{
ASSERT(nQuality >= -1 && nQuality <= 100);
struct jpeg_compress_struct cinfo;
struct my_error_mgr jerr;
FILE* outfile;
JSAMPARRAY ppbBuffer;
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
// Set return jump point for longjmp()
if(setjmp(jerr.setjmp_buffer))
{
// If we get here, it means that
// there was an error during compression.
jpeg_destroy_compress(&cinfo);
fclose(outfile);
#ifdef _DEBUG
char buffer[1024];
cinfo.err->format_message((jpeg_common_struct*)&cinfo, buffer);
AfxMessageBox(buffer);
#endif
return false;
}
/* Now we can initialize the JPEG compression object. */
jpeg_create_compress(&cinfo);
m_bSaveFile_FileExists = false;
if(!bAllowOverwrite)
{
if((outfile = fopen(strPath, "rb")) != NULL)
{
fclose(outfile);
m_bSaveFile_FileExists = true;
return false;
}
}
if((outfile = fopen(strPath, "wb")) == NULL)
return false;
jpeg_stdio_dest(&cinfo, outfile);
cinfo.image_width = m_bih.biWidth;
cinfo.image_height = m_bih.biHeight;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
if(nQuality > -1)
jpeg_set_quality(&cinfo, nQuality, TRUE);
jpeg_start_compress(&cinfo, TRUE);
GdiFlush();
DWORD dwByteWidth = GetRowSize_Bytes();
DWORD dwAlignedRowSize = GetAlignedRowSize_Bytes();
// Array of pointers to JPEG lines, here we process
// the image line by line so the array's size is 1.
ppbBuffer = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, dwByteWidth, 1);
BYTE* pNextLine = m_pDIBits + GetSize_Bytes() - dwAlignedRowSize;
switch(m_bih.biBitCount)
{
case 32:
{
// Compresion from 32 bit DIB section
while(pNextLine >= m_pDIBits)
{
BYTE* pbWriteBuf = *ppbBuffer;
// swap R and B components, remove A component
for(BYTE* pbReadBuf = pNextLine; pbReadBuf < pNextLine + dwAlignedRowSize; pbReadBuf += 4, pbWriteBuf +=3)
{
*pbWriteBuf = pbReadBuf[2];
pbWriteBuf[1] = pbReadBuf[1];
pbWriteBuf[2] = *pbReadBuf;
}
jpeg_write_scanlines(&cinfo, ppbBuffer, 1);
pNextLine -= dwAlignedRowSize;
}
break;
}
case 24:
{
// Compresion from 24 bit DIB section
while(pNextLine >= m_pDIBits)
{
BYTE* pbWriteBuf = *ppbBuffer;
// swap R and B components
for(BYTE* pbReadBuf = pNextLine; pbReadBuf < pNextLine + dwByteWidth; pbReadBuf += 3, pbWriteBuf += 3)
{
*pbWriteBuf = pbReadBuf[2];
pbWriteBuf[1] = pbReadBuf[1];
pbWriteBuf[2] = *pbReadBuf;
}
jpeg_write_scanlines(&cinfo, ppbBuffer, 1);
pNextLine -= dwAlignedRowSize;
}
break;
}
default:
ASSERT(0);
}
jpeg_finish_compress(&cinfo);
fclose(outfile);
jpeg_destroy_compress(&cinfo);
return true;
}
#endif
//////////////////////////////////////////////////////////////////////////////