1 /*---------------------------------------------------------------------*
2 Project: tc library
3 File: TCMipmap.cpp
4
5 Copyright 1998-2001 Nintendo. All rights reserved.
6
7 These coded instructions, statements and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 Change History:
14
15 $Log: TCMipmap.cpp,v $
16 Revision 1.1 2006/02/17 09:01:53 mitu
17 1st version
18
19
20 9 4/11/01 3:08p John
21 Updated header copyrights and pathname.
22
23 8 3/12/01 7:00p John
24 Hosogai-san extended the box filter.
25
26 7 8/10/00 6:02p Mikepc
27
28 6 4/03/00 5:39p Mikepc
29 change #include TCCreateDDS.h to TCCreateS3.h
30 change TCConvertToDDS call to TCConvertToS3
31
32 5 3/17/00 1:19p Mikepc
33 change tc to use indices numbered from 0.
34
35 4 2/14/00 1:03p Mikepc
36 removed power of 2 size restriction for single LOD images.
37
38 3 1/03/00 3:10p Mikepc
39 added comments regarding image size restrictions to
40 TCCheckMipMapConversionParams, TCCheckPower2.
41 even single LODs must have power2 dimensions due to
42 1) OpenGL emulation requirements.
43 2) converter's default filter mode is GX_REPEAT in TCWriteTplFile.
44
45
46 2 1/03/00 12:32p Mikepc
47 fixed potential divide by 0 bug in TCBoxFilter()
48
49 1 12/03/99 3:45p Ryan
50
51 2 11/23/99 2:52p Mikepc
52 improved mipmap code to use alpha information when mipmapping color.
53
54 22 10/08/99 2:45p Mikepc
55 update for tplConv portability: altered data structures, replaced
56 references to 'read tga code' with ' *fileFn, removed redundant
57 functions. Changed some file conversion paths.
58
59 21 10/01/99 1:28p Mikepc
60 changed CreateNextMipMapLayer() to remove redundant 'level' param.
61
62 20 10/01/99 12:20p Mikepc
63 Integrated s3.lib code to generate s3 textures 'on the fly' as direct
64 draw surfaces. Removed .dds file reading code. Changed CMP texture
65 generation 'order of operations' to- rgb layer->s3->CMPR format per
66 mipmap LOD.
67
68 19 9/27/99 11:32a Mikepc
69 line 342: changed for loop condition to allow (minLOD > 0) case when
70 creating mipmaps.
71
72
73 14 9/16/99 8:47p Mikepc
74 updated code for auto-palette generation
75
76 13 9/02/99 11:12a Mikepc
77 some code re-organization between files.
78 added code (verify.cpp) to invoke s3tc.exe from within tc program.
79 changed some routines to accommodate the new texture creation path.
80
81 12 8/30/99 5:07p Mikepc
82 changes to dds/cmp processing code to allow odd sized images and images
83 smaller than 8x8 texel.
84
85 11 8/26/99 4:59p Mikepc
86 renamed file extensions from .c to .cpp.
87 .cpp extension allows addition of namespace protection to remove
88 potential name collisions with tool code. Exceptions are CreateTplFile
89 and QuickConvert. These are extern "C" linked.
90
91 10 8/26/99 11:38a Mikepc
92
93 9 8/26/99 11:03a Mikepc
94 tplCon rewrite for efficient memory usage, batch file processing
95 ability.
96
97 $NoKeywords: $
98
99 -----------------------------------------------------------------------*/
100
101 #include <stdio.h>
102
103 #include <charPipeline/tc/TCCommon.h>
104
105 #include "TCMipmap.h"
106 #include "TCImageList.h"
107 #include "TCLayerInternal.h"
108 #include "TCMem.h"
109 #include "TCTPLToolbox.h"
110 #include "TCCreateS3.h"
111 #include "TCGXDefs.h"
112
113 /********************************/
114 // macro to check an LOD for valid range of mipmap pyramid ( 0 to 10 )
115 #define CheckRangeLOD( lod ) (((lod) < 0) || ((lod) > 10)) ? (0) : (1)
116
117 /*>*******************************(*)*******************************<*/
118 static void TCCreateNextMipMapLayer( TCImage* srcImage, TCLayer* dstColor,
119 TCLayer* dstAlpha );
120
121 static void TCBoxFilter ( TCLayer* srcC, TCLayer* srcA, TCLayer* dstC,
122 TCLayer* dstA, u32 dstCol, u32 dstRow,
123 u32 boxWid, u32 boxHgt );
124
125 static void TCCheckMipMapConvParams ( TCImage* thisImage );
126 static u32 TCCheckPower2 ( TCImage* thisImage );
127 static u32 TCCheckMipMapReduction ( TCImage* thisImage );
128 static u32 TCCheckMipMapFormats ( TCImage* thisImage );
129
130 /*>*******************************(*)*******************************<*/
131 // create a range of mipmaps from the source color layer
132 /*>*******************************(*)*******************************<*/
TCWriteTplImageMipMaps(FILE * fp,TCImage * thisImage)133 u32 TCWriteTplImageMipMaps ( FILE* fp, TCImage* thisImage )
134 {
135 u32 size, level, numLOD;
136 u8* tplBuffer, *thisBuffer;
137 u32 bytesWritten = 0;
138 TCLayer* colorLayer = NULL;
139 TCLayer* alphaLayer = NULL;
140 TCLayer cmpLayer; // intermediate layer for CMP textures
141 TCImage imTmp;
142
143
144 TCAssertMsg( (fp != NULL), "TCWriteTplImageMipMaps: NULL file pointer\n" );
145 TCAssertMsg( (thisImage != NULL), "TCWriteTplImageMipMaps: NULL image pointer\n" );
146
147
148 // check for acceptable image sizes, LOD range for mipmaps.
149 TCCheckMipMapConvParams( thisImage );
150
151 // create a copy of thisImage. The copy's layers will be
152 // modified to produce each successive LOD
153 TCCopyImage( thisImage, &imTmp );
154 imTmp.prev = NULL;
155 imTmp.next = NULL;
156
157 // create a temporary layer with a buffer large enough to hold the 1st LOD;
158 // each successive LODs will overwrite a portion of this buffer
159 colorLayer = &imTmp.lyColor;
160 colorLayer->type = thisImage->lyColor.type;
161 colorLayer->width = ( thisImage->lyColor.width >> thisImage->minLOD );
162 colorLayer->height = ( thisImage->lyColor.height >> thisImage->minLOD );
163 colorLayer->data = NULL;
164 TCSetLayerBuffer( colorLayer );
165
166
167 // create optional alpha layer
168 if( thisImage->alphaSrcImage != TC_UNUSED )
169 {
170 // check layer dimensions
171 if( (thisImage->lyColor.width != thisImage->lyAlpha.width) || (thisImage->lyColor.height != thisImage->lyAlpha.height) )
172 {
173 TCErrorMsg( "WriteTplImageMipMaps: color layer %d and alpha layer %d have different dimensions\n", thisImage->colorSrcImage, thisImage->alphaSrcImage );
174 return 0;
175 }
176
177 alphaLayer = &imTmp.lyAlpha;
178 alphaLayer->type = thisImage->lyAlpha.type;
179 alphaLayer->width = ( thisImage->lyAlpha.width >> thisImage->minLOD );
180 alphaLayer->height = ( thisImage->lyAlpha.height >> thisImage->minLOD );
181 alphaLayer->data = NULL;
182 TCSetLayerBuffer( alphaLayer );
183 }
184
185 // allocate and zero a buffer large enough to hold all output LODs end to end
186 // 'thisBuffer' points to the current LOD within 'tplBuffer'
187 tplBuffer = (u8*)TCCalloc( 1, thisImage->tplBufferSize );
188 thisBuffer = tplBuffer;
189
190 // generate mipmap pyramid sequentially from minLOD to maxLOD
191 // use LOD 0 from the source layers regardless of how many mipmaps are present
192
193 // convert each mipmap in turn to Dolphin format and pack
194 // sequentially in thisImage->tplBuffer
195
196 // swap the original image layers for temporary 'next mipmap' layers each iteration;
197 // restore the original at the end.
198
199 numLOD = imTmp.maxLOD - imTmp.minLOD + 1;
200
201 for( level = imTmp.minLOD; level < (imTmp.minLOD + numLOD); level++ )
202 {
203
204 // use the same allocated memory, but reset the buffer dimensions for each LOD
205 colorLayer->width = ( thisImage->lyColor.width >> level );
206 colorLayer->height = ( thisImage->lyColor.height >> level );
207
208 if( alphaLayer )
209 {
210 alphaLayer->width = ( thisImage->lyAlpha.width >> level );
211 alphaLayer->height = ( thisImage->lyAlpha.height >> level );
212 }
213
214 // convert from base layer to next LOD
215 TCCreateNextMipMapLayer( thisImage, colorLayer, alphaLayer );
216
217 // write the LOD to the buffer
218 switch( thisImage->texelFormat )
219 {
220 case TPL_IMAGE_TEXEL_FMT_I4: TCWriteTplImage_I4( colorLayer, thisBuffer ); break;
221 case TPL_IMAGE_TEXEL_FMT_I8: TCWriteTplImage_I8( colorLayer, thisBuffer ); break;
222 case TPL_IMAGE_TEXEL_FMT_IA4: TCWriteTplImage_IA4( colorLayer, alphaLayer, thisBuffer ); break;
223 case TPL_IMAGE_TEXEL_FMT_IA8: TCWriteTplImage_IA8( colorLayer, alphaLayer, thisBuffer ); break;
224 case TPL_IMAGE_TEXEL_FMT_R5G6B5: TCWriteTplImage_R5G6B5( colorLayer, thisBuffer ); break;
225 case TPL_IMAGE_TEXEL_FMT_RGB5A3: TCWriteTplImage_RGB5A3( colorLayer, alphaLayer, thisBuffer ); break;
226 case TPL_IMAGE_TEXEL_FMT_RGBA8: TCWriteTplImage_RGBA8( colorLayer, alphaLayer, thisBuffer ); break;
227
228 case TPL_IMAGE_TEXEL_FMT_CI4: TCWriteTplImage_CI4( colorLayer, thisBuffer ); break;
229 case TPL_IMAGE_TEXEL_FMT_CI8: TCWriteTplImage_CI8( colorLayer, thisBuffer ); break;
230 case TPL_IMAGE_TEXEL_FMT_CI14_X2: TCWriteTplImage_CI14_X2( colorLayer, thisBuffer ); break;
231
232 // special case- create a cmp layer from the color, alpha layers and use it instead
233 case TPL_IMAGE_TEXEL_FMT_CMP:
234 TCConvertToS3( colorLayer, alphaLayer, &cmpLayer );
235 TCWriteTplImage_CMP( &cmpLayer, thisBuffer );
236 TCFree( (void**)(&cmpLayer.data) );
237 break;
238
239 default:
240 TCErrorMsg( "TCCreateMipMaps: unknown texel format for image %d\n", thisImage->index );
241 return 0;
242 break;
243 }
244
245 // must update the 'current LOD' buffer pointer within the full size
246 // buffer to the start of the next LOD.
247 size = TCComputeTplImageBufferSize( &imTmp );
248 thisBuffer = ( thisBuffer + size );
249
250 } // end for
251
252 // free the temporary mipmap layer buffers
253 TCFree( (void**)( &colorLayer->data ) );
254
255 if( alphaLayer )
256 {
257 TCFree( (void**)( &alphaLayer->data ) );
258 }
259
260 // write the full mipmap block to the .tpl file
261 bytesWritten = fwrite( tplBuffer, 1, thisImage->tplBufferSize, fp );
262
263 TCFree( (void**)(&tplBuffer) );
264 return bytesWritten;
265 }
266
267 /*>*******************************(*)*******************************<*/
268 // create a mipmap level from srcLayer
269 // srcLayer is always LOD 0; 'level' is defined relative to this
270 // srcAlpha is only used when box-filtering a color layer
271 /*>*******************************(*)*******************************<*/
TCCreateNextMipMapLayer(TCImage * srcImage,TCLayer * dstColor,TCLayer * dstAlpha)272 static void TCCreateNextMipMapLayer ( TCImage* srcImage, TCLayer* dstColor, TCLayer* dstAlpha )
273 {
274 u32 row, col;
275 u32 boxWid, boxHgt;
276 TCLayer* srcColor = NULL;
277 TCLayer* srcAlpha = NULL;
278
279
280 TCAssertMsg( (srcImage != NULL), "TCCreateNextMipMapLayer: NULL TCImage ptr\n" );
281 TCAssertMsg( (dstColor != NULL), "TCCreateNextMipMapLayer: NULL TCLayer ptr\n" );
282
283
284 srcColor = &srcImage->lyColor;
285
286 if( srcImage->alphaSrcImage != TC_UNUSED )
287 {
288 srcAlpha = &srcImage->lyAlpha;
289 }
290
291 // compute the size of the filtering box
292 boxWid = srcColor->width / dstColor->width;
293 boxHgt = srcColor->height / dstColor->height;
294
295 // write out the mipmap into the dst layers
296 // note that both src and dst alpha may be NULL
297 for( row=0; row < dstColor->height; row++)
298 {
299 for( col=0; col < dstColor->width; col++ )
300 {
301 TCBoxFilter( srcColor, srcAlpha, dstColor, dstAlpha, col, row, boxWid, boxHgt );
302 }
303 }
304 }
305
306 /*>*******************************(*)*******************************<*/
307 // pre-multiply color by source alpha (if present) when filtering
308 /*>*******************************(*)*******************************<*/
TCBoxFilter(TCLayer * srcC,TCLayer * srcA,TCLayer * dstC,TCLayer * dstA,u32 dstCol,u32 dstRow,u32 boxWid,u32 boxHgt)309 static void TCBoxFilter ( TCLayer* srcC, TCLayer* srcA, TCLayer* dstC, TCLayer* dstA,
310 u32 dstCol, u32 dstRow, u32 boxWid, u32 boxHgt )
311 {
312 u32 row, col;
313 u32 boxRow = ( dstRow * boxHgt );
314 u32 boxCol = ( dstCol * boxWid );
315 f64 rAcc = 0;
316 f64 gAcc = 0;
317 f64 bAcc = 0;
318 f64 aAcc = 0;
319 u16 a = 255;
320 u16 r;
321 u8 g, b;
322
323 f64 rSum = 0; // added
324 f64 gSum = 0; // added
325 f64 bSum = 0; // added
326
327
328 // pre-multiply source texels by alpha
329 for( row = boxRow; row < (boxRow + boxHgt); row++ )
330 {
331 for( col = boxCol; col < (boxCol + boxWid); col++ )
332 {
333 TCGetLayerValue( srcC, col, row, &r, &g, &b );
334
335 if( srcA )
336 {
337 TCGetLayerValue( srcA, col, row, &a, NULL, NULL );
338 }
339
340 rAcc += (f64)( r * a );
341 gAcc += (f64)( g * a );
342 bAcc += (f64)( b * a );
343 aAcc += (f64)( a );
344
345 rSum += (f64)r; // added
346 gSum += (f64)g; // added
347 bSum += (f64)b; // added
348 }
349 }
350
351 r = g = b = 0;
352
353 if( aAcc )
354 {
355 r = (u16)( ( rAcc / aAcc ) + 0.5f );
356 g = (u8)( ( gAcc / aAcc ) + 0.5f );
357 b = (u8)( ( bAcc / aAcc ) + 0.5f );
358 }
359 else // added
360 {
361 r = (u16)( ( rSum / (boxWid * boxHgt)) + 0.5f );
362 g = (u8)( ( gSum / (boxWid * boxHgt)) + 0.5f );
363 b = (u8)( ( bSum / (boxWid * boxHgt)) + 0.5f );
364 }
365
366
367 TCSetLayerValue( dstC, dstCol, dstRow, r, g, b );
368
369 if( dstA )
370 {
371 a = (u16)( ( aAcc / (f32)(boxWid * boxHgt) ) + 0.5f );
372
373 TCSetLayerValue( dstA, dstCol, dstRow, a, 0, 0 );
374 }
375 }
376
377 /*>*******************************(*)*******************************<*/
378 // compute the required image buffer size for all LODs
379 /*>*******************************(*)*******************************<*/
TCComputeTplMipMapImageBufferSize(TCImage * thisImage)380 u32 TCComputeTplMipMapImageBufferSize ( TCImage* thisImage )
381 {
382 u32 level;
383 u32 totalSize = 0;
384 u32 saveWidth, saveHeight;
385
386
387 saveWidth = (thisImage->lyColor).width;
388 saveHeight = (thisImage->lyColor).height;
389
390 totalSize = 0;
391 for( level = thisImage->minLOD; level< ( thisImage->maxLOD + 1 ) ; level++ )
392 {
393 (thisImage->lyColor).width = ( saveWidth >> level );
394 (thisImage->lyColor).height = ( saveHeight >> level );
395
396 totalSize += TCComputeTplImageBufferSize( thisImage );
397 }
398
399 (thisImage->lyColor).width = saveWidth;
400 (thisImage->lyColor).height = saveHeight;
401
402 return totalSize;
403 }
404
405 /*>*******************************(*)*******************************<*/
406 // check that the proposed LOD conversion is ok
407 // wrt image size, LOD range
408 /*>*******************************(*)*******************************<*/
TCCheckMipMapConvParams(TCImage * thisImage)409 static void TCCheckMipMapConvParams ( TCImage* thisImage )
410 {
411 u32 check;
412
413
414 // check LODs for valid range
415 check = CheckRangeLOD( thisImage->minLOD );
416 TCAssertMsg( (check),"TCCheckMipMapConvParams: minLOD out of range for image %d\n", thisImage->index );
417
418 check = CheckRangeLOD( thisImage->maxLOD );
419 TCAssertMsg( (check),"TCCheckMipMapConvParams: maxLOD out of range for image %d\n", thisImage->index );
420
421 check = CheckRangeLOD( thisImage->remapMinLOD );
422 TCAssertMsg( (check),"TCCheckMipMapConvParams: remapLOD out of range for image %d\n", thisImage->index );
423
424 // scale down only
425 if( thisImage->minLOD > thisImage->maxLOD )
426 {
427 TCErrorMsg( "TCCheckMipMapConvParams: image %d has (minLOD > maxLOD)\n", thisImage->index );
428 }
429
430 // can't remap beyond maximum LOD pyramid size
431 check = thisImage->remapMinLOD + (thisImage->maxLOD - thisImage->minLOD);
432 if( check > 10 )
433 {
434 TCErrorMsg( "TCCheckMipMapConvParams: image %d remaps LODs beyond max pyramid size\n", thisImage->index );
435 }
436
437 // check for power of 2 image dimensions
438 check = TCCheckPower2( thisImage );
439 TCAssertMsg( (check),"TCCheckMipMapConvParams: image %d is not power of 2 dimensions\n", thisImage->index );
440
441 // ensure both width and height of source will support the minification
442 check = TCCheckMipMapReduction( thisImage );
443 TCAssertMsg( (check),"TCCheckMipMapConvParams: numLOD is greater than image %d minimum dimension\n", thisImage->index );
444
445 // check for a valid conversion format if multiple LODs
446 check = TCCheckMipMapFormats( thisImage );
447 TCAssertMsg( (check),"TCCheckMipMapConvParams: image %d invalid output format for mipmapping\n", thisImage->index );
448
449 }
450
451 /*>*******************************(*)*******************************<*/
452 // check an image size for an in-range power of two
453 // note: single LODs can have any dimensions
454 /*>*******************************(*)*******************************<*/
TCCheckPower2(TCImage * thisImage)455 static u32 TCCheckPower2( TCImage* thisImage )
456 {
457 u32 i, size;
458
459
460 // single/original LOD can be any dimension up to 1024 texels
461 if( (thisImage->minLOD == 0) && (thisImage->maxLOD == 0) && (thisImage->remapMinLOD == 0) )
462 {
463 if( ( thisImage->lyColor.width > 1024 ) || ( thisImage->lyColor.height > 1024 ) )
464 {
465 return 0;
466 }
467
468 return 1;
469 }
470
471
472 // multiple LODs must have hgt. and wid. as power of 2
473 for( i=0; i< 2; i++ )
474 {
475 if( i== 0 )
476 size = (thisImage->lyColor).width;
477 else
478 size = (thisImage->lyColor).height;
479
480
481 switch( size )
482 {
483 case 1024:
484 case 512:
485 case 256:
486 case 128:
487 case 64:
488 case 32:
489 case 16:
490 case 8:
491 case 4:
492 case 2:
493 case 1:
494 ; // do nothing- proceed with next pass
495 break;
496
497 default:
498 return 0;
499 break;
500 }
501
502 } // end for
503
504 return 1;
505
506 }
507
508 /*>*******************************(*)*******************************<*/
509 // ensure minification is within the width, height range of source image.
510 // note: use minimum image dimension as limiting factor
511 /*>*******************************(*)*******************************<*/
TCCheckMipMapReduction(TCImage * thisImage)512 static u32 TCCheckMipMapReduction ( TCImage* thisImage )
513 {
514 u32 scale;
515 u32 checkWidth, checkHeight;
516
517
518 // all we need is the total reduction from source image
519 // so 'minLOD' isn't required
520 scale = thisImage->maxLOD;
521
522 if( scale == 0 )
523 {
524 return 1;
525 }
526
527 checkWidth = ( (thisImage->lyColor).width >> scale );
528 checkHeight = ( (thisImage->lyColor).height >> scale );
529
530 if( (checkWidth == 0) || (checkHeight == 0) )
531 {
532 return 0;
533 }
534
535 return 1;
536 }
537
538 /*>*******************************(*)*******************************<*/
539 // multiple LODs are only available for true color formats
540 /*>*******************************(*)*******************************<*/
TCCheckMipMapFormats(TCImage * thisImage)541 static u32 TCCheckMipMapFormats( TCImage* thisImage )
542 {
543
544 // single LOD- all formats ok;
545 if( thisImage->minLOD == thisImage->maxLOD )
546 {
547 return 1;
548 }
549
550 // multiple LOD- only true color and compressed formats are ok
551 switch( thisImage->texelFormat )
552 {
553 case TPL_IMAGE_TEXEL_FMT_I4: // all fall through
554 case TPL_IMAGE_TEXEL_FMT_I8:
555 case TPL_IMAGE_TEXEL_FMT_IA4:
556 case TPL_IMAGE_TEXEL_FMT_IA8:
557 case TPL_IMAGE_TEXEL_FMT_R5G6B5:
558 case TPL_IMAGE_TEXEL_FMT_RGB5A3:
559 case TPL_IMAGE_TEXEL_FMT_RGBA8:
560 case TPL_IMAGE_TEXEL_FMT_CMP:
561 return 1;
562 break;
563
564 default:
565 return 0;
566 break;
567 }
568 }
569
570 /*>*******************************(*)*******************************<*/
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587