文档章节

Direct2D (39) : 使用 IDWriteTextLayout.Draw() 方法绘制文本

涂孟超
 涂孟超
发布于 2014/09/26 15:37
字数 1164
阅读 8
收藏 0

使用 IDWriteTextLayout.Draw() 方法绘制文本主要是实现 IDWriteTextRenderer 接口。
IDWriteTextRenderer 接口只能是手动实现,很灵活。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Direct2D, D2D1;

type
  TForm1 = class(TForm)
    procedure FormPaint(Sender: TObject);
    procedure FormResize(Sender: TObject);
  end;

  TMyWriteTextRenderer = class(TInterfacedObject, IDWriteTextRenderer)
  private
    FRenderTarge: ID2D1RenderTarget;
    FOutLineBrush,FFillBrush: ID2D1Brush;
  public
    constructor Create(ARenderTarge: ID2D1RenderTarget; AOutLineBrush,AFillBrush: ID2D1Brush);
    function IsPixelSnappingDisabled(clientDrawingContext: Pointer; var isDisabled: LongBool): HRESULT;
      stdcall;
    function GetCurrentTransform(clientDrawingContext: Pointer; var transform: DWRITE_MATRIX): HRESULT;
      stdcall;
    function GetPixelsPerDip(clientDrawingContext: Pointer; var pixelsPerDip: Single): HRESULT; stdcall;
    function DrawGlyphRun(clientDrawingContext: Pointer; baselineOriginX: Single; baselineOriginY: Single;
      measuringMode: DWRITE_MEASURING_MODE; var glyphRun: DWRITE_GLYPH_RUN;
      var glyphRunDescription: DWRITE_GLYPH_RUN_DESCRIPTION; const clientDrawingEffect: IInterface): HRESULT;
      stdcall;
    function DrawUnderline(clientDrawingContext: Pointer; baselineOriginX: Single; baselineOriginY: Single;
      var underline: DWRITE_UNDERLINE; const clientDrawingEffect: IInterface): HRESULT; stdcall;
    function DrawStrikethrough(clientDrawingContext: Pointer; baselineOriginX: Single;
      baselineOriginY: Single; var strikethrough: DWRITE_STRIKETHROUGH;
      const clientDrawingEffect: IInterface): HRESULT; stdcall;
    function DrawInlineObject(clientDrawingContext: Pointer; originX: Single; originY: Single;
      var inlineObject: IDWriteInlineObject; isSideways: LongBool; isRightToLeft: LongBool;
      const clientDrawingEffect: IInterface): HRESULT; stdcall;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}


{构建 DWRITE_TEXT_RANGE 结构的函数}
function DWriteTextRange(pos,len: Cardinal): TDwriteTextRange;
begin
  Result.startPosition := pos;
  Result.length := len;
end;

{构建 DWRITE_FONT_FEATURE 结构的函数}
function DWriteFontFeature(nameTag: Integer; parameter: Cardinal): TDwriteFontFeature;
begin
  Result.nameTag := nameTag;
  Result.parameter := parameter;
end;

{建立位图画刷的函数}
function GetBitmapBrush(Canvas: TDirect2DCanvas; filePath: string): ID2D1BitmapBrush;
var
  rBBP: TD2D1BitmapBrushProperties;
  bit: TBitmap;
begin
  bit := TBitmap.Create;
  bit.LoadFromFile(filePath);
  rBBP.extendModeX := D2D1_EXTEND_MODE_WRAP;
  rBBP.extendModeY := D2D1_EXTEND_MODE_WRAP;
  rBBP.interpolationMode := D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
  Canvas.RenderTarget.CreateBitmapBrush(Canvas.CreateBitmap(bit), @rBBP, nil, Result);
  bit.Free;
end;

procedure TForm1.FormPaint(Sender: TObject);
var
  cvs: TDirect2DCanvas;
  str: string;
  iTextFormat: IDWriteTextFormat;
  iSolidColorBrush: ID2D1SolidColorBrush;
  iBitmapBrush: ID2D1BitmapBrush;
  iTextLayout: IDWriteTextLayout;
  iTypography: IDWriteTypography;
  iTextRenderer: IDWriteTextRenderer;
begin
  str := 'Hello World using DirectWrite!';
  DWriteFactory.CreateTextFormat(
    'Gabriola',
    nil,
    DWRITE_FONT_WEIGHT_REGULAR,
    DWRITE_FONT_STYLE_NORMAL,
    DWRITE_FONT_STRETCH_NORMAL,
    72.0,
    'en-us',
    iTextFormat
  );
  iTextFormat.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
  iTextFormat.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);

  DWriteFactory.CreateTextLayout(
    PWideChar(str),
    Length(str),
    iTextFormat,
    ClientWidth,
    ClientHeight,
    iTextLayout
  );

  iTextLayout.SetFontSize(100.0, DWriteTextRange(18, 6));
  iTextLayout.SetUnderline(True, DWriteTextRange(18, 11));
  iTextLayout.SetFontWeight(DWRITE_FONT_WEIGHT_BOLD, DWriteTextRange(18, 11));

  DWriteFactory.CreateTypography(iTypography);
  iTypography.AddFontFeature(DWriteFontFeature(DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_6, 1));
  iTextLayout.SetTypography(iTypography, DWriteTextRange(0, Length(str)));

  cvs := TDirect2DCanvas.Create(Canvas, ClientRect);
  cvs.RenderTarget.CreateSolidColorBrush(D2D1ColorF(clBlack), nil, iSolidColorBrush);
  iBitmapBrush := GetBitmapBrush(cvs, 'C:\Temp\Test.bmp');
  iTextRenderer := TMyWriteTextRenderer.Create(cvs.RenderTarget, iSolidColorBrush, iBitmapBrush);
  cvs.RenderTarget.BeginDraw;
  cvs.RenderTarget.Clear(D2D1ColorF(clWhite));
  iTextLayout.Draw(nil, iTextRenderer, 0, 0);
  cvs.RenderTarget.EndDraw();
  cvs.Free;
end;

procedure TForm1.FormResize(Sender: TObject);
begin
  Repaint;
end;

{ TMyWriteTextRenderer }

constructor TMyWriteTextRenderer.Create(ARenderTarge: ID2D1RenderTarget; AOutLineBrush,
  AFillBrush: ID2D1Brush);
begin
  FRenderTarge := ARenderTarge;
  FOutLineBrush := AOutLineBrush;
  FFillBrush := AFillBrush;
end;

function TMyWriteTextRenderer.DrawGlyphRun(clientDrawingContext: Pointer; baselineOriginX,
  baselineOriginY: Single; measuringMode: DWRITE_MEASURING_MODE; var glyphRun: DWRITE_GLYPH_RUN;
  var glyphRunDescription: DWRITE_GLYPH_RUN_DESCRIPTION; const clientDrawingEffect: IInterface): HRESULT;
var
  iPathGeometry: ID2D1PathGeometry;
  iGeometrySink: ID2D1GeometrySink;
  iTransformedGeometry: ID2D1TransformedGeometry;
begin
  D2DFactory.CreatePathGeometry(iPathGeometry);
  iPathGeometry.Open(iGeometrySink);
  glyphRun.fontFace.GetGlyphRunOutline(
    glyphRun.fontEmSize,
    glyphRun.glyphIndices,
    glyphRun.glyphAdvances,
    glyphRun.glyphOffsets,
    glyphRun.glyphCount,
    glyphRun.isSideways,
    longBool(glyphRun.bidiLevel div 2),
    iGeometrySink
  );
  iGeometrySink.Close;

  D2DFactory.CreateTransformedGeometry(
    iPathGeometry,
    TD2DMatrix3x2F.Translation(baselineOriginX, baselineOriginY),
    iTransformedGeometry
  );

  FRenderTarge.DrawGeometry(iTransformedGeometry, FOutLineBrush);
  FRenderTarge.FillGeometry(iTransformedGeometry, FFillBrush);
  Result := S_OK;
end;

function TMyWriteTextRenderer.DrawInlineObject(clientDrawingContext: Pointer; originX, originY: Single;
  var inlineObject: IDWriteInlineObject; isSideways, isRightToLeft: LongBool;
  const clientDrawingEffect: IInterface): HRESULT;
begin
  Result := E_NOTIMPL; //未实现
end;

function TMyWriteTextRenderer.DrawStrikethrough(clientDrawingContext: Pointer; baselineOriginX,
  baselineOriginY: Single; var strikethrough: DWRITE_STRIKETHROUGH;
  const clientDrawingEffect: IInterface): HRESULT;
var
  rRectF: TD2DRectF;
  iRectangleGeometry: ID2D1RectangleGeometry;
  iTransformedGeometry: ID2D1TransformedGeometry;
begin
  rRectF := D2D1RectF(
    0,
    strikethrough.offset,
    strikethrough.width,
    strikethrough.offset + strikethrough.thickness
  );

  D2DFactory.CreateRectangleGeometry(rRectF, iRectangleGeometry);
  D2DFactory.CreateTransformedGeometry(
    iRectangleGeometry,
    TD2DMatrix3x2F.Translation(baselineOriginX, baselineOriginY),
    iTransformedGeometry
  );

  FRenderTarge.DrawGeometry(iTransformedGeometry, FOutLineBrush);
  FRenderTarge.FillGeometry(iTransformedGeometry, FFillBrush);
  Result := S_OK;
end;

function TMyWriteTextRenderer.DrawUnderline(clientDrawingContext: Pointer; baselineOriginX,
  baselineOriginY: Single; var underline: DWRITE_UNDERLINE; const clientDrawingEffect: IInterface): HRESULT;
var
  rRectF: TD2DRectF;
  iRectangleGeometry: ID2D1RectangleGeometry;
  iTransformedGeometry: ID2D1TransformedGeometry;
begin
  rRectF := D2D1RectF(
    0,
    underline.offset,
    underline.width,
    underline.offset + underline.thickness
  );

  D2DFactory.CreateRectangleGeometry(rRectF, iRectangleGeometry);
  D2DFactory.CreateTransformedGeometry(
    iRectangleGeometry,
    TD2DMatrix3x2F.Translation(baselineOriginX, baselineOriginY),
    iTransformedGeometry
  );

  FRenderTarge.DrawGeometry(iTransformedGeometry, FOutLineBrush);
  FRenderTarge.FillGeometry(iTransformedGeometry, FFillBrush);
  Result := S_OK;
end;

function TMyWriteTextRenderer.GetCurrentTransform(clientDrawingContext: Pointer;
  var transform: DWRITE_MATRIX): HRESULT;
begin
  FRenderTarge.GetTransform(TD2D1Matrix3x2F(transform));
  Result := S_OK;
end;

function TMyWriteTextRenderer.GetPixelsPerDip(clientDrawingContext: Pointer;
  var pixelsPerDip: Single): HRESULT;
var
  x,y: Single;
begin
  FRenderTarge.GetDpi(x, y);
  pixelsPerDip := x / 96;
  Result := S_OK;
end;

function TMyWriteTextRenderer.IsPixelSnappingDisabled(clientDrawingContext: Pointer;
  var isDisabled: LongBool): HRESULT;
begin
  isDisabled := False;
  Result := S_OK;
end;

end.


效果图:



简化一下,只描绘文本的轮廓:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Direct2D, D2D1;

type
  TForm1 = class(TForm)
    procedure FormPaint(Sender: TObject);
    procedure FormResize(Sender: TObject);
  end;

  TMyWriteTextRenderer = class(TInterfacedObject, IDWriteTextRenderer)
  private
    FRenderTarge: ID2D1RenderTarget;
    FOutLineBrush: ID2D1Brush;
  public
    constructor Create(ARenderTarge: ID2D1RenderTarget; AOutLineBrush: ID2D1Brush);
    function IsPixelSnappingDisabled(clientDrawingContext: Pointer; var isDisabled: LongBool): HRESULT;
      stdcall;
    function GetCurrentTransform(clientDrawingContext: Pointer; var transform: DWRITE_MATRIX): HRESULT;
      stdcall;
    function GetPixelsPerDip(clientDrawingContext: Pointer; var pixelsPerDip: Single): HRESULT; stdcall;
    function DrawGlyphRun(clientDrawingContext: Pointer; baselineOriginX: Single; baselineOriginY: Single;
      measuringMode: DWRITE_MEASURING_MODE; var glyphRun: DWRITE_GLYPH_RUN;
      var glyphRunDescription: DWRITE_GLYPH_RUN_DESCRIPTION; const clientDrawingEffect: IInterface): HRESULT;
      stdcall;
    function DrawUnderline(clientDrawingContext: Pointer; baselineOriginX: Single; baselineOriginY: Single;
      var underline: DWRITE_UNDERLINE; const clientDrawingEffect: IInterface): HRESULT; stdcall;
    function DrawStrikethrough(clientDrawingContext: Pointer; baselineOriginX: Single;
      baselineOriginY: Single; var strikethrough: DWRITE_STRIKETHROUGH;
      const clientDrawingEffect: IInterface): HRESULT; stdcall;
    function DrawInlineObject(clientDrawingContext: Pointer; originX: Single; originY: Single;
      var inlineObject: IDWriteInlineObject; isSideways: LongBool; isRightToLeft: LongBool;
      const clientDrawingEffect: IInterface): HRESULT; stdcall;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}


{构建 DWRITE_TEXT_RANGE 结构的函数}
function DWriteTextRange(pos,len: Cardinal): TDwriteTextRange;
begin
  Result.startPosition := pos;
  Result.length := len;
end;

{构建 DWRITE_FONT_FEATURE 结构的函数}
function DWriteFontFeature(nameTag: Integer; parameter: Cardinal): TDwriteFontFeature;
begin
  Result.nameTag := nameTag;
  Result.parameter := parameter;
end;

procedure TForm1.FormPaint(Sender: TObject);
var
  cvs: TDirect2DCanvas;
  str: string;
  iTextFormat: IDWriteTextFormat;
  iSolidColorBrush: ID2D1SolidColorBrush;
  iTextLayout: IDWriteTextLayout;
  iTypography: IDWriteTypography;
  iTextRenderer: IDWriteTextRenderer;
begin
  str := 'Hello World using DirectWrite!';
  DWriteFactory.CreateTextFormat(
    'Gabriola',
    nil,
    DWRITE_FONT_WEIGHT_ULTRA_BLACK,
    DWRITE_FONT_STYLE_NORMAL,
    DWRITE_FONT_STRETCH_NORMAL,
    72.0,
    'en-us',
    iTextFormat
  );
  iTextFormat.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
  iTextFormat.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);

  DWriteFactory.CreateTextLayout(
    PWideChar(str),
    Length(str),
    iTextFormat,
    ClientWidth,
    ClientHeight,
    iTextLayout
  );

  iTextLayout.SetFontSize(100.0, DWriteTextRange(18, 6));
  iTextLayout.SetUnderline(True, DWriteTextRange(18, 11));
  iTextLayout.SetFontWeight(DWRITE_FONT_WEIGHT_BOLD, DWriteTextRange(18, 11));

  DWriteFactory.CreateTypography(iTypography);
  iTypography.AddFontFeature(DWriteFontFeature(DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_6, 1));
  iTextLayout.SetTypography(iTypography, DWriteTextRange(0, Length(str)));

  cvs := TDirect2DCanvas.Create(Canvas, ClientRect);
  cvs.RenderTarget.CreateSolidColorBrush(D2D1ColorF(clRed), nil, iSolidColorBrush);
  iTextRenderer := TMyWriteTextRenderer.Create(cvs.RenderTarget, iSolidColorBrush);
  cvs.RenderTarget.BeginDraw;
  cvs.RenderTarget.Clear(D2D1ColorF(clWhite));
  iTextLayout.Draw(nil, iTextRenderer, 0, 0);
  cvs.RenderTarget.EndDraw();
  cvs.Free;
end;

procedure TForm1.FormResize(Sender: TObject);
begin
  Repaint;
end;

{ TMyWriteTextRenderer }

constructor TMyWriteTextRenderer.Create(ARenderTarge: ID2D1RenderTarget; AOutLineBrush: ID2D1Brush);
begin
  FRenderTarge := ARenderTarge;
  FOutLineBrush := AOutLineBrush;
end;

function TMyWriteTextRenderer.DrawGlyphRun(clientDrawingContext: Pointer; baselineOriginX,
  baselineOriginY: Single; measuringMode: DWRITE_MEASURING_MODE; var glyphRun: DWRITE_GLYPH_RUN;
  var glyphRunDescription: DWRITE_GLYPH_RUN_DESCRIPTION; const clientDrawingEffect: IInterface): HRESULT;
var
  iPathGeometry: ID2D1PathGeometry;
  iGeometrySink: ID2D1GeometrySink;
  iTransformedGeometry: ID2D1TransformedGeometry;
begin
  D2DFactory.CreatePathGeometry(iPathGeometry);
  iPathGeometry.Open(iGeometrySink);
  glyphRun.fontFace.GetGlyphRunOutline(
    glyphRun.fontEmSize,
    glyphRun.glyphIndices,
    glyphRun.glyphAdvances,
    glyphRun.glyphOffsets,
    glyphRun.glyphCount,
    glyphRun.isSideways,
    longBool(glyphRun.bidiLevel div 2),
    iGeometrySink
  );
  iGeometrySink.Close;

  D2DFactory.CreateTransformedGeometry(
    iPathGeometry,
    TD2DMatrix3x2F.Translation(baselineOriginX, baselineOriginY),
    iTransformedGeometry
  );

  FRenderTarge.DrawGeometry(iTransformedGeometry, FOutLineBrush);
  Result := S_OK;
end;

function TMyWriteTextRenderer.DrawInlineObject(clientDrawingContext: Pointer; originX, originY: Single;
  var inlineObject: IDWriteInlineObject; isSideways, isRightToLeft: LongBool;
  const clientDrawingEffect: IInterface): HRESULT;
begin
  Result := E_NOTIMPL;
end;

function TMyWriteTextRenderer.DrawStrikethrough(clientDrawingContext: Pointer; baselineOriginX,
  baselineOriginY: Single; var strikethrough: DWRITE_STRIKETHROUGH;
  const clientDrawingEffect: IInterface): HRESULT;
begin
  Result := E_NOTIMPL;
end;

function TMyWriteTextRenderer.DrawUnderline(clientDrawingContext: Pointer; baselineOriginX,
  baselineOriginY: Single; var underline: DWRITE_UNDERLINE; const clientDrawingEffect: IInterface): HRESULT;
var
  rRectF: TD2DRectF;
  iRectangleGeometry: ID2D1RectangleGeometry;
  iTransformedGeometry: ID2D1TransformedGeometry;
begin
  rRectF := D2D1RectF(
    0,
    underline.offset,
    underline.width,
    underline.offset + underline.thickness
  );

  D2DFactory.CreateRectangleGeometry(rRectF, iRectangleGeometry);
  D2DFactory.CreateTransformedGeometry(
    iRectangleGeometry,
    TD2DMatrix3x2F.Translation(baselineOriginX, baselineOriginY),
    iTransformedGeometry
  );

  FRenderTarge.DrawGeometry(iTransformedGeometry, FOutLineBrush);
  Result := S_OK;
end;

function TMyWriteTextRenderer.GetCurrentTransform(clientDrawingContext: Pointer;
  var transform: DWRITE_MATRIX): HRESULT;
begin
  FRenderTarge.GetTransform(TD2D1Matrix3x2F(transform));
  Result := S_OK;
end;

function TMyWriteTextRenderer.GetPixelsPerDip(clientDrawingContext: Pointer;
  var pixelsPerDip: Single): HRESULT;
var
  x,y: Single;
begin
  FRenderTarge.GetDpi(x, y);
  pixelsPerDip := x / 96;
  Result := S_OK;
end;

function TMyWriteTextRenderer.IsPixelSnappingDisabled(clientDrawingContext: Pointer;
  var isDisabled: LongBool): HRESULT;
begin
  isDisabled := False;
  Result := S_OK;
end;

end.


效果图:



本文转载自:http://www.cnblogs.com/del/archive/2011/04/13/2014946.html

共有 人打赏支持
涂孟超
粉丝 12
博文 2011
码字总数 14107
作品 0
深圳
程序员
Direct2D教程(十)绘制文本

概述 在Direct2D中,文本的绘制是通过DirectWrite来实现的,DirectWrite实际上已经是一个独立的DirectX组件了。关于DirectWrite,我摘录了MSDN的一段文字。 DirectWrite介绍 当今的应用程序应...

吞吞吐吐的
2017/10/17
0
0
WPF 使用 Direct2D1 画图 绘制基本图形

本文来告诉大家如何在 Direct2D1 绘制基本图形,包括线段、矩形、椭圆 本文是一个系列 WPF 使用 Direct2D1 画图入门 WPF 使用 Direct2D1 画图 绘制基本图形 本文的组织参考Direct2D,对大神表...

lindexi_gd
04/19
0
0
Windows桌面应用程序(1-2-4-7th) DPI和设备无关的像素

要使用Windows图形进行有效编程,您必须了解两个相关的概念: 每英寸点数(DPI) 设备无关像素(DIP)。 我们从DPI开始。这将需要短暂的绕行排版。在印刷术中,类型的大小以称为点的单位来测量。一点...

qq_37422196
01/12
0
0
杂谈SharpDx中的WIC组件——我们需要WIC的图片编码功能么?

在前文 SharpDX之Direct2D教程II——加载位图文件和保存位图文件 中,发现在VB2010中不能很好的运用SharpDx中的WIC组件进行图片的编码工作。可能是我的设置问题,也可能是SharpDx对VB2010支持...

万仓一黍
2013/10/08
0
0
Direct2D教程(十二)图层

什么是Layers? Layer,中文译成图层,在Direct2D中可以用来完成一些特殊效果,使用Layer的时候,先将Layer Push到render target,然后进行绘制,此时是直接绘制在Layer上的,绘制完毕后,将...

吞吞吐吐的
2017/10/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Dubbo分析之Transport层

前言 上一篇文章Dubbo分析之Serialize层,介绍了最底层的序列化/反序列化层,本文继续分析Serialize层的上一层transport网络传输层,此层使用了现有的一些通讯开源框架(ex:netty,mina,grizzl...

ksfzhaohui
14分钟前
0
0
通告!Android 9 Pie未适配应用公示

8月7日,谷歌正式发布Android 9 Pie,至今已两月有余。近日,华为终端开放实验室对国内主流应用在Android 9 Pie的兼容性进行测试,结果显示:目前TOP3000应用兼容率已经超过95%,但仍有少量应...

安卓绿色联盟
16分钟前
0
0
Linux下多网卡绑定模式详解

在我们日常Linux使用中,一般对于生产网都会使用双网卡或多网卡接入,这样既能添加网络带宽,同时又能做相应的冗余,可谓好处多多。而一般我们都会使用Linux操作系统下自带的网卡绑定模式。这...

openthings
18分钟前
0
0
SylixOS中AARCH64跳转表实现原理

1. 跳转表存在的意义 1.1 内核模块反汇编 如下的程序清单,为一个内核模块的源码。 #define __SYLIXOS_KERNEL#include <SylixOS.h>#include <module.h> /* * SylixOS call module_i......

zhywxyy
19分钟前
1
0
聊一聊 Spring 中的线程安全性

本文摘自ImportNew公众号,摘录做学习资料,向大家推荐该公众号 Spring与线程安全 Spring作为一个IOC/DI容器,帮助我们管理了许许多多的“bean”。但其实,Spring并没有保证这些对象的线程安...

木子SMZ
20分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部