I would like to display standard TV Colorbars in my WinForms app,
I am generating them on the fly using this function (I translated this from C sources)
public void GeneratePixel(byte[] buffer, int width, int height)
{
int[] bars = new int[]{
0x000000, //Black
0x0000BF, //Blue
0xBF0000, //Red
0xBF00BF, //Magenta
0x00BF00, //Green
0x00BFBF, //Cyan
0xBFBF00, //Yellow-green
0xFFFFFF //White
};
int c = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int pixelPosition = y * width + x;
if (y < height)
{
/* colour bars */
c = 7 - x * 8 / width;
c = bars[c];
}
buffer[pixelPosition] = (byte)c;
}
}
}
the output from this function is 24bit array with colors like this: 0xRRGGBB eg: 0xFFB0C0 is 0xFF for red, 0xB0 for green, 0xC0 for blue. (this is just an example how colors are packed)
so I tried to draw this exacly as on the picture with Bitmap
VideoWindow.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ColorBars
{
public partial class VideoWindow : Form
{
int _pictureWidth = 400;
int _pictureHeight = 625;
private int _oldVideoWindowWidth;
private int _oldVideoWindowHeight;
public VideoWindow()
{
InitializeComponent();
}
public void DrawFrame(byte[] buf, int width, int height)
{
this.videoPanel.BufferToBitmap(buf, width, height);
this.videoPanel.Refresh();
}
private void VideoWindows_ResizeBegin(object sender, EventArgs e)
{
Control control = (Control)sender;
this._oldVideoWindowWidth = control.Size.Width;
this._oldVideoWindowHeight = control.Size.Height;
}
private void VideoWindows_Resize(object sender, EventArgs e)
{
Control control = (Control)sender;
int width = control.Size.Width;
int height = control.Size.Height;
if (this._oldVideoWindowWidth != width && this._oldVideoWindowHeight == height)
{
height = width / 4 * 3;
}
else if (this._oldVideoWindowWidth == width && this._oldVideoWindowHeight != height)
{
width = height / 3 * 4;
}
else
{
height = width / 4 * 3;
}
control.Size = new Size(width, height);
}
public void GeneratePixel(byte[] buffer, int width, int height)
{
int[] bars = new int[]{
0x000000, //Black
0x0000BF, //Blue
0xBF0000, //Red
0xBF00BF, //Magenta
0x00BF00, //Green
0x00BFBF, //Cyan
0xBFBF00, //Yellow-green
0xFFFFFF //White
};
int c = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int pixelPosition = y * width + x;
if (y < height)
{
/* colour bars */
c = 7 - x * 8 / width;
c = bars[c];
}
buffer[pixelPosition] = (byte)c;
}
}
}
private void DisplayUpdateTimer_Tick(object sender, EventArgs e)
{
byte[] videoBufferPtr = new byte[this._pictureWidth * this._pictureHeight];
GeneratePixel(videoBufferPtr, this._pictureWidth, this._pictureHeight);
DrawFrame(videoBufferPtr, this._pictureWidth, this._pictureHeight);
}
}
}
VideoWindow.Designer.cs
namespace ColorBars
{
partial class VideoWindow
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
private global::ColorBars.DrawScreen videoPanel;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.DisplayUpdateTimer = new System.Windows.Forms.Timer(this.components);
this.videoPanel = new ColorBars.DrawScreen();
this.SuspendLayout();
//
// DisplayUpdateTimer
//
this.DisplayUpdateTimer.Enabled = true;
this.DisplayUpdateTimer.Tick += new System.EventHandler(this.DisplayUpdateTimer_Tick);
//
// videoPanel
//
this.videoPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.videoPanel.Location = new System.Drawing.Point(2, 3);
this.videoPanel.Name = "videoPanel";
this.videoPanel.Size = new System.Drawing.Size(421, 323);
this.videoPanel.TabIndex = 0;
//
// VideoWindow
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.AutoValidate = System.Windows.Forms.AutoValidate.EnableAllowFocusChange;
this.ClientSize = new System.Drawing.Size(424, 327);
this.Controls.Add(this.videoPanel);
this.DoubleBuffered = true;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
this.Name = "VideoWindow";
this.Text = "Video";
this.ResizeBegin += new System.EventHandler(this.VideoWindows_ResizeBegin);
this.Resize += new System.EventHandler(this.VideoWindows_Resize);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Timer DisplayUpdateTimer;
}
}
DrawScreen.cs
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ColorBars
{
internal class DrawScreen : UserControl
{
private Bitmap _buffer;
private Rectangle _rect;
public DrawScreen()
{
base.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
base.SetStyle(ControlStyles.DoubleBuffer, true);
base.SetStyle(ControlStyles.UserPaint, true);
base.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
base.UpdateStyles();
}
public void BufferToBitmap(byte[] buf, int width, int height)
{
if (buf == null)
{
return;
}
Rectangle rect = this._rect;
if (this._rect.Width != width || this._rect.Height != height)
{
this._rect = new Rectangle(0, 0, width, height);
}
if (this._buffer == null || this._buffer.Width != width || this._buffer.Height != height)
{
this._buffer = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
this._buffer.Palette = this.GetRGBColorPalette();
}
BitmapData bitmapData = this._buffer.LockBits(this._rect, ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
IntPtr scan = bitmapData.Scan0;
Marshal.Copy(buf, 0, scan, buf.Length);
this._buffer.UnlockBits(bitmapData);
}
//Create the right palette so your testcard will display properly
private ColorPalette GetRGBColorPalette()
{
ColorPalette palette = this._buffer.Palette;
Color[] entries = palette.Entries;
for (int i = 0; i <= 255; i++)
{
entries[i] = Color.FromArgb(255, i, i, i);
}
return palette;
}
protected override void OnPaint(PaintEventArgs e)
{
if (this._buffer == null)
{
return;
}
int width = this._rect.Width;
int height = this._rect.Height;
int x = 0;
int y = 0;
width -= x;
height -= y;
e.Graphics.DrawImage(this._buffer, e.ClipRectangle, x, y, width, height, GraphicsUnit.Pixel);
}
}
}
But for some reason, I cannot get colorbars to look right
This is how they look (they are not even proper grayscale
I tried to change my ColorPallete to be RGB but I am not able to do so
private ColorPalette GetRGBColorPalette()
{
ColorPalette palette = this._buffer.Palette;
Color[] entries = palette.Entries;
for (int i = 0; i <= 253; i++)
{
entries[i + 0] = Color.FromArgb(255, 255, i, i); //R
entries[i + 1] = Color.FromArgb(255, i, 255, i); //G
entries[i + 2] = Color.FromArgb(255, i, i, 255); //B
}
return palette;
}
I would just like to create some RGB color palette (not sure if this testcard is even RGB or some YUV or other wierd format), so I can display things on it (my screen) properly
and I have to use PixelFormat.Format8bppIndexed ,because if I use anything else like PixelFormat.Format24bppRgb (which seams most apropriate for PAL video) like this
public void BufferToBitmap(byte[] buf, int width, int height)
{
if (buf == null)
{
return;
}
Rectangle rect = this._rect;
if (this._rect.Width != width || this._rect.Height != height)
{
this._rect = new Rectangle(0, 0, width, height);
}
if (this._buffer == null || this._buffer.Width != width || this._buffer.Height != height)
{
this._buffer = new Bitmap(width, height, PixelFormat.Format24bppRgb);
//this._buffer.Palette = this.GetRGBColorPalette(); //I had to remove my ColorPallete because I get System.IndexOutOfRangeException with it
}
BitmapData bitmapData = this._buffer.LockBits(this._rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
IntPtr scan = bitmapData.Scan0;
Marshal.Copy(buf, 0, scan, buf.Length);
this._buffer.UnlockBits(bitmapData);
}
then not only that my TestCard still does not have colors, its also to small and it will not fit on the screen
and the screen has to have 400x625 resolution because thats the resolution for PAL video
Hopefully someone has an idea how can I display this testcard corecly on this window
Thanks for Anwesring and Best Regards