Плохое отображение текста с использованием DrawString поверх прозрачных пикселей - PullRequest
30 голосов
/ 07 июня 2010

При отображении текста в растровое изображение, я считаю, что текст выглядит очень плохо, когда отображается поверх области с непрозрачным альфа-каналом.Проблема становится все хуже, поскольку нижележащие пиксели становятся более прозрачными.Если бы мне пришлось угадывать, я бы сказал, что, когда нижележащие пиксели прозрачны, средство визуализации текста рисует любые сглаженные «серые» пиксели в виде сплошного черного.1005 * Текст, нарисованный поверх прозрачных пикселей:

alt text

Текст, нарисованный поверх полупрозрачных пикселей:

alt text

Текст, нарисованный на непрозрачных пикселях:

alt text

Вот код, используемый для визуализации текста:

g.SmoothingMode = SmoothingMode.HighQuality;
g.DrawString("Press the spacebar", Font, Brushes.Black, textLeft, textTop);

Ответы [ 4 ]

28 голосов
/ 16 августа 2011

Опция, которую я использовал для решения этой проблемы, была:

Graphics graphics = new Graphics();
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;

В TextRenderingHint есть несколько других полезных опций

Надеюсь, это поможет

15 голосов
/ 02 августа 2014

Существует очень простой ответ на этот вопрос ...

g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit

Если вы установите это до того, как вы представите свой текст, он станет понятен. Кроме того, этот метод поддерживает больший размер шрифта (по умолчанию используется только размер 56).

Спасибо за чтение этого поста.

12 голосов
/ 07 июня 2010

Первый вывод - это то, что вы получаете, когда рисуете черный текст на черном фоне, возможно Color.Transparent. Второе было нарисовано на почти черном фоне. Третий был нарисован на том же фоне, на котором он отображается.

Сглаживание не может работать на прозрачном фоне. Цвета, используемые для пикселей сглаживания, не будут смешивать форму буквы с фоном, если текст отображается с другим фоном. Эти пиксели теперь станут очень заметными, и текст будет выглядеть очень плохо.

Обратите внимание, что SmoothingMode не влияет на вывод текста. Это будет выглядеть немного менее плохо, если вы используете более низкое качество TextRenderingHint и фоновый цвет, который сероватый с альфа-ноль. Только TextRenderingHint.SingleBitPerPixelGridFit позволяет избежать всех проблем сглаживания.

Получить идеальное решение для этого очень сложно. Эффект стекла Vista в строке заголовка окна использует очень тонкое затенение, чтобы придать тексту четко определенный фоновый цвет. Вам понадобится инструмент ZoomIt от SysInternals, чтобы действительно его увидеть. Функция DrawThemeTextEx () с ненулевым iGlowSize.

2 голосов
/ 02 августа 2014

Если вы ищете что-то, что сохраняет сглаживание немного лучше, чем GDI + по умолчанию, вы можете вызвать Graphics.Clear с помощью хроматического ключа, а затем вручную удалить возникающие артефакты цветности. (См. Почему DrawString выглядит таким дурацким? и Проблема с уродливым текстом .)

Вот как в итоге я решил подобную проблему:

static Bitmap TextToBitmap(string text, Font font, Color foregroundColor)
{
  SizeF textSize;

  using ( var g = Graphics.FromHwndInternal(IntPtr.Zero) )
    textSize = g.MeasureString(text, font);

  var image = new Bitmap((int)Math.Ceiling(textSize.Width), (int)Math.Ceiling(textSize.Height));
  var brush = new SolidBrush(foregroundColor);

  using ( var g = Graphics.FromImage(image) )
  {
    g.Clear(Color.Magenta);
    g.SmoothingMode = SmoothingMode.AntiAlias;
    g.InterpolationMode = InterpolationMode.HighQualityBicubic;
    g.PixelOffsetMode = PixelOffsetMode.HighQuality;
    g.DrawString(text, font, brush, 0, 0);
    g.Flush();
  }

  image.MakeTransparent(Color.Magenta);

  // The image now has a transparent background, but around each letter are antialiasing artifacts still keyed to magenta.  We need to remove those.
  RemoveChroma(image, foregroundColor, Color.Magenta);
  return image;
}

static unsafe void RemoveChroma(Bitmap image, Color foregroundColor, Color chroma)
{
  if (image == null) throw new ArgumentNullException("image");
  BitmapData data = null;

  try
  {
    data = image.LockBits(new Rectangle(Point.Empty, image.Size), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

    for ( int y = data.Height - 1; y >= 0; --y )
    {
      int* row = (int*)(data.Scan0 + (y * data.Stride));
      for ( int x = data.Width - 1; x >= 0; --x )
      {
        if ( row[x] == 0 ) continue;
        Color pixel = Color.FromArgb(row[x]);

        if ( (pixel != foregroundColor) &&
             ((pixel.B >= foregroundColor.B) && (pixel.B <= chroma.B)) &&
             ((pixel.G >= foregroundColor.G) && (pixel.G <= chroma.G)) &&
             ((pixel.R >= foregroundColor.R) && (pixel.R <= chroma.R)) )
        {
          row[x] = Color.FromArgb(
            255 - ((int)
              ((Math.Abs(pixel.B - foregroundColor.B) +
                Math.Abs(pixel.G - foregroundColor.G) +
                Math.Abs(pixel.R - foregroundColor.R)) / 3)),
            foregroundColor).ToArgb();
        }
      }
    }
  }
  finally
  {
    if (data != null) image.UnlockBits(data);
  }
}

Жаль, что GDI / GDI + уже не делает этого, но это было бы разумно, не так ли? :)

Если вы не можете использовать контекст unsafe, вы можете легко использовать ту же логику с Bitmap.GetPixel и Bitmap.SetPixel, хотя это будет значительно медленнее.

...