]> git.cworth.org Git - apitrace/blob - thirdparty/directxtex/DirectXTex/DirectXTexTGA.cpp
thirdparty/directxtex: Import DirectXTex library.
[apitrace] / thirdparty / directxtex / DirectXTex / DirectXTexTGA.cpp
1 //-------------------------------------------------------------------------------------
2 // DirectXTexTGA.cpp
3 //  
4 // DirectX Texture Library - Targa Truevision (TGA) file format 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 // The implementation here has the following limitations:
20 //      * Does not support files that contain color maps (these are rare in practice)
21 //      * Interleaved files are not supported (deprecated aspect of TGA format)
22 //      * Only supports 8-bit greyscale; 16-, 24-, and 32-bit truecolor images
23 //      * Always writes uncompressed files (i.e. can read RLE compression, but does not write it)
24 //
25
26 enum TGAImageType
27 {
28     TGA_NO_IMAGE            = 0,
29     TGA_COLOR_MAPPED        = 1,
30     TGA_TRUECOLOR           = 2,
31     TGA_BLACK_AND_WHITE     = 3,
32     TGA_COLOR_MAPPED_RLE    = 9,
33     TGA_TRUECOLOR_RLE       = 10,
34     TGA_BLACK_AND_WHITE_RLE = 11,
35 };
36
37 enum TGADescriptorFlags
38 {
39     TGA_FLAGS_INVERTX           = 0x10,
40     TGA_FLAGS_INVERTY           = 0x20,
41     TGA_FLAGS_INTERLEAVED_2WAY  = 0x40, // Deprecated
42     TGA_FLAGS_INTERLEAVED_4WAY  = 0x80, // Deprecated
43 };
44
45 const char* g_TGA20_Signature = "TRUEVISION-XFILE.";
46
47 #pragma pack(push,1)
48 struct TGA_HEADER
49 {
50     uint8_t     bIDLength;
51     uint8_t     bColorMapType;
52     uint8_t     bImageType;
53     uint16_t    wColorMapFirst;
54     uint16_t    wColorMapLength;
55     uint8_t     bColorMapSize;
56     uint16_t    wXOrigin;
57     uint16_t    wYOrigin;
58     uint16_t    wWidth;
59     uint16_t    wHeight;
60     uint8_t     bBitsPerPixel;
61     uint8_t     bDescriptor;
62 };
63
64 struct TGA_FOOTER
65 {
66     uint16_t    dwExtensionOffset;
67     uint16_t    dwDeveloperOffset;
68     char        Signature[18];
69 };
70
71 struct TGA_EXTENSION
72 {
73     uint16_t    wSize;
74     char        szAuthorName[41];
75     char        szAuthorComment[324];
76     uint16_t    wStampMonth;
77     uint16_t    wStampDay;
78     uint16_t    wStampYear;
79     uint16_t    wStampHour;
80     uint16_t    wStampMinute;
81     uint16_t    wStampSecond;
82     char        szJobName[41];
83     uint16_t    wJobHour;
84     uint16_t    wJobMinute;
85     uint16_t    wJobSecond;
86     char        szSoftwareId[41];
87     uint16_t    wVersionNumber;
88     uint8_t     bVersionLetter;
89     uint32_t    dwKeyColor;
90     uint16_t    wPixelNumerator;
91     uint16_t    wPixelDenominator;
92     uint16_t    wGammaNumerator;
93     uint16_t    wGammaDenominator;
94     uint32_t    dwColorOffset;
95     uint32_t    dwStampOffset;
96     uint32_t    dwScanOffset;
97     uint8_t     bAttributesType;
98 };
99 #pragma pack(pop)
100
101 enum CONVERSION_FLAGS
102 {
103     CONV_FLAGS_NONE     = 0x0,
104     CONV_FLAGS_EXPAND   = 0x1,      // Conversion requires expanded pixel size
105     CONV_FLAGS_INVERTX  = 0x2,      // If set, scanlines are right-to-left
106     CONV_FLAGS_INVERTY  = 0x4,      // If set, scanlines are top-to-bottom
107     CONV_FLAGS_RLE      = 0x8,      // Source data is RLE compressed
108
109     CONV_FLAGS_SWIZZLE  = 0x10000,  // Swizzle BGR<->RGB data
110     CONV_FLAGS_888      = 0x20000,  // 24bpp format
111 };
112
113 namespace DirectX
114 {
115
116 //-------------------------------------------------------------------------------------
117 // Decodes TGA header
118 //-------------------------------------------------------------------------------------
119 static HRESULT _DecodeTGAHeader( _In_bytecount_(size) LPCVOID pSource, size_t size, _Out_ TexMetadata& metadata, size_t& offset,
120                                  _Inout_opt_ DWORD* convFlags )
121 {
122     if ( !pSource )
123         return E_INVALIDARG;
124
125     memset( &metadata, 0, sizeof(TexMetadata) );
126
127     if ( size < sizeof(TGA_HEADER) )
128     {
129         return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
130     }
131
132     const TGA_HEADER* pHeader = reinterpret_cast<const TGA_HEADER*>( pSource );
133     assert( pHeader );
134
135     if ( pHeader->bColorMapType != 0
136          || pHeader->wColorMapLength != 0 )
137     {
138         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
139     }
140
141     if ( pHeader->bDescriptor & (TGA_FLAGS_INTERLEAVED_2WAY|TGA_FLAGS_INTERLEAVED_4WAY) )
142     {
143         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
144     }
145
146     if ( !pHeader->wWidth || !pHeader->wHeight )
147     {
148         return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
149     }
150          
151     switch ( pHeader->bImageType )
152     {
153     case TGA_TRUECOLOR:
154     case TGA_TRUECOLOR_RLE:
155         switch( pHeader->bBitsPerPixel )
156         {
157         case 16:
158             metadata.format = DXGI_FORMAT_B5G5R5A1_UNORM;
159             break;
160
161         case 24:
162             metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM;
163             if ( convFlags )
164                 *convFlags |= CONV_FLAGS_EXPAND;
165             // We could use DXGI_FORMAT_B8G8R8X8_UNORM, but we prefer DXGI 1.0 formats
166             break;
167
168         case 32:
169             metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM;
170             // We could use DXGI_FORMAT_B8G8R8A8_UNORM, but we prefer DXGI 1.0 formats
171             break;
172         }
173
174         if ( convFlags && (pHeader->bImageType == TGA_TRUECOLOR_RLE) )
175         {
176             *convFlags |= CONV_FLAGS_RLE;
177         }
178         break;
179
180     case TGA_BLACK_AND_WHITE:
181     case TGA_BLACK_AND_WHITE_RLE:
182         switch( pHeader->bBitsPerPixel )
183         {
184         case 8:
185             metadata.format = DXGI_FORMAT_R8_UNORM;
186             break;
187
188         default:
189             return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
190         }
191
192         if ( convFlags && (pHeader->bImageType == TGA_BLACK_AND_WHITE_RLE) )
193         {
194             *convFlags |= CONV_FLAGS_RLE;
195         }
196         break;
197
198     case TGA_NO_IMAGE:
199     case TGA_COLOR_MAPPED:
200     case TGA_COLOR_MAPPED_RLE:
201         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
202
203     default:
204         return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
205     }
206
207     metadata.width = pHeader->wWidth;
208     metadata.height = pHeader->wHeight;
209     metadata.depth = metadata.arraySize = metadata.mipLevels = 1;
210     metadata.dimension = TEX_DIMENSION_TEXTURE2D;
211
212     if ( convFlags )
213     {
214         if ( pHeader->bDescriptor & TGA_FLAGS_INVERTX )
215             *convFlags |= CONV_FLAGS_INVERTX;
216
217         if ( pHeader->bDescriptor & TGA_FLAGS_INVERTY )
218             *convFlags |= CONV_FLAGS_INVERTY;
219     }
220
221     offset = sizeof( TGA_HEADER );
222
223     if ( pHeader->bIDLength != 0 )
224     {
225         offset += pHeader->bIDLength;
226     }
227
228     return S_OK;
229 }
230
231
232 //-------------------------------------------------------------------------------------
233 // Set alpha for images with all 0 alpha channel
234 //-------------------------------------------------------------------------------------
235 static HRESULT _SetAlphaChannelToOpaque( _In_ const Image* image )
236 {
237     assert( image );
238
239     uint8_t* pPixels = reinterpret_cast<uint8_t*>( image->pixels );
240     if ( !pPixels )
241         return E_POINTER;
242
243     for( size_t y = 0; y < image->height; ++y )
244     {
245         _CopyScanline( pPixels, image->rowPitch, pPixels, image->rowPitch, image->format, TEXP_SCANLINE_SETALPHA );
246         pPixels += image->rowPitch;
247     }
248
249     return S_OK;
250 }
251
252
253 //-------------------------------------------------------------------------------------
254 // Uncompress pixel data from a TGA into the target image
255 //-------------------------------------------------------------------------------------
256 static HRESULT _UncompressPixels( _In_bytecount_(size) LPCVOID pSource, size_t size, _In_ const Image* image, DWORD convFlags )
257 {
258     assert( pSource && size > 0 );
259
260     if ( !image || !image->pixels )
261         return E_POINTER;
262
263     // Compute TGA image data pitch
264     size_t rowPitch;
265     if ( convFlags & CONV_FLAGS_EXPAND )
266     {
267         rowPitch = image->width * 3;
268     }
269     else
270     {
271         size_t slicePitch;
272         ComputePitch( image->format, image->width, image->height, rowPitch, slicePitch, CP_FLAGS_NONE );
273     }
274
275     const uint8_t* sPtr = reinterpret_cast<const uint8_t*>( pSource );
276     const uint8_t* endPtr = sPtr + size;
277
278     switch( image->format )
279     {
280     //--------------------------------------------------------------------------- 8-bit
281     case DXGI_FORMAT_R8_UNORM:
282         for( size_t y=0; y < image->height; ++y )
283         {
284             size_t offset = ( (convFlags & CONV_FLAGS_INVERTX ) ? (image->width - 1) : 0 );
285             assert( offset < rowPitch);
286
287             uint8_t* dPtr = reinterpret_cast<uint8_t*>( image->pixels )
288                          + ( image->rowPitch * ( (convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1) ) )
289                          + offset;
290
291             for( size_t x=0; x < image->width; )
292             {
293                 if ( sPtr >= endPtr )
294                     return E_FAIL;
295
296                 if ( *sPtr & 0x80 )
297                 {
298                     // Repeat
299                     size_t j = (*sPtr & 0x7F) + 1;
300                     if ( ++sPtr >= endPtr )
301                         return E_FAIL;
302
303                     for( ; j > 0; --j, ++x )
304                     {
305                         if ( x >= image->width )
306                             return E_FAIL;
307
308                         *dPtr = *sPtr;
309
310                         if ( convFlags & CONV_FLAGS_INVERTX )
311                             --dPtr;
312                         else
313                             ++dPtr;
314                     }
315
316                     ++sPtr;
317                 }
318                 else
319                 {
320                     // Literal
321                     size_t j = (*sPtr & 0x7F) + 1;
322                     ++sPtr;
323
324                     if ( sPtr+j > endPtr )
325                         return E_FAIL;
326
327                     for( ; j > 0; --j, ++x )
328                     {
329                         if ( x >= image->width )
330                             return E_FAIL;
331
332                         *dPtr = *(sPtr++);
333
334                         if ( convFlags & CONV_FLAGS_INVERTX )
335                             --dPtr;
336                         else
337                             ++dPtr;
338                     }
339                 }
340             }
341         }
342         break;
343
344     //-------------------------------------------------------------------------- 16-bit
345     case DXGI_FORMAT_B5G5R5A1_UNORM:
346         {
347             bool nonzeroa = false;
348             for( size_t y=0; y < image->height; ++y )
349             {
350                 size_t offset = ( (convFlags & CONV_FLAGS_INVERTX ) ? (image->width - 1) : 0 );
351                 assert( offset*2 < rowPitch);
352
353                 uint16_t* dPtr = reinterpret_cast<uint16_t*>( reinterpret_cast<uint8_t*>( image->pixels )
354                              + ( image->rowPitch * ( (convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1) ) ) )
355                              + offset;
356
357                 for( size_t x=0; x < image->width; )
358                 {
359                     if ( sPtr >= endPtr )
360                         return E_FAIL;
361
362                     if ( *sPtr & 0x80 )
363                     {
364                         // Repeat
365                         size_t j = (*sPtr & 0x7F) + 1;
366                         ++sPtr;
367
368                         if ( sPtr+1 >= endPtr )
369                             return E_FAIL;
370
371                         uint16_t t =  *sPtr | (*(sPtr+1) << 8);
372                         if ( t & 0x8000 )
373                             nonzeroa = true;
374                         sPtr += 2;
375
376                         for( ; j > 0; --j, ++x )
377                         {
378                             if ( x >= image->width )
379                                 return E_FAIL;
380
381                             *dPtr = t;
382
383                             if ( convFlags & CONV_FLAGS_INVERTX )
384                                 --dPtr;
385                             else
386                                 ++dPtr;
387                         }
388                     }
389                     else
390                     {
391                         // Literal
392                         size_t j = (*sPtr & 0x7F) + 1;
393                         ++sPtr;
394
395                         if ( sPtr+(j*2) > endPtr )
396                             return E_FAIL;
397
398                         for( ; j > 0; --j, ++x )
399                         {
400                             if ( x >= image->width )
401                                 return E_FAIL;
402
403                             uint16_t t =  *sPtr | (*(sPtr+1) << 8);
404                             if ( t & 0x8000 )
405                                 nonzeroa = true;
406                             sPtr += 2;
407                             *dPtr = t;
408
409                             if ( convFlags & CONV_FLAGS_INVERTX )
410                                 --dPtr;
411                             else
412                                 ++dPtr;
413                         }
414                     }
415                 }
416             }
417
418             // If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
419             if ( !nonzeroa )
420             {
421                 HRESULT hr = _SetAlphaChannelToOpaque( image );
422                 if ( FAILED(hr) )
423                     return hr;
424             }
425         }
426         break;
427
428     //----------------------------------------------------------------------- 24/32-bit
429     case DXGI_FORMAT_R8G8B8A8_UNORM:
430         {
431             bool nonzeroa = false;
432             for( size_t y=0; y < image->height; ++y )
433             {
434                 size_t offset = ( (convFlags & CONV_FLAGS_INVERTX ) ? (image->width - 1) : 0 );
435
436                 uint32_t* dPtr = reinterpret_cast<uint32_t*>( reinterpret_cast<uint8_t*>( image->pixels )
437                               + ( image->rowPitch * ( (convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1) ) ) )
438                               + offset;
439
440                 for( size_t x=0; x < image->width; )
441                 {
442                     if ( sPtr >= endPtr )
443                         return E_FAIL;
444
445                     if ( *sPtr & 0x80 )
446                     {
447                         // Repeat
448                         size_t j = (*sPtr & 0x7F) + 1;
449                         ++sPtr;
450
451                         DWORD t;
452                         if ( convFlags & CONV_FLAGS_EXPAND )
453                         {
454                             assert( offset*3 < rowPitch);
455
456                             if ( sPtr+2 >= endPtr )
457                                 return E_FAIL;
458
459                             // BGR -> RGBA
460                             t = ( *sPtr << 16 ) | ( *(sPtr+1) << 8 ) | ( *(sPtr+2) ) | 0xFF000000;
461                             sPtr += 3;
462
463                             nonzeroa = true;
464                         }
465                         else
466                         {
467                             assert( offset*4 < rowPitch);
468
469                             if ( sPtr+3 >= endPtr )
470                                 return E_FAIL;
471
472                             // BGRA -> RGBA
473                             t = ( *sPtr << 16 ) | ( *(sPtr+1) << 8 ) | ( *(sPtr+2) ) | ( *(sPtr+3) << 24 );
474
475                             if ( *(sPtr+3) > 0 )
476                                 nonzeroa = true;
477
478                             sPtr += 4;
479                         }
480
481                         for( ; j > 0; --j, ++x )
482                         {
483                             if ( x >= image->width )
484                                 return E_FAIL;
485
486                             *dPtr = t;
487
488                             if ( convFlags & CONV_FLAGS_INVERTX )
489                                 --dPtr;
490                             else
491                                 ++dPtr;
492                         }
493                     }
494                     else
495                     {
496                         // Literal
497                         size_t j = (*sPtr & 0x7F) + 1;
498                         ++sPtr;
499
500                         if ( convFlags & CONV_FLAGS_EXPAND )
501                         {
502                             if ( sPtr+(j*3) > endPtr )
503                                 return E_FAIL;
504                         }
505                         else
506                         {
507                             if ( sPtr+(j*4) > endPtr )
508                                 return E_FAIL;
509                         }
510
511                         for( ; j > 0; --j, ++x )
512                         {
513                             if ( x >= image->width )
514                                 return E_FAIL;
515
516                             if ( convFlags & CONV_FLAGS_EXPAND )
517                             {
518                                 assert( offset*3 < rowPitch);
519
520                                 if ( sPtr+2 >= endPtr )
521                                     return E_FAIL;
522
523                                 // BGR -> RGBA
524                                 *dPtr = ( *sPtr << 16 ) | ( *(sPtr+1) << 8 ) | ( *(sPtr+2) ) | 0xFF000000;
525                                 sPtr += 3;
526
527                                 nonzeroa = true;
528                             }
529                             else
530                             {
531                                 assert( offset*4 < rowPitch);
532
533                                 if ( sPtr+3 >= endPtr )
534                                     return E_FAIL;
535
536                                 // BGRA -> RGBA
537                                 *dPtr = ( *sPtr << 16 ) | ( *(sPtr+1) << 8 ) | ( *(sPtr+2) ) | ( *(sPtr+3) << 24 );
538
539                                 if ( *(sPtr+3) > 0 )
540                                     nonzeroa = true;
541
542                                 sPtr += 4;
543                             }
544
545                             if ( convFlags & CONV_FLAGS_INVERTX )
546                                 --dPtr;
547                             else
548                                 ++dPtr;
549                         }
550                     }
551                 }
552             }
553
554             // If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
555             if ( !nonzeroa )
556             {
557                 HRESULT hr = _SetAlphaChannelToOpaque( image );
558                 if ( FAILED(hr) )
559                     return hr;
560             }
561         }
562         break;
563
564     //---------------------------------------------------------------------------------
565     default:
566         return E_FAIL;
567     }
568
569     return S_OK;
570 }
571
572
573 //-------------------------------------------------------------------------------------
574 // Copies pixel data from a TGA into the target image
575 //-------------------------------------------------------------------------------------
576 static HRESULT _CopyPixels( _In_bytecount_(size) LPCVOID pSource, size_t size, _In_ const Image* image, DWORD convFlags )
577 {
578     assert( pSource && size > 0 );
579
580     if ( !image || !image->pixels )
581         return E_POINTER;
582
583     // Compute TGA image data pitch
584     size_t rowPitch;
585     if ( convFlags & CONV_FLAGS_EXPAND )
586     {
587         rowPitch = image->width * 3;
588     }
589     else
590     {
591         size_t slicePitch;
592         ComputePitch( image->format, image->width, image->height, rowPitch, slicePitch, CP_FLAGS_NONE );
593     }
594
595     const uint8_t* sPtr = reinterpret_cast<const uint8_t*>( pSource );
596     const uint8_t* endPtr = sPtr + size;
597
598     switch( image->format )
599     {
600     //--------------------------------------------------------------------------- 8-bit
601     case DXGI_FORMAT_R8_UNORM:
602         for( size_t y=0; y < image->height; ++y )
603         {
604             size_t offset = ( (convFlags & CONV_FLAGS_INVERTX ) ? (image->width - 1) : 0 );
605             assert( offset < rowPitch);
606
607             uint8_t* dPtr = reinterpret_cast<uint8_t*>( image->pixels )
608                          + ( image->rowPitch * ( (convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1) ) )
609                          + offset;
610
611             for( size_t x=0; x < image->width; ++x )
612             {
613                 if ( sPtr >= endPtr )
614                     return E_FAIL;
615
616                 *dPtr = *(sPtr++);
617
618                 if ( convFlags & CONV_FLAGS_INVERTX )
619                     --dPtr;
620                 else
621                     ++dPtr;
622             }
623         }
624         break;
625
626     //-------------------------------------------------------------------------- 16-bit
627     case DXGI_FORMAT_B5G5R5A1_UNORM:
628         {
629             bool nonzeroa = false;
630             for( size_t y=0; y < image->height; ++y )
631             {
632                 size_t offset = ( (convFlags & CONV_FLAGS_INVERTX ) ? (image->width - 1) : 0 );
633                 assert( offset*2 < rowPitch);
634
635                 uint16_t* dPtr = reinterpret_cast<uint16_t*>( reinterpret_cast<uint8_t*>( image->pixels )
636                              + ( image->rowPitch * ( (convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1) ) ) )
637                              + offset;
638
639                 for( size_t x=0; x < image->width; ++x )
640                 {
641                     if ( sPtr+1 >= endPtr )
642                         return E_FAIL;
643
644                     uint16_t t =  *sPtr | (*(sPtr+1) << 8);
645                     sPtr += 2;
646                     *dPtr = t;
647
648                     if ( t & 0x8000 )
649                         nonzeroa = true;
650
651                     if ( convFlags & CONV_FLAGS_INVERTX )
652                         --dPtr;
653                     else
654                         ++dPtr;
655                 }
656             }
657
658             // If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
659             if ( !nonzeroa )
660             {
661                 HRESULT hr = _SetAlphaChannelToOpaque( image );
662                 if ( FAILED(hr) )
663                     return hr;
664             }
665         }
666         break;
667
668     //----------------------------------------------------------------------- 24/32-bit
669     case DXGI_FORMAT_R8G8B8A8_UNORM:
670         {
671             bool nonzeroa = false;
672             for( size_t y=0; y < image->height; ++y )
673             {
674                 size_t offset = ( (convFlags & CONV_FLAGS_INVERTX ) ? (image->width - 1) : 0 );
675
676                 uint32_t* dPtr = reinterpret_cast<uint32_t*>( reinterpret_cast<uint8_t*>( image->pixels )
677                               + ( image->rowPitch * ( (convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1) ) ) )
678                               + offset;
679
680                 for( size_t x=0; x < image->width; ++x )
681                 {
682                     if ( convFlags & CONV_FLAGS_EXPAND )
683                     {
684                         assert( offset*3 < rowPitch);
685
686                         if ( sPtr+2 >= endPtr )
687                             return E_FAIL;
688
689                         // BGR -> RGBA
690                         *dPtr = ( *sPtr << 16 ) | ( *(sPtr+1) << 8 ) | ( *(sPtr+2) ) | 0xFF000000;
691                         sPtr += 3;
692
693                         nonzeroa = true;
694                     }
695                     else
696                     {
697                         assert( offset*4 < rowPitch);
698
699                         if ( sPtr+3 >= endPtr )
700                             return E_FAIL;
701
702                         // BGRA -> RGBA
703                         *dPtr = ( *sPtr << 16 ) | ( *(sPtr+1) << 8 ) | ( *(sPtr+2) ) | ( *(sPtr+3) << 24 );
704
705                         if ( *(sPtr+3) > 0 )
706                             nonzeroa = true;
707
708                         sPtr += 4;
709                     }
710
711                     if ( convFlags & CONV_FLAGS_INVERTX )
712                         --dPtr;
713                     else
714                         ++dPtr;
715                 }
716             }
717
718             // If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
719             if ( !nonzeroa )
720             {
721                 HRESULT hr = _SetAlphaChannelToOpaque( image );
722                 if ( FAILED(hr) )
723                     return hr;
724             }
725         }
726         break;
727
728     //---------------------------------------------------------------------------------
729     default:
730         return E_FAIL;
731     }
732
733     return S_OK;   
734 }
735
736
737 //-------------------------------------------------------------------------------------
738 // Encodes TGA file header
739 //-------------------------------------------------------------------------------------
740 static HRESULT _EncodeTGAHeader( _In_ const Image& image, _Out_ TGA_HEADER& header, DWORD& convFlags )
741 {
742     assert( IsValid( image.format ) && !IsVideo( image.format ) );
743
744     memset( &header, 0, sizeof(TGA_HEADER) );
745
746     if ( (image.width > 0xFFFF)
747          || (image.height > 0xFFFF) )
748     {
749          return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
750     }
751
752     header.wWidth = static_cast<uint16_t>( image.width );
753     header.wHeight = static_cast<uint16_t>( image.height );
754
755     switch( image.format )
756     {
757     case DXGI_FORMAT_R8G8B8A8_UNORM:
758     case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
759         header.bImageType = TGA_TRUECOLOR;
760         header.bBitsPerPixel = 32;
761         header.bDescriptor = TGA_FLAGS_INVERTY | 8;
762         convFlags |= CONV_FLAGS_SWIZZLE;
763         break;
764
765     case DXGI_FORMAT_B8G8R8A8_UNORM:
766     case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
767         header.bImageType = TGA_TRUECOLOR;
768         header.bBitsPerPixel = 32;
769         header.bDescriptor = TGA_FLAGS_INVERTY | 8;
770         break;
771
772     case DXGI_FORMAT_B8G8R8X8_UNORM:
773     case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
774         header.bImageType = TGA_TRUECOLOR;
775         header.bBitsPerPixel = 24;
776         header.bDescriptor = TGA_FLAGS_INVERTY;
777         convFlags |= CONV_FLAGS_888;
778         break;
779
780     case DXGI_FORMAT_R8_UNORM:
781     case DXGI_FORMAT_A8_UNORM:
782         header.bImageType = TGA_BLACK_AND_WHITE;
783         header.bBitsPerPixel = 8;
784         header.bDescriptor = TGA_FLAGS_INVERTY;
785         break;
786
787     case DXGI_FORMAT_B5G5R5A1_UNORM:
788         header.bImageType = TGA_TRUECOLOR;
789         header.bBitsPerPixel = 16;
790         header.bDescriptor = TGA_FLAGS_INVERTY | 1;
791         break;
792
793     default:
794         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
795     }
796
797     return S_OK;
798 }
799
800
801 //-------------------------------------------------------------------------------------
802 // Copies BGRX data to form BGR 24bpp data
803 //-------------------------------------------------------------------------------------
804 #pragma warning(suppress: 6001 6101) // In the case where outSize is insufficient we do not write to pDestination
805 static void _Copy24bppScanline( _Out_bytecap_(outSize) LPVOID pDestination, _In_ size_t outSize, 
806                                 _In_bytecount_(inSize) LPCVOID pSource, _In_ size_t inSize )
807 {
808     assert( pDestination && outSize > 0 );
809     assert( pSource && inSize > 0 );
810
811     assert( pDestination != pSource );
812
813     const uint32_t * __restrict sPtr = reinterpret_cast<const uint32_t*>(pSource);
814     uint8_t * __restrict dPtr = reinterpret_cast<uint8_t*>(pDestination);
815
816     const uint8_t* endPtr = dPtr + outSize;
817
818     for( size_t count = 0; count < inSize; count += 4 )
819     {
820         uint32_t t = *(sPtr++);
821
822         if ( dPtr+2 > endPtr )
823             return;
824
825         *(dPtr++) = uint8_t(t & 0xFF);              // Blue
826         *(dPtr++) = uint8_t((t & 0xFF00) >> 8);     // Green
827         *(dPtr++) = uint8_t((t & 0xFF0000) >> 16);  // Red
828     }
829 }
830
831
832 //=====================================================================================
833 // Entry-points
834 //=====================================================================================
835
836 //-------------------------------------------------------------------------------------
837 // Obtain metadata from TGA file in memory/on disk
838 //-------------------------------------------------------------------------------------
839 HRESULT GetMetadataFromTGAMemory( LPCVOID pSource, size_t size, TexMetadata& metadata )
840 {
841     if ( !pSource || size == 0 )
842         return E_INVALIDARG;
843
844     size_t offset;
845     return _DecodeTGAHeader( pSource, size, metadata, offset, 0 );
846 }
847
848 HRESULT GetMetadataFromTGAFile( LPCWSTR szFile, TexMetadata& metadata )
849 {
850     if ( !szFile )
851         return E_INVALIDARG;
852
853 #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
854     ScopedHandle hFile( safe_handle( CreateFile2( szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0 ) ) );
855 #else
856     ScopedHandle hFile( safe_handle( CreateFileW( szFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING,
857                                                   FILE_FLAG_SEQUENTIAL_SCAN, 0 ) ) );
858 #endif
859     if ( !hFile )
860     {
861         return HRESULT_FROM_WIN32( GetLastError() );
862     }
863
864     // Get the file size
865     LARGE_INTEGER fileSize = {0};
866
867 #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
868     FILE_STANDARD_INFO fileInfo;
869     if ( !GetFileInformationByHandleEx( hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo) ) )
870     {
871         return HRESULT_FROM_WIN32( GetLastError() );
872     }
873     fileSize = fileInfo.EndOfFile;
874 #else
875     if ( !GetFileSizeEx( hFile.get(), &fileSize ) )
876     {
877         return HRESULT_FROM_WIN32( GetLastError() );
878     }
879 #endif
880
881     // File is too big for 32-bit allocation, so reject read (4 GB should be plenty large enough for a valid TGA file)
882     if ( fileSize.HighPart > 0 )
883     {
884         return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE );
885     }
886
887     // Need at least enough data to fill the standard header to be a valid TGA
888     if ( fileSize.LowPart < ( sizeof(TGA_HEADER) ) )
889     {
890         return E_FAIL;
891     }
892
893     // Read the standard header (we don't need the file footer to parse the file)
894     uint8_t header[sizeof(TGA_HEADER)];
895     DWORD bytesRead = 0;
896     if ( !ReadFile( hFile.get(), header, sizeof(TGA_HEADER), &bytesRead, 0 ) )
897     {
898         return HRESULT_FROM_WIN32( GetLastError() );
899     }
900
901     size_t offset;
902     return _DecodeTGAHeader( header, bytesRead, metadata, offset, 0 );
903 }
904
905
906 //-------------------------------------------------------------------------------------
907 // Load a TGA file in memory
908 //-------------------------------------------------------------------------------------
909 HRESULT LoadFromTGAMemory( LPCVOID pSource, size_t size, TexMetadata* metadata, ScratchImage& image )
910 {
911     if ( !pSource || size == 0 )
912         return E_INVALIDARG;
913
914     image.Release();
915
916     size_t offset;
917     DWORD convFlags = 0;
918     TexMetadata mdata;
919     HRESULT hr = _DecodeTGAHeader( pSource, size, mdata, offset, &convFlags );
920     if ( FAILED(hr) )
921         return hr;
922
923     if ( offset > size )
924         return E_FAIL;
925
926     LPCVOID pPixels = reinterpret_cast<LPCVOID>( reinterpret_cast<const uint8_t*>(pSource) + offset );
927     assert( pPixels );
928
929     size_t remaining = size - offset;
930     if ( remaining == 0 )
931         return E_FAIL;
932
933     hr = image.Initialize2D( mdata.format, mdata.width, mdata.height, 1, 1 );
934     if ( FAILED(hr) )
935         return hr;
936
937     if ( convFlags & CONV_FLAGS_RLE )
938     {
939         hr = _UncompressPixels( pPixels, remaining, image.GetImage(0,0,0), convFlags );
940     }
941     else
942     {
943         hr = _CopyPixels( pPixels, remaining, image.GetImage(0,0,0), convFlags );
944     }
945
946     if ( FAILED(hr) )
947     {
948         image.Release();
949         return hr;
950     }
951
952     if ( metadata )
953         memcpy( metadata, &mdata, sizeof(TexMetadata) );
954
955     return S_OK;
956 }
957
958
959 //-------------------------------------------------------------------------------------
960 // Load a TGA file from disk
961 //-------------------------------------------------------------------------------------
962 HRESULT LoadFromTGAFile( LPCWSTR szFile, TexMetadata* metadata, ScratchImage& image )
963 {
964     if ( !szFile )
965         return E_INVALIDARG;
966
967     image.Release();
968
969 #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
970     ScopedHandle hFile( safe_handle( CreateFile2( szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0 ) ) );
971 #else
972     ScopedHandle hFile( safe_handle( CreateFileW( szFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING,
973                                                   FILE_FLAG_SEQUENTIAL_SCAN, 0 ) ) );
974 #endif
975     if ( !hFile )
976     {
977         return HRESULT_FROM_WIN32( GetLastError() );
978     }
979
980     // Get the file size
981     LARGE_INTEGER fileSize = {0};
982
983 #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
984     FILE_STANDARD_INFO fileInfo;
985     if ( !GetFileInformationByHandleEx( hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo) ) )
986     {
987         return HRESULT_FROM_WIN32( GetLastError() );
988     }
989     fileSize = fileInfo.EndOfFile;
990 #else
991     if ( !GetFileSizeEx( hFile.get(), &fileSize ) )
992     {
993         return HRESULT_FROM_WIN32( GetLastError() );
994     }
995 #endif
996
997     // File is too big for 32-bit allocation, so reject read (4 GB should be plenty large enough for a valid TGA file)
998     if ( fileSize.HighPart > 0 )
999     {
1000         return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE );
1001     }
1002
1003     // Need at least enough data to fill the header to be a valid TGA
1004     if ( fileSize.LowPart < sizeof(TGA_HEADER) )
1005     {
1006         return E_FAIL;
1007     }
1008
1009     // Read the header
1010     uint8_t header[sizeof(TGA_HEADER)];
1011     DWORD bytesRead = 0;
1012     if ( !ReadFile( hFile.get(), header, sizeof(TGA_HEADER), &bytesRead, 0 ) )
1013     {
1014         return HRESULT_FROM_WIN32( GetLastError() );
1015     }
1016
1017     size_t offset;
1018     DWORD convFlags = 0;
1019     TexMetadata mdata;
1020     HRESULT hr = _DecodeTGAHeader( header, bytesRead, mdata, offset, &convFlags );
1021     if ( FAILED(hr) )
1022         return hr;
1023
1024     // Read the pixels
1025     DWORD remaining = static_cast<DWORD>( fileSize.LowPart - offset );
1026     if ( remaining == 0 )
1027         return E_FAIL;
1028
1029     if ( offset > sizeof(TGA_HEADER) )
1030     {
1031         // Skip past the id string
1032         LARGE_INTEGER filePos = { static_cast<DWORD>(offset), 0 };
1033         if ( !SetFilePointerEx( hFile.get(), filePos, 0, FILE_BEGIN ) )
1034         {
1035             return HRESULT_FROM_WIN32( GetLastError() );
1036         }
1037     }
1038
1039     hr = image.Initialize2D( mdata.format, mdata.width, mdata.height, 1, 1 );
1040     if ( FAILED(hr) )
1041         return hr;
1042
1043     assert( image.GetPixels() );
1044
1045     if ( !(convFlags & (CONV_FLAGS_RLE | CONV_FLAGS_EXPAND | CONV_FLAGS_INVERTX)) && (convFlags & CONV_FLAGS_INVERTY) )
1046     {
1047         // This case we can read directly into the image buffer in place
1048         if ( !ReadFile( hFile.get(), image.GetPixels(), static_cast<DWORD>( image.GetPixelsSize() ), &bytesRead, 0 ) )
1049         {
1050             image.Release();
1051             return HRESULT_FROM_WIN32( GetLastError() );
1052         }
1053
1054         if ( bytesRead != image.GetPixelsSize() )
1055         {
1056             image.Release();
1057             return E_FAIL;
1058         }
1059
1060         switch( mdata.format  )
1061         {
1062         case DXGI_FORMAT_R8G8B8A8_UNORM:
1063             {
1064                 // TGA stores 32-bit data in BGRA form, need to swizzle to RGBA
1065                 assert( image.GetImageCount() == 1 );
1066                 const Image* img = image.GetImage(0,0,0);
1067                 if ( !img )
1068                     return E_POINTER;
1069
1070                 uint8_t *pPixels = img->pixels;
1071                 if ( !pPixels )
1072                     return E_POINTER;
1073
1074                 size_t rowPitch = img->rowPitch;
1075
1076                 // Scan for non-zero alpha channel
1077                 bool nonzeroa = false;
1078
1079                 for( size_t h = 0; h < img->height; ++h )
1080                 {
1081                     const uint32_t* sPtr = reinterpret_cast<const uint32_t*>( pPixels );
1082
1083                     for( size_t x=0; x < img->width; ++x )
1084                     {
1085                         if ( (*sPtr) & 0xff000000 )
1086                         {
1087                             nonzeroa = true;
1088                             break;
1089                         }
1090
1091                         ++sPtr;
1092                     }
1093
1094                     if ( nonzeroa )
1095                         break;
1096
1097                     pPixels += rowPitch;
1098                 }
1099
1100                 DWORD tflags = ( !nonzeroa ) ? TEXP_SCANLINE_SETALPHA : TEXP_SCANLINE_NONE;
1101
1102                 // Swizzle scanlines
1103                 pPixels = img->pixels;
1104
1105                 for( size_t h = 0; h < img->height; ++h )
1106                 {
1107                     _SwizzleScanline( pPixels, rowPitch, pPixels, rowPitch, mdata.format, tflags );
1108
1109                     pPixels += rowPitch;
1110                 }
1111             }
1112             break;
1113
1114         // If we start using DXGI_FORMAT_B8G8R8X8_UNORM or DXGI_FORMAT_B8G8R8A8_UNORM we need to check for a fully 0 alpha channel
1115      
1116         case DXGI_FORMAT_B5G5R5A1_UNORM:
1117             {
1118                 assert( image.GetImageCount() == 1 );
1119                 const Image* img = image.GetImage(0,0,0);
1120                 if ( !img )
1121                     return E_POINTER;
1122
1123                 // Scan for non-zero alpha channel
1124                 bool nonzeroa = false;
1125
1126                 const uint8_t *pPixels = img->pixels;
1127                 if ( !pPixels )
1128                     return E_POINTER;
1129
1130                 size_t rowPitch = img->rowPitch;
1131
1132                 for( size_t h = 0; h < img->height; ++h )
1133                 {
1134                     const uint16_t* sPtr = reinterpret_cast<const uint16_t*>( pPixels );
1135
1136                     for( size_t x=0; x < img->width; ++x )
1137                     {
1138                         if ( *sPtr & 0x8000 )
1139                         {
1140                             nonzeroa = true;
1141                             break;
1142                         }
1143
1144                         ++sPtr;
1145                     }
1146
1147                     if ( nonzeroa )
1148                         break;
1149
1150                     pPixels += rowPitch;
1151                 }
1152
1153                 // If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
1154                 if ( !nonzeroa )
1155                 {
1156                     hr = _SetAlphaChannelToOpaque( img );
1157                     if ( FAILED(hr) )
1158                         return hr;
1159                 }
1160             }
1161             break;
1162         }
1163     }
1164     else // RLE || EXPAND || INVERTX || !INVERTY
1165     {
1166         std::unique_ptr<uint8_t[]> temp( new uint8_t[ remaining ] );
1167         if ( !temp )
1168         {
1169             image.Release();
1170             return E_OUTOFMEMORY;
1171         }
1172
1173         if ( !ReadFile( hFile.get(), temp.get(), remaining, &bytesRead, 0 ) )
1174         {
1175             image.Release();
1176             return HRESULT_FROM_WIN32( GetLastError() );
1177         }
1178
1179         if ( bytesRead != remaining )
1180         {
1181             image.Release();
1182             return E_FAIL;
1183         }
1184
1185         if ( convFlags & CONV_FLAGS_RLE )
1186         {
1187             hr = _UncompressPixels( temp.get(), remaining, image.GetImage(0,0,0), convFlags );
1188         }
1189         else
1190         {
1191             hr = _CopyPixels( temp.get(), remaining, image.GetImage(0,0,0), convFlags );
1192         }
1193
1194         if ( FAILED(hr) )
1195         {
1196             image.Release();
1197             return hr;
1198         }
1199     }
1200
1201     if ( metadata )
1202         memcpy( metadata, &mdata, sizeof(TexMetadata) );
1203
1204     return S_OK;
1205 }
1206
1207
1208 //-------------------------------------------------------------------------------------
1209 // Save a TGA file to memory
1210 //-------------------------------------------------------------------------------------
1211 HRESULT SaveToTGAMemory( const Image& image, Blob& blob )
1212 {
1213     if ( !image.pixels )
1214         return E_POINTER;
1215
1216     TGA_HEADER tga_header;
1217     DWORD convFlags = 0;
1218     HRESULT hr = _EncodeTGAHeader( image, tga_header, convFlags );
1219     if ( FAILED(hr) )
1220         return hr;
1221
1222     blob.Release();
1223
1224     // Determine memory required for image data
1225     size_t rowPitch, slicePitch;
1226     if ( convFlags & CONV_FLAGS_888 )
1227     {
1228         rowPitch = image.width * 3;
1229         slicePitch = image.height * rowPitch;
1230     }
1231     else
1232     {
1233         ComputePitch( image.format, image.width, image.height, rowPitch, slicePitch, CP_FLAGS_NONE );
1234     }
1235
1236     hr = blob.Initialize( sizeof(TGA_HEADER) + slicePitch );
1237     if ( FAILED(hr) )
1238         return hr;
1239
1240     // Copy header
1241     uint8_t* dPtr = reinterpret_cast<uint8_t*>( blob.GetBufferPointer() );
1242     assert( dPtr != 0 );
1243     memcpy_s( dPtr, blob.GetBufferSize(), &tga_header, sizeof(TGA_HEADER) );
1244     dPtr += sizeof(TGA_HEADER);
1245
1246     const uint8_t* pPixels = reinterpret_cast<const uint8_t*>( image.pixels );
1247     assert( pPixels );
1248
1249     for( size_t y = 0; y < image.height; ++y )
1250     {
1251         // Copy pixels
1252         if ( convFlags & CONV_FLAGS_888 )
1253         {
1254             _Copy24bppScanline( dPtr, rowPitch, pPixels, image.rowPitch );
1255         }
1256         else if ( convFlags & CONV_FLAGS_SWIZZLE )
1257         {
1258             _SwizzleScanline( dPtr, rowPitch, pPixels, image.rowPitch, image.format, TEXP_SCANLINE_NONE );
1259         }
1260         else
1261         {
1262             _CopyScanline( dPtr, rowPitch, pPixels, image.rowPitch, image.format, TEXP_SCANLINE_NONE );
1263         }
1264
1265         dPtr += rowPitch;
1266         pPixels += image.rowPitch;
1267     }
1268
1269     return S_OK;
1270 }
1271
1272
1273 //-------------------------------------------------------------------------------------
1274 // Save a TGA file to disk
1275 //-------------------------------------------------------------------------------------
1276 HRESULT SaveToTGAFile( const Image& image, LPCWSTR szFile )
1277 {
1278     if ( !szFile )
1279         return E_INVALIDARG;
1280
1281     if ( !image.pixels )
1282         return E_POINTER;
1283
1284     TGA_HEADER tga_header;
1285     DWORD convFlags = 0;
1286     HRESULT hr = _EncodeTGAHeader( image, tga_header, convFlags );
1287     if ( FAILED(hr) )
1288         return hr;
1289
1290     // Create file and write header
1291 #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
1292     ScopedHandle hFile( safe_handle( CreateFile2( szFile, GENERIC_WRITE, 0, CREATE_ALWAYS, 0 ) ) );
1293 #else
1294     ScopedHandle hFile( safe_handle( CreateFileW( szFile, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0 ) ) );
1295 #endif
1296     if ( !hFile )
1297     {
1298         return HRESULT_FROM_WIN32( GetLastError() );
1299     }
1300
1301     // Determine size for TGA pixel data
1302     size_t rowPitch, slicePitch;
1303     if ( convFlags & CONV_FLAGS_888 )
1304     {
1305         rowPitch = image.width * 3;
1306         slicePitch = image.height * rowPitch;
1307     }
1308     else
1309     {
1310         ComputePitch( image.format, image.width, image.height, rowPitch, slicePitch, CP_FLAGS_NONE );
1311     }
1312
1313     if ( slicePitch < 65535 )
1314     {
1315         // For small images, it is better to create an in-memory file and write it out
1316         Blob blob;
1317
1318         hr = SaveToTGAMemory( image, blob );
1319         if ( FAILED(hr) )
1320             return hr;
1321
1322         // Write blob
1323         const DWORD bytesToWrite = static_cast<DWORD>( blob.GetBufferSize() );
1324         DWORD bytesWritten;
1325         if ( !WriteFile( hFile.get(), blob.GetBufferPointer(), bytesToWrite,
1326                          &bytesWritten, 0 ) )
1327         {
1328             return HRESULT_FROM_WIN32( GetLastError() );
1329         }
1330
1331         if ( bytesWritten != bytesToWrite )
1332         {
1333             return E_FAIL;
1334         }
1335     }
1336     else
1337     {
1338         // Otherwise, write the image one scanline at a time...
1339         std::unique_ptr<uint8_t[]> temp( new uint8_t[ rowPitch ] );
1340         if ( !temp )
1341             return E_OUTOFMEMORY;
1342
1343         // Write header
1344         DWORD bytesWritten;
1345         if ( !WriteFile( hFile.get(), &tga_header, sizeof(TGA_HEADER), &bytesWritten, 0 ) )
1346         {
1347             return HRESULT_FROM_WIN32( GetLastError() );
1348         }
1349
1350         if ( bytesWritten != sizeof(TGA_HEADER) )
1351             return E_FAIL;
1352
1353         // Write pixels
1354         const uint8_t* pPixels = reinterpret_cast<const uint8_t*>( image.pixels );
1355
1356         for( size_t y = 0; y < image.height; ++y )
1357         {
1358             // Copy pixels
1359             if ( convFlags & CONV_FLAGS_888 )
1360             {
1361                 _Copy24bppScanline( temp.get(), rowPitch, pPixels, image.rowPitch );
1362             }
1363             else if ( convFlags & CONV_FLAGS_SWIZZLE )
1364             {
1365                 _SwizzleScanline( temp.get(), rowPitch, pPixels, image.rowPitch, image.format, TEXP_SCANLINE_NONE );
1366             }
1367             else
1368             {
1369                 _CopyScanline( temp.get(), rowPitch, pPixels, image.rowPitch, image.format, TEXP_SCANLINE_NONE );
1370             }
1371
1372             pPixels += image.rowPitch;
1373
1374             if ( !WriteFile( hFile.get(), temp.get(), static_cast<DWORD>( rowPitch ), &bytesWritten, 0 ) )
1375             {
1376                 return HRESULT_FROM_WIN32( GetLastError() );
1377             }
1378
1379             if ( bytesWritten != rowPitch )
1380                 return E_FAIL;
1381         }
1382     }
1383
1384     return S_OK;
1385 }
1386
1387 }; // namespace