?? pictureex.cpp
字號:
{
UINT nRet = 0;
UINT nCurOffset = nStartingOffset;
while (m_pRawData[nCurOffset] != 0)
{
nRet += m_pRawData[nCurOffset]+1;
nCurOffset += m_pRawData[nCurOffset]+1;
};
return nRet+1;
}
enum CPictureEx::GIFBlockTypes CPictureEx::GetNextBlock() const
{
switch(m_pRawData[m_nCurrOffset])
{
case 0x21:
// extension block
switch(m_pRawData[m_nCurrOffset+1])
{
case 0x01:
// plain text extension
return BLOCK_PLAINTEXT;
break;
case 0xF9:
// graphic control extension
return BLOCK_CONTROLEXT;
break;
case 0xFE:
// comment extension
return BLOCK_COMMEXT;
break;
case 0xFF:
// application extension
return BLOCK_APPEXT;
break;
};
break;
case 0x3B:
// trailer
return BLOCK_TRAILER;
break;
case 0x2C:
// image data
return BLOCK_IMAGE;
break;
};
return BLOCK_UNKNOWN;
}
BOOL CPictureEx::SkipNextBlock()
{
if (!m_pRawData) return FALSE;
int nLen = GetNextBlockLen();
if ((nLen <= 0) || ((m_nCurrOffset+nLen) > m_nDataSize))
return FALSE;
m_nCurrOffset += nLen;
return TRUE;
}
int CPictureEx::GetNextBlockLen() const
{
GIFBlockTypes nBlock = GetNextBlock();
int nTmp;
switch(nBlock)
{
case BLOCK_UNKNOWN:
return -1;
break;
case BLOCK_TRAILER:
return 1;
break;
case BLOCK_APPEXT:
nTmp = GetSubBlocksLen(m_nCurrOffset+sizeof(TGIFAppExtension));
if (nTmp > 0)
return sizeof(TGIFAppExtension)+nTmp;
break;
case BLOCK_COMMEXT:
nTmp = GetSubBlocksLen(m_nCurrOffset+sizeof(TGIFCommentExt));
if (nTmp > 0)
return sizeof(TGIFCommentExt)+nTmp;
break;
case BLOCK_CONTROLEXT:
return sizeof(TGIFControlExt);
break;
case BLOCK_PLAINTEXT:
nTmp = GetSubBlocksLen(m_nCurrOffset+sizeof(TGIFPlainTextExt));
if (nTmp > 0)
return sizeof(TGIFPlainTextExt)+nTmp;
break;
case BLOCK_IMAGE:
TGIFImageDescriptor *pIDescr =
reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]);
int nLCTSize = (int)
(pIDescr->GetPackedValue(ID_PACKED_LOCALCT)*3*
pow(2,pIDescr->GetPackedValue(ID_PACKED_LOCALCTSIZE)+1));
int nTmp = GetSubBlocksLen(m_nCurrOffset+
sizeof(TGIFImageDescriptor) + nLCTSize + 1);
if (nTmp > 0)
return sizeof(TGIFImageDescriptor) + nLCTSize + 1 + nTmp;
break;
};
return 0;
}
UINT WINAPI CPictureEx::_ThreadAnimation(LPVOID pParam)
{
ASSERT(pParam);
CPictureEx *pPic = reinterpret_cast<CPictureEx *> (pParam);
pPic->ThreadAnimation();
// this thread has finished its work so we close the handle
CloseHandle(pPic->m_hThread);
// and init the handle to zero (so that Stop() doesn't Wait on it)
pPic->m_hThread = 0;
return 0;
}
void CPictureEx::ThreadAnimation()
{
int nTemp = 0;
while (!m_bExitThread)
{
if (m_arrFrames[nTemp].m_pPicture)
{
///////////////////////////////////////////////////////
// Before rendering a frame we should take care of what's
// behind that frame. TFrame::m_nDisposal will be our guide:
// 0 - no disposal specified (do nothing)
// 1 - do not dispose (again, do nothing)
// 2 - restore to background color (m_clrBackground)
// 3 - restore to previous
//////// disposal method #3
HDC hMemDC = NULL;
HBITMAP hMemBM = NULL, hOldBM;
if (m_arrFrames[nTemp].m_nDisposal == 3)
{
// prepare a memory DC and store the background in it
hMemDC = CreateCompatibleDC(m_hMemDC);
hMemBM = CreateCompatibleBitmap(m_hMemDC,
m_arrFrames[nTemp].m_frameSize.cx,
m_arrFrames[nTemp].m_frameSize.cy);
if (hMemDC && hMemBM)
{
hOldBM = reinterpret_cast<HBITMAP> (SelectObject(hMemDC,hMemBM));
BitBlt(hMemDC,0,0,
m_arrFrames[nTemp].m_frameSize.cx,
m_arrFrames[nTemp].m_frameSize.cy,
m_hMemDC,
m_arrFrames[nTemp].m_frameOffset.cx,
m_arrFrames[nTemp].m_frameOffset.cy,
SRCCOPY);
};
};
///////////////////////
long hmWidth;
long hmHeight;
m_arrFrames[nTemp].m_pPicture->get_Width(&hmWidth);
m_arrFrames[nTemp].m_pPicture->get_Height(&hmHeight);
if (m_arrFrames[nTemp].m_pPicture->Render(m_hMemDC,
m_arrFrames[nTemp].m_frameOffset.cx,
m_arrFrames[nTemp].m_frameOffset.cy,
m_arrFrames[nTemp].m_frameSize.cx,
m_arrFrames[nTemp].m_frameSize.cy,
0, hmHeight, hmWidth, -hmHeight, NULL) == S_OK)
{
Invalidate(FALSE);
};
if (m_bExitThread)
{
if (hMemDC)
{
// dispose local variables
SelectObject(hMemDC,hOldBM);
DeleteDC(hMemDC);
DeleteObject(hMemBM);
};
break;
};
// if the delay time is too short (like in old GIFs), wait for 100ms
if (m_arrFrames[nTemp].m_nDelay < 5)
WaitForSingleObject(m_hExitEvent, 100);
else
WaitForSingleObject(m_hExitEvent, 10*m_arrFrames[nTemp].m_nDelay);
if (m_bExitThread)
{
if (hMemDC)
{
// dispose local variables
SelectObject(hMemDC,hOldBM);
DeleteDC(hMemDC);
DeleteObject(hMemBM);
};
break;
};
// disposal method #2
if (m_arrFrames[nTemp].m_nDisposal == 2)
{
HBRUSH hBrush = CreateSolidBrush(m_clrBackground);
if (hBrush)
{
RECT rect = {
m_arrFrames[nTemp].m_frameOffset.cx,
m_arrFrames[nTemp].m_frameOffset.cy,
m_arrFrames[nTemp].m_frameOffset.cx + m_arrFrames[nTemp].m_frameSize.cx,
m_arrFrames[nTemp].m_frameOffset.cy + m_arrFrames[nTemp].m_frameSize.cy };
FillRect(m_hMemDC,&rect,hBrush);
DeleteObject(hBrush);
};
}
else
if (hMemDC && (m_arrFrames[nTemp].m_nDisposal == 3) )
{
// put it back
BitBlt(m_hMemDC,
m_arrFrames[nTemp].m_frameOffset.cx,
m_arrFrames[nTemp].m_frameOffset.cy,
m_arrFrames[nTemp].m_frameSize.cx,
m_arrFrames[nTemp].m_frameSize.cy,
hMemDC,0,0, SRCCOPY);
// dispose local variables
SelectObject(hMemDC,hOldBM);
DeleteDC(hMemDC);
DeleteObject(hMemBM);
};
};
nTemp++;
if (nTemp == m_arrFrames.size())
{
nTemp = 0;
// init the screen for the first frame,
HBRUSH hBrush = CreateSolidBrush(m_clrBackground);
if (hBrush)
{
RECT rect = {0,0,m_PictureSize.cx,m_PictureSize.cy};
FillRect(m_hMemDC,&rect,hBrush);
DeleteObject(hBrush);
};
};
};
}
void CPictureEx::Stop()
{
m_bExitThread = TRUE;
SetEvent(m_hExitEvent);
if (m_hThread)
{
// we'll wait for 5 seconds then continue execution
WaitForSingleObject(m_hThread,5000);
CloseHandle(m_hThread);
m_hThread = NULL;
}
// make it possible to Draw() again
ResetEvent(m_hExitEvent);
m_bExitThread = FALSE;
}
HGLOBAL CPictureEx::GetNextGraphicBlock(UINT *pBlockLen,
UINT *pDelay, SIZE *pBlockSize, SIZE *pBlockOffset,
UINT *pDisposal)
{
if (!m_pRawData) return NULL;
// GIF header + LSDescriptor [+ GCT] [+ Control block] + Data
*pDisposal = 0;
enum GIFBlockTypes nBlock;
nBlock = GetNextBlock();
while (
(nBlock != BLOCK_CONTROLEXT) &&
(nBlock != BLOCK_IMAGE) &&
(nBlock != BLOCK_PLAINTEXT) &&
(nBlock != BLOCK_UNKNOWN) &&
(nBlock != BLOCK_TRAILER)
)
{
if (!SkipNextBlock()) return NULL;
nBlock = GetNextBlock();
};
if ((nBlock == BLOCK_UNKNOWN) ||
(nBlock == BLOCK_TRAILER))
return NULL;
// it's either a control ext.block, an image or a plain text
int nStart = m_nCurrOffset;
int nBlockLen = GetNextBlockLen();
if (nBlockLen <= 0) return NULL;
if (nBlock == BLOCK_CONTROLEXT)
{
// get the following data
TGIFControlExt *pControl =
reinterpret_cast<TGIFControlExt *> (&m_pRawData[m_nCurrOffset]);
// store delay time
*pDelay = pControl->m_wDelayTime;
// store disposal method
*pDisposal = pControl->GetPackedValue(GCX_PACKED_DISPOSAL);
if (!SkipNextBlock()) return NULL;
nBlock = GetNextBlock();
// skip everything until we find data to display
// (image block or plain-text block)
while (
(nBlock != BLOCK_IMAGE) &&
(nBlock != BLOCK_PLAINTEXT) &&
(nBlock != BLOCK_UNKNOWN) &&
(nBlock != BLOCK_TRAILER)
)
{
if (!SkipNextBlock()) return NULL;
nBlock = GetNextBlock();
nBlockLen += GetNextBlockLen();
};
if ((nBlock == BLOCK_UNKNOWN) || (nBlock == BLOCK_TRAILER))
return NULL;
nBlockLen += GetNextBlockLen();
}
else
*pDelay = -1; // to indicate that there was no delay value
if (nBlock == BLOCK_IMAGE)
{
// store size and offsets
TGIFImageDescriptor *pImage =
reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]);
pBlockSize->cx = pImage->m_wWidth;
pBlockSize->cy = pImage->m_wHeight;
pBlockOffset->cx = pImage->m_wLeftPos;
pBlockOffset->cy = pImage->m_wTopPos;
};
if (!SkipNextBlock()) return NULL;
HGLOBAL hGlobal = GlobalAlloc(GMEM_FIXED,
sizeof(TGIFHeader) +
sizeof(TGIFLSDescriptor) +
m_nGlobalCTSize +
nBlockLen +
1); // for the trailer
if (!hGlobal) return NULL;
int nOffset = 0;
// GMEM_FIXED means we get a pointer
unsigned char *pGlobal = reinterpret_cast<unsigned char *> (hGlobal);
CopyMemory(pGlobal,m_pRawData,
sizeof(TGIFHeader)+sizeof(TGIFLSDescriptor)+m_nGlobalCTSize);
nOffset += sizeof(TGIFHeader)+sizeof(TGIFLSDescriptor)+m_nGlobalCTSize;
CopyMemory(pGlobal + nOffset,&m_pRawData[nStart], nBlockLen);
nOffset += nBlockLen;
pGlobal[nOffset] = 0x3B; // trailer
nOffset++;
*pBlockLen = nOffset;
return hGlobal;
}
BOOL CPictureEx::IsGIF() const
{
return m_bIsGIF;
}
BOOL CPictureEx::IsAnimatedGIF() const
{
return (m_bIsGIF && (m_arrFrames.size() > 1));
}
int CPictureEx::GetFrameCount() const
{
if (!IsAnimatedGIF())
return 0;
return m_arrFrames.size();
}
COLORREF CPictureEx::GetBkColor() const
{
return m_clrBackground;
}
void CPictureEx::OnPaint()
{
CPaintDC dc(this); // device context for painting
::BitBlt(dc.m_hDC,0,0,m_PictureSize.cx,m_PictureSize.cy,
m_hMemDC,0,0,SRCCOPY);
}
BOOL CPictureEx::PrepareDC(int nWidth, int nHeight)
{
SetWindowPos(NULL,0,0,nWidth,nHeight,SWP_NOMOVE | SWP_NOZORDER);
HDC hWinDC = ::GetDC(m_hWnd);
if (!hWinDC) return FALSE;
m_hMemDC = CreateCompatibleDC(hWinDC);
if (!m_hMemDC)
{
::ReleaseDC(m_hWnd,hWinDC);
return FALSE;
};
m_hBitmap = CreateCompatibleBitmap(hWinDC,nWidth,nHeight);
if (!m_hBitmap)
{
::ReleaseDC(m_hWnd,hWinDC);
::DeleteDC(m_hMemDC);
return FALSE;
};
m_hOldBitmap = reinterpret_cast<HBITMAP>
(SelectObject(m_hMemDC,m_hBitmap));
// fill the background
m_clrBackground = GetSysColor(COLOR_3DFACE);
RECT rect = {0,0,nWidth,nHeight};
FillRect(m_hMemDC,&rect,(HBRUSH)(COLOR_WINDOW));
m_bIsInitialized = TRUE;
return TRUE;
}
void CPictureEx::OnDestroy()
{
Stop();
CStatic::OnDestroy();
}
void CPictureEx::SetBkColor(COLORREF clr)
{
if (!m_bIsInitialized) return;
m_clrBackground = clr;
HBRUSH hBrush = CreateSolidBrush(clr);
if (hBrush)
{
RECT rect = {0,0,m_PictureSize.cx,m_PictureSize.cy};
FillRect(m_hMemDC,&rect,hBrush);
DeleteObject(hBrush);
};
}
#ifdef GIF_TRACING
void CPictureEx::WriteDataOnDisk(CString szFileName, HGLOBAL hData, DWORD dwSize)
{
CFile file;
if (!file.Open(szFileName,
CFile::modeCreate |
CFile::modeWrite |
CFile::shareDenyNone))
{
TRACE(_T("WriteData: Error creating file %s\n"),szFileName);
return;
};
char *pData = reinterpret_cast<char *> (GlobalLock(hData));
if (!pData)
{
TRACE(_T("WriteData: Error locking memory\n"));
return;
};
TRY
{
file.Write(pData,dwSize);
}
CATCH(CFileException, e);
{
TRACE(_T("WriteData: An exception occured while writing to the file %s\n"),
szFileName);
//e->Delete(); // DO NOT CALL THIS, CAN CAUSE CRASH. majun, 7.31
GlobalUnlock(hData);
file.Close();
return;
}
END_CATCH
GlobalUnlock(hData);
file.Close();
}
void CPictureEx::EnumGIFBlocks()
{
enum GIFBlockTypes nBlock;
ResetDataPointer();
while(m_nCurrOffset < m_nDataSize)
{
nBlock = GetNextBlock();
switch(nBlock)
{
case BLOCK_UNKNOWN:
TRACE(_T("- Unknown block\n"));
return;
break;
case BLOCK_TRAILER:
TRACE(_T("- Trailer block\n"));
break;
case BLOCK_APPEXT:
TRACE(_T("- Application extension block\n"));
break;
case BLOCK_COMMEXT:
TRACE(_T("- Comment extension block\n"));
break;
case BLOCK_CONTROLEXT:
{
TGIFControlExt *pControl =
reinterpret_cast<TGIFControlExt *> (&m_pRawData[m_nCurrOffset]);
TRACE(_T("- Graphic control extension block (delay %d, disposal %d)\n"),
pControl->m_wDelayTime, pControl->GetPackedValue(GCX_PACKED_DISPOSAL));
};
break;
case BLOCK_PLAINTEXT:
TRACE(_T("- Plain text extension block\n"));
break;
case BLOCK_IMAGE:
TGIFImageDescriptor *pIDescr =
reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]);
TRACE(_T("- Image data block (%dx%d %d,%d)\n"),
pIDescr->m_wWidth,
pIDescr->m_wHeight,
pIDescr->m_wLeftPos,
pIDescr->m_wTopPos);
break;
};
SkipNextBlock();
};
TRACE(_T("\n"));
}
#endif // GIF_TRACING
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -