diff options
-rw-r--r-- | graphics/conversion.cpp | 202 | ||||
-rw-r--r-- | graphics/conversion.h | 19 | ||||
-rw-r--r-- | graphics/surface.cpp | 67 | ||||
-rw-r--r-- | graphics/surface.h | 14 | ||||
-rw-r--r-- | gui/widget.cpp | 64 | ||||
-rw-r--r-- | gui/widget.h | 4 |
6 files changed, 233 insertions, 137 deletions
diff --git a/graphics/conversion.cpp b/graphics/conversion.cpp index 713a06ea74..fec1d240e2 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> +FORCEINLINE 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> +FORCEINLINE 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/surface.cpp b/graphics/surface.cpp index a37dd57e61..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 { @@ -282,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 9c8c040cbf..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. * diff --git a/gui/widget.cpp b/gui/widget.cpp index 270cdc56de..4ffb63e945 100644 --- a/gui/widget.cpp +++ b/gui/widget.cpp @@ -376,7 +376,7 @@ void ButtonWidget::wantTickle(bool tickled) { PicButtonWidget::PicButtonWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip, uint32 cmd, uint8 hotkey) : ButtonWidget(boss, x, y, w, h, "", tooltip, cmd, hotkey), - _gfx(new Graphics::Surface()), _alpha(256), _transparency(false) { + _gfx(), _alpha(256), _transparency(false) { setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG); _type = kButtonWidget; @@ -384,18 +384,17 @@ PicButtonWidget::PicButtonWidget(GuiObject *boss, int x, int y, int w, int h, co PicButtonWidget::PicButtonWidget(GuiObject *boss, const Common::String &name, const char *tooltip, uint32 cmd, uint8 hotkey) : ButtonWidget(boss, name, "", tooltip, cmd, hotkey), - _gfx(new Graphics::Surface()), _alpha(256), _transparency(false) { + _gfx(), _alpha(256), _transparency(false) { setFlags(WIDGET_ENABLED/* | WIDGET_BORDER*/ | WIDGET_CLEARBG); _type = kButtonWidget; } PicButtonWidget::~PicButtonWidget() { - _gfx->free(); - delete _gfx; + _gfx.free(); } void PicButtonWidget::setGfx(const Graphics::Surface *gfx) { - _gfx->free(); + _gfx.free(); if (!gfx || !gfx->pixels) return; @@ -411,7 +410,7 @@ void PicButtonWidget::setGfx(const Graphics::Surface *gfx) { return; } - _gfx->copyFrom(*gfx); + _gfx.copyFrom(*gfx); } void PicButtonWidget::setGfx(int w, int h, int r, int g, int b) { @@ -422,29 +421,26 @@ void PicButtonWidget::setGfx(int w, int h, int r, int g, int b) { const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat(); - _gfx->free(); - _gfx->create(w, h, requiredFormat); - _gfx->fillRect(Common::Rect(0, 0, w, h), _gfx->format.RGBToColor(r, g, b)); + _gfx.free(); + _gfx.create(w, h, requiredFormat); + _gfx.fillRect(Common::Rect(0, 0, w, h), _gfx.format.RGBToColor(r, g, b)); } void PicButtonWidget::drawWidget() { g_gui.theme()->drawButton(Common::Rect(_x, _y, _x+_w, _y+_h), "", _state, getFlags()); - if (_gfx->pixels) { + if (_gfx.pixels) { // Check whether the set up surface needs to be converted to the GUI // color format. const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat(); - if (_gfx->format != requiredFormat) { - Graphics::Surface *converted = _gfx->convertTo(requiredFormat); - _gfx->free(); - delete _gfx; - _gfx = converted; + if (_gfx.format != requiredFormat) { + _gfx.convertToInPlace(requiredFormat); } - const int x = _x + (_w - _gfx->w) / 2; - const int y = _y + (_h - _gfx->h) / 2; + const int x = _x + (_w - _gfx.w) / 2; + const int y = _y + (_h - _gfx.h) / 2; - g_gui.theme()->drawSurface(Common::Rect(x, y, x + _gfx->w, y + _gfx->h), *_gfx, _state, _alpha, _transparency); + g_gui.theme()->drawSurface(Common::Rect(x, y, x + _gfx.w, y + _gfx.h), _gfx, _state, _alpha, _transparency); } } @@ -632,24 +628,23 @@ int SliderWidget::posToValue(int pos) { #pragma mark - GraphicsWidget::GraphicsWidget(GuiObject *boss, int x, int y, int w, int h, const char *tooltip) - : Widget(boss, x, y, w, h, tooltip), _gfx(new Graphics::Surface()), _alpha(256), _transparency(false) { + : Widget(boss, x, y, w, h, tooltip), _gfx(), _alpha(256), _transparency(false) { setFlags(WIDGET_ENABLED | WIDGET_CLEARBG); _type = kGraphicsWidget; } GraphicsWidget::GraphicsWidget(GuiObject *boss, const Common::String &name, const char *tooltip) - : Widget(boss, name, tooltip), _gfx(new Graphics::Surface()), _alpha(256), _transparency(false) { + : Widget(boss, name, tooltip), _gfx(), _alpha(256), _transparency(false) { setFlags(WIDGET_ENABLED | WIDGET_CLEARBG); _type = kGraphicsWidget; } GraphicsWidget::~GraphicsWidget() { - _gfx->free(); - delete _gfx; + _gfx.free(); } void GraphicsWidget::setGfx(const Graphics::Surface *gfx) { - _gfx->free(); + _gfx.free(); if (!gfx || !gfx->pixels) return; @@ -664,7 +659,7 @@ void GraphicsWidget::setGfx(const Graphics::Surface *gfx) { return; } - _gfx->copyFrom(*gfx); + _gfx.copyFrom(*gfx); } void GraphicsWidget::setGfx(int w, int h, int r, int g, int b) { @@ -675,27 +670,24 @@ void GraphicsWidget::setGfx(int w, int h, int r, int g, int b) { const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat(); - _gfx->free(); - _gfx->create(w, h, requiredFormat); - _gfx->fillRect(Common::Rect(0, 0, w, h), _gfx->format.RGBToColor(r, g, b)); + _gfx.free(); + _gfx.create(w, h, requiredFormat); + _gfx.fillRect(Common::Rect(0, 0, w, h), _gfx.format.RGBToColor(r, g, b)); } void GraphicsWidget::drawWidget() { - if (_gfx->pixels) { + if (_gfx.pixels) { // Check whether the set up surface needs to be converted to the GUI // color format. const Graphics::PixelFormat &requiredFormat = g_gui.theme()->getPixelFormat(); - if (_gfx->format != requiredFormat) { - Graphics::Surface *converted = _gfx->convertTo(requiredFormat); - _gfx->free(); - delete _gfx; - _gfx = converted; + if (_gfx.format != requiredFormat) { + _gfx.convertToInPlace(requiredFormat); } - const int x = _x + (_w - _gfx->w) / 2; - const int y = _y + (_h - _gfx->h) / 2; + const int x = _x + (_w - _gfx.w) / 2; + const int y = _y + (_h - _gfx.h) / 2; - g_gui.theme()->drawSurface(Common::Rect(x, y, x + _gfx->w, y + _gfx->h), *_gfx, _state, _alpha, _transparency); + g_gui.theme()->drawSurface(Common::Rect(x, y, x + _gfx.w, y + _gfx.h), _gfx, _state, _alpha, _transparency); } } diff --git a/gui/widget.h b/gui/widget.h index 6f710f302f..e3f712564f 100644 --- a/gui/widget.h +++ b/gui/widget.h @@ -230,7 +230,7 @@ public: protected: void drawWidget(); - Graphics::Surface *_gfx; + Graphics::Surface _gfx; int _alpha; bool _transparency; }; @@ -358,7 +358,7 @@ public: protected: void drawWidget(); - Graphics::Surface *_gfx; + Graphics::Surface _gfx; int _alpha; bool _transparency; }; |