0

Phew, first burden here is to explain what I'm looking for within a short title.

Basically, I want to improve my existing pixel draw bot. Main objective: It needs to be fast.

I am fairly content with its performance for now, but it can be better. What I am doing in a nutshell is very primitive; to sum it up:

  • Get an image and process it to be black and white (by setting a specific ColorMatrix)
  • Loop through every pixel in the image and decide to click the mouse, if it's black, and skip if not.

My main (performance) concerns right now that I am not sure how to solve:

  • I am currently still clicking 500 times in a row for drawing a black line in the image for example, instead of clicking and dragging once from x1 to x2
  • Use of System.Threading.Sleep(1): If I don't do that, the target application won't process and doesn't draw, except for a few pixels (testing in MS Paint). Shouldn't I be working with wait async etc.? I never quite learned how to successfully replace Sleep();
  • I hear img.GetPixel(x, y) is a slow approach and should be replaced with Bitmap.LockBits()? Agreements, concerns?
  • I'm importing RegisterHotKey via user32.dll and overriding WndProc to listen for a global system hotkey (to start the drawing process), could this also be better solved with native C# elements? I notice sometimes on building the solution, or while debugging that it takes up to a few seconds until my program runs initially.

Finally, the most important thing -- the codeZ:

Drawing:

    void Draw()
    {

        Bitmap imgClipBoard = MakeBlackAndWhite((Bitmap)Clipboard.GetImage());

        Point startPos = Cursor.Position;
        ClickMouse(MouseButtons.Left, startPos.X, startPos.Y, true);
        ClickMouse(MouseButtons.Left, startPos.X, startPos.Y, false);

        for (int y = 0; y < img.Height; y++)
        {
            for (int x = 0; x < img.Width; x++)
            {
                Color c = img.GetPixel(x, y);

                if (c.B == 0)
                {
                    int drawX = startPos.X + x;
                    int drawY = startPos.Y + y;
                    MoveMouse(drawX, drawY);
                    ClickMouse(MouseButtons.Left, drawX, drawY, true);
                    ClickMouse(MouseButtons.Left, drawX, drawY, false);
                    Thread.Sleep(1);
                }
                if (Keyboard.IsKeyDown(Keys.Escape))
                    return;
            }
        }
    }

What I am also wondering: Are there any more intelligent ways to redraw an image programmatically, than pixel by pixel? For instance, identifying paths (lines) in the image to trace with one stroke instead of looping from top to bottom and clicking pixels, or is this something very advanced already?

Edit #1:

Here is a demonstration in GIF format regarding my first performance concern: Click and drag is infinitely faster than clicking pixel by pixel (this test still using GetPixel()).

Edit #2

-- Snip --

Let's forget about LockBits() for now.

My main concern is: How can I scan each pixel "mass", either by line or ideally the entire picture, so I can tell my program: Okay, here is this black and white image: I don't want you to click hundreds of times in a row for each black pixel, but instead I want this logic: scan -> first black pixel -> hold down mouse -> keep going till you hit a white pixel -> release. Rinse and repeat.

Please check Edit 1 to see why I believe this to make sense: Much faster image printing!

This following test is too slow for my purposes! SEE ANIMATED GIF

Edit #3:

I expect a nice rectangle but get zigzags

    private void DrawRect()
    {
        int x = Cursor.Position.X;
        int y = Cursor.Position.Y;

        for (int counter = 0; counter < 100; counter++)
        {
            MoveMouse(x, y + counter);
            ClickMouse(MouseButtons.Left, x, y + counter, true);
            Thread.Sleep(1);
            MoveMouse(x + 100, y + counter);
            Thread.Sleep(1);
        }
        Thread.Sleep(15);
        ClickMouse(MouseButtons.Left, 0, 0, false);

    }

I know it's because of the short sleep intervals, and if I increase them I get exactly what I want: I suspect that I will have to use wait async if I want this to run as fast as possible, without having to try out several numbers for the sleep delay.

  • I don't understand at all what you are trying to do. So, do you really assume that images are drawn on a pixel by pixel basis? Not. – TaW Jul 06 '18 at 20:39
  • I am wondering about efficient "human like" drawing techniques to replicate how a user would use a paint brush to draw something (in software). I don't mean print techniques on screen, but imagine that I'm trying to use Microsoft Paint's brush tool to redraw a photo that I fed into my software. A (pixel) drawbot so to speak. Imagine that Copy Paste from Clipboard are forbidden/inaccessible. – Cassiopeia Jul 06 '18 at 21:00
  • Well, but a bot would somehow need to __anaylze__ the image or else it won't come close to what a human would do to paint the image but stick to a pixel-to-pixel approach. Tough stuff.. – TaW Jul 06 '18 at 21:17
  • @TaW, Hmm, yes I had almost thought so. But that is just sort of the appendix question. What should stand at the center, is whether there is room for performance improvements on a pixel by pixel basis approach :D – Cassiopeia Jul 07 '18 at 07:35
  • Well if you need speed you probably need to avoid get/setpixel. Lockbits is using just one locking maneuver whereas get/setpixel do the same on each call, making them at least 10x slower. Do you want to create a 'copy' of the same size? The whole idea reminds me of some photoshop etc art-filters. – TaW Jul 07 '18 at 07:39
  • Ahh thanks for the insight! I will try to incorporate the LockBit asap and update my post. In most cases the image is going to be resized to either 800x600 or smaller than that. The MakeBlackAndWhite method actually does something similar to Photoshop's "Adjust Levels", so it's easier for me to re-draw colored pictures. – Cassiopeia Jul 07 '18 at 13:48
  • @TaW I come back, defeated. I can't do it. I do not want to admit how many countless hours I have wasted today to figure out how to use LockBits and then to iterate pixel by pixel and getting my mouse to move in the respective position. I feels so god damn dumb right now, I just can't. Please look at my Edit #2. I have played around with the byte[] array to understand what the data looks like, but I can't figure out how to scan the image line by line and move the mouse back to x=0 with every y increment. ARGH – Cassiopeia Jul 08 '18 at 21:17
  • Um, lockbits is good for speed but I don't think speed is your actual problem. I have posted a few [examples](https://stackoverflow.com/search?q=user%3A3152130+Lockbits) (not all of them equally usful!!) with lockbits at work using 1-3 bitmaps you may want to study. But maybe using a [fastbitmap](https://github.com/hazdryx/FastBitmap) is the easier alternative to play with fast bitmap manipulations. Still I think your real goal now is about concepts not about optimizations.. – TaW Jul 08 '18 at 21:51
  • Thank you, I think you are very right in saying that. I will have a close look at your posts. At the same time: what would be the best way to edit my thread and post progress? I feel like altering or deleting parts and put one problem up at a time, instead of listing ten things. Should I create a new topic? What do you think? My issue right now is that the program loops too fast and that actions do not perform properly. Instead of drawing a rectangle with one stroke, it draw zig zag lines. What else can I do other than increasing Sleep times? – Cassiopeia Jul 09 '18 at 14:06
  • Wrt the post: It is way too broad and partially unclear, so it should be closed sooner or later. Wrt the zig-zag. what MouseMove and MouseClick functions do you use? Also: Is the aim to create a pixel-correct version or a lossy one? - Also: Is your aim to control another application? Paint? Or to do the drawing in your own program? – TaW Jul 09 '18 at 14:54
  • 1
    I use Dllimport on user32.dll to get SendInput for the Clicking and SetCursorPos for MouseMove. Let's say I want a lossy one. For now I work in black/white, but what I ultimately want is to translate any image to a limited color palette (think MS Paint's 20 default colors) and reduce the resolution to anything smaller than 800x600. In a nutshell: yes, a lossy one. I don't want to control other applications other than by sending mouse input (later I will just hard-code color swapping (click xy to select 'red' etc.)). Drawing is intended for MS Paint (testing) and a javascript based browser game – Cassiopeia Jul 09 '18 at 15:15

0 Answers0