diff options
author | Johannes Schickel | 2012-10-08 14:18:33 -0700 |
---|---|---|
committer | Johannes Schickel | 2012-10-08 14:18:33 -0700 |
commit | c2971374cdb189dbf9f72953a970d0769648e5ae (patch) | |
tree | 90734d450f4f30677f14b3a3a2ef29a28ff35d67 /graphics | |
parent | dbb09fe984373205453ca7989f9f4aefb245bc4f (diff) | |
parent | a1e56adad8eb8ed4c8de5850fb2c81af9adb9585 (diff) | |
download | scummvm-rg350-c2971374cdb189dbf9f72953a970d0769648e5ae.tar.gz scummvm-rg350-c2971374cdb189dbf9f72953a970d0769648e5ae.tar.bz2 scummvm-rg350-c2971374cdb189dbf9f72953a970d0769648e5ae.zip |
Merge pull request #257 from lordhoto/graphics-conversion
Extend crossBlit for abitrary (in-place) conversions and add a in-place conversion to Surface
Diffstat (limited to 'graphics')
-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 |
4 files changed, 203 insertions, 99 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. * |