Search code examples
.netvb.netgraphicssystem.drawingdrawing2d

Merge an image, a background and Text into a single image


I found this code here:

Private _BackgroundColours As New List(Of String)() From { _
    "339966", _
    "3366CC", _
    "CC33FF", _
    "FF5050" _
}

Public Function GenerateRactangle(firstName As String, lastName As String) As MemoryStream
    Dim imgSize() As Integer = {800, 800}
    Dim avatarString As String = String.Format("{0}{1}", firstName(0), lastName(0)).ToUpper()
    Dim bgColour = _BackgroundColours(New Random().[Next](0, _BackgroundColours.Count - 1))
    Dim bmp As Bitmap = New Bitmap(imgSize(0), imgSize(1))
    Dim sf As StringFormat = New StringFormat()
    Dim ms As MemoryStream = New MemoryStream()
    Dim font As Font = New Font("Arial", 172, FontStyle.Bold, GraphicsUnit.Pixel)
    Dim graphics__1 As Graphics = Nothing

    sf.Alignment = StringAlignment.Center
    sf.LineAlignment = StringAlignment.Center

    graphics__1 = Graphics.FromImage(bmp)
    graphics__1.Clear(DirectCast(New ColorConverter().ConvertFromString("#" + bgColour), Color))
    graphics__1.SmoothingMode = SmoothingMode.AntiAlias
    graphics__1.TextRenderingHint = TextRenderingHint.AntiAliasGridFit
    graphics__1.DrawString(avatarString, font, New SolidBrush(Color.WhiteSmoke), New RectangleF(0, 0, imgSize(0), imgSize(1)), sf)
    graphics__1.Flush()
    bmp.Save(ms, ImageFormat.Png)

    Return ms
End Function

On stackoverflow and it works great. However, I am in need of using a transparent PNG image in the background with the color changing background color.

What it currently looks like:

enter image description here

What I am looking for it to look like:

enter image description here

With the PNG image being this that was added:

enter image description here

I am hoping someone with more knowledge about the graphics calls can let me know how to go about doing this.


Solution

  • The method you found leaves at least the Font and Grahics objects undisposed, so if that is used as a factory to process numerous images, it will leak. Things like picking a random back color might be better left to the calling code, and a memstream seems an odd return type choice.

    A general method to create a background, overlay a PNG and apply the text to it:

    Private Function CreateLabeledAvatar(av As Image, bg As Color, text As String) As Image
    
        ' fixed size?
        Dim bmp As New Bitmap(250, 250)
        Using g As Graphics = Graphics.FromImage(bmp)
            Using br As New SolidBrush(bg)
                g.FillRectangle(br, 0, 0, bmp.Width, bmp.Height)
            End Using
            g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
            g.CompositingQuality = CompositingQuality.HighQuality
            g.TextRenderingHint = TextRenderingHint.AntiAlias
            g.SmoothingMode = SmoothingMode.HighQuality
            g.DrawImage(av, 0, 0, bmp.Width, bmp.Height)
    
            ' lastly the text, centred on the new image
            ' could also draw to the AV passed to center on IT
            Using fnt As New Font("Arial", 32, FontStyle.Bold, GraphicsUnit.Pixel)
                TextRenderer.DrawText(g, text, fnt, New Rectangle(0, 0, 250, 250), 
                      Color.WhiteSmoke,
                      TextFormatFlags.HorizontalCenter Or TextFormatFlags.VerticalCenter)
            End Using
    
        End Using
    
        Return bmp
    End Function
    

    Sample usage:

    Dim av = Image.FromFile("C:\temp\maleAV.png")
    Dim bg = Color.FromArgb(62, 103, 207)
    
    Dim newImg = CreateLabeledAvatar(av, bg, "BB")
    pb1.Image = newImg
    
    av.Dispose()
    

    When your code is done with it, newImg should also be disposed.

    There are other params you might want to be passed or set such as the desired size, font size and maybe even the text color. Passing any more, though and I would make it a class, so if it is used to processed a lot of them, many params could be set once.

    Result:

    enter image description here

    The image created was 250,250, it is displayed in a 150x150 PBox