DOSBox-X
|
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)