Direct2D- und GDI-Interoperabilität – Übersicht
In diesem Thema wird beschrieben, wie Sie Direct2D und GDI zusammen verwenden. Es gibt zwei Möglichkeiten, Direct2D mit GDI zu kombinieren: Sie können GDI-Inhalte in ein Direct2D-GDI-kompatibles Renderziel schreiben oder Direct2D-Inhalte in einen GDI-Gerätekontext (DC) schreiben.
Dieses Thema enthält folgende Abschnitte:
- Voraussetzungen
- Zeichnen von Direct2D-Inhalten in einen GDI-Gerätekontext
- ID2D1DCRenderTargets, GDI-Transformationen und Rechts-nach-Links-Sprachbuilds von Windows
- Zeichnen von GDI-Inhalten in ein Direct2D-GDI-Compatible-Renderziel
- Zugehörige Themen
Voraussetzungen
In dieser Übersicht wird vorausgesetzt, dass Sie mit den grundlegenden Direct2D-Zeichnungsvorgängen vertraut sind. Ein Tutorial finden Sie unter Erstellen einer einfachen Direct2D-Anwendung. Außerdem wird davon ausgegangen, dass Sie mit GDI-Zeichnungsvorgängen vertraut sind.
Zeichnen von Direct2D-Inhalten in einen GDI-Gerätekontext
Um Direct2D-Inhalte auf einen GDI-DC zu zeichnen, verwenden Sie ein ID2D1DCRenderTarget. Um ein DC-Renderziel zu erstellen, verwenden Sie die ID2D1Factory::CreateDCRenderTarget-Methode . Diese Methode benötigt zwei Parameter.
Der erste Parameter, eine D2D1_RENDER_TARGET_PROPERTIES-Struktur , gibt Rendering-, Remoting-, DPI-, Pixelformat- und Nutzungsinformationen an. Damit das DC-Renderziel mit GDI funktioniert, legen Sie das DXGI-Format auf DXGI_FORMAT_B8G8R8A8_UNORM und den Alphamodus auf D2D1_ALPHA_MODE_PREMULTIPLIED oder D2D1_ALPHA_MODE_IGNORE fest.
Der zweite Parameter ist die Adresse des Zeigers, der die DC-Renderzielreferenz empfängt.
Mit dem folgenden Code wird ein DC-Renderziel erstellt.
// Create a DC render target.
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(
DXGI_FORMAT_B8G8R8A8_UNORM,
D2D1_ALPHA_MODE_IGNORE),
0,
0,
D2D1_RENDER_TARGET_USAGE_NONE,
D2D1_FEATURE_LEVEL_DEFAULT
);
hr = m_pD2DFactory->CreateDCRenderTarget(&props, &m_pDCRT);
Im vorherigen Code ist m_pD2DFactory ein Zeiger auf eine ID2D1Factory, und m_pDCRT ist ein Zeiger auf ein ID2D1DCRenderTarget.
Bevor Sie mit dem DC-Renderziel rendern können, müssen Sie dessen BindDC-Methode verwenden, um es einem GDI-DC zuzuordnen. Dies geschieht jedes Mal, wenn Sie einen anderen DC oder die Größe des Bereichs verwenden, den Sie für Änderungen zeichnen möchten.
Die BindDC-Methode akzeptiert zwei Parameter: hDC und pSubRect. Der hDC-Parameter stellt ein Handle für den Gerätekontext bereit, der die Ausgabe des Renderziels empfängt. Der pSubRect-Parameter ist ein Rechteck, das den Teil des Gerätekontexts beschreibt, in den Inhalte gerendert werden. Das DC-Renderziel aktualisiert seine Größe so, dass es dem von pSubRect beschriebenen Gerätekontextbereich entspricht, falls es die Größe ändert.
Der folgende Code bindet einen DC an ein DC-Renderziel.
HRESULT DemoApp::OnRender(const PAINTSTRUCT &ps)
{
// Get the dimensions of the client drawing area.
GetClientRect(m_hwnd, &rc);
C++ |
---|
|
Nachdem Sie das DC-Renderziel einem DC zugeordnet haben, können Sie es zum Zeichnen verwenden. Der folgende Code zeichnet Direct2D- und GDI-Inhalte mithilfe eines DC.
HRESULT DemoApp::OnRender(const PAINTSTRUCT &ps)
{
HRESULT hr;
RECT rc;
// Get the dimensions of the client drawing area.
GetClientRect(m_hwnd, &rc);
//
// Draw the pie chart with Direct2D.
//
// Create the DC render target.
hr = CreateDeviceResources();
if (SUCCEEDED(hr))
{
// Bind the DC to the DC render target.
hr = m_pDCRT->BindDC(ps.hdc, &rc);
m_pDCRT->BeginDraw();
m_pDCRT->SetTransform(D2D1::Matrix3x2F::Identity());
m_pDCRT->Clear(D2D1::ColorF(D2D1::ColorF::White));
m_pDCRT->DrawEllipse(
D2D1::Ellipse(
D2D1::Point2F(150.0f, 150.0f),
100.0f,
100.0f),
m_pBlackBrush,
3.0
);
m_pDCRT->DrawLine(
D2D1::Point2F(150.0f, 150.0f),
D2D1::Point2F(
(150.0f + 100.0f * 0.15425f),
(150.0f - 100.0f * 0.988f)),
m_pBlackBrush,
3.0
);
m_pDCRT->DrawLine(
D2D1::Point2F(150.0f, 150.0f),
D2D1::Point2F(
(150.0f + 100.0f * 0.525f),
(150.0f + 100.0f * 0.8509f)),
m_pBlackBrush,
3.0
);
m_pDCRT->DrawLine(
D2D1::Point2F(150.0f, 150.0f),
D2D1::Point2F(
(150.0f - 100.0f * 0.988f),
(150.0f - 100.0f * 0.15425f)),
m_pBlackBrush,
3.0
);
hr = m_pDCRT->EndDraw();
if (SUCCEEDED(hr))
{
//
// Draw the pie chart with GDI.
//
// Save the original object.
HGDIOBJ original = NULL;
original = SelectObject(
ps.hdc,
GetStockObject(DC_PEN)
);
HPEN blackPen = CreatePen(PS_SOLID, 3, 0);
SelectObject(ps.hdc, blackPen);
Ellipse(ps.hdc, 300, 50, 500, 250);
POINT pntArray1[2];
pntArray1[0].x = 400;
pntArray1[0].y = 150;
pntArray1[1].x = static_cast<LONG>(400 + 100 * 0.15425);
pntArray1[1].y = static_cast<LONG>(150 - 100 * 0.9885);
POINT pntArray2[2];
pntArray2[0].x = 400;
pntArray2[0].y = 150;
pntArray2[1].x = static_cast<LONG>(400 + 100 * 0.525);
pntArray2[1].y = static_cast<LONG>(150 + 100 * 0.8509);
POINT pntArray3[2];
pntArray3[0].x = 400;
pntArray3[0].y = 150;
pntArray3[1].x = static_cast<LONG>(400 - 100 * 0.988);
pntArray3[1].y = static_cast<LONG>(150 - 100 * 0.15425);
Polyline(ps.hdc, pntArray1, 2);
Polyline(ps.hdc, pntArray2, 2);
Polyline(ps.hdc, pntArray3, 2);
DeleteObject(blackPen);
// Restore the original object.
SelectObject(ps.hdc, original);
}
}
if (hr == D2DERR_RECREATE_TARGET)
{
hr = S_OK;
DiscardDeviceResources();
}
return hr;
}
Dieser Code erzeugt Ausgaben, wie in der folgenden Abbildung gezeigt (Legenden wurden hinzugefügt, um den Unterschied zwischen Direct2D- und GDI-Rendering hervorzuheben.)
ID2D1DCRenderTargets, GDI-Transformationen und Rechts-nach-Links-Sprachbuilds von Windows
Wenn Sie ein ID2D1DCRenderTarget verwenden, werden Direct2D-Inhalte in einer internen Bitmap gerendert und anschließend mit GDI auf dem DC gerendert.
GDI kann eine GDI-Transformation (über die SetWorldTransform-Methode ) oder einen anderen Effekt auf den gleichen DC anwenden, der vom Renderziel verwendet wird. In diesem Fall transformiert GDI die von Direct2D erzeugte Bitmap. Die Verwendung einer GDI-Transformation zum Transformieren des Direct2D-Inhalts hat das Potenzial, die visuelle Qualität der Ausgabe zu beeinträchtigen, da Sie eine Bitmap transformieren, für die bereits Antialiasing und Subpixelpositionierung berechnet wurden.
Angenommen, Sie verwenden das Renderziel, um eine Szene zu zeichnen, die antialiasierte Geometrien und Text enthält. Wenn Sie eine GDI-Transformation verwenden, um eine Skalierungstransformation auf den DC anzuwenden und die Szene so zu skalieren, dass sie 10 Mal größer ist, werden Pixelisierung und zerklüftete Kanten angezeigt. (Wenn Sie jedoch eine ähnliche Transformation mit Direct2D angewendet haben, wird die visuelle Qualität der Szene nicht beeinträchtigt.)
In einigen Fällen ist es möglicherweise nicht offensichtlich, dass GDI zusätzliche Verarbeitung durchführt, die die Qualität des Direct2D-Inhalts beeinträchtigen könnte. Beispielsweise kann in einem RTL-Build (Right-to-Left) von Windows der von einem ID2D1DCRenderTarget gerenderte Inhalt horizontal invertiert werden, wenn GDI ihn an sein Ziel kopiert. Ob der Inhalt tatsächlich invertiert wird, hängt von den aktuellen Einstellungen des DC ab.
Je nach Art des gerenderten Inhalts können Sie die Inversion verhindern. Wenn der Direct2D-Inhalt ClearType-Text enthält, beeinträchtigt diese Inversion die Qualität des Texts.
Sie können das RTL-Renderingverhalten mithilfe der GDI-Funktion SetLayout steuern. Um die Spiegelung zu verhindern, rufen Sie die GDI-Funktion SetLayout auf, und geben Sie LAYOUT_BITMAPORIENTATIONPRESERVED als einzigen Wert für den zweiten Parameter an (kombinieren Sie ihn nicht mit LAYOUT_RTL), wie im folgenden Beispiel gezeigt:
SetLayout(m_hwnd, LAYOUT_BITMAPORIENTATIONPRESERVED);
Zeichnen von GDI-Inhalten in ein Direct2D-GDI-Compatible-Renderziel
Im vorherigen Abschnitt wird beschrieben, wie Direct2D-Inhalte in einen GDI-DC geschrieben werden. Sie können auch GDI-Inhalte in ein Direct2D-GDI-kompatibles Renderziel schreiben. Dieser Ansatz ist nützlich für Anwendungen, die hauptsächlich mit Direct2D rendern, aber über ein Erweiterbarkeitsmodell oder andere Legacyinhalte verfügen, die die Möglichkeit erfordern, mit GDI zu rendern.
Um GDI-Inhalte in einem Direct2D GDI-kompatiblen Renderziel zu rendern, verwenden Sie ein ID2D1GdiInteropRenderTarget, das Zugriff auf einen Gerätekontext bietet, der GDI-Zeichnungsaufrufe akzeptieren kann. Im Gegensatz zu anderen Schnittstellen wird ein ID2D1GdiInteropRenderTarget-Objekt nicht direkt erstellt. Verwenden Sie stattdessen die QueryInterface-Methode eines vorhandenen Renderziels instance. Dies wird im folgenden Code veranschaulicht:
D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties();
rtProps.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE;
// Create a GDI compatible Hwnd render target.
hr = m_pD2DFactory->CreateHwndRenderTarget(
rtProps,
D2D1::HwndRenderTargetProperties(m_hwnd, size),
&m_pRenderTarget
);
if (SUCCEEDED(hr))
{
hr = m_pRenderTarget->QueryInterface(__uuidof(ID2D1GdiInteropRenderTarget), (void**)&m_pGDIRT);
}
Im vorherigen Code ist m_pD2DFactory ein Zeiger auf eine ID2D1Factory, und m_pGDIRT ist ein Zeiger auf ein ID2D1GdiInteropRenderTarget.
Beachten Sie, dass dasflag D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE beim Erstellen des Hwnd GDI-kompatiblen Renderziels angegeben wird. Wenn ein Pixelformat erforderlich ist, verwenden Sie DXGI_FORMAT_B8G8R8A8_UNORM. Wenn ein Alphamodus erforderlich ist, verwenden Sie D2D1_ALPHA_MODE_PREMULTIPLIED oder D2D1_ALPHA_MODE_IGNORE.
Beachten Sie, dass die QueryInterface-Methode immer erfolgreich ist. Um zu testen, ob die Methoden der ID2D1GdiInteropRenderTarget-Schnittstelle für ein bestimmtes Renderziel funktionieren, erstellen Sie eine D2D1_RENDER_TARGET_PROPERTIES , die GDI-Kompatibilität und das entsprechende Pixelformat angibt, und rufen Sie dann die IsSupported-Methode des Renderziels auf, um festzustellen, ob das Renderziel GDI-kompatibel ist.
Das folgende Beispiel zeigt, wie Ein Kreisdiagramm (GDI-Inhalt) für das Hwnd-GDI-kompatible Renderziel gezeichnet wird.
HDC hDC = NULL;
hr = m_pGDIRT->GetDC(D2D1_DC_INITIALIZE_MODE_COPY, &hDC);
if (SUCCEEDED(hr))
{
// Draw the pie chart to the GDI render target associated with the Hwnd render target.
HGDIOBJ original = NULL;
original = SelectObject(
hDC,
GetStockObject(DC_PEN)
);
HPEN blackPen = CreatePen(PS_SOLID, 3, 0);
SelectObject(hDC, blackPen);
Ellipse(hDC, 300, 50, 500, 250);
POINT pntArray1[2];
pntArray1[0].x = 400;
pntArray1[0].y = 150;
pntArray1[1].x = static_cast<LONG>(400 + 100 * 0.15425);
pntArray1[1].y = static_cast<LONG>(150 - 100 * 0.9885);
POINT pntArray2[2];
pntArray2[0].x = 400;
pntArray2[0].y = 150;
pntArray2[1].x = static_cast<LONG>(400 + 100 * 0.525);
pntArray2[1].y = static_cast<LONG>(150 + 100 * 0.8509);
POINT pntArray3[2];
pntArray3[0].x = 400;
pntArray3[0].y = 150;
pntArray3[1].x = static_cast<LONG>(400 - 100 * 0.988);
pntArray3[1].y = static_cast<LONG>(150 - 100 * 0.15425);
Polyline(hDC, pntArray1, 2);
Polyline(hDC, pntArray2, 2);
Polyline(hDC, pntArray3, 2);
DeleteObject(blackPen);
// Restore the original object.
SelectObject(hDC, original);
m_pGDIRT->ReleaseDC(NULL);
}
Der Code gibt Diagramme aus, wie in der folgenden Abbildung gezeigt, mit Legenden, um den Unterschied in der Renderingqualität hervorzuheben. Das rechte Kreisdiagramm (GDI-Inhalt) weist eine niedrigere Renderingqualität auf als das linke Kreisdiagramm (Direct2D-Inhalt). Dies liegt daran, dass Direct2D in der Lage ist, mit Antialiasing zu rendern.
Zugehörige Themen