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;
}
}