/* * Copyright (C) 2002-2009 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 #include #include #include #include #include #include #include "zmbv_dosbox.h" #define DBZV_VERSION_HIGH 0 #define DBZV_VERSION_LOW 1 #define COMPRESSION_NONE 0 #define COMPRESSION_ZLIB 1 #define MAX_VECTOR (128+256) #define Mask_KeyFrame 0x01 #define Mask_DeltaPalette 0x02 zmbv_format_t ZMBV_Codec::BPPFormat(int bpp) { switch (bpp) { case 1: return ZMBV_FORMAT_1BPP; case 2: return ZMBV_FORMAT_2BPP; case 4: return ZMBV_FORMAT_4BPP; case 8: return ZMBV_FORMAT_8BPP; case 15: return ZMBV_FORMAT_15BPP; case 16: return ZMBV_FORMAT_16BPP; case 24: return ZMBV_FORMAT_24BPP; case 32: return ZMBV_FORMAT_32BPP; default: break; } return ZMBV_FORMAT_NONE; } int ZMBV_Codec::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_24BPP:f = 3;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 ZMBV_Codec::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_24BPP: pixelsize = 3; break; case ZMBV_FORMAT_32BPP: pixelsize = 4; break; default: return false; }; bufsize = (height+2*MAX_VECTOR)*pitch*pixelsize+2048; buf1 = (unsigned char*) malloc(bufsize); buf2 = (unsigned char*) malloc(bufsize); work = (unsigned char*) malloc(bufsize); bw = blockwidth; bh = blockheight; 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 = (FrameBlock*) malloc(blockcount * sizeof(FrameBlock)); if (!buf1 || !buf2 || !work || !blocks) { FreeBuffers(); return false; } int y,x,i; i=0; for (y=0;y 8 && ((y+64)%16) != 0) continue; if( abs(y) > 8 && ((x+64)%16) != 0) continue; if(x + bw >= MAX_VECTOR) continue; if(y + bh >= MAX_VECTOR) continue; if(-x >= MAX_VECTOR) continue; if(-y >= MAX_VECTOR) continue; /* // SPECIAL CASE FOR 4240x2400 VIDEO: if(abs(x) >= 4 || abs(y) >= 4) { if(abs(y)%4 != 0 && abs(y)%5 != 0 && abs(y)%12 != 0) continue; if(abs(x)%4 != 0 && abs(x)%5 != 0) continue; } // END SPECIAL CASE */ { CodecVector tmp = {x,y,0}; VectorTable.push_back(tmp); } } std::sort(VectorTable.begin(), VectorTable.end(), NearestVectorsFirst); fprintf(stderr, "ZMBV: %zu motion vectors will be used for testing\n", VectorTable.size()); } template INLINE int ZMBV_Codec::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; const int xskip = (bw+3)/4; const int yskip = (bh+3)/4; for (int y=0;yheight_clamped; y+=yskip) { for (int x=0;xwidth_clamped; x+=xskip) { ret += (pold[x] != pnew[x]); /* int test=0-((pold[x]-pnew[x])&0x00ffffff); ret-=(test>>31); */ } pold+=pitch*yskip; pnew+=pitch*yskip; } return ret; } template INLINE int ZMBV_Codec::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;yheight_clamped;y++) { unsigned nx = block->width_clamped * sizeof(P); const unsigned char* a = (const unsigned char*)pold; const unsigned char* b = (const unsigned char*)pnew; for(unsigned x=0; x= 0x04) if(d >= 0x10) ret += 5; //else if(d >= 0x08) ret += 4; else ret += 3; else if(d >= 0x02) ret += 2; else ret += (d > 0); /*if(d > 0x10) ret += 5; while(d > 0) { ret += 1; d >>= 1; }*/ } pold+=pitch; pnew+=pitch; } return ret; } template INLINE void ZMBV_Codec::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;yheight_clamped;y++) { for (int x=0;xwidth_clamped;x++) { *((P*)&work[workUsed])=pnew[x] ^ pold[x]; workUsed+=sizeof(P); } pold+=pitch; pnew+=pitch; } } template void ZMBV_Codec::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; #pragma omp parallel for ordered schedule(dynamic,1) for (int b=0;b(0,0, block); int possibles = 64; int VectorCount = (int) VectorTable.size(); for (int v=0;v(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

(vx,vy, block); if (testchange(bestvx, bestvy, block); if(bestvx || bestvy) ++n_offseted; else ++n_raw; } else { ++n_copied; } } } } bool ZMBV_Codec::SetupCompress( int _width, int _height ) { width = _width; height = _height; pitch = _width + 2*MAX_VECTOR; format = ZMBV_FORMAT_NONE; if (deflateInit2 (&zstream, 9,Z_DEFLATED, 15, 9,Z_FILTERED) != Z_OK) return false; blockadjust = 12; return true; } bool ZMBV_Codec::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 ZMBV_Codec::PrepareCompressFrame (int flags, zmbv_format_t _format, char * pal, void *writeBuf, int writeSize, int bw_base, int bh_base) { int i; unsigned char *firstByte; int good = int(n_copied + n_offseted); int bad = int(n_raw); bool adjusted = false; while(bw_base * blockadjust >= 256 || bh_base * blockadjust >= 256) { adjusted = true; blockadjust /= 2; } if(bw_base * blockadjust != bw || bh_base * blockadjust != bh) { adjusted = true; } if(good > bad*8) { // Decrease block size, we might be able to compress better if(bad == 0 && blockadjust >= 8) { adjusted = true; blockadjust /= 8; } else if(bad == 0 && blockadjust >= 4) { adjusted = true; blockadjust /= 4; } else if(blockadjust > 4) { adjusted = true; blockadjust /= 2; } else if(blockadjust > 1) { adjusted = true; --blockadjust; } } else if(good < bad) { // Increase block size, we're creating too large data if(n_copied == 0 && n_offseted < n_raw/16 && blockadjust < 8 && bw_base * (blockadjust*4) < 256) { adjusted = true; blockadjust*=4; } else if(blockadjust < 8 && bw_base * (blockadjust*2) < 256) { adjusted = true; blockadjust*=2; } else if(bw_base * (blockadjust+2) < 256) { adjusted = true; blockadjust += 2; } else if(bw_base * (blockadjust+1) < 256) { adjusted = true; ++blockadjust; } } if (_format != format || adjusted) { int w = bw_base * blockadjust; int h = bh_base * blockadjust; if (!SetupBuffers( _format, w,h)) 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 = bw; header->blockheight = bh; 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(); break; case ZMBV_FORMAT_15BPP: case ZMBV_FORMAT_16BPP: AddXorFrame(); break; case ZMBV_FORMAT_24BPP: AddXorFrame(); break; case ZMBV_FORMAT_32BPP: AddXorFrame(); 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); int res2 = compress.writeDone + zstream.total_out; fprintf(stderr, "ZMBV frame (%u-byte pixels, %ux%u, %s, %u raw, %u copied, %u offseted): %u bytes -> %u ;\n", pixelsize, bw,bh, (firstByte & Mask_KeyFrame) ? "keyframe" : "", n_raw, n_copied, n_offseted, (unsigned)workUsed, (unsigned)res2); return res2; } template INLINE void ZMBV_Codec::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;yheight_clamped;y++) { for (int x=0;xwidth_clamped;x++) { pnew[x]=pold[x]^*((P*)&work[workPos]); workPos+=sizeof(P); } pold+=pitch; pnew+=pitch; } } template INLINE void ZMBV_Codec::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;yheight_clamped;y++) { for (int x=0;xwidth_clamped;x++) { pnew[x]=pold[x]; } pold+=pitch; pnew+=pitch; } } template void ZMBV_Codec::UnXorFrame(void) { signed char * vectors=(signed char *)&work[workPos]; workPos=(workPos + blockcount*2 + 3) & ~3; for (int b=0;b> 1; int vy = vectors[b*2+1] >> 1; if (delta) UnXorBlock

(vx,vy,block); else CopyBlock

(vx,vy,block); } } bool ZMBV_Codec::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(); break; case ZMBV_FORMAT_15BPP: case ZMBV_FORMAT_16BPP: UnXorFrame(); break; case ZMBV_FORMAT_32BPP: UnXorFrame(); break; } } return true; } void ZMBV_Codec::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> 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> 2); *w++ = (unsigned char)(((c & 0x07e0) * 0x41) >> 9); *w++ = (unsigned char)(((c & 0xf800) * 0x21) >> 13); } break; case ZMBV_FORMAT_24BPP: for (int j=0;jSetupCompress(_width, _height); } int zmbv_SetupDecompress(zmbv_codec c, int _width, int _height) { return ((ZMBV_Codec*)c)->SetupDecompress(_width, _height); } zmbv_format_t zmbv_BPPFormat( int bpp ) { return ZMBV_Codec::BPPFormat(bpp); } int zmbv_NeededSize(int _width, int _height, zmbv_format_t _format) { return ZMBV_Codec::NeededSize(_width,_height,_format); } void zmbv_CompressLines(zmbv_codec c, int lineCount, void *lineData[]) { ((ZMBV_Codec*)c)->CompressLines(lineCount,lineData); } int zmbv_PrepareCompressFrame(zmbv_codec c, int flags, zmbv_format_t _format, char * pal, void *writeBuf, int writeSize) { return ((ZMBV_Codec*)c)->PrepareCompressFrame(flags,_format,pal,writeBuf,writeSize, 16,16); } int zmbv_FinishCompressFrame(zmbv_codec c) { return ((ZMBV_Codec*)c)->FinishCompressFrame(); } int zmbv_DecompressFrame(zmbv_codec c, void * framedata, int size) { return ((ZMBV_Codec*)c)->DecompressFrame(framedata,size); } void zmbv_Output_UpsideDown_24(zmbv_codec c, void * output) { ((ZMBV_Codec*)c)->Output_UpsideDown_24(output); } void zmbv_erase(zmbv_codec c) { ZMBV_Codec* v = (ZMBV_Codec*) c; v->~ZMBV_Codec(); free(v); }