0

I´m searching for a per pixel collision detection algorithm/ method for Windows Forms.

I have searched for it but I only find one for XNA (as you could see below). Isn´t such an algorithm agreeable with the concept of Windows Forms?!

/// <summary>
/// Determines if there is overlap of the non-transparent pixels
/// between two sprites.
/// </summary>
/// <param name="rectangleA">Bounding rectangle of the first sprite</param>
/// <param name="dataA">Pixel data of the first sprite</param>
/// <param name="rectangleB">Bouding rectangle of the second sprite</param>
/// <param name="dataB">Pixel data of the second sprite</param>
/// <returns>True if non-transparent pixels overlap; false otherwise</returns>
static bool IntersectPixels(Rectangle rectangleA, Color[] dataA,
                            Rectangle rectangleB, Color[] dataB)
{
    // Find the bounds of the rectangle intersection
    int top = Math.Max(rectangleA.Top, rectangleB.Top);
    int bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);
    int left = Math.Max(rectangleA.Left, rectangleB.Left);
    int right = Math.Min(rectangleA.Right, rectangleB.Right);

    // Check every point within the intersection bounds
    for (int y = top; y < bottom; y++)
    {
        for (int x = left; x < right; x++)
        {
            // Get the color of both pixels at this point
            Color colorA = dataA[(x - rectangleA.Left) +
                                 (y - rectangleA.Top) * rectangleA.Width];
            Color colorB = dataB[(x - rectangleB.Left) +
                                 (y - rectangleB.Top) * rectangleB.Width];

            // If both pixels are not completely transparent,
            if (colorA.A != 0 && colorB.A != 0)
            {
                // then an intersection has been found
                return true;
            }
        }
    }

The Problem with that one is, that I don´t know how to initialize the Color[] Arrays.

monty.py
  • 2,511
  • 1
  • 17
  • 29
  • Take a look at the `GetPixel` function: https://msdn.microsoft.com/de-de/library/system.drawing.bitmap.getpixel%28v=vs.110%29.aspx A fast method is using LockBits: http://stackoverflow.com/questions/24701703/c-sharp-faster-alternatives-to-setpixel-and-getpixel-for-bitmaps-for-windows-f I have no idea if that suits your needs. – Jens Feb 23 '15 at 15:45

1 Answers1

2

Here is a adaption to work with regular Winforms GDI+ Bitmaps & Lockbits:

static bool IntersectPixels(Rectangle rectangleA, Bitmap bmpA,
                            Rectangle rectangleB, Bitmap bmpB)
{
    bool collision = false;

    Size s1 = bmpA.Size;
    Size s2 = bmpB.Size;

    PixelFormat fmt1 = bmpA.PixelFormat;
    PixelFormat fmt2 = bmpB.PixelFormat;

    Rectangle rect = new Rectangle(0, 0, s1.Width, s1.Height);
    Rectangle rectB = new Rectangle(0, 0, s2.Width, s2.Height);

    BitmapData bmp1Data = bmpA.LockBits(rect, ImageLockMode.ReadOnly, fmt1);
    BitmapData bmp2Data = bmpB.LockBits(rectB, ImageLockMode.ReadOnly, fmt2);

    int size1 = bmp1Data.Stride * bmp1Data.Height;
    int size2 = bmp2Data.Stride * bmp2Data.Height;
    byte[] data1 = new byte[size1];
    byte[] data2 = new byte[size2];
    System.Runtime.InteropServices.Marshal.Copy(bmp1Data.Scan0, data1, 0, size1);
    System.Runtime.InteropServices.Marshal.Copy(bmp2Data.Scan0, data2, 0, size2);

    // Find the bounds of the rectangle intersection
    int top = Math.Max(rectangleA.Top, rectangleB.Top);
    int bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);
    int left = Math.Max(rectangleA.Left, rectangleB.Left);
    int right = Math.Min(rectangleA.Right, rectangleB.Right);


    // Check every point within the intersection bounds
    for (int y = top; y < bottom; y++)
    {
        for (int x = left; x < right; x++)
        {
            // Color data are BGRA!
            // Get the alpha (+3!) value of both pixels at this point
            byte colorA = data1[(x - rectangleA.Left) +  
                                (y - rectangleA.Top) * rectangleA.Width + 3];
            byte colorB = data2[(x - rectangleB.Left) + 
                                (y - rectangleB.Top) * rectangleB.Width + 3];

            // If both pixels are not completely transparent,
            if (colorA != 0 && colorB != 0)
            {
                // then an intersection has been found
                { collision = true; goto done; }
            }
        }
    }

  done:
    bmpA.UnlockBits(bmp1Data);
    bmpB.UnlockBits(bmp2Data);
    return collision;
}

For checking a collision with a fully non-transparent rectangle the code can be simplified greatly. You pass only the bounds (RectangleB) of that object:

static bool IntersectPixels(Rectangle rectangleA, Bitmap bmpA,  Rectangle rectangleB)
{
    bool collision = false;

    Size s1 = bmpA.Size;
    Rectangle rect = new Rectangle(0, 0, s1.Width, s1.Height);
    rectangleB.Intersect(rectangleA);
    BitmapData bmp1Data = bmpA.LockBits(rect, ImageLockMode.ReadOnly, bmpA.PixelFormat);
    int size1 = bmp1Data.Stride * bmp1Data.Height;
    byte[] data1 = new byte[size1];
    System.Runtime.InteropServices.Marshal.Copy(bmp1Data.Scan0, data1, 0, size1);
    // Check every point within the intersection bounds
    for (int y = rectangleB.Top; y < rectangleB.Bottom; y++)
    {
        for (int x = rectangleB.Left; x < rectangleB.Right; x++)
        {
            // Get the alpha value of both pixels at this point
            byte colorA = data1[(x - rectangleA.Left) + 
                                (y - rectangleA.Top) * rectangleA.Width + 3];

            // If a non-tranparent pixel
            if (colorA != 0 )   { collision = true; goto done; }
        }
    }
done:
    bmpA.UnlockBits(bmp1Data);
    return collision;
}

I have done some testing, but I'm not quite sure if I missed something.. Please come back with any errors you find!

TaW
  • 53,122
  • 8
  • 69
  • 111
  • I will test tomorrow! So the rectangles represent the whole image surface? Couldn´t you get these information from the Bitmap? So that the method only get passed Bitmaps? That would be awesome! – monty.py Feb 23 '15 at 21:15
  • 1
    I tried to leave the code as untouched as I could. The reason why the original author passes in the two rectangles probably is to allow for __optimizing the areas__ to check. If one is, say, a large wall and only the top of it is of interest, then passing in a smaller area could make it a lot faster. (Using only the intersection of the two instead of the outer hull as he does now would be another optimization..) – TaW Feb 24 '15 at 01:38
  • 1
    .. If you know that you will only do checks on small objects one certainly can leave out the params and create the rectangles from the Bitmaps in the method body..! I have added an overload to the answer. For a more general solution I think it is OK as it is.. – TaW Feb 24 '15 at 01:45
  • I need this method for the parameters (Rectangle rectA, Rectangle rectB, Bitmap bmpB) because one of my objects is a Rectangle, which I draw onto the Graphics like that: Dimension = new Rectangle(Location, size); g.FillRectangle(Brushes.Yellow, Dimension); – monty.py Feb 25 '15 at 14:46
  • 1
    Was that a question? If you know that one rectangle is always filled the check can be simplified because now only the pixels of the other bitmap must be checked.. – TaW Feb 25 '15 at 18:22
  • 1
    a) I have added code for your case. b) no we do need the rectangles because we need to know the location of them!! therefore c) the overload was wrong, forget it! And, of course my comments above regarding the need of the rectangles was wrong, too. d) The 2nd code version (without the 2nd bitmap) now contains an Intersect to optimize the code even further.. – TaW Feb 26 '15 at 11:00