1 //-------------------------------------------------------------------------------------
4 // DirectX Texture Library - Targa Truevision (TGA) file format reader/writer
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
11 // Copyright (c) Microsoft Corporation. All rights reserved.
13 // http://go.microsoft.com/fwlink/?LinkId=248926
14 //-------------------------------------------------------------------------------------
16 #include "DirectXTexP.h"
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)
31 TGA_BLACK_AND_WHITE = 3,
32 TGA_COLOR_MAPPED_RLE = 9,
33 TGA_TRUECOLOR_RLE = 10,
34 TGA_BLACK_AND_WHITE_RLE = 11,
37 enum TGADescriptorFlags
39 TGA_FLAGS_INVERTX = 0x10,
40 TGA_FLAGS_INVERTY = 0x20,
41 TGA_FLAGS_INTERLEAVED_2WAY = 0x40, // Deprecated
42 TGA_FLAGS_INTERLEAVED_4WAY = 0x80, // Deprecated
45 const char* g_TGA20_Signature = "TRUEVISION-XFILE.";
51 uint8_t bColorMapType;
53 uint16_t wColorMapFirst;
54 uint16_t wColorMapLength;
55 uint8_t bColorMapSize;
60 uint8_t bBitsPerPixel;
66 uint16_t dwExtensionOffset;
67 uint16_t dwDeveloperOffset;
74 char szAuthorName[41];
75 char szAuthorComment[324];
80 uint16_t wStampMinute;
81 uint16_t wStampSecond;
86 char szSoftwareId[41];
87 uint16_t wVersionNumber;
88 uint8_t bVersionLetter;
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;
101 enum CONVERSION_FLAGS
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
109 CONV_FLAGS_SWIZZLE = 0x10000, // Swizzle BGR<->RGB data
110 CONV_FLAGS_888 = 0x20000, // 24bpp format
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 )
125 memset( &metadata, 0, sizeof(TexMetadata) );
127 if ( size < sizeof(TGA_HEADER) )
129 return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
132 const TGA_HEADER* pHeader = reinterpret_cast<const TGA_HEADER*>( pSource );
135 if ( pHeader->bColorMapType != 0
136 || pHeader->wColorMapLength != 0 )
138 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
141 if ( pHeader->bDescriptor & (TGA_FLAGS_INTERLEAVED_2WAY|TGA_FLAGS_INTERLEAVED_4WAY) )
143 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
146 if ( !pHeader->wWidth || !pHeader->wHeight )
148 return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
151 switch ( pHeader->bImageType )
154 case TGA_TRUECOLOR_RLE:
155 switch( pHeader->bBitsPerPixel )
158 metadata.format = DXGI_FORMAT_B5G5R5A1_UNORM;
162 metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM;
164 *convFlags |= CONV_FLAGS_EXPAND;
165 // We could use DXGI_FORMAT_B8G8R8X8_UNORM, but we prefer DXGI 1.0 formats
169 metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM;
170 // We could use DXGI_FORMAT_B8G8R8A8_UNORM, but we prefer DXGI 1.0 formats
174 if ( convFlags && (pHeader->bImageType == TGA_TRUECOLOR_RLE) )
176 *convFlags |= CONV_FLAGS_RLE;
180 case TGA_BLACK_AND_WHITE:
181 case TGA_BLACK_AND_WHITE_RLE:
182 switch( pHeader->bBitsPerPixel )
185 metadata.format = DXGI_FORMAT_R8_UNORM;
189 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
192 if ( convFlags && (pHeader->bImageType == TGA_BLACK_AND_WHITE_RLE) )
194 *convFlags |= CONV_FLAGS_RLE;
199 case TGA_COLOR_MAPPED:
200 case TGA_COLOR_MAPPED_RLE:
201 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
204 return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
207 metadata.width = pHeader->wWidth;
208 metadata.height = pHeader->wHeight;
209 metadata.depth = metadata.arraySize = metadata.mipLevels = 1;
210 metadata.dimension = TEX_DIMENSION_TEXTURE2D;
214 if ( pHeader->bDescriptor & TGA_FLAGS_INVERTX )
215 *convFlags |= CONV_FLAGS_INVERTX;
217 if ( pHeader->bDescriptor & TGA_FLAGS_INVERTY )
218 *convFlags |= CONV_FLAGS_INVERTY;
221 offset = sizeof( TGA_HEADER );
223 if ( pHeader->bIDLength != 0 )
225 offset += pHeader->bIDLength;
232 //-------------------------------------------------------------------------------------
233 // Set alpha for images with all 0 alpha channel
234 //-------------------------------------------------------------------------------------
235 static HRESULT _SetAlphaChannelToOpaque( _In_ const Image* image )
239 uint8_t* pPixels = reinterpret_cast<uint8_t*>( image->pixels );
243 for( size_t y = 0; y < image->height; ++y )
245 _CopyScanline( pPixels, image->rowPitch, pPixels, image->rowPitch, image->format, TEXP_SCANLINE_SETALPHA );
246 pPixels += image->rowPitch;
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 )
258 assert( pSource && size > 0 );
260 if ( !image || !image->pixels )
263 // Compute TGA image data pitch
265 if ( convFlags & CONV_FLAGS_EXPAND )
267 rowPitch = image->width * 3;
272 ComputePitch( image->format, image->width, image->height, rowPitch, slicePitch, CP_FLAGS_NONE );
275 const uint8_t* sPtr = reinterpret_cast<const uint8_t*>( pSource );
276 const uint8_t* endPtr = sPtr + size;
278 switch( image->format )
280 //--------------------------------------------------------------------------- 8-bit
281 case DXGI_FORMAT_R8_UNORM:
282 for( size_t y=0; y < image->height; ++y )
284 size_t offset = ( (convFlags & CONV_FLAGS_INVERTX ) ? (image->width - 1) : 0 );
285 assert( offset < rowPitch);
287 uint8_t* dPtr = reinterpret_cast<uint8_t*>( image->pixels )
288 + ( image->rowPitch * ( (convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1) ) )
291 for( size_t x=0; x < image->width; )
293 if ( sPtr >= endPtr )
299 size_t j = (*sPtr & 0x7F) + 1;
300 if ( ++sPtr >= endPtr )
303 for( ; j > 0; --j, ++x )
305 if ( x >= image->width )
310 if ( convFlags & CONV_FLAGS_INVERTX )
321 size_t j = (*sPtr & 0x7F) + 1;
324 if ( sPtr+j > endPtr )
327 for( ; j > 0; --j, ++x )
329 if ( x >= image->width )
334 if ( convFlags & CONV_FLAGS_INVERTX )
344 //-------------------------------------------------------------------------- 16-bit
345 case DXGI_FORMAT_B5G5R5A1_UNORM:
347 bool nonzeroa = false;
348 for( size_t y=0; y < image->height; ++y )
350 size_t offset = ( (convFlags & CONV_FLAGS_INVERTX ) ? (image->width - 1) : 0 );
351 assert( offset*2 < rowPitch);
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) ) ) )
357 for( size_t x=0; x < image->width; )
359 if ( sPtr >= endPtr )
365 size_t j = (*sPtr & 0x7F) + 1;
368 if ( sPtr+1 >= endPtr )
371 uint16_t t = *sPtr | (*(sPtr+1) << 8);
376 for( ; j > 0; --j, ++x )
378 if ( x >= image->width )
383 if ( convFlags & CONV_FLAGS_INVERTX )
392 size_t j = (*sPtr & 0x7F) + 1;
395 if ( sPtr+(j*2) > endPtr )
398 for( ; j > 0; --j, ++x )
400 if ( x >= image->width )
403 uint16_t t = *sPtr | (*(sPtr+1) << 8);
409 if ( convFlags & CONV_FLAGS_INVERTX )
418 // If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
421 HRESULT hr = _SetAlphaChannelToOpaque( image );
428 //----------------------------------------------------------------------- 24/32-bit
429 case DXGI_FORMAT_R8G8B8A8_UNORM:
431 bool nonzeroa = false;
432 for( size_t y=0; y < image->height; ++y )
434 size_t offset = ( (convFlags & CONV_FLAGS_INVERTX ) ? (image->width - 1) : 0 );
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) ) ) )
440 for( size_t x=0; x < image->width; )
442 if ( sPtr >= endPtr )
448 size_t j = (*sPtr & 0x7F) + 1;
452 if ( convFlags & CONV_FLAGS_EXPAND )
454 assert( offset*3 < rowPitch);
456 if ( sPtr+2 >= endPtr )
460 t = ( *sPtr << 16 ) | ( *(sPtr+1) << 8 ) | ( *(sPtr+2) ) | 0xFF000000;
467 assert( offset*4 < rowPitch);
469 if ( sPtr+3 >= endPtr )
473 t = ( *sPtr << 16 ) | ( *(sPtr+1) << 8 ) | ( *(sPtr+2) ) | ( *(sPtr+3) << 24 );
481 for( ; j > 0; --j, ++x )
483 if ( x >= image->width )
488 if ( convFlags & CONV_FLAGS_INVERTX )
497 size_t j = (*sPtr & 0x7F) + 1;
500 if ( convFlags & CONV_FLAGS_EXPAND )
502 if ( sPtr+(j*3) > endPtr )
507 if ( sPtr+(j*4) > endPtr )
511 for( ; j > 0; --j, ++x )
513 if ( x >= image->width )
516 if ( convFlags & CONV_FLAGS_EXPAND )
518 assert( offset*3 < rowPitch);
520 if ( sPtr+2 >= endPtr )
524 *dPtr = ( *sPtr << 16 ) | ( *(sPtr+1) << 8 ) | ( *(sPtr+2) ) | 0xFF000000;
531 assert( offset*4 < rowPitch);
533 if ( sPtr+3 >= endPtr )
537 *dPtr = ( *sPtr << 16 ) | ( *(sPtr+1) << 8 ) | ( *(sPtr+2) ) | ( *(sPtr+3) << 24 );
545 if ( convFlags & CONV_FLAGS_INVERTX )
554 // If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
557 HRESULT hr = _SetAlphaChannelToOpaque( image );
564 //---------------------------------------------------------------------------------
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 )
578 assert( pSource && size > 0 );
580 if ( !image || !image->pixels )
583 // Compute TGA image data pitch
585 if ( convFlags & CONV_FLAGS_EXPAND )
587 rowPitch = image->width * 3;
592 ComputePitch( image->format, image->width, image->height, rowPitch, slicePitch, CP_FLAGS_NONE );
595 const uint8_t* sPtr = reinterpret_cast<const uint8_t*>( pSource );
596 const uint8_t* endPtr = sPtr + size;
598 switch( image->format )
600 //--------------------------------------------------------------------------- 8-bit
601 case DXGI_FORMAT_R8_UNORM:
602 for( size_t y=0; y < image->height; ++y )
604 size_t offset = ( (convFlags & CONV_FLAGS_INVERTX ) ? (image->width - 1) : 0 );
605 assert( offset < rowPitch);
607 uint8_t* dPtr = reinterpret_cast<uint8_t*>( image->pixels )
608 + ( image->rowPitch * ( (convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1) ) )
611 for( size_t x=0; x < image->width; ++x )
613 if ( sPtr >= endPtr )
618 if ( convFlags & CONV_FLAGS_INVERTX )
626 //-------------------------------------------------------------------------- 16-bit
627 case DXGI_FORMAT_B5G5R5A1_UNORM:
629 bool nonzeroa = false;
630 for( size_t y=0; y < image->height; ++y )
632 size_t offset = ( (convFlags & CONV_FLAGS_INVERTX ) ? (image->width - 1) : 0 );
633 assert( offset*2 < rowPitch);
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) ) ) )
639 for( size_t x=0; x < image->width; ++x )
641 if ( sPtr+1 >= endPtr )
644 uint16_t t = *sPtr | (*(sPtr+1) << 8);
651 if ( convFlags & CONV_FLAGS_INVERTX )
658 // If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
661 HRESULT hr = _SetAlphaChannelToOpaque( image );
668 //----------------------------------------------------------------------- 24/32-bit
669 case DXGI_FORMAT_R8G8B8A8_UNORM:
671 bool nonzeroa = false;
672 for( size_t y=0; y < image->height; ++y )
674 size_t offset = ( (convFlags & CONV_FLAGS_INVERTX ) ? (image->width - 1) : 0 );
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) ) ) )
680 for( size_t x=0; x < image->width; ++x )
682 if ( convFlags & CONV_FLAGS_EXPAND )
684 assert( offset*3 < rowPitch);
686 if ( sPtr+2 >= endPtr )
690 *dPtr = ( *sPtr << 16 ) | ( *(sPtr+1) << 8 ) | ( *(sPtr+2) ) | 0xFF000000;
697 assert( offset*4 < rowPitch);
699 if ( sPtr+3 >= endPtr )
703 *dPtr = ( *sPtr << 16 ) | ( *(sPtr+1) << 8 ) | ( *(sPtr+2) ) | ( *(sPtr+3) << 24 );
711 if ( convFlags & CONV_FLAGS_INVERTX )
718 // If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
721 HRESULT hr = _SetAlphaChannelToOpaque( image );
728 //---------------------------------------------------------------------------------
737 //-------------------------------------------------------------------------------------
738 // Encodes TGA file header
739 //-------------------------------------------------------------------------------------
740 static HRESULT _EncodeTGAHeader( _In_ const Image& image, _Out_ TGA_HEADER& header, DWORD& convFlags )
742 assert( IsValid( image.format ) && !IsVideo( image.format ) );
744 memset( &header, 0, sizeof(TGA_HEADER) );
746 if ( (image.width > 0xFFFF)
747 || (image.height > 0xFFFF) )
749 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
752 header.wWidth = static_cast<uint16_t>( image.width );
753 header.wHeight = static_cast<uint16_t>( image.height );
755 switch( image.format )
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;
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;
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;
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;
787 case DXGI_FORMAT_B5G5R5A1_UNORM:
788 header.bImageType = TGA_TRUECOLOR;
789 header.bBitsPerPixel = 16;
790 header.bDescriptor = TGA_FLAGS_INVERTY | 1;
794 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
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 )
808 assert( pDestination && outSize > 0 );
809 assert( pSource && inSize > 0 );
811 assert( pDestination != pSource );
813 const uint32_t * __restrict sPtr = reinterpret_cast<const uint32_t*>(pSource);
814 uint8_t * __restrict dPtr = reinterpret_cast<uint8_t*>(pDestination);
816 const uint8_t* endPtr = dPtr + outSize;
818 for( size_t count = 0; count < inSize; count += 4 )
820 uint32_t t = *(sPtr++);
822 if ( dPtr+2 > endPtr )
825 *(dPtr++) = uint8_t(t & 0xFF); // Blue
826 *(dPtr++) = uint8_t((t & 0xFF00) >> 8); // Green
827 *(dPtr++) = uint8_t((t & 0xFF0000) >> 16); // Red
832 //=====================================================================================
834 //=====================================================================================
836 //-------------------------------------------------------------------------------------
837 // Obtain metadata from TGA file in memory/on disk
838 //-------------------------------------------------------------------------------------
839 HRESULT GetMetadataFromTGAMemory( LPCVOID pSource, size_t size, TexMetadata& metadata )
841 if ( !pSource || size == 0 )
845 return _DecodeTGAHeader( pSource, size, metadata, offset, 0 );
848 HRESULT GetMetadataFromTGAFile( LPCWSTR szFile, TexMetadata& metadata )
853 #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
854 ScopedHandle hFile( safe_handle( CreateFile2( szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0 ) ) );
856 ScopedHandle hFile( safe_handle( CreateFileW( szFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING,
857 FILE_FLAG_SEQUENTIAL_SCAN, 0 ) ) );
861 return HRESULT_FROM_WIN32( GetLastError() );
865 LARGE_INTEGER fileSize = {0};
867 #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
868 FILE_STANDARD_INFO fileInfo;
869 if ( !GetFileInformationByHandleEx( hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo) ) )
871 return HRESULT_FROM_WIN32( GetLastError() );
873 fileSize = fileInfo.EndOfFile;
875 if ( !GetFileSizeEx( hFile.get(), &fileSize ) )
877 return HRESULT_FROM_WIN32( GetLastError() );
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 )
884 return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE );
887 // Need at least enough data to fill the standard header to be a valid TGA
888 if ( fileSize.LowPart < ( sizeof(TGA_HEADER) ) )
893 // Read the standard header (we don't need the file footer to parse the file)
894 uint8_t header[sizeof(TGA_HEADER)];
896 if ( !ReadFile( hFile.get(), header, sizeof(TGA_HEADER), &bytesRead, 0 ) )
898 return HRESULT_FROM_WIN32( GetLastError() );
902 return _DecodeTGAHeader( header, bytesRead, metadata, offset, 0 );
906 //-------------------------------------------------------------------------------------
907 // Load a TGA file in memory
908 //-------------------------------------------------------------------------------------
909 HRESULT LoadFromTGAMemory( LPCVOID pSource, size_t size, TexMetadata* metadata, ScratchImage& image )
911 if ( !pSource || size == 0 )
919 HRESULT hr = _DecodeTGAHeader( pSource, size, mdata, offset, &convFlags );
926 LPCVOID pPixels = reinterpret_cast<LPCVOID>( reinterpret_cast<const uint8_t*>(pSource) + offset );
929 size_t remaining = size - offset;
930 if ( remaining == 0 )
933 hr = image.Initialize2D( mdata.format, mdata.width, mdata.height, 1, 1 );
937 if ( convFlags & CONV_FLAGS_RLE )
939 hr = _UncompressPixels( pPixels, remaining, image.GetImage(0,0,0), convFlags );
943 hr = _CopyPixels( pPixels, remaining, image.GetImage(0,0,0), convFlags );
953 memcpy( metadata, &mdata, sizeof(TexMetadata) );
959 //-------------------------------------------------------------------------------------
960 // Load a TGA file from disk
961 //-------------------------------------------------------------------------------------
962 HRESULT LoadFromTGAFile( LPCWSTR szFile, TexMetadata* metadata, ScratchImage& image )
969 #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
970 ScopedHandle hFile( safe_handle( CreateFile2( szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0 ) ) );
972 ScopedHandle hFile( safe_handle( CreateFileW( szFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING,
973 FILE_FLAG_SEQUENTIAL_SCAN, 0 ) ) );
977 return HRESULT_FROM_WIN32( GetLastError() );
981 LARGE_INTEGER fileSize = {0};
983 #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
984 FILE_STANDARD_INFO fileInfo;
985 if ( !GetFileInformationByHandleEx( hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo) ) )
987 return HRESULT_FROM_WIN32( GetLastError() );
989 fileSize = fileInfo.EndOfFile;
991 if ( !GetFileSizeEx( hFile.get(), &fileSize ) )
993 return HRESULT_FROM_WIN32( GetLastError() );
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 )
1000 return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE );
1003 // Need at least enough data to fill the header to be a valid TGA
1004 if ( fileSize.LowPart < sizeof(TGA_HEADER) )
1010 uint8_t header[sizeof(TGA_HEADER)];
1011 DWORD bytesRead = 0;
1012 if ( !ReadFile( hFile.get(), header, sizeof(TGA_HEADER), &bytesRead, 0 ) )
1014 return HRESULT_FROM_WIN32( GetLastError() );
1018 DWORD convFlags = 0;
1020 HRESULT hr = _DecodeTGAHeader( header, bytesRead, mdata, offset, &convFlags );
1025 DWORD remaining = static_cast<DWORD>( fileSize.LowPart - offset );
1026 if ( remaining == 0 )
1029 if ( offset > sizeof(TGA_HEADER) )
1031 // Skip past the id string
1032 LARGE_INTEGER filePos = { static_cast<DWORD>(offset), 0 };
1033 if ( !SetFilePointerEx( hFile.get(), filePos, 0, FILE_BEGIN ) )
1035 return HRESULT_FROM_WIN32( GetLastError() );
1039 hr = image.Initialize2D( mdata.format, mdata.width, mdata.height, 1, 1 );
1043 assert( image.GetPixels() );
1045 if ( !(convFlags & (CONV_FLAGS_RLE | CONV_FLAGS_EXPAND | CONV_FLAGS_INVERTX)) && (convFlags & CONV_FLAGS_INVERTY) )
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 ) )
1051 return HRESULT_FROM_WIN32( GetLastError() );
1054 if ( bytesRead != image.GetPixelsSize() )
1060 switch( mdata.format )
1062 case DXGI_FORMAT_R8G8B8A8_UNORM:
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);
1070 uint8_t *pPixels = img->pixels;
1074 size_t rowPitch = img->rowPitch;
1076 // Scan for non-zero alpha channel
1077 bool nonzeroa = false;
1079 for( size_t h = 0; h < img->height; ++h )
1081 const uint32_t* sPtr = reinterpret_cast<const uint32_t*>( pPixels );
1083 for( size_t x=0; x < img->width; ++x )
1085 if ( (*sPtr) & 0xff000000 )
1097 pPixels += rowPitch;
1100 DWORD tflags = ( !nonzeroa ) ? TEXP_SCANLINE_SETALPHA : TEXP_SCANLINE_NONE;
1102 // Swizzle scanlines
1103 pPixels = img->pixels;
1105 for( size_t h = 0; h < img->height; ++h )
1107 _SwizzleScanline( pPixels, rowPitch, pPixels, rowPitch, mdata.format, tflags );
1109 pPixels += rowPitch;
1114 // If we start using DXGI_FORMAT_B8G8R8X8_UNORM or DXGI_FORMAT_B8G8R8A8_UNORM we need to check for a fully 0 alpha channel
1116 case DXGI_FORMAT_B5G5R5A1_UNORM:
1118 assert( image.GetImageCount() == 1 );
1119 const Image* img = image.GetImage(0,0,0);
1123 // Scan for non-zero alpha channel
1124 bool nonzeroa = false;
1126 const uint8_t *pPixels = img->pixels;
1130 size_t rowPitch = img->rowPitch;
1132 for( size_t h = 0; h < img->height; ++h )
1134 const uint16_t* sPtr = reinterpret_cast<const uint16_t*>( pPixels );
1136 for( size_t x=0; x < img->width; ++x )
1138 if ( *sPtr & 0x8000 )
1150 pPixels += rowPitch;
1153 // If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
1156 hr = _SetAlphaChannelToOpaque( img );
1164 else // RLE || EXPAND || INVERTX || !INVERTY
1166 std::unique_ptr<uint8_t[]> temp( new uint8_t[ remaining ] );
1170 return E_OUTOFMEMORY;
1173 if ( !ReadFile( hFile.get(), temp.get(), remaining, &bytesRead, 0 ) )
1176 return HRESULT_FROM_WIN32( GetLastError() );
1179 if ( bytesRead != remaining )
1185 if ( convFlags & CONV_FLAGS_RLE )
1187 hr = _UncompressPixels( temp.get(), remaining, image.GetImage(0,0,0), convFlags );
1191 hr = _CopyPixels( temp.get(), remaining, image.GetImage(0,0,0), convFlags );
1202 memcpy( metadata, &mdata, sizeof(TexMetadata) );
1208 //-------------------------------------------------------------------------------------
1209 // Save a TGA file to memory
1210 //-------------------------------------------------------------------------------------
1211 HRESULT SaveToTGAMemory( const Image& image, Blob& blob )
1213 if ( !image.pixels )
1216 TGA_HEADER tga_header;
1217 DWORD convFlags = 0;
1218 HRESULT hr = _EncodeTGAHeader( image, tga_header, convFlags );
1224 // Determine memory required for image data
1225 size_t rowPitch, slicePitch;
1226 if ( convFlags & CONV_FLAGS_888 )
1228 rowPitch = image.width * 3;
1229 slicePitch = image.height * rowPitch;
1233 ComputePitch( image.format, image.width, image.height, rowPitch, slicePitch, CP_FLAGS_NONE );
1236 hr = blob.Initialize( sizeof(TGA_HEADER) + slicePitch );
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);
1246 const uint8_t* pPixels = reinterpret_cast<const uint8_t*>( image.pixels );
1249 for( size_t y = 0; y < image.height; ++y )
1252 if ( convFlags & CONV_FLAGS_888 )
1254 _Copy24bppScanline( dPtr, rowPitch, pPixels, image.rowPitch );
1256 else if ( convFlags & CONV_FLAGS_SWIZZLE )
1258 _SwizzleScanline( dPtr, rowPitch, pPixels, image.rowPitch, image.format, TEXP_SCANLINE_NONE );
1262 _CopyScanline( dPtr, rowPitch, pPixels, image.rowPitch, image.format, TEXP_SCANLINE_NONE );
1266 pPixels += image.rowPitch;
1273 //-------------------------------------------------------------------------------------
1274 // Save a TGA file to disk
1275 //-------------------------------------------------------------------------------------
1276 HRESULT SaveToTGAFile( const Image& image, LPCWSTR szFile )
1279 return E_INVALIDARG;
1281 if ( !image.pixels )
1284 TGA_HEADER tga_header;
1285 DWORD convFlags = 0;
1286 HRESULT hr = _EncodeTGAHeader( image, tga_header, convFlags );
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 ) ) );
1294 ScopedHandle hFile( safe_handle( CreateFileW( szFile, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0 ) ) );
1298 return HRESULT_FROM_WIN32( GetLastError() );
1301 // Determine size for TGA pixel data
1302 size_t rowPitch, slicePitch;
1303 if ( convFlags & CONV_FLAGS_888 )
1305 rowPitch = image.width * 3;
1306 slicePitch = image.height * rowPitch;
1310 ComputePitch( image.format, image.width, image.height, rowPitch, slicePitch, CP_FLAGS_NONE );
1313 if ( slicePitch < 65535 )
1315 // For small images, it is better to create an in-memory file and write it out
1318 hr = SaveToTGAMemory( image, blob );
1323 const DWORD bytesToWrite = static_cast<DWORD>( blob.GetBufferSize() );
1325 if ( !WriteFile( hFile.get(), blob.GetBufferPointer(), bytesToWrite,
1326 &bytesWritten, 0 ) )
1328 return HRESULT_FROM_WIN32( GetLastError() );
1331 if ( bytesWritten != bytesToWrite )
1338 // Otherwise, write the image one scanline at a time...
1339 std::unique_ptr<uint8_t[]> temp( new uint8_t[ rowPitch ] );
1341 return E_OUTOFMEMORY;
1345 if ( !WriteFile( hFile.get(), &tga_header, sizeof(TGA_HEADER), &bytesWritten, 0 ) )
1347 return HRESULT_FROM_WIN32( GetLastError() );
1350 if ( bytesWritten != sizeof(TGA_HEADER) )
1354 const uint8_t* pPixels = reinterpret_cast<const uint8_t*>( image.pixels );
1356 for( size_t y = 0; y < image.height; ++y )
1359 if ( convFlags & CONV_FLAGS_888 )
1361 _Copy24bppScanline( temp.get(), rowPitch, pPixels, image.rowPitch );
1363 else if ( convFlags & CONV_FLAGS_SWIZZLE )
1365 _SwizzleScanline( temp.get(), rowPitch, pPixels, image.rowPitch, image.format, TEXP_SCANLINE_NONE );
1369 _CopyScanline( temp.get(), rowPitch, pPixels, image.rowPitch, image.format, TEXP_SCANLINE_NONE );
1372 pPixels += image.rowPitch;
1374 if ( !WriteFile( hFile.get(), temp.get(), static_cast<DWORD>( rowPitch ), &bytesWritten, 0 ) )
1376 return HRESULT_FROM_WIN32( GetLastError() );
1379 if ( bytesWritten != rowPitch )