Рисование текста на фоне стекла становится размытым по мере того, как Alpha опускается

Я делаю несколько обновлений для компонента TIceTabSet (вкладки Chrome). Одним из таких изменений является добавление прозрачности. Все работает отлично от текста. По мере того как альфа-канал фона становится ниже, текст становится все более размытым. Вот скриншот.

enter image description here

Вот код, который рисует вкладки. В основном это оригинальный код TIceTabSet. Я просто добавил несколько изменений, чтобы сделать вкладки прозрачными. Код также немного изменился для примера скриншота. Команда DrawText внизу - это текст, нарисованный на холсте.

procedure TIceTabSet.InnerDraw(Canvas: TCanvas; TabRect: TRect; Item: TIceTab);
var
  graphics : TGPGraphics;
  Pen: TGPPen;
  Brush: TGPSolidBrush;
  path, linePath: TGPGraphicsPath;
  linGrBrush: TGPLinearGradientBrush;
  font: TGPFont;
  solidBrush: TGPSolidBrush;
  rectF: TGPRectF;
  stringFormat: TGPStringFormat;
  DC: HDC;
  marginRight: integer;
  iconY, iconX: integer;
  textStart: Extended;
  startColor, EndColor, textColor, borderColor: cardinal;
  borderWidth: Integer;
  TabProperties: TIceTabProperties;
  Alpha: Byte;
begin
  DC := Canvas.Handle;

  TabProperties := GetTabProperties(Item);

  Alpha := Item.Index * 50;

  startColor := MakeGDIPColor(TabProperties.TabStyle.StartColor, Alpha);// TabProperties.TabStyle.Alpha);
  endColor := MakeGDIPColor(TabProperties.TabStyle.StopColor, Alpha); //TabProperties.TabStyle.Alpha);
  textColor := MakeGDIPColor(TabProperties.Font.Color, 255); //TabProperties.TabStyle.Alpha);
  borderColor := MakeGDIPColor(TabProperties.BorderColor, TabProperties.TabStyle.Alpha);
  borderWidth := TabProperties.BorderWidth;

  graphics := TGPGraphics.Create(DC);
  Brush := TGPSolidBrush.Create(borderColor);
  Pen:= TGPPen.Create(borderColor);
  Font := GetGDIPFont(Canvas, FTabActive.Font); //TabProperties.Font);
  try
    graphics.SetSmoothingMode(SmoothingModeHighQuality);

    pen.SetWidth(borderWidth);

    path := TGPGraphicsPath.Create();
    try
      path.AddBezier(TabRect.Left, TabRect.Bottom, TabRect.Left + FTabShape.LeftEdgeWidth / 2, TabRect.Bottom, TabRect.Left + FTabShape.LeftEdgeWidth / 2, TabRect.Top, TabRect.Left + FTabShape.LeftEdgeWidth, TabRect.Top);
      path.AddLine(TabRect.Left + FTabShape.LeftEdgeWidth, TabRect.Top, TabRect.Right - FTabShape.RightEdgeWidth, TabRect.Top);
      path.AddBezier(TabRect.Right - FTabShape.RightEdgeWidth, TabRect.Top, TabRect.Right - FTabShape.RightEdgeWidth / 2, TabRect.Top, TabRect.Right - FTabShape.RightEdgeWidth / 2, TabRect.Bottom, TabRect.Right, TabRect.Bottom);

      linePath := TGPGraphicsPath.Create;
      try
        linePath.AddPath(path, false);
        path.AddLine(TabRect.Right, TabRect.Bottom, TabRect.Left, TabRect.Bottom);

        linGrBrush := TGPLinearGradientBrush.Create(
          MakePoint(0, TabRect.Top),
          MakePoint(0, TabRect.Bottom),
          startColor,
          endColor);
        try
          graphics.DrawPath(pen, linePath);

          graphics.FillPath(linGrBrush, path);
        finally
          linGrBrush.Free;
        end;
      finally
        linePath.Free;
      end;
    finally
      path.Free;
    end;

    marginRight := 0;

    if TabDisplaysCloseButton(Item) then
    begin
      if (HighLightTabClose = Item) and
         (FTabCloseButton.ShowCircle) then
      begin
        pen.SetWidth(1);

        pen.SetColor(MakeGDIPColor(FTabCloseButton.CrossColorHotTrack, 255));
        brush.SetColor(MakeGDIPColor(FTabCloseButton.CircleColorHotTrack, 255));

        graphics.FillEllipse(brush, TabRect.Right - FTabShape.RightEdgeWidth - 7 - 2,
                                    TabRect.Top + ((TabRect.Bottom - TabRect.Top - 7) div 2) - 3,
                                    (TabRect.Right - FTabShape.RightEdgeWidth) - (TabRect.Right - FTabShape.RightEdgeWidth - 7) + 6,
                                    (TabRect.Top + ((TabRect.Bottom - TabRect.Top + 7) div 2)) - (TabRect.Top + ((TabRect.Bottom - TabRect.Top - 7) div 2)) + 6);

        graphics.DrawLine(pen, TabRect.Right - FTabShape.RightEdgeWidth - 5, TabRect.Top + ((TabRect.Bottom - TabRect.Top - 5) div 2),
                               TabRect.Right - FTabShape.RightEdgeWidth, TabRect.Top + ((TabRect.Bottom - TabRect.Top + 5) div 2));

        graphics.DrawLine(pen, TabRect.Right - FTabShape.RightEdgeWidth, TabRect.Top + ((TabRect.Bottom - TabRect.Top - 5) div 2),
                               TabRect.Right - FTabShape.RightEdgeWidth - 5, TabRect.Top + ((TabRect.Bottom - TabRect.Top + 5) div 2));
      end
      else
      begin
        pen.SetWidth(2);

        if HighlightTabClose = Item then
          pen.SetColor(MakeGDIPColor(FTabCloseButton.CrossColorHotTrack, 255))
        else
          pen.SetColor(MakeGDIPColor(FTabCloseButton.CrossColorNormal, 255));

        graphics.DrawLine(pen, TabRect.Right - FTabShape.RightEdgeWidth - 7, TabRect.Top + ((TabRect.Bottom - TabRect.Top - 7) div 2),
                               TabRect.Right - FTabShape.RightEdgeWidth, TabRect.Top + ((TabRect.Bottom - TabRect.Top + 7) div 2));

        graphics.DrawLine(pen, TabRect.Right - FTabShape.RightEdgeWidth, TabRect.Top + ((TabRect.Bottom - TabRect.Top - 7) div 2),
                               TabRect.Right - FTabShape.RightEdgeWidth - 7, TabRect.Top + ((TabRect.Bottom - TabRect.Top + 7) div 2));
      end;

      marginRight := 10;
    end;

    solidBrush:= TGPSolidBrush.Create(MakeGDIPColor(textColor, 255));

    stringFormat:= TGPStringFormat.Create;
    stringFormat.SetAlignment(StringAlignmentNear);
    stringFormat.SetLineAlignment(StringAlignmentCenter);
    stringFormat.SetTrimming(StringTrimmingEllipsisCharacter);
    stringFormat.SetFormatFlags(StringFormatFlagsNoWrap);

    SelectClipRgn(Canvas.Handle, 0);
    textStart := TabRect.Left + FTabShape.LeftEdgeWidth;
    iconX := 0;
    iconY := 0;

    if Assigned(Images) and (Item.ImageIndex <> -1) then
    begin
      iconY := TabRect.Top + ((TabRect.Bottom - TabRect.Top - Images.Height) div 2);
      iconX := Round(textStart);
      textStart := textStart + Images.Width + 4;
    end;

    rectF := MakeRect(textStart, TabRect.Top, TabRect.Right - textStart - FTabShape.RightEdgeWidth - marginRight,
      TabRect.Bottom - TabRect.Top);

    // ****** Text is drawn here *******
    if rectF.Width > 10 then
      graphics.DrawString(format('Alpha: %d', [Alpha]), -1, font, rectF, stringFormat, solidBrush);
    // *********************************

  finally
    font.Free;
    solidBrush.Free;
    Pen.Free;
    graphics.Free;
  end;

  if Assigned(Images) and
    (Item.ImageIndex <> -1) then
    Images.Draw(Canvas, iconX, iconY, Item.ImageIndex, true);
end;

Вы можете скачать полный источник здесь. Имейте в виду, что это незавершенное производство. Источник будет возвращен оригинальному автору, когда он будет завершен.

Обновление 1

Изменение кода, предложенное TLama, безусловно, помогает, но это не полностью устраняет проблему. Вот как выглядит текст:

enter image description here

... вот как выглядит Google Chrome:

enter image description here

Обновление 2

Здесь он выглядит с TextRenderingHintSingleBitPerPixelGridFit.

enter image description here

Я пробовал все варианты и TextRenderingHintAntiAlias ​​ дает наилучшие результаты.

Ответ 1

Как сказал Ян Бойд в своем хорошем посте о How to draw ClearType text on Aero glass ?, вы должны применять антиализацию, когда вы показываете текст на листе стекла. Поэтому, чтобы исправить вашу проблему, попробуйте изменить свой код следующим образом:

if rectF.Width > 10 then
begin
  if (GetParentForm.GlassFrame.Enabled) and (GetParentForm.GlassFrame.SheetOfGlass) then
    graphics.SetTextRenderingHint(TextRenderingHintAntiAliasGridFit);
  graphics.DrawString(Item.DisplayCaption, -1, font, rectF, stringFormat, solidBrush);
end;

Чтобы смоделировать вашу проблему, достаточно просто сделать текст на листе стекла Aero, как это делает следующий код тремя способами:

uses
  GDIPAPI, GDIPOBJ;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Font.Color := clWhite;
  GlassFrame.SheetOfGlass := True;
  GlassFrame.Enabled := True;
end;

procedure TForm1.FormPaint(Sender: TObject);
var
  S: WideString;
  GPFont: TGPFont;
  GPGraphics: TGPGraphics;
  GPSolidBrush: TGPSolidBrush;
  GPGraphicsPath: TGPGraphicsPath;
begin
  S := 'This is a sample text rendered on the sheet of Aero glass!';
  GPFont := TGPFont.Create(Canvas.Handle, Font.Handle);
  GPSolidBrush := TGPSolidBrush.Create(MakeColor(GetRValue(Font.Color),
    GetGValue(Font.Color), GetBValue(Font.Color)));
  GPGraphicsPath := TGPGraphicsPath.Create;
  GPGraphicsPath.AddString(S, Length(S), TGPFontFamily.Create(Font.Name),
    GPFont.GetStyle, GPFont.GetSize, MakePoint(20.0, 60.0), nil);
  try
    GPGraphics := TGPGraphics.Create(Canvas.Handle);
    try
      GPGraphics.SetSmoothingMode(SmoothingModeAntiAlias);
      GPGraphics.FillPath(GPSolidBrush, GPGraphicsPath);
      GPGraphics.DrawString(S, Length(S), GPFont, MakePoint(20.0, 20.0),
        nil, GPSolidBrush);
      GPGraphics.SetTextRenderingHint(
        TextRenderingHintSingleBitPerPixelGridFit);
      GPGraphics.DrawString(S, Length(S), GPFont, MakePoint(20.0, 40.0),
        nil, GPSolidBrush);
    finally
      GPGraphics.Free;
    end;
  finally
    GPFont.Free;
    GPSolidBrush.Free;
    GPGraphicsPath.Free;
  end;
end;

И это приводит к следующему изображению, где:

  • первый текст был обработан функцией DrawString без включенного сглаживания текста.
  • вторая была отображена функцией DrawString с включенным сглаживанием текста, настроенным на TextRenderingHintSingleBitPerPixelGridFit режим
  • третья была отображена путем заполнения пути, вдохновленного this article с режимом сглаживания, установленным на SmoothingModeAntiAlias style

enter image description here

Ответ 2

В качестве альтернативы вы можете попробовать рисовать с помощью темы api (Vista и позже). Играя с различными настройками тени/границы/свечения, может быть возможно создать удобочитаемый текст. Некоторая попытка на листе стекла:


enter image description here

code (XE2):

procedure TForm1.FormPaint(Sender: TObject);
var
  R: TRect;
  ThemeData: HTHEME;
  Opts: TDTTOpts;
begin
  R := Rect(10, 10, 150, 30);
  vcl.themes.DrawGlassText(Canvas.Handle, 'DrawGlassText Sample', R, 0, 3,
      clBlack, TStyleManager.SystemStyle.GetElementDetails(ttsLabel));


  OffsetRect(R, 160, 0);
  ThemeData := OpenThemeData(Handle, 'textstyle');

  Opts.dwSize := SizeOf(Opts);
  Opts.crText := ColorToRGB(clBlack);
  Opts.crShadow := $D0D0B0;
  Opts.iTextShadowType := TST_SINGLE;
  Opts.ptShadowOffset := Point(1, 1);
  Opts.fApplyOverlay := True;
  Opts.iGlowSize := 3;
  Opts.dwFlags := DTT_TEXTCOLOR or DTT_SHADOWTYPE or DTT_SHADOWCOLOR
      or DTT_SHADOWOFFSET or DTT_GLOWSIZE;
  DrawThemeTextEx(ThemeData, Canvas.Handle, TEXT_LABEL, TS_NORMAL,
      'DrawThemeTextEx Sample', -1, 0, @R, Opts);

  OffsetRect(R, 180, 0);
  Opts.crText := ColorToRGB(clBlack);
  Opts.iGlowSize := 4;
  Opts.fApplyOverlay := True;
  Opts.dwFlags := DTT_TEXTCOLOR or DTT_GLOWSIZE;
  DrawThemeTextEx(ThemeData, Canvas.Handle, TEXT_BODYTITLE, 0,
      'Another Sample', -1, 0, @R, Opts);

  CloseThemeData(ThemeData);
end;