• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PredatorCZ / PreCore / 476

25 Aug 2023 03:59PM UTC coverage: 55.317% (+2.7%) from 52.584%
476

push

github-actions-ci

PredatorCZ
add texel context

1382 of 1382 new or added lines in 8 files covered. (100.0%)

4146 of 7495 relevant lines covered (55.32%)

8934.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.25
/src/app/pvr_decompress.cpp
1
/*!
2
\brief Implementation of the Texture Decompression functions.
3
\file PVRCore/texture/PVRTDecompress.cpp
4
\author PowerVR by Imagination, Developer Technology Team
5
\copyright Copyright (c) Imagination Technologies Limited.
6
*/
7
//!\cond NO_DOXYGEN
8

9
#include <cstdlib>
10
#include <cstdio>
11
#include <climits>
12
#include <cmath>
13
#include <algorithm>
14
#include <cstring>
15
#include "pvr_decompress.hpp"
16
#include <cassert>
17
#include <vector>
18

19
namespace pvr {
20
enum
21
{
22
        ETC_MIN_TEXWIDTH = 4,
23
        ETC_MIN_TEXHEIGHT = 4,
24
        DXT_MIN_TEXWIDTH = 4,
25
        DXT_MIN_TEXHEIGHT = 4,
26
};
27

28
struct Pixel32
29
{
30
        uint8_t red, green, blue, alpha;
31
};
32

33
struct Pixel128S
34
{
35
        int32_t red, green, blue, alpha;
36
};
37

38
struct PVRTCWord
39
{
40
        uint32_t modulationData;
41
        uint32_t colorData;
42
};
43

44
struct PVRTCWordIndices
45
{
46
        int P[2], Q[2], R[2], S[2];
47
};
48

49
static Pixel32 getColorA(uint32_t colorData)
49,152✔
50
{
51
        Pixel32 color;
52

53
        // Opaque Color Mode - RGB 554
54
        if ((colorData & 0x8000) != 0)
49,152✔
55
        {
56
                color.red = static_cast<uint8_t>((colorData & 0x7c00) >> 10); // 5->5 bits
2,760✔
57
                color.green = static_cast<uint8_t>((colorData & 0x3e0) >> 5); // 5->5 bits
2,760✔
58
                color.blue = static_cast<uint8_t>(colorData & 0x1e) | ((colorData & 0x1e) >> 4); // 4->5 bits
2,760✔
59
                color.alpha = static_cast<uint8_t>(0xf); // 0->4 bits
60
        }
61
        // Transparent Color Mode - ARGB 3443
62
        else
63
        {
64
                color.red = static_cast<uint8_t>((colorData & 0xf00) >> 7) | ((colorData & 0xf00) >> 11); // 4->5 bits
46,392✔
65
                color.green = static_cast<uint8_t>((colorData & 0xf0) >> 3) | ((colorData & 0xf0) >> 7); // 4->5 bits
46,392✔
66
                color.blue = static_cast<uint8_t>((colorData & 0xe) << 1) | ((colorData & 0xe) >> 2); // 3->5 bits
46,392✔
67
                color.alpha = static_cast<uint8_t>((colorData & 0x7000) >> 11); // 3->4 bits - note 0 at right
46,392✔
68
        }
69

70
        return color;
49,152✔
71
}
72

73
static Pixel32 getColorB(uint32_t colorData)
49,152✔
74
{
75
        Pixel32 color;
76

77
        // Opaque Color Mode - RGB 555
78
        if (colorData & 0x80000000)
49,152✔
79
        {
80
                color.red = static_cast<uint8_t>((colorData & 0x7c000000) >> 26); // 5->5 bits
1,784✔
81
                color.green = static_cast<uint8_t>((colorData & 0x3e00000) >> 21); // 5->5 bits
1,784✔
82
                color.blue = static_cast<uint8_t>((colorData & 0x1f0000) >> 16); // 5->5 bits
1,784✔
83
                color.alpha = static_cast<uint8_t>(0xf); // 0 bits
84
        }
85
        // Transparent Color Mode - ARGB 3444
86
        else
87
        {
88
                color.red = static_cast<uint8_t>(((colorData & 0xf000000) >> 23) | ((colorData & 0xf000000) >> 27)); // 4->5 bits
47,368✔
89
                color.green = static_cast<uint8_t>(((colorData & 0xf00000) >> 19) | ((colorData & 0xf00000) >> 23)); // 4->5 bits
47,368✔
90
                color.blue = static_cast<uint8_t>(((colorData & 0xf0000) >> 15) | ((colorData & 0xf0000) >> 19)); // 4->5 bits
47,368✔
91
                color.alpha = static_cast<uint8_t>((colorData & 0x70000000) >> 27); // 3->4 bits - note 0 at right
47,368✔
92
        }
93

94
        return color;
49,152✔
95
}
96

97
static void interpolateColors(Pixel32 P, Pixel32 Q, Pixel32 R, Pixel32 S, Pixel128S* pPixel, uint8_t bpp)
24,576✔
98
{
99
        uint32_t wordWidth = 4;
100
        uint32_t wordHeight = 4;
101
        if (bpp == 2) { wordWidth = 8; }
24,576✔
102

103
        // Convert to int 32.
104
        Pixel128S hP = { static_cast<int32_t>(P.red), static_cast<int32_t>(P.green), static_cast<int32_t>(P.blue), static_cast<int32_t>(P.alpha) };
24,576✔
105
        Pixel128S hQ = { static_cast<int32_t>(Q.red), static_cast<int32_t>(Q.green), static_cast<int32_t>(Q.blue), static_cast<int32_t>(Q.alpha) };
24,576✔
106
        Pixel128S hR = { static_cast<int32_t>(R.red), static_cast<int32_t>(R.green), static_cast<int32_t>(R.blue), static_cast<int32_t>(R.alpha) };
24,576✔
107
        Pixel128S hS = { static_cast<int32_t>(S.red), static_cast<int32_t>(S.green), static_cast<int32_t>(S.blue), static_cast<int32_t>(S.alpha) };
24,576✔
108

109
        // Get vectors.
110
        Pixel128S QminusP = { hQ.red - hP.red, hQ.green - hP.green, hQ.blue - hP.blue, hQ.alpha - hP.alpha };
24,576✔
111
        Pixel128S SminusR = { hS.red - hR.red, hS.green - hR.green, hS.blue - hR.blue, hS.alpha - hR.alpha };
24,576✔
112

113
        // Multiply colors.
114
        hP.red *= wordWidth;
24,576✔
115
        hP.green *= wordWidth;
24,576✔
116
        hP.blue *= wordWidth;
24,576✔
117
        hP.alpha *= wordWidth;
24,576✔
118
        hR.red *= wordWidth;
24,576✔
119
        hR.green *= wordWidth;
24,576✔
120
        hR.blue *= wordWidth;
24,576✔
121
        hR.alpha *= wordWidth;
24,576✔
122

123
        if (bpp == 2)
24,576✔
124
        {
125
                // Loop through pixels to achieve results.
126
                for (uint32_t x = 0; x < wordWidth; x++)
73,728✔
127
                {
128
                        Pixel128S result = { 4 * hP.red, 4 * hP.green, 4 * hP.blue, 4 * hP.alpha };
65,536✔
129
                        Pixel128S dY = { hR.red - hP.red, hR.green - hP.green, hR.blue - hP.blue, hR.alpha - hP.alpha };
65,536✔
130

131
                        for (uint32_t y = 0; y < wordHeight; y++)
327,680✔
132
                        {
133
                                pPixel[y * wordWidth + x].red = static_cast<int32_t>((result.red >> 7) + (result.red >> 2));
262,144✔
134
                                pPixel[y * wordWidth + x].green = static_cast<int32_t>((result.green >> 7) + (result.green >> 2));
262,144✔
135
                                pPixel[y * wordWidth + x].blue = static_cast<int32_t>((result.blue >> 7) + (result.blue >> 2));
262,144✔
136
                                pPixel[y * wordWidth + x].alpha = static_cast<int32_t>((result.alpha >> 5) + (result.alpha >> 1));
262,144✔
137

138
                                result.red += dY.red;
262,144✔
139
                                result.green += dY.green;
262,144✔
140
                                result.blue += dY.blue;
262,144✔
141
                                result.alpha += dY.alpha;
262,144✔
142
                        }
143

144
                        hP.red += QminusP.red;
65,536✔
145
                        hP.green += QminusP.green;
65,536✔
146
                        hP.blue += QminusP.blue;
65,536✔
147
                        hP.alpha += QminusP.alpha;
65,536✔
148

149
                        hR.red += SminusR.red;
65,536✔
150
                        hR.green += SminusR.green;
65,536✔
151
                        hR.blue += SminusR.blue;
65,536✔
152
                        hR.alpha += SminusR.alpha;
65,536✔
153
                }
154
        }
155
        else
156
        {
157
                // Loop through pixels to achieve results.
158
                for (uint32_t y = 0; y < wordHeight; y++)
81,920✔
159
                {
160
                        Pixel128S result = { 4 * hP.red, 4 * hP.green, 4 * hP.blue, 4 * hP.alpha };
65,536✔
161
                        Pixel128S dY = { hR.red - hP.red, hR.green - hP.green, hR.blue - hP.blue, hR.alpha - hP.alpha };
65,536✔
162

163
                        for (uint32_t x = 0; x < wordWidth; x++)
327,680✔
164
                        {
165
                                pPixel[y * wordWidth + x].red = static_cast<int32_t>((result.red >> 6) + (result.red >> 1));
262,144✔
166
                                pPixel[y * wordWidth + x].green = static_cast<int32_t>((result.green >> 6) + (result.green >> 1));
262,144✔
167
                                pPixel[y * wordWidth + x].blue = static_cast<int32_t>((result.blue >> 6) + (result.blue >> 1));
262,144✔
168
                                pPixel[y * wordWidth + x].alpha = static_cast<int32_t>((result.alpha >> 4) + (result.alpha));
262,144✔
169

170
                                result.red += dY.red;
262,144✔
171
                                result.green += dY.green;
262,144✔
172
                                result.blue += dY.blue;
262,144✔
173
                                result.alpha += dY.alpha;
262,144✔
174
                        }
175

176
                        hP.red += QminusP.red;
65,536✔
177
                        hP.green += QminusP.green;
65,536✔
178
                        hP.blue += QminusP.blue;
65,536✔
179
                        hP.alpha += QminusP.alpha;
65,536✔
180

181
                        hR.red += SminusR.red;
65,536✔
182
                        hR.green += SminusR.green;
65,536✔
183
                        hR.blue += SminusR.blue;
65,536✔
184
                        hR.alpha += SminusR.alpha;
65,536✔
185
                }
186
        }
187
}
24,576✔
188

189
static void unpackModulations(const PVRTCWord& word, int32_t offsetX, int32_t offsetY, int32_t modulationValues[16][8], int32_t modulationModes[16][8], uint8_t bpp)
49,152✔
190
{
191
        uint32_t WordModMode = word.colorData & 0x1;
49,152✔
192
        uint32_t ModulationBits = word.modulationData;
49,152✔
193

194
        // Unpack differently depending on 2bpp or 4bpp modes.
195
        if (bpp == 2)
49,152✔
196
        {
197
                if (WordModMode)
16,384✔
198
                {
199
                        // determine which of the three modes are in use:
200

201
                        // If this is the either the H-only or V-only interpolation mode...
202
                        if (ModulationBits & 0x1)
13,992✔
203
                        {
204
                                // look at the "LSB" for the "centre" (V=2,H=4) texel. Its LSB is now
205
                                // actually used to indicate whether it's the H-only mode or the V-only...
206

207
                                // The centre texel data is the at (y==2, x==4) and so its LSB is at bit 20.
208
                                if (ModulationBits & (0x1 << 20))
3,968✔
209
                                {
210
                                        // This is the V-only mode
211
                                        WordModMode = 3;
212
                                }
213
                                else
214
                                {
215
                                        // This is the H-only mode
216
                                        WordModMode = 2;
217
                                }
218

219
                                // Create an extra bit for the centre pixel so that it looks like
220
                                // we have 2 actual bits for this texel. It makes later coding much easier.
221
                                if (ModulationBits & (0x1 << 21))
3,968✔
222
                                {
223
                                        // set it to produce code for 1.0
224
                                        ModulationBits |= (0x1 << 20);
2,624✔
225
                                }
226
                                else
227
                                {
228
                                        // clear it to produce 0.0 code
229
                                        ModulationBits &= ~(0x1 << 20);
1,344✔
230
                                }
231
                        } // end if H-Only or V-Only interpolation mode was chosen
232

233
                        if (ModulationBits & 0x2) { ModulationBits |= 0x1; /*set it*/ }
13,992✔
234
                        else
235
                        {
236
                                ModulationBits &= ~0x1; /*clear it*/
6,968✔
237
                        }
238

239
                        // run through all the pixels in the block. Note we can now treat all the
240
                        // "stored" values as if they have 2bits (even when they didn't!)
241
                        for (uint8_t y = 0; y < 4; y++)
69,960✔
242
                        {
243
                                for (uint8_t x = 0; x < 8; x++)
503,712✔
244
                                {
245
                                        modulationModes[static_cast<uint32_t>(x + offsetX)][static_cast<uint32_t>(y + offsetY)] = WordModMode;
447,744✔
246

247
                                        // if this is a stored value...
248
                                        if (((x ^ y) & 1) == 0) {modulationValues[static_cast<uint32_t>(x + offsetX)][static_cast<uint32_t>(y + offsetY)] = ModulationBits & 3;
447,744✔
249
                                                ModulationBits >>= 2;
223,872✔
250
                                        }
251
                                }
252
                        } // end for y
253
                }
254
                // else if direct encoded 2bit mode - i.e. 1 mode bit per pixel
255
                else
256
                {
257
                        for (uint8_t y = 0; y < 4; y++)
11,960✔
258
                        {
259
                                for (uint8_t x = 0; x < 8; x++)
86,112✔
260
                                {
261
                                        modulationModes[static_cast<uint32_t>(x + offsetX)][static_cast<uint32_t>(y + offsetY)] = WordModMode;
76,544✔
262

263
                                        /*
264
                                        // double the bits so 0=> 00, and 1=>11
265
                                        */
266
                                        if (ModulationBits & 1) { modulationValues[static_cast<uint32_t>(x + offsetX)][static_cast<uint32_t>(y + offsetY)] = 0x3; }
76,544✔
267
                                        else
268
                                        {
269
                                                modulationValues[static_cast<uint32_t>(x + offsetX)][static_cast<uint32_t>(y + offsetY)] = 0x0;
36,440✔
270
                                        }
271
                                        ModulationBits >>= 1;
76,544✔
272
                                }
273
                        } // end for y
274
                }
275
        }
276
        else
277
        {
278
                // Much simpler than the 2bpp decompression, only two modes, so the n/8 values are set directly.
279
                // run through all the pixels in the word.
280
                if (WordModMode)
32,768✔
281
                {
282
                        for (uint8_t y = 0; y < 4; y++)
39,800✔
283
                        {
284
                                for (uint8_t x = 0; x < 4; x++)
159,200✔
285
                                {
286
                                        modulationValues[static_cast<uint32_t>(y + offsetY)][static_cast<uint32_t>(x + offsetX)] = ModulationBits & 3;
127,360✔
287
                                        // if (modulationValues==0) {}. We don't need to check 0, 0 = 0/8.
288
                                        if (modulationValues[static_cast<uint32_t>(y + offsetY)][static_cast<uint32_t>(x + offsetX)] == 1)
127,360✔
289
                                        { modulationValues[static_cast<uint32_t>(y + offsetY)][static_cast<uint32_t>(x + offsetX)] = 4; }
68,496✔
290
                                        else if (modulationValues[static_cast<uint32_t>(y + offsetY)][static_cast<uint32_t>(x + offsetX)] == 2)
58,864✔
291
                                        {
292
                                                modulationValues[static_cast<uint32_t>(y + offsetY)][static_cast<uint32_t>(x + offsetX)] = 14; //+10 tells the decompressor to punch through alpha.
3,584✔
293
                                        }
294
                                        else if (modulationValues[static_cast<uint32_t>(y + offsetY)][static_cast<uint32_t>(x + offsetX)] == 3)
55,280✔
295
                                        {
296
                                                modulationValues[static_cast<uint32_t>(y + offsetY)][static_cast<uint32_t>(x + offsetX)] = 8;
30,440✔
297
                                        }
298
                                        ModulationBits >>= 2;
127,360✔
299
                                } // end for x
300
                        } // end for y
301
                }
302
                else
303
                {
304
                        for (uint8_t y = 0; y < 4; y++)
124,040✔
305
                        {
306
                                for (uint8_t x = 0; x < 4; x++)
496,160✔
307
                                {
308
                                        modulationValues[static_cast<uint32_t>(y + offsetY)][static_cast<uint32_t>(x + offsetX)] = ModulationBits & 3;
396,928✔
309
                                        modulationValues[static_cast<uint32_t>(y + offsetY)][static_cast<uint32_t>(x + offsetX)] *= 3;
396,928✔
310
                                        if (modulationValues[static_cast<uint32_t>(y + offsetY)][static_cast<uint32_t>(x + offsetX)] > 3)
396,928✔
311
                                        { modulationValues[static_cast<uint32_t>(y + offsetY)][static_cast<uint32_t>(x + offsetX)] -= 1; }
226,560✔
312
                                        ModulationBits >>= 2;
396,928✔
313
                                } // end for x
314
                        } // end for y
315
                }
316
        }
317
}
49,152✔
318

319
static int32_t getModulationValues(int32_t modulationValues[16][8], int32_t modulationModes[16][8], uint32_t xPos, uint32_t yPos, uint8_t bpp)
262,144✔
320
{
321
        if (bpp == 2)
262,144✔
322
        {
323
                const int32_t RepVals0[4] = { 0, 3, 5, 8 };
131,072✔
324

325
                // extract the modulation value. If a simple encoding
326
                if (modulationModes[xPos][yPos] == 0) { return RepVals0[modulationValues[xPos][yPos]]; }
131,072✔
327
                else
328
                {
329
                        // if this is a stored value
330
                        if (((xPos ^ yPos) & 1) == 0) { return RepVals0[modulationValues[xPos][yPos]]; }
111,936✔
331

332
                        // else average from the neighbours
333
                        // if H&V interpolation...
334
                        else if (modulationModes[xPos][yPos] == 1)
55,968✔
335
                        {
336
                                return (RepVals0[modulationValues[xPos][yPos - 1]] + RepVals0[modulationValues[xPos][yPos + 1]] + RepVals0[modulationValues[xPos - 1][yPos]] +
40,096✔
337
                                                   RepVals0[modulationValues[xPos + 1][yPos]] + 2) /
40,096✔
338
                                        4;
40,096✔
339
                        }
340
                        // else if H-Only
341
                        else if (modulationModes[xPos][yPos] == 2)
15,872✔
342
                        {
343
                                return (RepVals0[modulationValues[xPos - 1][yPos]] + RepVals0[modulationValues[xPos + 1][yPos]] + 1) / 2;
13,600✔
344
                        }
345
                        // else it's V-Only
346
                        else
347
                        {
348
                                return (RepVals0[modulationValues[xPos][yPos - 1]] + RepVals0[modulationValues[xPos][yPos + 1]] + 1) / 2;
2,272✔
349
                        }
350
                }
351
        }
352
        else if (bpp == 4)
131,072✔
353
        {
354
                return modulationValues[xPos][yPos];
131,072✔
355
        }
356

357
        return 0;
358
}
359

360
static void pvrtcGetDecompressedPixels(const PVRTCWord& P, const PVRTCWord& Q, const PVRTCWord& R, const PVRTCWord& S, Pixel32* pColorData, uint8_t bpp)
12,288✔
361
{
362
        // 4bpp only needs 8*8 values, but 2bpp needs 16*8, so rather than wasting processor time we just statically allocate 16*8.
363
        int32_t modulationValues[16][8];
364
        // Only 2bpp needs this.
365
        int32_t modulationModes[16][8];
366
        // 4bpp only needs 16 values, but 2bpp needs 32, so rather than wasting processor time we just statically allocate 32.
367
        Pixel128S upscaledColorA[32];
368
        Pixel128S upscaledColorB[32];
369

370
        uint32_t wordWidth = 4;
371
        uint32_t wordHeight = 4;
372
        if (bpp == 2) { wordWidth = 8; }
12,288✔
373

374
        // Get the modulations from each word.
375
        unpackModulations(P, 0, 0, modulationValues, modulationModes, bpp);
12,288✔
376
        unpackModulations(Q, wordWidth, 0, modulationValues, modulationModes, bpp);
12,288✔
377
        unpackModulations(R, 0, wordHeight, modulationValues, modulationModes, bpp);
12,288✔
378
        unpackModulations(S, wordWidth, wordHeight, modulationValues, modulationModes, bpp);
12,288✔
379

380
        // Bilinear upscale image data from 2x2 -> 4x4
381
        interpolateColors(getColorA(P.colorData), getColorA(Q.colorData), getColorA(R.colorData), getColorA(S.colorData), upscaledColorA, bpp);
12,288✔
382
        interpolateColors(getColorB(P.colorData), getColorB(Q.colorData), getColorB(R.colorData), getColorB(S.colorData), upscaledColorB, bpp);
12,288✔
383

384
        for (uint32_t y = 0; y < wordHeight; y++)
61,440✔
385
        {
386
                for (uint32_t x = 0; x < wordWidth; x++)
311,296✔
387
                {
388
                        int32_t mod = getModulationValues(modulationValues, modulationModes, x + wordWidth / 2, y + wordHeight / 2, bpp);
262,144✔
389
                        bool punchthroughAlpha = false;
390
                        if (mod > 10)
262,144✔
391
                        {
392
                                punchthroughAlpha = true;
393
                                mod -= 10;
896✔
394
                        }
395

396
                        Pixel128S result;
397
                        result.red = (upscaledColorA[y * wordWidth + x].red * (8 - mod) + upscaledColorB[y * wordWidth + x].red * mod) / 8;
262,144✔
398
                        result.green = (upscaledColorA[y * wordWidth + x].green * (8 - mod) + upscaledColorB[y * wordWidth + x].green * mod) / 8;
262,144✔
399
                        result.blue = (upscaledColorA[y * wordWidth + x].blue * (8 - mod) + upscaledColorB[y * wordWidth + x].blue * mod) / 8;
262,144✔
400
                        if (punchthroughAlpha) { result.alpha = 0; }
262,144✔
401
                        else
402
                        {
403
                                result.alpha = (upscaledColorA[y * wordWidth + x].alpha * (8 - mod) + upscaledColorB[y * wordWidth + x].alpha * mod) / 8;
261,248✔
404
                        }
405

406
                        // Convert the 32bit precision Result to 8 bit per channel color.
407
                        if (bpp == 2)
262,144✔
408
                        {
409
                                pColorData[y * wordWidth + x].red = static_cast<uint8_t>(result.red);
131,072✔
410
                                pColorData[y * wordWidth + x].green = static_cast<uint8_t>(result.green);
131,072✔
411
                                pColorData[y * wordWidth + x].blue = static_cast<uint8_t>(result.blue);
131,072✔
412
                                pColorData[y * wordWidth + x].alpha = static_cast<uint8_t>(result.alpha);
131,072✔
413
                        }
414
                        else if (bpp == 4)
131,072✔
415
                        {
416
                                pColorData[y + x * wordHeight].red = static_cast<uint8_t>(result.red);
131,072✔
417
                                pColorData[y + x * wordHeight].green = static_cast<uint8_t>(result.green);
131,072✔
418
                                pColorData[y + x * wordHeight].blue = static_cast<uint8_t>(result.blue);
131,072✔
419
                                pColorData[y + x * wordHeight].alpha = static_cast<uint8_t>(result.alpha);
131,072✔
420
                        }
421
                }
422
        }
423
}
12,288✔
424

425
static uint32_t wrapWordIndex(uint32_t numWords, int word) { return ((word + numWords) % numWords); }
12,288✔
426

427
static bool isPowerOf2(uint32_t input)
428
{
429
        uint32_t minus1;
430

431
        if (!input) { return 0; }
432

433
        minus1 = input - 1;
434
        return ((input | minus1) == (input ^ minus1));
435
}
436

437
static uint32_t TwiddleUV(uint32_t XSize, uint32_t YSize, uint32_t XPos, uint32_t YPos)
438
{
439
        // Initially assume X is the larger size.
440
        uint32_t MinDimension = XSize;
441
        uint32_t MaxValue = YPos;
442
        uint32_t Twiddled = 0;
443
        uint32_t SrcBitPos = 1;
444
        uint32_t DstBitPos = 1;
445
        int ShiftCount = 0;
446

447
        // Check the sizes are valid.
448
        assert(YPos < YSize);
449
        assert(XPos < XSize);
450
        assert(isPowerOf2(YSize));
451
        assert(isPowerOf2(XSize));
452

453
        // If Y is the larger dimension - switch the min/max values.
454
        if (YSize < XSize)
49,152✔
455
        {
456
                MinDimension = YSize;
457
                MaxValue = XPos;
458
        }
459

460
        // Step through all the bits in the "minimum" dimension
461
        while (SrcBitPos < MinDimension)
327,680✔
462
        {
463
                if (YPos & SrcBitPos) { Twiddled |= DstBitPos; }
278,528✔
464

465
                if (XPos & SrcBitPos) { Twiddled |= (DstBitPos << 1); }
278,528✔
466

467
                SrcBitPos <<= 1;
278,528✔
468
                DstBitPos <<= 2;
278,528✔
469
                ShiftCount += 1;
278,528✔
470
        }
471

472
        // Prepend any unused bits
473
        MaxValue >>= ShiftCount;
49,152✔
474
        Twiddled |= (MaxValue << (2 * ShiftCount));
49,152✔
475

476
        return Twiddled;
477
}
478

479
static void mapDecompressedData(Pixel32* pOutput, uint32_t width, const Pixel32* pWord, const PVRTCWordIndices& words, uint8_t bpp)
12,288✔
480
{
481
        uint32_t wordWidth = 4;
482
        uint32_t wordHeight = 4;
483
        if (bpp == 2) { wordWidth = 8; }
12,288✔
484

485
        for (uint32_t y = 0; y < wordHeight / 2; y++)
36,864✔
486
        {
487
                for (uint32_t x = 0; x < wordWidth / 2; x++)
90,112✔
488
                {
489
                        pOutput[(((words.P[1] * wordHeight) + y + wordHeight / 2) * width + words.P[0] * wordWidth + x + wordWidth / 2)] = pWord[y * wordWidth + x]; // map P
65,536✔
490

491
                        pOutput[(((words.Q[1] * wordHeight) + y + wordHeight / 2) * width + words.Q[0] * wordWidth + x)] = pWord[y * wordWidth + x + wordWidth / 2]; // map Q
65,536✔
492

493
                        pOutput[(((words.R[1] * wordHeight) + y) * width + words.R[0] * wordWidth + x + wordWidth / 2)] = pWord[(y + wordHeight / 2) * wordWidth + x]; // map R
65,536✔
494

495
                        pOutput[(((words.S[1] * wordHeight) + y) * width + words.S[0] * wordWidth + x)] = pWord[(y + wordHeight / 2) * wordWidth + x + wordWidth / 2]; // map S
65,536✔
496
                }
497
        }
498
}
12,288✔
499
static uint32_t pvrtcDecompress(uint8_t* pCompressedData, Pixel32* pDecompressedData, uint32_t width, uint32_t height, uint8_t bpp)
4✔
500
{
501
        uint32_t wordWidth = 4;
502
        uint32_t wordHeight = 4;
503
        if (bpp == 2) { wordWidth = 8; }
4✔
504

505
        uint32_t* pWordMembers = (uint32_t*)pCompressedData;
506
        Pixel32* pOutData = pDecompressedData;
507

508
        // Calculate number of words
509
        int i32NumXWords = static_cast<int>(width / wordWidth);
4✔
510
        int i32NumYWords = static_cast<int>(height / wordHeight);
4✔
511

512
        // Structs used for decompression
513
        PVRTCWordIndices indices;
514
        std::vector<Pixel32> pPixels(wordWidth * wordHeight * sizeof(Pixel32));
4✔
515

516
        // For each row of words
517
        for (int32_t wordY = -1; wordY < i32NumYWords - 1; wordY++)
260✔
518
        {
519
                // for each column of words
520
                for (int32_t wordX = -1; wordX < i32NumXWords - 1; wordX++)
12,544✔
521
                {
522
                        indices.P[0] = static_cast<int>(wrapWordIndex(i32NumXWords, wordX));
12,288✔
523
                        indices.P[1] = static_cast<int>(wrapWordIndex(i32NumYWords, wordY));
12,288✔
524
                        indices.Q[0] = static_cast<int>(wrapWordIndex(i32NumXWords, wordX + 1));
12,288✔
525
                        indices.Q[1] = static_cast<int>(wrapWordIndex(i32NumYWords, wordY));
12,288✔
526
                        indices.R[0] = static_cast<int>(wrapWordIndex(i32NumXWords, wordX));
12,288✔
527
                        indices.R[1] = static_cast<int>(wrapWordIndex(i32NumYWords, wordY + 1));
12,288✔
528
                        indices.S[0] = static_cast<int>(wrapWordIndex(i32NumXWords, wordX + 1));
12,288✔
529
                        indices.S[1] = static_cast<int>(wrapWordIndex(i32NumYWords, wordY + 1));
12,288✔
530

531
                        // Work out the offsets into the twiddle structs, multiply by two as there are two members per word.
532
                        uint32_t WordOffsets[4] = {
533
                                TwiddleUV(i32NumXWords, i32NumYWords, indices.P[0], indices.P[1]) * 2,
12,288✔
534
                                TwiddleUV(i32NumXWords, i32NumYWords, indices.Q[0], indices.Q[1]) * 2,
12,288✔
535
                                TwiddleUV(i32NumXWords, i32NumYWords, indices.R[0], indices.R[1]) * 2,
12,288✔
536
                                TwiddleUV(i32NumXWords, i32NumYWords, indices.S[0], indices.S[1]) * 2,
12,288✔
537
                        };
538

539
                        // Access individual elements to fill out PVRTCWord
540
                        PVRTCWord P, Q, R, S;
541
                        P.colorData = static_cast<uint32_t>(pWordMembers[WordOffsets[0] + 1]);
12,288✔
542
                        P.modulationData = static_cast<uint32_t>(pWordMembers[WordOffsets[0]]);
12,288✔
543
                        Q.colorData = static_cast<uint32_t>(pWordMembers[WordOffsets[1] + 1]);
12,288✔
544
                        Q.modulationData = static_cast<uint32_t>(pWordMembers[WordOffsets[1]]);
12,288✔
545
                        R.colorData = static_cast<uint32_t>(pWordMembers[WordOffsets[2] + 1]);
12,288✔
546
                        R.modulationData = static_cast<uint32_t>(pWordMembers[WordOffsets[2]]);
12,288✔
547
                        S.colorData = static_cast<uint32_t>(pWordMembers[WordOffsets[3] + 1]);
12,288✔
548
                        S.modulationData = static_cast<uint32_t>(pWordMembers[WordOffsets[3]]);
12,288✔
549

550
                        // assemble 4 words into struct to get decompressed pixels from
551
                        pvrtcGetDecompressedPixels(P, Q, R, S, pPixels.data(), bpp);
12,288✔
552
                        mapDecompressedData(pOutData, width, pPixels.data(), indices, bpp);
12,288✔
553

554
                } // for each word
555
        } // for each row of words
556

557
        // Return the data size
558
        return width * height / static_cast<uint32_t>((wordWidth / 2));
4✔
559
}
560

561
uint32_t PVRTDecompressPVRTC(const void* pCompressedData, uint32_t Do2bitMode, uint32_t XDim, uint32_t YDim, uint8_t* pResultImage)
4✔
562
{
563
        // Cast the output buffer to a Pixel32 pointer.
564
        Pixel32* pDecompressedData = (Pixel32*)pResultImage;
565

566
        // Check the X and Y values are at least the minimum size.
567
        uint32_t XTrueDim = std::max(XDim, ((Do2bitMode == 1u) ? 16u : 8u));
6✔
568
        uint32_t YTrueDim = std::max(YDim, 8u);
4✔
569

570
        // If the dimensions aren't correct, we need to create a new buffer instead of just using the provided one, as the buffer will overrun otherwise.
571
        if (XTrueDim != XDim || YTrueDim != YDim) { pDecompressedData = new Pixel32[XTrueDim * YTrueDim]; }
4✔
572

573
        // Decompress the surface.
574
        uint32_t retval = pvrtcDecompress((uint8_t*)pCompressedData, pDecompressedData, XTrueDim, YTrueDim, uint8_t(Do2bitMode == 1 ? 2 : 4));
6✔
575

576
        // If the dimensions were too small, then copy the new buffer back into the output buffer.
577
        if (XTrueDim != XDim || YTrueDim != YDim)
4✔
578
        {
579
                // Loop through all the required pixels.
580
                for (uint32_t x = 0; x < XDim; ++x)
×
581
                {
582
                        for (uint32_t y = 0; y < YDim; ++y) { ((Pixel32*)pResultImage)[x + y * XDim] = pDecompressedData[x + y * XTrueDim]; }
×
583
                }
584

585
                // Free the temporary buffer.
586
                delete[] pDecompressedData;
×
587
        }
588
        return retval;
4✔
589
}
590

591
////////////////////////////////////// ETC Compression //////////////////////////////////////
592

593
#define _CLAMP_(X, Xmin, Xmax) ((X) < (Xmax) ? ((X) < (Xmin) ? (Xmin) : (X)) : (Xmax))
594

595
uint32_t ETC_FLIP = 0x01000000;
596
uint32_t ETC_DIFF = 0x02000000;
597
const int mod[8][4] = { { 2, 8, -2, -8 }, { 5, 17, -5, -17 }, { 9, 29, -9, -29 }, { 13, 42, -13, -42 }, { 18, 60, -18, -60 }, { 24, 80, -24, -80 }, { 33, 106, -33, -106 },
598
        { 47, 183, -47, -183 } };
599

600
static uint32_t modifyPixel(int red, int green, int blue, int x, int y, uint32_t modBlock, int modTable)
131,072✔
601
{
602
        int index = x * 4 + y, pixelMod;
131,072✔
603
        uint32_t mostSig = modBlock << 1;
131,072✔
604

605
        if (index < 8) { pixelMod = mod[modTable][((modBlock >> (index + 24)) & 0x1) + ((mostSig >> (index + 8)) & 0x2)]; }
131,072✔
606
        else
607
        {
608
                pixelMod = mod[modTable][((modBlock >> (index + 8)) & 0x1) + ((mostSig >> (index - 8)) & 0x2)];
65,536✔
609
        }
610

611
        red = _CLAMP_(red + pixelMod, 0, 255);
131,072✔
612
        green = _CLAMP_(green + pixelMod, 0, 255);
131,072✔
613
        blue = _CLAMP_(blue + pixelMod, 0, 255);
131,072✔
614

615
        return ((red << 16) + (green << 8) + blue) | 0xff000000;
131,072✔
616
}
617

618
static uint32_t ETCTextureDecompress(const void* pSrcData, uint32_t x, uint32_t y, void* pDestData, uint32_t /*nMode*/)
2✔
619
{
620
        uint32_t* output;
621
        uint32_t blockTop, blockBot;
622
        const uint32_t* input = static_cast<const uint32_t*>(pSrcData);
623
        unsigned char red1, green1, blue1, red2, green2, blue2;
624
        bool bFlip, bDiff;
625
        int modtable1, modtable2;
626

627
        for (uint32_t i = 0; i < y; i += 4)
130✔
628
        {
629
                for (uint32_t m = 0; m < x; m += 4)
8,320✔
630
                {
631
                        blockTop = *(input++);
8,192✔
632
                        blockBot = *(input++);
8,192✔
633

634
                        output = (uint32_t*)pDestData + i * x + m;
8,192✔
635

636
                        // check flipbit
637
                        bFlip = (blockTop & ETC_FLIP) != 0;
8,192✔
638
                        bDiff = (blockTop & ETC_DIFF) != 0;
8,192✔
639

640
                        if (bDiff)
8,192✔
641
                        {
642
                                // differential mode 5 color bits + 3 difference bits
643
                                // get base color for subblock 1
644
                                blue1 = static_cast<unsigned char>((blockTop & 0xf80000) >> 16u);
7,334✔
645
                                green1 = static_cast<unsigned char>((blockTop & 0xf800) >> 8u);
7,334✔
646
                                red1 = static_cast<unsigned char>(blockTop & 0xf8);
7,334✔
647

648
                                // get differential color for subblock 2
649
                                signed char blues = static_cast<signed char>(blue1 >> 3) + (static_cast<signed char>((blockTop & 0x70000) >> 11) >> 5);
7,334✔
650
                                signed char greens = static_cast<signed char>(green1 >> 3) + (static_cast<signed char>((blockTop & 0x700) >> 3) >> 5);
7,334✔
651
                                signed char reds = static_cast<signed char>(red1 >> 3) + (static_cast<signed char>((blockTop & 0x7) << 5) >> 5);
7,334✔
652

653
                                blue2 = static_cast<unsigned char>(blues);
654
                                green2 = static_cast<unsigned char>(greens);
655
                                red2 = static_cast<unsigned char>(reds);
656

657
                                red1 = static_cast<unsigned char>(red1 + (red1 >> 5u)); // copy bits to lower sig
7,334✔
658
                                green1 = static_cast<unsigned char>(green1 + (green1 >> 5u)); // copy bits to lower sig
7,334✔
659
                                blue1 = static_cast<unsigned char>(blue1 + (blue1 >> 5u)); // copy bits to lower sig
7,334✔
660

661
                                red2 = static_cast<unsigned char>((red2 << 3u) + (red2 >> 2u)); // copy bits to lower sig
7,334✔
662
                                green2 = static_cast<unsigned char>((green2 << 3u) + (green2 >> 2u)); // copy bits to lower sig
7,334✔
663
                                blue2 = static_cast<unsigned char>((blue2 << 3u) + (blue2 >> 2u)); // copy bits to lower sig
7,334✔
664
                        }
665
                        else
666
                        {
667
                                // individual mode 4 + 4 color bits
668
                                // get base color for subblock 1
669
                                blue1 = static_cast<unsigned char>((blockTop & 0xf00000) >> 16);
858✔
670
                                blue1 = static_cast<unsigned char>(blue1 + (blue1 >> 4)); // copy bits to lower sig
858✔
671
                                green1 = static_cast<unsigned char>((blockTop & 0xf000) >> 8);
858✔
672
                                green1 = static_cast<unsigned char>(green1 + (green1 >> 4)); // copy bits to lower sig
858✔
673
                                red1 = static_cast<unsigned char>(blockTop & 0xf0);
858✔
674
                                red1 = static_cast<unsigned char>(red1 + (red1 >> 4)); // copy bits to lower sig
858✔
675

676
                                // get base color for subblock 2
677
                                blue2 = static_cast<unsigned char>((blockTop & 0xf0000) >> 12);
858✔
678
                                blue2 = static_cast<unsigned char>(blue2 + (blue2 >> 4)); // copy bits to lower sig
858✔
679
                                green2 = static_cast<unsigned char>((blockTop & 0xf00) >> 4);
858✔
680
                                green2 = static_cast<unsigned char>(green2 + (green2 >> 4)); // copy bits to lower sig
858✔
681
                                red2 = static_cast<unsigned char>((blockTop & 0xf) << 4);
858✔
682
                                red2 = static_cast<unsigned char>(red2 + (red2 >> 4)); // copy bits to lower sig
858✔
683
                        }
684
                        // get the modtables for each subblock
685
                        modtable1 = static_cast<int>((blockTop >> 29) & 0x7);
8,192✔
686
                        modtable2 = static_cast<int>((blockTop >> 26) & 0x7);
8,192✔
687

688
                        if (!bFlip)
8,192✔
689
                        {
690
                                // 2 2x4 blocks side by side
691

692
                                for (uint8_t j = 0; j < 4; j++) // vertical
20,360✔
693
                                {
694
                                        for (uint8_t k = 0; k < 2; k++) // horizontal
48,864✔
695
                                        {
696
                                                *(output + j * x + k) = modifyPixel(red1, green1, blue1, k, j, blockBot, modtable1);
32,576✔
697
                                                *(output + j * x + k + 2) = modifyPixel(red2, green2, blue2, k + 2, j, blockBot, modtable2);
32,576✔
698
                                        }
699
                                }
700
                        }
701
                        else
702
                        {
703
                                // 2 4x2 blocks on top of each other
704
                                for (uint8_t j = 0; j < 2; j++)
12,360✔
705
                                {
706
                                        for (uint8_t k = 0; k < 4; k++)
41,200✔
707
                                        {
708
                                                *(output + j * x + k) = modifyPixel(red1, green1, blue1, k, j, blockBot, modtable1);
32,960✔
709
                                                *(output + (j + 2) * x + k) = modifyPixel(red2, green2, blue2, k, j + 2, blockBot, modtable2);
32,960✔
710
                                        }
711
                                }
712
                        }
713
                }
714
        }
715

716
        return x * y / 2;
2✔
717
}
718

719
uint32_t PVRTDecompressETC(const void* pSrcData, uint32_t x, uint32_t y, void* pDestData, uint32_t nMode)
2✔
720
{
721
        uint32_t i32read;
722

723
        if (x < ETC_MIN_TEXWIDTH || y < ETC_MIN_TEXHEIGHT)
2✔
724
        {
725
                // decompress into a buffer big enough to take the minimum size
726
                char* pTempBuffer = new char[std::max<uint32_t>(x, ETC_MIN_TEXWIDTH) * std::max<uint32_t>(y, ETC_MIN_TEXHEIGHT) * 4];
×
727
                i32read = ETCTextureDecompress(pSrcData, std::max<uint32_t>(x, ETC_MIN_TEXWIDTH), std::max<uint32_t>(y, ETC_MIN_TEXHEIGHT), pTempBuffer, nMode);
×
728

729
                for (uint32_t i = 0; i < y; i++)
×
730
                {
731
                        // copy from larger temp buffer to output data
732
                        memcpy(static_cast<char*>(pDestData) + i * x * 4, pTempBuffer + std::max<uint32_t>(x, ETC_MIN_TEXWIDTH) * 4 * i, x * 4);
×
733
                }
734

735
                delete[] pTempBuffer;
×
736
        }
737
        else // decompress larger MIP levels straight into the output data
738
        {
739
                i32read = ETCTextureDecompress(pSrcData, x, y, pDestData, nMode);
2✔
740
        }
741

742
        // swap r and b channels
743
        unsigned char *pSwap = static_cast<unsigned char*>(pDestData), swap;
744

745
        for (uint32_t i = 0; i < y; i++)
514✔
746
                for (uint32_t j = 0; j < x; j++)
131,584✔
747
                {
748
                        swap = pSwap[0];
131,072✔
749
                        pSwap[0] = pSwap[2];
131,072✔
750
                        pSwap[2] = swap;
131,072✔
751
                        pSwap += 4;
131,072✔
752
                }
753

754
        return i32read;
2✔
755
}
756
} // namespace pvr
757
//!\endcond
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc