2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 static int s_resample_width = 256;
26 static int s_resample_height = 256;
30 #define UNCOMPRESSED 0
31 #define BTC_COMPRESSION 1
33 static int s_compression_method = BTC_COMPRESSION;
35 static const char *CIN_EXTENSION = "cn2";
36 static const int CIN_SIGNATURE = ( 'C' << 24 ) | ( 'I' << 16 ) | ( 'N' << 8 ) | ( '2' );
38 static byte *s_soundtrack;
39 static char s_base[32];
40 static char s_output_base[32];
43 ===============================================================================
47 ===============================================================================
57 int dataofs; // chunk starts this many bytes from file start
68 static int s_samplecounts[0x10000];
69 static wavinfo_t s_wavinfo;
71 short GetLittleShort( void ){
74 val = val + ( *( data_p + 1 ) << 8 );
79 int GetLittleLong( void ){
82 val = val + ( *( data_p + 1 ) << 8 );
83 val = val + ( *( data_p + 2 ) << 16 );
84 val = val + ( *( data_p + 3 ) << 24 );
89 void FindNextChunk( char *name ){
94 if ( data_p >= iff_end ) { // didn't find the chunk
100 iff_chunk_len = GetLittleLong();
101 if ( iff_chunk_len < 0 ) {
105 // if (iff_chunk_len > 1024*1024)
106 // Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
108 last_chunk = data_p + 8 + ( ( iff_chunk_len + 1 ) & ~1 );
109 if ( !strncmp((const char *) data_p, name, 4 ) ) {
115 void FindChunk( char *name ){
116 last_chunk = iff_data;
117 FindNextChunk( name );
121 void DumpChunks( void ){
128 memcpy( str, data_p, 4 );
130 iff_chunk_len = GetLittleLong();
131 printf( "%p : %s (%d)\n", (void *) ( data_p - 4 ), str, iff_chunk_len );
132 data_p += ( iff_chunk_len + 1 ) & ~1;
133 } while ( data_p < iff_end );
141 wavinfo_t GetWavinfo( char *name, byte *wav, int wavlength ){
147 memset( &info, 0, sizeof( info ) );
154 iff_end = wav + wavlength;
158 if ( !( data_p && !strncmp((const char *) (data_p + 8), "WAVE", 4 ) ) ) {
159 printf( "Missing RIFF/WAVE chunks\n" );
164 iff_data = data_p + 12;
169 printf( "Missing fmt chunk\n" );
173 format = GetLittleShort();
175 printf( "Microsoft PCM format only\n" );
179 info.channels = GetLittleShort();
180 info.rate = GetLittleLong();
182 info.width = GetLittleShort() / 8;
188 info.loopstart = GetLittleLong();
189 // Com_Printf("loopstart=%d\n", sfx->loopstart);
191 // if the next chunk is a LIST chunk, look for a cue length marker
192 FindNextChunk( "LIST" );
194 if ( !strncmp((const char *) (data_p + 28), "mark", 4 ) ) { // this is not a proper parse, but it works with cooledit...
196 i = GetLittleLong(); // samples in loop
197 info.samples = info.loopstart + i;
208 printf( "Missing data chunk\n" );
213 samples = GetLittleLong();
215 if ( info.samples ) {
216 if ( samples < info.samples ) {
217 Error( "Sound %s has a bad loop length", name );
221 info.samples = samples;
224 info.dataofs = data_p - wav;
229 //=====================================================================
236 void LoadSoundtrack( void ){
243 sprintf( name, "%svideo/%s/%s.wav", gamedir, s_base, s_base );
244 printf( "WAV: %s\n", name );
245 f = fopen( name, "rb" );
247 printf( "no soundtrack for %s\n", s_base );
250 len = Q_filelength( f );
251 s_soundtrack = malloc( len );
252 fread( s_soundtrack, 1, len, f );
255 s_wavinfo = GetWavinfo( name, s_soundtrack, len );
257 // count samples for compression
258 memset( s_samplecounts, 0, sizeof( s_samplecounts ) );
260 j = s_wavinfo.samples / 2;
261 for ( i = 0 ; i < j ; i++ )
263 val = ( (unsigned short *)( s_soundtrack + s_wavinfo.dataofs ) )[i];
264 s_samplecounts[val]++;
267 for ( i = 0 ; i < 0x10000 ; i++ )
268 if ( s_samplecounts[i] ) {
272 printf( "%i unique sample values\n", val );
280 void WriteSound( FILE *output, int frame ){
288 width = s_wavinfo.width * s_wavinfo.channels;
290 start = frame * s_wavinfo.rate / 14;
291 end = ( frame + 1 ) * s_wavinfo.rate / 14;
294 for ( i = 0 ; i < count ; i++ )
297 if ( sample > s_wavinfo.samples || !s_soundtrack ) {
298 fwrite( &empty, 1, width, output );
301 fwrite( s_soundtrack + s_wavinfo.dataofs + sample * width, 1, width,output );
306 //==========================================================================
308 static float s_resampleXRatio;
309 static float s_resampleYRatio;
311 static void BoxFilterHorizontalElements( unsigned char *dst, unsigned char *src, float s0, float s1 ){
313 float rSum = 0, gSum = 0, bSum = 0;
317 for ( x = s0; x < s1; x++, src += 4 )
320 w = ( int ) ( s0 + 1 ) - x;
322 else if ( x + 1 >= s1 ) {
340 dst[0] = ( unsigned char ) ( rSum + 0.5 );
341 dst[1] = ( unsigned char ) ( gSum + 0.5 );
342 dst[2] = ( unsigned char ) ( bSum + 0.5 );
345 static void BoxFilterVerticalElements( unsigned char *dst, // destination of the filter process
346 unsigned char *src, // source pixels
347 int srcStep, // stride of the source pixels
348 float s0, float s1 ){
350 float rSum = 0, gSum = 0, bSum = 0;
354 for ( y = s0; y < ( int ) ( s1 + 1 ) ; y++, src += srcStep )
357 w = ( int ) ( s0 + 1 ) - y;
359 else if ( y + 1 >= s1 ) {
377 dst[0] = ( unsigned char ) ( rSum + 0.5 );
378 dst[1] = ( unsigned char ) ( gSum + 0.5 );
379 dst[2] = ( unsigned char ) ( bSum + 0.5 );
384 static void BoxFilterRow( unsigned char *dstStart, cblock_t *in, int dstRow, int rowWidth ){
386 unsigned char *indata = ( unsigned char * ) in->data;
388 indata += 4 * dstRow * in->width;
390 for ( i = 0; i < rowWidth; i++ )
392 float c0 = i * s_resampleXRatio;
393 float c1 = ( i + 1 ) * s_resampleXRatio;
395 BoxFilterHorizontalElements( &dstStart[i * 4], &indata[( ( int ) c0 ) * 4], c0, c1 );
399 static void BoxFilterColumn( unsigned char *dstStart, unsigned char *srcStart, int dstCol, int dstRowWidth, int dstColHeight, int srcRowWidthInPels ){
403 for ( i = 0; i < dstColHeight; i++ )
405 c0 = i * s_resampleYRatio;
406 c1 = ( i + 1 ) * s_resampleYRatio;
408 BoxFilterVerticalElements( &dstStart[i * 4 * dstRowWidth], &srcStart[(int)c0 * srcRowWidthInPels * 4], srcRowWidthInPels * 4, c0, c1 );
412 #define DROP_SAMPLE 0
415 static void ResampleFrame( cblock_t *in, unsigned char *out, int method, int outWidth, int outHeight ){
417 unsigned char *indata = ( unsigned char * ) in->data;
419 s_resampleXRatio = in->width / ( float ) outWidth;
420 s_resampleYRatio = in->height / ( float ) outHeight;
422 if ( method == DROP_SAMPLE ) {
423 for ( row = 0; row < outHeight; row++ )
425 int r = ( int ) ( row * s_resampleYRatio );
427 for ( column = 0; column < outWidth; column++ )
429 int c = ( int ) ( column * s_resampleXRatio );
431 out[( row * outWidth + column ) * 4 + 0] = indata[( r * in->width + c ) * 4 + 0];
432 out[( row * outWidth + column ) * 4 + 1] = indata[( r * in->width + c ) * 4 + 1];
433 out[( row * outWidth + column ) * 4 + 2] = indata[( r * in->width + c ) * 4 + 2];
434 out[( row * outWidth + column ) * 4 + 3] = 0xff;
438 else if ( method == BOX_FILTER ) {
439 unsigned char intermediate[1024 * 1024 * 4];
441 assert( in->height <= 1024 );
442 assert( in->width <= 1024 );
445 // filter our M x N source image into a RESAMPLE_WIDTH x N horizontally filtered image
447 for ( row = 0; row < in->height; row++ )
449 BoxFilterRow( &intermediate[row * 4 * outWidth], in, row, outWidth );
453 // filter our RESAMPLE_WIDTH x N horizontally filtered image into a RESAMPLE_WIDTH x RESAMPLE_HEIGHT filtered image
455 for ( column = 0; column < outWidth; column++ )
457 BoxFilterColumn( &out[column * 4], &intermediate[column * 4], column, outWidth, outHeight, s_resample_width );
462 static float BTCDistanceSquared( float a[3], float b[3] ){
463 return ( b[0] - a[0] ) * ( b[0] - a[0] ) +
464 ( b[1] - a[1] ) * ( b[1] - a[1] ) +
465 ( b[2] - a[2] ) * ( b[2] - a[2] );
468 static void BTCFindEndpoints( float inBlock[4][4][3], unsigned int endPoints[2][2] ){
469 float longestDistance = -1;
474 // find the two points farthest from each other
476 for ( bY = 0; bY < 4; bY++ )
478 for ( bX = 0; bX < 4; bX++ )
484 // check the rest of the current row
486 for ( cX = bX + 1; cX < 4; cX++ )
488 if ( ( d = BTCDistanceSquared( inBlock[bY][bX], inBlock[bY][cX] ) ) > longestDistance ) {
490 endPoints[0][0] = bX;
491 endPoints[0][1] = bY;
492 endPoints[1][0] = cX;
493 endPoints[1][1] = bY;
498 // check remaining rows and columns
500 for ( cY = bY + 1; cY < 4; cY++ )
502 for ( cX = 0; cX < 4; cX++ )
504 if ( ( d = BTCDistanceSquared( inBlock[bY][bX], inBlock[cY][cX] ) ) > longestDistance ) {
506 endPoints[0][0] = bX;
507 endPoints[0][1] = bY;
508 endPoints[1][0] = cX;
509 endPoints[1][1] = cY;
517 static float BTCQuantizeBlock( float inBlock[4][4][3], unsigned long endPoints[2][2], int btcQuantizedBlock[4][4], float bestError ){
523 float colorLine[4][3];
526 // build the color line
528 dR = inBlock[endPoints[1][1]][endPoints[1][0]][0] -
529 inBlock[endPoints[0][1]][endPoints[0][0]][0];
530 dG = inBlock[endPoints[1][1]][endPoints[1][0]][1] -
531 inBlock[endPoints[0][1]][endPoints[0][0]][1];
532 dB = inBlock[endPoints[1][1]][endPoints[1][0]][2] -
533 inBlock[endPoints[0][1]][endPoints[0][0]][2];
539 R = inBlock[endPoints[0][1]][endPoints[0][0]][0];
540 G = inBlock[endPoints[0][1]][endPoints[0][0]][1];
541 B = inBlock[endPoints[0][1]][endPoints[0][0]][2];
543 for ( i = 0; i < 4; i++ )
555 // quantize each pixel into the appropriate range
557 for ( blockY = 0; blockY < 4; blockY++ )
559 for ( blockX = 0; blockX < 4; blockX++ )
561 float distance = 10000000000;
564 for ( i = 0; i < 4; i++ )
568 if ( ( d = BTCDistanceSquared( inBlock[blockY][blockX], colorLine[i] ) ) < distance ) {
577 // if bestError is not -1 then that means this is a speculative quantization
579 if ( bestError != -1 ) {
580 if ( error > bestError ) {
585 btcQuantizedBlock[blockY][blockX] = shortest;
593 ** float BTCCompressBlock
595 static float BTCCompressBlock( float inBlock[4][4][3], unsigned long out[2] ){
597 int btcQuantizedBlock[4][4]; // values should be [0..3]
598 unsigned long encodedEndPoints, encodedBitmap;
599 unsigned long endPoints[2][2]; // endPoints[0] = color start, endPoints[1] = color end
602 float bestError = 10000000000;
603 unsigned long bestEndPoints[2][2];
607 // find the "ideal" end points for the color vector
609 BTCFindEndpoints( inBlock, endPoints );
610 error = BTCQuantizeBlock( inBlock, endPoints, btcQuantizedBlock );
611 memcpy( bestEndPoints, endPoints, sizeof( bestEndPoints ) );
613 for ( blockY = 0; blockY < 4; blockY++ )
615 for ( blockX = 0; blockX < 4; blockX++ )
619 for ( y2 = 0; y2 < 4; y2++ )
621 for ( x2 = 0; x2 < 4; x2++ )
623 if ( ( x2 == blockX ) && ( y2 == blockY ) ) {
627 endPoints[0][0] = blockX;
628 endPoints[0][1] = blockY;
629 endPoints[1][0] = x2;
630 endPoints[1][1] = y2;
632 error = BTCQuantizeBlock( inBlock, endPoints, btcQuantizedBlock, -1 ); //bestError );
634 if ( error < bestError ) {
636 memcpy( bestEndPoints, endPoints, sizeof( bestEndPoints ) );
643 error = BTCQuantizeBlock( inBlock, bestEndPoints, btcQuantizedBlock, -1.0f );
647 // encode the results
650 for ( blockY = 0; blockY < 4; blockY++ )
652 for ( blockX = 0; blockX < 4; blockX++ )
654 int shift = ( blockX + blockY * 4 ) * 2;
655 encodedBitmap |= btcQuantizedBlock[blockY][blockX] << shift;
662 encodedEndPoints = 0;
663 for ( i = 0; i < 2; i++ )
667 iR = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][0] );
676 iG = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][1] );
685 iB = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][2] );
695 encodedEndPoints |= ( ( ( iR << 11 ) | ( iG << 5 ) | ( iB ) ) << ( i * 16 ) );
701 out[0] = encodedBitmap;
702 out[1] = encodedEndPoints;
708 ** void BTCDecompressFrame
710 static void BTCDecompressFrame( unsigned long *src, unsigned char *dst ){
714 float colorStart[3], colorEnd[3];
715 unsigned char colorRampABGR[4][4];
718 memset( colorRampABGR, 0xff, sizeof( colorRampABGR ) );
720 for ( y = 0; y < s_resample_height / 4; y++ )
722 for ( x = 0; x < s_resample_width / 4; x++ )
724 unsigned colorStartPacked = src[( y * s_resample_width / 4 + x ) * 2 + 1] & 0xffff;
725 unsigned colorEndPacked = src[( y * s_resample_width / 4 + x ) * 2 + 1] >> 16;
728 // grab the end points
732 iR = ( ( colorStartPacked >> 11 ) & ( ( 1 << 5 ) - 1 ) );
733 iR = ( iR << 3 ) | ( iR >> 2 );
734 iG = ( ( colorStartPacked >> 5 ) & ( ( 1 << 6 ) - 1 ) );
735 iG = ( iG << 2 ) | ( iG >> 4 );
736 iB = ( ( colorStartPacked ) & ( ( 1 << 5 ) - 1 ) );
737 iB = ( iB << 3 ) | ( iB >> 2 );
742 colorRampABGR[0][0] = iR;
743 colorRampABGR[0][1] = iG;
744 colorRampABGR[0][2] = iB;
746 iR = ( ( colorEndPacked >> 11 ) & ( ( 1 << 5 ) - 1 ) );
747 iR = ( iR << 3 ) | ( iR >> 2 );
748 iG = ( ( colorEndPacked >> 5 ) & ( ( 1 << 6 ) - 1 ) );
749 iG = ( iG << 2 ) | ( iG >> 4 );
750 iB = ( colorEndPacked & ( ( 1 << 5 ) - 1 ) );
751 iB = ( iB << 3 ) | ( iB >> 2 );
756 colorRampABGR[3][0] = iR;
757 colorRampABGR[3][1] = iG;
758 colorRampABGR[3][2] = iB;
761 // compute this block's color ramp
762 // FIXME: This needs to be reversed on big-endian machines
765 colorRampABGR[1][0] = colorStart[0] * 0.66f + colorEnd[0] * 0.33f;
766 colorRampABGR[1][1] = colorStart[1] * 0.66f + colorEnd[1] * 0.33f;
767 colorRampABGR[1][2] = colorStart[2] * 0.66f + colorEnd[2] * 0.33f;
769 colorRampABGR[2][0] = colorStart[0] * 0.33f + colorEnd[0] * 0.66f;
770 colorRampABGR[2][1] = colorStart[1] * 0.33f + colorEnd[1] * 0.66f;
771 colorRampABGR[2][2] = colorStart[2] * 0.33f + colorEnd[2] * 0.66f;
774 // decode the color data
775 // information is encoded in 2-bit pixels, with low order bits corresponding
776 // to upper left pixels. These 2-bit values are indexed into the block's
777 // computer color ramp.
779 encoded = src[( y * s_resample_width / 4 + x ) * 2 + 0];
781 for ( dstY = 0; dstY < 4; dstY++ )
783 for ( dstX = 0; dstX < 4; dstX++ )
785 memcpy( &dst[( y * 4 + dstY ) * s_resample_width * 4 + x * 4 * 4 + dstX * 4], colorRampABGR[encoded & 3], sizeof( colorRampABGR[0] ) );
796 ** Perform a BTC compression using a 2-bit encoding at each pixel. This
797 ** compression method is performed by decomposing the incoming image into
798 ** a sequence of 4x4 blocks. At each block two color values are computed
799 ** that define the endpoints of a vector in color space that represent
800 ** the two colors "farthest apart".
802 static float BTCCompressFrame( unsigned char *src, unsigned long *dst ){
805 float btcBlock[4][4][3];
809 for ( y = 0; y < s_resample_height / 4; y++ )
811 for ( x = 0; x < s_resample_width / 4; x++ )
814 // fill in the BTC block with raw values
816 for ( bY = 0; bY < 4; bY++ )
818 for ( bX = 0; bX < 4; bX++ )
820 btcBlock[bY][bX][0] = src[( y * 4 + bY ) * s_resample_width * 4 + ( x * 4 + bX ) * 4 + 0];
821 btcBlock[bY][bX][1] = src[( y * 4 + bY ) * s_resample_width * 4 + ( x * 4 + bX ) * 4 + 1];
822 btcBlock[bY][bX][2] = src[( y * 4 + bY ) * s_resample_width * 4 + ( x * 4 + bX ) * 4 + 2];
826 error += BTCCompressBlock( btcBlock, &dst[( y * s_resample_width / 4 + x ) * 2] );
830 return error / ( ( s_resample_width / 4 ) * ( s_resample_height / 4 ) );
838 cblock_t LoadFrame( char *base, int frame, int digits, byte **palette ){
839 int ten3, ten2, ten1, ten0;
849 ten2 = ( frame - ten3 * 1000 ) / 100;
850 ten1 = ( frame - ten3 * 1000 - ten2 * 100 ) / 10;
854 sprintf( name, "%svideo/%s/%s%i%i%i%i.tga", gamedir, base, base, ten3, ten2, ten1, ten0 );
857 sprintf( name, "%svideo/%s/%s%i%i%i.tga", gamedir, base, base, ten2, ten1, ten0 );
860 f = fopen( name, "rb" );
867 printf( "%s", name );
868 LoadTGA( name, ( unsigned char ** ) &in.data, &width, &height );
872 // Load256Image (name, &in.data, palette, &width, &height);
873 in.count = width * height;
876 // FIXME: map 0 and 255!
893 video <directory> <framedigits>
896 void Cmd_Video( void ){
897 float sumError = 0, error = 0, maxError = 0;
901 int startframe, frame;
909 unsigned char *resampled;
910 unsigned long *compressed;
914 strcpy( s_base, token );
916 // sprintf (savename, "video/%s.cin", token);
917 // ReleaseFile (savename);
922 strcpy( s_output_base, token );
925 digits = atoi( token );
929 if ( !strcmp( token, "btc" ) ) {
930 s_compression_method = BTC_COMPRESSION;
931 printf( "Compression: BTC\n" );
933 else if ( !strcmp( token, "uc" ) ) {
934 s_compression_method = UNCOMPRESSED;
935 printf( "Compression: none\n" );
939 Error( "Uknown compression method '%s'\n", token );
943 s_resample_width = atoi( token );
946 s_resample_height = atoi( token );
948 resampled = malloc( sizeof( unsigned char ) * 4 * s_resample_width * s_resample_height );
949 compressed = malloc( sizeof( long ) * 2 * ( s_resample_width / 4 ) * ( s_resample_height / 4 ) );
951 printf( "Resample width: %d\n", s_resample_width );
952 printf( "Resample height: %d\n", s_resample_height );
954 // optionally skip frames
955 if ( TokenAvailable() ) {
957 startframe = atoi( token );
963 sprintf( savename, "%svideo/%s.%s", writedir, s_output_base, CIN_EXTENSION );
965 // load the entire sound wav file if present
969 sprintf( name, "%svideo/%s/%s0000.tga", gamedir, s_base, s_base );
972 sprintf( name, "%svideo/%s/%s000.tga", gamedir, s_base, s_base );
975 printf( "%s\n", name );
976 LoadTGA( name, NULL, &width, &height );
978 output = fopen( savename, "wb" );
980 Error( "Can't open %s", savename );
984 i = LittleLong( CIN_SIGNATURE );
985 fwrite( &i, 4, 1, output );
986 i = LittleLong( s_resample_width );
987 fwrite( &i, 4, 1, output );
988 i = LittleLong( s_resample_height );
989 fwrite( &i, 4, 1, output );
990 i = LittleLong( s_wavinfo.rate );
991 fwrite( &i, 4, 1, output );
992 i = LittleLong( s_wavinfo.width );
993 fwrite( &i, 4, 1, output );
994 i = LittleLong( s_wavinfo.channels );
995 fwrite( &i, 4, 1, output );
996 i = LittleLong( s_compression_method );
997 fwrite( &i, 4, 1, output );
1001 // perform compression on a per frame basis
1002 for ( frame = startframe ; ; frame++ )
1004 printf( "%02d: ", frame );
1005 in = LoadFrame( s_base, frame, digits, 0 );
1010 ResampleFrame( &in, ( unsigned char * ) resampled, BOX_FILTER, s_resample_width, s_resample_height );
1012 if ( s_compression_method == UNCOMPRESSED ) {
1014 fwrite( resampled, 1, sizeof( unsigned char ) * s_resample_width * s_resample_height * 4, output );
1021 for ( y = 0; y < s_resample_height / 2; y++ )
1023 for ( x = 0; x < s_resample_width; x++ )
1025 unsigned char tmp[4];
1027 tmp[0] = resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 0];
1028 tmp[1] = resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 1];
1029 tmp[2] = resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 2];
1030 tmp[3] = resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 3];
1032 resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 0] = resampled[y * s_resample_width * 4 + x * 4 + 0];
1033 resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 1] = resampled[y * s_resample_width * 4 + x * 4 + 1];
1034 resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 2] = resampled[y * s_resample_width * 4 + x * 4 + 2];
1035 resampled[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 3] = resampled[y * s_resample_width * 4 + x * 4 + 3];
1037 resampled[y * s_resample_width * 4 + x * 4 + 0] = tmp[0];
1038 resampled[y * s_resample_width * 4 + x * 4 + 1] = tmp[1];
1039 resampled[y * s_resample_width * 4 + x * 4 + 2] = tmp[2];
1040 resampled[y * s_resample_width * 4 + x * 4 + 3] = tmp[3];
1044 sprintf( buffer, "%svideo/%s/uc%04d.tga", gamedir, s_base, frame );
1045 WriteTGA( buffer, resampled, s_resample_width, s_resample_height );
1049 else if ( s_compression_method == BTC_COMPRESSION ) {
1050 error = BTCCompressFrame( resampled, compressed );
1054 if ( error > maxError ) {
1058 printf( " (error = %f)\n", error );
1059 fwrite( compressed, 1, 2 * sizeof( long ) * ( s_resample_width / 4 ) * ( s_resample_height / 4 ), output );
1064 unsigned char *uncompressed;
1067 uncompressed = malloc( sizeof( unsigned char ) * 4 * s_resample_width * s_resample_height );
1068 BTCDecompressFrame( compressed, uncompressed );
1070 for ( y = 0; y < s_resample_height / 2; y++ )
1072 for ( x = 0; x < s_resample_width; x++ )
1074 unsigned char tmp[4];
1076 tmp[0] = uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 0];
1077 tmp[1] = uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 1];
1078 tmp[2] = uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 2];
1079 tmp[3] = uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 3];
1081 uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 0] = uncompressed[y * s_resample_width * 4 + x * 4 + 0];
1082 uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 1] = uncompressed[y * s_resample_width * 4 + x * 4 + 1];
1083 uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 2] = uncompressed[y * s_resample_width * 4 + x * 4 + 2];
1084 uncompressed[( s_resample_height - 1 - y ) * s_resample_width * 4 + x * 4 + 3] = uncompressed[y * s_resample_width * 4 + x * 4 + 3];
1086 uncompressed[y * s_resample_width * 4 + x * 4 + 0] = tmp[0];
1087 uncompressed[y * s_resample_width * 4 + x * 4 + 1] = tmp[1];
1088 uncompressed[y * s_resample_width * 4 + x * 4 + 2] = tmp[2];
1089 uncompressed[y * s_resample_width * 4 + x * 4 + 3] = tmp[3];
1094 sprintf( buffer, "%svideo/%s/btc%04d.tga", gamedir, s_base, frame );
1095 WriteTGA( buffer, uncompressed, s_resample_width, s_resample_height );
1097 free( uncompressed );
1102 WriteSound( output, frame );
1110 printf( "Total size: %ld\n", ftell( output ) );
1111 printf( "Average error: %f\n", sumError / ( frame - startframe ) );
1112 printf( "Max error: %f\n", maxError );
1114 fseconds = ( stop - start ) / 1000.0f;
1115 minutes = fseconds / 60;
1116 remSeconds = fseconds - minutes * 60;
1118 printf( "Total time: %d s (%d m %d s)\n", ( int ) fseconds, minutes, remSeconds );
1119 printf( "Time/frame: %.2f seconds\n", fseconds / ( frame - startframe ) );
1123 if ( s_soundtrack ) {
1124 free( s_soundtrack );