bmpencode.c 19 KB
Newer Older
1 2
/*
 * Copyright 2009 Vincent Povirk for CodeWeavers
3
 * Copyright 2016 Dmitry Timoshkov
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <stdarg.h>

#define COBJMACROS

#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "wingdi.h"
#include "objbase.h"

#include "wincodecs_private.h"

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);

36 37 38
struct bmp_pixelformat {
    const WICPixelFormatGUID *guid;
    UINT bpp;
39
    UINT colors; /* palette size */
40 41 42 43 44 45 46 47
    DWORD compression;
    DWORD redmask;
    DWORD greenmask;
    DWORD bluemask;
    DWORD alphamask;
};

static const struct bmp_pixelformat formats[] = {
48 49 50 51 52 53 54 55 56
    {&GUID_WICPixelFormat24bppBGR, 24, 0, BI_RGB},
    {&GUID_WICPixelFormatBlackWhite, 1, 2, BI_RGB},
    {&GUID_WICPixelFormat1bppIndexed, 1, 2, BI_RGB},
    {&GUID_WICPixelFormat2bppIndexed, 2, 4, BI_RGB},
    {&GUID_WICPixelFormat4bppIndexed, 4, 16, BI_RGB},
    {&GUID_WICPixelFormat8bppIndexed, 8, 256, BI_RGB},
    {&GUID_WICPixelFormat16bppBGR555, 16, 0, BI_RGB},
    {&GUID_WICPixelFormat16bppBGR565, 16, 0, BI_BITFIELDS, 0xf800, 0x7e0, 0x1f, 0},
    {&GUID_WICPixelFormat32bppBGR, 32, 0, BI_RGB},
57 58
#if 0
    /* Windows doesn't seem to support this one. */
59
    {&GUID_WICPixelFormat32bppBGRA, 32, 0, BI_BITFIELDS, 0xff0000, 0xff00, 0xff, 0xff000000},
60
#endif
61 62 63
    {NULL}
};

64
typedef struct BmpFrameEncode {
65
    IWICBitmapFrameEncode IWICBitmapFrameEncode_iface;
66 67
    LONG ref;
    IStream *stream;
68
    BOOL initialized;
69 70
    UINT width, height;
    BYTE *bits;
71
    const struct bmp_pixelformat *format;
72
    double xres, yres;
73 74
    UINT lineswritten;
    UINT stride;
75 76
    WICColor palette[256];
    UINT colors;
77
    BOOL committed;
78 79
} BmpFrameEncode;

80 81 82 83 84
static inline BmpFrameEncode *impl_from_IWICBitmapFrameEncode(IWICBitmapFrameEncode *iface)
{
    return CONTAINING_RECORD(iface, BmpFrameEncode, IWICBitmapFrameEncode_iface);
}

85 86 87
static HRESULT WINAPI BmpFrameEncode_QueryInterface(IWICBitmapFrameEncode *iface, REFIID iid,
    void **ppv)
{
88
    BmpFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
89 90 91 92 93 94 95
    TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);

    if (!ppv) return E_INVALIDARG;

    if (IsEqualIID(&IID_IUnknown, iid) ||
        IsEqualIID(&IID_IWICBitmapFrameEncode, iid))
    {
96
        *ppv = &This->IWICBitmapFrameEncode_iface;
97 98 99 100 101 102 103 104 105 106 107 108 109
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    IUnknown_AddRef((IUnknown*)*ppv);
    return S_OK;
}

static ULONG WINAPI BmpFrameEncode_AddRef(IWICBitmapFrameEncode *iface)
{
110
    BmpFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
111 112
    ULONG ref = InterlockedIncrement(&This->ref);

113
    TRACE("(%p) refcount=%lu\n", iface, ref);
114 115 116 117 118 119

    return ref;
}

static ULONG WINAPI BmpFrameEncode_Release(IWICBitmapFrameEncode *iface)
{
120
    BmpFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
121 122
    ULONG ref = InterlockedDecrement(&This->ref);

123
    TRACE("(%p) refcount=%lu\n", iface, ref);
124 125 126 127

    if (ref == 0)
    {
        if (This->stream) IStream_Release(This->stream);
128
        HeapFree(GetProcessHeap(), 0, This->bits);
129 130 131 132 133 134 135 136 137
        HeapFree(GetProcessHeap(), 0, This);
    }

    return ref;
}

static HRESULT WINAPI BmpFrameEncode_Initialize(IWICBitmapFrameEncode *iface,
    IPropertyBag2 *pIEncoderOptions)
{
138
    BmpFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
139 140 141 142
    TRACE("(%p,%p)\n", iface, pIEncoderOptions);

    if (This->initialized) return WINCODEC_ERR_WRONGSTATE;

143 144 145
    if (pIEncoderOptions)
        WARN("ignoring encoder options.\n");

146 147 148
    This->initialized = TRUE;

    return S_OK;
149 150 151 152 153
}

static HRESULT WINAPI BmpFrameEncode_SetSize(IWICBitmapFrameEncode *iface,
    UINT uiWidth, UINT uiHeight)
{
154
    BmpFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
155 156 157 158 159 160 161 162
    TRACE("(%p,%u,%u)\n", iface, uiWidth, uiHeight);

    if (!This->initialized || This->bits) return WINCODEC_ERR_WRONGSTATE;

    This->width = uiWidth;
    This->height = uiHeight;

    return S_OK;
163 164 165 166 167
}

static HRESULT WINAPI BmpFrameEncode_SetResolution(IWICBitmapFrameEncode *iface,
    double dpiX, double dpiY)
{
168
    BmpFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
169 170 171 172 173 174 175 176
    TRACE("(%p,%0.2f,%0.2f)\n", iface, dpiX, dpiY);

    if (!This->initialized || This->bits) return WINCODEC_ERR_WRONGSTATE;

    This->xres = dpiX;
    This->yres = dpiY;

    return S_OK;
177 178 179 180 181
}

static HRESULT WINAPI BmpFrameEncode_SetPixelFormat(IWICBitmapFrameEncode *iface,
    WICPixelFormatGUID *pPixelFormat)
{
182
    BmpFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
183 184 185 186 187
    int i;
    TRACE("(%p,%s)\n", iface, debugstr_guid(pPixelFormat));

    if (!This->initialized || This->bits) return WINCODEC_ERR_WRONGSTATE;

188 189 190 191 192
    if (IsEqualGUID(pPixelFormat, &GUID_WICPixelFormatBlackWhite))
        *pPixelFormat = GUID_WICPixelFormat1bppIndexed;
    else if (IsEqualGUID(pPixelFormat, &GUID_WICPixelFormat2bppIndexed))
        *pPixelFormat = GUID_WICPixelFormat4bppIndexed;

193 194
    for (i=0; formats[i].guid; i++)
    {
195
        if (IsEqualGUID(formats[i].guid, pPixelFormat))
196 197 198 199 200 201 202 203 204
            break;
    }

    if (!formats[i].guid) i = 0;

    This->format = &formats[i];
    memcpy(pPixelFormat, This->format->guid, sizeof(GUID));

    return S_OK;
205 206 207 208 209 210 211 212 213 214
}

static HRESULT WINAPI BmpFrameEncode_SetColorContexts(IWICBitmapFrameEncode *iface,
    UINT cCount, IWICColorContext **ppIColorContext)
{
    FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
    return E_NOTIMPL;
}

static HRESULT WINAPI BmpFrameEncode_SetPalette(IWICBitmapFrameEncode *iface,
215
    IWICPalette *palette)
216
{
217
    BmpFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
218
    HRESULT hr;
219 220 221 222 223 224 225 226

    TRACE("(%p,%p)\n", iface, palette);

    if (!palette) return E_INVALIDARG;

    if (!This->initialized)
        return WINCODEC_ERR_NOTINITIALIZED;

227 228 229 230 231 232 233 234
    hr = IWICPalette_GetColors(palette, 256, This->palette, &This->colors);
    if (hr == S_OK)
    {
        UINT i;
        for (i = 0; i < This->colors; i++)
            This->palette[i] |= 0xff000000; /* BMP palette has no alpha */
    }
    return hr;
235 236 237 238 239 240 241 242 243
}

static HRESULT WINAPI BmpFrameEncode_SetThumbnail(IWICBitmapFrameEncode *iface,
    IWICBitmapSource *pIThumbnail)
{
    FIXME("(%p,%p): stub\n", iface, pIThumbnail);
    return WINCODEC_ERR_UNSUPPORTEDOPERATION;
}

244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
static HRESULT BmpFrameEncode_AllocateBits(BmpFrameEncode *This)
{
    if (!This->bits)
    {
        if (!This->initialized || !This->width || !This->height || !This->format)
            return WINCODEC_ERR_WRONGSTATE;

        This->stride = (((This->width * This->format->bpp)+31)/32)*4;
        This->bits = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->stride * This->height);
        if (!This->bits) return E_OUTOFMEMORY;
    }

    return S_OK;
}

259 260 261
static HRESULT WINAPI BmpFrameEncode_WritePixels(IWICBitmapFrameEncode *iface,
    UINT lineCount, UINT cbStride, UINT cbBufferSize, BYTE *pbPixels)
{
262
    BmpFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
263 264
    UINT dstbuffersize, bytesperrow, row;
    BYTE *dst, *src;
265
    HRESULT hr;
266

267 268 269 270 271 272 273 274
    TRACE("(%p,%u,%u,%u,%p)\n", iface, lineCount, cbStride, cbBufferSize, pbPixels);

    if (!This->initialized || !This->width || !This->height || !This->format)
        return WINCODEC_ERR_WRONGSTATE;

    hr = BmpFrameEncode_AllocateBits(This);
    if (FAILED(hr)) return hr;

275
    bytesperrow = ((This->format->bpp * This->width) + 7) / 8;
276

277 278
    if (This->stride < bytesperrow)
        return E_INVALIDARG;
279

280 281 282
    dstbuffersize = This->stride * (This->height - This->lineswritten);
    if ((This->stride * (lineCount - 1)) + bytesperrow > dstbuffersize)
        return E_INVALIDARG;
283

284 285 286 287 288 289 290 291 292 293 294 295
    src = pbPixels;
    dst = This->bits + This->stride * (This->height - This->lineswritten - 1);
    for (row = 0; row < lineCount; row++)
    {
        memcpy(dst, src, bytesperrow);
        src += cbStride;
        dst -= This->stride;
    }

    This->lineswritten += lineCount;

    return S_OK;
296 297 298 299 300
}

static HRESULT WINAPI BmpFrameEncode_WriteSource(IWICBitmapFrameEncode *iface,
    IWICBitmapSource *pIBitmapSource, WICRect *prc)
{
301
    BmpFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
302
    HRESULT hr;
303
    TRACE("(%p,%p,%s)\n", iface, pIBitmapSource, debug_wic_rect(prc));
304

305
    if (!This->initialized)
306 307
        return WINCODEC_ERR_WRONGSTATE;

308 309 310
    hr = configure_write_source(iface, pIBitmapSource, prc,
        This->format ? This->format->guid : NULL, This->width, This->height,
        This->xres, This->yres);
311

312
    if (SUCCEEDED(hr))
313
    {
314
        hr = write_source(iface, pIBitmapSource, prc,
315 316
            This->format->guid, This->format->bpp, !This->colors && This->format->colors,
            This->width, This->height);
317 318
    }

319
    return hr;
320 321 322 323
}

static HRESULT WINAPI BmpFrameEncode_Commit(IWICBitmapFrameEncode *iface)
{
324
    BmpFrameEncode *This = impl_from_IWICBitmapFrameEncode(iface);
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
    BITMAPFILEHEADER bfh;
    BITMAPV5HEADER bih;
    UINT info_size;
    LARGE_INTEGER pos;
    ULONG byteswritten;
    HRESULT hr;

    TRACE("(%p)\n", iface);

    if (!This->bits || This->committed || This->height != This->lineswritten)
        return WINCODEC_ERR_WRONGSTATE;

    bfh.bfType = 0x4d42; /* "BM" */
    bfh.bfReserved1 = 0;
    bfh.bfReserved2 = 0;

    bih.bV5Size = info_size = sizeof(BITMAPINFOHEADER);
    bih.bV5Width = This->width;
343
    bih.bV5Height = This->height;
344 345 346 347
    bih.bV5Planes = 1;
    bih.bV5BitCount = This->format->bpp;
    bih.bV5Compression = This->format->compression;
    bih.bV5SizeImage = This->stride*This->height;
348 349
    bih.bV5XPelsPerMeter = (This->xres+0.0127) / 0.0254;
    bih.bV5YPelsPerMeter = (This->yres+0.0127) / 0.0254;
350 351
    bih.bV5ClrUsed = (This->format->bpp <= 8) ? This->colors : 0;
    bih.bV5ClrImportant = bih.bV5ClrUsed;
352

353 354 355 356 357 358 359 360 361 362
    if (This->format->compression == BI_BITFIELDS)
    {
        if (This->format->alphamask)
            bih.bV5Size = info_size = sizeof(BITMAPV4HEADER);
        else
            info_size = sizeof(BITMAPINFOHEADER)+12;
        bih.bV5RedMask = This->format->redmask;
        bih.bV5GreenMask = This->format->greenmask;
        bih.bV5BlueMask = This->format->bluemask;
        bih.bV5AlphaMask = This->format->alphamask;
363
        bih.bV5CSType = LCS_DEVICE_RGB;
364 365
    }

366 367
    bfh.bfSize = sizeof(BITMAPFILEHEADER) + info_size + bih.bV5SizeImage;
    bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + info_size;
368
    bfh.bfOffBits += bih.bV5ClrUsed * sizeof(WICColor);
369 370

    pos.QuadPart = 0;
371
    hr = IStream_Seek(This->stream, pos, STREAM_SEEK_SET, NULL);
372 373 374 375 376 377 378 379 380 381
    if (FAILED(hr)) return hr;

    hr = IStream_Write(This->stream, &bfh, sizeof(BITMAPFILEHEADER), &byteswritten);
    if (FAILED(hr)) return hr;
    if (byteswritten != sizeof(BITMAPFILEHEADER)) return E_FAIL;

    hr = IStream_Write(This->stream, &bih, info_size, &byteswritten);
    if (FAILED(hr)) return hr;
    if (byteswritten != info_size) return E_FAIL;

382 383 384 385 386 387 388 389
    /* write the palette */
    if (This->format->colors)
    {
        hr = IStream_Write(This->stream, This->palette, This->colors * sizeof(WICColor), &byteswritten);
        if (FAILED(hr)) return hr;
        if (byteswritten != This->colors * sizeof(WICColor)) return E_FAIL;
    }

390 391 392 393 394 395 396
    hr = IStream_Write(This->stream, This->bits, bih.bV5SizeImage, &byteswritten);
    if (FAILED(hr)) return hr;
    if (byteswritten != bih.bV5SizeImage) return E_FAIL;

    This->committed = TRUE;

    return S_OK;
397 398 399
}

static HRESULT WINAPI BmpFrameEncode_GetMetadataQueryWriter(IWICBitmapFrameEncode *iface,
400
        IWICMetadataQueryWriter **query_writer)
401
{
402 403 404 405 406 407 408 409
    BmpFrameEncode *encoder = impl_from_IWICBitmapFrameEncode(iface);

    TRACE("iface %p, query_writer %p.\n", iface, query_writer);

    if (!encoder->initialized)
        return WINCODEC_ERR_NOTINITIALIZED;

    return WINCODEC_ERR_UNSUPPORTEDOPERATION;
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
}

static const IWICBitmapFrameEncodeVtbl BmpFrameEncode_Vtbl = {
    BmpFrameEncode_QueryInterface,
    BmpFrameEncode_AddRef,
    BmpFrameEncode_Release,
    BmpFrameEncode_Initialize,
    BmpFrameEncode_SetSize,
    BmpFrameEncode_SetResolution,
    BmpFrameEncode_SetPixelFormat,
    BmpFrameEncode_SetColorContexts,
    BmpFrameEncode_SetPalette,
    BmpFrameEncode_SetThumbnail,
    BmpFrameEncode_WritePixels,
    BmpFrameEncode_WriteSource,
    BmpFrameEncode_Commit,
    BmpFrameEncode_GetMetadataQueryWriter
};

429
typedef struct BmpEncoder {
430
    IWICBitmapEncoder IWICBitmapEncoder_iface;
431
    LONG ref;
432
    IStream *stream;
433
    BmpFrameEncode *frame;
434 435
} BmpEncoder;

436 437 438 439 440
static inline BmpEncoder *impl_from_IWICBitmapEncoder(IWICBitmapEncoder *iface)
{
    return CONTAINING_RECORD(iface, BmpEncoder, IWICBitmapEncoder_iface);
}

441 442 443
static HRESULT WINAPI BmpEncoder_QueryInterface(IWICBitmapEncoder *iface, REFIID iid,
    void **ppv)
{
444
    BmpEncoder *This = impl_from_IWICBitmapEncoder(iface);
445 446 447 448 449 450 451
    TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);

    if (!ppv) return E_INVALIDARG;

    if (IsEqualIID(&IID_IUnknown, iid) ||
        IsEqualIID(&IID_IWICBitmapEncoder, iid))
    {
452
        *ppv = &This->IWICBitmapEncoder_iface;
453 454 455 456 457 458 459 460 461 462 463 464 465
    }
    else
    {
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    IUnknown_AddRef((IUnknown*)*ppv);
    return S_OK;
}

static ULONG WINAPI BmpEncoder_AddRef(IWICBitmapEncoder *iface)
{
466
    BmpEncoder *This = impl_from_IWICBitmapEncoder(iface);
467 468
    ULONG ref = InterlockedIncrement(&This->ref);

469
    TRACE("(%p) refcount=%lu\n", iface, ref);
470 471 472 473 474 475

    return ref;
}

static ULONG WINAPI BmpEncoder_Release(IWICBitmapEncoder *iface)
{
476
    BmpEncoder *This = impl_from_IWICBitmapEncoder(iface);
477 478
    ULONG ref = InterlockedDecrement(&This->ref);

479
    TRACE("(%p) refcount=%lu\n", iface, ref);
480 481 482

    if (ref == 0)
    {
483
        if (This->stream) IStream_Release(This->stream);
484
        if (This->frame) IWICBitmapFrameEncode_Release(&This->frame->IWICBitmapFrameEncode_iface);
485 486 487 488 489 490 491 492 493
        HeapFree(GetProcessHeap(), 0, This);
    }

    return ref;
}

static HRESULT WINAPI BmpEncoder_Initialize(IWICBitmapEncoder *iface,
    IStream *pIStream, WICBitmapEncoderCacheOption cacheOption)
{
494
    BmpEncoder *This = impl_from_IWICBitmapEncoder(iface);
495 496 497 498 499 500 501

    TRACE("(%p,%p,%u)\n", iface, pIStream, cacheOption);

    IStream_AddRef(pIStream);
    This->stream = pIStream;

    return S_OK;
502 503 504 505 506
}

static HRESULT WINAPI BmpEncoder_GetContainerFormat(IWICBitmapEncoder *iface,
    GUID *pguidContainerFormat)
{
507 508 509 510 511
    TRACE("(%p,%p)\n", iface, pguidContainerFormat);

    if (!pguidContainerFormat)
        return E_INVALIDARG;

512 513
    memcpy(pguidContainerFormat, &GUID_ContainerFormatBmp, sizeof(GUID));
    return S_OK;
514 515
}

516
static HRESULT WINAPI BmpEncoder_GetEncoderInfo(IWICBitmapEncoder *iface, IWICBitmapEncoderInfo **info)
517
{
518 519 520 521 522 523 524 525 526 527 528 529 530 531
    IWICComponentInfo *comp_info;
    HRESULT hr;

    TRACE("%p,%p\n", iface, info);

    if (!info) return E_INVALIDARG;

    hr = CreateComponentInfo(&CLSID_WICBmpEncoder, &comp_info);
    if (hr == S_OK)
    {
        hr = IWICComponentInfo_QueryInterface(comp_info, &IID_IWICBitmapEncoderInfo, (void **)info);
        IWICComponentInfo_Release(comp_info);
    }
    return hr;
532 533 534 535 536 537 538 539 540
}

static HRESULT WINAPI BmpEncoder_SetColorContexts(IWICBitmapEncoder *iface,
    UINT cCount, IWICColorContext **ppIColorContext)
{
    FIXME("(%p,%u,%p): stub\n", iface, cCount, ppIColorContext);
    return E_NOTIMPL;
}

541
static HRESULT WINAPI BmpEncoder_SetPalette(IWICBitmapEncoder *iface, IWICPalette *palette)
542
{
543 544 545 546
    BmpEncoder *This = impl_from_IWICBitmapEncoder(iface);

    TRACE("(%p,%p)\n", iface, palette);
    return This->stream ? WINCODEC_ERR_UNSUPPORTEDOPERATION : WINCODEC_ERR_NOTINITIALIZED;
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
}

static HRESULT WINAPI BmpEncoder_SetThumbnail(IWICBitmapEncoder *iface, IWICBitmapSource *pIThumbnail)
{
    TRACE("(%p,%p)\n", iface, pIThumbnail);
    return WINCODEC_ERR_UNSUPPORTEDOPERATION;
}

static HRESULT WINAPI BmpEncoder_SetPreview(IWICBitmapEncoder *iface, IWICBitmapSource *pIPreview)
{
    TRACE("(%p,%p)\n", iface, pIPreview);
    return WINCODEC_ERR_UNSUPPORTEDOPERATION;
}

static HRESULT WINAPI BmpEncoder_CreateNewFrame(IWICBitmapEncoder *iface,
    IWICBitmapFrameEncode **ppIFrameEncode, IPropertyBag2 **ppIEncoderOptions)
{
564
    BmpEncoder *This = impl_from_IWICBitmapEncoder(iface);
565 566
    BmpFrameEncode *encode;
    HRESULT hr;
567 568
    static const PROPBAG2 opts[1] =
    {
569
        { PROPBAG2_TYPE_DATA, VT_BOOL, 0, 0, (LPOLESTR)L"EnableV5Header32bppBGRA" },
570
    };
571 572 573 574 575 576 577

    TRACE("(%p,%p,%p)\n", iface, ppIFrameEncode, ppIEncoderOptions);

    if (This->frame) return WINCODEC_ERR_UNSUPPORTEDOPERATION;

    if (!This->stream) return WINCODEC_ERR_NOTINITIALIZED;

578 579
    if (ppIEncoderOptions)
    {
580
        hr = CreatePropertyBag2(opts, ARRAY_SIZE(opts), ppIEncoderOptions);
581 582
        if (FAILED(hr)) return hr;
    }
583 584 585 586 587 588 589 590

    encode = HeapAlloc(GetProcessHeap(), 0, sizeof(BmpFrameEncode));
    if (!encode)
    {
        IPropertyBag2_Release(*ppIEncoderOptions);
        *ppIEncoderOptions = NULL;
        return E_OUTOFMEMORY;
    }
591
    encode->IWICBitmapFrameEncode_iface.lpVtbl = &BmpFrameEncode_Vtbl;
592 593 594
    encode->ref = 2;
    IStream_AddRef(This->stream);
    encode->stream = This->stream;
595
    encode->initialized = FALSE;
596 597 598
    encode->width = 0;
    encode->height = 0;
    encode->bits = NULL;
599
    encode->format = NULL;
600 601
    encode->xres = 0.0;
    encode->yres = 0.0;
602
    encode->lineswritten = 0;
603
    encode->colors = 0;
604
    encode->committed = FALSE;
605

606
    *ppIFrameEncode = &encode->IWICBitmapFrameEncode_iface;
607
    This->frame = encode;
608 609

    return S_OK;
610 611 612 613
}

static HRESULT WINAPI BmpEncoder_Commit(IWICBitmapEncoder *iface)
{
614
    BmpEncoder *This = impl_from_IWICBitmapEncoder(iface);
615 616
    TRACE("(%p)\n", iface);

617
    if (!This->frame || !This->frame->committed) return WINCODEC_ERR_WRONGSTATE;
618 619

    return S_OK;
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
}

static HRESULT WINAPI BmpEncoder_GetMetadataQueryWriter(IWICBitmapEncoder *iface,
    IWICMetadataQueryWriter **ppIMetadataQueryWriter)
{
    FIXME("(%p,%p): stub\n", iface, ppIMetadataQueryWriter);
    return E_NOTIMPL;
}

static const IWICBitmapEncoderVtbl BmpEncoder_Vtbl = {
    BmpEncoder_QueryInterface,
    BmpEncoder_AddRef,
    BmpEncoder_Release,
    BmpEncoder_Initialize,
    BmpEncoder_GetContainerFormat,
    BmpEncoder_GetEncoderInfo,
    BmpEncoder_SetColorContexts,
    BmpEncoder_SetPalette,
    BmpEncoder_SetThumbnail,
    BmpEncoder_SetPreview,
    BmpEncoder_CreateNewFrame,
    BmpEncoder_Commit,
    BmpEncoder_GetMetadataQueryWriter
};

645
HRESULT BmpEncoder_CreateInstance(REFIID iid, void** ppv)
646 647 648 649
{
    BmpEncoder *This;
    HRESULT ret;

650
    TRACE("(%s,%p)\n", debugstr_guid(iid), ppv);
651 652 653 654 655 656

    *ppv = NULL;

    This = HeapAlloc(GetProcessHeap(), 0, sizeof(BmpEncoder));
    if (!This) return E_OUTOFMEMORY;

657
    This->IWICBitmapEncoder_iface.lpVtbl = &BmpEncoder_Vtbl;
658
    This->ref = 1;
659
    This->stream = NULL;
660
    This->frame = NULL;
661

662 663
    ret = IWICBitmapEncoder_QueryInterface(&This->IWICBitmapEncoder_iface, iid, ppv);
    IWICBitmapEncoder_Release(&This->IWICBitmapEncoder_iface);
664 665 666

    return ret;
}