1 // Copyright NVIDIA Corporation 2007 -- Ignacio Castano <icastano@nvidia.com>
2 //
3 // Permission is hereby granted, free of charge, to any person
4 // obtaining a copy of this software and associated documentation
5 // files (the "Software"), to deal in the Software without
6 // restriction, including without limitation the rights to use,
7 // copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following
10 // conditions:
11 //
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 // OTHER DEALINGS IN THE SOFTWARE.
23 
24 #include <nvcore/Memory.h>
25 
26 #include <nvimage/Image.h>
27 #include <nvimage/ColorBlock.h>
28 #include <nvimage/BlockDXT.h>
29 
30 #include "nvtt.h"
31 #include "CompressDXT.h"
32 #include "QuickCompressDXT.h"
33 #include "OptimalCompressDXT.h"
34 #include "CompressionOptions.h"
35 #include "OutputOptions.h"
36 
37 // squish
38 #include "squish/colourset.h"
39 //#include "squish/clusterfit.h"
40 #include "squish/fastclusterfit.h"
41 #include "squish/weightedclusterfit.h"
42 
43 
44 // s3_quant
45 #if defined(HAVE_S3QUANT)
46 #include "s3tc/s3_quant.h"
47 #endif
48 
49 // ati tc
50 #if defined(HAVE_ATITC)
51 #include "atitc/ATI_Compress.h"
52 #endif
53 
54 //#include <time.h>
55 
56 using namespace nv;
57 using namespace nvtt;
58 
59 
FastCompressor()60 nv::FastCompressor::FastCompressor() : m_image(NULL), m_alphaMode(AlphaMode_None)
61 {
62 }
63 
~FastCompressor()64 nv::FastCompressor::~FastCompressor()
65 {
66 }
67 
setImage(const Image * image,nvtt::AlphaMode alphaMode)68 void nv::FastCompressor::setImage(const Image * image, nvtt::AlphaMode alphaMode)
69 {
70 	m_image = image;
71 	m_alphaMode = alphaMode;
72 }
73 
compressDXT1(const OutputOptions::Private & outputOptions)74 void nv::FastCompressor::compressDXT1(const OutputOptions::Private & outputOptions)
75 {
76 	const uint w = m_image->width();
77 	const uint h = m_image->height();
78 
79 	ColorBlock rgba;
80 	BlockDXT1 block;
81 
82 	for (uint y = 0; y < h; y += 4) {
83 		for (uint x = 0; x < w; x += 4) {
84 			rgba.init(m_image, x, y);
85 
86 			QuickCompress::compressDXT1(rgba, &block);
87 
88 			if (outputOptions.outputHandler != NULL) {
89 				outputOptions.outputHandler->writeData(&block, sizeof(block));
90 			}
91 		}
92 	}
93 }
94 
95 
compressDXT1a(const OutputOptions::Private & outputOptions)96 void nv::FastCompressor::compressDXT1a(const OutputOptions::Private & outputOptions)
97 {
98 	const uint w = m_image->width();
99 	const uint h = m_image->height();
100 
101 	ColorBlock rgba;
102 	BlockDXT1 block;
103 
104 	for (uint y = 0; y < h; y += 4) {
105 		for (uint x = 0; x < w; x += 4) {
106 			rgba.init(m_image, x, y);
107 
108 			QuickCompress::compressDXT1a(rgba, &block);
109 
110 			if (outputOptions.outputHandler != NULL) {
111 				outputOptions.outputHandler->writeData(&block, sizeof(block));
112 			}
113 		}
114 	}
115 }
116 
117 
compressDXT3(const nvtt::OutputOptions::Private & outputOptions)118 void nv::FastCompressor::compressDXT3(const nvtt::OutputOptions::Private & outputOptions)
119 {
120 	const uint w = m_image->width();
121 	const uint h = m_image->height();
122 
123 	ColorBlock rgba;
124 	BlockDXT3 block;
125 
126 	for (uint y = 0; y < h; y += 4) {
127 		for (uint x = 0; x < w; x += 4) {
128 			rgba.init(m_image, x, y);
129 
130 			QuickCompress::compressDXT3(rgba, &block);
131 
132 			if (outputOptions.outputHandler != NULL) {
133 				outputOptions.outputHandler->writeData(&block, sizeof(block));
134 			}
135 		}
136 	}
137 }
138 
139 
compressDXT5(const nvtt::OutputOptions::Private & outputOptions)140 void nv::FastCompressor::compressDXT5(const nvtt::OutputOptions::Private & outputOptions)
141 {
142 	const uint w = m_image->width();
143 	const uint h = m_image->height();
144 
145 	ColorBlock rgba;
146 	BlockDXT5 block;
147 
148 	for (uint y = 0; y < h; y += 4) {
149 		for (uint x = 0; x < w; x += 4) {
150 			rgba.init(m_image, x, y);
151 
152 			QuickCompress::compressDXT5(rgba, &block, 0);
153 
154 			if (outputOptions.outputHandler != NULL) {
155 				outputOptions.outputHandler->writeData(&block, sizeof(block));
156 			}
157 		}
158 	}
159 }
160 
161 
compressDXT5n(const nvtt::OutputOptions::Private & outputOptions)162 void nv::FastCompressor::compressDXT5n(const nvtt::OutputOptions::Private & outputOptions)
163 {
164 	const uint w = m_image->width();
165 	const uint h = m_image->height();
166 
167 	ColorBlock rgba;
168 	BlockDXT5 block;
169 
170 	for (uint y = 0; y < h; y += 4) {
171 		for (uint x = 0; x < w; x += 4) {
172 			rgba.init(m_image, x, y);
173 
174 			rgba.swizzleDXT5n();
175 
176 			QuickCompress::compressDXT5(rgba, &block, 0);
177 
178 			if (outputOptions.outputHandler != NULL) {
179 				outputOptions.outputHandler->writeData(&block, sizeof(block));
180 			}
181 		}
182 	}
183 }
184 
185 
SlowCompressor()186 nv::SlowCompressor::SlowCompressor() : m_image(NULL), m_alphaMode(AlphaMode_None)
187 {
188 }
189 
~SlowCompressor()190 nv::SlowCompressor::~SlowCompressor()
191 {
192 }
193 
setImage(const Image * image,nvtt::AlphaMode alphaMode)194 void nv::SlowCompressor::setImage(const Image * image, nvtt::AlphaMode alphaMode)
195 {
196 	m_image = image;
197 	m_alphaMode = alphaMode;
198 }
199 
compressDXT1(const CompressionOptions::Private & compressionOptions,const OutputOptions::Private & outputOptions)200 void nv::SlowCompressor::compressDXT1(const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions)
201 {
202 	const uint w = m_image->width();
203 	const uint h = m_image->height();
204 
205 	ColorBlock rgba;
206 	BlockDXT1 block;
207 
208 	squish::WeightedClusterFit fit;
209 	//squish::ClusterFit fit;
210 	//squish::FastClusterFit fit;
211 	fit.SetMetric(compressionOptions.colorWeight.x(), compressionOptions.colorWeight.y(), compressionOptions.colorWeight.z());
212 
213 	for (uint y = 0; y < h; y += 4) {
214 		for (uint x = 0; x < w; x += 4) {
215 
216 			rgba.init(m_image, x, y);
217 
218 			if (rgba.isSingleColor())
219 			{
220 				OptimalCompress::compressDXT1(rgba.color(0), &block);
221 			}
222 			else
223 			{
224 				squish::ColourSet colours((uint8 *)rgba.colors(), 0, true);
225 				fit.SetColourSet(&colours, squish::kDxt1);
226 				fit.Compress(&block);
227 			}
228 
229 			if (outputOptions.outputHandler != NULL) {
230 				outputOptions.outputHandler->writeData(&block, sizeof(block));
231 			}
232 		}
233 	}
234 }
235 
236 
compressDXT1a(const CompressionOptions::Private & compressionOptions,const OutputOptions::Private & outputOptions)237 void nv::SlowCompressor::compressDXT1a(const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions)
238 {
239 	const uint w = m_image->width();
240 	const uint h = m_image->height();
241 
242 	ColorBlock rgba;
243 	BlockDXT1 block;
244 
245 	squish::WeightedClusterFit fit;
246 	fit.SetMetric(compressionOptions.colorWeight.x(), compressionOptions.colorWeight.y(), compressionOptions.colorWeight.z());
247 
248 	for (uint y = 0; y < h; y += 4) {
249 		for (uint x = 0; x < w; x += 4) {
250 
251 			rgba.init(m_image, x, y);
252 
253 			bool anyAlpha = false;
254 			bool allAlpha = true;
255 
256 			for (uint i = 0; i < 16; i++)
257 			{
258 				if (rgba.color(i).a < 128) anyAlpha = true;
259 				else allAlpha = false;
260 			}
261 
262 			if ((!anyAlpha && rgba.isSingleColor() || allAlpha))
263 			{
264 				OptimalCompress::compressDXT1a(rgba.color(0), &block);
265 			}
266 			else
267 			{
268 				squish::ColourSet colours((uint8 *)rgba.colors(), squish::kDxt1|squish::kWeightColourByAlpha);
269 				fit.SetColourSet(&colours, squish::kDxt1);
270 				fit.Compress(&block);
271 			}
272 
273 			if (outputOptions.outputHandler != NULL) {
274 				outputOptions.outputHandler->writeData(&block, sizeof(block));
275 			}
276 		}
277 	}
278 }
279 
280 
compressDXT3(const CompressionOptions::Private & compressionOptions,const OutputOptions::Private & outputOptions)281 void nv::SlowCompressor::compressDXT3(const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions)
282 {
283 	const uint w = m_image->width();
284 	const uint h = m_image->height();
285 
286 	ColorBlock rgba;
287 	BlockDXT3 block;
288 
289 	squish::WeightedClusterFit fit;
290 	//squish::FastClusterFit fit;
291 	fit.SetMetric(compressionOptions.colorWeight.x(), compressionOptions.colorWeight.y(), compressionOptions.colorWeight.z());
292 
293 	for (uint y = 0; y < h; y += 4) {
294 		for (uint x = 0; x < w; x += 4) {
295 
296 			rgba.init(m_image, x, y);
297 
298 			// Compress explicit alpha.
299 			OptimalCompress::compressDXT3A(rgba, &block.alpha);
300 
301 			// Compress color.
302 			if (rgba.isSingleColor())
303 			{
304 				OptimalCompress::compressDXT1(rgba.color(0), &block.color);
305 			}
306 			else
307 			{
308 				squish::ColourSet colours((uint8 *)rgba.colors(), squish::kWeightColourByAlpha);
309 				fit.SetColourSet(&colours, 0);
310 				fit.Compress(&block.color);
311 			}
312 
313 			if (outputOptions.outputHandler != NULL) {
314 				outputOptions.outputHandler->writeData(&block, sizeof(block));
315 			}
316 		}
317 	}
318 }
319 
compressDXT5(const CompressionOptions::Private & compressionOptions,const OutputOptions::Private & outputOptions)320 void nv::SlowCompressor::compressDXT5(const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions)
321 {
322 	const uint w = m_image->width();
323 	const uint h = m_image->height();
324 
325 	ColorBlock rgba;
326 	BlockDXT5 block;
327 
328 	squish::WeightedClusterFit fit;
329 	fit.SetMetric(compressionOptions.colorWeight.x(), compressionOptions.colorWeight.y(), compressionOptions.colorWeight.z());
330 
331 	for (uint y = 0; y < h; y += 4) {
332 		for (uint x = 0; x < w; x += 4) {
333 
334 			rgba.init(m_image, x, y);
335 
336 			// Compress alpha.
337 			if (compressionOptions.quality == Quality_Highest)
338 			{
339 				OptimalCompress::compressDXT5A(rgba, &block.alpha);
340 			}
341 			else
342 			{
343 				QuickCompress::compressDXT5A(rgba, &block.alpha);
344 			}
345 
346 			// Compress color.
347 			if (rgba.isSingleColor())
348 			{
349 				OptimalCompress::compressDXT1(rgba.color(0), &block.color);
350 			}
351 			else
352 			{
353 				squish::ColourSet colours((uint8 *)rgba.colors(), squish::kWeightColourByAlpha);
354 				fit.SetColourSet(&colours, 0);
355 				fit.Compress(&block.color);
356 			}
357 
358 			if (outputOptions.outputHandler != NULL) {
359 				outputOptions.outputHandler->writeData(&block, sizeof(block));
360 			}
361 		}
362 	}
363 }
364 
365 
compressDXT5n(const CompressionOptions::Private & compressionOptions,const OutputOptions::Private & outputOptions)366 void nv::SlowCompressor::compressDXT5n(const CompressionOptions::Private & compressionOptions, const OutputOptions::Private & outputOptions)
367 {
368 	const uint w = m_image->width();
369 	const uint h = m_image->height();
370 
371 	ColorBlock rgba;
372 	BlockDXT5 block;
373 
374 	for (uint y = 0; y < h; y += 4) {
375 		for (uint x = 0; x < w; x += 4) {
376 
377 			rgba.init(m_image, x, y);
378 
379 			rgba.swizzleDXT5n();
380 
381 			// Compress X.
382 			if (compressionOptions.quality == Quality_Highest)
383 			{
384 				OptimalCompress::compressDXT5A(rgba, &block.alpha);
385 			}
386 			else
387 			{
388 				QuickCompress::compressDXT5A(rgba, &block.alpha);
389 			}
390 
391 			// Compress Y.
392 			OptimalCompress::compressDXT1G(rgba, &block.color);
393 
394 			if (outputOptions.outputHandler != NULL) {
395 				outputOptions.outputHandler->writeData(&block, sizeof(block));
396 			}
397 		}
398 	}
399 }
400 
401 
compressBC4(const CompressionOptions::Private & compressionOptions,const nvtt::OutputOptions::Private & outputOptions)402 void nv::SlowCompressor::compressBC4(const CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions)
403 {
404 	const uint w = m_image->width();
405 	const uint h = m_image->height();
406 
407 	ColorBlock rgba;
408 	AlphaBlockDXT5 block;
409 
410 	for (uint y = 0; y < h; y += 4) {
411 		for (uint x = 0; x < w; x += 4) {
412 
413 			rgba.init(m_image, x, y);
414 
415 			if (compressionOptions.quality == Quality_Highest)
416 			{
417 				OptimalCompress::compressDXT5A(rgba, &block);
418 			}
419 			else
420 			{
421 				QuickCompress::compressDXT5A(rgba, &block);
422 			}
423 
424 			if (outputOptions.outputHandler != NULL) {
425 				outputOptions.outputHandler->writeData(&block, sizeof(block));
426 			}
427 		}
428 	}
429 }
430 
431 
compressBC5(const CompressionOptions::Private & compressionOptions,const nvtt::OutputOptions::Private & outputOptions)432 void nv::SlowCompressor::compressBC5(const CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions)
433 {
434 	const uint w = m_image->width();
435 	const uint h = m_image->height();
436 
437 	ColorBlock xcolor;
438 	ColorBlock ycolor;
439 
440 	BlockATI2 block;
441 
442 	for (uint y = 0; y < h; y += 4) {
443 		for (uint x = 0; x < w; x += 4) {
444 
445 			xcolor.init(m_image, x, y);
446 			xcolor.splatX();
447 
448 			ycolor.init(m_image, x, y);
449 			ycolor.splatY();
450 
451 			if (compressionOptions.quality == Quality_Highest)
452 			{
453 				OptimalCompress::compressDXT5A(xcolor, &block.x);
454 				OptimalCompress::compressDXT5A(ycolor, &block.y);
455 			}
456 			else
457 			{
458 				QuickCompress::compressDXT5A(xcolor, &block.x);
459 				QuickCompress::compressDXT5A(ycolor, &block.y);
460 			}
461 
462 			if (outputOptions.outputHandler != NULL) {
463 				outputOptions.outputHandler->writeData(&block, sizeof(block));
464 			}
465 		}
466 	}
467 }
468 
469 
470 #if defined(HAVE_S3QUANT)
471 
s3CompressDXT1(const Image * image,const nvtt::OutputOptions::Private & outputOptions)472 void nv::s3CompressDXT1(const Image * image, const nvtt::OutputOptions::Private & outputOptions)
473 {
474 	const uint w = image->width();
475 	const uint h = image->height();
476 
477 	float error = 0.0f;
478 
479 	BlockDXT1 dxtBlock3;
480 	BlockDXT1 dxtBlock4;
481 	ColorBlock block;
482 
483 	for (uint y = 0; y < h; y += 4) {
484 		for (uint x = 0; x < w; x += 4) {
485 			block.init(image, x, y);
486 
487 			// Init rgb block.
488 			RGBBlock rgbBlock;
489 			rgbBlock.n = 16;
490 			for (uint i = 0; i < 16; i++) {
491 				rgbBlock.colorChannel[i][0] = clamp(float(block.color(i).r) / 255.0f, 0.0f, 1.0f);
492 				rgbBlock.colorChannel[i][1] = clamp(float(block.color(i).g) / 255.0f, 0.0f, 1.0f);
493 				rgbBlock.colorChannel[i][2] = clamp(float(block.color(i).b) / 255.0f, 0.0f, 1.0f);
494 			}
495 			rgbBlock.weight[0] = 1.0f;
496 			rgbBlock.weight[1] = 1.0f;
497 			rgbBlock.weight[2] = 1.0f;
498 
499 			rgbBlock.inLevel = 4;
500 			CodeRGBBlock(&rgbBlock);
501 
502 			// Copy results to DXT block.
503 			dxtBlock4.col0.r = rgbBlock.endPoint[0][0];
504 			dxtBlock4.col0.g = rgbBlock.endPoint[0][1];
505 			dxtBlock4.col0.b = rgbBlock.endPoint[0][2];
506 
507 			dxtBlock4.col1.r = rgbBlock.endPoint[1][0];
508 			dxtBlock4.col1.g = rgbBlock.endPoint[1][1];
509 			dxtBlock4.col1.b = rgbBlock.endPoint[1][2];
510 
511 			dxtBlock4.setIndices(rgbBlock.index);
512 
513 			if (dxtBlock4.col0.u < dxtBlock4.col1.u) {
514 				swap(dxtBlock4.col0.u, dxtBlock4.col1.u);
515 				dxtBlock4.indices ^= 0x55555555;
516 			}
517 
518 			uint error4 = blockError(block, dxtBlock4);
519 
520 			rgbBlock.inLevel = 3;
521 
522 			CodeRGBBlock(&rgbBlock);
523 
524 			// Copy results to DXT block.
525 			dxtBlock3.col0.r = rgbBlock.endPoint[0][0];
526 			dxtBlock3.col0.g = rgbBlock.endPoint[0][1];
527 			dxtBlock3.col0.b = rgbBlock.endPoint[0][2];
528 
529 			dxtBlock3.col1.r = rgbBlock.endPoint[1][0];
530 			dxtBlock3.col1.g = rgbBlock.endPoint[1][1];
531 			dxtBlock3.col1.b = rgbBlock.endPoint[1][2];
532 
533 			dxtBlock3.setIndices(rgbBlock.index);
534 
535 			if (dxtBlock3.col0.u > dxtBlock3.col1.u) {
536 				swap(dxtBlock3.col0.u, dxtBlock3.col1.u);
537 				dxtBlock3.indices ^= (~dxtBlock3.indices  >> 1) & 0x55555555;
538 			}
539 
540 			uint error3 = blockError(block, dxtBlock3);
541 
542 			if (error3 < error4) {
543 				error += error3;
544 
545 				if (outputOptions.outputHandler != NULL) {
546 					outputOptions.outputHandler->writeData(&dxtBlock3, sizeof(dxtBlock3));
547 				}
548 			}
549 			else {
550 				error += error4;
551 
552 				if (outputOptions.outputHandler != NULL) {
553 					outputOptions.outputHandler->writeData(&dxtBlock4, sizeof(dxtBlock4));
554 				}
555 			}
556 		}
557 	}
558 
559 	printf("error = %f\n", error/((w+3)/4 * (h+3)/4));
560 }
561 
562 #endif // defined(HAVE_S3QUANT)
563 
564 
565 #if defined(HAVE_ATITC)
566 
atiCompressDXT1(const Image * image,const OutputOptions::Private & outputOptions)567 void nv::atiCompressDXT1(const Image * image, const OutputOptions::Private & outputOptions)
568 {
569 	// Init source texture
570 	ATI_TC_Texture srcTexture;
571 	srcTexture.dwSize = sizeof(srcTexture);
572 	srcTexture.dwWidth = image->width();
573 	srcTexture.dwHeight = image->height();
574 	srcTexture.dwPitch = image->width() * 4;
575 	srcTexture.format = ATI_TC_FORMAT_ARGB_8888;
576 	srcTexture.dwDataSize = ATI_TC_CalculateBufferSize(&srcTexture);
577 	srcTexture.pData = (ATI_TC_BYTE*) image->pixels();
578 
579 	// Init dest texture
580 	ATI_TC_Texture destTexture;
581 	destTexture.dwSize = sizeof(destTexture);
582 	destTexture.dwWidth = image->width();
583 	destTexture.dwHeight = image->height();
584 	destTexture.dwPitch = 0;
585 	destTexture.format = ATI_TC_FORMAT_DXT1;
586 	destTexture.dwDataSize = ATI_TC_CalculateBufferSize(&destTexture);
587 	destTexture.pData = (ATI_TC_BYTE*) mem::malloc(destTexture.dwDataSize);
588 
589 	// Compress
590 	ATI_TC_ConvertTexture(&srcTexture, &destTexture, NULL, NULL, NULL, NULL);
591 
592 	if (outputOptions.outputHandler != NULL) {
593 		outputOptions.outputHandler->writeData(destTexture.pData, destTexture.dwDataSize);
594 	}
595 }
596 
597 #endif // defined(HAVE_ATITC)
598