// This file is a part of Mesen // It is a heavily modified version of the zmbv.h/cpp file found in DOSBox's code. /* * Copyright (C) 2002-2011 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 "stdafx.h" #include #include #include #include #include "miniz.h" #include "ZmbvCodec.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 0x02 int ZmbvCodec::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 ZmbvCodec::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 INLINE int ZmbvCodec::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;ydy;y+=4) { for (int x=0;xdx;x+=4) { int test=0-((pold[x]-pnew[x])&0x00ffffff); ret-=(test>>31); } pold+=pitch*4; pnew+=pitch*4; } return ret; } template INLINE int ZmbvCodec::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;ydy;y++) { for (int x=0;xdx;x++) { int test=0-((pold[x]-pnew[x])&0x00ffffff); ret-=(test>>31); } pold+=pitch; pnew+=pitch; } return ret; } template INLINE void ZmbvCodec::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;ydy;y++) { for (int x=0;xdx;x++) { *((P*)&work[workUsed])=pnew[x] ^ pold[x]; workUsed+=sizeof(P); } pold+=pitch; pnew+=pitch; } } template void ZmbvCodec::AddXorFrame(void) { signed char * vectors=(signed char*)&work[workUsed]; /* Align the following xor data on 4 byte boundary*/ workUsed=(workUsed + blockcount*2 +3) & ~3; for (int b=0;b(0,0, block); int possibles=64; for (int v=0;v(vx, vy, block) < 4) { possibles--; int testchange=CompareBlock

(vx,vy, block); if (testchange(bestvx, bestvy, block); } } } bool ZmbvCodec::SetupCompress( int _width, int _height, uint32_t compressionLevel ) { width = _width; height = _height; pitch = _width + 2*MAX_VECTOR; format = ZMBV_FORMAT_NONE; if (deflateInit (&zstream, compressionLevel) != Z_OK) return false; return true; } bool ZmbvCodec::PrepareCompressFrame(int flags, zmbv_format_t _format, char * pal) { 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; compressInfo.linesDone = 0; compressInfo.writeSize = _bufSize; compressInfo.writeDone = 1; compressInfo.writeBuf = (unsigned char *)_buf; /* Set a pointer to the first byte which will contain info about this frame */ firstByte = compressInfo.writeBuf; *firstByte = 0; //Reset the work buffer workUsed = 0;workPos = 0; if (flags & 1) { /* Make a keyframe */ *firstByte |= Mask_KeyFrame; KeyframeHeader * header = (KeyframeHeader *)(compressInfo.writeBuf + compressInfo.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; compressInfo.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; default: 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 *)(compressInfo.writeBuf + compressInfo.writeDone); zstream.avail_out = compressInfo.writeSize - compressInfo.writeDone; zstream.total_out = 0; deflate(&zstream, Z_SYNC_FLUSH); *compressedData = _buf; return compressInfo.writeDone + zstream.total_out; } void ZmbvCodec::FreeBuffers() { if (blocks) { delete[] blocks; blocks= nullptr; } if (buf1) { delete[] buf1; buf1= nullptr; } if (buf2) { delete[] buf2; buf2= nullptr; } if (work) { delete[] work; work= nullptr; } if(_buf) { delete[] _buf; _buf = nullptr; } } ZmbvCodec::ZmbvCodec() { CreateVectorTable(); blocks = nullptr; buf1 = nullptr; buf2 = nullptr; work = nullptr; memset( &zstream, 0, sizeof(zstream)); } int ZmbvCodec::CompressFrame(bool isKeyFrame, uint8_t *frameData, uint8_t** compressedData) { if(!PrepareCompressFrame(isKeyFrame ? 1 : 0, ZMBV_FORMAT_32BPP, nullptr)) { return -1; } for(int i = 0; i < height; i++) { void * rowPointer = frameData + i*width*4; CompressLines(1, &rowPointer); } return FinishCompressFrame(compressedData); } const char* ZmbvCodec::GetFourCC() { return "ZMBV"; }