views:

499

answers:

1

I have a custom wpf control to do an arbitrary reverse image warp, that use a Viewport3D and custom matrix, and a mesh with an ImageBrush...

I'm trying to render this to a bitmap entirely off screen. I am able to render a bitmap fine (I found to call .Measure() .Arrange(new Rectangle(...)) and .UpdateLayout()) before rendering, but the binding hasn't completed yet so the mesh is invisible (the image brush source hasn't loaded yet)...

What am I missing? is there a blocking way to wait for binding to complete?

[My attempts to handle the "Loaded" event haven't been successful...]

Update, sample code:

Sample code is below. The expected output is a 300x300 blue rectangle with the stack overflow logo squashed/rotated in a diamond. (try opening the DiamondControl.xaml designer, and changing the code to have the image url hard coded... interestingly, this variation continues to not be in the rendered bitmap suggesting my problem isn't related to the image url being bound, but the image not yet being loaded)

Program.cs (a console app with added references to System.Windows.Presentation)

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace ConsoleApplication3 {
    class Program {
        [STAThread]
        static void Main(string[] args) {
            // try exporting a bitmap...
            var content = new DiamondControl();
            content.DataContext = new Uri("http://sstatic.net/so/img/logo.png");
            //content.ApplyTemplate(); // this doesn't seem to work

            int width = 300, height = 300;

            var rect = new Rect(0, 0, width, height);
            content.Measure(new Size(width, height));
            content.Arrange(rect);
            //content.UpdateLayout(); // not required in this case, seems to be required if I render the control more than once

            PngBitmapEncoder encoder = new PngBitmapEncoder();
            RenderTargetBitmap render = new RenderTargetBitmap(
                width,
                height,
                96,
                96,
                PixelFormats.Pbgra32);

            render.Render(content);
            encoder.Frames.Add(BitmapFrame.Create(render));
            using (Stream s = File.Open("outputfile.png", FileMode.Create)) {
                encoder.Save(s);
            }
        }
    }
}

DiamondControl.xaml:

<UserControl x:Class="ConsoleApplication3.DiamondControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="300" Width="300">
    <Grid>
        <Rectangle Fill="CornflowerBlue" />
        <Viewport3D>
            <ModelVisual3D>
                <ModelVisual3D.Content>
                    <Model3DGroup>
                        <GeometryModel3D>
                            <GeometryModel3D.Geometry>
                                <!-- simple diamond -->
                                <MeshGeometry3D x:Name="mesh"
                            Positions="-.5 0 0, 0 .5 0, 0 -.5 0, .5 0 0"
                            TextureCoordinates="0 1, 0 0, 1 1, 1 0" 
                            TriangleIndices="0 2 1, 2 3 1" />
                            </GeometryModel3D.Geometry>

                            <GeometryModel3D.Material>
                                <DiffuseMaterial>
                                    <DiffuseMaterial.Brush>
                                        <!--<SolidColorBrush Color="Red" />-->
                                        <ImageBrush ImageSource="{Binding}" />
                                        <!--<ImageBrush ImageSource="http://sstatic.net/so/img/logo.png" />-->
                                    </DiffuseMaterial.Brush>
                                </DiffuseMaterial>
                            </GeometryModel3D.Material>
                        </GeometryModel3D>

                        <!-- Light source. -->
                        <AmbientLight Color="White" />
                    </Model3DGroup>
                </ModelVisual3D.Content>
            </ModelVisual3D>

            <!-- Camera. -->
            <Viewport3D.Camera>
                <OrthographicCamera Position="0 0 1"
                        LookDirection="0 0 -1"
                        UpDirection="0 1 0"
                        Width="1" />
            </Viewport3D.Camera>
        </Viewport3D>
    </Grid>
</UserControl>
A: 

If your bitmap corresponds to a web address, you use the solution in http://stackoverflow.com/questions/426645/how-to-render-an-image-in-a-background-wpf-process

Also, include an <image> element (even if sized 0x0) in your control. I'm not sure why this works and it seems like a total kludge.

sphereinabox