Files
EzUI/sources/Direct2DRender.cpp
2026-01-24 22:42:46 +08:00

1272 lines
38 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "Direct2DRender.h"
#include <Shlwapi.h>
#include <GdiPlus.h>
#pragma comment(lib, "gdiplus.lib")
#if USED_DIRECT2D
#define _NOREND_IMAGE_
//#define _NOREND_IMAGE_ return;
#pragma comment(lib,"dwrite.lib")
#pragma comment(lib,"d2d1.lib")
#pragma comment(lib,"Windowscodecs.lib")
#undef min
#undef max
namespace ezui {
ULONG_PTR g_GdiplusToken = NULL;
namespace D2D {
ID2D1Factory* g_Direct2dFactory = NULL;
IDWriteFactory* g_WriteFactory = NULL;
IWICImagingFactory* g_ImageFactory = NULL;
}
#define __COLOR_SCALE 0.003921569f
#define __To_D2D_COLOR_F(color) D2D_COLOR_F{FLOAT(color.GetR() * __COLOR_SCALE), FLOAT(color.GetG() * __COLOR_SCALE), FLOAT(color.GetB() * __COLOR_SCALE),FLOAT(color.GetA() * __COLOR_SCALE)}
#define __To_D2D_RectF(rect) D2D_RECT_F{(FLOAT)rect.X,(FLOAT)rect.Y,(FLOAT)rect.GetRight(),(FLOAT)rect.GetBottom() }
#define __To_D2D_PointF(pt) D2D1_POINT_2F{(FLOAT)pt.X,(FLOAT)pt.Y}
template<typename Interface>
inline void SafeRelease(
Interface** ppInterfaceToRelease)
{
if (*ppInterfaceToRelease != NULL)
{
(*ppInterfaceToRelease)->Release();
(*ppInterfaceToRelease) = NULL;
}
}
//TextFormat
Font::Font(const std::wstring& fontFamily, float fontSize) {
ASSERT(!(fontSize == 0));
this->m_fontFamily = fontFamily;
this->m_fontSize = fontSize;
D2D::g_WriteFactory->CreateTextFormat(fontFamily.c_str(), NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, m_fontSize, L"", &m_value);
}
Font::~Font() {
if (m_value && !m_ref) {
SafeRelease(&m_value);
}
}
float Font::GetFontSize()const {
return m_fontSize;
}
const std::wstring& Font::GetFontFamily()const {
return m_fontFamily;
}
void Font::Copy(const Font& _copy) {
((Font&)(_copy)).m_ref = true;
this->m_value = _copy.Get();
this->m_fontFamily = _copy.GetFontFamily();
this->m_fontSize = _copy.GetFontSize();
}
IDWriteTextFormat* Font::Get() const {
return m_value;
}
Font::Font(const Font& rightValue) {
Copy(rightValue);
}
bool Font::operator==(const Font& _right) {
if (_right.GetFontFamily() == this->GetFontFamily() && (std::fabs(_right.GetFontSize() - this->GetFontSize()) < EZUI_FLOAT_EPSILON)) {
return true;
}
return false;
}
void TextLayout::GetMetrics()
{
if (m_textLayout) {
m_textLayout->GetMetrics(&m_textMetrics);
}
}
//TextLayout
TextLayout::TextLayout(const std::wstring& text, const Font& font, const SizeF& maxSize, TextAlign textAlign) {
this->m_unicodeSize = text.size();
this->m_fontSize = font.GetFontSize();
this->m_fontFamily = font.GetFontFamily();
D2D::g_WriteFactory->CreateTextLayout(text.c_str(), text.size(), font.Get(), maxSize.Width, maxSize.Height, &m_textLayout);
if (m_textLayout == NULL)return;
SetTextAlign(textAlign);
}
IDWriteTextLayout* TextLayout::Get() const {
return m_textLayout;
}
Point TextLayout::HitTestPoint(const Point& pt, int* _textPos, BOOL* _isTrailingHit, int* fontHeight) {
int& textPos = *_textPos;
BOOL& isTrailingHit = *_isTrailingHit;
DWRITE_HIT_TEST_METRICS hitTestMetrics;
BOOL isInside;
{
FLOAT x = (FLOAT)pt.X, y = (FLOAT)pt.Y;
m_textLayout->HitTestPoint(
(FLOAT)x,
(FLOAT)y,
&isTrailingHit,
&isInside,
&hitTestMetrics
);
}
int posX = (int)(hitTestMetrics.left + 0.5);
if (isTrailingHit) {//判断前侧还是尾侧
posX += (int)(hitTestMetrics.width + 0.5);
}
*fontHeight = (int)(hitTestMetrics.height + 0.5);
textPos = hitTestMetrics.textPosition;
return Point{ posX,(int)(hitTestMetrics.top + 0.5) };//返回光标所在的位置
}
void TextLayout::HitTestPoint(const Point& pt, HitTestMetrics* outHitTestMetrics) {
BOOL isTrailingHit;
DWRITE_HIT_TEST_METRICS hitTestMetrics;
BOOL isInside;
{
FLOAT x = (FLOAT)pt.X, y = (FLOAT)pt.Y;
m_textLayout->HitTestPoint(
(FLOAT)x,
(FLOAT)y,
&isTrailingHit,
&isInside,
&hitTestMetrics
);
}
outHitTestMetrics->Length = hitTestMetrics.length;
outHitTestMetrics->TextPos = hitTestMetrics.textPosition;
outHitTestMetrics->IsTrailingHit = isTrailingHit;
outHitTestMetrics->FontBox.X = hitTestMetrics.left;
outHitTestMetrics->FontBox.Y = hitTestMetrics.top;
outHitTestMetrics->FontBox.Width = hitTestMetrics.width;
outHitTestMetrics->FontBox.Height = hitTestMetrics.height;
}
Point TextLayout::HitTestTextPosition(int textPos, BOOL isTrailingHit) {
if (m_textLayout == NULL)return Point{ 0,0 };
DWRITE_HIT_TEST_METRICS hitTestMetrics;
FLOAT X, Y;
m_textLayout->HitTestTextPosition(textPos, isTrailingHit, &X, &Y, &hitTestMetrics);
return Point((int)(X + 0.5), (int)(Y + 0.5));
}
const std::wstring& TextLayout::GetFontFamily()
{
return this->m_fontFamily;
}
Size TextLayout::GetFontBox() {
this->GetMetrics();
FLOAT width = m_textMetrics.widthIncludingTrailingWhitespace;
FLOAT height = m_textMetrics.height;
return Size{ (int)(width + 1) ,(int)(height + 1) };//加1是为了向上取整
}
float TextLayout::GetFontSize()
{
return this->m_fontSize;
}
int TextLayout::Width() {
this->GetMetrics();
FLOAT width = m_textMetrics.widthIncludingTrailingWhitespace;
return (int)(width + 1);
}
int TextLayout::Height() {
this->GetMetrics();
FLOAT width = m_textMetrics.height;
return (width + 1);
}
int TextLayout::GetFontHeight() {
this->GetMetrics();
FLOAT height = m_textMetrics.height;
return ((height / m_textMetrics.lineCount) + 0.5);
}
int TextLayout::GetLineCount() {
this->GetMetrics();
return m_textMetrics.lineCount;
}
#undef min
#undef max
const std::vector<RectF>& TextLayout::GetLineRects() {
//如果unicode个数为0 且获取过m_lineRects 则直接返回不执行获取操作
if (!m_textLayout || m_unicodeSize == 0 || !m_lineRects.empty()) {
return m_lineRects;
}
UINT lineCount = GetLineCount();
//获取每行 metrics
std::vector<DWRITE_LINE_METRICS> lineMetrics(lineCount);
auto hr = m_textLayout->GetLineMetrics(lineMetrics.data(), lineCount, &lineCount);
if (FAILED(hr)) {
return m_lineRects;
}
//遍历每一行,计算对应矩形
UINT32 textPos = 0;
for (UINT32 i = 0; i < lineCount; i++) {
auto& lm = lineMetrics[i];
if (lm.length == 0) { // 空行
textPos += lm.length;
continue;
}
//给一个足够大的 buffer存放 hit test 的矩形
std::vector<DWRITE_HIT_TEST_METRICS> metrics(lm.length);
UINT32 actualCount = 0;
hr = m_textLayout->HitTestTextRange(
textPos, lm.length, // 这一行的字符范围
0.0f, 0.0f, // offsetX, offsetY
metrics.data(), (UINT32)metrics.size(),
&actualCount
);
if (SUCCEEDED(hr) && actualCount > 0) {
float minX = metrics[0].left;
float maxX = metrics[0].left + metrics[0].width;
float top = metrics[0].top;
float bottom = top + metrics[0].height;
for (UINT32 j = 1; j < actualCount; j++) {
auto& m = metrics[j];
minX = std::min(minX, m.left);
maxX = std::max(maxX, m.left + m.width);
top = std::min(top, m.top);
bottom = std::max(bottom, m.top + m.height);
}
m_lineRects.push_back(RectF(minX, top, maxX - minX, bottom - top));
}
textPos += lm.length; //下一行的起始字符
}
return m_lineRects;
}
void TextLayout::SetTextAlign(TextAlign textAlign) {
#define __Top DWRITE_PARAGRAPH_ALIGNMENT_NEAR
#define __Bottom DWRITE_PARAGRAPH_ALIGNMENT_FAR
#define __Left DWRITE_TEXT_ALIGNMENT_LEADING
#define __Right DWRITE_TEXT_ALIGNMENT_TRAILING
#define __Middle DWRITE_PARAGRAPH_ALIGNMENT_CENTER
#define __Center DWRITE_TEXT_ALIGNMENT_CENTER
if (m_textLayout == NULL)return;
//垂直对其方式
if (((int)textAlign & (int)VAlign::Top) == (int)VAlign::Top) {
m_textLayout->SetParagraphAlignment(__Top);
}
if (((int)textAlign & (int)VAlign::Mid) == (int)VAlign::Mid) {
m_textLayout->SetParagraphAlignment(__Middle);
}
if (((int)textAlign & (int)VAlign::Bottom) == (int)VAlign::Bottom) {
m_textLayout->SetParagraphAlignment(__Bottom);
}
//水平对其方式
if (((int)textAlign & (int)HAlign::Left) == (int)HAlign::Left) {
m_textLayout->SetTextAlignment(__Left);
}
if (((int)textAlign & (int)HAlign::Center) == (int)HAlign::Center) {
m_textLayout->SetTextAlignment(__Center);
}
if (((int)textAlign & (int)HAlign::Right) == (int)HAlign::Right) {
m_textLayout->SetTextAlignment(__Right);
}
#undef __Top
#undef __Bottom
#undef __Left
#undef __Right
#undef __Middle
#undef __Center
}
void TextLayout::SetUnderline(int pos, int count)
{
if (count > 0) {
m_textLayout->SetUnderline(TRUE, { (UINT32)pos,(UINT32)count });
}
}
TextLayout::~TextLayout() {
if (m_textLayout) {
SafeRelease(&m_textLayout);
}
}
//DXImage
void DXImage::DecodeOfRender(ID2D1RenderTarget* render) {
HRESULT hr = 0;
if (m_d2dBitmap != NULL) {
if (m_d2dBitmap) {
SafeRelease(&m_d2dBitmap);
}
}
if (m_fmtcovter) {
hr = render->CreateBitmapFromWicBitmap(m_fmtcovter, 0, &m_d2dBitmap);
}
if (m_bitMap) {
hr = render->CreateBitmapFromWicBitmap(m_bitMap, &m_d2dBitmap);
}
}
int DXImage::Width() {
return m_width;
}
int DXImage::Height() {
return m_height;
}
void DXImage::CreateFormStream(IStream* istram) {
HRESULT imgCreate = S_OK;
if (D2D::g_ImageFactory) {
imgCreate = D2D::g_ImageFactory->CreateDecoderFromStream(istram, NULL, WICDecodeMetadataCacheOnDemand, &m_bitmapdecoder);//
}
if (m_bitmapdecoder) {
D2D::g_ImageFactory->CreateFormatConverter(&m_fmtcovter);
m_bitmapdecoder->GetFrame(0, &m_pframe);
m_fmtcovter->Initialize(m_pframe, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0.0f, WICBitmapPaletteTypeCustom);
UINT width, height;
m_pframe->GetSize(&width, &height);
this->m_width = width;
this->m_height = height;
Init();
}
}
void DXImage::CreateFromFile(const std::wstring& filew)
{
HRESULT ret = S_OK;
if (D2D::g_ImageFactory) {
ret = D2D::g_ImageFactory->CreateDecoderFromFilename(filew.c_str(), NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &m_bitmapdecoder);//
}
if (m_bitmapdecoder) {
ret = D2D::g_ImageFactory->CreateFormatConverter(&m_fmtcovter);
ret = m_bitmapdecoder->GetFrame(0, &m_pframe);
ret = m_fmtcovter->Initialize(m_pframe, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0.0f, WICBitmapPaletteTypeCustom);
UINT width, height;
ret = m_pframe->GetSize(&width, &height);
this->m_width = width;
this->m_height = height;
Init();
}
}
// 获取 GIF 帧位置
WICRect GetFrameRect(IWICMetadataQueryReader* metaReader) {
WICRect rect = { 0,0,0,0 };
PROPVARIANT pv;
PropVariantInit(&pv);
if (SUCCEEDED(metaReader->GetMetadataByName(L"/imgdesc/Left", &pv))) {
rect.X = pv.uiVal;
}
PropVariantClear(&pv);
if (SUCCEEDED(metaReader->GetMetadataByName(L"/imgdesc/Top", &pv)))
{
rect.Y = pv.uiVal;
}
PropVariantClear(&pv);
if (SUCCEEDED(metaReader->GetMetadataByName(L"/imgdesc/Width", &pv)))
{
rect.Width = pv.uiVal;
}
PropVariantClear(&pv);
if (SUCCEEDED(metaReader->GetMetadataByName(L"/imgdesc/Height", &pv)))
{
rect.Height = pv.uiVal;
}
PropVariantClear(&pv);
return rect;
}
// 拷贝整个位图
void CopyBitmap(IWICBitmap* src, IWICBitmap* dst) {
if (!src || !dst) return;
UINT w = 0, h = 0;
src->GetSize(&w, &h);
WICRect rect = { 0,0,(INT)w,(INT)h };
IWICBitmapLock* lockSrc = NULL;
IWICBitmapLock* lockDst = NULL;
if (SUCCEEDED(src->Lock(&rect, WICBitmapLockRead, &lockSrc)) &&
SUCCEEDED(dst->Lock(&rect, WICBitmapLockWrite, &lockDst)))
{
UINT cbBufferSrc = 0, cbBufferDst = 0;
BYTE* pSrc = NULL;
BYTE* pDst = NULL;
lockSrc->GetDataPointer(&cbBufferSrc, &pSrc);
lockDst->GetDataPointer(&cbBufferDst, &pDst);
memcpy(pDst, pSrc, cbBufferSrc);
}
if (lockSrc) SafeRelease(&lockSrc);
if (lockDst) SafeRelease(&lockDst);
}
// 清除矩形区域(透明)
void ClearRegion(IWICBitmap* bmp, const WICRect& rect) {
if (!bmp) return;
IWICBitmapLock* lock = NULL;
if (SUCCEEDED(bmp->Lock(&rect, WICBitmapLockWrite, &lock)))
{
UINT cbBuffer = 0;
BYTE* pData = NULL;
lock->GetDataPointer(&cbBuffer, &pData);
memset(pData, 0, cbBuffer); // 全部置 0透明
}
SafeRelease(&lock);
}
void CopyFrameToCanvas(IWICBitmapSource* frame, IWICBitmap* canvas, UINT CanvasWidth, UINT CanvasHeight, const WICRect& frameRect)
{
if (!frame || !canvas) return;
// 锁定整个画布
WICRect fullRect{ 0, 0, (INT)CanvasWidth, (INT)CanvasHeight };
IWICBitmapLock* lock = NULL;
if (FAILED(canvas->Lock(&fullRect, WICBitmapLockWrite, &lock))) return;
UINT cbBuffer = 0;
BYTE* pDst = NULL;
lock->GetDataPointer(&cbBuffer, &pDst);
UINT canvasStride = CanvasWidth * 4;
UINT frameStride = frameRect.Width * 4;
UINT frameBufferSize = frameStride * frameRect.Height;
BYTE* tempBuffer = new BYTE[frameBufferSize];
WICRect copyRect = { 0, 0, frameRect.Width, frameRect.Height };
HRESULT hr = frame->CopyPixels(&copyRect, frameStride, frameBufferSize, tempBuffer);
if (FAILED(hr)) {
delete[] tempBuffer;
SafeRelease(&lock);
return;
}
// 复制像素到画布的对应偏移位置
for (UINT y = 0; y < frameRect.Height; y++) {
BYTE* srcLine = tempBuffer + y * frameStride;
BYTE* dstLine = pDst + (frameRect.Y + y) * canvasStride + frameRect.X * 4;
for (UINT x = 0; x < frameRect.Width; x++) {
BYTE* srcPx = srcLine + x * 4;
BYTE* dstPx = dstLine + x * 4;
BYTE srcA = srcPx[3];
if (srcA != 0) {
dstPx[0] = srcPx[0];
dstPx[1] = srcPx[1];
dstPx[2] = srcPx[2];
dstPx[3] = srcPx[3];
}
}
}
//释放
delete[] tempBuffer;
SafeRelease(&lock);
}
void DXImage::Init()
{
m_framePos = 0;
m_frames.clear();
if (!m_bitmapdecoder) return;
UINT fCount = 0;
m_bitmapdecoder->GetFrameCount(&fCount);
m_frameCount = fCount;
if (fCount <= 1) {
return;
}
if (m_fmtcovter) {
//如果是gif那就不需要m_fmtcovter 因为会把解析完毕的每一帧存储起来直接用
SafeRelease(&m_fmtcovter);
}
UINT width = Width();
UINT height = Height();
// 创建全尺寸画布
IWICBitmap* canvas = NULL;
D2D::g_ImageFactory->CreateBitmap(
width, height,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapCacheOnLoad,
&canvas);
// 备份画布(用于 disposal=3
IWICBitmap* prevCanvas = NULL;
D2D::g_ImageFactory->CreateBitmap(
width, height,
GUID_WICPixelFormat32bppPBGRA,
WICBitmapCacheOnLoad,
&prevCanvas);
for (UINT i = 0; i < fCount; i++)
{
IWICBitmapFrameDecode* srcFrame = NULL;
m_bitmapdecoder->GetFrame(i, &srcFrame);
// 元数据读取器
IWICMetadataQueryReader* metaReader = NULL;
srcFrame->GetMetadataQueryReader(&metaReader);
// 延时
UINT delayMs = 100;
PROPVARIANT pv;
PropVariantInit(&pv);
if (SUCCEEDED(metaReader->GetMetadataByName(L"/grctlext/Delay", &pv)))
{
delayMs = pv.uiVal * 10; // 单位 1/100 秒
}
PropVariantClear(&pv);
// Disposal
UINT disposal = 0;
if (SUCCEEDED(metaReader->GetMetadataByName(L"/grctlext/Disposal", &pv)))
{
disposal = pv.uiVal;
}
PropVariantClear(&pv);
// Disposal=3 保存备份
if (disposal == 3) {
CopyBitmap(canvas, prevCanvas);
}
// Disposal=2 清区域
if (disposal == 2 && i > 0)
{
WICRect rect = GetFrameRect(metaReader);
ClearRegion(canvas, rect);
}
else if (disposal == 3 && i > 0)
{
CopyBitmap(prevCanvas, canvas);
}
// 转换帧格式
IWICFormatConverter* converter = NULL;
D2D::g_ImageFactory->CreateFormatConverter(&converter);
converter->Initialize(srcFrame, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteTypeCustom);
// 帧矩形
WICRect rect = GetFrameRect(metaReader);
// 绘制到 canvas
CopyFrameToCanvas(converter, canvas, width, height, rect);
// 存一份当前画布的完整副本
IWICBitmap* frameCopy = NULL;
D2D::g_ImageFactory->CreateBitmapFromSource(canvas, WICBitmapCacheOnLoad, &frameCopy);
GifFrame gf = { frameCopy, delayMs };
m_frames.push_back(gf);
// 释放临时对象
SafeRelease(&converter);
SafeRelease(&metaReader);
SafeRelease(&srcFrame);
}
// 释放画布
SafeRelease(&canvas);
SafeRelease(&prevCanvas);
//读取第一帧
this->NextFrame();
}
WORD DXImage::NextFrame() {
if (m_frames.empty()) {
return 0; // 没有帧
}
if (m_framePos >= m_frames.size()) {
m_framePos = 0;
}
this->m_bitMap = m_frames[m_framePos].wicBitmap;
UINT delayMs = m_frames[m_framePos].delay;
// 这里不需要释放和创建格式转换器了,播放时再转 D2D 位图
// 只移动索引
m_framePos++;
return (WORD)delayMs;
}
DXImage* DXImage::Clone()
{
UINT width, height;
m_fmtcovter->GetSize(&width, &height); // 获取位图源的宽度和高度
//暂时不做clone函数
return NULL;
}
DXImage::DXImage(HBITMAP hBitmap) {
if (D2D::g_ImageFactory) {
//WICBitmapUsePremultipliedAlpha:传入的图像颜色值已经与 Alpha 通道预乘
D2D::g_ImageFactory->CreateBitmapFromHBITMAP(hBitmap, NULL, WICBitmapUsePremultipliedAlpha, &m_bitMap);
UINT width, height;
m_bitMap->GetSize(&width, &height);
this->m_width = width;
this->m_height = height;
Init();
}
}
DXImage::DXImage(const std::wstring& file) {
CreateFromFile(file);
}
DXImage::DXImage(int width, int height)
{
this->m_width = width;
this->m_height = height;
ASSERT(!(width <= 0 || height <= 0));
HRESULT hr = D2D::g_ImageFactory->CreateBitmap((UINT)width, (UINT)height, GUID_WICPixelFormat32bppPBGRA, WICBitmapCacheOnDemand, &m_bitMap);
}
DXImage::DXImage(const void* data, size_t imgSize)
{
ASSERT(data);
ASSERT(imgSize);
IStream* stream = SHCreateMemStream((BYTE*)data, imgSize);
if (stream) {
this->CreateFormStream(stream);
SafeRelease(&stream);
}
}
ID2D1Bitmap* DXImage::Get()
{
return m_d2dBitmap;
}
IWICBitmap* DXImage::GetIWICBitmap()
{
return m_bitMap;
}
DXImage::DXImage(IStream* istram) {
CreateFormStream(istram);
}
DXImage::~DXImage()
{
if (m_d2dBitmap) {
SafeRelease(&m_d2dBitmap);
}
if (m_bitmapdecoder) {
SafeRelease(&m_bitmapdecoder);
}
if (m_pframe) {
SafeRelease(&m_pframe);
}
if (m_fmtcovter) {
SafeRelease(&m_fmtcovter);
}
if (m_bitMap) {
SafeRelease(&m_bitMap);
}
for (auto& it : m_frames) {
SafeRelease(&it.wicBitmap);
}
}
void Geometry::Combine(Geometry& out, const Geometry& a, const Geometry& b, D2D1_COMBINE_MODE COMBINE_MODE)
{
ID2D1PathGeometry* outPathGeometry = NULL;
D2D::g_Direct2dFactory->CreatePathGeometry(&outPathGeometry);
ID2D1GeometrySink* geometrySink = NULL;
outPathGeometry->Open(&geometrySink);
HRESULT ret = a.m_rgn->CombineWithGeometry(b.m_rgn, COMBINE_MODE, NULL, geometrySink);
geometrySink->Close();
if (out.m_rgn) {
SafeRelease(&out.m_rgn);
}
out.m_rgn = outPathGeometry;
SafeRelease(&geometrySink);
}
void RectangleGeometry::Create(float x, float y, float width, float height, float _radius)
{
if (_radius > 0) {
float radius = GetMaxRadius(width, height, _radius);
D2D1_ROUNDED_RECT rectF{ x,y,(x + width),(y + height) ,radius ,radius };
D2D::g_Direct2dFactory->CreateRoundedRectangleGeometry(rectF, (ID2D1RoundedRectangleGeometry**)&m_rgn);
}
else {
D2D_RECT_F rectF{ x,y,(x + width),(y + height) };
D2D::g_Direct2dFactory->CreateRectangleGeometry(rectF, (ID2D1RectangleGeometry**)&m_rgn);
}
}
RectangleGeometry::RectangleGeometry(float x, float y, float width, float height, float _radius) {
Create(x, y, width, height, _radius);
}
RectangleGeometry::RectangleGeometry(const RectF& _rect, float _radius)
{
float x = _rect.X, y = _rect.Y, width = _rect.Width, height = _rect.Height;
Create(x, y, width, height, _radius);
}
RectangleGeometry::RectangleGeometry(const RectF& _rect, float topLeftRadius, float topRightRadius, float bottomRightRadius, float bottomLeftRadius)
{
ID2D1PathGeometry* pPathGeometry = NULL;
D2D1_RECT_F rect = __To_D2D_RectF(_rect);
D2D::g_Direct2dFactory->CreatePathGeometry(&pPathGeometry);
topLeftRadius = GetMaxRadius(_rect.Width, _rect.Height, topLeftRadius);
topRightRadius = GetMaxRadius(_rect.Width, _rect.Height, topRightRadius);
bottomRightRadius = GetMaxRadius(_rect.Width, _rect.Height, bottomRightRadius);
bottomLeftRadius = GetMaxRadius(_rect.Width, _rect.Height, bottomLeftRadius);
// 打开路径几何图形的几何接口
ID2D1GeometrySink* pSink;
pPathGeometry->Open(&pSink);
// 开始绘制路径
pSink->BeginFigure(D2D1::Point2F(rect.left, rect.top + topLeftRadius), D2D1_FIGURE_BEGIN_FILLED);
// 添加弧线段到左上角
pSink->AddArc(
D2D1::ArcSegment(
D2D1::Point2F(rect.left + topLeftRadius, rect.top),
D2D1::SizeF(topLeftRadius, topLeftRadius),
0.0f,
D2D1_SWEEP_DIRECTION_CLOCKWISE,
D2D1_ARC_SIZE_SMALL
)
);
// 添加弧线段到右上角
pSink->AddLine(D2D1::Point2F(rect.right - topRightRadius, rect.top));
pSink->AddArc(
D2D1::ArcSegment(
D2D1::Point2F(rect.right, rect.top + topRightRadius),
D2D1::SizeF(topRightRadius, topRightRadius),
0.0f,
D2D1_SWEEP_DIRECTION_CLOCKWISE,
D2D1_ARC_SIZE_SMALL
)
);
// 添加弧线段到右下角
pSink->AddLine(D2D1::Point2F(rect.right, rect.bottom - bottomRightRadius));
pSink->AddArc(
D2D1::ArcSegment(
D2D1::Point2F(rect.right - bottomRightRadius, rect.bottom),
D2D1::SizeF(bottomRightRadius, bottomRightRadius),
0.0f,
D2D1_SWEEP_DIRECTION_CLOCKWISE,
D2D1_ARC_SIZE_SMALL
)
);
// 添加弧线段到左下角
pSink->AddLine(D2D1::Point2F(rect.left + bottomLeftRadius, rect.bottom));
pSink->AddArc(
D2D1::ArcSegment(
D2D1::Point2F(rect.left, rect.bottom - bottomLeftRadius),
D2D1::SizeF(bottomLeftRadius, bottomLeftRadius),
0.0f,
D2D1_SWEEP_DIRECTION_CLOCKWISE,
D2D1_ARC_SIZE_SMALL
)
);
// 结束路径
pSink->EndFigure(D2D1_FIGURE_END_CLOSED);
pSink->Close();
SafeRelease(&pSink);
this->m_rgn = pPathGeometry;
}
Geometry::Geometry() {
}
void Geometry::AddArc(const PointF& endPoint, float radius) {
m_pSink->AddArc(
D2D1::ArcSegment(
D2D1::Point2F(endPoint.X, endPoint.Y),
D2D1::SizeF(radius, radius),
0.0f,
D2D1_SWEEP_DIRECTION_CLOCKWISE,
D2D1_ARC_SIZE_SMALL
)
);
}
void Geometry::AddAcr(const D2D1_ARC_SEGMENT& arc)
{
m_pSink->AddArc(arc);
}
void Geometry::AddLine(const PointF& endPoint) {
D2D1_POINT_2F p{ endPoint.X, endPoint.Y };
m_pSink->AddLine(p);
}
void Geometry::BeginFigure(const PointF& startPoint, D2D1_FIGURE_BEGIN figureBegin) {
ID2D1PathGeometry* path;
D2D::g_Direct2dFactory->CreatePathGeometry(&path);
path->Open(&m_pSink);
m_pSink->BeginFigure(__To_D2D_PointF(startPoint), figureBegin);
m_rgn = path;
}
void Geometry::CloseFigure(D2D1_FIGURE_END figureEnd) {
m_pSink->EndFigure(figureEnd);
m_pSink->Close(); // 完成 Path 定义
}
Geometry::~Geometry() {
if (m_pSink) {
SafeRelease(&m_pSink);
}
if (m_rgn) {
SafeRelease(&m_rgn);
}
}
ID2D1Geometry* Geometry::Get() const {
return m_rgn;
}
PieGeometry::PieGeometry(const RectF& rectF, float startAngle, float endAngle)
{
auto rect = __To_D2D_RectF(rectF);
// 1. 计算圆心和半径
D2D1_POINT_2F center = {
(rect.left + rect.right) / 2.0f,
(rect.top + rect.bottom) / 2.0f
};
float radiusX = (rect.right - rect.left) / 2.0f;
float radiusY = (rect.bottom - rect.top) / 2.0f;
bool isFullCircle = fabsf(endAngle - startAngle) >= 359.999f;
if (isFullCircle) {
// 方法2直接使用椭圆几何更高效
ID2D1EllipseGeometry* pEllipse = NULL;
D2D1_ELLIPSE ellipse{ center, radiusX, radiusY };
D2D::g_Direct2dFactory->CreateEllipseGeometry(&ellipse, &pEllipse);
//赋值
this->m_rgn = (ID2D1Geometry*)pEllipse;
}
else {
// 2. 角度处理(转换为弧度)
float startRad = startAngle * (3.1415926f / 180.0f);
float endRad = endAngle * (3.1415926f / 180.0f);
// 3. 创建路径几何
ID2D1PathGeometry* pGeometry = NULL;
D2D::g_Direct2dFactory->CreatePathGeometry(&pGeometry);
ID2D1GeometrySink* pSink = NULL;
pGeometry->Open(&pSink);
// 4. 计算弧线起点和终点
D2D1_POINT_2F startPoint = {
center.x + radiusX * cosf(startRad),
center.y + radiusY * sinf(startRad)
};
D2D1_POINT_2F endPoint = {
center.x + radiusX * cosf(endRad),
center.y + radiusY * sinf(endRad)
};
// 5. 构造扇形路径
pSink->BeginFigure(center, D2D1_FIGURE_BEGIN_FILLED);
pSink->AddLine(startPoint);
D2D1_ARC_SEGMENT arc{
endPoint,
D2D1::SizeF(radiusX, radiusY),
0.0f,
(endRad > startRad) ? D2D1_SWEEP_DIRECTION_CLOCKWISE : D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE,
(fabsf(endRad - startRad) <= 3.1415926f) ? D2D1_ARC_SIZE_SMALL : D2D1_ARC_SIZE_LARGE
};
pSink->AddArc(arc);
pSink->AddLine(center);
pSink->EndFigure(D2D1_FIGURE_END_CLOSED);
//释放
pSink->Close();
SafeRelease(&pSink);
//赋值
this->m_rgn = pGeometry;
}
}
};
namespace ezui {
void RenderInitialize()
{
HRESULT hr = S_OK;
// Create a Direct2D factory.
if (D2D::g_Direct2dFactory == NULL) {
hr = ::D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, &D2D::g_Direct2dFactory);
if (!D2D::g_Direct2dFactory) {
::MessageBoxW(NULL, L"Failed to create ID2D1Factory", L"Error", MB_ICONSTOP);
}
else {
D2D1_RENDER_TARGET_PROPERTIES defaultOption = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(
DXGI_FORMAT_B8G8R8A8_UNORM,
D2D1_ALPHA_MODE_PREMULTIPLIED),
0,
0,
D2D1_RENDER_TARGET_USAGE_NONE,
D2D1_FEATURE_LEVEL_DEFAULT
);
//初始化一下d2d让第一次启动窗口快一点
ID2D1DCRenderTarget* initRender = NULL;
hr = D2D::g_Direct2dFactory->CreateDCRenderTarget(&defaultOption, (ID2D1DCRenderTarget**)&initRender);
SafeRelease(&initRender);
}
}
if (D2D::g_WriteFactory == NULL) {
hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&D2D::g_WriteFactory));
if (!D2D::g_WriteFactory) {
::MessageBoxW(NULL, L"Failed to create IDWriteFactory", L"Error", MB_ICONSTOP);
}
}
if (D2D::g_ImageFactory == NULL) {
_GUID imageFactoryOld{ 0xcacaf262, 0x9370, 0x4615, 0xa1, 0x3b, 0x9f, 0x55, 0x39, 0xda, 0x4c, 0xa };//xp win7 旧版
_GUID WICImagingFactoryId = CLSID_WICImagingFactory;//当前平台
ImagingFactoryInit:
hr = CoCreateInstance(WICImagingFactoryId, NULL, CLSCTX_INPROC_SERVER, __uuidof(IWICImagingFactory), (LPVOID*)&D2D::g_ImageFactory);
if (hr != S_OK) {
//if (hr == 0x800401F0) {//未初始化com 自己在全局初始化一下就好了 (自己控制初始化时机)
// ::CoInitialize(NULL);
// goto ImagingFactory;
//}
if (hr == 0x80040154) {//没有注册类 不用win7的sdk生成的程序在下win7系统上运行会出现此错误
WICImagingFactoryId = imageFactoryOld;
goto ImagingFactoryInit;
}
CHAR buf[256]{ 0 };
sprintf_s(buf, "Code 0x%08X", hr);
::MessageBoxA(NULL, buf, "Failed to create IWICImagingFactory", MB_ICONSTOP);
}
}
if (g_GdiplusToken == NULL) {
//初始化GDI+
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup(&g_GdiplusToken, &gdiplusStartupInput, NULL);
}
}
void RenderUnInitialize()
{
if (D2D::g_Direct2dFactory) {
SafeRelease(&D2D::g_Direct2dFactory);
}
if (D2D::g_WriteFactory) {
SafeRelease(&D2D::g_WriteFactory);
}
if (D2D::g_ImageFactory) {
SafeRelease(&D2D::g_ImageFactory);
}
//释放GDI+
if (g_GdiplusToken) {
Gdiplus::GdiplusShutdown(g_GdiplusToken);
g_GdiplusToken = NULL;
}
}
float GetMaxRadius(float width, float height, float _radius)
{
float radius = _radius;//半径
float diameter = radius * 2;//直径
if (width > height || (std::fabs(width - height) < EZUI_FLOAT_EPSILON)) {
if (diameter > height) {
radius = height / 2.0f;
}
}
else if (height > width) {
if (diameter > width) {
radius = width / 2.0f;
}
}
return radius;
}
DXRender::DXRender(HDC dc, int x, int y, int width, int height) {
D2D1_RENDER_TARGET_PROPERTIES defaultOption = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(
DXGI_FORMAT_B8G8R8A8_UNORM,
D2D1_ALPHA_MODE_PREMULTIPLIED),//表示三通道的值已经是预乘了a通之后的数值
0,
0,
D2D1_RENDER_TARGET_USAGE_NONE,
D2D1_FEATURE_LEVEL_DEFAULT
);
HRESULT hr = D2D::g_Direct2dFactory->CreateDCRenderTarget(&defaultOption, (ID2D1DCRenderTarget**)&m_render);
RECT rc{ x,y,x + width ,y + height };
((ID2D1DCRenderTarget*)m_render)->BindDC(dc, &rc);
m_render->BeginDraw();
}
DXRender::DXRender(DXImage* dxImage) {
D2D1_RENDER_TARGET_PROPERTIES defaultOption = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(),
0,
0,
D2D1_RENDER_TARGET_USAGE_NONE,
D2D1_FEATURE_LEVEL_DEFAULT
);
HRESULT hr = D2D::g_Direct2dFactory->CreateWicBitmapRenderTarget(dxImage->GetIWICBitmap(), defaultOption, (ID2D1RenderTarget**)&m_render);
m_render->BeginDraw();
}
DXRender::~DXRender() {
if (m_render) {
m_render->EndDraw();
}
if (m_font) {
delete m_font;
}
SafeRelease(&m_render);
SafeRelease(&m_brush);
SafeRelease(&m_pStrokeStyle);
}
ID2D1SolidColorBrush* DXRender::GetBrush()
{
if (m_brush == NULL) {
m_render->CreateSolidColorBrush(D2D_COLOR_F{ 0,0,0,1 }, &m_brush);
}
return m_brush;
}
ID2D1StrokeStyle* DXRender::GetStrokeStyle() {
return m_pStrokeStyle;
}
void DXRender::SetFont(const std::wstring& fontFamily, float fontSize) {
if (m_font != NULL) {
if (m_font->GetFontFamily() == fontFamily && (std::fabs(m_font->GetFontSize() - fontSize) < EZUI_FLOAT_EPSILON)) {
return;
}
delete m_font;
}
m_font = new Font(fontFamily, fontSize);
}
void DXRender::SetFont(const Font& _copy_font) {
if (m_font != NULL) {
if (*m_font == _copy_font) {
return;
}
delete m_font;
}
m_font = new Font(_copy_font);
}
void DXRender::SetColor(const __EzUI__Color& color) {
if (m_brush == NULL) {
m_render->CreateSolidColorBrush(__To_D2D_COLOR_F(color), &m_brush);
}
else {
m_brush->SetColor(__To_D2D_COLOR_F(color));
}
}
void DXRender::SetStrokeStyle(StrokeStyle strokeStyle)
{
if (m_pStrokeStyle != NULL) {
SafeRelease(&m_pStrokeStyle);
}
if (strokeStyle == StrokeStyle::Dash) {
// 使用系统默认的虚线样式
D2D1_DASH_STYLE dashStyle = D2D1_DASH_STYLE_DASH; // 常规虚线样式
// 创建虚线样式
D2D::g_Direct2dFactory->CreateStrokeStyle(
D2D1::StrokeStyleProperties(
D2D1_CAP_STYLE_FLAT, // 线帽样式
D2D1_CAP_STYLE_FLAT, // 结束线帽样式
D2D1_CAP_STYLE_ROUND, // 虚线部分的线帽样式
D2D1_LINE_JOIN_ROUND, // 线段连接样式
10.0f, // 斜接限制
dashStyle, // 设置虚线样式为常规虚线
0.0f // 自定义虚线的间隔(不需要)
),
NULL, 0, &m_pStrokeStyle);
}
}
void DXRender::DrawTextLayout(const TextLayout& textLayout, const PointF& startLacation) {
m_render->DrawTextLayout(D2D1_POINT_2F{ startLacation.X ,startLacation.Y }, textLayout.Get(), GetBrush());
}
void DXRender::DrawString(const std::wstring& text, const RectF& rect, ezui::TextAlign textAlign) {
TextLayout textLayout(text, *m_font, { rect.Width, rect.Height }, textAlign);
this->DrawTextLayout(textLayout, { rect.X,rect.Y });
}
void DXRender::DrawLine(const PointF& A, const PointF& B, float width) {
m_render->DrawLine(D2D1_POINT_2F{ A.X,A.Y }, D2D1_POINT_2F{ B.X,B.Y }, GetBrush(), width, GetStrokeStyle());
}
void DXRender::DrawRectangle(const RectF& rect, float _radius, float width)
{
if (_radius > 0) {
float radius = GetMaxRadius(rect.Width, rect.Height, _radius);
D2D1_ROUNDED_RECT roundRect{ __To_D2D_RectF(rect), radius, radius };
m_render->DrawRoundedRectangle(roundRect, GetBrush(), width, GetStrokeStyle());
}
else {
m_render->DrawRectangle(__To_D2D_RectF(rect), GetBrush(), width, GetStrokeStyle());
}
}
void DXRender::FillRectangle(const RectF& rect, float _radius) {
if (_radius > 0) {
float radius = GetMaxRadius(rect.Width, rect.Height, _radius);
D2D1_ROUNDED_RECT roundRect{ __To_D2D_RectF(rect), radius, radius };
m_render->FillRoundedRectangle(roundRect, GetBrush());
}
else {
m_render->FillRectangle(__To_D2D_RectF(rect), GetBrush());
}
}
void DXRender::SetTransform(float offsetX, float offsetY)
{
this->m_offset.X = offsetX;
this->m_offset.Y = offsetY;
this->SetTransform(this->m_offset.X, this->m_offset.Y, this->m_rotatePoint.X, this->m_rotatePoint.Y, this->m_angle);
}
void DXRender::SetTransform(float startX, float startY, float angle) {
this->m_rotatePoint.X = startX;
this->m_rotatePoint.Y = startY;
this->m_angle = angle;
this->SetTransform(this->m_offset.X, this->m_offset.Y, this->m_rotatePoint.X, this->m_rotatePoint.Y, this->m_angle);
}
void DXRender::SetTransform(float offsetX, float offsetY, float startX, float startY, float angle)
{
// 创建D2D矩阵转换对象 平移变换
D2D1::Matrix3x2F transformMatrix = D2D1::Matrix3x2F::Translation(offsetX, offsetY);
// 判断是否需要进行旋转变换
if (angle != 0)
{
// 旋转变换
transformMatrix = transformMatrix * D2D1::Matrix3x2F::Rotation(angle, D2D1_POINT_2F{ startX, startY });
}
// 将转换矩阵应用于绘制对象
m_render->SetTransform(transformMatrix);
}
void DXRender::DrawBezier(const PointF& startPoint, const Bezier& points, float width) {
ID2D1GeometrySink* pSink = NULL;
ID2D1PathGeometry* pathGeometry = NULL;
D2D::g_Direct2dFactory->CreatePathGeometry(&pathGeometry);
pathGeometry->Open(&pSink);
pSink->BeginFigure(__To_D2D_PointF(startPoint), D2D1_FIGURE_BEGIN_FILLED);
D2D1_BEZIER_SEGMENT bzr{ __To_D2D_PointF(points.point1) ,__To_D2D_PointF(points.point2) ,__To_D2D_PointF(points.point3) };
pSink->AddBezier(bzr);
pSink->EndFigure(D2D1_FIGURE_END_OPEN);
pSink->Close();
m_render->DrawGeometry(pathGeometry, GetBrush(), width, GetStrokeStyle());
SafeRelease(&pathGeometry);
SafeRelease(&pSink);
}
void DXRender::DrawBezier(const PointF& startPoint, std::list<Bezier>& beziers, float width)
{
ID2D1GeometrySink* pSink = NULL;
ID2D1PathGeometry* pathGeometry = NULL;
D2D::g_Direct2dFactory->CreatePathGeometry(&pathGeometry);
pathGeometry->Open(&pSink);
pSink->BeginFigure(__To_D2D_PointF(startPoint), D2D1_FIGURE_BEGIN_FILLED);
for (auto& it : beziers) {
D2D1_BEZIER_SEGMENT bzr{ __To_D2D_PointF(it.point1) ,__To_D2D_PointF(it.point2) ,__To_D2D_PointF(it.point3) };
pSink->AddBezier(bzr);
}
pSink->EndFigure(D2D1_FIGURE_END_OPEN);
pSink->Close();
m_render->DrawGeometry(pathGeometry, GetBrush(), width, GetStrokeStyle());
SafeRelease(&pathGeometry);
SafeRelease(&pSink);
}
void DXRender::PushLayer(const Geometry& dxGeometry) {
ID2D1Layer* layer = NULL;
m_render->CreateLayer(&layer);
m_render->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), dxGeometry.Get()), layer);//放入layer
SafeRelease(&layer);
}
void DXRender::PopLayer() {
m_render->PopLayer();
}
void DXRender::PushAxisAlignedClip(const RectF& rectBounds) {
m_render->PushAxisAlignedClip(__To_D2D_RectF(rectBounds), D2D1_ANTIALIAS_MODE::D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
}
void DXRender::PopAxisAlignedClip() {
m_render->PopAxisAlignedClip();
}
void DXRender::DrawImage(DXImage* image, const RectF& tagRect, float opacity) {
_NOREND_IMAGE_
if (image->Visible == false) return;
//解码
image->DecodeOfRender(m_render);
ID2D1Bitmap* bitmap = image->Get();
if (!bitmap) {
//解码失败
return;
}
ImageSizeMode imageSizeMode = image->SizeMode;
const Rect& sourceRect = image->Clip;
//计算坐标
RectF rect(tagRect.X, tagRect.Y, tagRect.Width, tagRect.Height);
//转换坐标,缩放
Size imgSize(image->Width(), image->Height());
if (!sourceRect.IsEmptyArea()) {
imgSize.Width = sourceRect.Width;
imgSize.Height = sourceRect.Height;
}
//加上在Owner区域的偏移
rect.X += image->DrawPosition.X;
rect.Y += image->DrawPosition.Y;
RectF realRendRect;//最终渲染的矩形位置
if (!image->DrawSize.Empty()) {
//限制在Owner中的绘制区域
realRendRect = RectF(rect.X, rect.Y, image->DrawSize.Width, image->DrawSize.Height);
}
else {
//不限制绘制区域 根据imageSizeMode属性进行坐标转换
realRendRect = RectF::Transformation(imageSizeMode, rect, SizeF(imgSize.Width, imgSize.Height));
}
//开始绘制
D2D_RECT_F drawRectF = __To_D2D_RectF(realRendRect);
if (!sourceRect.IsEmptyArea()) {
//裁剪图片部分内容进行绘制
D2D_RECT_F imageClipRectF = __To_D2D_RectF(sourceRect);
m_render->DrawBitmap(bitmap, drawRectF, opacity, D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, imageClipRectF);
}
else {
//不裁剪图片的方式进行绘制
m_render->DrawBitmap(bitmap, drawRectF, opacity);
}
}
void DXRender::DrawEllipse(const RectF& rectF, float strokeWidth)
{
PieGeometry pie(rectF, 0, 360);
DrawGeometry(pie.Get(), strokeWidth);
}
void DXRender::FillEllipse(const RectF& rectF)
{
PieGeometry pie(rectF, 0, 360);
FillGeometry(pie.Get());
}
void DXRender::DrawPie(const RectF& rectF, float startAngle, float endAngle, float strokeWidth)
{
PieGeometry pie(rectF, startAngle, endAngle);
DrawGeometry(pie.Get(), strokeWidth);
}
void DXRender::FillPie(const RectF& rectF, float startAngle, float endAngle)
{
PieGeometry pie(rectF, startAngle, endAngle);
FillGeometry(pie.Get());
}
void DXRender::DrawPoint(const PointF& pt) {
D2D1_ELLIPSE ellipse = D2D1::Ellipse(__To_D2D_PointF(pt), 0.5, 0.5);
m_render->FillEllipse(ellipse, GetBrush());
}
void DXRender::DrawArc(const RectF& rect, float startAngle, float sweepAngle, float width) {
}
void DXRender::DrawArc(const PointF& point1, const PointF& point2, const PointF& point3, float width)
{
}
void DXRender::DrawGeometry(ID2D1Geometry* geometry, float width)
{
m_render->DrawGeometry(geometry, GetBrush(), width, this->GetStrokeStyle());
}
void DXRender::FillGeometry(ID2D1Geometry* geometry)
{
m_render->FillGeometry(geometry, GetBrush());
}
void DXRender::DrawGeometry(Geometry* path, float width)
{
DrawGeometry(path->Get(), width);
}
void DXRender::FillGeometry(Geometry* path)
{
FillGeometry(path->Get());
}
void DXRender::Flush()
{
m_render->Flush();
}
ID2D1DCRenderTarget* DXRender::Get() {
return m_render;
}
};
#endif