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