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