Fixed the displaying of the FOURCC codes in _dump_pixelformat.
[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  * NOTES
9  *   I will only improve this control once in a while.
10  *     Eric <ekohl@abo.rhein-zeitung.de>
11  *
12  * TODO:
13  *   - check for the 'rec ' list in some AVI files
14  *   - implement some missing flags (ACS_TRANSPARENT)
15  *   - protection between service thread and wndproc messages handling 
16  *     concurrent access to infoPtr
17  */
18
19
20 #include "winbase.h"
21 #include "commctrl.h"
22 #include "vfw.h"
23 #include "mmsystem.h"
24 #include "services.h"
25 #include "debugtools.h"
26
27 DEFAULT_DEBUG_CHANNEL(animate);
28
29 typedef struct
30 {
31    /* pointer to msvideo functions. it's easier to put them here.
32     * to be correct, they should be defined on a per process basis, but
33     * this would required a per process storage. We're using a per object
34     * storage instead, which is not efficient on memory usage, but
35     * will lead to less bugs in the future
36     */
37    HIC          WINAPI  (*fnICOpen)(DWORD, DWORD, UINT);
38    LRESULT      WINAPI  (*fnICClose)(HIC);
39    LRESULT      WINAPI  (*fnICSendMessage)(HIC, UINT, DWORD, DWORD);
40    DWORD        WINAPIV (*fnICDecompress)(HIC,DWORD,LPBITMAPINFOHEADER,LPVOID,LPBITMAPINFOHEADER,LPVOID);
41
42     HMMIO WINAPI (*fnmmioOpenA)(LPSTR,MMIOINFO*,DWORD);
43     MMRESULT WINAPI (*fnmmioClose)(HMMIO,UINT);
44     UINT  WINAPI (*fnmmioAscend)(HMMIO,MMCKINFO*,UINT);
45     UINT  WINAPI (*fnmmioDescend)(HMMIO,MMCKINFO*,const MMCKINFO*,UINT);
46     LONG  WINAPI (*fnmmioSeek)(HMMIO,LONG,INT);
47     LONG  WINAPI (*fnmmioRead)(HMMIO,HPSTR,LONG);
48
49    /* reference to input stream (file or resource) */
50    HGLOBAL              hRes;
51    HMMIO                        hMMio;  /* handle to mmio stream */
52    HWND                 hWnd;
53    /* information on the loaded AVI file */
54    MainAVIHeader        mah;
55    AVIStreamHeader      ash;
56    LPBITMAPINFOHEADER   inbih;
57    LPDWORD              lpIndex;
58    /* data for the decompressor */
59    HIC                  hic;
60    LPBITMAPINFOHEADER   outbih;
61    LPVOID               indata;
62    LPVOID               outdata;
63    /* data for the background mechanism */
64    CRITICAL_SECTION     cs;
65    HANDLE               hService;
66    UINT                 uTimer;
67    /* data for playing the file */
68    int                  nFromFrame;
69    int                  nToFrame;
70    int                  nLoop;
71    int                  currFrame;
72    /* Background frame info*/
73    HBITMAP          bkgFrameb;
74    LPDWORD              bkColor;
75 } ANIMATE_INFO;
76
77 #define ANIMATE_GetInfoPtr(hWnd) ((ANIMATE_INFO *)GetWindowLongA(hWnd, 0))
78
79 HMODULE hModWinmm;
80
81 static void ANIMATE_Notify(ANIMATE_INFO* infoPtr, UINT notif)
82 {
83     SendMessageA(GetParent(infoPtr->hWnd), WM_COMMAND, 
84                  MAKEWPARAM(GetDlgCtrlID(infoPtr->hWnd), notif), 
85                  (LPARAM)infoPtr->hWnd);
86 }
87
88 static BOOL ANIMATE_LoadResA(ANIMATE_INFO *infoPtr, HINSTANCE hInst, LPSTR lpName)
89 {
90     HRSRC       hrsrc;
91     MMIOINFO    mminfo;
92     LPVOID      lpAvi;
93     
94     hrsrc = FindResourceA(hInst, lpName, "AVI");
95     if (!hrsrc)
96         return FALSE;
97     
98     infoPtr->hRes = LoadResource(hInst, hrsrc);
99     if (!infoPtr->hRes)
100         return FALSE;
101     
102     lpAvi = LockResource(infoPtr->hRes);
103     if (!lpAvi)
104         return FALSE;
105     
106     memset(&mminfo, 0, sizeof(mminfo));
107     mminfo.fccIOProc = FOURCC_MEM;
108     mminfo.pchBuffer = (LPSTR)lpAvi;
109     mminfo.cchBuffer = SizeofResource(hInst, hrsrc);
110     infoPtr->hMMio = infoPtr->fnmmioOpenA(NULL, &mminfo, MMIO_READ);
111     
112     if (!infoPtr->hMMio) {
113         GlobalFree((HGLOBAL)lpAvi);
114         return FALSE;
115     }
116
117     infoPtr->bkgFrameb=(HBITMAP)NULL;
118     
119     return TRUE;
120 }
121
122
123 static BOOL ANIMATE_LoadFileA(ANIMATE_INFO *infoPtr, LPSTR lpName)
124 {
125     infoPtr->hMMio = infoPtr->fnmmioOpenA((LPSTR)lpName, NULL,
126                                MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE);
127     
128     if (!infoPtr->hMMio)
129         return FALSE;
130     
131     return TRUE;
132 }
133
134
135 static LRESULT ANIMATE_DoStop(ANIMATE_INFO *infoPtr)
136 {
137     EnterCriticalSection(&infoPtr->cs);
138
139     /* should stop playing */
140     if (infoPtr->hService) {
141         SERVICE_Delete(infoPtr->hService);
142         infoPtr->hService = 0;
143     }
144     if (infoPtr->uTimer) {
145         KillTimer(infoPtr->hWnd, infoPtr->uTimer);
146         infoPtr->uTimer = 0;
147     }
148
149     LeaveCriticalSection(&infoPtr->cs);
150
151     ANIMATE_Notify(infoPtr, ACN_STOP);
152
153     return TRUE;
154 }
155
156
157 static void ANIMATE_Free(ANIMATE_INFO *infoPtr)
158 {
159     if (infoPtr->hMMio) {
160         ANIMATE_DoStop(infoPtr);
161         infoPtr->fnmmioClose(infoPtr->hMMio, 0);
162     if (infoPtr->bkgFrameb) {
163             DeleteObject(infoPtr->bkgFrameb);
164         infoPtr->bkgFrameb=(HBITMAP)NULL;
165         infoPtr->bkColor=NULL;
166         }
167         if (infoPtr->hRes) {
168             FreeResource(infoPtr->hRes);
169             infoPtr->hRes = 0;
170         }
171         if (infoPtr->lpIndex) {
172             HeapFree(GetProcessHeap(), 0, infoPtr->lpIndex);
173             infoPtr->lpIndex = NULL;
174         }
175         if (infoPtr->hic) {
176             (infoPtr->fnICClose)(infoPtr->hic);
177             infoPtr->hic = 0;
178         }
179         if (infoPtr->inbih) {
180             HeapFree(GetProcessHeap(), 0, infoPtr->inbih);
181             infoPtr->inbih = NULL;
182         }
183         if (infoPtr->outbih) {
184             HeapFree(GetProcessHeap(), 0, infoPtr->outbih);
185             infoPtr->outbih = NULL;
186         }
187         HeapFree(GetProcessHeap(), 0, infoPtr->indata);
188         HeapFree(GetProcessHeap(), 0, infoPtr->outdata);
189         infoPtr->indata = infoPtr->outdata = NULL;
190         infoPtr->hWnd = 0;
191         infoPtr->hMMio = 0;
192         memset(&infoPtr->mah, 0, sizeof(infoPtr->mah));
193         memset(&infoPtr->ash, 0, sizeof(infoPtr->ash));
194         infoPtr->nFromFrame = infoPtr->nToFrame = infoPtr->nLoop = infoPtr->currFrame = 0;
195     }
196 }
197
198
199 static LRESULT ANIMATE_PaintFrame(ANIMATE_INFO* infoPtr, HDC hDC)
200 {
201     if (!hDC || !infoPtr->inbih)
202         return TRUE;
203     if (infoPtr->hic){
204         if (GetWindowLongA(infoPtr->hWnd, GWL_STYLE) & ACS_TRANSPARENT){
205             FIXME("TRANSPARENCY NOT SUPPORTED(NOT TESTED) FOR COMPRESSED IMAGE\n");
206         }
207         StretchDIBits(hDC, 0, 0, infoPtr->outbih->biWidth, infoPtr->outbih->biHeight,
208                       0, 0, infoPtr->outbih->biWidth, infoPtr->outbih->biHeight,
209                       infoPtr->outdata, (LPBITMAPINFO)infoPtr->outbih, DIB_RGB_COLORS,
210               SRCCOPY);
211     }
212     else{
213         if (GetWindowLongA(infoPtr->hWnd, GWL_STYLE) & ACS_TRANSPARENT){
214             HBITMAP hbmMem, hbmMem2,hbmMem3;
215             HDC hdcSrc, hdcMask, hdcMem;
216
217             hdcSrc = CreateCompatibleDC(hDC);
218             hdcMask = CreateCompatibleDC(hDC);
219             hdcMem = CreateCompatibleDC(hDC);
220
221             /* create a Black and white bitmap */
222             hbmMem = CreateCompatibleBitmap( hDC,infoPtr->inbih->biWidth,infoPtr->inbih->biHeight);
223             hbmMem2 = CreateBitmap(infoPtr->inbih->biWidth, infoPtr->inbih->biHeight, 1, 1, NULL);
224             hbmMem3 = CreateCompatibleBitmap( hDC,infoPtr->inbih->biWidth,infoPtr->inbih->biHeight);
225
226             SelectObject( hdcSrc, hbmMem);
227
228             StretchDIBits(hdcSrc, 0, 0, infoPtr->inbih->biWidth, infoPtr->inbih->biHeight,
229                       0, 0, infoPtr->inbih->biWidth, infoPtr->inbih->biHeight,
230                       infoPtr->indata, (LPBITMAPINFO)infoPtr->inbih, DIB_RGB_COLORS,
231                       SRCCOPY);
232
233             if (infoPtr->bkgFrameb==(HBITMAP)NULL)
234             {
235                 infoPtr->bkgFrameb = CreateCompatibleBitmap( hDC,infoPtr->inbih->biWidth,infoPtr->inbih->biHeight);
236                 SelectObject( hdcMem, infoPtr->bkgFrameb);
237                 BitBlt(hdcMem, 0, 0, infoPtr->inbih->biWidth, infoPtr->inbih->biHeight, hDC, 0, 0, SRCCOPY);
238                 /* Get the transparent color from the first frame*/
239                 switch (infoPtr->inbih->biBitCount) {
240                    case 1:                
241                    case 4:
242                      /*FIXME: Not supported Yet.*/
243                      break;
244                    case 8:
245                      infoPtr->bkColor = (LPVOID)((LPSTR)infoPtr->inbih + (WORD)(((LPBITMAPINFO)infoPtr->inbih)->bmiHeader.biSize));
246                      break;
247                    case 16:
248                    case 24:
249                    case 32:
250                      infoPtr->bkColor = (LPVOID)GetPixel(hdcSrc, 0, 0);
251                      /*FIXME:Has not been test with more than 8bpp, errors are possible*/
252                      break;
253             }
254
255             }
256             /* Need the copy of the original destination HDC*/
257             SelectObject( hdcSrc, infoPtr->bkgFrameb);
258             SelectObject( hdcMem, hbmMem3);
259             BitBlt(hdcMem, 0, 0, infoPtr->inbih->biWidth, infoPtr->inbih->biHeight, hdcSrc, 0, 0, SRCCOPY);
260
261             SelectObject( hdcSrc, hbmMem);
262             SelectObject( hdcMask, hbmMem2);
263
264             /*Windows converts a color source into monochrome when the destination is
265             monochrome. In this situation, all pixels in the color bitmap that are the same color
266             as the background color become 1s, and all the other pixels are converted to 0s. */
267
268             /* Set the transparent color from the first frame*/
269             switch (infoPtr->inbih->biBitCount) {
270                    case 1:
271                    case 4:
272                      /*FIXME: Not supported Yet.*/
273                      break;
274                    case 8:                     
275                      SetBkColor(hdcSrc, infoPtr->bkColor[(((BYTE*)infoPtr->indata)[0])]);
276                      break;
277                    case 16:
278                    case 24:
279                    case 32:                     
280                      SetBkColor(hdcSrc, (COLORREF)infoPtr->bkColor);
281                      break;
282             }
283
284             BitBlt(hdcMask, 0, 0, infoPtr->inbih->biWidth, infoPtr->inbih->biHeight, hdcSrc, 0, 0, SRCCOPY);
285
286             /*During a blt operation with a color destination, a monochrome source bitmap
287             (and/or a brush when applicable) is converted to color on the fly before the
288             actual ROP is carried out on the bits. The 0 (black) pixels in the monochrome
289             bitmap are converted to the destination's text (foreground) color, and the 1
290             (white) pixels are converted to the background color. */
291
292             SetBkColor(hdcSrc, RGB(0,0,0));          /* 1s --> black (0x000000)*/
293             SetTextColor(hdcSrc, RGB(255,255,255));  /* 0s --> white (0xFFFFFF)*/
294
295             BitBlt(hdcSrc, 0, 0, infoPtr->inbih->biWidth, infoPtr->inbih->biHeight, hdcMask, 0, 0, SRCAND);
296
297             SetBkColor(hdcMem, RGB(255,255,255));  /* 0s --> white (0xFFFFFF) */
298             SetTextColor(hdcMem, RGB(0,0,0));          /* 1s --> black (0x000000) */
299
300             BitBlt(hdcMem, 0, 0, infoPtr->inbih->biWidth, infoPtr->inbih->biHeight, hdcMask, 0, 0, SRCAND);
301
302             BitBlt(hdcMem, 0, 0, infoPtr->inbih->biWidth, infoPtr->inbih->biHeight, hdcSrc, 0, 0, SRCPAINT);
303
304             BitBlt(hDC, 0, 0, infoPtr->inbih->biWidth, infoPtr->inbih->biHeight, hdcMem, 0, 0, SRCCOPY);
305
306             DeleteDC(hdcMem);
307             DeleteDC(hdcSrc);
308             DeleteDC(hdcMask);
309             DeleteObject(hbmMem);
310             DeleteObject(hbmMem2);
311             DeleteObject(hbmMem3);
312
313         }
314         else{
315             StretchDIBits(hDC, 0, 0, infoPtr->inbih->biWidth, infoPtr->inbih->biHeight,
316                       0, 0, infoPtr->inbih->biWidth, infoPtr->inbih->biHeight,
317                       infoPtr->indata, (LPBITMAPINFO)infoPtr->inbih, DIB_RGB_COLORS,
318                       SRCCOPY);
319          }
320     }
321
322     return TRUE;
323 }
324
325 static LRESULT ANIMATE_DrawFrame(ANIMATE_INFO* infoPtr)
326 {
327     HDC         hDC;
328
329     TRACE("Drawing frame %d (loop %d)\n", infoPtr->currFrame, infoPtr->nLoop);
330
331     EnterCriticalSection(&infoPtr->cs);
332
333     infoPtr->fnmmioSeek(infoPtr->hMMio, infoPtr->lpIndex[infoPtr->currFrame], SEEK_SET);
334     infoPtr->fnmmioRead(infoPtr->hMMio, infoPtr->indata, infoPtr->ash.dwSuggestedBufferSize);
335     
336     if (infoPtr->hic &&
337         (infoPtr->fnICDecompress)(infoPtr->hic, 0, infoPtr->inbih, infoPtr->indata, 
338                                   infoPtr->outbih, infoPtr->outdata) != ICERR_OK) {
339         LeaveCriticalSection(&infoPtr->cs);
340         WARN("Decompression error\n");
341         return FALSE;
342     }
343
344     if ((hDC = GetDC(infoPtr->hWnd)) != 0) {
345         ANIMATE_PaintFrame(infoPtr, hDC);
346         ReleaseDC(infoPtr->hWnd, hDC);
347     }
348
349     if (infoPtr->currFrame++ >= infoPtr->nToFrame) {
350         infoPtr->currFrame = infoPtr->nFromFrame;
351         if (infoPtr->nLoop != -1) {
352             if (--infoPtr->nLoop == 0) {
353                 ANIMATE_DoStop(infoPtr);
354             }
355         }
356     }
357     LeaveCriticalSection(&infoPtr->cs);
358
359     return TRUE;
360 }
361
362 static void CALLBACK ANIMATE_ServiceCallback(ULONG_PTR ptr_)
363 {
364     ANIMATE_INFO*       infoPtr = (ANIMATE_INFO*)ptr_;
365
366     EnterCriticalSection(&infoPtr->cs);
367     ANIMATE_DrawFrame(infoPtr);
368     LeaveCriticalSection(&infoPtr->cs);
369 }
370
371 static LRESULT ANIMATE_Play(HWND hWnd, WPARAM wParam, LPARAM lParam)
372 {
373     ANIMATE_INFO *infoPtr = ANIMATE_GetInfoPtr(hWnd);
374
375     /* nothing opened */
376     if (!infoPtr->hMMio)
377         return FALSE;
378
379     if (infoPtr->hService || infoPtr->uTimer) {
380         FIXME("Already playing ? what should I do ??\n");
381         ANIMATE_DoStop(infoPtr);
382     }
383
384     infoPtr->nFromFrame = (INT)LOWORD(lParam);
385     infoPtr->nToFrame   = (INT)HIWORD(lParam);
386     infoPtr->nLoop      = (INT)wParam;
387
388     if (infoPtr->nToFrame == 0xFFFF)
389         infoPtr->nToFrame = infoPtr->mah.dwTotalFrames - 1;
390
391     TRACE("(repeat=%d from=%d to=%d);\n", 
392           infoPtr->nLoop, infoPtr->nFromFrame, infoPtr->nToFrame);
393
394     if (infoPtr->nFromFrame >= infoPtr->nToFrame ||
395         infoPtr->nToFrame >= infoPtr->mah.dwTotalFrames)
396         return FALSE;
397
398     infoPtr->currFrame = infoPtr->nFromFrame;
399
400     if (GetWindowLongA(hWnd, GWL_STYLE) & ACS_TIMER) {
401         TRACE("Using a timer\n");
402         /* create a timer to display AVI */
403         infoPtr->uTimer = SetTimer(hWnd, 1, infoPtr->mah.dwMicroSecPerFrame / 1000, NULL);
404     } else {
405         TRACE("Using the service thread\n");
406         /* time is in Âµs */
407         infoPtr->hService = SERVICE_AddTimer(infoPtr->mah.dwMicroSecPerFrame / 1000, 
408                                              ANIMATE_ServiceCallback, (DWORD)infoPtr);
409     }
410         
411     ANIMATE_Notify(infoPtr, ACN_START);
412
413     return TRUE;
414 }
415
416
417 static BOOL ANIMATE_GetAviInfo(ANIMATE_INFO *infoPtr)
418 {
419     MMCKINFO            ckMainRIFF;
420     MMCKINFO            mmckHead;
421     MMCKINFO            mmckList;
422     MMCKINFO            mmckInfo;
423     DWORD               numFrame;
424     DWORD               insize;
425
426     if (infoPtr->fnmmioDescend(infoPtr->hMMio, &ckMainRIFF, NULL, 0) != 0) {
427         WARN("Can't find 'RIFF' chunk\n");
428         return FALSE;
429     }
430
431     if ((ckMainRIFF.ckid != FOURCC_RIFF) ||
432         (ckMainRIFF.fccType != mmioFOURCC('A', 'V', 'I', ' '))) {
433         WARN("Can't find 'AVI ' chunk\n");
434         return FALSE;
435     }
436
437     mmckHead.fccType = mmioFOURCC('h', 'd', 'r', 'l');
438     if (infoPtr->fnmmioDescend(infoPtr->hMMio, &mmckHead, &ckMainRIFF, MMIO_FINDLIST) != 0) {
439         WARN("Can't find 'hdrl' list\n");
440         return FALSE;
441     }
442
443     mmckInfo.ckid = mmioFOURCC('a', 'v', 'i', 'h');
444     if (infoPtr->fnmmioDescend(infoPtr->hMMio, &mmckInfo, &mmckHead, MMIO_FINDCHUNK) != 0) {
445         WARN("Can't find 'avih' chunk\n");
446         return FALSE;
447     }
448
449     infoPtr->fnmmioRead(infoPtr->hMMio, (LPSTR)&infoPtr->mah, sizeof(infoPtr->mah));
450     TRACE("mah.dwMicroSecPerFrame=%ld\n",       infoPtr->mah.dwMicroSecPerFrame);
451     TRACE("mah.dwMaxBytesPerSec=%ld\n",         infoPtr->mah.dwMaxBytesPerSec);
452     TRACE("mah.dwPaddingGranularity=%ld\n",     infoPtr->mah.dwPaddingGranularity);
453     TRACE("mah.dwFlags=%ld\n",                  infoPtr->mah.dwFlags);
454     TRACE("mah.dwTotalFrames=%ld\n",            infoPtr->mah.dwTotalFrames);
455     TRACE("mah.dwInitialFrames=%ld\n",          infoPtr->mah.dwInitialFrames);
456     TRACE("mah.dwStreams=%ld\n",                infoPtr->mah.dwStreams);
457     TRACE("mah.dwSuggestedBufferSize=%ld\n",    infoPtr->mah.dwSuggestedBufferSize);
458     TRACE("mah.dwWidth=%ld\n",                  infoPtr->mah.dwWidth);
459     TRACE("mah.dwHeight=%ld\n",                 infoPtr->mah.dwHeight);
460     infoPtr->fnmmioAscend(infoPtr->hMMio, &mmckInfo, 0);
461
462     mmckList.fccType = mmioFOURCC('s', 't', 'r', 'l');
463     if (infoPtr->fnmmioDescend(infoPtr->hMMio, &mmckList, &mmckHead, MMIO_FINDLIST) != 0) {
464         WARN("Can't find 'strl' list\n");
465         return FALSE;
466     }
467
468     mmckInfo.ckid = mmioFOURCC('s', 't', 'r', 'h');
469     if (infoPtr->fnmmioDescend(infoPtr->hMMio, &mmckInfo, &mmckList, MMIO_FINDCHUNK) != 0) {
470         WARN("Can't find 'strh' chunk\n");
471         return FALSE;
472     }
473
474     infoPtr->fnmmioRead(infoPtr->hMMio, (LPSTR)&infoPtr->ash, sizeof(infoPtr->ash));
475     TRACE("ash.fccType='%c%c%c%c'\n",           LOBYTE(LOWORD(infoPtr->ash.fccType)), 
476                                                 HIBYTE(LOWORD(infoPtr->ash.fccType)), 
477                                                 LOBYTE(HIWORD(infoPtr->ash.fccType)), 
478                                                 HIBYTE(HIWORD(infoPtr->ash.fccType)));
479     TRACE("ash.fccHandler='%c%c%c%c'\n",        LOBYTE(LOWORD(infoPtr->ash.fccHandler)), 
480                                                 HIBYTE(LOWORD(infoPtr->ash.fccHandler)), 
481                                                 LOBYTE(HIWORD(infoPtr->ash.fccHandler)), 
482                                                 HIBYTE(HIWORD(infoPtr->ash.fccHandler)));
483     TRACE("ash.dwFlags=%ld\n",                  infoPtr->ash.dwFlags);
484     TRACE("ash.wPriority=%d\n",                 infoPtr->ash.wPriority);
485     TRACE("ash.wLanguage=%d\n",                 infoPtr->ash.wLanguage);
486     TRACE("ash.dwInitialFrames=%ld\n",          infoPtr->ash.dwInitialFrames);
487     TRACE("ash.dwScale=%ld\n",                  infoPtr->ash.dwScale);
488     TRACE("ash.dwRate=%ld\n",                   infoPtr->ash.dwRate);
489     TRACE("ash.dwStart=%ld\n",                  infoPtr->ash.dwStart);
490     TRACE("ash.dwLength=%ld\n",                 infoPtr->ash.dwLength);
491     TRACE("ash.dwSuggestedBufferSize=%ld\n",    infoPtr->ash.dwSuggestedBufferSize);
492     TRACE("ash.dwQuality=%ld\n",                infoPtr->ash.dwQuality);
493     TRACE("ash.dwSampleSize=%ld\n",             infoPtr->ash.dwSampleSize);
494     TRACE("ash.rcFrame=(%d,%d,%d,%d)\n",        infoPtr->ash.rcFrame.top, infoPtr->ash.rcFrame.left, 
495           infoPtr->ash.rcFrame.bottom, infoPtr->ash.rcFrame.right);
496     infoPtr->fnmmioAscend(infoPtr->hMMio, &mmckInfo, 0);
497
498     mmckInfo.ckid = mmioFOURCC('s', 't', 'r', 'f');
499     if (infoPtr->fnmmioDescend(infoPtr->hMMio, &mmckInfo, &mmckList, MMIO_FINDCHUNK) != 0) {
500         WARN("Can't find 'strh' chunk\n");
501         return FALSE;
502     }
503
504     infoPtr->inbih = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
505     if (!infoPtr->inbih) {
506         WARN("Can't alloc input BIH\n");
507         return FALSE;
508     }
509
510     infoPtr->fnmmioRead(infoPtr->hMMio, (LPSTR)infoPtr->inbih, mmckInfo.cksize);
511     TRACE("bih.biSize=%ld\n",           infoPtr->inbih->biSize);
512     TRACE("bih.biWidth=%ld\n",          infoPtr->inbih->biWidth);
513     TRACE("bih.biHeight=%ld\n",         infoPtr->inbih->biHeight);
514     TRACE("bih.biPlanes=%d\n",          infoPtr->inbih->biPlanes);
515     TRACE("bih.biBitCount=%d\n",        infoPtr->inbih->biBitCount);
516     TRACE("bih.biCompression=%ld\n",    infoPtr->inbih->biCompression);
517     TRACE("bih.biSizeImage=%ld\n",      infoPtr->inbih->biSizeImage);
518     TRACE("bih.biXPelsPerMeter=%ld\n",  infoPtr->inbih->biXPelsPerMeter);
519     TRACE("bih.biYPelsPerMeter=%ld\n",  infoPtr->inbih->biYPelsPerMeter);
520     TRACE("bih.biClrUsed=%ld\n",        infoPtr->inbih->biClrUsed);
521     TRACE("bih.biClrImportant=%ld\n",   infoPtr->inbih->biClrImportant);
522     infoPtr->fnmmioAscend(infoPtr->hMMio, &mmckInfo, 0);
523
524     infoPtr->fnmmioAscend(infoPtr->hMMio, &mmckList, 0);
525     
526 #if 0
527     /* an AVI has 0 or 1 video stream, and to be animated should not contain
528      * an audio stream, so only one strl is allowed 
529      */
530     mmckList.fccType = mmioFOURCC('s', 't', 'r', 'l');
531     if (infoPtr->fnmmioDescend(infoPtr->hMMio, &mmckList, &mmckHead, MMIO_FINDLIST) == 0) {
532         WARN("There should be a single 'strl' list\n");
533         return FALSE;
534     }
535 #endif
536
537     infoPtr->fnmmioAscend(infoPtr->hMMio, &mmckHead, 0);
538
539     /* no need to read optional JUNK chunk */
540
541     mmckList.fccType = mmioFOURCC('m', 'o', 'v', 'i');
542     if (infoPtr->fnmmioDescend(infoPtr->hMMio, &mmckList, &ckMainRIFF, MMIO_FINDLIST) != 0) {
543         WARN("Can't find 'movi' list\n");
544         return FALSE;
545     }
546
547     /* FIXME: should handle the 'rec ' LIST when present */
548
549     infoPtr->lpIndex = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 
550                                  infoPtr->mah.dwTotalFrames * sizeof(DWORD));
551     if (!infoPtr->lpIndex) {
552         WARN("Can't alloc index array\n");
553         return FALSE;
554     }
555
556     numFrame = insize = 0;
557     while (infoPtr->fnmmioDescend(infoPtr->hMMio, &mmckInfo, &mmckList, 0) == 0 && 
558            numFrame < infoPtr->mah.dwTotalFrames) {
559         infoPtr->lpIndex[numFrame] = mmckInfo.dwDataOffset;
560         if (insize < mmckInfo.cksize)
561             insize = mmckInfo.cksize;
562         numFrame++;
563         infoPtr->fnmmioAscend(infoPtr->hMMio, &mmckInfo, 0);
564     }
565     if (numFrame != infoPtr->mah.dwTotalFrames) {
566         WARN("Found %ld frames (/%ld)\n", numFrame, infoPtr->mah.dwTotalFrames);
567         return FALSE;
568     }
569     if (insize > infoPtr->ash.dwSuggestedBufferSize) {
570         WARN("insize=%ld suggestedSize=%ld\n", insize, infoPtr->ash.dwSuggestedBufferSize);
571         infoPtr->ash.dwSuggestedBufferSize = insize;
572     }
573
574     infoPtr->indata = HeapAlloc(GetProcessHeap(), 0, infoPtr->ash.dwSuggestedBufferSize);
575     if (!infoPtr->indata) {
576         WARN("Can't alloc input buffer\n");
577         return FALSE;
578     }
579
580     return TRUE;
581 }
582
583
584 static BOOL    ANIMATE_GetAviCodec(ANIMATE_INFO *infoPtr)
585 {
586     DWORD       outSize;
587
588     /* check uncompressed AVI */
589     if (infoPtr->ash.fccHandler == mmioFOURCC('D', 'I', 'B', ' ') ||
590         infoPtr->ash.fccHandler == mmioFOURCC('R', 'L', 'E', ' ')) {
591         infoPtr->hic = 0;
592         return TRUE;
593     }
594
595     /* try to get a decompressor for that type */
596     infoPtr->hic = (infoPtr->fnICOpen)(ICTYPE_VIDEO, 
597                                        infoPtr->ash.fccHandler, 
598                                        ICMODE_DECOMPRESS);
599     if (!infoPtr->hic) {
600         WARN("Can't load codec for the file\n");
601         return FALSE;
602     }
603     
604     outSize = (infoPtr->fnICSendMessage)(infoPtr->hic, 
605                                          ICM_DECOMPRESS_GET_FORMAT, 
606                                          (DWORD)infoPtr->inbih, 0L);
607     infoPtr->outbih = HeapAlloc(GetProcessHeap(), 0, outSize);
608     if (!infoPtr->outbih) {
609         WARN("Can't alloc output BIH\n");
610         return FALSE;
611     }
612
613     if ((infoPtr->fnICSendMessage)(infoPtr->hic, ICM_DECOMPRESS_GET_FORMAT, 
614                                    (DWORD)infoPtr->inbih, 
615                                    (DWORD)infoPtr->outbih) != ICERR_OK) {
616         WARN("Can't get output BIH\n");
617         return FALSE;
618     }
619
620     infoPtr->outdata = HeapAlloc(GetProcessHeap(), 0, infoPtr->outbih->biSizeImage);
621     if (!infoPtr->outdata) {
622         WARN("Can't alloc output buffer\n");
623         return FALSE;
624     }
625
626     if ((infoPtr->fnICSendMessage)(infoPtr->hic, ICM_DECOMPRESS_BEGIN, 
627                                    (DWORD)infoPtr->inbih, 
628                                    (DWORD)infoPtr->outbih) != ICERR_OK) {
629         WARN("Can't begin decompression\n");
630         return FALSE;
631     }
632
633     return TRUE;
634 }
635
636 static void ANIMATE_Center(ANIMATE_INFO *infoPtr)
637 {
638     int x,y,dx,dy;
639     RECT Rect;
640     HWND hWnd=infoPtr->hWnd;
641     HWND hWndParent=GetWindowLongA(hWnd, GWL_HWNDPARENT);
642
643     if (!hWndParent || !GetWindowRect(hWndParent, &Rect)) return;
644
645     dx=Rect.right-Rect.left+1;
646     dy=Rect.bottom-Rect.top+1;
647     x=infoPtr->mah.dwWidth < dx ? (dx-infoPtr->mah.dwWidth) >> 1 : 0;
648     y=infoPtr->mah.dwHeight < dy ? (dy-infoPtr->mah.dwHeight) >> 1 : 0;
649
650     MoveWindow(hWnd, x, y, infoPtr->mah.dwWidth, infoPtr->mah.dwHeight, TRUE);
651 }
652
653 static LRESULT ANIMATE_OpenA(HWND hWnd, WPARAM wParam, LPARAM lParam)
654 {
655     ANIMATE_INFO *infoPtr = ANIMATE_GetInfoPtr(hWnd);
656     HINSTANCE hInstance = (HINSTANCE)wParam;
657
658     ANIMATE_Free(infoPtr);
659
660     if (!lParam) {
661         TRACE("Closing avi!\n");
662         return TRUE;
663     }
664     
665     if (!hInstance)
666        hInstance = GetWindowLongA(hWnd, GWL_HINSTANCE);
667
668     if (HIWORD(lParam)) {
669         TRACE("(\"%s\");\n", (LPSTR)lParam);
670
671         if (!ANIMATE_LoadResA(infoPtr, hInstance, (LPSTR)lParam)) {
672             TRACE("No AVI resource found!\n");
673             if (!ANIMATE_LoadFileA(infoPtr, (LPSTR)lParam)) {
674                 WARN("No AVI file found!\n");
675                 return FALSE;
676             }
677         }
678     } else {
679         TRACE("(%u);\n", (WORD)LOWORD(lParam));
680
681         if (!ANIMATE_LoadResA(infoPtr, hInstance,
682                               MAKEINTRESOURCEA((INT)lParam))) {
683             WARN("No AVI resource found!\n");
684             return FALSE;
685         }
686     }
687
688     if (!ANIMATE_GetAviInfo(infoPtr)) {
689         WARN("Can't get AVI information\n");
690         ANIMATE_Free(infoPtr);
691         return FALSE;
692     }
693
694     if (!ANIMATE_GetAviCodec(infoPtr)) {
695         WARN("Can't get AVI Codec\n");
696         ANIMATE_Free(infoPtr);
697         return FALSE;
698     }
699
700     if (GetWindowLongA(hWnd, GWL_STYLE) & ACS_CENTER) {
701
702         ANIMATE_Center(infoPtr);
703
704     } else {
705         /*      MoveWindow(hWnd, 0, 0, infoPtr->mah.dwWidth, infoPtr->mah.dwHeight, FALSE);*/
706         SetWindowPos(hWnd, 0, 0, 0, infoPtr->mah.dwWidth, infoPtr->mah.dwHeight,
707                      SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
708     }
709
710     if (GetWindowLongA(hWnd, GWL_STYLE) & ACS_TRANSPARENT) {
711         FIXME("ACS_TRANSPARENT: NIY\n");
712     }
713
714     if (GetWindowLongA(hWnd, GWL_STYLE) & ACS_AUTOPLAY) {
715         return ANIMATE_Play(hWnd, -1, (LPARAM)MAKELONG(0, infoPtr->mah.dwTotalFrames-1));
716     }
717
718     return TRUE;
719 }
720
721
722 /* << ANIMATE_Open32W >> */
723
724 static LRESULT ANIMATE_Stop(HWND hWnd, WPARAM wParam, LPARAM lParam)
725 {
726     ANIMATE_INFO *infoPtr = ANIMATE_GetInfoPtr(hWnd);
727
728     /* nothing opened */
729     if (!infoPtr->hMMio)
730         return FALSE;
731
732     ANIMATE_DoStop(infoPtr);
733     return TRUE;
734 }
735
736
737 static LRESULT ANIMATE_Create(HWND hWnd, WPARAM wParam, LPARAM lParam)
738 {
739     ANIMATE_INFO*       infoPtr;
740     HMODULE             hModule = LoadLibraryA("msvfw32.dll");
741
742     if (!hModule)
743         return FALSE;
744
745     /* allocate memory for info structure */
746     infoPtr = (ANIMATE_INFO *)COMCTL32_Alloc(sizeof(ANIMATE_INFO));
747     if (!infoPtr) {
748         ERR("could not allocate info memory!\n");
749         return 0;
750     }
751
752     /* Temporary hack until we get dllglue up and running */
753     infoPtr->fnICOpen        = (void*)GetProcAddress(hModule, "ICOpen");
754     infoPtr->fnICClose       = (void*)GetProcAddress(hModule, "ICClose");
755     infoPtr->fnICSendMessage = (void*)GetProcAddress(hModule, "ICSendMessage");
756     infoPtr->fnICDecompress  = (void*)GetProcAddress(hModule, "ICDecompress");
757
758     TRACE("Animate style=0x%08lx, parent=%08lx\n", GetWindowLongA(hWnd, GWL_STYLE), (DWORD)GetParent(hWnd));
759
760     /* store crossref hWnd <-> info structure */
761     SetWindowLongA(hWnd, 0, (DWORD)infoPtr);
762     infoPtr->hWnd = hWnd;
763
764     hModWinmm = LoadLibraryA("WINMM");
765
766     infoPtr->fnmmioOpenA = (void*)GetProcAddress(hModWinmm, "mmioOpenA");
767     infoPtr->fnmmioClose = (void*)GetProcAddress(hModWinmm, "mmioClose");
768     infoPtr->fnmmioAscend = (void*)GetProcAddress(hModWinmm, "mmioAscend");
769     infoPtr->fnmmioDescend = (void*)GetProcAddress(hModWinmm, "mmioDescend");
770     infoPtr->fnmmioSeek = (void*)GetProcAddress(hModWinmm, "mmioSeek");
771     infoPtr->fnmmioRead = (void*)GetProcAddress(hModWinmm, "mmioRead");
772
773     InitializeCriticalSection(&infoPtr->cs);
774     
775     return 0;
776 }
777
778
779 static LRESULT ANIMATE_Destroy(HWND hWnd, WPARAM wParam, LPARAM lParam)
780 {
781     ANIMATE_INFO *infoPtr = ANIMATE_GetInfoPtr(hWnd);
782
783
784     /* free avi data */
785     ANIMATE_Free(infoPtr);
786
787     /* free animate info data */
788     COMCTL32_Free(infoPtr);
789     SetWindowLongA(hWnd, 0, 0);
790
791     FreeLibrary(hModWinmm);
792     return 0;
793 }
794
795
796 static LRESULT ANIMATE_EraseBackground(HWND hWnd, WPARAM wParam, LPARAM lParam)
797 {
798     RECT rect;
799
800     GetClientRect(hWnd, &rect);
801 #if 0
802     HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
803
804     FillRect((HDC)wParam, &rect, hBrush);
805     DeleteObject(hBrush);
806 #else
807     FillRect((HDC)wParam, &rect, GetSysColorBrush(COLOR_WINDOW));
808 #endif
809     return TRUE;
810 }
811
812 static LRESULT WINAPI ANIMATE_Size(HWND hWnd, WPARAM wParam, LPARAM lParam)
813 {
814     ANIMATE_INFO *infoPtr = ANIMATE_GetInfoPtr(hWnd);
815
816     if (GetWindowLongA(hWnd, GWL_STYLE) & ACS_CENTER) {
817         if (infoPtr->hMMio) {
818             /* centers the animation in the control, invalidates the control
819              */
820             ANIMATE_Center(infoPtr);
821         }
822         InvalidateRect(hWnd, NULL, TRUE);
823     }
824     return TRUE;
825 }
826
827 static LRESULT WINAPI ANIMATE_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
828 {
829     TRACE("hwnd=%x msg=%x wparam=%x lparam=%lx\n", hWnd, uMsg, wParam, lParam);
830     if (!ANIMATE_GetInfoPtr(hWnd) && (uMsg != WM_NCCREATE))
831         return DefWindowProcA(hWnd, uMsg, wParam, lParam);
832     switch (uMsg)
833     {
834     case ACM_OPENA:
835         return ANIMATE_OpenA(hWnd, wParam, lParam);
836         
837         /*      case ACM_OPEN32W: FIXME!! */
838         /*          return ANIMATE_Open32W(hWnd, wParam, lParam); */
839         
840     case ACM_PLAY:
841         return ANIMATE_Play(hWnd, wParam, lParam);
842         
843     case ACM_STOP:
844         return ANIMATE_Stop(hWnd, wParam, lParam);
845         
846     case WM_NCCREATE:
847         ANIMATE_Create(hWnd, wParam, lParam);
848         return DefWindowProcA(hWnd, uMsg, wParam, lParam);
849         
850     case WM_NCHITTEST:
851         return HTTRANSPARENT;
852
853     case WM_DESTROY:
854         ANIMATE_Destroy(hWnd, wParam, lParam);
855         return DefWindowProcA(hWnd, uMsg, wParam, lParam);
856         
857     case WM_ERASEBKGND:
858         ANIMATE_EraseBackground(hWnd, wParam, lParam);
859         break;
860
861     /*  case WM_STYLECHANGED: FIXME shall we do something ?? */
862
863     case WM_TIMER:
864         return ANIMATE_DrawFrame(ANIMATE_GetInfoPtr(hWnd));
865         
866     case WM_CLOSE:
867         ANIMATE_Free(ANIMATE_GetInfoPtr(hWnd));
868         return TRUE;
869
870     case WM_PAINT:
871         if (wParam) {
872             ANIMATE_PaintFrame(ANIMATE_GetInfoPtr(hWnd), (HDC)wParam);
873         } else {
874             PAINTSTRUCT ps;
875             HDC hDC = BeginPaint(hWnd, &ps);
876             ANIMATE_PaintFrame(ANIMATE_GetInfoPtr(hWnd), hDC);
877             EndPaint(hWnd, &ps);
878         }
879         break;
880
881     case WM_SIZE:
882         ANIMATE_Size(hWnd, wParam, lParam);
883         return DefWindowProcA(hWnd, uMsg, wParam, lParam);
884
885     default:
886         if (uMsg >= WM_USER)
887             ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
888         
889         return DefWindowProcA(hWnd, uMsg, wParam, lParam);
890     }
891     return 0;
892 }
893
894
895 void ANIMATE_Register(void)
896 {
897     WNDCLASSA wndClass;
898
899     ZeroMemory(&wndClass, sizeof(WNDCLASSA));
900     wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS;
901     wndClass.lpfnWndProc   = (WNDPROC)ANIMATE_WindowProc;
902     wndClass.cbClsExtra    = 0;
903     wndClass.cbWndExtra    = sizeof(ANIMATE_INFO *);
904     wndClass.hCursor       = LoadCursorA(0, IDC_ARROWA);
905     wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
906     wndClass.lpszClassName = ANIMATE_CLASSA;
907  
908     RegisterClassA(&wndClass);
909 }
910
911
912 void ANIMATE_Unregister(void)
913 {
914     UnregisterClassA(ANIMATE_CLASSA, (HINSTANCE)NULL);
915 }
916