]> git.cworth.org Git - apitrace/blob - thirdparty/directxtex/DirectXTex/DirectXTexWIC.cpp
thirdparty/directxtex: Import DirectXTex library.
[apitrace] / thirdparty / directxtex / DirectXTex / DirectXTexWIC.cpp
1 //-------------------------------------------------------------------------------------
2 // DirectXTexWIC.cpp
3 //  
4 // DirectX Texture Library - WIC-based file reader/writer
5 //
6 // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
7 // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
8 // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
9 // PARTICULAR PURPOSE.
10 //
11 // Copyright (c) Microsoft Corporation. All rights reserved.
12 //
13 // http://go.microsoft.com/fwlink/?LinkId=248926
14 //-------------------------------------------------------------------------------------
15
16 #include "directxtexp.h"
17
18 //-------------------------------------------------------------------------------------
19 // WIC Pixel Format nearest conversion table
20 //-------------------------------------------------------------------------------------
21
22 struct WICConvert
23 {
24     GUID        source;
25     GUID        target;
26 };
27
28 static WICConvert g_WICConvert[] = 
29 {
30     // Directly support the formats listed in XnaTexUtil::g_WICFormats, so no conversion required
31     // Note target GUID in this conversion table must be one of those directly supported formats.
32
33     { GUID_WICPixelFormat1bppIndexed,           GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
34     { GUID_WICPixelFormat2bppIndexed,           GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
35     { GUID_WICPixelFormat4bppIndexed,           GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
36     { GUID_WICPixelFormat8bppIndexed,           GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
37
38     { GUID_WICPixelFormat2bppGray,              GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM 
39     { GUID_WICPixelFormat4bppGray,              GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM 
40
41     { GUID_WICPixelFormat16bppGrayFixedPoint,   GUID_WICPixelFormat16bppGrayHalf }, // DXGI_FORMAT_R16_FLOAT 
42     { GUID_WICPixelFormat32bppGrayFixedPoint,   GUID_WICPixelFormat32bppGrayFloat }, // DXGI_FORMAT_R32_FLOAT 
43
44     { GUID_WICPixelFormat16bppBGR555,           GUID_WICPixelFormat16bppBGRA5551 }, // DXGI_FORMAT_B5G5R5A1_UNORM 
45     { GUID_WICPixelFormat32bppBGR101010,        GUID_WICPixelFormat32bppRGBA1010102 }, // DXGI_FORMAT_R10G10B10A2_UNORM
46
47     { GUID_WICPixelFormat24bppBGR,              GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
48     { GUID_WICPixelFormat24bppRGB,              GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
49     { GUID_WICPixelFormat32bppPBGRA,            GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
50     { GUID_WICPixelFormat32bppPRGBA,            GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
51
52     { GUID_WICPixelFormat48bppRGB,              GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
53     { GUID_WICPixelFormat48bppBGR,              GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
54     { GUID_WICPixelFormat64bppBGRA,             GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
55     { GUID_WICPixelFormat64bppPRGBA,            GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
56     { GUID_WICPixelFormat64bppPBGRA,            GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
57
58     { GUID_WICPixelFormat48bppRGBFixedPoint,    GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
59     { GUID_WICPixelFormat48bppBGRFixedPoint,    GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
60     { GUID_WICPixelFormat64bppRGBAFixedPoint,   GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
61     { GUID_WICPixelFormat64bppBGRAFixedPoint,   GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
62     { GUID_WICPixelFormat64bppRGBFixedPoint,    GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
63     { GUID_WICPixelFormat64bppRGBHalf,          GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
64     { GUID_WICPixelFormat48bppRGBHalf,          GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
65
66     { GUID_WICPixelFormat128bppPRGBAFloat,      GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT 
67     { GUID_WICPixelFormat128bppRGBFloat,        GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT 
68     { GUID_WICPixelFormat128bppRGBAFixedPoint,  GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT 
69     { GUID_WICPixelFormat128bppRGBFixedPoint,   GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT 
70
71     { GUID_WICPixelFormat32bppCMYK,             GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
72     { GUID_WICPixelFormat64bppCMYK,             GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
73     { GUID_WICPixelFormat40bppCMYKAlpha,        GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
74     { GUID_WICPixelFormat80bppCMYKAlpha,        GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
75
76 #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) || defined(_WIN7_PLATFORM_UPDATE)
77     { GUID_WICPixelFormat32bppRGB,              GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM
78     { GUID_WICPixelFormat64bppRGB,              GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
79     { GUID_WICPixelFormat64bppPRGBAHalf,        GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
80 #endif
81
82     // We don't support n-channel formats
83 };
84
85 namespace DirectX
86 {
87
88 //-------------------------------------------------------------------------------------
89 // Returns the DXGI format and optionally the WIC pixel GUID to convert to
90 //-------------------------------------------------------------------------------------
91 static DXGI_FORMAT _DetermineFormat( _In_ const WICPixelFormatGUID& pixelFormat, _In_ DWORD flags,
92                                      _Out_opt_ WICPixelFormatGUID* pConvert )
93 {
94     if ( pConvert )
95         memset( pConvert, 0, sizeof(WICPixelFormatGUID) );
96
97     DXGI_FORMAT format = _WICToDXGI( pixelFormat );
98
99     if ( format == DXGI_FORMAT_UNKNOWN )
100     {
101         if ( memcmp( &GUID_WICPixelFormat96bppRGBFixedPoint, &pixelFormat, sizeof(WICPixelFormatGUID) ) == 0 )
102         {
103 #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) || defined(_WIN7_PLATFORM_UPDATE)
104             if ( _IsWIC2() )
105             {
106                 if ( pConvert )
107                     memcpy( pConvert, &GUID_WICPixelFormat96bppRGBFloat, sizeof(WICPixelFormatGUID) );
108                 format = DXGI_FORMAT_R32G32B32_FLOAT;
109             }
110             else
111 #endif
112             {
113                 if ( pConvert )
114                     memcpy( pConvert, &GUID_WICPixelFormat128bppRGBAFloat, sizeof(WICPixelFormatGUID) );
115                 format = DXGI_FORMAT_R32G32B32A32_FLOAT;
116             }
117         }
118         else
119         {
120             for( size_t i=0; i < _countof(g_WICConvert); ++i )
121             {
122                 if ( memcmp( &g_WICConvert[i].source, &pixelFormat, sizeof(WICPixelFormatGUID) ) == 0 )
123                 {
124                     if ( pConvert )
125                         memcpy( pConvert, &g_WICConvert[i].target, sizeof(WICPixelFormatGUID) );
126
127                     format = _WICToDXGI( g_WICConvert[i].target );
128                     assert( format != DXGI_FORMAT_UNKNOWN );
129                     break;
130                 }
131             }
132         }
133     }
134
135     // Handle special cases based on flags
136     switch (format)
137     {
138     case DXGI_FORMAT_B8G8R8A8_UNORM:    // BGRA
139     case DXGI_FORMAT_B8G8R8X8_UNORM:    // BGRX
140         if ( flags & WIC_FLAGS_FORCE_RGB )
141         {
142             format = DXGI_FORMAT_R8G8B8A8_UNORM;
143             if ( pConvert )
144                 memcpy( pConvert, &GUID_WICPixelFormat32bppRGBA, sizeof(WICPixelFormatGUID) );
145         }
146         break;
147
148     case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM:
149         if ( flags & WIC_FLAGS_NO_X2_BIAS )
150         {
151             format = DXGI_FORMAT_R10G10B10A2_UNORM;
152             if ( pConvert )
153                 memcpy( pConvert, &GUID_WICPixelFormat32bppRGBA1010102, sizeof(WICPixelFormatGUID) );
154         }
155         break;
156
157     case DXGI_FORMAT_B5G5R5A1_UNORM:
158     case DXGI_FORMAT_B5G6R5_UNORM:
159         if ( flags & WIC_FLAGS_NO_16BPP ) 
160         {
161             format = DXGI_FORMAT_R8G8B8A8_UNORM;
162             if ( pConvert )
163                 memcpy( pConvert, &GUID_WICPixelFormat32bppRGBA, sizeof(WICPixelFormatGUID) );
164         }
165         break;
166
167     case DXGI_FORMAT_R1_UNORM:
168         if ( !(flags & WIC_FLAGS_ALLOW_MONO ) )
169         {
170             // By default we want to promote a black & white to gresycale since R1 is not a generally supported D3D format
171             format = DXGI_FORMAT_R8_UNORM;
172             if ( pConvert )
173                 memcpy( pConvert, &GUID_WICPixelFormat8bppGray, sizeof(WICPixelFormatGUID) );
174         }
175     }
176
177     return format;
178 }
179
180
181 //-------------------------------------------------------------------------------------
182 // Determines metadata for image
183 //-------------------------------------------------------------------------------------
184 static HRESULT _DecodeMetadata( _In_ DWORD flags,
185                                 _In_ IWICBitmapDecoder *decoder, _In_ IWICBitmapFrameDecode *frame,
186                                 _Out_ TexMetadata& metadata, _Out_opt_ WICPixelFormatGUID* pConvert )
187 {
188     if ( !decoder || !frame )
189         return E_POINTER;
190
191     memset( &metadata, 0, sizeof(TexMetadata) );
192     metadata.depth = 1;
193     metadata.mipLevels = 1;
194     metadata.dimension = TEX_DIMENSION_TEXTURE2D;
195
196     UINT w, h;
197     HRESULT hr = frame->GetSize( &w, &h );
198     if ( FAILED(hr) )
199         return hr;
200
201     metadata.width = w;
202     metadata.height = h;
203
204     if ( flags & WIC_FLAGS_ALL_FRAMES )
205     {
206         UINT fcount;
207         hr = decoder->GetFrameCount( &fcount );
208         if ( FAILED(hr) )
209             return hr;
210
211         metadata.arraySize = fcount;
212     }
213     else
214         metadata.arraySize = 1;
215
216     WICPixelFormatGUID pixelFormat;
217     hr = frame->GetPixelFormat( &pixelFormat );
218     if ( FAILED(hr) )
219         return hr;
220
221     metadata.format = _DetermineFormat( pixelFormat, flags, pConvert );
222     if ( metadata.format == DXGI_FORMAT_UNKNOWN )
223         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
224
225     return S_OK;
226 }
227
228
229 //-------------------------------------------------------------------------------------
230 // Decodes a single frame
231 //-------------------------------------------------------------------------------------
232 static HRESULT _DecodeSingleFrame( _In_ DWORD flags, _In_ const TexMetadata& metadata, _In_ const WICPixelFormatGUID& convertGUID,
233                                    _In_ IWICBitmapFrameDecode *frame, _Inout_ ScratchImage& image )
234 {
235     if ( !frame )
236         return E_POINTER;
237
238     HRESULT hr = image.Initialize2D( metadata.format, metadata.width, metadata.height, 1, 1 );
239     if ( FAILED(hr) )
240         return hr;
241
242     const Image *img = image.GetImage( 0, 0, 0 );
243     if ( !img )
244         return E_POINTER;
245
246     IWICImagingFactory* pWIC = _GetWIC();
247     if ( !pWIC )
248         return E_NOINTERFACE;
249
250     if ( memcmp( &convertGUID, &GUID_NULL, sizeof(GUID) ) == 0 )
251     {
252         hr = frame->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );  
253         if ( FAILED(hr) )
254             return hr;
255     }
256     else
257     {
258         ScopedObject<IWICFormatConverter> FC;
259         hr = pWIC->CreateFormatConverter( &FC );
260         if ( FAILED(hr) )
261             return hr;
262
263         hr = FC->Initialize( frame, convertGUID, _GetWICDither( flags ), 0, 0, WICBitmapPaletteTypeCustom );
264         if ( FAILED(hr) )
265             return hr;
266
267         hr = FC->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );  
268         if ( FAILED(hr) )
269             return hr;
270     }
271
272     return S_OK;
273 }
274
275
276 //-------------------------------------------------------------------------------------
277 // Decodes an image array, resizing/format converting as needed
278 //-------------------------------------------------------------------------------------
279 static HRESULT _DecodeMultiframe( _In_ DWORD flags, _In_ const TexMetadata& metadata,
280                                   _In_ IWICBitmapDecoder *decoder, _Inout_ ScratchImage& image )
281 {
282     if ( !decoder )
283         return E_POINTER;
284
285     HRESULT hr = image.Initialize2D( metadata.format, metadata.width, metadata.height, metadata.arraySize, 1 );
286     if ( FAILED(hr) )
287         return hr;
288
289     IWICImagingFactory* pWIC = _GetWIC();
290     if ( !pWIC )
291         return E_NOINTERFACE;
292
293     WICPixelFormatGUID sourceGUID;
294     if ( !_DXGIToWIC( metadata.format, sourceGUID ) )
295         return E_FAIL;
296
297     for( size_t index = 0; index < metadata.arraySize; ++index )
298     {
299         const Image* img = image.GetImage( 0, index, 0 );
300         if ( !img )
301             return E_POINTER;
302
303         ScopedObject<IWICBitmapFrameDecode> frame;
304         hr = decoder->GetFrame( static_cast<UINT>( index ), &frame );
305         if ( FAILED(hr) )
306             return hr;
307
308         WICPixelFormatGUID pfGuid;
309         hr = frame->GetPixelFormat( &pfGuid );
310         if ( FAILED(hr) )
311             return hr;
312
313         UINT w, h;
314         hr = frame->GetSize( &w, &h );
315         if ( FAILED(hr) )
316             return hr;
317
318         if ( memcmp( &pfGuid, &sourceGUID, sizeof(WICPixelFormatGUID) ) == 0 )
319         {
320             if ( w == metadata.width && h == metadata.height )
321             {
322                 // This frame does not need resized or format converted, just copy...
323                 hr = frame->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );  
324                 if ( FAILED(hr) )
325                     return hr;
326             }
327             else
328             {
329                 // This frame needs resizing, but not format converted
330                 ScopedObject<IWICBitmapScaler> scaler;
331                 hr = pWIC->CreateBitmapScaler( &scaler );
332                 if ( FAILED(hr) )
333                     return hr;
334
335                 hr = scaler->Initialize( frame.Get(), static_cast<UINT>( metadata.width ), static_cast<UINT>( metadata.height ), _GetWICInterp( flags ) );
336                 if ( FAILED(hr) )
337                     return hr;
338
339                 hr = scaler->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );
340                 if ( FAILED(hr) )
341                     return hr;
342             }
343         }
344         else
345         {
346             // This frame required format conversion
347             ScopedObject<IWICFormatConverter> FC;
348             hr = pWIC->CreateFormatConverter( &FC );
349             if ( FAILED(hr) )
350                 return hr;
351
352             hr = FC->Initialize( frame.Get(), pfGuid, _GetWICDither( flags ), 0, 0, WICBitmapPaletteTypeCustom );
353             if ( FAILED(hr) )
354                 return hr;
355             
356             if ( w == metadata.width && h == metadata.height )
357             {
358                 // This frame is the same size, no need to scale
359                 hr = FC->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );  
360                 if ( FAILED(hr) )
361                     return hr;
362             }
363             else
364             {
365                 // This frame needs resizing and format converted
366                 ScopedObject<IWICBitmapScaler> scaler;
367                 hr = pWIC->CreateBitmapScaler( &scaler );
368                 if ( FAILED(hr) )
369                     return hr;
370
371                 hr = scaler->Initialize( FC.Get(), static_cast<UINT>( metadata.width ), static_cast<UINT>( metadata.height ), _GetWICInterp( flags ) );
372                 if ( FAILED(hr) )
373                     return hr;
374
375                 hr = scaler->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );
376                 if ( FAILED(hr) )
377                     return hr;
378             }
379         }
380     }
381
382     return S_OK;
383 }
384
385
386 //-------------------------------------------------------------------------------------
387 // Encodes a single frame
388 //-------------------------------------------------------------------------------------
389 static HRESULT _EncodeImage( _In_ const Image& image, _In_ DWORD flags, _In_ IWICBitmapFrameEncode* frame, _In_opt_ IPropertyBag2* props, _In_opt_ const GUID* targetFormat )
390 {
391     if ( !frame )
392         return E_INVALIDARG;
393
394     if ( !image.pixels )
395         return E_POINTER;
396
397     WICPixelFormatGUID pfGuid;
398     if ( !_DXGIToWIC( image.format, pfGuid ) )
399         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
400
401     HRESULT hr = frame->Initialize( props );
402     if ( FAILED(hr) )
403         return hr;
404
405 #ifdef _AMD64_
406     if ( (image.width > 0xFFFFFFFF) || (image.height > 0xFFFFFFFF) )
407         return E_INVALIDARG;
408 #endif
409
410     hr = frame->SetSize( static_cast<UINT>( image.width ), static_cast<UINT>( image.height ) );
411     if ( FAILED(hr) )
412         return hr;
413
414     hr = frame->SetResolution( 72, 72 );
415     if ( FAILED(hr) )
416         return hr;
417
418     WICPixelFormatGUID targetGuid = (targetFormat) ? (*targetFormat) : pfGuid;
419     hr = frame->SetPixelFormat( &targetGuid );
420     if ( FAILED(hr) )
421         return hr;
422
423     if ( memcmp( &targetGuid, &pfGuid, sizeof(WICPixelFormatGUID) ) != 0 )
424     {
425         // Conversion required to write
426         IWICImagingFactory* pWIC = _GetWIC();
427         if ( !pWIC )
428             return E_NOINTERFACE;
429
430         ScopedObject<IWICBitmap> source;
431         hr = pWIC->CreateBitmapFromMemory( static_cast<UINT>( image.width ), static_cast<UINT>( image.height ), pfGuid,
432                                            static_cast<UINT>( image.rowPitch ), static_cast<UINT>( image.slicePitch ),
433                                            image.pixels, &source );
434         if ( FAILED(hr) )
435             return hr;
436
437         ScopedObject<IWICFormatConverter> FC;
438         hr = pWIC->CreateFormatConverter( &FC );
439         if ( FAILED(hr) )
440             return hr;
441
442         hr = FC->Initialize( source.Get(), targetGuid, _GetWICDither( flags ), 0, 0, WICBitmapPaletteTypeCustom );
443         if ( FAILED(hr) )
444             return hr;
445
446         WICRect rect = { 0, 0, static_cast<UINT>( image.width ), static_cast<UINT>( image.height ) };
447         hr = frame->WriteSource( FC.Get(), &rect );
448         if ( FAILED(hr) )
449             return hr;
450     }
451     else
452     {
453         // No conversion required
454         hr = frame->WritePixels( static_cast<UINT>( image.height ), static_cast<UINT>( image.rowPitch ), static_cast<UINT>( image.slicePitch ),
455                                  reinterpret_cast<uint8_t*>( image.pixels ) );
456         if ( FAILED(hr) )
457             return hr;
458     }
459
460     hr = frame->Commit();
461     if ( FAILED(hr) )
462         return hr;
463
464     return S_OK;
465 }
466
467 static HRESULT _EncodeSingleFrame( _In_ const Image& image, _In_ DWORD flags,
468                                    _In_ REFGUID guidContainerFormat, _Inout_ IStream* stream, _In_opt_ const GUID* targetFormat )
469 {
470     if ( !stream )
471         return E_INVALIDARG;
472
473     // Initialize WIC
474     IWICImagingFactory* pWIC = _GetWIC();
475     if ( !pWIC )
476         return E_NOINTERFACE;
477
478     ScopedObject<IWICBitmapEncoder> encoder;
479     HRESULT hr = pWIC->CreateEncoder( guidContainerFormat, 0, &encoder );
480     if ( FAILED(hr) )
481         return hr;
482
483     hr = encoder->Initialize( stream, WICBitmapEncoderNoCache );
484     if ( FAILED(hr) )
485         return hr;
486
487     ScopedObject<IWICBitmapFrameEncode> frame;
488     ScopedObject<IPropertyBag2> props;
489     hr = encoder->CreateNewFrame( &frame, &props );
490     if ( FAILED(hr) )
491         return hr;
492
493     if ( memcmp( &guidContainerFormat, &GUID_ContainerFormatBmp, sizeof(WICPixelFormatGUID) ) == 0  )
494     {
495         // Opt-in to the Windows 8 support for writing 32-bit Windows BMP files with an alpha channel if supported
496         PROPBAG2 option = { 0 };
497         option.pstrName = L"EnableV5Header32bppBGRA";
498
499         VARIANT varValue;    
500         varValue.vt = VT_BOOL;
501         varValue.boolVal = VARIANT_TRUE;      
502         hr = props->Write( 1, &option, &varValue ); 
503         if ( FAILED(hr) )
504         {
505             // Fails on older versions of WIC, so we default to the null property bag
506             props.Reset();
507         }
508     }
509
510     hr = _EncodeImage( image, flags, frame.Get(), props.Get(), targetFormat );
511     if ( FAILED(hr) )
512         return hr;
513
514     hr = encoder->Commit();
515     if ( FAILED(hr) )
516         return hr;
517
518     return S_OK;
519 }
520
521
522 //-------------------------------------------------------------------------------------
523 // Encodes an image array
524 //-------------------------------------------------------------------------------------
525 static HRESULT _EncodeMultiframe( _In_count_(nimages) const Image* images, _In_ size_t nimages, _In_ DWORD flags,
526                                   _In_ REFGUID guidContainerFormat, _Inout_ IStream* stream, _In_opt_ const GUID* targetFormat )
527 {
528     if ( !stream || nimages < 2 )
529         return E_INVALIDARG;
530
531     if ( !images )
532         return E_POINTER;
533
534     // Initialize WIC
535     IWICImagingFactory* pWIC = _GetWIC();
536     if ( !pWIC )
537         return E_NOINTERFACE;
538
539     ScopedObject<IWICBitmapEncoder> encoder;
540     HRESULT hr = pWIC->CreateEncoder( guidContainerFormat, 0, &encoder );
541     if ( FAILED(hr) )
542         return hr;
543
544     ScopedObject<IWICBitmapEncoderInfo> einfo;
545     hr = encoder->GetEncoderInfo( &einfo );
546     if ( FAILED(hr) )
547         return hr;
548
549     BOOL mframe = FALSE;
550     hr = einfo->DoesSupportMultiframe( &mframe );
551     if ( FAILED(hr) )
552         return hr;
553
554     if ( !mframe )
555         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
556
557     hr = encoder->Initialize( stream, WICBitmapEncoderNoCache );
558     if ( FAILED(hr) )
559         return hr;
560
561     for( size_t index=0; index < nimages; ++index )
562     {
563         ScopedObject<IWICBitmapFrameEncode> frame;
564         hr = encoder->CreateNewFrame( &frame, nullptr );
565         if ( FAILED(hr) )
566             return hr;
567
568         hr = _EncodeImage( images[index], flags, frame.Get(), nullptr, targetFormat );
569         if ( FAILED(hr) )
570             return hr;
571     }
572
573     hr = encoder->Commit();
574     if ( FAILED(hr) )
575         return hr;
576
577     return S_OK;
578 }
579
580
581 //=====================================================================================
582 // Entry-points
583 //=====================================================================================
584
585 //-------------------------------------------------------------------------------------
586 // Obtain metadata from WIC-supported file in memory
587 //-------------------------------------------------------------------------------------
588 HRESULT GetMetadataFromWICMemory( LPCVOID pSource, size_t size, DWORD flags, TexMetadata& metadata )
589 {
590     if ( !pSource || size == 0 )
591         return E_INVALIDARG;
592
593 #ifdef _AMD64_
594     if ( size > 0xFFFFFFFF )
595         return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE );
596 #endif
597
598     IWICImagingFactory* pWIC = _GetWIC();
599     if ( !pWIC )
600         return E_NOINTERFACE;
601
602     // Create input stream for memory
603     ScopedObject<IWICStream> stream;
604     HRESULT hr = pWIC->CreateStream( &stream );
605     if ( FAILED(hr) )
606         return hr;
607
608     hr = stream->InitializeFromMemory( reinterpret_cast<BYTE*>( const_cast<void*>( pSource ) ),
609                                        static_cast<UINT>( size ) );
610     if ( FAILED(hr) )
611         return hr;
612
613     // Initialize WIC
614     ScopedObject<IWICBitmapDecoder> decoder;
615     hr = pWIC->CreateDecoderFromStream( stream.Get(), 0, WICDecodeMetadataCacheOnDemand, &decoder );
616     if ( FAILED(hr) )
617         return hr;
618
619     ScopedObject<IWICBitmapFrameDecode> frame;
620     hr = decoder->GetFrame( 0, &frame );
621     if ( FAILED(hr) )
622         return hr;
623
624     // Get metadata
625     hr = _DecodeMetadata( flags, decoder.Get(), frame.Get(), metadata, 0 );
626     if ( FAILED(hr) )
627         return hr;
628
629     return S_OK;
630 }
631
632
633 //-------------------------------------------------------------------------------------
634 // Obtain metadata from WIC-supported file on disk
635 //-------------------------------------------------------------------------------------
636 HRESULT GetMetadataFromWICFile( LPCWSTR szFile, DWORD flags, TexMetadata& metadata )
637 {
638     if ( !szFile )
639         return E_INVALIDARG;
640
641     IWICImagingFactory* pWIC = _GetWIC();
642     if ( !pWIC )
643         return E_NOINTERFACE;
644     
645     // Initialize WIC
646     ScopedObject<IWICBitmapDecoder> decoder;
647     HRESULT hr = pWIC->CreateDecoderFromFilename( szFile, 0, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &decoder );
648     if ( FAILED(hr) )
649         return hr;
650
651     ScopedObject<IWICBitmapFrameDecode> frame;
652     hr = decoder->GetFrame( 0, &frame );
653     if ( FAILED(hr) )
654         return hr;
655
656     // Get metadata
657     hr = _DecodeMetadata( flags, decoder.Get(), frame.Get(), metadata, 0 );
658     if ( FAILED(hr) )
659         return hr;
660
661     return S_OK;
662 }
663
664
665 //-------------------------------------------------------------------------------------
666 // Load a WIC-supported file in memory
667 //-------------------------------------------------------------------------------------
668 HRESULT LoadFromWICMemory( LPCVOID pSource, size_t size, DWORD flags, TexMetadata* metadata, ScratchImage& image )
669 {
670     if ( !pSource || size == 0 )
671         return E_INVALIDARG;
672
673 #ifdef _AMD64_
674     if ( size > 0xFFFFFFFF )
675         return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE );
676 #endif
677
678     IWICImagingFactory* pWIC = _GetWIC();
679     if ( !pWIC )
680         return E_NOINTERFACE;
681
682     image.Release();
683
684     // Create input stream for memory
685     ScopedObject<IWICStream> stream;
686     HRESULT hr = pWIC->CreateStream( &stream );
687     if ( FAILED(hr) )
688         return hr;
689
690     hr = stream->InitializeFromMemory( reinterpret_cast<uint8_t*>( const_cast<void*>( pSource ) ), static_cast<DWORD>( size ) );
691     if ( FAILED(hr) )
692         return hr;
693
694     // Initialize WIC
695     ScopedObject<IWICBitmapDecoder> decoder;
696     hr = pWIC->CreateDecoderFromStream( stream.Get(), 0, WICDecodeMetadataCacheOnDemand, &decoder );
697     if ( FAILED(hr) )
698         return hr;
699
700     ScopedObject<IWICBitmapFrameDecode> frame;
701     hr = decoder->GetFrame( 0, &frame );
702     if ( FAILED(hr) )
703         return hr;
704
705     // Get metadata
706     TexMetadata mdata;
707     WICPixelFormatGUID convertGUID = {0};
708     hr = _DecodeMetadata( flags, decoder.Get(), frame.Get(), mdata, &convertGUID );
709     if ( FAILED(hr) )
710         return hr;
711
712     if ( (mdata.arraySize > 1) && (flags & WIC_FLAGS_ALL_FRAMES) )
713     {
714         hr = _DecodeMultiframe( flags, mdata, decoder.Get(), image );
715     }
716     else
717     {
718         hr = _DecodeSingleFrame( flags, mdata, convertGUID, frame.Get(), image );
719     }
720
721     if ( FAILED(hr) )
722     {
723         image.Release();
724         return hr;
725     }
726
727     if ( metadata )
728         memcpy( metadata, &mdata, sizeof(TexMetadata) );
729
730     return S_OK;
731 }
732
733
734 //-------------------------------------------------------------------------------------
735 // Load a WIC-supported file from disk
736 //-------------------------------------------------------------------------------------
737 HRESULT LoadFromWICFile( LPCWSTR szFile, DWORD flags, TexMetadata* metadata, ScratchImage& image )
738 {
739     if ( !szFile )
740         return E_INVALIDARG;
741
742     IWICImagingFactory* pWIC = _GetWIC();
743     if ( !pWIC )
744         return E_NOINTERFACE;
745     
746     image.Release();
747
748     // Initialize WIC
749     ScopedObject<IWICBitmapDecoder> decoder;
750     HRESULT hr = pWIC->CreateDecoderFromFilename( szFile, 0, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &decoder );
751     if ( FAILED(hr) )
752         return hr;
753
754     ScopedObject<IWICBitmapFrameDecode> frame;
755     hr = decoder->GetFrame( 0, &frame );
756     if ( FAILED(hr) )
757         return hr;
758
759     // Get metadata
760     TexMetadata mdata;
761     WICPixelFormatGUID convertGUID = {0};
762     hr = _DecodeMetadata( flags, decoder.Get(), frame.Get(), mdata, &convertGUID );
763     if ( FAILED(hr) )
764         return hr;
765
766     if ( (mdata.arraySize > 1) && (flags & WIC_FLAGS_ALL_FRAMES) )
767     {
768         hr = _DecodeMultiframe( flags, mdata, decoder.Get(), image );
769     }
770     else
771     {
772         hr = _DecodeSingleFrame( flags, mdata, convertGUID, frame.Get(), image );
773     }
774
775     if ( FAILED(hr) )
776     {
777         image.Release();
778         return hr;
779     }
780
781     if ( metadata )
782         memcpy( metadata, &mdata, sizeof(TexMetadata) );
783
784     return S_OK;
785 }
786
787
788 //-------------------------------------------------------------------------------------
789 // Save a WIC-supported file to memory
790 //-------------------------------------------------------------------------------------
791 HRESULT SaveToWICMemory( const Image& image, DWORD flags, REFGUID guidContainerFormat, Blob& blob, const GUID* targetFormat )
792 {
793     if ( !image.pixels )
794         return E_POINTER;
795
796     blob.Release();
797
798     ScopedObject<IStream> stream;
799     HRESULT hr = CreateStreamOnHGlobal( 0, TRUE, &stream );
800     if ( FAILED(hr) )
801         return hr;
802
803     hr = _EncodeSingleFrame( image, flags, guidContainerFormat, stream.Get(), targetFormat );
804     if ( FAILED(hr) )
805         return hr;
806
807     // Copy stream data into blob
808     STATSTG stat;
809     hr = stream->Stat( &stat, STATFLAG_NONAME );
810     if ( FAILED(hr) )
811         return hr;
812
813     if ( stat.cbSize.HighPart > 0 )
814         return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE );
815
816     hr = blob.Initialize( stat.cbSize.LowPart );
817     if ( FAILED(hr) )
818         return hr;
819
820     LARGE_INTEGER li = { 0 };
821     hr = stream->Seek( li, STREAM_SEEK_SET, 0 );
822     if ( FAILED(hr) )
823         return hr;
824
825     DWORD bytesRead;
826     hr = stream->Read( blob.GetBufferPointer(), static_cast<ULONG>( blob.GetBufferSize() ), &bytesRead );
827     if ( FAILED(hr) )
828         return hr;
829
830     if ( bytesRead != blob.GetBufferSize() )
831         return E_FAIL;
832
833     return S_OK;
834 }
835
836 HRESULT SaveToWICMemory( const Image* images, size_t nimages, DWORD flags, REFGUID guidContainerFormat, Blob& blob, const GUID* targetFormat )
837 {
838     if ( !images || nimages == 0 )
839         return E_INVALIDARG;
840
841     blob.Release();
842
843     ScopedObject<IStream> stream;
844     HRESULT hr = CreateStreamOnHGlobal( 0, TRUE, &stream );
845     if ( FAILED(hr) )
846         return hr;
847
848     if ( nimages > 1 )
849         hr = _EncodeMultiframe( images, nimages, flags, guidContainerFormat, stream.Get(), targetFormat );
850     else
851         hr = _EncodeSingleFrame( images[0], flags, guidContainerFormat, stream.Get(), targetFormat );
852
853     if ( FAILED(hr) )
854         return hr;
855
856     // Copy stream data into blob
857     STATSTG stat;
858     hr = stream->Stat( &stat, STATFLAG_NONAME );
859     if ( FAILED(hr) )
860         return hr;
861
862     if ( stat.cbSize.HighPart > 0 )
863         return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE );
864
865     hr = blob.Initialize( stat.cbSize.LowPart );
866     if ( FAILED(hr) )
867         return hr;
868
869     LARGE_INTEGER li = { 0 };
870     hr = stream->Seek( li, STREAM_SEEK_SET, 0 );
871     if ( FAILED(hr) )
872         return hr;
873
874     DWORD bytesRead;
875     hr = stream->Read( blob.GetBufferPointer(), static_cast<ULONG>( blob.GetBufferSize() ), &bytesRead );
876     if ( FAILED(hr) )
877         return hr;
878
879     if ( bytesRead != blob.GetBufferSize() )
880         return E_FAIL;
881
882     return S_OK;
883 }
884
885
886 //-------------------------------------------------------------------------------------
887 // Save a WIC-supported file to disk
888 //-------------------------------------------------------------------------------------
889 HRESULT SaveToWICFile( const Image& image, DWORD flags, REFGUID guidContainerFormat, LPCWSTR szFile, const GUID* targetFormat )
890 {
891     if ( !szFile )
892         return E_INVALIDARG;
893
894     if ( !image.pixels )
895         return E_POINTER;
896
897     IWICImagingFactory* pWIC = _GetWIC();
898     if ( !pWIC )
899         return E_NOINTERFACE;
900
901     ScopedObject<IWICStream> stream;
902     HRESULT hr = pWIC->CreateStream( &stream );
903     if ( FAILED(hr) )
904         return hr;
905
906     hr = stream->InitializeFromFilename( szFile, GENERIC_WRITE );
907     if ( FAILED(hr) )
908         return hr;
909
910     hr = _EncodeSingleFrame( image, flags, guidContainerFormat, stream.Get(), targetFormat );
911     if ( FAILED(hr) )
912         return hr;
913
914     return S_OK;
915 }
916
917 HRESULT SaveToWICFile( const Image* images, size_t nimages, DWORD flags, REFGUID guidContainerFormat, LPCWSTR szFile, const GUID* targetFormat )
918 {
919     if ( !szFile || !images || nimages == 0 )
920         return E_INVALIDARG;
921
922     IWICImagingFactory* pWIC = _GetWIC();
923     if ( !pWIC )
924         return E_NOINTERFACE;
925
926     ScopedObject<IWICStream> stream;
927     HRESULT hr = pWIC->CreateStream( &stream );
928     if ( FAILED(hr) )
929         return hr;
930
931     hr = stream->InitializeFromFilename( szFile, GENERIC_WRITE );
932     if ( FAILED(hr) )
933         return hr;
934
935     if ( nimages > 1 )
936         hr = _EncodeMultiframe( images, nimages, flags, guidContainerFormat, stream.Get(), targetFormat );
937     else
938         hr = _EncodeSingleFrame( images[0], flags, guidContainerFormat, stream.Get(), targetFormat );
939
940     if ( FAILED(hr) )
941         return hr;
942
943     return S_OK;
944 }
945
946 }; // namespace