Sorry, the Forum is closed :(

Unfortunately, the forum is now closed.

Please read the following post for further details.

We apologize for the inconvenience.
The SharpDX team.
Welcome, Guest
Username: Password: Remember me
  • Page:
  • 1

TOPIC: Image Manipulation in Metro

Image Manipulation in Metro 1 year 9 months ago #330

I am brand new to SharpDX. Since it seems a bit overwhelming, I would appreciate some direction where to even start looking given my needs. In my application, I need to provide the end user with the ability to color an image (.png) using a brush and a color palette. What namespace/classes should I start working with to allow for coloring an existing image (think interactive coloring book), and then saving the colored image? Is SharpDX even appropriate for this use case? Any help would be greatly appreciated. Thank you.
The administrator has disabled public write access.

Re: Image Manipulation in Metro 1 year 9 months ago #333


  • Posts:843 Thank you received: 1
  • xoofx's Avatar
  • xoofx
  • Administrator
  • OFFLINE
The best API for this kind of tasks are Direct2D (for painting, brushes...etc.) and WIC (for loading bitmaps), and potentially DirectWrite (if you are looking to display some text over the images).

The main source of documentation for Direct2D and WIC is on MSDN.
SharpDX is coming with some Win8 samples. Some of them were directly ported from "Windows 8 Release Preview Metro style app samples - C#, VB.NET, C++, JavaScript".

Also, lots of usage of Direct2D/DirectWrite can be found from the Windows 7 SDK. Some of the DirectWrite samples were for example ported from this SDK.

As explained in "SharpDX, Where do I start?", the only way to understand how to use SharpDX managed API is to know how it is used in C++.
The administrator has disabled public write access.

Re: Image Manipulation in Metro 1 year 9 months ago #339

Thank you. It looks like I am about to do some Direct2D API research. Thanks for the prompt response.
The administrator has disabled public write access.

Re: Image Manipulation in Metro 1 year 9 months ago #342

Would you mind reviewing the code below to see why I am not seeing the expected result? My goal is to show an image in a XAML image control, and use Direct2D to paint over it via the mouse or user gestures. Then, the goal is to save the output (code for save not present). I am doing this in a metro style application. The test application is attached.
protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            // Creates a new DeviceManager (Direct3D, Direct2D, DirectWrite, WIC)
            deviceManager = new DeviceManager();      
            wicFactory = new ImagingFactory();
            d2d1Factory = new SharpDX.Direct2D1.Factory();     

            using(var bitmapSource = Loader.LoadBitmap(wicFactory, "Page18.png"))
            {
                SharpDX.WIC.Bitmap wicBitmap = new Bitmap(wicFactory, bitmapSource, BitmapCreateCacheOption.CacheOnLoad);

                int pixelWidth = (int)(wicBitmap.Size.Width * DisplayProperties.LogicalDpi / 96.0);
                int pixelHeight = (int)(wicBitmap.Size.Height * DisplayProperties.LogicalDpi / 96.0);

                surfaceImageSource = new SurfaceImageSourceTarget(pixelWidth, pixelHeight);    

                var renderTargetProperties = new RenderTargetProperties(RenderTargetType.Default,
                    new D2DPixelFormat(Format.Unknown, AlphaMode.Unknown), 0, 0, RenderTargetUsage.None,
                    FeatureLevel.Level_DEFAULT);

                renderTarget = new WicRenderTarget(d2d1Factory, wicBitmap, renderTargetProperties);                                         
                brush = new SharpDX.Direct2D1.SolidColorBrush(renderTarget, Colors.Yellow);     
            }           
        }
               
        // User activates coloring       
        void colorButton_Click(object sender, RoutedEventArgs e)
        {
            myImage.PointerPressed += myImage_PointerPressed;
            myImage.PointerReleased += myImage_PointerReleased;
        }

        void myImage_PointerPressed(object sender, PointerRoutedEventArgs e)
        {
            this.myImage.PointerMoved += myImage_PointerMoved;
            e.Handled = true;
        }   

        void myImage_PointerReleased(object sender, PointerRoutedEventArgs e)
        {
            this.myImage.PointerMoved -= myImage_PointerMoved;
            e.Handled = true;
        }        

        //Draws using brush
        void myImage_PointerMoved(object sender, PointerRoutedEventArgs e)
        {
            Windows.UI.Input.PointerPoint pt = e.GetCurrentPoint(myImage);

            renderTarget.BeginDraw();
            DrawingPointF ellipseCenter = new DrawingPointF((float)pt.Position.X, (float)pt.Position.Y);
            Ellipse ellipse = new Ellipse(ellipseCenter, 15.0f, 20.0f);
            renderTarget.FillEllipse(ellipse, brush);
            Result result = renderTarget.EndDraw();
        }
The administrator has disabled public write access.

Re: Image Manipulation in Metro 1 year 9 months ago #343


  • Posts:843 Thank you received: 1
  • xoofx's Avatar
  • xoofx
  • Administrator
  • OFFLINE
erkjak2012 wrote:
Would you mind reviewing the code below to see why I am not seeing the expected result? My goal is to show an image in a XAML image control, and use Direct2D to paint over it via the mouse or user gestures. Then, the goal is to save the output (code for save not present). I am doing this in a metro style application. The test application is attached.
You are not following the way SharpDX samples were done (hm, did you really have a careful look at them?) and not really following how Direct2D is working, a couple of remarks:
  • DeviceManager needs to be initialized explicitly (Initialize method)
  • WicFactory and D2D1Factory are already created by DeviceManager
  • The rendering should not be done in an event, but in a rendering loop (as it is done in SharpDX samples)
  • It is not recommended to create objects in the rendering loop (Ellipse...etc.). Use matrix transformation on render target to move the object you have created. Moreover, you are creating a resource that is not disposed.
The administrator has disabled public write access.

Re: Image Manipulation in Metro 1 year 9 months ago #346

Thanks for taking the time to review and reply. While I did look at the samples and MSDN documentation, I evidently did not do it long and/or closely enough :). It looks like I have quite a way to go. Your feedback is very much appreciated. Thanks. I hope it's OK to keep posting code as I make progress (hopefully in the right direction).
The administrator has disabled public write access.

Re: Image Manipulation in Metro 1 year 9 months ago #347

I have made some major changes to the code based on your feedback. Please let me know if I misunderstood your guidance. The only item I was not able to execute is the matrix transformation you suggested. I have no idea how that works. Below is the updated version of the code.

I have been able to capture pointer press and move events, and successfully created a filled ellipse (actually a circle) on top of the image displayed. However, it seems that whenever the pointer moves, so does the ellipse. How can I save the state of the Direct2D context after I draw a filled ellipse, so that I can mimic a "brush stroke" upon pointer moves? My guess is that if I draw an ellipse for every pointer move position, I can create a brush paint stroke effect. The other approach I could take is to create a custom effect (BrushStrokeEffect?), but I am not sure if that is necessary. I have a suspicion that DeviceContext.SaveDrawingState might help with saving state after drawing an ellipse, but I don't know how to do that. Could you provide some input on that aspect as well as anything else you see wrong? Thanks for your help.

Is the code below on the right track? I am only asking to make sure I am in line with your initial guidance. Thanks.
    /// <summary>
    /// The Xaml view hosting the coloring page
    /// </summary>
    public sealed partial class TestPage : SwapChainBackgroundPanel
    {
        private DeviceManager deviceManager;
        private SwapChainBackgroundPanelTarget d2dTarget;
        private ColoringEffectRenderer coloringEffectRenderer;

        public TestPage()
        {
            this.InitializeComponent();

            coloringEffectRenderer = new ColoringEffectRenderer(root, root);            

            d2dTarget = new SwapChainBackgroundPanelTarget(root);
            d2dTarget.OnRender += coloringEffectRenderer.Render;           

            deviceManager = new DeviceManager();
            deviceManager.OnInitialize += d2dTarget.Initialize;
            deviceManager.OnInitialize += coloringEffectRenderer.Initialize;
            
            deviceManager.Initialize(DisplayProperties.LogicalDpi);

            // Setup rendering callback
            CompositionTarget.Rendering += CompositionTarget_Rendering;
        }

        void CompositionTarget_Rendering(object sender, object e)
        {
            d2dTarget.RenderAll();
            d2dTarget.Present();
        }
    }

    /// <summary>
    /// The Coloring effect renderer class that handles user brush gestures
    /// </summary>
    public class ColoringEffectRenderer : Component
    {
        private DeviceManager _deviceManager;
        private SharpDX.WIC.FormatConverter _formatConverter;
        private Brush coloringBrush;        

        private Windows.UI.Xaml.UIElement _root;
        private Windows.UI.Xaml.DependencyObject _rootParent;
        private SharpDX.Direct2D1.Effects.BitmapSourceEffect bitmapSourceEffect;
        
        private DrawingSize imageSize;
        private DrawingSize screenSize;

        public ColoringEffectRenderer(Windows.UI.Xaml.UIElement rootForPointerEvents, Windows.UI.Xaml.UIElement rootOfLayout)
        {
            _root = rootForPointerEvents;
            _rootParent = rootOfLayout;
            EnableClear = false;
            Show = true;           

            _root.PointerMoved += _root_PointerMoved;
            _root.PointerPressed += _root_PointerPressed;
            _root.PointerReleased += _root_PointerReleased;            
        }

        public bool EnableClear { get; set; }

        public bool Show { get; set; }
        public Vector2 PointsAt { get; set; }

        public virtual void Initialize(DeviceManager deviceManager)
        {
            _deviceManager = deviceManager;
            coloringBrush = new SolidColorBrush(deviceManager.ContextDirect2D, Colors.Red);
            
            //Get Image
            _formatConverter = DecodeImage();    
        
            //Show Image
            ShowImage();
        }


        private void ShowImage()
        {
            ShowCurrentImage(_formatConverter);
        }

        private void ShowCurrentImage(SharpDX.WIC.FormatConverter formatConverter)
        {
            var d2dContext = _deviceManager.ContextDirect2D;

            //Take decoded image data and get a BitmapSource from it
            bitmapSourceEffect = new SharpDX.Direct2D1.Effects.BitmapSourceEffect(d2dContext);
            bitmapSourceEffect.WicBitmapSource = formatConverter;            
        }

        public virtual void Render(TargetBase target)
        {
            if (!Show)
                return;           

            UpdateSize(target);
            var context2D = target.DeviceManager.ContextDirect2D;            

            context2D.BeginDraw();            

            if (EnableClear)
                context2D.Clear(Colors.White);         

            context2D.DrawImage(bitmapSourceEffect);

            Ellipse ellipse = new Ellipse(new DrawingPointF { X = PointsAt.X, Y = PointsAt.Y }, 15.0f, 15.0f);
            context2D.FillEllipse(ellipse, coloringBrush);            
           
            context2D.EndDraw();           
        }
      
        private void UpdateSize(TargetBase target)
        {
            var localSize = new DrawingSize((int)target.RenderTargetSize.Width, (int)target.RenderTargetSize.Height);
            if (localSize != screenSize)
            {
                screenSize = localSize;
                bitmapSourceEffect.ScaleSource = new Vector2((float)screenSize.Width / imageSize.Width, 
                    (float)screenSize.Height / imageSize.Height);
            }
        }

        private SharpDX.WIC.FormatConverter DecodeImage()
        {
            var path = Windows.ApplicationModel.Package.Current.InstalledLocation.Path;

            SharpDX.WIC.BitmapDecoder bitmapDecoder = new SharpDX.WIC.BitmapDecoder
                (
                    _deviceManager.WICFactory,
                    @"Assets\Page18.png",
                    SharpDX.IO.NativeFileAccess.Read,
                    SharpDX.WIC.DecodeOptions.CacheOnDemand
                );

            SharpDX.WIC.BitmapFrameDecode bitmapFrameDecode = bitmapDecoder.GetFrame(0);
            SharpDX.WIC.BitmapSource bitmapSource = new SharpDX.WIC.BitmapSource(bitmapFrameDecode.NativePointer);
            SharpDX.WIC.FormatConverter formatConverter = new SharpDX.WIC.FormatConverter(_deviceManager.WICFactory);
           
            formatConverter.Initialize(
                bitmapSource,
                SharpDX.WIC.PixelFormat.Format32bppBGRA,
                SharpDX.WIC.BitmapDitherType.None,
                null,
                0.0f,
                SharpDX.WIC.BitmapPaletteType.Custom
                );

            imageSize = formatConverter.Size;
            return formatConverter;
        }

        private void UpdatePointer(float x, float y)
        {
            PointsAt = new Vector2(x, y);           
        }

        private bool pointerPressed = false;

        void _root_PointerPressed(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
        {
            SetPointerPosition(e);
            pointerPressed = true;           
        }

        void _root_PointerMoved(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
        {
            if (pointerPressed)
            {
                SetPointerPosition(e);
            }             
        }

        private void SetPointerPosition(Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
        {
            var newPosition = e.GetCurrentPoint(null);
            var gtRoot = ((Windows.UI.Xaml.UIElement)_rootParent).TransformToVisual(_root);
            var rootPosition = gtRoot.TransformPoint(new Windows.Foundation.Point(newPosition.Position.X, newPosition.Position.Y));
            UpdatePointer((float)rootPosition.X, (float)rootPosition.Y);
        }

        void _root_PointerReleased(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
        {
            pointerPressed = false;
        }
    }
Last Edit: 1 year 9 months ago by erkjak2012. Reason: Request for feedback on submitted code.
The administrator has disabled public write access.

Re: Image Manipulation in Metro 1 year 7 months ago #708


  • Posts:22
  • ipavlic's Avatar
  • ipavlic
  • Junior Boarder
  • OFFLINE
Hey, did you make any progess on this? I'm having the same issue where the drawing is not saved (I'm doing DrawLine). When I draw the next line, I want the previous one to still be there on screen.

Thanks.
The administrator has disabled public write access.
  • Page:
  • 1
Time to create page: 0.207 seconds