windowscodecs: Support writing to memory streams in IWICStream.
[wine] / dlls / windowscodecs / stream.c
1 /*
2  * Copyright 2009 Tony Wasserka
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 "wine/debug.h"
20
21 #define COBJMACROS
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winreg.h"
25 #include "objbase.h"
26 #include "wincodec.h"
27 #include "wincodecs_private.h"
28
29 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
30
31 /******************************************
32  * StreamOnMemory implementation
33  *
34  * Used by IWICStream_InitializeFromMemory
35  *
36  */
37 typedef struct StreamOnMemory {
38     const IStreamVtbl *lpVtbl;
39     LONG ref;
40
41     BYTE *pbMemory;
42     DWORD dwMemsize;
43     DWORD dwCurPos;
44 } StreamOnMemory;
45
46 static HRESULT WINAPI StreamOnMemory_QueryInterface(IStream *iface,
47     REFIID iid, void **ppv)
48 {
49     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
50
51     if (!ppv) return E_INVALIDARG;
52
53     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IStream, iid) ||
54         IsEqualIID(&IID_ISequentialStream, iid))
55     {
56         *ppv = iface;
57         IUnknown_AddRef((IUnknown*)*ppv);
58         return S_OK;
59     }
60     else
61     {
62         *ppv = NULL;
63         return E_NOINTERFACE;
64     }
65 }
66
67 static ULONG WINAPI StreamOnMemory_AddRef(IStream *iface)
68 {
69     StreamOnMemory *This = (StreamOnMemory*)iface;
70     ULONG ref = InterlockedIncrement(&This->ref);
71
72     TRACE("(%p) refcount=%u\n", iface, ref);
73
74     return ref;
75 }
76
77 static ULONG WINAPI StreamOnMemory_Release(IStream *iface)
78 {
79     StreamOnMemory *This = (StreamOnMemory*)iface;
80     ULONG ref = InterlockedDecrement(&This->ref);
81
82     TRACE("(%p) refcount=%u\n", iface, ref);
83
84     if (ref == 0) {
85         HeapFree(GetProcessHeap(), 0, This);
86     }
87     return ref;
88 }
89
90 static HRESULT WINAPI StreamOnMemory_Read(IStream *iface,
91     void *pv, ULONG cb, ULONG *pcbRead)
92 {
93     StreamOnMemory *This = (StreamOnMemory*)iface;
94     ULONG uBytesRead;
95     TRACE("(%p)\n", This);
96
97     if (!pv) return E_INVALIDARG;
98
99     uBytesRead = min(cb, This->dwMemsize - This->dwCurPos);
100     memcpy(pv, This->pbMemory + This->dwCurPos, uBytesRead);
101     This->dwCurPos += uBytesRead;
102     if (pcbRead) *pcbRead = uBytesRead;
103
104     return S_OK;
105 }
106
107 static HRESULT WINAPI StreamOnMemory_Write(IStream *iface,
108     void const *pv, ULONG cb, ULONG *pcbWritten)
109 {
110     StreamOnMemory *This = (StreamOnMemory*)iface;
111     TRACE("(%p)\n", This);
112
113     if (!pv) return E_INVALIDARG;
114
115     if (cb > This->dwMemsize - This->dwCurPos) return STG_E_MEDIUMFULL;
116     if (cb) {
117         memcpy(This->pbMemory + This->dwCurPos, pv, cb);
118         This->dwCurPos += cb;
119     }
120     if (pcbWritten) *pcbWritten = cb;
121
122     return S_OK;
123 }
124
125 static HRESULT WINAPI StreamOnMemory_Seek(IStream *iface,
126     LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
127 {
128     FIXME("(%p): stub\n", iface);
129     return E_NOTIMPL;
130 }
131
132 /* SetSize isn't implemented in the native windowscodecs DLL either */
133 static HRESULT WINAPI StreamOnMemory_SetSize(IStream *iface,
134     ULARGE_INTEGER libNewSize)
135 {
136     TRACE("(%p)\n", iface);
137     return E_NOTIMPL;
138 }
139
140 /* CopyTo isn't implemented in the native windowscodecs DLL either */
141 static HRESULT WINAPI StreamOnMemory_CopyTo(IStream *iface,
142     IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
143 {
144     TRACE("(%p)\n", iface);
145     return E_NOTIMPL;
146 }
147
148 /* Commit isn't implemented in the native windowscodecs DLL either */
149 static HRESULT WINAPI StreamOnMemory_Commit(IStream *iface,
150     DWORD grfCommitFlags)
151 {
152     TRACE("(%p)\n", iface);
153     return E_NOTIMPL;
154 }
155
156 /* Revert isn't implemented in the native windowscodecs DLL either */
157 static HRESULT WINAPI StreamOnMemory_Revert(IStream *iface)
158 {
159     TRACE("(%p)\n", iface);
160     return E_NOTIMPL;
161 }
162
163 /* LockRegion isn't implemented in the native windowscodecs DLL either */
164 static HRESULT WINAPI StreamOnMemory_LockRegion(IStream *iface,
165     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
166 {
167     TRACE("(%p)\n", iface);
168     return E_NOTIMPL;
169 }
170
171 /* UnlockRegion isn't implemented in the native windowscodecs DLL either */
172 static HRESULT WINAPI StreamOnMemory_UnlockRegion(IStream *iface,
173     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
174 {
175     TRACE("(%p)\n", iface);
176     return E_NOTIMPL;
177 }
178
179 static HRESULT WINAPI StreamOnMemory_Stat(IStream *iface,
180     STATSTG *pstatstg, DWORD grfStatFlag)
181 {
182     FIXME("(%p): stub\n", iface);
183     return E_NOTIMPL;
184 }
185
186 /* Clone isn't implemented in the native windowscodecs DLL either */
187 static HRESULT WINAPI StreamOnMemory_Clone(IStream *iface,
188     IStream **ppstm)
189 {
190     TRACE("(%p)\n", iface);
191     return E_NOTIMPL;
192 }
193
194
195 const IStreamVtbl StreamOnMemory_Vtbl =
196 {
197     /*** IUnknown methods ***/
198     StreamOnMemory_QueryInterface,
199     StreamOnMemory_AddRef,
200     StreamOnMemory_Release,
201     /*** ISequentialStream methods ***/
202     StreamOnMemory_Read,
203     StreamOnMemory_Write,
204     /*** IStream methods ***/
205     StreamOnMemory_Seek,
206     StreamOnMemory_SetSize,
207     StreamOnMemory_CopyTo,
208     StreamOnMemory_Commit,
209     StreamOnMemory_Revert,
210     StreamOnMemory_LockRegion,
211     StreamOnMemory_UnlockRegion,
212     StreamOnMemory_Stat,
213     StreamOnMemory_Clone,
214 };
215
216 /******************************************
217  * IWICStream implementation
218  *
219  */
220 typedef struct IWICStreamImpl
221 {
222     const IWICStreamVtbl *lpVtbl;
223     LONG ref;
224
225     IStream *pStream;
226 } IWICStreamImpl;
227
228 static HRESULT WINAPI IWICStreamImpl_QueryInterface(IWICStream *iface,
229     REFIID iid, void **ppv)
230 {
231     IWICStreamImpl *This = (IWICStreamImpl*)iface;
232     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
233
234     if (!ppv) return E_INVALIDARG;
235
236     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IStream, iid) ||
237         IsEqualIID(&IID_ISequentialStream, iid) || IsEqualIID(&IID_IWICStream, iid))
238     {
239         *ppv = This;
240         IUnknown_AddRef((IUnknown*)*ppv);
241         return S_OK;
242     }
243     else
244     {
245         *ppv = NULL;
246         return E_NOINTERFACE;
247     }
248 }
249
250 static ULONG WINAPI IWICStreamImpl_AddRef(IWICStream *iface)
251 {
252     IWICStreamImpl *This = (IWICStreamImpl*)iface;
253     ULONG ref = InterlockedIncrement(&This->ref);
254
255     TRACE("(%p) refcount=%u\n", iface, ref);
256
257     return ref;
258 }
259
260 static ULONG WINAPI IWICStreamImpl_Release(IWICStream *iface)
261 {
262     IWICStreamImpl *This = (IWICStreamImpl*)iface;
263     ULONG ref = InterlockedDecrement(&This->ref);
264
265     TRACE("(%p) refcount=%u\n", iface, ref);
266
267     if (ref == 0) {
268         if (This->pStream) IStream_Release(This->pStream);
269         HeapFree(GetProcessHeap(), 0, This);
270     }
271     return ref;
272 }
273
274 static HRESULT WINAPI IWICStreamImpl_Read(IWICStream *iface,
275     void *pv, ULONG cb, ULONG *pcbRead)
276 {
277     IWICStreamImpl *This = (IWICStreamImpl*)iface;
278     TRACE("(%p): relay\n", This);
279
280     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
281     return IStream_Read(This->pStream, pv, cb, pcbRead);
282 }
283
284 static HRESULT WINAPI IWICStreamImpl_Write(IWICStream *iface,
285     void const *pv, ULONG cb, ULONG *pcbWritten)
286 {
287     IWICStreamImpl *This = (IWICStreamImpl*)iface;
288     TRACE("(%p): relay\n", This);
289
290     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
291     return IStream_Write(This->pStream, pv, cb, pcbWritten);
292 }
293
294 static HRESULT WINAPI IWICStreamImpl_Seek(IWICStream *iface,
295     LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
296 {
297     IWICStreamImpl *This = (IWICStreamImpl*)iface;
298     TRACE("(%p): relay\n", This);
299
300     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
301     return IStream_Seek(This->pStream, dlibMove, dwOrigin, plibNewPosition);
302 }
303
304 static HRESULT WINAPI IWICStreamImpl_SetSize(IWICStream *iface,
305     ULARGE_INTEGER libNewSize)
306 {
307     IWICStreamImpl *This = (IWICStreamImpl*)iface;
308     TRACE("(%p): relay\n", This);
309
310     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
311     return IStream_SetSize(This->pStream, libNewSize);
312 }
313
314 static HRESULT WINAPI IWICStreamImpl_CopyTo(IWICStream *iface,
315     IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
316 {
317     IWICStreamImpl *This = (IWICStreamImpl*)iface;
318     TRACE("(%p): relay\n", This);
319
320     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
321     return IStream_CopyTo(This->pStream, pstm, cb, pcbRead, pcbWritten);
322 }
323
324 static HRESULT WINAPI IWICStreamImpl_Commit(IWICStream *iface,
325     DWORD grfCommitFlags)
326 {
327     IWICStreamImpl *This = (IWICStreamImpl*)iface;
328     TRACE("(%p): relay\n", This);
329
330     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
331     return IStream_Commit(This->pStream, grfCommitFlags);
332 }
333
334 static HRESULT WINAPI IWICStreamImpl_Revert(IWICStream *iface)
335 {
336     IWICStreamImpl *This = (IWICStreamImpl*)iface;
337     TRACE("(%p): relay\n", This);
338
339     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
340     return IStream_Revert(This->pStream);
341 }
342
343 static HRESULT WINAPI IWICStreamImpl_LockRegion(IWICStream *iface,
344     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
345 {
346     IWICStreamImpl *This = (IWICStreamImpl*)iface;
347     TRACE("(%p): relay\n", This);
348
349     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
350     return IStream_LockRegion(This->pStream, libOffset, cb, dwLockType);
351 }
352
353 static HRESULT WINAPI IWICStreamImpl_UnlockRegion(IWICStream *iface,
354     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
355 {
356     IWICStreamImpl *This = (IWICStreamImpl*)iface;
357     TRACE("(%p): relay\n", This);
358
359     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
360     return IStream_UnlockRegion(This->pStream, libOffset, cb, dwLockType);
361 }
362
363 static HRESULT WINAPI IWICStreamImpl_Stat(IWICStream *iface,
364     STATSTG *pstatstg, DWORD grfStatFlag)
365 {
366     IWICStreamImpl *This = (IWICStreamImpl*)iface;
367     TRACE("(%p): relay\n", This);
368
369     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
370     return IStream_Stat(This->pStream, pstatstg, grfStatFlag);
371 }
372
373 static HRESULT WINAPI IWICStreamImpl_Clone(IWICStream *iface,
374     IStream **ppstm)
375 {
376     IWICStreamImpl *This = (IWICStreamImpl*)iface;
377     TRACE("(%p): relay\n", This);
378
379     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
380     return IStream_Clone(This->pStream, ppstm);
381 }
382
383 static HRESULT WINAPI IWICStreamImpl_InitializeFromIStream(IWICStream *iface,
384     IStream *pIStream)
385 {
386     FIXME("(%p): stub\n", iface);
387     return E_NOTIMPL;
388 }
389
390 static HRESULT WINAPI IWICStreamImpl_InitializeFromFilename(IWICStream *iface,
391     LPCWSTR wzFileName, DWORD dwDesiredAccess)
392 {
393     FIXME("(%p): stub\n", iface);
394     return E_NOTIMPL;
395 }
396
397 /******************************************
398  * IWICStream_InitializeFromMemory
399  *
400  * Initializes the internal IStream object to retrieve its data from a memory chunk.
401  *
402  * PARAMS
403  *   pbBuffer     [I] pointer to the memory chunk
404  *   cbBufferSize [I] number of bytes to use from the memory chunk
405  *
406  * RETURNS
407  *   SUCCESS: S_OK
408  *   FAILURE: E_INVALIDARG, if pbBuffer is NULL
409  *            E_OUTOFMEMORY, if we run out of memory
410  *            WINCODEC_ERR_WRONGSTATE, if the IStream object has already been initialized before
411  *
412  */
413 static HRESULT WINAPI IWICStreamImpl_InitializeFromMemory(IWICStream *iface,
414     BYTE *pbBuffer, DWORD cbBufferSize)
415 {
416     IWICStreamImpl *This = (IWICStreamImpl*)iface;
417     StreamOnMemory *pObject;
418     TRACE("(%p,%p)\n", iface, pbBuffer);
419
420     if (!pbBuffer) return E_INVALIDARG;
421     if (This->pStream) return WINCODEC_ERR_WRONGSTATE;
422
423     pObject = HeapAlloc(GetProcessHeap(), 0, sizeof(StreamOnMemory));
424     if (!pObject) return E_OUTOFMEMORY;
425
426     pObject->lpVtbl = &StreamOnMemory_Vtbl;
427     pObject->ref = 1;
428     pObject->pbMemory = pbBuffer;
429     pObject->dwMemsize = cbBufferSize;
430     pObject->dwCurPos = 0;
431
432     This->pStream = (IStream*)pObject;
433     return S_OK;
434 }
435
436 static HRESULT WINAPI IWICStreamImpl_InitializeFromIStreamRegion(IWICStream *iface,
437     IStream *pIStream, ULARGE_INTEGER ulOffset, ULARGE_INTEGER ulMaxSize)
438 {
439     FIXME("(%p): stub\n", iface);
440     return E_NOTIMPL;
441 }
442
443
444 const IWICStreamVtbl WICStream_Vtbl =
445 {
446     /*** IUnknown methods ***/
447     IWICStreamImpl_QueryInterface,
448     IWICStreamImpl_AddRef,
449     IWICStreamImpl_Release,
450     /*** ISequentialStream methods ***/
451     IWICStreamImpl_Read,
452     IWICStreamImpl_Write,
453     /*** IStream methods ***/
454     IWICStreamImpl_Seek,
455     IWICStreamImpl_SetSize,
456     IWICStreamImpl_CopyTo,
457     IWICStreamImpl_Commit,
458     IWICStreamImpl_Revert,
459     IWICStreamImpl_LockRegion,
460     IWICStreamImpl_UnlockRegion,
461     IWICStreamImpl_Stat,
462     IWICStreamImpl_Clone,
463     /*** IWICStream methods ***/
464     IWICStreamImpl_InitializeFromIStream,
465     IWICStreamImpl_InitializeFromFilename,
466     IWICStreamImpl_InitializeFromMemory,
467     IWICStreamImpl_InitializeFromIStreamRegion,
468 };
469
470 HRESULT StreamImpl_Create(IWICStream **stream)
471 {
472     IWICStreamImpl *pObject;
473
474     if( !stream ) return E_INVALIDARG;
475
476     pObject = HeapAlloc(GetProcessHeap(), 0, sizeof(IWICStreamImpl));
477     if( !pObject ) {
478         *stream = NULL;
479         return E_OUTOFMEMORY;
480     }
481
482     pObject->lpVtbl = &WICStream_Vtbl;
483     pObject->ref = 1;
484     pObject->pStream = NULL;
485
486     *stream = (IWICStream*)pObject;
487
488     return S_OK;
489 }