You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Sandbox/Editor/Util/Image_DXTC.cpp

813 lines
28 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "EditorDefs.h"
#include "Image_DXTC.h"
// CryCommon
#include <CryCommon/IImage.h>
#include <CryCommon/ImageExtensionHelper.h>
// Editor
#include "Util/Image.h"
#include "BitFiddling.h"
#ifndef MAKEFOURCC
#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
#endif //defined(MAKEFOURCC)
//////////////////////////////////////////////////////////////////////////
// HDR_UPPERNORM -> factor used when converting from [0,32768] high dynamic range images
// to [0,1] low dynamic range images; 32768 = 2^(2^4-1), 4 exponent bits
// LDR_UPPERNORM -> factor used when converting from [0,1] low dynamic range images
// to 8bit outputs
#define HDR_UPPERNORM 1.0f // factor set to 1.0, to be able to see content in our rather dark HDR images
#define LDR_UPPERNORM 255.0f
static float GammaToLinear(float x)
{
return (x <= 0.04045f) ? x / 12.92f : powf((x + 0.055f) / 1.055f, 2.4f);
}
static float LinearToGamma(float x)
{
return (x <= 0.0031308f) ? x * 12.92f : 1.055f * powf(x, 1.0f / 2.4f) - 0.055f;
}
//////////////////////////////////////////////////////////////////////////
// Squish uses non-standard inline friend templates which Recode cannot parse
#ifndef __RECODE__
AZ_PUSH_DISABLE_WARNING(4819 4828, "-Wunknown-warning-option") // Invalid character not in default code page
#include <squish-ccr/squish.h>
AZ_POP_DISABLE_WARNING
#endif
// number of bytes per block per type
#define BLOCKSIZE_BC1 8
#define BLOCKSIZE_BC2 16
#define BLOCKSIZE_BC3 16
#define BLOCKSIZE_BC4 8
#define BLOCKSIZE_BC5 16
#define BLOCKSIZE_BC6 16
#define BLOCKSIZE_BC7 16
CImage_DXTC::COMPRESSOR_ERROR CImage_DXTC::DecompressTextureBTC(
int width,
int height,
ETEX_Format sourceFormat,
CImage_DXTC::UNCOMPRESSED_FORMAT destinationFormat,
[[maybe_unused]] const int imageFlags,
const void* sourceData,
void* destinationData,
int destinationDataSize,
int destinationPageOffset)
{
// Squish uses non-standard inline friend templates which Recode cannot parse
#ifndef __RECODE__
{
const COMPRESSOR_ERROR result = CheckParameters(
width,
height,
destinationFormat,
sourceData,
destinationData,
destinationDataSize);
if (result != COMPRESSOR_ERROR_NONE)
{
return result;
}
}
int flags = 0;
int offs = 0;
int sourceChannels = 4;
switch (sourceFormat)
{
case eTF_BC1:
sourceChannels = 4;
flags = squish::kBtc1;
break;
case eTF_BC2:
sourceChannels = 4;
flags = squish::kBtc2;
break;
case eTF_BC3:
sourceChannels = 4;
flags = squish::kBtc3;
break;
case eTF_BC4U:
sourceChannels = 1;
flags = squish::kBtc4;
break;
case eTF_BC5U:
sourceChannels = 2;
flags = squish::kBtc5 + squish::kColourMetricUnit;
break;
case eTF_BC6UH:
sourceChannels = 3;
flags = squish::kBtc6;
break;
case eTF_BC7:
sourceChannels = 4;
flags = squish::kBtc7;
break;
case eTF_BC4S:
sourceChannels = 1;
flags = squish::kBtc4 + squish::kSignedInternal + squish::kSignedExternal;
offs = 0x80;
break;
case eTF_BC5S:
sourceChannels = 2;
flags = squish::kBtc5 + squish::kSignedInternal + squish::kSignedExternal + squish::kColourMetricUnit;
offs = 0x80;
break;
case eTF_BC6SH:
sourceChannels = 3;
flags = squish::kBtc6 + squish::kSignedInternal + squish::kSignedExternal;
offs = 0x80;
break;
default:
return COMPRESSOR_ERROR_UNSUPPORTED_SOURCE_FORMAT;
}
squish::sqio::dtp datatype = !IsLimitedHDR(sourceFormat) ? squish::sqio::dtp::DT_U8 : squish::sqio::dtp::DT_F23;
switch (destinationFormat)
{
case FORMAT_ARGB_8888: /*datatype = squish::sqio::dtp::DT_U8;*/
break;
// case FORMAT_ARGB_16161616: datatype = squish::sqio::dtp::DT_U16; break;
// case FORMAT_ARGB_32323232F: datatype = squish::sqio::dtp::DT_F23; break;
default:
return COMPRESSOR_ERROR_UNSUPPORTED_DESTINATION_FORMAT;
}
struct squish::sqio sqio = squish::GetSquishIO(width, height, datatype, flags);
const int blockChannels = 4;
const int blockWidth = 4;
const int blockHeight = 4;
const int pixelStride = blockChannels * sizeof(uint8);
const int rowStride = (destinationPageOffset ? destinationPageOffset : pixelStride * width);
if ((datatype == squish::sqio::dtp::DT_U8) && (destinationFormat == FORMAT_ARGB_8888))
{
const char* src = (const char*)sourceData;
for (int y = 0; y < height; y += blockHeight)
{
uint8* dst = ((uint8*)destinationData) + (y * rowStride);
for (int x = 0; x < width; x += blockWidth)
{
uint8 values[blockHeight][blockWidth][blockChannels] = { { { 0 } } };
// decode
sqio.decoder((uint8*)values, src, sqio.flags);
// transfer
for (int by = 0; by < blockHeight; by += 1)
{
uint8* bdst = ((uint8*)dst) + (by * rowStride);
for (int bx = 0; bx < blockWidth; bx += 1)
{
bdst[bx * pixelStride + 0] = sourceChannels <= 0 ? 0U : (values[by][bx][0] + offs);
bdst[bx * pixelStride + 1] = sourceChannels <= 1 ? bdst[bx * pixelStride + 0] : (values[by][bx][1] + offs);
bdst[bx * pixelStride + 2] = sourceChannels <= 1 ? bdst[bx * pixelStride + 0] : (values[by][bx][2] + offs);
bdst[bx * pixelStride + 3] = sourceChannels <= 3 ? 255U : (values[by][bx][3]);
}
}
dst += blockWidth * pixelStride;
src += sqio.blocksize;
}
}
}
else if ((datatype == squish::sqio::dtp::DT_F23) && (destinationFormat == FORMAT_ARGB_8888))
{
const char* src = (const char*)sourceData;
for (int y = 0; y < height; y += blockHeight)
{
uint8* dst = ((uint8*)destinationData) + (y * rowStride);
for (int x = 0; x < width; x += blockWidth)
{
float values[blockHeight][blockWidth][blockChannels] = { { { 0 } } };
// decode
sqio.decoder((float*)values, src, sqio.flags);
// transfer
for (int by = 0; by < blockHeight; by += 1)
{
uint8* bdst = ((uint8*)dst) + (by * rowStride);
for (int bx = 0; bx < blockWidth; bx += 1)
{
bdst[bx * pixelStride + 0] = sourceChannels <= 0 ? 0U : std::min((uint8)255, (uint8)floorf(values[by][bx][0] * LDR_UPPERNORM / HDR_UPPERNORM + 0.5f));
bdst[bx * pixelStride + 1] = sourceChannels <= 1 ? bdst[bx * pixelStride + 0] : std::min((uint8)255, (uint8)floorf(values[by][bx][1] * LDR_UPPERNORM / HDR_UPPERNORM + 0.5f));
bdst[bx * pixelStride + 2] = sourceChannels <= 1 ? bdst[bx * pixelStride + 0] : std::min((uint8)255, (uint8)floorf(values[by][bx][2] * LDR_UPPERNORM / HDR_UPPERNORM + 0.5f));
bdst[bx * pixelStride + 3] = sourceChannels <= 3 ? 255U : 255U;
}
}
dst += blockWidth * pixelStride;
src += sqio.blocksize;
}
}
}
#endif
return COMPRESSOR_ERROR_NONE;
}
//////////////////////////////////////////////////////////////////////////
CImage_DXTC::CImage_DXTC()
{
}
//////////////////////////////////////////////////////////////////////////
CImage_DXTC::~CImage_DXTC()
{
}
//////////////////////////////////////////////////////////////////////////
bool CImage_DXTC::Load(const char* filename, CImageEx& outImage, bool* pQualityLoss)
{
if (pQualityLoss)
{
*pQualityLoss = false;
}
_smart_ptr<IImageFile> pImage = gEnv->pRenderer->EF_LoadImage(filename, 0);
if (!pImage)
{
return(false);
}
BYTE* pDecompBytes;
// Does texture have mipmaps?
bool bMipTexture = pImage->mfGet_numMips() > 1;
ETEX_Format eFormat = pImage->mfGetFormat();
int imageFlags = pImage->mfGet_Flags();
if (eFormat == eTF_Unknown)
{
return false;
}
_smart_ptr<IImageFile> pAlphaImage;
ETEX_Format eAttachedFormat = eTF_Unknown;
if (imageFlags & FIM_HAS_ATTACHED_ALPHA)
{
if (pAlphaImage = gEnv->pRenderer->EF_LoadImage(filename, FIM_ALPHA))
{
eAttachedFormat = pAlphaImage->mfGetFormat();
}
}
const bool bIsSRGB = (imageFlags & FIM_SRGB_READ) != 0;
outImage.SetSRGB(bIsSRGB);
const uint32 imageWidth = pImage->mfGet_width();
const uint32 imageHeight = pImage->mfGet_height();
const uint32 numMips = pImage->mfGet_numMips();
int nHorizontalFaces(1);
int nVerticalFaces(1);
int nNumFaces(1);
int nMipMapsSize(0);
int nMipMapCount(numMips);
int nTargetPitch(imageWidth * 4);
int nTargetPageSize(nTargetPitch * imageHeight);
int nHorizontalPageOffset(nTargetPitch);
int nVerticalPageOffset(0);
bool boIsCubemap = pImage->mfGet_NumSides() == 6;
if (boIsCubemap)
{
nHorizontalFaces = 3;
nVerticalFaces = 2;
nHorizontalPageOffset = nTargetPitch * nHorizontalFaces;
nVerticalPageOffset = nTargetPageSize * nHorizontalFaces;
}
outImage.Allocate(imageWidth * nHorizontalFaces, imageHeight * nVerticalFaces);
pDecompBytes = (BYTE*)outImage.GetData();
if (!pDecompBytes)
{
Warning("Cannot allocate image %dx%d, Out of memory", imageWidth, imageHeight);
return false;
}
if (pQualityLoss)
{
*pQualityLoss = CImageExtensionHelper::IsQuantized(eFormat);
}
bool bOk = true;
int nCurrentFace(0);
int nCurrentHorizontalFace(0);
int nCurrentVerticalFace(0);
unsigned char* dest(NULL);
const unsigned char* src(NULL);
unsigned char* basedest(NULL);
const unsigned char* basesrc(NULL);
for (nCurrentHorizontalFace = 0; nCurrentHorizontalFace < nHorizontalFaces; ++nCurrentHorizontalFace)
{
basedest = &pDecompBytes[nTargetPitch * nCurrentHorizontalFace]; // Horizontal offset.
for (nCurrentVerticalFace = 0; nCurrentVerticalFace < nVerticalFaces; ++nCurrentVerticalFace, ++nCurrentFace)
{
basedest += nVerticalPageOffset * nCurrentVerticalFace; // Vertical offset.
basesrc = src = pImage->mfGet_image(nCurrentFace);
if (eFormat == eTF_R8G8B8A8 || eFormat == eTF_R8G8B8A8S)
{
for (int y = 0; y < imageHeight; y++)
{
dest = basedest + nHorizontalPageOffset * y; // Pixel position.
for (int x = 0; x < imageWidth; x++)
{
dest[0] = src[0];
dest[1] = src[1];
dest[2] = src[2];
dest[3] = src[3];
dest += 4;
src += 4;
}
}
}
else if (eFormat == eTF_B8G8R8A8)
{
for (int y = 0; y < imageHeight; y++)
{
dest = basedest + nHorizontalPageOffset * y; // Pixel position.
for (int x = 0; x < imageWidth; x++)
{
dest[0] = src[2];
dest[1] = src[1];
dest[2] = src[0];
dest[3] = src[3];
dest += 4;
src += 4;
}
}
}
else if (eFormat == eTF_B8G8R8X8)
{
for (int y = 0; y < imageHeight; y++)
{
dest = basedest + nHorizontalPageOffset * y; // Pixel position.
for (int x = 0; x < imageWidth; x++)
{
dest[0] = src[2];
dest[1] = src[1];
dest[2] = src[0];
dest[3] = 255;
dest += 4;
src += 4;
}
}
}
else if (eFormat == eTF_B8G8R8)
{
for (int y = 0; y < imageHeight; y++)
{
dest = basedest + nHorizontalPageOffset * y; // Pixel position.
for (int x = 0; x < imageWidth; x++)
{
dest[0] = src[2];
dest[1] = src[1];
dest[2] = src[0];
dest[3] = 255;
dest += 4;
src += 3;
}
}
}
else if (eFormat == eTF_L8)
{
for (int y = 0; y < imageHeight; y++)
{
dest = basedest + nHorizontalPageOffset * y; // Pixel position.
for (int x = 0; x < imageWidth; x++)
{
dest[0] = *src;
dest[1] = *src;
dest[2] = *src;
dest[3] = 255;
dest += 4;
src += 1;
}
}
}
else if (eFormat == eTF_A8)
{
for (int y = 0; y < imageHeight; y++)
{
dest = basedest + nHorizontalPageOffset * y; // Pixel position.
for (int x = 0; x < imageWidth; x++)
{
dest[0] = 0;
dest[1] = 0;
dest[2] = 0;
dest[3] = *src;
dest += 4;
src += 1;
}
}
}
else if (eFormat == eTF_A8L8)
{
for (int y = 0; y < imageHeight; y++)
{
dest = basedest + nHorizontalPageOffset * y; // Pixel position.
for (int x = 0; x < imageWidth; x++)
{
dest[0] = src[0];
dest[1] = src[0];
dest[2] = src[0];
dest[3] = src[1];
dest += 4;
src += 2;
}
}
}
else if (eFormat == eTF_R9G9B9E5)
{
const int nSourcePitch = imageWidth * 4;
for (int y = 0; y < imageHeight; y++)
{
src = basesrc + nSourcePitch * y; //Scanline position.
dest = basedest + nHorizontalPageOffset * y; // Pixel position.
for (int x = 0; x < imageWidth; x++)
{
const struct RgbE
{
unsigned int r : 9, g : 9, b : 9, e : 5;
}* srcv = (const struct RgbE*)src;
const float escale = powf(2.0f, int(srcv->e) - 15 - 9) * LDR_UPPERNORM / HDR_UPPERNORM;
dest[0] = std::min((uint8)255, (uint8)floorf(srcv->r * escale + 0.5f));
dest[1] = std::min((uint8)255, (uint8)floorf(srcv->g * escale + 0.5f));
dest[2] = std::min((uint8)255, (uint8)floorf(srcv->b * escale + 0.5f));
dest[3] = 255U;
dest += 4;
src += 4;
}
}
}
else
{
const int pixelCount = imageWidth * imageHeight;
const int outputBufferSize = pixelCount * 4;
const int mipCount = numMips;
const COMPRESSOR_ERROR err = DecompressTextureBTC(imageWidth, imageHeight, eFormat, FORMAT_ARGB_8888, imageFlags, basesrc, basedest, outputBufferSize, nHorizontalPageOffset);
if (err != COMPRESSOR_ERROR_NONE)
{
return false;
}
}
// alpha channel might be attached
if (imageFlags & FIM_HAS_ATTACHED_ALPHA)
{
if (IsBlockCompressed(eAttachedFormat))
{
const byte* const basealpha = pAlphaImage->mfGet_image(0);
const int alphaImageWidth = pAlphaImage->mfGet_width();
const int alphaImageHeight = pAlphaImage->mfGet_height();
const int alphaImageFlags = pAlphaImage->mfGet_Flags();
const int tmpOutputBufferSize = imageWidth * imageHeight * 4;
uint8* tmpOutputBuffer = new uint8[tmpOutputBufferSize];
const COMPRESSOR_ERROR err = DecompressTextureBTC(alphaImageWidth, alphaImageHeight, eAttachedFormat, FORMAT_ARGB_8888, alphaImageFlags, basealpha, tmpOutputBuffer, tmpOutputBufferSize, 0);
if (err != COMPRESSOR_ERROR_NONE)
{
delete []tmpOutputBuffer;
return false;
}
// assuming attached image can have lower res and difference is power of two
const uint32 reducex = IntegerLog2((uint32)(imageWidth / alphaImageWidth));
const uint32 reducey = IntegerLog2((uint32)(imageHeight / alphaImageHeight));
for (int y = 0; y < imageHeight; ++y)
{
dest = basedest + nHorizontalPageOffset * y; // Pixel position.
for (int x = 0; x < imageWidth; ++x)
{
dest[3] = tmpOutputBuffer[((x >> reducex) + (y >> reducey) * alphaImageWidth) * 4];
dest += 4;
}
}
delete []tmpOutputBuffer;
}
else if (eAttachedFormat != eTF_Unknown)
{
const byte* const basealpha = pAlphaImage->mfGet_image(0); // assuming it's A8 format (ensured with assets when loading)
const int alphaImageWidth = pAlphaImage->mfGet_width();
const int alphaImageHeight = pAlphaImage->mfGet_height();
const int alphaImageFlags = pAlphaImage->mfGet_Flags();
// assuming attached image can have lower res and difference is power of two
const uint32 reducex = IntegerLog2((uint32)(imageWidth / alphaImageWidth));
const uint32 reducey = IntegerLog2((uint32)(imageHeight / alphaImageHeight));
for (int y = 0; y < imageHeight; ++y)
{
dest = basedest + nHorizontalPageOffset * y; // Pixel position.
for (int x = 0; x < imageWidth; ++x)
{
dest[3] = basealpha[(x >> reducex) + (y >> reducey) * alphaImageWidth];
dest += 4;
}
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////
// destination range is 8bits
// rescale in linear space
float cScaleR = 1.0f;
float cScaleG = 1.0f;
float cScaleB = 1.0f;
float cScaleA = 1.0f;
float cLowR = 0.0f;
float cLowG = 0.0f;
float cLowB = 0.0f;
float cLowA = 0.0f;
if (imageFlags & FIM_RENORMALIZED_TEXTURE)
{
const ColorF cMinColor = pImage->mfGet_minColor();
const ColorF cMaxColor = pImage->mfGet_maxColor();
// base range after normalization, fe. [0,1] for 8bit images, or [0,2^15] for RGBE/HDR data
float cUprValue = 1.0f;
if ((eFormat == eTF_R9G9B9E5) || (eFormat == eTF_BC6UH) || (eFormat == eTF_BC6SH))
{
cUprValue = cMaxColor.a / HDR_UPPERNORM;
}
// original range before normalization, fe. [0,1.83567]
cScaleR = (cMaxColor.r - cMinColor.r) / cUprValue;
cScaleG = (cMaxColor.g - cMinColor.g) / cUprValue;
cScaleB = (cMaxColor.b - cMinColor.b) / cUprValue;
// original offset before normalization, fe. [0.0001204]
cLowR = cMinColor.r;
cLowG = cMinColor.g;
cLowB = cMinColor.b;
}
if (imageFlags & FIM_HAS_ATTACHED_ALPHA)
{
if (pAlphaImage)
{
const int alphaImageFlags = pAlphaImage->mfGet_Flags();
if (alphaImageFlags & FIM_RENORMALIZED_TEXTURE)
{
const ColorF cMinColor = pAlphaImage->mfGet_minColor();
const ColorF cMaxColor = pAlphaImage->mfGet_maxColor();
// base range after normalization, fe. [0,1] for 8bit images, or [0,2^15] for RGBE/HDR data
float cUprValue = 1.0f;
if ((eFormat == eTF_R9G9B9E5) || (eFormat == eTF_BC6UH) || (eFormat == eTF_BC6SH))
{
cUprValue = cMaxColor.a / HDR_UPPERNORM;
}
// original range before normalization, fe. [0,1.83567]
cScaleA = (cMaxColor.r - cMinColor.r) / cUprValue;
// original offset before normalization, fe. [0.0001204]
cLowA = cMinColor.r;
}
}
}
if (cScaleR != 1.0f || cScaleG != 1.0f || cScaleB != 1.0f || cScaleA != 1.0f ||
cLowR != 0.0f || cLowG != 0.0f || cLowB != 0.0f || cLowA != 0.0f)
{
if ((eFormat == eTF_R9G9B9E5) || (eFormat == eTF_BC6UH) || (eFormat == eTF_BC6SH))
{
imageFlags &= ~FIM_SRGB_READ;
}
if (imageFlags & FIM_SRGB_READ)
{
for (int s = 0; s < (imageWidth * nHorizontalFaces * imageHeight * nVerticalFaces * 4); s += 4)
{
pDecompBytes[s + 0] = std::min((uint8)255, uint8(LinearToGamma(GammaToLinear(pDecompBytes[s + 0] / LDR_UPPERNORM) * cScaleR + cLowR) * LDR_UPPERNORM + 0.5f));
pDecompBytes[s + 1] = std::min((uint8)255, uint8(LinearToGamma(GammaToLinear(pDecompBytes[s + 1] / LDR_UPPERNORM) * cScaleG + cLowG) * LDR_UPPERNORM + 0.5f));
pDecompBytes[s + 2] = std::min((uint8)255, uint8(LinearToGamma(GammaToLinear(pDecompBytes[s + 2] / LDR_UPPERNORM) * cScaleB + cLowB) * LDR_UPPERNORM + 0.5f));
pDecompBytes[s + 3] = std::min((uint8)255, uint8(LinearToGamma(GammaToLinear(pDecompBytes[s + 3] / LDR_UPPERNORM) * cScaleA + cLowA) * LDR_UPPERNORM + 0.5f));
}
}
else
{
for (int s = 0; s < (imageWidth * nHorizontalFaces * imageHeight * nVerticalFaces * 4); s += 4)
{
pDecompBytes[s + 0] = std::min((uint8)255, uint8(pDecompBytes[s + 0] * cScaleR + cLowR * LDR_UPPERNORM + 0.5f));
pDecompBytes[s + 1] = std::min((uint8)255, uint8(pDecompBytes[s + 1] * cScaleG + cLowG * LDR_UPPERNORM + 0.5f));
pDecompBytes[s + 2] = std::min((uint8)255, uint8(pDecompBytes[s + 2] * cScaleB + cLowB * LDR_UPPERNORM + 0.5f));
pDecompBytes[s + 3] = std::min((uint8)255, uint8(pDecompBytes[s + 3] * cScaleA + cLowA * LDR_UPPERNORM + 0.5f));
}
}
}
bool hasAlpha = (eAttachedFormat != eTF_Unknown) /*|| CImageExtensionHelper::HasAlphaForTextureFormat(eFormat)*/;
for (int s = 0; s < (imageWidth * nHorizontalFaces * imageHeight * nVerticalFaces); s += 4)
{
hasAlpha |= (pDecompBytes[s + 3] != 0xFF);
}
//////////////////////////////////////////////////////////////////////////
QString strFormat = NameForTextureFormat(eFormat);
QString mips;
mips = QStringLiteral(" Mips:%1").arg(numMips);
if (eAttachedFormat != eTF_Unknown)
{
strFormat += " + ";
strFormat += NameForTextureFormat(eAttachedFormat);
}
strFormat += mips;
// Check whether it's gamma-corrected or not and add a description accordingly.
if (imageFlags & FIM_SRGB_READ)
{
strFormat += ", SRGB/Gamma corrected";
}
if (imageFlags & FIM_RENORMALIZED_TEXTURE)
{
strFormat += ", Renormalized";
}
if (IsLimitedHDR(eFormat))
{
strFormat += ", HDR";
}
outImage.SetFormatDescription(strFormat);
outImage.SetNumberOfMipMaps(numMips);
outImage.SetHasAlphaChannel(hasAlpha);
outImage.SetIsLimitedHDR(IsLimitedHDR(eFormat));
outImage.SetIsCubemap(boIsCubemap);
outImage.SetFormat(eFormat);
outImage.SetSRGB(imageFlags & FIM_SRGB_READ);
// done reading file
return bOk;
}
//////////////////////////////////////////////////////////////////////////
int CImage_DXTC::TextureDataSize(int nWidth, int nHeight, int nDepth, int nMips, ETEX_Format eTF)
{
if (eTF == eTF_Unknown)
{
return 0;
}
if (nMips <= 0)
{
nMips = 0;
}
int nSize = 0;
int nM = 0;
while (nWidth || nHeight || nDepth)
{
if (!nWidth)
{
nWidth = 1;
}
if (!nHeight)
{
nHeight = 1;
}
if (!nDepth)
{
nDepth = 1;
}
nM++;
int nSingleMipSize;
if (IsBlockCompressed(eTF))
{
int blockSize = CImageExtensionHelper::BytesPerBlock(eTF);
const Vec2i blockDim = CImageExtensionHelper::GetBlockDim(eTF);
nSingleMipSize = ((nWidth + blockDim.x - 1) / blockDim.x) * ((nHeight + blockDim.y - 1) / blockDim.y) * nDepth * blockSize;
}
else
{
nSingleMipSize = nWidth * nHeight * nDepth * CImageExtensionHelper::BytesPerBlock(eTF);
}
nSize += nSingleMipSize;
nWidth >>= 1;
nHeight >>= 1;
nDepth >>= 1;
if (nMips == nM)
{
break;
}
}
//assert (nM == nMips);
return nSize;
}
//////////////////////////////////////////////////////////////////////////
CImage_DXTC::COMPRESSOR_ERROR CImage_DXTC::CheckParameters(
int width,
int height,
CImage_DXTC::UNCOMPRESSED_FORMAT destinationFormat,
const void* sourceData,
void* destinationData,
int destinationDataSize)
{
const int blockWidth = 4;
const int blockHeight = 4;
const int bgraPixelSize = 4 * sizeof(uint8);
const int bgraRowSize = bgraPixelSize * width;
if ((width <= 0) || (height <= 0) || (!sourceData))
{
return COMPRESSOR_ERROR_NO_INPUT_DATA;
}
if ((width % blockWidth) || (height % blockHeight))
{
return COMPRESSOR_ERROR_GENERIC;
}
if ((destinationData == 0) || (destinationDataSize <= 0))
{
return COMPRESSOR_ERROR_NO_OUTPUT_POINTER;
}
if (destinationFormat != FORMAT_ARGB_8888)
{
return COMPRESSOR_ERROR_UNSUPPORTED_DESTINATION_FORMAT;
}
if ((height * bgraRowSize <= 0) || (height * bgraRowSize > destinationDataSize))
{
return COMPRESSOR_ERROR_GENERIC;
}
return COMPRESSOR_ERROR_NONE;
}