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