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