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.
813 lines
28 KiB
C++
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;
|
|
}
|