Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: (Help) User draw/paint pixels by pixels?  (Read 15075 times)

0 Members and 3 Guests are viewing this topic.

Jackieryder

  • Newbie
  • *
  • Posts: 15
    • View Profile
    • Email
(Help) User draw/paint pixels by pixels?
« 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);
                }
            }
        }
 
« Last Edit: March 30, 2015, 09:26:34 am by Jackieryder »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
Re: (Help) User draw/paint pixels by pixels?
« Reply #1 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().
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Jackieryder

  • Newbie
  • *
  • Posts: 15
    • View Profile
    • Email
Re: (Help) User draw/paint pixels by pixels?
« Reply #2 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

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: (Help) User draw/paint pixels by pixels?
« Reply #3 on: March 30, 2015, 09:37:24 am »
As eXpl0it3r said, use Texture.Update.
Laurent Gomila - SFML developer

zsbzsb

  • Hero Member
  • *****
  • Posts: 1409
  • Active Maintainer of CSFML/SFML.NET
    • View Profile
    • My little corner...
    • Email
Re: (Help) User draw/paint pixels by pixels?
« Reply #4 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.
Motion / MotionNET - Complete video / audio playback for SFML / SFML.NET

NetEXT - An SFML.NET Extension Library based on Thor

Jackieryder

  • Newbie
  • *
  • Posts: 15
    • View Profile
    • Email
Re: (Help) User draw/paint pixels by pixels?
« Reply #5 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?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: (Help) User draw/paint pixels by pixels?
« Reply #6 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.
Laurent Gomila - SFML developer

Jackieryder

  • Newbie
  • *
  • Posts: 15
    • View Profile
    • Email
Re: (Help) User draw/paint pixels by pixels?
« Reply #7 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?

dabbertorres

  • Hero Member
  • *****
  • Posts: 505
    • View Profile
    • website/blog
Re: (Help) User draw/paint pixels by pixels?
« Reply #8 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,
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.

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: (Help) User draw/paint pixels by pixels?
« Reply #9 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.
« Last Edit: March 30, 2015, 11:50:15 pm by Nexus »
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Jackieryder

  • Newbie
  • *
  • Posts: 15
    • View Profile
    • Email
Re: (Help) User draw/paint pixels by pixels?
« Reply #10 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

Jackieryder

  • Newbie
  • *
  • Posts: 15
    • View Profile
    • Email
Re: (Help) User draw/paint pixels by pixels?
« Reply #11 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.

zsbzsb

  • Hero Member
  • *****
  • Posts: 1409
  • Active Maintainer of CSFML/SFML.NET
    • View Profile
    • My little corner...
    • Email
Re: (Help) User draw/paint pixels by pixels?
« Reply #12 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)
« Last Edit: April 03, 2015, 05:16:43 am by zsbzsb »
Motion / MotionNET - Complete video / audio playback for SFML / SFML.NET

NetEXT - An SFML.NET Extension Library based on Thor

Jackieryder

  • Newbie
  • *
  • Posts: 15
    • View Profile
    • Email
Re: (Help) User draw/paint pixels by pixels?
« Reply #13 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?

zsbzsb

  • Hero Member
  • *****
  • Posts: 1409
  • Active Maintainer of CSFML/SFML.NET
    • View Profile
    • My little corner...
    • Email
Re: (Help) User draw/paint pixels by pixels?
« Reply #14 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.
Motion / MotionNET - Complete video / audio playback for SFML / SFML.NET

NetEXT - An SFML.NET Extension Library based on Thor