aboutsummaryrefslogtreecommitdiff
path: root/graphics
diff options
context:
space:
mode:
authorJohannes Schickel2012-10-08 14:18:33 -0700
committerJohannes Schickel2012-10-08 14:18:33 -0700
commitc2971374cdb189dbf9f72953a970d0769648e5ae (patch)
tree90734d450f4f30677f14b3a3a2ef29a28ff35d67 /graphics
parentdbb09fe984373205453ca7989f9f4aefb245bc4f (diff)
parenta1e56adad8eb8ed4c8de5850fb2c81af9adb9585 (diff)
downloadscummvm-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.cpp202
-rw-r--r--graphics/conversion.h19
-rw-r--r--graphics/surface.cpp67
-rw-r--r--graphics/surface.h14
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.
*