1

I have a scenario where

  • choosing a image file and then using BitmapDecoder converting the source to a WriteableBitmap and setting an image.source to the WriteableBitmap.
  • Now when a user taps on the image I get the co-ordinates and then want to color the entire region surrounding that pixel with a particular color.(Like the fill option in paint).

The code I've used is

 private void setPixelColors(int xCord, int yCord, int newColor)
 {
    Color color = bit.GetPixel(xCord, yCord);
    if (color.R <= 5 && color.G <= 5 && color.B <= 5 || newColor == ConvertColorToInt(color))
    {
        //Debug.WriteLine("The color was black or same returning");
        return;
    }
    setPixelColors(xCord + 1, yCord, newColor);
    setPixelColors(xCord, yCord + 1, newColor);
    setPixelColors(xCord - 1, yCord, newColor);
    setPixelColors(xCord, yCord - 1, newColor);
    //Debug.WriteLine("Setting the color here");
    bit.SetPixel(xCord, yCord, newColor);
 }

This works but is terribly ineffecient. I'd like to know is there a better method to do this.

Edit: Using the library WriteableBitmapEx.

AbsoluteSith
  • 1,917
  • 25
  • 60
  • 1
    Is it too slow, or wasting too much memory or...? One possible optimization would be to map the coordinates that you've already visited using a hash table so you can skip the whole GetPixel call and return immediately if the coordinate is the one you've already visited. – Igor Ralic Oct 02 '15 at 10:56
  • Its too slow. But dont you think the first if condition takes care of that portion. – AbsoluteSith Oct 02 '15 at 11:13
  • What's the actual type of *bit*? WriteableBitmap in UWP doesn't have SetPixel method. – Alex Zhevzhik Oct 02 '15 at 11:17
  • Its of the type Windows.UI.Xaml.Media.Imaging.WriteableBitmap and it does have SetPixel method. – AbsoluteSith Oct 02 '15 at 11:22
  • Probably it's an extension method. Or do you use a library like WriteableBitmapEx? – Alex Zhevzhik Oct 02 '15 at 11:24
  • Oh yeah sorry I forgot to mention that. I use the library WriteableBitmapEx. – AbsoluteSith Oct 02 '15 at 11:25
  • Okay, my first suggestion was that you have inefficient implementation of SetPixel, but if you use WriteableBitmapEx everything is ok, it's the fastest library I know. But I have already another suggestion and it's worth to be an answer. – Alex Zhevzhik Oct 02 '15 at 11:30
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/91172/discussion-between-absolutesith-and-alex-zhevzhik). – AbsoluteSith Oct 02 '15 at 11:39
  • @AbsoluteSith well, I'm just throwing ideas here :) Each GetPixel creates a new disposable BitmapContext and brings an overhead that you don't need... Do you really need to do that? More optimization can be done with the recursive algorithm improvements, as others have suggested. – Igor Ralic Oct 02 '15 at 12:29

2 Answers2

5

The GetPixel and SetPixel extension methods are very expensive for multiple iterative changes, since they extract the BitmapContext (the WriteableBitmap's PixelBuffer), make the change, and then writes back the updated PixelBuffer when the call is done with the BitmapContext.

WriteableBitmapEx will share the BitmapContext between multiple calls if you get it first and keep a live reference. This will be significantly faster to read out the PixelBuffer only once, make all of the changes, and then write it back only once.

To do this, use WriteableBitmapEx's BitmapContext object (reachable via the GetBitmapContext extension method) to extract the PixelBuffer then call Get and SetPixel as often as needed on the bitmap context. When done with it, Dispose the BitmapContext to save it back into the WriteableBitmap's PixelBuffer (it'll generally be easier to auto-Dispose the BitmapContext via a using statement).

There is sample code that will give the general idea on the WriteableBitmapEx GitHub at https://github.com/teichgraf/WriteableBitmapEx

Something like:

 private void setPixelColors(int xCord, int yCord, int newColor)
 {
     using (bit.GetBitmapContext())
     {
         _setPixelColors(xCord, yCord, newColor);
     }
 }
 private void _setPixelColors(int xCord, int yCord, int newColor)
 {
    Color color = bit.GetPixel(xCord, yCord);
    if (color.R <= 5 && color.G <= 5 && color.B <= 5 || newColor == ConvertColorToInt(color))
    {
        //Debug.WriteLine("The color was black or same returning");
        return;
    }
    setPixelColors(xCord + 1, yCord, newColor);
    setPixelColors(xCord, yCord + 1, newColor);
    setPixelColors(xCord - 1, yCord, newColor);
    setPixelColors(xCord, yCord - 1, newColor);
    //Debug.WriteLine("Setting the color here");
    bit.SetPixel(xCord, yCord, newColor);
 }

This should get the overall speed reasonable, but (as Alex suggests) you should look into non-recursive flood fill algorithms, especially if you have large bitmaps. The recursive algorithm will overflow the stack for large fills. There are a few fairly easy options on Wikipedia: https://en.wikipedia.org/wiki/Flood_fill#Alternative_implementations A simple one is to keep basically the same structure you have here but instead of recursing to handle each new pixel, explicitly handle the stack of to-be-edited pixels. If you know you are targeting small areas then that will probably be fast enough on its own. To support larger ones you may want to optimize further.

Rob Caplan - MSFT
  • 21,714
  • 3
  • 32
  • 54
  • 1
    Thanks a lot Rob! Yes this is a much better approach and also works. I'll try using a loop instead of recurrsion! – AbsoluteSith Oct 03 '15 at 11:50
  • @Rob will this work to change the entire image pixel color? I want to download image from the server and then change its pixel color to something else. – Kinjan Bhavsar Mar 10 '16 at 05:52
0

First of all, I can't see how you check if xCord or yCord are out of bitmap boundaries and out of the area you want to fill. Do you know a shape of the tap area you want to fill in advance? For example if it has an elliptic shape, isn't it easier to call FillEllipse instead? Or if rectangular - FillRect?

Second, I believe you recursive algorithm is inefficient. Of course it declines already processed pixels and don't make useless SetPixel calls, but it makes many false checks, because it still gets pixel, analyzes it and produces resursive calls.

Try to visualize it. If you have a bitmap 10x10, and you tap in the middle (5; 5) even before the first pixel would be set (it'd be the pixel at 10;5) you will have 5 recursive calls, and each of them produces another 4 calls and so on. And every call will access to the bitmap, take pixels and spend processor time.

As a slight improvement try to put SetPixel call before recursive calls:

bit.SetPixel(xCord, yCord, newColor);
setPixelColors(xCord + 1, yCord, newColor);
setPixelColors(xCord, yCord + 1, newColor);
setPixelColors(xCord - 1, yCord, newColor);
setPixelColors(xCord, yCord - 1, newColor);

But I think you have to change the whole idea. When working with bitmap, recursive algorithm isn't the best idea.

Alex Zhevzhik
  • 3,347
  • 19
  • 19