aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--graphics/surface.cpp67
-rw-r--r--graphics/surface.h14
2 files changed, 81 insertions, 0 deletions
diff --git a/graphics/surface.cpp b/graphics/surface.cpp
index c0f1046eae..1604eaa488 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 eb8d1ac42e..eaf62f84eb 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.
*