Add text label with semi transparent background to an image using Magick.NET

408 Views Asked by At

I have some C# code that adds a simple text overlay with a border and semi-transparent background to an image. It works great, but I'm trying to get an equivalent result using Magick.NET. (The straight C# code drops the XMP tags from the original image, and I haven't found a way to deal with that.) Magick.NET handles the XMP tags well, but I'm having trouble replicating the original output.

Original code follows:

using (Image i = Image.FromStream(stream))
{
  int width = i.Width;
  int height = i.Height;

  using (Graphics graphics =  Graphics.FromImage(i))
  {
    string measureString = "my string";
    Size stringSize = graphics.MeasureString(measureString, stringFont).ToSize();
      
    Point drawLocation = new Point(width - stringSize.Width - 15, height - stringSize.Height - 15);
    Rectangle rect = new Rectangle(drawLocation.X, drawLocation.Y, stringSize.Width, stringSize.Height);
      
    graphics.DrawRectangle(blackPen, rect);
    graphics.FillRectangle(fillBrush, rect);
    graphics.DrawString(measureString, stringFont, Brushes.Yellow, drawLocation);
  }
  i.Save(outputFolder + Path.GetFileName(imgFileName));
}

I cobbled this together based on the Magick.NET examples. This get close to what I'm looking for, but adding the border removes the transparency value, and I'm left with a dark gray background, instead of the transparency.

 var settings = new MagickReadSettings{
                Font = "Calibri",
                FillColor=MagickColors.Yellow,
                StrokeColor=MagickColors.Black,
                TextGravity = Gravity.Center,
                BackgroundColor = new MagickColor("#66666699"),
                BorderColor = MagickColors.Black,
                Height = 250, // height of text box
                Width = 680 // width of text box
            };

using (var image = new MagickImage(inputFile))
{
  using (var caption = new MagickImage($"caption:{myString}", settings))
  {
    //adding this border removes transparency
    // caption.BorderColor = MagickColors.Black;
    // caption.Border(1);

    image.Composite(caption, Gravity.Southeast, CompositeOperator.Over);
    image.Write(outputFile);
  }
}
2

There are 2 best solutions below

5
fmw42 On

In command line ImageMagick, this seems to work for me in that the background color is transparent gray. The following the result may be what you want:

convert -font ubuntu -fill yellow -stroke black -gravity center -background "#66666699" -bordercolor black -size 250x680 caption:"This Is Some Text" result.png

Enter image description here

Note: I used -background, not -backgroundcolor. Also BorderColor is not the color for the outline of the text. That is the stroke. You have not used BorderColor, since you have not specified the Border amount (as in -border in command line), which would outline the image rectangle and not the text.

0
user120675 On

Due to time constraints with my project, I took a slightly different path to make this work. I wound up creating the transparent overlay using my original .NET drawing code, and passing that as a memory stream to Magick.NET to handle the merge.

Workaround:

string measureString = "build custom string here";

using (var tmpStreamImg = new MemoryStream())
{
  // Call custom function to get length of my string
  System.Drawing.Size stringSize = MeasureString(measureString, stringFont).ToSize();
  Rectangle rect = new Rectangle(0, 0, stringSize.Width, stringSize.Height);

  using (Bitmap overlay = new Bitmap(rect.Width, rect.Height))
  {
    overlay.SetResolution(350, 350);
    using (Graphics overlayGraphic = Graphics.FromImage(overlay))
    {
      overlayGraphic.DrawRectangle(blackPen, rect);
      overlayGraphic.FillRectangle(fillBrush, rect);

      overlayGraphic.DrawString(measureString, stringFont, Brushes.Yellow, 3, 3);
    }
    overlay.Save(tmpStreamImg, ImageFormat.Png);
  }
  tmpStreamImg.Position= 0;

  using (var originalImage = new MagickImage(imgFileName))
  {
    using (var overlayImage = new MagickImage(tmpStreamImg))
    {
      originalImage.Composite(overlayImage, Gravity.Southeast, CompositeOperator.Over);
      originalImage.Write(outputFolder + Path.GetFileName(imgFileName));
    }
  }
}