Index: source/ps/ScreenshotWriter.h
===================================================================
--- source/ps/ScreenshotWriter.h
+++ source/ps/ScreenshotWriter.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2019 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#ifndef INCLUDED_SCREENSHOTWRITER
+#define INCLUDED_SCREENSHOTWRITER
+
+#include "lib/file/vfs/vfs.h"
+#include "lib/ogl.h"
+
+struct Tex;
+
+class CScreenshotWriter
+{
+public:
+ /**
+ * identifies the file format that is to be written
+ * (case-insensitive). examples: "bmp", "png", "jpg".
+ * BMP is good for quick output at the expense of large files.
+ */
+ explicit CScreenshotWriter(const VfsPath& extension);
+
+ ~CScreenshotWriter() = default;
+
+ void WriteScreenshot();
+
+ /**
+ * Similar to WriteScreenshot, but generates an image of size 640*tiles x 480*tiles.
+ */
+ void WriteBigScreenshot(int tiles);
+
+private:
+ void WriteTextureToFile(Tex* texture);
+
+ VfsPath m_Path;
+
+ GLenum m_GLFormat;
+ size_t m_TexFlags;
+ size_t m_Bpp;
+};
+
+#endif // INCLUDED_SCREENSHOTWRITER
Index: source/ps/ScreenshotWriter.cpp
===================================================================
--- source/ps/ScreenshotWriter.cpp
+++ source/ps/ScreenshotWriter.cpp
@@ -0,0 +1,216 @@
+/* Copyright (C) 2019 Wildfire Games.
+ * This file is part of 0 A.D.
+ *
+ * 0 A.D. is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 0 A.D. is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with 0 A.D. If not, see .
+ */
+
+#include "precompiled.h"
+
+#include "ps/ScreenshotWriter.h"
+
+#include "graphics/Camera.h"
+#include "graphics/GameView.h"
+#include "maths/MathUtil.h"
+#include "i18n/L10n.h"
+#include "lib/allocators/shared_ptr.h"
+#include "lib/tex/tex.h"
+#include "lib/utf8.h"
+#include "ps/GameSetup/Config.h"
+#include "ps/GameSetup/GameSetup.h"
+#include "ps/Game.h"
+#include "ps/CLogger.h"
+#include "ps/Filesystem.h"
+#include "ps/Util.h"
+#include "ps/VideoMode.h"
+#include "renderer/Renderer.h"
+
+static size_t s_nextScreenshotNumber;
+
+CScreenshotWriter::CScreenshotWriter(const VfsPath& extension)
+ : m_Bpp(24)
+{
+ // Get next available numbered filename
+ // note: %04d -> always 4 digits, so sorting by filename works correctly.
+ const VfsPath basenameFormat(L"screenshots/screenshot%04d");
+ const VfsPath filenameFormat = basenameFormat.ChangeExtension(extension);
+ vfs::NextNumberedFilename(g_VFS, filenameFormat, s_nextScreenshotNumber, m_Path);
+
+ m_GLFormat = GL_RGB;
+ m_TexFlags = TEX_BOTTOM_UP;
+ // We want writing BMP to be as fast as possible,
+ // so read data from OpenGL in BMP format to obviate conversion.
+ if(extension == L".bmp")
+ {
+#if !CONFIG2_GLES // GLES doesn't support BGR
+ m_GLFormat = GL_BGR;
+ m_TexFlags |= TEX_BGR;
+#endif
+ }
+}
+
+void CScreenshotWriter::WriteTextureToFile(Tex* texture)
+{
+ if (tex_write(texture, m_Path) == INFO::OK)
+ {
+ OsPath realPath;
+ g_VFS->GetRealPath(m_Path, realPath);
+
+ LOGMESSAGERENDER(g_L10n.Translate("Screenshot written to '%s'"), realPath.string8());
+
+ debug_printf(
+ CStr(g_L10n.Translate("Screenshot written to '%s'") + "\n").c_str(),
+ realPath.string8().c_str());
+ }
+ else
+ LOGERROR("Error writing screenshot to '%s'", m_Path.string8());
+}
+
+void CScreenshotWriter::WriteScreenshot()
+{
+ // Hide log messages and re-render
+ RenderLogger(false);
+ Render();
+ RenderLogger(true);
+
+ const size_t w = static_cast(g_xres);
+ const size_t h = static_cast(g_yres);
+ const size_t img_size = w * h * m_Bpp / 8;
+ const size_t hdr_size = tex_hdr_size(m_Path);
+
+ shared_ptr buf;
+ AllocateAligned(buf, hdr_size + img_size, maxSectorSize);
+
+ GLvoid* img = buf.get() + hdr_size;
+ Tex t;
+ if (t.wrap(w, h, m_Bpp, m_TexFlags, buf, hdr_size) < 0)
+ return;
+ glReadPixels(0, 0, (GLsizei)w, (GLsizei)h, m_GLFormat, GL_UNSIGNED_BYTE, img);
+
+ WriteTextureToFile(&t);
+}
+
+void CScreenshotWriter::WriteBigScreenshot(int tiles)
+{
+ // If the game hasn't started yet then use WriteScreenshot to generate the image.
+ if(g_Game == nullptr)
+ {
+ WriteScreenshot();
+ return;
+ }
+
+ // Slightly ugly and inflexible: Always draw 640*480 tiles onto the screen, and
+ // hope the screen is actually large enough for that.
+ const int tile_w = 640, tile_h = 480;
+ ENSURE(g_xres >= tile_w && g_yres >= tile_h);
+ const size_t tile_size = tile_w * tile_h * m_Bpp / 8;
+ void* tile_data = malloc(tile_size);
+ if(!tile_data)
+ {
+ WARN_IF_ERR(ERR::NO_MEM);
+ return;
+ }
+
+ const int img_w = tile_w*tiles, img_h = tile_h*tiles;
+ const size_t img_size = img_w * img_h * m_Bpp / 8;
+ const size_t hdr_size = tex_hdr_size(m_Path);
+ shared_ptr img_buf;
+ AllocateAligned(img_buf, hdr_size + img_size, maxSectorSize);
+
+ Tex t;
+ GLvoid* img = img_buf.get() + hdr_size;
+ if(t.wrap(img_w, img_h, m_Bpp, m_TexFlags, img_buf, hdr_size) < 0)
+ {
+ free(tile_data);
+ return;
+ }
+
+ ogl_WarnIfError();
+
+ CCamera oldCamera = *g_Game->GetView()->GetCamera();
+
+ // Resize various things so that the sizes and aspect ratios are correct
+ {
+ g_Renderer.Resize(tile_w, tile_h);
+ SViewPort vp = { 0, 0, tile_w, tile_h };
+ g_Game->GetView()->SetViewport(vp);
+ }
+
+#if !CONFIG2_GLES
+ // Temporarily move everything onto the front buffer, so the user can
+ // see the exciting progress as it renders (and can tell when it's finished).
+ // (It doesn't just use SwapBuffers, because it doesn't know whether to
+ // call the SDL version or the Atlas version.)
+ GLint oldReadBuffer, oldDrawBuffer;
+ glGetIntegerv(GL_READ_BUFFER, &oldReadBuffer);
+ glGetIntegerv(GL_DRAW_BUFFER, &oldDrawBuffer);
+ glDrawBuffer(GL_FRONT);
+ glReadBuffer(GL_FRONT);
+#endif
+
+ // Hide the cursor
+ CStrW oldCursor = g_CursorName;
+ g_CursorName = L"";
+
+ // Render each tile
+ CMatrix3D projection;
+ projection.SetIdentity();
+ for (int tile_y = 0; tile_y < tiles; ++tile_y)
+ {
+ for (int tile_x = 0; tile_x < tiles; ++tile_x)
+ {
+ // Adjust the camera to render the appropriate region
+ if (oldCamera.GetProjectionType() == CCamera::PERSPECTIVE)
+ {
+ projection.SetPerspectiveTile(oldCamera.GetFOV(), oldCamera.GetAspectRatio(), oldCamera.GetNearPlane(), oldCamera.GetFarPlane(), tiles, tile_x, tile_y);
+ }
+ g_Game->GetView()->GetCamera()->SetProjection(projection);
+
+ RenderLogger(false);
+ RenderGui(false);
+ Render();
+ RenderGui(true);
+ RenderLogger(true);
+
+ // Copy the tile pixels into the main image
+ glReadPixels(0, 0, tile_w, tile_h, m_GLFormat, GL_UNSIGNED_BYTE, tile_data);
+ for (int y = 0; y < tile_h; ++y)
+ {
+ void* dest = static_cast(img) + ((tile_y*tile_h + y) * img_w + (tile_x*tile_w)) * m_Bpp / 8;
+ void* src = static_cast(tile_data) + y * tile_w * m_Bpp / 8;
+ memcpy(dest, src, tile_w * m_Bpp / 8);
+ }
+ }
+ }
+
+ // Restore the old cursor
+ g_CursorName = oldCursor;
+
+#if !CONFIG2_GLES
+ // Restore the buffer settings
+ glDrawBuffer(oldDrawBuffer);
+ glReadBuffer(oldReadBuffer);
+#endif
+
+ // Restore the viewport settings
+ {
+ g_Renderer.Resize(g_xres, g_yres);
+ SViewPort vp = { 0, 0, g_xres, g_yres };
+ g_Game->GetView()->SetViewport(vp);
+ g_Game->GetView()->GetCamera()->SetProjectionFromCamera(oldCamera);
+ }
+
+ WriteTextureToFile(&t);
+
+ free(tile_data);
+}
Index: source/ps/Util.cpp
===================================================================
--- source/ps/Util.cpp
+++ source/ps/Util.cpp
@@ -43,6 +43,7 @@
#include "ps/Game.h"
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
+#include "ps/ScreenshotWriter.h"
#include "ps/VideoMode.h"
#include "renderer/Renderer.h"
#include "maths/MathUtil.h"
@@ -228,208 +229,16 @@
return path;
}
-static size_t s_nextScreenshotNumber;
-
-// identifies the file format that is to be written
-// (case-insensitive). examples: "bmp", "png", "jpg".
-// BMP is good for quick output at the expense of large files.
void WriteScreenshot(const VfsPath& extension)
{
- // get next available numbered filename
- // note: %04d -> always 4 digits, so sorting by filename works correctly.
- const VfsPath basenameFormat(L"screenshots/screenshot%04d");
- const VfsPath filenameFormat = basenameFormat.ChangeExtension(extension);
- VfsPath filename;
- vfs::NextNumberedFilename(g_VFS, filenameFormat, s_nextScreenshotNumber, filename);
-
- const size_t w = (size_t)g_xres, h = (size_t)g_yres;
- const size_t bpp = 24;
- GLenum fmt = GL_RGB;
- int flags = TEX_BOTTOM_UP;
- // we want writing BMP to be as fast as possible,
- // so read data from OpenGL in BMP format to obviate conversion.
- if(extension == L".bmp")
- {
-#if !CONFIG2_GLES // GLES doesn't support BGR
- fmt = GL_BGR;
- flags |= TEX_BGR;
-#endif
- }
-
- // Hide log messages and re-render
- RenderLogger(false);
- Render();
- RenderLogger(true);
-
- const size_t img_size = w * h * bpp/8;
- const size_t hdr_size = tex_hdr_size(filename);
- shared_ptr buf;
- AllocateAligned(buf, hdr_size+img_size, maxSectorSize);
- GLvoid* img = buf.get() + hdr_size;
- Tex t;
- if(t.wrap(w, h, bpp, flags, buf, hdr_size) < 0)
- return;
- glReadPixels(0, 0, (GLsizei)w, (GLsizei)h, fmt, GL_UNSIGNED_BYTE, img);
-
- if (tex_write(&t, filename) == INFO::OK)
- {
- OsPath realPath;
- g_VFS->GetRealPath(filename, realPath);
-
- LOGMESSAGERENDER(g_L10n.Translate("Screenshot written to '%s'"), realPath.string8());
-
- debug_printf(
- CStr(g_L10n.Translate("Screenshot written to '%s'") + "\n").c_str(),
- realPath.string8().c_str());
- }
- else
- LOGERROR("Error writing screenshot to '%s'", filename.string8());
+ CScreenshotWriter screenshotWriter(extension);
+ screenshotWriter.WriteScreenshot();
}
-
-
-// Similar to WriteScreenshot, but generates an image of size 640*tiles x 480*tiles.
void WriteBigScreenshot(const VfsPath& extension, int tiles)
{
- // If the game hasn't started yet then use WriteScreenshot to generate the image.
- if(g_Game == NULL){ WriteScreenshot(L".bmp"); return; }
-
- // get next available numbered filename
- // note: %04d -> always 4 digits, so sorting by filename works correctly.
- const VfsPath basenameFormat(L"screenshots/screenshot%04d");
- const VfsPath filenameFormat = basenameFormat.ChangeExtension(extension);
- VfsPath filename;
- vfs::NextNumberedFilename(g_VFS, filenameFormat, s_nextScreenshotNumber, filename);
-
- // Slightly ugly and inflexible: Always draw 640*480 tiles onto the screen, and
- // hope the screen is actually large enough for that.
- const int tile_w = 640, tile_h = 480;
- ENSURE(g_xres >= tile_w && g_yres >= tile_h);
-
- const int img_w = tile_w*tiles, img_h = tile_h*tiles;
- const int bpp = 24;
- GLenum fmt = GL_RGB;
- int flags = TEX_BOTTOM_UP;
- // we want writing BMP to be as fast as possible,
- // so read data from OpenGL in BMP format to obviate conversion.
- if(extension == L".bmp")
- {
-#if !CONFIG2_GLES // GLES doesn't support BGR
- fmt = GL_BGR;
- flags |= TEX_BGR;
-#endif
- }
-
- const size_t img_size = img_w * img_h * bpp/8;
- const size_t tile_size = tile_w * tile_h * bpp/8;
- const size_t hdr_size = tex_hdr_size(filename);
- void* tile_data = malloc(tile_size);
- if(!tile_data)
- {
- WARN_IF_ERR(ERR::NO_MEM);
- return;
- }
- shared_ptr img_buf;
- AllocateAligned(img_buf, hdr_size+img_size, maxSectorSize);
-
- Tex t;
- GLvoid* img = img_buf.get() + hdr_size;
- if(t.wrap(img_w, img_h, bpp, flags, img_buf, hdr_size) < 0)
- {
- free(tile_data);
- return;
- }
-
- ogl_WarnIfError();
-
- CCamera oldCamera = *g_Game->GetView()->GetCamera();
-
- // Resize various things so that the sizes and aspect ratios are correct
- {
- g_Renderer.Resize(tile_w, tile_h);
- SViewPort vp = { 0, 0, tile_w, tile_h };
- g_Game->GetView()->SetViewport(vp);
- }
-
-#if !CONFIG2_GLES
- // Temporarily move everything onto the front buffer, so the user can
- // see the exciting progress as it renders (and can tell when it's finished).
- // (It doesn't just use SwapBuffers, because it doesn't know whether to
- // call the SDL version or the Atlas version.)
- GLint oldReadBuffer, oldDrawBuffer;
- glGetIntegerv(GL_READ_BUFFER, &oldReadBuffer);
- glGetIntegerv(GL_DRAW_BUFFER, &oldDrawBuffer);
- glDrawBuffer(GL_FRONT);
- glReadBuffer(GL_FRONT);
-#endif
-
- // Hide the cursor
- CStrW oldCursor = g_CursorName;
- g_CursorName = L"";
-
- // Render each tile
- CMatrix3D projection;
- projection.SetIdentity();
- for (int tile_y = 0; tile_y < tiles; ++tile_y)
- {
- for (int tile_x = 0; tile_x < tiles; ++tile_x)
- {
- // Adjust the camera to render the appropriate region
- if (oldCamera.GetProjectionType() == CCamera::PERSPECTIVE)
- {
- projection.SetPerspectiveTile(oldCamera.GetFOV(), oldCamera.GetAspectRatio(), oldCamera.GetNearPlane(), oldCamera.GetFarPlane(), tiles, tile_x, tile_y);
- }
- g_Game->GetView()->GetCamera()->SetProjection(projection);
-
- RenderLogger(false);
- RenderGui(false);
- Render();
- RenderGui(true);
- RenderLogger(true);
-
- // Copy the tile pixels into the main image
- glReadPixels(0, 0, tile_w, tile_h, fmt, GL_UNSIGNED_BYTE, tile_data);
- for (int y = 0; y < tile_h; ++y)
- {
- void* dest = (char*)img + ((tile_y*tile_h + y) * img_w + (tile_x*tile_w)) * bpp/8;
- void* src = (char*)tile_data + y * tile_w * bpp/8;
- memcpy(dest, src, tile_w * bpp/8);
- }
- }
- }
-
- // Restore the old cursor
- g_CursorName = oldCursor;
-
-#if !CONFIG2_GLES
- // Restore the buffer settings
- glDrawBuffer(oldDrawBuffer);
- glReadBuffer(oldReadBuffer);
-#endif
-
- // Restore the viewport settings
- {
- g_Renderer.Resize(g_xres, g_yres);
- SViewPort vp = { 0, 0, g_xres, g_yres };
- g_Game->GetView()->SetViewport(vp);
- g_Game->GetView()->GetCamera()->SetProjectionFromCamera(oldCamera);
- }
-
- if (tex_write(&t, filename) == INFO::OK)
- {
- OsPath realPath;
- g_VFS->GetRealPath(filename, realPath);
-
- LOGMESSAGERENDER(g_L10n.Translate("Screenshot written to '%s'"), realPath.string8());
-
- debug_printf(
- CStr(g_L10n.Translate("Screenshot written to '%s'") + "\n").c_str(),
- realPath.string8().c_str());
- }
- else
- LOGERROR("Error writing screenshot to '%s'", filename.string8());
-
- free(tile_data);
+ CScreenshotWriter screenshotWriter(extension);
+ screenshotWriter.WriteBigScreenshot(tiles);
}
std::string Hexify(const std::string& s)