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