0

What I am trying to achieve is - I want to draw a text watermark on an image using GDI. This text watermark should be white and semi-transparent, shadow - grey semi-transparent. I tried this solution but obviously, when I draw a white semi-transparent text on top of a grey semi-transparent text, I see no white text at all, because grey pops up. Would be very helpful if anyone could guide me through this, as I have no experience with graphics at all. What I also tried, except of this:

drawing.DrawString(text, font, shadowBrush, x + shadowOffset.Width, y + shadowOffset.Height);
drawing.DrawString(text, font, textBrush, x, y);

Is, I tried to apply the outlining here. I thought that, maybe it is possible to apply the outlining at only one side so it would look like a shadow, but I failed on it either.

Here's the code I have so far:

var scalingRatio = GetWatermarkScalingRatio(image);
var scaledFontSize = (int)Math.Ceiling(scalingRatio * textProperties.Size);
var fontFamily = textProperties.Font ?? DefaultFont;

var alpha = (int)(255.0f * textProperties.Opacity / 100.0f);
var color = Color.FromArgb(alpha, Color.White);

using (var gr = Graphics.FromImage(image))
using (var font = new Font(fontFamily, scaledFontSize, DefaultFontStyle, GraphicsUnit.Pixel))
using (var semiTransparentBrush = new SolidBrush(color))
using (var shadowBrush = new SolidBrush(_shadowBrushColor))
{
    var textSize = gr.MeasureString(textProperties.Message, font);

    double wmWidth = textSize.Width;
    double wmHeight = textSize.Height;
    double angleRadian = (DefaultTextAngle % 360 / 180.0) * Math.PI;

    var offset = GetTxtWatermarkOffset(textProperties.Position, image, wmWidth, wmHeight, angleRadian);

    using var m = gr.Transform;
    m.RotateAt(DefaultTextAngle, new PointF(offset.X, offset.Y), MatrixOrder.Append);
    gr.Transform = m;

    gr.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;

    gr.DrawString(textProperties.Message, font, shadowBrush, offset.X, offset.Y + DefaultShadowOffset);
    gr.DrawString(textProperties.Message, font, semiTransparentBrush, offset.X, offset.Y);
    
    gr.ResetTransform();
}
Gleb
  • 82
  • 4
  • What exactly are the colors and what is a (bad) result? In general this seems ok; GDI+ doesn't have helpful blending modes. – TaW Jul 17 '20 at 21:01
  • @TaW Text - white, semitransparent: `Color.FromArgb(130, Color.White)`, Shadow - black, semitransparent: `Color.FromArgb(150, Color.Black)` A bad result is a result where I see the shadow through the text. Yes, text should be transparent, but I want shadow only around it, not behind. – Gleb Jul 17 '20 at 21:20
  • While a __real__ shadow __would__ be visible behind a real semi-transparent area I can see what you want. Tweaking strokeA to 190 and shadowA to 64 may help but won't quite do. For the real thing you may need more involved code. - I would prepare a bitmap with the watermark and DrawImage it. For the Image drawText the stroke with A=255 over the shadow with maybe A=190 and then take alpha down for the whole image. [SetAlpha Example](https://stackoverflow.com/questions/44749869/picturebox-slider-control-transparency/44758640#44758640) – TaW Jul 17 '20 at 22:37
  • @TaW yes, I agree with you, but that's not my requirement, so I should find a way of doing this. I hoped there's more straightforward solution. FE guys just do `text-shadow: 0 2px 3px rgba(0, 0, 0, 0.6);` for it. Okay, thank you for your advise. I'll try to play around with what I got so far. – Gleb Jul 17 '20 at 23:41
  • @TaW I tried it with `DrawImage()`, but the bitmap image with my watermark won't show up. I am really bad with graphics. Could you, please, post a simple example as an answer so I'll be able to get it into work and mark it as an answer? – Gleb Jul 19 '20 at 17:38
  • I don't answer on SO any longer. Example : `void drawWatermark(Graphics g, Point pt, string text, Font f, Color shadow, int dist )` – TaW Jul 19 '20 at 18:06
  • `{ Size sz = Size.Round(g.MeasureString(text, f)); using (Bitmap bmp = new Bitmap(sz.Width + 2 + dist, sz.Height + 2 + dist)) { using (Graphics gr = Graphics.FromImage(bmp)) using (SolidBrush shBr = new SolidBrush(shadow)) { gr.Clear(Color.Transparent); gr.DrawString(text, f, shBr, dist + 2, dist + 2); gr.DrawString(text, f, Brushes.White, 2, 2); } using (Bitmap bmp2 = SetAlpha(bmp, 111)) g.DrawImage(bmp2, pt); } }` – TaW Jul 19 '20 at 18:07
  • I call is e.g. in a Paint event: `private void pictureBox1_Paint(object sender, PaintEventArgs e) { Font f = new Font("Consolas", 25f); drawWatermark(e.Graphics, new Point(22, 22), textBox1.Text, f, Color.Silver, 1); }` but you can call it where you want, of course.. – TaW Jul 19 '20 at 18:08
  • 1
    @TaW, thank you very much! That did it! – Gleb Jul 20 '20 at 00:58

0 Answers0