mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
GE Debugger: Copy images optionally with alpha.
Although, lots of apps don't support this.
This commit is contained in:
parent
f3722faef4
commit
3cc628beb4
8 changed files with 158 additions and 45 deletions
|
@ -406,8 +406,7 @@ bool Save888RGBScreenshot(const Path &filename, ScreenshotFormat fmt, const u8 *
|
|||
}
|
||||
|
||||
bool Save8888RGBAScreenshot(const Path &filename, const u8 *buffer, int w, int h) {
|
||||
png_image png;
|
||||
memset(&png, 0, sizeof(png));
|
||||
png_image png{};
|
||||
png.version = PNG_IMAGE_VERSION;
|
||||
png.format = PNG_FORMAT_RGBA;
|
||||
png.width = w;
|
||||
|
@ -421,3 +420,30 @@ bool Save8888RGBAScreenshot(const Path &filename, const u8 *buffer, int w, int h
|
|||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Save8888RGBAScreenshot(std::vector<uint8_t> &bufferPNG, const u8 *bufferRGBA8888, int w, int h) {
|
||||
png_image png{};
|
||||
png.version = PNG_IMAGE_VERSION;
|
||||
png.format = PNG_FORMAT_RGBA;
|
||||
png.width = w;
|
||||
png.height = h;
|
||||
|
||||
png_alloc_size_t allocSize = bufferPNG.size();
|
||||
int result = png_image_write_to_memory(&png, allocSize == 0 ? nullptr : bufferPNG.data(), &allocSize, 0, bufferRGBA8888, w * 4, nullptr);
|
||||
bool success = result != 0 && png.warning_or_error <= 1;
|
||||
if (!success && allocSize != bufferPNG.size()) {
|
||||
bufferPNG.resize(allocSize);
|
||||
png.warning_or_error = 0;
|
||||
result = png_image_write_to_memory(&png, bufferPNG.data(), &allocSize, 0, bufferRGBA8888, w * 4, nullptr);
|
||||
success = result != 0 && png.warning_or_error <= 1;
|
||||
}
|
||||
if (success)
|
||||
bufferPNG.resize(allocSize);
|
||||
png_image_free(&png);
|
||||
|
||||
if (!success) {
|
||||
ERROR_LOG(IO, "Buffering screenshot to PNG produced errors.");
|
||||
bufferPNG.clear();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
|
|
@ -39,5 +39,8 @@ const u8 *ConvertBufferToScreenshot(const GPUDebugBuffer &buf, bool alpha, u8 *&
|
|||
|
||||
// Can only be used while in game.
|
||||
bool TakeGameScreenshot(const Path &filename, ScreenshotFormat fmt, ScreenshotType type, int *width = nullptr, int *height = nullptr, int maxRes = -1);
|
||||
|
||||
bool Save888RGBScreenshot(const Path &filename, ScreenshotFormat fmt, const u8 *bufferRGB888, int w, int h);
|
||||
bool Save8888RGBAScreenshot(const Path &filename, const u8 *bufferRGBA8888, int w, int h);
|
||||
// Overallocate bufferPNG for better encoding speed.
|
||||
bool Save8888RGBAScreenshot(std::vector<uint8_t> &bufferPNG, const u8 *bufferRGBA8888, int w, int h);
|
||||
|
|
|
@ -292,7 +292,10 @@ void CGEDebugger::SetupPreviews() {
|
|||
PreviewExport(primaryBuffer_);
|
||||
break;
|
||||
case ID_GEDBG_COPY_IMAGE:
|
||||
PreviewToClipboard(primaryBuffer_);
|
||||
PreviewToClipboard(primaryBuffer_, false);
|
||||
break;
|
||||
case ID_GEDBG_COPY_IMAGE_ALPHA:
|
||||
PreviewToClipboard(primaryBuffer_, true);
|
||||
break;
|
||||
case ID_GEDBG_ENABLE_PREVIEW:
|
||||
previewsEnabled_ ^= 1;
|
||||
|
@ -325,7 +328,10 @@ void CGEDebugger::SetupPreviews() {
|
|||
PreviewExport(secondBuffer_);
|
||||
break;
|
||||
case ID_GEDBG_COPY_IMAGE:
|
||||
PreviewToClipboard(secondBuffer_);
|
||||
PreviewToClipboard(secondBuffer_, false);
|
||||
break;
|
||||
case ID_GEDBG_COPY_IMAGE_ALPHA:
|
||||
PreviewToClipboard(secondBuffer_, true);
|
||||
break;
|
||||
case ID_GEDBG_ENABLE_PREVIEW:
|
||||
previewsEnabled_ ^= 2;
|
||||
|
@ -399,59 +405,94 @@ void CGEDebugger::PreviewExport(const GPUDebugBuffer *dbgBuffer) {
|
|||
}
|
||||
}
|
||||
|
||||
void CGEDebugger::PreviewToClipboard(const GPUDebugBuffer *dbgBuffer) {
|
||||
void CGEDebugger::PreviewToClipboard(const GPUDebugBuffer *dbgBuffer, bool saveAlpha) {
|
||||
if (!OpenClipboard(GetDlgHandle())) {
|
||||
return;
|
||||
}
|
||||
EmptyClipboard();
|
||||
|
||||
uint32_t byteStride = 3 * dbgBuffer->GetStride();
|
||||
while ((byteStride & 3) != 0)
|
||||
++byteStride;
|
||||
|
||||
HANDLE memHandle = GlobalAlloc(GHND, sizeof(BITMAPV5HEADER) + byteStride * dbgBuffer->GetHeight());
|
||||
if (memHandle == NULL) {
|
||||
uint8_t *flipbuffer = nullptr;
|
||||
uint32_t w = (uint32_t)-1;
|
||||
uint32_t h = (uint32_t)-1;
|
||||
const uint8_t *buffer = ConvertBufferToScreenshot(*dbgBuffer, saveAlpha, flipbuffer, w, h);
|
||||
if (buffer == nullptr) {
|
||||
delete [] flipbuffer;
|
||||
CloseClipboard();
|
||||
return;
|
||||
}
|
||||
|
||||
BITMAPV5HEADER *header = (BITMAPV5HEADER *)GlobalLock(memHandle);
|
||||
uint32_t pixelSize = saveAlpha ? 4 : 3;
|
||||
uint32_t byteStride = pixelSize * w;
|
||||
while ((byteStride & 3) != 0)
|
||||
++byteStride;
|
||||
|
||||
// Various apps don't support alpha well, so also copy as PNG.
|
||||
std::vector<uint8_t> png;
|
||||
if (saveAlpha) {
|
||||
// Overallocate if we can.
|
||||
png.resize(byteStride * h);
|
||||
Save8888RGBAScreenshot(png, buffer, w, h);
|
||||
|
||||
W32Util::ClipboardData png1("PNG", png.size());
|
||||
W32Util::ClipboardData png2("image/png", png.size());
|
||||
if (!png.empty() && png1 && png2) {
|
||||
memcpy(png1.data, png.data(), png.size());
|
||||
memcpy(png2.data, png.data(), png.size());
|
||||
png1.Set();
|
||||
png2.Set();
|
||||
}
|
||||
}
|
||||
|
||||
W32Util::ClipboardData bitmap(CF_DIBV5, sizeof(BITMAPV5HEADER) + byteStride * h);
|
||||
if (!bitmap) {
|
||||
delete [] flipbuffer;
|
||||
CloseClipboard();
|
||||
return;
|
||||
}
|
||||
|
||||
BITMAPV5HEADER *header = (BITMAPV5HEADER *)bitmap.data;
|
||||
header->bV5Size = sizeof(BITMAPV5HEADER);
|
||||
header->bV5Width = dbgBuffer->GetStride();
|
||||
// Bitmaps are flipped, but we can specify negative height to not be flipped.
|
||||
header->bV5Height = -dbgBuffer->GetHeight();
|
||||
header->bV5Width = w;
|
||||
header->bV5Height = h;
|
||||
header->bV5Planes = 1;
|
||||
header->bV5BitCount = 24;
|
||||
header->bV5Compression = BI_RGB;
|
||||
header->bV5SizeImage = byteStride * dbgBuffer->GetHeight();
|
||||
header->bV5CSType = LCS_sRGB;
|
||||
header->bV5BitCount = saveAlpha ? 32 : 24;
|
||||
header->bV5Compression = saveAlpha ? BI_BITFIELDS : BI_RGB;
|
||||
header->bV5SizeImage = byteStride * h;
|
||||
header->bV5CSType = LCS_WINDOWS_COLOR_SPACE;
|
||||
header->bV5Intent = LCS_GM_GRAPHICS;
|
||||
|
||||
uint8_t *flipbuffer = nullptr;
|
||||
uint32_t w = (uint32_t)-1;
|
||||
uint32_t h = (uint32_t)-1;
|
||||
const uint8_t *buffer = ConvertBufferToScreenshot(*dbgBuffer, false, flipbuffer, w, h);
|
||||
if (buffer != nullptr) {
|
||||
if (saveAlpha) {
|
||||
header->bV5RedMask = 0x000000FF;
|
||||
header->bV5GreenMask = 0x0000FF00;
|
||||
header->bV5BlueMask = 0x00FF0000;
|
||||
// Only some applications respect the alpha mask...
|
||||
header->bV5AlphaMask = 0xFF000000;
|
||||
}
|
||||
|
||||
uint8_t *pixels = (uint8_t *)(header + 1);
|
||||
for (uint32_t y = 0; y < dbgBuffer->GetHeight(); ++y) {
|
||||
const uint8_t *src = buffer + y * 3 * w;
|
||||
uint8_t *dst = pixels + y * byteStride;
|
||||
for (uint32_t y = 0; y < h; ++y) {
|
||||
const uint8_t *src = buffer + y * pixelSize * w;
|
||||
uint8_t *dst = pixels + (h - y - 1) * byteStride;
|
||||
|
||||
if (saveAlpha) {
|
||||
// No RB swap needed.
|
||||
memcpy(dst, src, pixelSize * w);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32_t x = 0; x < w; ++x) {
|
||||
// Have to swap B/R again for the bitmap, unfortunate.
|
||||
dst[0] = src[2];
|
||||
dst[1] = src[1];
|
||||
dst[2] = src[0];
|
||||
src += 3;
|
||||
dst += 3;
|
||||
}
|
||||
src += pixelSize;
|
||||
dst += pixelSize;
|
||||
}
|
||||
}
|
||||
|
||||
delete [] flipbuffer;
|
||||
|
||||
GlobalUnlock(memHandle);
|
||||
|
||||
// Takes ownership.
|
||||
SetClipboardData(CF_DIBV5, memHandle);
|
||||
bitmap.Set();
|
||||
CloseClipboard();
|
||||
}
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ private:
|
|||
void PrimaryPreviewHover(int x, int y);
|
||||
void SecondPreviewHover(int x, int y);
|
||||
void PreviewExport(const GPUDebugBuffer *buffer);
|
||||
void PreviewToClipboard(const GPUDebugBuffer *buffer);
|
||||
void PreviewToClipboard(const GPUDebugBuffer *buffer, bool saveAlpha);
|
||||
static void DescribePixel(u32 pix, GPUDebugBufferFormat fmt, int x, int y, char desc[256]);
|
||||
static void DescribePixelRGBA(u32 pix, GPUDebugBufferFormat fmt, int x, int y, char desc[256]);
|
||||
void UpdateMenus();
|
||||
|
|
|
@ -157,6 +157,31 @@ namespace W32Util
|
|||
}
|
||||
ShellExecute(nullptr, nullptr, moduleFilename.c_str(), cmdline, workingDirectory.c_str(), SW_SHOW);
|
||||
}
|
||||
|
||||
ClipboardData::ClipboardData(const char *format, size_t sz) {
|
||||
format_ = RegisterClipboardFormatA(format);
|
||||
handle_ = format_ != 0 ? GlobalAlloc(GHND, sz) : 0;
|
||||
data = handle_ != 0 ? GlobalLock(handle_) : nullptr;
|
||||
}
|
||||
|
||||
ClipboardData::ClipboardData(UINT format, size_t sz) {
|
||||
format_ = format;
|
||||
handle_ = GlobalAlloc(GHND, sz);
|
||||
data = handle_ != 0 ? GlobalLock(handle_) : nullptr;
|
||||
}
|
||||
|
||||
ClipboardData::~ClipboardData() {
|
||||
if (handle_ != 0) {
|
||||
GlobalUnlock(handle_);
|
||||
GlobalFree(handle_);
|
||||
}
|
||||
}
|
||||
|
||||
void ClipboardData::Set() {
|
||||
if (format_ == 0 || handle_ == 0 || data == 0)
|
||||
return;
|
||||
SetClipboardData(format_, handle_);
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr UINT_PTR IDT_UPDATE = 0xC0DE0042;
|
||||
|
|
|
@ -14,6 +14,22 @@ namespace W32Util
|
|||
void ExitAndRestart(bool overrideArgs = false, const std::string &args = "");
|
||||
void SpawnNewInstance(bool overrideArgs = false, const std::string &args = "");
|
||||
void GetSelfExecuteParams(std::wstring &workingDirectory, std::wstring &moduleFilename);
|
||||
|
||||
struct ClipboardData {
|
||||
ClipboardData(const char *format, size_t sz);
|
||||
ClipboardData(UINT format, size_t sz);
|
||||
~ClipboardData();
|
||||
|
||||
void Set();
|
||||
|
||||
operator bool() {
|
||||
return data != nullptr;
|
||||
}
|
||||
|
||||
UINT format_;
|
||||
HANDLE handle_;
|
||||
void *data;
|
||||
};
|
||||
}
|
||||
|
||||
struct GenericListViewColumn
|
||||
|
|
|
@ -785,6 +785,7 @@ BEGIN
|
|||
BEGIN
|
||||
MENUITEM "Export Image...", ID_GEDBG_EXPORT_IMAGE
|
||||
MENUITEM "Copy Opaque Image", ID_GEDBG_COPY_IMAGE
|
||||
MENUITEM "Copy Image With Alpha", ID_GEDBG_COPY_IMAGE_ALPHA
|
||||
MENUITEM "Show Prim Preview", ID_GEDBG_ENABLE_PREVIEW
|
||||
END
|
||||
POPUP "matrixoptions"
|
||||
|
|
|
@ -334,6 +334,7 @@
|
|||
#define IDC_GEDBG_STEPVSYNC 40222
|
||||
#define ID_GEDBG_SETCOND 40223
|
||||
#define ID_GEDBG_COPY_IMAGE 40224
|
||||
#define ID_GEDBG_COPY_IMAGE_ALPHA 40225
|
||||
|
||||
|
||||
// Dummy option to let the buffered rendering hotkey cycle through all the options.
|
||||
|
@ -347,7 +348,7 @@
|
|||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 256
|
||||
#define _APS_NEXT_COMMAND_VALUE 40225
|
||||
#define _APS_NEXT_COMMAND_VALUE 40226
|
||||
#define _APS_NEXT_CONTROL_VALUE 1202
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
|
|
Loading…
Add table
Reference in a new issue