2010-06-07 7 views
30

При рендеринге текста в растровое изображение я считаю, что текст выглядит очень плохо, когда отображается поверх области с непрозрачной альфой. Проблема постепенно ухудшается, поскольку базовые пиксели становятся более прозрачными. Если бы я должен был догадаться, я бы сказал, что когда базовые пиксели прозрачны, текстовый рендеринг рисует любые сглаженные «серые» пиксели как сплошные черные.System.Drawing - плохой рендеринг текста с использованием DrawString поверх прозрачных пикселей

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

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

alt text

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

alt text

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

alt text

Вот код, который используется для отображения текста:

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

Я считаю, что результат также будет зависеть от того, включен ли ClearType или нет. – AMissico

+0

Похоже, вы не очищаете прозрачный фон (или, скорее, недействительны). – leppie

+0

любое окончательное решение с полным исходным кодом? – Kiquenet

ответ

23

Опции Я использовал, чтобы обойти эту проблему, была:

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

Есть некоторым другие полезные опции в TextRenderingHint

Надеется, что это помогает

+0

Помененный ответ для этого, хотя это очень старый вопрос, и я не тестировал этот ответ. – mackenir

+1

Ответ пользователя3470185 (g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit) дает лучший результат. – yclkvnc

11

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

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

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

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

+0

Ugh. Полагаю, я мог бы начертить черный текст на белый на временном растровом изображении, преобразовать белый в прозрачный и от серого до полупрозрачного черного, а затем нарисовать его до окончательного растрового изображения. Я попробую другой TextRenderingHints. В противном случае, я думаю, эффект с частичной прозрачностью фона не так уж плох. Одна вещь, хотя - в каждом случае, фон имеет один и тот же цвет (не совсем белый) с различной прозрачностью, поэтому я не ожидал, что текст будет рисоваться так же, как «эффективный» цвет фона черного. – mackenir

+0

Не уверен, что вы имеете в виду. Ключевой проблемой является то, что GDI не видит прозрачности, он видит значение RGB прозрачного цвета. Color.Transparent - очень плохой выбор, его значение RGB равно нулю. Черный. –

+0

Я имею в виду, что фон окрашен в не совсем белый цвет, с альфа-компонентом. Не уверен, где вы получили Color.Transparent. – mackenir

2

Если вы ищете что-то, что сохраняет сглаживании немного лучше, чем GDI + по умолчанию, вы можете вызвать Graphics.Clear с помощью цветного ключа, а затем вручную удалить получаемые цветовые артефакты. (См. Why does DrawString look so crappy? и Ugly looking text problem.)

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

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, хотя это будет значительно медленнее.

+0

Очень хорошее объяснение. – tmighty

10

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

g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit 

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

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