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


Generated on Sat Sep 26 2020 05:01:19 for ResidualVM by doxygen 1.7.1
curved edge   curved edge