Skip to content

Commit

Permalink
Fixes necessary for SDR desktop capture to work correctly and operate…
Browse files Browse the repository at this point in the history
… compatibly with most imaging software
  • Loading branch information
Kaldaien committed Jul 19, 2024
1 parent 24a2d5b commit fbe42af
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 40 deletions.
19 changes: 6 additions & 13 deletions include/utility/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ float SKIV_Image_LinearToPQY (float N);
DirectX::XMVECTOR SKIV_Image_Rec709toICtCp (DirectX::XMVECTOR N);
DirectX::XMVECTOR SKIV_Image_ICtCptoRec709 (DirectX::XMVECTOR N);

bool SKIV_Image_CopyToClipboard (const DirectX::Image* pImage, bool snipped, bool isHDR, bool force_sRGB);
bool SKIV_Image_CopyToClipboard (const DirectX::Image* pImage, bool snipped, bool isHDR);
HRESULT SKIV_Image_SaveToDisk_HDR (const DirectX::Image& image, const wchar_t* wszFileName);
HRESULT SKIV_Image_SaveToDisk_SDR (const DirectX::Image& image, const wchar_t* wszFileName, bool force_sRGB);
HRESULT SKIV_Image_CaptureDesktop (DirectX::ScratchImage& image, POINT pos, int flags = 0x0);
Expand All @@ -132,7 +132,6 @@ struct skiv_image_desktop_s {
CComPtr <ID3D11ShaderResourceView> _srv = nullptr;
CComPtr <ID3D11Resource> _res = nullptr;
bool _hdr_image = false;
bool _srgb_hack = false;
ImVec2 _resolution = ImVec2 (0.0f, 0.0f);

bool process (void)
Expand All @@ -153,23 +152,18 @@ struct skiv_image_desktop_s {
_resolution.x = static_cast <float> (texDesc.Width);
_resolution.y = static_cast <float> (texDesc.Height);

// Non-sRGB DXGI formats still use sRGB gamma so they need a hack to appear properly
if (texDesc.Format == DXGI_FORMAT_R8G8B8A8_UNORM ||
texDesc.Format == DXGI_FORMAT_B8G8R8X8_UNORM)
_srgb_hack = true;

// HDR formats indicates we are working with a HDR capture
else if (texDesc.Format == DXGI_FORMAT_R10G10B10A2_UNORM ||
texDesc.Format == DXGI_FORMAT_R16G16B16A16_FLOAT ||
texDesc.Format == DXGI_FORMAT_R32G32B32A32_FLOAT)
if (texDesc.Format == DXGI_FORMAT_R10G10B10A2_UNORM ||
texDesc.Format == DXGI_FORMAT_R16G16B16A16_FLOAT ||
texDesc.Format == DXGI_FORMAT_R32G32B32A32_FLOAT)
_hdr_image = true;

#ifdef _DEBUG
ImGui::InsertNotification ({
ImGuiToastType::Info, 5000,
"Screen Capture Data",
"HDR: %i\nsRGB Hack: %i",
_hdr_image, _srgb_hack
"HDR: %i",
_hdr_image
});
#endif

Expand All @@ -186,7 +180,6 @@ struct skiv_image_desktop_s {
_res = nullptr;
_srv = nullptr;
_hdr_image = false;
_srgb_hack = false;
_resolution = ImVec2 (0.0f, 0.0f);
}
};
22 changes: 11 additions & 11 deletions src/SKIV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1922,7 +1922,6 @@ wWinMain ( _In_ HINSTANCE hInstance,
extern skiv_image_desktop_s SKIV_DesktopImage;

bool HDR_Image = SKIV_DesktopImage._hdr_image;
bool sRGB_Hack = SKIV_DesktopImage._srgb_hack;
bool SKIV_HDR = (HDR_Image ? SKIF_ImGui_IsViewportHDR (SKIF_ImGui_hWnd) : false);

// Temporarily engage HDR mode for SKIV during snipping mode
Expand All @@ -1936,22 +1935,23 @@ wWinMain ( _In_ HINSTANCE hInstance,
if (SKIV_DesktopImage._srv != nullptr)
{

static const ImVec2 srgb_uv0 = ImVec2 (0, 0), // _SRGB format
srgb_uv1 = ImVec2 (1, 1), // _SRGB format
force_srgb_uv0 = ImVec2 (-4096.0f, -4096.0f), // Non-sRGB formats needs a hack to force them to appear properly
force_srgb_uv1 = ImVec2 (-5120.0f, -5120.0f), // Non-sRGB formats needs a hack to force them to appear properly
static const ImVec2 srgb_uv0 = ImVec2 (0, 0),
srgb_uv1 = ImVec2 (1, 1),
hdr_uv0 = ImVec2 (-1024.0f, -1024.0f), // HDR formats
hdr_uv1 = ImVec2 (-2048.0f, -2048.0f); // HDR formats

SKIF_ImGui_OptImage (SKIV_DesktopImage._srv, SKIV_DesktopImage._resolution,
(HDR_Image && SKIV_HDR) ? hdr_uv0 : (sRGB_Hack) ? force_srgb_uv0 : srgb_uv0,
(HDR_Image && SKIV_HDR) ? hdr_uv1 : (sRGB_Hack) ? force_srgb_uv1 : srgb_uv1);
(HDR_Image && SKIV_HDR) ? hdr_uv0 : srgb_uv0,
(HDR_Image && SKIV_HDR) ? hdr_uv1 : srgb_uv1);

ImDrawList* draw_list =
ImGui::GetForegroundDrawList ();
// This visual feedback is unnecessary given snipping now defaults to drawing a rectangle around
// the nearest window to the cursor when initiated...
//
//ImDrawList* draw_list =
// ImGui::GetForegroundDrawList ();

// Draw a slightly dark transparent overlay on top of the captured image
draw_list->AddRectFilled (ImVec2 (0, 0), SKIV_DesktopImage._resolution, ImGui::GetColorU32 (IM_COL32(20, 20, 20, 80)));
//// Draw a slightly dark transparent overlay on top of the captured image
//draw_list->AddRectFilled (ImVec2 (0, 0), SKIV_DesktopImage._resolution, ImGui::GetColorU32 (IM_COL32(20, 20, 20, 80)));
}

static ImRect selection;
Expand Down
4 changes: 2 additions & 2 deletions src/tabs/viewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1420,7 +1420,7 @@ SKIF_UI_Tab_DrawViewer (void)
SUCCEEDED (DirectX::CopyRectangle (*captured_img.GetImages (), src_rect,
*subrect.GetImages (), DirectX::TEX_FILTER_DEFAULT, 0, 0)))
{
if (SKIV_Image_CopyToClipboard (subrect.GetImages (), true, cover.is_hdr, false))
if (SKIV_Image_CopyToClipboard (subrect.GetImages (), true, cover.is_hdr))
{
ImGui::InsertNotification (
{
Expand Down Expand Up @@ -1455,7 +1455,7 @@ SKIF_UI_Tab_DrawViewer (void)

else
{
if (SKIV_Image_CopyToClipboard (captured_img.GetImages (), false, cover.is_hdr, false))
if (SKIV_Image_CopyToClipboard (captured_img.GetImages (), false, cover.is_hdr))
{
ImGui::InsertNotification (
{
Expand Down
118 changes: 104 additions & 14 deletions src/utility/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <utility/fsutil.h>
#include <dxgi1_5.h>
#include <Shlwapi.h>
#include <windowsx.h>
#include <ImGuiNotify.hpp>
#include <utility/skif_imgui.h>
#include <utility/utility.h>
Expand Down Expand Up @@ -947,7 +948,7 @@ SKIV_PNG_CopyToClipboard (const DirectX::Image& image, const void *pData, size_t
return false;
}

bool SKIV_Image_CopyToClipboard (const DirectX::Image* pImage, bool snipped, bool isHDR, bool force_sRGB)
bool SKIV_Image_CopyToClipboard (const DirectX::Image* pImage, bool snipped, bool isHDR)
{
if (pImage == nullptr)
return false;
Expand Down Expand Up @@ -978,20 +979,102 @@ bool SKIV_Image_CopyToClipboard (const DirectX::Image* pImage, bool snipped, boo
PLOG_VERBOSE << "SKIF_Image_SaveToDisk_HDR ( ): FAILED";
}

else {
if (SUCCEEDED (SKIV_Image_SaveToDisk_SDR (*pImage, wsPNGPath.c_str(), force_sRGB)))
else
{
if (OpenClipboard (nullptr))
{
PLOG_VERBOSE << "SKIF_Image_SaveToDisk_SDR ( ): SUCCEEDED";
const int
_bpc =
(int)(DirectX::BitsPerPixel (pImage->format)),
_width =
(int)( pImage->width),
_height =
(int)( pImage->height);

DirectX::ScratchImage swizzled_sdr;
// Swizzle the image and handle gamma if necessary
if (pImage->format != DXGI_FORMAT_B8G8R8X8_UNORM_SRGB)
{
if (SUCCEEDED (DirectX::Convert (*pImage, DXGI_FORMAT_B8G8R8X8_UNORM_SRGB, DirectX::TEX_FILTER_DEFAULT, 0.0f, swizzled_sdr)))
{
pImage = swizzled_sdr.GetImage (0,0,0);
}
}
////SK_ReleaseAssert (pImage->format == DXGI_FORMAT_B8G8R8X8_UNORM ||
//// pImage->format == DXGI_FORMAT_B8G8R8A8_UNORM ||
//// pImage->format == DXGI_FORMAT_B8G8R8X8_UNORM_SRGB);

HBITMAP hBitmapCopy =
CreateBitmap (
_width, _height, 1,
_bpc, pImage->pixels
);

BITMAPINFOHEADER
bmh = { };
bmh.biSize = sizeof (BITMAPINFOHEADER);
bmh.biWidth = _width;
bmh.biHeight = -_height;
bmh.biPlanes = 1;
bmh.biBitCount = (WORD)_bpc;
bmh.biCompression = BI_RGB;
bmh.biXPelsPerMeter = 10;
bmh.biYPelsPerMeter = 10;

BITMAPINFO
bmi = { };
bmi.bmiHeader = bmh;

HDC hdcDIB =
CreateCompatibleDC (GetDC (nullptr));

void* bitplane = nullptr;

HBITMAP
hBitmap =
CreateDIBSection ( hdcDIB, &bmi, DIB_RGB_COLORS,
&bitplane, nullptr, 0 );
memcpy ( bitplane,
pImage->pixels,
static_cast <size_t> (_bpc / 8) *
static_cast <size_t> (_width ) *
static_cast <size_t> (_height )
);

HDC hdcSrc = CreateCompatibleDC (GetDC (nullptr));
HDC hdcDst = CreateCompatibleDC (GetDC (nullptr));

if ( hBitmap != nullptr &&
hBitmapCopy != nullptr )
{
auto hbmpSrc = (HBITMAP)SelectObject (hdcSrc, hBitmap);
auto hbmpDst = (HBITMAP)SelectObject (hdcDst, hBitmapCopy);

if (SKIV_PNG_CopyToClipboard (*pImage, wsPNGPath.c_str(), 0))
BitBlt (hdcDst, 0, 0, _width,
_height, hdcSrc, 0, 0, SRCCOPY);

SelectObject (hdcSrc, hbmpSrc);
SelectObject (hdcDst, hbmpDst);

EmptyClipboard ();
SetClipboardData (CF_BITMAP, hBitmapCopy);
}

CloseClipboard ();

DeleteDC (hdcSrc);
DeleteDC (hdcDst);
DeleteDC (hdcDIB);

if ( hBitmap != nullptr &&
hBitmapCopy != nullptr )
{
PLOG_VERBOSE << "SKIV_PNG_CopyToClipboard ( ): TRUE";
DeleteBitmap (hBitmap);
DeleteBitmap (hBitmapCopy);

return true;
}
}

else
PLOG_VERBOSE << "SKIF_Image_SaveToDisk_SDR ( ): FAILED";
}

return false;
Expand Down Expand Up @@ -1410,11 +1493,20 @@ SKIV_Image_CaptureDesktop (DirectX::ScratchImage& image, POINT point, int flags)
//DXGI_FORMAT_B8G8R8A8_UNORM
};

static constexpr int num_sdr_formats = 4;
static constexpr int num_all_formats = 7;

HMONITOR hMon = MonitorFromPoint (point, MONITOR_DEFAULTTONEAREST);

extern bool SKIF_Util_IsHDRActive (HMONITOR hMonitor);
bool bHDR = SKIF_Util_IsHDRActive (hMon);

CComPtr <IDXGIOutputDuplication> pDuplicator;

if (pOutput5)
pOutput5->DuplicateOutput1 (pDevice, 0x0, _ARRAYSIZE (capture_formats),
capture_formats, &pDuplicator.p);
pOutput5->DuplicateOutput1 (pDevice, 0x0, bHDR ? num_all_formats
: num_sdr_formats,
capture_formats, &pDuplicator.p);
else if (pOutput1)
pOutput1->DuplicateOutput (pDevice, &pDuplicator.p);

Expand Down Expand Up @@ -1617,9 +1709,7 @@ SKIV_Image_CaptureRegion (ImRect capture_area)
{
PLOG_VERBOSE << "DirectX::CopyRectangle ( ): SUCCEEDED";

extern bool
SKIV_Image_CopyToClipboard (const DirectX::Image* pImage, bool snipped, bool isHDR, bool force_sRGB);
if (SKIV_Image_CopyToClipboard (subrect.GetImages (), true, SKIV_DesktopImage._hdr_image, SKIV_DesktopImage._srgb_hack))
if (SKIV_Image_CopyToClipboard (subrect.GetImages (), true, SKIV_DesktopImage._hdr_image))
{
PLOG_VERBOSE << "SKIV_Image_CopyToClipboard ( ): SUCCEEDED";

Expand Down

0 comments on commit fbe42af

Please sign in to comment.