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