751 lines
16 KiB
C++
751 lines
16 KiB
C++
// Copyright (c) 2016-2018 Easy2D - Nomango
|
||
//
|
||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
// of this software and associated documentation files (the "Software"), to deal
|
||
// in the Software without restriction, including without limitation the rights
|
||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
// copies of the Software, and to permit persons to whom the Software is
|
||
// furnished to do so, subject to the following conditions:
|
||
//
|
||
// The above copyright notice and this permission notice shall be included in
|
||
// all copies or substantial portions of the Software.
|
||
//
|
||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
// THE SOFTWARE.
|
||
|
||
#include "render.h"
|
||
#include "time.h"
|
||
#include "base.h"
|
||
#include "logs.h"
|
||
#include "modules.h"
|
||
#include "Image.h"
|
||
|
||
#pragma comment(lib, "d2d1.lib")
|
||
#pragma comment(lib, "dwrite.lib")
|
||
|
||
namespace easy2d
|
||
{
|
||
namespace devices
|
||
{
|
||
GraphicsDevice::GraphicsDevice()
|
||
: fps_text_format_(nullptr)
|
||
, fps_text_layout_(nullptr)
|
||
, clear_color_(D2D1::ColorF(D2D1::ColorF::Black))
|
||
, initialized(false)
|
||
{
|
||
ZeroMemory(&d2d, sizeof(D2DResources));
|
||
|
||
modules::Init();
|
||
}
|
||
|
||
GraphicsDevice::~GraphicsDevice()
|
||
{
|
||
E2D_LOG("Destroying graphics device");
|
||
|
||
ClearImageCache();
|
||
|
||
SafeRelease(fps_text_format_);
|
||
SafeRelease(fps_text_layout_);
|
||
|
||
SafeRelease(d2d.text_renderer);
|
||
SafeRelease(d2d.solid_brush);
|
||
SafeRelease(d2d.render_target);
|
||
|
||
SafeRelease(d2d.miter_stroke_style);
|
||
SafeRelease(d2d.bevel_stroke_style);
|
||
SafeRelease(d2d.round_stroke_style);
|
||
|
||
SafeRelease(d2d.imaging_factory);
|
||
SafeRelease(d2d.write_factory);
|
||
SafeRelease(d2d.factory);
|
||
|
||
modules::Destroy();
|
||
}
|
||
|
||
void GraphicsDevice::Init(HWND hwnd, bool debug)
|
||
{
|
||
if (initialized)
|
||
return;
|
||
|
||
E2D_LOG("Initing graphics device");
|
||
|
||
D2D1_FACTORY_OPTIONS options{ debug ? D2D1_DEBUG_LEVEL_INFORMATION : D2D1_DEBUG_LEVEL_NONE };
|
||
ThrowIfFailed(
|
||
D2D1CreateFactory(
|
||
D2D1_FACTORY_TYPE_SINGLE_THREADED,
|
||
__uuidof(ID2D1Factory),
|
||
&options,
|
||
reinterpret_cast<void**>(&d2d.factory)
|
||
)
|
||
);
|
||
|
||
ThrowIfFailed(
|
||
CoCreateInstance(
|
||
CLSID_WICImagingFactory,
|
||
nullptr,
|
||
CLSCTX_INPROC_SERVER,
|
||
__uuidof(IWICImagingFactory),
|
||
reinterpret_cast<void**>(&d2d.imaging_factory)
|
||
)
|
||
);
|
||
|
||
ThrowIfFailed(
|
||
DWriteCreateFactory(
|
||
DWRITE_FACTORY_TYPE_SHARED,
|
||
__uuidof(IDWriteFactory),
|
||
reinterpret_cast<IUnknown**>(&d2d.write_factory)
|
||
)
|
||
);
|
||
|
||
auto stroke_style = D2D1::StrokeStyleProperties(
|
||
D2D1_CAP_STYLE_FLAT,
|
||
D2D1_CAP_STYLE_FLAT,
|
||
D2D1_CAP_STYLE_FLAT,
|
||
D2D1_LINE_JOIN_MITER,
|
||
2.0f,
|
||
D2D1_DASH_STYLE_SOLID,
|
||
0.0f
|
||
);
|
||
|
||
ThrowIfFailed(
|
||
d2d.factory->CreateStrokeStyle(
|
||
stroke_style,
|
||
nullptr,
|
||
0,
|
||
&d2d.miter_stroke_style
|
||
)
|
||
);
|
||
|
||
stroke_style.lineJoin = D2D1_LINE_JOIN_BEVEL;
|
||
|
||
ThrowIfFailed(
|
||
d2d.factory->CreateStrokeStyle(
|
||
stroke_style,
|
||
nullptr,
|
||
0,
|
||
&d2d.bevel_stroke_style
|
||
)
|
||
);
|
||
|
||
stroke_style.lineJoin = D2D1_LINE_JOIN_ROUND;
|
||
|
||
ThrowIfFailed(
|
||
d2d.factory->CreateStrokeStyle(
|
||
stroke_style,
|
||
nullptr,
|
||
0,
|
||
&d2d.round_stroke_style
|
||
)
|
||
);
|
||
|
||
CreateDeviceResources(hwnd);
|
||
|
||
initialized = true;
|
||
}
|
||
|
||
void GraphicsDevice::BeginDraw(HWND hwnd)
|
||
{
|
||
CreateDeviceResources(hwnd);
|
||
|
||
d2d.render_target->BeginDraw();
|
||
d2d.render_target->Clear(clear_color_);
|
||
}
|
||
|
||
void GraphicsDevice::EndDraw()
|
||
{
|
||
HRESULT hr = d2d.render_target->EndDraw();
|
||
|
||
if (hr == D2DERR_RECREATE_TARGET)
|
||
{
|
||
// <20><><EFBFBD><EFBFBD> Direct3D <20>豸<EFBFBD><E8B1B8>ִ<EFBFBD>й<EFBFBD><D0B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʧ<EFBFBD><CAA7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0><EFBFBD>豸<EFBFBD><E8B1B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD>ε<EFBFBD><CEB5><EFBFBD>ʱ<EFBFBD>ؽ<EFBFBD><D8BD><EFBFBD>Դ
|
||
hr = S_OK;
|
||
|
||
SafeRelease(fps_text_format_);
|
||
SafeRelease(fps_text_layout_);
|
||
SafeRelease(d2d.text_renderer);
|
||
SafeRelease(d2d.solid_brush);
|
||
SafeRelease(d2d.render_target);
|
||
}
|
||
|
||
ThrowIfFailed(hr);
|
||
}
|
||
|
||
void GraphicsDevice::ClearImageCache()
|
||
{
|
||
if (bitmap_cache_.empty())
|
||
return;
|
||
|
||
for (const auto& bitmap : bitmap_cache_)
|
||
{
|
||
bitmap.second->Release();
|
||
}
|
||
bitmap_cache_.clear();
|
||
}
|
||
|
||
HRESULT GraphicsDevice::CreateRectGeometry(
|
||
const math::Matrix& matrix,
|
||
const Size& size,
|
||
ID2D1Geometry** geometry
|
||
) const
|
||
{
|
||
if (!d2d.factory)
|
||
return E_UNEXPECTED;
|
||
|
||
HRESULT hr;
|
||
|
||
ID2D1RectangleGeometry * rectangle = nullptr;
|
||
ID2D1TransformedGeometry * transformed = nullptr;
|
||
|
||
hr = d2d.factory->CreateRectangleGeometry(
|
||
D2D1::RectF(0, 0, size.width, size.height),
|
||
&rectangle
|
||
);
|
||
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
hr = d2d.factory->CreateTransformedGeometry(
|
||
rectangle,
|
||
matrix,
|
||
&transformed
|
||
);
|
||
}
|
||
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
*geometry = transformed;
|
||
}
|
||
|
||
SafeRelease(rectangle);
|
||
return hr;
|
||
}
|
||
|
||
HRESULT GraphicsDevice::CreateTextFormat(IDWriteTextFormat ** text_format, const Font & font) const
|
||
{
|
||
if (!d2d.write_factory)
|
||
return E_UNEXPECTED;
|
||
|
||
return d2d.write_factory->CreateTextFormat(
|
||
font.family.c_str(),
|
||
nullptr,
|
||
DWRITE_FONT_WEIGHT(font.weight),
|
||
font.italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL,
|
||
DWRITE_FONT_STRETCH_NORMAL,
|
||
font.size,
|
||
L"",
|
||
text_format
|
||
);
|
||
}
|
||
|
||
HRESULT GraphicsDevice::CreateTextLayout(IDWriteTextLayout ** text_layout, const String & text, IDWriteTextFormat * text_format, float wrap_width) const
|
||
{
|
||
if (!d2d.write_factory)
|
||
return E_UNEXPECTED;
|
||
|
||
UINT32 length = static_cast<UINT32>(text.length());
|
||
return d2d.write_factory->CreateTextLayout(
|
||
text.c_str(),
|
||
length,
|
||
text_format,
|
||
wrap_width,
|
||
0,
|
||
text_layout
|
||
);
|
||
}
|
||
|
||
HRESULT GraphicsDevice::CreateLayer(ID2D1Layer ** layer)
|
||
{
|
||
if (!d2d.render_target)
|
||
return E_UNEXPECTED;
|
||
|
||
return d2d.render_target->CreateLayer(layer);
|
||
}
|
||
|
||
HRESULT GraphicsDevice::DrawGeometry(
|
||
ID2D1Geometry * geometry,
|
||
const Color & border_color,
|
||
float opacity,
|
||
float stroke_width,
|
||
StrokeStyle stroke
|
||
)
|
||
{
|
||
if (!d2d.solid_brush ||
|
||
!d2d.render_target)
|
||
return E_UNEXPECTED;
|
||
|
||
d2d.solid_brush->SetColor(border_color);
|
||
d2d.solid_brush->SetOpacity(opacity);
|
||
d2d.render_target->DrawGeometry(
|
||
geometry,
|
||
d2d.solid_brush,
|
||
stroke_width,
|
||
GetStrokeStyle(stroke)
|
||
);
|
||
return S_OK;
|
||
}
|
||
|
||
ID2D1StrokeStyle * GraphicsDevice::GetStrokeStyle(StrokeStyle stroke) const
|
||
{
|
||
ID2D1StrokeStyle * stroke_style = nullptr;
|
||
switch (stroke)
|
||
{
|
||
case StrokeStyle::Miter:
|
||
stroke_style = d2d.miter_stroke_style;
|
||
break;
|
||
case StrokeStyle::Bevel:
|
||
stroke_style = d2d.bevel_stroke_style;
|
||
break;
|
||
case StrokeStyle::Round:
|
||
stroke_style = d2d.round_stroke_style;
|
||
break;
|
||
}
|
||
return stroke_style;
|
||
}
|
||
|
||
HRESULT GraphicsDevice::DrawImage(
|
||
spImage const& image,
|
||
float opacity,
|
||
const Rect & dest_rect,
|
||
const Rect & source_rect
|
||
)
|
||
{
|
||
if (!d2d.render_target)
|
||
return E_UNEXPECTED;
|
||
|
||
d2d.render_target->DrawBitmap(
|
||
image->GetBitmap(),
|
||
dest_rect,
|
||
opacity,
|
||
D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
|
||
source_rect
|
||
);
|
||
return S_OK;
|
||
}
|
||
|
||
HRESULT GraphicsDevice::DrawTextLayout(IDWriteTextLayout * text_layout)
|
||
{
|
||
if (!d2d.text_renderer)
|
||
return E_UNEXPECTED;
|
||
|
||
return text_layout->Draw(nullptr, d2d.text_renderer, 0, 0);
|
||
}
|
||
|
||
HRESULT GraphicsDevice::PushClip(const math::Matrix & clip_matrix, const Size & clip_size)
|
||
{
|
||
if (!d2d.render_target)
|
||
return E_UNEXPECTED;
|
||
|
||
d2d.render_target->SetTransform(clip_matrix);
|
||
d2d.render_target->PushAxisAlignedClip(
|
||
D2D1::RectF(0, 0, clip_size.width, clip_size.height),
|
||
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
|
||
);
|
||
return S_OK;
|
||
}
|
||
|
||
HRESULT GraphicsDevice::PopClip()
|
||
{
|
||
if (!d2d.render_target)
|
||
return E_UNEXPECTED;
|
||
|
||
d2d.render_target->PopAxisAlignedClip();
|
||
return S_OK;
|
||
}
|
||
|
||
HRESULT GraphicsDevice::PushLayer(ID2D1Layer * layer, LayerProperties properties)
|
||
{
|
||
if (!d2d.render_target ||
|
||
!d2d.solid_brush)
|
||
return E_UNEXPECTED;
|
||
|
||
d2d.render_target->PushLayer(
|
||
D2D1::LayerParameters(
|
||
properties.area,
|
||
nullptr,
|
||
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
|
||
D2D1::Matrix3x2F::Identity(),
|
||
properties.opacity,
|
||
d2d.solid_brush,
|
||
D2D1_LAYER_OPTIONS_NONE
|
||
),
|
||
layer
|
||
);
|
||
return S_OK;
|
||
}
|
||
|
||
HRESULT GraphicsDevice::PopLayer()
|
||
{
|
||
if (!d2d.render_target)
|
||
return E_UNEXPECTED;
|
||
|
||
d2d.render_target->PopLayer();
|
||
return S_OK;
|
||
}
|
||
|
||
HRESULT GraphicsDevice::CreateBitmapFromFile(const String & file_path, ID2D1Bitmap ** bitmap)
|
||
{
|
||
if (d2d.imaging_factory == nullptr ||
|
||
d2d.render_target == nullptr)
|
||
{
|
||
return E_UNEXPECTED;
|
||
}
|
||
|
||
if (bitmap == nullptr)
|
||
{
|
||
return E_POINTER;
|
||
}
|
||
|
||
size_t hash_code = std::hash<String>{}(file_path);
|
||
if (bitmap_cache_.find(hash_code) != bitmap_cache_.end())
|
||
{
|
||
*bitmap = bitmap_cache_[hash_code];
|
||
return S_OK;
|
||
}
|
||
|
||
IWICBitmapDecoder* decoder = nullptr;
|
||
IWICBitmapFrameDecode* source = nullptr;
|
||
IWICStream* stream = nullptr;
|
||
IWICFormatConverter* converter = nullptr;
|
||
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
HRESULT hr = d2d.imaging_factory->CreateDecoderFromFilename(
|
||
file_path.c_str(),
|
||
nullptr,
|
||
GENERIC_READ,
|
||
WICDecodeMetadataCacheOnLoad,
|
||
&decoder
|
||
);
|
||
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
hr = decoder->GetFrame(0, &source);
|
||
}
|
||
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
// <20><><EFBFBD><EFBFBD>ͼƬ<CDBC><C6AC>ʽת<CABD><D7AA><EFBFBD><EFBFBD>
|
||
hr = d2d.imaging_factory->CreateFormatConverter(&converter);
|
||
}
|
||
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
// ͼƬ<CDBC><C6AC>ʽת<CABD><D7AA><EFBFBD><EFBFBD> 32bppPBGRA
|
||
hr = converter->Initialize(
|
||
source,
|
||
GUID_WICPixelFormat32bppPBGRA,
|
||
WICBitmapDitherTypeNone,
|
||
nullptr,
|
||
0.f,
|
||
WICBitmapPaletteTypeMedianCut
|
||
);
|
||
}
|
||
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
// <20><> WIC λͼ<CEBB><CDBC><EFBFBD><EFBFBD>һ<EFBFBD><D2BB> Direct2D λͼ
|
||
hr = d2d.render_target->CreateBitmapFromWicBitmap(
|
||
converter,
|
||
nullptr,
|
||
bitmap
|
||
);
|
||
}
|
||
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
bitmap_cache_.insert(std::make_pair(hash_code, *bitmap));
|
||
}
|
||
|
||
// <20>ͷ<EFBFBD><CDB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
|
||
SafeRelease(decoder);
|
||
SafeRelease(source);
|
||
SafeRelease(stream);
|
||
SafeRelease(converter);
|
||
|
||
return hr;
|
||
}
|
||
|
||
HRESULT GraphicsDevice::CreateBitmapFromResource(Resource & res, ID2D1Bitmap ** bitmap)
|
||
{
|
||
if (d2d.imaging_factory == nullptr ||
|
||
d2d.render_target == nullptr)
|
||
{
|
||
return E_UNEXPECTED;
|
||
}
|
||
|
||
if (bitmap == nullptr)
|
||
{
|
||
return E_POINTER;
|
||
}
|
||
|
||
size_t hash_code = res.GetHashCode();
|
||
if (bitmap_cache_.find(hash_code) != bitmap_cache_.end())
|
||
{
|
||
*bitmap = bitmap_cache_[hash_code];
|
||
return S_OK;
|
||
}
|
||
|
||
HRESULT hr;
|
||
|
||
HINSTANCE hinstance = GetModuleHandle(nullptr);
|
||
IWICBitmapDecoder* decoder = nullptr;
|
||
IWICBitmapFrameDecode* source = nullptr;
|
||
IWICStream* stream = nullptr;
|
||
IWICFormatConverter* converter = nullptr;
|
||
ResourceData buffer;
|
||
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
|
||
hr = res.Load(&buffer) ? S_OK : E_FAIL;
|
||
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
// <20><><EFBFBD><EFBFBD> WIC <20><>
|
||
hr = d2d.imaging_factory->CreateStream(&stream);
|
||
}
|
||
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
// <20><>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD>
|
||
hr = stream->InitializeFromMemory(
|
||
static_cast<WICInProcPointer>(buffer.buffer),
|
||
buffer.buffer_size
|
||
);
|
||
}
|
||
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ľ<EFBFBD><C4BD><EFBFBD><EFBFBD><EFBFBD>
|
||
hr = d2d.imaging_factory->CreateDecoderFromStream(
|
||
stream,
|
||
nullptr,
|
||
WICDecodeMetadataCacheOnLoad,
|
||
&decoder
|
||
);
|
||
}
|
||
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
hr = decoder->GetFrame(0, &source);
|
||
}
|
||
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
// <20><><EFBFBD><EFBFBD>ͼƬ<CDBC><C6AC>ʽת<CABD><D7AA><EFBFBD><EFBFBD>
|
||
hr = d2d.imaging_factory->CreateFormatConverter(&converter);
|
||
}
|
||
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
// ͼƬ<CDBC><C6AC>ʽת<CABD><D7AA><EFBFBD><EFBFBD> 32bppPBGRA
|
||
hr = converter->Initialize(
|
||
source,
|
||
GUID_WICPixelFormat32bppPBGRA,
|
||
WICBitmapDitherTypeNone,
|
||
nullptr,
|
||
0.f,
|
||
WICBitmapPaletteTypeMedianCut
|
||
);
|
||
}
|
||
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
// <20><> WIC λͼ<CEBB><CDBC><EFBFBD><EFBFBD>һ<EFBFBD><D2BB> Direct2D λͼ
|
||
hr = d2d.render_target->CreateBitmapFromWicBitmap(
|
||
converter,
|
||
nullptr,
|
||
bitmap
|
||
);
|
||
}
|
||
|
||
if (SUCCEEDED(hr))
|
||
{
|
||
bitmap_cache_.insert(std::make_pair(hash_code, *bitmap));
|
||
}
|
||
|
||
// <20>ͷ<EFBFBD><CDB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ
|
||
SafeRelease(decoder);
|
||
SafeRelease(source);
|
||
SafeRelease(stream);
|
||
SafeRelease(converter);
|
||
|
||
return hr;
|
||
}
|
||
|
||
HRESULT GraphicsDevice::Resize(UINT32 width, UINT32 height)
|
||
{
|
||
if (!d2d.render_target)
|
||
return E_UNEXPECTED;
|
||
|
||
d2d.render_target->Resize(D2D1::SizeU(width, height));
|
||
return S_OK;
|
||
}
|
||
|
||
HRESULT GraphicsDevice::SetTransform(const math::Matrix & matrix)
|
||
{
|
||
if (!d2d.render_target)
|
||
return E_UNEXPECTED;
|
||
|
||
d2d.render_target->SetTransform(matrix);
|
||
return S_OK;
|
||
}
|
||
|
||
HRESULT GraphicsDevice::SetBrushOpacity(float opacity)
|
||
{
|
||
if (!d2d.render_target)
|
||
return E_UNEXPECTED;
|
||
|
||
d2d.solid_brush->SetOpacity(opacity);
|
||
return S_OK;
|
||
}
|
||
|
||
HRESULT GraphicsDevice::SetTextStyle(
|
||
const Color & color,
|
||
bool has_outline,
|
||
const Color & outline_color,
|
||
float outline_width,
|
||
StrokeStyle outline_stroke
|
||
)
|
||
{
|
||
if (!d2d.text_renderer)
|
||
return E_UNEXPECTED;
|
||
|
||
d2d.text_renderer->SetTextStyle(
|
||
color,
|
||
has_outline,
|
||
outline_color,
|
||
outline_width,
|
||
static_cast<D2D1_LINE_JOIN>(outline_stroke)
|
||
);
|
||
return S_OK;
|
||
}
|
||
|
||
void GraphicsDevice::SetBackgroundColor(const Color& color)
|
||
{
|
||
clear_color_ = color;
|
||
}
|
||
|
||
void GraphicsDevice::DrawDebugInfo()
|
||
{
|
||
static int render_times_ = 0;
|
||
static auto last_render_time_ = time::Now();
|
||
|
||
int64_t duration = (time::Now() - last_render_time_).Milliseconds();
|
||
|
||
if (!fps_text_format_)
|
||
{
|
||
ThrowIfFailed(
|
||
d2d.write_factory->CreateTextFormat(
|
||
L"",
|
||
nullptr,
|
||
DWRITE_FONT_WEIGHT_NORMAL,
|
||
DWRITE_FONT_STYLE_NORMAL,
|
||
DWRITE_FONT_STRETCH_NORMAL,
|
||
20,
|
||
L"",
|
||
&fps_text_format_
|
||
)
|
||
);
|
||
|
||
ThrowIfFailed(
|
||
fps_text_format_->SetWordWrapping(
|
||
DWRITE_WORD_WRAPPING_NO_WRAP
|
||
)
|
||
);
|
||
}
|
||
|
||
++render_times_;
|
||
if (duration >= 100LL)
|
||
{
|
||
wchar_t fps_text[12] = {};
|
||
int len = swprintf_s(fps_text, L"FPS: %.1f", 1000.f / static_cast<float>(duration) * render_times_);
|
||
|
||
last_render_time_ = time::Now();
|
||
render_times_ = 0;
|
||
|
||
SafeRelease(fps_text_layout_);
|
||
|
||
ThrowIfFailed(
|
||
d2d.write_factory->CreateTextLayout(
|
||
fps_text,
|
||
static_cast<UINT32>(len),
|
||
fps_text_format_,
|
||
0,
|
||
0,
|
||
&fps_text_layout_
|
||
)
|
||
);
|
||
}
|
||
|
||
if (fps_text_layout_)
|
||
{
|
||
d2d.render_target->SetTransform(D2D1::Matrix3x2F::Identity());
|
||
d2d.solid_brush->SetOpacity(1.0f);
|
||
d2d.text_renderer->SetTextStyle(
|
||
D2D1::ColorF(D2D1::ColorF::White),
|
||
TRUE,
|
||
D2D1::ColorF(D2D1::ColorF::Black, 0.4f),
|
||
1.5f,
|
||
D2D1_LINE_JOIN_ROUND
|
||
);
|
||
|
||
fps_text_layout_->Draw(nullptr, d2d.text_renderer, 10, 0);
|
||
}
|
||
}
|
||
|
||
void GraphicsDevice::CreateDeviceResources(HWND hwnd)
|
||
{
|
||
if (!d2d.render_target)
|
||
{
|
||
RECT rc;
|
||
::GetClientRect(hwnd, &rc);
|
||
|
||
D2D1_SIZE_U size = D2D1::SizeU(
|
||
rc.right - rc.left,
|
||
rc.bottom - rc.top
|
||
);
|
||
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD>豸<EFBFBD><E8B1B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Դ<EFBFBD><D4B4><EFBFBD><EFBFBD>Щ<EFBFBD><D0A9>ԴӦ<D4B4><D3A6> Direct2D <20>豸<EFBFBD><E8B1B8>ʧʱ<CAA7>ؽ<EFBFBD>
|
||
// <20><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB> Direct2D <20><>ȾĿ<C8BE><C4BF>
|
||
ThrowIfFailed(
|
||
d2d.factory->CreateHwndRenderTarget(
|
||
D2D1::RenderTargetProperties(),
|
||
D2D1::HwndRenderTargetProperties(
|
||
hwnd,
|
||
size,
|
||
D2D1_PRESENT_OPTIONS_NONE),
|
||
&d2d.render_target
|
||
)
|
||
);
|
||
}
|
||
|
||
if (!d2d.solid_brush)
|
||
{
|
||
ThrowIfFailed(
|
||
d2d.render_target->CreateSolidColorBrush(
|
||
D2D1::ColorF(D2D1::ColorF::White),
|
||
&d2d.solid_brush
|
||
)
|
||
);
|
||
}
|
||
|
||
if (!d2d.text_renderer)
|
||
{
|
||
ThrowIfFailed(
|
||
ITextRenderer::Create(
|
||
&d2d.text_renderer,
|
||
d2d.factory,
|
||
d2d.render_target,
|
||
d2d.solid_brush
|
||
)
|
||
);
|
||
}
|
||
}
|
||
}
|
||
}
|