DirectWrite msdn (二)

自己留着用的,翻译不好你可以不用看。

Tutorial: Getting Started with DirectWrite

Drawing Simple Text

描画简单的字体到屏幕需要四个部分

l  要描画的字符串

l  IDWriteTextFormat的实例

l  包含文本的区域

l  渲染文本的对象,你可以使用Direct2D的render target

接口IDWriteTextFormat描述了font-family name、size、weight、style、格式化文本的stretch,和locale信息。该接口定义了很多方法用于设置或获取下面这些信息。

l  行间距;

l  相对与layout box的左边界或右边界的对齐方式;

l  相对于layout box的上下边界的段落对齐方式;

l  阅读方向;

l  对溢出layout box的trimming(微调);

l  incremental(增量)tab stop;

l  段落的flow 方向。

    使用IDWriteTextFormat之前必须用IDWriteFactory创建它,而用DWriteCreateFactory来创建后者。

    Direct2D提供了两种类型的资源:设备独立资源和设备依赖资源,后者和具体设备相关,如果设备被移除,这些设备依赖资源必须释放掉,而设备无关资源则可以在程序整个生命周期中使用。DirectWrite资源是设备独立的。

1、创建工厂的例子:

 

    hr = DWriteCreateFactory(

        DWRITE_FACTORY_TYPE_SHARED,

        __uuidof(IDWriteFactory),

        reinterpret_cast<IUnknown**>(&pDWriteFactory_)

        );

2、创建IDWriteTextFormat的例子:

if (SUCCEEDED(hr))

{

    hr = pDWriteFactory_->CreateTextFormat(

        L"Gabriola",  // Font family name.

        NULL,   // Font collection (NULL sets it to use the system font collection).

        DWRITE_FONT_WEIGHT_REGULAR,

        DWRITE_FONT_STYLE_NORMAL,

        DWRITE_FONT_STRETCH_NORMAL,

        72.0f,

        L"en-us",

        &pTextFormat_

        );

}

3、水平和垂直居中文本通过调用IDWriteTextFormat::SetTextAlignment和SetParagraphAlignment:

// Center align (horizontally) the text.

if (SUCCEEDED(hr))

{

hr=pTextFormat_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);

}

if (SUCCEEDED(hr))

{

hr=pTextFormat_->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);

}

4、渲染文本的例子:

 

pRT_->DrawText(

    wszText_,        // The string to render.

    cTextLength_,    // The string's length.

    pTextFormat_,    // The text format.

    layoutRect,       // The region of the window where the text will be rendered.

    pBlackBrush_     // The brush used to draw the text.

    );

Drawing Text with Multiple Formats

为了创建一个MultipleFomat的字体,用接口IDWriteTextLayout接口。这个接口描述了一个文本和它的格式化信息。通过这个接口可以改变许多被IDWritetextformat接口默认设置的信息,这个接口还可以进行点击测试。

IDWriteTypography接口被用来给IDWriteTextLayout接口添加可选的OpenType排版特性,例如swashes和可变的stylistic text sets。排版特性还可以只加给一部分文本,用IDWriteTypography::AddFontFeature方法,这个方法接受一个DWRITE_FONT_FEATURE结构体,

1、创建IDWriteTextLayout的例子:

 

// Create a text layout using the text format.

if (SUCCEEDED(hr))

{

    RECT rect;

    GetClientRect(hwnd_, &rect);

    float width  = rect.right  / dpiScaleX_;

    float height = rect.bottom / dpiScaleY_;

    hr = pDWriteFactory_->CreateTextLayout(

        wszText_,      // The string to be laid out and formatted.

        cTextLength_,  // The length of the string.

        pTextFormat_,  // The text format to apply to the string (contains font information, etc).

        width,         // The width of the layout box.

        height,        // The height of the layout box.

        &pTextLayout_  // The IDWriteTextLayout interface pointer.

        );

}

2、用IDWriteTextLayout接口设置各种格式:

// Format the "DirectWrite" substring to be of font size 100.

if (SUCCEEDED(hr))

{

 DWRITE_TEXT_RANGE textRange={20,// Start index where "DirectWrite" appears.

                            6 };// Length of the substring "Direct" in"DirectWrite".

    hr = pTextLayout_->SetFontSize(100.0f, textRange);

}

3、添加下划线:

// Format the word "DWrite" to be underlined.

if (SUCCEEDED(hr))

{

DWRITE_TEXT_RANGE textRange = {20,      // Start index where "DirectWrite" appears.

                              11 };    // Length of the substring "DirectWrite".

 hr = pTextLayout_->SetUnderline(TRUE, textRange);

}

4、设置字体weight:

if (SUCCEEDED(hr))

{

 // Format the word "DWrite" to be bold.

 DWRITE_TEXT_RANGE textRange = {20,

                               11 };

  hr = pTextLayout_->SetFontWeight(DWRITE_FONT_WEIGHT_BOLD, textRange);

}

5、用接口IDWriteTypography添加排版特性:

 

// Declare a typography pointer.

IDWriteTypography* pTypography = NULL;

 

// Create a typography interface object.

if (SUCCEEDED(hr))

{

   hr = pDWriteFactory_->CreateTypography(&pTypography);

}

 

// Set the stylistic set.

DWRITE_FONT_FEATURE fontFeature = {DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_7,

                                   1};

if (SUCCEEDED(hr))

{

    hr = pTypography->AddFontFeature(fontFeature);

}

 

if (SUCCEEDED(hr))

{

    // Set the typography for the entire string.

    DWRITE_TEXT_RANGE textRange = {0,

                                   cTextLength_};

    hr = pTextLayout_->SetTypography(pTypography, textRange);

}

6、为了描画用IDWriteTextLayout具体化各种信息的字体,调用IDWriteTextLayout::DrawTextLayout方法。

pRT_->DrawTextLayout(

    origin,

   pTextLayout_,

    pBlackBrush_

    );

Text Formatting and Layout

DirectWrite提供了两个接口用于格式化文本,IDWriteTextLayout和IDWriteTextFormat,后者用于这个文本的所有风格全一致的情形而前者则更具灵活性,对于一个文本串,子串可以有不同的风格。

IDWriteTextFormat接口:

l  渲染时描述了整个字符串的格式;

l  当创建IDWriteTextLayout对象的时候,具体化默认的文本格式;

该接口的创建操作已经在上一篇中讲过了,下面只对上一篇中没有涉及到的进行描述。

一旦该接口被创建,有些值就不能被改变了,如font family、collection、weight、size、

local name。如果你想要不重新创建就该变某些属性,可以使用IDWriteTextLayout接口。

IDWriteTextLayout接口:

一旦该接口被创建,该Layout对应的文本就不能被改变了除非重新创建。该接口提供了方法设置某个范围文本的格式,也提供了方法该变font style和weight,添加OpenType字体特性和点击测试。

Rendering Options

被IDWriteTextFormat描述的字体可以被Direct2D渲染,然而,被IDWriteTextLayout描述的文本则有更多的描画选择。Direct2D、自定义的text renderer、渲染到GDI表面。

第一种:pRT_->DrawTextLayout(origin, pTextLayout_, pBlackBrush_ );
第二种:pTextLayout_->Draw(NULL, pTextRenderer_, origin.x, origin.y);

IDWriteTextLayout::Draw方法调用你提供的自定义的Renderer的回调函数,DrawGlyphRun、DrawUnderline、DrawInlineObject和DrawStrikethrough.

IDWriteTextRender声明的方法用于描画glyph run、underline、strikethrough、inline object。然后就是应用程序去实现这些方法了。创建自定义的text renderer允许应用程序渲染文字的时候实现特殊的效果例如自定义fill 或者outline。

第三种渲染到GDI表面实际上是自定义text renderer的一个例子,一些工作得通过接口IDWriteBitmapRenderTarget来完成,创建这个接口需要使用IDWriteGdiInterop::CreateBitmapRenderTarget方法。自定义的TextRenderer的DrawGlyphRun方法调用IDWriteBitmapRenderTarget::DrawGlyRun方法描画字形,其他的方法得由自定义的Text Renderer自己完成。IDWriteBitmapRenderTarget接口把内容渲染到内存中的DC上,获取DC通过IDWriteBitmapRenderTarget::GetMemoryDC方法如

memoryHdc = g_pBitmapRenderTarget->GetMemoryDC();

一旦描画完成,IDWriteBitmapRenderTarget对象的内存DC对象必须被拷贝到目的GDI的表面。注意,你也可以有转换位图到另一种类型的表面的选择,例如GDI+

// Transfer from DWrite's rendering target to the window.
BitBlt(hdc, 0, 0, size.cx, size.cy, memoryHdc, 0, 0, 
        SRCCOPY | NOMIRRORBITMAP);

Glyphs and Glyph Runs

Glyphs和Glyph Runs是DirectWrite中最底层的功能,字形渲染层。

Glyphs

字形是给定字体的字母的物理表现。字母可能有很多字形,系统中每一种字体可能为每一个字母定义了一个不同的字形。

两个或者更多的字形也可以被关联到一个单独的字形中,这个过程叫做字形组合。也能够被以相反的方向完成,一个单独的字形被分解成多个字形,叫字形解组合。

Alternate Glyphs

字体可能会为字母提供可更改的字形,例如stylistic alternate glyphs为Pericles OpenType字体。下面图片所显示的,A E O字符都是以stylistic alternate glyphs渲染的。

 

另外一个例子是swash glyphs。下图显示了标准的和swash glyphs的Pescadero字体。

 

swashes 和其他的排版特性,包括更加详细的alternate glyphs通过OpenType都是有效的。OpenType特性能够通过IDWriteTextLayout::SetTypography并且传递和期望特性关联的DWRITE_FONT_FEATURE_TAG枚举常量来应用给某范围文字。

 

Glyph Runs

    一个glyph run代表了一个连续的拥有相同font face和大小,相同的客户端描画效果的字形集合。对于下划线和删除线不是Glyph run的一部分,它们稍后描画,Inline Object也是单独描画的,它不是字体的一部分。

    DirectWrite使用和WPF相同的字体分类系统,因此,每一个font family有很多物理字体。一个font face 例如DirectWrite中的IDWriteFontFace接口,代表了一个物理字体,有具体的weight、slant和stretch。它包括了font face type,合适的文件引用,face分类数据和各种字体数据如metrics,name和字形框架。IDWriteFontFace能够以font name直接创建或者直接从一个font collection中获取。

Glyph Metrics

单独的字形有和它们本身关联的metrics。你可以在一个glyph run中为所有的字形获得metrics通过使用IDWriteFontFace::GetDesignGlyphMetrics方法。这个函数返回一个DWRITE_GLYPH_METRICS结构体包含了该字形的各种信息。

下面两个图显示了两种不同的字形的字母的Metrics:

 

Drawing a Glyph Run

    当实现一个自定义的文本渲染器的时候,渲染字形的操作通过IDWriteTextRender::DrawGlyphRun完成,它是你自己继承IDWriteTextRenderer写的类中的一个回调函数,结构体DWRITE_GLYPH_RUN被传递给DrawGlyphRun函数,该结构体包含一个IDWriteFontFace对象叫fontFace代表了整个glyph run的font face。

    IDWriteFontFace对象也提供了GetGlyphRunOutline方法,该方法计算字形的框架通过使用一个具体的geometry sink回调,例如当用Direct2D渲染的时候是ID2D1SimplifiedGeometrySink。

Interoperating with GDI

DirectWrite提供了一个从GDI的字体模型迁移路径,和一些与GDI的字体模型的互操作性,也包括渲染文本到画到窗口中位图上的一些接口。

Introduction

DirectWrite提供了在GDI LOGFONT结构体到DirectWrite字体接口之间转化的方法。这样就允许你可以使用GDI来枚举和选择一些或者所有字体的,同时利用DirectWrite对功能和性能上的改进。如果你想要渲染一个文本到GDI的表面的话,DirectWrite也提供了渲染位图的接口。

Part 1: IDWriteGdiInterop

IDWriteGdiInterop用于在GDI的结构体和DirectWrite字体接口之间进行转换,并创建了一个IDWriteBitmapRenderTarget对象。通过IDWriteFactory::GetGdiInterop方法可以获得一个IDWritegdiInterop的对象,例如:

// Create a GDI interop interface.

if (SUCCEEDED(hr))

{

    hr = g_pDWriteFactory->GetGdiInterop(&g_pGdiInterop);

}

Part 2: Font Objects

GDI使用LOGFONT存储字体信息和文本风格。IDWriteGdiInterop::CreateFontFromLOGFONT方法可以转换一个LOGFONT结构体为一个IDWriteFont对象,例如:

// Convert to a DirectWrite font.

if (SUCCEEDED(hr))

{

    hr = g_pGdiInterop->CreateFontFromLOGFONT(&lf, &pFont);

}

然而,IDWriteFont并不会封装LOGFONT结构体的所有相同的信息。LOGFONT结构体包括字体大小、权重、风格、下划线、删除线、font face name和其他信息。IDwriteFont对象包含一个字体和他的weights和style,而不包含字体大小、下划线等。在DirectWrite下,这些格式化信息被封装在IDWriteTextFormat对象中,对于具体的文本范围是一个IDWriteTextLayout对象。你也可以转换一个IDWriteFont对象成LOGFONT通过使用接口IDWriteGdiInterop::ConvertFontToLOGFONT.

Part 3: Rendering

在某些情况下,你可能想要在GDI的表面显示一些DirectWrite的文本。IDWriteBitmapRenderTarget接口封装了一个位图和用于渲染文本内存设备内容。创建代码如下:

if (SUCCEEDED(hr))

{

    hr = g_pGdiInterop->CreateBitmapRenderTarget(hdc, r.right, r.bottom, &g_pBitmapRenderTarget);

}

    为了使用IDWriteBitmapRenderTarget,你必须实现一个自定义的文本渲染器回调接口继承与IDWriteTextRenderer,必须实现它内部的方法。并不是每一个方法都必须被实现,可以简单的返回E_NOTIMP,描画仍然可以继续。

    你可以用IDWriteTextLayout::Draw方法并且传递你实现的回调接口最为参数来描画一个文本。IDWriteTextLayout::Draw方法调用你实现的回调接口里面的回调函数,DrawGlyphRun、DrawUnderline、DrawInlineObject、DrawStrikethrough方法来执行描画操作。

    在你实现DrawGlyphRun方法中,调用IDWriteBitmapRenderTarget::DrawGlyphRun方法来描画字形。下划线、删除线和InlineObject也必须被自定义的渲染器完成。

    IDWriteBitmapRenderTarget::DrawGlyphRun有一个可选的RECT输出参数代表了文本被描画的矩形。你可以使用这个信息来设置设备内容的边界矩形通过GDI提供的SetBoundsRect方法。下面的代码就是一个自定以的渲染器的DrawGlyphRun方法的一个实现。

STDMETHODIMP GdiTextRenderer::DrawGlyphRun(

    __maybenull void* clientDrawingContext,

    FLOAT baselineOriginX,

    FLOAT baselineOriginY,

    DWRITE_MEASURING_MODE measuringMode,

    __in DWRITE_GLYPH_RUN const* glyphRun,

    __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,

    IUnknown* clientDrawingEffect

    )

{

    HRESULT hr = S_OK;

    // Pass on the drawing call to the render target to do the real work.

    RECT dirtyRect = {0};

    hr = pRenderTarget_->DrawGlyphRun(

        baselineOriginX,

        baselineOriginY,

        measuringMode,

        glyphRun,

        pRenderingParams_,

        RGB(0,200,255),

        &dirtyRect

        );

    return hr;

}

    IDWriteBitmapRenderTarget接口渲染内容到一个内存中的DC,你可以通过IDWriteBitmapRenderTarget::GetMemoryDC获取指向这个DC句柄。一旦描画操作被执行完,这个IDWriteBitmapRenderTarget的内存DC必须被拷贝到目标GDI的表面。

    你可以使用GetBoundsRect函数获取已经绑定的矩形,然后使用BitBlt方法从内存DC中拷贝DirectWrite渲染的文本到GDI的表面。

// Transfer from DWrite's rendering target to the window.

BitBlt(hdc, 0, 0, size.cx, size.cy, memoryHdc, 0, 0,

            SRCCOPY | NOMIRRORBITMAP);

例子程序请参考GDI Interoperation Sample

Custom Font Collections

DirectWrite提供了通过IDWriteFactory::GetSystemFontCollection方访问系统字体集合的法功能。但是一些应用程序需要使用的字体并没有被安装到系统中,可能来自于字体文件或者应用程序中嵌入的字体文件。这种情况下你就需要使用IDWriteFontCollection来创建一个自定义的字体集合。

Registering and unregistering a Font Collection Loader

    你能注册一个字体集合的loader通过使用IDWriteFactory::RegisterFontCollectionLoader方法并且传递一个按单件模式实现的IDWriteFontCollectionLoader接口,这个对象在需要自定义字体集合的时候会载入需要的字体。系统字体集合和自定义字体集合都被缓存了,因此字体只被载入一次。这个字体集合的loader在最后必须通过IDWriteFactory::UnregisterFontCollectionLader。

    注意,注册字体集合的loader会增加引用计数,不在析构函数里调用反注册函数的话这个loader永远不会被反注册。

IDWriteFontCollectionLoader

通过使用IDWriteFactory::CreateCustomFontCollection并且传递一个应用程序自定义的Key就可以创建一个IDWriteFontFileEnumerator对象,这个key是一个void指针,数据类型、格式和意义都是程序定义的,对于字体系统是透明的。虽然key可以是任意值,但是DirectWrite需要key满足两种条件:在loader的范围内对于一个字体集合是唯一的;直到loader被工厂反注册的时候仍然是有效的。

   当CreateCustomFontCollection方法被调用的时候,DirectWrite回调被应用程序以单件模式实现的IDWriteFontCollectionLoader接口。然后IDWriteFontCollection::CreateEnumeratorFromKey回调函数被DirectWrite调用来获取一个IDWriteFontFileEnumerator对象,用来创建这个集合的工厂被当作参数传递给这个方法。用于font file enumerator创建集合中包含的IDWriteFontFile对象。传递给这个方法的Key用于标志字体集合,CreateCustomFontCollection用的是同一个Key。

IDWriteFontFileEnumerator

    被CreateEnumeratorFromKey方法创建的IDWriteFontFileEnumerator对象用于枚举一个集合中的字体文件,为每一个文件创建一个IDWriteFontFile对象。         IDWriteFontFileEnumerator::MoveNext方法指向写一个字体文件,如果存在的话,它会设置hasCurrentFile为TRUE,否则设置成FALSE,这时该方法会返回S_OK。

注意:字体文件的枚举器开始位置必须在第一个元素之前,使用时应先调用MoveNext。

    IDWriteFontFileEnumerator::GetCurrentFontFile方法输出一个IDWriteFontFile对象。如果当前位置没有IDWritefontFile对象,可能以为MoveNext方法还没有被调用或者hasCurrentFile被置为false,然后GetCurrentFontFile就会范围E_FAIL。

CreateCustomFontFileReference

通过GetCurrentFontFile方法获得的IDWriteFontFile对象可以通过调用IDWriteFactory::

CreateCustomFontFileReference来创建。字体文件引用key标志了一个具体font file的引用,在font file loader载入文件的范围内必须是唯一的。

IDWriteFontFileLoader

CreateCustomFontFileReference方法以一个IDWriteFontFileLoader对象为参数,IDWriteFontFileLoader::CreateStreamFromKey回调函数使用参数key,输出参数是一个IDWriteFontFileStream对象。

IDWriteFontFileStream

程序实现了IDWriteFontFileStream对象提供字体文件数据给一个来自与自定义字体文件载入器的字体文件引用。除了文件大小和上次更新日期,它还提供了一个方法ReadFileFragment来获取文件片段,这些片段都需要被编译到一个IDWriteFontFile对象。

注意:ReadFileFragment实现应该返回一个错误,如果需要的片段超出了文件的边界。一个IDWriteFontFileStream可以从任何地方获取字体文件的内容,例如本地硬盘或者嵌入式资源。

更详细的例子请参考Custom Font Sample

How-to Topics

How to Align Text

可以通过IDWriteTextFormat接口的SetTextAlignment来设置文本的对齐方式,如下:

if (SUCCEEDED(hr))

{

    hr = pTextFormat_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);

}

文本对齐方式可以是layout box的leading或者trailing或者居中。例如下图所示的:

 

注意:对齐方式取决与文字阅读方向,上图的阅读方向是从左到右的。

当让你也可以通过IDWriteTextLayout::SetTextAlignment来设置文字的对齐方式。

How to Add Support for Multiple Monitors

    DirectWrite包括了对多显示器的支持。不同的显示器可能有不同的像素geometry或者其他属性,想了解更多关于pixel geometry的东西,参考DWRITE_PIXEL_GEOMETRY相关的主题。

    为了支持多显示器,你必须处理WM_WINDOWPOSCHANGE消息,窗口移动的时候会发送这个消息,因此你必须检查是不是窗口被移到一个不同的显示器上,如下:

case WM_WINDOWPOSCHANGED:

    {

        HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);

        if (monitor != g_monitor)

        {

            g_monitor = monitor;

            if (g_spRenderTarget != NULL)

            {

                IDWriteRenderingParams* pRenderingParams = NULL;

                g_spDWriteFactory->CreateMonitorRenderingParams(monitor, &pRenderingParams);

                g_spRenderTarget->SetTextRenderingParams(pRenderingParams);

                SafeRelease(&pRenderingParams);

            }

            InvalidateRect(hwnd, NULL, TRUE);

        }

    }

    break;

如果窗口在一个新的显示器上,你需要用IDWriteFactory::CreateMonitorRenderingParams方法为新的显示器创建rendering parameters。

注意:不要使用IDWriteFactory::CreateRenderingParams方法去创建rendering parameters,因为它总是为主显示器创建参数。

How to Ensure That Your Application Displays Properly on High-DPI Displays

    虽然DirectWrite已经为你处理了很多高DPI的问题,但是你仍然需要做一些工作确保你的程序在高DPI的情况下运行正常。

1、用系统DPI创建窗口。

    ID2D1Factory接口提供了GetDesktopDpi方法获取系统DPI,它提供了水平和垂直的DPI,使用下的公式,水平DPI * 宽度(像素单位)/ 默认的水平DPI,默认的水平DPI是96.垂直的也一样计算。下面的代码演示了如何去做:

 

        // Because the CreateWindow function takes its size in pixels,

        // obtain the system DPI and use it to scale the window size.

        FLOAT dpiX, dpiY;

 

   // The factory returns the current system DPI. This is also the value it will use

   // to create its own windows.

   m_pDirect2dFactory->GetDesktopDpi(&dpiX, &dpiY);

   // Create the window.

   m_hwnd = CreateWindow(

       L"D2DDemoApp",  L"Direct2D Demo App", WS_OVERLAPPEDWINDOW,

       CW_USEDEFAULT,  CW_USEDEFAULT,

       static_cast<UINT>(ceil(640.f * dpiX / 96.f)),

       static_cast<UINT>(ceil(480.f * dpiY / 96.f)),

       NULL,  NULL,  HINST_THISCOMPONENT, this );

如果用GDI获取系统DPI的方法:

HDC screen = GetDC(0);

dpiX = static_cast<FLOAT>(GetDeviceCaps(screen, LOGPIXELSX));

dpiY = static_cast<FLOAT>(GetDeviceCaps(screen, LOGPIXELSY));

ReleaseDC(0, screen);

2、声明应用程序是DPI-Aware的。

当应用程序声明自己是DPI-aware的时候,应用程序在200DPI的范围内都会表现良好。在Vista和Win7下,DPI虚拟化开启的时候,不是DPI-aware的应用程序被缩放。应用程序从系统APIs收到数据,例如GetSystemMetric函数,为了声明应用程序是DPI-Aware的,完成以下步骤:

    创建一个文件叫DeclareDPIAware.manifest,内容如下:

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >

  <asmv3:application>

    <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">

      <dpiAware>true</dpiAware>

    </asmv3:windowsSettings>

  </asmv3:application>

</assembly>

    在工程文件中,VisualStudioProject/Configurations的每一个Configuration项中添加如下项:

<Tool

    Name="VCManifestTool"

    AdditionalManifestFiles="DeclareDPIAware.manifest"

/>

How to Ensure Text is Displayed with the Correct Reading Direction

    有些语言,例如Arabic和Hebrew,需要一个从右到左的阅读方向,对于一个DirectWrite文本格式对象,默认的阅读方向是从左到右,DirectWrite不会从locale中自动地推断出阅读方向,你必须自己完成这件事。

    首先,获取窗口的扩展风格

DWORD dwStyle = GetWindowExStyle(hwnd_);

你必须确定dwStyle中的具体标志是否影响阅读方向。和阅读方向有关的两个标志是WS_EX_LAYOUTRTL和WS_EX_RTLREADING。

对获取的风格和上面两个标志做与操作获取的接口指出了是否layout是mirrored。

BOOL bWSLayout = dwStyle & WS_EX_LAYOUTRTL;

BOOL bWSReading = dwStyle & WS_EX_RTLREADING;

通过使用IDWriteTextFormat::SetReadingDirection方法设置文字的阅读方向,默认的是从左到右,所以你只需要设置从右到左的阅读方向。

注意:如果WS_EX_LAYOUTRTL mirrors整个的layout就代表是从左到左的阅读方向,因此只要上面得到的两个布尔变量只有一个为真就意味着是从右到左的阅读方向,但是如果两个都为真的话就是从左到有的阅读方向,例如:

// If either the WS_EX_LAYOUTRTL flag or the WS_EX_RLTREADING flag is present,

// but NOT BOTH, set the reading direction to right to left.

if ((bWSLayout && !bWSReading)

||  (!bWSLayout && bWSReading))

{

    pTextFormat_->SetReadingDirection(DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);

}

 

How to Enumerate Fonts

该部分讨论了如何通过family name来枚举系统字体集合中的字体。

1、获取系统字体集合

    IDWriteFontCollection* pFontCollection = NULL;

// Get the system font collection.

if (SUCCEEDED(hr))

{

    hr = pDWriteFactory->GetSystemFontCollection(&pFontCollection);

}

2、获取font family数目

UINT32 familyCount = 0;

// Get the number of font families in the collection.

if (SUCCEEDED(hr))

{

    familyCount = pFontCollection->GetFontFamilyCount();

}

现在你已经有了字体集合和字体数,剩下的就是在循环中枚举字体family

3、获取font family

IDWriteFontFamily* pFontFamily = NULL;

// Get the font family.

if (SUCCEEDED(hr))

{

    hr = pFontCollection->GetFontFamily(i, &pFontFamily);

}

4、获取Family Names.

IDWriteLocalizedStrings* pFamilyNames = NULL;

// Get a list of localized strings for the family name.

if (SUCCEEDED(hr))

{

    hr = pFontFamily->GetFamilyNames(&pFamilyNames);

}

5、找出locale名字。

通过IDWriteLocalizedStrings::FindLocaleName方法获得font family name。在这种情况下首先获得默认的locale,如果不行的话,就用"en-us" locale.如果都不行的话,这个例子简单的回退到index 0,第一个有效的locale。

UINT32 index = 0;

BOOL exists = false;

wchar_t localeName[LOCALE_NAME_MAX_LENGTH];

if (SUCCEEDED(hr))

{

    // Get the default locale for this user.

    int defaultLocaleSuccess = GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH);

    // If the default locale is returned, find that locale name, otherwise use "en-us".

    if (defaultLocaleSuccess)

    {

        hr = pFamilyNames->FindLocaleName(localeName, &index, &exists);

    }

    if (SUCCEEDED(hr) && !exists) // if the above find did not find a match, retry with US English

    {

        hr = pFamilyNames->FindLocaleName(L"en-us", &index, &exists);

    }

}

// If the specified locale doesn't exist, select the first on the list.

if (!exists)

    index = 0;

6、获取Family Name的长度和字符串。

通过使用IDWriteLocalizedStrings::GetStringLength获取family name的字符串长度,然后分配缓冲区获取字符串。

UINT32 length = 0;

// Get the string length.

if (SUCCEEDED(hr))

{

    hr = pFamilyNames->GetStringLength(index, &length);

}

// Allocate a string big enough to hold the name.

wchar_t* name = new (std::nothrow) wchar_t[length+1];

if (name == NULL)

{

    hr = E_OUTOFMEMORY;

}

// Get the family name.

if (SUCCEEDED(hr))

{

    hr = pFamilyNames->GetString(index, name, length+1);

}

结论:

一旦你有了family name在当前的locale下,你就可以把它们列出来给客户或者用CreateTextFormat并且使用具体的font family来格式化字体。

完整的源代码请参考Font Enumation Sample

How to Perform Hit Testing on a Text Layout

下面例子的结果是会在点击到的字母下加上下划线。

 

1、创建一个TextLayout,而且你必须使用ID2D1RenderTarget::DrawTextLayout方法代替ID2DRenderTarget::DrawText。

2、定义一个单击响应函数。

3、使用IDWriteTextLayout::HitTestPoint方法来决定是否用户点击了当前的text layout。

  代码如下:

DWRITE_HIT_TEST_METRICS hitTestMetrics;

BOOL isTrailingHit;

BOOL isInside;

点击测试方法输出如下参数:

Variable

Description

hitTestMetrics

The geometry fully enclosing the hit-test location.

isInside

Indicates whether the hit-test location is inside the text string or not. When FALSE, the position nearest the text's edge is returned.

isTrailingHit

Indicates whether the hit-test location is at the leading or the trailing side of the character.

pTextLayout_->HitTestPoint((FLOAT)x, (FLOAT)y, &isTrailingHit,
             &isInside, &hitTestMetrics);

这个代码中的x,y没有经过任何的转换,之所以这么做是因为layout的大小和窗口的大小一样,如果不一样的话,就必须转换鼠标的坐标。

4、给点击测试到的字母加下划线。

if (isInside == TRUE)

{

    BOOL underline;

    pTextLayout_->GetUnderline(hitTestMetrics.textPosition, &underline);

    DWRITE_TEXT_RANGE textRange = {hitTestMetrics.textPosition, 1};

    pTextLayout_->SetUnderline(!underline, textRange);

}

How to Implement a Custom Text Renderer

DirectWrite的文本Layout能够被继承自IDWriteTextRenderer的自定义Renderer所渲染。自定义的Renderer需要利用DirectWrite的一些特性,例如渲染到位图或者GDI表面,inline object或者客户端描画效果,下面例子描述了IDWriteTextRenderer的方法并且提供了一个利用Direct2D渲染文本并且用一个位图填充的例子。

    你自定义的文本渲染器必须实现继承自IUnKnown的方法,还有IDWriteTextRenderer的方法。完整的源代码请参考DirectWrite Hello World Sample.

The Constructor

    自定义的文本渲染器需要一个构造函数,这个例子用solid和bitmap画刷渲染字体。因此,构造函数的参数需要下表列出来的四个。

Parameter

Description

pD2DFactory

用于创建需要的D2D的资源。

pRT

文本被渲染到这个ID2D1HwndRenderTarget上

pOutlineBrush

渲染文本的框架。

pFillBrush

填充文本的画刷ID2D1BitmapBrush

例子代码如下:

CustomTextRenderer::CustomTextRenderer(

    ID2D1Factory* pD2DFactory,

    ID2D1HwndRenderTarget* pRT,

    ID2D1SolidColorBrush* pOutlineBrush,

    ID2D1BitmapBrush* pFillBrush

    )

:

cRefCount_(0),

pD2DFactory_(pD2DFactory),

pRT_(pRT),

pOutlineBrush_(pOutlineBrush),

pFillBrush_(pFillBrush)

{

    pD2DFactory_->AddRef();

    pRT_->AddRef();

    pOutlineBrush_->AddRef();

    pFillBrush_->AddRef();

}

DrawGlyphRun()

    这个方法是文本渲染器的主要的回调函数,参数是一个将要被渲染的run of glyphs,也包括基线和测量模式,还有一个应用到glyph的客户端描画效果。

    这个例子通过把glyph runs转换成Direct2D的几何图形然后填充和描画这些几何图形,主要由下面的步骤完成:

1、创建一个ID2D1PathGeometry的对象,然后获取ID2D1GeometrySink对象通过ID2D1PathGeometry::Open方法如下:

// Create the path geometry.

ID2D1PathGeometry* pPathGeometry = NULL;

hr = pD2DFactory_->CreatePathGeometry(

        &pPathGeometry

        );

// Write to the path geometry using the geometry sink.

ID2D1GeometrySink* pSink = NULL;

if (SUCCEEDED(hr))

{

    hr = pPathGeometry->Open(

        &pSink

        );

}

// Create the path geometry.

ID2D1PathGeometry* pPathGeometry = NULL;

hr = pD2DFactory_->CreatePathGeometry(

        &pPathGeometry

        );

// Write to the path geometry using the geometry sink.

ID2D1GeometrySink* pSink = NULL;

if (SUCCEEDED(hr))

{

    hr = pPathGeometry->Open(

        &pSink

        );

}

2、参数DWRITE_GLYPH_RUN包含一个IDWriteFontFace对象,名字是fontFace,代表了整个glyph run的fontface,通过IDWriteFontFace::GetGlyphRunOutline方法把glyph run的几何图形设置到geometry sink中,如下:

// Get the glyph run outline geometries back from DirectWrite and place them within the

// geometry sink.

if (SUCCEEDED(hr))

{

    hr = glyphRun->fontFace->GetGlyphRunOutline(

        glyphRun->fontEmSize,

        glyphRun->glyphIndices,

        glyphRun->glyphAdvances,

        glyphRun->glyphOffsets,

        glyphRun->glyphCount,

        glyphRun->isSideways,

        glyphRun->bidiLevel%2,

        pSink

        );

}

然后关闭geometry sink。

// Close the geometry sink

if (SUCCEEDED(hr))

{

    hr = pSink->Close();

}

3、原始的glyph run必须被转换,因为它必须从正确的baseline orign渲染。

// Initialize a matrix to translate the origin of the glyph run.

D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(

    1.0f, 0.0f,

    0.0f, 1.0f,

    baselineOriginX, baselineOriginY

    );

其中baselineOriginX, baselineOriginY是传递给DrawGlyphRun回调函数的方法。

4、通过ID2D1Factory::CreateTransformedGeometry方法并且传递这个图形和平移矩阵来创建转换图形。

// Create the transformed geometry

ID2D1TransformedGeometry* pTransformedGeometry = NULL;

if (SUCCEEDED(hr))

{

    hr = pD2DFactory_->CreateTransformedGeometry(

        pPathGeometry,

        &matrix,

        &pTransformedGeometry

        );

}

5、最后描画这个转换图形的框架,并且通过ID2D1RenderTarget::DrawGeometry、ID2D1RenderTarget::FillGeometry方法和Direct2D的画刷来填充它。

    // Draw the outline of the glyph run

    pRT_->DrawGeometry(

        pTransformedGeometry,

        pOutlineBrush_

        );

    // Fill in the glyph run

    pRT_->FillGeometry(

        pTransformedGeometry,

        pFillBrush_

        );

完成描画后清理相关对象

SafeRelease(&pPathGeometry);

SafeRelease(&pSink);

SafeRelease(&pTransformedGeometry);

 

DrawUnderline() and DrawStrikethrough()

IDWriteTextRenderer也有描画下划线和删除线的回调函数,下面的例子就描画了一个简单的矩形作为下划线或删除线,当然其他的图形也可以。

1、首先,为下划线的大小和形状创建一个D2D_RECT_F的结构体,通过DrawUnderline函数的参数DWRITE_UNDERLINE结构体提供了下划线的偏移、宽度和厚度。

D2D1_RECT_F rect = D2D1::RectF(

    0,

    underline->offset,

    underline->width,

    underline->offset + underline->thickness

    );

2、接下来,创建一个ID2DRectangleGeometry对象,通过使用ID2D1Factory::CreateRectangleGeometry方法,并且初始化D2D1_RECT_F结构体。

ID2D1RectangleGeometry* pRectangleGeometry = NULL;

hr = pD2DFactory_->CreateRectangleGeometry(

        &rect,

        &pRectangleGeometry

        );

3、就像glyph run一样,下划线图行的原点也必须被平移,基于baseline的原点值,通过使用CreateTransformedGeometry方法。

// Initialize a matrix to translate the origin of the underline

D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(

    1.0f, 0.0f,

    0.0f, 1.0f,

    baselineOriginX, baselineOriginY

    );

ID2D1TransformedGeometry* pTransformedGeometry = NULL;

if (SUCCEEDED(hr))

{

    hr = pD2DFactory_->CreateTransformedGeometry(

        pRectangleGeometry,

        &matrix,

        &pTransformedGeometry

        );

}

4、最后描画这个转换图形的框架,并且通过ID2D1RenderTarget::DrawGeometry、ID2D1RenderTarget::FillGeometry方法和Direct2D的画刷来填充它。

    // Draw the outline of the glyph run

    pRT_->DrawGeometry(

        pTransformedGeometry,

        pOutlineBrush_

        );

    // Fill in the glyph run

    pRT_->FillGeometry(

        pTransformedGeometry,

        pFillBrush_

        );

用完后要记得清理资源

SafeRelease(&pRectangleGeometry);

SafeRelease(&pTransformedGeometry);

描画删除线的过程和描画下划线的过程一样,但是删除线的偏移和下划线不一样,宽度和厚度也是。

Pixel Snapping, Pixels per DIP, and Transform

IsPixelSnappingDisabled()方法可以知道是否像素snapping(断裂)是无效的。这个默认是FALSE的,下面是这个例子的输出。

*isDisabled = FALSE;

GetCurrentTransform(),这个例子是渲染到Direct2D的render target,然后使用ID2D1RenderTarget::GetTransform获取rendertarget的transform。

//forward the render target's transform

pRT_->GetTransform(reinterpret_cast<D2D1_MATRIX_3X2_F*>(transform));

GetPixelsPerDip()调用这个方法获取每个设备独立像素对应的像素数目。

float x, yUnused;

pRT_->GetDpi(&x, &yUnused);

*pixelsPerDip = x / 96;

DrawInlineObject()自定义的文本渲染器也有一个描画inlineObject对象的回调函数,在这个例子中,直接返回E_NOTIMPL,如何描画inlineObject对象的例子请参考下一篇。

析构函数

CustomTextRenderer::~CustomTextRenderer()

{

    SafeRelease(&pD2DFactory_);

    SafeRelease(&pRT_);

    SafeRelease(&pOutlineBrush_);

    SafeRelease(&pFillBrush_);

}

完整的例子请参考http://msdn.microsoft.com/en-us/library/dd368048(VS.85).aspx

How to Add Inline Objects to a Text Layout

本文提供了一个简单的例子使用IDWriteTextLayout对象添加一个InlineObject。

例子的结构如图

 

Step 1: Create a Text Layout.

开始,你的程序需要一个IDWriteTextLayout对象,而且你必须使用ID2DRenderTarget::DrawTextLayout方法替换ID2D1RenderTarget::DrawText方法。

Step 2: Define a class derived from the IDWriteInlineObject interface.

    DirectWrite中通过IDWriteInlineObject对象支持inlineObject。为了使用inline object你必须实现这个接口,它为渲染提供了必要的信息。

    创建一个类继承自IDWriteInlineObject接口,除了实现IUnknown中的接口还要实现IDWriteInlineObject接口的函数,Draw、GetMetrics、GetOverhangMetrics、GetBreakCondition.另外,这个类还有一个LoadBitmapFromFile方法来载入InlineImage和它的大小。这个类也存储了一个ID2D1Bitmap,文本和图片都会被渲染到ID2D1RenderTarget中,一个D2D1_RECT_F结构体存储图片的metrics,一个float的变量存储baseline。

Step 3: Implement the Inline Object Class.

InlineImage::InlineImage(

    ID2D1RenderTarget *pRenderTarget,

    IWICImagingFactory *pIWICFactory,

    PCWSTR uri

    )

{

    // Save the render target for later.

    pRT_ = pRenderTarget;

    pRT_->AddRef();

    // Load the bitmap from a file.

    LoadBitmapFromFile(

        pRenderTarget,

        pIWICFactory,

        uri,

        &pBitmap_

        );

}

    构造函数中, inlineimage将要渲染到第一个参数中,rendertarget、IWICImagingFactory和文件名都被传递给LoadBitmapFromFile函数用于载入位图并且存储位图的大小。

    Draw方法是被IDWriteTextRenderer对象调用的回调函数,这个text layout不直接调用这个方法。

HRESULT STDMETHODCALLTYPE InlineImage::Draw(

    __maybenull void* clientDrawingContext,

    IDWriteTextRenderer* renderer,

    FLOAT originX,

    FLOAT originY,

    BOOL isSideways,

    BOOL isRightToLeft,

    IUnknown* clientDrawingEffect

    )

{

    float height    = rect_.bottom - rect_.top;

    float width     = rect_.right  - rect_.left;

    D2D1_RECT_F destRect  = {originX, originY, originX + width, originY + height};

    pRT_->DrawBitmap(pBitmap_, destRect);

    return S_OK;

}

在这个例子中,通过使用ID2D1RenderTarget::DrawBitmap方法来描画位图。

GetMetrics方法代码如下:

HRESULT STDMETHODCALLTYPE InlineImage::GetMetrics(

    __out DWRITE_INLINE_OBJECT_METRICS* metrics

    )

{

    DWRITE_INLINE_OBJECT_METRICS inlineMetrics = {};

    inlineMetrics.width     = rect_.right  - rect_.left;

    inlineMetrics.height    = rect_.bottom - rect_.top;

    inlineMetrics.baseline  = baseline_;

    *metrics = inlineMetrics;

    return S_OK;

}

对于这个方法,存储width、height和basline信息在DWRITE_INLINE_OBJECT_METRICS结构体。IDWriteTextLayout调用这个回调函数来获取inline对象的度量信息。

GetOverhangMetrics方法:

HRESULT STDMETHODCALLTYPE InlineImage::GetOverhangMetrics(

    __out DWRITE_OVERHANG_METRICS* overhangs

    )

{

    overhangs->left      = 0;

    overhangs->top       = 0;

    overhangs->right     = 0;

    overhangs->bottom    = 0;

    return S_OK;

}

在这个例子中,不需要overhang信息,所以直接返回0;

还有GetBreakConditions方法。

HRESULT STDMETHODCALLTYPE InlineImage::GetBreakConditions(

    __out DWRITE_BREAK_CONDITION* breakConditionBefore,

    __out DWRITE_BREAK_CONDITION* breakConditionAfter

    )

{

    *breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL;

    *breakConditionAfter  = DWRITE_BREAK_CONDITION_NEUTRAL;

    return S_OK;

}

Step 4: Create an Instance of the InlineImage Class and Add it to the Text Layout.

最后,创建一个InlineImage类,并把他添加到text layout。因为它持有一个ID2D1RenderTarget对象的引用和被render target创建的ID2D1Bitmap, ID2D1RenderTarget是设备依赖的,所以InlineImage也是设备依赖的,如果render target创新创建的话它也必须重新创建。

// Create an InlineObject.

pInlineImage_ = new InlineImage(pRT_, pWICFactory_, L"img1.jpg");

DWRITE_TEXT_RANGE textRange = {14, 1};

pTextLayout_->SetInlineObject(pInlineImage_, textRange);

方法IDWriteTextLayout::SetInlineObject以一个text range结构体作为参数,inlineObject对象的位置就是被这个结构体指定的,任何在这个范围内的文本都会被supressed(抑制);如果这个范围是0的话,inlineObject对象不会被描画的。在这个例子中,用*作为一个占位符来显示Inline Image。

// The string to display.  The '*' character will be suppressed by our image.

wszText_ = L"Inline Object * Example";

cTextLength_ = wcslen(wszText_);

最后清理资源:

SafeRelease(&pInlineImage_);

完整的例子请参考http://msdn.microsoft.com/en-us/library/dd742732(VS.85).aspx

How to Add Client Drawing Effects to a Text Layout

本文提供了一个用IDWriteTextLayout接口添加客户端描画效果和自定义文本渲染器的例子。

效果如图:

 

注意:这个例子只是演示如何创建客户端描画效果,而不是如何描画带色文本的例子,更多信息请参考IDWriteTextLayout::SetDrawingEffect引用。

Step 1: Create a Text Layout:该部分就不再赘述了。

Step 2: Implement a Custom Drawing Effect Class

    除了继承自IUnknown的方法必须实现,没有额外的方法需要实现,ColorDrawingEffect类简单的持有一个D2D1_COLOR_F结构体,并且提供了设置和获取该结构体值的方法,包括设置初始值的结构体。

    在IDWriteTextLayout对象中把客户端效果应用到文本range中,这个效果是传递给IDWriteTextRenderer::DrawGlyphRun方法,这个效果对于文本渲染器来说就是有效的了。

    客户端的效果可以很复杂,可以达到比这个例子更好的效果,也可以提供方法去改变字形,创建用于描画的对象。

Step 3: Implement a Custom Text Renderer Class

为了利用客户端描画效果,你必须实现一个自定义的文本渲染器,文本渲染器将会用通过IDWriteTextLayout::Draw方法传递的客户端效果来应用到即将描画的字形上。

构造函数:

CustomTextRenderer::CustomTextRenderer(

    ID2D1Factory* pD2DFactory,

    ID2D1HwndRenderTarget* pRT

    )

:

cRefCount_(0),

pD2DFactory_(pD2DFactory),

pRT_(pRT)

{

    pD2DFactory_->AddRef();

    pRT_->AddRef();

}

DrawGlyphRun方法:

一个glyph run是一个共享相同格式包括客户端描画效果的glyphs集合。

首先创建一个ID2D1PathGeometry和ID2D1GeometrySink,然后通过IDWriteFontFace::GetGlyRunOutline获取glyph run框架,然后通过使用ID2D1Factory::CreateTransformedGeometry方法来平移图形的原点,如下:

HRESULT hr = S_OK;

 

// Create the path geometry.

ID2D1PathGeometry* pPathGeometry = NULL;

hr = pD2DFactory_->CreatePathGeometry(

        &pPathGeometry

        );

 

// Write to the path geometry using the geometry sink.

ID2D1GeometrySink* pSink = NULL;

if (SUCCEEDED(hr))

{

    hr = pPathGeometry->Open(

        &pSink

        );

}

 

// Get the glyph run outline geometries back from DirectWrite and place them within the

// geometry sink.

if (SUCCEEDED(hr))

{

    hr = glyphRun->fontFace->GetGlyphRunOutline(

        glyphRun->fontEmSize,

        glyphRun->glyphIndices,

        glyphRun->glyphAdvances,

        glyphRun->glyphOffsets,

        glyphRun->glyphCount,

        glyphRun->isSideways,

        glyphRun->bidiLevel%2,

        pSink

        );

}

// Close the geometry sink

if (SUCCEEDED(hr))

{

    hr = pSink->Close();

}

// Initialize a matrix to translate the origin of the glyph run.

D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(

    1.0f, 0.0f,

    0.0f, 1.0f,

    baselineOriginX, baselineOriginY

    );

// Create the transformed geometry

ID2D1TransformedGeometry* pTransformedGeometry = NULL;

if (SUCCEEDED(hr))

{

    hr = pD2DFactory_->CreateTransformedGeometry(

        pPathGeometry,

        &matrix,

        &pTransformedGeometry

        );

}

接下来声明一个Direct2D的画刷对象。ID2D1SolidColorBrush* pBrush = NULL

如果clientDrawingEffect参数不是空的话,查询ColorDrawingEffect接口的对象,因为你会在text layout对象的text ranges设置这个类作为客户端描画效果。

一旦你有ColorDrawingEffect接口的指针,你能获取D2D1_COLOR_F的值,它存储了颜色数据,然后用这个颜色数据创建画刷。如果clientDrawingEffect参数是空的话,就创建ID2D1SolidColorBrush画刷。

// If there is a drawing effect create a color brush using it, otherwise create a black brush.

if (clientDrawingEffect != NULL)

{

    // Go from IUnknown to ColorDrawingEffect.

    ColorDrawingEffect *colorDrawingEffect;

 

    clientDrawingEffect->QueryInterface(__uuidof(ColorDrawingEffect), reinterpret_cast<void**>(&colorDrawingEffect));

 

    // Get the color from the ColorDrawingEffect object.

    D2D1_COLOR_F color;

 

    colorDrawingEffect->GetColor(&color);

 

    // Create the brush using the color pecified by our ColorDrawingEffect object.

    if (SUCCEEDED(hr))

    {

        hr = pRT_->CreateSolidColorBrush(

            color,

            &pBrush);

    }

 

    SafeRelease(&colorDrawingEffect);

}

else

{

    // Create a black brush.

    if (SUCCEEDED(hr))

    {

        hr = pRT_->CreateSolidColorBrush(

            D2D1::ColorF(

            D2D1::ColorF::Black

            ),

            &pBrush);

    }

}

最后,描画这个图形的框架并且用刚才创建的solidcolorbrush填充它。

if (SUCCEEDED(hr))

{

    // Draw the outline of the glyph run

    pRT_->DrawGeometry(

        pTransformedGeometry,

        pBrush

        );

 

    // Fill in the glyph run

    pRT_->FillGeometry(

        pTransformedGeometry,

        pBrush

        );

}

析构函数要记得清理资源。

CustomTextRenderer::~CustomTextRenderer()

{

    SafeRelease(&pD2DFactory_);

    SafeRelease(&pRT_);

}

剩下的自定义文本渲染器的方法可以在How to implement a custom text render中看到。

Step 4: Create the Text Renderer

// Create the text renderer

pTextRenderer_ = new CustomTextRenderer(pD2DFactory_, pRT_ );

Step 5: Instantiate the Color Drawing Effect Objects

// Instantiate some custom color drawing effects.

redDrawingEffect_ = new ColorDrawingEffect(

    D2D1::ColorF(

        D2D1::ColorF::Red

        )

    );

 

blueDrawingEffect_ = new ColorDrawingEffect(

    D2D1::ColorF(

        D2D1::ColorF::Blue

        )

    );

 

greenDrawingEffect_ = new ColorDrawingEffect(

    D2D1::ColorF(

        D2D1::ColorF::Green

        )

    );

Step 6: Set the Drawing Effect for Specific Text Ranges

通过IDWriteTextLayout::SetDrawingEffect方法和一个DWRITE_TEXT_RANGE结构体来为具体的文本设置客户端描画效果。

// Set the drawing effects.

// Red.

if (SUCCEEDED(hr))

{

    // Set the drawing effect for the specified range.

    DWRITE_TEXT_RANGE textRange = {0, 14};

    if (SUCCEEDED(hr))

    {

        hr = pTextLayout_->SetDrawingEffect(redDrawingEffect_, textRange);

    }

}

// Blue.

if (SUCCEEDED(hr))

{

    // Set the drawing effect for the specified range.

    DWRITE_TEXT_RANGE textRange = {14, 7};

    if (SUCCEEDED(hr))

    {

        hr = pTextLayout_->SetDrawingEffect(blueDrawingEffect_, textRange);

    }

}

// Green.

if (SUCCEEDED(hr))

{

    // Set the drawing effect for the specified range.

    DWRITE_TEXT_RANGE textRange = {21, 8};

    if (SUCCEEDED(hr))

    {

        hr = pTextLayout_->SetDrawingEffect(greenDrawingEffect_, textRange);

    }

}

Step 7: Draw the Text Layout Using the Custom Renderer

// Draw the text layout using DirectWrite and the CustomTextRenderer class.

hr = pTextLayout_->Draw(NULL,

        pTextRenderer_,  // Custom text renderer.

        origin.x,  origin.y  );

 

最后清理所有资源:

SafeRelease(&pTextRenderer_);

SafeRelease(&redDrawingEffect_);

SafeRelease(&blueDrawingEffect_);

SafeRelease(&greenDrawingEffect_);

更多的信息请参考http://msdn.microsoft.com/en-us/library/dd941709(VS.85).aspx

posted on 2010-05-08 23:27  平常人做平常事  阅读(3623)  评论(3)    收藏  举报

导航