diff options
Diffstat (limited to 'graphics')
-rw-r--r-- | graphics/VectorRenderer.cpp | 4 | ||||
-rw-r--r-- | graphics/VectorRendererSpec.cpp | 4 | ||||
-rw-r--r-- | graphics/colormasks.h | 24 | ||||
-rw-r--r-- | graphics/jpeg.cpp | 2 | ||||
-rw-r--r-- | graphics/pict.cpp | 104 | ||||
-rw-r--r-- | graphics/pict.h | 8 | ||||
-rw-r--r-- | graphics/surface.cpp | 80 | ||||
-rw-r--r-- | graphics/video/avi_decoder.cpp | 26 | ||||
-rw-r--r-- | graphics/video/avi_decoder.h | 5 | ||||
-rw-r--r-- | graphics/video/codecs/indeo3.cpp | 189 | ||||
-rw-r--r-- | graphics/video/codecs/indeo3.h | 2 | ||||
-rw-r--r-- | graphics/video/codecs/qdm2.cpp | 2 | ||||
-rw-r--r-- | graphics/video/coktel_decoder.cpp | 40 | ||||
-rw-r--r-- | graphics/video/qt_decoder.cpp | 68 | ||||
-rw-r--r-- | graphics/video/qt_decoder.h | 15 | ||||
-rw-r--r-- | graphics/video/smk_decoder.cpp | 94 | ||||
-rw-r--r-- | graphics/video/smk_decoder.h | 5 |
17 files changed, 414 insertions, 258 deletions
diff --git a/graphics/VectorRenderer.cpp b/graphics/VectorRenderer.cpp index 725d7f173b..0237712f13 100644 --- a/graphics/VectorRenderer.cpp +++ b/graphics/VectorRenderer.cpp @@ -103,7 +103,7 @@ void VectorRenderer::stepGetPositions(const DrawStep &step, const Common::Rect & break; default: - error("Vertical alignment in horizontal data."); + error("Vertical alignment in horizontal data"); } } else { in_x = area.left; @@ -132,7 +132,7 @@ void VectorRenderer::stepGetPositions(const DrawStep &step, const Common::Rect & break; default: - error("Horizontal alignment in vertical data."); + error("Horizontal alignment in vertical data"); } } else { in_y = area.top; diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp index 666a59b888..0ee13033af 100644 --- a/graphics/VectorRendererSpec.cpp +++ b/graphics/VectorRendererSpec.cpp @@ -164,7 +164,7 @@ inline frac_t fp_sqroot(uint32 x) { x--; px -= pitch; \ } \ a2 = (T >> 8); \ - a1 = ~a2; \ + a1 = ~a2 >> 4; \ } @@ -255,7 +255,7 @@ void VectorRendererSpec<PixelType>:: fillSurface() { byte *ptr = (byte *)_activeSurface->getBasePtr(0, 0); - int h = _activeSurface->h ; + int h = _activeSurface->h; int pitch = _activeSurface->pitch; if (Base::_fillMode == kFillBackground) { diff --git a/graphics/colormasks.h b/graphics/colormasks.h index 1ab78fccc5..824d980ca3 100644 --- a/graphics/colormasks.h +++ b/graphics/colormasks.h @@ -168,6 +168,30 @@ struct ColorMasks<1555> { }; template<> +struct ColorMasks<5551> { + enum { + kBytesPerPixel = 2, + + kAlphaBits = 1, + kRedBits = 5, + kGreenBits = 5, + kBlueBits = 5, + + kAlphaShift = 0, + kRedShift = kGreenBits+kBlueBits+kAlphaBits, + kGreenShift = kBlueBits+kAlphaBits, + kBlueShift = kAlphaBits, + + kAlphaMask = ((1 << kAlphaBits) - 1) << kAlphaShift, + kRedMask = ((1 << kRedBits) - 1) << kRedShift, + kGreenMask = ((1 << kGreenBits) - 1) << kGreenShift, + kBlueMask = ((1 << kBlueBits) - 1) << kBlueShift, + + kRedBlueMask = kRedMask | kBlueMask + }; +}; + +template<> struct ColorMasks<4444> { enum { kBytesPerPixel = 2, diff --git a/graphics/jpeg.cpp b/graphics/jpeg.cpp index 1c7ad64646..fbc81b5db2 100644 --- a/graphics/jpeg.cpp +++ b/graphics/jpeg.cpp @@ -198,7 +198,7 @@ bool JPEG::readJFIF() { byte majorVersion = _str->readByte(); byte minorVersion = _str->readByte(); if(majorVersion != 1 || minorVersion != 1) - warning("JPEG::readJFIF() Non-v1.1 JPEGs may not be handled correctly!"); + warning("JPEG::readJFIF() Non-v1.1 JPEGs may not be handled correctly"); /* byte densityUnits = */ _str->readByte(); /* uint16 xDensity = */ _str->readUint16BE(); /* uint16 yDensity = */ _str->readUint16BE(); diff --git a/graphics/pict.cpp b/graphics/pict.cpp index f0dd7bbc6f..ebf643439b 100644 --- a/graphics/pict.cpp +++ b/graphics/pict.cpp @@ -48,6 +48,8 @@ PictDecoder::~PictDecoder() { Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *palette) { assert(stream); + _outputSurface = 0; + uint16 fileSize = stream->readUint16BE(); // If we have no file size here, we probably have a PICT from a file @@ -61,9 +63,6 @@ Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *pale _imageRect.bottom = stream->readUint16BE(); _imageRect.right = stream->readUint16BE(); _imageRect.debugPrint(0, "PICT Rect:"); - - Graphics::Surface *image = new Graphics::Surface(); - image->create(_imageRect.width(), _imageRect.height(), _pixelFormat.bytesPerPixel); _isPaletted = false; // NOTE: This is only a subset of the full PICT format. @@ -96,10 +95,10 @@ Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *pale } else if (opcode == 0x001E) { // DefHilite // Ignore, Contains no Data } else if (opcode == 0x0098) { // PackBitsRect - decodeDirectBitsRect(stream, image, true); + decodeDirectBitsRect(stream, _imageRect.width(), _imageRect.height(), true); _isPaletted = true; } else if (opcode == 0x009A) { // DirectBitsRect - decodeDirectBitsRect(stream, image, false); + decodeDirectBitsRect(stream, _imageRect.width(), _imageRect.height(), false); } else if (opcode == 0x00A1) { // LongComment stream->readUint16BE(); uint16 dataSize = stream->readUint16BE(); @@ -119,7 +118,7 @@ Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *pale origResRect.right = stream->readUint16BE(); stream->readUint32BE(); // Reserved } else if (opcode == 0x8200) { // CompressedQuickTime - decodeCompressedQuickTime(stream, image); + decodeCompressedQuickTime(stream); break; } else { warning("Unknown PICT opcode %04x", opcode); @@ -130,7 +129,7 @@ Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *pale if (palette && _isPaletted) memcpy(palette, _palette, 256 * 4); - return image; + return _outputSurface; } PictDecoder::PixMap PictDecoder::readPixMap(Common::SeekableReadStream *stream, bool hasBaseAddr) { @@ -163,7 +162,7 @@ struct DirectBitsRectData { uint16 mode; }; -void PictDecoder::decodeDirectBitsRect(Common::SeekableReadStream *stream, Surface *image, bool hasPalette) { +void PictDecoder::decodeDirectBitsRect(Common::SeekableReadStream *stream, uint16 width, uint16 height, bool hasPalette) { static const PixelFormat directBitsFormat16 = PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); // Clear the palette @@ -197,11 +196,18 @@ void PictDecoder::decodeDirectBitsRect(Common::SeekableReadStream *stream, Surfa directBitsData.dstRect.right = stream->readUint16BE(); directBitsData.mode = stream->readUint16BE(); - if (directBitsData.pixMap.pixelSize != 8 && directBitsData.pixMap.pixelSize != 16 && directBitsData.pixMap.pixelSize != 32) - error("Unhandled DirectBitsRect bitsPerPixel %d", directBitsData.pixMap.pixelSize); + byte bytesPerPixel = 0; - byte bytesPerPixel = (directBitsData.pixMap.pixelSize == 32) ? 3 : directBitsData.pixMap.pixelSize / 8; - byte *buffer = new byte[image->w * image->h * bytesPerPixel]; + if (directBitsData.pixMap.pixelSize <= 8) + bytesPerPixel = 1; + else if (directBitsData.pixMap.pixelSize == 32) + bytesPerPixel = 3; + else + bytesPerPixel = directBitsData.pixMap.pixelSize / 8; + + _outputSurface = new Graphics::Surface(); + _outputSurface->create(width, height, (bytesPerPixel == 1) ? 1 : _pixelFormat.bytesPerPixel); + byte *buffer = new byte[width * height * bytesPerPixel]; // Read in amount of data per row for (uint16 i = 0; i < directBitsData.pixMap.bounds.height(); i++) { @@ -217,37 +223,37 @@ void PictDecoder::decodeDirectBitsRect(Common::SeekableReadStream *stream, Surfa error("Unpacked DirectBitsRect data (not padded)"); } else if (directBitsData.pixMap.packType == 0 || directBitsData.pixMap.packType > 2) { // Packed uint16 byteCount = (directBitsData.pixMap.rowBytes > 250) ? stream->readUint16BE() : stream->readByte(); - decodeDirectBitsLine(buffer + i * image->w * bytesPerPixel, directBitsData.pixMap.rowBytes, stream->readStream(byteCount), bytesPerPixel); + decodeDirectBitsLine(buffer + i * _outputSurface->w * bytesPerPixel, directBitsData.pixMap.rowBytes, stream->readStream(byteCount), directBitsData.pixMap.pixelSize, bytesPerPixel); } } if (bytesPerPixel == 1) { // Just copy to the image - memcpy(image->pixels, buffer, image->w * image->h); + memcpy(_outputSurface->pixels, buffer, _outputSurface->w * _outputSurface->h); } else if (bytesPerPixel == 2) { // Convert from 16-bit to whatever surface we need - for (uint16 y = 0; y < image->h; y++) { - for (uint16 x = 0; x < image->w; x++) { + for (uint16 y = 0; y < _outputSurface->h; y++) { + for (uint16 x = 0; x < _outputSurface->w; x++) { byte r = 0, g = 0, b = 0; - uint32 color = READ_BE_UINT16(buffer + (y * image->w + x) * bytesPerPixel); + uint32 color = READ_BE_UINT16(buffer + (y * _outputSurface->w + x) * bytesPerPixel); directBitsFormat16.colorToRGB(color, r, g, b); if (_pixelFormat.bytesPerPixel == 2) - *((uint16 *)image->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); + *((uint16 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); else - *((uint32 *)image->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); + *((uint32 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); } } } else { // Convert from 24-bit (planar!) to whatever surface we need - for (uint16 y = 0; y < image->h; y++) { - for (uint16 x = 0; x < image->w; x++) { - byte r = *(buffer + y * image->w * 3 + x); - byte g = *(buffer + y * image->w * 3 + image->w + x); - byte b = *(buffer + y * image->w * 3 + image->w * 2 + x); + for (uint16 y = 0; y < _outputSurface->h; y++) { + for (uint16 x = 0; x < _outputSurface->w; x++) { + byte r = *(buffer + y * _outputSurface->w * 3 + x); + byte g = *(buffer + y * _outputSurface->w * 3 + _outputSurface->w + x); + byte b = *(buffer + y * _outputSurface->w * 3 + _outputSurface->w * 2 + x); if (_pixelFormat.bytesPerPixel == 2) - *((uint16 *)image->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); + *((uint16 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); else - *((uint32 *)image->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); + *((uint32 *)_outputSurface->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); } } } @@ -255,7 +261,7 @@ void PictDecoder::decodeDirectBitsRect(Common::SeekableReadStream *stream, Surfa delete[] buffer; } -void PictDecoder::decodeDirectBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bytesPerPixel) { +void PictDecoder::decodeDirectBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bitsPerPixel, byte bytesPerPixel) { uint32 dataDecoded = 0; byte bytesPerDecode = (bytesPerPixel == 2) ? 2 : 1; @@ -270,14 +276,17 @@ void PictDecoder::decodeDirectBitsLine(byte *out, uint32 length, Common::Seekabl if (bytesPerDecode == 2) { WRITE_BE_UINT16(out, value); out += 2; - } else - *out++ = value; + } else { + outputPixelBuffer(out, value, bitsPerPixel); + } } dataDecoded += runSize * bytesPerDecode; } else { uint32 runSize = (op + 1) * bytesPerDecode; + for (uint32 i = 0; i < runSize; i++) - *out++ = data->readByte(); + outputPixelBuffer(out, data->readByte(), bitsPerPixel); + dataDecoded += runSize; } } @@ -292,12 +301,31 @@ void PictDecoder::decodeDirectBitsLine(byte *out, uint32 length, Common::Seekabl delete data; } +void PictDecoder::outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel) { + switch (bitsPerPixel) { + case 1: + for (int i = 7; i >= 0; i--) + *out++ = (value >> i) & 1; + break; + case 2: + for (int i = 6; i >= 0; i -= 2) + *out++ = (value >> i) & 3; + break; + case 4: + *out++ = (value >> 4) & 0xf; + *out++ = value & 0xf; + break; + default: + *out++ = value; + } +} + // Compressed QuickTime details can be found here: // http://developer.apple.com/documentation/QuickTime/Rm/CompressDecompress/ImageComprMgr/B-Chapter/2TheImageCompression.html // http://developer.apple.com/documentation/QuickTime/Rm/CompressDecompress/ImageComprMgr/F-Chapter/6WorkingwiththeImage.html // I'm just ignoring that because Myst ME uses none of that extra stuff. The offset is always the same. -void PictDecoder::decodeCompressedQuickTime(Common::SeekableReadStream *stream, Graphics::Surface *image) { +void PictDecoder::decodeCompressedQuickTime(Common::SeekableReadStream *stream) { uint32 dataSize = stream->readUint32BE(); uint32 startPos = stream->pos(); @@ -310,23 +338,21 @@ void PictDecoder::decodeCompressedQuickTime(Common::SeekableReadStream *stream, Surface *uComponent = _jpeg->getComponent(2); Surface *vComponent = _jpeg->getComponent(3); - Surface jpegImage; - jpegImage.create(yComponent->w, yComponent->h, _pixelFormat.bytesPerPixel); + _outputSurface = new Graphics::Surface(); + _outputSurface->create(yComponent->w, yComponent->h, _pixelFormat.bytesPerPixel); - for (uint16 i = 0; i < jpegImage.h; i++) { - for (uint16 j = 0; j < jpegImage.w; j++) { + for (uint16 i = 0; i < _outputSurface->h; i++) { + for (uint16 j = 0; j < _outputSurface->w; j++) { byte r = 0, g = 0, b = 0; YUV2RGB(*((byte *)yComponent->getBasePtr(j, i)), *((byte *)uComponent->getBasePtr(j, i)), *((byte *)vComponent->getBasePtr(j, i)), r, g, b); if (_pixelFormat.bytesPerPixel == 2) - *((uint16 *)jpegImage.getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b); + *((uint16 *)_outputSurface->getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b); else - *((uint32 *)jpegImage.getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b); + *((uint32 *)_outputSurface->getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b); } } - image->copyFrom(jpegImage); stream->seek(startPos + dataSize); - jpegImage.free(); delete jpegStream; } diff --git a/graphics/pict.h b/graphics/pict.h index 12681f6128..a9ea170292 100644 --- a/graphics/pict.h +++ b/graphics/pict.h @@ -72,10 +72,12 @@ private: JPEG *_jpeg; byte _palette[256 * 4]; bool _isPaletted; + Graphics::Surface *_outputSurface; - void decodeDirectBitsRect(Common::SeekableReadStream *stream, Surface *image, bool hasPalette); - void decodeDirectBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bytesPerPixel); - void decodeCompressedQuickTime(Common::SeekableReadStream *stream, Surface *image); + void decodeDirectBitsRect(Common::SeekableReadStream *stream, uint16 width, uint16 height, bool hasPalette); + void decodeDirectBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bitsPerPixel, byte bytesPerPixel); + void decodeCompressedQuickTime(Common::SeekableReadStream *stream); + void outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel); }; } // End of namespace Graphics diff --git a/graphics/surface.cpp b/graphics/surface.cpp index 353803c23a..f06c24c2cd 100644 --- a/graphics/surface.cpp +++ b/graphics/surface.cpp @@ -24,6 +24,7 @@ #include "common/algorithm.h" #include "common/util.h" +#include "common/endian.h" #include "graphics/primitives.h" #include "graphics/surface.h" @@ -143,8 +144,10 @@ void Surface::fillRect(Common::Rect r, uint32 color) { lineLen *= 2; if ((uint16)color != ((color & 0xff) | (color & 0xff) << 8)) useMemset = false; + } else if (bytesPerPixel == 4) { + useMemset = false; } else if (bytesPerPixel != 1) { - error("Surface::fillRect: bytesPerPixel must be 1 or 2"); + error("Surface::fillRect: bytesPerPixel must be 1, 2 or 4"); } if (useMemset) { @@ -154,10 +157,18 @@ void Surface::fillRect(Common::Rect r, uint32 color) { ptr += pitch; } } else { - uint16 *ptr = (uint16 *)getBasePtr(r.left, r.top); - while (height--) { - Common::set_to(ptr, ptr + width, (uint16)color); - ptr += pitch/2; + if (bytesPerPixel == 2) { + uint16 *ptr = (uint16 *)getBasePtr(r.left, r.top); + while (height--) { + Common::set_to(ptr, ptr + width, (uint16)color); + ptr += pitch/2; + } + } else { + uint32 *ptr = (uint32 *)getBasePtr(r.left, r.top); + while (height--) { + Common::set_to(ptr, ptr + width, color); + ptr += pitch / 4; + } } } } @@ -169,65 +180,72 @@ void Surface::frameRect(const Common::Rect &r, uint32 color) { vLine(r.right-1, r.top, r.bottom-1, color); } -// FIXME: LordHoto asks why is this in Surface, since this -// just supports 8bpp surfaces. Looks like someone wants -// to subclass Surface to add this or it should be extended -// to support 16bpp (or marked as just working for 8bpp -// surfaces). void Surface::move(int dx, int dy, int height) { - // This function currently just works with 8bpp surfaces - assert(bytesPerPixel == 1); - // Short circuit check - do we have to do anything anyway? if ((dx == 0 && dy == 0) || height <= 0) return; + if (bytesPerPixel != 1 && bytesPerPixel != 2) + error("Surface::move: bytesPerPixel must be 1 or 2"); + byte *src, *dst; int x, y; // vertical movement if (dy > 0) { // move down - copy from bottom to top - dst = (byte *)pixels + (height - 1) * w; - src = dst - dy * w; + dst = (byte *)pixels + (height - 1) * pitch; + src = dst - dy * pitch; for (y = dy; y < height; y++) { - memcpy(dst, src, w); - src -= w; - dst -= w; + memcpy(dst, src, pitch); + src -= pitch; + dst -= pitch; } } else if (dy < 0) { // move up - copy from top to bottom dst = (byte *)pixels; - src = dst - dy * w; + src = dst - dy * pitch; for (y = -dy; y < height; y++) { - memcpy(dst, src, w); - src += w; - dst += w; + memcpy(dst, src, pitch); + src += pitch; + dst += pitch; } } // horizontal movement if (dx > 0) { // move right - copy from right to left - dst = (byte *)pixels + (w - 1); - src = dst - dx; + dst = (byte *)pixels + (pitch - bytesPerPixel); + src = dst - (dx * bytesPerPixel); for (y = 0; y < height; y++) { for (x = dx; x < w; x++) { - *dst-- = *src--; + if (bytesPerPixel == 1) { + *dst-- = *src--; + } else if (bytesPerPixel == 2) { + *(uint16 *)dst = *(const uint16 *)src; + src -= 2; + dst -= 2; + } } - src += w + (w - dx); - dst += w + (w - dx); + src += pitch + (pitch - dx * bytesPerPixel); + dst += pitch + (pitch - dx * bytesPerPixel); } } else if (dx < 0) { // move left - copy from left to right dst = (byte *)pixels; - src = dst - dx; + src = dst - (dx * bytesPerPixel); for (y = 0; y < height; y++) { for (x = -dx; x < w; x++) { - *dst++ = *src++; + if (bytesPerPixel == 1) { + *dst++ = *src++; + } else if (bytesPerPixel == 2) { + *(uint16 *)dst = *(const uint16 *)src; + src += 2; + dst += 2; + } } - src += w - (w + dx); - dst += w - (w + dx); + src += pitch - (pitch + dx * bytesPerPixel); + dst += pitch - (pitch + dx * bytesPerPixel); } } } diff --git a/graphics/video/avi_decoder.cpp b/graphics/video/avi_decoder.cpp index 4973cb3eb0..ceca89b8ee 100644 --- a/graphics/video/avi_decoder.cpp +++ b/graphics/video/avi_decoder.cpp @@ -42,6 +42,20 @@ namespace Graphics { +/* +static byte char2num(char c) { + return (c >= 48 && c <= 57) ? c - 48 : 0; +} + +static byte getStreamNum(uint32 tag) { + return char2num((char)(tag >> 24)) * 16 + char2num((char)(tag >> 16)); +} +*/ + +static uint16 getStreamType(uint32 tag) { + return tag & 0xffff; +} + AviDecoder::AviDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : _mixer(mixer) { _soundType = soundType; @@ -418,16 +432,4 @@ Audio::QueuingAudioStream *AviDecoder::createAudioStream() { return NULL; } -byte AviDecoder::char2num(char c) { - return (c >= 48 && c <= 57) ? c - 48 : 0; -} - -byte AviDecoder::getStreamNum(uint32 tag) { - return char2num((char)(tag >> 24)) * 16 + char2num((char)(tag >> 16)); -} - -uint16 AviDecoder::getStreamType(uint32 tag) { - return tag & 0xffff; -} - } // End of namespace Graphics diff --git a/graphics/video/avi_decoder.h b/graphics/video/avi_decoder.h index 72cf2d7ef5..68795149c0 100644 --- a/graphics/video/avi_decoder.h +++ b/graphics/video/avi_decoder.h @@ -221,11 +221,6 @@ private: Audio::SoundHandle *_audHandle; Audio::QueuingAudioStream *_audStream; Audio::QueuingAudioStream *createAudioStream(); - - // Helper functions - static byte char2num(char c); - static byte getStreamNum(uint32 tag); - static uint16 getStreamType(uint32 tag); }; } // End of namespace Graphics diff --git a/graphics/video/codecs/indeo3.cpp b/graphics/video/codecs/indeo3.cpp index f59ae81e81..443340de2f 100644 --- a/graphics/video/codecs/indeo3.cpp +++ b/graphics/video/codecs/indeo3.cpp @@ -69,27 +69,22 @@ PixelFormat Indeo3Decoder::getPixelFormat() const { return _pixelFormat; } -bool Indeo3Decoder::isIndeo3(byte *data, uint32 dataLen) { - // No data, no Indeo 3 - if (!data) - return false; - +bool Indeo3Decoder::isIndeo3(Common::SeekableReadStream &stream) { // Less than 16 bytes? This can't be right - if (dataLen < 16) + if (stream.size() < 16) return false; + uint32 id0 = stream.readUint32LE(); + uint32 id1 = stream.readUint32LE(); + uint32 id2 = stream.readUint32LE(); + uint32 id3 = stream.readUint32LE(); + // Unknown, but according to the docs, always 0 - if (READ_LE_UINT32(data + 4) != 0) + if (id1 != 0) return false; - uint32 id; - id = READ_LE_UINT32(data ); // frame number - id ^= READ_LE_UINT32(data + 4); // unknown - id ^= READ_LE_UINT32(data + 8); // checksum - id ^= READ_LE_UINT32(data + 12); // frame data length - // These 4 uint32s XOR'd need to spell "FRMH" - if (id != MKID_BE('FRMH')) + if ((id0 ^ id1 ^ id2 ^ id3) != MKID_BE('FRMH')) return false; return true; @@ -174,31 +169,23 @@ void Indeo3Decoder::allocFrames() { } Surface *Indeo3Decoder::decodeImage(Common::SeekableReadStream *stream) { - uint32 dataLen = stream->size(); - - byte *inData = new byte[dataLen]; - - if (stream->read(inData, dataLen) != dataLen) - return 0; - // Not Indeo 3? Fail - if (!isIndeo3(inData, dataLen)) + if (!isIndeo3(*stream)) return 0; - uint32 frameDataLen = READ_LE_UINT32(inData + 12); + stream->seek(12); + uint32 frameDataLen = stream->readUint32LE(); // Less data than the frame should have? Fail - if (dataLen < (frameDataLen - 16)) + if (stream->size() < (int)(frameDataLen - 16)) return 0; - Common::MemoryReadStream frame(inData, dataLen); + stream->seek(16); // Behind header + stream->skip(2); // Unknown - frame.skip(16); // Header - frame.skip(2); // Unknown - - uint16 flags1 = frame.readUint16LE(); - uint32 flags3 = frame.readUint32LE(); - uint8 flags2 = frame.readByte(); + uint16 flags1 = stream->readUint16LE(); + uint32 flags3 = stream->readUint32LE(); + uint8 flags2 = stream->readByte(); // Finding the reference frame if (flags1 & 0x200) { @@ -212,77 +199,153 @@ Surface *Indeo3Decoder::decodeImage(Common::SeekableReadStream *stream) { if (flags3 == 0x80) return _surface; - frame.skip(3); + stream->skip(3); - uint16 fHeight = frame.readUint16LE(); - uint16 fWidth = frame.readUint16LE(); + uint16 fHeight = stream->readUint16LE(); + uint16 fWidth = stream->readUint16LE(); uint32 chromaHeight = ((fHeight >> 2) + 3) & 0x7FFC; uint32 chromaWidth = ((fWidth >> 2) + 3) & 0x7FFC; uint32 offs; - uint32 offsY = frame.readUint32LE() + 16; - uint32 offsU = frame.readUint32LE() + 16; - uint32 offsV = frame.readUint32LE() + 16; + uint32 offsY = stream->readUint32LE() + 16; + uint32 offsU = stream->readUint32LE() + 16; + uint32 offsV = stream->readUint32LE() + 16; + + stream->skip(4); + + uint32 hPos = stream->pos(); + + if (offsY < hPos) { + warning("Indeo3Decoder::decodeImage: offsY < hPos"); + return 0; + } + if (offsU < hPos) { + warning("Indeo3Decoder::decodeImage: offsY < hPos"); + return 0; + } + if (offsV < hPos) { + warning("Indeo3Decoder::decodeImage: offsY < hPos"); + return 0; + } - frame.skip(4); + uint32 dataSize = stream->size() - hPos; - uint32 hPos = frame.pos(); + byte *inData = new byte[dataSize]; + + if (stream->read(inData, dataSize) != dataSize) { + delete[] inData; + return 0; + } - byte *hdr_pos = inData + hPos; + byte *hdr_pos = inData; byte *buf_pos; // Luminance Y - frame.seek(offsY); - buf_pos = inData + offsY + 4; - offs = frame.readUint32LE(); + stream->seek(offsY); + buf_pos = inData + offsY + 4 - hPos; + offs = stream->readUint32LE(); decodeChunk(_cur_frame->Ybuf, _ref_frame->Ybuf, fWidth, fHeight, buf_pos + offs * 2, flags2, hdr_pos, buf_pos, MIN<int>(fWidth, 160)); // Chrominance U - frame.seek(offsU); - buf_pos = inData + offsU + 4; - offs = frame.readUint32LE(); + stream->seek(offsU); + buf_pos = inData + offsU + 4 - hPos; + offs = stream->readUint32LE(); decodeChunk(_cur_frame->Vbuf, _ref_frame->Vbuf, chromaWidth, chromaHeight, buf_pos + offs * 2, flags2, hdr_pos, buf_pos, MIN<int>(chromaWidth, 40)); // Chrominance V - frame.seek(offsV); - buf_pos = inData + offsV + 4; - offs = frame.readUint32LE(); + stream->seek(offsV); + buf_pos = inData + offsV + 4 - hPos; + offs = stream->readUint32LE(); decodeChunk(_cur_frame->Ubuf, _ref_frame->Ubuf, chromaWidth, chromaHeight, buf_pos + offs * 2, flags2, hdr_pos, buf_pos, MIN<int>(chromaWidth, 40)); + delete[] inData; + // Blit the frame onto the surface const byte *srcY = _cur_frame->Ybuf; const byte *srcU = _cur_frame->Ubuf; const byte *srcV = _cur_frame->Vbuf; byte *dest = (byte *)_surface->pixels; + + const byte *srcUP = srcU; + const byte *srcVP = srcV; + const byte *srcUN = srcU + chromaWidth; + const byte *srcVN = srcV + chromaWidth; + + uint32 scaleWidth = _surface->w / fWidth; + uint32 scaleHeight = _surface->h / fHeight; + for (uint32 y = 0; y < fHeight; y++) { byte *rowDest = dest; - for (uint32 x = 0; x < fWidth; x++, rowDest += _surface->bytesPerPixel) { - const byte cY = srcY[x]; - const byte cU = srcU[x >> 2]; - const byte cV = srcV[x >> 2]; + for (uint32 sH = 0; sH < scaleHeight; sH++) { + for (uint32 x = 0; x < fWidth; x++) { + uint32 xP = MAX<int32>((x >> 2) - 1, 0); + uint32 xN = MIN<int32>((x >> 2) + 1, chromaWidth - 1); + + byte cY = srcY[x]; + byte cU = srcU[x >> 2]; + byte cV = srcV[x >> 2]; + + if (((x % 4) == 0) && ((y % 4) == 0)) { + cU = (((uint32) cU) + ((uint32) srcUP[xP])) / 2; + cV = (((uint32) cV) + ((uint32) srcVP[xP])) / 2; + } else if (((x % 4) == 3) && ((y % 4) == 0)) { + cU = (((uint32) cU) + ((uint32) srcUP[xN])) / 2; + cV = (((uint32) cV) + ((uint32) srcVP[xN])) / 2; + } else if (((x % 4) == 0) && ((y % 4) == 3)) { + cU = (((uint32) cU) + ((uint32) srcUN[xP])) / 2; + cV = (((uint32) cV) + ((uint32) srcVN[xP])) / 2; + } else if (((x % 4) == 3) && ((y % 4) == 3)) { + cU = (((uint32) cU) + ((uint32) srcUN[xN])) / 2; + cV = (((uint32) cV) + ((uint32) srcVN[xN])) / 2; + } else if ( (x % 4) == 0) { + cU = (((uint32) cU) + ((uint32) srcU[xP])) / 2; + cV = (((uint32) cV) + ((uint32) srcV[xP])) / 2; + } else if ( (x % 4) == 3) { + cU = (((uint32) cU) + ((uint32) srcU[xN])) / 2; + cV = (((uint32) cV) + ((uint32) srcV[xN])) / 2; + } else if ( (y % 4) == 0) { + cU = (((uint32) cU) + ((uint32) srcUP[x >> 2])) / 2; + cV = (((uint32) cV) + ((uint32) srcVP[x >> 2])) / 2; + } else if ( (y % 4) == 3) { + cU = (((uint32) cU) + ((uint32) srcUN[x >> 2])) / 2; + cV = (((uint32) cV) + ((uint32) srcVN[x >> 2])) / 2; + } + + byte r = 0, g = 0, b = 0; + YUV2RGB(cY, cU, cV, r, g, b); - byte r = 0, g = 0, b = 0; - YUV2RGB(cY, cU, cV, r, g, b); + const uint32 color = _pixelFormat.RGBToColor(r, g, b); - const uint32 color = _pixelFormat.RGBToColor(r, g, b); + for (uint32 sW = 0; sW < scaleWidth; sW++, rowDest += _surface->bytesPerPixel) { + if (_surface->bytesPerPixel == 1) + *((uint8 *)rowDest) = (uint8)color; + else if (_surface->bytesPerPixel == 2) + *((uint16 *)rowDest) = (uint16)color; + } + } - if (_surface->bytesPerPixel == 1) - *((uint8 *)rowDest) = (uint8)color; - else if (_surface->bytesPerPixel == 2) - *((uint16 *)rowDest) = (uint16)color; + dest += _surface->pitch; } - dest += _surface->pitch; srcY += fWidth; if ((y & 3) == 3) { - srcU += fWidth >> 2; - srcV += fWidth >> 2; + srcU += chromaWidth; + srcV += chromaWidth; + + if (y > 0) { + srcUP += chromaWidth; + srcVP += chromaWidth; + } + if (y < (fHeight - 4U)) { + srcUN += chromaWidth; + srcVN += chromaWidth; + } } } diff --git a/graphics/video/codecs/indeo3.h b/graphics/video/codecs/indeo3.h index 8d50b74b1e..319a8e5d42 100644 --- a/graphics/video/codecs/indeo3.h +++ b/graphics/video/codecs/indeo3.h @@ -49,7 +49,7 @@ public: Surface *decodeImage(Common::SeekableReadStream *stream); PixelFormat getPixelFormat() const; - static bool isIndeo3(byte *data, uint32 dataLen); + static bool isIndeo3(Common::SeekableReadStream &stream); private: Surface *_surface; diff --git a/graphics/video/codecs/qdm2.cpp b/graphics/video/codecs/qdm2.cpp index 9f151b4ba8..0050b256d1 100644 --- a/graphics/video/codecs/qdm2.cpp +++ b/graphics/video/codecs/qdm2.cpp @@ -1775,7 +1775,7 @@ QDM2Stream::QDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadS tmp = extraData->readUint32BE(); debug(1, "QDM2Stream::QDM2Stream() extraType: %d", tmp); if (tmp == MKID_BE('QDMC')) - warning("QDM2Stream::QDM2Stream() QDMC stream type not supported."); + warning("QDM2Stream::QDM2Stream() QDMC stream type not supported"); else if (tmp != MKID_BE('QDM2')) error("QDM2Stream::QDM2Stream() Unsupported stream type"); diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index 0709288091..10be09eb23 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -399,11 +399,11 @@ void CoktelDecoder::renderBlockWhole(const byte *src, Common::Rect &rect) { rect.clip(_surface.w, _surface.h); - byte *dst = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left; + byte *dst = (byte *)_surface.pixels + (rect.top * _surface.pitch) + rect.left * _surface.bytesPerPixel; for (int i = 0; i < rect.height(); i++) { - memcpy(dst, src, rect.width()); + memcpy(dst, src, rect.width() * _surface.bytesPerPixel); - src += srcRect.width(); + src += srcRect.width() * _surface.bytesPerPixel; dst += _surface.pitch; } } @@ -581,7 +581,7 @@ uint32 CoktelDecoder::getTimeToNextFrame() const { // the middle of a long video. if (!hasSound()) - return Common::Rational(1000, _frameRate).toInt(); + return (1000 / _frameRate).toInt(); // If there /is/ audio, we do need to keep video and audio // in sync, though. @@ -1589,12 +1589,6 @@ bool VMDDecoder::load(Common::SeekableReadStream *stream) { if (_version & 4) _bytesPerPixel = handle + 1; - if (_bytesPerPixel != 1) { - warning("TODO: _bytesPerPixel = %d", _bytesPerPixel); - close(); - return false; - } - if (_bytesPerPixel > 3) { warning("VMDDecoder::load(): Requested %d bytes per pixel (%d, %d, %d)", _bytesPerPixel, headerLength, handle, _version); @@ -1696,6 +1690,11 @@ bool VMDDecoder::assessVideoProperties() { _bytesPerPixel = n; } + if ((_bytesPerPixel > 1) && !_externalCodec) { + warning("VMDDecoder::assessVideoProperties(): TODO: Internal _bytesPerPixel == %d", _bytesPerPixel); + return false; + } + if (_hasVideo) { if ((_frameDataSize == 0) || (_frameDataSize > 1048576)) _frameDataSize = _width * _height + 1000; @@ -1837,7 +1836,7 @@ bool VMDDecoder::readFiles() { break; if (_frames[i].parts[j].type == kPartTypeFile) { - File file;; + File file; file.offset = _stream->pos() + 20; file.size = _frames[i].parts[j].size; @@ -2087,9 +2086,19 @@ bool VMDDecoder::renderFrame(Common::Rect &rect) { return false; if (_externalCodec) { - // TODO - warning("_external codec"); - return false; + if (!_codec) + return false; + + Common::MemoryReadStream frameStream(_frameData, _frameDataLen); + Surface *codecSurf = _codec->decodeImage(&frameStream); + if (!codecSurf) + return false; + + rect = Common::Rect(_x, _y, _x + codecSurf->w, _y + codecSurf->h); + rect.clip(Common::Rect(_x, _y, _x + _width, _y + _height)); + + renderBlockWhole((const byte *) codecSurf->pixels, rect); + return true; } if (_blitMode > 0) { @@ -2376,6 +2385,9 @@ byte *VMDDecoder::deADPCM(const byte *data, uint32 &size, int32 init, int32 inde } PixelFormat VMDDecoder::getPixelFormat() const { + if (_externalCodec && _codec) + return _codec->getPixelFormat(); + return PixelFormat::createFormatCLUT8(); } diff --git a/graphics/video/qt_decoder.cpp b/graphics/video/qt_decoder.cpp index 470441dab8..8437f0af43 100644 --- a/graphics/video/qt_decoder.cpp +++ b/graphics/video/qt_decoder.cpp @@ -67,6 +67,8 @@ QuickTimeDecoder::QuickTimeDecoder() : VideoDecoder() { _numStreams = 0; _fd = 0; _scaledSurface = 0; + _scaleFactorX = 1; + _scaleFactorY = 1; _dirtyPalette = false; _resFork = new Common::MacResManager(); @@ -82,14 +84,14 @@ uint16 QuickTimeDecoder::getWidth() const { if (_videoStreamIndex < 0) return 0; - return _streams[_videoStreamIndex]->width / getScaleMode(); + return (Common::Rational(_streams[_videoStreamIndex]->width) / getScaleFactorX()).toInt(); } uint16 QuickTimeDecoder::getHeight() const { if (_videoStreamIndex < 0) return 0; - return _streams[_videoStreamIndex]->height / getScaleMode(); + return (Common::Rational(_streams[_videoStreamIndex]->height) / getScaleFactorY()).toInt(); } uint32 QuickTimeDecoder::getFrameCount() const { @@ -113,11 +115,18 @@ uint32 QuickTimeDecoder::getCodecTag() { return _streams[_videoStreamIndex]->codec_tag; } -ScaleMode QuickTimeDecoder::getScaleMode() const { +Common::Rational QuickTimeDecoder::getScaleFactorX() const { if (_videoStreamIndex < 0) - return kScaleNormal; + return 1; - return (ScaleMode)(_scaleMode * _streams[_videoStreamIndex]->scaleMode); + return (_scaleFactorX * _streams[_videoStreamIndex]->scaleFactorX); +} + +Common::Rational QuickTimeDecoder::getScaleFactorY() const { + if (_videoStreamIndex < 0) + return 1; + + return (_scaleFactorY * _streams[_videoStreamIndex]->scaleFactorY); } uint32 QuickTimeDecoder::getFrameDuration() { @@ -231,14 +240,14 @@ Surface *QuickTimeDecoder::decodeNextFrame() { } Surface *QuickTimeDecoder::scaleSurface(Surface *frame) { - if (getScaleMode() == kScaleNormal) + if (getScaleFactorX() == 1 && getScaleFactorY() == 1) return frame; assert(_scaledSurface); - for (uint32 j = 0; j < _scaledSurface->h; j++) - for (uint32 k = 0; k < _scaledSurface->w; k++) - memcpy(_scaledSurface->getBasePtr(k, j), frame->getBasePtr(k * getScaleMode(), j * getScaleMode()), frame->bytesPerPixel); + for (int32 j = 0; j < _scaledSurface->h; j++) + for (int32 k = 0; k < _scaledSurface->w; k++) + memcpy(_scaledSurface->getBasePtr(k, j), frame->getBasePtr((k * getScaleFactorX()).toInt() , (j * getScaleFactorY()).toInt()), frame->bytesPerPixel); return _scaledSurface; } @@ -376,7 +385,7 @@ void QuickTimeDecoder::init() { if (_videoStreamIndex >= 0) { _videoCodec = createCodec(getCodecTag(), getBitsPerPixel()); - if (getScaleMode() != kScaleNormal) { + if (getScaleFactorX() != 1 || getScaleFactorY() != 1) { // We have to initialize the scaled surface _scaledSurface = new Surface(); _scaledSurface->create(getWidth(), getHeight(), getPixelFormat().bytesPerPixel); @@ -593,17 +602,11 @@ int QuickTimeDecoder::readMVHD(MOVatom atom) { uint32 yMod = _fd->readUint32BE(); _fd->skip(16); - if (xMod != yMod) - error("X and Y resolution modifiers differ"); - - if (xMod == 0x8000) - _scaleMode = kScaleHalf; - else if (xMod == 0x4000) - _scaleMode = kScaleQuarter; - else - _scaleMode = kScaleNormal; + _scaleFactorX = Common::Rational(0x10000, xMod); + _scaleFactorY = Common::Rational(0x10000, yMod); - debug(1, "readMVHD(): scaleMode = %d", (int)_scaleMode); + _scaleFactorX.debugPrint(1, "readMVHD(): scaleFactorX ="); + _scaleFactorY.debugPrint(1, "readMVHD(): scaleFactorY ="); _fd->readUint32BE(); // preview time _fd->readUint32BE(); // preview duration @@ -688,17 +691,11 @@ int QuickTimeDecoder::readTKHD(MOVatom atom) { uint32 yMod = _fd->readUint32BE(); _fd->skip(16); - if (xMod != yMod) - error("X and Y resolution modifiers differ"); + st->scaleFactorX = Common::Rational(0x10000, xMod); + st->scaleFactorY = Common::Rational(0x10000, yMod); - if (xMod == 0x8000) - st->scaleMode = kScaleHalf; - else if (xMod == 0x4000) - st->scaleMode = kScaleQuarter; - else - st->scaleMode = kScaleNormal; - - debug(1, "readTKHD(): scaleMode = %d", (int)_scaleMode); + st->scaleFactorX.debugPrint(1, "readTKHD(): scaleFactorX ="); + st->scaleFactorY.debugPrint(1, "readTKHD(): scaleFactorY ="); // these are fixed-point, 16:16 // uint32 tkWidth = _fd->readUint32BE() >> 16; // track width @@ -814,6 +811,17 @@ int QuickTimeDecoder::readSTSD(MOVatom atom) { _fd->readUint16BE(); // index debug(0, "size=%d 4CC= %s codec_type=%d", size, tag2str(format), st->codec_type); + + if (st->codec_tag && st->codec_tag != format) { + // HACK: Multiple FourCC, skip this. FFmpeg does this too and also + // skips it with a TODO. However, we really don't need to support + // multiple codec tags since the only two videos in Riven DVD that + // do this just have a fake second stream (or so it seems). + debug(3, "Multiple FourCC not supported"); + _fd->seek(start_pos + size); + continue; + } + st->codec_tag = format; if (st->codec_type == CODEC_TYPE_VIDEO) { diff --git a/graphics/video/qt_decoder.h b/graphics/video/qt_decoder.h index 196d4c02cb..6dcfc0944d 100644 --- a/graphics/video/qt_decoder.h +++ b/graphics/video/qt_decoder.h @@ -36,6 +36,7 @@ #include "common/scummsys.h" #include "common/queue.h" +#include "common/rational.h" #include "graphics/video/video_decoder.h" #include "graphics/video/codecs/codec.h" @@ -50,11 +51,6 @@ namespace Common { namespace Graphics { -enum ScaleMode { - kScaleNormal = 1, - kScaleHalf = 2, - kScaleQuarter = 4 -}; class QuickTimeDecoder : public RewindableVideoDecoder { public: @@ -217,7 +213,8 @@ protected: uint32 nb_frames; uint32 duration; uint32 start_time; - ScaleMode scaleMode; + Common::Rational scaleFactorX; + Common::Rational scaleFactorY; }; const ParseTable *_parseTable; @@ -230,7 +227,8 @@ protected: MOVStreamContext *_partial; uint32 _numStreams; int _ni; - ScaleMode _scaleMode; + Common::Rational _scaleFactorX; + Common::Rational _scaleFactorY; MOVStreamContext *_streams[20]; byte _palette[256 * 3]; bool _dirtyPalette; @@ -260,7 +258,8 @@ protected: Surface *_scaledSurface; Surface *scaleSurface(Surface *frame); - ScaleMode getScaleMode() const; + Common::Rational getScaleFactorX() const; + Common::Rational getScaleFactorY() const; void pauseVideoIntern(bool pause); diff --git a/graphics/video/smk_decoder.cpp b/graphics/video/smk_decoder.cpp index 4d03305cce..71858dd3aa 100644 --- a/graphics/video/smk_decoder.cpp +++ b/graphics/video/smk_decoder.cpp @@ -389,12 +389,13 @@ bool SmackerDecoder::load(Common::SeekableReadStream *stream) { _frameCount = _fileStream->readUint32LE(); int32 frameRate = _fileStream->readSint32LE(); + // framerate contains 2 digits after the comma, so 1497 is actually 14.97 fps if (frameRate > 0) - _frameRate = 1000 / frameRate; + _frameRate = Common::Rational(1000, frameRate); else if (frameRate < 0) - _frameRate = 100000 / (-frameRate); + _frameRate = Common::Rational(100000, -frameRate); else - _frameRate = 10; + _frameRate = 1000; // Flags are determined by which bit is set, which can be one of the following: // 0 - set to 1 if file contains a ring frame. @@ -540,53 +541,18 @@ Surface *SmackerDecoder::decodeNextFrame() { chunkSize = _fileStream->readUint32LE(); chunkSize -= 4; // subtract the first 4 bytes (chunk size) - if (_header.audioInfo[i].compression != kCompressionNone) { + if (_header.audioInfo[i].compression == kCompressionNone) { + dataSizeUnpacked = chunkSize; + } else { dataSizeUnpacked = _fileStream->readUint32LE(); chunkSize -= 4; // subtract the next 4 bytes (unpacked data size) - } else { - dataSizeUnpacked = 0; } - if (_header.audioInfo[i].hasAudio && chunkSize > 0 && i == 0) { - // If it's track 0, play the audio data - byte *soundBuffer = (byte *)malloc(chunkSize); - - _fileStream->read(soundBuffer, chunkSize); - - if (_header.audioInfo[i].compression == kCompressionRDFT || _header.audioInfo[i].compression == kCompressionDCT) { - // TODO: Compressed audio (Bink RDFT/DCT encoded) - free(soundBuffer); - continue; - } else if (_header.audioInfo[i].compression == kCompressionDPCM) { - // Compressed audio (Huffman DPCM encoded) - queueCompressedBuffer(soundBuffer, chunkSize, dataSizeUnpacked, i); - free(soundBuffer); - } else { - // Uncompressed audio (PCM) - byte flags = 0; - if (_header.audioInfo[0].is16Bits) - flags = flags | Audio::FLAG_16BITS; - if (_header.audioInfo[0].isStereo) - flags = flags | Audio::FLAG_STEREO; - - _audioStream->queueBuffer(soundBuffer, chunkSize, DisposeAfterUse::YES, flags); - // The sound buffer will be deleted by QueuingAudioStream - } - - if (!_audioStarted) { - _mixer->playStream(_soundType, &_audioHandle, _audioStream, -1, 255); - _audioStarted = true; - } - } else { - // Ignore the rest of the audio tracks, if they exist - // TODO: Are there any Smacker videos with more than one audio stream? - // If yes, we should play the rest of the audio streams as well - if (chunkSize > 0) - _fileStream->skip(chunkSize); - } + handleAudioTrack(i, chunkSize, dataSizeUnpacked); } uint32 frameSize = _frameSizes[_curFrame] & ~3; +// uint32 remainder = _frameSizes[_curFrame] & 3; if (_fileStream->pos() - startPos > frameSize) error("Smacker actual frame size exceeds recorded frame size"); @@ -746,6 +712,46 @@ Surface *SmackerDecoder::decodeNextFrame() { return _surface; } +void SmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize) { + if (_header.audioInfo[track].hasAudio && chunkSize > 0 && track == 0) { + // If it's track 0, play the audio data + byte *soundBuffer = (byte *)malloc(chunkSize); + + _fileStream->read(soundBuffer, chunkSize); + + if (_header.audioInfo[track].compression == kCompressionRDFT || _header.audioInfo[track].compression == kCompressionDCT) { + // TODO: Compressed audio (Bink RDFT/DCT encoded) + free(soundBuffer); + return; + } else if (_header.audioInfo[track].compression == kCompressionDPCM) { + // Compressed audio (Huffman DPCM encoded) + queueCompressedBuffer(soundBuffer, chunkSize, unpackedSize, track); + free(soundBuffer); + } else { + // Uncompressed audio (PCM) + byte flags = 0; + if (_header.audioInfo[track].is16Bits) + flags = flags | Audio::FLAG_16BITS; + if (_header.audioInfo[track].isStereo) + flags = flags | Audio::FLAG_STEREO; + + _audioStream->queueBuffer(soundBuffer, chunkSize, DisposeAfterUse::YES, flags); + // The sound buffer will be deleted by QueuingAudioStream + } + + if (!_audioStarted) { + _mixer->playStream(_soundType, &_audioHandle, _audioStream, -1, 255); + _audioStarted = true; + } + } else { + // Ignore the rest of the audio tracks, if they exist + // TODO: Are there any Smacker videos with more than one audio stream? + // If yes, we should play the rest of the audio streams as well + if (chunkSize > 0) + _fileStream->skip(chunkSize); + } +} + void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize, uint32 unpackedSize, int streamNum) { @@ -808,7 +814,7 @@ void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize, bases[k] += (int16) (audioTrees[k * 2]->getCode(audioBS) | (audioTrees[k * 2 + 1]->getCode(audioBS) << 8)); - WRITE_BE_UINT16(curPointer, CLIP<int32>(bases[k], -32768, 32767)); + WRITE_BE_UINT16(curPointer, bases[k]); curPointer += 2; curPos += 2; } diff --git a/graphics/video/smk_decoder.h b/graphics/video/smk_decoder.h index 43bb84a4f8..5faeab4343 100644 --- a/graphics/video/smk_decoder.h +++ b/graphics/video/smk_decoder.h @@ -69,12 +69,13 @@ public: PixelFormat getPixelFormat() const { return PixelFormat::createFormatCLUT8(); } byte *getPalette() { _dirtyPalette = false; return _palette; } bool hasDirtyPalette() const { return _dirtyPalette; } + virtual void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize); protected: Common::Rational getFrameRate() const { return _frameRate; } Common::SeekableReadStream *_fileStream; -private: +protected: void unpackPalette(); // Possible runs of blocks uint getBlockRun(int index) { return (index <= 58) ? index + 1 : 128 << (index - 59); } @@ -120,7 +121,7 @@ private: byte *_palette; bool _dirtyPalette; - uint32 _frameRate; + Common::Rational _frameRate; uint32 _frameCount; Surface *_surface; |