1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
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 //////////////////////////////////////////////////////////////////////////
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
31 double round(double x
)
33 return (double)int(x
>= 0 ? x
+ 0.5 : x
- 0.5);
37 ///////////////////////////////////////////////////////////////////////////////////
39 static EPVRTPixelFormat
FindPvrPixelFormat(EPixelFormat 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
;
56 return ePVRTPF_NumCompressedPFs
;
60 ImageToProcess::EResult
ImageToProcess::ConvertFormatWithPVRTCCompressor(const CImageProperties
* pProps
, EPixelFormat fmtDst
, EQuality quality
)
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
;
105 cquality
= pvrtexture::eETCSlowPerceptual
;
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
;
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
144 get()->GetImagePointer(dwMip
, pSrcMem
, dwSrcPitch
);
145 if (dwSrcPitch
!= dwLocalWidth
* srcPixelSize
)
147 RCLogError("%s : Unexpected problem. Inform an RC programmer.", __FUNCTION__
);
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
);
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
));
175 bOk
= pvrtexture::Transcode(
177 pvrtexture::PixelType(FindPvrPixelFormat(fmtDst
)),
178 ePVRTVarTypeUnsignedByteNorm
,
184 RCLogError("%s : Unknown exception in PVRTexLib. Inform an RC programmer.", __FUNCTION__
);
186 return eResult_Failed
;
191 RCLogError("%s : Failed to compress an image by using PVRTexLib. Inform an RC programmer.", __FUNCTION__
);
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__
);
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__
);
212 return eResult_Failed
;
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();
253 // restore alpha channel from attached image
254 ImageObject
* pAttached
= get()->GetAttachedImage();
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())
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__
);
326 return eResult_Failed
;
331 get()->GetImagePointer(dwMip
, pSrcMem
, dwSrcPitch
);
333 pvrtexture::CPVRTexture
cTexture(compressedHeader
, pSrcMem
);
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
));
344 bOk
= pvrtexture::Transcode(
346 pvrtexture::PVRStandard8PixelType
,
347 ePVRTVarTypeUnsignedByteNorm
,
349 pvrtexture::ePVRTCHigh
);
353 RCLogError("%s : Unknown exception in PVRTexLib. Inform an RC programmer.", __FUNCTION__
);
355 return eResult_Failed
;
360 RCLogError("%s : Failed to decompress an image by using PVRTexLib. Inform an RC programmer.", __FUNCTION__
);
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__
);
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__
);
381 return eResult_Failed
;
386 pRet
->GetImagePointer(dwMip
, pDstMem
, dwDstPitch
);
388 memcpy(pDstMem
, pDecompressedData
, decompressedDataSize
);
392 pRet
.get()->Swizzle("bgra");
396 if (fmtDst
!= ePixelFormat_A8R8G8B8
)
398 ConvertFormat(pProps
, fmtDst
);
401 return eResult_Success
;
404 return eResult_UnsupportedFormat
;