diff options
Diffstat (limited to 'graphics')
-rw-r--r-- | graphics/conversion.cpp | 152 | ||||
-rw-r--r-- | graphics/conversion.h | 76 | ||||
-rw-r--r-- | graphics/cursorman.cpp | 29 | ||||
-rw-r--r-- | graphics/cursorman.h | 36 | ||||
-rw-r--r-- | graphics/dither.cpp | 1 | ||||
-rw-r--r-- | graphics/dither.h | 13 | ||||
-rw-r--r-- | graphics/jpeg.cpp | 638 | ||||
-rw-r--r-- | graphics/jpeg.h | 118 | ||||
-rw-r--r-- | graphics/module.mk | 2 | ||||
-rw-r--r-- | graphics/pixelformat.h | 39 | ||||
-rw-r--r-- | graphics/scaler.cpp | 9 | ||||
-rw-r--r-- | graphics/scaler/thumbnail_intern.cpp | 20 | ||||
-rw-r--r-- | graphics/video/coktelvideo/coktelvideo.cpp | 5 |
13 files changed, 1095 insertions, 43 deletions
diff --git a/graphics/conversion.cpp b/graphics/conversion.cpp new file mode 100644 index 0000000000..1863814c1d --- /dev/null +++ b/graphics/conversion.cpp @@ -0,0 +1,152 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "graphics/conversion.h" + +namespace Graphics { + +// TODO: YUV to RGB conversion function + +// Function to blit a rect from one color format to another +bool crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch, + int w, int h, const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt) { + // Error out if conversion is impossible + if ((srcFmt.bytesPerPixel == 1) || (dstFmt.bytesPerPixel == 1) + || (!srcFmt.bytesPerPixel) || (!dstFmt.bytesPerPixel) + || (srcFmt.bytesPerPixel > dstFmt.bytesPerPixel)) + return false; + + // Don't perform unnecessary conversion + if (srcFmt == dstFmt) { + if (dst == src) + return true; + if (dstpitch == srcpitch && ((w * dstFmt.bytesPerPixel) == dstpitch)) { + memcpy(dst,src,dstpitch * h); + return true; + } else { + for (int i = 0; i < h; i++) { + memcpy(dst,src,w * dstFmt.bytesPerPixel); + dst += dstpitch; + src += srcpitch; + } + return true; + } + } + + // Faster, but larger, to provide optimized handling for each case. + int srcDelta, dstDelta; + srcDelta = (srcpitch - w * srcFmt.bytesPerPixel); + dstDelta = (dstpitch - w * dstFmt.bytesPerPixel); + + // TODO: optimized cases for dstDelta of 0 + uint8 r, g, b, a; + if (dstFmt.bytesPerPixel == 2) { + uint16 color; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++, src += 2, dst += 2) { + color = *(uint16 *) src; + srcFmt.colorToARGB(color, a, r, g, b); + color = dstFmt.ARGBToColor(a, r, g, b); + *(uint16 *) dst = color; + } + src += srcDelta; + dst += dstDelta; + } + } else if (dstFmt.bytesPerPixel == 3) { + uint32 color; + uint8 *col = (uint8 *) &color; +#ifdef SCUMM_BIG_ENDIAN + col++; +#endif + if (srcFmt.bytesPerPixel == 2) { + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++, src += 2, dst += 3) { + color = *(uint16 *) src; + srcFmt.colorToARGB(color, a, r, g, b); + color = dstFmt.ARGBToColor(a, r, g, b); + memcpy(dst, col, 3); + } + src += srcDelta; + dst += dstDelta; + } + } else { + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++, src += 3, dst += 3) { + uint8 r, g, b, a; + memcpy(col, src, 3); + srcFmt.colorToARGB(color, a, r, g, b); + color = dstFmt.ARGBToColor(a, r, g, b); + memcpy(dst, col, 3); + } + src += srcDelta; + dst += dstDelta; + } + } + } else if (dstFmt.bytesPerPixel == 4) { + uint32 color; + if (srcFmt.bytesPerPixel == 2) { + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++, src += 2, dst += 4) { + color = *(uint16 *) src; + srcFmt.colorToARGB(color, a, r, g, b); + color = dstFmt.ARGBToColor(a, r, g, b); + *(uint32 *) dst = color; + } + src += srcDelta; + dst += dstDelta; + } + } else if (srcFmt.bytesPerPixel == 3) { + uint8 *col = (uint8 *)&color; +#ifdef SCUMM_BIG_ENDIAN + col++; +#endif + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++, src += 2, dst += 4) { + memcpy(col, src, 3); + srcFmt.colorToARGB(color, a, r, g, b); + color = dstFmt.ARGBToColor(a, r, g, b); + *(uint32 *) dst = color; + } + src += srcDelta; + dst += dstDelta; + } + } else { + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++, src += 4, dst += 4) { + color = *(uint32 *) src; + srcFmt.colorToARGB(color, a, r, g, b); + color = dstFmt.ARGBToColor(a, r, g, b); + *(uint32 *) dst = color; + } + src += srcDelta; + dst += dstDelta; + } + } + } else { + return false; + } + return true; +} + +} // end of namespace Graphics diff --git a/graphics/conversion.h b/graphics/conversion.h new file mode 100644 index 0000000000..b0314046b9 --- /dev/null +++ b/graphics/conversion.h @@ -0,0 +1,76 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef GRAPHICS_CONVERSION_H +#define GRAPHICS_CONVERSION_H + +#include "common/util.h" +#include "graphics/pixelformat.h" + +namespace Graphics { + +/** Converting a color from YUV to RGB colorspace. */ +inline static void YUV2RGB(byte y, byte u, byte v, byte &r, byte &g, byte &b) { + r = CLIP<int>(y + ((1357 * (v - 128)) >> 10), 0, 255); + g = CLIP<int>(y - (( 691 * (v - 128)) >> 10) - ((333 * (u - 128)) >> 10), 0, 255); + b = CLIP<int>(y + ((1715 * (u - 128)) >> 10), 0, 255); +} + +/** Converting a color from RGB to YUV colorspace. */ +inline static void RGB2YUV(byte r, byte g, byte b, byte &y, byte &u, byte &v) { + y = CLIP<int>( ((r * 306) >> 10) + ((g * 601) >> 10) + ((b * 117) >> 10) , 0, 255); + u = CLIP<int>(-((r * 172) >> 10) - ((g * 340) >> 10) + ((b * 512) >> 10) + 128, 0, 255); + v = CLIP<int>( ((r * 512) >> 10) - ((g * 429) >> 10) - ((b * 83) >> 10) + 128, 0, 255); +} + +// TODO: generic YUV to RGB blit + +/** + * Blits a rectangle from one graphical format to another. + * + * @param dstbuf the buffer which will recieve the converted graphics data + * @param srcbuf the buffer containing the original graphics data + * @param dstpitch width in bytes of one full line of the dest buffer + * @param srcpitch width in bytes of one full line of the source buffer + * @param w the width of the graphics data + * @param h the height of the graphics data + * @param dstFmt the desired pixel format + * @param srcFmt the original pixel format + * @return true if conversion completes successfully, + * false if there is an error. + * + * @note This implementation currently arbitrarily requires that the + * destination's format have at least as high a bytedepth as + * the source's. + * @note This can convert a rectangle in place, if the source and + * destination format have the same bytedepth. + * + */ +bool crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch, + int w, int h, Graphics::PixelFormat dstFmt, Graphics::PixelFormat srcFmt); + +} // end of namespace Graphics + +#endif // GRAPHICS_CONVERSION_H diff --git a/graphics/cursorman.cpp b/graphics/cursorman.cpp index 2e71b548bc..b77aac37cf 100644 --- a/graphics/cursorman.cpp +++ b/graphics/cursorman.cpp @@ -57,14 +57,14 @@ bool CursorManager::showMouse(bool visible) { return g_system->showMouse(visible); } -void CursorManager::pushCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor, int targetScale) { - Cursor *cur = new Cursor(buf, w, h, hotspotX, hotspotY, keycolor, targetScale); +void CursorManager::pushCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int targetScale, const Graphics::PixelFormat *format) { + Cursor *cur = new Cursor(buf, w, h, hotspotX, hotspotY, keycolor, targetScale, format); cur->_visible = isVisible(); _cursorStack.push(cur); if (buf) { - g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, targetScale); + g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, targetScale, format); } } @@ -77,7 +77,7 @@ void CursorManager::popCursor() { if (!_cursorStack.empty()) { cur = _cursorStack.top(); - g_system->setMouseCursor(cur->_data, cur->_width, cur->_height, cur->_hotspotX, cur->_hotspotY, cur->_keycolor, cur->_targetScale); + g_system->setMouseCursor(cur->_data, cur->_width, cur->_height, cur->_hotspotX, cur->_hotspotY, cur->_keycolor, cur->_targetScale, &cur->_format); } g_system->showMouse(isVisible()); @@ -100,15 +100,24 @@ void CursorManager::popAllCursors() { g_system->showMouse(isVisible()); } +void CursorManager::replaceCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int targetScale, const Graphics::PixelFormat *format) { -void CursorManager::replaceCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor, int targetScale) { if (_cursorStack.empty()) { - pushCursor(buf, w, h, hotspotX, hotspotY, keycolor, targetScale); + pushCursor(buf, w, h, hotspotX, hotspotY, keycolor, targetScale, format); return; } Cursor *cur = _cursorStack.top(); + +#ifdef ENABLE_RGB_COLOR + uint size; + if (!format) + size = w * h; + else + size = w * h * format->bytesPerPixel; +#else uint size = w * h; +#endif if (cur->_size < size) { delete[] cur->_data; @@ -125,8 +134,14 @@ void CursorManager::replaceCursor(const byte *buf, uint w, uint h, int hotspotX, cur->_hotspotY = hotspotY; cur->_keycolor = keycolor; cur->_targetScale = targetScale; +#ifdef ENABLE_RGB_COLOR + if (format) + cur->_format = *format; + else + cur->_format = Graphics::PixelFormat::createFormatCLUT8(); +#endif - g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, targetScale); + g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, targetScale, format); } bool CursorManager::supportsCursorPalettes() { diff --git a/graphics/cursorman.h b/graphics/cursorman.h index f019e37b04..ae7008f54c 100644 --- a/graphics/cursorman.h +++ b/graphics/cursorman.h @@ -28,6 +28,10 @@ #include "common/scummsys.h" #include "common/stack.h" #include "common/singleton.h" +#include "graphics/pixelformat.h" +#ifdef ENABLE_RGB_COLOR +#include "common/system.h" +#endif namespace Graphics { @@ -59,18 +63,19 @@ public: * safely freed afterwards. * * @param buf the new cursor data - * @param w the width - * @param h the height + * @param w the width + * @param h the height * @param hotspotX the hotspot X coordinate * @param hotspotY the hotspot Y coordinate * @param keycolor the index for the transparent color * @param targetScale the scale for which the cursor is designed - * + * @param format a pointer to the pixel format which the cursor graphic uses, + * CLUT8 will be used if this is NULL or not specified. * @note It is ok for the buffer to be a NULL pointer. It is sometimes * useful to push a "dummy" cursor and modify it later. The * cursor will be added to the stack, but not to the backend. */ - void pushCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor = 255, int targetScale = 1); + void pushCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 0xFFFFFFFF, int targetScale = 1, const Graphics::PixelFormat *format = NULL); /** * Pop a cursor from the stack, and restore the previous one to the @@ -90,8 +95,10 @@ public: * @param hotspotY the hotspot Y coordinate * @param keycolor the index for the transparent color * @param targetScale the scale for which the cursor is designed + * @param format a pointer to the pixel format which the cursor graphic uses, + * CLUT8 will be used if this is NULL or not specified. */ - void replaceCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor = 255, int targetScale = 1); + void replaceCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 0xFFFFFFFF, int targetScale = 1, const Graphics::PixelFormat *format = NULL); /** * Pop all of the cursors and cursor palettes from their respective stacks. @@ -166,13 +173,24 @@ private: uint _height; int _hotspotX; int _hotspotY; - byte _keycolor; + uint32 _keycolor; + Graphics::PixelFormat _format; byte _targetScale; uint _size; - - Cursor(const byte *data, uint w, uint h, int hotspotX, int hotspotY, byte keycolor = 255, int targetScale = 1) { + Cursor(const byte *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor = 0xFFFFFFFF, int targetScale = 1, const Graphics::PixelFormat *format = NULL) { +#ifdef ENABLE_RGB_COLOR + if (!format) + _format = Graphics::PixelFormat::createFormatCLUT8(); + else + _format = *format; + _size = w * h * _format.bytesPerPixel; + _keycolor &= ((1 << (_format.bytesPerPixel << 3)) - 1); +#else + _format = Graphics::PixelFormat::createFormatCLUT8(); _size = w * h; + _keycolor &= 0xFF; +#endif _data = new byte[_size]; if (data && _data) memcpy(_data, data, _size); @@ -180,7 +198,6 @@ private: _height = h; _hotspotX = hotspotX; _hotspotY = hotspotY; - _keycolor = keycolor; _targetScale = targetScale; } @@ -216,7 +233,6 @@ private: delete[] _data; } }; - Common::Stack<Cursor *> _cursorStack; Common::Stack<Palette *> _cursorPaletteStack; }; diff --git a/graphics/dither.cpp b/graphics/dither.cpp index 7a92441571..e671de265e 100644 --- a/graphics/dither.cpp +++ b/graphics/dither.cpp @@ -23,6 +23,7 @@ */ #include "common/endian.h" +#include "graphics/conversion.h" #include "graphics/dither.h" namespace Graphics { diff --git a/graphics/dither.h b/graphics/dither.h index e6d606cdd4..18f98ce4e0 100644 --- a/graphics/dither.h +++ b/graphics/dither.h @@ -43,19 +43,6 @@ public: kPaletteYUV //!< Palette in YUV colorspace }; - /** Converting a color from YUV to RGB colorspace. */ - inline static void YUV2RGB(byte y, byte u, byte v, byte &r, byte &g, byte &b) { - r = CLIP<int>(y + ((1357 * (v - 128)) >> 10), 0, 255); - g = CLIP<int>(y - (( 691 * (v - 128)) >> 10) - ((333 * (u - 128)) >> 10), 0, 255); - b = CLIP<int>(y + ((1715 * (u - 128)) >> 10), 0, 255); - } - /** Converting a color from RGB to YUV colorspace. */ - inline static void RGB2YUV(byte r, byte g, byte b, byte &y, byte &u, byte &v) { - y = CLIP<int>( ((r * 306) >> 10) + ((g * 601) >> 10) + ((b * 117) >> 10) , 0, 255); - u = CLIP<int>(-((r * 172) >> 10) - ((g * 340) >> 10) + ((b * 512) >> 10) + 128, 0, 255); - v = CLIP<int>( ((r * 512) >> 10) - ((g * 429) >> 10) - ((b * 83) >> 10) + 128, 0, 255); - } - /** Create a lookup table of a given depth and palette format. * * @param depth How many bits of each color component to consider. diff --git a/graphics/jpeg.cpp b/graphics/jpeg.cpp new file mode 100644 index 0000000000..0ad2cf7699 --- /dev/null +++ b/graphics/jpeg.cpp @@ -0,0 +1,638 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "graphics/jpeg.h" + +#include "common/endian.h" +#include "common/util.h" + +namespace Graphics { + +// Order used to traverse the quantization tables +uint8 JPEG::_zigZagOrder[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +JPEG::JPEG() : + _str(NULL), _w(0), _h(0), _numComp(0), _components(NULL), _numScanComp(0), + _scanComp(NULL), _currentComp(NULL) { + + // Initialize the quantization tables + for (int i = 0; i < JPEG_MAX_QUANT_TABLES; i++) + _quant[i] = NULL; + + // Initialize the Huffman tables + for (int i = 0; i < 2 * JPEG_MAX_HUFF_TABLES; i++) { + _huff[i].count = 0; + _huff[i].values = NULL; + _huff[i].sizes = NULL; + _huff[i].codes = NULL; + } +} + +JPEG::~JPEG() { + reset(); +} + +void JPEG::reset() { + // Reset member variables + _str = NULL; + _w = _h = 0; + + // Free the components + for (int c = 0; c < _numComp; c++) + _components[c].surface.free(); + delete[] _components; _components = NULL; + _numComp = 0; + + // Free the scan components + delete[] _scanComp; _scanComp = NULL; + _numScanComp = 0; + _currentComp = NULL; + + // Free the quantization tables + for (int i = 0; i < JPEG_MAX_QUANT_TABLES; i++) { + delete[] _quant[i]; + _quant[i] = NULL; + } + + // Free the Huffman tables + for (int i = 0; i < 2 * JPEG_MAX_HUFF_TABLES; i++) { + _huff[i].count = 0; + delete[] _huff[i].values; _huff[i].values = NULL; + delete[] _huff[i].sizes; _huff[i].sizes = NULL; + delete[] _huff[i].codes; _huff[i].codes = NULL; + } +} + +bool JPEG::read(Common::SeekableReadStream *str) { + // Reset member variables and tables from previous reads + reset(); + + // Save the input stream + _str = str; + + bool ok = true; + bool done = false; + while (!_str->eos() && ok && !done) { + // Read the marker + uint16 marker = _str->readByte(); + if (marker != 0xFF) { + error("JPEG: Invalid marker[0]: 0x%02X", marker); + ok = false; + break; + } + + while (marker == 0xFF) + marker = _str->readByte(); + + // Process the marker data + switch (marker) { + case 0xC0: // Start Of Frame + ok = readSOF0(); + break; + case 0xC4: // Define Huffman Tables + ok = readDHT(); + break; + case 0xD8: // Start Of Image + break; + case 0xD9: // End Of Image + done = true; + break; + case 0xDA: // Start Of Scan + ok = readSOS(); + break; + case 0xDB: // Define Quantization Tables + ok = readDQT(); + break; + case 0xE0: // JFIF/JFXX segment + ok = readJFIF(); + break; + case 0xFE: // Comment + _str->seek(_str->readUint16BE() - 2, SEEK_CUR); + break; + default: { // Unknown marker + uint16 size = _str->readUint16BE(); + warning("JPEG: Unknown marker %02X, skipping %d bytes", marker, size - 2); + _str->seek(size - 2, SEEK_CUR); + } + } + } + return ok; +} + +bool JPEG::readJFIF() { + /* uint16 length = */ _str->readUint16BE(); + uint32 tag = _str->readUint32BE(); + if (tag != MKID_BE('JFIF')) + return false; + _str->readByte(); // NULL + /* byte majorVersion = */ _str->readByte(); + /* byte minorVersion = */ _str->readByte(); + /* byte densityUnits = */ _str->readByte(); + /* uint16 xDensity = */ _str->readUint16BE(); + /* uint16 yDensity = */ _str->readUint16BE(); + byte thumbW = _str->readByte(); + byte thumbH = _str->readByte(); + _str->seek(thumbW * thumbH * 3, SEEK_CUR); // Ignore thumbnail + return true; +} + +// Marker 0xC0 (Start Of Frame, Baseline DCT) +bool JPEG::readSOF0() { + debug(5, "JPEG: readSOF0"); + uint16 size = _str->readUint16BE(); + + // Read the sample precision + uint8 precision = _str->readByte(); + if (precision != 8) { + warning("JPEG: Just 8 bit precision supported at the moment"); + return false; + } + + // Image size + _h = _str->readUint16BE(); + _w = _str->readUint16BE(); + + // Number of components + _numComp = _str->readByte(); + if (size != 8 + 3 * _numComp) { + warning("JPEG: Invalid number of components"); + return false; + } + + // Allocate the new components + delete[] _components; + _components = new Component[_numComp]; + + // Read the components details + for (int c = 0; c < _numComp; c++) { + _components[c].id = _str->readByte(); + _components[c].factorH = _str->readByte(); + _components[c].factorV = _components[c].factorH & 0xF; + _components[c].factorH >>= 4; + _components[c].quantTableSelector = _str->readByte(); + } + + return true; +} + +// Marker 0xC4 (Define Huffman Tables) +bool JPEG::readDHT() { + debug(5, "JPEG: readDHT"); + uint16 size = _str->readUint16BE() - 2; + uint32 pos = _str->pos(); + + while ((uint32)_str->pos() < (size + pos)) { + // Read the table type and id + uint8 tableId = _str->readByte(); + uint8 tableType = tableId >> 4; // type 0: DC, 1: AC + tableId &= 0xF; + uint8 tableNum = (tableId << 1) + tableType; + + // Free the Huffman table + delete[] _huff[tableNum].values; _huff[tableNum].values = NULL; + delete[] _huff[tableNum].sizes; _huff[tableNum].sizes = NULL; + delete[] _huff[tableNum].codes; _huff[tableNum].codes = NULL; + + // Read the number of values for each length + uint8 numValues[16]; + _huff[tableNum].count = 0; + for (int len = 0; len < 16; len++) { + numValues[len] = _str->readByte(); + _huff[tableNum].count += numValues[len]; + } + + // Allocate memory for the current table + _huff[tableNum].values = new uint8[_huff[tableNum].count]; + _huff[tableNum].sizes = new uint8[_huff[tableNum].count]; + _huff[tableNum].codes = new uint16[_huff[tableNum].count]; + + // Read the table contents + int cur = 0; + for (int len = 0; len < 16; len++) { + for (int i = 0; i < numValues[len]; i++) { + _huff[tableNum].values[cur] = _str->readByte(); + _huff[tableNum].sizes[cur] = len + 1; + cur++; + } + } + + // Fill the table of Huffman codes + cur = 0; + uint16 curCode = 0; + uint8 curCodeSize = _huff[tableNum].sizes[0]; + while (cur < _huff[tableNum].count) { + // Increase the code size to fit the request + while (_huff[tableNum].sizes[cur] != curCodeSize) { + curCode <<= 1; + curCodeSize++; + } + + // Assign the current code + _huff[tableNum].codes[cur] = curCode; + curCode++; + cur++; + } + } + + return true; +} + +// Marker 0xDA (Start Of Scan) +bool JPEG::readSOS() { + debug(5, "JPEG: readSOS"); + uint16 size = _str->readUint16BE(); + + // Number of scan components + _numScanComp = _str->readByte(); + if (size != 6 + 2 * _numScanComp) { + warning("JPEG: Invalid number of components"); + return false; + } + + // Allocate the new scan components + delete[] _scanComp; + _scanComp = new Component *[_numScanComp]; + + // Reset the maximum sampling factors + _maxFactorV = 0; + _maxFactorH = 0; + + // Component-specification parameters + for (int c = 0; c < _numScanComp; c++) { + // Read the desired component id + uint8 id = _str->readByte(); + + // Search the component with the specified id + bool found = false; + for (int i = 0; !found && i < _numComp; i++) { + if (_components[i].id == id) { + // We found the desired component + found = true; + + // Assign the found component to the c'th scan component + _scanComp[c] = &_components[i]; + } + } + + if (!found) { + warning("JPEG: Invalid component"); + return false; + } + + // Read the entropy table selectors + _scanComp[c]->DCentropyTableSelector = _str->readByte(); + _scanComp[c]->ACentropyTableSelector = _scanComp[c]->DCentropyTableSelector & 0xF; + _scanComp[c]->DCentropyTableSelector >>= 4; + + // Calculate the maximum sampling factors + if (_scanComp[c]->factorV > _maxFactorV) + _maxFactorV = _scanComp[c]->factorV; + + if (_scanComp[c]->factorH > _maxFactorH) + _maxFactorH = _scanComp[c]->factorH; + + // Initialize the DC predictor + _scanComp[c]->DCpredictor = 0; + } + + // Initialize the scan surfaces + for (int c = 0; c < _numScanComp; c++) + _scanComp[c]->surface.create(_w, _h, 1); + + // Start of spectral selection + if (_str->readByte() != 0) { + warning("JPEG: Progressive scanning not supported"); + return false; + } + + // End of spectral selection + if (_str->readByte() != 63) { + warning("JPEG: Progressive scanning not supported"); + return false; + } + + // Successive approximation parameters + if (_str->readByte() != 0) { + warning("JPEG: Progressive scanning not supported"); + return false; + } + + // Entropy coded sequence starts, initialize Huffman decoder + _bitsNumber = 0; + + // Read all the scan MCUs + uint16 xMCU = _w / (_maxFactorH * 8); + uint16 yMCU = _h / (_maxFactorV * 8); + + // Check for non- multiple-of-8 dimensions + if (_w % 8 != 0) + xMCU++; + if (_h % 8 != 0) + yMCU++; + + bool ok = true; + for (int y = 0; ok && (y < yMCU); y++) + for (int x = 0; ok && (x < xMCU); x++) + ok = readMCU(x, y); + + return ok; +} + +// Marker 0xDB (Define Quantization Tables) +bool JPEG::readDQT() { + debug(5, "JPEG: readDQT"); + uint16 size = _str->readUint16BE() - 2; + uint32 pos = _str->pos(); + + while ((uint32)_str->pos() < (pos + size)) { + // Read the table precision and id + uint8 tableId = _str->readByte(); + bool highPrecision = (tableId & 0xF0) != 0; + + // Validate the table id + tableId &= 0xF; + if (tableId > JPEG_MAX_QUANT_TABLES) { + warning("JPEG: Invalid number of components"); + return false; + } + + // Create the new table if necessary + if (!_quant[tableId]) + _quant[tableId] = new uint16[64]; + + // Read the table (stored in Zig-Zag order) + for (int i = 0; i < 64; i++) + _quant[tableId][i] = highPrecision ? _str->readUint16BE() : _str->readByte(); + } + + return true; +} + +bool JPEG::readMCU(uint16 xMCU, uint16 yMCU) { + bool ok = true; + for (int c = 0; ok && (c < _numComp); c++) { + // Set the current component + _currentComp = _scanComp[c]; + + // Read the data units of the current component + for (int y = 0; ok && (y < _scanComp[c]->factorV); y++) + for (int x = 0; ok && (x < _scanComp[c]->factorH); x++) + ok = readDataUnit(xMCU * _scanComp[c]->factorH + x, yMCU * _scanComp[c]->factorV + y); + } + + return ok; +} + +float JPEG::idct(int x, int y, int weight, int fx, int fy) { + float vx = cos((2 * x + 1) * fx * PI / 16); + float vy = cos((2 * y + 1) * fy * PI / 16); + float ret = (float)weight * vx * vy; + + if (fx == 0) + ret /= sqrt(2.0f); + + if (fy == 0) + ret /= sqrt(2.0f); + + return ret; +} + +bool JPEG::readDataUnit(uint16 x, uint16 y) { + // Prepare an empty data array + int16 readData[64]; + for (int i = 1; i < 64; i++) + readData[i] = 0; + + // Read the DC component + readData[0] = _currentComp->DCpredictor + readDC(); + _currentComp->DCpredictor = readData[0]; + + // Read the AC components (stored in Zig-Zag) + readAC(readData); + + // Calculate the DCT coefficients from the input sequence + int16 DCT[64]; + for (int i = 0; i < 64; i++) { + // Dequantize + int16 val = readData[i]; + int16 quant = _quant[_currentComp->quantTableSelector][i]; + val *= quant; + + // Store the normalized coefficients, undoing the Zig-Zag + DCT[_zigZagOrder[i]] = val; + } + + // Shortcut the IDCT for DC component + float result[64]; + for (int i = 0; i < 64; i++) + result[i] = DCT[0] / 2; + + // Apply the IDCT (PAG31) + for (int i = 1; i < 64; i++) { + if (DCT[i]) + for (int _y = 0; _y < 8; _y++) + for (int _x = 0; _x < 8; _x++) + result[_y * 8 + _x] += idct(_x, _y, DCT[i], i % 8, i / 8); + } + + // Level shift to make the values unsigned + // Divide by 4 is final part of IDCT + for (int i = 0; i < 64; i++) { + result[i] = result[i] / 4 + 128; + + if (result[i] < 0) + result[i] = 0; + + if (result[i] > 255) + result[i] = 255; + } + + // Paint the component surface + uint8 scalingV = _maxFactorV / _currentComp->factorV; + uint8 scalingH = _maxFactorH / _currentComp->factorH; + + // Convert coordinates from MCU blocks to pixels + x <<= 3; + y <<= 3; + + // Handle non- multiple-of-8 dimensions + byte xLim = 8; + byte yLim = 8; + if (x*scalingH + 8 > _w) + xLim -= (x*scalingH + 8 - _w); + if (y*scalingV + 8 > _h) + yLim -= (y*scalingV + 8 - _h); + + for (int j = 0; j < yLim; j++) { + for (int sV = 0; sV < scalingV; sV++) { + // Get the beginning of the block line + byte *ptr = (byte *)_currentComp->surface.getBasePtr(x * scalingH, (y + j) * scalingV + sV); + + for (int i = 0; i < xLim; i++) { + for (uint8 sH = 0; sH < scalingH; sH++) { + *ptr = (byte)(result[j * 8 + i]); + ptr++; + } + } + } + } + + return true; +} + +int16 JPEG::readDC() { + // DC is type 0 + uint8 tableNum = _currentComp->DCentropyTableSelector << 1; + + // Get the number of bits to read + uint8 numBits = readHuff(tableNum); + + // Read the requested bits + return readSignedBits(numBits); +} + +void JPEG::readAC(int16 *out) { + // AC is type 1 + uint8 tableNum = (_currentComp->ACentropyTableSelector << 1) + 1; + + // Start reading AC element 1 + uint8 cur = 1; + while (cur < 64) { + uint8 s = readHuff(tableNum); + uint8 r = s >> 4; + s &= 0xF; + + if (s == 0) { + if (r == 15) { + // Skip 16 values + cur += 16; + } else { + // EOB: end of block + cur = 64; + } + } else { + // Skip r values + cur += r; + + // Read the next value + out[cur] = readSignedBits(s); + cur++; + } + } +} + +int16 JPEG::readSignedBits(uint8 numBits) { + uint16 ret = 0; + if (numBits > 16) error("requested %d bits", numBits); //XXX + + // MSB=0 for negatives, 1 for positives + for (int i = 0; i < numBits; i++) + ret = (ret << 1) + readBit(); + + // Extend sign bits (PAG109) + if (!(ret >> (numBits - 1))) + { + uint16 tmp = ((uint16)-1 << numBits) + 1; + ret = ret + tmp; + } + return ret; +} + +// TODO: optimize? +uint8 JPEG::readHuff(uint8 table) { + bool foundCode = false; + uint8 val = 0; + + uint8 cur = 0; + uint8 codeSize = 1; + uint16 code = readBit(); + while (!foundCode) { + // Prepare a code of the current size + while (codeSize < _huff[table].sizes[cur]) { + code = (code << 1) + readBit(); + codeSize++; + } + + // Compare the codes of the current size + while (!foundCode && (codeSize == _huff[table].sizes[cur])) { + if (code == _huff[table].codes[cur]) { + // Found the code + val = _huff[table].values[cur]; + foundCode = true; + } else { + // Continue reading + cur++; + } + } + } + + return val; +} + +uint8 JPEG::readBit() { + // Read a whole byte if necessary + if (_bitsNumber == 0) { + _bitsData = _str->readByte(); + _bitsNumber = 8; + + // Detect markers + if (_bitsData == 0xFF) { + uint8 byte2 = _str->readByte(); + + // A stuffed 0 validates the previous byte + if (byte2 != 0) { + if (byte2 == 0xDC) { + // DNL marker: Define Number of Lines + // TODO: terminate scan + printf("DNL marker detected: terminate scan\n"); + } else { + printf("Error: marker 0x%02X read in entropy data\n", byte2); + } + } + } + } + _bitsNumber--; + + return (_bitsData & (1 << _bitsNumber)) ? 1 : 0; +} + +Surface *JPEG::getComponent(uint c) { + for (int i = 0; i < _numComp; i++) + if (_components[i].id == c) // We found the desired component + return &_components[i].surface; + + return NULL; +} + +} // End of Graphics namespace diff --git a/graphics/jpeg.h b/graphics/jpeg.h new file mode 100644 index 0000000000..f4743a5e83 --- /dev/null +++ b/graphics/jpeg.h @@ -0,0 +1,118 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef GRAPHICS_JPEG_H +#define GRAPHICS_JPEG_H + +#include "common/stream.h" +#include "graphics/surface.h" + +namespace Graphics { + +#define JPEG_MAX_QUANT_TABLES 4 +#define JPEG_MAX_HUFF_TABLES 2 + +class JPEG { +public: + JPEG(); + ~JPEG(); + + bool read(Common::SeekableReadStream *str); + Surface *getComponent(uint c); + +private: + void reset(); + + Common::SeekableReadStream *_str; + uint16 _w, _h; + + // Image components + uint8 _numComp; + struct Component { + // Global values + uint8 id; + uint8 factorH; + uint8 factorV; + uint8 quantTableSelector; + + // Scan specific values + uint8 DCentropyTableSelector; + uint8 ACentropyTableSelector; + int16 DCpredictor; + + // Result image for this component + Surface surface; + } *_components; + + // Scan components + uint8 _numScanComp; + Component **_scanComp; + Component *_currentComp; + + // Maximum sampling factors, used to calculate the interleaving of the MCU + uint8 _maxFactorV; + uint8 _maxFactorH; + + // Zig-Zag order + static uint8 _zigZagOrder[64]; + + // Quantization tables + uint16 *_quant[JPEG_MAX_QUANT_TABLES]; + + // Huffman tables + struct HuffmanTable { + uint8 count; + uint8 *values; + uint8 *sizes; + uint16 *codes; + } _huff[2 * JPEG_MAX_HUFF_TABLES]; + + // Marker read functions + bool readJFIF(); + bool readSOF0(); + bool readDHT(); + bool readSOS(); + bool readDQT(); + + // Helper functions + bool readMCU(uint16 xMCU, uint16 yMCU); + bool readDataUnit(uint16 x, uint16 y); + int16 readDC(); + void readAC(int16 *out); + int16 readSignedBits(uint8 numBits); + + // Huffman decoding + uint8 readHuff(uint8 table); + uint8 readBit(); + uint8 _bitsData; + uint8 _bitsNumber; + + // Discrete Cosine Transformation + float idct(int x, int y, int weight, int fx, int fy); +}; + +} // End of Graphics namespace + +#endif // GRAPHICS_JPEG_H diff --git a/graphics/module.mk b/graphics/module.mk index 46ed564e1e..ed14051243 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -1,6 +1,7 @@ MODULE := graphics MODULE_OBJS := \ + conversion.o \ cursorman.o \ dither.o \ font.o \ @@ -11,6 +12,7 @@ MODULE_OBJS := \ fonts/scummfont.o \ iff.o \ imagedec.o \ + jpeg.o \ primitives.o \ scaler.o \ scaler/thumbnail_intern.o \ diff --git a/graphics/pixelformat.h b/graphics/pixelformat.h index f59650e5cc..d16de51ea7 100644 --- a/graphics/pixelformat.h +++ b/graphics/pixelformat.h @@ -27,6 +27,7 @@ #define GRAPHICS_PIXELFORMAT_H #include "common/scummsys.h" +#include "common/list.h" namespace Graphics { @@ -50,6 +51,24 @@ struct PixelFormat { byte rLoss, gLoss, bLoss, aLoss; /**< Precision loss of each color component. */ byte rShift, gShift, bShift, aShift; /**< Binary left shift of each color component in the pixel value. */ + inline PixelFormat() { + bytesPerPixel = + rLoss = gLoss = bLoss = aLoss = + rShift = gShift = bShift = aShift = 0; + } + + inline PixelFormat(byte BytesPerPixel, + byte RBits, byte GBits, byte BBits, byte ABits, + byte RShift, byte GShift, byte BShift, byte AShift) { + bytesPerPixel = BytesPerPixel; + rLoss = 8 - RBits, gLoss = 8 - GBits, bLoss = 8 - BBits, aLoss = 8 - ABits; + rShift = RShift, gShift = GShift, bShift = BShift, aShift = AShift; + } + + static inline PixelFormat createFormatCLUT8() { + return PixelFormat(1, 0, 0, 0, 0, 0, 0, 0, 0); + } + inline bool operator==(const PixelFormat &fmt) const { // TODO: If aLoss==8, then the value of aShift is irrelevant, and should be ignored. return 0 == memcmp(this, &fmt, sizeof(PixelFormat)); @@ -129,6 +148,26 @@ struct PixelFormat { } }; +/** + * Determines the first matching format between two lists. + * + * @param backend The higher priority list, meant to be a list of formats supported by the backend + * @param frontend The lower priority list, meant to be a list of formats supported by the engine + * @return The first item on the backend list that also occurs on the frontend list + * or PixelFormat::createFormatCLUT8() if no matching formats were found. + */ +inline PixelFormat findCompatibleFormat(Common::List<PixelFormat> backend, Common::List<PixelFormat> frontend) { +#ifdef ENABLE_RGB_COLOR + for (Common::List<PixelFormat>::iterator i = backend.begin(); i != backend.end(); ++i) { + for (Common::List<PixelFormat>::iterator j = frontend.begin(); j != frontend.end(); ++j) { + if (*i == *j) + return *i; + } + } +#endif + return PixelFormat::createFormatCLUT8(); +} + } // end of namespace Graphics #endif diff --git a/graphics/scaler.cpp b/graphics/scaler.cpp index a3aaaa121d..8c677cc083 100644 --- a/graphics/scaler.cpp +++ b/graphics/scaler.cpp @@ -30,18 +30,17 @@ int gBitFormat = 565; -static const Graphics::PixelFormat gPixelFormat555 = { +static const Graphics::PixelFormat gPixelFormat555( 2, 3, 3, 3, 8, 10, 5, 0, 0 - }; + ); -static const Graphics::PixelFormat gPixelFormat565 = { +static const Graphics::PixelFormat gPixelFormat565( 2, 3, 2, 3, 8, 11, 5, 0, 0 - }; - + ); #ifndef DISABLE_HQ_SCALERS diff --git a/graphics/scaler/thumbnail_intern.cpp b/graphics/scaler/thumbnail_intern.cpp index fabe07b031..a542fe2268 100644 --- a/graphics/scaler/thumbnail_intern.cpp +++ b/graphics/scaler/thumbnail_intern.cpp @@ -23,6 +23,7 @@ * */ +#include "common/endian.h" #include "common/scummsys.h" #include "common/system.h" @@ -98,7 +99,8 @@ static bool grabScreen565(Graphics::Surface *surf) { if (!screen) return false; - assert(screen->bytesPerPixel == 1 && screen->pixels != 0); + assert(screen->bytesPerPixel == 1 || screen->bytesPerPixel == 2); + assert(screen->pixels != 0); byte palette[256 * 4]; g_system->grabPalette(&palette[0], 0, 256); @@ -107,11 +109,17 @@ static bool grabScreen565(Graphics::Surface *surf) { for (uint y = 0; y < screen->h; ++y) { for (uint x = 0; x < screen->w; ++x) { - byte r, g, b; - r = palette[((uint8*)screen->pixels)[y * screen->pitch + x] * 4]; - g = palette[((uint8*)screen->pixels)[y * screen->pitch + x] * 4 + 1]; - b = palette[((uint8*)screen->pixels)[y * screen->pitch + x] * 4 + 2]; - + byte r, g, b; + if (screen->bytesPerPixel == 2) { + uint16 col = READ_UINT16(screen->getBasePtr(x, y)); + r = ((col >> 10) & 0x1F) << 3; + g = ((col >> 5) & 0x1F) << 3; + b = ((col >> 0) & 0x1F) << 3; + } else { + r = palette[((uint8*)screen->pixels)[y * screen->pitch + x] * 4]; + g = palette[((uint8*)screen->pixels)[y * screen->pitch + x] * 4 + 1]; + b = palette[((uint8*)screen->pixels)[y * screen->pitch + x] * 4 + 2]; + } ((uint16*)surf->pixels)[y * surf->w + x] = Graphics::RGBToColor<Graphics::ColorMasks<565> >(r, g, b); } } diff --git a/graphics/video/coktelvideo/coktelvideo.cpp b/graphics/video/coktelvideo/coktelvideo.cpp index 39aeca07bd..063ad190e6 100644 --- a/graphics/video/coktelvideo/coktelvideo.cpp +++ b/graphics/video/coktelvideo/coktelvideo.cpp @@ -26,6 +26,7 @@ #include "common/endian.h" #include "common/system.h" +#include "graphics/conversion.h" #include "graphics/dither.h" #include "graphics/video/coktelvideo/coktelvideo.h" #include "graphics/video/coktelvideo/indeo3.h" @@ -1915,7 +1916,7 @@ void Vmd::blit16(byte *dest, byte *src, int16 srcPitch, int16 width, int16 heigh byte b = ((data & 0x001F) >> 0); byte dY, dU, dV; - Graphics::PaletteLUT::RGB2YUV(r << 3, g << 3, b << 3, dY, dU, dV); + Graphics::RGB2YUV(r << 3, g << 3, b << 3, dY, dU, dV); byte p = dither->dither(dY, dU, dV, j); @@ -1949,7 +1950,7 @@ void Vmd::blit24(byte *dest, byte *src, int16 srcPitch, int16 width, int16 heigh byte b = s[0]; byte dY, dU, dV; - Graphics::PaletteLUT::RGB2YUV(r, g, b, dY, dU, dV); + Graphics::RGB2YUV(r, g, b, dY, dU, dV); byte p = dither->dither(dY, dU, dV, j); |