1

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)

they should look like this Colorbars

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 My broken color bars

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;
}

red cololr bars

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 testCard2Small

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

LimetaPeta
  • 67
  • 5
  • Is there a reason why you use pixels? Why don't you use a `LinearGradientBrush` for example? – Siegfried.V Nov 21 '22 at 18:10
  • I am trying to mimic the CRT display, as I am trying to decode composite video, and first I need to make my screen display colors properly before I can continue, also all modern televisions display pixels when decoding composite video, so I need to make my screen compatible for that) – LimetaPeta Nov 21 '22 at 18:12
  • @LimePeta, I created a project to make the try, but I still meet the same problem as you, looking forward, then will tell you if I find something – Siegfried.V Nov 21 '22 at 19:56
  • 1
    [How to make colors in a Palette transparent when drawing 8bpp Bitmaps](https://stackoverflow.com/a/65498663/7444103) (first snippet - you have 7 columns and 1 row) – Jimi Nov 21 '22 at 19:58
  • So you want the 7 colors in your image only? And mix other colors from them in adjacent pixels? Then you need a palette with these 7 colors and set only their 7 indices into the bitmap pixels. – TaW Nov 21 '22 at 20:22
  • OK, let's make that 8 colors. And the last time I met such a system (in early games and in CGA and EGA) there were another 8 colors which were more subdued, ie either darker or for black brighter, I think.. What will you actual input be/look like= – TaW Nov 21 '22 at 20:51
  • This is a standard NTSC/PAL testcard, I need enough colors to cover all the color options and that I can display PAL/NTSC/SECAM signal corecly, I guess if this color bars are displayed corecly, then I am good to go right? – LimetaPeta Nov 21 '22 at 21:22
  • Thanks @Jimi, very useful link, I also was trying to do that, but I now understand much better with it. – Siegfried.V Nov 22 '22 at 09:10

0 Answers0