shlwapi/tests: Make some variables static.
[wine] / dlls / windowscodecs / bmpencode.c
1 /*
2  * Copyright 2009 Vincent Povirk for CodeWeavers
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 "config.h"
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winreg.h"
28 #include "wingdi.h"
29 #include "objbase.h"
30 #include "wincodec.h"
31
32 #include "wincodecs_private.h"
33
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
37
38 struct bmp_pixelformat {
39     const WICPixelFormatGUID *guid;
40     UINT bpp;
41     DWORD compression;
42     DWORD redmask;
43     DWORD greenmask;
44     DWORD bluemask;
45     DWORD alphamask;
46 };
47
48 static const struct bmp_pixelformat formats[] = {
49     {&GUID_WICPixelFormat24bppBGR, 24, BI_RGB},
50     {&GUID_WICPixelFormat16bppBGR555, 16, BI_RGB},
51     {&GUID_WICPixelFormat16bppBGR565, 16, BI_BITFIELDS, 0xf800, 0x7e0, 0x1f, 0},
52     {&GUID_WICPixelFormat32bppBGR, 32, BI_RGB},
53 #if 0
54     /* Windows doesn't seem to support this one. */
55     {&GUID_WICPixelFormat32bppBGRA, 32, BI_BITFIELDS, 0xff0000, 0xff00, 0xff, 0xff000000},
56 #endif
57     {NULL}
58 };
59
60 typedef struct BmpFrameEncode {
61     const IWICBitmapFrameEncodeVtbl *lpVtbl;
62     LONG ref;
63     IStream *stream;
64     BOOL initialized;
65     UINT width, height;
66     BYTE *bits;
67     const struct bmp_pixelformat *format;
68     double xres, yres;
69     UINT lineswritten;
70     UINT stride;
71     BOOL committed;
72 } BmpFrameEncode;
73
74 static HRESULT WINAPI BmpFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid,
75     void **ppv)
76 {
77     BmpFrameEncode *This = (BmpFrameEncode*)iface;
78     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
79
80     if (!ppv) return E_INVALIDARG;
81
82     if (IsEqualIID(&IID_IUnknown, iid) ||
83         IsEqualIID(&IID_IWICBitmapFrameEncode, iid))
84     {
85         *ppv = This;
86     }
87     else
88     {
89         *ppv = NULL;
90         return E_NOINTERFACE;
91     }
92
93     IUnknown_AddRef((IUnknown*)*ppv);
94     return S_OK;
95 }
96
97 static ULONG WINAPI BmpFrameEncode_AddRef(IWICBitmapFrameEncode *iface)
98 {
99     BmpFrameEncode *This = (BmpFrameEncode*)iface;
100     ULONG ref = InterlockedIncrement(&This->ref);
101
102     TRACE("(%p) refcount=%u\n", iface, ref);
103
104     return ref;
105 }
106
107 static ULONG WINAPI BmpFrameEncode_Release(IWICBitmapFrameEncode *iface)
108 {
109     BmpFrameEncode *This = (BmpFrameEncode*)iface;
110     ULONG ref = InterlockedDecrement(&This->ref);
111
112     TRACE("(%p) refcount=%u\n", iface, ref);
113
114     if (ref == 0)
115     {
116         if (This->stream) IStream_Release(This->stream);
117         HeapFree(GetProcessHeap(), 0, This->bits);
118         HeapFree(GetProcessHeap(), 0, This);
119     }
120
121     return ref;
122 }
123
124 static HRESULT WINAPI BmpFrameEncode_Initialize(IWICBitmapFrameEncode *iface,
125     IPropertyBag2 *pIEncoderOptions)
126 {
127     BmpFrameEncode *This = (BmpFrameEncode*)iface;
128     TRACE("(%p,%p)\n", iface, pIEncoderOptions);
129
130     if (This->initialized) return WINCODEC_ERR_WRONGSTATE;
131
132     This->initialized = TRUE;
133
134     return S_OK;
135 }
136
137 static HRESULT WINAPI BmpFrameEncode_SetSize(IWICBitmapFrameEncode *iface,
138     UINT uiWidth, UINT uiHeight)
139 {
140     BmpFrameEncode *This = (BmpFrameEncode*)iface;
141     TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight);
142
143     if (!This->initialized || This->bits) return WINCODEC_ERR_WRONGSTATE;
144
145     This->width = uiWidth;
146     This->height = uiHeight;
147
148     return S_OK;
149 }
150
151 static HRESULT WINAPI BmpFrameEncode_SetResolution(IWICBitmapFrameEncode *iface,
152     double dpiX, double dpiY)
153 {
154     BmpFrameEncode *This = (BmpFrameEncode*)iface;
155     TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY);
156
157     if (!This->initialized || This->bits) return WINCODEC_ERR_WRONGSTATE;
158
159     This->xres = dpiX;
160     This->yres = dpiY;
161
162     return S_OK;
163 }
164
165 static HRESULT WINAPI BmpFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface,
166     WICPixelFormatGUID *pPixelFormat)
167 {
168     BmpFrameEncode *This = (BmpFrameEncode*)iface;
169     int i;
170     TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat));
171
172     if (!This->initialized || This->bits) return WINCODEC_ERR_WRONGSTATE;
173
174     for (i=0; formats[i].guid; i++)
175     {
176         if (memcmp(formats[i].guid, pPixelFormat, sizeof(GUID)) == 0)
177             break;
178     }
179
180     if (!formats[i].guid) i = 0;
181
182     This->format = &formats[i];
183     memcpy(pPixelFormat, This->format->guid, sizeof(GUID));
184
185     return S_OK;
186 }
187
188 static HRESULT WINAPI BmpFrameEncode_SetColorContexts(IWICBitmapFrameEncode *iface,
189     UINT cCount, IWICColorContext **ppIColorContext)
190 {
191     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
192     return E_NOTIMPL;
193 }
194
195 static HRESULT WINAPI BmpFrameEncode_SetPalette(IWICBitmapFrameEncode *iface,
196     IWICPalette *pIPalette)
197 {
198     FIXME("(%p,%p): stub\n", iface, pIPalette);
199     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
200 }
201
202 static HRESULT WINAPI BmpFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface,
203     IWICBitmapSource *pIThumbnail)
204 {
205     FIXME("(%p,%p): stub\n", iface, pIThumbnail);
206     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
207 }
208
209 static HRESULT BmpFrameEncode_AllocateBits(BmpFrameEncode *This)
210 {
211     if (!This->bits)
212     {
213         if (!This->initialized || !This->width || !This->height || !This->format)
214             return WINCODEC_ERR_WRONGSTATE;
215
216         This->stride = (((This->width * This->format->bpp)+31)/32)*4;
217         This->bits = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->stride * This->height);
218         if (!This->bits) return E_OUTOFMEMORY;
219     }
220
221     return S_OK;
222 }
223
224 static HRESULT WINAPI BmpFrameEncode_WritePixels(IWICBitmapFrameEncode *iface,
225     UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels)
226 {
227     BmpFrameEncode *This = (BmpFrameEncode*)iface;
228     HRESULT hr;
229     WICRect rc;
230     TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels);
231
232     if (!This->initialized || !This->width || !This->height || !This->format)
233         return WINCODEC_ERR_WRONGSTATE;
234
235     hr = BmpFrameEncode_AllocateBits(This);
236     if (FAILED(hr)) return hr;
237
238     rc.X = 0;
239     rc.Y = 0;
240     rc.Width = This->width;
241     rc.Height = lineCount;
242
243     hr = copy_pixels(This->format->bpp, pbPixels, This->width, lineCount, cbStride,
244         &rc, This->stride, This->stride*(This->height-This->lineswritten),
245         This->bits + This->stride*This->lineswritten);
246
247     if (SUCCEEDED(hr))
248         This->lineswritten += lineCount;
249
250     return hr;
251 }
252
253 static HRESULT WINAPI BmpFrameEncode_WriteSource(IWICBitmapFrameEncode *iface,
254     IWICBitmapSource *pIBitmapSource, WICRect *prc)
255 {
256     BmpFrameEncode *This = (BmpFrameEncode*)iface;
257     HRESULT hr;
258     WICRect rc;
259     WICPixelFormatGUID guid;
260     TRACE("(%p,%p,%p)\n", iface, pIBitmapSource, prc);
261
262     if (!This->initialized || !This->width || !This->height)
263         return WINCODEC_ERR_WRONGSTATE;
264
265     if (!This->format)
266     {
267         hr = IWICBitmapSource_GetPixelFormat(pIBitmapSource, &guid);
268         if (FAILED(hr)) return hr;
269         hr = BmpFrameEncode_SetPixelFormat(iface, &guid);
270         if (FAILED(hr)) return hr;
271     }
272
273     hr = IWICBitmapSource_GetPixelFormat(pIBitmapSource, &guid);
274     if (FAILED(hr)) return hr;
275     if (memcmp(&guid, This->format->guid, sizeof(GUID)) != 0)
276     {
277         /* should use WICConvertBitmapSource to convert, but that's unimplemented */
278         ERR("format %s unsupported\n", debugstr_guid(&guid));
279         return E_FAIL;
280     }
281
282     if (This->xres == 0.0 || This->yres == 0.0)
283     {
284         double xres, yres;
285         hr = IWICBitmapSource_GetResolution(pIBitmapSource, &xres, &yres);
286         if (FAILED(hr)) return hr;
287         hr = BmpFrameEncode_SetResolution(iface, xres, yres);
288         if (FAILED(hr)) return hr;
289     }
290
291     if (!prc)
292     {
293         UINT width, height;
294         hr = IWICBitmapSource_GetSize(pIBitmapSource, &width, &height);
295         if (FAILED(hr)) return hr;
296         rc.X = 0;
297         rc.Y = 0;
298         rc.Width = width;
299         rc.Height = height;
300         prc = &rc;
301     }
302
303     if (prc->Width != This->width) return E_INVALIDARG;
304
305     hr = BmpFrameEncode_AllocateBits(This);
306     if (FAILED(hr)) return hr;
307
308     hr = IWICBitmapSource_CopyPixels(pIBitmapSource, prc, This->stride,
309         This->stride*(This->height-This->lineswritten),
310         This->bits + This->stride*This->lineswritten);
311
312     This->lineswritten += rc.Height;
313
314     return S_OK;
315 }
316
317 static HRESULT WINAPI BmpFrameEncode_Commit(IWICBitmapFrameEncode *iface)
318 {
319     BmpFrameEncode *This = (BmpFrameEncode*)iface;
320     BITMAPFILEHEADER bfh;
321     BITMAPV5HEADER bih;
322     UINT info_size;
323     LARGE_INTEGER pos;
324     ULONG byteswritten;
325     HRESULT hr;
326
327     TRACE("(%p)\n", iface);
328
329     if (!This->bits || This->committed || This->height != This->lineswritten)
330         return WINCODEC_ERR_WRONGSTATE;
331
332     bfh.bfType = 0x4d42; /* "BM" */
333     bfh.bfReserved1 = 0;
334     bfh.bfReserved2 = 0;
335
336     bih.bV5Size = info_size = sizeof(BITMAPINFOHEADER);
337     bih.bV5Width = This->width;
338     bih.bV5Height = -This->height; /* top-down bitmap */
339     bih.bV5Planes = 1;
340     bih.bV5BitCount = This->format->bpp;
341     bih.bV5Compression = This->format->compression;
342     bih.bV5SizeImage = This->stride*This->height;
343     bih.bV5XPelsPerMeter = (This->xres+0.0127) / 0.0254;
344     bih.bV5YPelsPerMeter = (This->yres+0.0127) / 0.0254;
345     bih.bV5ClrUsed = 0;
346     bih.bV5ClrImportant = 0;
347
348     if (This->format->compression == BI_BITFIELDS)
349     {
350         if (This->format->alphamask)
351             bih.bV5Size = info_size = sizeof(BITMAPV4HEADER);
352         else
353             info_size = sizeof(BITMAPINFOHEADER)+12;
354         bih.bV5RedMask = This->format->redmask;
355         bih.bV5GreenMask = This->format->greenmask;
356         bih.bV5BlueMask = This->format->bluemask;
357         bih.bV5AlphaMask = This->format->alphamask;
358         bih.bV5AlphaMask = LCS_DEVICE_RGB;
359     }
360
361     bfh.bfSize = sizeof(BITMAPFILEHEADER) + info_size + bih.bV5SizeImage;
362     bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + info_size;
363
364     pos.QuadPart = 0;
365     hr = IStream_Seek(This->stream, pos, STREAM_SEEK_SET, NULL);
366     if (FAILED(hr)) return hr;
367
368     hr = IStream_Write(This->stream, &bfh, sizeof(BITMAPFILEHEADER), &byteswritten);
369     if (FAILED(hr)) return hr;
370     if (byteswritten != sizeof(BITMAPFILEHEADER)) return E_FAIL;
371
372     hr = IStream_Write(This->stream, &bih, info_size, &byteswritten);
373     if (FAILED(hr)) return hr;
374     if (byteswritten != info_size) return E_FAIL;
375
376     hr = IStream_Write(This->stream, This->bits, bih.bV5SizeImage, &byteswritten);
377     if (FAILED(hr)) return hr;
378     if (byteswritten != bih.bV5SizeImage) return E_FAIL;
379
380     This->committed = TRUE;
381
382     return S_OK;
383 }
384
385 static HRESULT WINAPI BmpFrameEncode_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface,
386     IWICMetadataQueryWriter **ppIMetadataQueryWriter)
387 {
388     FIXME("(%p, %p): stub\n", iface, ppIMetadataQueryWriter);
389     return E_NOTIMPL;
390 }
391
392 static const IWICBitmapFrameEncodeVtbl BmpFrameEncode_Vtbl = {
393     BmpFrameEncode_QueryInterface,
394     BmpFrameEncode_AddRef,
395     BmpFrameEncode_Release,
396     BmpFrameEncode_Initialize,
397     BmpFrameEncode_SetSize,
398     BmpFrameEncode_SetResolution,
399     BmpFrameEncode_SetPixelFormat,
400     BmpFrameEncode_SetColorContexts,
401     BmpFrameEncode_SetPalette,
402     BmpFrameEncode_SetThumbnail,
403     BmpFrameEncode_WritePixels,
404     BmpFrameEncode_WriteSource,
405     BmpFrameEncode_Commit,
406     BmpFrameEncode_GetMetadataQueryWriter
407 };
408
409 typedef struct BmpEncoder {
410     const IWICBitmapEncoderVtbl *lpVtbl;
411     LONG ref;
412     IStream *stream;
413     IWICBitmapFrameEncode *frame;
414 } BmpEncoder;
415
416 static HRESULT WINAPI BmpEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid,
417     void **ppv)
418 {
419     BmpEncoder *This = (BmpEncoder*)iface;
420     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
421
422     if (!ppv) return E_INVALIDARG;
423
424     if (IsEqualIID(&IID_IUnknown, iid) ||
425         IsEqualIID(&IID_IWICBitmapEncoder, iid))
426     {
427         *ppv = This;
428     }
429     else
430     {
431         *ppv = NULL;
432         return E_NOINTERFACE;
433     }
434
435     IUnknown_AddRef((IUnknown*)*ppv);
436     return S_OK;
437 }
438
439 static ULONG WINAPI BmpEncoder_AddRef(IWICBitmapEncoder *iface)
440 {
441     BmpEncoder *This = (BmpEncoder*)iface;
442     ULONG ref = InterlockedIncrement(&This->ref);
443
444     TRACE("(%p) refcount=%u\n", iface, ref);
445
446     return ref;
447 }
448
449 static ULONG WINAPI BmpEncoder_Release(IWICBitmapEncoder *iface)
450 {
451     BmpEncoder *This = (BmpEncoder*)iface;
452     ULONG ref = InterlockedDecrement(&This->ref);
453
454     TRACE("(%p) refcount=%u\n", iface, ref);
455
456     if (ref == 0)
457     {
458         if (This->stream) IStream_Release(This->stream);
459         if (This->frame) IWICBitmapFrameEncode_Release(This->frame);
460         HeapFree(GetProcessHeap(), 0, This);
461     }
462
463     return ref;
464 }
465
466 static HRESULT WINAPI BmpEncoder_Initialize(IWICBitmapEncoder *iface,
467     IStream *pIStream, WICBitmapEncoderCacheOption cacheOption)
468 {
469     BmpEncoder *This = (BmpEncoder*)iface;
470
471     TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption);
472
473     IStream_AddRef(pIStream);
474     This->stream = pIStream;
475
476     return S_OK;
477 }
478
479 static HRESULT WINAPI BmpEncoder_GetContainerFormat(IWICBitmapEncoder *iface,
480     GUID *pguidContainerFormat)
481 {
482     FIXME("(%p,%s): stub\n", iface, debugstr_guid(pguidContainerFormat));
483     return E_NOTIMPL;
484 }
485
486 static HRESULT WINAPI BmpEncoder_GetEncoderInfo(IWICBitmapEncoder *iface,
487     IWICBitmapEncoderInfo **ppIEncoderInfo)
488 {
489     FIXME("(%p,%p): stub\n", iface, ppIEncoderInfo);
490     return E_NOTIMPL;
491 }
492
493 static HRESULT WINAPI BmpEncoder_SetColorContexts(IWICBitmapEncoder *iface,
494     UINT cCount, IWICColorContext **ppIColorContext)
495 {
496     FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
497     return E_NOTIMPL;
498 }
499
500 static HRESULT WINAPI BmpEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *pIPalette)
501 {
502     TRACE("(%p,%p)\n", iface, pIPalette);
503     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
504 }
505
506 static HRESULT WINAPI BmpEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail)
507 {
508     TRACE("(%p,%p)\n", iface, pIThumbnail);
509     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
510 }
511
512 static HRESULT WINAPI BmpEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview)
513 {
514     TRACE("(%p,%p)\n", iface, pIPreview);
515     return WINCODEC_ERR_UNSUPPORTEDOPERATION;
516 }
517
518 static HRESULT WINAPI BmpEncoder_CreateNewFrame(IWICBitmapEncoder *iface,
519     IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions)
520 {
521     BmpEncoder *This = (BmpEncoder*)iface;
522     BmpFrameEncode *encode;
523     HRESULT hr;
524
525     TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions);
526
527     if (This->frame) return WINCODEC_ERR_UNSUPPORTEDOPERATION;
528
529     if (!This->stream) return WINCODEC_ERR_NOTINITIALIZED;
530
531     hr = CreatePropertyBag2(ppIEncoderOptions);
532     if (FAILED(hr)) return hr;
533
534     encode = HeapAlloc(GetProcessHeap(), 0, sizeof(BmpFrameEncode));
535     if (!encode)
536     {
537         IPropertyBag2_Release(*ppIEncoderOptions);
538         *ppIEncoderOptions = NULL;
539         return E_OUTOFMEMORY;
540     }
541     encode->lpVtbl = &BmpFrameEncode_Vtbl;
542     encode->ref = 2;
543     IStream_AddRef(This->stream);
544     encode->stream = This->stream;
545     encode->initialized = FALSE;
546     encode->width = 0;
547     encode->height = 0;
548     encode->bits = NULL;
549     encode->format = NULL;
550     encode->xres = 0.0;
551     encode->yres = 0.0;
552     encode->lineswritten = 0;
553     encode->committed = FALSE;
554
555     *ppIFrameEncode = (IWICBitmapFrameEncode*)encode;
556     This->frame = (IWICBitmapFrameEncode*)encode;
557
558     return S_OK;
559 }
560
561 static HRESULT WINAPI BmpEncoder_Commit(IWICBitmapEncoder *iface)
562 {
563     BmpEncoder *This = (BmpEncoder*)iface;
564     BmpFrameEncode *frame = (BmpFrameEncode*)This->frame;
565     TRACE("(%p)\n", iface);
566
567     if (!frame || !frame->committed) return WINCODEC_ERR_WRONGSTATE;
568
569     return S_OK;
570 }
571
572 static HRESULT WINAPI BmpEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface,
573     IWICMetadataQueryWriter **ppIMetadataQueryWriter)
574 {
575     FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter);
576     return E_NOTIMPL;
577 }
578
579 static const IWICBitmapEncoderVtbl BmpEncoder_Vtbl = {
580     BmpEncoder_QueryInterface,
581     BmpEncoder_AddRef,
582     BmpEncoder_Release,
583     BmpEncoder_Initialize,
584     BmpEncoder_GetContainerFormat,
585     BmpEncoder_GetEncoderInfo,
586     BmpEncoder_SetColorContexts,
587     BmpEncoder_SetPalette,
588     BmpEncoder_SetThumbnail,
589     BmpEncoder_SetPreview,
590     BmpEncoder_CreateNewFrame,
591     BmpEncoder_Commit,
592     BmpEncoder_GetMetadataQueryWriter
593 };
594
595 HRESULT BmpEncoder_CreateInstance(IUnknown *pUnkOuter, REFIID iid, void** ppv)
596 {
597     BmpEncoder *This;
598     HRESULT ret;
599
600     TRACE("(%p,%s,%p)\n", pUnkOuter, debugstr_guid(iid), ppv);
601
602     *ppv = NULL;
603
604     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
605
606     This = HeapAlloc(GetProcessHeap(), 0, sizeof(BmpEncoder));
607     if (!This) return E_OUTOFMEMORY;
608
609     This->lpVtbl = &BmpEncoder_Vtbl;
610     This->ref = 1;
611     This->stream = NULL;
612     This->frame = NULL;
613
614     ret = IUnknown_QueryInterface((IUnknown*)This, iid, ppv);
615     IUnknown_Release((IUnknown*)This);
616
617     return ret;
618 }