Changeset View
Changeset View
Standalone View
Standalone View
source/ps/ScreenshotWriter.cpp
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
/* 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 <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#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) | |||||
{ | |||||
elexis: could be done in the header | |||||
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<size_t>(g_xres); | |||||
const size_t h = static_cast<size_t>(g_yres); | |||||
const size_t img_size = w * h * m_Bpp / 8; | |||||
const size_t hdr_size = tex_hdr_size(m_Path); | |||||
Not Done Inline Actionsstatic_cast ? Stan: static_cast ? | |||||
shared_ptr<u8> 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) | |||||
StanUnsubmitted Not Done Inline ActionsCould it be size_t ? Stan: Could it be size_t ? | |||||
{ | |||||
// 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); | |||||
StanUnsubmitted Not Done Inline Actionscast to definite size. Stan: cast to definite size. | |||||
if(!tile_data) | |||||
{ | |||||
WARN_IF_ERR(ERR::NO_MEM); | |||||
return; | |||||
} | |||||
const int img_w = tile_w*tiles, img_h = tile_h*tiles; | |||||
StanUnsubmitted Not Done Inline ActionsSpace between operators :) I'm not a fan of combining line declarations, but it's up to you. Stan: Space between operators :)
I'm not a fan of combining line declarations, but it's up to you. | |||||
const size_t img_size = img_w * img_h * m_Bpp / 8; | |||||
const size_t hdr_size = tex_hdr_size(m_Path); | |||||
shared_ptr<u8> img_buf; | |||||
AllocateAligned(img_buf, hdr_size + img_size, maxSectorSize); | |||||
Tex t; | |||||
GLvoid* img = img_buf.get() + hdr_size; | |||||
StanUnsubmitted Not Done Inline ActionsDo you need that to be a GLvoid, or can you use char* ? Stan: Do you need that to be a GLvoid, or can you use char* ? | |||||
StanUnsubmitted Not Done Inline ActionsDoes it needs to be freed ? Stan: Does it needs to be freed ? | |||||
if(t.wrap(img_w, img_h, m_Bpp, m_TexFlags, img_buf, hdr_size) < 0) | |||||
{ | |||||
free(tile_data); | |||||
Not Done Inline Actionsif( in lib/, if ( elsewhere or so elexis: `if(` in lib/, `if (` elsewhere or so | |||||
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<char*>(img) + ((tile_y*tile_h + y) * img_w + (tile_x*tile_w)) * m_Bpp / 8; | |||||
void* src = static_cast<char*>(tile_data) + y * tile_w * m_Bpp / 8; | |||||
memcpy(dest, src, tile_w * m_Bpp / 8); | |||||
Not Done Inline Actionsstatic_casts ? space between operators. Stan: static_casts ? space between operators. | |||||
} | |||||
} | |||||
} | |||||
// 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); | |||||
} |
Wildfire Games · Phabricator
could be done in the header