Delphi 我可以生成大于149的抗锯齿字体吗?

Delphi 我可以生成大于149的抗锯齿字体吗?,delphi,winapi,fonts,gdi,Delphi,Winapi,Fonts,Gdi,我注意到,在Delphi XE6中(以及在生成在Windows上运行的应用程序并使用本机GDI字体呈现的其他工具/语言中),Win32 TextOut API似乎无法平滑任何大于149的字体,即font.Size>149。这是一个屏幕截图,显示了两个SpeedButton,都将Font.Quality设置为fqClearType,左边的一个设置为Font.Size为149,右边的一个设置为Font.Size为150。这是一点不同。高度值分别为-199和-200。这只是用Delphi组件和表单演示

我注意到,在Delphi XE6中(以及在生成在Windows上运行的应用程序并使用本机GDI字体呈现的其他工具/语言中),Win32 TextOut API似乎无法平滑任何大于149的字体,即font.Size>149。这是一个屏幕截图,显示了两个SpeedButton,都将Font.Quality设置为fqClearType,左边的一个设置为Font.Size为149,右边的一个设置为Font.Size为150。这是一点不同。高度值分别为-199和-200。这只是用Delphi组件和表单演示,也可以在TPaintBox中演示,使用Canvas.Font和对Win32 API
DrawText
的调用,或者使用纯Win32 API应用程序创建窗口,并使用
DrawText
绘制设备上下文

这里清楚地显示了GDI的局限性;请注意,ClearType在size=149时看起来一般(水平抗锯齿,但没有垂直),而ClearType在150时完全关闭:

我的问题是,有没有办法绕过Win32 API GDI中的这一限制,使用Windows 7及更高版本上的一些原始Win32函数来绘制文本并始终使用反别名?在这里,我假设逻辑字体处理在VCL内部正确完成,因为在C#应用程序(使用WinForms,它在GDI上运行)中出现了相同的限制,正如我在Delphi中尝试此操作时所看到的

我想画一个字体大小大于149的抗锯齿字符到GDI画布上,可以是清晰的类型,也可以是经典的抗锯齿。我该怎么做

请注意,我已经将Font.Quality显式地设置为抗锯齿模式和ClearType模式,并且Win32 GDI api调用忽略了某些大小的逻辑字体属性,这显然是出于设计。但是,某些应用程序(如Microsoft Word)显然具有绘制155点或更大字体的字体渲染功能,在本例中仍然具有反别名功能


更新:我回答了自己的问题,说明DirectWrite+GDI互操作有多简单。在windows 7和windows 8以及更高版本上,DirectWrite实际上提供了水平和垂直消除混叠,我相信这是一种高质量的屏幕字体呈现模式,MS Word 2013等应用程序正在使用这种模式。我相信有人可以很容易地回答我的问题,显示GDI+示例,这也符合我的上述要求(因为GDI+包含在Windows 7和8中)。

我发现与GDI+的互操作比GDI+更好的一种工作方法是使用
DirectWrite
,但这只适用于Windows 7和8,我在这里展示的示例代码有一个简单的GDI回退模式(普通GDI,无抗锯齿),它覆盖了XP和Vista,至少提供了一个优雅的降级;它仍然使用GDI在Win7之前的操作系统上绘制文本

原来的演示应用程序在这里,但它使用的是TForm,我将其更改为TWinControl,它没有GDI回退,只是一个例外

撰写上述演示的Pawel Glowacki的讨论/博文如下:

这里显示了一段代码片段,其中包括Pawel演示中修改的D2DUtils.pas,并添加了GDI回退功能(而不是因异常而崩溃)

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


type
   TCanvasD2D = class(TWinControl) // a base class, using TWinControl instead of TForm.
   private
      FInitFlag: Boolean;
      FGDIMode: Boolean; { Fallback }
      FD2DCanvas: TDirect2DCanvas; { Used When D2D is available and GDIMode=False }
      FGDICanvas: TCanvas; { Fallback canvas, used when FGDIMode=True }
      procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
   protected
      procedure Resize; override;

      procedure DoPaint(AHDC: HDC); virtual;

      procedure CreateD2DResources; virtual;

      procedure PaintD2D; virtual;
      procedure PaintGDI; virtual;

      function RenderTarget: ID2D1RenderTarget; // convenience function used during D2D Paints.

      procedure PaintWindow(DC: HDC); override;

   public
      constructor Create(AOwner: TComponent); override;
      destructor Destroy; override;

      procedure Init;
      property D2DCanvas: TDirect2DCanvas read FD2DCanvas;
      property GDICanvas: TCanvas read FGDICanvas;

      property GDIMode: Boolean read FGDIMode write FGDIMode;
      { Set to true to force GDI fallback, will automatically set true if D2D is not available, also }
   end;


TCanvasD2DSample = class(TCanvasD2D) // subclass of TCanvasD2D that is a primitive "TLabel"
    private
    FFontBrush: ID2D1SolidColorBrush;// Brush generated from current value of FFontColor
    FBackgroundColor:TColor; // clWhite
    FFontColor:TColor; //clBlack;
    FTextFormat: IDWriteTextFormat;
    FFontName: string;
    FFontSize: Integer;  { Units?}
    FDisplayText: String;
    FLocale: String;


    procedure SetFontName(const Value: String);
    procedure SetFontSize(const Value: Integer);
    procedure SetDisplayText(const Value: String);

  protected
    procedure PaintD2D; override;
    procedure PaintGDI; override;
    procedure CreateD2DResources; override;

    function FontSizeToDip(FontSize:Integer ):Double;

  public
    constructor Create(AOwner: TComponent); override;

    property TextFormat:IDWriteTextFormat read FTextFormat;
    property FontSize:Integer read FFontSize write SetFontSize;
    property FontName:String read FFontName write SetFontName;
    property DisplayText: String read FDisplayText write SetDisplayText;

    property BackgroundColor:TColor read FBackgroundColor write FBackgroundColor;
    property FontColor:TColor read FFontColor write FFontColor; //clBlack;


    property Locale: String read FLocale write FLocale; // string like 'en-us'

  end;

implementation

constructor TCanvasD2D.Create(AOwner: TComponent);
begin
   inherited;

end;

destructor TCanvasD2D.Destroy;
begin
   FD2DCanvas.Free;
   FD2DCanvas := nil;
   FGDICanvas.Free;
   FGDICanvas := nil;

   inherited;
end;

procedure TCanvasD2D.Init;
begin
   if not FInitFlag then
   begin
      FInitFlag := True;

      if (not FGDIMode) and (TDirect2DCanvas.Supported) then
      begin
         if Assigned(FD2DCanvas) then
            FD2DCanvas.Free;
         FD2DCanvas := TDirect2DCanvas.Create(Handle);
         CreateD2DResources;
      end
      else
      begin
         FGDIMode := True;
         if Assigned(FGDICanvas) then
            FGDICanvas.Free;
         FGDICanvas := TCanvas.Create;
         FGDICanvas.Handle := GetDC(Self.Handle);
      end;
   end;
end;

procedure TCanvasD2D.CreateD2DResources;
begin
   // create Direct2D resources in descendant class
end;

function TCanvasD2D.RenderTarget: ID2D1RenderTarget;
begin
   Result := D2DCanvas.RenderTarget;
end;

procedure TCanvasD2D.Resize;
var
   HwndTarget: ID2D1HwndRenderTarget;
   ASize: TD2D1SizeU;
begin
   inherited;

   if Assigned(D2DCanvas) then
      if Supports(RenderTarget, ID2D1HwndRenderTarget, HwndTarget) then
      begin
         ASize := D2D1SizeU(ClientWidth, ClientHeight);
         HwndTarget.Resize(ASize);
      end;

   Invalidate;
end;

procedure TCanvasD2D.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
   if (not FGDIMode) then
      // avoid flicker as described here:
      // http://chrisbensen.blogspot.com/2009/09/touch-demo-part-i.html
      Message.Result := 1
   else
      inherited;
end;

procedure TCanvasD2D.DoPaint(AHDC: HDC);
begin
   Init;
   if FGDIMode then
   begin
      FGDICanvas.Handle := AHDC;
      PaintGDI;
   end
   else
   begin
      D2DCanvas.BeginDraw;
      try
         PaintD2D;
      finally
         D2DCanvas.EndDraw;
      end;
   end;
end;

procedure TCanvasD2D.PaintD2D;
begin
   // implement painting code in descendant class
end;

procedure TCanvasD2D.PaintGDI;
begin
   // implement in descendant.
end;

procedure TCanvasD2D.PaintWindow(DC: HDC);
begin
   DoPaint(DC);
   inherited;

end;




{ Custom Control Subclass }
procedure TCanvasD2DSample.CreateD2DResources;

begin
  inherited;

  D2DCanvas.RenderTarget.CreateSolidColorBrush(
    D2D1ColorF(FFontColor, 1),
    nil,
    FFontBrush
    );

  DWriteFactory.CreateTextFormat(
    PWideChar(FontName),
    nil,
    DWRITE_FONT_WEIGHT_REGULAR,
    DWRITE_FONT_STYLE_NORMAL,
    DWRITE_FONT_STRETCH_NORMAL,
    FontSizeToDip( FontSize),
    PWideChar(FLocale),
    FTextFormat
   );

   FTextFormat.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
   FTextFormat.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
end;



function TCanvasD2DSample.FontSizeToDip(FontSize: Integer): Double;
begin
   result := FontSize * (96.0 / 72.0); { TODO: 96.0 should not be hard coded? }
end;

procedure TCanvasD2DSample.PaintD2D;
var
  aRect: TD2D1RectF;
//  ASize:D2D_SIZE_F;
begin

  // fill with white color the whole window
  RenderTarget.Clear(D2D1ColorF(FBackgroundColor));



  RenderTarget.DrawText(
    PWideChar(FDisplayText),
    Length(FDisplayText),
    FTextFormat,
    D2D1RectF(0, 0, ClientWidth, ClientHeight),
    FFontBrush
    );

  //  RenderTarget.GetSize(ASize);
end;

procedure TCanvasD2DSample.PaintGDI;
begin
  { FALLBACK PAINT MODE}
  GDICanvas.Lock;
  GDICanvas.Font.Name := FFontName;
  GDICanvas.Font.Size := FFontSize;
  GDICanvas.Font.Color := FFontColor;
  GDICanvas.Brush.Style := bsSolid;
  GDICanvas.Brush.Color := FBackgroundColor;
  GDICanvas.Rectangle(Self.ClientRect);


  GDICanvas.TextOut(0,0, FDisplayText);
GDICanvas.Unlock;
end;

procedure TCanvasD2DSample.SetDisplayText(const Value: String);
begin
   if Value<>FDisplayText then
   begin
    FDisplayText := Value;
    Invalidate;
   end;
end;

procedure TCanvasD2DSample.SetFontName(const Value: String);
begin
  FFontName := Value;
end;

procedure TCanvasD2DSample.SetFontSize(const Value: Integer);
begin
  FFontSize := Value;
end;
使用
窗户,
信息,
SysUtils,
变体,
班级,
绘图,
控制,
形式,
对话,
Winapi.D2D1,
Vcl.Direct2D;
类型
TCanvasD2D=class(TWinControl)//基类,使用TWinControl而不是TForm。
私有的
FInitFlag:布尔型;
FGDIMode:布尔型;{回退}
FD2DCanvas:TDirect2DCanvas;{当D2D可用且GDIMode=False时使用}
FGDICanvas:tcanavas;{后备画布,当FGDIMode=True时使用}
程序WMEraseBkgnd(var消息:TWMEraseBkgnd);消息WM_ERASEBKGND;
受保护的
程序调整;推翻
程序油漆(AHDC:HDC);事实上的
程序和数据源;事实上的
程序d2d;事实上的
GDI程序;事实上的
函数RenderTarget:ID2D1RenderTarget;//D2D绘制期间使用的方便功能。
程序窗口(DC:HDC);推翻
公众的
构造函数创建(AOwner:TComponent);推翻
毁灭者毁灭;推翻
程序初始化;
属性D2DCanvas:TDirect2DCanvas读取FD2DCanvas;
属性GDICanvas:TCanvas读取FGDICanvas;
属性GDIMode:布尔读取FGDIMode写入FGDIMode;
{设置为true以强制GDI回退,如果D2D不可用,也将自动设置为true}
结束;
TCANVASDSDSAMPLE=class(TCanvasD2D)//TCanvasD2D的子类,它是原语“TLabel”
私有的
FFontBrush:ID2D1SOLIDCORBRUSH;//根据FFontColor的当前值生成的笔刷
FBackgroundColor:TColor;//克莱怀特
FFontColor:TColor//clBlack;
FTextFormat:IDWriteTextFormat;
FFontName:字符串;
FFontSize:整数;{单位?}
FDisplayText:字符串;
弗洛卡尔:弦;
过程SetFontName(常量值:字符串);
过程SetFontSize(常量值:整数);
过程SetDisplayText(常量值:字符串);
受保护的
程序d2d;推翻
GDI程序;推翻
程序和数据源;推翻
函数FontSizeToDip(FontSize:Integer):双精度;
公众的
构造函数创建(AOwner:TComponent);推翻
属性TextFormat:IDWriteTextFormat读取FTextFormat;
属性FontSize:整数读取FontSize写入SetFontSize;
属性FontName:字符串读取FontName写入SetFontName;
属性DisplayText:字符串读取FDisplayText写入SetDisplayText;
属性BackgroundColor:TColor读取FBackgroundColor写入FBackgroundColor;
属性FontColor:TColor读取FFontColor写入FFontColor//clBlack;
属性区域设置:字符串读FLocale写FLocale;//像‘en-us’一样的字符串
结束;
实施
构造函数TCanvasD2D.Create(AOwner:TComponent);
开始
内赫里