如何将选择从 Direct2DCanvas 复制到剪贴板?
How to copy a selection from Direct2DCanvas to clipboard?
我制作了一个小程序来在 D2DBox(使用 Direct2DCanvas 和 RenderTarget)上绘制几何图形,我需要能够从它复制一个矩形到剪贴板。试过这个,但我坚持使用源句柄参数:
bm := TBitmap.Create;
try
bm.SetSize(maxx-minx, maxy-miny);
BitBlt(bm.Canvas.Handle, minx, miny, maxx, maxy, ??? , 0, 0, SRCCOPY);
Clipboard.Assign(bm);
finally
bm.Free;
end;
知道从哪里得到句柄吗?或者整个事情以不同的方式完成?谢谢!
BitBlt()
需要一个 GDI HDC
来复制,但是 TDirect2DCanvas
没有自己的 HDC
,它不能直接 渲染到屏幕外 HDC
/TCanvas
,例如 TBitmap.Canvas
,根据其 documentation:
TDirect2DCanvas will only work for on-screen device contexts. You cannot use the TDirect2DCanvas to draw on a printer device context, for example.
并且您不能关联自定义 RenderTarget
(例如使用 ID2D1Factory.CreateDCRenderTarget()
and ID2D1DCRenderTarget.BindDC()
) since the TDirect2DCanvas.RenderTarget 属性 创建的是只读的。
所以,您可能需要走很长的路才能得到您想要的。根据我在 Direct2d Desktop printing C++ 中找到的代码,它演示了将任意 ID2D1RenderTarget
复制到任意 HDC
,您可以尝试以下操作:
使用目标的 CreateBitmap()
方法之一创建一个绑定到 canvas 当前 RenderTarget
的 Direct2D ID2D1Bitmap
。
使用位图的 CopyFromRenderTarget()
方法将 canvas 中的像素复制到位图中。
创建一个 IWICBitmap
(you can probably use the VCL's TWICImage
for this) and render the Direct2D bitmap to it using one of the ID2D1Factory.CreateWicBitmapRenderTarget()
methods with one of the ID2D1RenderTarget.DrawBitmap()
方法。
创建 GDI DIB
位图并使用 WIC 位图的 CopyPixels()
方法将 IWICBitmap
渲染到它。
最后,您可以根据需要使用 DIB
,例如 select/copy 到您最终的 HDC
,或者您可以直接将其内容放在使用 CF_DIB
格式的剪贴板。
这是代码(抱歉,它是用 C++ 编写的,我不会将其翻译成 Delphi):
void Direct2DRender::RenderToDC(HDC hDC, UINT uiWidth, UINT uiHeight)
{
HRESULT hr = S_OK;
IWICImagingFactory *pImageFactory = WICImagingFactory::GetInstance().GetFactory();
CComPtr<IWICBitmap> wicBitmap;
hr = pImageFactory->CreateBitmap( uiWidth, uiHeight, GUID_WICPixelFormat32bppBGR, WICBitmapCacheOnLoad, &wicBitmap);
D2D1_SIZE_U bitmapPixelSize = D2D1::SizeU( uiWidth, uiHeight);
float dpiX, dpiY;
m_pRenderTarget->GetDpi( &dpiX, &dpiY);
CComPtr<ID2D1Bitmap> d2dBitmap;
hr = m_pRenderTarget->CreateBitmap( bitmapPixelSize, D2D1::BitmapProperties(
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE),
dpiX, dpiY), &d2dBitmap );
D2D1_POINT_2U dest = D2D1::Point2U(0,0);
D2D1_RECT_U src = D2D1::RectU(0, 0, uiWidth, uiHeight);
hr = d2dBitmap->CopyFromRenderTarget(&dest, m_pRenderTarget, &src);
D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties();
rtProps.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE);
rtProps.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
rtProps.usage = D2D1_RENDER_TARGET_USAGE_NONE;
CComPtr<ID2D1RenderTarget> wicRenderTarget;
hr = m_pDirect2dFactory->CreateWicBitmapRenderTarget( wicBitmap, rtProps, &wicRenderTarget);
wicRenderTarget->BeginDraw();
wicRenderTarget->DrawBitmap(d2dBitmap);
hr = wicRenderTarget->EndDraw();
// Render the image to a GDI device context
HBITMAP hDIBBitmap = NULL;
try
{
// Get a DC for the full screen
HDC hdcScreen = GetDC(NULL);
if (!hdcScreen)
throw 1;
BITMAPINFO bminfo;
ZeroMemory(&bminfo, sizeof(bminfo));
bminfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bminfo.bmiHeader.biWidth = uiWidth;
bminfo.bmiHeader.biHeight = -(LONG)uiHeight;
bminfo.bmiHeader.biPlanes = 1;
bminfo.bmiHeader.biBitCount = 32;
bminfo.bmiHeader.biCompression = BI_RGB;
void* pvImageBits = nullptr; // Freed with DeleteObject(hDIBBitmap)
hDIBBitmap = CreateDIBSection(hdcScreen, &bminfo, DIB_RGB_COLORS, &pvImageBits, NULL, 0);
if (!hDIBBitmap)
throw 2;
ReleaseDC(NULL, hdcScreen);
// Calculate the number of bytes in 1 scanline
UINT nStride = DIB_WIDTHBYTES(uiWidth * 32);
// Calculate the total size of the image
UINT nImage = nStride * uiHeight;
// Copy the pixels to the DIB section
hr = wicBitmap->CopyPixels(nullptr, nStride, nImage, reinterpret_cast<BYTE*>(pvImageBits));
// Copy the bitmap to the target device context
::SetDIBitsToDevice(hDC, 0, 0, uiWidth, uiHeight, 0, 0, 0, uiHeight, pvImageBits, &bminfo, DIB_RGB_COLORS);
DeleteObject(hDIBBitmap);
}
catch (...)
{
if (hDIBBitmap)
DeleteObject(hDIBBitmap);
// Rethrow the exception, so the client code can handle it
throw;
}
}
在同一个讨论中,描述了另一种选择:
Create a DIB section, and select it into a DC
Create a DC render target
Bind the render target to the DC that corresponds to the DIB section
Draw using Direct2D. After calling EndDraw the DIB contains what was rendered.
The final step is to draw the dib where you need it.
因此,请尝试将您的绘图代码移动到它自己的函数中,该函数将 ID2D1RenderTarget
作为输入并根据需要在其上绘制。然后,当你想在剪贴板上放置位图时,你可以创建一个基于 HDC
的 RenderTarget
,当你想在你的 D2DBox
上绘图时使用 TDirect2DCanvas.RenderTarget
。
我制作了一个小程序来在 D2DBox(使用 Direct2DCanvas 和 RenderTarget)上绘制几何图形,我需要能够从它复制一个矩形到剪贴板。试过这个,但我坚持使用源句柄参数:
bm := TBitmap.Create;
try
bm.SetSize(maxx-minx, maxy-miny);
BitBlt(bm.Canvas.Handle, minx, miny, maxx, maxy, ??? , 0, 0, SRCCOPY);
Clipboard.Assign(bm);
finally
bm.Free;
end;
知道从哪里得到句柄吗?或者整个事情以不同的方式完成?谢谢!
BitBlt()
需要一个 GDI HDC
来复制,但是 TDirect2DCanvas
没有自己的 HDC
,它不能直接 渲染到屏幕外 HDC
/TCanvas
,例如 TBitmap.Canvas
,根据其 documentation:
TDirect2DCanvas will only work for on-screen device contexts. You cannot use the TDirect2DCanvas to draw on a printer device context, for example.
并且您不能关联自定义 RenderTarget
(例如使用 ID2D1Factory.CreateDCRenderTarget()
and ID2D1DCRenderTarget.BindDC()
) since the TDirect2DCanvas.RenderTarget 属性 创建的是只读的。
所以,您可能需要走很长的路才能得到您想要的。根据我在 Direct2d Desktop printing C++ 中找到的代码,它演示了将任意 ID2D1RenderTarget
复制到任意 HDC
,您可以尝试以下操作:
使用目标的
CreateBitmap()
方法之一创建一个绑定到 canvas 当前RenderTarget
的 Direct2DID2D1Bitmap
。使用位图的
CopyFromRenderTarget()
方法将 canvas 中的像素复制到位图中。创建一个
IWICBitmap
(you can probably use the VCL'sTWICImage
for this) and render the Direct2D bitmap to it using one of theID2D1Factory.CreateWicBitmapRenderTarget()
methods with one of theID2D1RenderTarget.DrawBitmap()
方法。创建 GDI
DIB
位图并使用 WIC 位图的CopyPixels()
方法将IWICBitmap
渲染到它。最后,您可以根据需要使用
DIB
,例如 select/copy 到您最终的HDC
,或者您可以直接将其内容放在使用CF_DIB
格式的剪贴板。
这是代码(抱歉,它是用 C++ 编写的,我不会将其翻译成 Delphi):
void Direct2DRender::RenderToDC(HDC hDC, UINT uiWidth, UINT uiHeight) { HRESULT hr = S_OK; IWICImagingFactory *pImageFactory = WICImagingFactory::GetInstance().GetFactory(); CComPtr<IWICBitmap> wicBitmap; hr = pImageFactory->CreateBitmap( uiWidth, uiHeight, GUID_WICPixelFormat32bppBGR, WICBitmapCacheOnLoad, &wicBitmap); D2D1_SIZE_U bitmapPixelSize = D2D1::SizeU( uiWidth, uiHeight); float dpiX, dpiY; m_pRenderTarget->GetDpi( &dpiX, &dpiY); CComPtr<ID2D1Bitmap> d2dBitmap; hr = m_pRenderTarget->CreateBitmap( bitmapPixelSize, D2D1::BitmapProperties( D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), dpiX, dpiY), &d2dBitmap ); D2D1_POINT_2U dest = D2D1::Point2U(0,0); D2D1_RECT_U src = D2D1::RectU(0, 0, uiWidth, uiHeight); hr = d2dBitmap->CopyFromRenderTarget(&dest, m_pRenderTarget, &src); D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties(); rtProps.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE); rtProps.type = D2D1_RENDER_TARGET_TYPE_DEFAULT; rtProps.usage = D2D1_RENDER_TARGET_USAGE_NONE; CComPtr<ID2D1RenderTarget> wicRenderTarget; hr = m_pDirect2dFactory->CreateWicBitmapRenderTarget( wicBitmap, rtProps, &wicRenderTarget); wicRenderTarget->BeginDraw(); wicRenderTarget->DrawBitmap(d2dBitmap); hr = wicRenderTarget->EndDraw(); // Render the image to a GDI device context HBITMAP hDIBBitmap = NULL; try { // Get a DC for the full screen HDC hdcScreen = GetDC(NULL); if (!hdcScreen) throw 1; BITMAPINFO bminfo; ZeroMemory(&bminfo, sizeof(bminfo)); bminfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bminfo.bmiHeader.biWidth = uiWidth; bminfo.bmiHeader.biHeight = -(LONG)uiHeight; bminfo.bmiHeader.biPlanes = 1; bminfo.bmiHeader.biBitCount = 32; bminfo.bmiHeader.biCompression = BI_RGB; void* pvImageBits = nullptr; // Freed with DeleteObject(hDIBBitmap) hDIBBitmap = CreateDIBSection(hdcScreen, &bminfo, DIB_RGB_COLORS, &pvImageBits, NULL, 0); if (!hDIBBitmap) throw 2; ReleaseDC(NULL, hdcScreen); // Calculate the number of bytes in 1 scanline UINT nStride = DIB_WIDTHBYTES(uiWidth * 32); // Calculate the total size of the image UINT nImage = nStride * uiHeight; // Copy the pixels to the DIB section hr = wicBitmap->CopyPixels(nullptr, nStride, nImage, reinterpret_cast<BYTE*>(pvImageBits)); // Copy the bitmap to the target device context ::SetDIBitsToDevice(hDC, 0, 0, uiWidth, uiHeight, 0, 0, 0, uiHeight, pvImageBits, &bminfo, DIB_RGB_COLORS); DeleteObject(hDIBBitmap); } catch (...) { if (hDIBBitmap) DeleteObject(hDIBBitmap); // Rethrow the exception, so the client code can handle it throw; } }
在同一个讨论中,描述了另一种选择:
Create a DIB section, and select it into a DC
Create a DC render target
Bind the render target to the DC that corresponds to the DIB section
Draw using Direct2D. After calling EndDraw the DIB contains what was rendered.
The final step is to draw the dib where you need it.
因此,请尝试将您的绘图代码移动到它自己的函数中,该函数将 ID2D1RenderTarget
作为输入并根据需要在其上绘制。然后,当你想在剪贴板上放置位图时,你可以创建一个基于 HDC
的 RenderTarget
,当你想在你的 D2DBox
上绘图时使用 TDirect2DCanvas.RenderTarget
。