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