Skip to main content

Draw multiple text overlays using the OnVideoFrameBuffer event

Products: Video Capture SDK .Net, Video Edit SDK .Net, Media Player SDK .Net

Sample code

If video effect API to draw text overlay is not enough, you can draw overlay manually in OnVideoFrameBuffer, using Bitmap and Graphics classes. The bitmap should be 32-bit (with an alpha channel).

A version of code (simple) without an update of text or font on the fly:

        // Image
private Bitmap logoImage = null;

// Image RGB32 buffer
private IntPtr logoImageBuffer = IntPtr.Zero;
private int logoImageBufferSize = 0;

private string text1 = "Hello World";
private string text2 = "Hey-hey";
private string text3 = "Ocean of pancakes";

private void SDK_OnVideoFrameBuffer(Object sender, VideoFrameBufferEventArgs e)
{
// draw text to image
if (logoImage == null)
{
logoImage = new Bitmap(e.Frame.Width, e.Frame.Height, PixelFormat.Format32bppArgb);

using (var grf = Graphics.FromImage(logoImage))
{
// antialiasing mode
grf.TextRenderingHint = TextRenderingHint.AntiAlias;

// drawing mode
grf.InterpolationMode = InterpolationMode.HighQualityBicubic;

// smoothing mode
grf.SmoothingMode = SmoothingMode.HighQuality;

// text 1
var brush1 = new SolidBrush(Color.Blue);
var font1 = new Font("Arial", 30, FontStyle.Regular);
grf.DrawString(text1, font1, brush1, 100, 100);

// text 2
var brush2 = new SolidBrush(Color.Red);
var font2 = new Font("Times New Roman", 35, FontStyle.Strikeout);
grf.DrawString(text2, font2, brush2, e.Frame.Width / 2, e.Frame.Height / 2);

// text 3
var brush3 = new SolidBrush(Color.Green);
var font3 = new Font("Verdana", 40, FontStyle.Italic);
grf.DrawString(text3, font3, brush3, 200, 200);
}
}

// create image buffer if not allocated or have zero size
if (logoImageBuffer == IntPtr.Zero || logoImageBufferSize == 0)
{
if (logoImageBuffer == IntPtr.Zero)
{
logoImageBufferSize = ImageHelper.GetStrideRGB32(logoImage.Width) * logoImage.Height;
logoImageBuffer = Marshal.AllocCoTaskMem(logoImageBufferSize);
}
else
{
logoImageBufferSize = ImageHelper.GetStrideRGB32(logoImage.Width) * logoImage.Height;

Marshal.FreeCoTaskMem(logoImageBuffer);
logoImageBuffer = Marshal.AllocCoTaskMem(logoImageBufferSize);
}

ImageHelper.BitmapToIntPtr(logoImage, logoImageBuffer, logoImage.Width, logoImage.Height,
PixelFormat.Format32bppArgb);
}

// Draw image
FastImageProcessing.Draw_RGB32OnRGB24(logoImageBuffer, logoImage.Width, logoImage.Height, e.Frame.Data, e.Frame.Width, e.Frame.Height, 0, 0);

e.UpdateData = true;
}

A version of code with updating on the fly by a button click:

        // Image
Bitmap logoImage = null;

// Image RGB32 buffer
IntPtr logoImageBuffer = IntPtr.Zero;
int logoImageBufferSize = 0;

// text settings
string text1 = "Hello World";
Font font1 = new Font("Arial", 30, FontStyle.Regular);
SolidBrush brush1 = new SolidBrush(Color.Blue);

string text2 = "Hey-hey";
Font font2 = new Font("Times New Roman", 35, FontStyle.Strikeout);
SolidBrush brush2 = new SolidBrush(Color.Red);

string text3 = "Ocean of pancakes";
Font font3 = new Font("Verdana", 40, FontStyle.Italic);
SolidBrush brush3 = new SolidBrush(Color.Green);

// update flag
bool textUpdate = false;
object textLock = new object();

// Update text overlay, index is [1..3]
void UpdateText(int index, string text, Font font, SolidBrush brush)
{
lock (textLock)
{
textUpdate = true;
}

switch (index)
{
case 1:
text1 = text;
font1 = font;
brush1 = brush;
break;
case 2:
text2 = text;
font2 = font;
brush2 = brush;
break;
case 3:
text3 = text;
font3 = font;
brush3 = brush;
break;
default:
return;
}
}

private void SDK_OnVideoFrameBuffer(Object sender, VideoFrameBufferEventArgs e)
{
lock (textLock)
{
if (textUpdate)
{
logoImage.Dispose();
logoImage = null;
}

// draw text to image
if (logoImage == null)
{
logoImage = new Bitmap(e.Frame.Width, e.Frame.Height, PixelFormat.Format32bppArgb);

using (var grf = Graphics.FromImage(logoImage))
{
// antialiasing mode
grf.TextRenderingHint = TextRenderingHint.AntiAlias;

// drawing mode
grf.InterpolationMode = InterpolationMode.HighQualityBicubic;

// smoothing mode
grf.SmoothingMode = SmoothingMode.HighQuality;

// text 1
grf.DrawString(text1, font1, brush1, 100, 100);

// text 2
grf.DrawString(text2, font2, brush2, e.Frame.Width / 2, e.Frame.Height / 2);

// text 3
grf.DrawString(text3, font3, brush3, 200, 200);
}
}

// create image buffer if not allocated or have zero size
if (logoImageBuffer == IntPtr.Zero || logoImageBufferSize == 0)
{
if (logoImageBuffer == IntPtr.Zero)
{
logoImageBufferSize = ImageHelper.GetStrideRGB32(e.Frame.Width) * e.Frame.Height;
logoImageBuffer = Marshal.AllocCoTaskMem(logoImageBufferSize);
}
else
{
logoImageBufferSize = ImageHelper.GetStrideRGB32(e.Frame.Width) * e.Frame.Height;

Marshal.FreeCoTaskMem(logoImageBuffer);
logoImageBuffer = Marshal.AllocCoTaskMem(logoImageBufferSize);
}

ImageHelper.BitmapToIntPtr(logoImage, logoImageBuffer, logoImage.Width, logoImage.Height,
PixelFormat.Format32bppArgb);
}

if (textUpdate)
{
textUpdate = false;
ImageHelper.BitmapToIntPtr(logoImage, logoImageBuffer, logoImage.Width, logoImage.Height,
PixelFormat.Format32bppArgb);
}

// Draw image
FastImageProcessing.Draw_RGB32OnRGB24(logoImageBuffer, logoImage.Width, logoImage.Height, e.Frame.Data, e.Frame.Width,
e.Frame.Height, 0, 0);

e.UpdateData = true;
}
}

private void btUpdateText1_Click(object sender, EventArgs e)
{
UpdateText(1, "Hello world", new Font("Arial", 48, FontStyle.Underline),
new SolidBrush(Color.Aquamarine));
}

Required redists

  • SDK redist

Visit our GitHub page to get more code samples.