From 05d24e892c15a0bc2a8a923927125098704f42db Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sat, 14 Jul 2012 03:35:54 +0200 Subject: [PATCH 1/9] GRAPHICS: Clean up crossBlit a bit. --- graphics/conversion.cpp | 57 +++++++++++++++++++++-------------------- graphics/conversion.h | 6 +++-- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/graphics/conversion.cpp b/graphics/conversion.cpp index 713a06ea742..705f672a937 100644 --- a/graphics/conversion.cpp +++ b/graphics/conversion.cpp @@ -27,8 +27,10 @@ namespace Graphics { // TODO: YUV to RGB conversion function // Function to blit a rect from one color format to another -bool crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch, - int w, int h, const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt) { +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) @@ -37,32 +39,31 @@ bool crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch, // 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) { + for (uint y = 0; y < h; ++y) { + for (uint 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); @@ -78,8 +79,8 @@ bool crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch, col++; #endif if (srcFmt.bytesPerPixel == 2) { - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++, src += 2, dst += 3) { + for (uint y = 0; y < h; ++y) { + for (uint 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); @@ -89,8 +90,8 @@ bool crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch, dst += dstDelta; } } else { - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++, src += 3, dst += 3) { + for (uint y = 0; y < h; ++y) { + for (uint 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); @@ -103,8 +104,8 @@ bool crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch, } 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) { + for (uint y = 0; y < h; ++y) { + for (uint 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); @@ -118,8 +119,8 @@ bool crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch, #ifdef SCUMM_BIG_ENDIAN col++; #endif - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++, src += 2, dst += 4) { + for (uint y = 0; y < h; ++y) { + for (uint 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); @@ -129,8 +130,8 @@ bool crossBlit(byte *dst, const byte *src, int dstpitch, int srcpitch, dst += dstDelta; } } else { - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++, src += 4, dst += 4) { + for (uint y = 0; y < h; ++y) { + for (uint 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); diff --git a/graphics/conversion.h b/graphics/conversion.h index 6babc763e22..c25f413fab4 100644 --- a/graphics/conversion.h +++ b/graphics/conversion.h @@ -66,8 +66,10 @@ inline static void RGB2YUV(byte r, byte g, byte b, byte &y, byte &u, byte &v) { * destination format have the same bytedepth. * */ -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 From ea1bcaad3380132356adb567826e74e587c389cd Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sat, 14 Jul 2012 04:53:10 +0200 Subject: [PATCH 2/9] GRAPHICS: Refactor crossBlit logic into a template. --- graphics/conversion.cpp | 62 +++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/graphics/conversion.cpp b/graphics/conversion.cpp index 705f672a937..7b6bda30a44 100644 --- a/graphics/conversion.cpp +++ b/graphics/conversion.cpp @@ -26,6 +26,29 @@ namespace Graphics { // TODO: YUV to RGB conversion function +namespace { + +template +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) { + uint32 color = *(const SrcColor *)src; + byte a, r, g, b; + srcFmt.colorToARGB(color, a, r, g, b); + color = dstFmt.ARGBToColor(a, r, g, b); + *(DstColor *)dst = color; + src += sizeof(SrcColor); + dst += sizeof(DstColor); + } + 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, const uint dstPitch, const uint srcPitch, @@ -59,21 +82,11 @@ bool crossBlit(byte *dst, const byte *src, 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 (uint y = 0; y < h; ++y) { - for (uint 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; - } + crossBlitLogic(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } else if (dstFmt.bytesPerPixel == 3) { uint32 color; + uint8 r, g, b, a; uint8 *col = (uint8 *) &color; #ifdef SCUMM_BIG_ENDIAN col++; @@ -102,19 +115,11 @@ bool crossBlit(byte *dst, const byte *src, } } } else if (dstFmt.bytesPerPixel == 4) { - uint32 color; if (srcFmt.bytesPerPixel == 2) { - for (uint y = 0; y < h; ++y) { - for (uint 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; - } + crossBlitLogic(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } else if (srcFmt.bytesPerPixel == 3) { + uint32 color; + byte r, g, b, a; uint8 *col = (uint8 *)&color; #ifdef SCUMM_BIG_ENDIAN col++; @@ -130,16 +135,7 @@ bool crossBlit(byte *dst, const byte *src, dst += dstDelta; } } else { - for (uint y = 0; y < h; ++y) { - for (uint 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(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } } else { return false; From a0f46e9396861b9eb4ab8adebcbc4739e44b9716 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sat, 14 Jul 2012 04:53:37 +0200 Subject: [PATCH 3/9] GRAPHICS: Remove 3Bpp destination support in crossBlit. --- graphics/conversion.cpp | 31 +------------------------------ graphics/conversion.h | 1 + 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/graphics/conversion.cpp b/graphics/conversion.cpp index 7b6bda30a44..84f9bcbb9cc 100644 --- a/graphics/conversion.cpp +++ b/graphics/conversion.cpp @@ -56,6 +56,7 @@ bool crossBlit(byte *dst, const byte *src, const Graphics::PixelFormat &dstFmt, const Graphics::PixelFormat &srcFmt) { // Error out if conversion is impossible if ((srcFmt.bytesPerPixel == 1) || (dstFmt.bytesPerPixel == 1) + || (dstFmt.bytesPerPixel == 3) || (!srcFmt.bytesPerPixel) || (!dstFmt.bytesPerPixel) || (srcFmt.bytesPerPixel > dstFmt.bytesPerPixel)) return false; @@ -84,36 +85,6 @@ bool crossBlit(byte *dst, const byte *src, // TODO: optimized cases for dstDelta of 0 if (dstFmt.bytesPerPixel == 2) { crossBlitLogic(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); - } else if (dstFmt.bytesPerPixel == 3) { - uint32 color; - uint8 r, g, b, a; - uint8 *col = (uint8 *) &color; -#ifdef SCUMM_BIG_ENDIAN - col++; -#endif - if (srcFmt.bytesPerPixel == 2) { - for (uint y = 0; y < h; ++y) { - for (uint 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; - } - } else { - for (uint y = 0; y < h; ++y) { - for (uint 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; - } - } } else if (dstFmt.bytesPerPixel == 4) { if (srcFmt.bytesPerPixel == 2) { crossBlitLogic(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); diff --git a/graphics/conversion.h b/graphics/conversion.h index c25f413fab4..8fcb6ba12d8 100644 --- a/graphics/conversion.h +++ b/graphics/conversion.h @@ -59,6 +59,7 @@ 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 Blitting to a 3Bpp destination is not supported * @note This implementation currently arbitrarily requires that the * destination's format have at least as high a bytedepth as * the source's. From 3a55adbf5eaf2d80b48de4ff680452996cc3d1a8 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sat, 14 Jul 2012 05:09:19 +0200 Subject: [PATCH 4/9] GRAPHICS: Fix a bug in crossBlit with 3Bpp source. Formerly it added 2 to the source pixel pointer instead of 3. --- graphics/conversion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphics/conversion.cpp b/graphics/conversion.cpp index 84f9bcbb9cc..db99679d416 100644 --- a/graphics/conversion.cpp +++ b/graphics/conversion.cpp @@ -96,7 +96,7 @@ bool crossBlit(byte *dst, const byte *src, col++; #endif for (uint y = 0; y < h; ++y) { - for (uint x = 0; x < w; ++x, src += 2, dst += 4) { + for (uint x = 0; x < w; ++x, src += 3, dst += 4) { memcpy(col, src, 3); srcFmt.colorToARGB(color, a, r, g, b); color = dstFmt.ARGBToColor(a, r, g, b); From 0d78d46a0e82af81681727a94e01ff5f309887fa Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sat, 14 Jul 2012 05:12:31 +0200 Subject: [PATCH 5/9] GRAPHICS: Remove crossBlit's dstBpp >= srcBpp limitation. --- graphics/conversion.cpp | 54 ++++++++++++++++++++++++++--------------- graphics/conversion.h | 3 --- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/graphics/conversion.cpp b/graphics/conversion.cpp index db99679d416..ece1f759d94 100644 --- a/graphics/conversion.cpp +++ b/graphics/conversion.cpp @@ -22,6 +22,8 @@ #include "graphics/conversion.h" #include "graphics/pixelformat.h" +#include "common/endian.h" + namespace Graphics { // TODO: YUV to RGB conversion function @@ -47,6 +49,30 @@ FORCEINLINE void crossBlitLogic(byte *dst, const byte *src, const uint w, const } } +template +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); + color = dstFmt.ARGBToColor(a, r, g, b); + *(DstColor *)dst = color; + src += 3; + dst += sizeof(DstColor); + } + src += srcDelta; + dst += dstDelta; + } +} + } // End of anonymous namespace // Function to blit a rect from one color format to another @@ -57,8 +83,7 @@ bool crossBlit(byte *dst, const byte *src, // Error out if conversion is impossible if ((srcFmt.bytesPerPixel == 1) || (dstFmt.bytesPerPixel == 1) || (dstFmt.bytesPerPixel == 3) - || (!srcFmt.bytesPerPixel) || (!dstFmt.bytesPerPixel) - || (srcFmt.bytesPerPixel > dstFmt.bytesPerPixel)) + || (!srcFmt.bytesPerPixel) || (!dstFmt.bytesPerPixel)) return false; // Don't perform unnecessary conversion @@ -84,27 +109,18 @@ bool crossBlit(byte *dst, const byte *src, // TODO: optimized cases for dstDelta of 0 if (dstFmt.bytesPerPixel == 2) { - crossBlitLogic(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); + if (srcFmt.bytesPerPixel == 2) { + crossBlitLogic(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); + } else if (srcFmt.bytesPerPixel == 3) { + crossBlitLogic3BppSource(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); + } else { + crossBlitLogic(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); + } } else if (dstFmt.bytesPerPixel == 4) { if (srcFmt.bytesPerPixel == 2) { crossBlitLogic(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } else if (srcFmt.bytesPerPixel == 3) { - 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, src += 3, 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; - } + crossBlitLogic3BppSource(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } else { crossBlitLogic(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } diff --git a/graphics/conversion.h b/graphics/conversion.h index 8fcb6ba12d8..0dce3cf2793 100644 --- a/graphics/conversion.h +++ b/graphics/conversion.h @@ -60,9 +60,6 @@ inline static void RGB2YUV(byte r, byte g, byte b, byte &y, byte &u, byte &v) { * false if there is an error. * * @note Blitting to a 3Bpp destination is not supported - * @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. * From a92a509ac844fdad796748848d1143b27547173f Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sat, 14 Jul 2012 05:35:20 +0200 Subject: [PATCH 6/9] GRAPHICS: Allow in-place conversion with any color formats in crossBlit. --- graphics/conversion.cpp | 70 +++++++++++++++++++++++++++++++---------- graphics/conversion.h | 9 ++++-- 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/graphics/conversion.cpp b/graphics/conversion.cpp index ece1f759d94..27a44f4ed5b 100644 --- a/graphics/conversion.cpp +++ b/graphics/conversion.cpp @@ -30,7 +30,7 @@ namespace Graphics { namespace { -template +template 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) { @@ -41,15 +41,27 @@ FORCEINLINE void crossBlitLogic(byte *dst, const byte *src, const uint w, const srcFmt.colorToARGB(color, a, r, g, b); color = dstFmt.ARGBToColor(a, r, g, b); *(DstColor *)dst = color; - src += sizeof(SrcColor); - dst += sizeof(DstColor); + + 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; } - src += srcDelta; - dst += dstDelta; } } -template +template 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) { @@ -65,11 +77,23 @@ FORCEINLINE void crossBlitLogic3BppSource(byte *dst, const byte *src, const uint srcFmt.colorToARGB(color, a, r, g, b); color = dstFmt.ARGBToColor(a, r, g, b); *(DstColor *)dst = color; - src += 3; - dst += sizeof(DstColor); + + if (backward) { + src -= 3; + dst -= sizeof(DstColor); + } else { + src += 3; + dst += sizeof(DstColor); + } + } + + if (backward) { + src -= srcDelta; + dst -= dstDelta; + } else { + src += srcDelta; + dst += dstDelta; } - src += srcDelta; - dst += dstDelta; } } @@ -110,19 +134,33 @@ bool crossBlit(byte *dst, const byte *src, // TODO: optimized cases for dstDelta of 0 if (dstFmt.bytesPerPixel == 2) { if (srcFmt.bytesPerPixel == 2) { - crossBlitLogic(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); + crossBlitLogic(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } else if (srcFmt.bytesPerPixel == 3) { - crossBlitLogic3BppSource(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); + crossBlitLogic3BppSource(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } else { - crossBlitLogic(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); + crossBlitLogic(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } } else if (dstFmt.bytesPerPixel == 4) { if (srcFmt.bytesPerPixel == 2) { - crossBlitLogic(dst, src, w, h, srcFmt, dstFmt, srcDelta, 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(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } else if (srcFmt.bytesPerPixel == 3) { - crossBlitLogic3BppSource(dst, src, w, h, srcFmt, dstFmt, srcDelta, 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(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } else { - crossBlitLogic(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); + crossBlitLogic(dst, src, w, h, srcFmt, dstFmt, srcDelta, dstDelta); } } else { return false; diff --git a/graphics/conversion.h b/graphics/conversion.h index 0dce3cf2793..28e64a94fbd 100644 --- a/graphics/conversion.h +++ b/graphics/conversion.h @@ -60,9 +60,12 @@ inline static void RGB2YUV(byte r, byte g, byte b, byte &y, byte &u, byte &v) { * false if there is an error. * * @note Blitting to a 3Bpp destination is not supported - * @note This can convert a rectangle in place, if the source and - * destination format have the same bytedepth. - * + * @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, const uint dstPitch, const uint srcPitch, From e8cf0adf95217d4eedec526df960a8cdd46cc28f Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sat, 14 Jul 2012 05:40:09 +0200 Subject: [PATCH 7/9] GRAPHICS: Slight cleanup in crossBlit code. --- graphics/conversion.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/graphics/conversion.cpp b/graphics/conversion.cpp index 27a44f4ed5b..fec1d240e2b 100644 --- a/graphics/conversion.cpp +++ b/graphics/conversion.cpp @@ -36,11 +36,10 @@ FORCEINLINE void crossBlitLogic(byte *dst, const byte *src, const uint w, const const uint srcDelta, const uint dstDelta) { for (uint y = 0; y < h; ++y) { for (uint x = 0; x < w; ++x) { - uint32 color = *(const SrcColor *)src; + const uint32 color = *(const SrcColor *)src; byte a, r, g, b; srcFmt.colorToARGB(color, a, r, g, b); - color = dstFmt.ARGBToColor(a, r, g, b); - *(DstColor *)dst = color; + *(DstColor *)dst = dstFmt.ARGBToColor(a, r, g, b); if (backward) { src -= sizeof(SrcColor); @@ -75,8 +74,7 @@ FORCEINLINE void crossBlitLogic3BppSource(byte *dst, const byte *src, const uint for (uint x = 0; x < w; ++x) { memcpy(col, src, 3); srcFmt.colorToARGB(color, a, r, g, b); - color = dstFmt.ARGBToColor(a, r, g, b); - *(DstColor *)dst = color; + *(DstColor *)dst = dstFmt.ARGBToColor(a, r, g, b); if (backward) { src -= 3; From eeb39592599815875a736fdbef67204c2036e935 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sat, 14 Jul 2012 06:03:04 +0200 Subject: [PATCH 8/9] GRAPHICS: Add an in-place Surface PixelFormat conversion. --- graphics/surface.cpp | 67 ++++++++++++++++++++++++++++++++++++++++++++ graphics/surface.h | 14 +++++++++ 2 files changed, 81 insertions(+) diff --git a/graphics/surface.cpp b/graphics/surface.cpp index c0f1046eae6..1604eaa4889 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 { @@ -271,6 +272,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 eb8d1ac42eb..eaf62f84eb3 100644 --- a/graphics/surface.h +++ b/graphics/surface.h @@ -134,6 +134,20 @@ struct Surface { */ void copyFrom(const Surface &surf); + /** + * 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. * From a1e56adad8eb8ed4c8de5850fb2c81af9adb9585 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Tue, 28 Aug 2012 02:25:14 +0200 Subject: [PATCH 9/9] GUI: Use in-place Surface conversion in widget code. --- gui/widget.cpp | 64 ++++++++++++++++++++++---------------------------- gui/widget.h | 4 ++-- 2 files changed, 30 insertions(+), 38 deletions(-) diff --git a/gui/widget.cpp b/gui/widget.cpp index 9046bcc9c19..c5ca628576b 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 6f710f302f6..e3f712564f0 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; };