HostDisplay: Support screenshotting 16-bit display formats

This commit is contained in:
Connor McLaughlin
2020-12-18 01:59:32 +10:00
parent 189656cbc4
commit ca84925ff8
10 changed files with 196 additions and 76 deletions

View File

@ -1,4 +1,5 @@
#include "host_display.h"
#include "common/align.h"
#include "common/assert.h"
#include "common/file_system.h"
#include "common/log.h"
@ -277,9 +278,93 @@ std::tuple<float, float> HostDisplay::ConvertWindowCoordinatesToDisplayCoordinat
return std::make_tuple(display_x, display_y);
}
static bool ConvertTextureDataToRGBA8(u32 width, u32 height, std::vector<u32>& texture_data, u32& texture_data_stride,
HostDisplayPixelFormat format)
{
switch (format)
{
case HostDisplayPixelFormat::BGRA8:
{
for (u32 y = 0; y < height; y++)
{
u32* pixels = reinterpret_cast<u32*>(reinterpret_cast<u8*>(texture_data.data()) + (y * texture_data_stride));
for (u32 x = 0; x < width; x++)
pixels[x] = (pixels[x] & 0xFF00FF00) | ((pixels[x] & 0xFF) << 16) | ((pixels[x] >> 16) & 0xFF);
}
return true;
}
case HostDisplayPixelFormat::RGBA8:
return true;
case HostDisplayPixelFormat::RGB565:
{
std::vector<u32> temp(width * height);
for (u32 y = 0; y < height; y++)
{
const u8* pixels_in = reinterpret_cast<u8*>(texture_data.data()) + (y * texture_data_stride);
u32* pixels_out = &temp[y * width];
for (u32 x = 0; x < width; x++)
{
// RGB565 -> RGBA8
u16 pixel_in;
std::memcpy(&pixel_in, pixels_in, sizeof(u16));
pixels_in += sizeof(u16);
const u8 r5 = Truncate8(pixel_in >> 11);
const u8 g6 = Truncate8((pixel_in >> 5) & 0x3F);
const u8 b5 = Truncate8(pixel_in & 0x1F);
*(pixels_out++) = ZeroExtend32((r5 << 3) | (r5 & 7)) | (ZeroExtend32((g6 << 2) | (g6 & 3)) << 8) |
(ZeroExtend32((b5 << 3) | (b5 & 7)) << 16) | (0xFF000000u);
}
}
texture_data = std::move(temp);
texture_data_stride = sizeof(u32) * width;
return true;
}
case HostDisplayPixelFormat::RGBA5551:
{
std::vector<u32> temp(width * height);
for (u32 y = 0; y < height; y++)
{
const u8* pixels_in = reinterpret_cast<u8*>(texture_data.data()) + (y * texture_data_stride);
u32* pixels_out = &temp[y * width];
for (u32 x = 0; x < width; x++)
{
// RGBA5551 -> RGBA8
u16 pixel_in;
std::memcpy(&pixel_in, pixels_in, sizeof(u16));
pixels_in += sizeof(u16);
const u8 a1 = Truncate8(pixel_in >> 15);
const u8 r5 = Truncate8((pixel_in >> 10) & 0x1F);
const u8 g6 = Truncate8((pixel_in >> 5) & 0x1F);
const u8 b5 = Truncate8(pixel_in & 0x1F);
*(pixels_out++) = ZeroExtend32((r5 << 3) | (r5 & 7)) | (ZeroExtend32((g6 << 3) | (g6 & 7)) << 8) |
(ZeroExtend32((b5 << 3) | (b5 & 7)) << 16) | (a1 ? 0xFF000000u : 0u);
}
}
texture_data = std::move(temp);
texture_data_stride = sizeof(u32) * width;
return true;
}
default:
Log_ErrorPrintf("Unknown pixel format %u", static_cast<u32>(format));
return false;
}
}
static bool CompressAndWriteTextureToFile(u32 width, u32 height, std::string filename, FileSystem::ManagedCFilePtr fp,
bool clear_alpha, bool flip_y, u32 resize_width, u32 resize_height,
std::vector<u32> texture_data, u32 texture_data_stride)
std::vector<u32> texture_data, u32 texture_data_stride,
HostDisplayPixelFormat texture_format)
{
const char* extension = std::strrchr(filename.c_str(), '.');
@ -289,6 +374,9 @@ static bool CompressAndWriteTextureToFile(u32 width, u32 height, std::string fil
return false;
}
if (!ConvertTextureDataToRGBA8(width, height, texture_data, texture_data_stride, texture_format))
return false;
if (clear_alpha)
{
for (u32& pixel : texture_data)
@ -359,13 +447,13 @@ static bool CompressAndWriteTextureToFile(u32 width, u32 height, std::string fil
}
bool HostDisplay::WriteTextureToFile(const void* texture_handle, u32 x, u32 y, u32 width, u32 height,
std::string filename, bool clear_alpha /* = true */, bool flip_y /* = false */,
u32 resize_width /* = 0 */, u32 resize_height /* = 0 */,
HostDisplayPixelFormat format, std::string filename, bool clear_alpha /* = true */,
bool flip_y /* = false */, u32 resize_width /* = 0 */, u32 resize_height /* = 0 */,
bool compress_on_thread /* = false */)
{
std::vector<u32> texture_data(width * height);
u32 texture_data_stride = sizeof(u32) * width;
if (!DownloadTexture(texture_handle, x, y, width, height, texture_data.data(), texture_data_stride))
u32 texture_data_stride = Common::AlignUpPow2(GetDisplayPixelFormatSize(format) * width, 4);
if (!DownloadTexture(texture_handle, format, x, y, width, height, texture_data.data(), texture_data_stride))
{
Log_ErrorPrintf("Texture download failed");
return false;
@ -381,12 +469,13 @@ bool HostDisplay::WriteTextureToFile(const void* texture_handle, u32 x, u32 y, u
if (!compress_on_thread)
{
return CompressAndWriteTextureToFile(width, height, std::move(filename), std::move(fp), clear_alpha, flip_y,
resize_width, resize_height, std::move(texture_data), texture_data_stride);
resize_width, resize_height, std::move(texture_data), texture_data_stride,
format);
}
std::thread compress_thread(CompressAndWriteTextureToFile, width, height, std::move(filename), std::move(fp),
clear_alpha, flip_y, resize_width, resize_height, std::move(texture_data),
texture_data_stride);
texture_data_stride, format);
compress_thread.detach();
return true;
}
@ -394,7 +483,7 @@ bool HostDisplay::WriteTextureToFile(const void* texture_handle, u32 x, u32 y, u
bool HostDisplay::WriteDisplayTextureToFile(std::string filename, bool full_resolution /* = true */,
bool apply_aspect_ratio /* = true */, bool compress_on_thread /* = false */)
{
if (!m_display_texture_handle || m_display_texture_format != HostDisplayPixelFormat::RGBA8)
if (!m_display_texture_handle)
return false;
apply_aspect_ratio = (m_display_aspect_ratio > 0) ? apply_aspect_ratio : false;
@ -454,14 +543,14 @@ bool HostDisplay::WriteDisplayTextureToFile(std::string filename, bool full_reso
}
return WriteTextureToFile(m_display_texture_handle, m_display_texture_view_x, read_y, m_display_texture_view_width,
read_height, std::move(filename), true, flip_y, static_cast<u32>(resize_width),
static_cast<u32>(resize_height), compress_on_thread);
read_height, m_display_texture_format, std::move(filename), true, flip_y,
static_cast<u32>(resize_width), static_cast<u32>(resize_height), compress_on_thread);
}
bool HostDisplay::WriteDisplayTextureToBuffer(std::vector<u32>* buffer, u32 resize_width /* = 0 */,
u32 resize_height /* = 0 */, bool clear_alpha /* = true */)
{
if (!m_display_texture_handle || m_display_texture_format != HostDisplayPixelFormat::RGBA8)
if (!m_display_texture_handle)
return false;
const bool flip_y = (m_display_texture_view_height < 0);
@ -478,14 +567,17 @@ bool HostDisplay::WriteDisplayTextureToBuffer(std::vector<u32>* buffer, u32 resi
u32 width = static_cast<u32>(read_width);
u32 height = static_cast<u32>(read_height);
std::vector<u32> texture_data(width * height);
u32 texture_data_stride = sizeof(u32) * width;
if (!DownloadTexture(m_display_texture_handle, read_x, read_y, width, height, texture_data.data(),
texture_data_stride))
u32 texture_data_stride = Common::AlignUpPow2(GetDisplayPixelFormatSize(m_display_texture_format) * width, 4);
if (!DownloadTexture(m_display_texture_handle, m_display_texture_format, read_x, read_y, width, height,
texture_data.data(), texture_data_stride))
{
Log_ErrorPrintf("Failed to download texture from GPU.");
return false;
}
if (!ConvertTextureDataToRGBA8(width, height, texture_data, texture_data_stride, m_display_texture_format))
return false;
if (clear_alpha)
{
for (u32& pixel : texture_data)

View File

@ -95,8 +95,8 @@ public:
virtual void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data,
u32 data_stride) = 0;
virtual bool DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, void* out_data,
u32 out_data_stride) = 0;
virtual bool DownloadTexture(const void* texture_handle, HostDisplayPixelFormat texture_format, u32 x, u32 y,
u32 width, u32 height, void* out_data, u32 out_data_stride) = 0;
/// Returns false if the window was completely occluded.
virtual bool Render() = 0;
@ -194,8 +194,9 @@ public:
s32 window_height, s32 top_margin) const;
/// Helper function to save texture data to a PNG. If flip_y is set, the image will be flipped aka OpenGL.
bool WriteTextureToFile(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, std::string filename,
bool clear_alpha = true, bool flip_y = false, u32 resize_width = 0, u32 resize_height = 0,
bool WriteTextureToFile(const void* texture_handle, u32 x, u32 y, u32 width, u32 height,
HostDisplayPixelFormat format, std::string filename, bool clear_alpha = true,
bool flip_y = false, u32 resize_width = 0, u32 resize_height = 0,
bool compress_on_thread = false);
/// Helper function to save current display texture to PNG.