crossxtex/DirectXTex/DirectXTexTGA.cpp

1447 lines
44 KiB
C++
Raw Normal View History

2016-08-22 18:26:36 +00:00
//-------------------------------------------------------------------------------------
// DirectXTexTGA.cpp
//
// DirectX Texture Library - Targa Truevision (TGA) file format reader/writer
//
// Copyright (c) Microsoft Corporation. All rights reserved.
2018-02-24 06:24:46 +00:00
// Licensed under the MIT License.
2016-08-22 18:26:36 +00:00
//
// http://go.microsoft.com/fwlink/?LinkId=248926
//-------------------------------------------------------------------------------------
2018-06-13 01:07:34 +00:00
#include "DirectXTexp.h"
2016-08-22 18:26:36 +00:00
//
// The implementation here has the following limitations:
// * Does not support files that contain color maps (these are rare in practice)
// * Interleaved files are not supported (deprecated aspect of TGA format)
// * Only supports 8-bit grayscale; 16-, 24-, and 32-bit truecolor images
// * Always writes uncompressed files (i.e. can read RLE compression, but does not write it)
//
2016-09-09 02:09:46 +00:00
using namespace DirectX;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
namespace
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
enum TGAImageType
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
TGA_NO_IMAGE = 0,
TGA_COLOR_MAPPED = 1,
TGA_TRUECOLOR = 2,
TGA_BLACK_AND_WHITE = 3,
TGA_COLOR_MAPPED_RLE = 9,
TGA_TRUECOLOR_RLE = 10,
TGA_BLACK_AND_WHITE_RLE = 11,
};
enum TGADescriptorFlags
{
TGA_FLAGS_INVERTX = 0x10,
TGA_FLAGS_INVERTY = 0x20,
TGA_FLAGS_INTERLEAVED_2WAY = 0x40, // Deprecated
TGA_FLAGS_INTERLEAVED_4WAY = 0x80, // Deprecated
};
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
#pragma pack(push,1)
struct TGA_HEADER
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
uint8_t bIDLength;
uint8_t bColorMapType;
uint8_t bImageType;
uint16_t wColorMapFirst;
uint16_t wColorMapLength;
uint8_t bColorMapSize;
uint16_t wXOrigin;
uint16_t wYOrigin;
uint16_t wWidth;
uint16_t wHeight;
uint8_t bBitsPerPixel;
uint8_t bDescriptor;
};
struct TGA_FOOTER
{
uint16_t dwExtensionOffset;
uint16_t dwDeveloperOffset;
char Signature[18];
};
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
struct TGA_EXTENSION
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
uint16_t wSize;
char szAuthorName[41];
char szAuthorComment[324];
uint16_t wStampMonth;
uint16_t wStampDay;
uint16_t wStampYear;
uint16_t wStampHour;
uint16_t wStampMinute;
uint16_t wStampSecond;
char szJobName[41];
uint16_t wJobHour;
uint16_t wJobMinute;
uint16_t wJobSecond;
char szSoftwareId[41];
uint16_t wVersionNumber;
uint8_t bVersionLetter;
uint32_t dwKeyColor;
uint16_t wPixelNumerator;
uint16_t wPixelDenominator;
uint16_t wGammaNumerator;
uint16_t wGammaDenominator;
uint32_t dwColorOffset;
uint32_t dwStampOffset;
uint32_t dwScanOffset;
uint8_t bAttributesType;
};
#pragma pack(pop)
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
enum CONVERSION_FLAGS
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
CONV_FLAGS_NONE = 0x0,
CONV_FLAGS_EXPAND = 0x1, // Conversion requires expanded pixel size
CONV_FLAGS_INVERTX = 0x2, // If set, scanlines are right-to-left
CONV_FLAGS_INVERTY = 0x4, // If set, scanlines are top-to-bottom
CONV_FLAGS_RLE = 0x8, // Source data is RLE compressed
CONV_FLAGS_SWIZZLE = 0x10000, // Swizzle BGR<->RGB data
CONV_FLAGS_888 = 0x20000, // 24bpp format
};
//-------------------------------------------------------------------------------------
// Decodes TGA header
//-------------------------------------------------------------------------------------
HRESULT DecodeTGAHeader(
_In_reads_bytes_(size) const void* pSource,
size_t size,
_Out_ TexMetadata& metadata,
size_t& offset,
_Inout_opt_ DWORD* convFlags)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (!pSource)
return E_INVALIDARG;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
memset(&metadata, 0, sizeof(TexMetadata));
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (size < sizeof(TGA_HEADER))
{
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
2016-08-22 18:26:36 +00:00
}
2018-03-29 19:00:16 +00:00
auto pHeader = static_cast<const TGA_HEADER*>(pSource);
2016-09-09 02:09:46 +00:00
if (pHeader->bColorMapType != 0
|| pHeader->wColorMapLength != 0)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
if (pHeader->bDescriptor & (TGA_FLAGS_INTERLEAVED_2WAY | TGA_FLAGS_INTERLEAVED_4WAY))
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
if (!pHeader->wWidth || !pHeader->wHeight)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
switch (pHeader->bImageType)
{
case TGA_TRUECOLOR:
case TGA_TRUECOLOR_RLE:
switch (pHeader->bBitsPerPixel)
{
case 16:
metadata.format = DXGI_FORMAT_B5G5R5A1_UNORM;
break;
case 24:
metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM;
if (convFlags)
*convFlags |= CONV_FLAGS_EXPAND;
// We could use DXGI_FORMAT_B8G8R8X8_UNORM, but we prefer DXGI 1.0 formats
break;
case 32:
metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM;
// We could use DXGI_FORMAT_B8G8R8A8_UNORM, but we prefer DXGI 1.0 formats
break;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (convFlags && (pHeader->bImageType == TGA_TRUECOLOR_RLE))
{
*convFlags |= CONV_FLAGS_RLE;
}
break;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
case TGA_BLACK_AND_WHITE:
case TGA_BLACK_AND_WHITE_RLE:
switch (pHeader->bBitsPerPixel)
{
case 8:
metadata.format = DXGI_FORMAT_R8_UNORM;
break;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
default:
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (convFlags && (pHeader->bImageType == TGA_BLACK_AND_WHITE_RLE))
{
*convFlags |= CONV_FLAGS_RLE;
}
break;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
case TGA_NO_IMAGE:
case TGA_COLOR_MAPPED:
case TGA_COLOR_MAPPED_RLE:
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
default:
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
metadata.width = pHeader->wWidth;
metadata.height = pHeader->wHeight;
metadata.depth = metadata.arraySize = metadata.mipLevels = 1;
metadata.dimension = TEX_DIMENSION_TEXTURE2D;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (convFlags)
{
if (pHeader->bDescriptor & TGA_FLAGS_INVERTX)
*convFlags |= CONV_FLAGS_INVERTX;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (pHeader->bDescriptor & TGA_FLAGS_INVERTY)
*convFlags |= CONV_FLAGS_INVERTY;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
offset = sizeof(TGA_HEADER);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (pHeader->bIDLength != 0)
{
offset += pHeader->bIDLength;
}
return S_OK;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
//-------------------------------------------------------------------------------------
// Set alpha for images with all 0 alpha channel
//-------------------------------------------------------------------------------------
HRESULT SetAlphaChannelToOpaque(_In_ const Image* image)
{
assert(image);
2016-08-22 18:26:36 +00:00
2018-03-29 19:00:16 +00:00
uint8_t* pPixels = image->pixels;
2016-09-09 02:09:46 +00:00
if (!pPixels)
return E_POINTER;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
for (size_t y = 0; y < image->height; ++y)
{
_CopyScanline(pPixels, image->rowPitch, pPixels, image->rowPitch, image->format, TEXP_SCANLINE_SETALPHA);
pPixels += image->rowPitch;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
return S_OK;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
//-------------------------------------------------------------------------------------
// Uncompress pixel data from a TGA into the target image
//-------------------------------------------------------------------------------------
HRESULT UncompressPixels(
_In_reads_bytes_(size) const void* pSource,
size_t size,
_In_ const Image* image,
_In_ DWORD convFlags)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
assert(pSource && size > 0);
if (!image || !image->pixels)
return E_POINTER;
// Compute TGA image data pitch
size_t rowPitch;
if (convFlags & CONV_FLAGS_EXPAND)
{
rowPitch = image->width * 3;
}
else
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
size_t slicePitch;
HRESULT hr = ComputePitch(image->format, image->width, image->height, rowPitch, slicePitch, CP_FLAGS_NONE);
if (FAILED(hr))
return hr;
2016-09-09 02:09:46 +00:00
}
2016-08-22 18:26:36 +00:00
2018-03-29 19:00:16 +00:00
auto sPtr = static_cast<const uint8_t*>(pSource);
2016-09-09 02:09:46 +00:00
const uint8_t* endPtr = sPtr + size;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
switch (image->format)
{
//--------------------------------------------------------------------------- 8-bit
case DXGI_FORMAT_R8_UNORM:
for (size_t y = 0; y < image->height; ++y)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
size_t offset = ((convFlags & CONV_FLAGS_INVERTX) ? (image->width - 1) : 0);
assert(offset < rowPitch);
2018-03-29 19:00:16 +00:00
uint8_t* dPtr = image->pixels
2016-09-09 02:09:46 +00:00
+ (image->rowPitch * ((convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1)))
+ offset;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
for (size_t x = 0; x < image->width; )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (sPtr >= endPtr)
2016-08-22 18:26:36 +00:00
return E_FAIL;
2016-09-09 02:09:46 +00:00
if (*sPtr & 0x80)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
// Repeat
2018-05-02 07:20:07 +00:00
size_t j = size_t(*sPtr & 0x7F) + 1;
2016-09-09 02:09:46 +00:00
if (++sPtr >= endPtr)
2016-08-22 18:26:36 +00:00
return E_FAIL;
2016-09-09 02:09:46 +00:00
for (; j > 0; --j, ++x)
{
if (x >= image->width)
return E_FAIL;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
*dPtr = *sPtr;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_INVERTX)
--dPtr;
else
++dPtr;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
++sPtr;
}
else
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
// Literal
2018-05-02 07:20:07 +00:00
size_t j = size_t(*sPtr & 0x7F) + 1;
2016-09-09 02:09:46 +00:00
++sPtr;
if (sPtr + j > endPtr)
2016-08-22 18:26:36 +00:00
return E_FAIL;
2016-09-09 02:09:46 +00:00
for (; j > 0; --j, ++x)
{
if (x >= image->width)
return E_FAIL;
*dPtr = *(sPtr++);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_INVERTX)
--dPtr;
else
++dPtr;
}
2016-08-22 18:26:36 +00:00
}
}
}
2016-09-09 02:09:46 +00:00
break;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
//-------------------------------------------------------------------------- 16-bit
case DXGI_FORMAT_B5G5R5A1_UNORM:
2016-08-22 18:26:36 +00:00
{
bool nonzeroa = false;
2016-09-09 02:09:46 +00:00
for (size_t y = 0; y < image->height; ++y)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
size_t offset = ((convFlags & CONV_FLAGS_INVERTX) ? (image->width - 1) : 0);
assert(offset * 2 < rowPitch);
2016-08-22 18:26:36 +00:00
2018-03-29 19:00:16 +00:00
auto dPtr = reinterpret_cast<uint16_t*>(image->pixels
2016-09-09 02:09:46 +00:00
+ (image->rowPitch * ((convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1))))
+ offset;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
for (size_t x = 0; x < image->width; )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (sPtr >= endPtr)
2016-08-22 18:26:36 +00:00
return E_FAIL;
2016-09-09 02:09:46 +00:00
if (*sPtr & 0x80)
2016-08-22 18:26:36 +00:00
{
// Repeat
2018-05-02 07:20:07 +00:00
size_t j = size_t(*sPtr & 0x7F) + 1;
2016-08-22 18:26:36 +00:00
++sPtr;
2016-09-09 02:09:46 +00:00
if (sPtr + 1 >= endPtr)
2016-08-22 18:26:36 +00:00
return E_FAIL;
2018-06-13 01:07:34 +00:00
auto t = static_cast<uint16_t>(unsigned(*sPtr) | (*(sPtr + 1u) << 8));
2016-09-09 02:09:46 +00:00
if (t & 0x8000)
2016-08-22 18:26:36 +00:00
nonzeroa = true;
sPtr += 2;
2016-09-09 02:09:46 +00:00
for (; j > 0; --j, ++x)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (x >= image->width)
2016-08-22 18:26:36 +00:00
return E_FAIL;
*dPtr = t;
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_INVERTX)
2016-08-22 18:26:36 +00:00
--dPtr;
else
++dPtr;
}
}
else
{
// Literal
2018-05-02 07:20:07 +00:00
size_t j = size_t(*sPtr & 0x7F) + 1;
2016-08-22 18:26:36 +00:00
++sPtr;
2016-09-09 02:09:46 +00:00
if (sPtr + (j * 2) > endPtr)
2016-08-22 18:26:36 +00:00
return E_FAIL;
2016-09-09 02:09:46 +00:00
for (; j > 0; --j, ++x)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (x >= image->width)
2016-08-22 18:26:36 +00:00
return E_FAIL;
2018-06-13 01:07:34 +00:00
auto t = static_cast<uint16_t>(unsigned(*sPtr) | (*(sPtr + 1u) << 8));
2016-09-09 02:09:46 +00:00
if (t & 0x8000)
2016-08-22 18:26:36 +00:00
nonzeroa = true;
sPtr += 2;
*dPtr = t;
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_INVERTX)
2016-08-22 18:26:36 +00:00
--dPtr;
else
++dPtr;
}
}
}
}
// If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
2016-09-09 02:09:46 +00:00
if (!nonzeroa)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
HRESULT hr = SetAlphaChannelToOpaque(image);
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
return hr;
}
}
break;
2016-09-09 02:09:46 +00:00
//----------------------------------------------------------------------- 24/32-bit
case DXGI_FORMAT_R8G8B8A8_UNORM:
2016-08-22 18:26:36 +00:00
{
bool nonzeroa = false;
2016-09-09 02:09:46 +00:00
for (size_t y = 0; y < image->height; ++y)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
size_t offset = ((convFlags & CONV_FLAGS_INVERTX) ? (image->width - 1) : 0);
2016-08-22 18:26:36 +00:00
2018-03-29 19:00:16 +00:00
auto dPtr = reinterpret_cast<uint32_t*>(image->pixels
2016-09-09 02:09:46 +00:00
+ (image->rowPitch * ((convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1))))
+ offset;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
for (size_t x = 0; x < image->width; )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (sPtr >= endPtr)
2016-08-22 18:26:36 +00:00
return E_FAIL;
2016-09-09 02:09:46 +00:00
if (*sPtr & 0x80)
2016-08-22 18:26:36 +00:00
{
// Repeat
2018-05-02 07:20:07 +00:00
size_t j = size_t(*sPtr & 0x7F) + 1;
2016-08-22 18:26:36 +00:00
++sPtr;
DWORD t;
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_EXPAND)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
assert(offset * 3 < rowPitch);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (sPtr + 2 >= endPtr)
2016-08-22 18:26:36 +00:00
return E_FAIL;
// BGR -> RGBA
2016-09-09 02:09:46 +00:00
t = (*sPtr << 16) | (*(sPtr + 1) << 8) | (*(sPtr + 2)) | 0xFF000000;
2016-08-22 18:26:36 +00:00
sPtr += 3;
nonzeroa = true;
}
else
{
2016-09-09 02:09:46 +00:00
assert(offset * 4 < rowPitch);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (sPtr + 3 >= endPtr)
2016-08-22 18:26:36 +00:00
return E_FAIL;
// BGRA -> RGBA
2016-09-09 02:09:46 +00:00
t = (*sPtr << 16) | (*(sPtr + 1) << 8) | (*(sPtr + 2)) | (*(sPtr + 3) << 24);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (*(sPtr + 3) > 0)
2016-08-22 18:26:36 +00:00
nonzeroa = true;
sPtr += 4;
}
2016-09-09 02:09:46 +00:00
for (; j > 0; --j, ++x)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (x >= image->width)
2016-08-22 18:26:36 +00:00
return E_FAIL;
*dPtr = t;
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_INVERTX)
2016-08-22 18:26:36 +00:00
--dPtr;
else
++dPtr;
}
}
else
{
// Literal
2018-05-02 07:20:07 +00:00
size_t j = size_t(*sPtr & 0x7F) + 1;
2016-08-22 18:26:36 +00:00
++sPtr;
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_EXPAND)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (sPtr + (j * 3) > endPtr)
2016-08-22 18:26:36 +00:00
return E_FAIL;
}
else
{
2016-09-09 02:09:46 +00:00
if (sPtr + (j * 4) > endPtr)
2016-08-22 18:26:36 +00:00
return E_FAIL;
}
2016-09-09 02:09:46 +00:00
for (; j > 0; --j, ++x)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (x >= image->width)
2016-08-22 18:26:36 +00:00
return E_FAIL;
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_EXPAND)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
assert(offset * 3 < rowPitch);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (sPtr + 2 >= endPtr)
2016-08-22 18:26:36 +00:00
return E_FAIL;
// BGR -> RGBA
2016-09-09 02:09:46 +00:00
*dPtr = (*sPtr << 16) | (*(sPtr + 1) << 8) | (*(sPtr + 2)) | 0xFF000000;
2016-08-22 18:26:36 +00:00
sPtr += 3;
nonzeroa = true;
}
else
{
2016-09-09 02:09:46 +00:00
assert(offset * 4 < rowPitch);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (sPtr + 3 >= endPtr)
2016-08-22 18:26:36 +00:00
return E_FAIL;
// BGRA -> RGBA
2016-09-09 02:09:46 +00:00
*dPtr = (*sPtr << 16) | (*(sPtr + 1) << 8) | (*(sPtr + 2)) | (*(sPtr + 3) << 24);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (*(sPtr + 3) > 0)
2016-08-22 18:26:36 +00:00
nonzeroa = true;
sPtr += 4;
}
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_INVERTX)
2016-08-22 18:26:36 +00:00
--dPtr;
else
++dPtr;
}
}
}
}
// If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
2016-09-09 02:09:46 +00:00
if (!nonzeroa)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
HRESULT hr = SetAlphaChannelToOpaque(image);
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
return hr;
}
}
break;
2016-09-09 02:09:46 +00:00
//---------------------------------------------------------------------------------
default:
return E_FAIL;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
return S_OK;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
//-------------------------------------------------------------------------------------
// Copies pixel data from a TGA into the target image
//-------------------------------------------------------------------------------------
HRESULT CopyPixels(
_In_reads_bytes_(size) const void* pSource,
size_t size,
_In_ const Image* image,
_In_ DWORD convFlags)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
assert(pSource && size > 0);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (!image || !image->pixels)
return E_POINTER;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
// Compute TGA image data pitch
size_t rowPitch;
if (convFlags & CONV_FLAGS_EXPAND)
{
rowPitch = image->width * 3;
}
else
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
size_t slicePitch;
HRESULT hr = ComputePitch(image->format, image->width, image->height, rowPitch, slicePitch, CP_FLAGS_NONE);
if (FAILED(hr))
return hr;
2016-09-09 02:09:46 +00:00
}
2016-08-22 18:26:36 +00:00
2018-03-29 19:00:16 +00:00
auto sPtr = static_cast<const uint8_t*>(pSource);
2016-09-09 02:09:46 +00:00
const uint8_t* endPtr = sPtr + size;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
switch (image->format)
{
//--------------------------------------------------------------------------- 8-bit
case DXGI_FORMAT_R8_UNORM:
for (size_t y = 0; y < image->height; ++y)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
size_t offset = ((convFlags & CONV_FLAGS_INVERTX) ? (image->width - 1) : 0);
assert(offset < rowPitch);
2018-03-29 19:00:16 +00:00
uint8_t* dPtr = image->pixels
2016-09-09 02:09:46 +00:00
+ (image->rowPitch * ((convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1)))
+ offset;
for (size_t x = 0; x < image->width; ++x)
{
if (sPtr >= endPtr)
return E_FAIL;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
*dPtr = *(sPtr++);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_INVERTX)
--dPtr;
else
++dPtr;
}
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
break;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
//-------------------------------------------------------------------------- 16-bit
case DXGI_FORMAT_B5G5R5A1_UNORM:
2016-08-22 18:26:36 +00:00
{
bool nonzeroa = false;
2016-09-09 02:09:46 +00:00
for (size_t y = 0; y < image->height; ++y)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
size_t offset = ((convFlags & CONV_FLAGS_INVERTX) ? (image->width - 1) : 0);
assert(offset * 2 < rowPitch);
2016-08-22 18:26:36 +00:00
2018-03-29 19:00:16 +00:00
auto dPtr = reinterpret_cast<uint16_t*>(image->pixels
2016-09-09 02:09:46 +00:00
+ (image->rowPitch * ((convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1))))
+ offset;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
for (size_t x = 0; x < image->width; ++x)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (sPtr + 1 >= endPtr)
2016-08-22 18:26:36 +00:00
return E_FAIL;
2018-06-13 01:07:34 +00:00
auto t = static_cast<uint16_t>(unsigned(*sPtr) | (*(sPtr + 1u) << 8));
2016-08-22 18:26:36 +00:00
sPtr += 2;
*dPtr = t;
2016-09-09 02:09:46 +00:00
if (t & 0x8000)
2016-08-22 18:26:36 +00:00
nonzeroa = true;
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_INVERTX)
2016-08-22 18:26:36 +00:00
--dPtr;
else
++dPtr;
}
}
// If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
2016-09-09 02:09:46 +00:00
if (!nonzeroa)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
HRESULT hr = SetAlphaChannelToOpaque(image);
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
return hr;
}
}
break;
2016-09-09 02:09:46 +00:00
//----------------------------------------------------------------------- 24/32-bit
case DXGI_FORMAT_R8G8B8A8_UNORM:
2016-08-22 18:26:36 +00:00
{
bool nonzeroa = false;
2016-09-09 02:09:46 +00:00
for (size_t y = 0; y < image->height; ++y)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
size_t offset = ((convFlags & CONV_FLAGS_INVERTX) ? (image->width - 1) : 0);
2016-08-22 18:26:36 +00:00
2018-03-29 19:00:16 +00:00
auto dPtr = reinterpret_cast<uint32_t*>(image->pixels
2016-09-09 02:09:46 +00:00
+ (image->rowPitch * ((convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1))))
+ offset;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
for (size_t x = 0; x < image->width; ++x)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_EXPAND)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
assert(offset * 3 < rowPitch);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (sPtr + 2 >= endPtr)
2016-08-22 18:26:36 +00:00
return E_FAIL;
// BGR -> RGBA
2016-09-09 02:09:46 +00:00
*dPtr = (*sPtr << 16) | (*(sPtr + 1) << 8) | (*(sPtr + 2)) | 0xFF000000;
2016-08-22 18:26:36 +00:00
sPtr += 3;
nonzeroa = true;
}
else
{
2016-09-09 02:09:46 +00:00
assert(offset * 4 < rowPitch);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (sPtr + 3 >= endPtr)
2016-08-22 18:26:36 +00:00
return E_FAIL;
// BGRA -> RGBA
2016-09-09 02:09:46 +00:00
*dPtr = (*sPtr << 16) | (*(sPtr + 1) << 8) | (*(sPtr + 2)) | (*(sPtr + 3) << 24);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (*(sPtr + 3) > 0)
2016-08-22 18:26:36 +00:00
nonzeroa = true;
sPtr += 4;
}
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_INVERTX)
2016-08-22 18:26:36 +00:00
--dPtr;
else
++dPtr;
}
}
// If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
2016-09-09 02:09:46 +00:00
if (!nonzeroa)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
HRESULT hr = SetAlphaChannelToOpaque(image);
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
return hr;
}
}
break;
2016-09-09 02:09:46 +00:00
//---------------------------------------------------------------------------------
default:
return E_FAIL;
}
return S_OK;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
//-------------------------------------------------------------------------------------
// Encodes TGA file header
//-------------------------------------------------------------------------------------
HRESULT EncodeTGAHeader(_In_ const Image& image, _Out_ TGA_HEADER& header, _Inout_ DWORD& convFlags)
{
memset(&header, 0, sizeof(TGA_HEADER));
2016-08-22 18:26:36 +00:00
2018-07-31 19:24:34 +00:00
if ((image.width > UINT16_MAX)
|| (image.height > UINT16_MAX))
2016-09-09 02:09:46 +00:00
{
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
header.wWidth = static_cast<uint16_t>(image.width);
header.wHeight = static_cast<uint16_t>(image.height);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
switch (image.format)
{
case DXGI_FORMAT_R8G8B8A8_UNORM:
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
header.bImageType = TGA_TRUECOLOR;
header.bBitsPerPixel = 32;
header.bDescriptor = TGA_FLAGS_INVERTY | 8;
convFlags |= CONV_FLAGS_SWIZZLE;
break;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
case DXGI_FORMAT_B8G8R8A8_UNORM:
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
header.bImageType = TGA_TRUECOLOR;
header.bBitsPerPixel = 32;
header.bDescriptor = TGA_FLAGS_INVERTY | 8;
break;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
case DXGI_FORMAT_B8G8R8X8_UNORM:
case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
header.bImageType = TGA_TRUECOLOR;
header.bBitsPerPixel = 24;
header.bDescriptor = TGA_FLAGS_INVERTY;
convFlags |= CONV_FLAGS_888;
break;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
case DXGI_FORMAT_R8_UNORM:
case DXGI_FORMAT_A8_UNORM:
header.bImageType = TGA_BLACK_AND_WHITE;
header.bBitsPerPixel = 8;
header.bDescriptor = TGA_FLAGS_INVERTY;
break;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
case DXGI_FORMAT_B5G5R5A1_UNORM:
header.bImageType = TGA_TRUECOLOR;
header.bBitsPerPixel = 16;
header.bDescriptor = TGA_FLAGS_INVERTY | 1;
break;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
default:
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
return S_OK;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
//-------------------------------------------------------------------------------------
// Copies BGRX data to form BGR 24bpp data
//-------------------------------------------------------------------------------------
2016-08-22 18:26:36 +00:00
#pragma warning(suppress: 6001 6101) // In the case where outSize is insufficient we do not write to pDestination
2016-09-09 02:09:46 +00:00
void Copy24bppScanline(
_Out_writes_bytes_(outSize) void* pDestination,
_In_ size_t outSize,
_In_reads_bytes_(inSize) const void* pSource,
_In_ size_t inSize)
{
assert(pDestination && outSize > 0);
assert(pSource && inSize > 0);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
assert(pDestination != pSource);
2016-08-22 18:26:36 +00:00
2018-03-29 19:00:16 +00:00
const uint32_t * __restrict sPtr = static_cast<const uint32_t*>(pSource);
uint8_t * __restrict dPtr = static_cast<uint8_t*>(pDestination);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (inSize >= 4 && outSize >= 3)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
const uint8_t* endPtr = dPtr + outSize;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
for (size_t count = 0; count < (inSize - 3); count += 4)
{
uint32_t t = *(sPtr++);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (dPtr + 3 > endPtr)
return;
*(dPtr++) = uint8_t(t & 0xFF); // Blue
*(dPtr++) = uint8_t((t & 0xFF00) >> 8); // Green
*(dPtr++) = uint8_t((t & 0xFF0000) >> 16); // Red
}
2016-08-22 18:26:36 +00:00
}
}
}
//=====================================================================================
// Entry-points
//=====================================================================================
//-------------------------------------------------------------------------------------
// Obtain metadata from TGA file in memory/on disk
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
2016-09-09 02:09:46 +00:00
HRESULT DirectX::GetMetadataFromTGAMemory(
const void* pSource,
size_t size,
2018-03-16 19:52:21 +00:00
TexMetadata& metadata)
2016-08-22 18:26:36 +00:00
{
2018-03-16 19:52:21 +00:00
if (!pSource || size == 0)
2016-08-22 18:26:36 +00:00
return E_INVALIDARG;
size_t offset;
2018-06-13 01:07:34 +00:00
return DecodeTGAHeader(pSource, size, metadata, offset, nullptr);
2016-08-22 18:26:36 +00:00
}
_Use_decl_annotations_
2016-09-09 02:09:46 +00:00
HRESULT DirectX::GetMetadataFromTGAFile(const wchar_t* szFile, TexMetadata& metadata)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (!szFile)
2016-08-22 18:26:36 +00:00
return E_INVALIDARG;
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
2016-09-09 02:09:46 +00:00
ScopedHandle hFile(safe_handle(CreateFile2(szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr)));
2016-08-22 18:26:36 +00:00
#else
2016-09-09 02:09:46 +00:00
ScopedHandle hFile(safe_handle(CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN, nullptr)));
2016-08-22 18:26:36 +00:00
#endif
2016-09-09 02:09:46 +00:00
if (!hFile)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(GetLastError());
2016-08-22 18:26:36 +00:00
}
// Get the file size
FILE_STANDARD_INFO fileInfo;
2016-09-09 02:09:46 +00:00
if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo)))
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(GetLastError());
2016-08-22 18:26:36 +00:00
}
// File is too big for 32-bit allocation, so reject read (4 GB should be plenty large enough for a valid TGA file)
2016-09-09 02:09:46 +00:00
if (fileInfo.EndOfFile.HighPart > 0)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(ERROR_FILE_TOO_LARGE);
2016-08-22 18:26:36 +00:00
}
// Need at least enough data to fill the standard header to be a valid TGA
2016-09-09 02:09:46 +00:00
if (fileInfo.EndOfFile.LowPart < (sizeof(TGA_HEADER)))
2016-08-22 18:26:36 +00:00
{
return E_FAIL;
}
// Read the standard header (we don't need the file footer to parse the file)
2018-03-29 19:00:16 +00:00
uint8_t header[sizeof(TGA_HEADER)] = {};
2016-08-22 18:26:36 +00:00
DWORD bytesRead = 0;
2016-09-09 02:09:46 +00:00
if (!ReadFile(hFile.get(), header, sizeof(TGA_HEADER), &bytesRead, nullptr))
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(GetLastError());
2016-08-22 18:26:36 +00:00
}
size_t offset;
2018-06-13 01:07:34 +00:00
return DecodeTGAHeader(header, bytesRead, metadata, offset, nullptr);
2016-08-22 18:26:36 +00:00
}
//-------------------------------------------------------------------------------------
// Load a TGA file in memory
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
2016-09-09 02:09:46 +00:00
HRESULT DirectX::LoadFromTGAMemory(
const void* pSource,
size_t size,
TexMetadata* metadata,
ScratchImage& image)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (!pSource || size == 0)
2016-08-22 18:26:36 +00:00
return E_INVALIDARG;
image.Release();
size_t offset;
DWORD convFlags = 0;
TexMetadata mdata;
2016-09-09 02:09:46 +00:00
HRESULT hr = DecodeTGAHeader(pSource, size, mdata, offset, &convFlags);
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
return hr;
2016-09-09 02:09:46 +00:00
if (offset > size)
2016-08-22 18:26:36 +00:00
return E_FAIL;
2018-03-29 19:00:16 +00:00
const void* pPixels = static_cast<const uint8_t*>(pSource) + offset;
2016-08-22 18:26:36 +00:00
size_t remaining = size - offset;
2016-09-09 02:09:46 +00:00
if (remaining == 0)
2016-08-22 18:26:36 +00:00
return E_FAIL;
2016-09-09 02:09:46 +00:00
hr = image.Initialize2D(mdata.format, mdata.width, mdata.height, 1, 1);
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
return hr;
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_RLE)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
hr = UncompressPixels(pPixels, remaining, image.GetImage(0, 0, 0), convFlags);
2016-08-22 18:26:36 +00:00
}
else
{
2016-09-09 02:09:46 +00:00
hr = CopyPixels(pPixels, remaining, image.GetImage(0, 0, 0), convFlags);
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
{
image.Release();
return hr;
}
2016-09-09 02:09:46 +00:00
if (metadata)
memcpy(metadata, &mdata, sizeof(TexMetadata));
2016-08-22 18:26:36 +00:00
return S_OK;
}
//-------------------------------------------------------------------------------------
// Load a TGA file from disk
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
2016-09-09 02:09:46 +00:00
HRESULT DirectX::LoadFromTGAFile(
const wchar_t* szFile,
TexMetadata* metadata,
ScratchImage& image)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (!szFile)
2016-08-22 18:26:36 +00:00
return E_INVALIDARG;
image.Release();
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
2016-09-09 02:09:46 +00:00
ScopedHandle hFile(safe_handle(CreateFile2(szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr)));
2016-08-22 18:26:36 +00:00
#else
2016-09-09 02:09:46 +00:00
ScopedHandle hFile(safe_handle(CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN, nullptr)));
2016-08-22 18:26:36 +00:00
#endif
2016-09-09 02:09:46 +00:00
if (!hFile)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(GetLastError());
2016-08-22 18:26:36 +00:00
}
// Get the file size
FILE_STANDARD_INFO fileInfo;
2016-09-09 02:09:46 +00:00
if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo)))
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(GetLastError());
2016-08-22 18:26:36 +00:00
}
// File is too big for 32-bit allocation, so reject read (4 GB should be plenty large enough for a valid TGA file)
2016-09-09 02:09:46 +00:00
if (fileInfo.EndOfFile.HighPart > 0)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(ERROR_FILE_TOO_LARGE);
2016-08-22 18:26:36 +00:00
}
// Need at least enough data to fill the header to be a valid TGA
2016-09-09 02:09:46 +00:00
if (fileInfo.EndOfFile.LowPart < sizeof(TGA_HEADER))
2016-08-22 18:26:36 +00:00
{
return E_FAIL;
}
// Read the header
2018-03-29 19:00:16 +00:00
uint8_t header[sizeof(TGA_HEADER)] = {};
2016-08-22 18:26:36 +00:00
DWORD bytesRead = 0;
2016-09-09 02:09:46 +00:00
if (!ReadFile(hFile.get(), header, sizeof(TGA_HEADER), &bytesRead, nullptr))
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(GetLastError());
2016-08-22 18:26:36 +00:00
}
size_t offset;
DWORD convFlags = 0;
TexMetadata mdata;
2016-09-09 02:09:46 +00:00
HRESULT hr = DecodeTGAHeader(header, bytesRead, mdata, offset, &convFlags);
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
return hr;
// Read the pixels
2018-06-13 01:07:34 +00:00
auto remaining = static_cast<DWORD>(fileInfo.EndOfFile.LowPart - offset);
2016-09-09 02:09:46 +00:00
if (remaining == 0)
2016-08-22 18:26:36 +00:00
return E_FAIL;
2016-09-09 02:09:46 +00:00
if (offset > sizeof(TGA_HEADER))
2016-08-22 18:26:36 +00:00
{
// Skip past the id string
2017-07-12 07:56:51 +00:00
LARGE_INTEGER filePos = { { static_cast<DWORD>(offset), 0 } };
2018-06-13 01:07:34 +00:00
if (!SetFilePointerEx(hFile.get(), filePos, nullptr, FILE_BEGIN))
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(GetLastError());
2016-08-22 18:26:36 +00:00
}
}
2016-09-09 02:09:46 +00:00
hr = image.Initialize2D(mdata.format, mdata.width, mdata.height, 1, 1);
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
return hr;
2016-09-09 02:09:46 +00:00
assert(image.GetPixels());
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if (!(convFlags & (CONV_FLAGS_RLE | CONV_FLAGS_EXPAND | CONV_FLAGS_INVERTX)) && (convFlags & CONV_FLAGS_INVERTY))
2016-08-22 18:26:36 +00:00
{
// This case we can read directly into the image buffer in place
if (remaining < image.GetPixelsSize())
{
image.Release();
return HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
}
if (image.GetPixelsSize() > UINT32_MAX)
{
image.Release();
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
}
2016-09-09 02:09:46 +00:00
if (!ReadFile(hFile.get(), image.GetPixels(), static_cast<DWORD>(image.GetPixelsSize()), &bytesRead, nullptr))
2016-08-22 18:26:36 +00:00
{
image.Release();
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(GetLastError());
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
if (bytesRead != image.GetPixelsSize())
2016-08-22 18:26:36 +00:00
{
image.Release();
return E_FAIL;
}
2016-09-09 02:09:46 +00:00
switch (mdata.format)
2016-08-22 18:26:36 +00:00
{
case DXGI_FORMAT_R8G8B8A8_UNORM:
2016-09-09 02:09:46 +00:00
{
// TGA stores 32-bit data in BGRA form, need to swizzle to RGBA
assert(image.GetImageCount() == 1);
const Image* img = image.GetImage(0, 0, 0);
if (!img)
2016-09-12 19:01:48 +00:00
{
image.Release();
2016-09-09 02:09:46 +00:00
return E_POINTER;
2016-09-12 19:01:48 +00:00
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
uint8_t *pPixels = img->pixels;
if (!pPixels)
2016-09-12 19:01:48 +00:00
{
image.Release();
2016-09-09 02:09:46 +00:00
return E_POINTER;
2016-09-12 19:01:48 +00:00
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
size_t rowPitch = img->rowPitch;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
// Scan for non-zero alpha channel
bool nonzeroa = false;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
for (size_t h = 0; h < img->height; ++h)
{
2018-06-13 01:07:34 +00:00
auto sPtr = reinterpret_cast<const uint32_t*>(pPixels);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
for (size_t x = 0; x < img->width; ++x)
{
if ((*sPtr) & 0xff000000)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
nonzeroa = true;
2016-08-22 18:26:36 +00:00
break;
2016-09-09 02:09:46 +00:00
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
++sPtr;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
if (nonzeroa)
break;
pPixels += rowPitch;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
DWORD tflags = (!nonzeroa) ? TEXP_SCANLINE_SETALPHA : TEXP_SCANLINE_NONE;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
// Swizzle scanlines
pPixels = img->pixels;
for (size_t h = 0; h < img->height; ++h)
{
_SwizzleScanline(pPixels, rowPitch, pPixels, rowPitch, mdata.format, tflags);
pPixels += rowPitch;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
}
break;
2016-08-22 18:26:36 +00:00
// If we start using DXGI_FORMAT_B8G8R8X8_UNORM or DXGI_FORMAT_B8G8R8A8_UNORM we need to check for a fully 0 alpha channel
2016-09-09 02:09:46 +00:00
2016-08-22 18:26:36 +00:00
case DXGI_FORMAT_B5G5R5A1_UNORM:
2016-09-09 02:09:46 +00:00
{
assert(image.GetImageCount() == 1);
const Image* img = image.GetImage(0, 0, 0);
if (!img)
2016-09-12 19:01:48 +00:00
{
image.Release();
2016-09-09 02:09:46 +00:00
return E_POINTER;
2016-09-12 19:01:48 +00:00
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
// Scan for non-zero alpha channel
bool nonzeroa = false;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
const uint8_t *pPixels = img->pixels;
if (!pPixels)
2016-09-12 19:01:48 +00:00
{
image.Release();
2016-09-09 02:09:46 +00:00
return E_POINTER;
2016-09-12 19:01:48 +00:00
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
size_t rowPitch = img->rowPitch;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
for (size_t h = 0; h < img->height; ++h)
{
2018-06-13 01:07:34 +00:00
auto sPtr = reinterpret_cast<const uint16_t*>(pPixels);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
for (size_t x = 0; x < img->width; ++x)
{
if (*sPtr & 0x8000)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
nonzeroa = true;
2016-08-22 18:26:36 +00:00
break;
2016-09-09 02:09:46 +00:00
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
++sPtr;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
if (nonzeroa)
break;
pPixels += rowPitch;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
// If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
if (!nonzeroa)
{
hr = SetAlphaChannelToOpaque(img);
if (FAILED(hr))
2016-09-12 19:01:48 +00:00
{
image.Release();
2016-09-09 02:09:46 +00:00
return hr;
2016-09-12 19:01:48 +00:00
}
2016-09-09 02:09:46 +00:00
}
}
break;
2017-07-12 07:56:51 +00:00
default:
break;
2016-08-22 18:26:36 +00:00
}
}
else // RLE || EXPAND || INVERTX || !INVERTY
{
2016-09-09 02:09:46 +00:00
std::unique_ptr<uint8_t[]> temp(new (std::nothrow) uint8_t[remaining]);
if (!temp)
2016-08-22 18:26:36 +00:00
{
image.Release();
return E_OUTOFMEMORY;
}
2016-09-09 02:09:46 +00:00
if (!ReadFile(hFile.get(), temp.get(), remaining, &bytesRead, nullptr))
2016-08-22 18:26:36 +00:00
{
image.Release();
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(GetLastError());
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
if (bytesRead != remaining)
2016-08-22 18:26:36 +00:00
{
image.Release();
return E_FAIL;
}
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_RLE)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
hr = UncompressPixels(temp.get(), remaining, image.GetImage(0, 0, 0), convFlags);
2016-08-22 18:26:36 +00:00
}
else
{
2016-09-09 02:09:46 +00:00
hr = CopyPixels(temp.get(), remaining, image.GetImage(0, 0, 0), convFlags);
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
{
image.Release();
return hr;
}
}
2016-09-09 02:09:46 +00:00
if (metadata)
memcpy(metadata, &mdata, sizeof(TexMetadata));
2016-08-22 18:26:36 +00:00
return S_OK;
}
//-------------------------------------------------------------------------------------
// Save a TGA file to memory
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
2016-09-09 02:09:46 +00:00
HRESULT DirectX::SaveToTGAMemory(const Image& image, Blob& blob)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (!image.pixels)
2016-08-22 18:26:36 +00:00
return E_POINTER;
2018-03-29 19:00:16 +00:00
TGA_HEADER tga_header = {};
2016-08-22 18:26:36 +00:00
DWORD convFlags = 0;
2016-09-09 02:09:46 +00:00
HRESULT hr = EncodeTGAHeader(image, tga_header, convFlags);
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
return hr;
blob.Release();
// Determine memory required for image data
size_t rowPitch, slicePitch;
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_888)
2016-08-22 18:26:36 +00:00
{
rowPitch = image.width * 3;
slicePitch = image.height * rowPitch;
}
else
{
hr = ComputePitch(image.format, image.width, image.height, rowPitch, slicePitch, CP_FLAGS_NONE);
if (FAILED(hr))
return hr;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
hr = blob.Initialize(sizeof(TGA_HEADER) + slicePitch);
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
return hr;
// Copy header
2018-03-29 19:00:16 +00:00
auto dPtr = static_cast<uint8_t*>(blob.GetBufferPointer());
2018-06-13 01:07:34 +00:00
assert(dPtr != nullptr);
2016-09-09 02:09:46 +00:00
memcpy_s(dPtr, blob.GetBufferSize(), &tga_header, sizeof(TGA_HEADER));
2016-08-22 18:26:36 +00:00
dPtr += sizeof(TGA_HEADER);
2018-03-29 19:00:16 +00:00
const uint8_t* pPixels = image.pixels;
2016-09-09 02:09:46 +00:00
assert(pPixels);
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
for (size_t y = 0; y < image.height; ++y)
2016-08-22 18:26:36 +00:00
{
// Copy pixels
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_888)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
Copy24bppScanline(dPtr, rowPitch, pPixels, image.rowPitch);
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
else if (convFlags & CONV_FLAGS_SWIZZLE)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
_SwizzleScanline(dPtr, rowPitch, pPixels, image.rowPitch, image.format, TEXP_SCANLINE_NONE);
2016-08-22 18:26:36 +00:00
}
else
{
2016-09-09 02:09:46 +00:00
_CopyScanline(dPtr, rowPitch, pPixels, image.rowPitch, image.format, TEXP_SCANLINE_NONE);
2016-08-22 18:26:36 +00:00
}
dPtr += rowPitch;
pPixels += image.rowPitch;
}
return S_OK;
}
//-------------------------------------------------------------------------------------
// Save a TGA file to disk
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
2016-09-09 02:09:46 +00:00
HRESULT DirectX::SaveToTGAFile(const Image& image, const wchar_t* szFile)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if (!szFile)
2016-08-22 18:26:36 +00:00
return E_INVALIDARG;
2016-09-09 02:09:46 +00:00
if (!image.pixels)
2016-08-22 18:26:36 +00:00
return E_POINTER;
2018-03-29 19:00:16 +00:00
TGA_HEADER tga_header = {};
2016-08-22 18:26:36 +00:00
DWORD convFlags = 0;
2016-09-09 02:09:46 +00:00
HRESULT hr = EncodeTGAHeader(image, tga_header, convFlags);
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
return hr;
// Create file and write header
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
2016-09-09 02:09:46 +00:00
ScopedHandle hFile(safe_handle(CreateFile2(szFile, GENERIC_WRITE, 0, CREATE_ALWAYS, nullptr)));
2016-08-22 18:26:36 +00:00
#else
2016-09-09 02:09:46 +00:00
ScopedHandle hFile(safe_handle(CreateFileW(szFile, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr)));
2016-08-22 18:26:36 +00:00
#endif
2016-09-09 02:09:46 +00:00
if (!hFile)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(GetLastError());
2016-08-22 18:26:36 +00:00
}
2016-09-10 07:09:42 +00:00
auto_delete_file delonfail(hFile.get());
2016-08-22 18:26:36 +00:00
// Determine size for TGA pixel data
size_t rowPitch, slicePitch;
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_888)
2016-08-22 18:26:36 +00:00
{
uint64_t pitch = uint64_t(image.width) * 3u;
uint64_t slice = uint64_t(image.height) * pitch;
#if defined(_M_IX86) || defined(_M_ARM) || defined(_M_HYBRID_X86_ARM64)
static_assert(sizeof(size_t) == 4, "Not a 32-bit platform!");
if (pitch > UINT32_MAX || slice > UINT32_MAX)
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
#else
static_assert(sizeof(size_t) == 8, "Not a 64-bit platform!");
#endif
rowPitch = static_cast<size_t>(pitch);
slicePitch = static_cast<size_t>(slice);
2016-08-22 18:26:36 +00:00
}
else
{
hr = ComputePitch(image.format, image.width, image.height, rowPitch, slicePitch, CP_FLAGS_NONE);
if (FAILED(hr))
return hr;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
if (slicePitch < 65535)
2016-08-22 18:26:36 +00:00
{
// For small images, it is better to create an in-memory file and write it out
Blob blob;
2016-09-09 02:09:46 +00:00
hr = SaveToTGAMemory(image, blob);
if (FAILED(hr))
2016-08-22 18:26:36 +00:00
return hr;
// Write blob
2016-09-09 02:09:46 +00:00
const DWORD bytesToWrite = static_cast<DWORD>(blob.GetBufferSize());
2016-08-22 18:26:36 +00:00
DWORD bytesWritten;
2016-09-09 02:09:46 +00:00
if (!WriteFile(hFile.get(), blob.GetBufferPointer(), bytesToWrite, &bytesWritten, nullptr))
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(GetLastError());
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
if (bytesWritten != bytesToWrite)
2016-08-22 18:26:36 +00:00
{
return E_FAIL;
}
}
else
{
// Otherwise, write the image one scanline at a time...
2016-09-09 02:09:46 +00:00
std::unique_ptr<uint8_t[]> temp(new (std::nothrow) uint8_t[rowPitch]);
if (!temp)
2016-08-22 18:26:36 +00:00
return E_OUTOFMEMORY;
// Write header
DWORD bytesWritten;
2016-09-09 02:09:46 +00:00
if (!WriteFile(hFile.get(), &tga_header, sizeof(TGA_HEADER), &bytesWritten, nullptr))
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(GetLastError());
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
if (bytesWritten != sizeof(TGA_HEADER))
2016-08-22 18:26:36 +00:00
return E_FAIL;
if (rowPitch > UINT32_MAX)
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
2016-08-22 18:26:36 +00:00
// Write pixels
2018-03-29 19:00:16 +00:00
const uint8_t* pPixels = image.pixels;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
for (size_t y = 0; y < image.height; ++y)
2016-08-22 18:26:36 +00:00
{
// Copy pixels
2016-09-09 02:09:46 +00:00
if (convFlags & CONV_FLAGS_888)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
Copy24bppScanline(temp.get(), rowPitch, pPixels, image.rowPitch);
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
else if (convFlags & CONV_FLAGS_SWIZZLE)
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
_SwizzleScanline(temp.get(), rowPitch, pPixels, image.rowPitch, image.format, TEXP_SCANLINE_NONE);
2016-08-22 18:26:36 +00:00
}
else
{
2016-09-09 02:09:46 +00:00
_CopyScanline(temp.get(), rowPitch, pPixels, image.rowPitch, image.format, TEXP_SCANLINE_NONE);
2016-08-22 18:26:36 +00:00
}
pPixels += image.rowPitch;
2016-09-09 02:09:46 +00:00
if (!WriteFile(hFile.get(), temp.get(), static_cast<DWORD>(rowPitch), &bytesWritten, nullptr))
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
return HRESULT_FROM_WIN32(GetLastError());
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
if (bytesWritten != rowPitch)
2016-08-22 18:26:36 +00:00
return E_FAIL;
}
}
2016-09-10 07:09:42 +00:00
delonfail.clear();
2016-08-22 18:26:36 +00:00
return S_OK;
}