4 static int s_resample_width = 256;
\r
5 static int s_resample_height = 256;
\r
7 #define OUTPUT_TGAS 1
\r
9 #define UNCOMPRESSED 0
\r
10 #define BTC_COMPRESSION 1
\r
12 static int s_compression_method = BTC_COMPRESSION;
\r
14 static const char *CIN_EXTENSION = "cn2";
\r
15 static const int CIN_SIGNATURE = ( 'C' << 24 ) | ( 'I' << 16 ) | ( 'N' << 8 ) | ( '2' );
\r
17 static byte *s_soundtrack;
\r
18 static char s_base[32];
\r
19 static char s_output_base[32];
\r
22 ===============================================================================
\r
26 ===============================================================================
\r
36 int dataofs; // chunk starts this many bytes from file start
\r
47 static int s_samplecounts[0x10000];
\r
48 static wavinfo_t s_wavinfo;
\r
50 short GetLittleShort(void)
\r
54 val = val + (*(data_p+1)<<8);
\r
59 int GetLittleLong(void)
\r
63 val = val + (*(data_p+1)<<8);
\r
64 val = val + (*(data_p+2)<<16);
\r
65 val = val + (*(data_p+3)<<24);
\r
70 void FindNextChunk(char *name)
\r
76 if (data_p >= iff_end)
\r
77 { // didn't find the chunk
\r
83 iff_chunk_len = GetLittleLong();
\r
84 if (iff_chunk_len < 0)
\r
89 // if (iff_chunk_len > 1024*1024)
\r
90 // Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
\r
92 last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
\r
93 if (!strncmp(data_p, name, 4))
\r
98 void FindChunk(char *name)
\r
100 last_chunk = iff_data;
\r
101 FindNextChunk (name);
\r
105 void DumpChunks(void)
\r
113 memcpy (str, data_p, 4);
\r
115 iff_chunk_len = GetLittleLong();
\r
116 printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
\r
117 data_p += (iff_chunk_len + 1) & ~1;
\r
118 } while (data_p < iff_end);
\r
126 wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
\r
133 memset (&info, 0, sizeof(info));
\r
139 iff_end = wav + wavlength;
\r
141 // find "RIFF" chunk
\r
143 if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))
\r
145 printf("Missing RIFF/WAVE chunks\n");
\r
149 // get "fmt " chunk
\r
150 iff_data = data_p + 12;
\r
156 printf("Missing fmt chunk\n");
\r
160 format = GetLittleShort();
\r
163 printf("Microsoft PCM format only\n");
\r
167 info.channels = GetLittleShort();
\r
168 info.rate = GetLittleLong();
\r
170 info.width = GetLittleShort() / 8;
\r
177 info.loopstart = GetLittleLong();
\r
178 // Com_Printf("loopstart=%d\n", sfx->loopstart);
\r
180 // if the next chunk is a LIST chunk, look for a cue length marker
\r
181 FindNextChunk ("LIST");
\r
184 if (!strncmp (data_p + 28, "mark", 4))
\r
185 { // this is not a proper parse, but it works with cooledit...
\r
187 i = GetLittleLong (); // samples in loop
\r
188 info.samples = info.loopstart + i;
\r
193 info.loopstart = -1;
\r
199 printf("Missing data chunk\n");
\r
204 samples = GetLittleLong ();
\r
208 if (samples < info.samples)
\r
209 Error ("Sound %s has a bad loop length", name);
\r
212 info.samples = samples;
\r
214 info.dataofs = data_p - wav;
\r
219 //=====================================================================
\r
226 void LoadSoundtrack (void)
\r
233 s_soundtrack = NULL;
\r
234 sprintf (name, "%svideo/%s/%s.wav", gamedir, s_base, s_base);
\r
235 printf ("WAV: %s\n", name);
\r
236 f = fopen (name, "rb");
\r
239 printf ("no soundtrack for %s\n", s_base);
\r
242 len = Q_filelength(f);
\r
243 s_soundtrack = malloc(len);
\r
244 fread (s_soundtrack, 1, len, f);
\r
247 s_wavinfo = GetWavinfo (name, s_soundtrack, len);
\r
249 // count samples for compression
\r
250 memset (s_samplecounts, 0, sizeof(s_samplecounts));
\r
252 j = s_wavinfo.samples/2;
\r
253 for (i=0 ; i<j ; i++)
\r
255 val = ((unsigned short *)( s_soundtrack + s_wavinfo.dataofs))[i];
\r
256 s_samplecounts[val]++;
\r
259 for (i=0 ; i<0x10000 ; i++)
\r
260 if (s_samplecounts[i])
\r
263 printf ("%i unique sample values\n", val);
\r
271 void WriteSound (FILE *output, int frame)
\r
280 width = s_wavinfo.width * s_wavinfo.channels;
\r
282 start = frame*s_wavinfo.rate/14;
\r
283 end = (frame+1)*s_wavinfo.rate/14;
\r
284 count = end - start;
\r
286 for (i=0 ; i<count ; i++)
\r
289 if (sample > s_wavinfo.samples || !s_soundtrack)
\r
290 fwrite (&empty, 1, width, output);
\r
292 fwrite (s_soundtrack + s_wavinfo.dataofs + sample*width, 1, width,output);
\r
296 //==========================================================================
\r
298 static float s_resampleXRatio;
\r
299 static float s_resampleYRatio;
\r
301 static void BoxFilterHorizontalElements( unsigned char *dst, unsigned char *src, float s0, float s1 )
\r
304 float rSum = 0, gSum = 0, bSum = 0;
\r
306 float sumWeight = 0;
\r
308 for ( x = s0; x < s1; x++, src += 4 )
\r
312 w = ( int ) ( s0 + 1 ) - x;
\r
314 else if ( x + 1 >= s1 )
\r
316 w = s1 - ( int ) x;
\r
323 rSum += src[0] * w;
\r
324 gSum += src[1] * w;
\r
325 bSum += src[2] * w;
\r
333 dst[0] = ( unsigned char ) ( rSum + 0.5 );
\r
334 dst[1] = ( unsigned char ) ( gSum + 0.5 );
\r
335 dst[2] = ( unsigned char ) ( bSum + 0.5 );
\r
338 static void BoxFilterVerticalElements( unsigned char *dst, // destination of the filter process
\r
339 unsigned char *src, // source pixels
\r
340 int srcStep, // stride of the source pixels
\r
341 float s0, float s1 )
\r
344 float rSum = 0, gSum = 0, bSum = 0;
\r
346 float sumWeight = 0;
\r
348 for ( y = s0; y < ( int ) ( s1 + 1 ) ; y++, src += srcStep )
\r
352 w = ( int ) ( s0 + 1 ) - y;
\r
354 else if ( y + 1 >= s1 )
\r
356 w = s1 - ( int ) y;
\r
363 rSum += src[0] * w;
\r
364 gSum += src[1] * w;
\r
365 bSum += src[2] * w;
\r
373 dst[0] = ( unsigned char ) ( rSum + 0.5 );
\r
374 dst[1] = ( unsigned char ) ( gSum + 0.5 );
\r
375 dst[2] = ( unsigned char ) ( bSum + 0.5 );
\r
380 static void BoxFilterRow( unsigned char *dstStart, cblock_t *in, int dstRow, int rowWidth )
\r
383 unsigned char *indata = ( unsigned char * ) in->data;
\r
385 indata += 4 * dstRow * in->width;
\r
387 for ( i = 0; i < rowWidth; i++ )
\r
389 float c0 = i * s_resampleXRatio;
\r
390 float c1 = ( i + 1 ) * s_resampleXRatio;
\r
392 BoxFilterHorizontalElements( &dstStart[i*4], &indata[( ( int ) c0 ) * 4], c0, c1 );
\r
396 static void BoxFilterColumn( unsigned char *dstStart, unsigned char *srcStart, int dstCol, int dstRowWidth, int dstColHeight, int srcRowWidthInPels )
\r
401 for ( i = 0; i < dstColHeight; i++ )
\r
403 c0 = i * s_resampleYRatio;
\r
404 c1 = ( i + 1 ) * s_resampleYRatio;
\r
406 BoxFilterVerticalElements( &dstStart[i*4*dstRowWidth], &srcStart[(int)c0*srcRowWidthInPels*4], srcRowWidthInPels*4, c0, c1 );
\r
410 #define DROP_SAMPLE 0
\r
411 #define BOX_FILTER 1
\r
413 static void ResampleFrame( cblock_t *in, unsigned char *out, int method, int outWidth, int outHeight )
\r
416 unsigned char *indata = ( unsigned char * ) in->data;
\r
418 s_resampleXRatio = in->width / ( float ) outWidth;
\r
419 s_resampleYRatio = in->height / ( float ) outHeight;
\r
421 if ( method == DROP_SAMPLE )
\r
423 for ( row = 0; row < outHeight; row++ )
\r
425 int r = ( int ) ( row * s_resampleYRatio );
\r
427 for ( column = 0; column < outWidth; column++ )
\r
429 int c = ( int ) ( column * s_resampleXRatio );
\r
431 out[(row*outWidth+column)*4+0] = indata[(r*in->width+c)*4+0];
\r
432 out[(row*outWidth+column)*4+1] = indata[(r*in->width+c)*4+1];
\r
433 out[(row*outWidth+column)*4+2] = indata[(r*in->width+c)*4+2];
\r
434 out[(row*outWidth+column)*4+3] = 0xff;
\r
438 else if ( method == BOX_FILTER )
\r
440 unsigned char intermediate[1024*1024*4];
\r
442 assert( in->height <= 1024 );
\r
443 assert( in->width <= 1024 );
\r
446 // filter our M x N source image into a RESAMPLE_WIDTH x N horizontally filtered image
\r
448 for ( row = 0; row < in->height; row++ )
\r
450 BoxFilterRow( &intermediate[row*4*outWidth], in, row, outWidth );
\r
454 // filter our RESAMPLE_WIDTH x N horizontally filtered image into a RESAMPLE_WIDTH x RESAMPLE_HEIGHT filtered image
\r
456 for ( column = 0; column < outWidth; column++ )
\r
458 BoxFilterColumn( &out[column*4], &intermediate[column*4], column, outWidth, outHeight, s_resample_width );
\r
463 static float BTCDistanceSquared( float a[3], float b[3] )
\r
465 return ( b[0] - a[0] ) * ( b[0] - a[0] ) +
\r
466 ( b[1] - a[1] ) * ( b[1] - a[1] ) +
\r
467 ( b[2] - a[2] ) * ( b[2] - a[2] );
\r
470 static void BTCFindEndpoints( float inBlock[4][4][3], unsigned int endPoints[2][2] )
\r
472 float longestDistance = -1;
\r
477 // find the two points farthest from each other
\r
479 for ( bY = 0; bY < 4; bY++ )
\r
481 for ( bX = 0; bX < 4; bX++ )
\r
487 // check the rest of the current row
\r
489 for ( cX = bX + 1; cX < 4; cX++ )
\r
491 if ( ( d = BTCDistanceSquared( inBlock[bY][bX], inBlock[bY][cX] ) ) > longestDistance )
\r
493 longestDistance = d;
\r
494 endPoints[0][0] = bX;
\r
495 endPoints[0][1] = bY;
\r
496 endPoints[1][0] = cX;
\r
497 endPoints[1][1] = bY;
\r
502 // check remaining rows and columns
\r
504 for ( cY = bY+1; cY < 4; cY++ )
\r
506 for ( cX = 0; cX < 4; cX++ )
\r
508 if ( ( d = BTCDistanceSquared( inBlock[bY][bX], inBlock[cY][cX] ) ) > longestDistance )
\r
510 longestDistance = d;
\r
511 endPoints[0][0] = bX;
\r
512 endPoints[0][1] = bY;
\r
513 endPoints[1][0] = cX;
\r
514 endPoints[1][1] = cY;
\r
522 static float BTCQuantizeBlock( float inBlock[4][4][3], unsigned long endPoints[2][2], int btcQuantizedBlock[4][4], float bestError )
\r
525 int blockY, blockX;
\r
529 float colorLine[4][3];
\r
532 // build the color line
\r
534 dR = inBlock[endPoints[1][1]][endPoints[1][0]][0] -
\r
535 inBlock[endPoints[0][1]][endPoints[0][0]][0];
\r
536 dG = inBlock[endPoints[1][1]][endPoints[1][0]][1] -
\r
537 inBlock[endPoints[0][1]][endPoints[0][0]][1];
\r
538 dB = inBlock[endPoints[1][1]][endPoints[1][0]][2] -
\r
539 inBlock[endPoints[0][1]][endPoints[0][0]][2];
\r
545 R = inBlock[endPoints[0][1]][endPoints[0][0]][0];
\r
546 G = inBlock[endPoints[0][1]][endPoints[0][0]][1];
\r
547 B = inBlock[endPoints[0][1]][endPoints[0][0]][2];
\r
549 for ( i = 0; i < 4; i++ )
\r
551 colorLine[i][0] = R;
\r
552 colorLine[i][1] = G;
\r
553 colorLine[i][2] = B;
\r
561 // quantize each pixel into the appropriate range
\r
563 for ( blockY = 0; blockY < 4; blockY++ )
\r
565 for ( blockX = 0; blockX < 4; blockX++ )
\r
567 float distance = 10000000000;
\r
570 for ( i = 0; i < 4; i++ )
\r
574 if ( ( d = BTCDistanceSquared( inBlock[blockY][blockX], colorLine[i] ) ) < distance )
\r
584 // if bestError is not -1 then that means this is a speculative quantization
\r
586 if ( bestError != -1 )
\r
588 if ( error > bestError )
\r
592 btcQuantizedBlock[blockY][blockX] = shortest;
\r
600 ** float BTCCompressBlock
\r
602 static float BTCCompressBlock( float inBlock[4][4][3], unsigned long out[2] )
\r
605 int btcQuantizedBlock[4][4]; // values should be [0..3]
\r
606 unsigned long encodedEndPoints, encodedBitmap;
\r
607 unsigned int endPoints[2][2]; // endPoints[0] = color start, endPoints[1] = color end
\r
608 int blockY, blockX;
\r
610 float bestError = 10000000000;
\r
611 unsigned int bestEndPoints[2][2];
\r
615 // find the "ideal" end points for the color vector
\r
617 BTCFindEndpoints( inBlock, endPoints );
\r
618 error = BTCQuantizeBlock( inBlock, endPoints, btcQuantizedBlock );
\r
619 memcpy( bestEndPoints, endPoints, sizeof( bestEndPoints ) );
\r
621 for ( blockY = 0; blockY < 4; blockY++ )
\r
623 for ( blockX = 0; blockX < 4; blockX++ )
\r
627 for ( y2 = 0; y2 < 4; y2++ )
\r
629 for ( x2 = 0; x2 < 4; x2++ )
\r
631 if ( ( x2 == blockX ) && ( y2 == blockY ) )
\r
634 endPoints[0][0] = blockX;
\r
635 endPoints[0][1] = blockY;
\r
636 endPoints[1][0] = x2;
\r
637 endPoints[1][1] = y2;
\r
639 error = BTCQuantizeBlock( inBlock, endPoints, btcQuantizedBlock, -1 ); //bestError );
\r
641 if ( error < bestError )
\r
644 memcpy( bestEndPoints, endPoints, sizeof( bestEndPoints ) );
\r
651 error = BTCQuantizeBlock( inBlock, bestEndPoints, btcQuantizedBlock, -1.0f );
\r
655 // encode the results
\r
658 for ( blockY = 0; blockY < 4; blockY++ )
\r
660 for ( blockX = 0; blockX < 4; blockX++ )
\r
662 int shift = ( blockX + blockY * 4 ) * 2;
\r
663 encodedBitmap |= btcQuantizedBlock[blockY][blockX] << shift;
\r
668 // encode endpoints
\r
670 encodedEndPoints = 0;
\r
671 for ( i = 0; i < 2; i++ )
\r
675 iR = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][0] );
\r
678 else if ( iR < 0 )
\r
682 iG = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][1] );
\r
689 iB = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][2] );
\r
697 encodedEndPoints |= ( ( ( iR << 11 ) | ( iG << 5 ) | ( iB ) ) << ( i * 16 ) );
\r
703 out[0] = encodedBitmap;
\r
704 out[1] = encodedEndPoints;
\r
710 ** void BTCDecompressFrame
\r
712 static void BTCDecompressFrame( unsigned long *src, unsigned char *dst )
\r
717 float colorStart[3], colorEnd[3];
\r
718 unsigned char colorRampABGR[4][4];
\r
721 memset( colorRampABGR, 0xff, sizeof( colorRampABGR ) );
\r
723 for ( y = 0; y < s_resample_height / 4; y++ )
\r
725 for ( x = 0; x < s_resample_width / 4; x++ )
\r
727 unsigned colorStartPacked = src[(y*s_resample_width/4 + x)*2 + 1] & 0xffff;
\r
728 unsigned colorEndPacked = src[(y*s_resample_width/4 + x)*2 + 1] >> 16;
\r
731 // grab the end points
\r
735 iR = ( ( colorStartPacked >> 11 ) & ( ( 1 << 5 ) - 1 ) );
\r
736 iR = ( iR << 3 ) | ( iR >> 2 );
\r
737 iG = ( ( colorStartPacked >> 5 ) & ( ( 1 << 6 ) - 1 ) );
\r
738 iG = ( iG << 2 ) | ( iG >> 4 );
\r
739 iB = ( ( colorStartPacked ) & ( ( 1 << 5 ) - 1 ) );
\r
740 iB = ( iB << 3 ) | ( iB >> 2 );
\r
742 colorStart[0] = iR;
\r
743 colorStart[1] = iG;
\r
744 colorStart[2] = iB;
\r
745 colorRampABGR[0][0] = iR;
\r
746 colorRampABGR[0][1] = iG;
\r
747 colorRampABGR[0][2] = iB;
\r
749 iR = ( ( colorEndPacked >> 11 ) & ( ( 1 << 5 ) - 1 ) );
\r
750 iR = ( iR << 3 ) | ( iR >> 2 );
\r
751 iG = ( ( colorEndPacked >> 5 ) & ( ( 1 << 6 ) - 1 ) );
\r
752 iG = ( iG << 2 ) | ( iG >> 4 );
\r
753 iB = ( colorEndPacked & ( ( 1 << 5 ) - 1 ) );
\r
754 iB = ( iB << 3 ) | ( iB >> 2 );
\r
759 colorRampABGR[3][0] = iR;
\r
760 colorRampABGR[3][1] = iG;
\r
761 colorRampABGR[3][2] = iB;
\r
764 // compute this block's color ramp
\r
765 // FIXME: This needs to be reversed on big-endian machines
\r
768 colorRampABGR[1][0] = colorStart[0] * 0.66f + colorEnd[0] * 0.33f;
\r
769 colorRampABGR[1][1] = colorStart[1] * 0.66f + colorEnd[1] * 0.33f;
\r
770 colorRampABGR[1][2] = colorStart[2] * 0.66f + colorEnd[2] * 0.33f;
\r
772 colorRampABGR[2][0] = colorStart[0] * 0.33f + colorEnd[0] * 0.66f;
\r
773 colorRampABGR[2][1] = colorStart[1] * 0.33f + colorEnd[1] * 0.66f;
\r
774 colorRampABGR[2][2] = colorStart[2] * 0.33f + colorEnd[2] * 0.66f;
\r
777 // decode the color data
\r
778 // information is encoded in 2-bit pixels, with low order bits corresponding
\r
779 // to upper left pixels. These 2-bit values are indexed into the block's
\r
780 // computer color ramp.
\r
782 encoded = src[(y*s_resample_width/4 + x)*2 + 0];
\r
784 for ( dstY = 0; dstY < 4; dstY++ )
\r
786 for ( dstX = 0; dstX < 4; dstX++ )
\r
788 memcpy( &dst[(y*4+dstY)*s_resample_width*4+x*4*4+dstX*4], colorRampABGR[encoded&3], sizeof( colorRampABGR[0] ) );
\r
797 ** BTCCompressFrame
\r
799 ** Perform a BTC compression using a 2-bit encoding at each pixel. This
\r
800 ** compression method is performed by decomposing the incoming image into
\r
801 ** a sequence of 4x4 blocks. At each block two color values are computed
\r
802 ** that define the endpoints of a vector in color space that represent
\r
803 ** the two colors "farthest apart".
\r
805 static float BTCCompressFrame( unsigned char *src, unsigned long *dst )
\r
809 float btcBlock[4][4][3];
\r
813 for ( y = 0; y < s_resample_height / 4; y++ )
\r
815 for ( x = 0; x < s_resample_width / 4; x++ )
\r
818 // fill in the BTC block with raw values
\r
820 for ( bY = 0; bY < 4; bY++ )
\r
822 for ( bX = 0; bX < 4; bX++ )
\r
824 btcBlock[bY][bX][0] = src[(y*4+bY)*s_resample_width*4 + (x*4+bX)*4 + 0];
\r
825 btcBlock[bY][bX][1] = src[(y*4+bY)*s_resample_width*4 + (x*4+bX)*4 + 1];
\r
826 btcBlock[bY][bX][2] = src[(y*4+bY)*s_resample_width*4 + (x*4+bX)*4 + 2];
\r
830 error += BTCCompressBlock( btcBlock, &dst[(y*s_resample_width/4+x)*2] );
\r
834 return error / ( ( s_resample_width / 4 ) * ( s_resample_height / 4 ) );
\r
838 ===================
\r
840 ===================
\r
842 cblock_t LoadFrame (char *base, int frame, int digits, byte **palette)
\r
844 int ten3, ten2, ten1, ten0;
\r
854 ten2 = (frame-ten3*1000)/100;
\r
855 ten1 = (frame-ten3*1000-ten2*100)/10;
\r
859 sprintf (name, "%svideo/%s/%s%i%i%i%i.tga", gamedir, base, base, ten3, ten2, ten1, ten0);
\r
861 sprintf (name, "%svideo/%s/%s%i%i%i.tga", gamedir, base, base, ten2, ten1, ten0);
\r
863 f = fopen(name, "rb");
\r
871 printf ("%s", name);
\r
872 LoadTGA( name, ( unsigned char ** ) &in.data, &width, &height );
\r
875 // Load256Image (name, &in.data, palette, &width, &height);
\r
876 in.count = width*height;
\r
878 in.height = height;
\r
879 // FIXME: map 0 and 255!
\r
896 video <directory> <framedigits>
\r
899 void Cmd_Video (void)
\r
901 float sumError = 0, error = 0, maxError = 0;
\r
902 char savename[1024];
\r
905 int startframe, frame;
\r
913 unsigned char *resampled;
\r
914 unsigned long *compressed;
\r
915 clock_t start, stop;
\r
918 strcpy (s_base, token);
\r
921 // sprintf (savename, "video/%s.cin", token);
\r
922 // ReleaseFile (savename);
\r
926 GetToken( qfalse );
\r
927 strcpy( s_output_base, token );
\r
930 digits = atoi(token);
\r
932 GetToken( qfalse );
\r
934 if ( !strcmp( token, "btc" ) )
\r
936 s_compression_method = BTC_COMPRESSION;
\r
937 printf( "Compression: BTC\n" );
\r
939 else if ( !strcmp( token, "uc" ) )
\r
941 s_compression_method = UNCOMPRESSED;
\r
942 printf( "Compression: none\n" );
\r
946 Error( "Uknown compression method '%s'\n", token );
\r
949 GetToken( qfalse );
\r
950 s_resample_width = atoi( token );
\r
952 GetToken( qfalse );
\r
953 s_resample_height = atoi( token );
\r
955 resampled = malloc( sizeof( unsigned char ) * 4 * s_resample_width * s_resample_height );
\r
956 compressed = malloc( sizeof( long ) * 2 * ( s_resample_width / 4 ) * ( s_resample_height / 4 ) );
\r
958 printf( "Resample width: %d\n", s_resample_width );
\r
959 printf( "Resample height: %d\n", s_resample_height );
\r
961 // optionally skip frames
\r
962 if (TokenAvailable ())
\r
965 startframe = atoi(token);
\r
970 sprintf (savename, "%svideo/%s.%s", writedir, s_output_base, CIN_EXTENSION );
\r
972 // load the entire sound wav file if present
\r
976 sprintf (name, "%svideo/%s/%s0000.tga", gamedir, s_base, s_base);
\r
978 sprintf (name, "%svideo/%s/%s000.tga", gamedir, s_base, s_base);
\r
980 printf ("%s\n", name);
\r
981 LoadTGA( name, NULL, &width, &height);
\r
983 output = fopen (savename, "wb");
\r
985 Error ("Can't open %s", savename);
\r
987 // write header info
\r
988 i = LittleLong( CIN_SIGNATURE );
\r
989 fwrite (&i, 4, 1, output );
\r
990 i = LittleLong (s_resample_width);
\r
991 fwrite (&i, 4, 1, output);
\r
992 i = LittleLong (s_resample_height);
\r
993 fwrite (&i, 4, 1, output);
\r
994 i = LittleLong (s_wavinfo.rate);
\r
995 fwrite (&i, 4, 1, output);
\r
996 i = LittleLong (s_wavinfo.width);
\r
997 fwrite (&i, 4, 1, output);
\r
998 i = LittleLong (s_wavinfo.channels);
\r
999 fwrite (&i, 4, 1, output);
\r
1000 i = LittleLong ( s_compression_method );
\r
1001 fwrite (&i, 4, 1, output );
\r
1005 // perform compression on a per frame basis
\r
1006 for ( frame=startframe ; ; frame++)
\r
1008 printf ("%02d: ", frame);
\r
1009 in = LoadFrame (s_base, frame, digits, 0 );
\r
1013 ResampleFrame( &in, ( unsigned char * ) resampled, BOX_FILTER, s_resample_width, s_resample_height );
\r
1015 if ( s_compression_method == UNCOMPRESSED )
\r
1018 fwrite( resampled, 1, sizeof( unsigned char ) * s_resample_width * s_resample_height * 4, output );
\r
1023 char buffer[1000];
\r
1025 for ( y = 0; y < s_resample_height/2; y++ )
\r
1027 for ( x = 0; x < s_resample_width; x++ )
\r
1029 unsigned char tmp[4];
\r
1031 tmp[0] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0];
\r
1032 tmp[1] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1];
\r
1033 tmp[2] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2];
\r
1034 tmp[3] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3];
\r
1036 resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0] = resampled[y*s_resample_width*4 + x*4 + 0];
\r
1037 resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1] = resampled[y*s_resample_width*4 + x*4 + 1];
\r
1038 resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2] = resampled[y*s_resample_width*4 + x*4 + 2];
\r
1039 resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3] = resampled[y*s_resample_width*4 + x*4 + 3];
\r
1041 resampled[y*s_resample_width*4 + x*4 + 0] = tmp[0];
\r
1042 resampled[y*s_resample_width*4 + x*4 + 1] = tmp[1];
\r
1043 resampled[y*s_resample_width*4 + x*4 + 2] = tmp[2];
\r
1044 resampled[y*s_resample_width*4 + x*4 + 3] = tmp[3];
\r
1048 sprintf( buffer, "%svideo/%s/uc%04d.tga", gamedir, s_base, frame );
\r
1049 WriteTGA( buffer, resampled, s_resample_width, s_resample_height );
\r
1053 else if ( s_compression_method == BTC_COMPRESSION )
\r
1055 error = BTCCompressFrame( resampled, compressed );
\r
1057 sumError += error;
\r
1059 if ( error > maxError )
\r
1062 printf( " (error = %f)\n", error );
\r
1063 fwrite( compressed, 1, 2 * sizeof( long ) * ( s_resample_width / 4 ) * ( s_resample_height / 4 ), output );
\r
1068 unsigned char *uncompressed;
\r
1069 char buffer[1000];
\r
1071 uncompressed = malloc( sizeof( unsigned char ) * 4 * s_resample_width * s_resample_height );
\r
1072 BTCDecompressFrame( compressed, uncompressed );
\r
1074 for ( y = 0; y < s_resample_height/2; y++ )
\r
1076 for ( x = 0; x < s_resample_width; x++ )
\r
1078 unsigned char tmp[4];
\r
1080 tmp[0] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0];
\r
1081 tmp[1] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1];
\r
1082 tmp[2] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2];
\r
1083 tmp[3] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3];
\r
1085 uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0] = uncompressed[y*s_resample_width*4 + x*4 + 0];
\r
1086 uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1] = uncompressed[y*s_resample_width*4 + x*4 + 1];
\r
1087 uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2] = uncompressed[y*s_resample_width*4 + x*4 + 2];
\r
1088 uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3] = uncompressed[y*s_resample_width*4 + x*4 + 3];
\r
1090 uncompressed[y*s_resample_width*4 + x*4 + 0] = tmp[0];
\r
1091 uncompressed[y*s_resample_width*4 + x*4 + 1] = tmp[1];
\r
1092 uncompressed[y*s_resample_width*4 + x*4 + 2] = tmp[2];
\r
1093 uncompressed[y*s_resample_width*4 + x*4 + 3] = tmp[3];
\r
1098 sprintf( buffer, "%svideo/%s/btc%04d.tga", gamedir, s_base, frame );
\r
1099 WriteTGA( buffer, uncompressed, s_resample_width, s_resample_height );
\r
1101 free( uncompressed );
\r
1106 WriteSound( output, frame );
\r
1114 printf ("Total size: %i\n", ftell( output ) );
\r
1115 printf ("Average error: %f\n", sumError / ( frame - startframe ) );
\r
1116 printf ("Max error: %f\n", maxError );
\r
1118 fseconds = ( stop - start ) / 1000.0f;
\r
1119 minutes = fseconds / 60;
\r
1120 remSeconds = fseconds - minutes * 60;
\r
1122 printf ("Total time: %d s (%d m %d s)\n", ( int ) fseconds, minutes, remSeconds );
\r
1123 printf ("Time/frame: %.2f seconds\n", fseconds / ( frame - startframe ) );
\r
1127 if ( s_soundtrack )
\r
1129 free( s_soundtrack );
\r