зеркало из https://github.com/microsoft/terminal.git
Added experimental.pixelShaderImagePath (#14073)
I realize I might be one of the few developers that care about custom shader support in terminal but I thought it's worth proposing it and see what you think. This is to support custom shaders with custom textures. I was thinking of exposing the background image to the shader but that felt complicated after looking into it. I have tested exploratively. I think the texture loader is possible to unit test so that is a possible improvement. The error reporting (as with other custom pixel shader code) is not very good. That is also an area that I could improve upon. I do think the risk of adding this is rather low as the new code is only executed when experimental.pixelShaderImagePath is set. ### Details Only added to the Atlas engine. Instead I load the texture using WIC into a shader resource view. When binding shader resources I test for presence of custom texture and bind it to register t1. The image loading code was found in [the D3D Texture documentation]. It's a mouthful but seems rather robust. Tested setting: "experimental.pixelShaderImagePath" 1. Tested not specifying it. 2. Tested setting it. 3. Tested changing it (the changes are picked up) 4. Tested invalid path 5. Tested a custom shader that made use of the custom texture. [the D3D Texture documentation]: https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-textures-how-to Co-authored-by: Mike Griese <migrie@microsoft.com> Co-authored-by: Leonard Hecker <lhecker@microsoft.com>
This commit is contained in:
Родитель
8a1e8ace97
Коммит
0ba680ad53
|
@ -0,0 +1,23 @@
|
|||
// Demo shader to show passing in an image using
|
||||
// experimental.pixelShaderImagePath. This shader simply displays the Terminal
|
||||
// contents on top of the given image.
|
||||
//
|
||||
// The image loaded by the terminal will be placed into the `image` texture.
|
||||
|
||||
SamplerState samplerState;
|
||||
Texture2D shaderTexture : register(t0);
|
||||
Texture2D image : register(t1);
|
||||
|
||||
cbuffer PixelShaderSettings {
|
||||
float Time;
|
||||
float Scale;
|
||||
float2 Resolution;
|
||||
float4 Background;
|
||||
};
|
||||
|
||||
float4 main(float4 pos : SV_POSITION, float2 tex : TEXCOORD) : SV_TARGET
|
||||
{
|
||||
float4 terminalColor = shaderTexture.Sample(samplerState, tex);
|
||||
float4 imageColor = image.Sample(samplerState, tex);
|
||||
return lerp(imageColor, terminalColor, terminalColor.a);
|
||||
}
|
|
@ -387,6 +387,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
|
||||
_renderEngine->SetRetroTerminalEffect(_settings->RetroTerminalEffect());
|
||||
_renderEngine->SetPixelShaderPath(_settings->PixelShaderPath());
|
||||
_renderEngine->SetPixelShaderImagePath(_settings->PixelShaderImagePath());
|
||||
_renderEngine->SetForceFullRepaintRendering(_settings->ForceFullRepaintRendering());
|
||||
_renderEngine->SetSoftwareRendering(_settings->SoftwareRendering());
|
||||
|
||||
|
@ -914,6 +915,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
|||
_renderEngine->SetSelectionBackground(til::color{ newAppearance->SelectionBackground() });
|
||||
_renderEngine->SetRetroTerminalEffect(newAppearance->RetroTerminalEffect());
|
||||
_renderEngine->SetPixelShaderPath(newAppearance->PixelShaderPath());
|
||||
_renderEngine->SetPixelShaderImagePath(newAppearance->PixelShaderImagePath());
|
||||
|
||||
// Incase EnableUnfocusedAcrylic is disabled and Focused Acrylic is set to true,
|
||||
// the terminal should ignore the unfocused opacity from settings.
|
||||
|
|
|
@ -18,5 +18,6 @@ namespace Microsoft.Terminal.Control
|
|||
// Experimental settings
|
||||
Boolean RetroTerminalEffect { get; };
|
||||
String PixelShaderPath { get; };
|
||||
String PixelShaderImagePath { get; };
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1602,7 +1602,7 @@ til::point Terminal::GetViewportRelativeCursorPosition() const noexcept
|
|||
// These functions are used by TerminalInput, which must build in conhost
|
||||
// against OneCore compatible signatures. See the definitions in
|
||||
// VtApiRedirection.hpp (which we cannot include cross-project.)
|
||||
// Since we do nto run on OneCore, we can dispense with the compatibility
|
||||
// Since we don't run on OneCore, we can dispense with the compatibility
|
||||
// shims.
|
||||
extern "C" UINT OneCoreSafeMapVirtualKeyW(_In_ UINT uCode, _In_ UINT uMapType)
|
||||
{
|
||||
|
|
|
@ -51,6 +51,7 @@ namespace Microsoft.Terminal.Settings.Model
|
|||
|
||||
INHERITABLE_APPEARANCE_SETTING(Boolean, RetroTerminalEffect);
|
||||
INHERITABLE_APPEARANCE_SETTING(String, PixelShaderPath);
|
||||
INHERITABLE_APPEARANCE_SETTING(String, PixelShaderImagePath);
|
||||
INHERITABLE_APPEARANCE_SETTING(IntenseStyle, IntenseTextStyle);
|
||||
INHERITABLE_APPEARANCE_SETTING(Microsoft.Terminal.Core.AdjustTextMode, AdjustIndistinguishableColors);
|
||||
INHERITABLE_APPEARANCE_SETTING(Double, Opacity);
|
||||
|
|
|
@ -124,6 +124,7 @@ Author(s):
|
|||
X(winrt::Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, "backgroundImageStretchMode", winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill) \
|
||||
X(bool, RetroTerminalEffect, "experimental.retroTerminalEffect", false) \
|
||||
X(hstring, PixelShaderPath, "experimental.pixelShaderPath") \
|
||||
X(hstring, PixelShaderImagePath, "experimental.pixelShaderImagePath") \
|
||||
X(ConvergedAlignment, BackgroundImageAlignment, "backgroundImageAlignment", ConvergedAlignment::Horizontal_Center | ConvergedAlignment::Vertical_Center) \
|
||||
X(hstring, BackgroundImagePath, "backgroundImage") \
|
||||
X(Model::IntenseStyle, IntenseTextStyle, "intenseTextStyle", Model::IntenseStyle::Bright) \
|
||||
|
|
|
@ -256,6 +256,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
|
||||
_RetroTerminalEffect = appearance.RetroTerminalEffect();
|
||||
_PixelShaderPath = winrt::hstring{ wil::ExpandEnvironmentStringsW<std::wstring>(appearance.PixelShaderPath().c_str()) };
|
||||
_PixelShaderImagePath = winrt::hstring{ wil::ExpandEnvironmentStringsW<std::wstring>(appearance.PixelShaderImagePath().c_str()) };
|
||||
|
||||
_IntenseIsBold = WI_IsFlagSet(appearance.IntenseTextStyle(), Microsoft::Terminal::Settings::Model::IntenseStyle::Bold);
|
||||
_IntenseIsBright = WI_IsFlagSet(appearance.IntenseTextStyle(), Microsoft::Terminal::Settings::Model::IntenseStyle::Bright);
|
||||
|
|
|
@ -160,6 +160,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
|||
INHERITABLE_SETTING(Model::TerminalSettings, bool, ForceVTInput, false);
|
||||
|
||||
INHERITABLE_SETTING(Model::TerminalSettings, hstring, PixelShaderPath);
|
||||
INHERITABLE_SETTING(Model::TerminalSettings, hstring, PixelShaderImagePath);
|
||||
|
||||
INHERITABLE_SETTING(Model::TerminalSettings, bool, Elevate, false);
|
||||
|
||||
|
|
|
@ -27,7 +27,8 @@
|
|||
X(winrt::Windows::UI::Xaml::HorizontalAlignment, BackgroundImageHorizontalAlignment, winrt::Windows::UI::Xaml::HorizontalAlignment::Center) \
|
||||
X(winrt::Windows::UI::Xaml::VerticalAlignment, BackgroundImageVerticalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment::Center) \
|
||||
X(bool, RetroTerminalEffect, false) \
|
||||
X(winrt::hstring, PixelShaderPath)
|
||||
X(winrt::hstring, PixelShaderPath) \
|
||||
X(winrt::hstring, PixelShaderImagePath)
|
||||
|
||||
// --------------------------- Core Settings ---------------------------
|
||||
// All of these settings are defined in ICoreSettings.
|
||||
|
|
|
@ -328,6 +328,11 @@ HRESULT AtlasEngine::Enable() noexcept
|
|||
return _api.s->misc->customPixelShaderPath;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::wstring_view AtlasEngine::GetPixelShaderImagePath() noexcept
|
||||
{
|
||||
return _api.s->misc->customPixelShaderImagePath;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool AtlasEngine::GetRetroTerminalEffect() const noexcept
|
||||
{
|
||||
return _api.s->misc->useRetroTerminalEffect;
|
||||
|
@ -400,6 +405,17 @@ try
|
|||
}
|
||||
CATCH_LOG()
|
||||
|
||||
void AtlasEngine::SetPixelShaderImagePath(std::wstring_view value) noexcept
|
||||
try
|
||||
{
|
||||
if (_api.s->misc->customPixelShaderImagePath != value)
|
||||
{
|
||||
_api.s.write()->misc.write()->customPixelShaderImagePath = value;
|
||||
_resolveTransparencySettings();
|
||||
}
|
||||
}
|
||||
CATCH_LOG()
|
||||
|
||||
void AtlasEngine::SetRetroTerminalEffect(bool enable) noexcept
|
||||
{
|
||||
if (_api.s->misc->useRetroTerminalEffect != enable)
|
||||
|
|
|
@ -643,7 +643,7 @@ void AtlasEngine::_recreateCellCountDependentResources()
|
|||
// and 40x (AMD) faster for allocations with an alignment of 32 or greater.
|
||||
// backgroundBitmapStride is a "count" of u32 and not in bytes,
|
||||
// so we round up to multiple of 8 because 8 * sizeof(u32) == 32.
|
||||
_p.colorBitmapRowStride = (static_cast<size_t>(_p.s->viewportCellCount.x) + 7) & ~7;
|
||||
_p.colorBitmapRowStride = alignForward<size_t>(_p.s->viewportCellCount.x, 8);
|
||||
_p.colorBitmapDepthStride = _p.colorBitmapRowStride * _p.s->viewportCellCount.y;
|
||||
_p.colorBitmap = Buffer<u32, 32>(_p.colorBitmapDepthStride * 2);
|
||||
_p.backgroundBitmap = { _p.colorBitmap.data(), _p.colorBitmapDepthStride };
|
||||
|
|
|
@ -61,6 +61,7 @@ namespace Microsoft::Console::Render::Atlas
|
|||
// DxRenderer - getter
|
||||
HRESULT Enable() noexcept override;
|
||||
[[nodiscard]] std::wstring_view GetPixelShaderPath() noexcept override;
|
||||
[[nodiscard]] std::wstring_view GetPixelShaderImagePath() noexcept override;
|
||||
[[nodiscard]] bool GetRetroTerminalEffect() const noexcept override;
|
||||
[[nodiscard]] float GetScaling() const noexcept override;
|
||||
[[nodiscard]] Types::Viewport GetViewportInCharacters(const Types::Viewport& viewInPixels) const noexcept override;
|
||||
|
@ -72,6 +73,7 @@ namespace Microsoft::Console::Render::Atlas
|
|||
void SetForceFullRepaintRendering(bool enable) noexcept override;
|
||||
[[nodiscard]] HRESULT SetHwnd(HWND hwnd) noexcept override;
|
||||
void SetPixelShaderPath(std::wstring_view value) noexcept override;
|
||||
void SetPixelShaderImagePath(std::wstring_view value) noexcept override;
|
||||
void SetRetroTerminalEffect(bool enable) noexcept override;
|
||||
void SetSelectionBackground(COLORREF color, float alpha = 0.5f) noexcept override;
|
||||
void SetSoftwareRendering(bool enable) noexcept override;
|
||||
|
|
|
@ -86,6 +86,13 @@ namespace Microsoft::Console::Render::Atlas
|
|||
return val < min ? min : (max < val ? max : val);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T alignForward(T val, T alignment) noexcept
|
||||
{
|
||||
assert((alignment & (alignment - 1)) == 0); // alignment should be a power of 2
|
||||
return (val + alignment - 1) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
inline constexpr D2D1_RECT_F GlyphRunEmptyBounds{ 1e38f, 1e38f, -1e38f, -1e38f };
|
||||
void GlyphRunAccumulateBounds(const ID2D1DeviceContext* d2dRenderTarget, D2D1_POINT_2F baselineOrigin, const DWRITE_GLYPH_RUN* glyphRun, D2D1_RECT_F& bounds);
|
||||
|
||||
|
|
|
@ -13,16 +13,13 @@
|
|||
|
||||
#include "BuiltinGlyphs.h"
|
||||
#include "dwrite.h"
|
||||
#include "wic.h"
|
||||
#include "../../types/inc/ColorFix.hpp"
|
||||
|
||||
#if ATLAS_DEBUG_SHOW_DIRTY || ATLAS_DEBUG_COLORIZE_GLYPH_ATLAS
|
||||
#include "colorbrewer.h"
|
||||
#endif
|
||||
|
||||
#if ATLAS_DEBUG_DUMP_RENDER_TARGET
|
||||
#include "wic.h"
|
||||
#endif
|
||||
|
||||
TIL_FAST_MATH_BEGIN
|
||||
|
||||
#pragma warning(disable : 4100) // '...': unreferenced formal parameter
|
||||
|
@ -345,6 +342,8 @@ void BackendD3D::_recreateCustomShader(const RenderingPayload& p)
|
|||
_customPixelShader.reset();
|
||||
_customShaderConstantBuffer.reset();
|
||||
_customShaderSamplerState.reset();
|
||||
_customShaderTexture.reset();
|
||||
_customShaderTextureView.reset();
|
||||
_requiresContinuousRedraw = false;
|
||||
|
||||
if (!p.s->misc->customPixelShaderPath.empty())
|
||||
|
@ -436,6 +435,23 @@ void BackendD3D::_recreateCustomShader(const RenderingPayload& p)
|
|||
p.warningCallback(D2DERR_SHADER_COMPILE_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
if (!p.s->misc->customPixelShaderImagePath.empty())
|
||||
{
|
||||
try
|
||||
{
|
||||
WIC::LoadTextureFromFile(p.device.get(), p.s->misc->customPixelShaderImagePath.c_str(), _customShaderTexture.addressof(), _customShaderTextureView.addressof());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
_customPixelShader.reset();
|
||||
if (p.warningCallback)
|
||||
{
|
||||
p.warningCallback(D2DERR_SHADER_COMPILE_FAILED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (p.s->misc->useRetroTerminalEffect)
|
||||
{
|
||||
|
@ -899,7 +915,7 @@ void BackendD3D::_recreateInstanceBuffers(const RenderingPayload& p)
|
|||
auto newSize = newCapacity * sizeof(QuadInstance);
|
||||
// Round up to multiples of 64kB to avoid reallocating too often.
|
||||
// 64kB is the minimum alignment for committed resources in D3D12.
|
||||
newSize = (newSize + 0xffff) & ~size_t{ 0xffff };
|
||||
newSize = alignForward<size_t>(newSize, 64 * 1024);
|
||||
newCapacity = newSize / sizeof(QuadInstance);
|
||||
|
||||
_instanceBuffer.reset();
|
||||
|
@ -2121,7 +2137,11 @@ void BackendD3D::_executeCustomShader(RenderingPayload& p)
|
|||
// PS: Pixel Shader
|
||||
p.deviceContext->PSSetShader(_customPixelShader.get(), nullptr, 0);
|
||||
p.deviceContext->PSSetConstantBuffers(0, 1, _customShaderConstantBuffer.addressof());
|
||||
p.deviceContext->PSSetShaderResources(0, 1, _customOffscreenTextureView.addressof());
|
||||
ID3D11ShaderResourceView* const resourceViews[]{
|
||||
_customOffscreenTextureView.get(), // The terminal contents
|
||||
_customShaderTextureView.get(), // the experimental.pixelShaderImagePath, if there is one
|
||||
};
|
||||
p.deviceContext->PSSetShaderResources(0, resourceViews[1] ? 2 : 1, &resourceViews[0]);
|
||||
p.deviceContext->PSSetSamplers(0, 1, _customShaderSamplerState.addressof());
|
||||
|
||||
// OM: Output Merger
|
||||
|
|
|
@ -247,6 +247,8 @@ namespace Microsoft::Console::Render::Atlas
|
|||
wil::com_ptr<ID3D11PixelShader> _customPixelShader;
|
||||
wil::com_ptr<ID3D11Buffer> _customShaderConstantBuffer;
|
||||
wil::com_ptr<ID3D11SamplerState> _customShaderSamplerState;
|
||||
wil::com_ptr<ID3D11Texture2D> _customShaderTexture;
|
||||
wil::com_ptr<ID3D11ShaderResourceView> _customShaderTextureView;
|
||||
std::chrono::steady_clock::time_point _customShaderStartTime;
|
||||
|
||||
wil::com_ptr<ID3D11Texture2D> _backgroundBitmap;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<Import Project="$(SolutionDir)src\common.nugetversions.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AtlasEngine.cpp" />
|
||||
<ClCompile Include="AtlasEngine.api.cpp" />
|
||||
<ClCompile Include="AtlasEngine.r.cpp" />
|
||||
<ClCompile Include="Backend.cpp" />
|
||||
|
@ -22,10 +23,11 @@
|
|||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AtlasEngine.cpp" />
|
||||
<ClCompile Include="stb_rect_pack.cpp" />
|
||||
<ClCompile Include="wic.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AtlasEngine.h" />
|
||||
<ClInclude Include="Backend.h" />
|
||||
<ClInclude Include="BackendD2D.h" />
|
||||
<ClInclude Include="BackendD3D.h" />
|
||||
|
@ -35,7 +37,6 @@
|
|||
<ClInclude Include="dwrite.h" />
|
||||
<ClInclude Include="DWriteTextAnalysis.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="AtlasEngine.h" />
|
||||
<ClInclude Include="wic.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -380,6 +380,7 @@ namespace Microsoft::Console::Render::Atlas
|
|||
u32 backgroundColor = 0;
|
||||
u32 selectionColor = 0x7fffffff;
|
||||
std::wstring customPixelShaderPath;
|
||||
std::wstring customPixelShaderImagePath;
|
||||
bool useRetroTerminalEffect = false;
|
||||
};
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <dxgi1_3.h>
|
||||
#include <dxgidebug.h>
|
||||
#include <VersionHelpers.h>
|
||||
#include <wincodec.h>
|
||||
|
||||
#include <gsl/gsl_util>
|
||||
#include <gsl/pointers>
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "pch.h"
|
||||
#include "wic.h"
|
||||
|
||||
#include "Backend.h"
|
||||
|
||||
using namespace Microsoft::Console::Render::Atlas;
|
||||
using namespace Microsoft::Console::Render::Atlas::WIC;
|
||||
|
||||
static wil::com_ptr<IWICImagingFactory2> getWicFactory()
|
||||
{
|
||||
static const auto coUninitialize = wil::CoInitializeEx();
|
||||
static const auto wicFactory = wil::CoCreateInstance<IWICImagingFactory2>(CLSID_WICImagingFactory2);
|
||||
return wicFactory;
|
||||
}
|
||||
|
||||
void WIC::SaveTextureToPNG(ID3D11DeviceContext* deviceContext, ID3D11Resource* source, double dpi, const wchar_t* fileName)
|
||||
{
|
||||
__assume(deviceContext != nullptr);
|
||||
__assume(source != nullptr);
|
||||
|
||||
wil::com_ptr<ID3D11Texture2D> texture;
|
||||
THROW_IF_FAILED(source->QueryInterface(IID_PPV_ARGS(texture.addressof())));
|
||||
|
||||
wil::com_ptr<ID3D11Device> d3dDevice;
|
||||
deviceContext->GetDevice(d3dDevice.addressof());
|
||||
|
||||
D3D11_TEXTURE2D_DESC desc{};
|
||||
texture->GetDesc(&desc);
|
||||
desc.BindFlags = 0;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
desc.Usage = D3D11_USAGE_STAGING;
|
||||
|
||||
wil::com_ptr<ID3D11Texture2D> staging;
|
||||
THROW_IF_FAILED(d3dDevice->CreateTexture2D(&desc, nullptr, staging.put()));
|
||||
|
||||
deviceContext->CopyResource(staging.get(), source);
|
||||
|
||||
const auto wicFactory = getWicFactory();
|
||||
|
||||
wil::com_ptr<IWICStream> stream;
|
||||
THROW_IF_FAILED(wicFactory->CreateStream(stream.addressof()));
|
||||
THROW_IF_FAILED(stream->InitializeFromFilename(fileName, GENERIC_WRITE));
|
||||
|
||||
wil::com_ptr<IWICBitmapEncoder> encoder;
|
||||
THROW_IF_FAILED(wicFactory->CreateEncoder(GUID_ContainerFormatPng, nullptr, encoder.addressof()));
|
||||
THROW_IF_FAILED(encoder->Initialize(stream.get(), WICBitmapEncoderNoCache));
|
||||
|
||||
wil::com_ptr<IWICBitmapFrameEncode> frame;
|
||||
wil::com_ptr<IPropertyBag2> props;
|
||||
THROW_IF_FAILED(encoder->CreateNewFrame(frame.addressof(), props.addressof()));
|
||||
THROW_IF_FAILED(frame->Initialize(props.get()));
|
||||
THROW_IF_FAILED(frame->SetSize(desc.Width, desc.Height));
|
||||
THROW_IF_FAILED(frame->SetResolution(dpi, dpi));
|
||||
auto pixelFormat = GUID_WICPixelFormat32bppBGRA;
|
||||
THROW_IF_FAILED(frame->SetPixelFormat(&pixelFormat));
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE mapped;
|
||||
THROW_IF_FAILED(deviceContext->Map(staging.get(), 0, D3D11_MAP_READ, 0, &mapped));
|
||||
THROW_IF_FAILED(frame->WritePixels(desc.Height, mapped.RowPitch, mapped.RowPitch * desc.Height, static_cast<BYTE*>(mapped.pData)));
|
||||
deviceContext->Unmap(staging.get(), 0);
|
||||
|
||||
THROW_IF_FAILED(frame->Commit());
|
||||
THROW_IF_FAILED(encoder->Commit());
|
||||
}
|
||||
|
||||
void WIC::LoadTextureFromFile(ID3D11Device* device, const wchar_t* fileName, ID3D11Texture2D** out_texture, ID3D11ShaderResourceView** out_textureView)
|
||||
{
|
||||
__assume(device != nullptr);
|
||||
__assume(fileName != nullptr);
|
||||
__assume(out_texture != nullptr);
|
||||
__assume(out_textureView != nullptr);
|
||||
|
||||
const auto wicFactory = getWicFactory();
|
||||
|
||||
wil::com_ptr<IWICBitmapDecoder> decoder;
|
||||
THROW_IF_FAILED(wicFactory->CreateDecoderFromFilename(fileName, nullptr, GENERIC_READ, WICDecodeMetadataCacheOnDemand, decoder.addressof()));
|
||||
|
||||
wil::com_ptr<IWICBitmapFrameDecode> frame;
|
||||
THROW_IF_FAILED(decoder->GetFrame(0, frame.addressof()));
|
||||
|
||||
WICPixelFormatGUID srcFormat;
|
||||
THROW_IF_FAILED(frame->GetPixelFormat(&srcFormat));
|
||||
|
||||
UINT srcWidth, srcHeight;
|
||||
THROW_IF_FAILED(frame->GetSize(&srcWidth, &srcHeight));
|
||||
|
||||
static constexpr u32 maxSizeU32 = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
||||
static constexpr f32 maxSizeF32 = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
||||
UINT tgtWidth = srcWidth;
|
||||
UINT tgtHeight = srcHeight;
|
||||
wil::com_ptr<IWICBitmapScaler> scaler;
|
||||
IWICBitmapSource* currentSource = frame.get();
|
||||
if (srcWidth > maxSizeU32 || srcHeight > maxSizeU32)
|
||||
{
|
||||
const auto ar = static_cast<float>(srcHeight) / static_cast<float>(srcWidth);
|
||||
if (srcWidth > srcHeight)
|
||||
{
|
||||
tgtWidth = maxSizeU32;
|
||||
tgtHeight = std::max<UINT>(1, lroundf(maxSizeF32 * ar));
|
||||
}
|
||||
else
|
||||
{
|
||||
tgtHeight = maxSizeU32;
|
||||
tgtWidth = std::max<UINT>(1, lroundf(maxSizeF32 / ar));
|
||||
}
|
||||
|
||||
THROW_IF_FAILED(wicFactory->CreateBitmapScaler(scaler.addressof()));
|
||||
THROW_IF_FAILED(scaler->Initialize(currentSource, tgtWidth, tgtHeight, WICBitmapInterpolationModeFant));
|
||||
currentSource = scaler.get();
|
||||
}
|
||||
|
||||
wil::com_ptr<IWICFormatConverter> converter;
|
||||
THROW_IF_FAILED(wicFactory->CreateFormatConverter(converter.addressof()));
|
||||
BOOL canConvert = FALSE;
|
||||
THROW_IF_FAILED(converter->CanConvert(srcFormat, GUID_WICPixelFormat32bppPBGRA, &canConvert));
|
||||
THROW_HR_IF(E_UNEXPECTED, !canConvert);
|
||||
THROW_IF_FAILED(converter->Initialize(currentSource, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeErrorDiffusion, nullptr, 0, WICBitmapPaletteTypeMedianCut));
|
||||
|
||||
// Aligning the width by 8 pixels, results in a 32 byte stride, which is better for memcpy on contemporary hardware.
|
||||
const uint64_t stride = alignForward<uint64_t>(tgtWidth, 8) * sizeof(u32);
|
||||
const uint64_t bytes = stride * static_cast<uint64_t>(tgtHeight);
|
||||
THROW_HR_IF(ERROR_ARITHMETIC_OVERFLOW, bytes > UINT32_MAX);
|
||||
|
||||
Buffer<BYTE, 32> buffer{ gsl::narrow_cast<size_t>(bytes) };
|
||||
THROW_IF_FAILED(converter->CopyPixels(nullptr, gsl::narrow_cast<UINT>(stride), gsl::narrow_cast<UINT>(bytes), buffer.data()));
|
||||
|
||||
const D3D11_TEXTURE2D_DESC desc = {
|
||||
.Width = tgtWidth,
|
||||
.Height = tgtHeight,
|
||||
.MipLevels = 1,
|
||||
.ArraySize = 1,
|
||||
.Format = DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
.SampleDesc = { 1, 0 },
|
||||
.Usage = D3D11_USAGE_IMMUTABLE,
|
||||
.BindFlags = D3D11_BIND_SHADER_RESOURCE,
|
||||
};
|
||||
const D3D11_SUBRESOURCE_DATA initData = {
|
||||
.pSysMem = buffer.data(),
|
||||
.SysMemPitch = gsl::narrow_cast<UINT>(stride),
|
||||
.SysMemSlicePitch = gsl::narrow_cast<UINT>(bytes),
|
||||
};
|
||||
wil::com_ptr<ID3D11Texture2D> texture;
|
||||
THROW_IF_FAILED(device->CreateTexture2D(&desc, &initData, texture.addressof()));
|
||||
|
||||
wil::com_ptr<ID3D11ShaderResourceView> textureView;
|
||||
THROW_IF_FAILED(device->CreateShaderResourceView(texture.get(), nullptr, textureView.addressof()));
|
||||
|
||||
*out_texture = texture.detach();
|
||||
*out_textureView = textureView.detach();
|
||||
}
|
|
@ -3,55 +3,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <wincodec.h>
|
||||
|
||||
inline void SaveTextureToPNG(ID3D11DeviceContext* deviceContext, ID3D11Resource* source, double dpi, const wchar_t* fileName)
|
||||
namespace Microsoft::Console::Render::Atlas::WIC
|
||||
{
|
||||
__assume(deviceContext != nullptr);
|
||||
__assume(source != nullptr);
|
||||
|
||||
wil::com_ptr<ID3D11Texture2D> texture;
|
||||
THROW_IF_FAILED(source->QueryInterface(IID_PPV_ARGS(texture.addressof())));
|
||||
|
||||
wil::com_ptr<ID3D11Device> d3dDevice;
|
||||
deviceContext->GetDevice(d3dDevice.addressof());
|
||||
|
||||
D3D11_TEXTURE2D_DESC desc{};
|
||||
texture->GetDesc(&desc);
|
||||
desc.BindFlags = 0;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
desc.Usage = D3D11_USAGE_STAGING;
|
||||
|
||||
wil::com_ptr<ID3D11Texture2D> staging;
|
||||
THROW_IF_FAILED(d3dDevice->CreateTexture2D(&desc, nullptr, staging.put()));
|
||||
|
||||
deviceContext->CopyResource(staging.get(), source);
|
||||
|
||||
static const auto coUninitialize = wil::CoInitializeEx();
|
||||
static const auto wicFactory = wil::CoCreateInstance<IWICImagingFactory2>(CLSID_WICImagingFactory2);
|
||||
|
||||
wil::com_ptr<IWICStream> stream;
|
||||
THROW_IF_FAILED(wicFactory->CreateStream(stream.addressof()));
|
||||
THROW_IF_FAILED(stream->InitializeFromFilename(fileName, GENERIC_WRITE));
|
||||
|
||||
wil::com_ptr<IWICBitmapEncoder> encoder;
|
||||
THROW_IF_FAILED(wicFactory->CreateEncoder(GUID_ContainerFormatPng, nullptr, encoder.addressof()));
|
||||
THROW_IF_FAILED(encoder->Initialize(stream.get(), WICBitmapEncoderNoCache));
|
||||
|
||||
wil::com_ptr<IWICBitmapFrameEncode> frame;
|
||||
wil::com_ptr<IPropertyBag2> props;
|
||||
THROW_IF_FAILED(encoder->CreateNewFrame(frame.addressof(), props.addressof()));
|
||||
THROW_IF_FAILED(frame->Initialize(props.get()));
|
||||
THROW_IF_FAILED(frame->SetSize(desc.Width, desc.Height));
|
||||
THROW_IF_FAILED(frame->SetResolution(dpi, dpi));
|
||||
auto pixelFormat = GUID_WICPixelFormat32bppBGRA;
|
||||
THROW_IF_FAILED(frame->SetPixelFormat(&pixelFormat));
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE mapped;
|
||||
THROW_IF_FAILED(deviceContext->Map(staging.get(), 0, D3D11_MAP_READ, 0, &mapped));
|
||||
THROW_IF_FAILED(frame->WritePixels(desc.Height, mapped.RowPitch, mapped.RowPitch * desc.Height, static_cast<BYTE*>(mapped.pData)));
|
||||
deviceContext->Unmap(staging.get(), 0);
|
||||
|
||||
THROW_IF_FAILED(frame->Commit());
|
||||
THROW_IF_FAILED(encoder->Commit());
|
||||
void SaveTextureToPNG(ID3D11DeviceContext* deviceContext, ID3D11Resource* source, double dpi, const wchar_t* fileName);
|
||||
void LoadTextureFromFile(ID3D11Device* device, const wchar_t* fileName, ID3D11Texture2D** out_texture, ID3D11ShaderResourceView** out_textureView);
|
||||
}
|
||||
|
|
|
@ -97,6 +97,7 @@ namespace Microsoft::Console::Render
|
|||
// DxRenderer - getter
|
||||
virtual HRESULT Enable() noexcept { return S_OK; }
|
||||
[[nodiscard]] virtual std::wstring_view GetPixelShaderPath() noexcept { return {}; }
|
||||
[[nodiscard]] virtual std::wstring_view GetPixelShaderImagePath() noexcept { return {}; }
|
||||
[[nodiscard]] virtual bool GetRetroTerminalEffect() const noexcept { return false; }
|
||||
[[nodiscard]] virtual float GetScaling() const noexcept { return 1; }
|
||||
[[nodiscard]] virtual Types::Viewport GetViewportInCharacters(const Types::Viewport& viewInPixels) const noexcept { return Types::Viewport::Empty(); }
|
||||
|
@ -108,6 +109,7 @@ namespace Microsoft::Console::Render
|
|||
virtual void SetForceFullRepaintRendering(bool enable) noexcept {}
|
||||
[[nodiscard]] virtual HRESULT SetHwnd(const HWND hwnd) noexcept { return E_NOTIMPL; }
|
||||
virtual void SetPixelShaderPath(std::wstring_view value) noexcept {}
|
||||
virtual void SetPixelShaderImagePath(std::wstring_view value) noexcept {}
|
||||
virtual void SetRetroTerminalEffect(bool enable) noexcept {}
|
||||
virtual void SetSelectionBackground(const COLORREF color, const float alpha = 0.5f) noexcept {}
|
||||
virtual void SetSoftwareRendering(bool enable) noexcept {}
|
||||
|
|
Загрузка…
Ссылка в новой задаче