From 0605cf082acb466ef480dd4fd4a3476c8d0f28c8 Mon Sep 17 00:00:00 2001 From: vortex Date: Sat, 19 Mar 2011 20:47:35 +0000 Subject: [PATCH] Added JAM video decoder plugin, used by Blood Omnicide git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@10938 d7cf8633-e32d-0410-b094-e92efae38249 --- cl_video.c | 4 +- cl_video_jamdecode.c | 355 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 357 insertions(+), 2 deletions(-) create mode 100644 cl_video_jamdecode.c diff --git a/cl_video.c b/cl_video.c index 2db4756e..28be575a 100644 --- a/cl_video.c +++ b/cl_video.c @@ -5,9 +5,9 @@ #include "dpvsimpledecode.h" // VorteX: JAM video module used by Blood Omnicide -//#define USEJAM +#define USEJAM #ifdef USEJAM - #include "jamdecode.c" + #include "cl_video_jamdecode.c" #endif // cvars diff --git a/cl_video_jamdecode.c b/cl_video_jamdecode.c new file mode 100644 index 00000000..eb6f922e --- /dev/null +++ b/cl_video_jamdecode.c @@ -0,0 +1,355 @@ +// JAM format decoder, used by Blood Omnicide + +typedef struct jamdecodestream_s +{ + int error; + + qfile_t *file; + double info_framerate; + unsigned int info_frames; + unsigned int info_imagewidth; + unsigned int info_imageheight; + int doubleres; + float colorscale; + unsigned char colorsub; + float stipple; + + // info used durign decoding + unsigned char *videopixels; + unsigned char *compressed; + unsigned char *framedata; + unsigned char *prevframedata; + unsigned char colormap[768]; + unsigned int framesize; + unsigned int framenum; + + // channel the sound file is being played on + int sndchan; +} +jamdecodestream_t; + +#define JAMDECODEERROR_NONE 0 +#define JAMDECODEERROR_EOF 1 +#define JAMDECODEERROR_READERROR 2 +#define JAMDECODEERROR_BAD_FRAME_HEADER 3 +#define JAMDECODEERROR_BAD_OUTPUT_SIZE 4 +#define JAMDECODEERROR_BAD_COLORMAP 5 + +// opens a stream +void jam_close(void *stream); +unsigned int jam_getwidth(void *stream); +unsigned int jam_getheight(void *stream); +double jam_getframerate(void *stream); +int jam_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow); +void *jam_open(clvideo_t *video, char *filename, const char **errorstring) +{ + unsigned char jamHead[16]; + char *wavename; + jamdecodestream_t *s; + qfile_t *file; + + s = (jamdecodestream_t *)Z_Malloc(sizeof(jamdecodestream_t)); + if (s != NULL) + { + if ((file = FS_OpenVirtualFile(filename, false))) + { + s->file = file; + if (FS_Read(s->file, &jamHead, 16)) + { + if (!memcmp(jamHead, "JAM", 3)) + { + s->info_imagewidth = LittleLong(*(jamHead + 4)); + s->info_imageheight = LittleLong(*(jamHead + 8)); + s->info_frames = LittleLong(*(jamHead + 12)); + s->info_framerate = 15; + s->doubleres = 0; + s->colorscale = 0.70; + s->colorsub = 8; + s->stipple = 0.4; + s->framesize = s->info_imagewidth * s->info_imageheight; + if (s->framesize > 0) + { + s->compressed = (unsigned char *)Z_Malloc(s->framesize); + s->framedata = (unsigned char *)Z_Malloc(s->framesize * 2); + s->prevframedata = (unsigned char *)Z_Malloc(s->framesize * 2); + s->videopixels = (unsigned char *)Z_Malloc(s->framesize * 4); // bgra, doubleres + if (s->compressed != NULL && s->framedata != NULL && s->prevframedata != NULL && s->videopixels != NULL) + { + size_t namelen; + + namelen = strlen(filename) + 10; + wavename = (char *)Z_Malloc(namelen); + if (wavename) + { + sfx_t* sfx; + + FS_StripExtension(filename, wavename, namelen); + strlcat(wavename, ".wav", namelen); + sfx = S_PrecacheSound(wavename, false, false); + if (sfx != NULL) + s->sndchan = S_StartSound (-1, 0, sfx, vec3_origin, 1.0f, 0); + else + s->sndchan = -1; + Z_Free(wavename); + } + // all is well... + // set the module functions + s->framenum = 0; + video->close = jam_close; + video->getwidth = jam_getwidth; + video->getheight = jam_getheight; + video->getframerate = jam_getframerate; + video->decodeframe = jam_video; + return s; + } + else if (errorstring != NULL) + *errorstring = "unable to allocate memory for stream info structure"; + if (s->compressed != NULL) + Z_Free(s->compressed); + if (s->framedata != NULL) + Z_Free(s->framedata); + if (s->prevframedata != NULL) + Z_Free(s->prevframedata); + if (s->videopixels != NULL) + Z_Free(s->videopixels); + } + else if (errorstring != NULL) + *errorstring = "bad framesize"; + } + else if (errorstring != NULL) + *errorstring = "not JAM videofile"; + } + else if (errorstring != NULL) + *errorstring = "unexpected EOF"; + FS_Close(file); + } + else if (errorstring != NULL) + *errorstring = "unable to open videofile"; + Z_Free(s); + } + else if (errorstring != NULL) + *errorstring = "unable to allocate memory for stream info structure"; + return NULL; +} + +// closes a stream +void jam_close(void *stream) +{ + jamdecodestream_t *s = (jamdecodestream_t *)stream; + if (s == NULL) + return; + Z_Free(s->compressed); + Z_Free(s->framedata); + Z_Free(s->prevframedata); + Z_Free(s->videopixels); + if (s->sndchan != -1) + S_StopChannel(s->sndchan, true); + if (s->file) + FS_Close(s->file); + Z_Free(s); +} + +// returns the width of the image data +unsigned int jam_getwidth(void *stream) +{ + jamdecodestream_t *s = (jamdecodestream_t *)stream; + if (s->doubleres) + return s->info_imagewidth * 2; + return s->info_imagewidth; +} + +// returns the height of the image data +unsigned int jam_getheight(void *stream) +{ + jamdecodestream_t *s = (jamdecodestream_t *)stream; + if (s->doubleres) + return s->info_imageheight * 2; + return s->info_imageheight; +} + +// returns the framerate of the stream +double jam_getframerate(void *stream) +{ + jamdecodestream_t *s = (jamdecodestream_t *)stream; + return s->info_framerate; +} + + +// decode JAM frame +void jam_decodeframe(unsigned char *inbuf, unsigned char *outbuf, unsigned char *prevbuf, int outsize, int frametype) +{ + unsigned char *srcptr, *destptr, *prevptr; + int bytesleft; + unsigned int mark; + unsigned short int bits; + int rep; + int backoffs; + unsigned char *back; + int i; + + srcptr = inbuf; + destptr = outbuf; + prevptr = prevbuf; + bytesleft = outsize; + + if (frametype == 2) + { + memcpy(outbuf, inbuf, outsize); + return; + } + while(bytesleft > 0) + { + memcpy(&mark, srcptr, 4); + srcptr += 4; + for(i=0; i<32 && bytesleft > 0; i++,mark=mark>>1) + { + if(mark & 1) + { + *destptr = *srcptr; + destptr ++; + prevptr ++; + srcptr ++; + bytesleft --; + } + else + { + bits = srcptr[0] + 256*srcptr[1]; + rep = (bits >> 11) + 3; + if(frametype == 1) + { + backoffs = 0x821 - (bits & 0x7ff); + back = destptr - backoffs; + } + else + { + backoffs = 0x400 - (bits & 0x7ff); + back = prevptr - backoffs; + } + srcptr += 2; + memcpy(destptr, back, rep); + destptr += rep; + prevptr += rep; + bytesleft -= rep; + } + } + } +} + +// decodes a video frame to the supplied output pixels +int jam_video(void *stream, void *imagedata, unsigned int Rmask, unsigned int Gmask, unsigned int Bmask, unsigned int bytesperpixel, int imagebytesperrow) +{ + unsigned char frameHead[16], *b; + unsigned int compsize, outsize, i, j; + jamdecodestream_t *s = (jamdecodestream_t *)stream; + + s->error = DPVSIMPLEDECODEERROR_NONE; + if (s->framenum < s->info_frames) + { +readframe: + if (FS_Read(s->file, &frameHead, 16)) + { + compsize = LittleLong(*(frameHead + 8)) - 16; + outsize = LittleLong(*(frameHead + 12)); + if (compsize < 0 || compsize > s->framesize || outsize < 0 || outsize > s->framesize) + s->error = JAMDECODEERROR_BAD_FRAME_HEADER; + else if (FS_Read(s->file, s->compressed, compsize)) + { + // palette goes interleaved with special flag + if (frameHead[0] == 2) + { + if (compsize == 768) + { + memcpy(s->colormap, s->compressed, 768); + for(i = 0; i < 768; i++) + s->colormap[i] = (unsigned char)(bound(0, (s->colormap[i] * s->colorscale) - s->colorsub, 255)); + goto readframe; + } + //else + // s->error = JAMDECODEERROR_BAD_COLORMAP; + } + else + { + // decode frame + // shift buffers to provide current and previous one, decode + b = s->prevframedata; + s->prevframedata = s->framedata; + s->framedata = b; + jam_decodeframe(s->compressed, s->framedata, s->prevframedata, outsize, frameHead[4]); + // make 32bit imagepixels from 8bit palettized frame + if (s->doubleres) + b = s->videopixels; + else + b = (unsigned char *)imagedata; + for(i = 0; i < s->framesize; i++) + { + // bgra + *b++ = s->colormap[s->framedata[i]*3 + 2]; + *b++ = s->colormap[s->framedata[i]*3 + 1]; + *b++ = s->colormap[s->framedata[i]*3]; + *b++ = 255; + } + // nearest 2x + if (s->doubleres) + { + for (i = 0; i < s->info_imageheight; i++) + { + b = (unsigned char *)imagedata + (s->info_imagewidth*2*4)*(i*2); + for (j = 0; j < s->info_imagewidth; j++) + { + *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4]; + *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1]; + *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2]; + *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3]; + // + *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4]; + *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1]; + *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2]; + *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3]; + } + b = (unsigned char *)imagedata + (s->info_imagewidth*2*4)*(i*2 + 1); + for (j = 0; j < s->info_imagewidth; j++) + { + *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4]; + *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1]; + *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2]; + *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3]; + // + *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4]; + *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 1]; + *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 2]; + *b++ = s->videopixels[i*s->info_imagewidth*4 + j*4 + 3]; + } + } + // do stippling + if (s->stipple) + { + for (i = 0; i < s->info_imageheight; i++) + { + b = (unsigned char *)imagedata + (s->info_imagewidth * 4 * 2 * 2 * i); + for (j = 0; j < s->info_imagewidth; j++) + { + b[0] = b[0] * s->stipple; + b[1] = b[1] * s->stipple; + b[2] = b[2] * s->stipple; + b += 4; + b[0] = b[0] * s->stipple; + b[1] = b[1] * s->stipple; + b[2] = b[2] * s->stipple; + b += 4; + } + } + } + } + + } + } + else + s->error = JAMDECODEERROR_READERROR; + } + else + s->error = JAMDECODEERROR_READERROR; + } + else + s->error = DPVSIMPLEDECODEERROR_EOF; + return s->error; +} \ No newline at end of file -- 2.39.2