PictureEx.cpp
上传用户:swkcbjrc
上传日期:2016-04-02
资源大小:45277k
文件大小:33k
源码类别:

游戏

开发平台:

Visual C++

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