none
Render the canvas, the whole canvas (or Render transform causing clipping)

    Question

  •  

    I have a canvas onto which I can place images and other objects and drag them around to a desired position. This canvas is rendered out into a bitmap source which is used as the source for a 3D rendering (basically a height map).

     

    The canvas is inside a viewbox like this:

     

    Code Block

    <Viewbox x:Name="vb2D" MinWidth="20">

    <src:DragCanvas x:Name="DragCanvas2D" Margin="2,2,2,2" Width="380" Height="Auto">

    <Rectangle x:Name="BorderRectangle" Width="10" Height="10"

    Stroke="Blue" StrokeThickness="0"/>

    </src:DragCanvas>

     

     

    Now if I keep the Viewbox stretch on, I can resize the viewbox with a gridsplitter (Viewbox is in a grid cell), and the image resulting from rendering the canvas is always the same size in pixels and it contains the entire canvas as 'rendered' on the screen. In other words I can zoom the canvas, make it visually larger/smaller without screwing up how it is rendered to a bitmapsource (i.e.

    Dim bmp As RenderTargetBitmap = New RenderTargetBitmap(Me.Width, Me.Height, 96, 96, PixelFormats.Pbgra32)

    )

     

    If I set the Viewbox stretch to none and use RenderTransform or LayoutTransform to 'zoom' into/out of the canvas, the canvas is rendered properly to the screen BUT the RenderTargetBitmap will be wrong; for instance it will be clipped if you are zoomed in, or tiny visually (same pixel size) when zoomed out. So it seems to me that RenderTargetBitmap must ignore any transformations that have been done to the canvas. Or, I am once again missing something obvious.

     

    I would really appreciate any tips.

    Friday, November 02, 2007 4:57 AM

Answers

  •  

    Marco thanks for the feedback. I feel better knowing that I was not going crazy, I could not seem to find a resource stating that indeed you could not directly render a transformed canvas. In my case I found that applying the transform to the Viewbox that contains the canvas gave me the best results. On the Viewbox I kept stretch on and set clip to bounds to true. When a scale transform is applied to the viewbox, the canvas will of course follow suit (the viewbox scales its child to fit), the view seen by the user will be clipped to fit the bounds of the Viewbox (its parent a border I think is really the limiting factor) and when I render the canvas I get the whole canvas
    Monday, November 05, 2007 5:47 PM

All replies

  •  

    It's amazing how the process of thinking about how to ask a question can lead you to diffrent ways to look at and solve a problem. I just tried doing the RenderTransform to the Viewbox instead of the canvas. I can zoom the Viewbox in and out (the canvas is visually scaled as well) and the RenderTargetBitamp is not affected

     

    I am still curious as to why the applying a RenderTransform to a Canvas throws off the RenderTargetBitmap.

     

     

    Friday, November 02, 2007 5:27 AM
  •    RenderTargetBitmap will take into consideration any transformation you applied to Canvas, the following code (borrowed from Adam Smith's code here) shows how to undo the transformation when rendering a visual into a bitmap:

     

    public static BitmapSource CreateBitmapSourceFromVisual(Double width,

        Double height,

        Visual visualToRender,

        Boolean undoTransformation)

    {

        if (visualToRender == null)

        {

            return null;

        }

     

        RenderTargetBitmap bmp = new RenderTargetBitmap((Int32)Math.Ceiling(width),

                                                        (Int32)Math.Ceiling(height),

                                                        (Double)DeviceHelper.PixelsPerInch(Orientation.Horizontal),

                                                        (Double)DeviceHelper.PixelsPerInch(Orientation.Vertical),

                                                        PixelFormats.Pbgra32);

        if (undoTransformation)

        {

            DrawingVisual dv = new DrawingVisual();

            using (DrawingContext dc = dv.RenderOpen())

            {

                VisualBrush vb = new VisualBrush(visualToRender);

                dc.DrawRectangle(vb, null, new Rect(new Point(), new Size(width, height)));

            }

            bmp.Render(dv);

        }

        else

        {

            bmp.Render(visualToRender);

        }

     

        return bmp;

    }

    internal class DeviceHelper

    {

        public static Int32 PixelsPerInch(Orientation orientation)

        {

            Int32 capIndex = (orientation == Orientation.Horizontal) ? 0x58 : 90;

            using (DCSafeHandle handle = UnsafeNativeMethods.CreateDC("DISPLAY"))

            {

                return (handle.IsInvalid ? 0x60 : UnsafeNativeMethods.GetDeviceCaps(handle, capIndex));

            }

        }

    }

    [DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]

    public static extern Int32 GetDeviceCaps(DCSafeHandle hDC, Int32 nIndex);

    [DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]

    public static extern Boolean DeleteDC(IntPtr hDC);

    internal sealed class DCSafeHandle : SafeHandleZeroOrMinusOneIsInvalid

    {

        private DCSafeHandle() : base(true) { }

     

        protected override Boolean ReleaseHandle()

        {

            return UnsafeNativeMethods.DeleteDC(base.handle);

        }

    }

       Hope this helps

    Monday, November 05, 2007 6:19 AM
  •  

    Marco thanks for the feedback. I feel better knowing that I was not going crazy, I could not seem to find a resource stating that indeed you could not directly render a transformed canvas. In my case I found that applying the transform to the Viewbox that contains the canvas gave me the best results. On the Viewbox I kept stretch on and set clip to bounds to true. When a scale transform is applied to the viewbox, the canvas will of course follow suit (the viewbox scales its child to fit), the view seen by the user will be clipped to fit the bounds of the Viewbox (its parent a border I think is really the limiting factor) and when I render the canvas I get the whole canvas
    Monday, November 05, 2007 5:47 PM
  • Marco,

     

    I can't quite get your utility to work (or even compile) - can you please post your full source? I'm trying to cache a rendered shadowed element without having to leverage bitmapeffects in the visual tree.

     

    Thanks!

    James

    Saturday, April 05, 2008 6:46 AM
  • Here is the full code mutated from Adam Smith's sample:
     public class VisualUtility
        {
            public static BitmapSource CreateBitmapSourceFromVisual(Double width,
                Double height,
                Visual visualToRender,
                Boolean undoTransformation)
            {
                if (visualToRender == null)
                {
                    return null;
                }

                RenderTargetBitmap bmp = new RenderTargetBitmap((Int32)Math.Ceiling(width),
                                                                (Int32)Math.Ceiling(height),
                                                                (Double)DeviceHelper.PixelsPerInch(Orientation.Horizontal),
                                                                (Double)DeviceHelper.PixelsPerInch(Orientation.Vertical),
                                                                PixelFormats.Pbgra32);
                if (undoTransformation)
                {
                    DrawingVisual dv = new DrawingVisual();
                    using (DrawingContext dc = dv.RenderOpen())
                    {
                        VisualBrush vb = new VisualBrush(visualToRender);
                        dc.DrawRectangle(vb, null, new Rect(new Point(), new Size(width, height)));
                    }
                    bmp.Render(dv);
                }
                else
                {
                    bmp.Render(visualToRender);
                }

                return bmp;
            }

            public static BitmapSource CreateBitmapFromVisual(Visual visualToRender, Boolean undoTransformation)
            {
                if (visualToRender == null)
                {
                    return null;
                }

                Rect bounds = VisualTreeHelper.GetContentBounds(visualToRender);
                return CreateBitmapSourceFromVisual(bounds.Width, bounds.Height, visualToRender, undoTransformation);
            }

            public static BitmapSource CreateBitmapFromVisual(Visual visualToRender)
            {
                return CreateBitmapFromVisual(visualToRender, false);
            }
        }

        internal class DeviceHelper
        {
            public static Int32 PixelsPerInch(Orientation orientation)
            {
                Int32 capIndex = (orientation == Orientation.Horizontal) ? 0x58 : 90;
                using (DCSafeHandle handle = UnsafeNativeMethods.CreateDC("DISPLAY"))
                {
                    return (handle.IsInvalid ? 0x60 : UnsafeNativeMethods.GetDeviceCaps(handle, capIndex));
                }
            }
        }

        internal sealed class DCSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
        {
            private DCSafeHandle() : base(true) { }

            protected override Boolean ReleaseHandle()
            {
                return UnsafeNativeMethods.DeleteDC(base.handle);
            }
        }

        [System.Security.SuppressUnmanagedCodeSecurity]
        internal static class UnsafeNativeMethods
        {
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
            public static extern Boolean DeleteDC(IntPtr hDC);

            [DllImport("gdi32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
            public static extern Int32 GetDeviceCaps(DCSafeHandle hDC, Int32 nIndex);

            [DllImport("gdi32.dll", EntryPoint = "CreateDC", CharSet = CharSet.Auto)]
            public static extern DCSafeHandle IntCreateDC(String lpszDriver, String lpszDeviceName, String lpszOutput, IntPtr devMode);

            public static DCSafeHandle CreateDC(String lpszDriver)
            {
                return UnsafeNativeMethods.IntCreateDC(lpszDriver, null, null, IntPtr.Zero);
            }
        }

    Hope this helps
    Monday, April 07, 2008 7:58 AM
  • Thanks, Marco!

    Monday, April 07, 2008 2:53 PM
  • This example was useful.

    It solved my problem.

    I was capturing a frame from video and displaying the frame at a different size than the video window.

    When I was failing the image  was clipped.

    Now the software is successful.

    I thought there was a way to choose a check box or button in this forum to indicate

    ....... this thread is usefuil ... but I do not now see it.

    Wednesday, June 09, 2010 3:46 PM