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

qtrle.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 // QuickTime RLE Decoder
00024 // Based off ffmpeg's QuickTime RLE decoder (written by Mike Melanson)
00025 
00026 #include "image/codecs/qtrle.h"
00027 
00028 #include "common/debug.h"
00029 #include "common/scummsys.h"
00030 #include "common/stream.h"
00031 #include "common/system.h"
00032 #include "common/textconsole.h"
00033 #include "graphics/colormasks.h"
00034 #include "graphics/surface.h"
00035 
00036 namespace Image {
00037 
00038 QTRLEDecoder::QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel) : Codec() {
00039     _bitsPerPixel = bitsPerPixel;
00040     _ditherPalette = 0;
00041     _width = width;
00042     _height = height;
00043     _surface = 0;
00044     _dirtyPalette = false;
00045     _colorMap = 0;
00046 
00047     // We need to ensure the width is a multiple of 4
00048     _paddedWidth = width;
00049     uint16 wMod = width % 4;
00050     if (wMod != 0)
00051         _paddedWidth += 4 - wMod;
00052 }
00053 
00054 QTRLEDecoder::~QTRLEDecoder() {
00055     if (_surface) {
00056         _surface->free();
00057         delete _surface;
00058     }
00059 
00060     delete[] _colorMap;
00061     delete[] _ditherPalette;
00062 }
00063 
00064 #define CHECK_STREAM_PTR(n) \
00065     do { \
00066         if ((stream.pos() + n) > stream.size()) { \
00067             warning("QTRLE Problem: stream out of bounds (%d > %d)", stream.pos() + n, stream.size()); \
00068             return; \
00069         } \
00070     } while (0)
00071 
00072 #define CHECK_PIXEL_PTR(n) \
00073     do { \
00074         if ((int32)pixelPtr + n > (int)_paddedWidth * _surface->h) { \
00075             warning("QTRLE Problem: pixel ptr = %d, pixel limit = %d", pixelPtr + n, _paddedWidth * _surface->h); \
00076             return; \
00077         } \
00078     } while (0)
00079 
00080 void QTRLEDecoder::decode1(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
00081     uint32 pixelPtr = 0;
00082     byte *rgb = (byte *)_surface->getPixels();
00083 
00084     while (linesToChange) {
00085         CHECK_STREAM_PTR(2);
00086         byte skip = stream.readByte();
00087         int8 rleCode = stream.readSByte();
00088 
00089         if (rleCode == 0)
00090             break;
00091 
00092         if (skip & 0x80) {
00093             linesToChange--;
00094             rowPtr += _paddedWidth;
00095             pixelPtr = rowPtr + 2 * (skip & 0x7f);
00096         } else
00097             pixelPtr += 2 * skip;
00098 
00099         if (rleCode < 0) {
00100             // decode the run length code
00101             rleCode = -rleCode;
00102             // get the next 2 bytes from the stream, treat them as groups of 8 pixels, and output them rleCode times */
00103             CHECK_STREAM_PTR(2);
00104             byte pi0 = stream.readByte();
00105             byte pi1 = stream.readByte();
00106             CHECK_PIXEL_PTR(rleCode * 2);
00107 
00108             while (rleCode--) {
00109                 rgb[pixelPtr++] = pi0;
00110                 rgb[pixelPtr++] = pi1;
00111             }
00112         } else {
00113             // copy the same pixel directly to output 2 times
00114             rleCode *= 2;
00115             CHECK_STREAM_PTR(rleCode);
00116             CHECK_PIXEL_PTR(rleCode);
00117 
00118             while (rleCode--)
00119                 rgb[pixelPtr++] = stream.readByte();
00120         }
00121     }
00122 }
00123 
00124 void QTRLEDecoder::decode2_4(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange, byte bpp) {
00125     uint32 pixelPtr = 0;
00126     byte *rgb = (byte *)_surface->getPixels();
00127     byte numPixels = (bpp == 4) ? 8 : 16;
00128 
00129     while (linesToChange--) {
00130         CHECK_STREAM_PTR(2);
00131         pixelPtr = rowPtr + (numPixels * (stream.readByte() - 1));
00132 
00133         for (int8 rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
00134             if (rleCode == 0) {
00135                 // there's another skip code in the stream
00136                 CHECK_STREAM_PTR(1);
00137                 pixelPtr += (numPixels * (stream.readByte() - 1));
00138             } else if (rleCode < 0) {
00139                 // decode the run length code
00140                 rleCode = -rleCode;
00141 
00142                 // get the next 4 bytes from the stream, treat them as palette indices, and output them rleCode times */
00143                 CHECK_STREAM_PTR(4);
00144 
00145                 byte pi[16]; // 16 palette indices
00146 
00147                 for (int8 i = numPixels - 1; i >= 0; i--) {
00148                     pi[numPixels - 1 - i] = (stream.readByte() >> ((i * bpp) & 0x07)) & ((1 << bpp) - 1);
00149 
00150                     if ((i & ((numPixels >> 2) - 1)) == 0)
00151                         stream.readByte();
00152                 }
00153 
00154                 CHECK_PIXEL_PTR(rleCode * numPixels);
00155 
00156                 while (rleCode--)
00157                     for (byte i = 0; i < numPixels; i++)
00158                         rgb[pixelPtr++] = pi[i];
00159             } else {
00160                 // copy the same pixel directly to output 4 times
00161                 rleCode *= 4;
00162                 CHECK_STREAM_PTR(rleCode);
00163                 CHECK_PIXEL_PTR(rleCode * (numPixels >> 2));
00164 
00165                 while (rleCode--) {
00166                     byte temp = stream.readByte();
00167                     if (bpp == 4) {
00168                         rgb[pixelPtr++] = (temp >> 4) & 0x0f;
00169                         rgb[pixelPtr++] = temp & 0x0f;
00170                     } else {
00171                         rgb[pixelPtr++] = (temp >> 6) & 0x03;
00172                         rgb[pixelPtr++] = (temp >> 4) & 0x03;
00173                         rgb[pixelPtr++] = (temp >> 2) & 0x03;
00174                         rgb[pixelPtr++] = temp & 0x03;
00175                     }
00176                 }
00177             }
00178         }
00179 
00180         rowPtr += _paddedWidth;
00181     }
00182 }
00183 
00184 void QTRLEDecoder::decode8(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
00185     uint32 pixelPtr = 0;
00186     byte *rgb = (byte *)_surface->getPixels();
00187 
00188     while (linesToChange--) {
00189         CHECK_STREAM_PTR(2);
00190         pixelPtr = rowPtr + 4 * (stream.readByte() - 1);
00191 
00192         for (int8 rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
00193             if (rleCode == 0) {
00194                 // there's another skip code in the stream
00195                 CHECK_STREAM_PTR(1);
00196                 pixelPtr += 4 * (stream.readByte() - 1);
00197             } else if (rleCode < 0) {
00198                 // decode the run length code
00199                 rleCode = -rleCode;
00200 
00201                 // get the next 4 bytes from the stream, treat them as palette indices, and output them rleCode times
00202                 CHECK_STREAM_PTR(4);
00203 
00204                 byte pi[4];  // 4 palette indexes
00205 
00206                 for (byte i = 0; i < 4; i++)
00207                     pi[i] = stream.readByte();
00208 
00209                 CHECK_PIXEL_PTR(rleCode * 4);
00210 
00211                 while (rleCode--)
00212                     for (byte i = 0; i < 4; i++)
00213                         rgb[pixelPtr++] = pi[i];
00214             } else {
00215                 // copy the same pixel directly to output 4 times
00216                 rleCode *= 4;
00217                 CHECK_STREAM_PTR(rleCode);
00218                 CHECK_PIXEL_PTR(rleCode);
00219 
00220                 while (rleCode--)
00221                     rgb[pixelPtr++] = stream.readByte();
00222             }
00223         }
00224 
00225         rowPtr += _paddedWidth;
00226     }
00227 }
00228 
00229 void QTRLEDecoder::decode16(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
00230     uint32 pixelPtr = 0;
00231     uint16 *rgb = (uint16 *)_surface->getPixels();
00232 
00233     while (linesToChange--) {
00234         CHECK_STREAM_PTR(2);
00235         pixelPtr = rowPtr + stream.readByte() - 1;
00236 
00237         for (int8 rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
00238             if (rleCode == 0) {
00239                 // there's another skip code in the stream
00240                 CHECK_STREAM_PTR(1);
00241                 pixelPtr += stream.readByte() - 1;
00242             } else if (rleCode < 0) {
00243                 // decode the run length code
00244                 rleCode = -rleCode;
00245                 CHECK_STREAM_PTR(2);
00246 
00247                 uint16 rgb16 = stream.readUint16BE();
00248 
00249                 CHECK_PIXEL_PTR(rleCode);
00250 
00251                 while (rleCode--)
00252                     rgb[pixelPtr++] = rgb16;
00253             } else {
00254                 CHECK_STREAM_PTR(rleCode * 2);
00255                 CHECK_PIXEL_PTR(rleCode);
00256 
00257                 // copy pixels directly to output
00258                 while (rleCode--)
00259                     rgb[pixelPtr++] = stream.readUint16BE();
00260             }
00261         }
00262 
00263         rowPtr += _paddedWidth;
00264     }
00265 }
00266 
00267 void QTRLEDecoder::decode24(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
00268     uint32 pixelPtr = 0;
00269     uint32 *rgb = (uint32 *)_surface->getPixels();
00270 
00271     while (linesToChange--) {
00272         CHECK_STREAM_PTR(2);
00273         pixelPtr = rowPtr + stream.readByte() - 1;
00274 
00275         for (int8 rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
00276             if (rleCode == 0) {
00277                 // there's another skip code in the stream
00278                 CHECK_STREAM_PTR(1);
00279                 pixelPtr += stream.readByte() - 1;
00280             } else if (rleCode < 0) {
00281                 // decode the run length code
00282                 rleCode = -rleCode;
00283 
00284                 CHECK_STREAM_PTR(3);
00285 
00286                 byte r = stream.readByte();
00287                 byte g = stream.readByte();
00288                 byte b = stream.readByte();
00289                 uint32 color = _surface->format.RGBToColor(r, g, b);
00290 
00291                 CHECK_PIXEL_PTR(rleCode);
00292 
00293                 while (rleCode--)
00294                     rgb[pixelPtr++] = color;
00295             } else {
00296                 CHECK_STREAM_PTR(rleCode * 3);
00297                 CHECK_PIXEL_PTR(rleCode);
00298 
00299                 // copy pixels directly to output
00300                 while (rleCode--) {
00301                     byte r = stream.readByte();
00302                     byte g = stream.readByte();
00303                     byte b = stream.readByte();
00304                     rgb[pixelPtr++] = _surface->format.RGBToColor(r, g, b);
00305                 }
00306             }
00307         }
00308 
00309         rowPtr += _paddedWidth;
00310     }
00311 }
00312 
00313 namespace {
00314 
00315 inline uint16 readDitherColor24(Common::ReadStream &stream) {
00316     uint16 color = (stream.readByte() & 0xF8) << 6;
00317     color |= (stream.readByte() & 0xF8) << 1;
00318     color |= stream.readByte() >> 4;
00319     return color;
00320 }
00321 
00322 } // End of anonymous namespace
00323 
00324 void QTRLEDecoder::dither24(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
00325     uint32 pixelPtr = 0;
00326     byte *output = (byte *)_surface->getPixels();
00327 
00328     static const uint16 colorTableOffsets[] = { 0x0000, 0xC000, 0x4000, 0x8000 };
00329 
00330     // clone2727 thinks this should be startLine & 3, but the original definitely
00331     // isn't doing this. Unless startLine & 3 is always 0? Kinda defeats the
00332     // purpose of the compression then.
00333     byte curColorTableOffset = 0;
00334 
00335     while (linesToChange--) {
00336         CHECK_STREAM_PTR(2);
00337 
00338         byte rowOffset = stream.readByte() - 1;
00339         pixelPtr = rowPtr + rowOffset;
00340         uint16 colorTableOffset = colorTableOffsets[curColorTableOffset] + (rowOffset << 14);
00341 
00342         for (int8 rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
00343             if (rleCode == 0) {
00344                 // there's another skip code in the stream
00345                 CHECK_STREAM_PTR(1);
00346                 pixelPtr += stream.readByte() - 1;
00347             } else if (rleCode < 0) {
00348                 // decode the run length code
00349                 rleCode = -rleCode;
00350 
00351                 CHECK_STREAM_PTR(3);
00352                 CHECK_PIXEL_PTR(rleCode);
00353 
00354                 uint16 color = readDitherColor24(stream);
00355 
00356                 while (rleCode--) {
00357                     output[pixelPtr++] = _colorMap[colorTableOffset + color];
00358                     colorTableOffset += 0x4000;
00359                 }
00360             } else {
00361                 CHECK_STREAM_PTR(rleCode * 3);
00362                 CHECK_PIXEL_PTR(rleCode);
00363 
00364                 // copy pixels directly to output
00365                 while (rleCode--) {
00366                     uint16 color = readDitherColor24(stream);
00367                     output[pixelPtr++] = _colorMap[colorTableOffset + color];
00368                     colorTableOffset += 0x4000;
00369                 }
00370             }
00371         }
00372 
00373         rowPtr += _paddedWidth;
00374         curColorTableOffset = (curColorTableOffset + 1) & 3;
00375     }
00376 }
00377 
00378 void QTRLEDecoder::decode32(Common::SeekableReadStream &stream, uint32 rowPtr, uint32 linesToChange) {
00379     uint32 pixelPtr = 0;
00380     uint32 *rgb = (uint32 *)_surface->getPixels();
00381 
00382     while (linesToChange--) {
00383         CHECK_STREAM_PTR(2);
00384         pixelPtr = rowPtr + stream.readByte() - 1;
00385 
00386         for (int8 rleCode = stream.readSByte(); rleCode != -1; rleCode = stream.readSByte()) {
00387             if (rleCode == 0) {
00388                 // there's another skip code in the stream
00389                 CHECK_STREAM_PTR(1);
00390                 pixelPtr += stream.readByte() - 1;
00391             } else if (rleCode < 0) {
00392                 // decode the run length code
00393                 rleCode = -rleCode;
00394 
00395                 CHECK_STREAM_PTR(4);
00396 
00397                 byte a = stream.readByte();
00398                 byte r = stream.readByte();
00399                 byte g = stream.readByte();
00400                 byte b = stream.readByte();
00401                 uint32 color = _surface->format.ARGBToColor(a, r, g, b);
00402 
00403                 CHECK_PIXEL_PTR(rleCode);
00404 
00405                 while (rleCode--)
00406                     rgb[pixelPtr++] = color;
00407             } else {
00408                 CHECK_STREAM_PTR(rleCode * 4);
00409                 CHECK_PIXEL_PTR(rleCode);
00410 
00411                 // copy pixels directly to output
00412                 while (rleCode--) {
00413                     byte a = stream.readByte();
00414                     byte r = stream.readByte();
00415                     byte g = stream.readByte();
00416                     byte b = stream.readByte();
00417                     rgb[pixelPtr++] = _surface->format.ARGBToColor(a, r, g, b);
00418                 }
00419             }
00420         }
00421 
00422         rowPtr += _paddedWidth;
00423     }
00424 }
00425 
00426 const Graphics::Surface *QTRLEDecoder::decodeFrame(Common::SeekableReadStream &stream) {
00427     if (!_surface)
00428         createSurface();
00429 
00430     uint16 startLine = 0;
00431     uint16 height = _height;
00432 
00433     // check if this frame is even supposed to change
00434     if (stream.size() < 8)
00435         return _surface;
00436 
00437     // start after the chunk size
00438     stream.readUint32BE();
00439 
00440     // fetch the header
00441     uint16 header = stream.readUint16BE();
00442 
00443     // if a header is present, fetch additional decoding parameters
00444     if (header & 8) {
00445         if (stream.size() < 14)
00446             return _surface;
00447 
00448         startLine = stream.readUint16BE();
00449         stream.readUint16BE(); // Unknown
00450         height = stream.readUint16BE();
00451         stream.readUint16BE(); // Unknown
00452     }
00453 
00454     uint32 rowPtr = _paddedWidth * startLine;
00455 
00456     switch (_bitsPerPixel) {
00457     case 1:
00458     case 33:
00459         decode1(stream, rowPtr, height);
00460         break;
00461     case 2:
00462     case 34:
00463         decode2_4(stream, rowPtr, height, 2);
00464         break;
00465     case 4:
00466     case 36:
00467         decode2_4(stream, rowPtr, height, 4);
00468         break;
00469     case 8:
00470     case 40:
00471         decode8(stream, rowPtr, height);
00472         break;
00473     case 16:
00474         decode16(stream, rowPtr, height);
00475         break;
00476     case 24:
00477         if (_ditherPalette)
00478             dither24(stream, rowPtr, height);
00479         else
00480             decode24(stream, rowPtr, height);
00481         break;
00482     case 32:
00483         decode32(stream, rowPtr, height);
00484         break;
00485     default:
00486         error("Unsupported QTRLE bits per pixel %d", _bitsPerPixel);
00487     }
00488 
00489     return _surface;
00490 }
00491 
00492 Graphics::PixelFormat QTRLEDecoder::getPixelFormat() const {
00493     if (_ditherPalette)
00494         return Graphics::PixelFormat::createFormatCLUT8();
00495 
00496     switch (_bitsPerPixel) {
00497     case 1:
00498     case 33:
00499     case 2:
00500     case 34:
00501     case 4:
00502     case 36:
00503     case 8:
00504     case 40:
00505         return Graphics::PixelFormat::createFormatCLUT8();
00506     case 16:
00507         return Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);
00508     case 24:
00509     case 32:
00510         return Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24);
00511     default:
00512         error("Unsupported QTRLE bits per pixel %d", _bitsPerPixel);
00513     }
00514 
00515     return Graphics::PixelFormat();
00516 }
00517 
00518 bool QTRLEDecoder::canDither(DitherType type) const {
00519     // Only 24-bit dithering is implemented at the moment
00520     return type == kDitherTypeQT && _bitsPerPixel == 24;
00521 }
00522 
00523 void QTRLEDecoder::setDither(DitherType type, const byte *palette) {
00524     assert(canDither(type));
00525 
00526     _ditherPalette = new byte[256 * 3];
00527     memcpy(_ditherPalette, palette, 256 * 3);
00528     _dirtyPalette = true;
00529 
00530     delete[] _colorMap;
00531     _colorMap = createQuickTimeDitherTable(palette, 256);
00532 }
00533 
00534 void QTRLEDecoder::createSurface() {
00535     if (_surface) {
00536         _surface->free();
00537         delete _surface;
00538     }
00539 
00540     _surface = new Graphics::Surface();
00541     _surface->create(_paddedWidth, _height, getPixelFormat());
00542     _surface->w = _width;
00543 }
00544 
00545 } // End of namespace Image


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