views:

38

answers:

1

According to msdn in Silverlight images are hit testable over their image/media display areas, basically their Height and Width. Transparent / full alpha pixels in the image file are still hit testable. My question is now, what is the best way to have only non-transparent pixel hit testable in images in Silverlight?

A: 

This is not going to be possible using the normal hit testing capability, as you found out with the MSDN reference.

The only idea I had was to convert your image to the WritableBitmap class and use the Pixels property to do alpha channel hit testing. I have not actually tried this and I can't imagine it's trivial to do, but it should work in theory.

The pixels are one large int[] with the 4 bytes of each integer corresponding to ARGB. It uses the premultiplied ARGB32 format, so if there is any alpha transparency besides full 255 the other RGB values are scaled accordingly. I am assuming you want anything NOT full alpha to be considered a "hit" so you could just check against the alpha byte to see if it is 255.

You would access the row/col pixel you are looking to check by array index like this:

int pixel = myBitmap.Pixels[row * myBitmap.PixelWidth + col];

Check out this post for some more ideas.

EDIT:

I threw together a quick test, it works and it's pretty straightforward:

public MainPage()
{
    InitializeComponent();

    this.image = new BitmapImage(new Uri("my_tranny_image.png", UriKind.Relative));
    this.MyImage.Source = image;

    this.LayoutRoot.MouseMove += (sender, e) =>
    {
        bool isHit = ImageHitTest(image, e.GetPosition(this.MyImage));
        this.Result.Text = string.Format("Hit Test Result: {0}", isHit);
    };
}

bool ImageHitTest(BitmapSource image, Point point)
{
    var writableBitmap = new WriteableBitmap(image);

    // check bounds
    if (point.X < 0.0 || point.X > writableBitmap.PixelWidth - 1 ||
        point.Y < 0.0 || point.Y > writableBitmap.PixelHeight - 1)
        return false;

    int row = (int)Math.Floor(point.Y);
    int col = (int)Math.Floor(point.X);

    int pixel = writableBitmap.Pixels[row * writableBitmap.PixelWidth + col];
    byte[] pixelBytes = BitConverter.GetBytes(pixel);

    if (pixelBytes[0] != 0x00)
        return true;
    else
        return false;
}

You would probably want to make some optimizations like not create the WritableBitmap on every MouseMove event but this is just a proof of concept to show that it works.

Dan Auclair
I thought about doing something like that but it seemed unnecessarily complicated, so I want to make sure there isn't an easier solution. Another approach that I came across tried to use the object contours to create a path geometry and then check if the mouseclick was within or not. That would also work in my application, but again seemed too complicated for such a basic problem.
cfroehlich
Are there multiple PNGs or just one? I don't think it would be that bad, I'm guessing you are hit testing on the MouseMove event so you only have one Point to translate to the image coordinates and then check X and Y against pixel row and column. Path geometry sounds more complicated to me.
Dan Auclair
Edited my answer with some sample code.
Dan Auclair
thx for the code sample. you seem to have to have the x and y confused though. (x=col and y=row). works well enough, but still seems like a waste of memory to have a copy of every image I use^^
cfroehlich