使用 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.
效果图: