3

This is kind of a two part question- First, why doesn't this code work?

Canvas canvas = new Canvas { Width = 640, Height = 480 };
System.Windows.Size size = new System.Windows.Size( canvas.Width, canvas.Height);

//Measure and arrange the surface
canvas.Measure( size );
canvas.Arrange( new Rect( size ) );

canvas.Background = new SolidColorBrush( Colors.Purple );

RenderTargetBitmap bitmap = new RenderTargetBitmap( (int)canvas.Width, (int)canvas.Height, 96d, 96d, PixelFormats.Pbgra32 );
bitmap.Render( canvas );
BitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add( BitmapFrame.Create( bitmap ) );

using ( MemoryStream outStream = new MemoryStream() )
{
    encoder.Save( outStream );

    outStream.Seek( 0, SeekOrigin.Begin );
    BitmapImage bmp = new BitmapImage { CacheOption = BitmapCacheOption.OnLoad };
    bmp.BeginInit();
    bmp.StreamSource = outStream;
    bmp.EndInit();
}

When I write the image to disk, all I see is a black image- I've done this before and had no problems, but now something is escaping me... I've checked the width and height and the buffer data in the MemoryStream and everything look okay...

This is just a test, the real goal would be to create a BitmapSource from the Canvas visual image. The Canvas is getting drawn on with Shapes (polylines etc) in code. I then need to pass this BitmapSource to an Image in xaml, at a rate of about 60 frames per second. I noticed that the Image.Source is using CachedBitmap if I create a mock BitmapSource, but it is rebinding to my BitmapImage everytime I update my (black) Bitmap.

Suggestions on how to create a Canvas in memory at 60fps and create a BitmapSource from it that is seen by Image.Source as a CachedBitmap?

Noam M
  • 3,156
  • 5
  • 26
  • 41
Nicros
  • 5,031
  • 12
  • 57
  • 101

2 Answers2

0

Makubex is correct - you need to wait until things get loaded up before the visuals are in a state where they've actually rendered anything capable of being copied; that said, while I'm not on a computer where I've got Studio installed, I do have LINQPad installed....

void Main()
{   
    mainWindow = new Window(){ Width = 640, Height = 480, Title = "Main Window" };
    canvas = new Canvas { Width = 640, Height = 480 };
    System.Windows.Size size = new System.Windows.Size( canvas.Width, canvas.Height );
    // Measure and arrange the surface
    canvas.Measure( size );
    canvas.Arrange( new Rect( size ) );
    canvas.Background = new SolidColorBrush( Colors.Purple );   

    mirrorTimer = new DispatcherTimer(
        TimeSpan.FromMilliseconds(1000.0 / 60.0), 
        DispatcherPriority.Background, 
        CopyToMirror, 
        Dispatcher.CurrentDispatcher);
    updateTimer = new DispatcherTimer(
        TimeSpan.FromMilliseconds(1000.0 / 60.0), 
        DispatcherPriority.Background, 
        DrawSomething, 
        Dispatcher.CurrentDispatcher);

    mainWindow.Loaded += 
        (o,e) => 
        {
            mirrorWindow = new Window { Width = 640, Height = 480, Title = "Mirror Window" };
            mirrorWindow.Show();
            mirrorWindow.Loaded += 
                (o2,e2) => 
                { 
                    mirrorTimer.Start(); 
                };          
        };
    mainWindow.Closed += 
        (o,e) => 
        { 
            if(mirrorTimer != null) 
            {
                mirrorTimer.Stop();
                mirrorWindow.Close();
            }
        };
    mainWindow.Content = canvas;
    mainWindow.Show();  
}

Window mainWindow;
Window mirrorWindow;
Canvas canvas;
DispatcherTimer mirrorTimer;
DispatcherTimer updateTimer;
Random rnd = new Random();

private void DrawSomething(object sender, EventArgs args)
{
    canvas.Children.Clear();
    canvas.Background = Brushes.White;
    var blob = new Ellipse() { Width = rnd.Next(0, 20), Height = rnd.Next(0, 20) };
    blob.Fill = new SolidColorBrush(Color.FromArgb(255, (byte)rnd.Next(0,255), (byte)rnd.Next(0,255), (byte)rnd.Next(0,255)));
    Canvas.SetLeft(blob, (int)rnd.Next(0, (int)canvas.ActualWidth));
    Canvas.SetTop(blob, (int)rnd.Next(0, (int)canvas.ActualHeight));
    canvas.Children.Add(blob);
}

private void CopyToMirror(object sender, EventArgs args)
{       
    var currentImage = (mirrorWindow.Content as Image);
    if(currentImage == null)
    {
        currentImage = new Image(){ Width = 640, Height = 480 };
        mirrorWindow.Content = currentImage;
    }

    RenderTargetBitmap bitmap = new RenderTargetBitmap( (int)canvas.Width, (int)canvas.Height, 96d, 96d, PixelFormats.Pbgra32 );
    bitmap.Render( canvas );
    BitmapEncoder encoder = new BmpBitmapEncoder();
    encoder.Frames.Add( BitmapFrame.Create( bitmap ) );

    BitmapImage bmp = new BitmapImage() { CacheOption = BitmapCacheOption.OnLoad };
    MemoryStream outStream = new MemoryStream();
    encoder.Save(outStream);
    outStream.Seek(0, SeekOrigin.Begin);
    bmp.BeginInit();
    bmp.StreamSource = outStream;
    bmp.EndInit();      
    currentImage.Source = bmp;  
}
JerKimball
  • 16,584
  • 3
  • 43
  • 55
0

If you are still having issues with the black image move your CacheOption set to just after the BeingInit call:

bmp.BeginInit();
bmp.CacheOption = BitmapCacheOption.OnLoad;
bmp.StreamSource = outStream;
bmp.EndInit();
Cameron
  • 133
  • 3
  • 7