Avoid excessive heap memory reallocation when generating EMF
[wine] / dlls / comctl32 / animate.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3  * Animation control
4  *
5  * Copyright 1998, 1999 Eric Kohl
6  *                 1999 Eric Pouech
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  * NOTES
23  *   I will only improve this control once in a while.
24  *     Eric <ekohl@abo.rhein-zeitung.de>
25  *
26  * TODO:
27  *   - check for the 'rec ' list in some AVI files
28  *   - concurrent access to infoPtr
29  */
30
31 #define COM_NO_WINDOWS_H
32 #include <stdarg.h>
33 #include <string.h>
34 #include "windef.h"
35 #include "winbase.h"
36 #include "wingdi.h"
37 #include "winuser.h"
38 #include "winnls.h"
39 #include "commctrl.h"
40 #include "vfw.h"
41 #include "mmsystem.h"
42 #include "comctl32.h"
43 #include "wine/debug.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(animate);
46
47 static struct {
48     HMODULE     hModule;
49     HIC         (WINAPI *fnICOpen)(DWORD, DWORD, UINT);
50     LRESULT     (WINAPI *fnICClose)(HIC);
51     LRESULT     (WINAPI *fnICSendMessage)(HIC, UINT, DWORD, DWORD);
52     DWORD       (WINAPIV *fnICDecompress)(HIC,DWORD,LPBITMAPINFOHEADER,LPVOID,LPBITMAPINFOHEADER,LPVOID);
53 } fnIC;
54
55 typedef struct
56 {
57    /* reference to input stream (file or resource) */
58    HGLOBAL              hRes;
59    HMMIO                hMMio;  /* handle to mmio stream */
60    HWND                 hWnd;
61    /* information on the loaded AVI file */
62    MainAVIHeader        mah;
63    AVIStreamHeader      ash;
64    LPBITMAPINFOHEADER   inbih;
65    LPDWORD              lpIndex;
66    /* data for the decompressor */
67    HIC                  hic;
68    LPBITMAPINFOHEADER   outbih;
69    LPVOID               indata;
70    LPVOID               outdata;
71    /* data for the background mechanism */
72    CRITICAL_SECTION     cs;
73    HANDLE               hThread;
74    UINT                 uTimer;
75    /* data for playing the file */
76    int                  nFromFrame;
77    int                  nToFrame;
78    int                  nLoop;
79    int                  currFrame;
80    /* tranparency info*/
81    COLORREF             transparentColor;
82    HBRUSH               hbrushBG;
83    HBITMAP              hbmPrevFrame;
84 } ANIMATE_INFO;
85
86 #define ANIMATE_GetInfoPtr(hWnd) ((ANIMATE_INFO *)GetWindowLongA(hWnd, 0))
87 #define ANIMATE_COLOR_NONE      0xffffffff
88
89 static void ANIMATE_Notify(ANIMATE_INFO* infoPtr, UINT notif)
90 {
91     SendMessageA(GetParent(infoPtr->hWnd), WM_COMMAND,
92                  MAKEWPARAM(GetDlgCtrlID(infoPtr->hWnd), notif),
93                  (LPARAM)infoPtr->hWnd);
94 }
95
96 static BOOL ANIMATE_LoadResA(ANIMATE_INFO *infoPtr, HINSTANCE hInst, LPSTR lpName)
97 {
98     HRSRC       hrsrc;
99     MMIOINFO    mminfo;
100     LPVOID      lpAvi;
101
102     hrsrc = FindResourceA(hInst, lpName, "AVI");
103     if (!hrsrc)
104         return FALSE;
105
106     infoPtr->hRes = LoadResource(hInst, hrsrc);
107     if (!infoPtr->hRes)
108         return FALSE;
109
110     lpAvi = LockResource(infoPtr->hRes);
111     if (!lpAvi)
112         return FALSE;
113
114     memset(&mminfo, 0, sizeof(mminfo));
115     mminfo.fccIOProc = FOURCC_MEM;
116     mminfo.pchBuffer = (LPSTR)lpAvi;
117     mminfo.cchBuffer = SizeofResource(hInst, hrsrc);
118     infoPtr->hMMio = mmioOpenA(NULL, &mminfo, MMIO_READ);
119     if (!infoPtr->hMMio) {
120         GlobalFree((HGLOBAL)lpAvi);
121         return FALSE;
122     }
123
124     return TRUE;
125 }
126
127
128 static BOOL ANIMATE_LoadFileA(ANIMATE_INFO *infoPtr, LPSTR lpName)
129 {
130     infoPtr->hMMio = mmioOpenA((LPSTR)lpName, NULL,
131                                MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);
132
133     if (!infoPtr->hMMio)
134         return FALSE;
135
136     return TRUE;
137 }
138
139
140 static LRESULT ANIMATE_DoStop(ANIMATE_INFO *infoPtr)
141 {
142     EnterCriticalSection(&infoPtr->cs);
143
144     /* should stop playing */
145     if (infoPtr->hThread)
146     {
147         if (!TerminateThread(infoPtr->hThread,0))
148             WARN("could not destroy animation thread!\n");
149             infoPtr->hThread = 0;
150     }
151     if (infoPtr->uTimer) {
152         KillTimer(infoPtr->hWnd, infoPtr->uTimer);
153         infoPtr->uTimer = 0;
154     }
155
156     LeaveCriticalSection(&infoPtr->cs);
157
158     ANIMATE_Notify(infoPtr, ACN_STOP);
159
160     return TRUE;
161 }
162
163
164 static void ANIMATE_Free(ANIMATE_INFO *infoPtr)
165 {
166     if (infoPtr->hMMio) {
167         ANIMATE_DoStop(infoPtr);
168         mmioClose(infoPtr->hMMio, 0);
169         if (infoPtr->hRes) {
170             FreeResource(infoPtr->hRes);
171             infoPtr->hRes = 0;
172         }
173         if (infoPtr->lpIndex) {
174             HeapFree(GetProcessHeap(), 0, infoPtr->lpIndex);
175             infoPtr->lpIndex = NULL;
176         }
177         if (infoPtr->hic) {
178             fnIC.fnICClose(infoPtr->hic);
179             infoPtr->hic = 0;
180         }
181         if (infoPtr->inbih) {
182             HeapFree(GetProcessHeap(), 0, infoPtr->inbih);
183             infoPtr->inbih = NULL;
184         }
185         if (infoPtr->outbih) {
186             HeapFree(GetProcessHeap(), 0, infoPtr->outbih);
187             infoPtr->outbih = NULL;
188         }
189         if( infoPtr->indata )
190         {
191         HeapFree(GetProcessHeap(), 0, infoPtr->indata);
192             infoPtr->indata = NULL;
193         }
194         if( infoPtr->outdata )
195         {
196         HeapFree(GetProcessHeap(), 0, infoPtr->outdata);
197             infoPtr->outdata = NULL;
198         }
199         if( infoPtr->hbmPrevFrame )
200         {
201             DeleteObject(infoPtr->hbmPrevFrame);
202             infoPtr->hbmPrevFrame = 0;
203         }
204         infoPtr->indata = infoPtr->outdata = NULL;
205         infoPtr->hWnd = 0;
206         infoPtr->hMMio = 0;
207
208         memset(&infoPtr->mah, 0, sizeof(infoPtr->mah));
209         memset(&infoPtr->ash, 0, sizeof(infoPtr->ash));
210         infoPtr->nFromFrame = infoPtr->nToFrame = infoPtr->nLoop = infoPtr->currFrame = 0;
211     }
212     infoPtr->transparentColor = ANIMATE_COLOR_NONE;
213 }
214
215 static void ANIMATE_TransparentBlt(ANIMATE_INFO* infoPtr, HDC hdcDest, HDC hdcSource)
216 {
217     HDC hdcMask;
218     HBITMAP hbmMask;
219     HBITMAP hbmOld;
220
221     /* create a transparency mask */
222     hdcMask = CreateCompatibleDC(hdcDest);
223     hbmMask = CreateBitmap(infoPtr->inbih->biWidth, infoPtr->inbih->biHeight, 1,1,NULL);
224     hbmOld = SelectObject(hdcMask, hbmMask);
225
226     SetBkColor(hdcSource,infoPtr->transparentColor);
227     BitBlt(hdcMask,0,0,infoPtr->inbih->biWidth, infoPtr->inbih->biHeight,hdcSource,0,0,SRCCOPY);
228
229     /* mask the source bitmap */
230     SetBkColor(hdcSource, RGB(0,0,0));
231     SetTextColor(hdcSource, RGB(255,255,255));
232     BitBlt(hdcSource, 0, 0, infoPtr->inbih->biWidth, infoPtr->inbih->biHeight, hdcMask, 0, 0, SRCAND);
233
234     /* mask the destination bitmap */
235     SetBkColor(hdcDest, RGB(255,255,255));
236     SetTextColor(hdcDest, RGB(0,0,0));
237     BitBlt(hdcDest, 0, 0, infoPtr->inbih->biWidth, infoPtr->inbih->biHeight, hdcMask, 0, 0, SRCAND);
238
239     /* combine source and destination */
240     BitBlt(hdcDest,0,0,infoPtr->inbih->biWidth, infoPtr->inbih->biHeight,hdcSource,0,0,SRCPAINT);
241
242     SelectObject(hdcMask, hbmOld);
243     DeleteObject(hbmMask);
244     DeleteDC(hdcMask);
245 }
246
247 static LRESULT ANIMATE_PaintFrame(ANIMATE_INFO* infoPtr, HDC hDC)
248 {
249     void* pBitmapData = NULL;
250     LPBITMAPINFO pBitmapInfo = NULL;
251
252     HDC hdcMem;
253     HBITMAP hbmOld;
254
255     int nOffsetX = 0;
256     int nOffsetY = 0;
257
258     int nWidth;
259     int nHeight;
260
261     if (!hDC || !infoPtr->inbih)
262         return TRUE;
263
264     if (infoPtr->hic )
265     {
266         pBitmapData = infoPtr->outdata;
267         pBitmapInfo = (LPBITMAPINFO)infoPtr->outbih;
268
269         nWidth = infoPtr->outbih->biWidth;
270         nHeight = infoPtr->outbih->biHeight;
271     } else
272     {
273         pBitmapData = infoPtr->indata;
274         pBitmapInfo = (LPBITMAPINFO)infoPtr->inbih;
275
276         nWidth = infoPtr->inbih->biWidth;
277         nHeight = infoPtr->inbih->biHeight;
278     }
279
280     if(!infoPtr->hbmPrevFrame)
281     {
282         infoPtr->hbmPrevFrame=CreateCompatibleBitmap(hDC, nWidth,nHeight );
283     }
284
285     SetDIBits(hDC, infoPtr->hbmPrevFrame, 0, nHeight, pBitmapData, (LPBITMAPINFO)pBitmapInfo, DIB_RGB_COLORS);
286
287     hdcMem = CreateCompatibleDC(hDC);
288     hbmOld = SelectObject(hdcMem, infoPtr->hbmPrevFrame);
289
290     /*
291      * we need to get the transparent color even without ACS_TRANSPARENT,
292      * because the style can be changed later on and the color should always
293      * be obtained in the first frame
294      */
295     if(infoPtr->transparentColor == ANIMATE_COLOR_NONE)
296     {
297         infoPtr->transparentColor = GetPixel(hdcMem,0,0);
298     }
299
300     if(GetWindowLongA(infoPtr->hWnd, GWL_STYLE) & ACS_TRANSPARENT)
301     {
302         HDC hdcFinal = CreateCompatibleDC(hDC);
303         HBITMAP hbmFinal = CreateCompatibleBitmap(hDC,nWidth, nHeight);
304         HBITMAP hbmOld2 = SelectObject(hdcFinal, hbmFinal);
305         RECT rect;
306
307         rect.left = 0;
308         rect.top = 0;
309         rect.right = nWidth;
310         rect.bottom = nHeight;
311
312         if(!infoPtr->hbrushBG)
313             infoPtr->hbrushBG = GetCurrentObject(hDC, OBJ_BRUSH);
314
315         FillRect(hdcFinal, &rect, infoPtr->hbrushBG);
316         ANIMATE_TransparentBlt(infoPtr, hdcFinal, hdcMem);
317
318         SelectObject(hdcFinal, hbmOld2);
319         SelectObject(hdcMem, hbmFinal);
320         DeleteDC(hdcFinal);
321         DeleteObject(infoPtr->hbmPrevFrame);
322         infoPtr->hbmPrevFrame = hbmFinal;
323          }
324
325     if (GetWindowLongA(infoPtr->hWnd, GWL_STYLE) & ACS_CENTER)
326     {
327        RECT rect;
328
329        GetWindowRect(infoPtr->hWnd, &rect);
330        nOffsetX = ((rect.right - rect.left) - nWidth)/2;
331        nOffsetY = ((rect.bottom - rect.top) - nHeight)/2;
332     }
333     BitBlt(hDC, nOffsetX, nOffsetY, nWidth, nHeight, hdcMem, 0, 0, SRCCOPY);
334
335     SelectObject(hdcMem, hbmOld);
336     DeleteDC(hdcMem);
337     return TRUE;
338 }
339
340 static LRESULT ANIMATE_DrawFrame(ANIMATE_INFO* infoPtr)
341 {
342     HDC         hDC;
343
344     TRACE("Drawing frame %d (loop %d)\n", infoPtr->currFrame, infoPtr->nLoop);
345
346     EnterCriticalSection(&infoPtr->cs);
347
348     mmioSeek(infoPtr->hMMio, infoPtr->lpIndex[infoPtr->currFrame], SEEK_SET);
349     mmioRead(infoPtr->hMMio, infoPtr->indata, infoPtr->ash.dwSuggestedBufferSize);
350
351     if (infoPtr->hic &&
352         fnIC.fnICDecompress(infoPtr->hic, 0, infoPtr->inbih, infoPtr->indata,
353                      infoPtr->outbih, infoPtr->outdata) != ICERR_OK) {
354         LeaveCriticalSection(&infoPtr->cs);
355         WARN("Decompression error\n");
356         return FALSE;
357     }
358
359     if ((hDC = GetDC(infoPtr->hWnd)) != 0) {
360         ANIMATE_PaintFrame(infoPtr, hDC);
361         ReleaseDC(infoPtr->hWnd, hDC);
362     }
363
364     if (infoPtr->currFrame++ >= infoPtr->nToFrame) {
365         infoPtr->currFrame = infoPtr->nFromFrame;
366         if (infoPtr->nLoop != -1) {
367             if (--infoPtr->nLoop == 0) {
368                 ANIMATE_DoStop(infoPtr);
369             }
370         }
371     }
372     LeaveCriticalSection(&infoPtr->cs);
373
374     return TRUE;
375 }
376
377 static DWORD CALLBACK ANIMATE_AnimationThread(LPVOID ptr_)
378 {
379     ANIMATE_INFO*       infoPtr = (ANIMATE_INFO*)ptr_;
380     HDC hDC;
381
382     if(!infoPtr)
383     {
384         WARN("animation structure undefined!\n");
385         return FALSE;
386     }
387
388     while(1)
389     {
390         if(GetWindowLongA(infoPtr->hWnd, GWL_STYLE) & ACS_TRANSPARENT)
391         {
392             hDC = GetDC(infoPtr->hWnd);
393             /* sometimes the animation window will be destroyed in between
394              * by the main program, so a ReleaseDC() error msg is possible */
395             infoPtr->hbrushBG = (HBRUSH)SendMessageA(GetParent(infoPtr->hWnd),
396                                              WM_CTLCOLORSTATIC, (WPARAM)hDC,
397                                              (LPARAM)infoPtr->hWnd);
398             ReleaseDC(infoPtr->hWnd,hDC);
399         }
400
401         EnterCriticalSection(&infoPtr->cs);
402         ANIMATE_DrawFrame(infoPtr);
403         LeaveCriticalSection(&infoPtr->cs);
404
405         /* time is in microseconds, we should convert it to milliseconds */
406         Sleep((infoPtr->mah.dwMicroSecPerFrame+500)/1000);
407     }
408     return TRUE;
409 }
410
411 static LRESULT ANIMATE_Play(HWND hWnd, WPARAM wParam, LPARAM lParam)
412 {
413     ANIMATE_INFO *infoPtr = ANIMATE_GetInfoPtr(hWnd);
414
415     /* nothing opened */
416     if (!infoPtr->hMMio)
417         return FALSE;
418
419     if (infoPtr->hThread || infoPtr->uTimer) {
420         FIXME("Already playing ? what should I do ??\n");
421         ANIMATE_DoStop(infoPtr);
422     }
423
424     infoPtr->nFromFrame = (INT)LOWORD(lParam);
425     infoPtr->nToFrame   = (INT)HIWORD(lParam);
426     infoPtr->nLoop      = (INT)wParam;
427
428     if (infoPtr->nToFrame == 0xFFFF)
429         infoPtr->nToFrame = infoPtr->mah.dwTotalFrames - 1;
430
431     TRACE("(repeat=%d from=%d to=%d);\n",
432           infoPtr->nLoop, infoPtr->nFromFrame, infoPtr->nToFrame);
433
434     if (infoPtr->nFromFrame >= infoPtr->nToFrame ||
435         infoPtr->nToFrame >= infoPtr->mah.dwTotalFrames)
436         return FALSE;
437
438     infoPtr->currFrame = infoPtr->nFromFrame;
439
440     if (GetWindowLongA(hWnd, GWL_STYLE) & ACS_TIMER) {
441         TRACE("Using a timer\n");
442         /* create a timer to display AVI */
443         infoPtr->uTimer = SetTimer(hWnd, 1, infoPtr->mah.dwMicroSecPerFrame / 1000, NULL);
444     } else {
445         DWORD threadID;
446
447         TRACE("Using an animation thread\n");
448         infoPtr->hThread = CreateThread(0,0,ANIMATE_AnimationThread,(LPVOID)infoPtr, 0, &threadID);
449         if(!infoPtr->hThread)
450         {
451            ERR("Could not create animation thread!\n");
452            return FALSE;
453     }
454
455     }
456
457     ANIMATE_Notify(infoPtr, ACN_START);
458
459     return TRUE;
460 }
461
462
463 static BOOL ANIMATE_GetAviInfo(ANIMATE_INFO *infoPtr)
464 {
465     MMCKINFO            ckMainRIFF;
466     MMCKINFO            mmckHead;
467     MMCKINFO            mmckList;
468     MMCKINFO            mmckInfo;
469     DWORD               numFrame;
470     DWORD               insize;
471
472     if (mmioDescend(infoPtr->hMMio, &ckMainRIFF, NULL, 0) != 0) {
473         WARN("Can't find 'RIFF' chunk\n");
474         return FALSE;
475     }
476
477     if ((ckMainRIFF.ckid != FOURCC_RIFF) ||
478         (ckMainRIFF.fccType != mmioFOURCC('A', 'V', 'I', ' '))) {
479         WARN("Can't find 'AVI ' chunk\n");
480         return FALSE;
481     }
482
483     mmckHead.fccType = mmioFOURCC('h', 'd', 'r', 'l');
484     if (mmioDescend(infoPtr->hMMio, &mmckHead, &ckMainRIFF, MMIO_FINDLIST) != 0) {
485         WARN("Can't find 'hdrl' list\n");
486         return FALSE;
487     }
488
489     mmckInfo.ckid = mmioFOURCC('a', 'v', 'i', 'h');
490     if (mmioDescend(infoPtr->hMMio, &mmckInfo, &mmckHead, MMIO_FINDCHUNK) != 0) {
491         WARN("Can't find 'avih' chunk\n");
492         return FALSE;
493     }
494
495     mmioRead(infoPtr->hMMio, (LPSTR)&infoPtr->mah, sizeof(infoPtr->mah));
496
497     TRACE("mah.dwMicroSecPerFrame=%ld\n",       infoPtr->mah.dwMicroSecPerFrame);
498     TRACE("mah.dwMaxBytesPerSec=%ld\n",         infoPtr->mah.dwMaxBytesPerSec);
499     TRACE("mah.dwPaddingGranularity=%ld\n",     infoPtr->mah.dwPaddingGranularity);
500     TRACE("mah.dwFlags=%ld\n",                  infoPtr->mah.dwFlags);
501     TRACE("mah.dwTotalFrames=%ld\n",            infoPtr->mah.dwTotalFrames);
502     TRACE("mah.dwInitialFrames=%ld\n",          infoPtr->mah.dwInitialFrames);
503     TRACE("mah.dwStreams=%ld\n",                infoPtr->mah.dwStreams);
504     TRACE("mah.dwSuggestedBufferSize=%ld\n",    infoPtr->mah.dwSuggestedBufferSize);
505     TRACE("mah.dwWidth=%ld\n",                  infoPtr->mah.dwWidth);
506     TRACE("mah.dwHeight=%ld\n",                 infoPtr->mah.dwHeight);
507
508     mmioAscend(infoPtr->hMMio, &mmckInfo, 0);
509
510     mmckList.fccType = mmioFOURCC('s', 't', 'r', 'l');
511     if (mmioDescend(infoPtr->hMMio, &mmckList, &mmckHead, MMIO_FINDLIST) != 0) {
512         WARN("Can't find 'strl' list\n");
513         return FALSE;
514     }
515
516     mmckInfo.ckid = mmioFOURCC('s', 't', 'r', 'h');
517     if (mmioDescend(infoPtr->hMMio, &mmckInfo, &mmckList, MMIO_FINDCHUNK) != 0) {
518         WARN("Can't find 'strh' chunk\n");
519         return FALSE;
520     }
521
522     mmioRead(infoPtr->hMMio, (LPSTR)&infoPtr->ash, sizeof(infoPtr->ash));
523
524     TRACE("ash.fccType='%c%c%c%c'\n",           LOBYTE(LOWORD(infoPtr->ash.fccType)),
525                                                 HIBYTE(LOWORD(infoPtr->ash.fccType)),
526                                                 LOBYTE(HIWORD(infoPtr->ash.fccType)),
527                                                 HIBYTE(HIWORD(infoPtr->ash.fccType)));
528     TRACE("ash.fccHandler='%c%c%c%c'\n",        LOBYTE(LOWORD(infoPtr->ash.fccHandler)),
529                                                 HIBYTE(LOWORD(infoPtr->ash.fccHandler)),
530                                                 LOBYTE(HIWORD(infoPtr->ash.fccHandler)),
531                                                 HIBYTE(HIWORD(infoPtr->ash.fccHandler)));
532     TRACE("ash.dwFlags=%ld\n",                  infoPtr->ash.dwFlags);
533     TRACE("ash.wPriority=%d\n",                 infoPtr->ash.wPriority);
534     TRACE("ash.wLanguage=%d\n",                 infoPtr->ash.wLanguage);
535     TRACE("ash.dwInitialFrames=%ld\n",          infoPtr->ash.dwInitialFrames);
536     TRACE("ash.dwScale=%ld\n",                  infoPtr->ash.dwScale);
537     TRACE("ash.dwRate=%ld\n",                   infoPtr->ash.dwRate);
538     TRACE("ash.dwStart=%ld\n",                  infoPtr->ash.dwStart);
539     TRACE("ash.dwLength=%ld\n",                 infoPtr->ash.dwLength);
540     TRACE("ash.dwSuggestedBufferSize=%ld\n",    infoPtr->ash.dwSuggestedBufferSize);
541     TRACE("ash.dwQuality=%ld\n",                infoPtr->ash.dwQuality);
542     TRACE("ash.dwSampleSize=%ld\n",             infoPtr->ash.dwSampleSize);
543     TRACE("ash.rcFrame=(%d,%d,%d,%d)\n",        infoPtr->ash.rcFrame.top, infoPtr->ash.rcFrame.left,
544           infoPtr->ash.rcFrame.bottom, infoPtr->ash.rcFrame.right);
545
546     mmioAscend(infoPtr->hMMio, &mmckInfo, 0);
547
548     mmckInfo.ckid = mmioFOURCC('s', 't', 'r', 'f');
549     if (mmioDescend(infoPtr->hMMio, &mmckInfo, &mmckList, MMIO_FINDCHUNK) != 0) {
550         WARN("Can't find 'strh' chunk\n");
551         return FALSE;
552     }
553
554     infoPtr->inbih = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
555     if (!infoPtr->inbih) {
556         WARN("Can't alloc input BIH\n");
557         return FALSE;
558     }
559
560     mmioRead(infoPtr->hMMio, (LPSTR)infoPtr->inbih, mmckInfo.cksize);
561
562     TRACE("bih.biSize=%ld\n",           infoPtr->inbih->biSize);
563     TRACE("bih.biWidth=%ld\n",          infoPtr->inbih->biWidth);
564     TRACE("bih.biHeight=%ld\n",         infoPtr->inbih->biHeight);
565     TRACE("bih.biPlanes=%d\n",          infoPtr->inbih->biPlanes);
566     TRACE("bih.biBitCount=%d\n",        infoPtr->inbih->biBitCount);
567     TRACE("bih.biCompression=%ld\n",    infoPtr->inbih->biCompression);
568     TRACE("bih.biSizeImage=%ld\n",      infoPtr->inbih->biSizeImage);
569     TRACE("bih.biXPelsPerMeter=%ld\n",  infoPtr->inbih->biXPelsPerMeter);
570     TRACE("bih.biYPelsPerMeter=%ld\n",  infoPtr->inbih->biYPelsPerMeter);
571     TRACE("bih.biClrUsed=%ld\n",        infoPtr->inbih->biClrUsed);
572     TRACE("bih.biClrImportant=%ld\n",   infoPtr->inbih->biClrImportant);
573
574     mmioAscend(infoPtr->hMMio, &mmckInfo, 0);
575
576     mmioAscend(infoPtr->hMMio, &mmckList, 0);
577
578 #if 0
579     /* an AVI has 0 or 1 video stream, and to be animated should not contain
580      * an audio stream, so only one strl is allowed
581      */
582     mmckList.fccType = mmioFOURCC('s', 't', 'r', 'l');
583     if (mmioDescend(infoPtr->hMMio, &mmckList, &mmckHead, MMIO_FINDLIST) == 0) {
584         WARN("There should be a single 'strl' list\n");
585         return FALSE;
586     }
587 #endif
588
589     mmioAscend(infoPtr->hMMio, &mmckHead, 0);
590
591     /* no need to read optional JUNK chunk */
592
593     mmckList.fccType = mmioFOURCC('m', 'o', 'v', 'i');
594     if (mmioDescend(infoPtr->hMMio, &mmckList, &ckMainRIFF, MMIO_FINDLIST) != 0) {
595         WARN("Can't find 'movi' list\n");
596         return FALSE;
597     }
598
599     /* FIXME: should handle the 'rec ' LIST when present */
600
601     infoPtr->lpIndex = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
602                                  infoPtr->mah.dwTotalFrames * sizeof(DWORD));
603     if (!infoPtr->lpIndex) {
604         WARN("Can't alloc index array\n");
605         return FALSE;
606     }
607
608     numFrame = insize = 0;
609     while (mmioDescend(infoPtr->hMMio, &mmckInfo, &mmckList, 0) == 0 &&
610            numFrame < infoPtr->mah.dwTotalFrames) {
611         infoPtr->lpIndex[numFrame] = mmckInfo.dwDataOffset;
612         if (insize < mmckInfo.cksize)
613             insize = mmckInfo.cksize;
614         numFrame++;
615         mmioAscend(infoPtr->hMMio, &mmckInfo, 0);
616     }
617     if (numFrame != infoPtr->mah.dwTotalFrames) {
618         WARN("Found %ld frames (/%ld)\n", numFrame, infoPtr->mah.dwTotalFrames);
619         return FALSE;
620     }
621     if (insize > infoPtr->ash.dwSuggestedBufferSize) {
622         WARN("insize=%ld suggestedSize=%ld\n", insize, infoPtr->ash.dwSuggestedBufferSize);
623         infoPtr->ash.dwSuggestedBufferSize = insize;
624     }
625
626     infoPtr->indata = HeapAlloc(GetProcessHeap(), 0, infoPtr->ash.dwSuggestedBufferSize);
627     if (!infoPtr->indata) {
628         WARN("Can't alloc input buffer\n");
629         return FALSE;
630     }
631
632     return TRUE;
633 }
634
635
636 static BOOL    ANIMATE_GetAviCodec(ANIMATE_INFO *infoPtr)
637 {
638     DWORD       outSize;
639
640     /* check uncompressed AVI */
641     if ((infoPtr->ash.fccHandler == mmioFOURCC('D', 'I', 'B', ' ')) ||
642        (infoPtr->ash.fccHandler == mmioFOURCC('R', 'L', 'E', ' ')) ||
643        (infoPtr->ash.fccHandler == mmioFOURCC(0, 0, 0, 0)))
644     {
645         infoPtr->hic = 0;
646         return TRUE;
647     }
648
649     /* try to get a decompressor for that type */
650     infoPtr->hic = fnIC.fnICOpen(ICTYPE_VIDEO, infoPtr->ash.fccHandler, ICMODE_DECOMPRESS);
651     if (!infoPtr->hic) {
652         WARN("Can't load codec for the file\n");
653         return FALSE;
654     }
655
656     outSize = fnIC.fnICSendMessage(infoPtr->hic, ICM_DECOMPRESS_GET_FORMAT,
657                             (DWORD)infoPtr->inbih, 0L);
658
659     infoPtr->outbih = HeapAlloc(GetProcessHeap(), 0, outSize);
660     if (!infoPtr->outbih) {
661         WARN("Can't alloc output BIH\n");
662         return FALSE;
663     }
664
665     if (fnIC.fnICSendMessage(infoPtr->hic, ICM_DECOMPRESS_GET_FORMAT,
666                       (DWORD)infoPtr->inbih, (DWORD)infoPtr->outbih) != outSize) {
667         WARN("Can't get output BIH\n");
668         return FALSE;
669     }
670
671     infoPtr->outdata = HeapAlloc(GetProcessHeap(), 0, infoPtr->outbih->biSizeImage);
672     if (!infoPtr->outdata) {
673         WARN("Can't alloc output buffer\n");
674         return FALSE;
675     }
676
677     if (fnIC.fnICSendMessage(infoPtr->hic, ICM_DECOMPRESS_BEGIN,
678                       (DWORD)infoPtr->inbih, (DWORD)infoPtr->outbih) != ICERR_OK) {
679         WARN("Can't begin decompression\n");
680         return FALSE;
681     }
682
683     return TRUE;
684 }
685
686 static LRESULT ANIMATE_OpenA(HWND hWnd, WPARAM wParam, LPARAM lParam)
687 {
688     ANIMATE_INFO *infoPtr = ANIMATE_GetInfoPtr(hWnd);
689     HINSTANCE hInstance = (HINSTANCE)wParam;
690
691     ANIMATE_Free(infoPtr);
692     infoPtr->hWnd = hWnd;
693
694     if (!lParam) {
695         TRACE("Closing avi!\n");
696         /* installer of thebat! v1.62 requires FALSE here */
697         return (infoPtr->hMMio != 0);
698     }
699
700     if (!hInstance)
701        hInstance = (HINSTANCE)GetWindowLongA(hWnd, GWL_HINSTANCE);
702
703     if (HIWORD(lParam)) {
704         TRACE("(\"%s\");\n", (LPSTR)lParam);
705
706         if (!ANIMATE_LoadResA(infoPtr, hInstance, (LPSTR)lParam)) {
707             TRACE("No AVI resource found!\n");
708             if (!ANIMATE_LoadFileA(infoPtr, (LPSTR)lParam)) {
709                 WARN("No AVI file found!\n");
710                 return FALSE;
711             }
712         }
713     } else {
714         TRACE("(%u);\n", (WORD)LOWORD(lParam));
715
716         if (!ANIMATE_LoadResA(infoPtr, hInstance,
717                               MAKEINTRESOURCEA((INT)lParam))) {
718             WARN("No AVI resource found!\n");
719             return FALSE;
720         }
721     }
722
723     if (!ANIMATE_GetAviInfo(infoPtr)) {
724         WARN("Can't get AVI information\n");
725         ANIMATE_Free(infoPtr);
726         return FALSE;
727     }
728
729     if (!ANIMATE_GetAviCodec(infoPtr)) {
730         WARN("Can't get AVI Codec\n");
731         ANIMATE_Free(infoPtr);
732         return FALSE;
733     }
734
735     if (!GetWindowLongA(hWnd, GWL_STYLE) & ACS_CENTER) {
736         SetWindowPos(hWnd, 0, 0, 0, infoPtr->mah.dwWidth, infoPtr->mah.dwHeight,
737                      SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
738     }
739
740     if (GetWindowLongA(hWnd, GWL_STYLE) & ACS_AUTOPLAY) {
741         return ANIMATE_Play(hWnd, -1, (LPARAM)MAKELONG(0, infoPtr->mah.dwTotalFrames-1));
742     }
743
744     return TRUE;
745 }
746
747
748 /* << ANIMATE_Open32W >> */
749
750 static LRESULT ANIMATE_Stop(HWND hWnd, WPARAM wParam, LPARAM lParam)
751 {
752     ANIMATE_INFO *infoPtr = ANIMATE_GetInfoPtr(hWnd);
753
754     /* nothing opened */
755     if (!infoPtr->hMMio)
756         return FALSE;
757
758     ANIMATE_DoStop(infoPtr);
759     return TRUE;
760 }
761
762
763 static LRESULT ANIMATE_Create(HWND hWnd, WPARAM wParam, LPARAM lParam)
764 {
765     ANIMATE_INFO*       infoPtr;
766
767     if (!fnIC.hModule) /* FIXME: not thread safe */
768     {
769         /* since there's a circular dep between msvfw32 and comctl32, we could either:
770          * - fix the build chain to allow this circular dep
771          * - handle it by hand
772          * AJ wants the latter :-(
773          */
774         fnIC.hModule = LoadLibraryA("msvfw32.dll");
775         if (!fnIC.hModule) return FALSE;
776
777         fnIC.fnICOpen        = (void*)GetProcAddress(fnIC.hModule, "ICOpen");
778         fnIC.fnICClose       = (void*)GetProcAddress(fnIC.hModule, "ICClose");
779         fnIC.fnICSendMessage = (void*)GetProcAddress(fnIC.hModule, "ICSendMessage");
780         fnIC.fnICDecompress  = (void*)GetProcAddress(fnIC.hModule, "ICDecompress");
781     }
782
783     /* allocate memory for info structure */
784     infoPtr = (ANIMATE_INFO *)Alloc(sizeof(ANIMATE_INFO));
785     if (!infoPtr) {
786         ERR("could not allocate info memory!\n");
787         return 0;
788     }
789
790     TRACE("Animate style=0x%08lx, parent=%08lx\n", GetWindowLongA(hWnd, GWL_STYLE), (DWORD)GetParent(hWnd));
791
792     /* store crossref hWnd <-> info structure */
793     SetWindowLongA(hWnd, 0, (DWORD)infoPtr);
794     infoPtr->hWnd = hWnd;
795     infoPtr->transparentColor = ANIMATE_COLOR_NONE;
796     infoPtr->hbmPrevFrame = 0;
797
798     InitializeCriticalSection(&infoPtr->cs);
799
800     return 0;
801 }
802
803
804 static LRESULT ANIMATE_Destroy(HWND hWnd, WPARAM wParam, LPARAM lParam)
805 {
806     ANIMATE_INFO *infoPtr = ANIMATE_GetInfoPtr(hWnd);
807
808
809     /* free avi data */
810     ANIMATE_Free(infoPtr);
811
812     /* free animate info data */
813     Free(infoPtr);
814     SetWindowLongA(hWnd, 0, 0);
815
816     return 0;
817 }
818
819
820 static LRESULT ANIMATE_EraseBackground(HWND hWnd, WPARAM wParam, LPARAM lParam)
821 {
822     RECT rect;
823     HBRUSH hBrush = 0;
824
825     if(GetWindowLongA(hWnd, GWL_STYLE) & ACS_TRANSPARENT)
826     {
827         hBrush = (HBRUSH)SendMessageA(GetParent(hWnd),WM_CTLCOLORSTATIC,
828                                       wParam, (LPARAM)hWnd);
829     }
830
831     GetClientRect(hWnd, &rect);
832     FillRect((HDC)wParam, &rect, hBrush ? hBrush : GetCurrentObject((HDC)wParam, OBJ_BRUSH));
833
834     return TRUE;
835 }
836
837 static LRESULT WINAPI ANIMATE_Size(HWND hWnd, WPARAM wParam, LPARAM lParam)
838 {
839     if (GetWindowLongA(hWnd, GWL_STYLE) & ACS_CENTER) {
840         InvalidateRect(hWnd, NULL, TRUE);
841     }
842     return TRUE;
843 }
844
845 static LRESULT WINAPI ANIMATE_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
846 {
847     TRACE("hwnd=%p msg=%x wparam=%x lparam=%lx\n", hWnd, uMsg, wParam, lParam);
848     if (!ANIMATE_GetInfoPtr(hWnd) && (uMsg != WM_NCCREATE))
849         return DefWindowProcA(hWnd, uMsg, wParam, lParam);
850     switch (uMsg)
851     {
852     case ACM_OPENA:
853         return ANIMATE_OpenA(hWnd, wParam, lParam);
854
855         /*      case ACM_OPEN32W: FIXME!! */
856         /*          return ANIMATE_Open32W(hWnd, wParam, lParam); */
857
858     case ACM_PLAY:
859         return ANIMATE_Play(hWnd, wParam, lParam);
860
861     case ACM_STOP:
862         return ANIMATE_Stop(hWnd, wParam, lParam);
863
864     case WM_NCCREATE:
865         ANIMATE_Create(hWnd, wParam, lParam);
866         return DefWindowProcA(hWnd, uMsg, wParam, lParam);
867
868     case WM_NCHITTEST:
869         return HTTRANSPARENT;
870
871     case WM_DESTROY:
872         ANIMATE_Destroy(hWnd, wParam, lParam);
873         return DefWindowProcA(hWnd, uMsg, wParam, lParam);
874
875     case WM_ERASEBKGND:
876         ANIMATE_EraseBackground(hWnd, wParam, lParam);
877         break;
878
879     /*  case WM_STYLECHANGED: FIXME shall we do something ?? */
880
881     case WM_TIMER:
882         if (GetWindowLongA(hWnd, GWL_STYLE) & ACS_TRANSPARENT)
883         {
884             ANIMATE_INFO* infoPtr = ANIMATE_GetInfoPtr(hWnd);
885             infoPtr->hbrushBG = (HBRUSH)SendMessageA(GetParent(hWnd),
886                                                      WM_CTLCOLORSTATIC,
887                                                      wParam, (LPARAM)hWnd);
888         }
889         return ANIMATE_DrawFrame(ANIMATE_GetInfoPtr(hWnd));
890
891     case WM_CLOSE:
892         ANIMATE_Free(ANIMATE_GetInfoPtr(hWnd));
893         return TRUE;
894
895     case WM_PAINT:
896         {
897             ANIMATE_INFO* infoPtr = ANIMATE_GetInfoPtr(hWnd);
898
899             /* the animation isn't playing, don't paint */
900             if(!infoPtr->uTimer && !infoPtr->hThread)
901                 /* default paint handling */
902                 return DefWindowProcA(hWnd, uMsg, wParam, lParam);
903
904             if (GetWindowLongA(hWnd, GWL_STYLE) & ACS_TRANSPARENT)
905                 infoPtr->hbrushBG = (HBRUSH)SendMessageA(GetParent(hWnd),
906                                                          WM_CTLCOLORSTATIC,
907                                                          wParam, (LPARAM)hWnd);
908
909             if (wParam)
910             {
911                 EnterCriticalSection(&infoPtr->cs);
912                 ANIMATE_PaintFrame(infoPtr, (HDC)wParam);
913                 LeaveCriticalSection(&infoPtr->cs);
914             }
915             else
916             {
917                 PAINTSTRUCT ps;
918                 HDC hDC = BeginPaint(hWnd, &ps);
919
920                 EnterCriticalSection(&infoPtr->cs);
921                 ANIMATE_PaintFrame(infoPtr, hDC);
922                 LeaveCriticalSection(&infoPtr->cs);
923
924                 EndPaint(hWnd, &ps);
925             }
926         }
927         break;
928
929     case WM_SIZE:
930         ANIMATE_Size(hWnd, wParam, lParam);
931         return DefWindowProcA(hWnd, uMsg, wParam, lParam);
932
933     default:
934         if ((uMsg >= WM_USER) && (uMsg < WM_APP))
935             ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
936
937         return DefWindowProcA(hWnd, uMsg, wParam, lParam);
938     }
939     return 0;
940 }
941
942 void ANIMATE_Register(void)
943 {
944     WNDCLASSA wndClass;
945
946     ZeroMemory(&wndClass, sizeof(WNDCLASSA));
947     wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS;
948     wndClass.lpfnWndProc   = (WNDPROC)ANIMATE_WindowProc;
949     wndClass.cbClsExtra    = 0;
950     wndClass.cbWndExtra    = sizeof(ANIMATE_INFO *);
951     wndClass.hCursor       = LoadCursorA(0, (LPSTR)IDC_ARROW);
952     wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
953     wndClass.lpszClassName = ANIMATE_CLASSA;
954
955     RegisterClassA(&wndClass);
956 }
957
958
959 void ANIMATE_Unregister(void)
960 {
961     UnregisterClassA(ANIMATE_CLASSA, NULL);
962 }