Как скопировать выделение из Direct2DCanvas в буфер обмена? - PullRequest
0 голосов
/ 14 апреля 2020

Я создал небольшую программу для рисования геометрических фигур на 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;

Есть идеи, где взять дескриптор? Или все сделано по-другому? Thanx!

1 Ответ

1 голос
/ 14 апреля 2020

BitBlt() требует GDI HDC для копирования, но TDirect2DCanvas не имеет собственного HDC, и он не может напрямую отображать за пределами экрана HDC / TCanvas, например TBitmap.Canvas, согласно документации :

TDirect2DCanvas будет работать только для контекстов экранного устройства . Вы не можете использовать TDirect2DCanvas для рисования в контексте устройства принтера, например.

И вы не можете связать пользовательский RenderTarget (например, созданный с ID2D1Factory.CreateDCRenderTarget()* 1023). * и ID2D1DCRenderTarget.BindDC()), поскольку свойство TDirect2DCanvas.RenderTarget доступно только для чтения.

Таким образом, вам, вероятно, потребуется go длинный путь, чтобы получить то, что ты хочешь. Основываясь на коде, который я нашел в Direct2d Desktop для печати C ++ , который демонстрирует копирование произвольного ID2D1RenderTarget в произвольное HDC, вы можете попробовать следующее:

  • Создайте Direct2D ID2D1Bitmap, привязанный к текущему RenderTarget холста, используя один из методов CreateBitmap() цели.

  • Скопируйте пиксели из холста в растровое изображение с использованием метода CopyFromRenderTarget().

  • Создайте IWICBitmap (вы, вероятно, можете использовать VCL TWICImage для этого) и визуализировать в него растровое изображение Direct2D, используя один из методов ID2D1Factory.CreateWicBitmapRenderTarget() с одним из методов ID2D1RenderTarget.DrawBitmap().

  • Создайте растровое изображение GDI DIB и визуализируйте в него IWICBitmap, используя растровое изображение WI C CopyPixels().

  • Наконец, вы используете DIB так, как вам нужно, например, выберите / скопируйте его в ваш окончательный 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;
  }
}

В том же обсуждении описывается другая альтернатива:

  • Создайте раздел DIB и выберите его в D C

  • Создание цели рендеринга D C

  • Привязка цели рендеринга к D C, который соответствует секции DIB

  • Рисование с использованием Direct2D. После вызова EndDraw DIB содержит то, что было отрендерено.

  • Последний шаг - нарисовать метку там, где она вам нужна.

Итак, попробуйте переместить ваш код для рисования в его собственную функцию, которая принимает ID2D1RenderTarget в качестве входных данных и dr aws в случае необходимости. Затем вы можете создать RenderTarget на основе HDC, когда хотите поместить растровое изображение в буфер обмена, и использовать TDirect2DCanvas.RenderTarget, когда хотите нарисовать на D2DBox.

...