Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Reworked Screenshot saving.
Now OGL doesn't rely on WX for PNG saving.
FlipImageData supports (pixel data len > 3) now.
TextureToPng is now in ImageWrite.cpp/h
Video Common depends on zlib and png.
D3D no longer depends on zlib and png.
  • Loading branch information
Parlane committed Nov 15, 2013
1 parent 2703cae commit 033ed94
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 196 deletions.
6 changes: 0 additions & 6 deletions Source/Core/VideoBackends/D3D/D3D.vcxproj
Expand Up @@ -93,15 +93,9 @@
<ClInclude Include="Src\XFBEncoder.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\Externals\libpng\png\png.vcxproj">
<Project>{4c9f135b-a85e-430c-bad4-4c67ef5fc12c}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Externals\wxWidgets3\build\msw\wx_base.vcxproj">
<Project>{1c8436c9-dbaf-42be-83bc-cf3ec9175abe}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Externals\zlib\zlib.vcxproj">
<Project>{ff213b23-2c26-4214-9f88-85271e557e87}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\Core\VideoCommon\VideoCommon.vcxproj">
<Project>{3de9ee35-3e91-4f27-a014-2866ad8c3fe3}</Project>
</ProjectReference>
Expand Down
84 changes: 0 additions & 84 deletions Source/Core/VideoBackends/D3D/Src/D3DTexture.cpp
Expand Up @@ -5,96 +5,12 @@
#include "D3DBase.h"
#include "D3DTexture.h"

#include "png.h"

namespace DX11
{

namespace D3D
{

bool TextureToPng(D3D11_MAPPED_SUBRESOURCE &map, const char* filename, int width, int height, bool saveAlpha)
{
bool success = false;
if (map.pData != NULL)
{
FILE *fp = NULL;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;

// Open file for writing (binary mode)
fp = fopen(filename, "wb");
if (fp == NULL) {
PanicAlert("Could not open file %s for writing\n", filename);
goto finalise;
}

// Initialize write structure
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
PanicAlert("Could not allocate write struct\n");
goto finalise;

}

// Initialize info structure
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
PanicAlert("Could not allocate info struct\n");
goto finalise;
}

// Setup Exception handling
if (setjmp(png_jmpbuf(png_ptr))) {
PanicAlert("Error during png creation\n");
goto finalise;
}

png_init_io(png_ptr, fp);

// Write header (8 bit colour depth)
png_set_IHDR(png_ptr, info_ptr, width, height,
8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

char title[] = "Dolphin Screenshot";
png_text title_text;
title_text.compression = PNG_TEXT_COMPRESSION_NONE;
title_text.key = "Title";
title_text.text = title;
png_set_text(png_ptr, info_ptr, &title_text, 1);

png_write_info(png_ptr, info_ptr);

// Write image data
for (auto y = 0; y < height; ++y)
{
u8* row_ptr = (u8*)map.pData + y * map.RowPitch;
u8* ptr = row_ptr;
for (UINT x = 0; x < map.RowPitch / 4; ++x)
{
if (!saveAlpha)
ptr[3] = 0xff;
ptr += 4;
}
png_write_row(png_ptr, row_ptr);
}

// End write
png_write_end(png_ptr, NULL);

success = true;

finalise:

if (fp != NULL) fclose(fp);
if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL);

}
return false;
}

void ReplaceRGBATexture2D(ID3D11Texture2D* pTexture, const u8* buffer, unsigned int width, unsigned int height, unsigned int pitch, unsigned int level, D3D11_USAGE usage)
{
if (usage == D3D11_USAGE_DYNAMIC || usage == D3D11_USAGE_STAGING)
Expand Down
1 change: 0 additions & 1 deletion Source/Core/VideoBackends/D3D/Src/D3DTexture.h
Expand Up @@ -11,7 +11,6 @@ namespace DX11

namespace D3D
{
bool TextureToPng(D3D11_MAPPED_SUBRESOURCE& map, const char* filename, int width, int height, bool saveAlpha = true);
void ReplaceRGBATexture2D(ID3D11Texture2D* pTexture, const u8* buffer, unsigned int width, unsigned int height, unsigned int pitch, unsigned int level, D3D11_USAGE usage);
}

Expand Down
17 changes: 13 additions & 4 deletions Source/Core/VideoBackends/D3D/Src/Render.cpp
Expand Up @@ -34,6 +34,7 @@
#include "FPSCounter.h"
#include "ConfigManager.h"
#include <strsafe.h>
#include "ImageWrite.h"

namespace DX11
{
Expand Down Expand Up @@ -693,11 +694,19 @@ bool Renderer::SaveScreenshot(const std::string &filename, const TargetRectangle
D3D11_MAPPED_SUBRESOURCE map;
D3D::context->Map(s_screenshot_texture, 0, D3D11_MAP_READ_WRITE, 0, &map);

// ready to be saved
HRESULT hr = D3D::TextureToPng(map, filename.c_str(), rc.GetWidth(), rc.GetHeight(), false);
bool saved_png = false;
if (map.pData)
{
u8* data = new u8[map.RowPitch * rc.GetHeight()];
memcpy(data, map.pData, map.RowPitch * rc.GetHeight());

saved_png = TextureToPng(data, map.RowPitch, filename.c_str(), rc.GetWidth(), rc.GetHeight(), false);
}

D3D::context->Unmap(s_screenshot_texture, 0);

if (SUCCEEDED(hr))

if (saved_png)
{
OSD::AddMessage(StringFromFormat("Saved %i x %i %s", rc.GetWidth(),
rc.GetHeight(), filename.c_str()));
Expand All @@ -707,7 +716,7 @@ bool Renderer::SaveScreenshot(const std::string &filename, const TargetRectangle
OSD::AddMessage(StringFromFormat("Error saving %s", filename.c_str()));
}

return SUCCEEDED(hr);
return saved_png;
}

void formatBufferDump(const u8* in, u8* out, int w, int h, int p)
Expand Down
13 changes: 11 additions & 2 deletions Source/Core/VideoBackends/D3D/Src/TextureCache.cpp
Expand Up @@ -14,6 +14,7 @@
#include "PSTextureEncoder.h"
#include "HW/Memmap.h"
#include "VideoConfig.h"
#include "ImageWrite.h"

namespace DX11
{
Expand Down Expand Up @@ -54,6 +55,8 @@ bool TextureCache::TCacheEntry::Save(const char filename[], unsigned int level)

HRESULT hr = D3D::device->CreateTexture2D(&desc, NULL, &pNewTexture);

bool saved_png = false;

if (SUCCEEDED(hr) && pNewTexture)
{
D3D::context->CopyResource(pNewTexture, pSurface);
Expand All @@ -62,13 +65,19 @@ bool TextureCache::TCacheEntry::Save(const char filename[], unsigned int level)
HRESULT hr = D3D::context->Map(pNewTexture, 0, D3D11_MAP_READ_WRITE, 0, &map);
if (SUCCEEDED(hr))
{
hr = D3D::TextureToPng(map, filename, desc.Width, desc.Height);
if (map.pData)
{
u8* data = new u8[map.RowPitch * desc.Height];
memcpy(data, map.pData, map.RowPitch * desc.Height);

saved_png = TextureToPng(data, map.RowPitch, filename, desc.Width, desc.Height);
}
D3D::context->Unmap(pNewTexture, 0);
}
SAFE_RELEASE(pNewTexture);
}

return SUCCEEDED(hr);
return saved_png;
}

void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height,
Expand Down
107 changes: 9 additions & 98 deletions Source/Core/VideoBackends/OGL/Src/Render.cpp
Expand Up @@ -63,10 +63,6 @@
#include "AVIDump.h"
#endif

#if defined(HAVE_WX) && HAVE_WX
#include <wx/image.h>
#endif

// glew1.8 doesn't define KHR_debug
#ifndef GL_DEBUG_OUTPUT
#define GL_DEBUG_OUTPUT 0x92E0
Expand All @@ -78,18 +74,6 @@ void VideoConfig::UpdateProjectionHack()
::UpdateProjectionHack(g_Config.iPhackvalue, g_Config.sPhackvalue);
}


#if defined(HAVE_WX) && HAVE_WX
// Screenshot thread struct
typedef struct
{
int W, H;
std::string filename;
wxImage *img;
} ScrStrct;
#endif


int OSDInternalW, OSDInternalH;

namespace OGL
Expand Down Expand Up @@ -1804,80 +1788,32 @@ void Renderer::SetInterlacingMode()
// TODO
}

void Renderer::FlipImageData(u8 *data, int w, int h)
void Renderer::FlipImageData(u8 *data, int w, int h, int pixel_width)
{
// Flip image upside down. Damn OpenGL.
for (int y = 0; y < h / 2; y++)
for (int y = 0; y < h / 2; ++y)
{
for(int x = 0; x < w; x++)
for(int x = 0; x < w; ++x)
{
std::swap(data[(y * w + x) * 3], data[((h - 1 - y) * w + x) * 3]);
std::swap(data[(y * w + x) * 3 + 1], data[((h - 1 - y) * w + x) * 3 + 1]);
std::swap(data[(y * w + x) * 3 + 2], data[((h - 1 - y) * w + x) * 3 + 2]);
for (auto delta = 0; delta < pixel_width; ++delta)
std::swap(data[(y * w + x) * pixel_width + delta], data[((h - 1 - y) * w + x) * pixel_width + delta]);
}
}
}

}

// TODO: remove
extern bool g_aspect_wide;

#if defined(HAVE_WX) && HAVE_WX
void TakeScreenshot(ScrStrct* threadStruct)
{
// These will contain the final image size
float FloatW = (float)threadStruct->W;
float FloatH = (float)threadStruct->H;

// Handle aspect ratio for the final ScrStrct to look exactly like what's on screen.
if (g_ActiveConfig.iAspectRatio != ASPECT_STRETCH)
{
bool use16_9 = g_aspect_wide;

// Check for force-settings and override.
if (g_ActiveConfig.iAspectRatio == ASPECT_FORCE_16_9)
use16_9 = true;
else if (g_ActiveConfig.iAspectRatio == ASPECT_FORCE_4_3)
use16_9 = false;

float Ratio = (FloatW / FloatH) / (!use16_9 ? (4.0f / 3.0f) : (16.0f / 9.0f));

// If ratio > 1 the picture is too wide and we have to limit the width.
if (Ratio > 1)
FloatW /= Ratio;
// ratio == 1 or the image is too high, we have to limit the height.
else
FloatH *= Ratio;

// This is a bit expensive on high resolutions
threadStruct->img->Rescale((int)FloatW, (int)FloatH, wxIMAGE_QUALITY_HIGH);
}

// Save the screenshot and finally kill the wxImage object
// This is really expensive when saving to PNG, but not at all when using BMP
threadStruct->img->SaveFile(StrToWxStr(threadStruct->filename),
wxBITMAP_TYPE_PNG);
threadStruct->img->Destroy();

// Show success messages
OSD::AddMessage(StringFromFormat("Saved %i x %i %s", (int)FloatW, (int)FloatH,
threadStruct->filename.c_str()), 2000);
delete threadStruct;
}
#endif

namespace OGL
{

bool Renderer::SaveScreenshot(const std::string &filename, const TargetRectangle &back_rc)
{
u32 W = back_rc.GetWidth();
u32 H = back_rc.GetHeight();
u8 *data = (u8 *)malloc((sizeof(u8) * 3 * W * H));
u8 *data = (u8 *)malloc((sizeof(u8) * 4 * W * H));
glPixelStorei(GL_PACK_ALIGNMENT, 1);

glReadPixels(back_rc.left, back_rc.bottom, W, H, GL_RGB, GL_UNSIGNED_BYTE, data);
glReadPixels(back_rc.left, back_rc.bottom, W, H, GL_RGBA, GL_UNSIGNED_BYTE, data);

// Show failure message
if (GL_REPORT_ERROR() != GL_NO_ERROR)
Expand All @@ -1888,34 +1824,9 @@ bool Renderer::SaveScreenshot(const std::string &filename, const TargetRectangle
}

// Turn image upside down
FlipImageData(data, W, H);

#if defined(HAVE_WX) && HAVE_WX
// Create wxImage
wxImage *a = new wxImage(W, H, data);

if (scrshotThread.joinable())
scrshotThread.join();
FlipImageData(data, W, H, 4);

ScrStrct *threadStruct = new ScrStrct;
threadStruct->filename = filename;
threadStruct->img = a;
threadStruct->H = H; threadStruct->W = W;

scrshotThread = std::thread(TakeScreenshot, threadStruct);
#ifdef _WIN32
SetThreadPriority(scrshotThread.native_handle(), THREAD_PRIORITY_BELOW_NORMAL);
#endif
bool result = true;

OSD::AddMessage("Saving Screenshot... ", 2000);

#else
bool result = SaveTGA(filename.c_str(), W, H, data);
free(data);
#endif

return result;
return TextureToPng(data, W*4, filename.c_str(), W, H, false);
}

}
2 changes: 1 addition & 1 deletion Source/Core/VideoBackends/OGL/Src/Render.h
Expand Up @@ -63,7 +63,7 @@ class Renderer : public ::Renderer

void RenderText(const char* pstr, int left, int top, u32 color) override;
void DrawDebugInfo();
void FlipImageData(u8 *data, int w, int h);
void FlipImageData(u8 *data, int w, int h, int pixel_width = 3);

u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override;

Expand Down

0 comments on commit 033ed94

Please sign in to comment.