winex11.drv: Do not ignore the keysyms assigned to a group switch, it leads to side...
[wine] / dlls / avifil32 / getframe.c
1 /*
2  * Copyright 2002-2003 Michael Günnewig
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include <assert.h>
20 #include <stdarg.h>
21
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winnls.h"
25 #include "wingdi.h"
26 #include "winuser.h"
27 #include "vfw.h"
28
29 #include "avifile_private.h"
30
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
34
35 #ifndef DIBPTR
36 #define DIBPTR(lp)      ((LPBYTE)(lp) + (lp)->biSize + \
37                          (lp)->biClrUsed * sizeof(RGBQUAD))
38 #endif
39
40 /***********************************************************************/
41
42 static HRESULT WINAPI IGetFrame_fnQueryInterface(IGetFrame *iface,
43                                                  REFIID refiid, LPVOID *obj);
44 static ULONG   WINAPI IGetFrame_fnAddRef(IGetFrame *iface);
45 static ULONG   WINAPI IGetFrame_fnRelease(IGetFrame *iface);
46 static LPVOID  WINAPI IGetFrame_fnGetFrame(IGetFrame *iface, LONG lPos);
47 static HRESULT WINAPI IGetFrame_fnBegin(IGetFrame *iface, LONG lStart,
48                                         LONG lEnd, LONG lRate);
49 static HRESULT WINAPI IGetFrame_fnEnd(IGetFrame *iface);
50 static HRESULT WINAPI IGetFrame_fnSetFormat(IGetFrame *iface,
51                                             LPBITMAPINFOHEADER lpbi,
52                                             LPVOID lpBits, INT x, INT y,
53                                             INT dx, INT dy);
54
55 static const struct IGetFrameVtbl igetframeVtbl = {
56   IGetFrame_fnQueryInterface,
57   IGetFrame_fnAddRef,
58   IGetFrame_fnRelease,
59   IGetFrame_fnGetFrame,
60   IGetFrame_fnBegin,
61   IGetFrame_fnEnd,
62   IGetFrame_fnSetFormat
63 };
64
65 typedef struct _IGetFrameImpl {
66   /* IUnknown stuff */
67   const IGetFrameVtbl *lpVtbl;
68   LONG               ref;
69
70   /* IGetFrame stuff */
71   BOOL               bFixedStream;
72   PAVISTREAM         pStream;
73
74   LPVOID             lpInBuffer;
75   LONG               cbInBuffer;
76   LPBITMAPINFOHEADER lpInFormat;
77   LONG               cbInFormat;
78
79   LONG               lCurrentFrame;
80   LPBITMAPINFOHEADER lpOutFormat;
81   LPVOID             lpOutBuffer;
82
83   HIC                hic;
84   BOOL               bResize;
85   DWORD              x;
86   DWORD              y;
87   DWORD              dx;
88   DWORD              dy;
89
90   BOOL               bFormatChanges;
91   DWORD              dwFormatChangeCount;
92   DWORD              dwEditCount;
93 } IGetFrameImpl;
94
95 /***********************************************************************/
96
97 static void AVIFILE_CloseCompressor(IGetFrameImpl *This)
98 {
99   if (This->lpInFormat != This->lpOutFormat) {
100     HeapFree(GetProcessHeap(), 0, This->lpOutFormat);
101     This->lpOutFormat = NULL;
102   }
103   HeapFree(GetProcessHeap(), 0, This->lpInFormat);
104   This->lpInFormat = NULL;
105   if (This->hic != NULL) {
106     if (This->bResize)
107       ICDecompressExEnd(This->hic);
108     else
109       ICDecompressEnd(This->hic);
110     ICClose(This->hic);
111     This->hic = NULL;
112   }
113 }
114
115 PGETFRAME AVIFILE_CreateGetFrame(PAVISTREAM pStream)
116 {
117   IGetFrameImpl *pg;
118
119   /* check parameter */
120   if (pStream == NULL)
121     return NULL;
122
123   pg = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IGetFrameImpl));
124   if (pg != NULL) {
125     pg->lpVtbl        = &igetframeVtbl;
126     pg->ref           = 1;
127     pg->lCurrentFrame = -1;
128     pg->pStream       = pStream;
129     IAVIStream_AddRef(pStream);
130   }
131
132   return (PGETFRAME)pg;
133 }
134
135 static HRESULT WINAPI IGetFrame_fnQueryInterface(IGetFrame *iface,
136                                                  REFIID refiid, LPVOID *obj)
137 {
138   IGetFrameImpl *This = (IGetFrameImpl *)iface;
139
140   TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);
141
142   if (IsEqualGUID(&IID_IUnknown, refiid) ||
143       IsEqualGUID(&IID_IGetFrame, refiid)) {
144     *obj = iface;
145     return S_OK;
146   }
147
148   return OLE_E_ENUM_NOMORE;
149 }
150
151 static ULONG   WINAPI IGetFrame_fnAddRef(IGetFrame *iface)
152 {
153   IGetFrameImpl *This = (IGetFrameImpl *)iface;
154   ULONG ref = InterlockedIncrement(&This->ref);
155
156   TRACE("(%p)\n", iface);
157
158   return ref;
159 }
160
161 static ULONG   WINAPI IGetFrame_fnRelease(IGetFrame *iface)
162 {
163   IGetFrameImpl *This = (IGetFrameImpl *)iface;
164   ULONG ref = InterlockedDecrement(&This->ref);
165
166   TRACE("(%p)\n", iface);
167
168   if (!ref) {
169     AVIFILE_CloseCompressor(This);
170     if (This->pStream != NULL) {
171       IAVIStream_Release(This->pStream);
172       This->pStream = NULL;
173     }
174
175     HeapFree(GetProcessHeap(), 0, iface);
176     return 0;
177   }
178
179   return ref;
180 }
181
182 static LPVOID  WINAPI IGetFrame_fnGetFrame(IGetFrame *iface, LONG lPos)
183 {
184   IGetFrameImpl *This = (IGetFrameImpl *)iface;
185
186   LONG readBytes;
187   LONG readSamples;
188
189   TRACE("(%p,%d)\n", iface, lPos);
190
191   /* We don't want negative start values! -- marks invalid buffer content */
192   if (lPos < 0)
193     return NULL;
194
195   /* check state */
196   if (This->pStream == NULL)
197     return NULL;
198   if (This->lpInFormat == NULL)
199     return NULL;
200
201   /* Could stream have changed? */
202   if (! This->bFixedStream) {
203     AVISTREAMINFOW sInfo;
204
205     IAVIStream_Info(This->pStream, &sInfo, sizeof(sInfo));
206
207     if (sInfo.dwEditCount != This->dwEditCount) {
208       This->dwEditCount   = sInfo.dwEditCount;
209       This->lCurrentFrame = -1;
210     }
211
212     if (sInfo.dwFormatChangeCount != This->dwFormatChangeCount) {
213       /* stream has changed */
214       if (This->lpOutFormat != NULL) {
215         BITMAPINFOHEADER bi;
216
217         memcpy(&bi, This->lpOutFormat, sizeof(bi));
218         AVIFILE_CloseCompressor(This);
219
220         if (FAILED(IGetFrame_SetFormat(iface, &bi, NULL, 0, 0, -1, -1))) {
221           if (FAILED(IGetFrame_SetFormat(iface, NULL, NULL, 0, 0, -1, -1)))
222             return NULL;
223         }
224       } else if (FAILED(IGetFrame_SetFormat(iface, NULL, NULL, 0, 0, -1, -1)))
225         return NULL;
226     }
227   }
228
229   if (lPos != This->lCurrentFrame) {
230     LONG lNext = IAVIStream_FindSample(This->pStream,lPos,FIND_KEY|FIND_PREV);
231
232     if (lNext == -1)
233       return NULL; /* frame doesn't exist */
234     if (lNext <= This->lCurrentFrame && This->lCurrentFrame < lPos)
235       lNext = This->lCurrentFrame + 1;
236
237     for (; lNext <= lPos; lNext++) {
238       /* new format for this frame? */
239       if (This->bFormatChanges) {
240         IAVIStream_ReadFormat(This->pStream, lNext,
241                               This->lpInFormat, &This->cbInFormat);
242         if (This->lpOutFormat != NULL) {
243           if (This->lpOutFormat->biBitCount <= 8)
244             ICDecompressGetPalette(This->hic, This->lpInFormat,
245                                    This->lpOutFormat);
246         }
247       }
248
249       /* read input frame */
250       while (FAILED(AVIStreamRead(This->pStream, lNext, 1, This->lpInBuffer,
251                                   This->cbInBuffer, &readBytes, &readSamples))) {
252         /* not enough memory for input buffer? */
253         readBytes = 0;
254         if (FAILED(AVIStreamSampleSize(This->pStream, lNext, &readBytes)))
255           return NULL; /* bad thing, but bad things will happen */
256         if (readBytes <= 0) {
257           ERR(": IAVIStream::REad doesn't return needed bytes!\n");
258           return NULL;
259         }
260
261         /* IAVIStream::Read failed because of other reasons not buffersize? */
262         if (This->cbInBuffer >= readBytes)
263           break;
264         This->cbInBuffer = This->cbInFormat + readBytes;
265         This->lpInFormat = HeapReAlloc(GetProcessHeap(), 0, This->lpInFormat, This->cbInBuffer);
266         if (This->lpInFormat == NULL)
267           return NULL; /* out of memory */
268         This->lpInBuffer = (BYTE*)This->lpInFormat + This->cbInFormat;
269       }
270
271       if (readSamples != 1) {
272         ERR(": no frames read\n");
273         return NULL;
274       }
275       if (readBytes != 0) {
276         This->lpInFormat->biSizeImage = readBytes;
277
278         /* nothing to decompress? */
279         if (This->hic == NULL) {
280           This->lCurrentFrame = lPos;
281           return This->lpInFormat;
282         }
283
284         if (This->bResize) {
285           ICDecompressEx(This->hic,0,This->lpInFormat,This->lpInBuffer,0,0,
286                          This->lpInFormat->biWidth,This->lpInFormat->biHeight,
287                          This->lpOutFormat,This->lpOutBuffer,This->x,This->y,
288                          This->dx,This->dy);
289         } else {
290           ICDecompress(This->hic, 0, This->lpInFormat, This->lpInBuffer,
291                        This->lpOutFormat, This->lpOutBuffer);
292         }
293       }
294     } /* for (lNext < lPos) */
295   } /* if (This->lCurrentFrame != lPos) */
296
297   return (This->hic == NULL ? This->lpInFormat : This->lpOutFormat);
298 }
299
300 static HRESULT WINAPI IGetFrame_fnBegin(IGetFrame *iface, LONG lStart,
301                                         LONG lEnd, LONG lRate)
302 {
303   IGetFrameImpl *This = (IGetFrameImpl *)iface;
304
305   TRACE("(%p,%d,%d,%d)\n", iface, lStart, lEnd, lRate);
306
307   This->bFixedStream = TRUE;
308
309   return (IGetFrame_GetFrame(iface, lStart) ? AVIERR_OK : AVIERR_ERROR);
310 }
311
312 static HRESULT WINAPI IGetFrame_fnEnd(IGetFrame *iface)
313 {
314   IGetFrameImpl *This = (IGetFrameImpl *)iface;
315
316   TRACE("(%p)\n", iface);
317
318   This->bFixedStream = FALSE;
319
320   return AVIERR_OK;
321 }
322
323 static HRESULT WINAPI IGetFrame_fnSetFormat(IGetFrame *iface,
324                                             LPBITMAPINFOHEADER lpbiWanted,
325                                             LPVOID lpBits, INT x, INT y,
326                                             INT dx, INT dy)
327 {
328   IGetFrameImpl *This = (IGetFrameImpl *)iface;
329
330   AVISTREAMINFOW     sInfo;
331   LPBITMAPINFOHEADER lpbi         = lpbiWanted;
332   BOOL               bBestDisplay = FALSE;
333
334   TRACE("(%p,%p,%p,%d,%d,%d,%d)\n", iface, lpbiWanted, lpBits,
335         x, y, dx, dy);
336
337   if (This->pStream == NULL)
338     return AVIERR_ERROR;
339
340   if (lpbiWanted == (LPBITMAPINFOHEADER)AVIGETFRAMEF_BESTDISPLAYFMT) {
341     lpbi = NULL;
342     bBestDisplay = TRUE;
343   }
344
345   IAVIStream_Info(This->pStream, &sInfo, sizeof(sInfo));
346   if (sInfo.fccType != streamtypeVIDEO)
347     return AVIERR_UNSUPPORTED;
348
349   This->bFormatChanges =
350     (sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES ? TRUE : FALSE );
351   This->dwFormatChangeCount = sInfo.dwFormatChangeCount;
352   This->dwEditCount         = sInfo.dwEditCount;
353   This->lCurrentFrame       = -1;
354
355   /* get input format from stream */
356   if (This->lpInFormat == NULL) {
357     HRESULT hr;
358
359     This->cbInBuffer = (LONG)sInfo.dwSuggestedBufferSize;
360     if (This->cbInBuffer == 0)
361       This->cbInBuffer = 1024;
362
363     IAVIStream_ReadFormat(This->pStream, sInfo.dwStart,
364                           NULL, &This->cbInFormat);
365
366     This->lpInFormat = HeapAlloc(GetProcessHeap(), 0, This->cbInFormat + This->cbInBuffer);
367     if (This->lpInFormat == NULL) {
368       AVIFILE_CloseCompressor(This);
369       return AVIERR_MEMORY;
370     }
371
372     hr = IAVIStream_ReadFormat(This->pStream, sInfo.dwStart, This->lpInFormat, &This->cbInFormat);
373     if (FAILED(hr)) {
374       AVIFILE_CloseCompressor(This);
375       return hr;
376     }
377
378     This->lpInBuffer = ((LPBYTE)This->lpInFormat) + This->cbInFormat;
379   }
380
381   /* check input format */
382   if (This->lpInFormat->biClrUsed == 0 && This->lpInFormat->biBitCount <= 8)
383     This->lpInFormat->biClrUsed = 1u << This->lpInFormat->biBitCount;
384   if (This->lpInFormat->biSizeImage == 0 &&
385       This->lpInFormat->biCompression == BI_RGB) {
386     This->lpInFormat->biSizeImage =
387       DIBWIDTHBYTES(*This->lpInFormat) * This->lpInFormat->biHeight;
388   }
389
390   /* only to pass through? */
391   if (This->lpInFormat->biCompression == BI_RGB && lpBits == NULL) {
392     if (lpbi == NULL || 
393         (lpbi->biCompression == BI_RGB &&
394          lpbi->biWidth == This->lpInFormat->biWidth &&
395          lpbi->biHeight == This->lpInFormat->biHeight &&
396          lpbi->biBitCount == This->lpInFormat->biBitCount)) {
397       This->lpOutFormat = This->lpInFormat;
398       This->lpOutBuffer = DIBPTR(This->lpInFormat);
399       return AVIERR_OK;
400     }
401   }
402
403   /* need memory for output format? */
404   if (This->lpOutFormat == NULL) {
405     This->lpOutFormat =
406       HeapAlloc(GetProcessHeap(), 0, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
407     if (This->lpOutFormat == NULL) {
408       AVIFILE_CloseCompressor(This);
409       return AVIERR_MEMORY;
410     }
411   }
412
413   /* need handle to video compressor */
414   if (This->hic == NULL) {
415     FOURCC fccHandler;
416
417     if (This->lpInFormat->biCompression == BI_RGB)
418       fccHandler = comptypeDIB;
419     else if (This->lpInFormat->biCompression == BI_RLE8)
420       fccHandler = mmioFOURCC('R','L','E',' ');
421     else
422       fccHandler = sInfo.fccHandler;
423
424     if (lpbi != NULL) {
425       if (lpbi->biWidth == 0)
426         lpbi->biWidth = This->lpInFormat->biWidth;
427       if (lpbi->biHeight == 0)
428         lpbi->biHeight = This->lpInFormat->biHeight;
429     }
430
431     This->hic = ICLocate(ICTYPE_VIDEO, fccHandler, This->lpInFormat, lpbi, ICMODE_DECOMPRESS);
432     if (This->hic == NULL) {
433       AVIFILE_CloseCompressor(This);
434       return AVIERR_NOCOMPRESSOR;
435     }
436   }
437
438   /* output format given? */
439   if (lpbi != NULL) {
440     /* check the given output format ... */
441     if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
442       lpbi->biClrUsed = 1u << lpbi->biBitCount;
443
444     /* ... and remember it */
445     memcpy(This->lpOutFormat, lpbi,
446            lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD));
447     if (lpbi->biBitCount <= 8)
448       ICDecompressGetPalette(This->hic, This->lpInFormat, This->lpOutFormat);
449
450     return AVIERR_OK;
451   } else {
452     if (bBestDisplay) {
453       ICGetDisplayFormat(This->hic, This->lpInFormat,
454                          This->lpOutFormat, 0, dx, dy);
455     } else if (ICDecompressGetFormat(This->hic, This->lpInFormat,
456                                      This->lpOutFormat) < 0) {
457       AVIFILE_CloseCompressor(This);
458       return AVIERR_NOCOMPRESSOR;
459     }
460
461     /* check output format */
462     if (This->lpOutFormat->biClrUsed == 0 &&
463         This->lpOutFormat->biBitCount <= 8)
464       This->lpOutFormat->biClrUsed = 1u << This->lpOutFormat->biBitCount;
465     if (This->lpOutFormat->biSizeImage == 0 &&
466         This->lpOutFormat->biCompression == BI_RGB) {
467       This->lpOutFormat->biSizeImage =
468         DIBWIDTHBYTES(*This->lpOutFormat) * This->lpOutFormat->biHeight;
469     }
470
471     if (lpBits == NULL) {
472       register DWORD size = This->lpOutFormat->biClrUsed * sizeof(RGBQUAD);
473
474       size += This->lpOutFormat->biSize + This->lpOutFormat->biSizeImage;
475       This->lpOutFormat = HeapReAlloc(GetProcessHeap(), 0, This->lpOutFormat, size);
476       if (This->lpOutFormat == NULL) {
477         AVIFILE_CloseCompressor(This);
478         return AVIERR_MEMORY;
479       }
480       This->lpOutBuffer = DIBPTR(This->lpOutFormat);
481     } else
482       This->lpOutBuffer = lpBits;
483
484     /* for user size was irrelevant */
485     if (dx == -1)
486       dx = This->lpOutFormat->biWidth;
487     if (dy == -1)
488       dy = This->lpOutFormat->biHeight;
489
490     /* need to resize? */
491     if (x != 0 || y != 0) {
492       if (dy == This->lpOutFormat->biHeight &&
493           dx == This->lpOutFormat->biWidth)
494         This->bResize = FALSE;
495       else
496         This->bResize = TRUE;
497     }
498
499     if (This->bResize) {
500       This->x  = x;
501       This->y  = y;
502       This->dx = dx;
503       This->dy = dy;
504
505       if (ICDecompressExBegin(This->hic,0,This->lpInFormat,This->lpInBuffer,0,
506                               0,This->lpInFormat->biWidth,
507                               This->lpInFormat->biHeight,This->lpOutFormat,
508                               This->lpOutBuffer, x, y, dx, dy) == ICERR_OK)
509         return AVIERR_OK;
510     } else if (ICDecompressBegin(This->hic, This->lpInFormat,
511                                  This->lpOutFormat) == ICERR_OK)
512       return AVIERR_OK;
513
514     AVIFILE_CloseCompressor(This);
515
516     return AVIERR_COMPRESSOR;
517   }
518 }
519
520 /***********************************************************************/