2015-02-26 2 views
1

Я хочу напечатать текст на изображении 24bpp с помощью DrawString(). Проблема в том, что если я выбираю белый цвет текста, текст почти невидим в более ярких областях изображения. Если я выбираю красный цвет текста, текст почти невидим в областях изображения, который содержит больше красного. И так далее.Нарисуйте видимый текст на изображении, независимо от цвета изображения

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

Но каково решение? Есть ли?

Спасибо!

+0

Рисует непрозрачный белый прямоугольник фона за текстом, приемлемым в вашей ситуации? –

+1

Я не помню, где я это слышал, но из того, что я помню, лучший способ убедиться, что текст всегда виден, будет иметь белый текст с черной рамкой. – krillgar

+0

Может ли цвет сильно отличаться в пределах одной строки?Или разумно предположить, что одного цвета на строку будет достаточно? –

ответ

2

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

Вы можете сначала использовать алгоритм для расчета наиболее различного цвета от другого цвета следующим образом:

public static byte MostDifferent (byte original) { 
    if(original < 0x80) { 
     return 0xff; 
    } else { 
     return 0x00; 
    } 
} 
public static Color MostDifferent (Color original) { 
    byte r = MostDifferent(original.R); 
    byte g = MostDifferent(original.G); 
    byte b = MostDifferent(original.B); 
    return Color.FromArgb(r,g,b); 
} 

Теперь, когда мы сделали это, мы должны рассчитать средний цвет в пределах области, где string будет нарисован. Вы можете сделать это на Bitmap уровне:

public static unsafe Color AverageColor (Bitmap bmp, Rectangle r) { 
    BitmapData bmd = bmp.LockBits (r, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); 
    int s = bmd.Stride; 
    int cr = 0; 
    int cg = 0; 
    int cb = 0; 
    int* clr = (int*)(void*)bmd.Scan0; 
    int tmp; 
    int* row = clr; 
    for (int i = 0; i < r.Height; i++) { 
     int* col = row; 
     for (int j = 0; j < r.Width; j++) { 
      tmp = *col; 
      cr += (tmp >> 0x10) & 0xff; 
      cg += (tmp >> 0x08) & 0xff; 
      cb += tmp & 0xff; 
      col++; 
     } 
     row += s>>0x02; 
    } 
    int div = r.Width * r.Height; 
    int d2 = div >> 0x01; 
    cr = (cr + d2)/div; 
    cg = (cg + d2)/div; 
    cb = (cb + d2)/div; 
    bmp.UnlockBits (bmd); 
    return Color.FromArgb (cr, cg, cb); 
} 

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

public static void DrawColorString (this Graphics g, Bitmap bmp, string text, Font font, PointF point) { 
    SizeF sf = g.MeasureString (text, font); 
    Rectangle r = new Rectangle (Point.Truncate (point), Size.Ceiling (sf)); 
    r.Intersect (new Rectangle(0,0,bmp.Width,bmp.Height)); 
    Color brsh = MostDifferent (AverageColor (bmp, r)); 
    g.DrawString (text, font, new SolidBrush (brsh), point); 
} 

Теперь вы можете вызвать метод, например, как:

Bitmap bmp = new Bitmap("Foo.png"); 
Graphics g = Graphics.FromImage(bmp); 
g.DrawColorString (bmp, "Sky", new Font ("Arial", 72.0f), new PointF (600.0f, 150.0f)); 
g.DrawColorString (bmp, "Sand", new Font ("Arial", 72.0f), new PointF (600.0f, 450.0f)); 
bmp.Save ("result.jpg"); 

Этот результат, например, в:

enter image description here

+0

Привет, отлично, использовал ваш подход к большинству. Отличная помощь, спасибо! – RobertK

2

Я предлагаю рисунок текст два (или три) раза: один раз в темноте и один раз в яркий цвет, отделил 1 или 2 пикселя вверх & влево, в зависимости от размера шрифта.

Если ваш шрифт очень маленький и только 1 пиксель ширина от многих ударов затем с помощью тройного рисунка поможет: две наружных в одном цвете (темный) и 3-й один в середине (яркий)

Random R = new Random(); 
private void pictureBox2_Paint(object sender, PaintEventArgs e) 
{ 
    // using a loop for create random locations..: 
    for (int i=0; i< 33; i++) 
    { 
     Point pt = new Point(R.Next(pictureBox2.ClientSize.Width), 
          R.Next(pictureBox2.ClientSize.Width)); 

     e.Graphics.DrawString("Hello World", Font, Brushes.Black, pt.X - 1, pt.Y - 1); 
     e.Graphics.DrawString("Hello World", Font, Brushes.Black, pt.X + 1, pt.Y + 1); 
     e.Graphics.DrawString("Hello World", Font, Brushes.White, pt.X , pt.Y); 
    } 
} 

Вот пример с тройным рисунком

: (. Обратите внимание, что оригинал выглядит очень отчетливый, что я вижу в браузере Вы можете загрузить его, чтобы проверить ..) enter image description here

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

+0

Вы также можете просто написать черный текст в виде жирного шрифта (возможно, с увеличением шрифта), затем белый шрифт, в центре обоих вызовов, чтобы они правильно выстроились. Какого черта это фотография: автомобильная катастрофа? – LarsTech

+1

Я не думаю, что выстроиться будет работать дольше, чем несколько персонажей. ОП попытался что-то вроде этого, и персонажи не выстроились хорошо. - Не случайно (пока); один из серии [замечательных kludges] (http://www.heise.de/autos/artikel/Die-skurrilsten-Eigenreparaturen-1717729.html?bild=4;view=bildergalerie) – TaW

Смежные вопросы