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