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