Подтвердить что ты не робот

Плохое отображение текста с использованием DrawString поверх прозрачных пикселей

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

Вот несколько скриншотов:

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

alt text

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

alt text

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

alt text

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

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

Ответ 1

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

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

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

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

Ответ 2

Есть очень простой ответ на этот вопрос...

g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit

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

Спасибо, что прочитали этот пост.

Ответ 3

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

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

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

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

Ответ 4

Если вы ищете что-то, что сохраняет сглаживание немного лучше, чем 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, хотя она будет значительно медленнее.