DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
src/libs/zmbv/zmbv.cpp
00001 /*
00002  *  Copyright (C) 2002-2013  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 #include <zlib.h>
00020 #include <stdio.h>
00021 #include <stdlib.h>
00022 #include <string.h>
00023 #include <math.h>
00024 
00025 #include "zmbv.h"
00026 
00027 #define DBZV_VERSION_HIGH 0
00028 #define DBZV_VERSION_LOW 1
00029 
00030 #define COMPRESSION_NONE 0
00031 #define COMPRESSION_ZLIB 1
00032 
00033 #define MAX_VECTOR      16
00034 
00035 #define Mask_KeyFrame                   0x01
00036 #define Mask_DeltaPalette               0x02
00037 
00038 zmbv_format_t BPPFormat( int bpp ) {
00039         switch (bpp) {
00040         case 8:
00041                 return ZMBV_FORMAT_8BPP;
00042         case 15:
00043                 return ZMBV_FORMAT_15BPP;
00044         case 16:
00045                 return ZMBV_FORMAT_16BPP;
00046         case 32:
00047                 return ZMBV_FORMAT_32BPP;
00048         }
00049         return ZMBV_FORMAT_NONE;
00050 }
00051 int VideoCodec::NeededSize( int _width, int _height, zmbv_format_t _format) {
00052         int f;
00053         switch (_format) {
00054         case ZMBV_FORMAT_8BPP:f = 1;break;
00055         case ZMBV_FORMAT_15BPP:f = 2;break;
00056         case ZMBV_FORMAT_16BPP:f = 2;break;
00057         case ZMBV_FORMAT_32BPP:f = 4;break;
00058         default:
00059                 return -1;
00060         }
00061         f = f*_width*_height + 2*(1+(_width/8)) * (1+(_height/8))+1024;
00062         return f + f/1000;
00063 }
00064 
00065 bool VideoCodec::SetupBuffers(zmbv_format_t _format, int blockwidth, int blockheight) {
00066         FreeBuffers();
00067         palsize = 0;
00068         switch (_format) {
00069         case ZMBV_FORMAT_8BPP:
00070                 pixelsize = 1;
00071                 palsize = 256;
00072                 break;
00073         case ZMBV_FORMAT_15BPP:
00074                 pixelsize = 2;
00075                 break;
00076         case ZMBV_FORMAT_16BPP:
00077                 pixelsize = 2;
00078                 break;
00079         case ZMBV_FORMAT_32BPP:
00080                 pixelsize = 4;
00081                 break;
00082         default:
00083                 return false;
00084         };
00085         bufsize = (height+2*MAX_VECTOR)*pitch*pixelsize+2048;
00086 
00087         buf1 = new unsigned char[bufsize];
00088         buf2 = new unsigned char[bufsize];
00089         work = new unsigned char[bufsize];
00090 
00091         int xblocks = (width/blockwidth);
00092         int xleft = width % blockwidth;
00093         if (xleft) xblocks++;
00094         int yblocks = (height/blockheight);
00095         int yleft = height % blockheight;
00096         if (yleft) yblocks++;
00097         blockcount=yblocks*xblocks;
00098         blocks=new FrameBlock[blockcount];
00099 
00100         if (!buf1 || !buf2 || !work || !blocks) {
00101                 FreeBuffers();
00102                 return false;
00103         }
00104         int y,x,i;
00105         i=0;
00106         for (y=0;y<yblocks;y++) {
00107                 for (x=0;x<xblocks;x++) {
00108                         blocks[i].start=((y*blockheight)+MAX_VECTOR)*pitch+
00109                                 (x*blockwidth)+MAX_VECTOR;
00110                         if (xleft && x==(xblocks-1)) {
00111                 blocks[i].dx=xleft;
00112                         } else {
00113                                 blocks[i].dx=blockwidth;
00114                         }
00115                         if (yleft && y==(yblocks-1)) {
00116                 blocks[i].dy=yleft;
00117                         } else {
00118                                 blocks[i].dy=blockheight;
00119                         }
00120                         i++;
00121                 }
00122         }
00123 
00124         memset(buf1,0,(unsigned int)bufsize);
00125         memset(buf2,0,(unsigned int)bufsize);
00126         memset(work,0,(unsigned int)bufsize);
00127         oldframe=buf1;
00128         newframe=buf2;
00129         format = _format;
00130         return true;
00131 }
00132 
00133 void VideoCodec::CreateVectorTable(void) {
00134         int x,y,s;
00135         VectorCount=1;
00136 
00137         VectorTable[0].x=VectorTable[0].y=0;
00138         for (s=1;s<=10;s++) {
00139                 for (y=0-s;y<=0+s;y++) for (x=0-s;x<=0+s;x++) {
00140                         if (abs(x)==s || abs(y)==s) {
00141                                 VectorTable[VectorCount].x=x;
00142                                 VectorTable[VectorCount].y=y;
00143                                 VectorCount++;
00144                         }
00145                 }
00146         }
00147 }
00148 
00149 template<class P>
00150 INLINE int VideoCodec::PossibleBlock(int vx,int vy,FrameBlock * block) {
00151         int ret=0;
00152         P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx;
00153         P * pnew=((P*)newframe)+block->start;;  
00154         for (int y=0;y<block->dy;y+=4) {
00155                 for (int x=0;x<block->dx;x+=4) {
00156                         int test=0-(int)((pold[x]-pnew[x])&0x00ffffffu);
00157                         ret-=(test>>31);
00158                 }
00159                 pold+=pitch*4;
00160                 pnew+=pitch*4;
00161         }
00162         return ret;
00163 }
00164 
00165 template<class P>
00166 INLINE int VideoCodec::CompareBlock(int vx,int vy,FrameBlock * block) {
00167         int ret=0;
00168         P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx;
00169         P * pnew=((P*)newframe)+block->start;;  
00170         for (int y=0;y<block->dy;y++) {
00171                 for (int x=0;x<block->dx;x++) {
00172                         int test=0-(int)((pold[x]-pnew[x])&0x00ffffffu);
00173                         ret-=(test>>31);
00174                 }
00175                 pold+=pitch;
00176                 pnew+=pitch;
00177         }
00178         return ret;
00179 }
00180 
00181 template<class P>
00182 INLINE void VideoCodec::AddXorBlock(int vx,int vy,FrameBlock * block) {
00183         P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx;
00184         P * pnew=((P*)newframe)+block->start;
00185         for (int y=0;y<block->dy;y++) {
00186                 for (int x=0;x<block->dx;x++) {
00187                         *((P*)&work[workUsed])=pnew[x] ^ pold[x];
00188                         workUsed+=(int)sizeof(P);
00189                 }
00190                 pold+=pitch;
00191                 pnew+=pitch;
00192         }
00193 }
00194 
00195 template<class P>
00196 void VideoCodec::AddXorFrame(void) {
00197 //      int written=0;
00198 //      int lastvector=0;
00199         signed char * vectors=(signed char*)&work[workUsed];
00200         /* Align the following xor data on 4 byte boundary*/
00201         workUsed=(workUsed + blockcount*2 +3) & ~3;
00202 //      int totalx=0;
00203 //      int totaly=0;
00204         for (int b=0;b<blockcount;b++) {
00205                 FrameBlock * block=&blocks[b];
00206                 int bestvx = 0;
00207                 int bestvy = 0;
00208                 int bestchange=CompareBlock<P>(0,0, block);
00209                 int possibles=64;
00210                 for (int v=0;v<VectorCount && possibles;v++) {
00211                         if (bestchange<4) break;
00212                         int vx = VectorTable[v].x;
00213                         int vy = VectorTable[v].y;
00214                         if (PossibleBlock<P>(vx, vy, block) < 4) {
00215                                 possibles--;
00216 //                              if (!possibles) Msg("Ran out of possibles, at %d of %d best %d\n",v,VectorCount,bestchange);
00217                                 int testchange=CompareBlock<P>(vx,vy, block);
00218                                 if (testchange<bestchange) {
00219                                         bestchange=testchange;
00220                                         bestvx = vx;
00221                                         bestvy = vy;
00222                                 }
00223                         }
00224                 }
00225                 vectors[b*2+0]=(bestvx << 1);
00226                 vectors[b*2+1]=(bestvy << 1);
00227                 if (bestchange) {
00228                         vectors[b*2+0]|=1;
00229                         AddXorBlock<P>(bestvx, bestvy, block);
00230                 }
00231         }
00232 }
00233 
00234 bool VideoCodec::SetupCompress( int _width, int _height ) {
00235         width = _width;
00236         height = _height;
00237         pitch = _width + 2*MAX_VECTOR;
00238         format = ZMBV_FORMAT_NONE;
00239         if (deflateInit (&zstream, 4) != Z_OK)
00240                 return false;
00241         return true;
00242 }
00243 
00244 bool VideoCodec::SetupDecompress( int _width, int _height) {
00245         width = _width;
00246         height = _height;
00247         pitch = _width + 2*MAX_VECTOR;
00248         format = ZMBV_FORMAT_NONE;
00249         if (inflateInit (&zstream) != Z_OK)
00250                 return false;
00251         return true;
00252 }
00253 
00254 bool VideoCodec::PrepareCompressFrame(int flags,  zmbv_format_t _format, char * pal, void *writeBuf, int writeSize) {
00255         int i;
00256         unsigned char *firstByte;
00257 
00258         if (_format != format) {
00259                 if (!SetupBuffers( _format, 16, 16))
00260                         return false;
00261                 flags|=1;       //Force a keyframe
00262         }
00263         /* replace oldframe with new frame */
00264         unsigned char *copyFrame = newframe;
00265         newframe = oldframe;
00266         oldframe = copyFrame;
00267 
00268         compress.linesDone = 0;
00269         compress.writeSize = writeSize;
00270         compress.writeDone = 1;
00271         compress.writeBuf = (unsigned char *)writeBuf;
00272         /* Set a pointer to the first byte which will contain info about this frame */
00273         firstByte = compress.writeBuf;
00274         *firstByte = 0;
00275         //Reset the work buffer
00276         workUsed = 0;workPos = 0;
00277         if (flags & 1) {
00278                 /* Make a keyframe */
00279                 *firstByte |= Mask_KeyFrame;
00280                 KeyframeHeader * header = (KeyframeHeader *)(compress.writeBuf + compress.writeDone);
00281                 header->high_version = DBZV_VERSION_HIGH;
00282                 header->low_version = DBZV_VERSION_LOW;
00283                 header->compression = COMPRESSION_ZLIB;
00284                 header->format = format;
00285                 header->blockwidth = 16;
00286                 header->blockheight = 16;
00287                 compress.writeDone += (int)sizeof(KeyframeHeader);
00288                 /* Copy the new frame directly over */
00289                 if (palsize) {
00290                         if (pal)
00291                                 memcpy(&palette, pal, sizeof(palette));
00292                         else 
00293                                 memset(&palette,0, sizeof(palette));
00294                         /* keyframes get the full palette */
00295                         for (i=0;i<palsize;i++) {
00296                                 work[workUsed++] = (unsigned char)palette[i*4+0];
00297                                 work[workUsed++] = (unsigned char)palette[i*4+1];
00298                                 work[workUsed++] = (unsigned char)palette[i*4+2];
00299                         }
00300                 }
00301                 /* Restart deflate */
00302                 deflateReset(&zstream);
00303         } else {
00304                 if (palsize && pal && memcmp(pal, palette, (unsigned int)palsize * 4u)) {
00305                         *firstByte |= Mask_DeltaPalette;
00306                         for(i=0;i<palsize;i++) {
00307                                 work[workUsed++]=(unsigned char)palette[i*4+0] ^ (unsigned char)pal[i*4+0];
00308                                 work[workUsed++]=(unsigned char)palette[i*4+1] ^ (unsigned char)pal[i*4+1];
00309                                 work[workUsed++]=(unsigned char)palette[i*4+2] ^ (unsigned char)pal[i*4+2];
00310                         }
00311                         memcpy(&palette,pal, (unsigned int)palsize * 4u);
00312                 }
00313         }
00314         return true;
00315 }
00316 
00317 void VideoCodec::CompressLines(int lineCount, void *lineData[]) {
00318         int linePitch = pitch * pixelsize;
00319         int lineWidth = width * pixelsize;
00320         int i = 0;
00321         unsigned char *destStart = newframe + pixelsize*(MAX_VECTOR+(compress.linesDone+MAX_VECTOR)*pitch);
00322         while ( i < lineCount && (compress.linesDone < height)) {
00323                 memcpy(destStart, lineData[i], (size_t)lineWidth );
00324                 destStart += linePitch;
00325                 i++;compress.linesDone++;
00326         }
00327 }
00328 
00329 int VideoCodec::FinishCompressFrame( void ) {
00330         unsigned char firstByte = *compress.writeBuf;
00331         if (firstByte & Mask_KeyFrame) {
00332                 int i;
00333                 /* Add the full frame data */
00334                 unsigned char * readFrame = newframe + pixelsize*(MAX_VECTOR+MAX_VECTOR*pitch); 
00335                 for (i=0;i<height;i++) {
00336                         memcpy(&work[workUsed], readFrame, (unsigned int)width * (unsigned int)pixelsize);
00337                         readFrame += pitch*pixelsize;
00338                         workUsed += width*pixelsize;
00339                 }
00340         } else {
00341                 /* Add the delta frame data */
00342                 switch (format) {
00343                 case ZMBV_FORMAT_8BPP:
00344                         AddXorFrame<uint8_t>();
00345                         break;
00346                 case ZMBV_FORMAT_15BPP:
00347                 case ZMBV_FORMAT_16BPP:
00348                         AddXorFrame<uint16_t>();
00349                         break;
00350                 case ZMBV_FORMAT_32BPP:
00351                         AddXorFrame<uint32_t>();
00352                         break;
00353                 default:
00354                         break;
00355                 }
00356         }
00357         /* Create the actual frame with compression */
00358         zstream.next_in = (Bytef *)work;
00359         zstream.avail_in = (unsigned int)workUsed;
00360         zstream.total_in = 0;
00361 
00362         zstream.next_out = (Bytef *)(compress.writeBuf + compress.writeDone);
00363         zstream.avail_out = (unsigned int)compress.writeSize - (unsigned int)compress.writeDone;
00364         zstream.total_out = 0;
00365         deflate(&zstream, Z_SYNC_FLUSH);
00366         return (int)compress.writeDone + (int)zstream.total_out;
00367 }
00368 
00369 template<class P>
00370 INLINE void VideoCodec::UnXorBlock(int vx,int vy,FrameBlock * block) {
00371         P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx;
00372         P * pnew=((P*)newframe)+block->start;
00373         for (int y=0;y<block->dy;y++) {
00374                 for (int x=0;x<block->dx;x++) {
00375                         pnew[x]=pold[x]^*((P*)&work[workPos]);
00376                         workPos+=(int)sizeof(P);
00377                 }
00378                 pold+=pitch;
00379                 pnew+=pitch;
00380         }
00381 }
00382 
00383 template<class P>
00384 INLINE void VideoCodec::CopyBlock(int vx,int vy,FrameBlock * block) {
00385         P * pold=((P*)oldframe)+block->start+(vy*pitch)+vx;
00386         P * pnew=((P*)newframe)+block->start;
00387         for (int y=0;y<block->dy;y++) {
00388                 for (int x=0;x<block->dx;x++) {
00389                         pnew[x]=pold[x];
00390                 }
00391                 pold+=pitch;
00392                 pnew+=pitch;
00393         }
00394 }
00395 
00396 template<class P>
00397 void VideoCodec::UnXorFrame(void) {
00398         signed char * vectors=(signed char *)&work[workPos];
00399         workPos=(workPos + blockcount*2 + 3) & ~3;
00400         for (int b=0;b<blockcount;b++) {
00401                 FrameBlock * block=&blocks[b];
00402                 int delta = vectors[b*2+0] & 1;
00403                 int vx = vectors[b*2+0] >> 1;
00404                 int vy = vectors[b*2+1] >> 1;
00405                 if (delta) UnXorBlock<P>(vx,vy,block);
00406                 else CopyBlock<P>(vx,vy,block);
00407         }
00408 }
00409 
00410 bool VideoCodec::DecompressFrame(void * framedata, int size) {
00411         unsigned char *data=(unsigned char *)framedata;
00412         unsigned char tag;int i;
00413 
00414         tag = *data++;
00415         if (--size<=0)
00416                 return false;
00417         if (tag & Mask_KeyFrame) {
00418                 KeyframeHeader * header = (KeyframeHeader *)data;
00419                 size -= (int)sizeof(KeyframeHeader);data += sizeof(KeyframeHeader);
00420                 if (size<=0)
00421             return false;
00422                 if (header->low_version != DBZV_VERSION_LOW || header->high_version != DBZV_VERSION_HIGH) 
00423                         return false;
00424                 if (format != (zmbv_format_t)header->format && !SetupBuffers((zmbv_format_t)header->format, header->blockwidth, header->blockheight))
00425                         return false;
00426                 inflateReset(&zstream);
00427         } 
00428         zstream.next_in = (Bytef *)data;
00429         zstream.avail_in = (unsigned int)size;
00430         zstream.total_in = 0;
00431 
00432         zstream.next_out = (Bytef *)work;
00433         zstream.avail_out = (unsigned int)bufsize;
00434         zstream.total_out = 0;
00435         inflate(&zstream, Z_FINISH);
00436         workUsed= zstream.total_out;
00437         workPos = 0;
00438         if (tag & Mask_KeyFrame) {
00439                 if (palsize) {
00440                         for (i=0;i<palsize;i++) {
00441                                 palette[i*4+0] = (char)work[workPos++];
00442                                 palette[i*4+1] = (char)work[workPos++];
00443                                 palette[i*4+2] = (char)work[workPos++];
00444                         }
00445                 }
00446                 newframe = buf1;
00447                 oldframe = buf2;
00448                 unsigned char * writeframe = newframe + pixelsize*(MAX_VECTOR+MAX_VECTOR*pitch);        
00449                 for (i=0;i<height;i++) {
00450                         memcpy(writeframe,&work[workPos],(unsigned int)width*(unsigned int)pixelsize);
00451                         writeframe+=pitch*pixelsize;
00452                         workPos+=width*pixelsize;
00453                 }
00454         } else {
00455                 data = oldframe;
00456                 oldframe = newframe;
00457                 newframe = data;
00458                 if (tag & Mask_DeltaPalette) {
00459                         for (i=0;i<palsize;i++) {
00460                                 palette[i*4+0] ^= (unsigned char)work[workPos++];
00461                                 palette[i*4+1] ^= (unsigned char)work[workPos++];
00462                                 palette[i*4+2] ^= (unsigned char)work[workPos++];
00463                         }
00464                 }
00465                 switch (format) {
00466                 case ZMBV_FORMAT_8BPP:
00467                         UnXorFrame<uint8_t>();
00468                         break;
00469                 case ZMBV_FORMAT_15BPP:
00470                 case ZMBV_FORMAT_16BPP:
00471                         UnXorFrame<uint16_t>();
00472                         break;
00473                 case ZMBV_FORMAT_32BPP:
00474                         UnXorFrame<uint32_t>();
00475                         break;
00476                 default:
00477                         break;
00478                 }
00479         }
00480         return true;
00481 }
00482 
00483 void VideoCodec::Output_UpsideDown_24(void *output) {
00484         int i;
00485         unsigned char *r;
00486         unsigned char *w = (unsigned char *)output;
00487         unsigned int pad = width & 3;
00488 
00489         for (i=height-1;i>=0;i--) {
00490                 r = newframe + pixelsize*(MAX_VECTOR+(i+MAX_VECTOR)*pitch);
00491                 switch (format) {
00492                 case ZMBV_FORMAT_8BPP:
00493                         for (unsigned int j=0;(int)j<width;j++) {
00494                                 unsigned int c=r[j];
00495                                 *w++=(unsigned char)palette[c*4u+2u];
00496                                 *w++=(unsigned char)palette[c*4u+1u];
00497                                 *w++=(unsigned char)palette[c*4u+0u];
00498                         }
00499                         break;
00500                 case ZMBV_FORMAT_15BPP:
00501                         for (unsigned int j=0;(int)j<width;j++) {
00502                                 unsigned short c = *(unsigned short *)&r[j*2u];
00503                                 *w++ = (unsigned char)(((c & 0x001fu) * 0x21u) >>  2u);
00504                                 *w++ = (unsigned char)(((c & 0x03e0u) * 0x21u) >>  7u);
00505                                 *w++ = (unsigned char)(((c & 0x7c00u) * 0x21u) >> 12u);
00506                         }
00507                         break;
00508                 case ZMBV_FORMAT_16BPP:
00509                         for (unsigned int j=0;(int)j<width;j++) {
00510                                 unsigned short c = *(unsigned short *)&r[j*2u];
00511                                 *w++ = (unsigned char)(((c & 0x001fu) * 0x21u) >>  2u);
00512                                 *w++ = (unsigned char)(((c & 0x07e0u) * 0x41u) >>  9u);
00513                                 *w++ = (unsigned char)(((c & 0xf800u) * 0x21u) >> 13u);
00514                         }
00515                         break;
00516                 case ZMBV_FORMAT_32BPP:
00517                         for (unsigned int j=0;(int)j<width;j++) {
00518                                 *w++ = r[j*4u+0u];
00519                                 *w++ = r[j*4u+1u];
00520                                 *w++ = r[j*4u+2u];
00521                         }
00522                         break;
00523                 default:
00524                         break;
00525                 }
00526 
00527                 // Maintain 32-bit alignment for scanlines.
00528                 w += pad;
00529         }
00530 }
00531 
00532 void VideoCodec::FreeBuffers(void) {
00533         if (blocks) {
00534                 delete[] blocks;blocks=0;
00535         }
00536         if (buf1) {
00537                 delete[] buf1;buf1=0;
00538         }
00539         if (buf2) {
00540                 delete[] buf2;buf2=0;
00541         }
00542         if (work) {
00543                 delete[] work;work=0;
00544         }
00545 }
00546 
00547 
00548 VideoCodec::VideoCodec() {
00549         CreateVectorTable();
00550         blocks = 0;
00551         buf1 = 0;
00552         buf2 = 0;
00553         work = 0;
00554         memset( &zstream, 0, sizeof(zstream));
00555 }