#include "stdafx.h" #include "global.h" #include "pbrush.h" #include "pbrusdoc.h" #include "pbrusfrm.h" #include "pbrusvw.h" #include "minifwnd.h" #include "bmobject.h" #include "imgsuprt.h" #include "imgwnd.h" #include "imgbrush.h" #include "imgwell.h" #include "imgtools.h" #include "toolbox.h" #include "imgfile.h" #include "colorsrc.h" #include "undo.h" #include "props.h" #include "ferr.h" #include "cmpmsg.h" #include "loadimag.h" #ifdef _DEBUG #undef THIS_FILE static CHAR BASED_CODE THIS_FILE[] = __FILE__; #endif IMPLEMENT_DYNCREATE( CBitmapObj, CObject ) #include "memtrace.h" /***************************************************************************/ // Map from the value in CBitmapObj::m_nColors to bits per pixel int mpncolorsbits [] = { 1, 4, 8, 24 }; /***************************************************************************/ CBitmapObj::CBitmapObj() : CObject(), m_dependants() { m_bDirty = FALSE; m_bTempName = FALSE; m_hThing = NULL; m_lMemSize = 0L; m_pImg = NULL; m_nWidth = 0; m_nHeight = 0; m_nColors = 0; m_nSaveColors = -1; #ifdef ICO_SUPPORT m_bSaveIcon = FALSE; #endif #ifdef PCX_SUPPORT m_bPCX = FALSE; #endif m_bCompressed = FALSE; m_nShrink = 0; m_dwOffBits = 0; } /***************************************************************************/ CBitmapObj::~CBitmapObj() { ASSERT_VALID(this); InformDependants( SN_DESTROY ); if (m_hThing != NULL) { Free(); } if (m_pImg) FreeImg(m_pImg); } /***************************************************************************/ void CBitmapObj::AddDependant( CBitmapObj* newDependant ) { POSITION pos = m_dependants.Find( newDependant ); if (pos == NULL) m_dependants.AddTail( newDependant ); } /***************************************************************************/ void CBitmapObj::RemoveDependant( CBitmapObj* oldDependant ) { POSITION pos = m_dependants.Find(oldDependant); if (pos != NULL) m_dependants.RemoveAt(pos); } /***************************************************************************/ void CBitmapObj::InformDependants( UINT idChange ) { POSITION pos = m_dependants.GetHeadPosition(); while (pos != NULL) { CBitmapObj* pSlob = (CBitmapObj*)m_dependants.GetNext(pos); pSlob->OnInform(this, idChange); } } /***************************************************************************/ void CBitmapObj::OnInform( CBitmapObj* pChangedSlob, UINT idChange ) { if (idChange == SN_DESTROY) { POSITION pos = m_dependants.Find(pChangedSlob); if (pos != NULL) m_dependants.RemoveAt(pos); } } /***************************************************************************/ void CBitmapObj::SetDirty(BOOL bDirty) { m_bDirty = bDirty; } /*****************************************************************************/ void CBitmapObj::Zap() { m_bDirty = FALSE; } /*****************************************************************************/ BOOL CBitmapObj::Alloc() // m_hThing of size m_lMemSize { if (m_lMemSize == 0L) return FALSE; m_hThing = GlobalAlloc(GPTR, m_lMemSize); if (m_hThing == NULL) { theApp.SetMemoryEmergency( TRUE ); return FALSE; } return TRUE; } /*****************************************************************************/ void CBitmapObj::Free() // m_hThing and set m_lMemSize to zero { if (m_hThing == NULL) { TRACE(TEXT("Warning: called Free on a CBitmapObj with no thing!\n")); return; } GlobalFree(m_hThing); m_hThing = NULL; m_lMemSize = 0; } /***************************************************************************/ CString CBitmapObj::GetDefExtension(int iStringId) { CString cStringExtension; if (iStringId != 0) { TRY { cStringExtension.LoadString( iStringId ); } CATCH(CMemoryException,e) { cStringExtension.Empty(); } END_CATCH } else { cStringExtension.Empty(); } return cStringExtension; } void PBGetDefDims(int &pnWidth, int &pnHeight) { // Setup default parameters... // Don't use the whole screen, those bitmaps get HUGE // pnWidth = GetSystemMetrics( SM_CXSCREEN )/2; pnHeight = GetSystemMetrics( SM_CYSCREEN )/2; // Check if this is a low memory machine and use a small default bitmap // size if (GetSystemMetrics(SM_SLOWMACHINE) & 0x0002) { pnWidth = 640/2; pnHeight = 480/2; } } /*****************************************************************************/ BOOL CBitmapObj::MakeEmpty() { PBGetDefDims(m_nWidth, m_nHeight); if (theApp.m_sizeBitmap.cx && theApp.m_sizeBitmap.cy) { m_nWidth = theApp.m_sizeBitmap.cx; m_nHeight = theApp.m_sizeBitmap.cy; } if (theApp.m_bEmbedded) { // make a nice size for embedded objects, lets try for 5 centimeters m_nWidth = theApp.ScreenDeviceInfo.ixPelsPerDM / 2; m_nHeight = theApp.ScreenDeviceInfo.iyPelsPerDM / 2; } // // default to 256 colors if not monochrome // m_nColors = theApp.m_bMonoDevice? 0 : 2; m_bDirty = TRUE; return TRUE; } /*****************************************************************************/ // Create and setup an IMG for this resource BOOL CBitmapObj::CreateImg() { ASSERT(! m_pImg); LONG cXPelsPerMeter = 0; LONG cYPelsPerMeter = 0; LPSTR lpbi = (LPSTR) GlobalLock(m_hThing); // NOTE: this is NULL for new resources! if (lpbi) { if (IS_WIN30_DIB( lpbi )) { PBITMAPINFOHEADER pbmih = (PBITMAPINFOHEADER) lpbi; m_bCompressed = pbmih->biCompression != BI_RGB; cXPelsPerMeter = pbmih->biXPelsPerMeter; cYPelsPerMeter = pbmih->biYPelsPerMeter; } m_nWidth = (int)DIBWidth ( lpbi ); m_nHeight = (int)DIBHeight( lpbi ); m_nColors = DIBNumColors( lpbi, FALSE ); if (m_nColors <= 0 || m_nColors > 256) m_nColors = 3; else if (m_nColors <= 2) m_nColors = 0; else if (m_nColors <= 16) m_nColors = 1; else if (m_nColors <= 256) m_nColors = 2; } UINT nColors = (m_nColors? 0: 1); m_pImg = ::CreateImg( lpbi ? 0 : m_nWidth, lpbi ? 0 : m_nHeight, nColors, nColors, cXPelsPerMeter, cYPelsPerMeter, theApp.m_bPaletted ); if (! m_pImg) { TRACE(TEXT("CreateImg failed\n")); theApp.SetMemoryEmergency(); GlobalUnlock(m_hThing); return FALSE; } if (g_pColors) { g_pColors->ResetColors ((m_nColors==1)?16:256); } m_pImg->cxWidth = m_nWidth; m_pImg->cyHeight = m_nHeight; if (! lpbi) { nColors = m_pImg->cPlanes * m_pImg->cBitCount; //FEATURE - Shouldn't this be " == 0 || == 1" ?? //Half a page up negative values == TRUE color! //This shell game with the values isn't very good... if (nColors <= 1) m_nColors = 0; else if (nColors <= 4) m_nColors = 1; else if (nColors <= 8) m_nColors = 2; else // 24-bit image m_nColors = 3; } m_pImg->m_pBitmapObj = this; m_pImg->bDirty = m_bDirty; if (lpbi) { // Load the bitmap/icon/cursor... HBITMAP hbm = DIBToDS( lpbi, m_dwOffBits, m_pImg->hDC ); if (! hbm) { theApp.SetMemoryEmergency(); GlobalUnlock(m_hThing); return FALSE; } m_pImg->hBitmap = hbm; m_pImg->hBitmapOld = (HBITMAP)::SelectObject( m_pImg->hDC, hbm ); } if ( theApp.m_bPaletted) // If LoadImage was used && paletted { // Create the Palette from the dib section instead. m_pImg->m_pPalette = PaletteFromDS(m_pImg->hDC); } theApp.m_pPalette = NULL; if (m_pImg->m_pPalette && theApp.m_bPaletted) { m_pImg->m_hPalOld = SelectPalette( m_pImg->hDC, (HPALETTE)m_pImg->m_pPalette->m_hObject, FALSE ); RealizePalette( m_pImg->hDC ); theApp.m_pPalette = m_pImg->m_pPalette; } else if (m_pImg->m_pPalette) { delete m_pImg->m_pPalette; m_pImg->m_pPalette = NULL; m_pImg->m_hPalOld = NULL; } if (g_pColors) g_pColors->SetMono( ! m_nColors ); GlobalUnlock(m_hThing); return TRUE; } /*****************************************************************************/ BOOL CBitmapObj::Export(const TCHAR* szFileName) { // If the file already exists and we aren't dirty, then don't bother // saving, just return... CFileStatus fStat; CString strFullName; MkFullPath( strFullName, (const TCHAR*)szFileName ); if (CFile::GetStatus( strFullName, fStat ) && ! m_bDirty) return TRUE; CFile file; CFileException e; CFileSaver saver( szFileName ); if (! saver.CanSave()) return FALSE; theApp.SetFileError( IDS_ERROR_EXPORT, CFileException::none, szFileName ); if (! OpenSubFile( file, saver, CFile::modeWrite | CFile::modeCreate | CFile::typeBinary, &e )) { theApp.SetFileError( IDS_ERROR_EXPORT, e.m_cause ); return FALSE; } BOOL bWritten = FALSE; TRY { #ifdef PCX_SUPPORT if (m_bPCX) bWritten = WritePCX( &file ); else #endif bWritten = WriteResource( &file ); file.Close(); } CATCH( CFileException, ex ) { file.Abort(); theApp.SetFileError( IDS_ERROR_EXPORT, ex->m_cause ); return FALSE; } END_CATCH if (bWritten) bWritten = saver.Finish(); else saver.Finish(); return bWritten; } typedef union _BITMAPHEADER { BITMAPINFOHEADER bmi; BITMAPCOREHEADER bmc; } BITMAPHEADER, *LPBITMAPHEADER; inline WORD PaletteSize(LPBITMAPHEADER lpHdr) {return(PaletteSize((LPSTR)lpHdr));} inline WORD DIBNumColors(LPBITMAPHEADER lpHdr) {return(DIBNumColors((LPSTR)lpHdr));} inline DWORD DIBWidth(LPBITMAPHEADER lpHdr) {return(DIBWidth((LPSTR)lpHdr));} inline DWORD DIBHeight(LPBITMAPHEADER lpHdr) {return(DIBHeight((LPSTR)lpHdr));} /*****************************************************************************/ BOOL CBitmapObj::WriteResource( CFile* pfile, PBResType rtType ) { BOOL bPBrushOLEHeader = (rtType == rtPBrushOLEObj); BOOL bFileHeader = (rtType == rtFile)|| (rtType == rtPaintOLEObj) || bPBrushOLEHeader; if (m_pImg == NULL) { // The image has not been loaded, so we'll just copy the // original out to the file... ASSERT( m_hThing ); if (! m_hThing) return FALSE; } else { // The image has been loaded and may have been edited, so // we'll convert it back to a dib to save... if (! m_hThing) SaveResource( FALSE ); if (! m_hThing) return FALSE; } LPBITMAPHEADER lpDib = (LPBITMAPHEADER)GlobalLock(m_hThing); DWORD dwLength = m_lMemSize; DWORD dwWriteLength = dwLength; DWORD dwHeadLength = 0; struct _BMINFO { BITMAPINFOHEADER hdr; RGBQUAD rgb[256]; } bmInfo; LPBITMAPHEADER lpOldHdr = lpDib; LPBITMAPHEADER lpNewHdr = lpOldHdr; DWORD dwOldHdrLen = lpOldHdr->bmi.biSize + PaletteSize(lpOldHdr); DWORD dwNewHdrLen = dwOldHdrLen; if (bPBrushOLEHeader) { if (!IS_WIN30_DIB(lpDib)) { LPBITMAPCOREINFO lpCoreInfo = (LPBITMAPCOREINFO)(&lpOldHdr->bmc); memset(&bmInfo.hdr, 0, sizeof(bmInfo.hdr)); bmInfo.hdr.biSize = sizeof(bmInfo.hdr); bmInfo.hdr.biWidth = lpCoreInfo->bmciHeader.bcWidth; bmInfo.hdr.biHeight = lpCoreInfo->bmciHeader.bcHeight; bmInfo.hdr.biPlanes = lpCoreInfo->bmciHeader.bcPlanes; bmInfo.hdr.biBitCount = lpCoreInfo->bmciHeader.bcBitCount; bmInfo.hdr.biCompression = BI_RGB; for (int i=DIBNumColors(lpOldHdr)-1; i>=0; --i) { bmInfo.rgb[i].rgbBlue = lpCoreInfo->bmciColors[i].rgbtBlue; bmInfo.rgb[i].rgbGreen = lpCoreInfo->bmciColors[i].rgbtGreen; bmInfo.rgb[i].rgbRed = lpCoreInfo->bmciColors[i].rgbtRed; bmInfo.rgb[i].rgbReserved = 0; } lpNewHdr = (LPBITMAPHEADER)(&bmInfo); dwNewHdrLen = lpNewHdr->bmi.biSize + PaletteSize(lpNewHdr); } dwWriteLength += dwNewHdrLen - dwOldHdrLen; dwLength += dwNewHdrLen - dwOldHdrLen; if (bFileHeader) { #ifdef ICO_SUPPORT if (IsSaveIcon()) { dwHeadLength = sizeof(ICONFILEHEADER); dwWriteLength += dwHeadLength; } else #endif { dwHeadLength = sizeof(BITMAPFILEHEADER); dwWriteLength += dwHeadLength; // PBrush rounded up to 32 bytes (I don't know why) dwWriteLength = (dwWriteLength+31) & ~31; } } pfile->Write( &dwWriteLength, sizeof( dwWriteLength )); } if (bFileHeader) { // Icon support is not in application anymore, right? #ifdef ICO_SUPPORT if (IsSaveIcon()) { ICONFILEHEADER hdr; hdr.icoReserved = 0; hdr.icoResourceType = 1; hdr.icoResourceCount = 1; pfile->Write( &hdr, sizeof( ICONFILEHEADER ) ); pfile->Seek( sizeof( ICONDIRENTRY ), CFile::current ); } else #endif { BITMAPFILEHEADER hdr; hdr.bfType = ((WORD)('M' << 8) | 'B'); hdr.bfSize = dwLength + sizeof( BITMAPFILEHEADER ); hdr.bfReserved1 = 0; hdr.bfReserved2 = 0; hdr.bfOffBits = (DWORD)sizeof(hdr) + lpNewHdr->bmi.biSize + PaletteSize(lpNewHdr); pfile->Write( &hdr, sizeof( hdr )); } } pfile->Write(lpNewHdr, dwNewHdrLen); BYTE* hp = ((BYTE*)lpDib) + dwOldHdrLen; // We subtract the new header length because we have already translated // dwLength to the new size DWORD dwWrite = dwLength - dwNewHdrLen; DWORD dwIconPos = pfile->GetPosition();; while (dwWrite > 0) { UINT cbWrite = (UINT)min( dwWrite, 16384 ); pfile->Write( (LPVOID)hp, cbWrite ); hp += cbWrite; dwWrite -= cbWrite; } dwWriteLength -= dwHeadLength; if (dwWriteLength > dwLength) { // We rounded up to 32 bytes above, so this should always be < 32 ASSERT(dwWriteLength-dwLength < 32); DWORD dwZeros[] = { 0, 0, 0, 0, 0, 0, 0, 0, } ; pfile->Write( dwZeros, dwWriteLength-dwLength ); } ASSERT( dwWrite == 0 ); // Icon support is not in application anymore, right? #ifdef ICO_SUPPORT if (IsSaveIcon()) { DWORD nextPos = pfile->GetPosition(); pfile->Seek( (bFileHeader? sizeof( ICONFILEHEADER ): 0), CFile::begin ); ICONDIRENTRY dir; dir.nWidth = (BYTE)DIBWidth ( lpDib ); dir.nHeight = (BYTE)DIBHeight ( lpDib ) / 2; dir.nColorCount = (BYTE)DIBNumColors( lpDib ); dir.bReserved = 0; dir.wReserved1 = 0; dir.wReserved2 = 0; dir.icoDIBSize = dwLength; dir.icoDIBOffset = dwIconPos; pfile->Write( &dir, sizeof( ICONDIRENTRY ) ); pfile->Seek( nextPos, CFile::begin ); } else #endif m_bDirty = FALSE; pfile->Flush(); GlobalUnlock(m_hThing); return TRUE; } /*****************************************************************************/ BOOL CBitmapObj::Import( LPCTSTR szFileName ) { CFile file; CFileException e; theApp.SetFileError( IDS_ERROR_READLOAD, CFileException::none, szFileName ); if (! file.Open( szFileName, CFile::modeRead | CFile::typeBinary, &e )) { theApp.SetFileError( IDS_ERROR_READLOAD, e.m_cause ); return FALSE; } BOOL bGoodFile = TRUE; TRY { bGoodFile = ReadResource( &file ); file.Close(); } CATCH(CFileException, ex) { file.Abort(); bGoodFile = FALSE; } END_CATCH if (!bGoodFile) { HGLOBAL hDib; if (hDib = LoadDIBFromFile(szFileName, &theApp.m_guidFltTypeUsed)) { bGoodFile = ReadResource(hDib); if (bGoodFile) { theApp.SetFileError(0, CFileException::none); } else { theApp.SetFileError( IDS_ERROR_READLOAD, ferrNotValidBmp); } } } return bGoodFile; } /*****************************************************************************/ BOOL CBitmapObj::ReadResource( CFile* pfile, PBResType rtType ) { BOOL bPBrushOLEHeader = (rtType == rtPBrushOLEObj); BOOL bFileHeader = (rtType == rtFile) || (rtType == rtPaintOLEObj)|| bPBrushOLEHeader; DWORD dwLength = pfile->GetLength(); // special case zero length files. if (! dwLength) { if (m_hThing) Free(); m_bDirty = TRUE; return TRUE; } if (bPBrushOLEHeader) { DWORD dwReadLen; if (pfile->Read( &dwReadLen, sizeof( dwReadLen )) != sizeof( dwReadLen ) || dwReadLen > dwLength) { theApp.SetFileError( IDS_ERROR_READLOAD, ferrNotValidBmp ); return FALSE; } dwLength -= sizeof(dwReadLen); } m_dwOffBits = 0; if (bFileHeader) { BITMAPFILEHEADER hdr; if (pfile->Read( &hdr, sizeof( hdr )) != sizeof( hdr )) { theApp.SetFileError( IDS_ERROR_READLOAD, ferrNotValidBmp ); return FALSE; } if (hdr.bfType != ((WORD)('M' << 8) | 'B')) { theApp.SetFileError( IDS_ERROR_READLOAD, ferrNotValidBmp ); return FALSE; } dwLength -= sizeof( hdr ); // Store the offset from the beginning of the BITMAPINFO if (hdr.bfOffBits) { m_dwOffBits = hdr.bfOffBits - sizeof(hdr); } else { m_dwOffBits = 0; } } if (m_hThing != NULL) Free(); m_lMemSize = dwLength; if (! Alloc()) return FALSE; ASSERT( m_hThing ); PVOID lpvThing = GlobalLock(m_hThing); BYTE* hp = (BYTE*)lpvThing; while (dwLength > 0) { UINT cbRead = (UINT)min( dwLength, 16384 ); if (pfile->Read( (void FAR*)hp, cbRead ) != cbRead) { theApp.SetFileError( IDS_ERROR_READLOAD, ferrReadFailed ); GlobalUnlock(m_hThing); return FALSE; } dwLength -= cbRead; hp += cbRead; } ASSERT( dwLength == 0 ); // // Calculate the bits offset because the BITMAPFILEHEADER had 0 // if (!m_dwOffBits) { m_dwOffBits = (DWORD)(FindDIBBits ((LPSTR)lpvThing, 0) - (LPSTR)lpvThing); } GlobalUnlock(m_hThing); return TRUE; } /*****************************************************************************/ BOOL CBitmapObj::ReadResource( HGLOBAL hDib ) { LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER) GlobalLock(hDib); DWORD dwSizeImage; if (lpbi == NULL || lpbi->biSize != sizeof(BITMAPINFOHEADER)) { theApp.SetFileError( IDS_ERROR_READLOAD, ferrNotValidBmp ); return FALSE; } m_dwOffBits = lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD); if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8) m_dwOffBits += (1 << lpbi->biBitCount) * sizeof(RGBQUAD); if (lpbi->biSizeImage) { lpbi->biSizeImage = abs(lpbi->biSizeImage); dwSizeImage = lpbi->biSizeImage; } else { dwSizeImage = abs(lpbi->biHeight) * ((lpbi->biWidth*lpbi->biBitCount+31)&~31)/8; } if (m_hThing != NULL) Free(); m_lMemSize = m_dwOffBits + dwSizeImage; m_hThing = hDib; GlobalUnlock(hDib); return TRUE; } /*****************************************************************************/ void CBitmapObj::ReLoadImage( CPBDoc* pbDoc ) { FreeImg( m_pImg ); CleanupImgUndo(); CleanupImgRubber(); m_pImg = NULL; if (CreateImg()) { POSITION pos = pbDoc->GetFirstViewPosition(); CPBView* pView = (CPBView*)pbDoc->GetNextView( pos ); if (pView) { pView->m_pImgWnd->SetImg( m_pImg ); pbDoc->UpdateAllViews( pView ); InvalImgRect( m_pImg, NULL ); } } } /*****************************************************************************/ void SwapBitmaps(HDC hDC1, int x1, int y1, int wid, int hgt, HDC hDC2, int x2, int y2, CPalette* pPalette) { #if 0 // We would like to just XOR 3 times to swap, but sometimes the middle of the // palette is empty, so we cannot BitBlt(m_pImg->hDC, rect.left , rect.top, rect.Width(), rect.Height(), hDC, 0, 0, DSx); BitBlt(hDC, 0, 0, rect.Width(), rect.Height(), m_pImg->hDC, rect.left, rect.top, DSx); BitBlt(m_pImg->hDC, rect.left, rect.top, rect.Width(), rect.Height(), hDC, 0, 0, DSx); #else CDC dcTemp; CDC dc1, dc2; dc1.Attach(hDC1); dc2.Attach(hDC2); BOOL bSuccess = dcTemp.CreateCompatibleDC(&dc1); // Don't create a bitmap that is too large, or we will spend all our time // swapping int hgtTemp = 0x10000/wid; hgtTemp = min(hgt, max(1, hgtTemp)); CBitmap bmTemp; bSuccess = bSuccess && bmTemp.CreateCompatibleBitmap(&dc1, wid, hgtTemp); bSuccess = bSuccess && dcTemp.SelectObject(&bmTemp)!=NULL; if (!bSuccess) { // Make sure the DC's do not get deleted dc1.Detach(); dc2.Detach(); return; } if (pPalette) { dcTemp.SelectPalette(pPalette, TRUE); dcTemp.RealizePalette(); } int yTemp; for (yTemp=0; yTempRetrieveInt( cb ); ASSERT(cb == sizeof( CRect ) + sizeof( hImgBitmap )); pSeq->RetrieveRect( rect ); int nCursor = pSeq->m_nCursor; pSeq->Retrieve( (BYTE*)&hImgBitmap, sizeof( hImgBitmap ) ); // Wipe out the old handles since we're reusing them in the // new record and we don't what them deleted when this record // is removed! memset(&pSeq->ElementAt(nCursor), 0, sizeof( hImgBitmap )); // Perform undo using these parameters... SetupRubber(m_pImg); SetUndo(m_pImg); // For redo... HideBrush(); ASSERT(m_pImg != NULL); HDC hDC = CreateCompatibleDC(m_pImg->hDC); if (hDC == NULL) { theApp.SetGdiEmergency(); return; } HPALETTE hOldPalette = NULL; HBITMAP hOldBitmap = (HBITMAP)SelectObject(hDC, hImgBitmap); ASSERT(hOldBitmap != NULL); if (m_pImg->m_pPalette) { hOldPalette = SelectPalette( hDC, (HPALETTE)m_pImg->m_pPalette->m_hObject, FALSE ); // Background ?? RealizePalette( hDC ); } // Three blits here swap the image and the undo bits, that // way the undo bits are set up for a redo! ASSERT(m_pImg->hDC != NULL); SwapBitmaps(m_pImg->hDC, rect.left, rect.top, rect.Width(), rect.Height(), hDC, 0, 0, m_pImg->m_pPalette); if (hOldPalette) SelectPalette( hDC, hOldPalette, FALSE ); // Background ?? SelectObject(hDC, hOldBitmap); DeleteDC(hDC); InvalImgRect (m_pImg, &rect); CommitImgRect(m_pImg, &rect); // Record the redo information... theUndo.Insert((BYTE*)&hImgBitmap, sizeof (hImgBitmap)); theUndo.InsertRect(rect); theUndo.InsertInt(sizeof (CRect) + sizeof (hImgBitmap)); theUndo.InsertInt(A_ImageChange); theUndo.InsertPtr(m_pImg->m_pBitmapObj); theUndo.InsertByte(CUndoBmObj::opAction); break; } } /*****************************************************************************/ void CBitmapObj::DeleteUndoAction(CBmObjSequence* pSeq, UINT nActionID) { switch (nActionID) { default: break; case A_ImageChange: CRect rect; HBITMAP hImgBitmap; pSeq->RetrieveRect(rect); pSeq->Retrieve((BYTE*)&hImgBitmap, sizeof (hImgBitmap)); if (hImgBitmap != NULL) DeleteObject(hImgBitmap); break; } } /*****************************************************************************/ BOOL CBitmapObj::FinishUndo(const CRect* pRect) { ASSERT( g_hUndoImgBitmap ); CRect rect; if (pRect == NULL) rect.SetRect(0, 0, m_pImg->cxWidth, m_pImg->cyHeight); else rect = *pRect; HDC hDC1 = NULL; HDC hDC2 = NULL; HPALETTE hOldPalette = NULL; HPALETTE hOldPalette2 = NULL; HBITMAP hImgBitmap = NULL; HBITMAP hOldBitmap1; HBITMAP hOldBitmap2; if (rect.left >= rect.right || rect.top >= rect.bottom) { // Not an error, just nothing to do... return TRUE; } hImgBitmap = CreateCompatibleBitmap( m_pImg->hDC, rect.Width(), rect.Height() ); if (hImgBitmap == NULL) goto LError; if ((hDC1 = CreateCompatibleDC(m_pImg->hDC)) == NULL) goto LError; if ((hDC2 = CreateCompatibleDC(m_pImg->hDC)) == NULL) goto LError; if (m_pImg->m_pPalette) { hOldPalette = SelectPalette(hDC1, (HPALETTE)m_pImg->m_pPalette->m_hObject, FALSE ); RealizePalette( hDC1 ); hOldPalette2 = SelectPalette(hDC2, (HPALETTE)m_pImg->m_pPalette->m_hObject, FALSE ); RealizePalette( hDC2 ); } VERIFY((hOldBitmap1 = (HBITMAP)SelectObject(hDC1, hImgBitmap)) != NULL); VERIFY((hOldBitmap2 = (HBITMAP)SelectObject(hDC2, g_hUndoImgBitmap)) != NULL); BitBlt(hDC1, 0, 0, rect.Width(), rect.Height(), hDC2, rect.left , rect.top, SRCCOPY); SelectObject(hDC1, hOldBitmap1); SelectObject(hDC2, hOldBitmap2); if (hOldPalette != NULL) { ::SelectPalette(hDC1, hOldPalette, FALSE ); // Background ?? } if (hOldPalette2 != NULL) { ::SelectPalette(hDC2, hOldPalette2, FALSE ); // Background ?? } DeleteDC(hDC1); DeleteDC(hDC2); theUndo.BeginUndo( IDS_UNDO_PAINTING ); theUndo.Insert((BYTE*)&hImgBitmap , sizeof (hImgBitmap)); theUndo.InsertRect(rect); theUndo.InsertInt(sizeof (CRect) + sizeof (hImgBitmap)); theUndo.InsertInt(A_ImageChange); theUndo.InsertPtr(this); theUndo.InsertByte(CUndoBmObj::opAction); theUndo.EndUndo(); // NOTE: At this point, we could free the undo bitmaps, but instead // they are left around for next time... return TRUE; LError: if (hImgBitmap != NULL) DeleteObject(hImgBitmap); if (hDC1 != NULL) DeleteDC(hDC1); if (hDC2 != NULL) DeleteDC(hDC2); // REVIEW: Since we couldn't allocate something here, there will // be no way to undo the last operation... What should we do? // Chances are, the system is so low on memory, a message box // giving an option might even fail. // // For now, let's just beep to try to tell the user that whatever // just happend can't be undone. Also, free the image sized bitmaps // so the system has a little free memory. CleanupImgUndo(); MessageBeep(0); #ifdef _DEBUG TRACE(TEXT("Not enough memory to undo image change!\n")); #endif return FALSE; } /*****************************************************************************/ BOOL CBitmapObj::SetIntProp(UINT nPropID, int val) { CWaitCursor waitCursor; // these all take awhile! switch (nPropID) { case P_Width: return SetSizeProp( P_Size, CSize( val, m_nHeight ) ); break; case P_Height: return SetSizeProp( P_Size, CSize( m_nWidth, val ) ); break; case P_Colors: if (CImgTool::GetCurrentID() == IDMB_PICKTOOL || CImgTool::GetCurrentID() == IDMB_PICKRGNTOOL) { CommitSelection( TRUE ); theImgBrush.m_pImg = NULL; } SetUndo( m_pImg ); FinishUndo( NULL ); // Perform the color-count conversion with DIBs DWORD dwSize; ::SelectObject( m_pImg->hDC, m_pImg->hBitmapOld ); LPSTR lpDib = (LPSTR) DibFromBitmap( m_pImg->hBitmap, BI_RGB, m_pImg->cPlanes * m_pImg->cBitCount, m_pImg->m_pPalette, NULL, dwSize, m_pImg->cXPelsPerMeter, m_pImg->cYPelsPerMeter ); ::SelectObject( m_pImg->hDC, m_pImg->hBitmap ); if (lpDib == NULL) { theApp.SetGdiEmergency(); return FALSE; } // Make a new palette appropriate for this colors setting CPalette* pNewPalette = NULL; int iPlanes = (val? 1: ::GetDeviceCaps( m_pImg->hDC, PLANES )); int iBitCnt = (val? 1: ::GetDeviceCaps( m_pImg->hDC, BITSPIXEL )); int iColors = iPlanes * iBitCnt; val = 3; if (theApp.m_bPaletted) switch (iColors) { case 1: pNewPalette = GetStd2Palette(); break; case 4: pNewPalette = GetStd16Palette(); break; case 8: pNewPalette = GetStd256Palette(); break; } switch (iColors) { case 8: val = 2; break; case 4: val = 1; break; case 1: val = 0; break; } HBITMAP hTmpBitmap = CreateBitmap( 1, 1, iPlanes, iBitCnt, NULL ); HBITMAP hNewBitmap = CreateBitmap( m_pImg->cxWidth, m_pImg->cyHeight, iPlanes, iBitCnt, NULL ); if (! hTmpBitmap || ! hNewBitmap) { FreeDib( lpDib ); if (hTmpBitmap) ::DeleteObject( hTmpBitmap ); if (hNewBitmap) ::DeleteObject( hNewBitmap ); if (pNewPalette) delete pNewPalette; theApp.SetGdiEmergency(); return FALSE; } HPALETTE hPalOld = NULL; ::SelectObject( m_pImg->hDC, hTmpBitmap ); if (pNewPalette) { hPalOld = ::SelectPalette( m_pImg->hDC, (HPALETTE)pNewPalette->m_hObject, FALSE ); ::RealizePalette( m_pImg->hDC ); } int iLinesDone = SetDIBits( m_pImg->hDC, hNewBitmap, 0, m_pImg->cyHeight, FindDIBBits( lpDib ), (LPBITMAPINFO)lpDib, DIB_RGB_COLORS ); FreeDib( lpDib ); if (iLinesDone != m_pImg->cyHeight) { ::SelectObject( m_pImg->hDC, m_pImg->hBitmap ); if (hPalOld) { ::SelectPalette( m_pImg->hDC, hPalOld, FALSE ); ::RealizePalette( m_pImg->hDC ); delete pNewPalette; } ::DeleteObject( hTmpBitmap ); ::DeleteObject( hNewBitmap ); theApp.SetGdiEmergency(); return FALSE; } m_pImg->cPlanes = iPlanes; m_pImg->cBitCount = iBitCnt; m_nColors = val; ::SelectObject( m_pImg->hDC, hNewBitmap ); ::DeleteObject( m_pImg->hBitmap ); m_pImg->hBitmap = hNewBitmap; if (m_pImg->m_pPalette) { if (! pNewPalette) { ::SelectPalette( m_pImg->hDC, m_pImg->m_hPalOld, FALSE ); m_pImg->m_hPalOld = NULL; } delete m_pImg->m_pPalette; } m_pImg->m_pPalette = pNewPalette; theApp.m_pPalette = pNewPalette; ::DeleteObject( hTmpBitmap ); DirtyImg( m_pImg ); InvalImgRect( m_pImg, NULL ); // The rubber-banding bitmap is now invalid... if (m_pImg == pRubberImg) { TRACE(TEXT("Clearing rubber\n")); pRubberImg = NULL; SetupRubber( m_pImg ); } if (g_pColors) g_pColors->SetMono( ! m_nColors ); InformDependants( P_Image ); break; } m_bDirty = TRUE; return TRUE; } /*****************************************************************************/ GPT CBitmapObj::GetIntProp(UINT nPropID, int& val) { switch (nPropID) { case P_Colors: val = m_nColors; return valid; break; case P_Image: val = NULL; return valid; // Must return now since this is a fake prop... } return invalid; } /*****************************************************************************/ BOOL CBitmapObj::SetSizeProp(UINT nPropID, const CSize& val) { ASSERT(m_pImg != NULL); if ((CImgTool::GetCurrentID() == IDMB_PICKTOOL) || (CImgTool::GetCurrentID() == IDMB_PICKRGNTOOL)) { CommitSelection(TRUE); theImgBrush.m_pImg = NULL; } switch (nPropID) { default: ASSERT(FALSE); case P_Size: if (val.cx == m_pImg->cxWidth && val.cy == m_pImg->cyHeight) return TRUE; if (val.cx < 1 || val.cy < 1) { CmpMessageBox(IDS_ERROR_BITMAPSIZE, AFX_IDS_APP_TITLE, MB_OK | MB_ICONEXCLAMATION); return FALSE; } CWaitCursor waitCursor; BOOL bStretch = FALSE; CSize curSize; GetImgSize(m_pImg, curSize); bStretch = m_nShrink; SetUndo(m_pImg); CRect undoRect(0, 0, m_pImg->cxWidth, m_pImg->cyHeight); if (! SetImgSize(m_pImg, (CSize)val, bStretch)) { theApp.SetMemoryEmergency(); return FALSE; } FinishUndo(&undoRect); DirtyImg(m_pImg); pRubberImg = NULL; SetupRubber(m_pImg); if (theUndo.IsRecording()) { theUndo.OnSetIntProp(this, P_Width, m_nWidth); theUndo.OnSetIntProp(this, P_Height, m_nHeight); } int nOldWidth = m_nWidth; int nOldHeight = m_nHeight; m_nWidth = val.cx; m_nHeight = val.cy; if (m_nWidth != nOldWidth) InformDependants(P_Width); if (m_nHeight != nOldHeight) InformDependants(P_Height); InformDependants(P_Image); break; } return TRUE; } /*****************************************************************************/ BOOL CBitmapObj::SaveResource( BOOL bClear ) { if (m_pImg == NULL) return TRUE; if (bClear) { if (m_hThing && ! m_pImg->bDirty && ! m_bDirty) return TRUE; // nothing to save m_bDirty |= m_pImg->bDirty; if (m_pImg == theImgBrush.m_pImg) theImgBrush.m_pImg = NULL; if (m_pImg == pRubberImg) pRubberImg = NULL; HideBrush(); } DWORD dwStyle = BI_RGB; int iColors = m_nColors; if (m_nSaveColors >= 0) { iColors = m_nSaveColors; m_nSaveColors = -1; } if (m_bCompressed) { switch (iColors) { case 1: dwStyle = BI_RLE4; break; case 2: dwStyle = BI_RLE8; break; } } switch (iColors) { case 0: iColors = 1; break; case 1: iColors = 4; break; case 2: iColors = 8; break; case 3: iColors = 24; break; default: iColors = 0; break; } HBITMAP hBitmap = m_pImg->hBitmap; HBITMAP hMaskBitmap = NULL; BOOL bNewBitmap = FALSE; HGLOBAL lpDIB; DWORD dwSize; // Icon support is not in application anymore, right? #ifdef ICO_SUPPORT if (IsSaveIcon()) { // build a mask based on the current background color // and make sure the bitmap is the icon size bNewBitmap = SetupForIcon( hBitmap, hMaskBitmap ); if (iColors > 4 || iColors < 1) iColors = 4; } #endif ::SelectObject( m_pImg->hDC, m_pImg->hBitmapOld ); lpDIB = DibFromBitmap( hBitmap, dwStyle, (WORD)iColors, theApp.m_pPalette, hMaskBitmap, dwSize, m_pImg->cXPelsPerMeter, m_pImg->cYPelsPerMeter ); ::SelectObject( m_pImg->hDC, m_pImg->hBitmap ); if (bNewBitmap) { ::DeleteObject( hBitmap ); ::DeleteObject( hMaskBitmap ); } if (lpDIB == NULL) { theApp.SetMemoryEmergency(); return FALSE; } if (m_hThing != NULL) Free(); // We packed the DIB, so the offset will always be right after the palette, // which is implied by this being 0 m_dwOffBits = 0; m_hThing = lpDIB; m_lMemSize = dwSize; if (bClear) m_pImg->bDirty = FALSE; return TRUE; } /*****************************************************************************/ // Icon support is not in application anymore, right? BOOL CBitmapObj::SetupForIcon( HBITMAP& hBitmap, HBITMAP& hMaskBitmap ) { CDC dcIcon; CDC dcMask; CBitmap bmIcon; CBitmap bmMask; CDC* pdcBitmap = CDC::FromHandle( m_pImg->hDC ); CSize sizeIcon( ::GetSystemMetrics( SM_CXICON ), ::GetSystemMetrics( SM_CYICON ) ); BOOL bNewBitmap = FALSE; if (dcIcon.CreateCompatibleDC( pdcBitmap ) && dcMask.CreateCompatibleDC( pdcBitmap ) && bmIcon.CreateCompatibleBitmap( pdcBitmap, sizeIcon.cx, sizeIcon.cy ) && bmMask.CreateBitmap( sizeIcon.cx, sizeIcon.cy, 1, 1, NULL )) { CPalette* ppalOld = NULL; CBitmap* pbmOldIcon = dcIcon.SelectObject( &bmIcon ); CBitmap* pbmOldMask = dcMask.SelectObject( &bmMask ); if (theApp.m_pPalette) { ppalOld = dcIcon.SelectPalette( theApp.m_pPalette, FALSE ); dcIcon.RealizePalette(); } dcIcon.PatBlt( 0, 0, sizeIcon.cx, sizeIcon.cy, WHITENESS ); CBrush brBackGround( crRight ); if (brBackGround.GetSafeHandle() != NULL) { CRect rect( 0, 0, sizeIcon.cx, sizeIcon.cy ); dcIcon.FillRect( &rect, &brBackGround ); brBackGround.DeleteObject(); } int iWidth = min( sizeIcon.cx, m_pImg->cxWidth ); int iHeight = min( sizeIcon.cy, m_pImg->cyHeight ); dcIcon.BitBlt( 0, 0, iWidth, iHeight, pdcBitmap, 0, 0, SRCCOPY ); COLORREF oldBkColor = dcIcon.SetBkColor( crRight ); dcMask.BitBlt( 0, 0, sizeIcon.cx, sizeIcon.cy, &dcIcon, 0, 0, SRCCOPY ); COLORREF cRefFGColorOld = dcMask.SetTextColor( RGB( 0, 0, 0 ) ); COLORREF cRefBKColorOld = dcMask.SetBkColor ( RGB( 255, 255, 255 ) ); dcIcon.BitBlt( 0, 0, sizeIcon.cx, sizeIcon.cy, &dcMask, 0, 0, DSna ); dcMask.SetTextColor( cRefFGColorOld ); dcMask.SetBkColor ( cRefBKColorOld ); dcIcon.SetBkColor ( oldBkColor ); if (ppalOld != NULL) dcIcon.SelectPalette( ppalOld, FALSE ); if (pbmOldIcon != NULL) dcIcon.SelectObject( pbmOldIcon ); if (pbmOldMask != NULL) dcMask.SelectObject( pbmOldMask ); hBitmap = (HBITMAP)bmIcon.Detach(); hMaskBitmap = (HBITMAP)bmMask.Detach(); bNewBitmap = TRUE; } if (dcIcon.GetSafeHdc() != NULL) dcIcon.DeleteDC(); if (dcMask.GetSafeHdc() != NULL) dcMask.DeleteDC(); if (bmIcon.GetSafeHandle() != NULL) bmIcon.DeleteObject(); if (bmMask.GetSafeHandle() != NULL) bmMask.DeleteObject(); return bNewBitmap; } /*****************************************************************************/