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)
75 val = val + (*(data_p+1)<<8);
80 int GetLittleLong(void)
84 val = val + (*(data_p+1)<<8);
85 val = val + (*(data_p+2)<<16);
86 val = val + (*(data_p+3)<<24);
91 void FindNextChunk(char *name)
97 if (data_p >= iff_end)
98 { // didn't find the chunk
104 iff_chunk_len = GetLittleLong();
105 if (iff_chunk_len < 0)
110 // if (iff_chunk_len > 1024*1024)
111 // Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
113 last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
114 if (!strncmp(data_p, name, 4))
119 void FindChunk(char *name)
121 last_chunk = iff_data;
122 FindNextChunk (name);
126 void DumpChunks(void)
134 memcpy (str, data_p, 4);
136 iff_chunk_len = GetLittleLong();
137 printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
138 data_p += (iff_chunk_len + 1) & ~1;
139 } while (data_p < iff_end);
147 wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
154 memset (&info, 0, sizeof(info));
160 iff_end = wav + wavlength;
164 if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))
166 printf("Missing RIFF/WAVE chunks\n");
171 iff_data = data_p + 12;
177 printf("Missing fmt chunk\n");
181 format = GetLittleShort();
184 printf("Microsoft PCM format only\n");
188 info.channels = GetLittleShort();
189 info.rate = GetLittleLong();
191 info.width = GetLittleShort() / 8;
198 info.loopstart = GetLittleLong();
199 // Com_Printf("loopstart=%d\n", sfx->loopstart);
201 // if the next chunk is a LIST chunk, look for a cue length marker
202 FindNextChunk ("LIST");
205 if (!strncmp (data_p + 28, "mark", 4))
206 { // this is not a proper parse, but it works with cooledit...
208 i = GetLittleLong (); // samples in loop
209 info.samples = info.loopstart + i;
220 printf("Missing data chunk\n");
225 samples = GetLittleLong ();
229 if (samples < info.samples)
230 Error ("Sound %s has a bad loop length", name);
233 info.samples = samples;
235 info.dataofs = data_p - wav;
240 //=====================================================================
247 void LoadSoundtrack (void)
255 sprintf (name, "%svideo/%s/%s.wav", gamedir, s_base, s_base);
256 printf ("WAV: %s\n", name);
257 f = fopen (name, "rb");
260 printf ("no soundtrack for %s\n", s_base);
263 len = Q_filelength(f);
264 s_soundtrack = malloc(len);
265 fread (s_soundtrack, 1, len, f);
268 s_wavinfo = GetWavinfo (name, s_soundtrack, len);
270 // count samples for compression
271 memset (s_samplecounts, 0, sizeof(s_samplecounts));
273 j = s_wavinfo.samples/2;
274 for (i=0 ; i<j ; i++)
276 val = ((unsigned short *)( s_soundtrack + s_wavinfo.dataofs))[i];
277 s_samplecounts[val]++;
280 for (i=0 ; i<0x10000 ; i++)
281 if (s_samplecounts[i])
284 printf ("%i unique sample values\n", val);
292 void WriteSound (FILE *output, int frame)
301 width = s_wavinfo.width * s_wavinfo.channels;
303 start = frame*s_wavinfo.rate/14;
304 end = (frame+1)*s_wavinfo.rate/14;
307 for (i=0 ; i<count ; i++)
310 if (sample > s_wavinfo.samples || !s_soundtrack)
311 fwrite (&empty, 1, width, output);
313 fwrite (s_soundtrack + s_wavinfo.dataofs + sample*width, 1, width,output);
317 //==========================================================================
319 static float s_resampleXRatio;
320 static float s_resampleYRatio;
322 static void BoxFilterHorizontalElements( unsigned char *dst, unsigned char *src, float s0, float s1 )
325 float rSum = 0, gSum = 0, bSum = 0;
329 for ( x = s0; x < s1; x++, src += 4 )
333 w = ( int ) ( s0 + 1 ) - x;
335 else if ( x + 1 >= s1 )
354 dst[0] = ( unsigned char ) ( rSum + 0.5 );
355 dst[1] = ( unsigned char ) ( gSum + 0.5 );
356 dst[2] = ( unsigned char ) ( bSum + 0.5 );
359 static void BoxFilterVerticalElements( unsigned char *dst, // destination of the filter process
360 unsigned char *src, // source pixels
361 int srcStep, // stride of the source pixels
365 float rSum = 0, gSum = 0, bSum = 0;
369 for ( y = s0; y < ( int ) ( s1 + 1 ) ; y++, src += srcStep )
373 w = ( int ) ( s0 + 1 ) - y;
375 else if ( y + 1 >= s1 )
394 dst[0] = ( unsigned char ) ( rSum + 0.5 );
395 dst[1] = ( unsigned char ) ( gSum + 0.5 );
396 dst[2] = ( unsigned char ) ( bSum + 0.5 );
401 static void BoxFilterRow( unsigned char *dstStart, cblock_t *in, int dstRow, int rowWidth )
404 unsigned char *indata = ( unsigned char * ) in->data;
406 indata += 4 * dstRow * in->width;
408 for ( i = 0; i < rowWidth; i++ )
410 float c0 = i * s_resampleXRatio;
411 float c1 = ( i + 1 ) * s_resampleXRatio;
413 BoxFilterHorizontalElements( &dstStart[i*4], &indata[( ( int ) c0 ) * 4], c0, c1 );
417 static void BoxFilterColumn( unsigned char *dstStart, unsigned char *srcStart, int dstCol, int dstRowWidth, int dstColHeight, int srcRowWidthInPels )
422 for ( i = 0; i < dstColHeight; i++ )
424 c0 = i * s_resampleYRatio;
425 c1 = ( i + 1 ) * s_resampleYRatio;
427 BoxFilterVerticalElements( &dstStart[i*4*dstRowWidth], &srcStart[(int)c0*srcRowWidthInPels*4], srcRowWidthInPels*4, c0, c1 );
431 #define DROP_SAMPLE 0
434 static void ResampleFrame( cblock_t *in, unsigned char *out, int method, int outWidth, int outHeight )
437 unsigned char *indata = ( unsigned char * ) in->data;
439 s_resampleXRatio = in->width / ( float ) outWidth;
440 s_resampleYRatio = in->height / ( float ) outHeight;
442 if ( method == DROP_SAMPLE )
444 for ( row = 0; row < outHeight; row++ )
446 int r = ( int ) ( row * s_resampleYRatio );
448 for ( column = 0; column < outWidth; column++ )
450 int c = ( int ) ( column * s_resampleXRatio );
452 out[(row*outWidth+column)*4+0] = indata[(r*in->width+c)*4+0];
453 out[(row*outWidth+column)*4+1] = indata[(r*in->width+c)*4+1];
454 out[(row*outWidth+column)*4+2] = indata[(r*in->width+c)*4+2];
455 out[(row*outWidth+column)*4+3] = 0xff;
459 else if ( method == BOX_FILTER )
461 unsigned char intermediate[1024*1024*4];
463 assert( in->height <= 1024 );
464 assert( in->width <= 1024 );
467 // filter our M x N source image into a RESAMPLE_WIDTH x N horizontally filtered image
469 for ( row = 0; row < in->height; row++ )
471 BoxFilterRow( &intermediate[row*4*outWidth], in, row, outWidth );
475 // filter our RESAMPLE_WIDTH x N horizontally filtered image into a RESAMPLE_WIDTH x RESAMPLE_HEIGHT filtered image
477 for ( column = 0; column < outWidth; column++ )
479 BoxFilterColumn( &out[column*4], &intermediate[column*4], column, outWidth, outHeight, s_resample_width );
484 static float BTCDistanceSquared( float a[3], float b[3] )
486 return ( b[0] - a[0] ) * ( b[0] - a[0] ) +
487 ( b[1] - a[1] ) * ( b[1] - a[1] ) +
488 ( b[2] - a[2] ) * ( b[2] - a[2] );
491 static void BTCFindEndpoints( float inBlock[4][4][3], unsigned int endPoints[2][2] )
493 float longestDistance = -1;
498 // find the two points farthest from each other
500 for ( bY = 0; bY < 4; bY++ )
502 for ( bX = 0; bX < 4; bX++ )
508 // check the rest of the current row
510 for ( cX = bX + 1; cX < 4; cX++ )
512 if ( ( d = BTCDistanceSquared( inBlock[bY][bX], inBlock[bY][cX] ) ) > longestDistance )
515 endPoints[0][0] = bX;
516 endPoints[0][1] = bY;
517 endPoints[1][0] = cX;
518 endPoints[1][1] = bY;
523 // check remaining rows and columns
525 for ( cY = bY+1; cY < 4; cY++ )
527 for ( cX = 0; cX < 4; cX++ )
529 if ( ( d = BTCDistanceSquared( inBlock[bY][bX], inBlock[cY][cX] ) ) > longestDistance )
532 endPoints[0][0] = bX;
533 endPoints[0][1] = bY;
534 endPoints[1][0] = cX;
535 endPoints[1][1] = cY;
543 static float BTCQuantizeBlock( float inBlock[4][4][3], unsigned long endPoints[2][2], int btcQuantizedBlock[4][4], float bestError )
550 float colorLine[4][3];
553 // build the color line
555 dR = inBlock[endPoints[1][1]][endPoints[1][0]][0] -
556 inBlock[endPoints[0][1]][endPoints[0][0]][0];
557 dG = inBlock[endPoints[1][1]][endPoints[1][0]][1] -
558 inBlock[endPoints[0][1]][endPoints[0][0]][1];
559 dB = inBlock[endPoints[1][1]][endPoints[1][0]][2] -
560 inBlock[endPoints[0][1]][endPoints[0][0]][2];
566 R = inBlock[endPoints[0][1]][endPoints[0][0]][0];
567 G = inBlock[endPoints[0][1]][endPoints[0][0]][1];
568 B = inBlock[endPoints[0][1]][endPoints[0][0]][2];
570 for ( i = 0; i < 4; i++ )
582 // quantize each pixel into the appropriate range
584 for ( blockY = 0; blockY < 4; blockY++ )
586 for ( blockX = 0; blockX < 4; blockX++ )
588 float distance = 10000000000;
591 for ( i = 0; i < 4; i++ )
595 if ( ( d = BTCDistanceSquared( inBlock[blockY][blockX], colorLine[i] ) ) < distance )
605 // if bestError is not -1 then that means this is a speculative quantization
607 if ( bestError != -1 )
609 if ( error > bestError )
613 btcQuantizedBlock[blockY][blockX] = shortest;
621 ** float BTCCompressBlock
623 static float BTCCompressBlock( float inBlock[4][4][3], unsigned long out[2] )
626 int btcQuantizedBlock[4][4]; // values should be [0..3]
627 unsigned long encodedEndPoints, encodedBitmap;
628 unsigned int endPoints[2][2]; // endPoints[0] = color start, endPoints[1] = color end
631 float bestError = 10000000000;
632 unsigned int bestEndPoints[2][2];
636 // find the "ideal" end points for the color vector
638 BTCFindEndpoints( inBlock, endPoints );
639 error = BTCQuantizeBlock( inBlock, endPoints, btcQuantizedBlock );
640 memcpy( bestEndPoints, endPoints, sizeof( bestEndPoints ) );
642 for ( blockY = 0; blockY < 4; blockY++ )
644 for ( blockX = 0; blockX < 4; blockX++ )
648 for ( y2 = 0; y2 < 4; y2++ )
650 for ( x2 = 0; x2 < 4; x2++ )
652 if ( ( x2 == blockX ) && ( y2 == blockY ) )
655 endPoints[0][0] = blockX;
656 endPoints[0][1] = blockY;
657 endPoints[1][0] = x2;
658 endPoints[1][1] = y2;
660 error = BTCQuantizeBlock( inBlock, endPoints, btcQuantizedBlock, -1 ); //bestError );
662 if ( error < bestError )
665 memcpy( bestEndPoints, endPoints, sizeof( bestEndPoints ) );
672 error = BTCQuantizeBlock( inBlock, bestEndPoints, btcQuantizedBlock, -1.0f );
676 // encode the results
679 for ( blockY = 0; blockY < 4; blockY++ )
681 for ( blockX = 0; blockX < 4; blockX++ )
683 int shift = ( blockX + blockY * 4 ) * 2;
684 encodedBitmap |= btcQuantizedBlock[blockY][blockX] << shift;
691 encodedEndPoints = 0;
692 for ( i = 0; i < 2; i++ )
696 iR = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][0] );
703 iG = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][1] );
710 iB = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][2] );
718 encodedEndPoints |= ( ( ( iR << 11 ) | ( iG << 5 ) | ( iB ) ) << ( i * 16 ) );
724 out[0] = encodedBitmap;
725 out[1] = encodedEndPoints;
731 ** void BTCDecompressFrame
733 static void BTCDecompressFrame( unsigned long *src, unsigned char *dst )
738 float colorStart[3], colorEnd[3];
739 unsigned char colorRampABGR[4][4];
742 memset( colorRampABGR, 0xff, sizeof( colorRampABGR ) );
744 for ( y = 0; y < s_resample_height / 4; y++ )
746 for ( x = 0; x < s_resample_width / 4; x++ )
748 unsigned colorStartPacked = src[(y*s_resample_width/4 + x)*2 + 1] & 0xffff;
749 unsigned colorEndPacked = src[(y*s_resample_width/4 + x)*2 + 1] >> 16;
752 // grab the end points
756 iR = ( ( colorStartPacked >> 11 ) & ( ( 1 << 5 ) - 1 ) );
757 iR = ( iR << 3 ) | ( iR >> 2 );
758 iG = ( ( colorStartPacked >> 5 ) & ( ( 1 << 6 ) - 1 ) );
759 iG = ( iG << 2 ) | ( iG >> 4 );
760 iB = ( ( colorStartPacked ) & ( ( 1 << 5 ) - 1 ) );
761 iB = ( iB << 3 ) | ( iB >> 2 );
766 colorRampABGR[0][0] = iR;
767 colorRampABGR[0][1] = iG;
768 colorRampABGR[0][2] = iB;
770 iR = ( ( colorEndPacked >> 11 ) & ( ( 1 << 5 ) - 1 ) );
771 iR = ( iR << 3 ) | ( iR >> 2 );
772 iG = ( ( colorEndPacked >> 5 ) & ( ( 1 << 6 ) - 1 ) );
773 iG = ( iG << 2 ) | ( iG >> 4 );
774 iB = ( colorEndPacked & ( ( 1 << 5 ) - 1 ) );
775 iB = ( iB << 3 ) | ( iB >> 2 );
780 colorRampABGR[3][0] = iR;
781 colorRampABGR[3][1] = iG;
782 colorRampABGR[3][2] = iB;
785 // compute this block's color ramp
786 // FIXME: This needs to be reversed on big-endian machines
789 colorRampABGR[1][0] = colorStart[0] * 0.66f + colorEnd[0] * 0.33f;
790 colorRampABGR[1][1] = colorStart[1] * 0.66f + colorEnd[1] * 0.33f;
791 colorRampABGR[1][2] = colorStart[2] * 0.66f + colorEnd[2] * 0.33f;
793 colorRampABGR[2][0] = colorStart[0] * 0.33f + colorEnd[0] * 0.66f;
794 colorRampABGR[2][1] = colorStart[1] * 0.33f + colorEnd[1] * 0.66f;
795 colorRampABGR[2][2] = colorStart[2] * 0.33f + colorEnd[2] * 0.66f;
798 // decode the color data
799 // information is encoded in 2-bit pixels, with low order bits corresponding
800 // to upper left pixels. These 2-bit values are indexed into the block's
801 // computer color ramp.
803 encoded = src[(y*s_resample_width/4 + x)*2 + 0];
805 for ( dstY = 0; dstY < 4; dstY++ )
807 for ( dstX = 0; dstX < 4; dstX++ )
809 memcpy( &dst[(y*4+dstY)*s_resample_width*4+x*4*4+dstX*4], colorRampABGR[encoded&3], sizeof( colorRampABGR[0] ) );
820 ** Perform a BTC compression using a 2-bit encoding at each pixel. This
821 ** compression method is performed by decomposing the incoming image into
822 ** a sequence of 4x4 blocks. At each block two color values are computed
823 ** that define the endpoints of a vector in color space that represent
824 ** the two colors "farthest apart".
826 static float BTCCompressFrame( unsigned char *src, unsigned long *dst )
830 float btcBlock[4][4][3];
834 for ( y = 0; y < s_resample_height / 4; y++ )
836 for ( x = 0; x < s_resample_width / 4; x++ )
839 // fill in the BTC block with raw values
841 for ( bY = 0; bY < 4; bY++ )
843 for ( bX = 0; bX < 4; bX++ )
845 btcBlock[bY][bX][0] = src[(y*4+bY)*s_resample_width*4 + (x*4+bX)*4 + 0];
846 btcBlock[bY][bX][1] = src[(y*4+bY)*s_resample_width*4 + (x*4+bX)*4 + 1];
847 btcBlock[bY][bX][2] = src[(y*4+bY)*s_resample_width*4 + (x*4+bX)*4 + 2];
851 error += BTCCompressBlock( btcBlock, &dst[(y*s_resample_width/4+x)*2] );
855 return error / ( ( s_resample_width / 4 ) * ( s_resample_height / 4 ) );
863 cblock_t LoadFrame (char *base, int frame, int digits, byte **palette)
865 int ten3, ten2, ten1, ten0;
875 ten2 = (frame-ten3*1000)/100;
876 ten1 = (frame-ten3*1000-ten2*100)/10;
880 sprintf (name, "%svideo/%s/%s%i%i%i%i.tga", gamedir, base, base, ten3, ten2, ten1, ten0);
882 sprintf (name, "%svideo/%s/%s%i%i%i.tga", gamedir, base, base, ten2, ten1, ten0);
884 f = fopen(name, "rb");
893 LoadTGA( name, ( unsigned char ** ) &in.data, &width, &height );
896 // Load256Image (name, &in.data, palette, &width, &height);
897 in.count = width*height;
900 // FIXME: map 0 and 255!
917 video <directory> <framedigits>
920 void Cmd_Video (void)
922 float sumError = 0, error = 0, maxError = 0;
926 int startframe, frame;
934 unsigned char *resampled;
935 unsigned long *compressed;
939 strcpy (s_base, token);
942 // sprintf (savename, "video/%s.cin", token);
943 // ReleaseFile (savename);
948 strcpy( s_output_base, token );
951 digits = atoi(token);
955 if ( !strcmp( token, "btc" ) )
957 s_compression_method = BTC_COMPRESSION;
958 printf( "Compression: BTC\n" );
960 else if ( !strcmp( token, "uc" ) )
962 s_compression_method = UNCOMPRESSED;
963 printf( "Compression: none\n" );
967 Error( "Uknown compression method '%s'\n", token );
971 s_resample_width = atoi( token );
974 s_resample_height = atoi( token );
976 resampled = malloc( sizeof( unsigned char ) * 4 * s_resample_width * s_resample_height );
977 compressed = malloc( sizeof( long ) * 2 * ( s_resample_width / 4 ) * ( s_resample_height / 4 ) );
979 printf( "Resample width: %d\n", s_resample_width );
980 printf( "Resample height: %d\n", s_resample_height );
982 // optionally skip frames
983 if (TokenAvailable ())
986 startframe = atoi(token);
991 sprintf (savename, "%svideo/%s.%s", writedir, s_output_base, CIN_EXTENSION );
993 // load the entire sound wav file if present
997 sprintf (name, "%svideo/%s/%s0000.tga", gamedir, s_base, s_base);
999 sprintf (name, "%svideo/%s/%s000.tga", gamedir, s_base, s_base);
1001 printf ("%s\n", name);
1002 LoadTGA( name, NULL, &width, &height);
1004 output = fopen (savename, "wb");
1006 Error ("Can't open %s", savename);
1008 // write header info
1009 i = LittleLong( CIN_SIGNATURE );
1010 fwrite (&i, 4, 1, output );
1011 i = LittleLong (s_resample_width);
1012 fwrite (&i, 4, 1, output);
1013 i = LittleLong (s_resample_height);
1014 fwrite (&i, 4, 1, output);
1015 i = LittleLong (s_wavinfo.rate);
1016 fwrite (&i, 4, 1, output);
1017 i = LittleLong (s_wavinfo.width);
1018 fwrite (&i, 4, 1, output);
1019 i = LittleLong (s_wavinfo.channels);
1020 fwrite (&i, 4, 1, output);
1021 i = LittleLong ( s_compression_method );
1022 fwrite (&i, 4, 1, output );
1026 // perform compression on a per frame basis
1027 for ( frame=startframe ; ; frame++)
1029 printf ("%02d: ", frame);
1030 in = LoadFrame (s_base, frame, digits, 0 );
1034 ResampleFrame( &in, ( unsigned char * ) resampled, BOX_FILTER, s_resample_width, s_resample_height );
1036 if ( s_compression_method == UNCOMPRESSED )
1039 fwrite( resampled, 1, sizeof( unsigned char ) * s_resample_width * s_resample_height * 4, output );
1046 for ( y = 0; y < s_resample_height/2; y++ )
1048 for ( x = 0; x < s_resample_width; x++ )
1050 unsigned char tmp[4];
1052 tmp[0] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0];
1053 tmp[1] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1];
1054 tmp[2] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2];
1055 tmp[3] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3];
1057 resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0] = resampled[y*s_resample_width*4 + x*4 + 0];
1058 resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1] = resampled[y*s_resample_width*4 + x*4 + 1];
1059 resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2] = resampled[y*s_resample_width*4 + x*4 + 2];
1060 resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3] = resampled[y*s_resample_width*4 + x*4 + 3];
1062 resampled[y*s_resample_width*4 + x*4 + 0] = tmp[0];
1063 resampled[y*s_resample_width*4 + x*4 + 1] = tmp[1];
1064 resampled[y*s_resample_width*4 + x*4 + 2] = tmp[2];
1065 resampled[y*s_resample_width*4 + x*4 + 3] = tmp[3];
1069 sprintf( buffer, "%svideo/%s/uc%04d.tga", gamedir, s_base, frame );
1070 WriteTGA( buffer, resampled, s_resample_width, s_resample_height );
1074 else if ( s_compression_method == BTC_COMPRESSION )
1076 error = BTCCompressFrame( resampled, compressed );
1080 if ( error > maxError )
1083 printf( " (error = %f)\n", error );
1084 fwrite( compressed, 1, 2 * sizeof( long ) * ( s_resample_width / 4 ) * ( s_resample_height / 4 ), output );
1089 unsigned char *uncompressed;
1092 uncompressed = malloc( sizeof( unsigned char ) * 4 * s_resample_width * s_resample_height );
1093 BTCDecompressFrame( compressed, uncompressed );
1095 for ( y = 0; y < s_resample_height/2; y++ )
1097 for ( x = 0; x < s_resample_width; x++ )
1099 unsigned char tmp[4];
1101 tmp[0] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0];
1102 tmp[1] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1];
1103 tmp[2] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2];
1104 tmp[3] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3];
1106 uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0] = uncompressed[y*s_resample_width*4 + x*4 + 0];
1107 uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1] = uncompressed[y*s_resample_width*4 + x*4 + 1];
1108 uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2] = uncompressed[y*s_resample_width*4 + x*4 + 2];
1109 uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3] = uncompressed[y*s_resample_width*4 + x*4 + 3];
1111 uncompressed[y*s_resample_width*4 + x*4 + 0] = tmp[0];
1112 uncompressed[y*s_resample_width*4 + x*4 + 1] = tmp[1];
1113 uncompressed[y*s_resample_width*4 + x*4 + 2] = tmp[2];
1114 uncompressed[y*s_resample_width*4 + x*4 + 3] = tmp[3];
1119 sprintf( buffer, "%svideo/%s/btc%04d.tga", gamedir, s_base, frame );
1120 WriteTGA( buffer, uncompressed, s_resample_width, s_resample_height );
1122 free( uncompressed );
1127 WriteSound( output, frame );
1135 printf ("Total size: %i\n", ftell( output ) );
1136 printf ("Average error: %f\n", sumError / ( frame - startframe ) );
1137 printf ("Max error: %f\n", maxError );
1139 fseconds = ( stop - start ) / 1000.0f;
1140 minutes = fseconds / 60;
1141 remSeconds = fseconds - minutes * 60;
1143 printf ("Total time: %d s (%d m %d s)\n", ( int ) fseconds, minutes, remSeconds );
1144 printf ("Time/frame: %.2f seconds\n", fseconds / ( frame - startframe ) );
1150 free( s_soundtrack );