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