SFML community forums

Bindings - other languages => DotNet => Topic started by: Jackieryder on March 30, 2015, 09:19:18 am

Title: (Help) User draw/paint pixels by pixels?
Post by: Jackieryder on March 30, 2015, 09:19:18 am
Hi, I'm trying to create a paint software of sort. I was able to do it with the following code, however it proved to be super slow and seem to be very very inefficient; do you guys have any suggestion on how to improve it or to make it faster?

In Constructor:
 
imagePixels = new byte[1612900]; // 1612900 = 635 * 635 * 4

drawn = new SFML.Graphics.Image(635, 635, imagePixels);
drawnImage = new Texture(drawn);



In drawing loop: (toDraw is a sprite)

 
DrawWindow.DispatchEvents();
DrawWindow.Clear(SFML.Graphics.Color.Black);
ToDraw.Texture = drawnImage;
DrawWindow.Draw(ToDraw);
DrawWindow.Display();
 


In MouseMove:

 
private void OnMouseMove(object sender, MouseMoveEventArgs e)
        {
            RenderWindow window = (RenderWindow)sender;
            if (Mouse.IsButtonPressed(Mouse.Button.Left))
            {
                if (Mouse.GetPosition(DrawWindow).X >= 0 && Mouse.GetPosition(DrawWindow).Y > 0 && Mouse.GetPosition(DrawWindow).X < 635 && Mouse.GetPosition(DrawWindow).Y < 635)
                {
                    imagePixels[(Mouse.GetPosition(DrawWindow).Y * 635 + Mouse.GetPosition(DrawWindow).X) * 4] = MainViewer.Instance.PickedColor[0].R; // R?
                    imagePixels[(Mouse.GetPosition(DrawWindow).Y * 635 + Mouse.GetPosition(DrawWindow).X) * 4 + 1] = MainViewer.Instance.PickedColor[0].G; // G?
                    imagePixels[(Mouse.GetPosition(DrawWindow).Y * 635 + Mouse.GetPosition(DrawWindow).X) * 4 + 2] = MainViewer.Instance.PickedColor[0].B; // B?
                    imagePixels[(Mouse.GetPosition(DrawWindow).Y * 635 + Mouse.GetPosition(DrawWindow).X) * 4 + 3] = MainViewer.Instance.PickedColor[0].A; // A?
                    drawn = new SFML.Graphics.Image(635, 635, imagePixels);
                    drawnImage = new Texture(drawn);
                }
            }
        }
 
Title: Re: (Help) User draw/paint pixels by pixels?
Post by: eXpl0it3r on March 30, 2015, 09:23:54 am
Please use the [code=csharp][/code] tags when posting code.

I will never be very performant since you'll end up uploading all the pixels from RAM to the VRAM.
What you could try is to upload your array directly instead of first creating a copy of it by using sf::Texture::update().
Title: Re: (Help) User draw/paint pixels by pixels?
Post by: Jackieryder on March 30, 2015, 09:28:50 am
So how does Microsoft Paint or other drawing software do their drawing method?

I can't come up with any idea of how to do this efficiently at all
Title: Re: (Help) User draw/paint pixels by pixels?
Post by: Laurent on March 30, 2015, 09:37:24 am
As eXpl0it3r said, use Texture.Update.
Title: Re: (Help) User draw/paint pixels by pixels?
Post by: zsbzsb on March 30, 2015, 01:41:30 pm
So how does Microsoft Paint or other drawing software do their drawing method?

How does MS Paint work? They use GDI+ which doesn't involve touching the GPU (its all rendered on the software side). GDI+ is really how WinForms are drawn.
Title: Re: (Help) User draw/paint pixels by pixels?
Post by: Jackieryder on March 30, 2015, 10:43:50 pm
I tried to use Texture.Update(byte[] pixel);

They still don't seem to draw every single pixel that I move my mouse to. For example if i was to move my mouse over a distance of 20 pixels, the software only draw the pixel at 1, 3, 5, 7, 9, 11, go on etc. And sometime this can be very sporadic.

Any other suggestion?
Title: Re: (Help) User draw/paint pixels by pixels?
Post by: Laurent on March 30, 2015, 10:46:57 pm
Of course your move cannot pass exactly through all the pixels from starting point to end point. Draw lines, not single points.
Title: Re: (Help) User draw/paint pixels by pixels?
Post by: Jackieryder on March 30, 2015, 10:56:15 pm
So if I was to draw line, I would do something kinda like:

while MouseDown, update every second (or half a second while mouse down)

get coordinate of starting point
get coordinate of the mouse after half a second or a second.
Draw a line betwene those 2 location

if mouse goes up before those time, get coordinate of the pixel at the mouse up time and draw line?
Title: Re: (Help) User draw/paint pixels by pixels?
Post by: dabbertorres on March 30, 2015, 11:18:11 pm
while MouseDown, update every second (or half a second while mouse down)

??

No wonder you're only getting some pixels. What if the user moves the mouse really fast? You would miss a lot of pixels.

For drawing a line, I would:
store the start position on MouseDown.
then
store the end position on MouseUp.

Then calculate the pixels in between:
using Bresenham's Line Algorithm (https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm),
or
use a sf::RectangleShape of width 1, using it's functions getPointCount() and getPoint() to get all the points.

Personally (assuming you're going to want to save these creations), I would have an sf::Image, and use it's setPixel() function for drawing.
Then update the sf::Texture from that.
Title: Re: (Help) User draw/paint pixels by pixels?
Post by: Nexus on March 30, 2015, 11:31:52 pm
Why so complicated?

Create a sf::VertexArray with sf::LinesStrip primitive. If the line is not continuous, use sf::Lines instead. Every frame (not every half or full second!) you sample the mouse position and insert a new vertex into the vertex array. Then you draw it.

In case you don't want to keep a data structure for the lines drawn so far, you can render the lines to a sf::RenderTexture.
Title: Re: (Help) User draw/paint pixels by pixels?
Post by: Jackieryder on March 31, 2015, 12:46:50 am
Dabbestore, you took my quote out of context

I said that when I was asking if that is how I should do the line, not for drawing pixels.

I had tried drawing/updating pixel every frame (60 frame every second) AND draw 1000 frames per second, same result. It is the input not taking the information fast enough (that why i asked about drawing lines afterward).

I'm still relatively new to SFML, so I'm not sure about VertexArray or RenderTexture, etc
Title: Re: (Help) User draw/paint pixels by pixels?
Post by: Jackieryder on April 03, 2015, 01:04:19 am
Update: This is what I came up with, somehow it's still not draw like i expected:

private void OnMouseMove(object sender, MouseMoveEventArgs e)
        {
            RenderWindow window = (RenderWindow)sender;
            if (Mouse.IsButtonPressed(Mouse.Button.Left))
            {
                Vector2i pos = Mouse.GetPosition(DrawWindow);
                if (pos.X >= 0 && pos.Y > 0 && pos.X < 635 && pos.Y < 635)
                {
                    distanceX = curX - pos.X;
                    distanceY = curY - pos.Y;
                    float len = (float)Math.Sqrt(distanceX * distanceX + distanceY * distanceY);
                    Console.WriteLine("\nCurX :" + curX + " CurY: " + curY + " posX: " + pos.X + " posY: " + pos.Y);
                    Console.WriteLine("Distance X: " + distanceX + " Distance Y: " + distanceY);
                    Console.WriteLine("Length: " + len);

                    rectLine.Size = new Vector2f(len, 10);
                    if (distanceX != 0)
                    {
                        rectLine.Rotation = (float)(Math.Atan2(distanceY, distanceX) * (180 / Math.PI));
                        Console.WriteLine("Rotation: " + rectLine.Rotation);
                    }
                    rectLine.Position = new Vector2f(curX, curY);

                    for (int i = 0; i < rectLine.GetPointCount(); i++)
                    {
                        imagePixels[(int)(rectLine.GetPoint((uint)i).Y * 635 + rectLine.GetPoint((uint)i).X) * 4] = MainViewer.Instance.PickedColor[0].R;
                        imagePixels[(int)(rectLine.GetPoint((uint)i).Y * 635 + rectLine.GetPoint((uint)i).X) * 4 + 1] = MainViewer.Instance.PickedColor[0].G;
                        imagePixels[(int)(rectLine.GetPoint((uint)i).Y * 635 + rectLine.GetPoint((uint)i).X) * 4 + 2] = MainViewer.Instance.PickedColor[0].B;
                        imagePixels[(int)(rectLine.GetPoint((uint)i).Y * 635 + rectLine.GetPoint((uint)i).X) * 4 + 3] = MainViewer.Instance.PickedColor[0].A;
                    }

                    drawnImage.Update(imagePixels);

                    // Definitely don't change these with every move
                    /*curX = Mouse.GetPosition(DrawWindow).X;
                    curY = Mouse.GetPosition(DrawWindow).Y;*/

                }
            }
        }


private void OnMousePress(object sender, MouseButtonEventArgs e)
        {
            RenderWindow window = (RenderWindow)sender;
            curX = Mouse.GetPosition(DrawWindow).X;
            curY = Mouse.GetPosition(DrawWindow).Y;
           
        }
 


I am not sure where the problem is. I checked my math multiple time, i'm fairly positive that it's right. The software do not draw a line at all, it still draw pixel by pixel of sort and for some reason, only draw at the very first line (y = 0) of the render window.
Title: Re: (Help) User draw/paint pixels by pixels?
Post by: zsbzsb on April 03, 2015, 05:01:31 am
If you want to change a texture why don't you just draw to it? Updating individual pixels is extremely slow operation even if you do it right (what you are trying to do doesn't make much sense).

Another thing, you should listen to Nexus when he says....

Why so complicated?

...heck, he even gave you a just about perfect implementation.

Look what I did in 3mins without any complex math (based on what Nexus suggested)....

(click to show/hide)
Title: Re: (Help) User draw/paint pixels by pixels?
Post by: Jackieryder on April 03, 2015, 05:19:17 am
I didn't do what he suggested because I wasn't sure if I would be able to erase pixel by pixel if the user wanted to.

If I was to do what Nexus said, how would I click and erase whatever was at the location my mouse clicked on?
Title: Re: (Help) User draw/paint pixels by pixels?
Post by: zsbzsb on April 03, 2015, 12:47:07 pm
By just drawing in the same way (just change the primitive type) the background on top of everything else and bam, you got an erase method.
Title: Re: (Help) User draw/paint pixels by pixels?
Post by: Jackieryder on April 03, 2015, 04:14:06 pm
Would that erase at specific spot and not the whole thing? Like the eraser from Paint or Photoshop/GIMP?

What kind of primitive wuld I switch it to?
Title: Re: (Help) User draw/paint pixels by pixels?
Post by: zsbzsb on April 05, 2015, 03:08:14 pm
Would that erase at specific spot and not the whole thing? Like the eraser from Paint or Photoshop/GIMP?

It would erase where you draw it. Think of it as layers, once you draw the eraser on top everything below it will no longer exist. You can take my basic example and change it to draw everything to a render texture and then not store the primitives. Once you draw the eraser everything below it is gone.

Quote
What kind of primitive wuld I switch it to?

You would probably want 2 triangles (total of 6 vertices). Start drawing the eraser square at the begin point and end the other corner at the current mouse position. Really though, you need to play with it and try it. Having us do it all for you takes the fun out of it.  ;)
Title: Re: (Help) User draw/paint pixels by pixels?
Post by: Jackieryder on April 10, 2015, 06:10:30 am
Sorry, I think I need a little bit more clarification. By erase/draw on top of the layer, do you mean the eraser is basically the background color drawing on top of the image, or it actually remove the vertex at that point?

I get what you are trying to do with the 2 triangle thing (or atleast I think I do). What you are trying to do is creating a rectangle (or square) that will draw things in the shape of a square that take more than one pixel. I think I can do that. However I'm still not sure about what i just asked above, as far as I understand, by doing what you said I'm just drawing the color white in the shape of a square over them, which in turn of course erase the bottom layer. But that isn't what I'm looking for however. When I erase something, I want the pixel at that location to be gone, as oppose to being recolored.