From 554a85e6d8830fea159400caf64047bfeba3f386 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 21 Dec 2017 23:47:49 -0800 Subject: [PATCH] GE Debugger: Save with alpha for PNGs. --- Core/AVIDump.cpp | 2 +- Core/Screenshot.cpp | 84 +++++++++++++++++++++++-------- Core/Screenshot.h | 3 +- Windows/GEDebugger/GEDebugger.cpp | 9 +++- 4 files changed, 72 insertions(+), 26 deletions(-) diff --git a/Core/AVIDump.cpp b/Core/AVIDump.cpp index 6188a2bda..37c6cd620 100644 --- a/Core/AVIDump.cpp +++ b/Core/AVIDump.cpp @@ -174,7 +174,7 @@ void AVIDump::AddFrame() u32 h = buf.GetHeight(); CheckResolution(w, h); u8 *flipbuffer = nullptr; - const u8 *buffer = ConvertBufferTo888RGB(buf, flipbuffer, w, h); + const u8 *buffer = ConvertBufferToScreenshot(buf, false, flipbuffer, w, h); #ifdef USE_FFMPEG diff --git a/Core/Screenshot.cpp b/Core/Screenshot.cpp index bc5b04605..46694c35a 100644 --- a/Core/Screenshot.cpp +++ b/Core/Screenshot.cpp @@ -125,12 +125,14 @@ static bool WriteScreenshotToPNG(png_imagep image, const char *filename, int con } #endif -static bool ConvertPixelTo888RGB(GPUDebugBufferFormat fmt, u8 &r, u8 &g, u8 &b, const void *buffer, int offset, bool rev) { +static bool ConvertPixelTo8888RGBA(GPUDebugBufferFormat fmt, u8 &r, u8 &g, u8 &b, u8 &a, const void *buffer, int offset, bool rev) { const u8 *buf8 = (const u8 *)buffer; const u16 *buf16 = (const u16 *)buffer; const u32 *buf32 = (const u32 *)buffer; const float *fbuf = (const float *)buffer; + // NOTE: a and r might be the same channel. This is used for RGB. + u32 src; double fsrc; switch (fmt) { @@ -139,6 +141,7 @@ static bool ConvertPixelTo888RGB(GPUDebugBufferFormat fmt, u8 &r, u8 &g, u8 &b, if (rev) { src = bswap16(src); } + a = 255; r = Convert5To8((src >> 0) & 0x1F); g = Convert6To8((src >> 5) & 0x3F); b = Convert5To8((src >> 11) & 0x1F); @@ -148,6 +151,7 @@ static bool ConvertPixelTo888RGB(GPUDebugBufferFormat fmt, u8 &r, u8 &g, u8 &b, if (rev) { src = bswap16(src); } + a = (src >> 15) ? 255 : 0; r = Convert5To8((src >> 0) & 0x1F); g = Convert5To8((src >> 5) & 0x1F); b = Convert5To8((src >> 10) & 0x1F); @@ -157,6 +161,7 @@ static bool ConvertPixelTo888RGB(GPUDebugBufferFormat fmt, u8 &r, u8 &g, u8 &b, if (rev) { src = bswap16(src); } + a = Convert4To8((src >> 12) & 0xF); r = Convert4To8((src >> 0) & 0xF); g = Convert4To8((src >> 4) & 0xF); b = Convert4To8((src >> 8) & 0xF); @@ -166,54 +171,62 @@ static bool ConvertPixelTo888RGB(GPUDebugBufferFormat fmt, u8 &r, u8 &g, u8 &b, if (rev) { src = bswap32(src); } + a = (src >> 24) & 0xFF; r = (src >> 0) & 0xFF; g = (src >> 8) & 0xFF; b = (src >> 16) & 0xFF; break; case GPU_DBG_FORMAT_FLOAT: fsrc = fbuf[offset]; - r = fsrc >= 1.0 ? 255 : (fsrc < 0.0 ? 0 : (int)(fsrc * 255.0)); + r = 255; g = 0; b = 0; + a = fsrc >= 1.0 ? 255 : (fsrc < 0.0 ? 0 : (int)(fsrc * 255.0)); break; case GPU_DBG_FORMAT_16BIT: src = buf16[offset]; - r = src >> 8; + r = 255; g = 0; b = 0; + a = src >> 8; break; case GPU_DBG_FORMAT_8BIT: src = buf8[offset]; - r = src; + r = 255; g = 0; b = 0; + a = src; break; case GPU_DBG_FORMAT_24BIT_8X: src = buf32[offset]; - r = (src >> 16) & 0xFF; + r = 255; g = 0; b = 0; + a = (src >> 16) & 0xFF; break; case GPU_DBG_FORMAT_24X_8BIT: src = buf32[offset]; - r = (src >> 24) & 0xFF; + r = 255; g = 0; b = 0; + a = (src >> 24) & 0xFF; break; case GPU_DBG_FORMAT_24BIT_8X_DIV_256: src = buf32[offset]& 0x00FFFFFF; src = src - 0x800000 + 0x8000; - r = (src >> 8) & 0xFF; + r = 255; g = 0; b = 0; + a = (src >> 8) & 0xFF; break; case GPU_DBG_FORMAT_FLOAT_DIV_256: fsrc = fbuf[offset]; src = (int)(fsrc * 16777215.0); src = src - 0x800000 + 0x8000; - r = (src >> 8) & 0xFF; + r = 255; g = 0; b = 0; + a = (src >> 8) & 0xFF; break; default: _assert_msg_(SYSTEM, false, "Unsupported framebuffer format for screenshot: %d", fmt); @@ -223,24 +236,27 @@ static bool ConvertPixelTo888RGB(GPUDebugBufferFormat fmt, u8 &r, u8 &g, u8 &b, return true; } -const u8 *ConvertBufferTo888RGB(const GPUDebugBuffer &buf, u8 *&temp, u32 &w, u32 &h) { +const u8 *ConvertBufferToScreenshot(const GPUDebugBuffer &buf, bool alpha, u8 *&temp, u32 &w, u32 &h) { + int pixelSize = alpha ? 4 : 3; + GPUDebugBufferFormat nativeFmt = alpha ? GPU_DBG_FORMAT_8888 : GPU_DBG_FORMAT_888_RGB; + w = std::min(w, buf.GetStride()); h = std::min(h, buf.GetHeight()); // The temp buffer will be freed by the caller if set, and can be the return value. - if (buf.GetFlipped() || buf.GetFormat() != GPU_DBG_FORMAT_888_RGB) { - temp = new u8[3 * w * h]; + if (buf.GetFlipped() || buf.GetFormat() != nativeFmt) { + temp = new u8[pixelSize * w * h]; } else { temp = nullptr; } const u8 *buffer = buf.GetData(); - if (buf.GetFlipped() && buf.GetFormat() == GPU_DBG_FORMAT_888_RGB) { + if (buf.GetFlipped() && buf.GetFormat() == nativeFmt) { // Silly OpenGL reads upside down, we flip to another buffer for simplicity. for (u32 y = 0; y < h; y++) { - memcpy(temp + y * w * 3, buffer + (buf.GetHeight() - y - 1) * buf.GetStride() * 3, w * 3); + memcpy(temp + y * w * pixelSize, buffer + (buf.GetHeight() - y - 1) * buf.GetStride() * pixelSize, w * pixelSize); } - } else if (buf.GetFormat() < GPU_DBG_FORMAT_FLOAT) { + } else if (buf.GetFormat() < GPU_DBG_FORMAT_FLOAT && buf.GetFormat() != nativeFmt) { // Let's boil it down to how we need to interpret the bits. int baseFmt = buf.GetFormat() & ~(GPU_DBG_FORMAT_REVERSE_FLAG | GPU_DBG_FORMAT_BRSWAP_FLAG); bool rev = (buf.GetFormat() & GPU_DBG_FORMAT_REVERSE_FLAG) != 0; @@ -252,21 +268,22 @@ const u8 *ConvertBufferTo888RGB(const GPUDebugBuffer &buf, u8 *&temp, u32 &w, u3 for (u32 x = 0; x < w; x++) { u8 *dst; if (flip) { - dst = &temp[(h - y - 1) * w * 3 + x * 3]; + dst = &temp[(h - y - 1) * w * pixelSize + x * pixelSize]; } else { - dst = &temp[y * w * 3 + x * 3]; + dst = &temp[y * w * pixelSize + x * pixelSize]; } u8 &r = brswap ? dst[2] : dst[0]; u8 &g = dst[1]; u8 &b = brswap ? dst[0] : dst[2]; + u8 &a = alpha ? dst[3] : r; - if (!ConvertPixelTo888RGB(GPUDebugBufferFormat(baseFmt), r, g, b, buffer, y * buf.GetStride() + x, rev)) { + if (!ConvertPixelTo8888RGBA(GPUDebugBufferFormat(baseFmt), r, g, b, a, buffer, y * buf.GetStride() + x, rev)) { return nullptr; } } } - } else if (buf.GetFormat() != GPU_DBG_FORMAT_888_RGB) { + } else if (buf.GetFormat() != nativeFmt) { bool flip = buf.GetFlipped(); // This is pretty inefficient. @@ -274,12 +291,13 @@ const u8 *ConvertBufferTo888RGB(const GPUDebugBuffer &buf, u8 *&temp, u32 &w, u3 for (u32 x = 0; x < w; x++) { u8 *dst; if (flip) { - dst = &temp[(h - y - 1) * w * 3 + x * 3]; + dst = &temp[(h - y - 1) * w * pixelSize + x * pixelSize]; } else { - dst = &temp[y * w * 3 + x * 3]; + dst = &temp[y * w * pixelSize + x * pixelSize]; } - if (!ConvertPixelTo888RGB(buf.GetFormat(), dst[0], dst[1], dst[2], buffer, y * buf.GetStride() + x, false)) { + u8 &a = alpha ? dst[3] : dst[0]; + if (!ConvertPixelTo8888RGBA(buf.GetFormat(), dst[0], dst[1], dst[2], a, buffer, y * buf.GetStride() + x, false)) { return nullptr; } } @@ -316,7 +334,7 @@ bool TakeGameScreenshot(const char *filename, ScreenshotFormat fmt, ScreenshotTy u8 *flipbuffer = nullptr; if (success) { - const u8 *buffer = ConvertBufferTo888RGB(buf, flipbuffer, w, h); + const u8 *buffer = ConvertBufferToScreenshot(buf, false, flipbuffer, w, h); success = buffer != nullptr; if (success) { if (width) @@ -364,3 +382,25 @@ bool Save888RGBScreenshot(const char *filename, ScreenshotFormat fmt, const u8 * } #endif } + +bool Save8888RGBAScreenshot(const char *filename, const u8 *buffer, int w, int h) { +#ifdef USING_QT_UI + QImage image(buffer, w, h, QImage::Format_RGBA8888); + return image.save(filename, "PNG"); +#else + png_image png; + memset(&png, 0, sizeof(png)); + png.version = PNG_IMAGE_VERSION; + png.format = PNG_FORMAT_RGBA; + png.width = w; + png.height = h; + bool success = WriteScreenshotToPNG(&png, filename, 0, buffer, w * 4, nullptr); + png_image_free(&png); + + if (png.warning_or_error >= 2) { + ERROR_LOG(SYSTEM, "Saving screenshot to PNG produced errors."); + success = false; + } + return success; +#endif +} diff --git a/Core/Screenshot.h b/Core/Screenshot.h index 75214b54a..1632a1434 100644 --- a/Core/Screenshot.h +++ b/Core/Screenshot.h @@ -33,8 +33,9 @@ enum ScreenshotType { SCREENSHOT_RENDER, }; -const u8 *ConvertBufferTo888RGB(const GPUDebugBuffer &buf, u8 *&temp, u32 &w, u32 &h); +const u8 *ConvertBufferToScreenshot(const GPUDebugBuffer &buf, bool alpha, u8 *&temp, u32 &w, u32 &h); // Can only be used while in game. bool TakeGameScreenshot(const char *filename, ScreenshotFormat fmt, ScreenshotType type, int *width = nullptr, int *height = nullptr, int maxRes = -1); bool Save888RGBScreenshot(const char *filename, ScreenshotFormat fmt, const u8 *bufferRGB888, int w, int h); +bool Save8888RGBAScreenshot(const char *filename, const u8 *bufferRGBA8888, int w, int h); diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index 325b346a3..86cf69d1b 100644 --- a/Windows/GEDebugger/GEDebugger.cpp +++ b/Windows/GEDebugger/GEDebugger.cpp @@ -229,13 +229,18 @@ void CGEDebugger::PreviewExport(const GPUDebugBuffer *dbgBuffer) { std::string fn; if (W32Util::BrowseForFileName(false, GetDlgHandle(), L"Save Preview Image...", nullptr, filter, L"png", fn)) { ScreenshotFormat fmt = fn.find(".jpg") != fn.npos ? ScreenshotFormat::JPG : ScreenshotFormat::PNG; + bool saveAlpha = fmt == ScreenshotFormat::PNG; u8 *flipbuffer = nullptr; u32 w = (u32)-1; u32 h = (u32)-1; - const u8 *buffer = ConvertBufferTo888RGB(*dbgBuffer, flipbuffer, w, h); + const u8 *buffer = ConvertBufferToScreenshot(*dbgBuffer, saveAlpha, flipbuffer, w, h); if (buffer != nullptr) { - Save888RGBScreenshot(fn.c_str(), fmt, buffer, w, h); + if (saveAlpha) { + Save8888RGBAScreenshot(fn.c_str(), buffer, w, h); + } else { + Save888RGBScreenshot(fn.c_str(), fmt, buffer, w, h); + } } delete [] flipbuffer; }