887 lines
32 KiB
C++

//=====================================================================
// Copyright 2008 (c), ATI Technologies Inc. All rights reserved.
// Copyright 2016 (c), Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
/// \file TextureIO.cpp
/// \version 2.20
//
//=====================================================================
#include "textureio.h"
#include "compressonator.h"
#include "common.h"
#include "cmp_fileio.h"
#include "pluginmanager.h"
#include "plugininterface.h"
#include <gpu_decode.h>
// #if defined(WIN32) && !defined(NO_LEGACY_BEHAVIOR)
// #define OPTION_CMP_QT
// #endif
#if (OPTION_CMP_QT == 1)
#include <mipstoqimage.h>
#include <QtCore/QCoreApplication>
#include <QtGui/qimage.h>
#include <QtGui/qrgb.h>
#ifdef _DEBUG
#pragma comment(lib, "Qt5Cored.lib")
#pragma comment(lib, "Qt5Guid.lib")
#else
#pragma comment(lib, "Qt5Core.lib")
#pragma comment(lib, "Qt5Gui.lib")
#endif
#endif // CMP_USE_QT
#include <algorithm>
#include <iostream>
#include <string>
#include <locale>
#include <cstdio>
using namespace std;
// Global plugin manager instance
extern PluginManager g_pluginManager;
extern bool g_bAbortCompression;
inline CMP_FLOAT clamp(CMP_FLOAT a, CMP_FLOAT l, CMP_FLOAT h) {
return (a < l) ? l : ((a > h) ? h : a);
}
void astc_find_closest_blockdim_2d(float target_bitrate, int *x, int *y, int consider_illegal) {
int blockdims[6] = { 4, 5, 6, 8, 10, 12 };
float best_error = 1000;
float aspect_of_best = 1;
int i, j;
// Y dimension
for (i = 0; i < 6; i++) {
// X dimension
for (j = i; j < 6; j++) {
// NxN MxN 8x5 10x5 10x6
int is_legal = (j == i) || (j == i + 1) || (j == 3 && j == 1) || (j == 4 && j == 1) || (j == 4 && j == 2);
if (consider_illegal || is_legal) {
float bitrate = 128.0f / (blockdims[i] * blockdims[j]);
float bitrate_error = fabs(bitrate - target_bitrate);
float aspect = (float)blockdims[j] / blockdims[i];
if (bitrate_error < best_error || (bitrate_error == best_error && aspect < aspect_of_best)) {
*x = blockdims[j];
*y = blockdims[i];
best_error = bitrate_error;
aspect_of_best = aspect;
}
}
}
}
}
void astc_find_closest_blockxy_2d(int *x, int *y, int consider_illegal) {
(void)consider_illegal;
int blockdims[6] = { 4, 5, 6, 8, 10, 12 };
bool exists_x = std::find(std::begin(blockdims), std::end(blockdims), (*x)) != std::end(blockdims);
bool exists_y = std::find(std::begin(blockdims), std::end(blockdims), (*y)) != std::end(blockdims);
if (exists_x && exists_y) {
if ((*x) < (*y)) {
int temp = *x;
*x = *y;
*y = temp;
}
float bitrateF = float(128.0f / ((*x)*(*y)));
astc_find_closest_blockdim_2d(bitrateF, x, y, 0);
} else {
float bitrateF = float(128.0f / ((*x)*(*y)));
astc_find_closest_blockdim_2d(bitrateF, x, y, 0);
}
}
bool DeCompressionCallback(float fProgress, CMP_DWORD_PTR pUser1, CMP_DWORD_PTR pUser2) {
UNREFERENCED_PARAMETER(pUser1);
UNREFERENCED_PARAMETER(pUser2);
static float Progress = 0;
if (fProgress > Progress)
Progress = fProgress;
PrintInfo("\rDeCompression progress = %3.0f", Progress);
return g_bAbortCompression;
}
bool IsDestinationUnCompressed(const char *fname) {
bool isuncompressed = true;
std::string file_extension = CMP_GetJustFileExt(fname);
// tolower
for(char& c : file_extension)
c =tolower(c);
if (file_extension.compare(".dds") == 0) {
isuncompressed = false;
}
else if (file_extension.compare(".astc") == 0) {
isuncompressed = false;
}
else if (file_extension.compare(".ktx") == 0) {
isuncompressed = false;
}
else if (file_extension.compare(".ktx2") == 0)
{
isuncompressed = false;
}
else if (file_extension.compare(".raw") == 0)
{
isuncompressed = false;
} else if (file_extension.compare(".basis") == 0) {
isuncompressed = false;
}
#ifdef USE_CRN
else if (file_extension.compare(".crn") == 0) {
isuncompressed = false;
}
#endif
return isuncompressed;
}
CMP_FORMAT FormatByFileExtension(const char *fname, MipSet *pMipSet) {
std::string file_extension = CMP_GetJustFileExt(fname);
// To upper
std::transform(file_extension.begin(), file_extension.end(), file_extension.begin(),
[](unsigned char c) -> unsigned char { return tolower(c); });
pMipSet->m_TextureDataType = TDT_ARGB;
if (file_extension.compare(".exr") == 0) {
pMipSet->m_ChannelFormat = CF_Float16;
return CMP_FORMAT_ARGB_16F;
}
pMipSet->m_ChannelFormat = CF_8bit;
return CMP_FORMAT_ARGB_8888;
}
CMP_FORMAT GetFormat(CMP_DWORD dwFourCC) {
switch(dwFourCC) {
case CMP_FOURCC_ATI1N: return CMP_FORMAT_ATI1N;
case CMP_FOURCC_ATI2N: return CMP_FORMAT_ATI2N;
case CMP_FOURCC_ATI2N_XY: return CMP_FORMAT_ATI2N_XY;
case CMP_FOURCC_ATI2N_DXT5: return CMP_FORMAT_ATI2N_DXT5;
case CMP_FOURCC_DXT1: return CMP_FORMAT_DXT1;
case CMP_FOURCC_DXT3: return CMP_FORMAT_DXT3;
case CMP_FOURCC_DXT5: return CMP_FORMAT_DXT5;
case CMP_FOURCC_DXT5_xGBR: return CMP_FORMAT_DXT5_xGBR;
case CMP_FOURCC_DXT5_RxBG: return CMP_FORMAT_DXT5_RxBG;
case CMP_FOURCC_DXT5_RBxG: return CMP_FORMAT_DXT5_RBxG;
case CMP_FOURCC_DXT5_xRBG: return CMP_FORMAT_DXT5_xRBG;
case CMP_FOURCC_DXT5_RGxB: return CMP_FORMAT_DXT5_RGxB;
case CMP_FOURCC_DXT5_xGxR: return CMP_FORMAT_DXT5_xGxR;
// Deprecated but still supported for decompression
// Some definition are not valid FOURCC values nut are used as Custom formats
// so that DDS files can be used for storage
case CMP_FOURCC_DXT5_GXRB: return CMP_FORMAT_DXT5_xRBG;
case CMP_FOURCC_DXT5_GRXB: return CMP_FORMAT_DXT5_RxBG;
case CMP_FOURCC_DXT5_RXGB: return CMP_FORMAT_DXT5_xGBR;
case CMP_FOURCC_DXT5_BRGX: return CMP_FORMAT_DXT5_RGxB;
case CMP_FOURCC_ATC_RGB: return CMP_FORMAT_ATC_RGB;
case CMP_FOURCC_ATC_RGBA_EXPLICIT: return CMP_FORMAT_ATC_RGBA_Explicit;
case CMP_FOURCC_ATC_RGBA_INTERP: return CMP_FORMAT_ATC_RGBA_Interpolated;
case CMP_FOURCC_ETC_RGB: return CMP_FORMAT_ETC_RGB;
case CMP_FOURCC_ETC2_RGB: return CMP_FORMAT_ETC2_RGB;
case CMP_FOURCC_ETC2_SRGB: return CMP_FORMAT_ETC2_SRGB;
case CMP_FOURCC_ETC2_RGBA: return CMP_FORMAT_ETC2_RGBA;
case CMP_FOURCC_ETC2_RGBA1: return CMP_FORMAT_ETC2_RGBA1;
case CMP_FOURCC_ETC2_SRGBA: return CMP_FORMAT_ETC2_SRGBA;
case CMP_FOURCC_ETC2_SRGBA1: return CMP_FORMAT_ETC2_SRGBA1;
case CMP_FOURCC_BC4S: return CMP_FORMAT_BC4_S;
case CMP_FOURCC_BC4:
case CMP_FOURCC_BC4U: return CMP_FORMAT_ATI1N;
case CMP_FOURCC_BC5: return CMP_FORMAT_BC5;
case CMP_FOURCC_BC5S: return CMP_FORMAT_BC5_S;
case CMP_FOURCC_BC6H: return CMP_FORMAT_BC6H;
case CMP_FOURCC_BC7: return CMP_FORMAT_BC7;
case CMP_FOURCC_ASTC: return CMP_FORMAT_ASTC;
#ifdef USE_APC
case CMP_FOURCC_APC: return CMP_FORMAT_APC;
#endif
#ifdef USE_GTC
case CMP_FOURCC_GTC: return CMP_FORMAT_GTC;
#endif
#ifdef USE_BASIS
case CMP_FOURCC_BASIS: return CMP_FORMAT_BASIS;
#endif
default:
return CMP_FORMAT_Unknown;
}
}
CMP_FORMAT GetFormat(MipSet* pMipSet) {
assert(pMipSet);
if(pMipSet == NULL)
return CMP_FORMAT_Unknown;
switch(pMipSet->m_ChannelFormat) {
case CF_8bit:
switch(pMipSet->m_TextureDataType) {
case TDT_R:
return CMP_FORMAT_R_8;
case TDT_RG:
return CMP_FORMAT_RG_8;
default:
return CMP_FORMAT_ARGB_8888;
}
case CF_Float16:
switch(pMipSet->m_TextureDataType) {
case TDT_R:
return CMP_FORMAT_R_16F;
case TDT_RG:
return CMP_FORMAT_RG_16F;
default:
return CMP_FORMAT_ARGB_16F;
}
case CF_Float32:
switch(pMipSet->m_TextureDataType) {
case TDT_R:
return CMP_FORMAT_R_32F;
case TDT_RG:
return CMP_FORMAT_RG_32F;
default:
return CMP_FORMAT_ARGB_32F;
}
case CF_Float9995E:
return CMP_FORMAT_RGBE_32F;
case CF_Compressed:
return GetFormat(pMipSet->m_dwFourCC2 ? pMipSet->m_dwFourCC2 : pMipSet->m_dwFourCC);
case CF_16bit:
switch(pMipSet->m_TextureDataType) {
case TDT_R:
return CMP_FORMAT_R_16;
case TDT_RG:
return CMP_FORMAT_RG_16;
default:
return CMP_FORMAT_ARGB_16;
}
case CF_2101010:
return CMP_FORMAT_ARGB_2101010;
#ifdef ARGB_32_SUPPORT
case CF_32bit:
switch(pMipSet->m_TextureDataType) {
case TDT_R:
return CMP_FORMAT_R_32;
case TDT_RG:
return CMP_FORMAT_RG_32;
default:
return CMP_FORMAT_ARGB_32;
}
#endif // ARGB_32_SUPPORT
default:
return CMP_FORMAT_Unknown;
}
}
bool FloatFormat(CMP_FORMAT InFormat) {
switch (InFormat) {
case CMP_FORMAT_ARGB_16F:
case CMP_FORMAT_ABGR_16F:
case CMP_FORMAT_RGBA_16F:
case CMP_FORMAT_BGRA_16F:
case CMP_FORMAT_RG_16F:
case CMP_FORMAT_R_16F:
case CMP_FORMAT_ARGB_32F:
case CMP_FORMAT_ABGR_32F:
case CMP_FORMAT_RGBA_32F:
case CMP_FORMAT_BGRA_32F:
case CMP_FORMAT_RGB_32F:
case CMP_FORMAT_BGR_32F:
case CMP_FORMAT_RG_32F:
case CMP_FORMAT_R_32F:
case CMP_FORMAT_BC6H:
case CMP_FORMAT_BC6H_SF:
case CMP_FORMAT_RGBE_32F: {
return true;
}
break;
default:
break;
}
return false;
}
bool CompressedFileFormat(std::string file) {
std::string file_extension = CMP_GetJustFileExt(file);
// To upper
std::transform(file_extension.begin(), file_extension.end(), file_extension.begin(),
[](unsigned char c) -> unsigned char { return toupper(c); });
file_extension.erase(std::remove(file_extension.begin(), file_extension.end(), '.'), file_extension.end());
if (file_extension == "BMP")
return false;
else
return true;
}
//
// Used exclusively by the GUI app: when using CPU/GPU based decode
// ToDo : Remove this code and try to use ProcessCMDLine
MipSet* DecompressMIPSet(MipSet *MipSetIn, CMP_GPUDecode decodeWith, Config *configSetting, CMP_Feedback_Proc pFeedbackProc) {
// validate MipSet is Compressed
if (!MipSetIn->m_compressed) return NULL;
if ((MipSetIn->m_TextureType == TT_CubeMap) && !(configSetting->useCPU)) {
configSetting->errMessage = "GPU based cubemap decode is currently not supported in this version.Please view decode images using CPU (under Settings->Application Options)";
PrintInfo("GPU based cubemap decode is currently not supported in this version. Please view decode images using CPU (under Settings->Application Options)\n");
return NULL;
}
if (MipSetIn->m_format == CMP_FORMAT_ASTC && !(configSetting->useCPU) && decodeWith == CMP_GPUDecode::GPUDecode_DIRECTX) {
configSetting->errMessage = "ASTC format does not supported by DirectX API. Please view ASTC compressed images using other options (CPU) (under Settings->Application Options).";
PrintInfo("Decompress Error: ASTC format does not supported by DirectX API. Please view ASTC compressed images using CPU (under Settings->Application Options).\n");
return NULL;
} else if (MipSetIn->m_format == CMP_FORMAT_ASTC && !(configSetting->useCPU) && decodeWith == CMP_GPUDecode::GPUDecode_OPENGL) {
configSetting->errMessage = "Decode ASTC with OpenGL is not supported. Please view ASTC compressed images using other options (CPU) (under Settings->Application Options).";
PrintInfo("Decompress Error: Decode ASTC with OpenGL is not supported. Please view ASTC compressed images using CPU (under Settings->Application Options).\n");
return NULL;
}
// Compress Options
bool silent = true;
CMP_CompressOptions CompressOptions;
memset(&CompressOptions, 0, sizeof(CMP_CompressOptions));
CompressOptions.dwnumThreads = 0;
CMIPS m_CMIPS;
MipSet *MipSetOut = new MipSet();
memset(MipSetOut, 0, sizeof(MipSet));
MipSetOut->m_TextureDataType = TDT_ARGB;
MipSetOut->m_swizzle = false;
MipSetOut->m_CubeFaceMask = MipSetIn->m_CubeFaceMask;
MipSetOut->m_Flags = MipSetIn->m_Flags;
MipSetOut->m_nDepth = MipSetIn->m_nDepth;
MipSetOut->m_nMaxMipLevels = MipSetIn->m_nMaxMipLevels;
MipSetOut->m_nHeight = MipSetIn->m_nHeight;
MipSetOut->m_nWidth = MipSetIn->m_nWidth;
// BMP is saved as CMP_FORMAT_ARGB_8888
// EXR is saved as CMP_FORMAT_ARGB_16F
switch (MipSetIn->m_format) {
case CMP_FORMAT_BC4_S:
case CMP_FORMAT_BC5_S:
MipSetOut->m_format = CMP_FORMAT_RGBA_8888_S;
break;
case CMP_FORMAT_BC6H:
case CMP_FORMAT_BC6H_SF:
MipSetOut->m_format = CMP_FORMAT_ARGB_16F;
MipSetOut->m_ChannelFormat = CF_Float16;
break;
default:
MipSetOut->m_format = CMP_FORMAT_ARGB_8888;
break;
}
// Allocate output MipSet
if (!m_CMIPS.AllocateMipSet(MipSetOut,
MipSetOut->m_ChannelFormat,
MipSetOut->m_TextureDataType,
MipSetIn->m_TextureType,
MipSetIn->m_nWidth,
MipSetIn->m_nHeight,
MipSetIn->m_nDepth)) { // depthsupport, what should nDepth be set as here?
configSetting->errMessage = "Memory Error(2): allocating MIPSet Output buffer.";
PrintInfo("Memory Error(2): allocating MIPSet Output buffer\n");
m_CMIPS.FreeMipSet(MipSetOut);
delete MipSetOut;
MipSetOut = NULL;
return NULL;
}
MipLevel* pCmpMipLevel = m_CMIPS.GetMipLevel(MipSetIn, 0);
int nMaxFaceOrSlice = CMP_MaxFacesOrSlices(MipSetIn, 0);
int nMaxMipLevels = MipSetIn->m_nMipLevels;
int nWidth = pCmpMipLevel->m_nWidth;
int nHeight = pCmpMipLevel->m_nHeight;
#ifdef USE_BASIS
if (MipSetIn->m_format == CMP_FORMAT_BASIS) {
// These are handled by basis structure: for compressonator use only top most mip levels
nMaxFaceOrSlice = 1;
nMaxMipLevels = 1;
}
#endif
CMP_BYTE* pMipData = m_CMIPS.GetMipLevel(MipSetIn, 0, 0)->m_pbData;
for (int nFaceOrSlice = 0; nFaceOrSlice<nMaxFaceOrSlice; nFaceOrSlice++) {
int nMipWidth = nWidth;
int nMipHeight = nHeight;
for (int nMipLevel = 0; nMipLevel< nMaxMipLevels; nMipLevel++) {
MipLevel* pInMipLevel = m_CMIPS.GetMipLevel(MipSetIn, nMipLevel, nFaceOrSlice);
if (!pInMipLevel) {
configSetting->errMessage = "Memory Error(2): allocating MIPSet Output Cmp level buffer";
PrintInfo("Memory Error(2): allocating MIPSet Output Cmp level buffer\n");
m_CMIPS.FreeMipSet(MipSetOut);
delete MipSetOut;
MipSetOut = NULL;
return NULL;
}
// Valid Mip Level ?
if (pInMipLevel->m_pbData)
pMipData = pInMipLevel->m_pbData;
if (!m_CMIPS.AllocateMipLevelData(m_CMIPS.GetMipLevel(MipSetOut, nMipLevel, nFaceOrSlice), nMipWidth,
nMipHeight, MipSetOut->m_ChannelFormat, MipSetOut->m_TextureDataType)) {
configSetting->errMessage = "Memory Error(2): allocating MIPSet Output level buffer.";
PrintInfo("Memory Error(2): allocating MIPSet Output level buffer\n");
m_CMIPS.FreeMipSet(MipSetOut);
delete MipSetOut;
MipSetOut = NULL;
return NULL;
}
//----------------------------
// Compressed source
//-----------------------------
CMP_Texture srcTexture;
srcTexture.dwSize = sizeof(srcTexture);
srcTexture.dwWidth = nMipWidth;
srcTexture.dwHeight = nMipHeight;
srcTexture.dwPitch = 0;
srcTexture.nBlockWidth = MipSetIn->m_nBlockWidth;
srcTexture.nBlockHeight = MipSetIn->m_nBlockHeight;
srcTexture.nBlockDepth = MipSetIn->m_nBlockDepth;
srcTexture.format = MipSetIn->m_format;
srcTexture.transcodeFormat = MipSetIn->m_transcodeFormat;
srcTexture.dwDataSize = CMP_CalculateBufferSize(&srcTexture);
srcTexture.pData = pMipData;
srcTexture.pMipSet = MipSetIn;
//-----------------------------
// Uncompressed Destination
//-----------------------------
CMP_Texture destTexture;
destTexture.dwSize = sizeof(destTexture);
destTexture.dwWidth = nMipWidth;
destTexture.dwHeight = nMipHeight;
destTexture.dwPitch = 0;
destTexture.nBlockWidth = MipSetOut->m_nBlockWidth;
destTexture.nBlockHeight = MipSetOut->m_nBlockHeight;
destTexture.nBlockDepth = MipSetOut->m_nBlockDepth;
destTexture.format = MipSetOut->m_format;
destTexture.dwDataSize = CMP_CalculateBufferSize(&destTexture);
destTexture.pData = m_CMIPS.GetMipLevel(MipSetOut, nMipLevel, nFaceOrSlice)->m_pbData;
destTexture.pMipSet = MipSetOut;
if (!silent) {
if ((nMipLevel > 1) || (nFaceOrSlice > 1))
PrintInfo("\rProcessing destination MipLevel %2d FaceOrSlice %2d", nMipLevel + 1, nFaceOrSlice);
else
PrintInfo("\rProcessing destination ");
}
try {
#ifdef _WIN32
if ((IsBadReadPtr(srcTexture.pData, srcTexture.dwDataSize))) {
configSetting->errMessage = "Memory Error(2): Source image cannot be accessed.";
PrintInfo("Memory Error(2): Source image\n");
m_CMIPS.FreeMipSet(MipSetOut);
delete MipSetOut;
MipSetOut = NULL;
return NULL;
}
if (/*(srcTexture.dwDataSize > destTexture.dwDataSize) ||*/ (IsBadWritePtr(destTexture.pData, destTexture.dwDataSize))) {
configSetting->errMessage = "Memory Error(2): Destination image must be compatible with source.";
PrintInfo("Memory Error(2): Destination image must be compatible with source\n");
m_CMIPS.FreeMipSet(MipSetOut);
delete MipSetOut;
MipSetOut = NULL;
return NULL;
}
#else
FILE *nullfd = std::fopen("/dev/random", "w");
if (std::fwrite(srcTexture.pData, srcTexture.dwDataSize, 1, nullfd) < 0) {
configSetting->errMessage = "Memory Error(2): Source image cannot be accessed.";
PrintInfo("Memory Error(2): Source image\n");
m_CMIPS.FreeMipSet(MipSetOut);
delete MipSetOut;
MipSetOut = NULL;
return NULL;
}
std::fclose(nullfd);
nullfd = std::fopen("/dev/random", "w");
if (std::fwrite(destTexture.pData, destTexture.dwDataSize, 1, nullfd) < 0) {
configSetting->errMessage = "Memory Error(2): Destination image must be compatible with source.";
PrintInfo("Memory Error(2): Destination image must be compatible with source\n");
m_CMIPS.FreeMipSet(MipSetOut);
delete MipSetOut;
MipSetOut = NULL;
return NULL;
}
std::fclose(nullfd);
#endif
// Return values of the CMP_ calls should be checked for failures
CMP_ERROR res;
if (configSetting->useCPU) {
res = CMP_ConvertTexture(&srcTexture, &destTexture, &CompressOptions, pFeedbackProc);
if (res != CMP_OK) {
configSetting->errMessage = "Compress Failed with Error " + res;
PrintInfo("Compress Failed with Error %d\n", res);
m_CMIPS.FreeMipSet(MipSetOut);
delete MipSetOut;
MipSetOut = NULL;
return NULL;
}
} else {
#ifdef _WIN32
CMP_ERROR res;
CMP_FORMAT hold_destformat = destTexture.format;
res = CMP_DecompressTexture(&srcTexture, &destTexture, decodeWith);
// Did decompress adjust any formats, if so make sure MipSetOut->m_format is updated
// This typically can happen when GPU views are used and the captured framebuffer differs
// from that which was set as a destination texture format. Note all buffer sizes and channel formats
// should match when this happens. Typical case can be moving from SNORM to UNORM bytes
if (hold_destformat != destTexture.format)
MipSetOut->m_format = destTexture.format;
if (res == CMP_ERR_UNSUPPORTED_GPU_ASTC_DECODE) {
configSetting->errMessage = "Error: ASTC compressed texture is not supported by the GPU device.\n";
PrintInfo("Error: ASTC compressed texture is not supported by the GPU device.\n");
m_CMIPS.FreeMipSet(MipSetOut);
delete MipSetOut;
MipSetOut = NULL;
return NULL;
} else if (res == CMP_ERR_UNABLE_TO_INIT_DECOMPRESSLIB) {
configSetting->errMessage = "Error: Failed to load decompress lib for this image or view.\n";
PrintInfo("Error: Failed to decompress with the API selected.\n");
m_CMIPS.FreeMipSet(MipSetOut);
delete MipSetOut;
MipSetOut = NULL;
return NULL;
} else if (res != CMP_OK) {
configSetting->errMessage = "Decompress Failed. Texture format not supported. Please view the compressed images using other options (CPU) (under Settings->Application Options)";
PrintInfo("Decompress Failed with Error %d\n", res);
m_CMIPS.FreeMipSet(MipSetOut);
delete MipSetOut;
MipSetOut = NULL;
return NULL;
}
#else
PrintInfo("GPU Decompress is not supported in linux.\n");
m_CMIPS.FreeMipSet(MipSetOut);
delete MipSetOut;
MipSetOut = NULL;
return NULL;
#endif
}
} catch (std::exception& e) {
PrintInfo(e.what());
m_CMIPS.FreeMipSet(MipSetOut);
delete MipSetOut;
MipSetOut = NULL;
return NULL;
}
pMipData += srcTexture.dwDataSize;
nMipWidth = (nMipWidth>1) ? (nMipWidth >> 1) : 1;
nMipHeight = (nMipHeight>1) ? (nMipHeight >> 1) : 1;
}
}
MipSetOut->m_nMipLevels = MipSetIn->m_nMipLevels;
return MipSetOut;
}
void swap_Bytes(CMP_BYTE *src, int width, int height, int offset) {
int i, j;
CMP_BYTE b;
for (i = 0; i<height; i++) {
for (j = 0; j<width; j++) {
b = *src; // hold 1st byte
*src = *(src + 2); // move 1st to offsetrd
*(src + 2) = b; // save offset to 1st
src = src + offset; // move to next set of bytes
}
}
}
void SwizzleMipMap(MipSet *pMipSet) {
CMP_DWORD dwWidth;
CMP_DWORD dwHeight;
CMP_BYTE *pData;
for (int nMipLevel = 0; nMipLevel<pMipSet->m_nMipLevels; nMipLevel++) {
for (int nFaceOrSlice = 0; nFaceOrSlice< CMP_MaxFacesOrSlices(pMipSet, nMipLevel); nFaceOrSlice++) {
//=====================
// Uncompressed source
//======================
MipLevel* pInMipLevel = g_CMIPS->GetMipLevel(pMipSet, nMipLevel, nFaceOrSlice);
dwWidth = pInMipLevel->m_nWidth;
dwHeight = pInMipLevel->m_nHeight;
pData = pInMipLevel->m_pbData;
// Swizzle to RGBA format when compressing from uncompressed DDS file! This is a Patch for now.
// may want to try this patch on other file types BMP & PNG to move swizzle out to main code.
switch (pMipSet->m_TextureDataType) {
case TDT_ARGB:
swap_Bytes(pInMipLevel->m_pbData, dwWidth, dwHeight, 4);
break;
case TDT_XRGB:
swap_Bytes(pInMipLevel->m_pbData, dwWidth, dwHeight, 3);
break;
default:
break;
}
}
}
}
// Determine if RGB channel to BGA can be done or skipped
// for special cases of compressed formats.
bool KeepSwizzle(CMP_FORMAT destformat) {
// determin of the swizzle flag needs to be turned on!
switch (destformat) {
case CMP_FORMAT_BC4:
case CMP_FORMAT_BC4_S:
case CMP_FORMAT_ATI1N: // same as BC4
case CMP_FORMAT_BC5:
case CMP_FORMAT_BC5_S:
case CMP_FORMAT_ATI2N: // same as BC5 channels swizzled to : Green & Red
case CMP_FORMAT_ATI2N_XY: // BC5 Red & Green Channel
case CMP_FORMAT_ATI2N_DXT5: //
case CMP_FORMAT_BC1:
case CMP_FORMAT_DXT1: // same as BC1
case CMP_FORMAT_BC2:
case CMP_FORMAT_DXT3: // same as BC2
case CMP_FORMAT_BC3:
case CMP_FORMAT_DXT5: // same as BC3
case CMP_FORMAT_ATC_RGB:
case CMP_FORMAT_ATC_RGBA_Explicit:
case CMP_FORMAT_ATC_RGBA_Interpolated:
return true;
break;
default:
break;
}
return false;
}
int AMDLoadMIPSTextureImage(const char *SourceFile, MipSet *MipSetIn, bool use_OCV, void *pluginManager) {
if (pluginManager == NULL)
return -1;
PluginInterface_Image *plugin_Image;
PluginManager *plugin_Manager = (PluginManager *)pluginManager;
if (use_OCV) {
plugin_Image = reinterpret_cast<PluginInterface_Image *>(plugin_Manager->GetPlugin("IMAGE", "OCV"));
} else {
std::string file_extension = CMP_GetFileExtension(SourceFile, false, true);
plugin_Image = reinterpret_cast<PluginInterface_Image*>(plugin_Manager->GetPlugin("IMAGE", (char*)file_extension.c_str()));
}
// do the load
if (plugin_Image) {
plugin_Image->TC_PluginSetSharedIO(&g_CMIPS);
if (plugin_Image->TC_PluginFileLoadTexture(SourceFile, MipSetIn) != 0) {
// Process Error
delete plugin_Image;
plugin_Image = NULL;
return -1;
}
delete plugin_Image;
plugin_Image = NULL;
} else {
#if (OPTION_CMP_QT == 1)
// Failed to load using a AMD Plugin
// Try Qt based
int result = -1;
QString filename(SourceFile);
QImage *qimage = new QImage(filename);
if (qimage) {
result = QImage2MIPS(qimage, g_CMIPS, MipSetIn);
delete qimage;
qimage = NULL;
}
return result;
#else
return -1;
#endif
}
return 0;
}
int AMDSaveMIPSTextureImage(const char *DestFile, MipSet *MipSetIn, bool use_OCV, CMP_CompressOptions option) {
bool filesaved = false;
CMIPS m_CMIPS;
std::string file_extension = CMP_GetJustFileExt(DestFile);
file_extension.erase(std::remove(file_extension.begin(), file_extension.end(), '.'), file_extension.end());
for(char& c : file_extension)
c = toupper(c);
PluginInterface_Image *plugin_Image;
if (use_OCV) {
plugin_Image = reinterpret_cast<PluginInterface_Image *>(g_pluginManager.GetPlugin("IMAGE", "OCV"));
} else {
plugin_Image = reinterpret_cast<PluginInterface_Image *>(g_pluginManager.GetPlugin("IMAGE", (char *)file_extension.c_str()));
}
if (plugin_Image) {
plugin_Image->TC_PluginSetSharedIO(&m_CMIPS);
bool holdswizzle = MipSetIn->m_swizzle;
if (file_extension.compare("TGA") == 0) {
// Special case Patch for TGA plugin
// to be fixed in V2.5 release
MipSetIn->m_swizzle = false;
switch (MipSetIn->m_isDeCompressed) {
case CMP_FORMAT_ASTC:
case CMP_FORMAT_BC7:
case CMP_FORMAT_BC6H:
case CMP_FORMAT_BC6H_SF:
case CMP_FORMAT_ETC_RGB:
case CMP_FORMAT_ETC2_RGB:
MipSetIn->m_swizzle = true;
break;
}
if ((MipSetIn->m_ChannelFormat == CF_Float32) || (MipSetIn->m_ChannelFormat == CF_Float16)) {
PrintInfo("\nError: TGA plugin does not support floating point data saving. Please use other file extension (i.e. dds).\n");
}
}
if (plugin_Image->TC_PluginFileSaveTexture(DestFile, MipSetIn) == 0) {
filesaved = true;
}
MipSetIn->m_swizzle = holdswizzle;
delete plugin_Image;
plugin_Image = NULL;
}
#if (OPTION_CMP_QT == 1)
if (!filesaved)
{
// Try Qt based filesave!
QImage *qimage = MIPS2QImage(&m_CMIPS, MipSetIn, 0, 0, option, nullptr);
if (qimage) {
if (!qimage->save(DestFile)) {
delete qimage;
qimage = NULL;
return(-1);
}
delete qimage;
qimage = NULL;
filesaved = true;
} else
return -1;
}
#endif
if (!filesaved) {
return -1;
}
return 0;
}
bool FormatSupportsQualitySetting(CMP_FORMAT format) {
return CMP_IsCompressedFormat(format);
}
bool FormatSupportsDXTCBase(CMP_FORMAT format) {
switch (format) {
case CMP_FORMAT_ATI1N :
case CMP_FORMAT_ATI2N :
case CMP_FORMAT_ATI2N_XY :
case CMP_FORMAT_ATI2N_DXT5 :
case CMP_FORMAT_BC1 :
case CMP_FORMAT_BC2 :
case CMP_FORMAT_BC3 :
case CMP_FORMAT_BC4 :
case CMP_FORMAT_BC5 :
case CMP_FORMAT_DXT1 :
case CMP_FORMAT_DXT3 :
case CMP_FORMAT_DXT5 :
case CMP_FORMAT_DXT5_xGBR :
case CMP_FORMAT_DXT5_RxBG :
case CMP_FORMAT_DXT5_RBxG :
case CMP_FORMAT_DXT5_xRBG :
case CMP_FORMAT_DXT5_RGxB :
case CMP_FORMAT_DXT5_xGxR :
return (true);
break;
default:
break;
}
return false;
}