2

I am writing an emulator program, and the virtual display is supposed to be able to take in 3 bytes of color data and display the correct color pixel, similar to how a real screen works. But when I set up some scroll bars to test the generation of pixels nothing happens. Here is my code and a screenshot of the form:

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 TSC_Multi_System_Emulator
{

    public partial class Form1 : Form
    {
        private PictureBox Display = new PictureBox();
        string @emulationfolderpath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
        Bitmap screen = new Bitmap(@Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + @"\Resource_Folder\" + @"FirstFrame.bmp");
        int x = 0;
        int y = 0;

        public Form1()
        {
            InitializeComponent();

        }
        private void Form1_Load(object sender, System.EventArgs e) {
            // Dock the PictureBox to the form and set its background to black.
            Display.BackColor = Color.Black;
            // Connect the Paint event of the PictureBox to the event handler method.

            // Add the PictureBox control to the Form. 
            this.Controls.Add(Display);


        }
        public void DigitalGraphicsDisplay(int red, int green, int blue) {
            Graphics g = Display.CreateGraphics(); 
            screen.SetPixel(x, y, Color.FromArgb(red, green, blue));
            g.DrawImage(screen, 0, 0, screen.Width, screen.Height);
            g.Save();
            if (x < screen.Width)
            {
                x = x + 1;
            }
            else if (x == screen.Width)
            {
                x = 0;
                if (y < screen.Height)
                {
                    y = y + 1;
                }
                else if (y == screen.Height)
                {
                    y = 0;
                }
            }
        }



        private void button1_Click(object sender, EventArgs e){
            int rchannel = redControl.Value;
            int gchannel = greenControl.Value;
            int bchannel = blueControl.Value;             
            DigitalGraphicsDisplay(rchannel, gchannel, bchannel);
        }




    }
}

Screenshot of IDE

UPDATE:

The code is now working somewhat, but I can't test the code using just a test button. I had to use the exact code given to me in the first answer, which only displayed a gradient, I wonder what I am doing wrong... :(

public partial class Form1 : Form
{

    string @emulationfolderpath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
    Bitmap screen = new Bitmap(@Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + @"\Resource_Folder\" + @"FirstFrame.bmp");
    int x = 0;
    int y = 0;

    public Form1()
    {
        InitializeComponent();

    }
    private void Form1_Load(object sender, System.EventArgs e) {
        // Dock the PictureBox to the form and set its background to black.
        Display.BackColor = Color.Black;
        // Connect the Paint event of the PictureBox to the event handler method.

        // Add the PictureBox control to the Form. 
        this.Controls.Add(Display);


    }

    public void DigitalGraphicsDisplay(int red, int green, int blue)
    {
        if (Display.Image == null)
        {
            Bitmap NewBMP = new Bitmap(Display.ClientRectangle.Width, Display.ClientRectangle.Height);
            using (Graphics g = Graphics.FromImage(NewBMP))
            {
                g.Clear(Color.White);
            }
            Display.Image = NewBMP;
        }

        (Display.Image as Bitmap).SetPixel(x, y, Color.FromArgb(red, green, blue));

        Display.Invalidate();

        x++;

        if (x >= Display.Image.Width)
        {
            x = 0;
            y++;

            if (y >= Display.Image.Height)
            {
                y = 0;
            }
        }
    }


    private void button1_Click(object sender, EventArgs e){
        Boolean a = false;
        int b = 0;
        do
        {
            DigitalGraphicsDisplay(51, 153, 102);
            if (b == 10000)
            {
                a = true;
            }
            b = b + 1;
        } while (a);

    }





}

}

All I am getting is a white picturebox with nothing else in it... (The gradient code did work though)

  • 1
    Make the code example shorter. For instance, the Exit-Event is not necessary to understand and will not help to figure out your problem – Jannik Dec 21 '15 at 06:01

2 Answers2

4

It looks like you are trying to draw directly on the PictureBox control itself.

Instead you should have an Image assigned to the PictureBox and then draw on the image.

Try changing your code as shown below. (Including the click event for testing.)
Note, the PictureBox keeps the reference to the image directly so you don't need a separate screen image in your class, unless you have a different purpose for it.

Also, this uses Bitmap.SetPixel() which is an extremely slow way to set pixels. There is a much faster but slightly more complex way, in these other links:

Remember your button click will only draw one pixel at a time.
So be sure to look carefully:
enter image description here

Running my test code within the click event will yield this:
enter image description here

int x = 0;
int y = 0;
public void DigitalGraphicsDisplay(int red, int green, int blue)
{
    if (Display.Image == null)
    {
        Bitmap NewBMP = new Bitmap(Display.ClientRectangle.Width, Display.ClientRectangle.Height);
        using (Graphics g = Graphics.FromImage(NewBMP))
        {
            g.Clear(Color.White);
        }
        Display.Image = NewBMP;
    }

    (Display.Image as Bitmap).SetPixel(x, y, Color.FromArgb(red, green, blue));

    Display.Invalidate();

    x++;

    if (x >= Display.Image.Width)
    {
        x = 0;
        y++;

        if (y >= Display.Image.Height)
        {
            y = 0;
        }
    }
}

private void button1_Click(object sender, EventArgs e)
{
    // Temporary code to show that it works (Due to Bitmap.SetPixel() it will be slow)
    for (int I = 1; I < Display.ClientRectangle.Width * Display.ClientRectangle.Height; I++)
        DigitalGraphicsDisplay((I/255)%255, (I % Display.ClientRectangle.Width) % 255, 127);
}

UPDATE: Per your comment, try this sample code:

private void button1_Click(object sender, EventArgs e)
{ 
    Boolean a = true; 
    int b = 0; 
    do
    { 
        DigitalGraphicsDisplay(51, 153, 102); 

        if (b == 10000) 
        {
            a = false;
        } 

        b = b + 1; 
    } while (a); 
}
Community
  • 1
  • 1
Moon
  • 1,141
  • 2
  • 11
  • 25
  • I replaced my function with your code and it is still doing nothing... Also, why are you drawing a rectangle? I just wanted to draw a pixel at a time, is that the only way you could figure how to draw a pixel is a very small rectangle? I am a bit confused :( – Mitchell Spanheimer Dec 22 '15 at 00:43
  • See the updated code in my answer. I tested it and it works with screenshots showing what you should see. I switched the code back to SetPixel for simplicity, although it's much slower that way, see the links for a better way. Also, I simplified your x,y increment code. Also, your click event was only drawing a single pixel at a time, so, you need to look close. to see the change. see the screenshot for reference. I provided a test sample that changes the whole image, but it will be a bit slow due to the SetPixel. Don't forget to mark as answered if this works for you, thanks. – Moon Dec 22 '15 at 14:29
  • Your code works but my button test still won't work, here is the code for my button test that won't work: `private void button1_Click(object sender, EventArgs e){ Boolean a = false; int b = 0; do { DigitalGraphicsDisplay(51, 153, 102); if (b == 10000) { a = true; } b = b + 1; } while (a); }` All it displays is a white picturebox... – Mitchell Spanheimer Dec 26 '15 at 02:39
  • It was working, but you're loop is designed to only run one time and only ONE pixel was changed in the top left corner (It's hard to see). Change your code so that the `a` variable is set to `true` until `b == 10000` and then set it to `false`. While loops will only run when the condition == true. I will put a working copy of your code at the bottom of the answer above. PS: Don't forget to mark it answered once it works. We can still discuss your designs after if you like. – Moon Dec 26 '15 at 02:50
  • It works!!! Thank you so much for your help you really saved me a lot of trouble, I can't believe I messed up that while loop. It really does help when a fresh set of eyes checks for mistakes :) – Mitchell Spanheimer Dec 26 '15 at 03:01
  • Awesome. No Problem. You have a great Christmas! By the way, in case you're curious, you're entire loop can be reduced to this: `private void button1_Click(object sender, EventArgs e) { for(int i = 0; i < 10000; i++) DigitalGraphicsDisplay(51, 153, 102); }` – Moon Dec 26 '15 at 03:02
  • Thanks you too! Also, I think the top left of the picturebox on my form wasn't exactly 0,0 so I would have never saw the pixels until I clicked a few thousand times XD... – Mitchell Spanheimer Dec 26 '15 at 03:04
  • Yes, since you "Docked" it to FILL, it will take up the WHOLE form client area. Which is underneath your menu. You should consider, explicitly sizing the PictureBox and then setting the Anchor to `Top, Bottom, Left, Right` PS, see my prior comment I added some code. – Moon Dec 26 '15 at 03:08
1
    public void DigitalGraphicsDisplay(int red, int green, int blue) {
        Graphics g = Display.CreateGraphics(); 
        screen.SetPixel(x, y, Color.FromArgb(red, green, blue));
        g.DrawImage(screen, 0, 0, screen.Width, screen.Height);
        g.Save();

All possible mistakes in one go..

  • Never use CreateGraphics to draw persistent Graphics! Always either go for the Paint event or draw into the Image.

  • Graphics.Save does not save any drawn pixels. It saves the state of the Graphics object, which does not contain graphics but is a tool to write into a related bitmap. The state includes scale, rotation, smoothing-mode and then some..

You already write into the Bitmap so you can simply make it your new PictureBox.Image..

Or the PictureBox.BackgroundImage.

And, as I said, you can instead write on top of both that is onto the PBox's surface. For this use the Paint event, Invalidate to trigger it and class level variables to hold the necessary data..

The latter is for graphics that will change a lot, the two former ones are for changes that accumulate.

Control.CreateGraphics is for transient graphics only, like a rubber-band line or a cursor cross..

TaW
  • 53,122
  • 8
  • 69
  • 111
  • What do you mean by draw into the image, I think I have somewhat of an idea of what you mean but my code is still not working... Can you give me a code example? – Mitchell Spanheimer Dec 22 '15 at 01:02
  • It means to change the pixels of a bitmap by using the related Graphics object. The relation is created by `Graphics G = Graphics.FromImage(someBitmap)`.. You had such code in the original, longish version of the question, think. The difference of drawing __onto a Control's surface__ and __into a Bitmap__ is explained [here](http://stackoverflow.com/questions/27337825/picturebox-paintevent-with-other-method/27341797?s=2|0.0380#27341797) - To change pixels you either use `SetPixel`on a bitmap or `e.Graphics.FillRectangle(brush, x, y, 1, 1);` in a control's `Paint` event.. – TaW Dec 22 '15 at 02:47
  • Loking a little closer at your code I wonder what is supposed to be shown: One pixel per button-click, right? Should the previous pixel still be there or should it be cleared? – TaW Dec 22 '15 at 09:31
  • The previous pixel should still be there, it should just automatically advance to the next pixel value, (It should work asynchronously) – Mitchell Spanheimer Dec 26 '15 at 02:23