Несмотря что «нагуглить» в интернете можно все, для новых технологий это далеко не так. В частности, когда я захотел использовать достаточно новые технологии Direct2D (не бойтесь, это никак не связано с DirectX 7) и DirectWrite в своем .Net-приложении, то столкнулся с проблемой что примеров взаимодействия этих библиотек и .Net нет. Поэтому пришлось самому покопаться.
Upd.: переношу в C++ т.к. дотнетчикам явно не интересно.
Сначала следует пояснить что это за библиотеки. Direct2D – это новый API от Microsoft для быстрого, аппаратно-усторенного рисования двухмерной графики. Такое нововведение обрадовало бы тех, кто пока должен смиряться с тормозами GDI+ но, увы, на данный момент этот функционал доступен только из С++. Да, работа над оберткой ведется, например, командой SlimDX, но использовать хочется сегодня, поэтому снова лезем в P/Invoke.
Вторая библиотечка – DirectWrite – сделана для качественного отображения текста. Под «качественным» подразумевается поддержка ClearType и OpenType-фич, которые важны в основном для тех кто любит типографику. Естественно, DirectWrite намного быстрее чем другие кустарные и неэффективные методы получения аналогичного результата.
Warning: все это счастье доступно через DirectX 10.1, т.е. работает только в Висте и 7ке (а также в 2008 и 2008R2 соответственно). Да и еще, для того чтобы разрабатывать под эти фреймворки нужно скачать и установить последний Windows SDK (имеется ввиду тот, который для Windows 7/2008R2, я его в подписке долго искал) т.к. by default эти фичи с Visual Studio не поставляются.

Библиотека DirectDraw использует т.н. «легковесный COM» что в переводе на русский означает что собственно COM-конструкты à la
Помимо DirectDraw, мы будем использовать некий компонент под названием Windows Imaging Component или просто WIC. Для наших (точнее моих) целей этот компонент актуален т.к. я планирую пробрасывать все тот же старый добрый
Ну что, попробуем чего-нибудь нарисовать. Я для начала сделаю прототип функции, которую буду вызывать из .Net…
Прошу прощения за многословность в опредении – это проблема 64-битной разработки и я об этом уже писал. Вот собственно аналог на С++. Как вы уже наверное догадались, функция просто рисует текст на предоставленной картинке. Для начального примера пойдет.

Итак, суть всей затеи в том чтобы воспользоваться конструктами D2D и DW для того чтобы что-то там нарисовать. Первый этап – это создать фабрики, причем не одну а целых три – для Direct2D, DirectWrite и WIC соответственно. Тем кто работал в DirectX это будет знакомо:
Здесь и далее я буду отделять троеточием декларацию интерфейсов от их инициализации дабы показать что декларации эти выполняются в любом случае, а инициализация – только если предыдущие шаги были правильно выполнены. Именно для этого и нужны постоянные проверки в стиле
Самое время использовать фабрику WIC для того чтобы создать Bitmap на который мы будем рисовать. Позднее нам придется скопировать все данные из этого битмапа в наш, дотнетный – но только после того, как мы нарисуем все, что хотим.
Инициализоровав битмап (я потратил уйму времени прежде чем нашел тот PixelFormat который работает), нужно еще найти какой-нибудь объект, который будет на этом битмапе рисовать. В отличии от GDI+, где «рисованием» занимался класс
Именно этот объект будет использован нами для рисования. Естественно, что собственно картинка будет храниться в

Форматирование текста реализует интерфейс
В отличии от DirectDraw, DirectWrite без проблем работает не только с TrueType, но и с OpenType шрифтами. Проблемы когда вы знаете что шрифт существует но система исполользует MS Sans Serif больше нет.
Прежде чем нарисовать текст, нужно создать объект типа Brush (ну прям как в GDI+, не так ли?) который будет определять «кисть», которой будет нарисован наш текст.

У нас все готово. Смело используем наш render target для отрисовки текста. Точно так же как и в DirectX, процесс рисования происходит между
Теперь, когда текст отрисован, можно скопировать его в наш
А дальше нужно просто «отпустить» все интерфейсы, и возвращаться поскорее в мир управляемого кода.

Несмотря на то, что интерфейсы D2D пока остаются доступными только для С++ программистов, взаимодействие с .Net оказалось не таким сложным как я ожидал. Поэтому не бойтесь экспериментировать. Оставляю вас с примером текста, сгенерированного с использованием вышеописанного подхода:

Upd.: переношу в C++ т.к. дотнетчикам явно не интересно.
Сначала следует пояснить что это за библиотеки. Direct2D – это новый API от Microsoft для быстрого, аппаратно-усторенного рисования двухмерной графики. Такое нововведение обрадовало бы тех, кто пока должен смиряться с тормозами GDI+ но, увы, на данный момент этот функционал доступен только из С++. Да, работа над оберткой ведется, например, командой SlimDX, но использовать хочется сегодня, поэтому снова лезем в P/Invoke.
Вторая библиотечка – DirectWrite – сделана для качественного отображения текста. Под «качественным» подразумевается поддержка ClearType и OpenType-фич, которые важны в основном для тех кто любит типографику. Естественно, DirectWrite намного быстрее чем другие кустарные и неэффективные методы получения аналогичного результата.
Warning: все это счастье доступно через DirectX 10.1, т.е. работает только в Висте и 7ке (а также в 2008 и 2008R2 соответственно). Да и еще, для того чтобы разрабатывать под эти фреймворки нужно скачать и установить последний Windows SDK (имеется ввиду тот, который для Windows 7/2008R2, я его в подписке долго искал) т.к. by default эти фичи с Visual Studio не поставляются.

Библиотека DirectDraw использует т.н. «легковесный COM» что в переводе на русский означает что собственно COM-конструкты à la
QueryInterface()
не будут появляться в вашем коде слишком часто. Единственное – останутся постоянные проверки возвращаемых результатов на корректность. Да, и еще нужно после использования интерфейсов релизить их, или использовать что-нть вроде CComPtr (хотя для этого вроде как нужно втыкать поддержку ATL – точно не знаю т.к. не пробовал).Помимо DirectDraw, мы будем использовать некий компонент под названием Windows Imaging Component или просто WIC. Для наших (точнее моих) целей этот компонент актуален т.к. я планирую пробрасывать все тот же старый добрый
System.Drawing.Bitmap
из моего .Net-кода, и рисовать в него с помощью D2D/DWrite.Ну что, попробуем чего-нибудь нарисовать. Я для начала сделаю прототип функции, которую буду вызывать из .Net…
[DllImport("Typografix.Bitmap.dll", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true,<br/>
EntryPoint = "?RenderMarkup@@YAXPEAEPEB_WHHH1M@Z", CharSet = CharSet.Unicode)]<br/>
public static extern void RenderMarkup(IntPtr dst, string markup, int width, <br/>
int height, int stride, string fontFamily, float fontSize);<br/>
Прошу прощения за многословность в опредении – это проблема 64-битной разработки и я об этом уже писал. Вот собственно аналог на С++. Как вы уже наверное догадались, функция просто рисует текст на предоставленной картинке. Для начального примера пойдет.
MYAPI void RenderMarkup(BYTE* dst, LPCWSTR markup, int width, int height, <br/>
int stride, LPCWSTR fontFamily, float fontSize)<br/>
{<br/>
⋮<br/>
}<br/>

Итак, суть всей затеи в том чтобы воспользоваться конструктами D2D и DW для того чтобы что-то там нарисовать. Первый этап – это создать фабрики, причем не одну а целых три – для Direct2D, DirectWrite и WIC соответственно. Тем кто работал в DirectX это будет знакомо:
IWICImagingFactory *pWICFactory = NULL;<br/>
ID2D1Factory *pD2DFactory = NULL;<br/>
IDWriteFactory *pDWriteFactory = NULL;<br/>
⋮<br/>
hr = CoCreateInstance(<br/>
CLSID_WICImagingFactory,<br/>
NULL,<br/>
CLSCTX_INPROC_SERVER,<br/>
IID_IWICImagingFactory,<br/>
reinterpret_cast<void **>(&pWICFactory));<br/>
<br/>
if (SUCCEEDED(hr))<br/>
{<br/>
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory);<br/>
}<br/>
<br/>
if (SUCCEEDED(hr))<br/>
{<br/>
hr = DWriteCreateFactory(<br/>
DWRITE_FACTORY_TYPE_SHARED,<br/>
__uuidof(pDWriteFactory),<br/>
reinterpret_cast<IUnknown **>(&pDWriteFactory));<br/>
}<br/>
Здесь и далее я буду отделять троеточием декларацию интерфейсов от их инициализации дабы показать что декларации эти выполняются в любом случае, а инициализация – только если предыдущие шаги были правильно выполнены. Именно для этого и нужны постоянные проверки в стиле
if (SUCCEEDED(hr))
.Самое время использовать фабрику WIC для того чтобы создать Bitmap на который мы будем рисовать. Позднее нам придется скопировать все данные из этого битмапа в наш, дотнетный – но только после того, как мы нарисуем все, что хотим.
IWICBitmap *pWICBitmap = NULL;<br/>
⋮<br/>
if (SUCCEEDED(hr))<br/>
{<br/>
hr = pWICFactory->CreateBitmap(width, height,<br/>
GUID_WICPixelFormat32bppBGR,<br/>
WICBitmapCacheOnLoad, &pWICBitmap);<br/>
}<br/>
Инициализоровав битмап (я потратил уйму времени прежде чем нашел тот PixelFormat который работает), нужно еще найти какой-нибудь объект, который будет на этом битмапе рисовать. В отличии от GDI+, где «рисованием» занимался класс
Graphics
, в DirectDraw рисовать умеет ID2D1RenderTarget
.if (SUCCEEDED(hr))<br/>
{<br/>
hr = pD2DFactory->CreateWicBitmapRenderTarget(<br/>
pWICBitmap, D2D1::RenderTargetProperties(), &pRT);<br/>
}<br/>
Именно этот объект будет использован нами для рисования. Естественно, что собственно картинка будет храниться в
pWICBitmap
который мы создали ранее, а наш RenderTarget
– это лишь промежуточный объект, который позволяет делать разные реализации рисования в зависимости от существующих ресурсов.
Форматирование текста реализует интерфейс
IDWriteTextFormat
, конкретный инстанс которого нам предстоит создать. В примере ниже, мы используем дефолтные опции, а потом просим текст отображаться в левой верхней части битмапа.IDWriteTextFormat *pTextFormat = NULL;<br/>
⋮<br/>
if (SUCCEEDED(hr))<br/>
{<br/>
hr = pDWriteFactory->CreateTextFormat(<br/>
fontFamily,<br/>
NULL,<br/>
DWRITE_FONT_WEIGHT_NORMAL,<br/>
DWRITE_FONT_STYLE_NORMAL,<br/>
DWRITE_FONT_STRETCH_NORMAL,<br/>
fontSize,<br/>
L"",<br/>
&pTextFormat);<br/>
}<br/>
if (SUCCEEDED(hr))<br/>
{<br/>
pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);<br/>
pTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR);<br/>
}<br/>
В отличии от DirectDraw, DirectWrite без проблем работает не только с TrueType, но и с OpenType шрифтами. Проблемы когда вы знаете что шрифт существует но система исполользует MS Sans Serif больше нет.
Прежде чем нарисовать текст, нужно создать объект типа Brush (ну прям как в GDI+, не так ли?) который будет определять «кисть», которой будет нарисован наш текст.
ID2D1SolidColorBrush *pBlackBrush = NULL;<br/>
⋮<br/>
if (SUCCEEDED(hr))<br/>
{<br/>
hr = pRT->CreateSolidColorBrush(<br/>
D2D1::ColorF(D2D1::ColorF::Black),<br/>
&pBlackBrush);<br/>
}<br/>

У нас все готово. Смело используем наш render target для отрисовки текста. Точно так же как и в DirectX, процесс рисования происходит между
Begin-
и End-
директивами:if (SUCCEEDED(hr))<br/>
{<br/>
pRT->BeginDraw();<br/>
pRT->Clear(D2D1::ColorF(D2D1::ColorF::White));<br/>
D2D1_SIZE_F rtSize = pRT->GetSize();<br/>
pRT->DrawText(<br/>
markup,<br/>
wcslen(markup),<br/>
pTextFormat,<br/>
D2D1::RectF(0, 0, rtSize.width, rtSize.height),<br/>
pBlackBrush);<br/>
hr = pRT->EndDraw();<br/>
}<br/>
Теперь, когда текст отрисован, можно скопировать его в наш
System.Drawing.Bitmap
WICRect r;<br/>
r.X = r.Y = 0;<br/>
r.Width = width;<br/>
r.Height = height;<br/>
hr = pWICBitmap->CopyPixels(&r, stride, sizeof(Pixel) * width * height, dst);<br/>
А дальше нужно просто «отпустить» все интерфейсы, и возвращаться поскорее в мир управляемого кода.

Несмотря на то, что интерфейсы D2D пока остаются доступными только для С++ программистов, взаимодействие с .Net оказалось не таким сложным как я ожидал. Поэтому не бойтесь экспериментировать. Оставляю вас с примером текста, сгенерированного с использованием вышеописанного подхода:
