ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

smc.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 // Based off ffmpeg's SMC decoder
00024 
00025 #include "image/codecs/smc.h"
00026 #include "common/stream.h"
00027 #include "common/textconsole.h"
00028 
00029 namespace Image {
00030 
00031 #define GET_BLOCK_COUNT() \
00032   (opcode & 0x10) ? (1 + stream.readByte()) : 1 + (opcode & 0x0F);
00033 
00034 #define ADVANCE_BLOCK() \
00035 { \
00036     pixelPtr += 4; \
00037     if (pixelPtr >= _surface->w) { \
00038         pixelPtr = 0; \
00039         rowPtr += _surface->w * 4; \
00040     } \
00041     totalBlocks--; \
00042     if (totalBlocks < 0) { \
00043         warning("block counter just went negative (this should not happen)"); \
00044         return _surface; \
00045     } \
00046 }
00047 
00048 SMCDecoder::SMCDecoder(uint16 width, uint16 height) {
00049     _surface = new Graphics::Surface();
00050     _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
00051 }
00052 
00053 SMCDecoder::~SMCDecoder() {
00054     _surface->free();
00055     delete _surface;
00056 }
00057 
00058 const Graphics::Surface *SMCDecoder::decodeFrame(Common::SeekableReadStream &stream) {
00059     byte *pixels = (byte *)_surface->getPixels();
00060 
00061     uint32 numBlocks = 0;
00062     uint32 colorFlags = 0;
00063     uint32 colorFlagsA = 0;
00064     uint32 colorFlagsB = 0;
00065 
00066     const uint16 rowInc = _surface->w - 4;
00067     int32 rowPtr = 0;
00068     int32 pixelPtr = 0;
00069     uint32 blockPtr = 0;
00070     uint32 prevBlockPtr = 0;
00071     uint32 prevBlockPtr1 = 0, prevBlockPtr2 = 0;
00072     byte prevBlockFlag = false;
00073     uint32 pixel = 0;
00074 
00075     uint32 colorPairIndex = 0;
00076     uint32 colorQuadIndex = 0;
00077     uint32 colorOctetIndex = 0;
00078     uint32 colorTableIndex = 0;  // indices to color pair, quad, or octet tables
00079 
00080     int32 chunkSize = stream.readUint32BE() & 0x00FFFFFF;
00081     if (chunkSize != stream.size())
00082         warning("MOV chunk size != SMC chunk size (%d != %d); ignoring SMC chunk size", chunkSize, stream.size());
00083 
00084     int32 totalBlocks = ((_surface->w + 3) / 4) * ((_surface->h + 3) / 4);
00085 
00086     // traverse through the blocks
00087     while (totalBlocks != 0) {
00088         // sanity checks
00089 
00090         // make sure stream ptr hasn't gone out of bounds
00091         if (stream.pos() > stream.size()) {
00092             warning("SMC decoder just went out of bounds (stream ptr = %d, chunk size = %d)", stream.pos(), stream.size());
00093             return _surface;
00094         }
00095 
00096         // make sure the row pointer hasn't gone wild
00097         if (rowPtr >= _surface->w * _surface->h) {
00098             warning("SMC decoder just went out of bounds (row ptr = %d, size = %d)", rowPtr, _surface->w * _surface->h);
00099             return _surface;
00100         }
00101 
00102         byte opcode = stream.readByte();
00103 
00104         switch (opcode & 0xF0) {
00105         // skip n blocks
00106         case 0x00:
00107         case 0x10:
00108             numBlocks = GET_BLOCK_COUNT();
00109             while (numBlocks--) {
00110                 ADVANCE_BLOCK();
00111             }
00112             break;
00113 
00114         // repeat last block n times
00115         case 0x20:
00116         case 0x30:
00117             numBlocks = GET_BLOCK_COUNT();
00118 
00119             // sanity check
00120             if (rowPtr == 0 && pixelPtr == 0) {
00121                 warning("encountered repeat block opcode (%02X) but no blocks rendered yet", opcode & 0xF0);
00122                 break;
00123             }
00124 
00125             // figure out where the previous block started
00126             if (pixelPtr == 0)
00127                 prevBlockPtr1 = (rowPtr - _surface->w * 4) + _surface->w - 4;
00128             else
00129                 prevBlockPtr1 = rowPtr + pixelPtr - 4;
00130 
00131             while (numBlocks--) {
00132                 blockPtr = rowPtr + pixelPtr;
00133                 prevBlockPtr = prevBlockPtr1;
00134                 for (byte y = 0; y < 4; y++) {
00135                     for (byte x = 0; x < 4; x++)
00136                         pixels[blockPtr++] = pixels[prevBlockPtr++];
00137                     blockPtr += rowInc;
00138                     prevBlockPtr += rowInc;
00139                 }
00140                 ADVANCE_BLOCK();
00141             }
00142             break;
00143 
00144         // repeat previous pair of blocks n times
00145         case 0x40:
00146         case 0x50:
00147             numBlocks = GET_BLOCK_COUNT();
00148             numBlocks *= 2;
00149 
00150             // sanity check
00151             if (rowPtr == 0 && pixelPtr < 2 * 4) {
00152                 warning("encountered repeat block opcode (%02X) but not enough blocks rendered yet", opcode & 0xF0);
00153                 break;
00154             }
00155 
00156             // figure out where the previous 2 blocks started
00157             if (pixelPtr == 0)
00158                 prevBlockPtr1 = (rowPtr - _surface->w * 4) + _surface->w - 4 * 2;
00159             else if (pixelPtr == 4)
00160                 prevBlockPtr1 = (rowPtr - _surface->w * 4) + rowInc;
00161             else
00162                 prevBlockPtr1 = rowPtr + pixelPtr - 4 * 2;
00163 
00164             if (pixelPtr == 0)
00165                 prevBlockPtr2 = (rowPtr - _surface->w * 4) + rowInc;
00166             else
00167                 prevBlockPtr2 = rowPtr + pixelPtr - 4;
00168 
00169             prevBlockFlag = 0;
00170             while (numBlocks--) {
00171                 blockPtr = rowPtr + pixelPtr;
00172 
00173                 if (prevBlockFlag)
00174                     prevBlockPtr = prevBlockPtr2;
00175                 else
00176                     prevBlockPtr = prevBlockPtr1;
00177 
00178                 prevBlockFlag = !prevBlockFlag;
00179 
00180                 for (byte y = 0; y < 4; y++) {
00181                     for (byte x = 0; x < 4; x++)
00182                         pixels[blockPtr++] = pixels[prevBlockPtr++];
00183 
00184                     blockPtr += rowInc;
00185                     prevBlockPtr += rowInc;
00186                 }
00187                 ADVANCE_BLOCK();
00188             }
00189             break;
00190 
00191         // 1-color block encoding
00192         case 0x60:
00193         case 0x70:
00194             numBlocks = GET_BLOCK_COUNT();
00195             pixel = stream.readByte();
00196 
00197             while (numBlocks--) {
00198                 blockPtr = rowPtr + pixelPtr;
00199                 for (byte y = 0; y < 4; y++) {
00200                     for (byte x = 0; x < 4; x++)
00201                         pixels[blockPtr++] = pixel;
00202 
00203                     blockPtr += rowInc;
00204                 }
00205                 ADVANCE_BLOCK();
00206             }
00207             break;
00208 
00209         // 2-color block encoding
00210         case 0x80:
00211         case 0x90:
00212             numBlocks = (opcode & 0x0F) + 1;
00213 
00214             // figure out which color pair to use to paint the 2-color block
00215             if ((opcode & 0xF0) == 0x80) {
00216                 // fetch the next 2 colors from bytestream and store in next
00217                 // available entry in the color pair table
00218                 for (byte i = 0; i < CPAIR; i++) {
00219                     pixel = stream.readByte();
00220                     colorTableIndex = CPAIR * colorPairIndex + i;
00221                     _colorPairs[colorTableIndex] = pixel;
00222                 }
00223 
00224                 // this is the base index to use for this block
00225                 colorTableIndex = CPAIR * colorPairIndex;
00226                 colorPairIndex++;
00227 
00228                 // wraparound
00229                 if (colorPairIndex == COLORS_PER_TABLE)
00230                     colorPairIndex = 0;
00231             } else
00232                 colorTableIndex = CPAIR * stream.readByte();
00233 
00234             while (numBlocks--) {
00235                 colorFlags = stream.readUint16BE();
00236                 uint16 flagMask = 0x8000;
00237                 blockPtr = rowPtr + pixelPtr;
00238                 for (byte y = 0; y < 4; y++) {
00239                     for (byte x = 0; x < 4; x++) {
00240                         if (colorFlags & flagMask)
00241                             pixel = colorTableIndex + 1;
00242                         else
00243                             pixel = colorTableIndex;
00244 
00245                         flagMask >>= 1;
00246                         pixels[blockPtr++] = _colorPairs[pixel];
00247                     }
00248 
00249                     blockPtr += rowInc;
00250                 }
00251                 ADVANCE_BLOCK();
00252             }
00253             break;
00254 
00255         // 4-color block encoding
00256         case 0xA0:
00257         case 0xB0:
00258             numBlocks = (opcode & 0x0F) + 1;
00259 
00260             // figure out which color quad to use to paint the 4-color block
00261             if ((opcode & 0xF0) == 0xA0) {
00262                 // fetch the next 4 colors from bytestream and store in next
00263                 // available entry in the color quad table
00264                 for (byte i = 0; i < CQUAD; i++) {
00265                     pixel = stream.readByte();
00266                     colorTableIndex = CQUAD * colorQuadIndex + i;
00267                     _colorQuads[colorTableIndex] = pixel;
00268                 }
00269 
00270                 // this is the base index to use for this block
00271                 colorTableIndex = CQUAD * colorQuadIndex;
00272                 colorQuadIndex++;
00273 
00274                 // wraparound
00275                 if (colorQuadIndex == COLORS_PER_TABLE)
00276                     colorQuadIndex = 0;
00277             } else
00278                 colorTableIndex = CQUAD * stream.readByte();
00279 
00280             while (numBlocks--) {
00281                 colorFlags = stream.readUint32BE();
00282 
00283                 // flag mask actually acts as a bit shift count here
00284                 byte flagMask = 30;
00285                 blockPtr = rowPtr + pixelPtr;
00286 
00287                 for (byte y = 0; y < 4; y++) {
00288                     for (byte x = 0; x < 4; x++) {
00289                         pixel = colorTableIndex + ((colorFlags >> flagMask) & 0x03);
00290                         flagMask -= 2;
00291                         pixels[blockPtr++] = _colorQuads[pixel];
00292                     }
00293                     blockPtr += rowInc;
00294                 }
00295                 ADVANCE_BLOCK();
00296             }
00297             break;
00298 
00299         // 8-color block encoding
00300         case 0xC0:
00301         case 0xD0:
00302             numBlocks = (opcode & 0x0F) + 1;
00303 
00304             // figure out which color octet to use to paint the 8-color block
00305             if ((opcode & 0xF0) == 0xC0) {
00306                 // fetch the next 8 colors from bytestream and store in next
00307                 // available entry in the color octet table
00308                 for (byte i = 0; i < COCTET; i++) {
00309                     pixel = stream.readByte();
00310                     colorTableIndex = COCTET * colorOctetIndex + i;
00311                     _colorOctets[colorTableIndex] = pixel;
00312                 }
00313 
00314                 // this is the base index to use for this block
00315                 colorTableIndex = COCTET * colorOctetIndex;
00316                 colorOctetIndex++;
00317 
00318                 // wraparound
00319                 if (colorOctetIndex == COLORS_PER_TABLE)
00320                     colorOctetIndex = 0;
00321             } else
00322                 colorTableIndex = COCTET * stream.readByte();
00323 
00324             while (numBlocks--) {
00325                 /*
00326                   For this input of 6 hex bytes:
00327                     01 23 45 67 89 AB
00328                   Mangle it to this output:
00329                     flags_a = xx012456, flags_b = xx89A37B
00330                 */
00331 
00332                 // build the color flags
00333                 byte flagData[6];
00334                 stream.read(flagData, 6);
00335 
00336                 colorFlagsA = ((READ_BE_UINT16(flagData) & 0xFFF0) << 8) | (READ_BE_UINT16(flagData + 2) >> 4);
00337                 colorFlagsB = ((READ_BE_UINT16(flagData + 4) & 0xFFF0) << 8) | ((flagData[1] & 0xF) << 8) |
00338                                 ((flagData[3] & 0xF) << 4) | (flagData[5] & 0xf);
00339 
00340                 colorFlags = colorFlagsA;
00341 
00342                 // flag mask actually acts as a bit shift count here
00343                 byte flagMask = 21;
00344                 blockPtr = rowPtr + pixelPtr;
00345                 for (byte y = 0; y < 4; y++) {
00346                     // reload flags at third row (iteration y == 2)
00347                     if (y == 2) {
00348                         colorFlags = colorFlagsB;
00349                         flagMask = 21;
00350                     }
00351 
00352                     for (byte x = 0; x < 4; x++) {
00353                         pixel = colorTableIndex + ((colorFlags >> flagMask) & 0x07);
00354                         flagMask -= 3;
00355                         pixels[blockPtr++] = _colorOctets[pixel];
00356                     }
00357 
00358                     blockPtr += rowInc;
00359                 }
00360                 ADVANCE_BLOCK();
00361             }
00362             break;
00363 
00364         // 16-color block encoding (every pixel is a different color)
00365         case 0xE0:
00366             numBlocks = (opcode & 0x0F) + 1;
00367 
00368             while (numBlocks--) {
00369                 blockPtr = rowPtr + pixelPtr;
00370                 for (byte y = 0; y < 4; y++) {
00371                     for (byte x = 0; x < 4; x++)
00372                         pixels[blockPtr++] = stream.readByte();
00373 
00374                     blockPtr += rowInc;
00375                 }
00376                 ADVANCE_BLOCK();
00377             }
00378             break;
00379 
00380         case 0xF0:
00381             warning("0xF0 opcode seen in SMC chunk (contact the developers)");
00382             break;
00383         }
00384     }
00385 
00386     return _surface;
00387 }
00388 
00389 } // End of namespace Image


Generated on Sat Mar 16 2019 05:01:54 for ResidualVM by doxygen 1.7.1
curved edge   curved edge