1 //============================================================================
2 
3 #include "PvrTcEncoder.h"
4 #include "AlphaBitmap.h"
5 #include "PvrTcPacket.h"
6 #include "RgbBitmap.h"
7 #include "RgbaBitmap.h"
8 #include "MortonTable.h"
9 #include "BitUtility.h"
10 #include "Interval.h"
11 #include <assert.h>
12 #include <math.h>
13 #include <stdint.h>
14 
15 //============================================================================
16 
17 using namespace Javelin;
18 using Data::MORTON_TABLE;
19 
20 //============================================================================
21 
22 static const unsigned char MODULATION_LUT[16] =
23 {
24 	0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3
25 };
26 
27 //============================================================================
28 
GetMortonNumber(int x,int y)29 inline unsigned PvrTcEncoder::GetMortonNumber(int x, int y)
30 {
31 	return MORTON_TABLE[x >> 8] << 17 | MORTON_TABLE[y >> 8] << 16 | MORTON_TABLE[x & 0xFF] << 1 | MORTON_TABLE[y & 0xFF];
32 }
33 
34 //============================================================================
35 
EncodeAlpha2Bpp(void * result,const AlphaBitmap & bitmap)36 void PvrTcEncoder::EncodeAlpha2Bpp(void* result, const AlphaBitmap& bitmap)
37 {
38 	int size = bitmap.GetBitmapWidth();
39 	assert(size == bitmap.GetBitmapHeight());
40 	assert(BitUtility::IsPowerOf2(size));
41 
42 	// Blocks in each dimension.
43 	int xBlocks = size/8;
44 	int yBlocks = size/4;
45 
46 	const unsigned char* bitmapData = bitmap.GetRawData();
47 
48 	PvrTcPacket* packets = static_cast<PvrTcPacket*>(result);
49 	for(int y = 0; y < yBlocks; ++y)
50 	{
51 		for(int x = 0; x < xBlocks; ++x)
52 		{
53 			PvrTcPacket* packet = packets + GetMortonNumber(x, y);
54 			packet->usePunchthroughAlpha = 0;
55 			packet->colorAIsOpaque = 0;
56 			packet->colorA = 0x7ff;		// White, with 0 alpha
57 			packet->colorBIsOpaque = 1;
58 			packet->colorB = 0x7fff;	// White with full alpha
59 
60 			const unsigned char* blockBitmapData = &bitmapData[y*4*size + x*8];
61 
62 			uint32_t modulationData = 0;
63 			for(int py = 0; py < 4; ++py)
64 			{
65 				const unsigned char* rowBitmapData = blockBitmapData;
66 				for(int px = 0; px < 8; ++px)
67 				{
68 					unsigned char pixel = *rowBitmapData++;
69 					modulationData = BitUtility::RotateRight(modulationData | (pixel >> 7), 1);
70 				}
71 				blockBitmapData += size;
72 			}
73 			packet->modulationData = modulationData;
74 		}
75 	}
76 }
77 
EncodeAlpha4Bpp(void * result,const AlphaBitmap & bitmap)78 void PvrTcEncoder::EncodeAlpha4Bpp(void* result, const AlphaBitmap& bitmap)
79 {
80 	int size = bitmap.GetBitmapWidth();
81 	assert(size == bitmap.GetBitmapHeight());
82 	assert(BitUtility::IsPowerOf2(size));
83 
84 	// Blocks in each dimension.
85 	int blocks = size/4;
86 
87 	const unsigned char* bitmapData = bitmap.GetRawData();
88 
89 	PvrTcPacket* packets = static_cast<PvrTcPacket*>(result);
90 	for(int y = 0; y < blocks; ++y)
91 	{
92 		for(int x = 0; x < blocks; ++x)
93 		{
94 			PvrTcPacket* packet = packets + GetMortonNumber(x, y);
95 			packet->usePunchthroughAlpha = 0;
96 			packet->colorAIsOpaque = 0;
97 			packet->colorA = 0x7ff;		// White, with 0 alpha
98 			packet->colorBIsOpaque = 1;
99 			packet->colorB = 0x7fff;	// White with full alpha
100 
101 			const unsigned char* blockBitmapData = &bitmapData[(y*size + x)*4];
102 
103 			uint32_t modulationData = 0;
104 			for(int py = 0; py < 4; ++py)
105 			{
106 				const unsigned char* rowBitmapData = blockBitmapData;
107 				for(int px = 0; px < 4; ++px)
108 				{
109 					unsigned char pixel = *rowBitmapData++;
110 					modulationData = BitUtility::RotateRight(modulationData | MODULATION_LUT[pixel>>4], 2);
111 				}
112 				blockBitmapData += size;
113 			}
114 			packet->modulationData = modulationData;
115 		}
116 	}
117 }
118 
119 //============================================================================
120 
121 typedef Interval<ColorRgb<unsigned char> > ColorRgbBoundingBox;
122 
CalculateBoundingBox(ColorRgbBoundingBox & cbb,const RgbBitmap & bitmap,int blockX,int blockY)123 static void CalculateBoundingBox(ColorRgbBoundingBox& cbb, const RgbBitmap& bitmap, int blockX, int blockY)
124 {
125 	int size = bitmap.GetBitmapWidth();
126 	const ColorRgb<unsigned char>* data = bitmap.GetData() + blockY * 4 * size + blockX * 4;
127 
128 	cbb.min = data[0];
129 	cbb.max = data[0];
130 	cbb |= data[1];
131 	cbb |= data[2];
132 	cbb |= data[3];
133 
134 	cbb |= data[size];
135 	cbb |= data[size+1];
136 	cbb |= data[size+2];
137 	cbb |= data[size+3];
138 
139 	cbb |= data[2*size];
140 	cbb |= data[2*size+1];
141 	cbb |= data[2*size+2];
142 	cbb |= data[2*size+3];
143 
144 	cbb |= data[3*size];
145 	cbb |= data[3*size+1];
146 	cbb |= data[3*size+2];
147 	cbb |= data[3*size+3];
148 }
149 
EncodeRgb4Bpp(void * result,const RgbBitmap & bitmap)150 void PvrTcEncoder::EncodeRgb4Bpp(void* result, const RgbBitmap& bitmap)
151 {
152 	assert(bitmap.GetBitmapWidth() == bitmap.GetBitmapHeight());
153 	assert(BitUtility::IsPowerOf2(bitmap.GetBitmapWidth()));
154 	const int size = bitmap.GetBitmapWidth();
155 	const int blocks = size / 4;
156 	const int blockMask = blocks-1;
157 
158 	PvrTcPacket* packets = static_cast<PvrTcPacket*>(result);
159 
160 	for(int y = 0; y < blocks; ++y)
161 	{
162 		for(int x = 0; x < blocks; ++x)
163 		{
164 			ColorRgbBoundingBox cbb;
165 			CalculateBoundingBox(cbb, bitmap, x, y);
166 			PvrTcPacket* packet = packets + GetMortonNumber(x, y);
167 			packet->usePunchthroughAlpha = 0;
168 			packet->SetColorA(cbb.min);
169 			packet->SetColorB(cbb.max);
170 		}
171 	}
172 
173 	for(int y = 0; y < blocks; ++y)
174 	{
175 		for(int x = 0; x < blocks; ++x)
176 		{
177 			const unsigned char (*factor)[4] = PvrTcPacket::BILINEAR_FACTORS;
178 			const ColorRgb<unsigned char>* data = bitmap.GetData() + y * 4 * size + x * 4;
179 
180 			uint32_t modulationData = 0;
181 
182 			for(int py = 0; py < 4; ++py)
183 			{
184 				const int yOffset = (py < 2) ? -1 : 0;
185 				const int y0 = (y + yOffset) & blockMask;
186 				const int y1 = (y0+1) & blockMask;
187 
188 				for(int px = 0; px < 4; ++px)
189 				{
190 					const int xOffset = (px < 2) ? -1 : 0;
191 					const int x0 = (x + xOffset) & blockMask;
192 					const int x1 = (x0+1) & blockMask;
193 
194 					const PvrTcPacket* p0 = packets + GetMortonNumber(x0, y0);
195 					const PvrTcPacket* p1 = packets + GetMortonNumber(x1, y0);
196 					const PvrTcPacket* p2 = packets + GetMortonNumber(x0, y1);
197 					const PvrTcPacket* p3 = packets + GetMortonNumber(x1, y1);
198 
199 					ColorRgb<int> ca = p0->GetColorRgbA() * (*factor)[0] +
200 									   p1->GetColorRgbA() * (*factor)[1] +
201 									   p2->GetColorRgbA() * (*factor)[2] +
202 									   p3->GetColorRgbA() * (*factor)[3];
203 
204 					ColorRgb<int> cb = p0->GetColorRgbB() * (*factor)[0] +
205 									   p1->GetColorRgbB() * (*factor)[1] +
206 									   p2->GetColorRgbB() * (*factor)[2] +
207 									   p3->GetColorRgbB() * (*factor)[3];
208 
209 					const ColorRgb<unsigned char>& pixel = data[py*size + px];
210 					ColorRgb<int> d = cb - ca;
211 					ColorRgb<int> p;
212 					p.r=pixel.r*16;
213 					p.g=pixel.g*16;
214 					p.b=pixel.b*16;
215 					ColorRgb<int> v = p - ca;
216 
217 					// PVRTC uses weightings of 0, 3/8, 5/8 and 1
218 					// The boundaries for these are 3/16, 1/2 (=8/16), 13/16
219 					int projection = (v % d) * 16;
220 					int lengthSquared = d % d;
221 					if(projection > 3*lengthSquared) modulationData++;
222 					if(projection > 8*lengthSquared) modulationData++;
223 					if(projection > 13*lengthSquared) modulationData++;
224 
225 					modulationData = BitUtility::RotateRight(modulationData, 2);
226 
227 					factor++;
228 				}
229 			}
230 
231 			PvrTcPacket* packet = packets + GetMortonNumber(x, y);
232 			packet->modulationData = modulationData;
233 		}
234 	}
235 }
236 
237 //============================================================================
238 
CalculateBoundingBox(ColorRgbBoundingBox & cbb,const RgbaBitmap & bitmap,int blockX,int blockY)239 static void CalculateBoundingBox(ColorRgbBoundingBox& cbb, const RgbaBitmap& bitmap, int blockX, int blockY)
240 {
241 	int size = bitmap.GetBitmapWidth();
242 	const ColorRgba<unsigned char>* data = bitmap.GetData() + blockY * 4 * size + blockX * 4;
243 
244 	cbb.min = data[0];
245 	cbb.max = data[0];
246 
247 	cbb |= data[1];
248 	cbb |= data[2];
249 	cbb |= data[3];
250 
251 	cbb |= data[size];
252 	cbb |= data[size+1];
253 	cbb |= data[size+2];
254 	cbb |= data[size+3];
255 
256 	cbb |= data[2*size];
257 	cbb |= data[2*size+1];
258 	cbb |= data[2*size+2];
259 	cbb |= data[2*size+3];
260 
261 	cbb |= data[3*size];
262 	cbb |= data[3*size+1];
263 	cbb |= data[3*size+2];
264 	cbb |= data[3*size+3];
265 }
266 
EncodeRgb4Bpp(void * result,const RgbaBitmap & bitmap)267 void PvrTcEncoder::EncodeRgb4Bpp(void* result, const RgbaBitmap& bitmap)
268 {
269 	assert(bitmap.GetBitmapWidth() == bitmap.GetBitmapHeight());
270 	assert(BitUtility::IsPowerOf2(bitmap.GetBitmapWidth()));
271 	const int size = bitmap.GetBitmapWidth();
272 	const int blocks = size / 4;
273 	const int blockMask = blocks-1;
274 
275 	PvrTcPacket* packets = static_cast<PvrTcPacket*>(result);
276 
277 	for(int y = 0; y < blocks; ++y)
278 	{
279 		for(int x = 0; x < blocks; ++x)
280 		{
281 			ColorRgbBoundingBox cbb;
282 			CalculateBoundingBox(cbb, bitmap, x, y);
283 			PvrTcPacket* packet = packets + GetMortonNumber(x, y);
284 			packet->usePunchthroughAlpha = 0;
285 			packet->SetColorA(cbb.min);
286 			packet->SetColorB(cbb.max);
287 		}
288 	}
289 
290 	for(int y = 0; y < blocks; ++y)
291 	{
292 		for(int x = 0; x < blocks; ++x)
293 		{
294 			const unsigned char (*factor)[4] = PvrTcPacket::BILINEAR_FACTORS;
295 			const ColorRgba<unsigned char>* data = bitmap.GetData() + y * 4 * size + x * 4;
296 
297 			uint32_t modulationData = 0;
298 
299 			for(int py = 0; py < 4; ++py)
300 			{
301 				const int yOffset = (py < 2) ? -1 : 0;
302 				const int y0 = (y + yOffset) & blockMask;
303 				const int y1 = (y0+1) & blockMask;
304 
305 				for(int px = 0; px < 4; ++px)
306 				{
307 					const int xOffset = (px < 2) ? -1 : 0;
308 					const int x0 = (x + xOffset) & blockMask;
309 					const int x1 = (x0+1) & blockMask;
310 
311 					const PvrTcPacket* p0 = packets + GetMortonNumber(x0, y0);
312 					const PvrTcPacket* p1 = packets + GetMortonNumber(x1, y0);
313 					const PvrTcPacket* p2 = packets + GetMortonNumber(x0, y1);
314 					const PvrTcPacket* p3 = packets + GetMortonNumber(x1, y1);
315 
316 					ColorRgb<int> ca = p0->GetColorRgbA() * (*factor)[0] +
317 									   p1->GetColorRgbA() * (*factor)[1] +
318 									   p2->GetColorRgbA() * (*factor)[2] +
319 									   p3->GetColorRgbA() * (*factor)[3];
320 
321 					ColorRgb<int> cb = p0->GetColorRgbB() * (*factor)[0] +
322 									   p1->GetColorRgbB() * (*factor)[1] +
323 									   p2->GetColorRgbB() * (*factor)[2] +
324 									   p3->GetColorRgbB() * (*factor)[3];
325 
326 					const ColorRgb<unsigned char>& pixel = data[py*size + px];
327 					ColorRgb<int> d = cb - ca;
328 					ColorRgb<int> p;
329 					p.r=pixel.r*16;
330 					p.g=pixel.g*16;
331 					p.b=pixel.b*16;
332 					ColorRgb<int> v = p - ca;
333 
334 					// PVRTC uses weightings of 0, 3/8, 5/8 and 1
335 					// The boundaries for these are 3/16, 1/2 (=8/16), 13/16
336 					int projection = (v % d) * 16;
337 					int lengthSquared = d % d;
338 					if(projection > 3*lengthSquared) modulationData++;
339 					if(projection > 8*lengthSquared) modulationData++;
340 					if(projection > 13*lengthSquared) modulationData++;
341 
342 					modulationData = BitUtility::RotateRight(modulationData, 2);
343 
344 					factor++;
345 				}
346 			}
347 
348 			PvrTcPacket* packet = packets + GetMortonNumber(x, y);
349 			packet->modulationData = modulationData;
350 		}
351 	}
352 }
353 
354 //============================================================================
355 
356 typedef Interval<ColorRgba<unsigned char> > ColorRgbaBoundingBox;
357 
CalculateBoundingBox(ColorRgbaBoundingBox & cbb,const RgbaBitmap & bitmap,int blockX,int blockY)358 static void CalculateBoundingBox(ColorRgbaBoundingBox& cbb, const RgbaBitmap& bitmap, int blockX, int blockY)
359 {
360 	int size = bitmap.GetBitmapWidth();
361 	const ColorRgba<unsigned char>* data = bitmap.GetData() + blockY * 4 * size + blockX * 4;
362 
363 	cbb.min = data[0];
364 	cbb.max = data[0];
365 
366 	cbb |= data[1];
367 	cbb |= data[2];
368 	cbb |= data[3];
369 
370 	cbb |= data[size];
371 	cbb |= data[size+1];
372 	cbb |= data[size+2];
373 	cbb |= data[size+3];
374 
375 	cbb |= data[2*size];
376 	cbb |= data[2*size+1];
377 	cbb |= data[2*size+2];
378 	cbb |= data[2*size+3];
379 
380 	cbb |= data[3*size];
381 	cbb |= data[3*size+1];
382 	cbb |= data[3*size+2];
383 	cbb |= data[3*size+3];
384 }
385 
EncodeRgba4Bpp(void * result,const RgbaBitmap & bitmap)386 void PvrTcEncoder::EncodeRgba4Bpp(void* result, const RgbaBitmap& bitmap)
387 {
388 	assert(bitmap.GetBitmapWidth() == bitmap.GetBitmapHeight());
389 	assert(BitUtility::IsPowerOf2(bitmap.GetBitmapWidth()));
390 	const int size = bitmap.GetBitmapWidth();
391 	const int blocks = size / 4;
392 	const int blockMask = blocks-1;
393 
394 	PvrTcPacket* packets = static_cast<PvrTcPacket*>(result);
395 
396 	for(int y = 0; y < blocks; ++y)
397 	{
398 		for(int x = 0; x < blocks; ++x)
399 		{
400 			ColorRgbaBoundingBox cbb;
401 			CalculateBoundingBox(cbb, bitmap, x, y);
402 			PvrTcPacket* packet = packets + GetMortonNumber(x, y);
403 			packet->usePunchthroughAlpha = 0;
404 			packet->SetColorA(cbb.min);
405 			packet->SetColorB(cbb.max);
406 		}
407 	}
408 
409 	for(int y = 0; y < blocks; ++y)
410 	{
411 		for(int x = 0; x < blocks; ++x)
412 		{
413 			const unsigned char (*factor)[4] = PvrTcPacket::BILINEAR_FACTORS;
414 			const ColorRgba<unsigned char>* data = bitmap.GetData() + y * 4 * size + x * 4;
415 
416 			uint32_t modulationData = 0;
417 
418 			for(int py = 0; py < 4; ++py)
419 			{
420 				const int yOffset = (py < 2) ? -1 : 0;
421 				const int y0 = (y + yOffset) & blockMask;
422 				const int y1 = (y0+1) & blockMask;
423 
424 				for(int px = 0; px < 4; ++px)
425 				{
426 					const int xOffset = (px < 2) ? -1 : 0;
427 					const int x0 = (x + xOffset) & blockMask;
428 					const int x1 = (x0+1) & blockMask;
429 
430 					const PvrTcPacket* p0 = packets + GetMortonNumber(x0, y0);
431 					const PvrTcPacket* p1 = packets + GetMortonNumber(x1, y0);
432 					const PvrTcPacket* p2 = packets + GetMortonNumber(x0, y1);
433 					const PvrTcPacket* p3 = packets + GetMortonNumber(x1, y1);
434 
435 					ColorRgba<int> ca = p0->GetColorRgbaA() * (*factor)[0] +
436 										p1->GetColorRgbaA() * (*factor)[1] +
437 										p2->GetColorRgbaA() * (*factor)[2] +
438 										p3->GetColorRgbaA() * (*factor)[3];
439 
440 					ColorRgba<int> cb = p0->GetColorRgbaB() * (*factor)[0] +
441 										p1->GetColorRgbaB() * (*factor)[1] +
442 										p2->GetColorRgbaB() * (*factor)[2] +
443 										p3->GetColorRgbaB() * (*factor)[3];
444 
445 					const ColorRgba<unsigned char>& pixel = data[py*size + px];
446 					ColorRgba<int> d = cb - ca;
447 					ColorRgba<int> p;
448 					p.r=pixel.r*16;
449 					p.g=pixel.g*16;
450 					p.b=pixel.b*16;
451 					p.a=pixel.a*16;
452 					ColorRgba<int> v = p - ca;
453 
454 					// PVRTC uses weightings of 0, 3/8, 5/8 and 1
455 					// The boundaries for these are 3/16, 1/2 (=8/16), 13/16
456 					int projection = (v % d) * 16;
457 					int lengthSquared = d % d;
458 					if(projection > 3*lengthSquared) modulationData++;
459 					if(projection > 8*lengthSquared) modulationData++;
460 					if(projection > 13*lengthSquared) modulationData++;
461 
462 					modulationData = BitUtility::RotateRight(modulationData, 2);
463 
464 					factor++;
465 				}
466 			}
467 
468 			PvrTcPacket* packet = packets + GetMortonNumber(x, y);
469 			packet->modulationData = modulationData;
470 		}
471 	}
472 }
473 
474 //============================================================================
475