| 1 | // tImagePVR.cpp  |
| 2 | //  |
| 3 | // This class knows how to load PowerVR (.pvr) files. It knows the details of the pvr file format and loads the data  |
| 4 | // into tLayers, optionally decompressing them. Saving is not implemented yet. The layers may be 'stolen' from a  |
| 5 | // tImagePVR so that excessive memcpys are avoided. After they are stolen the tImagePVR is invalid. The tImagePVR  |
| 6 | // class supports V1, V2, and V3 pvr files.  |
| 7 | //  |
| 8 | // Copyright (c) 2023, 2024 Tristan Grimmer.  |
| 9 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby  |
| 10 | // granted, provided that the above copyright notice and this permission notice appear in all copies.  |
| 11 | //  |
| 12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL  |
| 13 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,  |
| 14 | // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN  |
| 15 | // AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR  |
| 16 | // PERFORMANCE OF THIS SOFTWARE.  |
| 17 |   |
| 18 | #include <Foundation/tString.h>  |
| 19 | #include "Image/tImagePVR.h"  |
| 20 | #include "Image/tPixelUtil.h"  |
| 21 | #include "Image/tPicture.h"  |
| 22 | namespace tImage  |
| 23 | {  |
| 24 |   |
| 25 |   |
| 26 | namespace tPVR  |
| 27 | {  |
| 28 | // There are 3 possible headers for V1, V2, and V3 PVR files. V1 and V2 are very similar with V2 having two more  |
| 29 | // 4-byte fields than the V1 header.  |
| 30 | #pragma pack(push, 1)  |
| 31 |   |
| 32 | struct   |
| 33 | {  |
| 34 | uint32 ; // 44 for V1. 52 for V2.  |
| 35 | uint32 ;  |
| 36 | uint32 ;  |
| 37 | uint32 ;  |
| 38 | uint8 ;  |
| 39 | uint8 ;  |
| 40 | uint8 ;  |
| 41 | uint8 ;  |
| 42 | uint32 ;  |
| 43 | uint32 ;  |
| 44 | uint32 ;  |
| 45 | uint32 ;  |
| 46 | uint32 ;  |
| 47 | uint32 ;  |
| 48 | uint32 ; // Only read for V2 headers.  |
| 49 | uint32 ; // Only read for V2 headers. Set to 1 for V1 files.  |
| 50 | };  |
| 51 | tStaticAssert(sizeof(HeaderV1V2) == 52);  |
| 52 |   |
| 53 | struct   |
| 54 | {  |
| 55 | uint32 ; // 'PVR3' for V3. LE = 0x03525650.  |
| 56 | uint32 ;  |
| 57 | uint64 ;  |
| 58 | uint32 ; // 0 = Linear RGB. 1 = sRGB (I assume linear alpha for both).  |
| 59 | uint32 ; // Matches PVR3CHANTYPE.  |
| 60 | uint32 ;  |
| 61 | uint32 ;  |
| 62 | uint32 ;  |
| 63 | uint32 ;  |
| 64 | uint32 ;  |
| 65 | uint32 ;  |
| 66 | uint32 ;  |
| 67 | };  |
| 68 | tStaticAssert(sizeof(HeaderV3) == 52);  |
| 69 | #pragma pack(pop)  |
| 70 |   |
| 71 | // These are the PVR legacy (V1 and V2 pvr files) format IDs from the specification document at:  |
| 72 | // https://powervr-graphics.github.io/WebGL_SDK/WebGL_SDK/Documentation/Specifications/PVR%20File%20Format.Specification.Legacy.pdf  |
| 73 | // The names match the names in the document.  |
| 74 | enum PVRLFMT : uint8  |
| 75 | {  |
| 76 | PVRLFMT_ARGB_4444 = 0x00,  |
| 77 | PVRLFMT_ARGB_1555 = 0x01,  |
| 78 | PVRLFMT_RGB_565 = 0x02,  |
| 79 | PVRLFMT_RGB_555 = 0x03,  |
| 80 | PVRLFMT_RGB_888 = 0x04,  |
| 81 | PVRLFMT_ARGB_8888 = 0x05,  |
| 82 | PVRLFMT_ARGB_8332 = 0x06,  |
| 83 | PVRLFMT_I_8 = 0x07,  |
| 84 | PVRLFMT_AI_88 = 0x08,  |
| 85 | PVRLFMT_1BPP = 0x09,  |
| 86 | PVRLFMT_V_Y1_U_Y0 = 0x0A,  |
| 87 | PVRLFMT_Y1_V_Y0_U = 0x0B,  |
| 88 | PVRLFMT_PVRTC2 = 0x0C, // Better name would be PVRTCIBPP2 but want to match docs.  |
| 89 | PVRLFMT_PVRTC4 = 0x0D, // Better name would be PVRTCIBPP4 but want to match docs.  |
| 90 |   |
| 91 | PVRLFMT_ARGB_4444_ALT = 0x10,  |
| 92 | PVRLFMT_ARGB_1555_ALT = 0x11,  |
| 93 | PVRLFMT_ARGB_8888_ALT = 0x12,  |
| 94 | PVRLFMT_RGB_565_ALT = 0x13,  |
| 95 | PVRLFMT_RGB_555_ALT = 0x14,  |
| 96 | PVRLFMT_RGB_888_ALT = 0x15,  |
| 97 | PVRLFMT_I_8_ALT = 0x16,  |
| 98 | PVRLFMT_AI_88_ALT = 0x17,  |
| 99 | PVRLFMT_PVRTC2_ALT = 0x18, // Better name would be PVRTCIBPP2 but want to match docs.  |
| 100 | PVRLFMT_PVRTC4_ALT = 0x19, // Better name would be PVRTCIBPP4 but want to match docs.  |
| 101 |   |
| 102 | PVRLFMT_BGRA_8888 = 0x1A,  |
| 103 | PVRLFMT_DXT1 = 0x20,  |
| 104 | PVRLFMT_DXT2 = 0x21,  |
| 105 | PVRLFMT_DXT3 = 0x22,  |
| 106 | PVRLFMT_DXT4 = 0x23,  |
| 107 | PVRLFMT_DXT5 = 0x24,  |
| 108 | PVRLFMT_RGB_332 = 0x25,  |
| 109 | PVRLFMT_AL_44 = 0x26,  |
| 110 | PVRLFMT_LVU_655 = 0x27,  |
| 111 | PVRLFMT_XLVU_8888 = 0x28,  |
| 112 | PVRLFMT_QWVU_8888 = 0x29,  |
| 113 | PVRLFMT_ABGR_2101010 = 0x2A,  |
| 114 | PVRLFMT_ARGB_2101010 = 0x2B,  |
| 115 | PVRLFMT_AWVU_2101010 = 0x2C,  |
| 116 | PVRLFMT_GR_1616 = 0x2D,  |
| 117 | PVRLFMT_VU_1616 = 0x2E,  |
| 118 | PVRLFMT_ABGR_16161616 = 0x2F,  |
| 119 | PVRLFMT_R_16F = 0x30,  |
| 120 | PVRLFMT_GR_1616F = 0x31,  |
| 121 | PVRLFMT_ABGR_16161616F = 0x32,  |
| 122 | PVRLFMT_R_32F = 0x33,  |
| 123 | PVRLFMT_GR_3232F = 0x34,  |
| 124 | PVRLFMT_ABGR_32323232F = 0x35,  |
| 125 | PVRLFMT_ETC = 0x36,  |
| 126 | PVRLFMT_A_8 = 0x40,  |
| 127 | PVRLFMT_VU_88 = 0x41,  |
| 128 | PVRLFMT_L16 = 0x42,  |
| 129 | PVRLFMT_L8 = 0x43,  |
| 130 | PVRLFMT_AL_88 = 0x44,  |
| 131 | PVRLFMT_UYVY = 0x45,  |
| 132 | PVRLFMT_YUY2 = 0x46  |
| 133 | };  |
| 134 |   |
| 135 | // These match the names of the channel type in the official PVR3 filespec found here:  |
| 136 | // https://imagination-technologies-cloudfront-assets.s3.eu-west-1.amazonaws.com/website-files/documents/PVR+File+Format.Specification.pdf  |
| 137 | enum PVR3CHANTYPE : uint32  |
| 138 | {  |
| 139 | PVR3CHANTYPE_UnsignedByteNormalised = 0x00000000,  |
| 140 | PVR3CHANTYPE_SignedByteNormalised = 0x00000001,  |
| 141 | PVR3CHANTYPE_UnsignedByte = 0x00000002,  |
| 142 | PVR3CHANTYPE_SignedByte = 0x00000003,  |
| 143 | PVR3CHANTYPE_UnsignedShortNormalised = 0x00000004,  |
| 144 | PVR3CHANTYPE_SignedShortNormalised = 0x00000005,  |
| 145 | PVR3CHANTYPE_UnsignedShort = 0x00000006,  |
| 146 | PVR3CHANTYPE_SignedShort = 0x00000007,  |
| 147 | PVR3CHANTYPE_UnsignedIntegerNormalised = 0x00000008,  |
| 148 | PVR3CHANTYPE_SignedIntegerNormalised = 0x00000009,  |
| 149 | PVR3CHANTYPE_UnsignedInteger = 0x0000000A,  |
| 150 | PVR3CHANTYPE_SignedInteger = 0x0000000B,  |
| 151 | PVR3CHANTYPE_Float = 0x0000000C,  |
| 152 | PVR3CHANTYPE_UnsignedFloat = 0x0000000D // Inferred from files generated by PVRTexTool. Not found in spec doc.  |
| 153 | };  |
| 154 |   |
| 155 | // These match the names of the LS 32-bits of the 64-bit pixel format as specified in the PVR3 file-spec found here:  |
| 156 | // https://imagination-technologies-cloudfront-assets.s3.eu-west-1.amazonaws.com/website-files/documents/PVR+File+Format.Specification.pdf  |
| 157 | enum PVR3FMT : uint32  |
| 158 | {  |
| 159 | PVR3FMT_PVRTC_2BPP_RGB = 0x00000000,  |
| 160 | PVR3FMT_PVRTC_2BPP_RGBA = 0x00000001,  |
| 161 | PVR3FMT_PVRTC_4BPP_RGB = 0x00000002,  |
| 162 | PVR3FMT_PVRTC_4BPP_RGBA = 0x00000003,  |
| 163 | PVR3FMT_PVRTC_II_2BPP = 0x00000004,  |
| 164 | PVR3FMT_PVRTC_II_4BPP = 0x00000005,  |
| 165 | PVR3FMT_ETC1 = 0x00000006,  |
| 166 | PVR3FMT_DXT1_BC1 = 0x00000007,  |
| 167 | PVR3FMT_DXT2 = 0x00000008,  |
| 168 | PVR3FMT_DXT3_BC2 = 0x00000009,  |
| 169 | PVR3FMT_DXT4 = 0x0000000A,  |
| 170 | PVR3FMT_DXT5_BC3 = 0x0000000B,  |
| 171 | PVR3FMT_BC4 = 0x0000000C,  |
| 172 | PVR3FMT_BC5 = 0x0000000D,  |
| 173 | PVR3FMT_BC6 = 0x0000000E,  |
| 174 | PVR3FMT_BC7 = 0x0000000F,  |
| 175 | PVR3FMT_UYVY = 0x00000010,  |
| 176 | PVR3FMT_YUY2 = 0x00000011,  |
| 177 | PVR3FMT_BW1BPP = 0x00000012,  |
| 178 | PVR3FMT_R9G9B9E5_Shared_Exponent = 0x00000013,  |
| 179 | PVR3FMT_RGBG8888 = 0x00000014,  |
| 180 | PVR3FMT_GRGB8888 = 0x00000015,  |
| 181 | PVR3FMT_ETC2_RGB = 0x00000016,  |
| 182 | PVR3FMT_ETC2_RGBA = 0x00000017,  |
| 183 | PVR3FMT_ETC2_RGB_A1 = 0x00000018,  |
| 184 | PVR3FMT_EAC_R11 = 0x00000019,  |
| 185 | PVR3FMT_EAC_RG11 = 0x0000001A,  |
| 186 | PVR3FMT_ASTC_4X4 = 0x0000001B,  |
| 187 | PVR3FMT_ASTC_5X4 = 0x0000001C,  |
| 188 | PVR3FMT_ASTC_5X5 = 0x0000001D,  |
| 189 | PVR3FMT_ASTC_6X5 = 0x0000001E,  |
| 190 | PVR3FMT_ASTC_6X6 = 0x0000001F,  |
| 191 | PVR3FMT_ASTC_8X5 = 0x00000020,  |
| 192 | PVR3FMT_ASTC_8X6 = 0x00000021,  |
| 193 | PVR3FMT_ASTC_8X8 = 0x00000022,  |
| 194 | PVR3FMT_ASTC_10X5 = 0x00000023,  |
| 195 | PVR3FMT_ASTC_10X6 = 0x00000024,  |
| 196 | PVR3FMT_ASTC_10X8 = 0x00000025,  |
| 197 | PVR3FMT_ASTC_10X10 = 0x00000026,  |
| 198 | PVR3FMT_ASTC_12X10 = 0x00000027,  |
| 199 | PVR3FMT_ASTC_12X12 = 0x00000028,  |
| 200 | PVR3FMT_ASTC_3X3X3 = 0x00000029,  |
| 201 | PVR3FMT_ASTC_4X3X3 = 0x0000002A,  |
| 202 | PVR3FMT_ASTC_4X4X3 = 0x0000002B,  |
| 203 | PVR3FMT_ASTC_4X4X4 = 0x0000002C,  |
| 204 | PVR3FMT_ASTC_5X4X4 = 0x0000002D,  |
| 205 | PVR3FMT_ASTC_5X5X4 = 0x0000002E,  |
| 206 | PVR3FMT_ASTC_5X5X5 = 0x0000002F,  |
| 207 | PVR3FMT_ASTC_6X5X5 = 0x00000030,  |
| 208 | PVR3FMT_ASTC_6X6X5 = 0x00000031,  |
| 209 | PVR3FMT_ASTC_6X6X6 = 0x00000032,  |
| 210 |   |
| 211 | PVR3FMT_RGBM = 0x00000035,  |
| 212 | PVR3FMT_RGBD = 0x00000036,  |
| 213 | };  |
| 214 |   |
| 215 | enum PVR3KEY : uint32  |
| 216 | {  |
| 217 | PVR3KEY_ATLAS = 0x00000000,  |
| 218 | PVR3KEY_NORMALMAP = 0x00000001,  |
| 219 | PVR3KEY_CUBEMAP = 0x00000002,  |
| 220 | PVR3KEY_ORIENTATION = 0x00000003,  |
| 221 | PVR3KEY_BORDER = 0x00000004,  |
| 222 | PVR3KEY_PADDING = 0x00000005,  |
| 223 | PVR3KEY_UNKNOWN = 0x00000006  |
| 224 | };  |
| 225 |   |
| 226 | int DetermineVersionFromFirstFourBytes(const uint8 bytes[4]);  |
| 227 |   |
| 228 | // Determine the pixel-format and, if possible, the alpha-mode and channel-type. There is no possibility of  |
| 229 | // determining the colour-profile for V1V2 file headers but we pass it in anyways so it behaves similarly to the V3  |
| 230 | // GetFormatInfo. If the pixel format is returned unspecified the headerFmt is not supported or was invalid. In  |
| 231 | // this case the returned colour-profile, alpha-mode, and channel-type will be unspecified.  |
| 232 | void GetFormatInfo_FromV1V2Header(tPixelFormat&, tColourProfile&, tAlphaMode&, tChannelType&, const HeaderV1V2&);  |
| 233 |   |
| 234 | // For V3 file headers the channel-type, alpha-mode, and colour-space can always be determined. In addition some  |
| 235 | // V3 pixel-formats imply a particular colour space and alpha-mode. In cases where these do not match the required  |
| 236 | // type, mode, or space of the pixel-format, the pixel-format's required setting is chosen.  |
| 237 | void GetFormatInfo_FromV3Header(tPixelFormat&, tColourProfile&, tAlphaMode&, tChannelType&, const HeaderV3&);  |
| 238 |   |
| 239 | void Flip(tLayer*, bool horizontal);  |
| 240 | inline int GetIndex(int x, int y, int w, int h) { tAssert((x >= 0) && (y >= 0) && (x < w) && (y < h)); return y * w + x; }  |
| 241 | }  |
| 242 |   |
| 243 |   |
| 244 | int tPVR::DetermineVersionFromFirstFourBytes(const uint8 bytes[4])  |
| 245 | {  |
| 246 | if (!bytes)  |
| 247 | return 0;  |
| 248 |   |
| 249 | uint32 value = *((uint32*)bytes);  |
| 250 | if (value == 44)  |
| 251 | return 1;  |
| 252 |   |
| 253 | if (value == 52)  |
| 254 | return 2;  |
| 255 |   |
| 256 | if (value == 0x03525650)  |
| 257 | return 3;  |
| 258 |   |
| 259 | return 0;  |
| 260 | }  |
| 261 |   |
| 262 |   |
| 263 | void tPVR::(tPixelFormat& format, tColourProfile& profile, tAlphaMode& alphaMode, tChannelType& chanType, const HeaderV1V2& )  |
| 264 | {  |
| 265 | format = tPixelFormat::Invalid;  |
| 266 | profile = tColourProfile::sRGB;  |
| 267 | alphaMode = tAlphaMode::Unspecified;  |
| 268 | chanType = tChannelType::Unspecified;  |
| 269 |   |
| 270 | #define C(c) case PVRLFMT_##c  |
| 271 | #define F(f) format = tPixelFormat::f;  |
| 272 | #define P(p) profile = tColourProfile::p;  |
| 273 | #define M(m) alphaMode = tAlphaMode::m;  |
| 274 | #define T(t) chanType = tChannelType::t;  |
| 275 | switch (header.PixelFormat)  |
| 276 | {  |
| 277 | // Commented out PVRLFMT formats are not implemented yet.  |
| 278 | // Format Profile AlphaMode ChanType Break  |
| 279 | C(ARGB_4444): F(G4B4A4R4) break;  |
| 280 | C(ARGB_1555): F(G3B5A1R5G2) break;  |
| 281 | C(RGB_565): F(G3B5R5G3) break;  |
| 282 | //C(RGB_555):  |
| 283 | C(RGB_888): F(R8G8B8) break;  |
| 284 | C(ARGB_8888): F(B8G8R8A8) break;  |
| 285 | //C(ARGB_8332):  |
| 286 | C(I_8): F(L8) break;  |
| 287 | C(AI_88): F(A8L8) break;  |
| 288 | //C(1BPP):  |
| 289 | //C(V_Y1_U_Y0):  |
| 290 | //C(Y1_V_Y0_U):  |
| 291 | C(PVRTC2): F(PVRBPP2) break;  |
| 292 | C(PVRTC4): F(PVRBPP4) break;  |
| 293 |   |
| 294 | C(ARGB_4444_ALT): F(G4B4A4R4) break;  |
| 295 | C(ARGB_1555_ALT): F(G3B5A1R5G2) break;  |
| 296 | C(ARGB_8888_ALT): F(R8G8B8A8) break;  |
| 297 | C(RGB_565_ALT): F(G3B5R5G3) break;  |
| 298 | //C(RGB_555_ALT):  |
| 299 | C(RGB_888_ALT): F(R8G8B8) break;  |
| 300 | C(I_8_ALT): F(L8) break;  |
| 301 | C(AI_88_ALT): F(A8L8) break;  |
| 302 | C(PVRTC2_ALT): F(PVRBPP2) break;  |
| 303 | C(PVRTC4_ALT): F(PVRBPP4) break;  |
| 304 |   |
| 305 | C(BGRA_8888): F(B8G8R8A8) break;  |
| 306 | C(DXT1): F(BC1DXT1) break;  |
| 307 | C(DXT2): F(BC2DXT2DXT3) M(Mult) break;  |
| 308 | C(DXT3): F(BC2DXT2DXT3) break;  |
| 309 | C(DXT4): F(BC3DXT4DXT5) M(Mult) break;  |
| 310 | C(DXT5): F(BC3DXT4DXT5) break;  |
| 311 | //C(RGB_332):  |
| 312 | //C(AL_44):  |
| 313 | //C(LVU_655):  |
| 314 | //C(XLVU_8888):  |
| 315 | //C(QWVU_8888):  |
| 316 | //C(ABGR_2101010):  |
| 317 | //C(ARGB_2101010):  |
| 318 | //C(AWVU_2101010):  |
| 319 | //C(GR_1616):  |
| 320 | //C(VU_1616):  |
| 321 | //C(ABGR_16161616):  |
| 322 | C(R_16F): F(R16f) P(lRGB) T(SFLOAT) break;  |
| 323 | C(GR_1616F): F(R16G16f) P(lRGB) T(SFLOAT) break;  |
| 324 | C(ABGR_16161616F): F(R16G16B16A16f) P(lRGB) T(SFLOAT) break;  |
| 325 | C(R_32F): F(R32f) P(lRGB) T(SFLOAT) break;  |
| 326 | C(GR_3232F): F(R32G32f) P(lRGB) T(SFLOAT) break;  |
| 327 | C(ABGR_32323232F): F(R32G32B32A32f) P(lRGB) T(SFLOAT) break;  |
| 328 |   |
| 329 | // V2 ETC1 files generated from PVRTexTool are always in linear space. There is no sRGB option.  |
| 330 | C(ETC): F(ETC1) P(lRGB) break;  |
| 331 | C(A_8): F(A8) break;  |
| 332 | //C(VU_88):  |
| 333 | //C(L16):  |
| 334 | C(L8): F(L8) P(lRGB) T(UINT) break;  |
| 335 | //C(AL_88):  |
| 336 | //C(UYVY):  |
| 337 | //C(YUY2):  |
| 338 | default: P(None) break;  |
| 339 | }  |
| 340 | #undef C  |
| 341 | #undef F  |
| 342 | #undef P  |
| 343 | #undef M  |
| 344 | #undef T  |
| 345 | }  |
| 346 |   |
| 347 |   |
| 348 | void tPVR::(tPixelFormat& format, tColourProfile& profile, tAlphaMode& alphaMode, tChannelType& chanType, const HeaderV3& )  |
| 349 | {  |
| 350 | format = tPixelFormat::Invalid;  |
| 351 | profile = (header.ColourSpace == 0) ? tColourProfile::lRGB : tColourProfile::sRGB;  |
| 352 | alphaMode = (header.Flags & 0x00000002) ? tAlphaMode::Premultiplied : tAlphaMode::Normal;  |
| 353 | chanType = tChannelType::Unspecified;  |
| 354 |   |
| 355 | #define C(c) case PVR3CHANTYPE_##c  |
| 356 | #define T(t) chanType = tChannelType::t;  |
| 357 | switch (header.ChannelType)  |
| 358 | {  |
| 359 | C(UnsignedByteNormalised): T(UNORM) break;  |
| 360 | C(SignedByteNormalised): T(SNORM) break;  |
| 361 | C(UnsignedByte): T(UINT) break;  |
| 362 | C(SignedByte): T(SINT) break;  |
| 363 |   |
| 364 | C(UnsignedShortNormalised): T(UNORM) break;  |
| 365 | C(SignedShortNormalised): T(SNORM) break;  |
| 366 | C(UnsignedShort): T(UINT) break;  |
| 367 | C(SignedShort): T(SINT) break;  |
| 368 |   |
| 369 | C(UnsignedIntegerNormalised): T(UNORM) break;  |
| 370 | C(SignedIntegerNormalised): T(SNORM) break;  |
| 371 | C(UnsignedInteger): T(UINT) break;  |
| 372 | C(SignedInteger): T(SINT) break;  |
| 373 |   |
| 374 | C(Float): T(SFLOAT) break;  |
| 375 | C(UnsignedFloat): T(UFLOAT) break;  |
| 376 | }  |
| 377 | #undef C  |
| 378 | #undef T  |
| 379 |   |
| 380 | // For V3 files if the MS 32 bits are 0, the format is determined by the LS 32 bits.  |
| 381 | // If the MS 32 do bits are non zero, the MS 32 bits contain the number of bits for  |
| 382 | // each channel and the present channels are specified by the LS 32 bits.  |
| 383 | uint32 fmtMS32 = header.PixelFormat >> 32;  |
| 384 | uint32 fmtLS32 = header.PixelFormat & 0x00000000FFFFFFFF;  |
| 385 | if (fmtMS32 == 0)  |
| 386 | {  |
| 387 | #define C(c) case PVR3FMT_##c  |
| 388 | #define F(f) format = tPixelFormat::f;  |
| 389 | #define P(p) profile = tColourProfile::p;  |
| 390 | #define M(m) alphaMode = tAlphaMode::m;  |
| 391 | #define T(t) chanType = tChannelType::t;  |
| 392 | switch (fmtLS32)  |
| 393 | {  |
| 394 | // Commented out PVRLFMT formats are not implemented yet. Some formats require specific profiles and/or  |
| 395 | // alpha-modes. When these are required they override the settings from the header.  |
| 396 | // PVR stores alpha on a per-block basis, not the entire image. Images without alpha just happen  |
| 397 | // to have all opaque blocks. In either case, the pixel format is the same -- PVRBPP2 or PVRBPP4.  |
| 398 | // Format Profile AlphaMode ChanType Break  |
| 399 | C(PVRTC_2BPP_RGB): F(PVRBPP2) break;  |
| 400 | C(PVRTC_2BPP_RGBA): F(PVRBPP2) break;  |
| 401 | C(PVRTC_4BPP_RGB): F(PVRBPP4) break;  |
| 402 | C(PVRTC_4BPP_RGBA): F(PVRBPP4) break;  |
| 403 |   |
| 404 | #ifdef PIXEL_FORMAT_INCLUDE_NOT_IMPLEMENTED  |
| 405 | C(PVRTC_II_2BPP): F(PVR2BPP2) break;  |
| 406 | C(PVRTC_II_4BPP): F(PVR2BPP4) break;  |
| 407 | #endif  |
| 408 | C(ETC1): F(ETC1) break;  |
| 409 |   |
| 410 | C(DXT1_BC1): F(BC1DXT1) break;  |
| 411 | C(DXT2): F(BC2DXT2DXT3) M(Mult) break;  |
| 412 | C(DXT3_BC2): F(BC2DXT2DXT3) break;  |
| 413 | C(DXT4): F(BC3DXT4DXT5) M(Mult) break;  |
| 414 | C(DXT5_BC3): F(BC3DXT4DXT5) break;  |
| 415 | C(BC4): P(lRGB) if (chanType == tChannelType::SNORM) F(BC4ATI1S) else F(BC4ATI1U) break;  |
| 416 | C(BC5): P(lRGB) if (chanType == tChannelType::SNORM) F(BC5ATI2S) else F(BC5ATI2U) break;  |
| 417 | C(BC6): F(BC6U) P(HDRa) T(UFLOAT) break; // Not sure whether signed or unsigned. Assuming unsigned.  |
| 418 | C(BC7): F(BC7) break;  |
| 419 | //C(UYVY):  |
| 420 | //C(YUY2):  |
| 421 | //C(BW1BPP):  |
| 422 | C(R9G9B9E5_Shared_Exponent): F(E5B9G9R9uf) P(HDRa) T(UFLOAT) break;  |
| 423 | //C(RGBG8888):  |
| 424 | //C(GRGB8888):  |
| 425 | C(ETC2_RGB): F(ETC2RGB) break;  |
| 426 | C(ETC2_RGBA): F(ETC2RGBA) break;  |
| 427 | C(ETC2_RGB_A1): F(ETC2RGBA1) break;  |
| 428 | C(EAC_R11): if (chanType == tChannelType::SNORM) F(EACR11S) else F(EACR11U) break;  |
| 429 | C(EAC_RG11): if (chanType == tChannelType::SNORM) F(EACRG11S) else F(EACRG11U) break;  |
| 430 | C(ASTC_4X4): F(ASTC4X4) break;  |
| 431 | C(ASTC_5X4): F(ASTC5X4) break;  |
| 432 | C(ASTC_5X5): F(ASTC5X5) break;  |
| 433 | C(ASTC_6X5): F(ASTC6X5) break;  |
| 434 | C(ASTC_6X6): F(ASTC6X6) break;  |
| 435 | C(ASTC_8X5): F(ASTC8X5) break;  |
| 436 | C(ASTC_8X6): F(ASTC8X6) break;  |
| 437 | C(ASTC_8X8): F(ASTC8X8) break;  |
| 438 | C(ASTC_10X5): F(ASTC10X5) break;  |
| 439 | C(ASTC_10X6): F(ASTC10X6) break;  |
| 440 | C(ASTC_10X8): F(ASTC10X8) break;  |
| 441 | C(ASTC_10X10): F(ASTC10X10) break;  |
| 442 | C(ASTC_12X10): F(ASTC12X10) break;  |
| 443 | C(ASTC_12X12): F(ASTC12X12) break;  |
| 444 | //C(ASTC_3X3X3):  |
| 445 | //C(ASTC_4X3X3):  |
| 446 | //C(ASTC_4X4X3):  |
| 447 | //C(ASTC_4X4X4):  |
| 448 | //C(ASTC_5X4X4):  |
| 449 | //C(ASTC_5X5X4):  |
| 450 | //C(ASTC_5X5X5):  |
| 451 | //C(ASTC_6X5X5):  |
| 452 | //C(ASTC_6X6X5):  |
| 453 | //C(ASTC_6X6X6):  |
| 454 | C(RGBM): F(R8G8B8M8) P(HDRa) break;  |
| 455 | C(RGBD): F(R8G8B8D8) P(HDRa) break;  |
| 456 | default: P(None) M(None) T(NONE) break;  |
| 457 | }  |
| 458 | #undef C  |
| 459 | #undef F  |
| 460 | #undef P  |
| 461 | #undef M  |
| 462 | #undef T  |
| 463 | }  |
| 464 | else  |
| 465 | {  |
| 466 | // The FourCC and the tSwapEndian32 calls below deal with endianness. The values  |
| 467 | // of the literals in the fourCC match the values of the masks in fmtMS32 member.  |
| 468 | #define F(f) format = tPixelFormat::f;  |
| 469 | #define P(p) profile = tColourProfile::p;  |
| 470 | switch (fmtLS32)  |
| 471 | {  |
| 472 | case tImage::FourCC(ch0: 'r', ch1: '\0', ch2: '\0', ch3: '\0'):  |
| 473 | {  |
| 474 | if (chanType == tChannelType::SFLOAT)  |
| 475 | {  |
| 476 | switch (fmtMS32)  |
| 477 | {  |
| 478 | case tSwapEndian32(val: 0x10000000): F(R16f) P(HDRa) break;  |
| 479 | case tSwapEndian32(val: 0x20000000): F(R32f) P(HDRa) break;  |
| 480 | }  |
| 481 | }  |
| 482 | else  |
| 483 | {  |
| 484 | switch (fmtMS32)  |
| 485 | {  |
| 486 | case tSwapEndian32(val: 0x10000000): F(R16) P(lRGB) break;  |
| 487 | case tSwapEndian32(val: 0x20000000): F(R32) P(lRGB) break;  |
| 488 | }  |
| 489 | }  |
| 490 | break;  |
| 491 | }  |
| 492 |   |
| 493 | case tImage::FourCC(ch0: 'r', ch1: 'g', ch2: '\0', ch3: '\0'):  |
| 494 | {  |
| 495 | if (chanType == tChannelType::SFLOAT)  |
| 496 | {  |
| 497 | switch (fmtMS32)  |
| 498 | {  |
| 499 | case tSwapEndian32(val: 0x10100000): F(R16G16f) P(HDRa) break;  |
| 500 | case tSwapEndian32(val: 0x20200000): F(R32G32f) P(HDRa) break;  |
| 501 | }  |
| 502 | }  |
| 503 | else  |
| 504 | {  |
| 505 | switch (fmtMS32)  |
| 506 | {  |
| 507 | case tSwapEndian32(val: 0x10100000): F(R16G16) P(lRGB) break;  |
| 508 | case tSwapEndian32(val: 0x20200000): F(R32G32) P(lRGB) break;  |
| 509 | }  |
| 510 | }  |
| 511 | break;  |
| 512 | }  |
| 513 |   |
| 514 | case tImage::FourCC(ch0: 'r', ch1: 'g', ch2: 'b', ch3: '\0'):  |
| 515 | {  |
| 516 | if (chanType == tChannelType::SFLOAT)  |
| 517 | {  |
| 518 | switch (fmtMS32)  |
| 519 | {  |
| 520 | case tSwapEndian32(val: 0x10101000): F(R16G16B16f) P(HDRa) break;  |
| 521 | case tSwapEndian32(val: 0x20202000): F(R32G32B32f) P(HDRa) break;  |
| 522 | }  |
| 523 | }  |
| 524 | else  |
| 525 | {  |
| 526 | switch (fmtMS32)  |
| 527 | {  |
| 528 | case tSwapEndian32(val: 0x05060500): F(G3B5R5G3) break; // LE PVR: R5 G6 B5.  |
| 529 | case tSwapEndian32(val: 0x10101000): F(R16G16B16) P(lRGB) break;  |
| 530 | case tSwapEndian32(val: 0x20202000): F(R32G32B32) P(lRGB) break;  |
| 531 | }  |
| 532 | }  |
| 533 | break;  |
| 534 | }  |
| 535 |   |
| 536 | case tImage::FourCC(ch0: 'b', ch1: 'g', ch2: 'r', ch3: '\0'):  |
| 537 | {  |
| 538 | if (chanType == tChannelType::UFLOAT)  |
| 539 | {  |
| 540 | switch (fmtMS32)  |
| 541 | {  |
| 542 | case tSwapEndian32(val: 0x0a0b0b00): F(B10G11R11uf) break; // PVR: B10 G11 R11 UFLOAT.  |
| 543 | }  |
| 544 | break;  |
| 545 | }  |
| 546 | break;  |
| 547 | }  |
| 548 |   |
| 549 | case tImage::FourCC(ch0: 'r', ch1: 'g', ch2: 'b', ch3: 'a'):  |
| 550 | {  |
| 551 | if (chanType == tChannelType::SFLOAT)  |
| 552 | {  |
| 553 | switch (fmtMS32)  |
| 554 | {  |
| 555 | case tSwapEndian32(val: 0x10101010): F(R16G16B16A16f) break;  |
| 556 | case tSwapEndian32(val: 0x20202020): F(R32G32B32A32f) break;  |
| 557 | }  |
| 558 | }  |
| 559 | else  |
| 560 | {  |
| 561 | switch (fmtMS32)  |
| 562 | {  |
| 563 | case tSwapEndian32(val: 0x08080808): F(R8G8B8A8) break;  |
| 564 | case tSwapEndian32(val: 0x04040404): F(B4A4R4G4) break;  |
| 565 | case tSwapEndian32(val: 0x05050501): F(G2B5A1R5G3) break;  |
| 566 | case tSwapEndian32(val: 0x10101010): F(R16G16B16A16) P(lRGB) break;  |
| 567 | case tSwapEndian32(val: 0x20202020): F(R32G32B32A32) P(lRGB) break;  |
| 568 | }  |
| 569 | }  |
| 570 | break;  |
| 571 | }  |
| 572 |   |
| 573 | case tImage::FourCC(ch0: 'a', ch1: 'r', ch2: 'g', ch3: 'b'):  |
| 574 | {  |
| 575 | switch (fmtMS32)  |
| 576 | {  |
| 577 | case tSwapEndian32(val: 0x01050505): F(G3B5A1R5G2) break; // LE PVR: A1 R5 G5 B5.  |
| 578 | case tSwapEndian32(val: 0x04040404): F(G4B4A4R4) break; // LE PVR: A4 R4 G4 B4.  |
| 579 | }  |
| 580 | break;  |
| 581 | }  |
| 582 |   |
| 583 | case tImage::FourCC(ch0: 'b', ch1: 'g', ch2: 'r', ch3: 'a'):  |
| 584 | {  |
| 585 | switch (fmtMS32)  |
| 586 | {  |
| 587 | case tSwapEndian32(val: 0x08080808): F(B8G8R8A8) break; // LE PVR: A1 R5 G5 B5.  |
| 588 | }  |
| 589 | break;  |
| 590 | }  |
| 591 | }  |
| 592 | #undef F  |
| 593 | #undef P  |
| 594 | }  |
| 595 |   |
| 596 | // PVR V3 files do not distinguish between lRGB and HDRa profiles -- it only supports lRGB. While they  |
| 597 | // both are linear, Tacent follows the ASTC convention of distinguishing between them by supporting > 1.0  |
| 598 | // for HDR profiles. Here, if the type is float and the profile is linear, we switch to HDR.  |
| 599 | if ((profile == tColourProfile::lRGB) && ((chanType == tChannelType::UFLOAT) || (chanType == tChannelType::SFLOAT)))  |
| 600 | profile = tColourProfile::HDRa;  |
| 601 | }  |
| 602 |   |
| 603 |   |
| 604 | void tPVR::Flip(tLayer* layer, bool horizontal)  |
| 605 | {  |
| 606 | if (!layer->IsValid() || (layer->PixelFormat != tPixelFormat::R8G8B8A8))  |
| 607 | return;  |
| 608 |   |
| 609 | int w = layer->Width;  |
| 610 | int h = layer->Height;  |
| 611 | tPixel4b* srcPixels = (tPixel4b*)layer->Data;  |
| 612 | tPixel4b* newPixels = new tPixel4b[w*h];  |
| 613 |   |
| 614 | for (int y = 0; y < h; y++)  |
| 615 | for (int x = 0; x < w; x++)  |
| 616 | newPixels[ GetIndex(x, y, w, h) ] = srcPixels[ GetIndex(x: horizontal ? w-1-x : x, y: horizontal ? y : h-1-y, w, h) ];  |
| 617 |   |
| 618 | // We modify the existing data just in case layer doesn't own it.  |
| 619 | tStd::tMemcpy(dest: layer->Data, src: newPixels, numBytes: w*h*sizeof(tPixel4b));  |
| 620 | delete[] newPixels;  |
| 621 | }  |
| 622 |   |
| 623 |   |
| 624 | void tImagePVR::Clear()  |
| 625 | {  |
| 626 | // Clear all layers no matter what they're used for.  |
| 627 | for (int layer = 0; layer < NumLayers; layer++)  |
| 628 | delete Layers[layer];  |
| 629 |   |
| 630 | // Now delete the layers array.  |
| 631 | delete[] Layers;  |
| 632 | Layers = nullptr;  |
| 633 | NumLayers = 0;  |
| 634 |   |
| 635 | States = 0; // Image will be invalid now since Valid state not set.  |
| 636 | PVRVersion = 0;  |
| 637 | AlphaMode = tAlphaMode::Unspecified;  |
| 638 | ChannelType = tChannelType::Unspecified;  |
| 639 | RowReversalOperationPerformed = false;  |
| 640 |   |
| 641 | NumSurfaces = 0; // For storing arrays of image data.  |
| 642 | NumFaces = 0; // For cubemaps.  |
| 643 | NumMipmaps = 0;  |
| 644 |   |
| 645 | Depth = 0; // Number of slices.  |
| 646 | Width = 0;  |
| 647 | Height = 0;  |
| 648 |   |
| 649 | MetaData_Orientation_Flip_X = false;  |
| 650 | MetaData_Orientation_Flip_Y = false;  |
| 651 |   |
| 652 | tBaseImage::Clear();  |
| 653 | }  |
| 654 |   |
| 655 |   |
| 656 | bool tImagePVR::Set(tPixel4b* pixels, int width, int height, bool steal)  |
| 657 | {  |
| 658 | Clear();  |
| 659 | if (!pixels || (width <= 0) || (height <= 0))  |
| 660 | return false;  |
| 661 |   |
| 662 | PixelFormatSrc = tPixelFormat::R8G8B8A8;  |
| 663 | PixelFormat = tPixelFormat::R8G8B8A8;  |
| 664 | ColourProfileSrc = tColourProfile::sRGB; // We assume pixels must be sRGB.  |
| 665 | ColourProfile = tColourProfile::sRGB;  |
| 666 |   |
| 667 | AlphaMode = tAlphaMode::Normal;  |
| 668 | ChannelType = tChannelType::UNORM;  |
| 669 | RowReversalOperationPerformed = false;  |
| 670 |   |
| 671 | NumSurfaces = 1;  |
| 672 | NumFaces = 1;  |
| 673 | NumMipmaps = 1;  |
| 674 | Depth = 1;  |
| 675 | Width = width;  |
| 676 | Height = height;  |
| 677 |   |
| 678 | NumLayers = 1;  |
| 679 | Layers = new tLayer*[1];  |
| 680 |   |
| 681 | // Order is surface, face, mipmap, slice.  |
| 682 | Layers[0] = new tLayer(tPixelFormat::R8G8B8A8, Width, Height, (uint8*)pixels, steal);  |
| 683 |   |
| 684 | SetStateBit(StateBit::Valid);  |
| 685 | return true;  |
| 686 | }  |
| 687 |   |
| 688 |   |
| 689 | bool tImagePVR::Set(tFrame* frame, bool steal)  |
| 690 | {  |
| 691 | Clear();  |
| 692 | if (!frame || !frame->IsValid())  |
| 693 | return false;  |
| 694 |   |
| 695 | PixelFormatSrc = frame->PixelFormatSrc;  |
| 696 | PixelFormat = tPixelFormat::R8G8B8A8;  |
| 697 | ColourProfileSrc = tColourProfile::sRGB; // We assume frame must be sRGB.  |
| 698 | ColourProfile = tColourProfile::sRGB;  |
| 699 |   |
| 700 | tPixel4b* pixels = frame->GetPixels(steal);  |
| 701 | Set(pixels, width: frame->Width, height: frame->Height, steal);  |
| 702 | if (steal)  |
| 703 | delete frame;  |
| 704 |   |
| 705 | return IsValid();  |
| 706 | }  |
| 707 |   |
| 708 |   |
| 709 | bool tImagePVR::Set(tPicture& picture, bool steal)  |
| 710 | {  |
| 711 | Clear();  |
| 712 | if (!picture.IsValid())  |
| 713 | return false;  |
| 714 |   |
| 715 | PixelFormatSrc = picture.PixelFormatSrc;  |
| 716 | PixelFormat = tPixelFormat::R8G8B8A8;  |
| 717 | // We don't know colour profile of tPicture.  |
| 718 |   |
| 719 | // This is worth some explanation. If steal is true the picture becomes invalid and the  |
| 720 | // 'set' call will steal the stolen pixels. If steal is false GetPixels is called and the  |
| 721 | // 'set' call will memcpy them out... which makes sure the picture is still valid after and  |
| 722 | // no-one is sharing the pixel buffer. We don't check the success of 'set' because it must  |
| 723 | // succeed if picture was valid.  |
| 724 | tPixel4b* pixels = steal ? picture.StealPixels() : picture.GetPixels();  |
| 725 | bool success = Set(pixels, width: picture.GetWidth(), height: picture.GetHeight(), steal);  |
| 726 | tAssert(success);  |
| 727 | return true;  |
| 728 | }  |
| 729 |   |
| 730 |   |
| 731 | bool tImagePVR::Load(const tString& pvrFile, const LoadParams& loadParams)  |
| 732 | {  |
| 733 | Clear();  |
| 734 | Filename = pvrFile;  |
| 735 | if (tSystem::tGetFileType(file: pvrFile) != tSystem::tFileType::PVR)  |
| 736 | {  |
| 737 | SetStateBit(StateBit::Fatal_IncorrectFileType);  |
| 738 | return false;  |
| 739 | }  |
| 740 |   |
| 741 | if (!tSystem::tFileExists(file: pvrFile))  |
| 742 | {  |
| 743 | SetStateBit(StateBit::Fatal_FileDoesNotExist);  |
| 744 | return false;  |
| 745 | }  |
| 746 |   |
| 747 | int pvrSizeBytes = 0;  |
| 748 | uint8* pvrData = (uint8*)tSystem::tLoadFile(file: pvrFile, buffer: 0, fileSize: &pvrSizeBytes);  |
| 749 | bool success = Load(pvrFileInMemory: pvrData, numBytes: pvrSizeBytes, loadParams);  |
| 750 | delete[] pvrData;  |
| 751 |   |
| 752 | return success;  |
| 753 | }  |
| 754 |   |
| 755 |   |
| 756 | bool tImagePVR::Load(const uint8* pvrData, int pvrDataSize, const LoadParams& paramsIn)  |
| 757 | {  |
| 758 | Clear();  |
| 759 | LoadParams params(paramsIn);  |
| 760 |   |
| 761 | PVRVersion = tPVR::DetermineVersionFromFirstFourBytes(bytes: pvrData);  |
| 762 | if (PVRVersion == 0)  |
| 763 | {  |
| 764 | SetStateBit(StateBit::Fatal_UnsupportedPVRFileVersion);  |
| 765 | return false;  |
| 766 | }  |
| 767 |   |
| 768 | ColourProfile = tColourProfile::Unspecified;  |
| 769 | ColourProfileSrc = tColourProfile::Unspecified;  |
| 770 | AlphaMode = tAlphaMode::Unspecified;  |
| 771 | ChannelType = tChannelType::Unspecified;  |
| 772 |   |
| 773 | int metaDataSize = 0;  |
| 774 | const uint8* metaData = nullptr;  |
| 775 | const uint8* textureData = nullptr;  |
| 776 |   |
| 777 | switch (PVRVersion)  |
| 778 | {  |
| 779 | case 1:  |
| 780 | case 2:  |
| 781 | {  |
| 782 | tPVR::HeaderV1V2* = (tPVR::HeaderV1V2*)pvrData;  |
| 783 | tPVR::GetFormatInfo_FromV1V2Header(format&: PixelFormatSrc, profile&: ColourProfileSrc, alphaMode&: AlphaMode, chanType&: ChannelType, header: *header);  |
| 784 | if (PixelFormatSrc == tPixelFormat::Invalid)  |
| 785 | {  |
| 786 | SetStateBit(StateBit::Fatal_PixelFormatNotSupported);  |
| 787 | return false;  |
| 788 | }  |
| 789 | PixelFormat = PixelFormatSrc;  |
| 790 | ColourProfile = ColourProfileSrc;  |
| 791 | NumSurfaces = (PVRVersion == 2) ? header->NumSurfaces : 1; // NumSurfaces not supported by V1 files.  |
| 792 | NumFaces = 1;  |
| 793 | NumMipmaps = header->MipMapCount;  |
| 794 | Depth = 1;  |
| 795 | Width = header->Width;  |
| 796 | Height = header->Height;  |
| 797 | if  |
| 798 | (  |
| 799 | ((PixelFormat == tPixelFormat::PVRBPP2) || (PixelFormat == tPixelFormat::PVRBPP4)) &&  |
| 800 | ((Width < 4) || (Height < 4) || !tMath::tIsPower2(v: Width) || !tMath::tIsPower2(v: Height))  |
| 801 | )  |
| 802 | {  |
| 803 | if (params.Flags & LoadFlag_StrictLoading)  |
| 804 | {  |
| 805 | SetStateBit(StateBit::Fatal_V1V2InvalidDimensionsPVRTC1);  |
| 806 | return false;  |
| 807 | }  |
| 808 | SetStateBit(StateBit::Conditional_V1V2InvalidDimensionsPVRTC1);  |
| 809 | }  |
| 810 |   |
| 811 | // Flags are LE order on disk.  |
| 812 | uint32 flags = (header->Flags1 << 24) | (header->Flags2 << 16) | (header->Flags1 << 8);  |
| 813 | bool hasMipmaps = (flags & 0x00000100) ? true : false;  |
| 814 | bool dataTwiddled = (flags & 0x00000200) ? true : false;  |
| 815 | bool containsNormalData = (flags & 0x00000400) ? true : false;  |
| 816 | bool hasABorder = (flags & 0x00000800) ? true : false;  |
| 817 | bool isACubemap = (flags & 0x00001000) ? true : false; // Every 6 surfaces is one cubemap.  |
| 818 | bool mipmapsDebugColour = (flags & 0x00002000) ? true : false;  |
| 819 | bool isAVolumeTexture = (flags & 0x00004000) ? true : false; // NumSurfaces is the number of slices (depth).  |
| 820 | bool alphaPresentInPVRTC = (flags & 0x00008000) ? true : false;  |
| 821 |   |
| 822 | // This is a bit odd, but if a PVR V1 V2 does not have mipmaps it does not set  |
| 823 | // the number of mipmaps to 1. It would be cleaner if it did, so we do it here.  |
| 824 | if (!hasMipmaps && (NumMipmaps == 0))  |
| 825 | NumMipmaps = 1;  |
| 826 |   |
| 827 | if ((!hasMipmaps && (NumMipmaps > 1)) || (hasMipmaps && (NumMipmaps <= 1)))  |
| 828 | {  |
| 829 | if (params.Flags & LoadFlag_StrictLoading)  |
| 830 | {  |
| 831 | SetStateBit(StateBit::Fatal_V1V2MipmapFlagInconsistent);  |
| 832 | return false;  |
| 833 | }  |
| 834 | SetStateBit(StateBit::Conditional_V1V2MipmapFlagInconsistent);  |
| 835 | }   |
| 836 |   |
| 837 | if (dataTwiddled)  |
| 838 | {  |
| 839 | SetStateBit(StateBit::Fatal_V1V2TwiddlingUnsupported);  |
| 840 | return false;  |
| 841 | }  |
| 842 |   |
| 843 | if (isACubemap && (NumSurfaces != 6))  |
| 844 | {  |
| 845 | SetStateBit(StateBit::Fatal_V1V2CubemapFlagInconsistent);  |
| 846 | return false;  |
| 847 | }  |
| 848 |   |
| 849 | if (isACubemap)  |
| 850 | {  |
| 851 | NumFaces = NumSurfaces;  |
| 852 | NumSurfaces = 1;  |
| 853 | }  |
| 854 | else if (isAVolumeTexture)  |
| 855 | {  |
| 856 | Depth = NumSurfaces;  |
| 857 | NumSurfaces = 1;  |
| 858 | }  |
| 859 |   |
| 860 | int bytesPerSurface = header->SurfaceSize;  |
| 861 | int bitsPerPixel = header->BitsPerPixel;  |
| 862 |   |
| 863 | // Only check the FourCC magic for V2 files.  |
| 864 | if ((PVRVersion == 2) && (header->FourCC != tImage::FourCC(ch0: 'P', ch1: 'V', ch2: 'R', ch3: '!'))) // LE 0x21525650.  |
| 865 | {  |
| 866 | if (params.Flags & LoadFlag_StrictLoading)  |
| 867 | {  |
| 868 | SetStateBit(StateBit::Fatal_V2IncorrectFourCC);  |
| 869 | return false;  |
| 870 | }  |
| 871 | else  |
| 872 | {  |
| 873 | SetStateBit(StateBit::Conditional_V2IncorrectFourCC);  |
| 874 | }  |
| 875 | }  |
| 876 |   |
| 877 | textureData = pvrData + header->HeaderSize;  |
| 878 | break;  |
| 879 | }  |
| 880 |   |
| 881 | case 3:  |
| 882 | {  |
| 883 | tPVR::HeaderV3* = (tPVR::HeaderV3*)pvrData;  |
| 884 | tPVR::GetFormatInfo_FromV3Header(format&: PixelFormatSrc, profile&: ColourProfileSrc, alphaMode&: AlphaMode, chanType&: ChannelType, header: *header);  |
| 885 | if (PixelFormatSrc == tPixelFormat::Invalid)  |
| 886 | {  |
| 887 | SetStateBit(StateBit::Fatal_PixelFormatNotSupported);  |
| 888 | return false;  |
| 889 | }  |
| 890 | PixelFormat = PixelFormatSrc;  |
| 891 | ColourProfile = ColourProfileSrc;  |
| 892 | NumSurfaces = header->NumSurfaces;  |
| 893 | NumFaces = header->NumFaces;  |
| 894 | NumMipmaps = header->NumMipmaps;  |
| 895 | Depth = header->Depth;  |
| 896 | Width = header->Width;  |
| 897 | Height = header->Height;  |
| 898 | metaDataSize = header->MetaDataSize;  |
| 899 | metaData = pvrData + sizeof(tPVR::HeaderV3);  |
| 900 | textureData = metaData + metaDataSize;  |
| 901 | break;  |
| 902 | }  |
| 903 |   |
| 904 | default:  |
| 905 | SetStateBit(StateBit::Fatal_UnsupportedPVRFileVersion);  |
| 906 | return false;  |
| 907 | }  |
| 908 |   |
| 909 | ParseMetaData(metaData, metaDataSize);  |
| 910 |   |
| 911 | NumLayers = NumSurfaces * NumFaces * NumMipmaps * Depth;  |
| 912 | if (NumLayers <= 0)  |
| 913 | {  |
| 914 | SetStateBit(StateBit::Fatal_BadHeaderData);  |
| 915 | return false;  |
| 916 | }  |
| 917 |   |
| 918 | Layers = new tLayer*[NumLayers];  |
| 919 | for (int l = 0; l < NumLayers; l++)  |
| 920 | Layers[l] = nullptr;  |
| 921 |   |
| 922 | // These return 1 for packed formats. This allows us to treat BC, ASTC, Packed, etc all the same way.  |
| 923 | int blockW = tGetBlockWidth(PixelFormatSrc);  |
| 924 | int blockH = tGetBlockHeight(PixelFormatSrc);  |
| 925 | int bytesPerBlock = tImage::tGetBytesPerBlock(PixelFormatSrc);  |
| 926 | const uint8* srcPixelData = textureData;  |
| 927 |   |
| 928 | // The gamma-compression load flags only apply when decoding. If the gamma mode is auto, we determine here  |
| 929 | // whether to apply sRGB compression. If the space is linear and a format that often encodes colours, we apply it.  |
| 930 | if (params.Flags & LoadFlag_AutoGamma)  |
| 931 | {  |
| 932 | // Clear all related flags.  |
| 933 | params.Flags &= ~(LoadFlag_AutoGamma | LoadFlag_SRGBCompression | LoadFlag_GammaCompression);  |
| 934 | if (tMath::tIsProfileLinearInRGB(profile: ColourProfileSrc))  |
| 935 | {  |
| 936 | // Just cuz it's linear doesn't mean we want to gamma transform. Some formats should be kept linear.  |
| 937 | if  |
| 938 | (  |
| 939 | (PixelFormatSrc != tPixelFormat::A8) && (PixelFormatSrc != tPixelFormat::A8L8) &&  |
| 940 | (PixelFormatSrc != tPixelFormat::BC4ATI1U) && (PixelFormatSrc != tPixelFormat::BC4ATI1S) &&  |
| 941 | (PixelFormatSrc != tPixelFormat::BC5ATI2U) && (PixelFormatSrc != tPixelFormat::BC5ATI2S)  |
| 942 | )  |
| 943 | {  |
| 944 | params.Flags |= LoadFlag_SRGBCompression;  |
| 945 | }  |
| 946 | }  |
| 947 | }  |
| 948 |   |
| 949 | // The ordering is different depending on V1V2 or V3. We have already checked for unsupported versions.  |
| 950 | // Start with a V1V2. NumFaces and Depth have already been adjusted.  |
| 951 | if ((PVRVersion == 1) || (PVRVersion == 2))  |
| 952 | {  |
| 953 | for (int surf = 0; surf < NumSurfaces; surf++)  |
| 954 | {  |
| 955 | for (int face = 0; face < NumFaces; face++)  |
| 956 | {  |
| 957 | int width = Width;  |
| 958 | int height = Height;  |
| 959 | for (int mip = 0; mip < NumMipmaps; mip++)  |
| 960 | {  |
| 961 | int numBlocksW = tGetNumBlocks(blockWH: blockW, imageWH: width);  |
| 962 | int numBlocksH = tGetNumBlocks(blockWH: blockH, imageWH: height);  |
| 963 | int numBlocks = numBlocksW*numBlocksH;  |
| 964 | int numBytes = numBlocks * bytesPerBlock;  |
| 965 | for (int slice = 0; slice < Depth; slice++)  |
| 966 | {  |
| 967 | int index = LayerIdx(surf, face, mip, depth: slice);  |
| 968 | tAssert(Layers[index] == nullptr);  |
| 969 | tLayer* newLayer = CreateNewLayer(params, srcPixelData, numBytes, width, height);  |
| 970 | if (!newLayer)  |
| 971 | {  |
| 972 | Clear();  |
| 973 | return false;  |
| 974 | }  |
| 975 | Layers[index] = newLayer;  |
| 976 | srcPixelData += numBytes;  |
| 977 | }  |
| 978 | width /= 2; tMath::tiClampMin(val&: width, min: 1);  |
| 979 | height /= 2; tMath::tiClampMin(val&: height, min: 1);  |
| 980 | }  |
| 981 | }  |
| 982 | }  |
| 983 | }  |
| 984 | else  |
| 985 | {  |
| 986 | tAssert(PVRVersion == 3);  |
| 987 | int width = Width;  |
| 988 | int height = Height;  |
| 989 | for (int mip = 0; mip < NumMipmaps; mip++)  |
| 990 | {  |
| 991 | int numBlocksW = tGetNumBlocks(blockWH: blockW, imageWH: width);  |
| 992 | int numBlocksH = tGetNumBlocks(blockWH: blockH, imageWH: height);  |
| 993 | int numBlocks = numBlocksW*numBlocksH;  |
| 994 | int numBytes = numBlocks * bytesPerBlock;  |
| 995 | for (int surf = 0; surf < NumSurfaces; surf++)  |
| 996 | {  |
| 997 | for (int face = 0; face < NumFaces; face++)  |
| 998 | {  |
| 999 | for (int slice = 0; slice < Depth; slice++)  |
| 1000 | {  |
| 1001 | int index = LayerIdx(surf, face, mip, depth: slice);  |
| 1002 | tAssert(Layers[index] == nullptr);  |
| 1003 |   |
| 1004 | // CreateNewLayer is the workhorse. It may or may not decode depending on params.  |
| 1005 | tLayer* newLayer = CreateNewLayer(params, srcPixelData, numBytes, width, height);  |
| 1006 | if (!newLayer)  |
| 1007 | {  |
| 1008 | Clear();  |
| 1009 | return false;  |
| 1010 | }  |
| 1011 | Layers[index] = newLayer;  |
| 1012 | srcPixelData += numBytes;  |
| 1013 | }  |
| 1014 | }  |
| 1015 | }  |
| 1016 | width /= 2; tMath::tiClampMin(val&: width, min: 1);  |
| 1017 | height /= 2; tMath::tiClampMin(val&: height, min: 1);  |
| 1018 | }  |
| 1019 | }  |
| 1020 |   |
| 1021 | // The flips and rotates below do not clear the pixel format.  |
| 1022 | if ((params.Flags & LoadFlag_MetaDataOrient) && (MetaData_Orientation_Flip_X || MetaData_Orientation_Flip_Y))  |
| 1023 | {  |
| 1024 | // The order of the loops doesn't matter here. We just need to hit every layer.  |
| 1025 | for (int surf = 0; surf < NumSurfaces; surf++)  |
| 1026 | {  |
| 1027 | for (int face = 0; face < NumFaces; face++)  |
| 1028 | {  |
| 1029 | for (int mip = 0; mip < NumMipmaps; mip++)  |
| 1030 | {  |
| 1031 | for (int slice = 0; slice < Depth; slice++)  |
| 1032 | {  |
| 1033 | int index = LayerIdx(surf, face, mip, depth: slice);  |
| 1034 | tLayer* layer = Layers[index];  |
| 1035 | tAssert(layer != nullptr);  |
| 1036 | if (MetaData_Orientation_Flip_X)  |
| 1037 | tPVR::Flip(layer, horizontal: true);  |
| 1038 | if (MetaData_Orientation_Flip_Y)  |
| 1039 | tPVR::Flip(layer, horizontal: false);  |
| 1040 | }  |
| 1041 | }  |
| 1042 | }  |
| 1043 | }  |
| 1044 | }  |
| 1045 |   |
| 1046 | // If we were asked to decode, set the current PixelFormat to the decoded format.  |
| 1047 | // Otherwise set the current PixelFormat to be the same as the original PixelFormatSrc.  |
| 1048 | PixelFormat = (params.Flags & LoadFlag_Decode) ? tPixelFormat::R8G8B8A8 : PixelFormatSrc;  |
| 1049 |   |
| 1050 | if (params.Flags & LoadFlag_SRGBCompression) ColourProfile = tColourProfile::sRGB;  |
| 1051 | if (params.Flags & LoadFlag_GammaCompression) ColourProfile = tColourProfile::gRGB;  |
| 1052 |   |
| 1053 | SetStateBit(StateBit::Valid);  |
| 1054 | tAssert(IsValid());  |
| 1055 | return true;  |
| 1056 | }  |
| 1057 |   |
| 1058 |   |
| 1059 | int tImagePVR::LayerIdx(int surf, int face, int mip, int depth) const  |
| 1060 | {  |
| 1061 | int index = depth + mip*(Depth) + face*(NumMipmaps*Depth) + surf*(NumFaces*NumMipmaps*Depth);  |
| 1062 | tAssert(index < NumLayers);  |
| 1063 | return index;  |
| 1064 | }  |
| 1065 |   |
| 1066 |   |
| 1067 | tLayer* tImagePVR::CreateNewLayer(const LoadParams& params, const uint8* srcPixelData, int numBytes, int width, int height)  |
| 1068 | {  |
| 1069 | tLayer* layer = new tLayer();  |
| 1070 |   |
| 1071 | bool reverseRowOrderRequested = params.Flags & LoadFlag_ReverseRowOrder;  |
| 1072 | RowReversalOperationPerformed = false;  |
| 1073 |   |
| 1074 | // For images whose height is not a multiple of the block size it makes it tricky when deoompressing to do  |
| 1075 | // the more efficient row reversal here, so we defer it. Packed formats have a block height of 1. Only BC  |
| 1076 | // and astc have non-unity block dimensins.  |
| 1077 | bool doRowReversalBeforeDecode = false;  |
| 1078 | if (reverseRowOrderRequested)  |
| 1079 | {  |
| 1080 | bool canDo = true;  |
| 1081 | if (!CanReverseRowData(PixelFormatSrc, height))  |
| 1082 | canDo = false;  |
| 1083 | doRowReversalBeforeDecode = canDo;  |
| 1084 | }  |
| 1085 |   |
| 1086 | if (doRowReversalBeforeDecode)  |
| 1087 | {  |
| 1088 | int blockW = tGetBlockWidth(PixelFormatSrc);  |
| 1089 | int blockH = tGetBlockHeight(PixelFormatSrc);  |
| 1090 | int numBlocksW = tGetNumBlocks(blockWH: blockW, imageWH: width);  |
| 1091 | int numBlocksH = tGetNumBlocks(blockWH: blockH, imageWH: height);  |
| 1092 |   |
| 1093 | uint8* reversedPixelData = tImage::CreateReversedRowData(pixelData: srcPixelData, pixelDataFormat: PixelFormat, numBlocksW, numBlocksH);  |
| 1094 | tAssert(reversedPixelData);  |
| 1095 |   |
| 1096 | // We can simply get the layer to steal the memory (the last true arg).  |
| 1097 | layer->Set(format: PixelFormatSrc, width, height, data: reversedPixelData, steal: true);  |
| 1098 | }  |
| 1099 | else  |
| 1100 | {  |
| 1101 | // Not reversing. Use the current srcPixelData. Note that steal is false here so the data  |
| 1102 | // is both copied and owned by the new tLayer. The srcPixelData gets deleted later.  |
| 1103 | layer->Set(format: PixelFormatSrc, width, height, data: (uint8*)srcPixelData, steal: false);  |
| 1104 | }  |
| 1105 | if (doRowReversalBeforeDecode)  |
| 1106 | RowReversalOperationPerformed = true;  |
| 1107 |   |
| 1108 | // Not asked to decode. We're basically done.  |
| 1109 | if (!(params.Flags & LoadFlag_Decode))  |
| 1110 | {  |
| 1111 | if (reverseRowOrderRequested && !RowReversalOperationPerformed)  |
| 1112 | SetStateBit(StateBit::Conditional_CouldNotFlipRows);  |
| 1113 |   |
| 1114 | return layer;  |
| 1115 | }  |
| 1116 |   |
| 1117 | // We were asked to decode if we made it here.  |
| 1118 | // Spread only applies to single-channel (R-only or L-only) formats.  |
| 1119 | bool spread = params.Flags & LoadFlag_SpreadLuminance;  |
| 1120 |   |
| 1121 | // Decode to 32-bit RGBA.  |
| 1122 | bool didRowReversalAfterDecode = false;  |
| 1123 |   |
| 1124 | // At the end of decoding _either_ decoded4b _or_ decoded4f will be valid, not both.  |
| 1125 | // The decoded4b format used for LDR images.  |
| 1126 | // The decoded4f format used for HDR images.  |
| 1127 | tColour4b* decoded4b = nullptr;  |
| 1128 | tColour4f* decoded4f = nullptr;  |
| 1129 | tAssert(layer->GetDataSize() == numBytes);  |
| 1130 | DecodeResult result = DecodePixelData  |
| 1131 | (  |
| 1132 | layer->PixelFormat, data: layer->Data, dataSize: numBytes,  |
| 1133 | width, height, dstLDR&: decoded4b, dstHDR&: decoded4f, tColourProfile::Auto, RGBM_RGBD_MaxRange: params.MaxRange  |
| 1134 | );  |
| 1135 |   |
| 1136 | if (result != DecodeResult::Success)  |
| 1137 | {  |
| 1138 | switch (result)  |
| 1139 | {  |
| 1140 | case DecodeResult::PackedDecodeError: SetStateBit(StateBit::Fatal_PackedDecodeError); break;  |
| 1141 | case DecodeResult::BlockDecodeError: SetStateBit(StateBit::Fatal_BCDecodeError); break;  |
| 1142 | case DecodeResult::ASTCDecodeError: SetStateBit(StateBit::Fatal_ASTCDecodeError); break;  |
| 1143 | case DecodeResult::PVRDecodeError: SetStateBit(StateBit::Fatal_PVRDecodeError); break;  |
| 1144 | default: SetStateBit(StateBit::Fatal_PixelFormatNotSupported); break;  |
| 1145 | }  |
| 1146 | delete layer;  |
| 1147 | return nullptr;  |
| 1148 | }  |
| 1149 |   |
| 1150 | // Apply any decode flags.  |
| 1151 | tAssert(decoded4f || decoded4b);  |
| 1152 | bool flagTone = (params.Flags & tImagePVR::LoadFlag_ToneMapExposure) ? true : false;  |
| 1153 | bool flagSRGB = (params.Flags & tImagePVR::LoadFlag_SRGBCompression) ? true : false;  |
| 1154 | bool flagGama = (params.Flags & tImagePVR::LoadFlag_GammaCompression)? true : false;  |
| 1155 | if (decoded4f && (flagTone || flagSRGB || flagGama))  |
| 1156 | {  |
| 1157 | for (int p = 0; p < width*height; p++)  |
| 1158 | {  |
| 1159 | tColour4f& colour = decoded4f[p];  |
| 1160 | if (flagTone)  |
| 1161 | colour.TonemapExposure(exposure: params.Exposure, chans: tCompBit_RGB);  |
| 1162 | if (flagSRGB)  |
| 1163 | colour.LinearToSRGB(chans: tCompBit_RGB);  |
| 1164 | if (flagGama)  |
| 1165 | colour.LinearToGamma(gamma: params.Gamma, chans: tCompBit_RGB);  |
| 1166 | }  |
| 1167 | }  |
| 1168 | if (decoded4b && (flagSRGB || flagGama))  |
| 1169 | {  |
| 1170 | for (int p = 0; p < width*height; p++)  |
| 1171 | {  |
| 1172 | tColour4f colour(decoded4b[p]);  |
| 1173 | if (flagSRGB)  |
| 1174 | colour.LinearToSRGB(chans: tCompBit_RGB);  |
| 1175 | if (flagGama)  |
| 1176 | colour.LinearToGamma(gamma: params.Gamma, chans: tCompBit_RGB);  |
| 1177 | decoded4b[p].SetR(colour.R);  |
| 1178 | decoded4b[p].SetG(colour.G);  |
| 1179 | decoded4b[p].SetB(colour.B);  |
| 1180 | }  |
| 1181 | }  |
| 1182 |   |
| 1183 | // Update the layer with the 32-bit RGBA decoded data. If the data was HDR (float)  |
| 1184 | // convert it to 32 bit. Start by getting rid of the existing layer pixel data.  |
| 1185 | delete[] layer->Data;  |
| 1186 | if (decoded4f)  |
| 1187 | {  |
| 1188 | tAssert(!decoded4b);  |
| 1189 | decoded4b = new tColour4b[width*height];  |
| 1190 | for (int p = 0; p < width*height; p++)  |
| 1191 | decoded4b[p].Set(decoded4f[p]);  |
| 1192 | delete[] decoded4f;  |
| 1193 | }  |
| 1194 |   |
| 1195 | // Possibly spread the L/Red channel.  |
| 1196 | if (spread && tIsLuminanceFormat(format: layer->PixelFormat))  |
| 1197 | {  |
| 1198 | for (int p = 0; p < width*height; p++)  |
| 1199 | {  |
| 1200 | decoded4b[p].G = decoded4b[p].R;  |
| 1201 | decoded4b[p].B = decoded4b[p].R;  |
| 1202 | }  |
| 1203 | }  |
| 1204 |   |
| 1205 | layer->Data = (uint8*)decoded4b;  |
| 1206 | layer->PixelFormat = tPixelFormat::R8G8B8A8;  |
| 1207 |   |
| 1208 | // We've got one more chance to reverse the rows here (if we still need to) because we were asked to decode.  |
| 1209 | if (reverseRowOrderRequested && !RowReversalOperationPerformed && (layer->PixelFormat == tPixelFormat::R8G8B8A8))  |
| 1210 | {  |
| 1211 | // This shouldn't ever fail. Too easy to reverse RGBA 32-bit.  |
| 1212 | uint8* reversedRowData = tImage::CreateReversedRowData(pixelData: layer->Data, pixelDataFormat: layer->PixelFormat, numBlocksW: width, numBlocksH: height);  |
| 1213 | tAssert(reversedRowData);  |
| 1214 | delete[] layer->Data;  |
| 1215 | layer->Data = reversedRowData;  |
| 1216 | didRowReversalAfterDecode = true;  |
| 1217 | }  |
| 1218 |   |
| 1219 | if (reverseRowOrderRequested && !RowReversalOperationPerformed && didRowReversalAfterDecode)  |
| 1220 | RowReversalOperationPerformed = true;  |
| 1221 |   |
| 1222 | if (reverseRowOrderRequested && !RowReversalOperationPerformed)  |
| 1223 | SetStateBit(StateBit::Conditional_CouldNotFlipRows);  |
| 1224 |   |
| 1225 | return layer;  |
| 1226 | }  |
| 1227 |   |
| 1228 |   |
| 1229 | bool tImagePVR::ParseMetaData(const uint8* metaData, int metaDataSize)  |
| 1230 | {  |
| 1231 | if (!metaData || (metaDataSize <= 0))  |
| 1232 | return false;  |
| 1233 |   |
| 1234 | while (metaDataSize >= 12)  |
| 1235 | {  |
| 1236 | uint32 fourCC = *((uint32*)metaData); metaData += 4; metaDataSize -= 4;  |
| 1237 | uint32 key = *((uint32*)metaData); metaData += 4; metaDataSize -= 4;  |
| 1238 | uint32 dataSize = *((uint32*)metaData); metaData += 4; metaDataSize -= 4;  |
| 1239 |   |
| 1240 | switch (fourCC)  |
| 1241 | {  |
| 1242 | case tImage::FourCC(ch0: 'P', ch1: 'V', ch2: 'R', ch3: 3):  |
| 1243 | {  |
| 1244 | // Most of the built-in meta-data does not affect display of image. Indeed nearly all  |
| 1245 | // of the built-in meta-data is not even supported by the official PVRTexTool as of 2023_12_30.  |
| 1246 | switch (key)  |
| 1247 | {  |
| 1248 | case tPVR::PVR3KEY_ATLAS: break;  |
| 1249 | case tPVR::PVR3KEY_NORMALMAP: break;  |
| 1250 | case tPVR::PVR3KEY_CUBEMAP: break;  |
| 1251 |   |
| 1252 | case tPVR::PVR3KEY_ORIENTATION:  |
| 1253 | // Three bytes, one for each axis in the order X, Y, Z.   |
| 1254 | // X == 0: Increases right. X != 0: Increases left.  |
| 1255 | // Y == 0: Increases down. Y != 0: Increases up.  |
| 1256 | // X == 0: Increases inward. Z != 0: Increases outward.  |
| 1257 | if (dataSize != 3)  |
| 1258 | break;  |
| 1259 | MetaData_Orientation_Flip_X = metaData[0] ? true : false;  |
| 1260 | MetaData_Orientation_Flip_Y = metaData[1] ? true : false;  |
| 1261 | break;  |
| 1262 |   |
| 1263 | case tPVR::PVR3KEY_BORDER: break;  |
| 1264 | case tPVR::PVR3KEY_PADDING: break;  |
| 1265 | case tPVR::PVR3KEY_UNKNOWN: break;  |
| 1266 | }  |
| 1267 | break;  |
| 1268 | }  |
| 1269 | }  |
| 1270 |   |
| 1271 | metaData += dataSize; metaDataSize -= dataSize;  |
| 1272 | }  |
| 1273 |   |
| 1274 | return true;  |
| 1275 | }  |
| 1276 |   |
| 1277 |   |
| 1278 | const char* tImagePVR::GetStateDesc(StateBit state)  |
| 1279 | {  |
| 1280 | return StateDescriptions[int(state)];  |
| 1281 | }  |
| 1282 |   |
| 1283 |   |
| 1284 | tFrame* tImagePVR::GetFrame(bool steal)  |
| 1285 | {  |
| 1286 | // Data must be decoded for this to work.  |
| 1287 | tLayer* layer = Layers ? Layers[ LayerIdx(surf: 0) ] : nullptr;  |
| 1288 | if (!IsValid() || (PixelFormat != tPixelFormat::R8G8B8A8) || (layer == nullptr))  |
| 1289 | return nullptr;  |
| 1290 |   |
| 1291 | tFrame* frame = new tFrame();  |
| 1292 | frame->Width = layer->Width;  |
| 1293 | frame->Height = layer->Height;  |
| 1294 | frame->PixelFormatSrc = PixelFormatSrc;  |
| 1295 |   |
| 1296 | if (steal)  |
| 1297 | {  |
| 1298 | frame->Pixels = (tPixel4b*)layer->StealData();  |
| 1299 | delete layer;  |
| 1300 | Layers[ LayerIdx(surf: 0) ] = nullptr;  |
| 1301 | }  |
| 1302 | else  |
| 1303 | {  |
| 1304 | frame->Pixels = new tPixel4b[frame->Width * frame->Height];  |
| 1305 | tStd::tMemcpy(dest: frame->Pixels, src: (tPixel4b*)layer->Data, numBytes: frame->Width * frame->Height * sizeof(tPixel4b));  |
| 1306 | }  |
| 1307 |   |
| 1308 | return frame;  |
| 1309 | }  |
| 1310 |   |
| 1311 |   |
| 1312 | bool tImagePVR::StealLayers(tList<tLayer>& layers)  |
| 1313 | {  |
| 1314 | if (!IsValid() || IsCubemap())  |
| 1315 | return false;  |
| 1316 |   |
| 1317 | for (int layer = 0; layer < NumLayers; layer++)  |
| 1318 | {  |
| 1319 | layers.Append(item: Layers[layer]);  |
| 1320 | Layers[layer] = nullptr;  |
| 1321 | }  |
| 1322 |   |
| 1323 | Clear();  |
| 1324 | return true;  |
| 1325 | }  |
| 1326 |   |
| 1327 |   |
| 1328 | int tImagePVR::GetLayers(tList<tLayer>& layers) const  |
| 1329 | {  |
| 1330 | if (!IsValid() || IsCubemap())  |
| 1331 | return 0;  |
| 1332 |   |
| 1333 | for (int layer = 0; layer < NumLayers; layer++)  |
| 1334 | layers.Append(item: Layers[layer]);  |
| 1335 |   |
| 1336 | return NumLayers;  |
| 1337 | }  |
| 1338 |   |
| 1339 |   |
| 1340 | int tImagePVR::StealCubemapLayers(tList<tLayer> layerLists[tFaceIndex_NumFaces], uint32 faceFlags)  |
| 1341 | {  |
| 1342 | if (!IsValid() || !IsCubemap() || !faceFlags)  |
| 1343 | return 0;  |
| 1344 |   |
| 1345 | int faceCount = 0;  |
| 1346 | for (int face = 0; face < tFaceIndex_NumFaces; face++)  |
| 1347 | {  |
| 1348 | uint32 faceFlag = 1 << face;  |
| 1349 | if (!(faceFlag & faceFlags))  |
| 1350 | continue;  |
| 1351 |   |
| 1352 | tList<tLayer>& dstLayers = layerLists[face];  |
| 1353 | for (int mip = 0; mip < NumMipmaps; mip++)  |
| 1354 | {  |
| 1355 | int index = LayerIdx(surf: 0, face, mip, depth: 0);  |
| 1356 | tLayer* layer = Layers[index];  |
| 1357 | dstLayers.Append(item: layer);  |
| 1358 | Layers[index] = nullptr;  |
| 1359 | }  |
| 1360 | faceCount++;  |
| 1361 | }  |
| 1362 |   |
| 1363 | Clear();  |
| 1364 | return faceCount;  |
| 1365 | }  |
| 1366 |   |
| 1367 |   |
| 1368 | int tImagePVR::GetCubemapLayers(tList<tLayer> layerLists[tFaceIndex_NumFaces], uint32 faceFlags) const  |
| 1369 | {  |
| 1370 | if (!IsValid() || !IsCubemap() || !faceFlags)  |
| 1371 | return 0;  |
| 1372 |   |
| 1373 | int faceCount = 0;  |
| 1374 | for (int face = 0; face < tFaceIndex_NumFaces; face++)  |
| 1375 | {  |
| 1376 | uint32 faceFlag = 1 << face;  |
| 1377 | if (!(faceFlag & faceFlags))  |
| 1378 | continue;  |
| 1379 |   |
| 1380 | tList<tLayer>& dstLayers = layerLists[face];  |
| 1381 | for (int mip = 0; mip < NumMipmaps; mip++)  |
| 1382 | {  |
| 1383 | int index = LayerIdx(surf: 0, face, mip, depth: 0);  |
| 1384 | tLayer* layer = Layers[index];  |
| 1385 | dstLayers.Append(item: layer);  |
| 1386 | }  |
| 1387 |   |
| 1388 | faceCount++;  |
| 1389 | }  |
| 1390 |   |
| 1391 | return faceCount;  |
| 1392 | }  |
| 1393 |   |
| 1394 |   |
| 1395 | const char* tImagePVR::StateDescriptions[] =  |
| 1396 | {  |
| 1397 | "Valid" ,  |
| 1398 | "Conditional Valid. Image rows could not be flipped." ,  |
| 1399 | "Conditional Valid. Pixel format specification ill-formed." ,  |
| 1400 | "Conditional Valid. V2 Magic FourCC Incorrect." ,  |
| 1401 | "Conditional Valid. V1 V2 PVRTC1 non-POT dimension or less than 4." ,  |
| 1402 | "Conditional Valid. V1 V2 Mipmap flag doesn't match mipmap count." ,  |
| 1403 | "Fatal Error. File does not exist." ,  |
| 1404 | "Fatal Error. Incorrect file type. Must be a PVR file." ,  |
| 1405 | "Fatal Error. Filesize incorrect." ,  |
| 1406 | "Fatal Error. V2 Magic FourCC Incorrect." ,  |
| 1407 | "Fatal Error. Incorrect PVR header size." ,  |
| 1408 | "Fatal Error. Bad PVR header data." ,  |
| 1409 | "Fatal Error. Unsupported PVR file version." ,  |
| 1410 | "Fatal Error. V1 V2 PVRTC1 non-POT dimension or less than 4." ,  |
| 1411 | "Fatal Error. Pixel format header size incorrect." ,  |
| 1412 | "Fatal Error. Pixel format specification incorrect." ,  |
| 1413 | "Fatal Error. Unsupported pixel format." ,  |
| 1414 | "Fatal Error. V1 V2 Mipmap flag doesn't match mipmap count." ,  |
| 1415 | "Fatal Error. V1 V2 Cubemap flag doesn't match map count." ,  |
| 1416 | "Fatal Error. V1 V2 Twiddled data not supported." ,  |
| 1417 | "Fatal Error. Unable to decode packed pixels." ,  |
| 1418 | "Fatal Error. Unable to decode BC pixels." ,  |
| 1419 | "Fatal Error. Unable to decode ASTC pixels." ,  |
| 1420 | "Fatal Error. Unable to decode PVR pixels."   |
| 1421 | };  |
| 1422 | tStaticAssert(tNumElements(tImagePVR::StateDescriptions) == int(tImagePVR::StateBit::NumStateBits));  |
| 1423 | tStaticAssert(int(tImagePVR::StateBit::NumStateBits) <= int(tImagePVR::StateBit::MaxStateBits));  |
| 1424 |   |
| 1425 |   |
| 1426 | }  |
| 1427 | |