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