PictureEx.cpp
上传用户:xiuanze55
上传日期:2013-06-16
资源大小:85k
文件大小:27k
源码类别:

其他数据库

开发平台:

Visual C++

  1. //////////////////////////////////////////////////////////////////////
  2. // PictureEx.cpp: implementation of the CPicture class.
  3. //
  4. // Picture displaying control with support for the following formats:
  5. // GIF (including animated GIF87a and GIF89a), JPEG, BMP, WMF, ICO, CUR
  6. // 
  7. // Written by Oleg Bykov (oleg_bykoff@rsdn.ru)
  8. //
  9. // Modified by jingzhou Xu
  10. //////////////////////////////////////////////////////////////////////
  11. #include "stdafx.h"
  12. #include "PictureEx.h"
  13. #include <math.h>
  14. #include <process.h>
  15. #ifdef _DEBUG
  16. #undef THIS_FILE
  17. static char THIS_FILE[]=__FILE__;
  18. #define new DEBUG_NEW
  19. #endif
  20. //////////////////////////////////////////////////////////////////////
  21. // Nested structures member functions
  22. //////////////////////////////////////////////////////////////////////
  23. inline int CPictureEx::TGIFControlExt::GetPackedValue(enum ControlExtValues Value)
  24. {
  25. int nRet = (int)m_cPacked;
  26. switch (Value)
  27. {
  28. case GCX_PACKED_DISPOSAL:
  29. nRet = (nRet & 28) >> 2;
  30. break;
  31. case GCX_PACKED_USERINPUT:
  32. nRet = (nRet & 2) >> 1;
  33. break;
  34. case GCX_PACKED_TRANSPCOLOR:
  35. nRet &= 1;
  36. break;
  37. };
  38. return nRet;
  39. }
  40. inline int CPictureEx::TGIFLSDescriptor::GetPackedValue(enum LSDPackedValues Value)
  41. {
  42. int nRet = (int)m_cPacked;
  43. switch (Value)
  44. {
  45. case LSD_PACKED_GLOBALCT:
  46. nRet = nRet >> 7;
  47. break;
  48. case LSD_PACKED_CRESOLUTION:
  49. nRet = ((nRet & 0x70) >> 4) + 1;
  50. break;
  51. case LSD_PACKED_SORT:
  52. nRet = (nRet & 8) >> 3;
  53. break;
  54. case LSD_PACKED_GLOBALCTSIZE:
  55. nRet &= 7;
  56. break;
  57. };
  58. return nRet;
  59. }
  60. inline int CPictureEx::TGIFImageDescriptor::GetPackedValue(enum IDPackedValues Value)
  61. {
  62. int nRet = (int)m_cPacked;
  63. switch (Value)
  64. {
  65. case ID_PACKED_LOCALCT:
  66. nRet >>= 7;
  67. break;
  68. case ID_PACKED_INTERLACE:
  69. nRet = ((nRet & 0x40) >> 6);
  70. break;
  71. case ID_PACKED_SORT:
  72. nRet = (nRet & 0x20) >> 5;
  73. break;
  74. case ID_PACKED_LOCALCTSIZE:
  75. nRet &= 7;
  76. break;
  77. };
  78. return nRet;
  79. }
  80. //////////////////////////////////////////////////////////////////////
  81. // Construction/Destruction
  82. //////////////////////////////////////////////////////////////////////
  83. CPictureEx::CPictureEx()
  84. {
  85. // check structures size
  86. ASSERT(sizeof(TGIFImageDescriptor) == 10);
  87. ASSERT(sizeof(TGIFAppExtension)    == 14);
  88. ASSERT(sizeof(TGIFPlainTextExt)    == 15);
  89. ASSERT(sizeof(TGIFLSDescriptor)    ==  7);
  90. ASSERT(sizeof(TGIFControlExt)    ==  8);
  91. ASSERT(sizeof(TGIFCommentExt)    ==  2);
  92. ASSERT(sizeof(TGIFHeader)    ==  6);
  93. m_pGIFLSDescriptor = NULL;
  94. m_pGIFHeader    = NULL;
  95. m_pPicture    = NULL;
  96. m_pRawData    = NULL;
  97. m_hThread    = NULL;
  98. m_hBitmap          = NULL;
  99. m_hMemDC    = NULL;
  100. m_bIsInitialized   = FALSE;
  101. m_bExitThread    = FALSE;
  102. m_bIsGIF    = FALSE;
  103. m_clrBackground    = RGB(255,255,255); // white by default
  104. m_nGlobalCTSize    = 0;
  105. m_nCurrOffset    = 0;
  106. m_nDataSize    = 0;
  107. m_PictureSize.cx = m_PictureSize.cy = 0;
  108. m_hExitEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
  109. }
  110. CPictureEx::~CPictureEx()
  111. {
  112. UnLoad();
  113. CloseHandle(m_hExitEvent);
  114. }
  115. BEGIN_MESSAGE_MAP(CPictureEx, CStatic)
  116. //{{AFX_MSG_MAP(CMyStatic)
  117. ON_WM_DESTROY()
  118. ON_WM_PAINT()
  119. //}}AFX_MSG_MAP
  120. END_MESSAGE_MAP()
  121. BOOL CPictureEx::Load(HGLOBAL hGlobal, DWORD dwSize)
  122. {
  123. IStream *pStream = NULL;
  124. UnLoad();
  125. if (!(m_pRawData = reinterpret_cast<unsigned char*> (GlobalLock(hGlobal))) )
  126. {
  127. TRACE(_T("Load: Error locking memoryn"));
  128. return FALSE;
  129. };
  130. m_nDataSize = dwSize;
  131. m_pGIFHeader = reinterpret_cast<TGIFHeader *> (m_pRawData);
  132. if ((memcmp(&m_pGIFHeader->m_cSignature,"GIF",3) != 0) &&
  133. ((memcmp(&m_pGIFHeader->m_cVersion,"87a",3) != 0) ||
  134.  (memcmp(&m_pGIFHeader->m_cVersion,"89a",3) != 0)) )
  135. {
  136. // it's neither GIF87a nor GIF89a
  137. // do the default processing
  138. // clear GIF variables
  139. m_pRawData = NULL;
  140. GlobalUnlock(hGlobal);
  141. // don't delete memory on object's release
  142. if (CreateStreamOnHGlobal(hGlobal,FALSE,&pStream) != S_OK)
  143. return FALSE;
  144. if (OleLoadPicture(pStream,dwSize,FALSE,IID_IPicture,
  145. reinterpret_cast<LPVOID *>(&m_pPicture)) != S_OK)
  146. {
  147. pStream->Release();
  148. return FALSE;
  149. };
  150. pStream->Release();
  151. // store picture's size
  152. long hmWidth;
  153. long hmHeight;
  154. m_pPicture->get_Width(&hmWidth);
  155. m_pPicture->get_Height(&hmHeight);
  156. HDC hDC = ::GetDC(m_hWnd);
  157. m_PictureSize.cx = MulDiv(hmWidth, GetDeviceCaps(hDC,LOGPIXELSX), 2540);
  158. m_PictureSize.cy = MulDiv(hmHeight, GetDeviceCaps(hDC,LOGPIXELSY), 2540);
  159. ::ReleaseDC(m_hWnd,hDC);
  160. }
  161. else
  162. {
  163. // it's a GIF
  164. m_bIsGIF = TRUE;
  165. m_pGIFLSDescriptor = reinterpret_cast<TGIFLSDescriptor *>
  166. (m_pRawData + sizeof(TGIFHeader));
  167. if (m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCT) == 1)
  168. {
  169. // calculate the globat color table size
  170. m_nGlobalCTSize = static_cast<int>
  171. (3*pow(2,m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCTSIZE)+1));
  172. // get the background color if GCT is present
  173. unsigned char *pBkClr = m_pRawData + sizeof(TGIFHeader) + 
  174. sizeof(TGIFLSDescriptor) + 3*m_pGIFLSDescriptor->m_cBkIndex;
  175. m_clrBackground = RGB(pBkClr[0],pBkClr[1],pBkClr[2]);
  176. };
  177. // store the picture's size
  178. m_PictureSize.cx = m_pGIFLSDescriptor->m_wWidth;
  179. m_PictureSize.cy = m_pGIFLSDescriptor->m_wHeight;
  180. // determine frame count for this picture
  181. UINT nFrameCount=0;
  182. ResetDataPointer();
  183. while (SkipNextGraphicBlock())
  184. nFrameCount++;
  185. #ifdef GIF_TRACING
  186. TRACE(
  187. _T(" -= GIF encounteredn"
  188.    "Logical Screen dimensions = %dx%dn"
  189.    "Global color table = %dn"
  190.    "Color depth = %dn"
  191.    "Sort flag = %dn"
  192.    "Size of Global Color Table = %dn"
  193.    "Background color index = %dn"
  194.    "Pixel aspect ratio = %dn"
  195.    "Frame count = %dn"
  196.    "Background color = %06Xhnn"
  197.   ),
  198. m_pGIFLSDescriptor->m_wWidth,
  199. m_pGIFLSDescriptor->m_wHeight,
  200. m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCT),
  201. m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_CRESOLUTION),
  202. m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_SORT),
  203. m_pGIFLSDescriptor->GetPackedValue(LSD_PACKED_GLOBALCTSIZE),
  204. m_pGIFLSDescriptor->m_cBkIndex,
  205. m_pGIFLSDescriptor->m_cPixelAspect,
  206. nFrameCount,
  207. m_clrBackground
  208. );
  209. EnumGIFBlocks();
  210. #endif
  211. if (nFrameCount == 0) // it's an empty GIF!
  212. {
  213. m_pRawData = NULL;
  214. GlobalUnlock(hGlobal);
  215. return FALSE;
  216. };
  217. // now check the frame count
  218. // if there's only one frame, no need to animate this GIF
  219. // therefore, treat it like any other pic
  220. if (nFrameCount == 1)
  221. {
  222. // clear GIF variables
  223. m_pRawData = NULL;
  224. GlobalUnlock(hGlobal);
  225. // don't delete memory on object's release
  226. if (CreateStreamOnHGlobal(hGlobal,FALSE,&pStream) != S_OK)
  227. return FALSE;
  228. if (OleLoadPicture(pStream,dwSize,FALSE,IID_IPicture,
  229. (LPVOID *)&m_pPicture) != S_OK)
  230. {
  231. pStream->Release();
  232. return FALSE;
  233. };
  234. pStream->Release();
  235. }
  236. else
  237. {
  238. // if, on the contrary, there are several frames
  239. // then store separate frames in an array
  240. TFrame frame;
  241. UINT nBlockLen;
  242. HGLOBAL hFrameData;
  243. UINT nCurFrame = 0;
  244. ResetDataPointer();
  245. while (hFrameData = GetNextGraphicBlock(&nBlockLen,
  246. &frame.m_nDelay, &frame.m_frameSize,
  247. &frame.m_frameOffset, &frame.m_nDisposal) )
  248. {
  249. #ifdef GIF_TRACING
  250. //////////////////////////////////////////////
  251. // uncomment the following strings if you want 
  252. // to write separate frames on disk
  253. //
  254. // CString szName;
  255. // szName.Format(_T("%.4d.gif"),nCurFrame);
  256. // WriteDataOnDisk(szName,hFrameData,nBlockLen);
  257. // nCurFrame++;
  258. #endif // GIF_TRACING
  259. IStream *pStream = NULL;
  260. // delete memory on object's release
  261. if (CreateStreamOnHGlobal(hFrameData,TRUE,&pStream) != S_OK)
  262. {
  263. GlobalFree(hFrameData);
  264. continue;
  265. };
  266. if (OleLoadPicture(pStream,nBlockLen,FALSE,
  267. IID_IPicture,
  268. reinterpret_cast<LPVOID *>(&frame.m_pPicture)) != S_OK)
  269. {
  270. pStream->Release();
  271. continue;
  272. };
  273. pStream->Release();
  274. // everything went well, add this frame
  275. m_arrFrames.push_back(frame);
  276. };
  277. // clean after ourselves
  278. m_pRawData = NULL;
  279. GlobalUnlock(hGlobal);
  280. if (m_arrFrames.empty()) // couldn't load any frames
  281. return FALSE;
  282. };
  283. }; // if (!IsGIF...
  284. return PrepareDC(m_PictureSize.cx,m_PictureSize.cy);
  285. }
  286. void CPictureEx::UnLoad()
  287. {
  288. Stop();
  289. if (m_pPicture)
  290. {
  291. m_pPicture->Release();
  292. m_pPicture = NULL;
  293. };
  294. std::vector<TFrame>::iterator it;
  295. for (it=m_arrFrames.begin();it<m_arrFrames.end();it++)
  296. (*it).m_pPicture->Release();
  297. m_arrFrames.clear();
  298. if (m_hMemDC)
  299. {
  300. SelectObject(m_hMemDC,m_hOldBitmap);
  301. ::DeleteDC(m_hMemDC);
  302. ::DeleteObject(m_hBitmap);
  303. m_hMemDC  = NULL;
  304. m_hBitmap = NULL;
  305. };
  306. m_pGIFLSDescriptor = NULL;
  307. m_pGIFHeader    = NULL;
  308. m_pRawData    = NULL;
  309. m_hThread    = NULL;
  310. m_bIsInitialized   = FALSE;
  311. m_bExitThread    = FALSE;
  312. m_bIsGIF    = FALSE;
  313. m_clrBackground    = RGB(255,255,255); // white by default
  314. m_nGlobalCTSize    = 0;
  315. m_nCurrOffset    = 0;
  316. m_nDataSize    = 0;
  317. }
  318. BOOL CPictureEx::Draw()
  319. {
  320. if (!m_bIsInitialized)
  321. {
  322. TRACE(_T("Call of one the CPictureEx::Load() member functions before calling Draw()n"));
  323. return FALSE;
  324. };
  325. if (IsAnimatedGIF())
  326. {
  327. // the picture needs some animation
  328. // we'll start the thread that will handle it for us
  329. unsigned int nDummy;
  330. m_hThread = (HANDLE) _beginthreadex(NULL,0,_ThreadAnimation,this,
  331. CREATE_SUSPENDED,&nDummy);
  332. if (!m_hThread)
  333. {
  334. TRACE(_T("Draw: Couldn't start a GIF animation threadn"));
  335. return FALSE;
  336. else 
  337. ResumeThread(m_hThread);
  338. else
  339. {
  340. if (m_pPicture)
  341. {
  342. long hmWidth;
  343. long hmHeight;
  344. m_pPicture->get_Width(&hmWidth);
  345. m_pPicture->get_Height(&hmHeight);
  346. if (m_pPicture->Render(m_hMemDC, 0, 0, m_PictureSize.cx, m_PictureSize.cy, 
  347. 0, hmHeight, hmWidth, -hmHeight, NULL) == S_OK)
  348. {
  349. Invalidate(FALSE);
  350. return TRUE;
  351. };
  352. };
  353. };
  354. return FALSE;
  355. }
  356. SIZE CPictureEx::GetSize() const
  357. {
  358. return m_PictureSize;
  359. }
  360. BOOL CPictureEx::Load(LPCTSTR szFileName)
  361. {
  362. ASSERT(szFileName);
  363. CFile file;
  364. HGLOBAL hGlobal;
  365. DWORD dwSize;
  366. if (!file.Open(szFileName,
  367. CFile::modeRead | 
  368. CFile::shareDenyWrite) )
  369. {
  370. TRACE(_T("Load (file): Error opening file %sn"),szFileName);
  371. return FALSE;
  372. };
  373. dwSize = file.GetLength();
  374. hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD,dwSize);
  375. if (!hGlobal)
  376. {
  377. TRACE(_T("Load (file): Error allocating memoryn"));
  378. return FALSE;
  379. };
  380. char *pData = reinterpret_cast<char*>(GlobalLock(hGlobal));
  381. if (!pData)
  382. {
  383. TRACE(_T("Load (file): Error locking memoryn"));
  384. GlobalFree(hGlobal);
  385. return FALSE;
  386. };
  387. TRY
  388. {
  389. file.Read(pData,dwSize);
  390. }
  391. CATCH(CFileException, e);                                          
  392. {
  393. TRACE(_T("Load (file): An exception occured while reading the file %sn"),
  394. szFileName);
  395. GlobalFree(hGlobal);
  396. //e->Delete();   // DO NOT CALL THIS, CAN CAUSE CRASH.  majun, 7.31
  397. file.Close();
  398. return FALSE;
  399. }
  400. END_CATCH
  401. GlobalUnlock(hGlobal);
  402. file.Close();
  403. BOOL bRetValue = Load(hGlobal,dwSize);
  404. GlobalFree(hGlobal);
  405. return bRetValue;
  406. }
  407. BOOL CPictureEx::Load(LPCTSTR szResourceName, LPCTSTR szResourceType)
  408. {
  409. ASSERT(szResourceName);
  410. ASSERT(szResourceType);
  411. HRSRC hPicture = FindResource(AfxGetResourceHandle(),szResourceName,szResourceType);
  412. HGLOBAL hResData;
  413. if (!hPicture || !(hResData = LoadResource(AfxGetResourceHandle(),hPicture)))
  414. {
  415. TRACE(_T("Load (resource): Error loading resource %sn"),szResourceName);
  416. return FALSE;
  417. };
  418. DWORD dwSize = SizeofResource(AfxGetResourceHandle(),hPicture);
  419. // hResData is not the real HGLOBAL (we can't lock it)
  420. // let's make it real
  421. HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD,dwSize);
  422. if (!hGlobal)
  423. {
  424. TRACE("Load (resource): Error allocating memoryn");
  425. FreeResource(hResData);
  426. return FALSE;
  427. };
  428. char *pDest = reinterpret_cast<char *> (GlobalLock(hGlobal));
  429. char *pSrc = reinterpret_cast<char *> (LockResource(hResData));
  430. if (!pSrc || !pDest)
  431. {
  432. TRACE(_T("Load (resource): Error locking memoryn"));
  433. GlobalFree(hGlobal);
  434. FreeResource(hResData);
  435. return FALSE;
  436. };
  437. CopyMemory(pDest,pSrc,dwSize);
  438. FreeResource(hResData);
  439. GlobalUnlock(hGlobal);
  440. BOOL bRetValue = Load(hGlobal,dwSize);
  441. GlobalFree(hGlobal);
  442. return bRetValue;
  443. }
  444. void CPictureEx::ResetDataPointer()
  445. {
  446. // skip header and logical screen descriptor
  447. m_nCurrOffset = 
  448. sizeof(TGIFHeader)+sizeof(TGIFLSDescriptor)+m_nGlobalCTSize;
  449. }
  450. BOOL CPictureEx::SkipNextGraphicBlock()
  451. {
  452. if (!m_pRawData) return FALSE;
  453. // GIF header + LSDescriptor [+ GCT] [+ Control block] + Data
  454. enum GIFBlockTypes nBlock;
  455. nBlock = GetNextBlock();
  456. while ((nBlock != BLOCK_CONTROLEXT) &&
  457.    (nBlock != BLOCK_IMAGE) &&
  458.    (nBlock != BLOCK_PLAINTEXT) &&
  459.    (nBlock != BLOCK_UNKNOWN) &&
  460.    (nBlock != BLOCK_TRAILER) )
  461. {
  462. if (!SkipNextBlock()) return NULL;
  463. nBlock = GetNextBlock();
  464. };
  465. if ((nBlock == BLOCK_UNKNOWN) ||
  466. (nBlock == BLOCK_TRAILER))
  467. return FALSE;
  468. // it's either a control ext.block, an image or a plain text
  469. if (GetNextBlockLen() <= 0) return FALSE;
  470. if (nBlock == BLOCK_CONTROLEXT)
  471. {
  472. if (!SkipNextBlock()) return FALSE;
  473. nBlock = GetNextBlock();
  474. // skip everything until we meet an image block or a plain-text block
  475. while ((nBlock != BLOCK_IMAGE) &&
  476.    (nBlock != BLOCK_PLAINTEXT) &&
  477.    (nBlock != BLOCK_UNKNOWN) &&
  478.    (nBlock != BLOCK_TRAILER) )
  479. {
  480. if (!SkipNextBlock()) return NULL;
  481. nBlock = GetNextBlock();
  482. };
  483. if ((nBlock == BLOCK_UNKNOWN) ||
  484. (nBlock == BLOCK_TRAILER))
  485. return FALSE;
  486. };
  487. // skip the found data block (image or plain-text)
  488. if (!SkipNextBlock()) return FALSE;
  489. return TRUE;
  490. }
  491. UINT CPictureEx::GetSubBlocksLen(UINT nStartingOffset) const
  492. {
  493. UINT nRet = 0;
  494. UINT nCurOffset = nStartingOffset;
  495. while (m_pRawData[nCurOffset] != 0)
  496. {
  497. nRet += m_pRawData[nCurOffset]+1;
  498. nCurOffset += m_pRawData[nCurOffset]+1;
  499. };
  500. return nRet+1;
  501. }
  502. enum CPictureEx::GIFBlockTypes CPictureEx::GetNextBlock() const
  503. {
  504. switch(m_pRawData[m_nCurrOffset])
  505. {
  506. case 0x21:
  507. // extension block
  508. switch(m_pRawData[m_nCurrOffset+1])
  509. {
  510. case 0x01:
  511. // plain text extension
  512. return BLOCK_PLAINTEXT;
  513. break;
  514. case 0xF9:
  515. // graphic control extension
  516. return BLOCK_CONTROLEXT;
  517. break;
  518. case 0xFE:
  519. // comment extension
  520. return BLOCK_COMMEXT;
  521. break;
  522. case 0xFF:
  523. // application extension
  524. return BLOCK_APPEXT;
  525. break;
  526. };
  527. break;
  528. case 0x3B:
  529. // trailer
  530. return BLOCK_TRAILER;
  531. break;
  532. case 0x2C:
  533. // image data
  534. return BLOCK_IMAGE;
  535. break;
  536. };
  537. return BLOCK_UNKNOWN;
  538. }
  539. BOOL CPictureEx::SkipNextBlock()
  540. {
  541. if (!m_pRawData) return FALSE;
  542. int nLen = GetNextBlockLen();
  543. if ((nLen <= 0) || ((m_nCurrOffset+nLen) > m_nDataSize))
  544. return FALSE;
  545. m_nCurrOffset += nLen;
  546. return TRUE;
  547. }
  548. int CPictureEx::GetNextBlockLen() const
  549. {
  550. GIFBlockTypes nBlock = GetNextBlock();
  551. int nTmp;
  552. switch(nBlock)
  553. {
  554. case BLOCK_UNKNOWN:
  555. return -1;
  556. break;
  557. case BLOCK_TRAILER:
  558. return 1;
  559. break;
  560. case BLOCK_APPEXT:
  561. nTmp = GetSubBlocksLen(m_nCurrOffset+sizeof(TGIFAppExtension));
  562. if (nTmp > 0)
  563. return sizeof(TGIFAppExtension)+nTmp;
  564. break;
  565. case BLOCK_COMMEXT:
  566. nTmp = GetSubBlocksLen(m_nCurrOffset+sizeof(TGIFCommentExt));
  567. if (nTmp > 0)
  568. return sizeof(TGIFCommentExt)+nTmp;
  569. break;
  570. case BLOCK_CONTROLEXT:
  571. return sizeof(TGIFControlExt);
  572. break;
  573. case BLOCK_PLAINTEXT:
  574. nTmp = GetSubBlocksLen(m_nCurrOffset+sizeof(TGIFPlainTextExt));
  575. if (nTmp > 0)
  576. return sizeof(TGIFPlainTextExt)+nTmp;
  577. break;
  578. case BLOCK_IMAGE:
  579. TGIFImageDescriptor *pIDescr = 
  580. reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]);
  581. int nLCTSize = (int)
  582. (pIDescr->GetPackedValue(ID_PACKED_LOCALCT)*3*
  583. pow(2,pIDescr->GetPackedValue(ID_PACKED_LOCALCTSIZE)+1));
  584. int nTmp = GetSubBlocksLen(m_nCurrOffset+
  585. sizeof(TGIFImageDescriptor) + nLCTSize + 1);
  586. if (nTmp > 0)
  587. return sizeof(TGIFImageDescriptor) + nLCTSize + 1 + nTmp;
  588. break;
  589. };
  590. return 0;
  591. }
  592. UINT WINAPI CPictureEx::_ThreadAnimation(LPVOID pParam)
  593. {
  594. ASSERT(pParam);
  595. CPictureEx *pPic = reinterpret_cast<CPictureEx *> (pParam);
  596. pPic->ThreadAnimation();
  597. // this thread has finished its work so we close the handle
  598. CloseHandle(pPic->m_hThread); 
  599. // and init the handle to zero (so that Stop() doesn't Wait on it)
  600. pPic->m_hThread = 0;
  601. return 0;
  602. }
  603. void CPictureEx::ThreadAnimation()
  604. {
  605. int nTemp = 0;
  606. while (!m_bExitThread)
  607. {
  608. if (m_arrFrames[nTemp].m_pPicture)
  609. {
  610. ///////////////////////////////////////////////////////
  611. // Before rendering a frame we should take care of what's 
  612. // behind that frame. TFrame::m_nDisposal will be our guide:
  613. //   0 - no disposal specified (do nothing)
  614. //   1 - do not dispose (again, do nothing)
  615. //   2 - restore to background color (m_clrBackground)
  616. //   3 - restore to previous
  617. //////// disposal method #3
  618. HDC hMemDC = NULL;
  619. HBITMAP hMemBM = NULL, hOldBM;
  620. if (m_arrFrames[nTemp].m_nDisposal == 3)
  621. {
  622. // prepare a memory DC and store the background in it
  623. hMemDC = CreateCompatibleDC(m_hMemDC);
  624. hMemBM = CreateCompatibleBitmap(m_hMemDC,
  625. m_arrFrames[nTemp].m_frameSize.cx,
  626. m_arrFrames[nTemp].m_frameSize.cy);
  627. if (hMemDC && hMemBM)
  628. {
  629. hOldBM = reinterpret_cast<HBITMAP> (SelectObject(hMemDC,hMemBM));
  630. BitBlt(hMemDC,0,0,
  631. m_arrFrames[nTemp].m_frameSize.cx,
  632. m_arrFrames[nTemp].m_frameSize.cy,
  633. m_hMemDC,
  634. m_arrFrames[nTemp].m_frameOffset.cx,
  635. m_arrFrames[nTemp].m_frameOffset.cy,
  636. SRCCOPY);
  637. };
  638. };
  639. ///////////////////////
  640. long hmWidth;
  641. long hmHeight;
  642. m_arrFrames[nTemp].m_pPicture->get_Width(&hmWidth);
  643. m_arrFrames[nTemp].m_pPicture->get_Height(&hmHeight);
  644. if (m_arrFrames[nTemp].m_pPicture->Render(m_hMemDC, 
  645. m_arrFrames[nTemp].m_frameOffset.cx, 
  646. m_arrFrames[nTemp].m_frameOffset.cy, 
  647. m_arrFrames[nTemp].m_frameSize.cx, 
  648. m_arrFrames[nTemp].m_frameSize.cy, 
  649. 0, hmHeight, hmWidth, -hmHeight, NULL) == S_OK)
  650. {
  651. Invalidate(FALSE);
  652. };
  653. if (m_bExitThread)
  654. {
  655. if (hMemDC)
  656. {
  657. // dispose local variables
  658. SelectObject(hMemDC,hOldBM);
  659. DeleteDC(hMemDC);
  660. DeleteObject(hMemBM);
  661. };
  662. break;
  663. };
  664. // if the delay time is too short (like in old GIFs), wait for 100ms
  665. if (m_arrFrames[nTemp].m_nDelay < 5) 
  666. WaitForSingleObject(m_hExitEvent, 100);
  667. else
  668. WaitForSingleObject(m_hExitEvent, 10*m_arrFrames[nTemp].m_nDelay);
  669. if (m_bExitThread)
  670. {
  671. if (hMemDC)
  672. {
  673. // dispose local variables
  674. SelectObject(hMemDC,hOldBM);
  675. DeleteDC(hMemDC);
  676. DeleteObject(hMemBM);
  677. };
  678. break;
  679. };
  680. // disposal method #2
  681. if (m_arrFrames[nTemp].m_nDisposal == 2)
  682. {
  683. HBRUSH hBrush = CreateSolidBrush(m_clrBackground);
  684. if (hBrush)
  685. {
  686. RECT rect = {
  687. m_arrFrames[nTemp].m_frameOffset.cx,
  688. m_arrFrames[nTemp].m_frameOffset.cy,
  689. m_arrFrames[nTemp].m_frameOffset.cx + m_arrFrames[nTemp].m_frameSize.cx,
  690. m_arrFrames[nTemp].m_frameOffset.cy + m_arrFrames[nTemp].m_frameSize.cy };
  691. FillRect(m_hMemDC,&rect,hBrush);
  692. DeleteObject(hBrush);
  693. };
  694. else
  695. if (hMemDC && (m_arrFrames[nTemp].m_nDisposal == 3) )
  696. {
  697. // put it back
  698. BitBlt(m_hMemDC,
  699. m_arrFrames[nTemp].m_frameOffset.cx,
  700. m_arrFrames[nTemp].m_frameOffset.cy,
  701. m_arrFrames[nTemp].m_frameSize.cx,
  702. m_arrFrames[nTemp].m_frameSize.cy,
  703. hMemDC,0,0, SRCCOPY);
  704. // dispose local variables
  705. SelectObject(hMemDC,hOldBM);
  706. DeleteDC(hMemDC);
  707. DeleteObject(hMemBM);
  708. };
  709. };
  710. nTemp++;
  711. if (nTemp == m_arrFrames.size())
  712. {
  713. nTemp = 0; 
  714. // init the screen for the first frame,
  715. HBRUSH hBrush = CreateSolidBrush(m_clrBackground);
  716. if (hBrush)
  717. {
  718. RECT rect = {0,0,m_PictureSize.cx,m_PictureSize.cy};
  719. FillRect(m_hMemDC,&rect,hBrush);
  720. DeleteObject(hBrush);
  721. };
  722. };
  723. };
  724. }
  725. void CPictureEx::Stop()
  726. {
  727. m_bExitThread = TRUE;
  728. SetEvent(m_hExitEvent);
  729. if (m_hThread)
  730. {
  731. // we'll wait for 5 seconds then continue execution
  732. WaitForSingleObject(m_hThread,5000);
  733. CloseHandle(m_hThread);
  734. m_hThread = NULL;
  735. }
  736. // make it possible to Draw() again
  737. ResetEvent(m_hExitEvent);
  738. m_bExitThread = FALSE;
  739. }
  740. HGLOBAL CPictureEx::GetNextGraphicBlock(UINT *pBlockLen, 
  741. UINT *pDelay, SIZE *pBlockSize, SIZE *pBlockOffset, 
  742. UINT *pDisposal)
  743. {
  744. if (!m_pRawData) return NULL;
  745. // GIF header + LSDescriptor [+ GCT] [+ Control block] + Data
  746. *pDisposal = 0;
  747. enum GIFBlockTypes nBlock;
  748. nBlock = GetNextBlock();
  749. while (
  750. (nBlock != BLOCK_CONTROLEXT) &&
  751. (nBlock != BLOCK_IMAGE) &&
  752. (nBlock != BLOCK_PLAINTEXT) &&
  753. (nBlock != BLOCK_UNKNOWN) &&
  754. (nBlock != BLOCK_TRAILER)
  755. )
  756. {
  757. if (!SkipNextBlock()) return NULL;
  758. nBlock = GetNextBlock();
  759. };
  760. if ((nBlock == BLOCK_UNKNOWN) ||
  761. (nBlock == BLOCK_TRAILER))
  762. return NULL;
  763. // it's either a control ext.block, an image or a plain text
  764. int nStart = m_nCurrOffset;
  765. int nBlockLen = GetNextBlockLen();
  766. if (nBlockLen <= 0) return NULL;
  767. if (nBlock == BLOCK_CONTROLEXT)
  768. {
  769. // get the following data
  770. TGIFControlExt *pControl = 
  771. reinterpret_cast<TGIFControlExt *> (&m_pRawData[m_nCurrOffset]);
  772. // store delay time
  773. *pDelay = pControl->m_wDelayTime;
  774. // store disposal method
  775. *pDisposal = pControl->GetPackedValue(GCX_PACKED_DISPOSAL);
  776. if (!SkipNextBlock()) return NULL;
  777. nBlock = GetNextBlock();
  778. // skip everything until we find data to display 
  779. // (image block or plain-text block)
  780. while (
  781. (nBlock != BLOCK_IMAGE) &&
  782. (nBlock != BLOCK_PLAINTEXT) &&
  783. (nBlock != BLOCK_UNKNOWN) &&
  784. (nBlock != BLOCK_TRAILER)
  785. )
  786. {
  787. if (!SkipNextBlock()) return NULL;
  788. nBlock = GetNextBlock();
  789. nBlockLen += GetNextBlockLen();
  790. };
  791. if ((nBlock == BLOCK_UNKNOWN) || (nBlock == BLOCK_TRAILER))
  792. return NULL;
  793. nBlockLen += GetNextBlockLen();
  794. }
  795. else
  796. *pDelay = -1; // to indicate that there was no delay value
  797. if (nBlock == BLOCK_IMAGE)
  798. {
  799. // store size and offsets
  800. TGIFImageDescriptor *pImage = 
  801. reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]);
  802. pBlockSize->cx = pImage->m_wWidth;
  803. pBlockSize->cy = pImage->m_wHeight;
  804. pBlockOffset->cx = pImage->m_wLeftPos;
  805. pBlockOffset->cy = pImage->m_wTopPos;
  806. };
  807. if (!SkipNextBlock()) return NULL;
  808. HGLOBAL hGlobal = GlobalAlloc(GMEM_FIXED,
  809. sizeof(TGIFHeader) +
  810. sizeof(TGIFLSDescriptor) +
  811. m_nGlobalCTSize +
  812. nBlockLen + 
  813. 1);  // for the trailer
  814. if (!hGlobal) return NULL;
  815. int nOffset = 0; 
  816. // GMEM_FIXED means we get a pointer
  817. unsigned char *pGlobal = reinterpret_cast<unsigned char *> (hGlobal);
  818. CopyMemory(pGlobal,m_pRawData, 
  819. sizeof(TGIFHeader)+sizeof(TGIFLSDescriptor)+m_nGlobalCTSize);
  820. nOffset += sizeof(TGIFHeader)+sizeof(TGIFLSDescriptor)+m_nGlobalCTSize;
  821. CopyMemory(pGlobal + nOffset,&m_pRawData[nStart], nBlockLen);
  822. nOffset += nBlockLen;
  823. pGlobal[nOffset] = 0x3B; // trailer
  824. nOffset++;
  825. *pBlockLen = nOffset;
  826. return hGlobal;
  827. }
  828. BOOL CPictureEx::IsGIF() const
  829. {
  830. return m_bIsGIF;
  831. }
  832. BOOL CPictureEx::IsAnimatedGIF() const
  833. {
  834. return (m_bIsGIF && (m_arrFrames.size() > 1));
  835. }
  836. int CPictureEx::GetFrameCount() const
  837. {
  838. if (!IsAnimatedGIF())
  839. return 0;
  840. return m_arrFrames.size();
  841. }
  842. COLORREF CPictureEx::GetBkColor() const
  843. {
  844. return m_clrBackground;
  845. }
  846. void CPictureEx::OnPaint() 
  847. {
  848. CPaintDC dc(this); // device context for painting
  849. ::BitBlt(dc.m_hDC,0,0,m_PictureSize.cx,m_PictureSize.cy,
  850. m_hMemDC,0,0,SRCCOPY);
  851. }
  852. BOOL CPictureEx::PrepareDC(int nWidth, int nHeight)
  853. {
  854. SetWindowPos(NULL,0,0,nWidth,nHeight,SWP_NOMOVE | SWP_NOZORDER);
  855. HDC hWinDC = ::GetDC(m_hWnd);
  856. if (!hWinDC) return FALSE;
  857. m_hMemDC = CreateCompatibleDC(hWinDC);
  858. if (!m_hMemDC) 
  859. {
  860. ::ReleaseDC(m_hWnd,hWinDC);
  861. return FALSE;
  862. };
  863. m_hBitmap  = CreateCompatibleBitmap(hWinDC,nWidth,nHeight);
  864. if (!m_hBitmap) 
  865. {
  866. ::ReleaseDC(m_hWnd,hWinDC);
  867. ::DeleteDC(m_hMemDC);
  868. return FALSE;
  869. };
  870. m_hOldBitmap = reinterpret_cast<HBITMAP> 
  871. (SelectObject(m_hMemDC,m_hBitmap));
  872. // fill the background
  873. m_clrBackground = GetSysColor(COLOR_3DFACE);
  874. RECT rect = {0,0,nWidth,nHeight};
  875. FillRect(m_hMemDC,&rect,(HBRUSH)(COLOR_WINDOW));
  876. m_bIsInitialized = TRUE;
  877. return TRUE;
  878. }
  879. void CPictureEx::OnDestroy() 
  880. {
  881. Stop();
  882. CStatic::OnDestroy();
  883. }
  884. void CPictureEx::SetBkColor(COLORREF clr)
  885. {
  886. if (!m_bIsInitialized) return;
  887. m_clrBackground = clr;
  888. HBRUSH hBrush = CreateSolidBrush(clr);
  889. if (hBrush)
  890. {
  891. RECT rect = {0,0,m_PictureSize.cx,m_PictureSize.cy};
  892. FillRect(m_hMemDC,&rect,hBrush);
  893. DeleteObject(hBrush);
  894. };
  895. }
  896. #ifdef GIF_TRACING
  897. void CPictureEx::WriteDataOnDisk(CString szFileName, HGLOBAL hData, DWORD dwSize)
  898. {
  899. CFile file;
  900. if (!file.Open(szFileName,
  901. CFile::modeCreate |
  902. CFile::modeWrite |
  903. CFile::shareDenyNone))
  904. {
  905. TRACE(_T("WriteData: Error creating file %sn"),szFileName);
  906. return;
  907. };
  908. char *pData = reinterpret_cast<char *> (GlobalLock(hData));
  909. if (!pData)
  910. {
  911. TRACE(_T("WriteData: Error locking memoryn"));
  912. return;
  913. };
  914. TRY
  915. {
  916. file.Write(pData,dwSize);
  917. }
  918. CATCH(CFileException, e);                                          
  919. {
  920. TRACE(_T("WriteData: An exception occured while writing to the file %sn"),
  921. szFileName);
  922. //e->Delete();   // DO NOT CALL THIS, CAN CAUSE CRASH.  majun, 7.31
  923. GlobalUnlock(hData);
  924. file.Close();
  925. return;
  926. }
  927. END_CATCH
  928. GlobalUnlock(hData);
  929. file.Close();
  930. }
  931. void CPictureEx::EnumGIFBlocks()
  932. {
  933. enum GIFBlockTypes nBlock;
  934. ResetDataPointer();
  935. while(m_nCurrOffset < m_nDataSize)
  936. {
  937. nBlock = GetNextBlock();
  938. switch(nBlock)
  939. {
  940. case BLOCK_UNKNOWN:
  941. TRACE(_T("- Unknown blockn"));
  942. return;
  943. break;
  944. case BLOCK_TRAILER:
  945. TRACE(_T("- Trailer blockn"));
  946. break;
  947. case BLOCK_APPEXT:
  948. TRACE(_T("- Application extension blockn"));
  949. break;
  950. case BLOCK_COMMEXT:
  951. TRACE(_T("- Comment extension blockn"));
  952. break;
  953. case BLOCK_CONTROLEXT:
  954. {
  955. TGIFControlExt *pControl = 
  956. reinterpret_cast<TGIFControlExt *> (&m_pRawData[m_nCurrOffset]);
  957. TRACE(_T("- Graphic control extension block (delay %d, disposal %d)n"),
  958. pControl->m_wDelayTime, pControl->GetPackedValue(GCX_PACKED_DISPOSAL));
  959. };
  960. break;
  961. case BLOCK_PLAINTEXT:
  962. TRACE(_T("- Plain text extension blockn"));
  963. break;
  964. case BLOCK_IMAGE:
  965. TGIFImageDescriptor *pIDescr = 
  966. reinterpret_cast<TGIFImageDescriptor *> (&m_pRawData[m_nCurrOffset]);
  967. TRACE(_T("- Image data block (%dx%d  %d,%d)n"),
  968. pIDescr->m_wWidth,
  969. pIDescr->m_wHeight,
  970. pIDescr->m_wLeftPos,
  971. pIDescr->m_wTopPos);
  972. break;
  973. };
  974. SkipNextBlock();
  975. };
  976. TRACE(_T("n"));
  977. }
  978. #endif // GIF_TRACING