chore: checkpoint current workspace changes
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
|
||||
namespace XCEngine::UI::Editor::Host {
|
||||
|
||||
@@ -85,6 +86,11 @@ bool NativeRenderer::Initialize(HWND hwnd) {
|
||||
}
|
||||
|
||||
void NativeRenderer::Shutdown() {
|
||||
while (!m_liveTextures.empty()) {
|
||||
auto it = m_liveTextures.begin();
|
||||
delete *it;
|
||||
m_liveTextures.erase(it);
|
||||
}
|
||||
m_textFormats.clear();
|
||||
m_solidBrush.Reset();
|
||||
m_renderTarget.Reset();
|
||||
@@ -149,6 +155,46 @@ const std::string& NativeRenderer::GetLastRenderError() const {
|
||||
return m_lastRenderError;
|
||||
}
|
||||
|
||||
bool NativeRenderer::LoadTextureFromFile(
|
||||
const std::filesystem::path& path,
|
||||
::XCEngine::UI::UITextureHandle& outTexture,
|
||||
std::string& outError) {
|
||||
outError.clear();
|
||||
ReleaseTexture(outTexture);
|
||||
|
||||
auto texture = std::make_unique<NativeTextureResource>();
|
||||
if (!DecodeTextureFile(path, *texture, outError)) {
|
||||
outTexture = {};
|
||||
return false;
|
||||
}
|
||||
|
||||
outTexture.nativeHandle = reinterpret_cast<std::uintptr_t>(texture.get());
|
||||
outTexture.width = texture->width;
|
||||
outTexture.height = texture->height;
|
||||
outTexture.kind = ::XCEngine::UI::UITextureHandleKind::DescriptorHandle;
|
||||
m_liveTextures.insert(texture.get());
|
||||
texture.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
void NativeRenderer::ReleaseTexture(::XCEngine::UI::UITextureHandle& texture) {
|
||||
if (!texture.IsValid()) {
|
||||
texture = {};
|
||||
return;
|
||||
}
|
||||
|
||||
auto* resource = reinterpret_cast<NativeTextureResource*>(texture.nativeHandle);
|
||||
if (resource != nullptr) {
|
||||
const auto found = m_liveTextures.find(resource);
|
||||
if (found != m_liveTextures.end()) {
|
||||
m_liveTextures.erase(found);
|
||||
delete resource;
|
||||
}
|
||||
}
|
||||
|
||||
texture = {};
|
||||
}
|
||||
|
||||
float NativeRenderer::MeasureTextWidth(
|
||||
const ::XCEngine::UI::Editor::UIEditorTextMeasureRequest& request) const {
|
||||
if (!m_dwriteFactory || request.text.empty()) {
|
||||
@@ -380,6 +426,7 @@ bool NativeRenderer::EnsureWicFactory(std::string& outError) {
|
||||
}
|
||||
|
||||
void NativeRenderer::DiscardRenderTarget() {
|
||||
InvalidateCachedTextureBitmaps(m_renderTarget.Get());
|
||||
m_solidBrush.Reset();
|
||||
m_renderTarget.Reset();
|
||||
}
|
||||
@@ -427,6 +474,133 @@ bool NativeRenderer::CreateDeviceResources() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void NativeRenderer::InvalidateCachedTextureBitmaps(const ID2D1RenderTarget* renderTarget) {
|
||||
for (NativeTextureResource* texture : m_liveTextures) {
|
||||
if (texture == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (renderTarget == nullptr || texture->cachedTarget == renderTarget) {
|
||||
texture->cachedBitmap.Reset();
|
||||
texture->cachedTarget = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool NativeRenderer::DecodeTextureFile(
|
||||
const std::filesystem::path& path,
|
||||
NativeTextureResource& outTexture,
|
||||
std::string& outError) {
|
||||
outError.clear();
|
||||
if (!EnsureWicFactory(outError)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::wstring widePath = path.wstring();
|
||||
Microsoft::WRL::ComPtr<IWICBitmapDecoder> decoder;
|
||||
HRESULT hr = m_wicFactory->CreateDecoderFromFilename(
|
||||
widePath.c_str(),
|
||||
nullptr,
|
||||
GENERIC_READ,
|
||||
WICDecodeMetadataCacheOnLoad,
|
||||
decoder.ReleaseAndGetAddressOf());
|
||||
if (FAILED(hr) || !decoder) {
|
||||
outError = HrToString("IWICImagingFactory::CreateDecoderFromFilename", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
Microsoft::WRL::ComPtr<IWICBitmapFrameDecode> frame;
|
||||
hr = decoder->GetFrame(0u, frame.ReleaseAndGetAddressOf());
|
||||
if (FAILED(hr) || !frame) {
|
||||
outError = HrToString("IWICBitmapDecoder::GetFrame", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
Microsoft::WRL::ComPtr<IWICFormatConverter> converter;
|
||||
hr = m_wicFactory->CreateFormatConverter(converter.ReleaseAndGetAddressOf());
|
||||
if (FAILED(hr) || !converter) {
|
||||
outError = HrToString("IWICImagingFactory::CreateFormatConverter", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
hr = converter->Initialize(
|
||||
frame.Get(),
|
||||
GUID_WICPixelFormat32bppPBGRA,
|
||||
WICBitmapDitherTypeNone,
|
||||
nullptr,
|
||||
0.0f,
|
||||
WICBitmapPaletteTypeCustom);
|
||||
if (FAILED(hr)) {
|
||||
outError = HrToString("IWICFormatConverter::Initialize", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
UINT width = 0u;
|
||||
UINT height = 0u;
|
||||
hr = converter->GetSize(&width, &height);
|
||||
if (FAILED(hr) || width == 0u || height == 0u) {
|
||||
outError = HrToString("IWICBitmapSource::GetSize", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> pixels(
|
||||
static_cast<std::size_t>(width) * static_cast<std::size_t>(height) * 4u);
|
||||
hr = converter->CopyPixels(
|
||||
nullptr,
|
||||
width * 4u,
|
||||
static_cast<UINT>(pixels.size()),
|
||||
pixels.data());
|
||||
if (FAILED(hr)) {
|
||||
outError = HrToString("IWICBitmapSource::CopyPixels", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
outTexture.pixels = std::move(pixels);
|
||||
outTexture.width = width;
|
||||
outTexture.height = height;
|
||||
outTexture.cachedBitmap.Reset();
|
||||
outTexture.cachedTarget = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeRenderer::ResolveTextureBitmap(
|
||||
ID2D1RenderTarget& renderTarget,
|
||||
NativeTextureResource& texture,
|
||||
Microsoft::WRL::ComPtr<ID2D1Bitmap>& outBitmap) {
|
||||
outBitmap.Reset();
|
||||
if (texture.width == 0u || texture.height == 0u || texture.pixels.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (texture.cachedBitmap && texture.cachedTarget == &renderTarget) {
|
||||
outBitmap = texture.cachedBitmap;
|
||||
return true;
|
||||
}
|
||||
|
||||
Microsoft::WRL::ComPtr<ID2D1Bitmap> bitmap;
|
||||
const D2D1_BITMAP_PROPERTIES properties = D2D1::BitmapProperties(
|
||||
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
|
||||
kBaseDpi,
|
||||
kBaseDpi);
|
||||
const HRESULT hr = renderTarget.CreateBitmap(
|
||||
D2D1::SizeU(texture.width, texture.height),
|
||||
texture.pixels.data(),
|
||||
texture.width * 4u,
|
||||
&properties,
|
||||
bitmap.ReleaseAndGetAddressOf());
|
||||
if (FAILED(hr) || !bitmap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (&renderTarget == m_renderTarget.Get()) {
|
||||
texture.cachedBitmap = bitmap;
|
||||
texture.cachedTarget = &renderTarget;
|
||||
}
|
||||
|
||||
outBitmap = std::move(bitmap);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NativeRenderer::RenderToTarget(
|
||||
ID2D1RenderTarget& renderTarget,
|
||||
ID2D1SolidColorBrush& solidBrush,
|
||||
@@ -624,8 +798,29 @@ void NativeRenderer::RenderCommand(
|
||||
break;
|
||||
}
|
||||
|
||||
auto* texture = reinterpret_cast<NativeTextureResource*>(command.texture.nativeHandle);
|
||||
if (texture == nullptr || m_liveTextures.find(texture) == m_liveTextures.end()) {
|
||||
const D2D1_RECT_F rect = ToD2DRect(command.rect, dpiScale);
|
||||
renderTarget.DrawRectangle(rect, &solidBrush, 1.0f);
|
||||
break;
|
||||
}
|
||||
|
||||
Microsoft::WRL::ComPtr<ID2D1Bitmap> bitmap;
|
||||
if (!ResolveTextureBitmap(renderTarget, *texture, bitmap) || !bitmap) {
|
||||
break;
|
||||
}
|
||||
|
||||
const D2D1_RECT_F rect = ToD2DRect(command.rect, dpiScale);
|
||||
renderTarget.DrawRectangle(rect, &solidBrush, 1.0f);
|
||||
const float sourceLeft = static_cast<float>(texture->width) * std::clamp(command.uvMin.x, 0.0f, 1.0f);
|
||||
const float sourceTop = static_cast<float>(texture->height) * std::clamp(command.uvMin.y, 0.0f, 1.0f);
|
||||
const float sourceRight = static_cast<float>(texture->width) * std::clamp(command.uvMax.x, 0.0f, 1.0f);
|
||||
const float sourceBottom = static_cast<float>(texture->height) * std::clamp(command.uvMax.y, 0.0f, 1.0f);
|
||||
renderTarget.DrawBitmap(
|
||||
bitmap.Get(),
|
||||
rect,
|
||||
std::clamp(command.color.a, 0.0f, 1.0f),
|
||||
D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
|
||||
D2D1::RectF(sourceLeft, sourceTop, sourceRight, sourceBottom));
|
||||
break;
|
||||
}
|
||||
case ::XCEngine::UI::UIDrawCommandType::PushClipRect: {
|
||||
|
||||
Reference in New Issue
Block a user