1

I'm trying to draw rectangles into a WriteableBitmap, unfortunatelly the WriteableBitmapEx that provides Fill* extensions are too slow and can be run only at the main thread.

I'm looking for alternatives specific for WP8.1 and don't know the best solution so far.

I need a way to draw the rectangles async, one approach was creating a Canvas at the MainWindow and adding xaml.Rectangles on it, this almost can be used as solution for the problem, but I want specific draw the rectangles on the WriteableBitmap instead of creating a ton of UIElements and adding all of then on the screen.

Sorry if any given solution can be found on internet, I can't find almost nothing about C#.

A test I did:

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        var container = new Canvas()
        {
            Width = 300,
            Height = 500
        };

        var winImage = new Image()
        {
            Width = 300,
            Height = 500
        };
        container.Children.Add(winImage);

        //var winImage = imageView.NativeView<Image>();
        var img = new WriteableBitmap((int)winImage.Width, (int)winImage.Height);

        var clr = Color.FromArgb(255, 0, 0, 255);


        var start = DateTime.Now;
        var random = new Random();

        for (int i = 0; i < 50; i++)
        {
            //var color = Color.FromArgb(255, (byte)random.Next(255), (byte)random.Next(255), (byte)random.Next(255));
            img.FillRectangle(i * 2, i * 2, i * 2 + 10, i * 2 + 10, clr);
        }
        Debug.WriteLine((DateTime.Now - start).TotalMilliseconds + "ms drawing");

        winImage.Source = img;

        Content = container;
    }

This results in "792.1397ms drawing" running on debug mode on a Nokia Lumia 1020, that is pretty slow.

Marcos Vasconcelos
  • 18,136
  • 30
  • 106
  • 167
  • Regarding your claim that WriteableBitmapEx is too slow, you need to elaborate on what you have tried and what your needs are. I just tested it - I can draw 2500 visible randomly colored rectangles with 300 frames per second. – Anders Carstensen Oct 21 '15 at 21:24
  • Can you add code of it please? I'm updating the question with a test. – Marcos Vasconcelos Oct 22 '15 at 13:00
  • 1
    Please use a `Stopwatch` to measure how long an operation took. http://stackoverflow.com/questions/2923283/stopwatch-vs-using-system-datetime-now-for-timing-events – Daniel Hilgarth Oct 22 '15 at 13:11
  • Cool, but for this test, increasing the number of rectangles (for 150 that is a good max number of objects on screen) do a visible delay. Thanks for the tip. – Marcos Vasconcelos Oct 22 '15 at 13:13
  • 1
    @MarcosVasconcelos I don't have the possibility of testing on a phone. Could you check if [this code](https://gist.github.com/F2/2483a17a1e8ce66af1f4) is any different performance-wise? – Anders Carstensen Oct 22 '15 at 13:42
  • Anders, your code indeed is faster, I drawed 10000 rectangles and its performatic, but the trick was the img.getBitmapContext(), without it it last 16secs to draw. Please post as a Answer that I'll accept it after a few more tests. Really thanks. – Marcos Vasconcelos Oct 22 '15 at 15:15
  • Fantastic. I will post an answer. – Anders Carstensen Oct 22 '15 at 19:15

1 Answers1

3

Using GetBitmapContext() should make it a lot faster.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    var container = new Canvas()
    {
        Width = 300,
        Height = 500
    };

    var winImage = new Image()
    {
        Width = 300,
        Height = 500
    };
    container.Children.Add(winImage);

    var img = BitmapFactory.New((int)winImage.Width, (int)winImage.Height); 
    winImage.Source = img;
    Content = container;

    var clr = Color.FromArgb(255, 0, 0, 255);
    var random = new Random();

    var sw = new Stopwatch();
    sw.Start();

    using (img.GetBitmapContext()) {
        img.Clear(Colors.White);
        for (var x = 0; x < 10; x++) {
          for (var y = 0; y < 10; y++) {
            img.FillRectangle(x * 10, y * 10, x * 10 + 10, y * 10 + 10, clr); 
          }
        }
    }

    sw.Stop();

    Debug.WriteLine(sw.ElapsedMilliseconds + "ms drawing");
}
Anders Carstensen
  • 2,949
  • 23
  • 23
  • Indeed, I was missing "img.getBitmapContext()" and dispose() it, thanks – Marcos Vasconcelos Oct 22 '15 at 19:39
  • Why exactly this improves performance? As far i saw on source this is a copy reference of the object, but dont looks to do nothing – Marcos Vasconcelos Oct 23 '15 at 14:07
  • @MarcosVasconcelos It creates (and disposes) a BitmapContext, which calls [`Lock`](https://msdn.microsoft.com/en-us/library/system.windows.media.imaging.writeablebitmap.lock(v=vs.110).aspx) and [`Unlock`](https://msdn.microsoft.com/en-us/library/system.windows.media.imaging.writeablebitmap.unlock(v=vs.110).aspx). From the documentation: *When a WriteableBitmap is locked, the rendering system does not send updates until the WriteableBitmap is fully unlocked by calls to the Unlock method.* – Anders Carstensen Oct 23 '15 at 14:15
  • Really thanks, I didnt found the Lock and Unlock methods for WindowsPhone8.1, they are at the WriteableBitmapEx too? – Marcos Vasconcelos Oct 23 '15 at 14:35
  • @MarcosVasconcelos I'm not sure about WP8.1 - You can find the source [here](https://github.com/teichgraf/WriteableBitmapEx/blob/5e4b7605f81f41b0518befd1583a801e7877b6d2/Source/WriteableBitmapEx/BitmapContext.cs). – Anders Carstensen Oct 23 '15 at 14:47