home *** CD-ROM | disk | FTP | other *** search
/ PC PowerPlay 58 / pcpp58a.iso / extras / quake 3 source / Q3A_ToolSource.exe / Main / video.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-01-02  |  26.6 KB  |  1,133 lines

  1. #include <assert.h>
  2. #include "q3data.h"
  3.  
  4. static int s_resample_width = 256;
  5. static int s_resample_height = 256;
  6.  
  7. #define OUTPUT_TGAS            1
  8.  
  9. #define UNCOMPRESSED        0
  10. #define BTC_COMPRESSION        1
  11.  
  12. static int s_compression_method = BTC_COMPRESSION;
  13.  
  14. static const char *CIN_EXTENSION = "cn2";
  15. static const int CIN_SIGNATURE = ( 'C' << 24 ) | ( 'I' << 16 ) | ( 'N' << 8 ) | ( '2' );
  16.  
  17. static byte    *s_soundtrack;
  18. static char    s_base[32];
  19. static char    s_output_base[32];
  20.  
  21. /*
  22. ===============================================================================
  23.  
  24. WAV loading
  25.  
  26. ===============================================================================
  27. */
  28.  
  29. typedef struct
  30. {
  31.     int            rate;
  32.     int            width;
  33.     int            channels;
  34.     int            loopstart;
  35.     int            samples;
  36.     int            dataofs;        // chunk starts this many bytes from file start
  37. } wavinfo_t;
  38.  
  39.  
  40. byte    *data_p;
  41. byte     *iff_end;
  42. byte     *last_chunk;
  43. byte     *iff_data;
  44. int     iff_chunk_len;
  45.  
  46.  
  47. static int            s_samplecounts[0x10000];
  48. static wavinfo_t    s_wavinfo;
  49.  
  50. short GetLittleShort(void)
  51. {
  52.     short val = 0;
  53.     val = *data_p;
  54.     val = val + (*(data_p+1)<<8);
  55.     data_p += 2;
  56.     return val;
  57. }
  58.  
  59. int GetLittleLong(void)
  60. {
  61.     int val = 0;
  62.     val = *data_p;
  63.     val = val + (*(data_p+1)<<8);
  64.     val = val + (*(data_p+2)<<16);
  65.     val = val + (*(data_p+3)<<24);
  66.     data_p += 4;
  67.     return val;
  68. }
  69.  
  70. void FindNextChunk(char *name)
  71. {
  72.     while (1)
  73.     {
  74.         data_p=last_chunk;
  75.  
  76.         if (data_p >= iff_end)
  77.         {    // didn't find the chunk
  78.             data_p = NULL;
  79.             return;
  80.         }
  81.         
  82.         data_p += 4;
  83.         iff_chunk_len = GetLittleLong();
  84.         if (iff_chunk_len < 0)
  85.         {
  86.             data_p = NULL;
  87.             return;
  88.         }
  89. //        if (iff_chunk_len > 1024*1024)
  90. //            Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
  91.         data_p -= 8;
  92.         last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
  93.         if (!strncmp(data_p, name, 4))
  94.             return;
  95.     }
  96. }
  97.  
  98. void FindChunk(char *name)
  99. {
  100.     last_chunk = iff_data;
  101.     FindNextChunk (name);
  102. }
  103.  
  104.  
  105. void DumpChunks(void)
  106. {
  107.     char    str[5];
  108.     
  109.     str[4] = 0;
  110.     data_p=iff_data;
  111.     do
  112.     {
  113.         memcpy (str, data_p, 4);
  114.         data_p += 4;
  115.         iff_chunk_len = GetLittleLong();
  116.         printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
  117.         data_p += (iff_chunk_len + 1) & ~1;
  118.     } while (data_p < iff_end);
  119. }
  120.  
  121. /*
  122. ============
  123. GetWavinfo
  124. ============
  125. */
  126. wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
  127. {
  128.     wavinfo_t    info;
  129.     int     i;
  130.     int     format;
  131.     int        samples;
  132.  
  133.     memset (&info, 0, sizeof(info));
  134.  
  135.     if (!wav)
  136.         return info;
  137.         
  138.     iff_data = wav;
  139.     iff_end = wav + wavlength;
  140.  
  141. // find "RIFF" chunk
  142.     FindChunk("RIFF");
  143.     if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))
  144.     {
  145.         printf("Missing RIFF/WAVE chunks\n");
  146.         return info;
  147.     }
  148.  
  149. // get "fmt " chunk
  150.     iff_data = data_p + 12;
  151. // DumpChunks ();
  152.  
  153.     FindChunk("fmt ");
  154.     if (!data_p)
  155.     {
  156.         printf("Missing fmt chunk\n");
  157.         return info;
  158.     }
  159.     data_p += 8;
  160.     format = GetLittleShort();
  161.     if (format != 1)
  162.     {
  163.         printf("Microsoft PCM format only\n");
  164.         return info;
  165.     }
  166.  
  167.     info.channels = GetLittleShort();
  168.     info.rate = GetLittleLong();
  169.     data_p += 4+2;
  170.     info.width = GetLittleShort() / 8;
  171.  
  172. // get cue chunk
  173.     FindChunk("cue ");
  174.     if (data_p)
  175.     {
  176.         data_p += 32;
  177.         info.loopstart = GetLittleLong();
  178. //        Com_Printf("loopstart=%d\n", sfx->loopstart);
  179.  
  180.     // if the next chunk is a LIST chunk, look for a cue length marker
  181.         FindNextChunk ("LIST");
  182.         if (data_p)
  183.         {
  184.             if (!strncmp (data_p + 28, "mark", 4))
  185.             {    // this is not a proper parse, but it works with cooledit...
  186.                 data_p += 24;
  187.                 i = GetLittleLong ();    // samples in loop
  188.                 info.samples = info.loopstart + i;
  189.             }
  190.         }
  191.     }
  192.     else
  193.         info.loopstart = -1;
  194.  
  195. // find data chunk
  196.     FindChunk("data");
  197.     if (!data_p)
  198.     {
  199.         printf("Missing data chunk\n");
  200.         return info;
  201.     }
  202.  
  203.     data_p += 4;
  204.     samples = GetLittleLong ();
  205.  
  206.     if (info.samples)
  207.     {
  208.         if (samples < info.samples)
  209.             Error ("Sound %s has a bad loop length", name);
  210.     }
  211.     else
  212.         info.samples = samples;
  213.  
  214.     info.dataofs = data_p - wav;
  215.  
  216.     return info;
  217. }
  218.  
  219. //=====================================================================
  220.  
  221. /*
  222. ==============
  223. LoadSoundtrack
  224. ==============
  225. */
  226. void LoadSoundtrack (void)
  227. {
  228.     char    name[1024];
  229.     FILE    *f;
  230.     int        len;
  231.     int     i, val, j;
  232.  
  233.     s_soundtrack = NULL;
  234.     sprintf (name, "%svideo/%s/%s.wav", gamedir, s_base, s_base);
  235.     printf ("WAV: %s\n", name);
  236.     f = fopen (name, "rb");
  237.     if (!f)
  238.     {
  239.         printf ("no soundtrack for %s\n", s_base);
  240.         return;
  241.     }
  242.     len = Q_filelength(f);
  243.     s_soundtrack = malloc(len);
  244.     fread (s_soundtrack, 1, len, f);
  245.     fclose (f);
  246.  
  247.     s_wavinfo = GetWavinfo (name, s_soundtrack, len);
  248.  
  249.     // count samples for compression
  250.     memset (s_samplecounts, 0, sizeof(s_samplecounts));
  251.  
  252.     j = s_wavinfo.samples/2;
  253.     for (i=0 ; i<j ; i++)
  254.     {
  255.         val = ((unsigned short *)( s_soundtrack + s_wavinfo.dataofs))[i];
  256.         s_samplecounts[val]++;
  257.     }
  258.     val = 0;
  259.     for (i=0 ; i<0x10000 ; i++)
  260.         if (s_samplecounts[i])
  261.             val++;
  262.  
  263.     printf ("%i unique sample values\n", val);
  264. }
  265.  
  266. /*
  267. ==================
  268. WriteSound
  269. ==================
  270. */
  271. void WriteSound (FILE *output, int frame)
  272. {
  273.     int        start, end;
  274.     int        count;
  275.     int        empty = 0;
  276.     int        i;
  277.     int        sample;
  278.     int        width;
  279.  
  280.     width = s_wavinfo.width * s_wavinfo.channels;
  281.  
  282.     start = frame*s_wavinfo.rate/14;
  283.     end = (frame+1)*s_wavinfo.rate/14;
  284.     count = end - start;
  285.  
  286.     for (i=0 ; i<count ; i++)
  287.     {
  288.         sample = start+i;
  289.         if (sample > s_wavinfo.samples || !s_soundtrack)
  290.             fwrite (&empty, 1, width, output);
  291.         else
  292.             fwrite (s_soundtrack + s_wavinfo.dataofs + sample*width, 1, width,output);
  293.     }
  294. }
  295.  
  296. //==========================================================================
  297.  
  298. static float s_resampleXRatio;
  299. static float s_resampleYRatio;
  300.  
  301. static void BoxFilterHorizontalElements( unsigned char *dst, unsigned char *src, float s0, float s1 )
  302. {
  303.     float w;
  304.     float rSum = 0, gSum = 0, bSum = 0;
  305.     float x = s0;
  306.     float sumWeight = 0;
  307.  
  308.     for ( x = s0; x < s1; x++, src += 4 )
  309.     {
  310.         if ( x == s0 )
  311.         {
  312.             w = ( int ) ( s0 + 1 ) - x;
  313.         }
  314.         else if ( x + 1 >= s1 )
  315.         {
  316.             w = s1 - ( int ) x;
  317.         }
  318.         else
  319.         {
  320.             w = 1.0f;
  321.         }
  322.  
  323.         rSum += src[0] * w;
  324.         gSum += src[1] * w;
  325.         bSum += src[2] * w;
  326.         sumWeight += w;
  327.     }
  328.  
  329.     rSum /= sumWeight;
  330.     gSum /= sumWeight;
  331.     bSum /= sumWeight;
  332.  
  333.     dst[0] = ( unsigned char ) ( rSum + 0.5 );
  334.     dst[1] = ( unsigned char ) ( gSum + 0.5 );
  335.     dst[2] = ( unsigned char ) ( bSum + 0.5 );
  336. }
  337.  
  338. static void BoxFilterVerticalElements( unsigned char *dst, // destination of the filter process
  339.                                        unsigned char *src, // source pixels
  340.                                        int srcStep,           // stride of the source pixels
  341.                                        float s0, float s1 )
  342. {
  343.     float w;
  344.     float rSum = 0, gSum = 0, bSum = 0;
  345.     float y = s0;
  346.     float sumWeight = 0;
  347.  
  348.     for ( y = s0; y < ( int ) ( s1 + 1 ) ; y++, src += srcStep )
  349.     {
  350.         if ( y == s0 )
  351.         {
  352.             w = ( int ) ( s0 + 1 ) - y;
  353.         }
  354.         else if ( y + 1 >= s1 )
  355.         {
  356.             w = s1 - ( int ) y;
  357.         }
  358.         else
  359.         {
  360.             w = 1.0f;
  361.         }
  362.  
  363.         rSum += src[0] * w;
  364.         gSum += src[1] * w;
  365.         bSum += src[2] * w;
  366.         sumWeight += w;
  367.     }
  368.  
  369.     rSum /= sumWeight;
  370.     gSum /= sumWeight;
  371.     bSum /= sumWeight;
  372.  
  373.     dst[0] = ( unsigned char ) ( rSum + 0.5 );
  374.     dst[1] = ( unsigned char ) ( gSum + 0.5 );
  375.     dst[2] = ( unsigned char ) ( bSum + 0.5 );
  376.     dst[3] = 0xff;
  377.  
  378. }
  379.  
  380. static void BoxFilterRow( unsigned char *dstStart, cblock_t *in, int dstRow, int rowWidth )
  381. {
  382.     int i;
  383.     unsigned char *indata = ( unsigned char * ) in->data;
  384.  
  385.     indata += 4 * dstRow * in->width;
  386.  
  387.     for ( i = 0; i < rowWidth; i++ )
  388.     {
  389.         float c0 = i * s_resampleXRatio;
  390.         float c1 = ( i + 1 ) * s_resampleXRatio;
  391.  
  392.         BoxFilterHorizontalElements( &dstStart[i*4], &indata[( ( int ) c0 ) * 4], c0, c1 );
  393.     }
  394. }
  395.  
  396. static void BoxFilterColumn( unsigned char *dstStart, unsigned char *srcStart, int dstCol, int dstRowWidth, int dstColHeight, int srcRowWidthInPels )
  397. {
  398.     float c0, c1;
  399.     int i;
  400.  
  401.     for ( i = 0; i < dstColHeight; i++ )
  402.     {
  403.         c0 = i * s_resampleYRatio;
  404.         c1 = ( i + 1 ) * s_resampleYRatio;
  405.  
  406.         BoxFilterVerticalElements( &dstStart[i*4*dstRowWidth], &srcStart[(int)c0*srcRowWidthInPels*4], srcRowWidthInPels*4, c0, c1 );
  407.     }
  408. }
  409.  
  410. #define DROP_SAMPLE        0
  411. #define BOX_FILTER        1
  412.  
  413. static void ResampleFrame( cblock_t *in, unsigned char *out, int method, int outWidth, int outHeight )
  414. {
  415.     int row, column;
  416.     unsigned char *indata = ( unsigned char * ) in->data;
  417.  
  418.     s_resampleXRatio = in->width / ( float ) outWidth;
  419.     s_resampleYRatio = in->height / ( float ) outHeight;
  420.  
  421.     if ( method == DROP_SAMPLE )
  422.     {
  423.         for ( row = 0; row < outHeight; row++ )
  424.         {
  425.             int r = ( int ) ( row * s_resampleYRatio );
  426.  
  427.             for ( column = 0; column < outWidth; column++ )
  428.             {
  429.                 int c = ( int ) ( column * s_resampleXRatio );
  430.  
  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;
  435.             }
  436.         }
  437.     }
  438.     else if ( method == BOX_FILTER )
  439.     {
  440.         unsigned char intermediate[1024*1024*4];
  441.  
  442.         assert( in->height <= 1024 );
  443.         assert( in->width <= 1024 );
  444.  
  445.         //
  446.         // filter our M x N source image into a RESAMPLE_WIDTH x N horizontally filtered image
  447.         //
  448.         for ( row = 0; row < in->height; row++ )
  449.         {
  450.             BoxFilterRow( &intermediate[row*4*outWidth], in, row, outWidth );
  451.         }
  452.  
  453.         //
  454.         // filter our RESAMPLE_WIDTH x N horizontally filtered image into a RESAMPLE_WIDTH x RESAMPLE_HEIGHT filtered image
  455.         //
  456.         for ( column = 0; column < outWidth; column++ )
  457.         {
  458.             BoxFilterColumn( &out[column*4], &intermediate[column*4], column, outWidth, outHeight, s_resample_width );
  459.         }
  460.     }
  461. }
  462.  
  463. static float BTCDistanceSquared( float a[3], float b[3] )
  464. {
  465.     return ( b[0] - a[0] ) * ( b[0] - a[0] ) + 
  466.            ( b[1] - a[1] ) * ( b[1] - a[1] ) +
  467.            ( b[2] - a[2] ) * ( b[2] - a[2] );
  468. }
  469.  
  470. static void BTCFindEndpoints( float inBlock[4][4][3], unsigned int endPoints[2][2] )
  471. {
  472.     float longestDistance = -1;
  473.  
  474.     int bX, bY;
  475.  
  476.     //
  477.     // find the two points farthest from each other
  478.     //
  479.     for ( bY = 0; bY < 4; bY++ )
  480.     {
  481.         for ( bX = 0; bX < 4; bX++ )
  482.         {
  483.             int cX, cY;
  484.             float d;
  485.  
  486.             //
  487.             // check the rest of the current row
  488.             //
  489.             for ( cX = bX + 1; cX < 4; cX++ )
  490.             {
  491.                 if ( ( d = BTCDistanceSquared( inBlock[bY][bX], inBlock[bY][cX] ) ) > longestDistance )
  492.                 {
  493.                     longestDistance = d;
  494.                     endPoints[0][0] = bX;
  495.                     endPoints[0][1] = bY;
  496.                     endPoints[1][0] = cX;
  497.                     endPoints[1][1] = bY;
  498.                 }
  499.             }
  500.  
  501.             //
  502.             // check remaining rows and columns
  503.             //
  504.             for ( cY = bY+1; cY < 4; cY++ )
  505.             {
  506.                 for ( cX = 0; cX < 4; cX++ )
  507.                 {
  508.                     if ( ( d = BTCDistanceSquared( inBlock[bY][bX], inBlock[cY][cX] ) ) > longestDistance )
  509.                     {
  510.                         longestDistance = d;
  511.                         endPoints[0][0] = bX;
  512.                         endPoints[0][1] = bY;
  513.                         endPoints[1][0] = cX;
  514.                         endPoints[1][1] = cY;
  515.                     }
  516.                 }
  517.             }
  518.         }
  519.     }
  520. }
  521.  
  522. static float BTCQuantizeBlock( float inBlock[4][4][3], unsigned long endPoints[2][2], int btcQuantizedBlock[4][4], float bestError )
  523. {
  524.     int i;
  525.     int blockY, blockX;
  526.     float dR, dG, dB;
  527.     float R, G, B;
  528.     float error = 0;
  529.     float colorLine[4][3];
  530.  
  531.     //
  532.     // build the color line
  533.     //
  534.     dR = inBlock[endPoints[1][1]][endPoints[1][0]][0] -
  535.          inBlock[endPoints[0][1]][endPoints[0][0]][0];
  536.     dG = inBlock[endPoints[1][1]][endPoints[1][0]][1] -
  537.          inBlock[endPoints[0][1]][endPoints[0][0]][1];
  538.     dB = inBlock[endPoints[1][1]][endPoints[1][0]][2] -
  539.          inBlock[endPoints[0][1]][endPoints[0][0]][2];
  540.  
  541.     dR *= 0.33f;
  542.     dG *= 0.33f;
  543.     dB *= 0.33f;
  544.  
  545.     R = inBlock[endPoints[0][1]][endPoints[0][0]][0];
  546.     G = inBlock[endPoints[0][1]][endPoints[0][0]][1];
  547.     B = inBlock[endPoints[0][1]][endPoints[0][0]][2];
  548.  
  549.     for ( i = 0; i < 4; i++ )
  550.     {
  551.         colorLine[i][0] = R;
  552.         colorLine[i][1] = G;
  553.         colorLine[i][2] = B;
  554.  
  555.         R += dR;
  556.         G += dG;
  557.         B += dB;
  558.     }
  559.  
  560.     //
  561.     // quantize each pixel into the appropriate range
  562.     //
  563.     for ( blockY = 0; blockY < 4; blockY++ )
  564.     {
  565.         for ( blockX = 0; blockX < 4; blockX++ )
  566.         {
  567.             float distance = 10000000000;
  568.             int shortest = -1;
  569.  
  570.             for ( i = 0; i < 4; i++ )
  571.             {
  572.                 float d;
  573.  
  574.                 if ( ( d = BTCDistanceSquared( inBlock[blockY][blockX], colorLine[i] ) ) < distance )
  575.                 {
  576.                     distance = d;
  577.                     shortest = i;
  578.                 }
  579.             }
  580.  
  581.             error += distance;
  582.  
  583.             //
  584.             // if bestError is not -1 then that means this is a speculative quantization
  585.             //
  586.             if ( bestError != -1 )
  587.             {
  588.                 if ( error > bestError )
  589.                     return error;
  590.             }
  591.  
  592.             btcQuantizedBlock[blockY][blockX] = shortest;
  593.         }
  594.     }
  595.  
  596.     return error;
  597. }
  598.  
  599. /*
  600. ** float BTCCompressBlock
  601. */
  602. static float BTCCompressBlock( float inBlock[4][4][3], unsigned long out[2] )
  603. {
  604.     int i;
  605.     int btcQuantizedBlock[4][4];    // values should be [0..3]
  606.     unsigned long encodedEndPoints, encodedBitmap;
  607.     unsigned int endPoints[2][2];        // endPoints[0] = color start, endPoints[1] = color end
  608.     int blockY, blockX;
  609.     float error = 0;
  610.     float bestError = 10000000000;
  611.     unsigned int bestEndPoints[2][2];
  612.  
  613. #if 0
  614.     //
  615.     // find the "ideal" end points for the color vector 
  616.     //
  617.     BTCFindEndpoints( inBlock, endPoints );
  618.     error = BTCQuantizeBlock( inBlock, endPoints, btcQuantizedBlock );
  619.     memcpy( bestEndPoints, endPoints, sizeof( bestEndPoints ) );
  620. #else
  621.     for ( blockY = 0; blockY < 4; blockY++ )
  622.     {
  623.         for ( blockX = 0; blockX < 4; blockX++ )
  624.         {
  625.             int x2, y2;
  626.  
  627.             for ( y2 = 0; y2 < 4; y2++ )
  628.             {
  629.                 for ( x2 = 0; x2 < 4; x2++ )
  630.                 {
  631.                     if ( ( x2 == blockX ) && ( y2 == blockY ) )
  632.                         continue;
  633.  
  634.                     endPoints[0][0] = blockX;
  635.                     endPoints[0][1] = blockY;
  636.                     endPoints[1][0] = x2;
  637.                     endPoints[1][1] = y2;
  638.  
  639.                     error = BTCQuantizeBlock( inBlock, endPoints, btcQuantizedBlock, -1 ); //bestError );
  640.  
  641.                     if ( error < bestError )
  642.                     {
  643.                         bestError = error;
  644.                         memcpy( bestEndPoints, endPoints, sizeof( bestEndPoints ) );
  645.                     }
  646.                 }
  647.             }
  648.         }
  649.     }
  650.  
  651.     error = BTCQuantizeBlock( inBlock, bestEndPoints, btcQuantizedBlock, -1.0f );
  652. #endif
  653.  
  654.     //
  655.     // encode the results
  656.     //
  657.     encodedBitmap = 0;
  658.     for ( blockY = 0; blockY < 4; blockY++ )
  659.     {
  660.         for ( blockX = 0; blockX < 4; blockX++ )
  661.         {
  662.             int shift = ( blockX + blockY * 4 ) * 2;
  663.             encodedBitmap |= btcQuantizedBlock[blockY][blockX] << shift;
  664.         }
  665.     }
  666.  
  667.     //
  668.     // encode endpoints
  669.     //
  670.     encodedEndPoints = 0;
  671.     for ( i = 0; i < 2; i++ )
  672.     {
  673.         int iR, iG, iB;
  674.  
  675.         iR = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][0] );
  676.         if ( iR > 255 ) 
  677.             iR = 255;
  678.         else if ( iR < 0 ) 
  679.             iR = 0;
  680.         iR >>= 3;
  681.  
  682.         iG = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][1] );
  683.         if ( iG > 255 )
  684.             iG = 255;
  685.         else if ( iG < 0 )
  686.             iG = 0;
  687.         iG >>= 2;
  688.  
  689.         iB = ( ( int ) inBlock[bestEndPoints[i][1]][bestEndPoints[i][0]][2] );
  690.         if ( iB > 255 )
  691.             iB = 255;
  692.         else if ( iB < 0 )
  693.             iB = 0;
  694.         iB >>= 3;
  695.  
  696.  
  697.         encodedEndPoints |= ( ( ( iR << 11 ) | ( iG << 5 ) | ( iB ) ) << ( i * 16 ) );
  698.     }
  699.  
  700.     //
  701.     // store 
  702.     //
  703.     out[0] = encodedBitmap;
  704.     out[1] = encodedEndPoints;
  705.  
  706.     return error;
  707. }
  708.  
  709. /*
  710. ** void BTCDecompressFrame
  711. */
  712. static void BTCDecompressFrame( unsigned long *src, unsigned char *dst )
  713. {
  714.     int x, y;
  715.     int iR, iG, iB;
  716.     int dstX, dstY;
  717.     float colorStart[3], colorEnd[3];
  718.     unsigned char colorRampABGR[4][4];
  719.     unsigned encoded;
  720.  
  721.     memset( colorRampABGR, 0xff, sizeof( colorRampABGR ) );
  722.  
  723.     for ( y = 0; y < s_resample_height / 4; y++ )
  724.     {
  725.         for ( x = 0; x < s_resample_width / 4; x++ )
  726.         {
  727.             unsigned colorStartPacked = src[(y*s_resample_width/4 + x)*2 + 1] & 0xffff;
  728.             unsigned colorEndPacked = src[(y*s_resample_width/4 + x)*2 + 1] >> 16;
  729.  
  730.             //
  731.             // grab the end points
  732.             //   0 = color start
  733.             //   1 = color end
  734.             //
  735.             iR = ( ( colorStartPacked >> 11 ) & ( ( 1 << 5 ) - 1 ) );
  736.             iR = ( iR << 3 ) | ( iR >> 2 );
  737.             iG = ( ( colorStartPacked >> 5 ) & ( ( 1 << 6 )  - 1 ) );
  738.             iG = ( iG << 2 ) | ( iG >> 4 );
  739.             iB = ( ( colorStartPacked ) & ( ( 1 << 5  ) - 1 ) );
  740.             iB = ( iB << 3 ) | ( iB >> 2 );
  741.  
  742.             colorStart[0] = iR;
  743.             colorStart[1] = iG;
  744.             colorStart[2] = iB;
  745.             colorRampABGR[0][0] = iR;
  746.             colorRampABGR[0][1] = iG;
  747.             colorRampABGR[0][2] = iB;
  748.  
  749.             iR = ( ( colorEndPacked >> 11 ) & ( ( 1 << 5 ) - 1 ) );
  750.             iR = ( iR << 3 ) | ( iR >> 2 );
  751.             iG = ( ( colorEndPacked >> 5 ) & ( ( 1 << 6 )  - 1 ) );
  752.             iG = ( iG << 2 ) | ( iG >> 4 );
  753.             iB = ( colorEndPacked & ( ( 1 << 5  ) - 1 ) );
  754.             iB = ( iB << 3 ) | ( iB >> 2 );
  755.  
  756.             colorEnd[0] = iR;
  757.             colorEnd[1] = iG;
  758.             colorEnd[2] = iB;
  759.             colorRampABGR[3][0] = iR;
  760.             colorRampABGR[3][1] = iG;
  761.             colorRampABGR[3][2] = iB;
  762.             
  763.             //
  764.             // compute this block's color ramp
  765.             // FIXME: This needs to be reversed on big-endian machines
  766.             //
  767.             
  768.             colorRampABGR[1][0] = colorStart[0] * 0.66f + colorEnd[0] * 0.33f;
  769.             colorRampABGR[1][1] = colorStart[1] * 0.66f + colorEnd[1] * 0.33f;
  770.             colorRampABGR[1][2] = colorStart[2] * 0.66f + colorEnd[2] * 0.33f;
  771.  
  772.             colorRampABGR[2][0] = colorStart[0] * 0.33f + colorEnd[0] * 0.66f;
  773.             colorRampABGR[2][1] = colorStart[1] * 0.33f + colorEnd[1] * 0.66f;
  774.             colorRampABGR[2][2] = colorStart[2] * 0.33f + colorEnd[2] * 0.66f;
  775.  
  776.             //
  777.             // decode the color data
  778.             // information is encoded in 2-bit pixels, with low order bits corresponding
  779.             // to upper left pixels.  These 2-bit values are indexed into the block's
  780.             // computer color ramp.
  781.             //
  782.             encoded = src[(y*s_resample_width/4 + x)*2 + 0];
  783.  
  784.             for ( dstY = 0; dstY < 4; dstY++ )
  785.             {
  786.                 for ( dstX = 0; dstX < 4; dstX++ )
  787.                 {
  788.                     memcpy( &dst[(y*4+dstY)*s_resample_width*4+x*4*4+dstX*4], colorRampABGR[encoded&3], sizeof( colorRampABGR[0] ) );
  789.                     encoded >>= 2;
  790.                 }
  791.             }
  792.         }
  793.     }
  794. }
  795.  
  796. /*
  797. ** BTCCompressFrame
  798. **
  799. ** Perform a BTC compression using a 2-bit encoding at each pixel.  This
  800. ** compression method is performed by decomposing the incoming image into
  801. ** a sequence of 4x4 blocks.  At each block two color values are computed
  802. ** that define the endpoints of a vector in color space that represent
  803. ** the two colors "farthest apart".
  804. */
  805. static float BTCCompressFrame( unsigned char *src, unsigned long *dst )
  806. {
  807.     int x, y;
  808.     int bX, bY;
  809.     float btcBlock[4][4][3];
  810.  
  811.     float error = 0;
  812.     
  813.     for ( y = 0; y < s_resample_height / 4; y++ )
  814.     {
  815.         for ( x = 0; x < s_resample_width / 4; x++ )
  816.         {
  817.             //
  818.             // fill in the BTC block with raw values
  819.             //
  820.             for ( bY = 0; bY < 4; bY++ )
  821.             {
  822.                 for ( bX = 0; bX < 4; bX++ )
  823.                 {
  824.                     btcBlock[bY][bX][0] = src[(y*4+bY)*s_resample_width*4 + (x*4+bX)*4 + 0];
  825.                     btcBlock[bY][bX][1] = src[(y*4+bY)*s_resample_width*4 + (x*4+bX)*4 + 1];
  826.                     btcBlock[bY][bX][2] = src[(y*4+bY)*s_resample_width*4 + (x*4+bX)*4 + 2];
  827.                 }
  828.             }
  829.  
  830.             error += BTCCompressBlock( btcBlock, &dst[(y*s_resample_width/4+x)*2] );
  831.         }
  832.     }
  833.  
  834.     return error / ( ( s_resample_width / 4 ) * ( s_resample_height / 4 ) );
  835. }
  836.  
  837. /*
  838. ===================
  839. LoadFrame
  840. ===================
  841. */
  842. cblock_t LoadFrame (char *base, int frame, int digits, byte **palette)
  843. {
  844.     int            ten3, ten2, ten1, ten0;
  845.     cblock_t    in;
  846.     int            width, height;
  847.     char        name[1024];
  848.     FILE        *f;
  849.  
  850.     in.data = NULL;
  851.     in.count = -1;
  852.  
  853.     ten3 = frame/1000;
  854.     ten2 = (frame-ten3*1000)/100;
  855.     ten1 = (frame-ten3*1000-ten2*100)/10;
  856.     ten0 = frame%10;
  857.  
  858.     if (digits == 4)
  859.         sprintf (name, "%svideo/%s/%s%i%i%i%i.tga", gamedir, base, base, ten3, ten2, ten1, ten0);
  860.     else
  861.         sprintf (name, "%svideo/%s/%s%i%i%i.tga", gamedir, base, base, ten2, ten1, ten0);
  862.  
  863.     f = fopen(name, "rb");
  864.     if (!f)
  865.     {
  866.         in.data = NULL;
  867.         return in;
  868.     }
  869.     fclose (f);
  870.  
  871.     printf ("%s", name);
  872.     LoadTGA( name, ( unsigned char ** ) &in.data, &width, &height );
  873.     if ( palette )
  874.         *palette = 0;
  875. //    Load256Image (name, &in.data, palette, &width, &height);
  876.     in.count = width*height;
  877.     in.width = width;
  878.     in.height = height;
  879. // FIXME: map 0 and 255!
  880.  
  881. #if 0
  882.     // rle compress
  883.     rle = RLE(in);
  884.     free (in.data);
  885.  
  886.     return rle;
  887. #endif
  888.  
  889.     return in;
  890. }
  891.  
  892. /*
  893. ===============
  894. Cmd_Video
  895.  
  896. video <directory> <framedigits>
  897. ===============
  898. */
  899. void Cmd_Video (void)
  900. {
  901.     float sumError = 0, error = 0, maxError = 0;
  902.     char    savename[1024];
  903.     char    name[1024];
  904.     FILE    *output;
  905.     int        startframe, frame;
  906.     int        width, height;
  907.     int        i;
  908.     int        digits;
  909.     int        minutes;
  910.     float    fseconds;
  911.     int        remSeconds;
  912.     cblock_t    in;
  913.     unsigned char *resampled;
  914.     unsigned long *compressed;
  915.     clock_t start, stop;
  916.  
  917.     GetToken (qfalse);
  918.     strcpy (s_base, token);
  919.     if (g_release)
  920.     {
  921. //        sprintf (savename, "video/%s.cin", token);
  922. //        ReleaseFile (savename);
  923.         return;
  924.     }
  925.  
  926.     GetToken( qfalse );
  927.     strcpy( s_output_base, token );
  928.  
  929.     GetToken (qfalse);
  930.     digits = atoi(token);
  931.  
  932.     GetToken( qfalse );
  933.  
  934.     if ( !strcmp( token, "btc" ) )
  935.     {
  936.         s_compression_method = BTC_COMPRESSION;
  937.         printf( "Compression: BTC\n" );
  938.     }
  939.     else if ( !strcmp( token, "uc" ) )
  940.     {
  941.         s_compression_method = UNCOMPRESSED;
  942.         printf( "Compression: none\n" );
  943.     }
  944.     else
  945.     {
  946.         Error( "Uknown compression method '%s'\n", token );
  947.     }
  948.  
  949.     GetToken( qfalse );
  950.     s_resample_width = atoi( token );
  951.  
  952.     GetToken( qfalse );
  953.     s_resample_height = atoi( token );
  954.  
  955.     resampled = malloc( sizeof( unsigned char ) * 4 * s_resample_width * s_resample_height );
  956.     compressed = malloc( sizeof( long ) * 2 * ( s_resample_width / 4 ) * ( s_resample_height / 4 ) );
  957.  
  958.     printf( "Resample width: %d\n", s_resample_width );
  959.     printf( "Resample height: %d\n", s_resample_height );
  960.  
  961.     // optionally skip frames
  962.     if (TokenAvailable ())
  963.     {
  964.         GetToken (qfalse);
  965.         startframe = atoi(token);
  966.     }
  967.     else
  968.         startframe=0;
  969.  
  970.     sprintf (savename, "%svideo/%s.%s", writedir, s_output_base, CIN_EXTENSION );
  971.  
  972.     // load the entire sound wav file if present
  973.     LoadSoundtrack ();
  974.  
  975.     if (digits == 4)
  976.         sprintf (name, "%svideo/%s/%s0000.tga", gamedir, s_base, s_base);
  977.     else
  978.         sprintf (name, "%svideo/%s/%s000.tga", gamedir, s_base, s_base);
  979.  
  980.     printf ("%s\n", name);
  981.     LoadTGA( name, NULL, &width, &height);
  982.  
  983.     output = fopen (savename, "wb");
  984.     if (!output)
  985.         Error ("Can't open %s", savename);
  986.  
  987.     // write header info
  988.     i = LittleLong( CIN_SIGNATURE );
  989.     fwrite (&i, 4, 1, output );
  990.     i = LittleLong (s_resample_width);
  991.     fwrite (&i, 4, 1, output);
  992.     i = LittleLong (s_resample_height);
  993.     fwrite (&i, 4, 1, output);
  994.     i = LittleLong (s_wavinfo.rate);
  995.     fwrite (&i, 4, 1, output);
  996.     i = LittleLong (s_wavinfo.width);
  997.     fwrite (&i, 4, 1, output);
  998.     i = LittleLong (s_wavinfo.channels);
  999.     fwrite (&i, 4, 1, output);
  1000.     i = LittleLong ( s_compression_method );
  1001.     fwrite (&i, 4, 1, output );
  1002.  
  1003.     start = clock();
  1004.  
  1005.     // perform compression on a per frame basis
  1006.     for ( frame=startframe ;  ; frame++)
  1007.     {
  1008.         printf ("%02d: ", frame);
  1009.         in = LoadFrame (s_base, frame, digits, 0 );
  1010.         if (!in.data)
  1011.             break;
  1012.  
  1013.         ResampleFrame( &in, ( unsigned char * ) resampled, BOX_FILTER, s_resample_width, s_resample_height );
  1014.  
  1015.         if ( s_compression_method == UNCOMPRESSED )
  1016.         {
  1017.             printf( "\n" );
  1018.             fwrite( resampled, 1, sizeof( unsigned char ) * s_resample_width * s_resample_height * 4, output );
  1019.  
  1020. #if OUTPUT_TGAS
  1021.             {
  1022.                 int x, y;
  1023.                 char buffer[1000];
  1024.  
  1025.                 for ( y = 0; y < s_resample_height/2; y++ )
  1026.                 {
  1027.                     for ( x = 0; x < s_resample_width; x++ )
  1028.                     {
  1029.                         unsigned char tmp[4];
  1030.  
  1031.                         tmp[0] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0];
  1032.                         tmp[1] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1];
  1033.                         tmp[2] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2];
  1034.                         tmp[3] = resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3];
  1035.  
  1036.                         resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0] = resampled[y*s_resample_width*4 + x*4 + 0];
  1037.                         resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1] = resampled[y*s_resample_width*4 + x*4 + 1];
  1038.                         resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2] = resampled[y*s_resample_width*4 + x*4 + 2];
  1039.                         resampled[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3] = resampled[y*s_resample_width*4 + x*4 + 3];
  1040.  
  1041.                         resampled[y*s_resample_width*4 + x*4 + 0] = tmp[0];
  1042.                         resampled[y*s_resample_width*4 + x*4 + 1] = tmp[1];
  1043.                         resampled[y*s_resample_width*4 + x*4 + 2] = tmp[2];
  1044.                         resampled[y*s_resample_width*4 + x*4 + 3] = tmp[3];
  1045.                     }
  1046.                 }
  1047.  
  1048.                 sprintf( buffer, "%svideo/%s/uc%04d.tga", gamedir, s_base, frame );
  1049.                 WriteTGA( buffer, resampled, s_resample_width, s_resample_height );
  1050.             }
  1051. #endif
  1052.         }
  1053.         else if ( s_compression_method == BTC_COMPRESSION )
  1054.         {
  1055.             error = BTCCompressFrame( resampled, compressed );
  1056.  
  1057.             sumError += error;
  1058.  
  1059.             if ( error > maxError ) 
  1060.                 maxError = error;
  1061.  
  1062.             printf( " (error = %f)\n", error );
  1063.             fwrite( compressed, 1, 2 * sizeof( long ) * ( s_resample_width / 4 ) * ( s_resample_height / 4 ), output );
  1064.  
  1065. #if OUTPUT_TGAS
  1066.             {
  1067.                 int x, y;
  1068.                 unsigned char *uncompressed;
  1069.                 char buffer[1000];
  1070.  
  1071.                 uncompressed = malloc( sizeof( unsigned char ) * 4 * s_resample_width * s_resample_height );
  1072.                 BTCDecompressFrame( compressed, uncompressed );
  1073.  
  1074.                 for ( y = 0; y < s_resample_height/2; y++ )
  1075.                 {
  1076.                     for ( x = 0; x < s_resample_width; x++ )
  1077.                     {
  1078.                         unsigned char tmp[4];
  1079.  
  1080.                         tmp[0] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0];
  1081.                         tmp[1] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1];
  1082.                         tmp[2] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2];
  1083.                         tmp[3] = uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3];
  1084.  
  1085.                         uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 0] = uncompressed[y*s_resample_width*4 + x*4 + 0];
  1086.                         uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 1] = uncompressed[y*s_resample_width*4 + x*4 + 1];
  1087.                         uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 2] = uncompressed[y*s_resample_width*4 + x*4 + 2];
  1088.                         uncompressed[(s_resample_height-1-y)*s_resample_width*4 + x*4 + 3] = uncompressed[y*s_resample_width*4 + x*4 + 3];
  1089.  
  1090.                         uncompressed[y*s_resample_width*4 + x*4 + 0] = tmp[0];
  1091.                         uncompressed[y*s_resample_width*4 + x*4 + 1] = tmp[1];
  1092.                         uncompressed[y*s_resample_width*4 + x*4 + 2] = tmp[2];
  1093.                         uncompressed[y*s_resample_width*4 + x*4 + 3] = tmp[3];
  1094.                     }
  1095.                 }
  1096.  
  1097.  
  1098.                 sprintf( buffer, "%svideo/%s/btc%04d.tga", gamedir, s_base, frame );
  1099.                 WriteTGA( buffer, uncompressed, s_resample_width, s_resample_height );
  1100.  
  1101.                 free( uncompressed );
  1102.             }
  1103. #endif
  1104.         }
  1105.  
  1106.         WriteSound( output, frame );
  1107.  
  1108.         free (in.data);
  1109.     }
  1110.     stop = clock();
  1111.  
  1112.     printf ("\n");
  1113.  
  1114.     printf ("Total size: %i\n", ftell( output ) );
  1115.     printf ("Average error: %f\n", sumError / ( frame - startframe ) );
  1116.     printf ("Max error: %f\n", maxError );
  1117.  
  1118.     fseconds = ( stop - start ) / 1000.0f;
  1119.     minutes = fseconds / 60;
  1120.     remSeconds = fseconds - minutes * 60;
  1121.  
  1122.     printf ("Total time: %d s (%d m %d s)\n", ( int ) fseconds, minutes, remSeconds );
  1123.     printf ("Time/frame: %.2f seconds\n", fseconds / ( frame - startframe ) );
  1124.  
  1125.     fclose (output);
  1126.  
  1127.     if ( s_soundtrack )
  1128.     {
  1129.         free( s_soundtrack );
  1130.         s_soundtrack = 0;
  1131.     }
  1132. }
  1133.