in Kits/DirectXTex/DirectXTexTGA.cpp [284:805]
HRESULT UncompressPixels(
_In_reads_bytes_(size) const void* pSource,
size_t size,
TGA_FLAGS flags,
_In_ const Image* image,
_In_ uint32_t convFlags) noexcept
{
assert(pSource && size > 0);
if (!image || !image->pixels)
return E_POINTER;
// Compute TGA image data pitch
size_t rowPitch, slicePitch;
HRESULT hr = ComputePitch(image->format, image->width, image->height, rowPitch, slicePitch,
(convFlags & CONV_FLAGS_EXPAND) ? CP_FLAGS_24BPP : CP_FLAGS_NONE);
if (FAILED(hr))
return hr;
auto sPtr = static_cast<const uint8_t*>(pSource);
const uint8_t* endPtr = sPtr + size;
bool opaquealpha = false;
switch (image->format)
{
//--------------------------------------------------------------------------- 8-bit
case DXGI_FORMAT_R8_UNORM:
for (size_t y = 0; y < image->height; ++y)
{
size_t offset = ((convFlags & CONV_FLAGS_INVERTX) ? (image->width - 1) : 0);
assert(offset < rowPitch);
uint8_t* dPtr = image->pixels
+ (image->rowPitch * ((convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1)))
+ offset;
for (size_t x = 0; x < image->width; )
{
if (sPtr >= endPtr)
return E_FAIL;
if (*sPtr & 0x80)
{
// Repeat
size_t j = size_t(*sPtr & 0x7F) + 1;
if (++sPtr >= endPtr)
return E_FAIL;
for (; j > 0; --j, ++x)
{
if (x >= image->width)
return E_FAIL;
*dPtr = *sPtr;
if (convFlags & CONV_FLAGS_INVERTX)
--dPtr;
else
++dPtr;
}
++sPtr;
}
else
{
// Literal
size_t j = size_t(*sPtr & 0x7F) + 1;
++sPtr;
if (sPtr + j > endPtr)
return E_FAIL;
for (; j > 0; --j, ++x)
{
if (x >= image->width)
return E_FAIL;
*dPtr = *(sPtr++);
if (convFlags & CONV_FLAGS_INVERTX)
--dPtr;
else
++dPtr;
}
}
}
}
break;
//-------------------------------------------------------------------------- 16-bit
case DXGI_FORMAT_B5G5R5A1_UNORM:
{
uint32_t minalpha = 255;
uint32_t maxalpha = 0;
for (size_t y = 0; y < image->height; ++y)
{
size_t offset = ((convFlags & CONV_FLAGS_INVERTX) ? (image->width - 1) : 0);
assert(offset * 2 < rowPitch);
auto dPtr = reinterpret_cast<uint16_t*>(image->pixels
+ (image->rowPitch * ((convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1))))
+ offset;
for (size_t x = 0; x < image->width; )
{
if (sPtr >= endPtr)
return E_FAIL;
if (*sPtr & 0x80)
{
// Repeat
size_t j = size_t(*sPtr & 0x7F) + 1;
++sPtr;
if (sPtr + 1 >= endPtr)
return E_FAIL;
auto t = static_cast<uint16_t>(uint32_t(*sPtr) | uint32_t(*(sPtr + 1u) << 8));
uint32_t alpha = (t & 0x8000) ? 255 : 0;
minalpha = std::min(minalpha, alpha);
maxalpha = std::max(maxalpha, alpha);
sPtr += 2;
for (; j > 0; --j, ++x)
{
if (x >= image->width)
return E_FAIL;
*dPtr = t;
if (convFlags & CONV_FLAGS_INVERTX)
--dPtr;
else
++dPtr;
}
}
else
{
// Literal
size_t j = size_t(*sPtr & 0x7F) + 1;
++sPtr;
if (sPtr + (j * 2) > endPtr)
return E_FAIL;
for (; j > 0; --j, ++x)
{
if (x >= image->width)
return E_FAIL;
auto t = static_cast<uint16_t>(uint32_t(*sPtr) | uint32_t(*(sPtr + 1u) << 8));
uint32_t alpha = (t & 0x8000) ? 255 : 0;
minalpha = std::min(minalpha, alpha);
maxalpha = std::max(maxalpha, alpha);
sPtr += 2;
*dPtr = t;
if (convFlags & CONV_FLAGS_INVERTX)
--dPtr;
else
++dPtr;
}
}
}
}
// If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
if (maxalpha == 0 && !(flags & TGA_FLAGS_ALLOW_ALL_ZERO_ALPHA))
{
opaquealpha = true;
hr = SetAlphaChannelToOpaque(image);
if (FAILED(hr))
return hr;
}
else if (minalpha == 255)
{
opaquealpha = true;
}
}
break;
//------------------------------------------------------ 24/32-bit (with swizzling)
case DXGI_FORMAT_R8G8B8A8_UNORM:
{
uint32_t minalpha = 255;
uint32_t maxalpha = 0;
for (size_t y = 0; y < image->height; ++y)
{
size_t offset = ((convFlags & CONV_FLAGS_INVERTX) ? (image->width - 1) : 0);
auto dPtr = reinterpret_cast<uint32_t*>(image->pixels
+ (image->rowPitch * ((convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1))))
+ offset;
for (size_t x = 0; x < image->width; )
{
if (sPtr >= endPtr)
return E_FAIL;
if (*sPtr & 0x80)
{
// Repeat
size_t j = size_t(*sPtr & 0x7F) + 1;
++sPtr;
uint32_t t;
if (convFlags & CONV_FLAGS_EXPAND)
{
assert(offset * 3 < rowPitch);
if (sPtr + 2 >= endPtr)
return E_FAIL;
// BGR -> RGBA
t = uint32_t(*sPtr << 16) | uint32_t(*(sPtr + 1) << 8) | uint32_t(*(sPtr + 2)) | 0xFF000000;
sPtr += 3;
minalpha = maxalpha = 255;
}
else
{
assert(offset * 4 < rowPitch);
if (sPtr + 3 >= endPtr)
return E_FAIL;
// BGRA -> RGBA
uint32_t alpha = *(sPtr + 3);
t = uint32_t(*sPtr << 16) | uint32_t(*(sPtr + 1) << 8) | uint32_t(*(sPtr + 2)) | uint32_t(alpha << 24);
minalpha = std::min(minalpha, alpha);
maxalpha = std::max(maxalpha, alpha);
sPtr += 4;
}
for (; j > 0; --j, ++x)
{
if (x >= image->width)
return E_FAIL;
*dPtr = t;
if (convFlags & CONV_FLAGS_INVERTX)
--dPtr;
else
++dPtr;
}
}
else
{
// Literal
size_t j = size_t(*sPtr & 0x7F) + 1;
++sPtr;
if (convFlags & CONV_FLAGS_EXPAND)
{
if (sPtr + (j * 3) > endPtr)
return E_FAIL;
}
else
{
if (sPtr + (j * 4) > endPtr)
return E_FAIL;
}
for (; j > 0; --j, ++x)
{
if (x >= image->width)
return E_FAIL;
if (convFlags & CONV_FLAGS_EXPAND)
{
assert(offset * 3 < rowPitch);
if (sPtr + 2 >= endPtr)
return E_FAIL;
// BGR -> RGBA
*dPtr = uint32_t(*sPtr << 16) | uint32_t(*(sPtr + 1) << 8) | uint32_t(*(sPtr + 2)) | 0xFF000000;
sPtr += 3;
minalpha = maxalpha = 255;
}
else
{
assert(offset * 4 < rowPitch);
if (sPtr + 3 >= endPtr)
return E_FAIL;
// BGRA -> RGBA
uint32_t alpha = *(sPtr + 3);
*dPtr = uint32_t(*sPtr << 16) | uint32_t(*(sPtr + 1) << 8) | uint32_t(*(sPtr + 2)) | uint32_t(alpha << 24);
minalpha = std::min(minalpha, alpha);
maxalpha = std::max(maxalpha, alpha);
sPtr += 4;
}
if (convFlags & CONV_FLAGS_INVERTX)
--dPtr;
else
++dPtr;
}
}
}
}
// If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
if (maxalpha == 0 && !(flags & TGA_FLAGS_ALLOW_ALL_ZERO_ALPHA))
{
opaquealpha = true;
hr = SetAlphaChannelToOpaque(image);
if (FAILED(hr))
return hr;
}
else if (minalpha == 255)
{
opaquealpha = true;
}
}
break;
//-------------------------------------------------------------------- 32-bit (BGR)
case DXGI_FORMAT_B8G8R8A8_UNORM:
{
assert((convFlags & CONV_FLAGS_EXPAND) == 0);
uint32_t minalpha = 255;
uint32_t maxalpha = 0;
for (size_t y = 0; y < image->height; ++y)
{
size_t offset = ((convFlags & CONV_FLAGS_INVERTX) ? (image->width - 1) : 0);
auto dPtr = reinterpret_cast<uint32_t*>(image->pixels
+ (image->rowPitch * ((convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1))))
+ offset;
for (size_t x = 0; x < image->width; )
{
if (sPtr >= endPtr)
return E_FAIL;
if (*sPtr & 0x80)
{
// Repeat
size_t j = size_t(*sPtr & 0x7F) + 1;
++sPtr;
assert(offset * 4 < rowPitch);
if (sPtr + 3 >= endPtr)
return E_FAIL;
uint32_t alpha = *(sPtr + 3);
auto t = *reinterpret_cast<const uint32_t*>(sPtr);
minalpha = std::min(minalpha, alpha);
maxalpha = std::max(maxalpha, alpha);
sPtr += 4;
for (; j > 0; --j, ++x)
{
if (x >= image->width)
return E_FAIL;
*dPtr = t;
if (convFlags & CONV_FLAGS_INVERTX)
--dPtr;
else
++dPtr;
}
}
else
{
// Literal
size_t j = size_t(*sPtr & 0x7F) + 1;
++sPtr;
if (sPtr + (j * 4) > endPtr)
return E_FAIL;
for (; j > 0; --j, ++x)
{
if (x >= image->width)
return E_FAIL;
assert(offset * 4 < rowPitch);
if (sPtr + 3 >= endPtr)
return E_FAIL;
uint32_t alpha = *(sPtr + 3);
*dPtr = *reinterpret_cast<const uint32_t*>(sPtr);
minalpha = std::min(minalpha, alpha);
maxalpha = std::max(maxalpha, alpha);
sPtr += 4;
if (convFlags & CONV_FLAGS_INVERTX)
--dPtr;
else
++dPtr;
}
}
}
}
// If there are no non-zero alpha channel entries, we'll assume alpha is not used and force it to opaque
if (maxalpha == 0 && !(flags & TGA_FLAGS_ALLOW_ALL_ZERO_ALPHA))
{
opaquealpha = true;
hr = SetAlphaChannelToOpaque(image);
if (FAILED(hr))
return hr;
}
else if (minalpha == 255)
{
opaquealpha = true;
}
}
break;
//-------------------------------------------------------------------- 24-bit (BGR)
case DXGI_FORMAT_B8G8R8X8_UNORM:
{
assert((convFlags & CONV_FLAGS_EXPAND) != 0);
for (size_t y = 0; y < image->height; ++y)
{
size_t offset = ((convFlags & CONV_FLAGS_INVERTX) ? (image->width - 1) : 0);
auto dPtr = reinterpret_cast<uint32_t*>(image->pixels
+ (image->rowPitch * ((convFlags & CONV_FLAGS_INVERTY) ? y : (image->height - y - 1))))
+ offset;
for (size_t x = 0; x < image->width; )
{
if (sPtr >= endPtr)
return E_FAIL;
if (*sPtr & 0x80)
{
// Repeat
size_t j = size_t(*sPtr & 0x7F) + 1;
++sPtr;
assert(offset * 3 < rowPitch);
if (sPtr + 2 >= endPtr)
return E_FAIL;
uint32_t t = uint32_t(*sPtr) | uint32_t(*(sPtr + 1) << 8) | uint32_t(*(sPtr + 2) << 16);
sPtr += 3;
for (; j > 0; --j, ++x)
{
if (x >= image->width)
return E_FAIL;
*dPtr = t;
if (convFlags & CONV_FLAGS_INVERTX)
--dPtr;
else
++dPtr;
}
}
else
{
// Literal
size_t j = size_t(*sPtr & 0x7F) + 1;
++sPtr;
if (sPtr + (j * 3) > endPtr)
return E_FAIL;
for (; j > 0; --j, ++x)
{
if (x >= image->width)
return E_FAIL;
assert(offset * 3 < rowPitch);
if (sPtr + 2 >= endPtr)
return E_FAIL;
*dPtr = uint32_t(*sPtr) | uint32_t(*(sPtr + 1) << 8) | uint32_t(*(sPtr + 2) << 16);
sPtr += 3;
if (convFlags & CONV_FLAGS_INVERTX)
--dPtr;
else
++dPtr;
}
}
}
}
}
break;
//---------------------------------------------------------------------------------
default:
return E_FAIL;
}
return opaquealpha ? S_FALSE : S_OK;
}