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

flic_decoder.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 #include "video/flic_decoder.h"
00024 #include "common/endian.h"
00025 #include "common/rect.h"
00026 #include "common/stream.h"
00027 #include "common/system.h"
00028 #include "common/textconsole.h"
00029 #include "graphics/surface.h"
00030 
00031 namespace Video {
00032 
00033 FlicDecoder::FlicDecoder() {
00034 }
00035 
00036 FlicDecoder::~FlicDecoder() {
00037     close();
00038 }
00039 
00040 bool FlicDecoder::loadStream(Common::SeekableReadStream *stream) {
00041     close();
00042 
00043     /* uint32 frameSize = */ stream->readUint32LE();
00044     uint16 frameType = stream->readUint16LE();
00045 
00046     // Check FLC magic number
00047     if (frameType != 0xAF12) {
00048         warning("FlicDecoder::loadStream(): attempted to load non-FLC data (type = 0x%04X)", frameType);
00049         return false;
00050     }
00051 
00052     uint16 frameCount = stream->readUint16LE();
00053     uint16 width = stream->readUint16LE();
00054     uint16 height = stream->readUint16LE();
00055     uint16 colorDepth = stream->readUint16LE();
00056     if (colorDepth != 8) {
00057         warning("FlicDecoder::loadStream(): attempted to load an FLC with a palette of color depth %d. Only 8-bit color palettes are supported", colorDepth);
00058         return false;
00059     }
00060 
00061     addTrack(new FlicVideoTrack(stream, frameCount, width, height));
00062     return true;
00063 }
00064 
00065 const Common::List<Common::Rect> *FlicDecoder::getDirtyRects() const {
00066     const Track *track = getTrack(0);
00067 
00068     if (track)
00069         return ((const FlicVideoTrack *)track)->getDirtyRects();
00070 
00071     return 0;
00072 }
00073 
00074 void FlicDecoder::clearDirtyRects() {
00075     Track *track = getTrack(0);
00076 
00077     if (track)
00078         ((FlicVideoTrack *)track)->clearDirtyRects();
00079 }
00080 
00081 void FlicDecoder::copyDirtyRectsToBuffer(uint8 *dst, uint pitch) {
00082     Track *track = getTrack(0);
00083 
00084     if (track)
00085         ((FlicVideoTrack *)track)->copyDirtyRectsToBuffer(dst, pitch);
00086 }
00087 
00088 FlicDecoder::FlicVideoTrack::FlicVideoTrack(Common::SeekableReadStream *stream, uint16 frameCount, uint16 width, uint16 height, bool skipHeader) {
00089     _fileStream = stream;
00090     _frameCount = frameCount;
00091 
00092     _surface = new Graphics::Surface();
00093     _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
00094     _palette = new byte[3 * 256];
00095     memset(_palette, 0, 3 * 256);
00096     _dirtyPalette = false;
00097 
00098     _curFrame = -1;
00099     _nextFrameStartTime = 0;
00100     _atRingFrame = false;
00101 
00102     if (!skipHeader)
00103         readHeader();
00104 }
00105 
00106 FlicDecoder::FlicVideoTrack::~FlicVideoTrack() {
00107     delete _fileStream;
00108     delete[] _palette;
00109 
00110     _surface->free();
00111     delete _surface;
00112 }
00113 
00114 void FlicDecoder::FlicVideoTrack::readHeader() {
00115     _fileStream->readUint16LE();    // flags
00116     // Note: The normal delay is a 32-bit integer (dword), whereas the overridden delay is a 16-bit integer (word)
00117     // the frame delay is the FLIC "speed", in milliseconds.
00118     _frameDelay = _startFrameDelay = _fileStream->readUint32LE();
00119 
00120     _fileStream->seek(80);
00121     _offsetFrame1 = _fileStream->readUint32LE();
00122     _offsetFrame2 = _fileStream->readUint32LE();
00123 
00124     // Seek to the first frame
00125     _fileStream->seek(_offsetFrame1);
00126 }
00127 
00128 bool FlicDecoder::FlicVideoTrack::endOfTrack() const {
00129     return getCurFrame() >= getFrameCount() - 1;
00130 }
00131 
00132 bool FlicDecoder::FlicVideoTrack::rewind() {
00133     if (endOfTrack() && _fileStream->pos() < _fileStream->size() && _frameCount != 1)
00134         _atRingFrame = true;
00135     else
00136         _fileStream->seek(_offsetFrame1);
00137 
00138     _curFrame = -1;
00139     _nextFrameStartTime = 0;
00140     _frameDelay = _startFrameDelay;
00141     return true;
00142 }
00143 
00144 uint16 FlicDecoder::FlicVideoTrack::getWidth() const {
00145     return _surface->w;
00146 }
00147 
00148 uint16 FlicDecoder::FlicVideoTrack::getHeight() const {
00149     return _surface->h;
00150 }
00151 
00152 Graphics::PixelFormat FlicDecoder::FlicVideoTrack::getPixelFormat() const {
00153     return _surface->format;
00154 }
00155 
00156 #define FLI_SETPAL 4
00157 #define FLI_SS2    7
00158 #define FLI_BRUN   15
00159 #define FLI_COPY   16
00160 #define PSTAMP     18
00161 #define FRAME_TYPE 0xF1FA
00162 
00163 const Graphics::Surface *FlicDecoder::FlicVideoTrack::decodeNextFrame() {
00164     // Read chunk
00165     /*uint32 frameSize = */ _fileStream->readUint32LE();
00166     uint16 frameType = _fileStream->readUint16LE();
00167 
00168     switch (frameType) {
00169     case FRAME_TYPE:
00170         handleFrame();
00171         break;
00172     default:
00173         error("FlicDecoder::decodeFrame(): unknown main chunk type (type = 0x%02X)", frameType);
00174         break;
00175      }
00176 
00177     _curFrame++;
00178     _nextFrameStartTime += _frameDelay;
00179 
00180     if (_atRingFrame) {
00181         // If we decoded the ring frame, seek to the second frame
00182         _atRingFrame = false;
00183         _fileStream->seek(_offsetFrame2);
00184     }
00185 
00186     return _surface;
00187 }
00188 
00189 void FlicDecoder::FlicVideoTrack::handleFrame() {
00190     uint16 chunkCount = _fileStream->readUint16LE();
00191     // Note: The overridden delay is a 16-bit integer (word), whereas the normal delay is a 32-bit integer (dword)
00192     // the frame delay is the FLIC "speed", in milliseconds.
00193     uint16 newFrameDelay = _fileStream->readUint16LE(); // "speed", in milliseconds
00194     if (newFrameDelay > 0)
00195         _frameDelay = newFrameDelay;
00196 
00197     _fileStream->readUint16LE();    // reserved, always 0
00198     uint16 newWidth = _fileStream->readUint16LE();
00199     uint16 newHeight = _fileStream->readUint16LE();
00200 
00201     if ((newWidth != 0) || (newHeight != 0)) {
00202         if (newWidth == 0)
00203             newWidth = _surface->w;
00204         if (newHeight == 0)
00205             newHeight = _surface->h;
00206 
00207         _surface->free();
00208         delete _surface;
00209         _surface = new Graphics::Surface();
00210         _surface->create(newWidth, newHeight, Graphics::PixelFormat::createFormatCLUT8());
00211     }
00212 
00213     // Read subchunks
00214     for (uint32 i = 0; i < chunkCount; ++i) {
00215         uint32 frameSize = _fileStream->readUint32LE();
00216         uint16 frameType = _fileStream->readUint16LE();
00217         uint8 *data = new uint8[frameSize - 6];
00218         _fileStream->read(data, frameSize - 6);
00219 
00220         switch (frameType) {
00221         case FLI_SETPAL:
00222             unpackPalette(data);
00223             _dirtyPalette = true;
00224             break;
00225         case FLI_SS2:
00226             decodeDeltaFLC(data);
00227             break;
00228         case FLI_BRUN:
00229             decodeByteRun(data);
00230             break;
00231         case FLI_COPY:
00232             copyFrame(data);
00233             break;
00234         case PSTAMP:
00235             /* PSTAMP - skip for now */
00236             break;
00237         default:
00238             error("FlicDecoder::decodeNextFrame(): unknown subchunk type (type = 0x%02X)", frameType);
00239             break;
00240         }
00241 
00242         delete[] data;
00243     }
00244 }
00245 
00246 void FlicDecoder::FlicVideoTrack::copyDirtyRectsToBuffer(uint8 *dst, uint pitch) {
00247     for (Common::List<Common::Rect>::const_iterator it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
00248         for (int y = (*it).top; y < (*it).bottom; ++y) {
00249             const int x = (*it).left;
00250             memcpy(dst + y * pitch + x, (byte *)_surface->getBasePtr(x, y), (*it).right - x);
00251         }
00252     }
00253 
00254     clearDirtyRects();
00255 }
00256 
00257 void FlicDecoder::FlicVideoTrack::copyFrame(uint8 *data) {
00258     memcpy((byte *)_surface->getPixels(), data, getWidth() * getHeight());
00259 
00260     // Redraw
00261     _dirtyRects.clear();
00262     _dirtyRects.push_back(Common::Rect(0, 0, getWidth(), getHeight()));
00263 }
00264 
00265 void FlicDecoder::FlicVideoTrack::decodeByteRun(uint8 *data) {
00266     byte *ptr = (byte *)_surface->getPixels();
00267     for (int i = 0; i < getHeight(); ++i) {
00268         data++;
00269         for (int j = 0; j < getWidth();) {
00270             int count = (int8)*data++;
00271             if (count > 0) {
00272                 memset(ptr, *data++, count);
00273             } else {
00274                 count = -count;
00275                 memcpy(ptr, data, count);
00276                 data += count;
00277             }
00278             ptr += count;
00279             j += count;
00280         }
00281     }
00282 
00283     // Redraw
00284     _dirtyRects.clear();
00285     _dirtyRects.push_back(Common::Rect(0, 0, getWidth(), getHeight()));
00286 }
00287 
00288 #define OP_PACKETCOUNT   0
00289 #define OP_UNDEFINED     1
00290 #define OP_LASTPIXEL     2
00291 #define OP_LINESKIPCOUNT 3
00292 
00293 void FlicDecoder::FlicVideoTrack::decodeDeltaFLC(uint8 *data) {
00294     uint16 linesInChunk = READ_LE_UINT16(data); data += 2;
00295     uint16 currentLine = 0;
00296     uint16 packetCount = 0;
00297 
00298     while (linesInChunk--) {
00299         uint16 opcode;
00300 
00301         // First process all the opcodes.
00302         do {
00303             opcode = READ_LE_UINT16(data); data += 2;
00304 
00305             switch ((opcode >> 14) & 3) {
00306             case OP_PACKETCOUNT:
00307                 packetCount = opcode;
00308                 break;
00309             case OP_UNDEFINED:
00310                 break;
00311             case OP_LASTPIXEL:
00312                 *((byte *)_surface->getBasePtr(getWidth() - 1, currentLine)) = (opcode & 0xFF);
00313                 _dirtyRects.push_back(Common::Rect(getWidth() - 1, currentLine, getWidth(), currentLine + 1));
00314                 break;
00315             case OP_LINESKIPCOUNT:
00316                 currentLine += -(int16)opcode;
00317                 break;
00318             }
00319         } while (((opcode >> 14) & 3) != OP_PACKETCOUNT);
00320 
00321         uint16 column = 0;
00322 
00323         // Now interpret the RLE data
00324         while (packetCount--) {
00325             column += *data++;
00326             int rleCount = (int8)*data++;
00327             if (rleCount > 0) {
00328                 memcpy((byte *)_surface->getBasePtr(column, currentLine), data, rleCount * 2);
00329                 data += rleCount * 2;
00330                 _dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1));
00331             } else if (rleCount < 0) {
00332                 rleCount = -rleCount;
00333                 uint16 dataWord = READ_UINT16(data); data += 2;
00334                 for (int i = 0; i < rleCount; ++i) {
00335                     WRITE_UINT16((byte *)_surface->getBasePtr(column + i * 2, currentLine), dataWord);
00336                 }
00337                 _dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1));
00338             } else { // End of cutscene ?
00339                 return;
00340             }
00341             column += rleCount * 2;
00342         }
00343 
00344         currentLine++;
00345     }
00346 }
00347 
00348 void FlicDecoder::FlicVideoTrack::unpackPalette(uint8 *data) {
00349     uint16 numPackets = READ_LE_UINT16(data); data += 2;
00350 
00351     if (0 == READ_LE_UINT16(data)) { //special case
00352         data += 2;
00353         for (int i = 0; i < 256; ++i) {
00354             memcpy(_palette + i * 3, data + i * 3, 3);
00355         }
00356     } else {
00357         uint8 palPos = 0;
00358 
00359         while (numPackets--) {
00360             palPos += *data++;
00361             uint8 change = *data++;
00362 
00363             for (int i = 0; i < change; ++i) {
00364                 memcpy(_palette + (palPos + i) * 3, data + i * 3, 3);
00365             }
00366 
00367             palPos += change;
00368             data += (change * 3);
00369         }
00370     }
00371 }
00372 
00373 } // End of namespace Video


Generated on Sat Aug 17 2019 05:00:47 for ResidualVM by doxygen 1.7.1
curved edge   curved edge