diff options
| -rw-r--r-- | Makefile.common | 2 | ||||
| -rw-r--r-- | backends/graphics/opengl/opengl-graphics.cpp | 40 | ||||
| -rw-r--r-- | backends/graphics/openglsdl/openglsdl-graphics.cpp | 4 | ||||
| -rw-r--r-- | backends/graphics/surfacesdl/surfacesdl-graphics.cpp | 68 | ||||
| -rw-r--r-- | image/png.cpp | 67 | ||||
| -rw-r--r-- | image/png.h | 9 | 
6 files changed, 169 insertions, 21 deletions
| diff --git a/Makefile.common b/Makefile.common index a01636ec3f..dc1e0ad7bd 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 a595d076db..fc27270648 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 75e2cf8ef7..e41148062f 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 ccfcc94ab3..9594587d88 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 dffd512b88..37617a1690 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 b7ac91a3d2..7ecf68e76d 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 | 
