[Scummvm-git-logs] scummvm master -> fa0bb7dd5a60c8f323ecbd5e190ad705bec3e934

csnover csnover at users.noreply.github.com
Sun May 21 22:56:17 CEST 2017


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
fa0bb7dd5a BACKENDS: Compress screenshots using PNG if available


Commit: fa0bb7dd5a60c8f323ecbd5e190ad705bec3e934
    https://github.com/scummvm/scummvm/commit/fa0bb7dd5a60c8f323ecbd5e190ad705bec3e934
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-05-21T15:55:39-05:00

Commit Message:
BACKENDS: Compress screenshots using PNG if available

Closes gh-948.

Changed paths:
    Makefile.common
    backends/graphics/opengl/opengl-graphics.cpp
    backends/graphics/openglsdl/openglsdl-graphics.cpp
    backends/graphics/surfacesdl/surfacesdl-graphics.cpp
    image/png.cpp
    image/png.h


diff --git a/Makefile.common b/Makefile.common
index a01636e..dc1e0ad 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -24,8 +24,8 @@ MODULES += \
 	backends \
 	engines \
 	video \
-	graphics \
 	image \
+	graphics \
 	audio \
 	common \
 	po
diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp
index a595d07..fc27270 100644
--- a/backends/graphics/opengl/opengl-graphics.cpp
+++ b/backends/graphics/opengl/opengl-graphics.cpp
@@ -28,6 +28,7 @@
 #include "backends/graphics/opengl/pipelines/shader.h"
 #include "backends/graphics/opengl/shader.h"
 
+#include "common/array.h"
 #include "common/textconsole.h"
 #include "common/translation.h"
 #include "common/algorithm.h"
@@ -43,6 +44,10 @@
 #include "graphics/font.h"
 #endif
 
+#ifdef USE_PNG
+#include "image/png.h"
+#endif
+
 namespace OpenGL {
 
 OpenGLGraphicsManager::OpenGLGraphicsManager()
@@ -1311,33 +1316,35 @@ bool OpenGLGraphicsManager::saveScreenshot(const Common::String &filename) const
 	// Since we use a 3 byte per pixel mode, we can use width % 4 here, since
 	// it is equal to 4 - (width * 3) % 4. (4 - (width * Bpp) % 4, is the
 	// usual way of computing the padding bytes required).
+	// GL_PACK_ALIGNMENT is 4, so this line padding is required for PNG too
 	const uint linePaddingSize = width % 4;
 	const uint lineSize        = width * 3 + linePaddingSize;
 
-	// Allocate memory for screenshot
-	uint8 *pixels = new uint8[lineSize * height];
+	Common::DumpFile out;
+	if (!out.open(filename)) {
+		return false;
+	}
 
-	// Get pixel data from OpenGL buffer
-	GL_CALL(glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels));
+	Common::Array<uint8> pixels;
+	pixels.resize(lineSize * height);
+	GL_CALL(glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, &pixels.front()));
 
+#ifdef USE_PNG
+	const Graphics::PixelFormat format(3, 8, 8, 8, 0, 16, 8, 0, 0);
+	Graphics::Surface data;
+	data.init(width, height, lineSize, &pixels.front(), format);
+	return Image::writePNG(out, data, true);
+#else
 	// BMP stores as BGR. Since we can't assume that GL_BGR is supported we
 	// will swap the components from the RGB we read to BGR on our own.
 	for (uint y = height; y-- > 0;) {
-		uint8 *line = pixels + y * lineSize;
+		uint8 *line = &pixels.front() + y * lineSize;
 
 		for (uint x = width; x > 0; --x, line += 3) {
 			SWAP(line[0], line[2]);
 		}
 	}
 
-	// Open file
-	Common::DumpFile out;
-	if (!out.open(filename)) {
-		delete[] pixels;
-		return false;
-	}
-
-	// Write BMP header
 	out.writeByte('B');
 	out.writeByte('M');
 	out.writeUint32LE(height * lineSize + 54);
@@ -1354,13 +1361,10 @@ bool OpenGLGraphicsManager::saveScreenshot(const Common::String &filename) const
 	out.writeUint32LE(0);
 	out.writeUint32LE(0);
 	out.writeUint32LE(0);
+	out.write(&pixels.front(), pixels.size());
 
-	// Write pixel data to BMP
-	out.write(pixels, lineSize * height);
-
-	// Free allocated memory
-	delete[] pixels;
 	return true;
+#endif
 }
 
 } // End of namespace OpenGL
diff --git a/backends/graphics/openglsdl/openglsdl-graphics.cpp b/backends/graphics/openglsdl/openglsdl-graphics.cpp
index 75e2cf8..e411480 100644
--- a/backends/graphics/openglsdl/openglsdl-graphics.cpp
+++ b/backends/graphics/openglsdl/openglsdl-graphics.cpp
@@ -633,7 +633,11 @@ bool OpenGLSdlGraphicsManager::notifyEvent(const Common::Event &event) {
 				for (int n = 0;; n++) {
 					SDL_RWops *file;
 
+#ifdef USE_PNG
+					filename = Common::String::format("scummvm%05d.png", n);
+#else
 					filename = Common::String::format("scummvm%05d.bmp", n);
+#endif
 
 					file = SDL_RWFromFile((screenshotsPath + filename).c_str(), "r");
 
diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
index ccfcc94..9594587 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
@@ -42,6 +42,10 @@
 #include "graphics/scaler/aspect.h"
 #include "graphics/surface.h"
 #include "gui/EventRecorder.h"
+#ifdef USE_PNG
+#include "common/file.h"
+#include "image/png.h"
+#endif
 
 static const OSystem::GraphicsMode s_supportedShaders[] = {
 	{"NONE", "Normal (no shader)", 0},
@@ -1321,8 +1325,66 @@ void SurfaceSdlGraphicsManager::internUpdateScreen() {
 bool SurfaceSdlGraphicsManager::saveScreenshot(const char *filename) {
 	assert(_hwscreen != NULL);
 
-	Common::StackLock lock(_graphicsMutex);	// Lock the mutex until this function ends
+	Common::StackLock lock(_graphicsMutex);
+#ifdef USE_PNG
+	Common::DumpFile out;
+	if (!out.open(filename)) {
+		return false;
+	}
+
+#if SDL_VERSION_ATLEAST(2, 0, 0)
+	SDL_Surface *rgbScreen = SDL_ConvertSurfaceFormat(_hwscreen, SDL_PIXELFORMAT_RGB24, 0);
+#else
+	// This block of code was taken mostly as-is from SDL 1.2's SDL_SaveBMP_RW
+	SDL_Surface *rgbScreen = SDL_CreateRGBSurface(SDL_SWSURFACE,
+												  _hwscreen->w,
+												  _hwscreen->h,
+												  24,
+#ifdef SCUMM_LITTLE_ENDIAN
+												  0x0000FF, 0x00FF00, 0xFF0000,
+#else
+												  0xFF0000, 0x00FF00, 0x0000FF,
+#endif
+												  0);
+	if (rgbScreen == nullptr) {
+		warning("Could not create RGB24 surface");
+		return false;
+	}
+
+	SDL_Rect bounds;
+	bounds.x = bounds.y = 0;
+	bounds.w = _hwscreen->w;
+	bounds.h = _hwscreen->h;
+	if (SDL_LowerBlit(_hwscreen, &bounds, rgbScreen, &bounds) < 0) {
+		SDL_FreeSurface(rgbScreen);
+		rgbScreen = nullptr;
+	}
+#endif
+
+	if (rgbScreen == nullptr) {
+		warning("Could not convert hardware surface to RGB24");
+		return false;
+	}
+
+	int result = SDL_LockSurface(rgbScreen);
+	if (result < 0) {
+		warning("Could not lock RGB surface");
+		SDL_FreeSurface(rgbScreen);
+		return false;
+	}
+
+	const Graphics::PixelFormat format(3, 8, 8, 8, 0, 16, 8, 0, 0);
+	Graphics::Surface data;
+	data.init(rgbScreen->w, rgbScreen->h, rgbScreen->pitch, rgbScreen->pixels, format);
+	const bool success = Image::writePNG(out, data);
+
+	SDL_UnlockSurface(rgbScreen);
+	SDL_FreeSurface(rgbScreen);
+
+	return success;
+#else
 	return SDL_SaveBMP(_hwscreen, filename) == 0;
+#endif
 }
 
 void SurfaceSdlGraphicsManager::setFullscreenMode(bool enable) {
@@ -2534,7 +2596,11 @@ bool SurfaceSdlGraphicsManager::notifyEvent(const Common::Event &event) {
 			for (int n = 0;; n++) {
 				SDL_RWops *file;
 
+#ifdef USE_PNG
+				filename = Common::String::format("scummvm%05d.png", n);
+#else
 				filename = Common::String::format("scummvm%05d.bmp", n);
+#endif
 
 				file = SDL_RWFromFile((screenshotsPath + filename).c_str(), "r");
 
diff --git a/image/png.cpp b/image/png.cpp
index dffd512..37617a1 100644
--- a/image/png.cpp
+++ b/image/png.cpp
@@ -34,6 +34,7 @@
 #include "graphics/pixelformat.h"
 #include "graphics/surface.h"
 
+#include "common/array.h"
 #include "common/stream.h"
 
 namespace Image {
@@ -65,12 +66,24 @@ void pngWarning(png_structp pngptr, png_const_charp warningMsg) {
 	warning("%s", warningMsg);
 }
 
-// libpng-I/O-helper:
+// libpng-I/O-helpers:
 void pngReadFromStream(png_structp pngPtr, png_bytep data, png_size_t length) {
 	void *readIOptr = png_get_io_ptr(pngPtr);
 	Common::SeekableReadStream *stream = (Common::SeekableReadStream *)readIOptr;
 	stream->read(data, length);
 }
+
+void pngWriteToStream(png_structp pngPtr, png_bytep data, png_size_t length) {
+	void *writeIOptr = png_get_io_ptr(pngPtr);
+	Common::WriteStream *stream = (Common::WriteStream *)writeIOptr;
+	stream->write(data, length);
+}
+
+void pngFlushStream(png_structp pngPtr) {
+	void *writeIOptr = png_get_io_ptr(pngPtr);
+	Common::WriteStream *stream = (Common::WriteStream *)writeIOptr;
+	stream->flush();
+}
 #endif
 
 /*
@@ -232,4 +245,56 @@ bool PNGDecoder::loadStream(Common::SeekableReadStream &stream) {
 #endif
 }
 
+bool writePNG(Common::WriteStream &out, const Graphics::Surface &input, const bool bottomUp) {
+#ifdef USE_PNG
+	const Graphics::PixelFormat requiredFormat(3, 8, 8, 8, 0, 16, 8, 0, 0);
+
+	if (input.format != requiredFormat) {
+		warning("Cannot currently write PNG with pixel format other than %s", requiredFormat.toString().c_str());
+		return false;
+	}
+
+	png_structp pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+	if (!pngPtr) {
+		return false;
+	}
+	png_infop infoPtr = png_create_info_struct(pngPtr);
+	if (!infoPtr) {
+		png_destroy_write_struct(&pngPtr, NULL);
+		return false;
+	}
+	png_infop endInfo = png_create_info_struct(pngPtr);
+	if (!endInfo) {
+		png_destroy_write_struct(&pngPtr, &infoPtr);
+		return false;
+	}
+
+	png_set_error_fn(pngPtr, NULL, pngError, pngWarning);
+	// TODO: The manual says errors should be handled via setjmp
+
+	png_set_write_fn(pngPtr, &out, pngWriteToStream, pngFlushStream);
+
+	png_set_IHDR(pngPtr, infoPtr, input.w, input.h, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+	Common::Array<const uint8 *> rows;
+	rows.reserve(input.h);
+	if (bottomUp) {
+		for (uint y = input.h; y-- > 0;) {
+			rows.push_back((const uint8 *)input.getBasePtr(0, y));
+		}
+	} else {
+		for (uint y = 0; y < input.h; ++y) {
+			rows.push_back((const uint8 *)input.getBasePtr(0, y));
+		}
+	}
+
+	png_set_rows(pngPtr, infoPtr, const_cast<uint8 **>(&rows.front()));
+	png_write_png(pngPtr, infoPtr, 0, NULL);
+	png_destroy_write_struct(&pngPtr, &infoPtr);
+	return true;
+#else
+	return false;
+#endif
+}
+
 } // End of namespace Image
diff --git a/image/png.h b/image/png.h
index b7ac91a..7ecf68e 100644
--- a/image/png.h
+++ b/image/png.h
@@ -37,6 +37,7 @@
 
 namespace Common {
 class SeekableReadStream;
+class WriteStream;
 }
 
 namespace Graphics {
@@ -62,6 +63,14 @@ private:
 	Graphics::Surface *_outputSurface;
 };
 
+/**
+ * Outputs a compressed PNG stream of the given input surface.
+ *
+ * @param bottomUp Flip the vertical axis so pixel data is drawn from the
+ * bottom up, instead of from the top down.
+ */
+bool writePNG(Common::WriteStream &out, const Graphics::Surface &input, const bool bottomUp = false);
+
 } // End of namespace Image
 
 #endif





More information about the Scummvm-git-logs mailing list