summaryrefslogtreecommitdiff
path: root/thirdparty/basis_universal/basisu_pvrtc1_4.h
blob: 71c4fba1d8002d54baba22308007b6e1d6d21b46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
// basisu_pvrtc1_4.cpp
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "basisu_gpu_texture.h"

namespace basisu
{
	enum 
	{ 
		PVRTC2_MIN_WIDTH = 16, 
		PVRTC2_MIN_HEIGHT = 8, 
		PVRTC4_MIN_WIDTH = 8, 
		PVRTC4_MIN_HEIGHT = 8 
	};
	
	struct pvrtc4_block
	{
		uint32_t m_modulation;
		uint32_t m_endpoints;

		pvrtc4_block() : m_modulation(0), m_endpoints(0) { }

		inline bool operator== (const pvrtc4_block& rhs) const
		{
			return (m_modulation == rhs.m_modulation) && (m_endpoints == rhs.m_endpoints);
		}

		inline void clear()
		{
			m_modulation = 0;
			m_endpoints = 0;
		}

		inline bool get_block_uses_transparent_modulation() const
		{
			return (m_endpoints & 1) != 0;
		}

		inline bool is_endpoint_opaque(uint32_t endpoint_index) const
		{
			static const uint32_t s_bitmasks[2] = { 0x8000U, 0x80000000U };
			return (m_endpoints & s_bitmasks[open_range_check(endpoint_index, 2U)]) != 0;
		}

		// Returns raw endpoint or 8888
		color_rgba get_endpoint(uint32_t endpoint_index, bool unpack) const;
		
		color_rgba get_endpoint_5554(uint32_t endpoint_index) const;
		
		static uint32_t get_component_precision_in_bits(uint32_t c, uint32_t endpoint_index, bool opaque_endpoint)
		{
			static const uint32_t s_comp_prec[4][4] =
			{
				// R0 G0 B0 A0      R1 G1 B1 A1
				{  4, 4, 3, 3 }, {  4, 4, 4, 3 }, // transparent endpoint

				{  5, 5, 4, 0 }, {  5, 5, 5, 0 }  // opaque endpoint
			};
			return s_comp_prec[open_range_check(endpoint_index, 2U) + (opaque_endpoint * 2)][open_range_check(c, 4U)];
		}

		static color_rgba get_color_precision_in_bits(uint32_t endpoint_index, bool opaque_endpoint)
		{
			static const color_rgba s_color_prec[4] =
			{
			   color_rgba(4, 4, 3, 3), color_rgba(4, 4, 4, 3), // transparent endpoint
			   color_rgba(5, 5, 4, 0), color_rgba(5, 5, 5, 0)  // opaque endpoint
			};
			return s_color_prec[open_range_check(endpoint_index, 2U) + (opaque_endpoint * 2)];
		}
		
		inline uint32_t get_modulation(uint32_t x, uint32_t y) const
		{
			assert((x < 4) && (y < 4));
			return (m_modulation >> ((y * 4 + x) * 2)) & 3;
		}

		// Scaled by 8
		inline const uint32_t* get_scaled_modulation_values(bool block_uses_transparent_modulation) const
		{
			static const uint32_t s_block_scales[2][4] = { { 0, 3, 5, 8 }, { 0, 4, 4, 8 } };
			return s_block_scales[block_uses_transparent_modulation];
		}

		// Scaled by 8
		inline uint32_t get_scaled_modulation(uint32_t x, uint32_t y) const
		{
			return get_scaled_modulation_values(get_block_uses_transparent_modulation())[get_modulation(x, y)];
		}

		inline void byte_swap()
		{
			m_modulation = byteswap32(m_modulation);
			m_endpoints = byteswap32(m_endpoints);
		}

		// opaque endpoints:	554, 555
		// transparent endpoints: 3443 or 3444
		inline void set_endpoint_raw(uint32_t endpoint_index, const color_rgba& c, bool opaque_endpoint)
		{
			assert(endpoint_index < 2);
			const uint32_t m = m_endpoints & 1;
			uint32_t r = c[0], g = c[1], b = c[2], a = c[3];
						
			uint32_t packed;

			if (opaque_endpoint)
			{
				if (!endpoint_index)
				{
					// 554
					// 1RRRRRGGGGGBBBBM
					assert((r < 32) && (g < 32) && (b < 16));
					packed = 0x8000 | (r << 10) | (g << 5) | (b << 1) | m;
				}
				else
				{
					// 555
					// 1RRRRRGGGGGBBBBB
					assert((r < 32) && (g < 32) && (b < 32));
					packed = 0x8000 | (r << 10) | (g << 5) | b;
				}
			}
			else
			{
				if (!endpoint_index)
				{
					// 3443
					// 0AAA RRRR GGGG BBBM
					assert((r < 16) && (g < 16) && (b < 8) && (a < 8));
					packed = (a << 12) | (r << 8) | (g << 4) | (b << 1) | m;
				}
				else
				{
					// 3444
					// 0AAA RRRR GGGG BBBB
					assert((r < 16) && (g < 16) && (b < 16) && (a < 8));
					packed = (a << 12) | (r << 8) | (g << 4) | b;
				}
			}

			assert(packed <= 0xFFFF);

			if (endpoint_index)
				m_endpoints = (m_endpoints & 0xFFFFU) | (packed << 16);
			else
				m_endpoints = (m_endpoints & 0xFFFF0000U) | packed;
		}
	};

	typedef vector2D<pvrtc4_block> pvrtc4_block_vector2D;

	uint32_t pvrtc4_swizzle_uv(uint32_t XSize, uint32_t YSize, uint32_t XPos, uint32_t YPos);

	class pvrtc4_image
	{
	public:
		inline pvrtc4_image() :
			m_width(0), m_height(0), m_block_width(0), m_block_height(0), m_wrap_addressing(false), m_uses_alpha(false)
		{
		}

		inline pvrtc4_image(uint32_t width, uint32_t height, bool wrap_addressing = false) :
			m_width(0), m_height(0), m_block_width(0), m_block_height(0), m_wrap_addressing(false), m_uses_alpha(false)
		{
			resize(width, height);
			set_wrap_addressing(wrap_addressing);
		}

		inline void clear()
		{
			m_width = 0;
			m_height = 0;
			m_block_width = 0;
			m_block_height = 0;
			m_blocks.clear();
			m_uses_alpha = false;
			m_wrap_addressing = false;
		}

		inline void resize(uint32_t width, uint32_t height)
		{
			if ((width == m_width) && (height == m_height))
				return;

			m_width = width;
			m_height = height;

			m_block_width = (width + 3) >> 2;
			m_block_height = (height + 3) >> 2;

			m_blocks.resize(m_block_width, m_block_height);
		}

		inline uint32_t get_width() const { return m_width; }
		inline uint32_t get_height() const { return m_height; }

		inline uint32_t get_block_width() const { return m_block_width; }
		inline uint32_t get_block_height() const { return m_block_height; }

		inline const pvrtc4_block_vector2D &get_blocks() const { return m_blocks; }
		inline		 pvrtc4_block_vector2D &get_blocks() { return m_blocks; }

		inline uint32_t get_total_blocks() const { return m_block_width * m_block_height; }

		inline bool get_uses_alpha() const { return m_uses_alpha; }
		inline void set_uses_alpha(bool uses_alpha) { m_uses_alpha = uses_alpha; }

		inline void set_wrap_addressing(bool wrapping) { m_wrap_addressing = wrapping; }
		inline bool get_wrap_addressing() const { return m_wrap_addressing; }

		inline bool are_blocks_equal(const pvrtc4_image& rhs) const
		{
			return m_blocks == rhs.m_blocks;
		}

		inline void set_to_black()
		{
			memset(m_blocks.get_ptr(), 0, m_blocks.size_in_bytes());
		}

		inline bool get_block_uses_transparent_modulation(uint32_t bx, uint32_t by) const
		{
			return m_blocks(bx, by).get_block_uses_transparent_modulation();
		}

		inline bool is_endpoint_opaque(uint32_t bx, uint32_t by, uint32_t endpoint_index) const
		{
			return m_blocks(bx, by).is_endpoint_opaque(endpoint_index);
		}
				
		color_rgba get_endpoint(uint32_t bx, uint32_t by, uint32_t endpoint_index, bool unpack) const
		{
			assert((bx < m_block_width) && (by < m_block_height));
			return m_blocks(bx, by).get_endpoint(endpoint_index, unpack);
		}

		inline uint32_t get_modulation(uint32_t x, uint32_t y) const
		{
			assert((x < m_width) && (y < m_height));
			return m_blocks(x >> 2, y >> 2).get_modulation(x & 3, y & 3);
		}
				
		// Returns true if the block uses transparent modulation.
		bool get_interpolated_colors(uint32_t x, uint32_t y, color_rgba* pColors) const;
		
		color_rgba get_pixel(uint32_t x, uint32_t y, uint32_t m) const;
		
		inline color_rgba get_pixel(uint32_t x, uint32_t y) const
		{
			assert((x < m_width) && (y < m_height));
			return get_pixel(x, y, m_blocks(x >> 2, y >> 2).get_modulation(x & 3, y & 3));
		}

		void deswizzle()
		{
			pvrtc4_block_vector2D temp(m_blocks);

			for (uint32_t y = 0; y < m_block_height; y++)
				for (uint32_t x = 0; x < m_block_width; x++)
					m_blocks(x, y) = temp[pvrtc4_swizzle_uv(m_block_width, m_block_height, x, y)];
		}

		void swizzle()
		{
			pvrtc4_block_vector2D temp(m_blocks);

			for (uint32_t y = 0; y < m_block_height; y++)
				for (uint32_t x = 0; x < m_block_width; x++)
					m_blocks[pvrtc4_swizzle_uv(m_block_width, m_block_height, x, y)] = temp(x, y);
		}

		void unpack_all_pixels(image& img) const
		{
			img.crop(m_width, m_height);

			for (uint32_t y = 0; y < m_height; y++)
				for (uint32_t x = 0; x < m_width; x++)
					img(x, y) = get_pixel(x, y);
		}

		void unpack_block(image &dst, uint32_t block_x, uint32_t block_y)
		{
			for (uint32_t y = 0; y < 4; y++)
				for (uint32_t x = 0; x < 4; x++)
					dst(x, y) = get_pixel(block_x * 4 + x, block_y * 4 + y);
		}

		inline int wrap_or_clamp_x(int x) const
		{
			return m_wrap_addressing ? posmod(x, m_width) : clamp<int>(x, 0, m_width - 1);
		}

		inline int wrap_or_clamp_y(int y) const
		{
			return m_wrap_addressing ? posmod(y, m_height) : clamp<int>(y, 0, m_height - 1);
		}

		inline int wrap_or_clamp_block_x(int bx) const
		{
			return m_wrap_addressing ? posmod(bx, m_block_width) : clamp<int>(bx, 0, m_block_width - 1);
		}

		inline int wrap_or_clamp_block_y(int by) const
		{
			return m_wrap_addressing ? posmod(by, m_block_height) : clamp<int>(by, 0, m_block_height - 1);
		}

		inline vec2F get_interpolation_factors(uint32_t x, uint32_t y) const
		{
			// 0 1 2 3
			// 2 3 0 1
			// .5 .75 0 .25
			static const float s_interp[4] = { 2, 3, 0, 1 };
			return vec2F(s_interp[x & 3], s_interp[y & 3]);
		}

		inline color_rgba interpolate(int x, int y,
			const color_rgba& p, const color_rgba& q,
			const color_rgba& r, const color_rgba& s) const
		{
			static const int s_interp[4] = { 2, 3, 0, 1 };
			const int u_interp = s_interp[x & 3];
			const int v_interp = s_interp[y & 3];

			color_rgba result;

			for (uint32_t c = 0; c < 4; c++)
			{
				int t = p[c] * 4 + u_interp * ((int)q[c] - (int)p[c]);
				int b = r[c] * 4 + u_interp * ((int)s[c] - (int)r[c]);
				int v = t * 4 + v_interp * (b - t);
				if (c < 3)
				{
					v >>= 1;
					v += (v >> 5);
				}
				else
				{
					v += (v >> 4);
				}
				assert((v >= 0) && (v < 256));
				result[c] = static_cast<uint8_t>(v);
			}

			return result;
		}
						
		uint32_t m_width, m_height;
		pvrtc4_block_vector2D m_blocks;
		uint32_t m_block_width, m_block_height;
						
		bool m_wrap_addressing;
		bool m_uses_alpha;
	};

} // namespace basisu