views:

53

answers:

3

The problem is how to invert colors of a Silverlight Image element.

There is an Image with a JPG as a source. On a button click I need to invert the colors. Sounds simple, right. Take each pixel, then modify it's value by 255 - pixel value. But when I tried WritableBitmap loaded with the Image source, I got security exception disallowing pixel access. Here is my code:

            if (MainLeftImage.Source != null)
            {
                WriteableBitmap bitmap = new WriteableBitmap((BitmapSource)MainLeftImage.Source);
                byte[] pixels = new byte[bitmap.Pixels.Length];

                int size = pixels.Count();
                for (int i = 0; i < size; i++)
                    pixels[i] = (byte)(255 - pixels[i]);

                bitmap.Invalidate();//redraw and then plug it back on

                MainLeftImage.Source = bitmap;
            }
        }
        catch (Exception ex)
        {
        }

Looks that WritableBitmap is not a solution, right? Any help appreciated. Thanks guys.

A: 

Yeah, that seems like a pretty silly restriction on the WritableBitmap. I'm not sure what scenario they're trying to prevent there. I believe that particular exception gets thrown whenever you're accessing cross-domain content, and it might work if the JPEG is coming from exactly the same server that is hosting the Silverlight app. See here:

http://forums.silverlight.net/forums/t/118030.aspx

But I'm honestly not sure of all the possible causes.

One option would be to use the FJCore library to decode the JPEG image to a bitmap, do the pixel flipping on the decoded bitmap, and then load the resulting bitmap into an image directly.

http://code.google.com/p/fjcore/

Ken Smith
Thanks Ken. What's bugging me is examples where they show how to manipulate WritableBitmap pixels in some silly scenarios like here http://www.silverlight.net/learn/videos/all/how-do-i-create-a-bitmap-using-writeablebitmap/
val
+3  A: 

Easiest way to invert an image would be to apply a pixel shader effect. I believe that there is an invert colors pixel shader in the WPF Pixel Shader Effects Library.

The reason that you are hitting the Security Exception is that WriteableBitmap prevents pixel access to cross-domain content. To understand why this is important, you can see my explanation of the necessity of client access policy files in this answer. To understand the point for images, just replace "secretaccountdetails.html" with "myPrivatePhoto.jpg" in the example.

If you really want to use a WriteableBitmap to access the pixels of an Image, either:
1. Make sure that the image source is served from the same domain as the application.
or
2. Make sure that the server from which the image is being served provides an appropriate client access policy and download the image file as a Stream. Use BitmapSource.SetSource(Stream) to set the source of the Image instead of setting it to the URL of the image.

In both of the above cases you will be able to access the pixels of an Image in a WriteableBitmap.

KeithMahoney
I see, thank you Keith. I got the issue with cross domain access, which is my case. I'll try the shader first. Cheers.
val
Hi Keith, I tried the pix shaders and they look alright for effects, but they don't modify the Image.Source which I need too. As for my security access exception: what is the place for the cross domain policy xml file exactly? Should it be in the root of my image virtual directory on the server? or with SL web app? Thanks.
val
The clientaccesspolicy file should go at the root of the server from which the image is being served. So if the url of the image is http://myserver.com/images/myphoto.jpg then the url of the clientaccesspolicy file should be http://myserver.com/clientaccesspolicy.xml. More info [here](http://msdn.microsoft.com/en-us/library/cc645032%28v=VS.95%29.aspx).
KeithMahoney
A: 

You need to solve the cross domain access like the other answers proposed. But there's also a failure in your algorithm. The WriteableBitmap.Pixels array stores each pixel as integer value. The 4 bytes of the int represent the alpha, red, green and blue component of the pixel (ARGB). You should implement it like this:

 int[] pixels = bitmap.Pixels;
 int size = pixels.Length;
 for (int i = 0; i < size; i++)
 {
        // Extract color components
        var c = pixels[i];
        var a = 0x000000FF & (c >> 24);
        var r = 0x000000FF & (c >> 16);
        var g = 0x000000FF & (c >> 8);
        var b = 0x000000FF & (c);

        // Invert
        r = 0x000000FF & (0xFF - r);
        g = 0x000000FF & (0xFF - g);
        b = 0x000000FF & (0xFF - b);

        // Set result color
        pixels[i] = (a << 24) | (r << 16) | (g << 8) | b;
 }

As others proposed, you can use a pixel shader effect instead. But you'll have to show the picture in an Image control in order to apply a shader to it. After that you can render the result into a WriteableBitmap.

var wb = new WriteableBitmap(myImageUiElement, null);
Rene Schulte
Rene, thanks a lot for pointing out for error in my invert algorigthm. You are right. Do you know how to solve the cross domain access? Where exactly the cross domain xml file must be placed?
val