diff options
author | Strangerke | 2012-10-10 08:26:41 +0200 |
---|---|---|
committer | Strangerke | 2012-10-10 08:26:41 +0200 |
commit | b164cbb571fc4e0f2a6f002760a851d8ac592540 (patch) | |
tree | 4d25f2e1f8241f6f3352fd9fb1135f5faa36dfd4 /graphics | |
parent | b2f2f8d7b08b40e43702e8db325f8136066f10be (diff) | |
parent | 1e200620d673af4acdd2d128ed6e390df001aacf (diff) | |
download | scummvm-rg350-b164cbb571fc4e0f2a6f002760a851d8ac592540.tar.gz scummvm-rg350-b164cbb571fc4e0f2a6f002760a851d8ac592540.tar.bz2 scummvm-rg350-b164cbb571fc4e0f2a6f002760a851d8ac592540.zip |
Merge branch 'master' of github.com:scummvm/scummvm into mortevielle
Conflicts:
base/plugins.cpp
configure
Diffstat (limited to 'graphics')
42 files changed, 1795 insertions, 924 deletions
diff --git a/graphics/VectorRenderer.cpp b/graphics/VectorRenderer.cpp index 30ef9eeeeb..f426dd8c41 100644 --- a/graphics/VectorRenderer.cpp +++ b/graphics/VectorRenderer.cpp @@ -80,7 +80,7 @@ void VectorRenderer::stepGetPositions(const DrawStep &step, const Common::Rect & case Graphics::DrawStep::kVectorAlignManual: if (step.x >= 0) in_x = area.left + step.x + step.padding.left; - else + else in_x = area.left + area.width() + step.x + step.padding.left; // value relative to the opposite corner. break; diff --git a/graphics/VectorRenderer.h b/graphics/VectorRenderer.h index e98f4aa761..0467cac946 100644 --- a/graphics/VectorRenderer.h +++ b/graphics/VectorRenderer.h @@ -55,7 +55,7 @@ struct DrawStep { bool autoWidth, autoHeight; int16 x, y, w, h; /**< width, height and position, if not measured automatically. negative values mean counting from the opposite direction */ - + Common::Rect padding; enum VectorAlignment { diff --git a/graphics/VectorRendererSpec.cpp b/graphics/VectorRendererSpec.cpp index 7817725664..6a3ee306a5 100644 --- a/graphics/VectorRendererSpec.cpp +++ b/graphics/VectorRendererSpec.cpp @@ -369,12 +369,12 @@ gradientFill(PixelType *ptr, int width, int x, int y) { int grad = (((y - _gradIndexes[curGrad]) % stripSize) << 2) / stripSize; // Dithering: - // +--+ +--+ +--+ +--+ - // | | | | | *| | *| - // | | | *| |* | |**| - // +--+ +--+ +--+ +--+ + // +--+ +--+ +--+ +--+ + // | | | | | *| | *| + // | | | *| |* | |**| + // +--+ +--+ +--+ +--+ // 0 1 2 3 - if (grad == 0 || + if (grad == 0 || _gradCache[curGrad] == _gradCache[curGrad + 1] || // no color change stripSize < 2) { // the stip is small colorFill<PixelType>(ptr, ptr + width, _gradCache[curGrad]); @@ -422,8 +422,8 @@ void VectorRendererSpec<PixelType>:: copyFrame(OSystem *sys, const Common::Rect &r) { sys->copyRectToOverlay( - (const OverlayColor *)_activeSurface->getBasePtr(r.left, r.top), - _activeSurface->pitch / _activeSurface->format.bytesPerPixel, + _activeSurface->getBasePtr(r.left, r.top), + _activeSurface->pitch, r.left, r.top, r.width(), r.height() ); } @@ -873,7 +873,7 @@ drawTriangle(int x, int y, int w, int h, TriangleOrientation orient) { case kTriangleDown: drawTriangleVertAlg(x, y, newW, newH, (orient == kTriangleDown), color, Base::_fillMode); break; - + case kTriangleLeft: case kTriangleRight: case kTriangleAuto: @@ -1206,14 +1206,14 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color pitch = -pitch; y1 += h; } - + PixelType *ptr_right = (PixelType *)_activeSurface->getBasePtr(x1, y1); PixelType *floor = ptr_right - 1; PixelType *ptr_left = (PixelType *)_activeSurface->getBasePtr(x1 + w, y1); int x2 = x1 + w / 2; int y2 = y1 + h; - + #if FIXED_POINT int dx = (x2 - x1) << 8; int dy = (y2 - y1) << 8; @@ -1227,7 +1227,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color #endif while (floor++ != ptr_left) blendPixelPtr(floor, color, 50); - + #if FIXED_POINT int gradient = (dy << 8) / dx; int intery = (y1 << 8) + gradient; @@ -1250,7 +1250,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color ptr_right += pitch; intery += gradient; - + switch (fill_m) { case kFillDisabled: *ptr_left = *ptr_right = color; @@ -1262,16 +1262,16 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color blendPixelPtr(ptr_left, color, rfpart(intery)); break; case kFillGradient: - colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h)); + colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h)); blendPixelPtr(ptr_right, color, rfpart(intery)); blendPixelPtr(ptr_left, color, rfpart(intery)); break; } } - + return; } - + #if FIXED_POINT if (abs(dx) < abs(dy)) { #else @@ -1280,7 +1280,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color ptr_left--; while (floor++ != ptr_left) blendPixelPtr(floor, color, 50); - + #if FIXED_POINT int gradient = (dx << 8) / (dy + 0x100); int interx = (x1 << 8) + gradient; @@ -1303,7 +1303,7 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color ptr_right += pitch; interx += gradient; - + switch (fill_m) { case kFillDisabled: *ptr_left = *ptr_right = color; @@ -1315,18 +1315,18 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color blendPixelPtr(ptr_left, color, rfpart(interx)); break; case kFillGradient: - colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h)); + colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h)); blendPixelPtr(ptr_right, color, rfpart(interx)); blendPixelPtr(ptr_left, color, rfpart(interx)); break; } } - + return; } - + ptr_left--; - + while (floor++ != ptr_left) blendPixelPtr(floor, color, 50); @@ -1341,12 +1341,12 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color for (int y = y1 + 1; y < y2; y++) { ptr_right++; ptr_left--; - + ptr_left += pitch; ptr_right += pitch; interx += gradient; - + switch (fill_m) { case kFillDisabled: *ptr_left = *ptr_right = color; @@ -1358,13 +1358,13 @@ drawTriangleVertAlg(int x1, int y1, int w, int h, bool inverted, PixelType color blendPixelPtr(ptr_left, color, rfpart(interx)); break; case kFillGradient: - colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h)); + colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, h)); blendPixelPtr(ptr_right, color, rfpart(interx)); blendPixelPtr(ptr_left, color, rfpart(interx)); break; } } - + } /** VERTICAL TRIANGLE DRAWING - FAST VERSION FOR SQUARED TRIANGLES */ @@ -1372,12 +1372,12 @@ template<typename PixelType> void VectorRendererSpec<PixelType>:: drawTriangleFast(int x1, int y1, int size, bool inverted, PixelType color, VectorRenderer::FillMode fill_m) { int pitch = _activeSurface->pitch / _activeSurface->format.bytesPerPixel; - + if (!inverted) { pitch = -pitch; y1 += size; } - + int gradient_h = 0; PixelType *ptr_right = (PixelType *)_activeSurface->getBasePtr(x1, y1); PixelType *ptr_left = (PixelType *)_activeSurface->getBasePtr(x1 + size, y1); @@ -1388,9 +1388,9 @@ drawTriangleFast(int x1, int y1, int size, bool inverted, PixelType color, Vecto int signX = x1 < x2 ? 1 : -1; int signY = y1 < y2 ? 1 : -1; int error = deltaX - deltaY; - + colorFill<PixelType>(ptr_right, ptr_left, color); - + while (1) { switch (fill_m) { case kFillDisabled: @@ -1401,22 +1401,22 @@ drawTriangleFast(int x1, int y1, int size, bool inverted, PixelType color, Vecto colorFill<PixelType>(ptr_right, ptr_left, color); break; case kFillGradient: - colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, size)); + colorFill<PixelType>(ptr_right, ptr_left, calcGradient(gradient_h++, size)); break; } - + if (x1 == x2 && y1 == y2) break; - + int error2 = error * 2; - + if (error2 > -deltaY) { error -= deltaY; x1 += signX; ptr_right += signX; ptr_left += -signX; } - + if (error2 < deltaX) { error += deltaX; y1 += signY; diff --git a/graphics/conversion.cpp b/graphics/conversion.cpp index 713a06ea74..2da8b6f0ce 100644 --- a/graphics/conversion.cpp +++ b/graphics/conversion.cpp @@ -22,123 +22,143 @@ #include "graphics/conversion.h" #include "graphics/pixelformat.h" +#include "common/endian.h" + namespace Graphics { // TODO: YUV to RGB conversion function +namespace { + +template<typename SrcColor, typename DstColor, bool backward> +inline void crossBlitLogic(byte *dst, const byte *src, const uint w, const uint h, + const PixelFormat &srcFmt, const PixelFormat &dstFmt, + const uint srcDelta, const uint dstDelta) { + for (uint y = 0; y < h; ++y) { + for (uint x = 0; x < w; ++x) { + const uint32 color = *(const SrcColor *)src; + byte a, r, g, b; + srcFmt.colorToARGB(color, a, r, g, b); + *(DstColor *)dst = dstFmt.ARGBToColor(a, r, g, b); + + if (backward) { + src -= sizeof(SrcColor); + dst -= sizeof(DstColor); + } else { + src += sizeof(SrcColor); + dst += sizeof(DstColor); + } + } + + if (backward) { + src -= srcDelta; + dst -= dstDelta; + } else { + src += srcDelta; + dst += dstDelta; + } + } +} + +template<typename DstColor, bool backward> +inline void crossBlitLogic3BppSource(byte *dst, const byte *src, const uint w, const uint h, + const PixelFormat &srcFmt, const PixelFormat &dstFmt, + const uint srcDelta, const uint dstDelta) { + uint32 color; + byte r, g, b, a; + uint8 *col = (uint8 *)&color; +#ifdef SCUMM_BIG_ENDIAN + col++; +#endif + for (uint y = 0; y < h; ++y) { + for (uint x = 0; x < w; ++x) { + memcpy(col, src, 3); + srcFmt.colorToARGB(color, a, r, g, b); + *(DstColor *)dst = dstFmt.ARGBToColor(a, r, g, b); + + if (backward) { + src -= 3; + dst -= sizeof(DstColor); + } else { + src += 3; + dst += sizeof(DstColor); + } + } + + if (backward) { + src -= srcDelta; + dst -= dstDelta; + } else { + src += srcDelta; + dst += dstDelta; + } + } +} + +} // End of anonymous namespace + // 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) { +bool crossBlit(byte *dst, const byte *src, + const uint dstPitch, const uint srcPitch, + const uint w, const uint 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)) + || (dstFmt.bytesPerPixel == 3) + || (!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; + if (dst != src) { + if (dstPitch == srcPitch && ((w * dstFmt.bytesPerPixel) == dstPitch)) { + memcpy(dst, src, dstPitch * h); + } else { + for (uint i = 0; i < h; ++i) { + memcpy(dst, src, w * dstFmt.bytesPerPixel); + dst += dstPitch; + src += srcPitch; + } } - return true; } + + 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); + const uint srcDelta = (srcPitch - w * srcFmt.bytesPerPixel); + const uint 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 = *(const 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 = *(const uint16 *)src; - srcFmt.colorToARGB(color, a, r, g, b); - color = dstFmt.ARGBToColor(a, r, g, b); - memcpy(dst, col, 3); - } - src += srcDelta; - dst += dstDelta; - } + crossBlitLogic<uint16, uint16, false>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); + } else if (srcFmt.bytesPerPixel == 3) { + crossBlitLogic3BppSource<uint16, false>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } else { - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++, src += 3, dst += 3) { - 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; - } + crossBlitLogic<uint32, uint16, false>(dst, src, w, h, srcFmt, dstFmt, srcDelta, 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 = *(const uint16 *)src; - srcFmt.colorToARGB(color, a, r, g, b); - color = dstFmt.ARGBToColor(a, r, g, b); - *(uint32 *)dst = color; - } - src += srcDelta; - dst += dstDelta; - } + // We need to blit the surface from bottom right to top left here. + // This is neeeded, because when we convert to the same memory + // buffer copying the surface from top left to bottom right would + // overwrite the source, since we have more bits per destination + // color than per source color. + dst += h * dstPitch - dstDelta - dstFmt.bytesPerPixel; + src += h * srcPitch - srcDelta - srcFmt.bytesPerPixel; + crossBlitLogic<uint16, uint32, true>(dst, src, w, h, srcFmt, dstFmt, srcDelta, 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; - } + // We need to blit the surface from bottom right to top left here. + // This is neeeded, because when we convert to the same memory + // buffer copying the surface from top left to bottom right would + // overwrite the source, since we have more bits per destination + // color than per source color. + dst += h * dstPitch - dstDelta - dstFmt.bytesPerPixel; + src += h * srcPitch - srcDelta - srcFmt.bytesPerPixel; + crossBlitLogic3BppSource<uint32, true>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } else { - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++, src += 4, dst += 4) { - color = *(const uint32 *)src; - srcFmt.colorToARGB(color, a, r, g, b); - color = dstFmt.ARGBToColor(a, r, g, b); - *(uint32 *)dst = color; - } - src += srcDelta; - dst += dstDelta; - } + crossBlitLogic<uint32, uint32, false>(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } } else { return false; diff --git a/graphics/conversion.h b/graphics/conversion.h index 6babc763e2..28e64a94fb 100644 --- a/graphics/conversion.h +++ b/graphics/conversion.h @@ -59,15 +59,18 @@ inline static void RGB2YUV(byte r, byte g, byte b, byte &y, byte &u, byte &v) { * @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. - * + * @note Blitting to a 3Bpp destination is not supported + * @note This can convert a surface in place, regardless of the + * source and destination format, as long as there is enough + * space for the destination. The dstPitch / srcPitch ratio + * must at least equal the dstBpp / srcBpp ratio for + * dstPitch >= srcPitch and at most dstBpp / srcBpp for + * dstPitch < srcPitch though. */ -bool crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch, - int w, int h, const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt); +bool crossBlit(byte *dst, const byte *src, + const uint dstPitch, const uint srcPitch, + const uint w, const uint h, + const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt); } // End of namespace Graphics diff --git a/graphics/cursorman.cpp b/graphics/cursorman.cpp index 425714ea34..c818101645 100644 --- a/graphics/cursorman.cpp +++ b/graphics/cursorman.cpp @@ -55,14 +55,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, uint32 keycolor, int targetScale, const Graphics::PixelFormat *format) { - Cursor *cur = new Cursor(buf, w, h, hotspotX, hotspotY, keycolor, targetScale, format); +void CursorManager::pushCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) { + Cursor *cur = new Cursor(buf, w, h, hotspotX, hotspotY, keycolor, dontScale, format); cur->_visible = isVisible(); _cursorStack.push(cur); if (buf) { - g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, targetScale, format); + g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, dontScale, format); } } @@ -75,7 +75,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, &cur->_format); + g_system->setMouseCursor(cur->_data, cur->_width, cur->_height, cur->_hotspotX, cur->_hotspotY, cur->_keycolor, cur->_dontScale, &cur->_format); } g_system->showMouse(isVisible()); @@ -98,10 +98,10 @@ 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 void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) { if (_cursorStack.empty()) { - pushCursor(buf, w, h, hotspotX, hotspotY, keycolor, targetScale, format); + pushCursor(buf, w, h, hotspotX, hotspotY, keycolor, dontScale, format); return; } @@ -131,7 +131,7 @@ void CursorManager::replaceCursor(const byte *buf, uint w, uint h, int hotspotX, cur->_hotspotX = hotspotX; cur->_hotspotY = hotspotY; cur->_keycolor = keycolor; - cur->_targetScale = targetScale; + cur->_dontScale = dontScale; #ifdef USE_RGB_COLOR if (format) cur->_format = *format; @@ -139,7 +139,7 @@ void CursorManager::replaceCursor(const byte *buf, uint w, uint h, int hotspotX, cur->_format = Graphics::PixelFormat::createFormatCLUT8(); #endif - g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, targetScale, format); + g_system->setMouseCursor(cur->_data, w, h, hotspotX, hotspotY, keycolor, dontScale, format); } bool CursorManager::supportsCursorPalettes() { @@ -225,7 +225,7 @@ void CursorManager::replaceCursorPalette(const byte *colors, uint start, uint nu } } -CursorManager::Cursor::Cursor(const byte *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int targetScale, const Graphics::PixelFormat *format) { +CursorManager::Cursor::Cursor(const void *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale, const Graphics::PixelFormat *format) { #ifdef USE_RGB_COLOR if (!format) _format = Graphics::PixelFormat::createFormatCLUT8(); @@ -245,7 +245,7 @@ CursorManager::Cursor::Cursor(const byte *data, uint w, uint h, int hotspotX, in _height = h; _hotspotX = hotspotX; _hotspotY = hotspotY; - _targetScale = targetScale; + _dontScale = dontScale; } CursorManager::Cursor::~Cursor() { diff --git a/graphics/cursorman.h b/graphics/cursorman.h index 543a5d0a5c..66e8d1ba56 100644 --- a/graphics/cursorman.h +++ b/graphics/cursorman.h @@ -63,14 +63,15 @@ public: * @param hotspotY the hotspot Y coordinate * @param keycolor the color value for the transparent color. This may not exceed * the maximum color value as defined by format. - * @param targetScale the scale for which the cursor is designed + * @param dontScale Whether the cursor should never be scaled. An exception are high ppi displays, where the cursor + * would be too small to notice otherwise, these are allowed to scale the cursor anyway. * @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, uint32 keycolor, int targetScale = 1, const Graphics::PixelFormat *format = NULL); + void pushCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL); /** * Pop a cursor from the stack, and restore the previous one to the @@ -90,11 +91,12 @@ public: * @param hotspotY the hotspot Y coordinate * @param keycolor the color value for the transparent color. This may not exceed * the maximum color value as defined by format. - * @param targetScale the scale for which the cursor is designed + * @param dontScale Whether the cursor should never be scaled. An exception are high ppi displays, where the cursor + * would be too small to notice otherwise, these are allowed to scale the cursor anyway. * @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, uint32 keycolor, int targetScale = 1, const Graphics::PixelFormat *format = NULL); + void replaceCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL); /** * Pop all of the cursors and cursor palettes from their respective stacks. @@ -175,11 +177,11 @@ private: int _hotspotY; uint32 _keycolor; Graphics::PixelFormat _format; - int _targetScale; + bool _dontScale; uint _size; - Cursor(const byte *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int targetScale = 1, const Graphics::PixelFormat *format = NULL); + Cursor(const void *data, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, bool dontScale = false, const Graphics::PixelFormat *format = NULL); ~Cursor(); }; diff --git a/graphics/decoders/bmp.cpp b/graphics/decoders/bmp.cpp index 0d44881d7c..bcfd0abbda 100644 --- a/graphics/decoders/bmp.cpp +++ b/graphics/decoders/bmp.cpp @@ -31,6 +31,7 @@ namespace Graphics { BitmapDecoder::BitmapDecoder() { _surface = 0; _palette = 0; + _paletteColorCount = 0; } BitmapDecoder::~BitmapDecoder() { @@ -44,6 +45,7 @@ void BitmapDecoder::destroy() { } delete[] _palette; _palette = 0; + _paletteColorCount = 0; } bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) { @@ -80,7 +82,7 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) { /* uint16 planes = */ stream.readUint16LE(); uint16 bitsPerPixel = stream.readUint16LE(); - if (bitsPerPixel != 8 && bitsPerPixel != 24) { + if (bitsPerPixel != 8 && bitsPerPixel != 24 && bitsPerPixel != 32) { warning("%dbpp bitmaps not supported", bitsPerPixel); return false; } @@ -95,16 +97,16 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) { /* uint32 imageSize = */ stream.readUint32LE(); /* uint32 pixelsPerMeterX = */ stream.readUint32LE(); /* uint32 pixelsPerMeterY = */ stream.readUint32LE(); - uint32 colorsUsed = stream.readUint32LE(); + _paletteColorCount = stream.readUint32LE(); /* uint32 colorsImportant = */ stream.readUint32LE(); - if (colorsUsed == 0) - colorsUsed = 256; - if (bitsPerPixel == 8) { + if (_paletteColorCount == 0) + _paletteColorCount = 256; + // Read the palette - _palette = new byte[colorsUsed * 3]; - for (uint16 i = 0; i < colorsUsed; i++) { + _palette = new byte[_paletteColorCount * 3]; + for (uint16 i = 0; i < _paletteColorCount; i++) { _palette[i * 3 + 2] = stream.readByte(); _palette[i * 3 + 1] = stream.readByte(); _palette[i * 3 + 0] = stream.readByte(); @@ -117,8 +119,8 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) { Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8(); - // BGRA for 24bpp - if (bitsPerPixel == 24) + // BGRA for 24bpp and 32 bpp + if (bitsPerPixel == 24 || bitsPerPixel == 32) format = Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0); _surface = new Graphics::Surface(); @@ -134,7 +136,24 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) { stream.read(dst + (height - i - 1) * width, width); stream.skip(extraDataLength); } - } else { + } else if (bitsPerPixel == 24) { + byte *dst = (byte *)_surface->pixels + (height - 1) * _surface->pitch; + + for (int32 i = 0; i < height; i++) { + for (uint32 j = 0; j < width; j++) { + byte b = stream.readByte(); + byte g = stream.readByte(); + byte r = stream.readByte(); + uint32 color = format.RGBToColor(r, g, b); + + *((uint32 *)dst) = color; + dst += format.bytesPerPixel; + } + + stream.skip(extraDataLength); + dst -= _surface->pitch * 2; + } + } else { // 32 bpp byte *dst = (byte *)_surface->pixels + (height - 1) * _surface->pitch; for (int32 i = 0; i < height; i++) { @@ -142,6 +161,10 @@ bool BitmapDecoder::loadStream(Common::SeekableReadStream &stream) { byte b = stream.readByte(); byte g = stream.readByte(); byte r = stream.readByte(); + // Ignore the last byte, as in v3 it is unused + // and should thus NOT be used as alpha. + // ref: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183376%28v=vs.85%29.aspx + stream.readByte(); uint32 color = format.RGBToColor(r, g, b); *((uint32 *)dst) = color; diff --git a/graphics/decoders/bmp.h b/graphics/decoders/bmp.h index e11b12fad6..59da682e4d 100644 --- a/graphics/decoders/bmp.h +++ b/graphics/decoders/bmp.h @@ -19,6 +19,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/** + * @file + * Image decoder used in engines: + * - hugo + * - mohawk + */ + #ifndef GRAPHICS_DECODERS_BMP_H #define GRAPHICS_DECODERS_BMP_H @@ -44,11 +51,13 @@ public: void destroy(); virtual bool loadStream(Common::SeekableReadStream &stream); virtual const Surface *getSurface() const { return _surface; } - virtual const byte *getPalette() { return _palette; } + const byte *getPalette() const { return _palette; } + uint16 getPaletteColorCount() const { return _paletteColorCount; } private: Surface *_surface; byte *_palette; + uint16 _paletteColorCount; }; } // End of namespace Graphics diff --git a/graphics/decoders/image_decoder.h b/graphics/decoders/image_decoder.h index e768f7f9a2..49e31c6e3a 100644 --- a/graphics/decoders/image_decoder.h +++ b/graphics/decoders/image_decoder.h @@ -75,9 +75,22 @@ public: * until destroy() or loadStream() is called, or until this ImageDecoder's * destructor is called. * - * @return the decoded palette, or 0 if no palette is present + * The palette's format is the same as PaletteManager's palette + * (interleaved RGB values). + * + * @return the decoded palette, or undefined if no palette is present */ virtual const byte *getPalette() const { return 0; } + + /** + * Query if the decoded image has a palette. + */ + virtual bool hasPalette() const { return getPaletteColorCount() != 0; } + + /** Return the starting index of the palette. */ + virtual byte getPaletteStartIndex() const { return 0; } + /** Return the number of colors in the palette. */ + virtual uint16 getPaletteColorCount() const { return 0; } }; } // End of namespace Graphics diff --git a/graphics/decoders/jpeg.cpp b/graphics/decoders/jpeg.cpp index a871377ca1..08bc1f7a3d 100644 --- a/graphics/decoders/jpeg.cpp +++ b/graphics/decoders/jpeg.cpp @@ -81,7 +81,7 @@ const Surface *JPEGDecoder::getSurface() const { const Graphics::Surface *uComponent = getComponent(2); const Graphics::Surface *vComponent = getComponent(3); - convertYUV444ToRGB(_rgbSurface, (byte *)yComponent->pixels, (byte *)uComponent->pixels, (byte *)vComponent->pixels, yComponent->w, yComponent->h, yComponent->pitch, uComponent->pitch); + YUVToRGBMan.convert444(_rgbSurface, Graphics::YUVToRGBManager::kScaleFull, (byte *)yComponent->pixels, (byte *)uComponent->pixels, (byte *)vComponent->pixels, yComponent->w, yComponent->h, yComponent->pitch, uComponent->pitch); return _rgbSurface; } @@ -452,7 +452,7 @@ bool JPEGDecoder::readSOS() { _bitsNumber = 0; for (byte i = 0; i < _numScanComp; i++) - _scanComp[i]->DCpredictor = 0; + _scanComp[i]->DCpredictor = 0; } } } diff --git a/graphics/decoders/jpeg.h b/graphics/decoders/jpeg.h index c566d5ad21..c74aa57ca1 100644 --- a/graphics/decoders/jpeg.h +++ b/graphics/decoders/jpeg.h @@ -20,6 +20,13 @@ * */ +/** + * @file + * Image decoder used in engines: + * - groovie + * - mohawk + */ + #ifndef GRAPHICS_JPEG_H #define GRAPHICS_JPEG_H diff --git a/graphics/decoders/pcx.cpp b/graphics/decoders/pcx.cpp new file mode 100644 index 0000000000..1250398c73 --- /dev/null +++ b/graphics/decoders/pcx.cpp @@ -0,0 +1,213 @@ +/* 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. + */ + +#include "common/stream.h" +#include "common/textconsole.h" + +#include "graphics/pixelformat.h" +#include "graphics/surface.h" +#include "graphics/decoders/pcx.h" + +/** + * Based on the PCX specs: + * http://www.fileformat.info/format/pcx/spec/a10e75307b3a4cc49c3bbe6db4c41fa2/view.htm + * and the PCX decoder of FFmpeg (libavcodec/pcx.c): + * http://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavcodec/pcx.c + */ + +namespace Graphics { + +PCXDecoder::PCXDecoder() { + _surface = 0; + _palette = 0; + _paletteColorCount = 0; +} + +PCXDecoder::~PCXDecoder() { + destroy(); +} + +void PCXDecoder::destroy() { + if (_surface) { + _surface->free(); + delete _surface; + _surface = 0; + } + + delete[] _palette; + _palette = 0; + _paletteColorCount = 0; +} + +bool PCXDecoder::loadStream(Common::SeekableReadStream &stream) { + destroy(); + + if (stream.readByte() != 0x0a) // ZSoft PCX + return false; + + byte version = stream.readByte(); // 0 - 5 + if (version > 5) + return false; + + bool compressed = stream.readByte(); // encoding, 1 = run length encoding + byte bitsPerPixel = stream.readByte(); // 1, 2, 4 or 8 + + // Window + uint16 xMin = stream.readUint16LE(); + uint16 yMin = stream.readUint16LE(); + uint16 xMax = stream.readUint16LE(); + uint16 yMax = stream.readUint16LE(); + + uint16 width = xMax - xMin + 1; + uint16 height = yMax - yMin + 1; + + if (xMax < xMin || yMax < yMin) { + warning("Invalid PCX image dimensions"); + return false; + } + + stream.skip(4); // HDpi, VDpi + + // Read the EGA palette (colormap) + _palette = new byte[16 * 3]; + for (uint16 i = 0; i < 16; i++) { + _palette[i * 3 + 0] = stream.readByte(); + _palette[i * 3 + 1] = stream.readByte(); + _palette[i * 3 + 2] = stream.readByte(); + } + + if (stream.readByte() != 0) // reserved, should be set to 0 + return false; + + byte nPlanes = stream.readByte(); + uint16 bytesPerLine = stream.readUint16LE(); + uint16 bytesPerscanLine = nPlanes * bytesPerLine; + + if (bytesPerscanLine < width * bitsPerPixel * nPlanes / 8) { + warning("PCX data is corrupted"); + return false; + } + + stream.skip(60); // PaletteInfo, HscreenSize, VscreenSize, Filler + + _surface = new Graphics::Surface(); + + byte *scanLine = new byte[bytesPerscanLine]; + byte *dst; + int x, y; + + if (nPlanes == 3 && bitsPerPixel == 8) { // 24bpp + Graphics::PixelFormat format = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); + _surface->create(width, height, format); + dst = (byte *)_surface->pixels; + _paletteColorCount = 0; + + for (y = 0; y < height; y++) { + decodeRLE(stream, scanLine, bytesPerscanLine, compressed); + + for (x = 0; x < width; x++) { + byte b = scanLine[x]; + byte g = scanLine[x + bytesPerLine]; + byte r = scanLine[x + (bytesPerLine << 1)]; + uint32 color = format.RGBToColor(r, g, b); + + *((uint32 *)dst) = color; + dst += format.bytesPerPixel; + } + } + } else if (nPlanes == 1 && bitsPerPixel == 8) { // 8bpp indexed + _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + dst = (byte *)_surface->pixels; + _paletteColorCount = 16; + + for (y = 0; y < height; y++, dst += _surface->pitch) { + decodeRLE(stream, scanLine, bytesPerscanLine, compressed); + memcpy(dst, scanLine, width); + } + + if (version == 5) { + if (stream.readByte() != 12) { + warning("Expected a palette after the PCX image data"); + delete[] scanLine; + return false; + } + + // Read the VGA palette + delete[] _palette; + _palette = new byte[256 * 3]; + for (uint16 i = 0; i < 256; i++) { + _palette[i * 3 + 0] = stream.readByte(); + _palette[i * 3 + 1] = stream.readByte(); + _palette[i * 3 + 2] = stream.readByte(); + } + + _paletteColorCount = 256; + } + } else if ((nPlanes == 2 || nPlanes == 3 || nPlanes == 4) && bitsPerPixel == 1) { // planar, 4, 8 or 16 colors + _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + dst = (byte *)_surface->pixels; + _paletteColorCount = 16; + + for (y = 0; y < height; y++, dst += _surface->pitch) { + decodeRLE(stream, scanLine, bytesPerscanLine, compressed); + + for (x = 0; x < width; x++) { + int m = 0x80 >> (x & 7), v = 0; + for (int i = nPlanes - 1; i >= 0; i--) { + v <<= 1; + v += (scanLine[i * bytesPerLine + (x >> 3)] & m) == 0 ? 0 : 1; + } + dst[x] = v; + } + } + } else { + // Known unsupported case: 1 plane and bpp < 8 (1, 2 or 4) + warning("Invalid PCX file (%d planes, %d bpp)", nPlanes, bitsPerPixel); + delete[] scanLine; + return false; + } + + delete[] scanLine; + + return true; +} + +void PCXDecoder::decodeRLE(Common::SeekableReadStream &stream, byte *dst, uint32 bytesPerscanLine, bool compressed) { + uint32 i = 0; + byte run, value; + + if (compressed) { + while (i < bytesPerscanLine) { + run = 1; + value = stream.readByte(); + if (value >= 0xc0) { + run = value & 0x3f; + value = stream.readByte(); + } + while (i < bytesPerscanLine && run--) + dst[i++] = value; + } + } else { + stream.read(dst, bytesPerscanLine); + } +} + +} // End of namespace Graphics diff --git a/graphics/decoders/pcx.h b/graphics/decoders/pcx.h new file mode 100644 index 0000000000..b25166b3d9 --- /dev/null +++ b/graphics/decoders/pcx.h @@ -0,0 +1,68 @@ +/* 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. + */ + +/** + * PCX decoder used in engines: + * - dreamweb + * - hugo + * - queen + * - tucker + */ + +#ifndef GRAPHICS_DECODERS_PCX_H +#define GRAPHICS_DECODERS_PCX_H + +#include "common/scummsys.h" +#include "common/str.h" +#include "graphics/decoders/image_decoder.h" + +namespace Common{ +class SeekableReadStream; +} + +namespace Graphics { + +struct PixelFormat; +struct Surface; + +class PCXDecoder : public ImageDecoder { +public: + PCXDecoder(); + virtual ~PCXDecoder(); + + // ImageDecoder API + void destroy(); + virtual bool loadStream(Common::SeekableReadStream &stream); + virtual const Surface *getSurface() const { return _surface; } + const byte *getPalette() const { return _palette; } + uint16 getPaletteColorCount() const { return _paletteColorCount; } + +private: + void decodeRLE(Common::SeekableReadStream &stream, byte *dst, uint32 bytesPerScanline, bool compressed); + + Surface *_surface; + byte *_palette; + uint16 _paletteColorCount; +}; + +} // End of namespace Graphics + +#endif diff --git a/graphics/decoders/pict.cpp b/graphics/decoders/pict.cpp index 957342084e..d35e5c3064 100644 --- a/graphics/decoders/pict.cpp +++ b/graphics/decoders/pict.cpp @@ -38,6 +38,7 @@ namespace Graphics { PICTDecoder::PICTDecoder() { _outputSurface = 0; + _paletteColorCount = 0; } PICTDecoder::~PICTDecoder() { @@ -50,6 +51,8 @@ void PICTDecoder::destroy() { delete _outputSurface; _outputSurface = 0; } + + _paletteColorCount = 0; } #define OPCODE(a, b, c) _opcodes.push_back(PICTOpcode(a, &PICTDecoder::b, c)) @@ -289,18 +292,18 @@ struct PackBitsRectData { uint16 mode; }; -void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool hasPalette) { +void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool withPalette) { PackBitsRectData packBitsData; - packBitsData.pixMap = readPixMap(stream, !hasPalette); + packBitsData.pixMap = readPixMap(stream, !withPalette); // Read in the palette if there is one present - if (hasPalette) { + if (withPalette) { // See http://developer.apple.com/legacy/mac/library/documentation/mac/QuickDraw/QuickDraw-267.html stream.readUint32BE(); // seed stream.readUint16BE(); // flags - uint16 colorCount = stream.readUint16BE() + 1; + _paletteColorCount = stream.readUint16BE() + 1; - for (uint32 i = 0; i < colorCount; i++) { + for (uint32 i = 0; i < _paletteColorCount; i++) { stream.readUint16BE(); _palette[i * 3] = stream.readUint16BE() >> 8; _palette[i * 3 + 1] = stream.readUint16BE() >> 8; @@ -361,14 +364,14 @@ void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool hasPal memcpy(_outputSurface->pixels, buffer, _outputSurface->w * _outputSurface->h); break; case 2: - // Convert from 16-bit to whatever surface we need + // We have a 16-bit surface _outputSurface->create(width, height, PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); for (uint16 y = 0; y < _outputSurface->h; y++) for (uint16 x = 0; x < _outputSurface->w; x++) WRITE_UINT16(_outputSurface->getBasePtr(x, y), READ_UINT16(buffer + (y * _outputSurface->w + x) * 2)); break; case 3: - // Convert from 24-bit (planar!) to whatever surface we need + // We have a planar 24-bit surface _outputSurface->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); for (uint16 y = 0; y < _outputSurface->h; y++) { for (uint16 x = 0; x < _outputSurface->w; x++) { @@ -380,15 +383,18 @@ void PICTDecoder::unpackBitsRect(Common::SeekableReadStream &stream, bool hasPal } break; case 4: - // Convert from 32-bit (planar!) to whatever surface we need + // We have a planar 32-bit surface + // Note that we ignore the alpha channel since it seems to not be correct + // Mac OS X does not ignore it, but then displays it incorrectly. Photoshop + // does ignore it and displays it correctly. _outputSurface->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); for (uint16 y = 0; y < _outputSurface->h; y++) { for (uint16 x = 0; x < _outputSurface->w; x++) { - byte r = *(buffer + y * _outputSurface->w * 4 + x); - byte g = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w + x); - byte b = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 2 + x); - byte a = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 3 + x); - *((uint32 *)_outputSurface->getBasePtr(x, y)) = _outputSurface->format.ARGBToColor(r, g, b, a); + byte a = 0xFF; + byte r = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w + x); + byte g = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 2 + x); + byte b = *(buffer + y * _outputSurface->w * 4 + _outputSurface->w * 3 + x); + *((uint32 *)_outputSurface->getBasePtr(x, y)) = _outputSurface->format.ARGBToColor(a, r, g, b); } } break; @@ -463,10 +469,10 @@ void PICTDecoder::outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel) { } } -void PICTDecoder::skipBitsRect(Common::SeekableReadStream &stream, bool hasPalette) { +void PICTDecoder::skipBitsRect(Common::SeekableReadStream &stream, bool withPalette) { // Step through a PackBitsRect/DirectBitsRect function - if (!hasPalette) + if (!withPalette) stream.readUint32BE(); uint16 rowBytes = stream.readUint16BE(); @@ -486,7 +492,7 @@ void PICTDecoder::skipBitsRect(Common::SeekableReadStream &stream, bool hasPalet stream.readUint16BE(); // pixelSize stream.skip(16); - if (hasPalette) { + if (withPalette) { stream.readUint32BE(); stream.readUint16BE(); stream.skip((stream.readUint16BE() + 1) * 8); @@ -537,14 +543,18 @@ void PICTDecoder::decodeCompressedQuickTime(Common::SeekableReadStream &stream) // Skip the matte and mask stream.skip(matteSize + maskSize); - + // Now we've reached the image descriptor, so read the relevant data from that uint32 idStart = stream.pos(); uint32 idSize = stream.readUint32BE(); - stream.skip(40); // miscellaneous stuff + uint32 codec = stream.readUint32BE(); + stream.skip(36); // miscellaneous stuff uint32 jpegSize = stream.readUint32BE(); stream.skip(idSize - (stream.pos() - idStart)); // more useless stuff + if (codec != MKTAG('j', 'p', 'e', 'g')) + error("Unhandled CompressedQuickTime format '%s'", tag2str(codec)); + Common::SeekableSubReadStream jpegStream(&stream, stream.pos(), stream.pos() + jpegSize); JPEGDecoder jpeg; diff --git a/graphics/decoders/pict.h b/graphics/decoders/pict.h index b1e45a6bc1..6f0d86c7a1 100644 --- a/graphics/decoders/pict.h +++ b/graphics/decoders/pict.h @@ -20,6 +20,14 @@ * */ +/** + * @file + * Image decoder used in engines: + * - mohawk + * - pegasus + * - sci + */ + #ifndef GRAPHICS_PICT_H #define GRAPHICS_PICT_H @@ -50,6 +58,7 @@ public: void destroy(); const Surface *getSurface() const { return _outputSurface; } const byte *getPalette() const { return _palette; } + uint16 getPaletteColorCount() const { return _paletteColorCount; } struct PixMap { uint32 baseAddr; @@ -74,13 +83,14 @@ public: private: Common::Rect _imageRect; byte _palette[256 * 3]; + uint16 _paletteColorCount; Graphics::Surface *_outputSurface; bool _continueParsing; // Utility Functions - void unpackBitsRect(Common::SeekableReadStream &stream, bool hasPalette); + void unpackBitsRect(Common::SeekableReadStream &stream, bool withPalette); void unpackBitsLine(byte *out, uint32 length, Common::SeekableReadStream *stream, byte bitsPerPixel, byte bytesPerPixel); - void skipBitsRect(Common::SeekableReadStream &stream, bool hasPalette); + void skipBitsRect(Common::SeekableReadStream &stream, bool withPalette); void decodeCompressedQuickTime(Common::SeekableReadStream &stream); void outputPixelBuffer(byte *&out, byte value, byte bitsPerPixel); diff --git a/graphics/decoders/png.cpp b/graphics/decoders/png.cpp index b87b6fdc7a..4f917b44b1 100644 --- a/graphics/decoders/png.cpp +++ b/graphics/decoders/png.cpp @@ -20,86 +20,25 @@ * */ +// Since we need to work with libpng here, we need to allow all symbols +// to avoid compilation issues. +#define FORBIDDEN_SYMBOL_ALLOW_ALL +#include "common/scummsys.h" + +#ifdef USE_PNG +#include <png.h> +#endif + #include "graphics/decoders/png.h" #include "graphics/pixelformat.h" #include "graphics/surface.h" -#include "common/endian.h" -#include "common/memstream.h" #include "common/stream.h" -#include "common/types.h" -#include "common/util.h" -#include "common/zlib.h" - -// PNG decoder, based on the W3C specs: -// http://www.w3.org/TR/PNG/ -// Parts of the code have been adapted from LodePNG, by Lode Vandevenne: -// http://members.gamedev.net/lode/projects/LodePNG/ - -/* -LodePNG version 20101211 - -Copyright (c) 2005-2010 Lode Vandevenne - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*/ namespace Graphics { -enum PNGChunks { - // == Critical chunks ===================================================== - kChunkIHDR = MKTAG('I','H','D','R'), // Image header - kChunkIDAT = MKTAG('I','D','A','T'), // Image data - kChunkPLTE = MKTAG('P','L','T','E'), // Palette - kChunkIEND = MKTAG('I','E','N','D'), // Image trailer - // == Ancillary chunks ==================================================== - kChunktRNS = MKTAG('t','R','N','S') // Transparency - // All of the other ancillary chunks are ignored. They're added here for - // reference only. - // cHRM - Primary chromacities and white point - // gAMA - Image gamma - // iCCP - Embedded ICC profile - // sBIT - Significant bits - // sRGB - Standard RGB color space - // tEXT - Textual data - // sTXt - Compressed textual data - // iTXt - International textual data - // bKGD - Background color - // hIST - Image histogram - // pHYs - Physical pixel dimensions - // sPLT - Suggested palette - // tIME - Image last-modification time -}; - -// Refer to http://www.w3.org/TR/PNG/#9Filters -enum PNGFilters { - kFilterNone = 0, - kFilterSub = 1, - kFilterUp = 2, - kFilterAverage = 3, - kFilterPaeth = 4 -}; - -PNGDecoder::PNGDecoder() : _compressedBuffer(0), _compressedBufferSize(0), - _transparentColorSpecified(false), _outputSurface(0) { +PNGDecoder::PNGDecoder() : _outputSurface(0), _palette(0), _paletteColorCount(0) { } PNGDecoder::~PNGDecoder() { @@ -112,12 +51,41 @@ void PNGDecoder::destroy() { delete _outputSurface; _outputSurface = 0; } + delete[] _palette; + _palette = NULL; } +#ifdef USE_PNG +// libpng-error-handling: +void pngError(png_structp pngptr, png_const_charp errorMsg) { + error("%s", errorMsg); +} + +void pngWarning(png_structp pngptr, png_const_charp warningMsg) { + warning("%s", warningMsg); +} + +// libpng-I/O-helper: +void pngReadFromStream(png_structp pngPtr, png_bytep data, png_size_t length) { + void *readIOptr = png_get_io_ptr(pngPtr); + Common::SeekableReadStream *stream = (Common::SeekableReadStream *)readIOptr; + stream->read(data, length); +} +#endif + +/* + * This code is based on Broken Sword 2.5 engine + * + * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer + * + * Licensed under GNU GPL v2 + * + */ + bool PNGDecoder::loadStream(Common::SeekableReadStream &stream) { +#ifdef USE_PNG destroy(); - uint32 chunkLength = 0, chunkType = 0; _stream = &stream; // First, check the PNG signature @@ -130,374 +98,144 @@ bool PNGDecoder::loadStream(Common::SeekableReadStream &stream) { return false; } - // Start reading chunks till we reach an IEND chunk - while (chunkType != kChunkIEND) { - // The chunk length does not include the type or CRC bytes - chunkLength = _stream->readUint32BE(); - chunkType = _stream->readUint32BE(); - - switch (chunkType) { - case kChunkIHDR: - readHeaderChunk(); - break; - case kChunkIDAT: - if (_compressedBufferSize == 0) { - _compressedBufferSize += chunkLength; - _compressedBuffer = (byte *)malloc(_compressedBufferSize); - _stream->read(_compressedBuffer, chunkLength); - } else { - // Expand the buffer - uint32 prevSize = _compressedBufferSize; - _compressedBufferSize += chunkLength; - byte *tmp = new byte[prevSize]; - memcpy(tmp, _compressedBuffer, prevSize); - free(_compressedBuffer); - _compressedBuffer = (byte *)malloc(_compressedBufferSize); - memcpy(_compressedBuffer, tmp, prevSize); - delete[] tmp; - _stream->read(_compressedBuffer + prevSize, chunkLength); - } - break; - case kChunkPLTE: // only available in indexed PNGs - if (_header.colorType != kIndexed) - error("A palette chunk has been found in a non-indexed PNG file"); - if (chunkLength % 3 != 0) - error("Palette chunk not divisible by 3"); - - _paletteEntries = chunkLength / 3; - _stream->read(_palette, _paletteEntries * 3); - memset(_paletteTransparency, 0xff, sizeof(_paletteTransparency)); - break; - case kChunkIEND: - // End of stream - break; - case kChunktRNS: - readTransparencyChunk(chunkLength); - break; - default: - // Skip the chunk content - _stream->skip(chunkLength); - break; - } - - if (chunkType != kChunkIEND) - _stream->skip(4); // skip the chunk CRC checksum + // The following is based on the guide provided in: + //http://www.libpng.org/pub/png/libpng-1.2.5-manual.html#section-3 + //http://www.libpng.org/pub/png/libpng-1.4.0-manual.pdf + // along with the png-loading code used in the sword25-engine. + png_structp pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!pngPtr) { + delete _stream; + return false; + } + png_infop infoPtr = png_create_info_struct(pngPtr); + if (!infoPtr) { + png_destroy_read_struct(&pngPtr, NULL, NULL); + delete _stream; + return false; + } + png_infop endInfo = png_create_info_struct(pngPtr); + if (!endInfo) { + png_destroy_read_struct(&pngPtr, &infoPtr, NULL); + delete _stream; + return false; } - // We no longer need the file stream, thus close it here - _stream = 0; + png_set_error_fn(pngPtr, NULL, pngError, pngWarning); + // TODO: The manual says errors should be handled via setjmp - // Unpack the compressed buffer - Common::MemoryReadStream *compData = new Common::MemoryReadStream(_compressedBuffer, _compressedBufferSize, DisposeAfterUse::YES); - _imageData = Common::wrapCompressedReadStream(compData); + png_set_read_fn(pngPtr, _stream, pngReadFromStream); + png_set_crc_action(pngPtr, PNG_CRC_DEFAULT, PNG_CRC_WARN_USE); + // We already verified the PNG-header + png_set_sig_bytes(pngPtr, 8); - // Construct the final image - constructImage(); + // Read PNG header + png_read_info(pngPtr, infoPtr); - // Close the uncompressed stream, which will also delete the memory stream, - // and thus the original compressed buffer - delete _imageData; + // No handling for unknown chunks yet. + int bitDepth, colorType, width, height, interlaceType; + png_uint_32 w, h; + png_get_IHDR(pngPtr, infoPtr, &w, &h, &bitDepth, &colorType, &interlaceType, NULL, NULL); + width = w; + height = h; - return true; -} - -/** - * Paeth predictor, used by PNG filter type 4 - * The parameters are of signed 16-bit integers, but should come - * from unsigned chars. The integers are only needed to make - * the paeth calculation correct. - * - * Taken from lodePNG, with a slight patch: - * http://www.atalasoft.com/cs/blogs/stevehawley/archive/2010/02/23/libpng-you-re-doing-it-wrong.aspx - */ -byte PNGDecoder::paethPredictor(int16 a, int16 b, int16 c) { - int16 pa = ABS<int16>(b - c); - int16 pb = ABS<int16>(a - c); - int16 pc = ABS<int16>(a + b - c - c); - - if (pa <= MIN<int16>(pb, pc)) - return (byte)a; - else if (pb <= pc) - return (byte)b; - else - return (byte)c; -} + // Allocate memory for the final image data. + // To keep memory framentation low this happens before allocating memory for temporary image data. + _outputSurface = new Graphics::Surface(); -/** - * Unfilters a filtered PNG scan line. - * PNG filters are defined in: http://www.w3.org/TR/PNG/#9Filters - * Note that filters are always applied to bytes - * - * Taken from lodePNG - */ -void PNGDecoder::unfilterScanLine(byte *dest, const byte *scanLine, const byte *prevLine, uint16 byteWidth, byte filterType, uint16 length) { - uint16 i; - - switch (filterType) { - case kFilterNone: // no change - for (i = 0; i < length; i++) - dest[i] = scanLine[i]; - break; - case kFilterSub: // add the bytes to the left - for (i = 0; i < byteWidth; i++) - dest[i] = scanLine[i]; - for (i = byteWidth; i < length; i++) - dest[i] = scanLine[i] + dest[i - byteWidth]; - break; - case kFilterUp: // add the bytes of the above scanline - if (prevLine) { - for (i = 0; i < length; i++) - dest[i] = scanLine[i] + prevLine[i]; - } else { - for (i = 0; i < length; i++) - dest[i] = scanLine[i]; + // Images of all color formats except PNG_COLOR_TYPE_PALETTE + // will be transformed into ARGB images + if (colorType == PNG_COLOR_TYPE_PALETTE && !png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS)) { + int numPalette = 0; + png_colorp palette = NULL; + uint32 success = png_get_PLTE(pngPtr, infoPtr, &palette, &numPalette); + if (success != PNG_INFO_PLTE) { + png_destroy_read_struct(&pngPtr, &infoPtr, NULL); + return false; } - break; - case kFilterAverage: // average value of the left and top left - if (prevLine) { - for (i = 0; i < byteWidth; i++) - dest[i] = scanLine[i] + prevLine[i] / 2; - for (i = byteWidth; i < length; i++) - dest[i] = scanLine[i] + ((dest[i - byteWidth] + prevLine[i]) / 2); - } else { - for (i = 0; i < byteWidth; i++) - dest[i] = scanLine[i]; - for (i = byteWidth; i < length; i++) - dest[i] = scanLine[i] + dest[i - byteWidth] / 2; + _paletteColorCount = numPalette; + _palette = new byte[_paletteColorCount * 3]; + for (int i = 0; i < _paletteColorCount; i++) { + _palette[(i * 3)] = palette[i].red; + _palette[(i * 3) + 1] = palette[i].green; + _palette[(i * 3) + 2] = palette[i].blue; + } - break; - case kFilterPaeth: // Paeth filter: http://www.w3.org/TR/PNG/#9Filter-type-4-Paeth - if (prevLine) { - for(i = 0; i < byteWidth; i++) - dest[i] = (scanLine[i] + prevLine[i]); // paethPredictor(0, prevLine[i], 0) is always prevLine[i] - for(i = byteWidth; i < length; i++) - dest[i] = (scanLine[i] + paethPredictor(dest[i - byteWidth], prevLine[i], prevLine[i - byteWidth])); - } else { - for(i = 0; i < byteWidth; i++) - dest[i] = scanLine[i]; - for(i = byteWidth; i < length; i++) - dest[i] = (scanLine[i] + dest[i - byteWidth]); // paethPredictor(dest[i - byteWidth], 0, 0) is always dest[i - byteWidth] + _outputSurface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + png_set_packing(pngPtr); + } else { + _outputSurface->create(width, height, Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0)); + if (!_outputSurface->pixels) { + error("Could not allocate memory for output image."); } - break; - default: - error("Unknown line filter"); - } + if (bitDepth == 16) + png_set_strip_16(pngPtr); + if (bitDepth < 8) + png_set_expand(pngPtr); + if (png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS)) + png_set_expand(pngPtr); + if (colorType == PNG_COLOR_TYPE_GRAY || + colorType == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(pngPtr); + + // PNGs are Big-Endian: +#ifdef SCUMM_LITTLE_ENDIAN + png_set_bgr(pngPtr); + png_set_swap_alpha(pngPtr); + if (colorType != PNG_COLOR_TYPE_RGB_ALPHA) + png_set_filler(pngPtr, 0xff, PNG_FILLER_BEFORE); +#else + if (colorType != PNG_COLOR_TYPE_RGB_ALPHA) + png_set_filler(pngPtr, 0xff, PNG_FILLER_AFTER); +#endif -} + } -int PNGDecoder::getBytesPerPixel() const { - return (getNumColorChannels() * _header.bitDepth + 7) / 8; -} + // After the transformations have been registered, the image data is read again. + png_set_interlace_handling(pngPtr); + png_read_update_info(pngPtr, infoPtr); + png_get_IHDR(pngPtr, infoPtr, &w, &h, &bitDepth, &colorType, NULL, NULL, NULL); + width = w; + height = h; + + if (interlaceType == PNG_INTERLACE_NONE) { + // PNGs without interlacing can simply be read row by row. + for (int i = 0; i < height; i++) { + png_read_row(pngPtr, (png_bytep)_outputSurface->getBasePtr(0, i), NULL); + } + } else { + // PNGs with interlacing require us to allocate an auxillary + // buffer with pointers to all row starts. -void PNGDecoder::constructImage() { - assert (_header.bitDepth != 0); - - int bytesPerPixel = getBytesPerPixel(); - int pitch = bytesPerPixel * _header.width; - byte *unfilteredSurface = new byte[pitch * _header.height]; - byte *dest = unfilteredSurface; - uint16 scanLineWidth = (_header.width * getNumColorChannels() * _header.bitDepth + 7) / 8; - byte *scanLine = new byte[scanLineWidth]; - byte *prevLine = 0; - - switch(_header.interlaceType) { - case kNonInterlaced: - for (uint16 y = 0; y < _header.height; y++) { - byte filterType = _imageData->readByte(); - _imageData->read(scanLine, scanLineWidth); - unfilterScanLine(dest, scanLine, prevLine, bytesPerPixel, filterType, scanLineWidth); - prevLine = dest; - dest += pitch; + // Allocate row pointer buffer + png_bytep *rowPtr = new png_bytep[height]; + if (!rowPtr) { + error("Could not allocate memory for row pointers."); } - break; - case kInterlaced: - // Theoretically, this shouldn't be needed, as interlacing is only - // useful for web images. Interlaced PNG images require more complex - // handling, so unless having support for such images is needed, there - // is no reason to add support for them. - error("TODO: Support for interlaced PNG images"); - break; - } - delete[] scanLine; + // Initialize row pointers + for (int i = 0; i < height; i++) + rowPtr[i] = (png_bytep)_outputSurface->getBasePtr(0, i); - constructOutput(unfilteredSurface); - delete[] unfilteredSurface; -} + // Read image data + png_read_image(pngPtr, rowPtr); -Graphics::PixelFormat PNGDecoder::findPixelFormat() const { - // Try to find the best pixel format based on what we have here - // Which is basically 8bpp for paletted non-transparent - // and 32bpp for everything else - - switch (_header.colorType) { - case kIndexed: - if (!_transparentColorSpecified) - return Graphics::PixelFormat::createFormatCLUT8(); - // fall through - case kGrayScale: - case kTrueColor: - case kGrayScaleWithAlpha: - case kTrueColorWithAlpha: - // We'll go with standard RGBA 32-bit - return Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); + // Free row pointer buffer + delete[] rowPtr; } - error("Unknown PNG color type"); - return Graphics::PixelFormat(); -} + // Read additional data at the end. + png_read_end(pngPtr, NULL); -void PNGDecoder::constructOutput(const byte *surface) { - _outputSurface = new Graphics::Surface(); - _outputSurface->create(_header.width, _header.height, findPixelFormat()); - - const byte *src = surface; - byte a = 0xFF; - int bytesPerPixel = getBytesPerPixel(); - - if (_header.colorType != kIndexed) { - if (_header.colorType == kTrueColor || - _header.colorType == kTrueColorWithAlpha) { - if (bytesPerPixel != 3 && bytesPerPixel != 4) - error("Unsupported truecolor PNG format"); - } else if (_header.colorType == kGrayScale || - _header.colorType == kGrayScaleWithAlpha) { - if (bytesPerPixel != 1 && bytesPerPixel != 2) - error("Unsupported grayscale PNG format"); - } - - for (uint16 i = 0; i < _outputSurface->h; i++) { - for (uint16 j = 0; j < _outputSurface->w; j++) { - uint32 result = 0; - - switch (bytesPerPixel) { - case 1: // Grayscale - if (_transparentColorSpecified) - a = (src[0] == _transparentColor[0]) ? 0 : 0xFF; - result = _outputSurface->format.ARGBToColor(a, src[0], src[0], src[0]); - break; - case 2: // Grayscale + alpha - result = _outputSurface->format.ARGBToColor(src[1], src[0], src[0], src[0]); - break; - case 3: // RGB - if (_transparentColorSpecified) { - bool isTransparentColor = (src[0] == _transparentColor[0] && - src[1] == _transparentColor[1] && - src[2] == _transparentColor[2]); - a = isTransparentColor ? 0 : 0xFF; - } - - result = _outputSurface->format.ARGBToColor(a, src[0], src[1], src[2]); - break; - case 4: // RGBA - result = _outputSurface->format.ARGBToColor(src[3], src[0], src[1], src[2]); - break; - } - - *((uint32 *)_outputSurface->getBasePtr(j, i)) = result; - src += bytesPerPixel; - } - } - } else { - uint32 mask = (0xff >> (8 - _header.bitDepth)) << (8 - _header.bitDepth); - - // Convert the indexed surface to the target pixel format - for (uint16 i = 0; i < _outputSurface->h; i++) { - int data = 0; - int bitCount = 8; - const byte *src1 = src; - - for (uint16 j = 0; j < _outputSurface->w; j++) { - if (bitCount == 8) { - data = *src; - src++; - } - - byte index = (data & mask) >> (8 - _header.bitDepth); - data = (data << _header.bitDepth) & 0xff; - bitCount -= _header.bitDepth; - - if (bitCount == 0) - bitCount = 8; - - if (_transparentColorSpecified) { - byte r = _palette[index * 3 + 0]; - byte g = _palette[index * 3 + 1]; - byte b = _palette[index * 3 + 2]; - a = _paletteTransparency[index]; - *((uint32 *)_outputSurface->getBasePtr(j, i)) = _outputSurface->format.ARGBToColor(a, r, g, b); - } else { - *((byte *)_outputSurface->getBasePtr(j, i)) = index; - } - } - - src = src1 + _outputSurface->w; - } - } -} + // Destroy libpng structures + png_destroy_read_struct(&pngPtr, &infoPtr, NULL); -void PNGDecoder::readHeaderChunk() { - _header.width = _stream->readUint32BE(); - _header.height = _stream->readUint32BE(); - _header.bitDepth = _stream->readByte(); - if (_header.bitDepth > 8) - error("Only PNGs with a bit depth of 1-8 bits are supported (i.e. PNG24)"); - _header.colorType = (PNGColorType)_stream->readByte(); - _header.compressionMethod = _stream->readByte(); - // Compression methods: http://www.w3.org/TR/PNG/#10Compression - // Only compression method 0 (deflate) is documented and supported - if (_header.compressionMethod != 0) - error("Unknown PNG compression method: %d", _header.compressionMethod); - _header.filterMethod = _stream->readByte(); - // Filter methods: http://www.w3.org/TR/PNG/#9Filters - // Only filter method 0 is documented and supported - if (_header.filterMethod != 0) - error("Unknown PNG filter method: %d", _header.filterMethod); - _header.interlaceType = (PNGInterlaceType)_stream->readByte(); -} - -byte PNGDecoder::getNumColorChannels() const { - switch (_header.colorType) { - case kGrayScale: - return 1; // Gray - case kTrueColor: - return 3; // RGB - case kIndexed: - return 1; // Indexed - case kGrayScaleWithAlpha: - return 2; // Gray + Alpha - case kTrueColorWithAlpha: - return 4; // RGBA - default: - error("Unknown color type"); - } -} + // We no longer need the file stream, thus close it here + _stream = 0; -void PNGDecoder::readTransparencyChunk(uint32 chunkLength) { - _transparentColorSpecified = true; - - switch(_header.colorType) { - case kGrayScale: - _transparentColor[0] = _stream->readUint16BE(); - _transparentColor[1] = _transparentColor[0]; - _transparentColor[2] = _transparentColor[0]; - break; - case kTrueColor: - _transparentColor[0] = _stream->readUint16BE(); - _transparentColor[1] = _stream->readUint16BE(); - _transparentColor[2] = _stream->readUint16BE(); - break; - case kIndexed: - _stream->read(_paletteTransparency, chunkLength); - - // A transparency chunk may have less entries - // than the palette entries. The remaining ones - // are unmodified (set to 255). Check here: - // http://www.w3.org/TR/PNG/#11tRNS - break; - default: - error("Transparency chunk found in a PNG that has a separate transparency channel"); - } + return true; +#else + return false; +#endif } } // End of Graphics namespace diff --git a/graphics/decoders/png.h b/graphics/decoders/png.h index 1da0bea1ab..e52ddabd7d 100644 --- a/graphics/decoders/png.h +++ b/graphics/decoders/png.h @@ -24,33 +24,12 @@ * PNG decoder used in engines: * - sword25 * Dependencies: - * - zlib + * - libpng */ #ifndef GRAPHICS_PNG_H #define GRAPHICS_PNG_H -// PNG decoder, based on the W3C specs: -// http://www.w3.org/TR/PNG/ -// Parts of the code have been adapted from LodePNG, by Lode Vandevenne: -// http://members.gamedev.net/lode/projects/LodePNG/ - -// All the numbers are BE: http://www.w3.org/TR/PNG/#7Integers-and-byte-order - -// Note: At the moment, this decoder only supports non-interlaced images, and -// does not support truecolor/grayscale images with 16bit depth. -// -// Theoretically, interlaced images shouldn't be needed for games, as -// interlacing is only useful for images in websites. -// -// PNG images with 16bit depth (i.e. 48bit images) are quite rare, and can -// theoretically contain more than 16.7 millions of colors (the so-called "deep -// color" representation). In essence, each of the R, G, B and A components in -// them is specified with 2 bytes, instead of 1. However, the PNG specification -// always refers to color components with 1 byte each, so this part of the spec -// is a bit unclear. For now, these won't be supported, until a suitable sample -// is found. - #include "common/scummsys.h" #include "common/textconsole.h" #include "graphics/decoders/image_decoder.h" @@ -73,61 +52,13 @@ public: void destroy(); const Graphics::Surface *getSurface() const { return _outputSurface; } const byte *getPalette() const { return _palette; } - + uint16 getPaletteColorCount() const { return _paletteColorCount; } private: - enum PNGColorType { - kGrayScale = 0, // bit depths: 1, 2, 4, 8, 16 - kTrueColor = 2, // bit depths: 8, 16 - kIndexed = 3, // bit depths: 1, 2, 4, 8 - kGrayScaleWithAlpha = 4, // bit depths: 8, 16 - kTrueColorWithAlpha = 6 // bit depths: 8, 16 - }; - - enum PNGInterlaceType { - kNonInterlaced = 0, - kInterlaced = 1 - }; - - struct PNGHeader { - uint32 width; - uint32 height; - byte bitDepth; - PNGColorType colorType; - byte compressionMethod; - byte filterMethod; - PNGInterlaceType interlaceType; - }; - - void readHeaderChunk(); - byte getNumColorChannels() const; - - void readPaletteChunk(); - void readTransparencyChunk(uint32 chunkLength); - - void constructImage(); - void unfilterScanLine(byte *dest, const byte *scanLine, const byte *prevLine, uint16 byteWidth, byte filterType, uint16 length); - byte paethPredictor(int16 a, int16 b, int16 c); - - // The original file stream Common::SeekableReadStream *_stream; - // The unzipped image data stream - Common::SeekableReadStream *_imageData; - - PNGHeader _header; - - byte _palette[256 * 3]; // RGB - byte _paletteTransparency[256]; - uint16 _paletteEntries; - uint16 _transparentColor[3]; - bool _transparentColorSpecified; - - byte *_compressedBuffer; - uint32 _compressedBufferSize; + byte *_palette; + uint16 _paletteColorCount; Graphics::Surface *_outputSurface; - Graphics::PixelFormat findPixelFormat() const; - int getBytesPerPixel() const; - void constructOutput(const byte *surface); }; } // End of namespace Graphics diff --git a/graphics/decoders/tga.cpp b/graphics/decoders/tga.cpp new file mode 100644 index 0000000000..c3b9d84055 --- /dev/null +++ b/graphics/decoders/tga.cpp @@ -0,0 +1,427 @@ +/* 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. + */ + +/* Based on code from xoreos https://github.com/DrMcCoy/xoreos/ + * relicensed under GPLv2+ with permission from DrMcCoy and clone2727 + */ + +#include "common/util.h" +#include "common/stream.h" +#include "common/textconsole.h" +#include "common/error.h" + +#include "graphics/decoders/tga.h" + +namespace Graphics { + +TGADecoder::TGADecoder() { + _colorMapSize = 0; + _colorMapOrigin = 0; + _colorMapLength = 0; + _colorMapEntryLength = 0; + _colorMap = NULL; +} + +TGADecoder::~TGADecoder() { + destroy(); +} + +void TGADecoder::destroy() { + _surface.free(); + delete[] _colorMap; +} + +bool TGADecoder::loadStream(Common::SeekableReadStream &tga) { + byte imageType, pixelDepth; + bool success; + success = readHeader(tga, imageType, pixelDepth); + if (success) { + switch (imageType) { + case TYPE_BW: + case TYPE_TRUECOLOR: + success = readData(tga, imageType, pixelDepth); + break; + case TYPE_RLE_BW: + case TYPE_RLE_TRUECOLOR: + case TYPE_RLE_CMAP: + success = readDataRLE(tga, imageType, pixelDepth); + break; + case TYPE_CMAP: + success = readDataColorMapped(tga, imageType, pixelDepth); + break; + default: + success = false; + break; + } + } + if (tga.err() || !success) { + warning("Failed reading TGA-file"); + return false; + } + return success; +} + +bool TGADecoder::readHeader(Common::SeekableReadStream &tga, byte &imageType, byte &pixelDepth) { + if (!tga.seek(0)) { + warning("Failed reading TGA-file"); + return false; + } + + // TGAs have an optional "id" string in the header + uint32 idLength = tga.readByte(); + + // Number of colors in the color map / palette + int hasColorMap = tga.readByte(); + + // Image type. See header for numeric constants + imageType = tga.readByte(); + + switch (imageType) { + case TYPE_CMAP: + case TYPE_TRUECOLOR: + case TYPE_BW: + case TYPE_RLE_CMAP: + case TYPE_RLE_TRUECOLOR: + case TYPE_RLE_BW: + break; + default: + warning("Unsupported image type: %d", imageType); + return false; + } + + // Color map specifications + if (hasColorMap == 0) { + tga.skip(5); + } else { + _colorMapOrigin = tga.readUint16LE(); + _colorMapLength = tga.readUint16LE(); + _colorMapEntryLength = tga.readByte(); + } + // Origin-defintions + tga.skip(2 + 2); + + // Image dimensions + _surface.w = tga.readUint16LE(); + _surface.h = tga.readUint16LE(); + + // Bits per pixel + pixelDepth = tga.readByte(); + _surface.format.bytesPerPixel = pixelDepth / 8; + + // Image descriptor + byte imgDesc = tga.readByte(); + int attributeBits = imgDesc & 0x0F; + assert((imgDesc & 0x10) == 0); + _originTop = (imgDesc & 0x20); + + // Interleaving is not handled at this point + //int interleave = (imgDesc & 0xC); + if (imageType == TYPE_CMAP || imageType == TYPE_RLE_CMAP) { + if (pixelDepth == 8) { + _format = PixelFormat::createFormatCLUT8(); + } else { + warning("Unsupported index-depth: %d", pixelDepth); + return false; + } + } else if (imageType == TYPE_TRUECOLOR || imageType == TYPE_RLE_TRUECOLOR) { + if (pixelDepth == 24) { + _format = PixelFormat(3, 8, 8, 8, 0, 16, 8, 0, 0); + } else if (pixelDepth == 32) { + // HACK: According to the spec, attributeBits should determine the amount + // of alpha-bits, however, as the game files that use this decoder seems + // to ignore that fact, we force the amount to 8 for 32bpp files for now. + _format = PixelFormat(4, 8, 8, 8, /* attributeBits */ 8, 16, 8, 0, 24); + } else if (pixelDepth == 16 && imageType == TYPE_TRUECOLOR) { + // 16bpp TGA is ARGB1555 + _format = PixelFormat(2, 5, 5, 5, attributeBits, 10, 5, 0, 15); + } else { + warning("Unsupported pixel depth: %d, %d", imageType, pixelDepth); + return false; + } + } else if (imageType == TYPE_BW || TYPE_RLE_BW) { + if (pixelDepth == 8) { + _format = PixelFormat(4, 8, 8, 8, 0, 16, 8, 0, 0); + } else { + warning("Unsupported pixel depth: %d, %d", imageType, pixelDepth); + return false; + } + + } else { + warning("Unsupported image type: %d", imageType); + return false; + } + + // Skip the id string + tga.skip(idLength); + + if (hasColorMap) { + return readColorMap(tga, imageType, pixelDepth); + } + return true; +} + +bool TGADecoder::readColorMap(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) { + _colorMap = new byte[3 * _colorMapLength]; + for (int i = 0; i < _colorMapLength * 3; i += 3) { + byte r, g, b; + if (_colorMapEntryLength == 32) { + byte a; + PixelFormat format(4, 8, 8, 8, 0, 16, 8, 0, 24); + uint32 color = tga.readUint32LE(); + format.colorToARGB(color, a, r, g, b); + } else if (_colorMapEntryLength == 24) { + r = tga.readByte(); + g = tga.readByte(); + b = tga.readByte(); + } else if (_colorMapEntryLength == 16) { + byte a; + PixelFormat format(2, 5, 5, 5, 0, 10, 5, 0, 15); + uint16 color = tga.readUint16LE(); + format.colorToARGB(color, a, r, g, b); + } else { + warning("Unsupported image type: %d", imageType); + r = g = b = 0; + } +#ifdef SCUMM_LITTLE_ENDIAN + _colorMap[i] = r; + _colorMap[i + 1] = g; + _colorMap[i + 2] = b; +#else + _colorMap[i] = b; + _colorMap[i + 1] = g; + _colorMap[i + 2] = r; +#endif + } + return true; +} + +// Additional information found from http://paulbourke.net/dataformats/tga/ +// With some details from the link referenced in the header. +bool TGADecoder::readData(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) { + // TrueColor + if (imageType == TYPE_TRUECOLOR) { + _surface.create(_surface.w, _surface.h, _format); + + if (pixelDepth == 16) { + for (int i = 0; i < _surface.h; i++) { + uint16 *dst; + if (!_originTop) { + dst = (uint16 *)_surface.getBasePtr(0, _surface.h - i - 1); + } else { + dst = (uint16 *)_surface.getBasePtr(0, i); + } + for (int j = 0; j < _surface.w; j++) { + *dst++ = tga.readUint16LE(); + } + } + } else if (pixelDepth == 32) { + for (int i = 0; i < _surface.h; i++) { + uint32 *dst; + if (!_originTop) { + dst = (uint32 *)_surface.getBasePtr(0, _surface.h - i - 1); + } else { + dst = (uint32 *)_surface.getBasePtr(0, i); + } + for (int j = 0; j < _surface.w; j++) { + *dst++ = tga.readUint32LE(); + } + } + } else if (pixelDepth == 24) { + for (int i = 0; i < _surface.h; i++) { + byte *dst; + if (!_originTop) { + dst = (byte *)_surface.getBasePtr(0, _surface.h - i - 1); + } else { + dst = (byte *)_surface.getBasePtr(0, i); + } + for (int j = 0; j < _surface.w; j++) { + byte r = tga.readByte(); + byte g = tga.readByte(); + byte b = tga.readByte(); +#ifdef SCUMM_LITTLE_ENDIAN + *dst++ = r; + *dst++ = g; + *dst++ = b; +#else + *dst++ = b; + *dst++ = g; + *dst++ = r; +#endif + } + } + } + // Black/White + } else if (imageType == TYPE_BW) { + _surface.create(_surface.w, _surface.h, _format); + + byte *data = (byte *)_surface.pixels; + uint32 count = _surface.w * _surface.h; + + while (count-- > 0) { + byte g = tga.readByte(); + *data++ = g; + *data++ = g; + *data++ = g; + *data++ = g; + } + } + return true; +} + +bool TGADecoder::readDataColorMapped(Common::SeekableReadStream &tga, byte imageType, byte indexDepth) { + // Color-mapped + if (imageType == TYPE_CMAP) { + _surface.create(_surface.w, _surface.h, _format); + if (indexDepth == 8) { + for (int i = 0; i < _surface.h; i++) { + byte *dst; + if (!_originTop) { + dst = (byte *)_surface.getBasePtr(0, _surface.h - i - 1); + } else { + dst = (byte *)_surface.getBasePtr(0, i); + } + for (int j = 0; j < _surface.w; j++) { + byte index = tga.readByte(); + *dst++ = index; + } + } + } else if (indexDepth == 16) { + warning("16 bit indexes not supported"); + return false; + } + } else { + return false; + } + return true; +} + +bool TGADecoder::readDataRLE(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth) { + // RLE-TrueColor / RLE-Black/White + if (imageType == TYPE_RLE_TRUECOLOR || imageType == TYPE_RLE_BW || imageType == TYPE_RLE_CMAP) { + _surface.create(_surface.w, _surface.h, _format); + uint32 count = _surface.w * _surface.h; + byte *data = (byte *)_surface.pixels; + + while (count > 0) { + uint32 header = tga.readByte(); + byte type = (header & 0x80) >> 7; + uint32 rleCount = (header & 0x7F) + 1; + + // RLE-packet + if (type == 1) { + if (pixelDepth == 32 && imageType == TYPE_RLE_TRUECOLOR) { + uint32 color = tga.readUint32LE(); + while (rleCount-- > 0) { + *((uint32 *)data) = color; + data += 4; + count--; + } + } else if (pixelDepth == 24 && imageType == TYPE_RLE_TRUECOLOR) { + byte r = tga.readByte(); + byte g = tga.readByte(); + byte b = tga.readByte(); + while (rleCount-- > 0) { +#ifdef SCUMM_LITTLE_ENDIAN + *data++ = r; + *data++ = g; + *data++ = b; +#else + *data++ = b; + *data++ = g; + *data++ = r; +#endif + count--; + } + } else if (pixelDepth == 8 && imageType == TYPE_RLE_BW) { + byte color = tga.readByte(); + while (rleCount-- > 0) { + *data++ = color; + *data++ = color; + *data++ = color; + *data++ = color; + count--; + } + } else if (pixelDepth == 8 && imageType == TYPE_RLE_CMAP) { + byte index = tga.readByte(); + while (rleCount-- > 0) { + *data++ = index; + count--; + } + } else { + warning("Unhandled pixel-depth for image-type 10"); + return false; + } + // Raw-packet + } else if (type == 0) { + if (pixelDepth == 32 && imageType == TYPE_RLE_TRUECOLOR) { + while (rleCount-- > 0) { + uint32 color = tga.readUint32LE(); + *((uint32 *)data) = color; + data += 4; + count--; + } + } else if (pixelDepth == 24 && imageType == TYPE_RLE_TRUECOLOR) { + while (rleCount-- > 0) { + byte r = tga.readByte(); + byte g = tga.readByte(); + byte b = tga.readByte(); +#ifdef SCUMM_LITTLE_ENDIAN + *data++ = r; + *data++ = g; + *data++ = b; +#else + *data++ = b; + *data++ = g; + *data++ = r; +#endif + count--; + } + } else if (pixelDepth == 8 && imageType == TYPE_RLE_BW) { + while (rleCount-- > 0) { + byte color = tga.readByte(); + *data++ = color; + *data++ = color; + *data++ = color; + *data++ = color; + count--; + } + } else if (pixelDepth == 8 && imageType == TYPE_RLE_CMAP) { + while (rleCount-- > 0) { + byte index = tga.readByte(); + *data++ = index; + count--; + } + } else { + warning("Unhandled pixel-depth for image-type 10"); + return false; + } + } else { + warning("Unknown header for RLE-packet %d", type); + return false; + } + } + } else { + return false; + } + return true; +} + +} // End of namespace Graphics diff --git a/graphics/decoders/tga.h b/graphics/decoders/tga.h new file mode 100644 index 0000000000..dfdc5a4da9 --- /dev/null +++ b/graphics/decoders/tga.h @@ -0,0 +1,98 @@ +/* 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. + */ + +/* Based on code from eos https://github.com/DrMcCoy/xoreos/ + * relicensed under GPLv2+ with permission from DrMcCoy and clone2727 + */ + +/* + * TGA decoder used in engines: + * - none + */ + +#ifndef GRAPHICS_DECODERS_TGA_H +#define GRAPHICS_DECODERS_TGA_H + +#include "graphics/surface.h" +#include "graphics/decoders/image_decoder.h" + +namespace Common { +class SeekableReadStream; +} + +namespace Graphics { + +/** TarGa image-decoder + * The following variations of TGA are supported: + * - Type 1 - Color-mapped images in 16/24/32 bpp with 8 bit indexes + * - Type 2 - 16/24/32 bpp Top AND Bottom origined. + * - Type 3 - Black/White images, 8bpp. + * - Type 9 - RLE-encoded color-mapped images. (8 bit indexes only) + * - Type 10 - RLE-encoded TrueColor, 24/32bpp. + * - Type 11 - RLE-encoded Black/White, 8bpp. + * + * No images are returned with a palette, instead they are converted + * to 16 bpp for Type 1, or 32 bpp for Black/White-images. + */ +class TGADecoder : public ImageDecoder { +public: + TGADecoder(); + virtual ~TGADecoder(); + virtual void destroy(); + virtual const Surface *getSurface() const { return &_surface; } + virtual const byte *getPalette() const { return _colorMap; } + virtual uint16 getPaletteColorCount() const { return _colorMapLength; } + virtual bool loadStream(Common::SeekableReadStream &stream); +private: + // Format-spec from: + //http://www.ludorg.net/amnesia/TGA_File_Format_Spec.html + enum { + TYPE_CMAP = 1, + TYPE_TRUECOLOR = 2, + TYPE_BW = 3, + TYPE_RLE_CMAP = 9, + TYPE_RLE_TRUECOLOR = 10, + TYPE_RLE_BW = 11 + }; + + // Color-map: + bool _colorMapSize; + byte *_colorMap; + int16 _colorMapOrigin; + int16 _colorMapLength; + byte _colorMapEntryLength; + + // Origin may be at the top, or bottom + bool _originTop; + + PixelFormat _format; + Surface _surface; + // Loading helpers + bool readHeader(Common::SeekableReadStream &tga, byte &imageType, byte &pixelDepth); + bool readData(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth); + bool readDataColorMapped(Common::SeekableReadStream &tga, byte imageType, byte indexDepth); + bool readDataRLE(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth); + bool readColorMap(Common::SeekableReadStream &tga, byte imageType, byte pixelDepth); +}; + +} // End of namespace Graphics + +#endif // GRAPHICS_DECODERS_TGA_H diff --git a/graphics/fontman.cpp b/graphics/fontman.cpp index 8d967d595f..99dd3d664f 100644 --- a/graphics/fontman.cpp +++ b/graphics/fontman.cpp @@ -47,6 +47,13 @@ FontManager::FontManager() { } FontManager::~FontManager() { + for (uint i = 0; i < _ownedFonts.size(); ++i) { + const Font *font = _ownedFonts[i]; + if (font == g_sysfont || font == g_sysfont_big || font == g_consolefont) + continue; + delete font; + } + delete g_sysfont; g_sysfont = 0; delete g_sysfont_big; @@ -90,6 +97,8 @@ bool FontManager::assignFontToName(const Common::String &name, const Font *font) Common::String lowercaseName = name; lowercaseName.toLowercase(); _fontMap[lowercaseName] = font; + if (Common::find(_ownedFonts.begin(), _ownedFonts.end(), font) == _ownedFonts.end()) + _ownedFonts.push_back(font); return true; } @@ -116,8 +125,35 @@ bool FontManager::setFont(FontUsage usage, const BdfFont *font) { void FontManager::removeFontName(const Common::String &name) { Common::String lowercaseName = name; lowercaseName.toLowercase(); + if (!_fontMap.contains(lowercaseName)) + return; + + const Font *font = _fontMap[lowercaseName]; _fontMap.erase(lowercaseName); + // Check if we still have a copy of this font in the map. + bool stillHasFont = false; + for (Common::HashMap<Common::String, const Font *>::iterator i = _fontMap.begin(); i != _fontMap.end(); ++i) { + if (i->_value != font) + continue; + stillHasFont = true; + break; + } + + if (!stillHasFont) { + // We don't have a copy of the font, so remove it from our list and delete it. + stillHasFont = true; + for (uint i = 0; i < _ownedFonts.size(); ++i) { + if (_ownedFonts[i] != font) + continue; + stillHasFont = false; + _ownedFonts.remove_at(i); + break; + } + assert(!stillHasFont); + delete font; + } + // In case the current localized font is removed, we fall back to the // default font again. if (_localizedFontName == lowercaseName) diff --git a/graphics/fontman.h b/graphics/fontman.h index 42f7d856fa..b06ddea860 100644 --- a/graphics/fontman.h +++ b/graphics/fontman.h @@ -60,7 +60,9 @@ public: const Font *getFontByName(const Common::String &name) const; /** - * Associates a font object with an 'name' + * Associates a font object with an 'name'. + * The FontManager takes ownership of the provided font object + * and will delete it when necesssary. * * @param name the name of the font * @param font the font object @@ -111,6 +113,7 @@ private: ~FontManager(); Common::HashMap<Common::String, const Font *> _fontMap; + Common::Array<const Font *> _ownedFonts; Common::String _localizedFontName; }; diff --git a/graphics/fonts/bdf.h b/graphics/fonts/bdf.h index 5b615cc043..b0166a2095 100644 --- a/graphics/fonts/bdf.h +++ b/graphics/fonts/bdf.h @@ -77,7 +77,7 @@ private: #define DEFINE_FONT(n) \ const BdfFont *n = 0; \ void create_##n() { \ - n = new BdfFont(desc, DisposeAfterUse::YES); \ + n = new BdfFont(desc, DisposeAfterUse::NO); \ } #define FORWARD_DECLARE_FONT(n) \ diff --git a/graphics/fonts/consolefont.cpp b/graphics/fonts/consolefont.cpp index 8244d75fc2..748aa08a5c 100644 --- a/graphics/fonts/consolefont.cpp +++ b/graphics/fonts/consolefont.cpp @@ -1,7 +1,7 @@ // Generated by convbdf on Fri Jan 6 14:32:21 2012 #include "graphics/fonts/bdf.h" -// Font information: +// Font information: // Name: -Misc-Fixed-Medium-R-Normal--8-80-75-75-C-50-ISO8859-1 // Size: 5x8 // Box: 5 8 0 -1 diff --git a/graphics/fonts/newfont.cpp b/graphics/fonts/newfont.cpp index 10af1efb0c..4922e24676 100644 --- a/graphics/fonts/newfont.cpp +++ b/graphics/fonts/newfont.cpp @@ -1,7 +1,7 @@ // Generated by convbdf on Fri Jan 6 14:33:07 2012 #include "graphics/fonts/bdf.h" -// Font information: +// Font information: // Name: -Schumacher-Clean-Medium-R-Normal--12-120-75-75-C-60-ISO8859-1 // Size: 6x12 // Box: 6 12 0 -3 diff --git a/graphics/fonts/newfont_big.cpp b/graphics/fonts/newfont_big.cpp index 0e61068ade..550d6dbfa9 100644 --- a/graphics/fonts/newfont_big.cpp +++ b/graphics/fonts/newfont_big.cpp @@ -1,7 +1,7 @@ // Generated by convbdf on Fri Jan 6 14:33:14 2012 #include "graphics/fonts/bdf.h" -// Font information: +// Font information: // Name: -Adobe-Helvetica-Bold-R-Normal--12-120-75-75-P-70-ISO8859-1 // Size: 13x14 // Box: 13 15 -1 -3 diff --git a/graphics/fonts/ttf.cpp b/graphics/fonts/ttf.cpp index 7505f7913e..2b1dca1eae 100644 --- a/graphics/fonts/ttf.cpp +++ b/graphics/fonts/ttf.cpp @@ -43,10 +43,6 @@ namespace Graphics { namespace { -inline int ftFloor26_6(FT_Pos x) { - return x / 64; -} - inline int ftCeil26_6(FT_Pos x) { return (x + 63) / 64; } @@ -70,6 +66,10 @@ private: bool _initialized; }; +void shutdownTTF() { + TTFLibrary::destroy(); +} + #define g_ttf ::Graphics::TTFLibrary::instance() TTFLibrary::TTFLibrary() : _library(), _initialized(false) { @@ -101,7 +101,7 @@ public: TTFFont(); virtual ~TTFFont(); - bool load(Common::SeekableReadStream &stream, int size, bool monochrome, const uint32 *mapping); + bool load(Common::SeekableReadStream &stream, int size, uint dpi, bool monochrome, const uint32 *mapping); virtual int getFontHeight() const; @@ -157,7 +157,7 @@ TTFFont::~TTFFont() { } } -bool TTFFont::load(Common::SeekableReadStream &stream, int size, bool monochrome, const uint32 *mapping) { +bool TTFFont::load(Common::SeekableReadStream &stream, int size, uint dpi, bool monochrome, const uint32 *mapping) { if (!g_ttf.isInitialized()) return false; @@ -195,7 +195,7 @@ bool TTFFont::load(Common::SeekableReadStream &stream, int size, bool monochrome // Check whether we have kerning support _hasKerning = (FT_HAS_KERNING(_face) != 0); - if (FT_Set_Char_Size(_face, 0, size * 64, 0, 0)) { + if (FT_Set_Char_Size(_face, 0, size * 64, dpi, dpi)) { delete[] _ttfFile; _ttfFile = 0; @@ -462,10 +462,10 @@ bool TTFFont::cacheGlyph(Glyph &glyph, FT_UInt &slot, uint chr) { return true; } -Font *loadTTFFont(Common::SeekableReadStream &stream, int size, bool monochrome, const uint32 *mapping) { +Font *loadTTFFont(Common::SeekableReadStream &stream, int size, uint dpi, bool monochrome, const uint32 *mapping) { TTFFont *font = new TTFFont(); - if (!font->load(stream, size, monochrome, mapping)) { + if (!font->load(stream, size, dpi, monochrome, mapping)) { delete font; return 0; } diff --git a/graphics/fonts/ttf.h b/graphics/fonts/ttf.h index 7222d6e112..e1464b1f45 100644 --- a/graphics/fonts/ttf.h +++ b/graphics/fonts/ttf.h @@ -32,7 +32,9 @@ namespace Graphics { class Font; -Font *loadTTFFont(Common::SeekableReadStream &stream, int size, bool monochrome = false, const uint32 *mapping = 0); +Font *loadTTFFont(Common::SeekableReadStream &stream, int size, uint dpi = 0, bool monochrome = false, const uint32 *mapping = 0); + +void shutdownTTF(); } // End of namespace Graphics diff --git a/graphics/iff.cpp b/graphics/iff.cpp index 7434a6bebc..4011126bd3 100644 --- a/graphics/iff.cpp +++ b/graphics/iff.cpp @@ -68,7 +68,7 @@ void ILBMDecoder::loadBitmap(uint32 mode, byte *buffer, Common::ReadStream *stre Graphics::PackBitsReadStream packStream(*stream); // setup a buffer to hold enough data to build a line in the output - uint32 scanlineWidth = ((_header.width + 15)/16) << 1; + uint32 scanlineWidth = ((_header.width + 15) / 16) << 1; byte *scanline = new byte[scanlineWidth * _header.depth]; for (uint i = 0; i < _header.height; ++i) { @@ -82,7 +82,7 @@ void ILBMDecoder::loadBitmap(uint32 mode, byte *buffer, Common::ReadStream *stre out += outPitch; } - delete []scanline; + delete[] scanline; break; } @@ -121,15 +121,12 @@ void ILBMDecoder::planarToChunky(byte *out, uint32 outPitch, byte *in, uint32 in // then output the pixel according to the requested packing if (!packPlanes) { out[x] = pix; - } else - if (nPlanes == 1) { - out[x/8] |= (pix << (x & 7)); - } else - if (nPlanes == 2) { - out[x/4] |= (pix << ((x & 3) << 1)); - } else - if (nPlanes == 4) { - out[x/2] |= (pix << ((x & 1) << 2)); + } else if (nPlanes == 1) { + out[x / 8] |= (pix << (x & 7)); + } else if (nPlanes == 2) { + out[x / 4] |= (pix << ((x & 3) << 1)); + } else if (nPlanes == 4) { + out[x / 2] |= (pix << ((x & 1) << 2)); } } @@ -187,7 +184,7 @@ struct PBMLoader { _surface = &surface; _colors = colors; Common::IFFParser parser(&input); - Common::Functor1Mem< Common::IFFChunk&, bool, PBMLoader > c(this, &PBMLoader::callback); + Common::Functor1Mem<Common::IFFChunk &, bool, PBMLoader> c(this, &PBMLoader::callback); parser.parse(c); } @@ -251,7 +248,7 @@ uint32 PackBitsReadStream::read(void *dataPtr, uint32 dataSize) { for (uint32 j = 0; j < lenW; j++) { *out++ = _input->readByte(); } - for ( ; lenR > lenW; lenR--) { + for (; lenR > lenW; lenR--) { _input->readByte(); } } else { // len > 128 diff --git a/graphics/module.mk b/graphics/module.mk index 281f904b38..f560d9dc97 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -25,8 +25,10 @@ MODULE_OBJS := \ yuv_to_rgb.o \ decoders/bmp.o \ decoders/jpeg.o \ + decoders/pcx.o \ decoders/pict.o \ - decoders/png.o + decoders/png.o \ + decoders/tga.o ifdef USE_SCALERS MODULE_OBJS += \ diff --git a/graphics/pixelformat.h b/graphics/pixelformat.h index e0cf6ce401..ca4ef11c17 100644 --- a/graphics/pixelformat.h +++ b/graphics/pixelformat.h @@ -97,7 +97,7 @@ struct PixelFormat { } inline void colorToARGB(uint32 color, uint8 &a, uint8 &r, uint8 &g, uint8 &b) const { - a = ((color >> aShift) << aLoss) & 0xFF; + a = (aBits() == 0) ? 0xFF : (((color >> aShift) << aLoss) & 0xFF); r = ((color >> rShift) << rLoss) & 0xFF; g = ((color >> gShift) << gLoss) & 0xFF; b = ((color >> bShift) << bLoss) & 0xFF; diff --git a/graphics/primitives.cpp b/graphics/primitives.cpp index 9834af65ba..b88db39f36 100644 --- a/graphics/primitives.cpp +++ b/graphics/primitives.cpp @@ -61,59 +61,21 @@ void drawLine(int x0, int y0, int x1, int y1, int color, void (*plotProc)(int, i } } +void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, int color, void (*plotProc)(int, int, int, void *), void *data) { + assert(penX > 0 && penY > 0); -// FIXME: This is a limited version of thick line drawing -// it draws striped lines at some angles. Better algorithm could -// be found here: -// -// http://homepages.enterprise.net/murphy/thickline/index.html -// -// Feel free to replace it with better implementation -void drawThickLine(int x0, int y0, int x1, int y1, int thickness, int color, void (*plotProc)(int, int, int, void *), void *data) { - const bool steep = ABS(y1 - y0) > ABS(x1 - x0); - - if (steep) { - SWAP(x0, y0); - SWAP(x1, y1); - } - - float dx = x1 - x0; - float dy = y1 - y0; - float d = (float)sqrt(dx * dx + dy * dy); - - if (!d) + // Shortcut + if (penX == 1 && penY == 1) { + drawLine(x0, y0, x1, y1, color, plotProc, data); return; - - int thickX = (int)((float)thickness * dy / d / 2); - int thickY = (int)((float)thickness * dx / d / 2); - - const int delta_x = ABS(x1 - x0); - const int delta_y = ABS(y1 - y0); - const int delta_err = delta_y; - int x = x0; - int y = y0; - int err = 0; - - const int x_step = (x0 < x1) ? 1 : -1; - const int y_step = (y0 < y1) ? 1 : -1; - - if (steep) - drawLine(y - thickY, x + thickX, y + thickY, x - thickX, color, plotProc, data); - else - drawLine(x - thickX, y + thickY, x + thickX, y - thickY, color, plotProc, data); - - while (x != x1) { - x += x_step; - err += delta_err; - if (2 * err > delta_x) { - y += y_step; - err -= delta_x; - } - if (steep) - drawLine(y - thickY, x + thickX, y + thickY, x - thickX, color, plotProc, data); - else - drawLine(x - thickX, y + thickY, x + thickX, y - thickY, color, plotProc, data); } + + // TODO: Optimize this. It currently is a very naive way of handling + // thick lines since quite often it will be drawing to the same pixel + // multiple times. + for (int x = 0; x < penX; x++) + for (int y = 0; y < penY; y++) + drawLine(x0 + x, y0 + y, x1 + x, y1 + y, color, plotProc, data); } } // End of namespace Graphics diff --git a/graphics/primitives.h b/graphics/primitives.h index 0ab2dabcd8..f0780afc2e 100644 --- a/graphics/primitives.h +++ b/graphics/primitives.h @@ -25,7 +25,7 @@ namespace Graphics { void drawLine(int x0, int y0, int x1, int y1, int color, void (*plotProc)(int, int, int, void *), void *data); -void drawThickLine(int x0, int y0, int x1, int y1, int thickness, int color, void (*plotProc)(int, int, int, void *), void *data); +void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, int color, void (*plotProc)(int, int, int, void *), void *data); } // End of namespace Graphics diff --git a/graphics/scaler.cpp b/graphics/scaler.cpp index 9ade0e6c57..b81e8937a8 100644 --- a/graphics/scaler.cpp +++ b/graphics/scaler.cpp @@ -167,12 +167,12 @@ void DestroyScalers(){ void Normal1x(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPitch, int width, int height) { // Spot the case when it can all be done in 1 hit - if ((srcPitch == sizeof(OverlayColor) * (uint)width) && (dstPitch == sizeof(OverlayColor) * (uint)width)) { - memcpy(dstPtr, srcPtr, sizeof(OverlayColor) * width * height); + if ((srcPitch == sizeof(uint16) * (uint)width) && (dstPitch == sizeof(uint16) * (uint)width)) { + memcpy(dstPtr, srcPtr, sizeof(uint16) * width * height); return; } while (height--) { - memcpy(dstPtr, srcPtr, sizeof(OverlayColor) * width); + memcpy(dstPtr, srcPtr, sizeof(uint16) * width); srcPtr += srcPitch; dstPtr += dstPitch; } @@ -207,11 +207,10 @@ void Normal2x(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, uint32 dstPit uint8 *r; assert(IS_ALIGNED(dstPtr, 4)); - assert(sizeof(OverlayColor) == 2); while (height--) { r = dstPtr; for (int i = 0; i < width; ++i, r += 4) { - uint32 color = *(((const OverlayColor *)srcPtr) + i); + uint32 color = *(((const uint16 *)srcPtr) + i); color |= color << 16; diff --git a/graphics/scaler/aspect.cpp b/graphics/scaler/aspect.cpp index 7ad37b1ba8..327e7c5e89 100644 --- a/graphics/scaler/aspect.cpp +++ b/graphics/scaler/aspect.cpp @@ -23,6 +23,13 @@ #include "graphics/scaler/intern.h" #include "graphics/scaler/aspect.h" +#ifdef OPENPANDORA +#define NEON_ASPECT_CORRECTOR +#endif + +#ifdef NEON_ASPECT_CORRECTOR +#include <arm_neon.h> +#endif #define kSuperFastAndUglyAspectMode 0 // No interpolation at all, but super-fast #define kVeryFastAndGoodAspectMode 1 // Good quality with very good speed @@ -55,13 +62,66 @@ static inline void interpolate5Line(uint16 *dst, const uint16 *srcA, const uint1 #if ASPECT_MODE == kVeryFastAndGoodAspectMode +#ifdef NEON_ASPECT_CORRECTOR + +template<typename ColorMask> +static void interpolate5LineNeon(uint16 *dst, const uint16 *srcA, const uint16 *srcB, int width, int k1, int k2) { + uint16x4_t kRedBlueMask_4 = vdup_n_u16(ColorMask::kRedBlueMask); + uint16x4_t kGreenMask_4 = vdup_n_u16(ColorMask::kGreenMask); + uint16x4_t k1_4 = vdup_n_u16(k1); + uint16x4_t k2_4 = vdup_n_u16(k2); + while (width >= 4) { + uint16x4_t srcA_4 = vld1_u16(srcA); + uint16x4_t srcB_4 = vld1_u16(srcB); + uint16x4_t p1_4 = srcB_4; + uint16x4_t p2_4 = srcA_4; + + uint16x4_t p1_rb_4 = vand_u16(p1_4, kRedBlueMask_4); + uint16x4_t p1_g_4 = vand_u16(p1_4, kGreenMask_4); + uint16x4_t p2_rb_4 = vand_u16(p2_4, kRedBlueMask_4); + uint16x4_t p2_g_4 = vand_u16(p2_4, kGreenMask_4); + + uint32x4_t tmp_rb_4 = vshrq_n_u32(vmlal_u16(vmull_u16(p2_rb_4, k2_4), p1_rb_4, k1_4), 3); + uint32x4_t tmp_g_4 = vshrq_n_u32(vmlal_u16(vmull_u16(p2_g_4, k2_4), p1_g_4, k1_4), 3); + uint16x4_t p_rb_4 = vmovn_u32(tmp_rb_4); + p_rb_4 = vand_u16(p_rb_4, kRedBlueMask_4); + uint16x4_t p_g_4 = vmovn_u32(tmp_g_4); + p_g_4 = vand_u16(p_g_4, kGreenMask_4); + + uint16x4_t result_4 = p_rb_4 | p_g_4; + vst1_u16(dst, result_4); + + dst += 4; + srcA += 4; + srcB += 4; + width -= 4; + } +} +#endif + template<typename ColorMask, int scale> -static inline void interpolate5Line(uint16 *dst, const uint16 *srcA, const uint16 *srcB, int width) { +static void interpolate5Line(uint16 *dst, const uint16 *srcA, const uint16 *srcB, int width) { if (scale == 1) { +#ifdef NEON_ASPECT_CORRECTOR + int width4 = width & ~3; + interpolate5LineNeon<ColorMask>(dst, srcA, srcB, width4, 7, 1); + srcA += width4; + srcB += width4; + dst += width4; + width -= width4; +#endif while (width--) { *dst++ = interpolate16_7_1<ColorMask>(*srcB++, *srcA++); } } else { +#ifdef NEON_ASPECT_CORRECTOR + int width4 = width & ~3; + interpolate5LineNeon<ColorMask>(dst, srcA, srcB, width4, 5, 3); + srcA += width4; + srcB += width4; + dst += width4; + width -= width4; +#endif while (width--) { *dst++ = interpolate16_5_3<ColorMask>(*srcB++, *srcA++); } @@ -160,14 +220,14 @@ int stretch200To240(uint8 *buf, uint32 pitch, int width, int height, int srcX, i #if ASPECT_MODE == kSuperFastAndUglyAspectMode if (srcPtr == dstPtr) break; - memcpy(dstPtr, srcPtr, sizeof(OverlayColor) * width); + memcpy(dstPtr, srcPtr, sizeof(uint16) * width); #else // Bilinear filter switch (y % 6) { case 0: case 5: if (srcPtr != dstPtr) - memcpy(dstPtr, srcPtr, sizeof(OverlayColor) * width); + memcpy(dstPtr, srcPtr, sizeof(uint16) * width); break; case 1: interpolate5Line<ColorMask, 1>((uint16 *)dstPtr, (const uint16 *)(srcPtr - pitch), (const uint16 *)srcPtr, width); @@ -206,13 +266,13 @@ void Normal1xAspectTemplate(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, #if ASPECT_MODE == kSuperFastAndUglyAspectMode if ((y % 6) == 5) srcPtr -= srcPitch; - memcpy(dstPtr, srcPtr, sizeof(OverlayColor) * width); + memcpy(dstPtr, srcPtr, sizeof(uint16) * width); #else // Bilinear filter five input lines onto six output lines switch (y % 6) { case 0: // First output line is copied from first input line - memcpy(dstPtr, srcPtr, sizeof(OverlayColor) * width); + memcpy(dstPtr, srcPtr, sizeof(uint16) * width); break; case 1: // Second output line is mixed from first and second input line @@ -233,7 +293,7 @@ void Normal1xAspectTemplate(const uint8 *srcPtr, uint32 srcPitch, uint8 *dstPtr, case 5: // Sixth (and last) output line is copied from fifth (and last) input line srcPtr -= srcPitch; - memcpy(dstPtr, srcPtr, sizeof(OverlayColor) * width); + memcpy(dstPtr, srcPtr, sizeof(uint16) * width); break; } #endif diff --git a/graphics/sjis.h b/graphics/sjis.h index 2d05005fc3..928332f712 100644 --- a/graphics/sjis.h +++ b/graphics/sjis.h @@ -169,7 +169,7 @@ protected: bool _flippedMode; int _fontWidth, _fontHeight; uint8 _bitPosNewLineMask; - + bool isASCII(uint16 ch) const; virtual const uint8 *getCharData(uint16 c) const = 0; diff --git a/graphics/surface.cpp b/graphics/surface.cpp index c0f1046eae..41ae8dcebb 100644 --- a/graphics/surface.cpp +++ b/graphics/surface.cpp @@ -26,6 +26,7 @@ #include "common/textconsole.h" #include "graphics/primitives.h" #include "graphics/surface.h" +#include "graphics/conversion.h" namespace Graphics { @@ -49,6 +50,17 @@ void Surface::drawLine(int x0, int y0, int x1, int y1, uint32 color) { error("Surface::drawLine: bytesPerPixel must be 1, 2, or 4"); } +void Surface::drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, uint32 color) { + if (format.bytesPerPixel == 1) + Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<byte>, this); + else if (format.bytesPerPixel == 2) + Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<uint16>, this); + else if (format.bytesPerPixel == 4) + Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<uint32>, this); + else + error("Surface::drawThickLine: bytesPerPixel must be 1, 2, or 4"); +} + void Surface::create(uint16 width, uint16 height, const PixelFormat &f) { free(); @@ -271,6 +283,72 @@ void Surface::move(int dx, int dy, int height) { } } +void Surface::convertToInPlace(const PixelFormat &dstFormat, const byte *palette) { + // Do not convert to the same format and ignore empty surfaces. + if (format == dstFormat || pixels == 0) { + return; + } + + if (format.bytesPerPixel == 0 || format.bytesPerPixel > 4) + error("Surface::convertToInPlace(): Can only convert from 1Bpp, 2Bpp, 3Bpp, and 4Bpp"); + + if (dstFormat.bytesPerPixel != 2 && dstFormat.bytesPerPixel != 4) + error("Surface::convertToInPlace(): Can only convert to 2Bpp and 4Bpp"); + + // In case the surface data needs more space allocate it. + if (dstFormat.bytesPerPixel > format.bytesPerPixel) { + void *const newPixels = realloc(pixels, w * h * dstFormat.bytesPerPixel); + if (!newPixels) { + error("Surface::convertToInPlace(): Out of memory"); + } + pixels = newPixels; + } + + // We take advantage of the fact that pitch is always w * format.bytesPerPixel. + // This is assured by the logic of Surface::create. + + // We need to handle 1 Bpp surfaces special here. + if (format.bytesPerPixel == 1) { + assert(palette); + + for (int y = h; y > 0; --y) { + const byte *srcRow = (const byte *)pixels + y * pitch - 1; + byte *dstRow = (byte *)pixels + y * w * dstFormat.bytesPerPixel - dstFormat.bytesPerPixel; + + for (int x = 0; x < w; x++) { + byte index = *srcRow--; + byte r = palette[index * 3]; + byte g = palette[index * 3 + 1]; + byte b = palette[index * 3 + 2]; + + uint32 color = dstFormat.RGBToColor(r, g, b); + + if (dstFormat.bytesPerPixel == 2) + *((uint16 *)dstRow) = color; + else + *((uint32 *)dstRow) = color; + + dstRow -= dstFormat.bytesPerPixel; + } + } + } else { + crossBlit((byte *)pixels, (const byte *)pixels, w * dstFormat.bytesPerPixel, pitch, w, h, dstFormat, format); + } + + // In case the surface data got smaller, free up some memory. + if (dstFormat.bytesPerPixel < format.bytesPerPixel) { + void *const newPixels = realloc(pixels, w * h * dstFormat.bytesPerPixel); + if (!newPixels) { + error("Surface::convertToInPlace(): Freeing memory failed"); + } + pixels = newPixels; + } + + // Update the surface specific data. + format = dstFormat; + pitch = w * dstFormat.bytesPerPixel; +} + Graphics::Surface *Surface::convertTo(const PixelFormat &dstFormat, const byte *palette) const { assert(pixels); diff --git a/graphics/surface.h b/graphics/surface.h index eb8d1ac42e..6c9e464657 100644 --- a/graphics/surface.h +++ b/graphics/surface.h @@ -137,6 +137,20 @@ struct Surface { /** * Convert the data to another pixel format. * + * This works in-place. This means it will not create an additional buffer + * for the conversion process. The value of pixels might change though. + * + * Note that you should only use this, when you created the Surface data via + * create! Otherwise this function has undefined behavior. + * + * @param dstFormat The desired format + * @param palette The palette (in RGB888), if the source format has a Bpp of 1 + */ + void convertToInPlace(const PixelFormat &dstFormat, const byte *palette = 0); + + /** + * Convert the data to another pixel format. + * * The calling code must call free on the returned surface and then delete * it. * @@ -153,10 +167,26 @@ struct Surface { * @param x1 The x coordinate of the end point. * @param y1 The y coordinate of the end point. * @param color The color of the line. + * @note This is just a wrapper around Graphics::drawLine */ void drawLine(int x0, int y0, int x1, int y1, uint32 color); /** + * Draw a thick line. + * + * @param x0 The x coordinate of the start point. + * @param y0 The y coordiante of the start point. + * @param x1 The x coordinate of the end point. + * @param y1 The y coordinate of the end point. + * @param penX The width of the pen (thickness in the x direction) + * @param penY The height of the pen (thickness in the y direction) + * @param color The color of the line. + * @note This is just a wrapper around Graphics::drawThickLine + * @note The x/y coordinates of the start and end points are the upper-left most part of the pen + */ + void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, uint32 color); + + /** * Draw a horizontal line. * * @param x The start x coordinate of the line. diff --git a/graphics/wincursor.cpp b/graphics/wincursor.cpp index 2db72a2874..1d599f7130 100644 --- a/graphics/wincursor.cpp +++ b/graphics/wincursor.cpp @@ -30,6 +30,46 @@ namespace Graphics { +/** A Windows cursor. */ +class WinCursor : public Cursor { +public: + WinCursor(); + ~WinCursor(); + + /** Return the cursor's width. */ + uint16 getWidth() const; + /** Return the cursor's height. */ + uint16 getHeight() const; + /** Return the cursor's hotspot's x coordinate. */ + uint16 getHotspotX() const; + /** Return the cursor's hotspot's y coordinate. */ + uint16 getHotspotY() const; + /** Return the cursor's transparent key. */ + byte getKeyColor() const; + + const byte *getSurface() const { return _surface; } + + const byte *getPalette() const { return _palette; } + byte getPaletteStartIndex() const { return 0; } + uint16 getPaletteCount() const { return 256; } + + /** Read the cursor's data out of a stream. */ + bool readFromStream(Common::SeekableReadStream &stream); + +private: + byte *_surface; + byte _palette[256 * 3]; + + uint16 _width; ///< The cursor's width. + uint16 _height; ///< The cursor's height. + uint16 _hotspotX; ///< The cursor's hotspot's x coordinate. + uint16 _hotspotY; ///< The cursor's hotspot's y coordinate. + byte _keyColor; ///< The cursor's transparent key + + /** Clear the cursor. */ + void clear(); +}; + WinCursor::WinCursor() { _width = 0; _height = 0; diff --git a/graphics/wincursor.h b/graphics/wincursor.h index e6b35dc80c..9e73e3a12f 100644 --- a/graphics/wincursor.h +++ b/graphics/wincursor.h @@ -36,46 +36,6 @@ class SeekableReadStream; namespace Graphics { -/** A Windows cursor. */ -class WinCursor : public Cursor { -public: - WinCursor(); - ~WinCursor(); - - /** Return the cursor's width. */ - uint16 getWidth() const; - /** Return the cursor's height. */ - uint16 getHeight() const; - /** Return the cursor's hotspot's x coordinate. */ - uint16 getHotspotX() const; - /** Return the cursor's hotspot's y coordinate. */ - uint16 getHotspotY() const; - /** Return the cursor's transparent key. */ - byte getKeyColor() const; - - const byte *getSurface() const { return _surface; } - - const byte *getPalette() const { return _palette; } - byte getPaletteStartIndex() const { return 0; } - uint16 getPaletteCount() const { return 256; } - - /** Read the cursor's data out of a stream. */ - bool readFromStream(Common::SeekableReadStream &stream); - -private: - byte *_surface; - byte _palette[256 * 3]; - - uint16 _width; ///< The cursor's width. - uint16 _height; ///< The cursor's height. - uint16 _hotspotX; ///< The cursor's hotspot's x coordinate. - uint16 _hotspotY; ///< The cursor's hotspot's y coordinate. - byte _keyColor; ///< The cursor's transparent key - - /** Clear the cursor. */ - void clear(); -}; - /** * A structure holding an array of cursors from a single Windows Executable cursor group. * @@ -91,7 +51,7 @@ struct WinCursorGroup { struct CursorItem { Common::WinResourceID id; - WinCursor *cursor; + Cursor *cursor; }; Common::Array<CursorItem> cursors; diff --git a/graphics/yuv_to_rgb.cpp b/graphics/yuv_to_rgb.cpp index ac7f217fee..6043315a13 100644 --- a/graphics/yuv_to_rgb.cpp +++ b/graphics/yuv_to_rgb.cpp @@ -83,129 +83,127 @@ // BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, // SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -#include "common/scummsys.h" -#include "common/singleton.h" - #include "graphics/surface.h" +#include "graphics/yuv_to_rgb.h" + +namespace Common { +DECLARE_SINGLETON(Graphics::YUVToRGBManager); +} namespace Graphics { class YUVToRGBLookup { public: - YUVToRGBLookup(Graphics::PixelFormat format); - ~YUVToRGBLookup(); + YUVToRGBLookup(Graphics::PixelFormat format, YUVToRGBManager::LuminanceScale scale); - int16 *_colorTab; - uint32 *_rgbToPix; -}; + Graphics::PixelFormat getFormat() const { return _format; } + YUVToRGBManager::LuminanceScale getScale() const { return _scale; } + const uint32 *getRGBToPix() const { return _rgbToPix; } -YUVToRGBLookup::YUVToRGBLookup(Graphics::PixelFormat format) { - _colorTab = new int16[4 * 256]; // 2048 bytes - - int16 *Cr_r_tab = &_colorTab[0 * 256]; - int16 *Cr_g_tab = &_colorTab[1 * 256]; - int16 *Cb_g_tab = &_colorTab[2 * 256]; - int16 *Cb_b_tab = &_colorTab[3 * 256]; +private: + Graphics::PixelFormat _format; + YUVToRGBManager::LuminanceScale _scale; + uint32 _rgbToPix[3 * 768]; // 9216 bytes +}; - _rgbToPix = new uint32[3 * 768]; // 9216 bytes +YUVToRGBLookup::YUVToRGBLookup(Graphics::PixelFormat format, YUVToRGBManager::LuminanceScale scale) { + _format = format; + _scale = scale; uint32 *r_2_pix_alloc = &_rgbToPix[0 * 768]; uint32 *g_2_pix_alloc = &_rgbToPix[1 * 768]; uint32 *b_2_pix_alloc = &_rgbToPix[2 * 768]; - int16 CR, CB; - int i; + if (scale == YUVToRGBManager::kScaleFull) { + // Set up entries 0-255 in rgb-to-pixel value tables. + for (int i = 0; i < 256; i++) { + r_2_pix_alloc[i + 256] = format.RGBToColor(i, 0, 0); + g_2_pix_alloc[i + 256] = format.RGBToColor(0, i, 0); + b_2_pix_alloc[i + 256] = format.RGBToColor(0, 0, i); + } + + // Spread out the values we have to the rest of the array so that we do + // not need to check for overflow. + for (int i = 0; i < 256; i++) { + r_2_pix_alloc[i] = r_2_pix_alloc[256]; + r_2_pix_alloc[i + 512] = r_2_pix_alloc[511]; + g_2_pix_alloc[i] = g_2_pix_alloc[256]; + g_2_pix_alloc[i + 512] = g_2_pix_alloc[511]; + b_2_pix_alloc[i] = b_2_pix_alloc[256]; + b_2_pix_alloc[i + 512] = b_2_pix_alloc[511]; + } + } else { + // Set up entries 16-235 in rgb-to-pixel value tables + for (int i = 16; i < 236; i++) { + int scaledValue = (i - 16) * 255 / 219; + r_2_pix_alloc[i + 256] = format.RGBToColor(scaledValue, 0, 0); + g_2_pix_alloc[i + 256] = format.RGBToColor(0, scaledValue, 0); + b_2_pix_alloc[i + 256] = format.RGBToColor(0, 0, scaledValue); + } + + // Spread out the values we have to the rest of the array so that we do + // not need to check for overflow. We have to do it here in two steps. + for (int i = 0; i < 256 + 16; i++) { + r_2_pix_alloc[i] = r_2_pix_alloc[256 + 16]; + g_2_pix_alloc[i] = g_2_pix_alloc[256 + 16]; + b_2_pix_alloc[i] = b_2_pix_alloc[256 + 16]; + } + + for (int i = 256 + 236; i < 768; i++) { + r_2_pix_alloc[i] = r_2_pix_alloc[256 + 236 - 1]; + g_2_pix_alloc[i] = g_2_pix_alloc[256 + 236 - 1]; + b_2_pix_alloc[i] = b_2_pix_alloc[256 + 236 - 1]; + } + } +} + +YUVToRGBManager::YUVToRGBManager() { + _lookup = 0; + + int16 *Cr_r_tab = &_colorTab[0 * 256]; + int16 *Cr_g_tab = &_colorTab[1 * 256]; + int16 *Cb_g_tab = &_colorTab[2 * 256]; + int16 *Cb_b_tab = &_colorTab[3 * 256]; // Generate the tables for the display surface - for (i = 0; i < 256; i++) { + for (int i = 0; i < 256; i++) { // Gamma correction (luminescence table) and chroma correction // would be done here. See the Berkeley mpeg_play sources. - CR = CB = (i - 128); + int16 CR = (i - 128), CB = CR; Cr_r_tab[i] = (int16) ( (0.419 / 0.299) * CR) + 0 * 768 + 256; Cr_g_tab[i] = (int16) (-(0.299 / 0.419) * CR) + 1 * 768 + 256; Cb_g_tab[i] = (int16) (-(0.114 / 0.331) * CB); Cb_b_tab[i] = (int16) ( (0.587 / 0.331) * CB) + 2 * 768 + 256; } - - // Set up entries 0-255 in rgb-to-pixel value tables. - for (i = 0; i < 256; i++) { - r_2_pix_alloc[i + 256] = format.RGBToColor(i, 0, 0); - g_2_pix_alloc[i + 256] = format.RGBToColor(0, i, 0); - b_2_pix_alloc[i + 256] = format.RGBToColor(0, 0, i); - } - - // Spread out the values we have to the rest of the array so that we do - // not need to check for overflow. - for (i = 0; i < 256; i++) { - r_2_pix_alloc[i] = r_2_pix_alloc[256]; - r_2_pix_alloc[i + 512] = r_2_pix_alloc[511]; - g_2_pix_alloc[i] = g_2_pix_alloc[256]; - g_2_pix_alloc[i + 512] = g_2_pix_alloc[511]; - b_2_pix_alloc[i] = b_2_pix_alloc[256]; - b_2_pix_alloc[i + 512] = b_2_pix_alloc[511]; - } -} - -YUVToRGBLookup::~YUVToRGBLookup() { - delete[] _rgbToPix; - delete[] _colorTab; -} - -class YUVToRGBManager : public Common::Singleton<YUVToRGBManager> { -public: - const YUVToRGBLookup *getLookup(Graphics::PixelFormat format); - -private: - friend class Common::Singleton<SingletonBaseType>; - YUVToRGBManager(); - ~YUVToRGBManager(); - - Graphics::PixelFormat _lastFormat; - YUVToRGBLookup *_lookup; -}; - -YUVToRGBManager::YUVToRGBManager() { - _lookup = 0; } YUVToRGBManager::~YUVToRGBManager() { delete _lookup; } -const YUVToRGBLookup *YUVToRGBManager::getLookup(Graphics::PixelFormat format) { - if (_lastFormat == format) +const YUVToRGBLookup *YUVToRGBManager::getLookup(Graphics::PixelFormat format, YUVToRGBManager::LuminanceScale scale) { + if (_lookup && _lookup->getFormat() == format && _lookup->getScale() == scale) return _lookup; delete _lookup; - _lookup = new YUVToRGBLookup(format); - _lastFormat = format; + _lookup = new YUVToRGBLookup(format, scale); return _lookup; } -} // End of namespace Graphics - -namespace Common { -DECLARE_SINGLETON(Graphics::YUVToRGBManager); -} - -#define YUVToRGBMan (Graphics::YUVToRGBManager::instance()) - -namespace Graphics { - #define PUT_PIXEL(s, d) \ L = &rgbToPix[(s)]; \ *((PixelInt *)(d)) = (L[cr_r] | L[crb_g] | L[cb_b]) template<typename PixelInt> -void convertYUV444ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { +void convertYUV444ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, int16 *colorTab, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { // Keep the tables in pointers here to avoid a dereference on each pixel - const int16 *Cr_r_tab = lookup->_colorTab; + const int16 *Cr_r_tab = colorTab; const int16 *Cr_g_tab = Cr_r_tab + 256; const int16 *Cb_g_tab = Cr_g_tab + 256; const int16 *Cb_b_tab = Cb_g_tab + 256; - const uint32 *rgbToPix = lookup->_rgbToPix; + const uint32 *rgbToPix = lookup->getRGBToPix(); for (int h = 0; h < yHeight; h++) { for (int w = 0; w < yWidth; w++) { @@ -229,32 +227,32 @@ void convertYUV444ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup } } -void convertYUV444ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { +void YUVToRGBManager::convert444(Graphics::Surface *dst, YUVToRGBManager::LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { // Sanity checks assert(dst && dst->pixels); assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4); assert(ySrc && uSrc && vSrc); - const YUVToRGBLookup *lookup = YUVToRGBMan.getLookup(dst->format); + const YUVToRGBLookup *lookup = getLookup(dst->format, scale); // Use a templated function to avoid an if check on every pixel if (dst->format.bytesPerPixel == 2) - convertYUV444ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); + convertYUV444ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); else - convertYUV444ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); + convertYUV444ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); } template<typename PixelInt> -void convertYUV420ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { +void convertYUV420ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, int16 *colorTab, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { int halfHeight = yHeight >> 1; int halfWidth = yWidth >> 1; // Keep the tables in pointers here to avoid a dereference on each pixel - const int16 *Cr_r_tab = lookup->_colorTab; + const int16 *Cr_r_tab = colorTab; const int16 *Cr_g_tab = Cr_r_tab + 256; const int16 *Cb_g_tab = Cr_g_tab + 256; const int16 *Cb_b_tab = Cb_g_tab + 256; - const uint32 *rgbToPix = lookup->_rgbToPix; + const uint32 *rgbToPix = lookup->getRGBToPix(); for (int h = 0; h < halfHeight; h++) { for (int w = 0; w < halfWidth; w++) { @@ -283,7 +281,7 @@ void convertYUV420ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup } } -void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { +void YUVToRGBManager::convert420(Graphics::Surface *dst, YUVToRGBManager::LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { // Sanity checks assert(dst && dst->pixels); assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4); @@ -291,13 +289,98 @@ void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uS assert((yWidth & 1) == 0); assert((yHeight & 1) == 0); - const YUVToRGBLookup *lookup = YUVToRGBMan.getLookup(dst->format); + const YUVToRGBLookup *lookup = getLookup(dst->format, scale); + + // Use a templated function to avoid an if check on every pixel + if (dst->format.bytesPerPixel == 2) + convertYUV420ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); + else + convertYUV420ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); +} + +#define READ_QUAD(ptr, prefix) \ + byte prefix##A = ptr[index]; \ + byte prefix##B = ptr[index + 1]; \ + byte prefix##C = ptr[index + uvPitch]; \ + byte prefix##D = ptr[index + uvPitch + 1] + +#define DO_INTERPOLATION(out) \ + out = (out##A * (4 - xDiff) * (4 - yDiff) + out##B * xDiff * (4 - yDiff) + \ + out##C * yDiff * (4 - xDiff) + out##D * xDiff * yDiff) >> 4 + +#define DO_YUV410_PIXEL() \ + DO_INTERPOLATION(u); \ + DO_INTERPOLATION(v); \ + \ + cr_r = Cr_r_tab[v]; \ + crb_g = Cr_g_tab[v] + Cb_g_tab[u]; \ + cb_b = Cb_b_tab[u]; \ + \ + PUT_PIXEL(*ySrc, dstPtr); \ + dstPtr += sizeof(PixelInt); \ + \ + ySrc++; \ + xDiff++ + +template<typename PixelInt> +void convertYUV410ToRGB(byte *dstPtr, int dstPitch, const YUVToRGBLookup *lookup, int16 *colorTab, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { + // Keep the tables in pointers here to avoid a dereference on each pixel + const int16 *Cr_r_tab = colorTab; + const int16 *Cr_g_tab = Cr_r_tab + 256; + const int16 *Cb_g_tab = Cr_g_tab + 256; + const int16 *Cb_b_tab = Cb_g_tab + 256; + const uint32 *rgbToPix = lookup->getRGBToPix(); + + int quarterWidth = yWidth >> 2; + + for (int y = 0; y < yHeight; y++) { + for (int x = 0; x < quarterWidth; x++) { + // Perform bilinear interpolation on the the chroma values + // Based on the algorithm found here: http://tech-algorithm.com/articles/bilinear-image-scaling/ + // Feel free to optimize further + int targetY = y >> 2; + int xDiff = 0; + int yDiff = y & 3; + int index = targetY * uvPitch + x; + + // Declare some variables for the following macros + byte u, v; + int16 cr_r, crb_g, cb_b; + register const uint32 *L; + + READ_QUAD(uSrc, u); + READ_QUAD(vSrc, v); + + DO_YUV410_PIXEL(); + DO_YUV410_PIXEL(); + DO_YUV410_PIXEL(); + DO_YUV410_PIXEL(); + } + + dstPtr += dstPitch - yWidth * sizeof(PixelInt); + ySrc += yPitch - yWidth; + } +} + +#undef READ_QUAD +#undef DO_INTERPOLATION +#undef DO_YUV410_PIXEL + +void YUVToRGBManager::convert410(Graphics::Surface *dst, YUVToRGBManager::LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch) { + // Sanity checks + assert(dst && dst->pixels); + assert(dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4); + assert(ySrc && uSrc && vSrc); + assert((yWidth & 3) == 0); + assert((yHeight & 3) == 0); + + const YUVToRGBLookup *lookup = getLookup(dst->format, scale); // Use a templated function to avoid an if check on every pixel if (dst->format.bytesPerPixel == 2) - convertYUV420ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); + convertYUV410ToRGB<uint16>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); else - convertYUV420ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); + convertYUV410ToRGB<uint32>((byte *)dst->pixels, dst->pitch, lookup, _colorTab, ySrc, uSrc, vSrc, yWidth, yHeight, yPitch, uvPitch); } } // End of namespace Graphics diff --git a/graphics/yuv_to_rgb.h b/graphics/yuv_to_rgb.h index 8e025042dc..f785422c5a 100644 --- a/graphics/yuv_to_rgb.h +++ b/graphics/yuv_to_rgb.h @@ -32,38 +32,85 @@ #define GRAPHICS_YUV_TO_RGB_H #include "common/scummsys.h" +#include "common/singleton.h" #include "graphics/surface.h" namespace Graphics { -/** - * Convert a YUV444 image to an RGB surface - * - * @param dst the destination surface - * @param ySrc the source of the y component - * @param uSrc the source of the u component - * @param vSrc the source of the v component - * @param yWidth the width of the y surface - * @param yHeight the height of the y surface - * @param yPitch the pitch of the y surface - * @param uvPitch the pitch of the u and v surfaces - */ -void convertYUV444ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch); +class YUVToRGBLookup; -/** - * Convert a YUV420 image to an RGB surface - * - * @param dst the destination surface - * @param ySrc the source of the y component - * @param uSrc the source of the u component - * @param vSrc the source of the v component - * @param yWidth the width of the y surface (must be divisible by 2) - * @param yHeight the height of the y surface (must be divisible by 2) - * @param yPitch the pitch of the y surface - * @param uvPitch the pitch of the u and v surfaces - */ -void convertYUV420ToRGB(Graphics::Surface *dst, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch); +class YUVToRGBManager : public Common::Singleton<YUVToRGBManager> { +public: + /** The scale of the luminance values */ + enum LuminanceScale { + kScaleFull, /** Luminance values range from [0, 255] */ + kScaleITU /** Luminance values range from [16, 235], the range from ITU-R BT.601 */ + }; + + /** + * Convert a YUV444 image to an RGB surface + * + * @param dst the destination surface + * @param scale the scale of the luminance values + * @param ySrc the source of the y component + * @param uSrc the source of the u component + * @param vSrc the source of the v component + * @param yWidth the width of the y surface + * @param yHeight the height of the y surface + * @param yPitch the pitch of the y surface + * @param uvPitch the pitch of the u and v surfaces + */ + void convert444(Graphics::Surface *dst, LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch); + + /** + * Convert a YUV420 image to an RGB surface + * + * @param dst the destination surface + * @param scale the scale of the luminance values + * @param ySrc the source of the y component + * @param uSrc the source of the u component + * @param vSrc the source of the v component + * @param yWidth the width of the y surface (must be divisible by 2) + * @param yHeight the height of the y surface (must be divisible by 2) + * @param yPitch the pitch of the y surface + * @param uvPitch the pitch of the u and v surfaces + */ + void convert420(Graphics::Surface *dst, LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch); + + /** + * Convert a YUV410 image to an RGB surface + * + * Since the chroma has a very low resolution in 410, we perform bilinear scaling + * on the two chroma planes to produce the image. The chroma planes must have + * at least one extra row and one extra column that can be read from in order to + * produce a proper image. It is suggested that you fill these in with the previous + * row and column's data. This is required in order to speed up this function. + * + * @param dst the destination surface + * @param scale the scale of the luminance values + * @param ySrc the source of the y component + * @param uSrc the source of the u component + * @param vSrc the source of the v component + * @param yWidth the width of the y surface (must be divisible by 4) + * @param yHeight the height of the y surface (must be divisible by 4) + * @param yPitch the pitch of the y surface + * @param uvPitch the pitch of the u and v surfaces + */ + void convert410(Graphics::Surface *dst, LuminanceScale scale, const byte *ySrc, const byte *uSrc, const byte *vSrc, int yWidth, int yHeight, int yPitch, int uvPitch); + +private: + friend class Common::Singleton<SingletonBaseType>; + YUVToRGBManager(); + ~YUVToRGBManager(); + + const YUVToRGBLookup *getLookup(Graphics::PixelFormat format, LuminanceScale scale); + + YUVToRGBLookup *_lookup; + int16 _colorTab[4 * 256]; // 2048 bytes +}; } // End of namespace Graphics +#define YUVToRGBMan (::Graphics::YUVToRGBManager::instance()) + #endif |