📄 video.c
字号:
#include <assert.h>
#include "q3data.h"
static int s_resample_width = 256;
static int s_resample_height = 256;
#define OUTPUT_TGAS 1
#define UNCOMPRESSED 0
#define BTC_COMPRESSION 1
static int s_compression_method = BTC_COMPRESSION;
static const char *CIN_EXTENSION = "cn2";
static const int CIN_SIGNATURE = ( 'C' << 24 ) | ( 'I' << 16 ) | ( 'N' << 8 ) | ( '2' );
static byte *s_soundtrack;
static char s_base[32];
static char s_output_base[32];
/*
===============================================================================
WAV loading
===============================================================================
*/
typedef struct
{
int rate;
int width;
int channels;
int loopstart;
int samples;
int dataofs; // chunk starts this many bytes from file start
} wavinfo_t;
byte *data_p;
byte *iff_end;
byte *last_chunk;
byte *iff_data;
int iff_chunk_len;
static int s_samplecounts[0x10000];
static wavinfo_t s_wavinfo;
short GetLittleShort(void)
{
short val = 0;
val = *data_p;
val = val + (*(data_p+1)<<8);
data_p += 2;
return val;
}
int GetLittleLong(void)
{
int val = 0;
val = *data_p;
val = val + (*(data_p+1)<<8);
val = val + (*(data_p+2)<<16);
val = val + (*(data_p+3)<<24);
data_p += 4;
return val;
}
void FindNextChunk(char *name)
{
while (1)
{
data_p=last_chunk;
if (data_p >= iff_end)
{ // didn't find the chunk
data_p = NULL;
return;
}
data_p += 4;
iff_chunk_len = GetLittleLong();
if (iff_chunk_len < 0)
{
data_p = NULL;
return;
}
// if (iff_chunk_len > 1024*1024)
// Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
data_p -= 8;
last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
if (!strncmp(data_p, name, 4))
return;
}
}
void FindChunk(char *name)
{
last_chunk = iff_data;
FindNextChunk (name);
}
void DumpChunks(void)
{
char str[5];
str[4] = 0;
data_p=iff_data;
do
{
memcpy (str, data_p, 4);
data_p += 4;
iff_chunk_len = GetLittleLong();
printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
data_p += (iff_chunk_len + 1) & ~1;
} while (data_p < iff_end);
}
/*
============
GetWavinfo
============
*/
wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
{
wavinfo_t info;
int i;
int format;
int samples;
memset (&info, 0, sizeof(info));
if (!wav)
return info;
iff_data = wav;
iff_end = wav + wavlength;
// find "RIFF" chunk
FindChunk("RIFF");
if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))
{
printf("Missing RIFF/WAVE chunks\n");
return info;
}
// get "fmt " chunk
iff_data = data_p + 12;
// DumpChunks ();
FindChunk("fmt ");
if (!data_p)
{
printf("Missing fmt chunk\n");
return info;
}
data_p += 8;
format = GetLittleShort();
if (format != 1)
{
printf("Microsoft PCM format only\n");
return info;
}
info.channels = GetLittleShort();
info.rate = GetLittleLong();
data_p += 4+2;
info.width = GetLittleShort() / 8;
// get cue chunk
FindChunk("cue ");
if (data_p)
{
data_p += 32;
info.loopstart = GetLittleLong();
// Com_Printf("loopstart=%d\n", sfx->loopstart);
// if the next chunk is a LIST chunk, look for a cue length marker
FindNextChunk ("LIST");
if (data_p)
{
if (!strncmp (data_p + 28, "mark", 4))
{ // this is not a proper parse, but it works with cooledit...
data_p += 24;
i = GetLittleLong (); // samples in loop
info.samples = info.loopstart + i;
}
}
}
else
info.loopstart = -1;
// find data chunk
FindChunk("data");
if (!data_p)
{
printf("Missing data chunk\n");
return info;
}
data_p += 4;
samples = GetLittleLong ();
if (info.samples)
{
if (samples < info.samples)
Error ("Sound %s has a bad loop length", name);
}
else
info.samples = samples;
info.dataofs = data_p - wav;
return info;
}
//=====================================================================
/*
==============
LoadSoundtrack
==============
*/
void LoadSoundtrack (void)
{
char name[1024];
FILE *f;
int len;
int i, val, j;
s_soundtrack = NULL;
sprintf (name, "%svideo/%s/%s.wav", gamedir, s_base, s_base);
printf ("WAV: %s\n", name);
f = fopen (name, "rb");
if (!f)
{
printf ("no soundtrack for %s\n", s_base);
return;
}
len = Q_filelength(f);
s_soundtrack = malloc(len);
fread (s_soundtrack, 1, len, f);
fclose (f);
s_wavinfo = GetWavinfo (name, s_soundtrack, len);
// count samples for compression
memset (s_samplecounts, 0, sizeof(s_samplecounts));
j = s_wavinfo.samples/2;
for (i=0 ; i<j ; i++)
{
val = ((unsigned short *)( s_soundtrack + s_wavinfo.dataofs))[i];
s_samplecounts[val]++;
}
val = 0;
for (i=0 ; i<0x10000 ; i++)
if (s_samplecounts[i])
val++;
printf ("%i unique sample values\n", val);
}
/*
==================
WriteSound
==================
*/
void WriteSound (FILE *output, int frame)
{
int start, end;
int count;
int empty = 0;
int i;
int sample;
int width;
width = s_wavinfo.width * s_wavinfo.channels;
start = frame*s_wavinfo.rate/14;
end = (frame+1)*s_wavinfo.rate/14;
count = end - start;
for (i=0 ; i<count ; i++)
{
sample = start+i;
if (sample > s_wavinfo.samples || !s_soundtrack)
fwrite (&empty, 1, width, output);
else
fwrite (s_soundtrack + s_wavinfo.dataofs + sample*width, 1, width,output);
}
}
//==========================================================================
static float s_resampleXRatio;
static float s_resampleYRatio;
static void BoxFilterHorizontalElements( unsigned char *dst, unsigned char *src, float s0, float s1 )
{
float w;
float rSum = 0, gSum = 0, bSum = 0;
float x = s0;
float sumWeight = 0;
for ( x = s0; x < s1; x++, src += 4 )
{
if ( x == s0 )
{
w = ( int ) ( s0 + 1 ) - x;
}
else if ( x + 1 >= s1 )
{
w = s1 - ( int ) x;
}
else
{
w = 1.0f;
}
rSum += src[0] * w;
gSum += src[1] * w;
bSum += src[2] * w;
sumWeight += w;
}
rSum /= sumWeight;
gSum /= sumWeight;
bSum /= sumWeight;
dst[0] = ( unsigned char ) ( rSum + 0.5 );
dst[1] = ( unsigned char ) ( gSum + 0.5 );
dst[2] = ( unsigned char ) ( bSum + 0.5 );
}
static void BoxFilterVerticalElements( unsigned char *dst, // destination of the filter process
unsigned char *src, // source pixels
int srcStep, // stride of the source pixels
float s0, float s1 )
{
float w;
float rSum = 0, gSum = 0, bSum = 0;
float y = s0;
float sumWeight = 0;
for ( y = s0; y < ( int ) ( s1 + 1 ) ; y++, src += srcStep )
{
if ( y == s0 )
{
w = ( int ) ( s0 + 1 ) - y;
}
else if ( y + 1 >= s1 )
{
w = s1 - ( int ) y;
}
else
{
w = 1.0f;
}
rSum += src[0] * w;
gSum += src[1] * w;
bSum += src[2] * w;
sumWeight += w;
}
rSum /= sumWeight;
gSum /= sumWeight;
bSum /= sumWeight;
dst[0] = ( unsigned char ) ( rSum + 0.5 );
dst[1] = ( unsigned char ) ( gSum + 0.5 );
dst[2] = ( unsigned char ) ( bSum + 0.5 );
dst[3] = 0xff;
}
static void BoxFilterRow( unsigned char *dstStart, cblock_t *in, int dstRow, int rowWidth )
{
int i;
unsigned char *indata = ( unsigned char * ) in->data;
indata += 4 * dstRow * in->width;
for ( i = 0; i < rowWidth; i++ )
{
float c0 = i * s_resampleXRatio;
float c1 = ( i + 1 ) * s_resampleXRatio;
BoxFilterHorizontalElements( &dstStart[i*4], &indata[( ( int ) c0 ) * 4], c0, c1 );
}
}
static void BoxFilterColumn( unsigned char *dstStart, unsigned char *srcStart, int dstCol, int dstRowWidth, int dstColHeight, int srcRowWidthInPels )
{
float c0, c1;
int i;
for ( i = 0; i < dstColHeight; i++ )
{
c0 = i * s_resampleYRatio;
c1 = ( i + 1 ) * s_resampleYRatio;
BoxFilterVerticalElements( &dstStart[i*4*dstRowWidth], &srcStart[(int)c0*srcRowWidthInPels*4], srcRowWidthInPels*4, c0, c1 );
}
}
#define DROP_SAMPLE 0
#define BOX_FILTER 1
static void ResampleFrame( cblock_t *in, unsigned char *out, int method, int outWidth, int outHeight )
{
int row, column;
unsigned char *indata = ( unsigned char * ) in->data;
s_resampleXRatio = in->width / ( float ) outWidth;
s_resampleYRatio = in->height / ( float ) outHeight;
if ( method == DROP_SAMPLE )
{
for ( row = 0; row < outHeight; row++ )
{
int r = ( int ) ( row * s_resampleYRatio );
for ( column = 0; column < outWidth; column++ )
{
int c = ( int ) ( column * s_resampleXRatio );
out[(row*outWidth+column)*4+0] = indata[(r*in->width+c)*4+0];
out[(row*outWidth+column)*4+1] = indata[(r*in->width+c)*4+1];
out[(row*outWidth+column)*4+2] = indata[(r*in->width+c)*4+2];
out[(row*outWidth+column)*4+3] = 0xff;
}
}
}
else if ( method == BOX_FILTER )
{
unsigned char intermediate[1024*1024*4];
assert( in->height <= 1024 );
assert( in->width <= 1024 );
//
// filter our M x N source image into a RESAMPLE_WIDTH x N horizontally filtered image
//
for ( row = 0; row < in->height; row++ )
{
BoxFilterRow( &intermediate[row*4*outWidth], in, row, outWidth );
}
//
// filter our RESAMPLE_WIDTH x N horizontally filtered image into a RESAMPLE_WIDTH x RESAMPLE_HEIGHT filtered image
//
for ( column = 0; column < outWidth; column++ )
{
BoxFilterColumn( &out[column*4], &intermediate[column*4], column, outWidth, outHeight, s_resample_width );
}
}
}
static float BTCDistanceSquared( float a[3], float b[3] )
{
return ( b[0] - a[0] ) * ( b[0] - a[0] ) +
( b[1] - a[1] ) * ( b[1] - a[1] ) +
( b[2] - a[2] ) * ( b[2] - a[2] );
}
static void BTCFindEndpoints( float inBlock[4][4][3], unsigned int endPoints[2][2] )
{
float longestDistance = -1;
int bX, bY;
//
// find the two points farthest from each other
//
for ( bY = 0; bY < 4; bY++ )
{
for ( bX = 0; bX < 4; bX++ )
{
int cX, cY;
float d;
//
// check the rest of the current row
//
for ( cX = bX + 1; cX < 4; cX++ )
{
if ( ( d = BTCDistanceSquared( inBlock[bY][bX], inBlock[bY][cX] ) ) > longestDistance )
{
longestDistance = d;
endPoints[0][0] = bX;
endPoints[0][1] = bY;
endPoints[1][0] = cX;
endPoints[1][1] = bY;
}
}
//
// check remaining rows and columns
//
for ( cY = bY+1; cY < 4; cY++ )
{
for ( cX = 0; cX < 4; cX++ )
{
if ( ( d = BTCDistanceSquared( inBlock[bY][bX], inBlock[cY][cX] ) ) > longestDistance )
{
longestDistance = d;
endPoints[0][0] = bX;
endPoints[0][1] = bY;
endPoints[1][0] = cX;
endPoints[1][1] = cY;
}
}
}
}
}
}
static float BTCQuantizeBlock( float inBlock[4][4][3], unsigned long endPoints[2][2], int btcQuantizedBlock[4][4], float bestError )
{
int i;
int blockY, blockX;
float dR, dG, dB;
float R, G, B;
float error = 0;
float colorLine[4][3];
//
// build the color line
//
dR = inBlock[endPoints[1][1]][endPoints[1][0]][0] -
inBlock[endPoints[0][1]][endPoints[0][0]][0];
dG = inBlock[endPoints[1][1]][endPoints[1][0]][1] -
inBlock[endPoints[0][1]][endPoints[0][0]][1];
dB = inBlock[endPoints[1][1]][endPoints[1][0]][2] -
inBlock[endPoints[0][1]][endPoints[0][0]][2];
dR *= 0.33f;
dG *= 0.33f;
dB *= 0.33f;
R = inBlock[endPoints[0][1]][endPoints[0][0]][0];
G = inBlock[endPoints[0][1]][endPoints[0][0]][1];
B = inBlock[endPoints[0][1]][endPoints[0][0]][2];
for ( i = 0; i < 4; i++ )
{
colorLine[i][0] = R;
colorLine[i][1] = G;
colorLine[i][2] = B;
R += dR;
G += dG;
B += dB;
}
//
// quantize each pixel into the appropriate range
//
for ( blockY = 0; blockY < 4; blockY++ )
{
for ( blockX = 0; blockX < 4; blockX++ )
{
float distance = 10000000000;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -