!I (1670414, 1670415, 1670416, 1670424, 1670431):
[CRYENGINE.git] / Code / Tools / RC / ResourceCompilerImage / Compressors / PVRTC.cpp
blob5baf05d32d1007f0a9e8634a444b27b9a6fe422b
1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
3 #include "stdafx.h"
5 #if defined(TOOLS_SUPPORT_POWERVR)
7 #include <assert.h> // assert()
9 #include "../ImageObject.h" // ImageToProcess
11 #include "IRCLog.h" // RCLogError
12 #include "ThreadUtils.h" // ThreadUtils
13 #include "MathHelpers.h" // MathHelpers
15 //////////////////////////////////////////////////////////////////////////
16 // POWERVR PVRTexLib
18 #include <PVRTexture.h>
19 #include <PVRTextureUtilities.h>
21 #pragma comment(lib, "PVRTexLib.lib")
23 //////////////////////////////////////////////////////////////////////////
26 // This function was added just to avoid the following linking error when
27 // compiling with Visual Studio 2012:
28 // PVRTexLib.lib(amtc_localised.obj) : error LNK2019: unresolved external symbol round referenced in function DetermineOptimisationParametersForPass
29 extern "C"
31 double round(double x)
33 return (double)int(x >= 0 ? x + 0.5 : x - 0.5);
37 ///////////////////////////////////////////////////////////////////////////////////
39 static EPVRTPixelFormat FindPvrPixelFormat(EPixelFormat fmt)
41 switch (fmt)
43 case ePixelFormat_PVRTC2:
44 return ePVRTPF_PVRTCI_2bpp_RGBA;
45 case ePixelFormat_PVRTC4:
46 return ePVRTPF_PVRTCI_4bpp_RGBA;
47 case ePixelFormat_EAC_R11:
48 return ePVRTPF_EAC_R11;
49 case ePixelFormat_EAC_RG11:
50 return ePVRTPF_EAC_RG11;
51 case ePixelFormat_ETC2:
52 return ePVRTPF_ETC2_RGB;
53 case ePixelFormat_ETC2a:
54 return ePVRTPF_ETC2_RGBA;
55 default:
56 return ePVRTPF_NumCompressedPFs;
60 ImageToProcess::EResult ImageToProcess::ConvertFormatWithPVRTCCompressor(const CImageProperties* pProps, EPixelFormat fmtDst, EQuality quality)
62 assert(get());
64 if ((get()->GetPixelFormat() == fmtDst))
66 return eResult_Success;
69 if (FindPvrPixelFormat(fmtDst) != ePVRTPF_NumCompressedPFs)
71 if (get()->GetPixelFormat() != ePixelFormat_A8R8G8B8)
73 ConvertFormat(pProps, ePixelFormat_A8R8G8B8);
76 const pvrtexture::PixelType srcPixelType('b', 'g', 'r', 'a', 8, 8, 8, 8);
78 const size_t srcPixelSize = 4;
80 std::auto_ptr<ImageObject> pRet(get()->AllocateImage(0, fmtDst));
83 // ETC2 compressor is extremely slow, so let's use "Fast" quality setting for it.
84 // Note that visual quality in this case is not as good as it could be.
85 pvrtexture::ECompressorQuality cquality = pvrtexture::eETCFast;
87 if (fmtDst == ePixelFormat_ETC2 || fmtDst == ePixelFormat_ETC2a || fmtDst == ePixelFormat_EAC_R11 || fmtDst == ePixelFormat_EAC_RG11)
89 const string rgbweights = pProps->GetConfigAsString("rgbweights", "uniform", "uniform");
91 if ((quality <= eQuality_Normal) && StringHelpers::EqualsIgnoreCase(rgbweights, "uniform"))
93 cquality = pvrtexture::eETCFast;
95 else if (quality <= eQuality_Normal)
97 cquality = pvrtexture::eETCFastPerceptual;
99 else if (StringHelpers::EqualsIgnoreCase(rgbweights, "uniform"))
101 cquality = pvrtexture::eETCSlow;
103 else
105 cquality = pvrtexture::eETCSlowPerceptual;
108 else
110 if (quality == eQuality_Preview)
112 cquality = pvrtexture::ePVRTCFastest;
114 else if (quality == eQuality_Fast)
116 cquality = pvrtexture::ePVRTCFast;
118 else if (quality == eQuality_Normal)
120 cquality = pvrtexture::ePVRTCNormal;
122 else
124 cquality = pvrtexture::ePVRTCHigh;
128 EPVRTColourSpace cspace = ePVRTCSpacelRGB;
129 if (get()->GetImageFlags() & CImageExtensionHelper::EIF_SRGBRead)
131 cspace = ePVRTCSpacesRGB;
134 const uint32 dwDstMips = pRet->GetMipCount();
135 for (uint32 dwMip = 0; dwMip < dwDstMips; ++dwMip)
137 const uint32 dwLocalWidth = get()->GetWidth(dwMip);
138 const uint32 dwLocalHeight = get()->GetHeight(dwMip);
140 // Prepare source data
142 char* pSrcMem;
143 uint32 dwSrcPitch;
144 get()->GetImagePointer(dwMip, pSrcMem, dwSrcPitch);
145 if (dwSrcPitch != dwLocalWidth * srcPixelSize)
147 RCLogError("%s : Unexpected problem. Inform an RC programmer.", __FUNCTION__);
148 set(0);
149 return eResult_Failed;
152 const pvrtexture::CPVRTextureHeader srcHeader(
153 srcPixelType.PixelTypeID, // uint64 u64PixelFormat,
154 dwLocalHeight, // uint32 u32Height=1,
155 dwLocalWidth, // uint32 u32Width=1,
156 1, // uint32 u32Depth=1,
157 1, // uint32 u32NumMipMaps=1,
158 1, // uint32 u32NumArrayMembers=1,
159 1, // uint32 u32NumFaces=1,
160 cspace, // EPVRTColourSpace eColourSpace=ePVRTCSpacelRGB,
161 ePVRTVarTypeUnsignedByteNorm, // EPVRTVariableType eChannelType=ePVRTVarTypeUnsignedByteNorm,
162 false); // bool bPreMultiplied=false);
164 pvrtexture::CPVRTexture cTexture(srcHeader, pSrcMem);
166 // Compress
168 // Sokov: PVRTexLib's compressor has internal bugs (at least in POWERVR SDK 2.07.27.0471),
169 // so we must disable _EM_OVERFLOW floating point exception before calling it
170 MathHelpers::AutoFloatingPointExceptions autoFpe(~(_EM_INEXACT | _EM_UNDERFLOW | _EM_OVERFLOW));
172 bool bOk = false;
173 try
175 bOk = pvrtexture::Transcode(
176 cTexture,
177 pvrtexture::PixelType(FindPvrPixelFormat(fmtDst)),
178 ePVRTVarTypeUnsignedByteNorm,
179 cspace,
180 cquality);
182 catch (...)
184 RCLogError("%s : Unknown exception in PVRTexLib. Inform an RC programmer.", __FUNCTION__);
185 set(0);
186 return eResult_Failed;
189 if (!bOk)
191 RCLogError("%s : Failed to compress an image by using PVRTexLib. Inform an RC programmer.", __FUNCTION__);
192 set(0);
193 return eResult_Failed;
197 // Getting compressed data
199 const void* const pCompressedData = cTexture.getDataPtr();
200 if (!pCompressedData)
202 RCLogError("%s : Failed to obtain compressed image data by using PVRTexLib. Inform an RC programmer.", __FUNCTION__);
203 set(0);
204 return eResult_Failed;
207 const uint32 compressedDataSize = cTexture.getDataSize();
208 if (pRet->GetMipDataSize(dwMip) != compressedDataSize)
210 RCLogError("%s : Compressed image data size mismatch while using PVRTexLib. Inform an RC programmer.", __FUNCTION__);
211 set(0);
212 return eResult_Failed;
215 char* pDstMem;
216 uint32 dwDstPitch;
217 pRet->GetImagePointer(dwMip, pDstMem, dwDstPitch);
219 memcpy(pDstMem, pCompressedData, compressedDataSize);
222 // check if we lost the alpha-channel in the conversion
223 if (CPixelFormats::IsPixelFormatWithoutAlpha(fmtDst))
225 // save alpha channel as *attached* image, because it's impossible to store it in the 2-channel normal map image
226 if (pProps->m_bPreserveAlpha && get()->HasNonOpaqueAlpha() && !pProps->GetDiscardAlpha())
228 // alpha2bump suppress the alpha channel export - this behavior saves memory
229 // if (pProps->m_AlphaAsBump.GetBumpToNormalFilterI() == 0)
231 const EPixelFormat destinationFormat = pProps->GetDestAlphaPixelFormat();
233 // we are limiting alpha mip count because A8/R32F may contain more mips than a block compressed format
234 ImageToProcess tempImageToProcess(
235 get()->GetPixelFormat() == ePixelFormat_A32B32G32R32F && destinationFormat != ePixelFormat_A8
236 ? get()->CopyAnyAlphaIntoR32FImage(pRet->GetMipCount())
237 : get()->CopyAnyAlphaIntoR8Image(pRet->GetMipCount())
240 const EPixelFormat currentFormat = tempImageToProcess.get()->GetPixelFormat();
241 if (destinationFormat != currentFormat)
243 tempImageToProcess.ConvertFormat(pProps, destinationFormat);
246 pRet->SetAttachedImage(tempImageToProcess.get());
247 tempImageToProcess.forget();
251 else
253 // restore alpha channel from attached image
254 ImageObject* pAttached = get()->GetAttachedImage();
255 if (pAttached)
257 const EPixelFormat currentFormat = pAttached->GetPixelFormat();
258 const EPixelFormat destinationFormat = (fmtDst == ePixelFormat_A32B32G32R32F ? ePixelFormat_R32F : ePixelFormat_A8);
259 if (destinationFormat != currentFormat)
261 ImageToProcess tempImageToProcess(pAttached->CopyImage());
263 tempImageToProcess.ConvertFormat(pProps, destinationFormat);
265 pAttached = tempImageToProcess.get();
266 tempImageToProcess.forget();
269 if (pAttached->GetPixelFormat() == ePixelFormat_R32F)
271 pRet->TakeoverAnyAlphaFromR32FImage(pAttached);
273 else if (pAttached->GetPixelFormat() == ePixelFormat_A8)
275 pRet->TakeoverAnyAlphaFromA8Image(pAttached);
278 if (pAttached != get()->GetAttachedImage())
280 delete pAttached;
286 set(pRet.release());
287 return eResult_Success;
290 if (FindPvrPixelFormat(get()->GetPixelFormat()) != ePVRTPF_NumCompressedPFs)
292 std::auto_ptr<ImageObject> pRet(get()->AllocateImage(0, ePixelFormat_A8R8G8B8));
295 EPVRTColourSpace colorSpace = ePVRTCSpacelRGB;
296 if (get()->GetImageFlags() & CImageExtensionHelper::EIF_SRGBRead)
298 colorSpace = ePVRTCSpacesRGB;
301 const uint32 dwDstMips = pRet->GetMipCount();
302 for (uint32 dwMip = 0; dwMip < dwDstMips; ++dwMip)
304 const uint32 dwLocalWidth = get()->GetWidth(dwMip);
305 const uint32 dwLocalHeight = get()->GetHeight(dwMip);
307 // Preparing source compressed data
309 const pvrtexture::CPVRTextureHeader compressedHeader(
310 FindPvrPixelFormat(get()->GetPixelFormat()), // uint64 u64PixelFormat,
311 dwLocalHeight, // uint32 u32Height=1,
312 dwLocalWidth, // uint32 u32Width=1,
313 1, // uint32 u32Depth=1,
314 1, // uint32 u32NumMipMaps=1,
315 1, // uint32 u32NumArrayMembers=1,
316 1, // uint32 u32NumFaces=1,
317 colorSpace, // EPVRTColourSpace eColourSpace=ePVRTCSpacelRGB,
318 ePVRTVarTypeUnsignedByteNorm, // EPVRTVariableType eChannelType=ePVRTVarTypeUnsignedByteNorm,
319 false); // bool bPreMultiplied=false);
321 const uint32 compressedDataSize = compressedHeader.getDataSize();
322 if (get()->GetMipDataSize(dwMip) != compressedDataSize)
324 RCLogError("%s : Compressed image data size mismatch. Inform an RC programmer.", __FUNCTION__);
325 set(0);
326 return eResult_Failed;
329 char* pSrcMem;
330 uint32 dwSrcPitch;
331 get()->GetImagePointer(dwMip, pSrcMem, dwSrcPitch);
333 pvrtexture::CPVRTexture cTexture(compressedHeader, pSrcMem);
335 // Decompress
337 // Sokov: PVRTexLib's compressor has internal bugs (at least in POWERVR SDK 2.07.27.0471),
338 // so we must disable _EM_OVERFLOW floating point exception before calling it
339 MathHelpers::AutoFloatingPointExceptions autoFpe(~(_EM_INEXACT | _EM_UNDERFLOW | _EM_OVERFLOW));
341 bool bOk = false;
342 try
344 bOk = pvrtexture::Transcode(
345 cTexture,
346 pvrtexture::PVRStandard8PixelType,
347 ePVRTVarTypeUnsignedByteNorm,
348 colorSpace,
349 pvrtexture::ePVRTCHigh);
351 catch (...)
353 RCLogError("%s : Unknown exception in PVRTexLib. Inform an RC programmer.", __FUNCTION__);
354 set(0);
355 return eResult_Failed;
358 if (!bOk)
360 RCLogError("%s : Failed to decompress an image by using PVRTexLib. Inform an RC programmer.", __FUNCTION__);
361 set(0);
362 return eResult_Failed;
366 // Getting decompressed data
368 const void* const pDecompressedData = cTexture.getDataPtr();
369 if (!pDecompressedData)
371 RCLogError("%s : Failed to obtain decompressed image data by using PVRTexLib. Inform an RC programmer.", __FUNCTION__);
372 set(0);
373 return eResult_Failed;
376 const uint32 decompressedDataSize = cTexture.getDataSize();
377 if (pRet->GetMipDataSize(dwMip) != decompressedDataSize)
379 RCLogError("%s : Decompressed image data size mismatch while using PVRTexLib. Inform an RC programmer.", __FUNCTION__);
380 set(0);
381 return eResult_Failed;
384 char* pDstMem;
385 uint32 dwDstPitch;
386 pRet->GetImagePointer(dwMip, pDstMem, dwDstPitch);
388 memcpy(pDstMem, pDecompressedData, decompressedDataSize);
392 pRet.get()->Swizzle("bgra");
394 set(pRet.release());
396 if (fmtDst != ePixelFormat_A8R8G8B8)
398 ConvertFormat(pProps, fmtDst);
401 return eResult_Success;
404 return eResult_UnsupportedFormat;
407 #endif