zoukankan      html  css  js  c++  java
  • Direct2D (39) : 使用 IDWriteTextLayout.Draw() 方法绘制文本


    使用 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.
    


    效果图:



  • 相关阅读:
    Account group in ERP and its mapping relationship with CRM partner group
    错误消息Number not in interval XXX when downloading
    错误消息Form of address 0001 not designated for organization
    Algorithm类介绍(core)
    梯度下降与随机梯度下降
    反思
    绘图: matplotlib核心剖析
    ORB
    SIFT
    Harris角点
  • 原文地址:https://www.cnblogs.com/del/p/2014946.html
Copyright © 2011-2022 走看看