diff options
author | Paul Gilbert | 2015-05-31 14:45:10 -0400 |
---|---|---|
committer | Paul Gilbert | 2015-05-31 14:45:10 -0400 |
commit | e5296ebf8dd09f603499b1894a33865ec71bb28f (patch) | |
tree | d7de032efd54dfdb3159cbc778a0c9ce8cd8aa91 /video/qt_decoder.cpp | |
parent | 673537bad93f0b440172a0cc263ebf19cc95ffc0 (diff) | |
parent | 141ff4d08dc24b6bb17098bd71801e2a58e6a38f (diff) | |
download | scummvm-rg350-e5296ebf8dd09f603499b1894a33865ec71bb28f.tar.gz scummvm-rg350-e5296ebf8dd09f603499b1894a33865ec71bb28f.tar.bz2 scummvm-rg350-e5296ebf8dd09f603499b1894a33865ec71bb28f.zip |
Merge branch 'master' into phantom
Diffstat (limited to 'video/qt_decoder.cpp')
-rw-r--r-- | video/qt_decoder.cpp | 270 |
1 files changed, 201 insertions, 69 deletions
diff --git a/video/qt_decoder.cpp b/video/qt_decoder.cpp index 0a29692948..324bf65294 100644 --- a/video/qt_decoder.cpp +++ b/video/qt_decoder.cpp @@ -286,13 +286,15 @@ QuickTimeDecoder::VideoTrackHandler::VideoTrackHandler(QuickTimeDecoder *decoder _curEdit = 0; enterNewEditList(false); - _holdNextFrameStartTime = false; _curFrame = -1; _durationOverride = -1; _scaledSurface = 0; _curPalette = 0; _dirtyPalette = false; _reversed = false; + _forcedDitherPalette = 0; + _ditherTable = 0; + _ditherFrame = 0; } QuickTimeDecoder::VideoTrackHandler::~VideoTrackHandler() { @@ -300,6 +302,14 @@ QuickTimeDecoder::VideoTrackHandler::~VideoTrackHandler() { _scaledSurface->free(); delete _scaledSurface; } + + delete[] _forcedDitherPalette; + delete[] _ditherTable; + + if (_ditherFrame) { + _ditherFrame->free(); + delete _ditherFrame; + } } bool QuickTimeDecoder::VideoTrackHandler::endOfTrack() const { @@ -347,15 +357,12 @@ bool QuickTimeDecoder::VideoTrackHandler::seek(const Audio::Timestamp &requested } } - // All that's left is to figure out what our starting time is going to be - // Compare the starting point for the frame to where we need to be - _holdNextFrameStartTime = getRateAdjustedFrameTime() != (uint32)time.totalNumberOfFrames(); - - // If we went past the time, go back a frame. _curFrame before this point is at the frame - // that should be displayed. This adjustment ensures it is on the frame before the one that - // should be displayed. - if (_holdNextFrameStartTime) + // Check if we went past, then adjust the frame times + if (getRateAdjustedFrameTime() != (uint32)time.totalNumberOfFrames()) { _curFrame--; + _durationOverride = getRateAdjustedFrameTime() - time.totalNumberOfFrames(); + _nextFrameStartTime = time.totalNumberOfFrames(); + } if (_reversed) { // Call setReverse again to update @@ -386,6 +393,9 @@ uint16 QuickTimeDecoder::VideoTrackHandler::getHeight() const { } Graphics::PixelFormat QuickTimeDecoder::VideoTrackHandler::getPixelFormat() const { + if (_forcedDitherPalette) + return Graphics::PixelFormat::createFormatCLUT8(); + return ((VideoSampleDesc *)_parent->sampleDescs[0])->_videoCodec->getPixelFormat(); } @@ -397,8 +407,22 @@ uint32 QuickTimeDecoder::VideoTrackHandler::getNextFrameStartTime() const { if (endOfTrack()) return 0; - // Convert to milliseconds so the tracks can be compared - return getRateAdjustedFrameTime() * 1000 / _parent->timeScale; + Audio::Timestamp frameTime(0, getRateAdjustedFrameTime(), _parent->timeScale); + + // Check if the frame goes beyond the end of the edit. In that case, the next frame + // should really be when we cross the edit boundary. + if (_reversed) { + Audio::Timestamp editStartTime(0, _parent->editList[_curEdit].timeOffset, _decoder->_timeScale); + if (frameTime < editStartTime) + return editStartTime.msecs(); + } else { + Audio::Timestamp nextEditStartTime(0, _parent->editList[_curEdit].timeOffset + _parent->editList[_curEdit].trackDuration, _decoder->_timeScale); + if (frameTime > nextEditStartTime) + return nextEditStartTime.msecs(); + } + + // Not past an edit boundary, so the frame time is what should be used + return frameTime.msecs(); } const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::decodeNextFrame() { @@ -422,39 +446,41 @@ const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::decodeNextFrame() bufferNextFrame(); } + // Update the edit list, if applicable + if (endOfCurEdit()) { + _curEdit++; + + if (atLastEdit()) + return 0; + + enterNewEditList(true); + } + const Graphics::Surface *frame = bufferNextFrame(); if (_reversed) { - if (_holdNextFrameStartTime) { - // Don't set the next frame start time here; we just did a seek - _holdNextFrameStartTime = false; + if (_durationOverride >= 0) { + // Use our own duration overridden from a media seek + _nextFrameStartTime -= _durationOverride; + _durationOverride = -1; } else { // Just need to subtract the time _nextFrameStartTime -= getFrameDuration(); } } else { - if (_holdNextFrameStartTime) { - // Don't set the next frame start time here; we just did a seek - _holdNextFrameStartTime = false; - } else if (_durationOverride >= 0) { - // Use our own duration from the edit list calculation - _nextFrameStartTime += _durationOverride; + if (_durationOverride >= 0) { + // Use our own duration overridden from a media seek + _nextFrameStartTime += _durationOverride; _durationOverride = -1; } else { _nextFrameStartTime += getFrameDuration(); } - - // Update the edit list, if applicable - // HACK: We're also accepting the time minus one because edit lists - // aren't as accurate as one would hope. - if (!atLastEdit() && getRateAdjustedFrameTime() >= getCurEditTimeOffset() + getCurEditTrackDuration() - 1) { - _curEdit++; - - if (!atLastEdit()) - enterNewEditList(true); - } } + // Handle forced dithering + if (frame && _forcedDitherPalette) + frame = forceDither(*frame); + if (frame && (_parent->scaleFactorX != 1 || _parent->scaleFactorY != 1)) { if (!_scaledSurface) { _scaledSurface = new Graphics::Surface(); @@ -468,6 +494,11 @@ const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::decodeNextFrame() return frame; } +const byte *QuickTimeDecoder::VideoTrackHandler::getPalette() const { + _dirtyPalette = false; + return _forcedDitherPalette ? _forcedDitherPalette : _curPalette; +} + bool QuickTimeDecoder::VideoTrackHandler::setReverse(bool reverse) { _reversed = reverse; @@ -485,11 +516,11 @@ bool QuickTimeDecoder::VideoTrackHandler::setReverse(bool reverse) { _curEdit = _parent->editCount - 1; _curFrame = _parent->frameCount; _nextFrameStartTime = _parent->editList[_curEdit].trackDuration + _parent->editList[_curEdit].timeOffset; - } else if (_holdNextFrameStartTime) { - // We just seeked, so "pivot" around the frame that should be displayed - _curFrame++; - _nextFrameStartTime -= getFrameDuration(); - _curFrame++; + } else if (_durationOverride >= 0) { + // We just had a media seek, so "pivot" around the frame that should + // be displayed. + _curFrame += 2; + _nextFrameStartTime += _durationOverride; } else { // We need to put _curFrame to be the one after the one that should be displayed. // Since we're on the frame that should be displaying right now, add one. @@ -497,20 +528,19 @@ bool QuickTimeDecoder::VideoTrackHandler::setReverse(bool reverse) { } } else { // Update the edit list, if applicable - // HACK: We're also accepting the time minus one because edit lists - // aren't as accurate as one would hope. - if (!atLastEdit() && getRateAdjustedFrameTime() >= getCurEditTimeOffset() + getCurEditTrackDuration() - 1) { + if (!atLastEdit() && endOfCurEdit()) { _curEdit++; if (atLastEdit()) return true; } - if (_holdNextFrameStartTime) { - // We just seeked, so "pivot" around the frame that should be displayed + if (_durationOverride >= 0) { + // We just had a media seek, so "pivot" around the frame that should + // be displayed. _curFrame--; - _nextFrameStartTime += getFrameDuration(); - } + _nextFrameStartTime -= _durationOverride; + } // We need to put _curFrame to be the one before the one that should be displayed. // Since we're on the frame that should be displaying right now, subtract one. @@ -559,10 +589,8 @@ Common::SeekableReadStream *QuickTimeDecoder::VideoTrackHandler::getNextFramePac } } - if (actualChunk < 0) { - warning("Could not find data for frame %d", _curFrame); - return 0; - } + if (actualChunk < 0) + error("Could not find data for frame %d", _curFrame); // Next seek to that frame Common::SeekableReadStream *stream = _decoder->_fd; @@ -617,30 +645,32 @@ void QuickTimeDecoder::VideoTrackHandler::enterNewEditList(bool bufferFrames) { if (atLastEdit()) return; + uint32 mediaTime = _parent->editList[_curEdit].mediaTime; uint32 frameNum = 0; - bool done = false; uint32 totalDuration = 0; - uint32 prevDuration = 0; + _durationOverride = -1; // Track down where the mediaTime is in the media // This is basically time -> frame mapping // Note that this code uses first frame = 0 - for (int32 i = 0; i < _parent->timeToSampleCount && !done; i++) { - for (int32 j = 0; j < _parent->timeToSample[i].count; j++) { - if (totalDuration == (uint32)_parent->editList[_curEdit].mediaTime) { - done = true; - prevDuration = totalDuration; - break; - } else if (totalDuration > (uint32)_parent->editList[_curEdit].mediaTime) { - done = true; - frameNum--; - break; - } + for (int32 i = 0; i < _parent->timeToSampleCount; i++) { + uint32 duration = _parent->timeToSample[i].count * _parent->timeToSample[i].duration; + + if (totalDuration + duration >= mediaTime) { + uint32 frameInc = (mediaTime - totalDuration) / _parent->timeToSample[i].duration; + frameNum += frameInc; + totalDuration += frameInc * _parent->timeToSample[i].duration; - prevDuration = totalDuration; - totalDuration += _parent->timeToSample[i].duration; - frameNum++; + // If we didn't get to the exact media time, mark an override for + // the time. + if (totalDuration != mediaTime) + _durationOverride = totalDuration + _parent->timeToSample[i].duration - mediaTime; + + break; } + + frameNum += _parent->timeToSample[i].count; + totalDuration += duration; } if (bufferFrames) { @@ -656,12 +686,6 @@ void QuickTimeDecoder::VideoTrackHandler::enterNewEditList(bool bufferFrames) { } _nextFrameStartTime = getCurEditTimeOffset(); - - // Set an override for the duration since we came up in-between two frames - if (prevDuration != totalDuration) - _durationOverride = totalDuration - prevDuration; - else - _durationOverride = -1; } const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::bufferNextFrame() { @@ -709,7 +733,12 @@ const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::bufferNextFrame() uint32 QuickTimeDecoder::VideoTrackHandler::getRateAdjustedFrameTime() const { // Figure out what time the next frame is at taking the edit list rate into account - uint32 convertedTime = (Common::Rational(_nextFrameStartTime - getCurEditTimeOffset()) / _parent->editList[_curEdit].mediaRate).toInt(); + Common::Rational offsetFromEdit = Common::Rational(_nextFrameStartTime - getCurEditTimeOffset()) / _parent->editList[_curEdit].mediaRate; + uint32 convertedTime = offsetFromEdit.toInt(); + + if ((offsetFromEdit.getNumerator() % offsetFromEdit.getDenominator()) > (offsetFromEdit.getDenominator() / 2)) + convertedTime++; + return convertedTime + getCurEditTimeOffset(); } @@ -739,4 +768,107 @@ bool QuickTimeDecoder::VideoTrackHandler::atLastEdit() const { return _curEdit == _parent->editCount; } +bool QuickTimeDecoder::VideoTrackHandler::endOfCurEdit() const { + // We're at the end of the edit once the next frame's time would + // bring us past the end of the edit. + return getRateAdjustedFrameTime() >= getCurEditTimeOffset() + getCurEditTrackDuration(); +} + +bool QuickTimeDecoder::VideoTrackHandler::canDither() const { + for (uint i = 0; i < _parent->sampleDescs.size(); i++) { + VideoSampleDesc *desc = (VideoSampleDesc *)_parent->sampleDescs[i]; + + if (!desc || !desc->_videoCodec) + return false; + } + + return true; +} + +void QuickTimeDecoder::VideoTrackHandler::setDither(const byte *palette) { + assert(canDither()); + + for (uint i = 0; i < _parent->sampleDescs.size(); i++) { + VideoSampleDesc *desc = (VideoSampleDesc *)_parent->sampleDescs[i]; + + if (desc->_videoCodec->canDither(Image::Codec::kDitherTypeQT)) { + // Codec dither + desc->_videoCodec->setDither(Image::Codec::kDitherTypeQT, palette); + } else { + // Forced dither + _forcedDitherPalette = new byte[256 * 3]; + memcpy(_forcedDitherPalette, palette, 256 * 3); + _ditherTable = Image::Codec::createQuickTimeDitherTable(_forcedDitherPalette, 256); + _dirtyPalette = true; + } + } +} + +namespace { + +// Return a pixel in RGB554 +uint16 makeDitherColor(byte r, byte g, byte b) { + return ((r & 0xF8) << 6) | ((g & 0xF8) << 1) | (b >> 4); +} + +// Default template to convert a dither color +template<typename PixelInt> +inline uint16 readDitherColor(PixelInt srcColor, const Graphics::PixelFormat& format, const byte *palette) { + byte r, g, b; + format.colorToRGB(srcColor, r, g, b); + return makeDitherColor(r, g, b); +} + +// Specialized version for 8bpp +template<> +inline uint16 readDitherColor(byte srcColor, const Graphics::PixelFormat& format, const byte *palette) { + return makeDitherColor(palette[srcColor * 3], palette[srcColor * 3 + 1], palette[srcColor * 3 + 2]); +} + +template<typename PixelInt> +void ditherFrame(const Graphics::Surface &src, Graphics::Surface &dst, const byte *ditherTable, const byte *palette = 0) { + static const uint16 colorTableOffsets[] = { 0x0000, 0xC000, 0x4000, 0x8000 }; + + for (int y = 0; y < dst.h; y++) { + const PixelInt *srcPtr = (const PixelInt *)src.getBasePtr(0, y); + byte *dstPtr = (byte *)dst.getBasePtr(0, y); + uint16 colorTableOffset = colorTableOffsets[y & 3]; + + for (int x = 0; x < dst.w; x++) { + uint16 color = readDitherColor(*srcPtr++, src.format, palette); + *dstPtr++ = ditherTable[colorTableOffset + color]; + colorTableOffset += 0x4000; + } + } +} + +} // End of anonymous namespace + +const Graphics::Surface *QuickTimeDecoder::VideoTrackHandler::forceDither(const Graphics::Surface &frame) { + if (frame.format.bytesPerPixel == 1) { + // This should always be true, but this is for sanity + if (!_curPalette) + return &frame; + + // If the palettes match, bail out + if (memcmp(_forcedDitherPalette, _curPalette, 256 * 3) == 0) + return &frame; + } + + // Need to create a new one + if (!_ditherFrame) { + _ditherFrame = new Graphics::Surface(); + _ditherFrame->create(frame.w, frame.h, Graphics::PixelFormat::createFormatCLUT8()); + } + + if (frame.format.bytesPerPixel == 1) + ditherFrame<byte>(frame, *_ditherFrame, _ditherTable, _curPalette); + else if (frame.format.bytesPerPixel == 2) + ditherFrame<uint16>(frame, *_ditherFrame, _ditherTable); + else if (frame.format.bytesPerPixel == 4) + ditherFrame<uint32>(frame, *_ditherFrame, _ditherTable); + + return _ditherFrame; +} + } // End of namespace Video |