📄 zmbv.cpp
字号:
/* * Copyright (C) 2002-2007 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <zlib.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <math.h>#include "zmbv.h"#define DBZV_VERSION_HIGH 0#define DBZV_VERSION_LOW 1#define COMPRESSION_NONE 0#define COMPRESSION_ZLIB 1#define MAX_VECTOR 16#define Mask_KeyFrame 0x01#define Mask_DeltaPalette 0x02zmbv_format_t BPPFormat( int bpp ) { switch (bpp) { case 8: return ZMBV_FORMAT_8BPP; case 15: return ZMBV_FORMAT_15BPP; case 16: return ZMBV_FORMAT_16BPP; case 32: return ZMBV_FORMAT_32BPP; } return ZMBV_FORMAT_NONE;}int VideoCodec::NeededSize( int _width, int _height, zmbv_format_t _format) { int f; switch (_format) { case ZMBV_FORMAT_8BPP:f = 1;break; case ZMBV_FORMAT_15BPP:f = 2;break; case ZMBV_FORMAT_16BPP:f = 2;break; case ZMBV_FORMAT_32BPP:f = 4;break; default: return -1; } f = f*_width*_height + 2*(1+(_width/8)) * (1+(_height/8))+1024; return f + f/1000;}bool VideoCodec::SetupBuffers(zmbv_format_t _format, int blockwidth, int blockheight) { FreeBuffers(); palsize = 0; switch (_format) { case ZMBV_FORMAT_8BPP: pixelsize = 1; palsize = 256; break; case ZMBV_FORMAT_15BPP: pixelsize = 2; break; case ZMBV_FORMAT_16BPP: pixelsize = 2; break; case ZMBV_FORMAT_32BPP: pixelsize = 4; break; default: return false; }; bufsize = (height+2*MAX_VECTOR)*pitch*pixelsize+2048; buf1 = new unsigned char[bufsize]; buf2 = new unsigned char[bufsize]; work = new unsigned char[bufsize]; int xblocks = (width/blockwidth); int xleft = width % blockwidth; if (xleft) xblocks++; int yblocks = (height/blockheight); int yleft = height % blockheight; if (yleft) yblocks++; blockcount=yblocks*xblocks; blocks=new FrameBlock[blockcount]; if (!buf1 || !buf2 || !work || !blocks) { FreeBuffers(); return false; } int y,x,i; i=0; for (y=0;y<yblocks;y++) { for (x=0;x<xblocks;x++) { blocks[i].start=((y*blockheight)+MAX_VECTOR)*pitch+ (x*blockwidth)+MAX_VECTOR; if (xleft && x==(xblocks-1)) { blocks[i].dx=xleft; } else { blocks[i].dx=blockwidth; } if (yleft && y==(yblocks-1)) { blocks[i].dy=yleft; } else { blocks[i].dy=blockheight; } i++; } } memset(buf1,0,bufsize); memset(buf2,0,bufsize); memset(work,0,bufsize); oldframe=buf1; newframe=buf2; format = _format; return true;}void VideoCodec::CreateVectorTable(void) { int x,y,s; VectorCount=1; VectorTable[0].x=VectorTable[0].y=0; for (s=1;s<=10;s++) { for (y=0-s;y<=0+s;y++) for (x=0-s;x<=0+s;x++) { if (abs(x)==s || abs(y)==s) { VectorTable[VectorCount].x=x; VectorTable[VectorCount].y=y; VectorCount++; } } }}template<class P>INLINE int VideoCodec::PossibleBlock(int vx,int vy,FrameBlock * block) { int ret=0; P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx; P * pnew=((P*)newframe)+block->start;; for (int y=0;y<block->dy;y+=4) { for (int x=0;x<block->dx;x+=4) { int test=0-((pold[x]-pnew[x])&0x00ffffff); ret-=(test>>31); } pold+=pitch*4; pnew+=pitch*4; } return ret;}template<class P>INLINE int VideoCodec::CompareBlock(int vx,int vy,FrameBlock * block) { int ret=0; P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx; P * pnew=((P*)newframe)+block->start;; for (int y=0;y<block->dy;y++) { for (int x=0;x<block->dx;x++) { int test=0-((pold[x]-pnew[x])&0x00ffffff); ret-=(test>>31); } pold+=pitch; pnew+=pitch; } return ret;}template<class P>INLINE void VideoCodec::AddXorBlock(int vx,int vy,FrameBlock * block) { P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx; P * pnew=((P*)newframe)+block->start; for (int y=0;y<block->dy;y++) { for (int x=0;x<block->dx;x++) { *((P*)&work[workUsed])=pnew[x] ^ pold[x]; workUsed+=sizeof(P); } pold+=pitch; pnew+=pitch; }}template<class P>void VideoCodec::AddXorFrame(void) { int written=0; int lastvector=0; signed char * vectors=(signed char*)&work[workUsed]; /* Align the following xor data on 4 byte boundary*/ workUsed=(workUsed + blockcount*2 +3) & ~3; int totalx=0; int totaly=0; for (int b=0;b<blockcount;b++) { FrameBlock * block=&blocks[b]; int bestvx = 0; int bestvy = 0; int bestchange=CompareBlock<P>(0,0, block); int possibles=64; for (int v=0;v<VectorCount && possibles;v++) { if (bestchange<4) break; int vx = VectorTable[v].x; int vy = VectorTable[v].y; if (PossibleBlock<P>(vx, vy, block) < 4) { possibles--;// if (!possibles) Msg("Ran out of possibles, at %d of %d best %d\n",v,VectorCount,bestchange); int testchange=CompareBlock<P>(vx,vy, block); if (testchange<bestchange) { bestchange=testchange; bestvx = vx; bestvy = vy; } } } vectors[b*2+0]=(bestvx << 1); vectors[b*2+1]=(bestvy << 1); if (bestchange) { vectors[b*2+0]|=1; AddXorBlock<P>(bestvx, bestvy, block); } }}bool VideoCodec::SetupCompress( int _width, int _height ) { width = _width; height = _height; pitch = _width + 2*MAX_VECTOR; format = ZMBV_FORMAT_NONE; if (deflateInit (&zstream, 4) != Z_OK) return false; return true;}bool VideoCodec::SetupDecompress( int _width, int _height) { width = _width; height = _height; pitch = _width + 2*MAX_VECTOR; format = ZMBV_FORMAT_NONE; if (inflateInit (&zstream) != Z_OK) return false; return true;}bool VideoCodec::PrepareCompressFrame(int flags, zmbv_format_t _format, char * pal, void *writeBuf, int writeSize) { int i; unsigned char *firstByte; if (_format != format) { if (!SetupBuffers( _format, 16, 16)) return false; flags|=1; //Force a keyframe } /* replace oldframe with new frame */ unsigned char *copyFrame = newframe; newframe = oldframe; oldframe = copyFrame; compress.linesDone = 0; compress.writeSize = writeSize; compress.writeDone = 1; compress.writeBuf = (unsigned char *)writeBuf; /* Set a pointer to the first byte which will contain info about this frame */ firstByte = compress.writeBuf; *firstByte = 0; //Reset the work buffer workUsed = 0;workPos = 0; if (flags & 1) { /* Make a keyframe */ *firstByte |= Mask_KeyFrame; KeyframeHeader * header = (KeyframeHeader *)(compress.writeBuf + compress.writeDone); header->high_version = DBZV_VERSION_HIGH; header->low_version = DBZV_VERSION_LOW; header->compression = COMPRESSION_ZLIB; header->format = format; header->blockwidth = 16; header->blockheight = 16; compress.writeDone += sizeof(KeyframeHeader); /* Copy the new frame directly over */ if (palsize) { if (pal) memcpy(&palette, pal, sizeof(palette)); else memset(&palette,0, sizeof(palette)); /* keyframes get the full palette */ for (i=0;i<palsize;i++) { work[workUsed++] = palette[i*4+0]; work[workUsed++] = palette[i*4+1]; work[workUsed++] = palette[i*4+2]; } } /* Restart deflate */ deflateReset(&zstream); } else { if (palsize && pal && memcmp(pal, palette, palsize * 4)) { *firstByte |= Mask_DeltaPalette; for(i=0;i<palsize;i++) { work[workUsed++]=palette[i*4+0] ^ pal[i*4+0]; work[workUsed++]=palette[i*4+1] ^ pal[i*4+1]; work[workUsed++]=palette[i*4+2] ^ pal[i*4+2]; } memcpy(&palette,pal, palsize * 4); } } return true;}void VideoCodec::CompressLines(int lineCount, void *lineData[]) { int linePitch = pitch * pixelsize; int lineWidth = width * pixelsize; int i = 0; unsigned char *destStart = newframe + pixelsize*(MAX_VECTOR+(compress.linesDone+MAX_VECTOR)*pitch); while ( i < lineCount && (compress.linesDone < height)) { memcpy(destStart, lineData[i], lineWidth ); destStart += linePitch; i++;compress.linesDone++; }}int VideoCodec::FinishCompressFrame( void ) { unsigned char firstByte = *compress.writeBuf; if (firstByte & Mask_KeyFrame) { int i; /* Add the full frame data */ unsigned char * readFrame = newframe + pixelsize*(MAX_VECTOR+MAX_VECTOR*pitch); for (i=0;i<height;i++) { memcpy(&work[workUsed], readFrame, width*pixelsize); readFrame += pitch*pixelsize; workUsed += width*pixelsize; } } else { /* Add the delta frame data */ switch (format) { case ZMBV_FORMAT_8BPP: AddXorFrame<char>(); break; case ZMBV_FORMAT_15BPP: case ZMBV_FORMAT_16BPP: AddXorFrame<short>(); break; case ZMBV_FORMAT_32BPP: AddXorFrame<long>(); break; } } /* Create the actual frame with compression */ zstream.next_in = (Bytef *)work; zstream.avail_in = workUsed; zstream.total_in = 0; zstream.next_out = (Bytef *)(compress.writeBuf + compress.writeDone); zstream.avail_out = compress.writeSize - compress.writeDone; zstream.total_out = 0; int res = deflate(&zstream, Z_SYNC_FLUSH); return compress.writeDone + zstream.total_out;}template<class P>INLINE void VideoCodec::UnXorBlock(int vx,int vy,FrameBlock * block) { P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx; P * pnew=((P*)newframe)+block->start; for (int y=0;y<block->dy;y++) { for (int x=0;x<block->dx;x++) { pnew[x]=pold[x]^*((P*)&work[workPos]); workPos+=sizeof(P); } pold+=pitch; pnew+=pitch; }}template<class P>INLINE void VideoCodec::CopyBlock(int vx,int vy,FrameBlock * block) { P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx; P * pnew=((P*)newframe)+block->start; for (int y=0;y<block->dy;y++) { for (int x=0;x<block->dx;x++) { pnew[x]=pold[x]; } pold+=pitch; pnew+=pitch; }}template<class P>void VideoCodec::UnXorFrame(void) { signed char * vectors=(signed char *)&work[workPos]; workPos=(workPos + blockcount*2 + 3) & ~3; for (int b=0;b<blockcount;b++) { FrameBlock * block=&blocks[b]; int delta = vectors[b*2+0] & 1; int vx = vectors[b*2+0] >> 1; int vy = vectors[b*2+1] >> 1; if (delta) UnXorBlock<P>(vx,vy,block); else CopyBlock<P>(vx,vy,block); }}bool VideoCodec::DecompressFrame(void * framedata, int size) { unsigned char *data=(unsigned char *)framedata; unsigned char tag;int i; tag = *data++; if (--size<=0) return false; if (tag & Mask_KeyFrame) { KeyframeHeader * header = (KeyframeHeader *)data; size -= sizeof(KeyframeHeader);data += sizeof(KeyframeHeader); if (size<=0) return false; if (header->low_version != DBZV_VERSION_LOW || header->high_version != DBZV_VERSION_HIGH) return false; if (format != (zmbv_format_t)header->format && !SetupBuffers((zmbv_format_t)header->format, header->blockwidth, header->blockheight)) return false; inflateReset(&zstream); } zstream.next_in = (Bytef *)data; zstream.avail_in = size; zstream.total_in = 0; zstream.next_out = (Bytef *)work; zstream.avail_out = bufsize; zstream.total_out = 0; int res = inflate(&zstream, Z_FINISH); workUsed= zstream.total_out; workPos = 0; if (tag & Mask_KeyFrame) { if (palsize) { for (i=0;i<palsize;i++) { palette[i*4+0] = work[workPos++]; palette[i*4+1] = work[workPos++]; palette[i*4+2] = work[workPos++]; } } newframe = buf1; oldframe = buf2; unsigned char * writeframe = newframe + pixelsize*(MAX_VECTOR+MAX_VECTOR*pitch); for (i=0;i<height;i++) { memcpy(writeframe,&work[workPos],width*pixelsize); writeframe+=pitch*pixelsize; workPos+=width*pixelsize; } } else { data = oldframe; oldframe = newframe; newframe = data; if (tag & Mask_DeltaPalette) { for (i=0;i<palsize;i++) { palette[i*4+0] ^= work[workPos++]; palette[i*4+1] ^= work[workPos++]; palette[i*4+2] ^= work[workPos++]; } } switch (format) { case ZMBV_FORMAT_8BPP: UnXorFrame<char>(); break; case ZMBV_FORMAT_15BPP: case ZMBV_FORMAT_16BPP: UnXorFrame<short>(); break; case ZMBV_FORMAT_32BPP: UnXorFrame<long>(); break; } } return true;}void VideoCodec::Output_UpsideDown_24(void *output) { int i; unsigned char *r; unsigned char *w = (unsigned char *)output; int pad = width & 3; for (i=height-1;i>=0;i--) { r = newframe + pixelsize*(MAX_VECTOR+(i+MAX_VECTOR)*pitch); switch (format) { case ZMBV_FORMAT_8BPP: for (int j=0;j<width;j++) { int c=r[j]; *w++=palette[c*4+2]; *w++=palette[c*4+1]; *w++=palette[c*4+0]; } break; case ZMBV_FORMAT_15BPP: for (int j=0;j<width;j++) { unsigned short c = *(unsigned short *)&r[j*2]; *w++ = (unsigned char)(((c & 0x001f) * 0x21) >> 2); *w++ = (unsigned char)(((c & 0x03e0) * 0x21) >> 7); *w++ = (unsigned char)(((c & 0x7c00) * 0x21) >> 12); } break; case ZMBV_FORMAT_16BPP: for (int j=0;j<width;j++) { unsigned short c = *(unsigned short *)&r[j*2]; *w++ = (unsigned char)(((c & 0x001f) * 0x21) >> 2); *w++ = (unsigned char)(((c & 0x07e0) * 0x41) >> 9); *w++ = (unsigned char)(((c & 0xf800) * 0x21) >> 13); } break; case ZMBV_FORMAT_32BPP: for (int j=0;j<width;j++) { *w++ = r[j*4+0]; *w++ = r[j*4+1]; *w++ = r[j*4+2]; } break; } // Maintain 32-bit alignment for scanlines. w += pad; }}void VideoCodec::FreeBuffers(void) { if (blocks) { delete[] blocks;blocks=0; } if (buf1) { delete[] buf1;buf1=0; } if (buf2) { delete[] buf2;buf2=0; } if (work) { delete[] work;work=0; }}VideoCodec::VideoCodec() { CreateVectorTable(); blocks = 0; buf1 = 0; buf2 = 0; work = 0; memset( &zstream, 0, sizeof(zstream));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -