How to make Win2D BlendEffect apply to current drawing surface (background)? - c#

I want to draw some images on to existing canvas using multiply blend mode. However, I don't know how to do it as the BlendEffect class require me to assign the Background variable but that is suppose to be the canvas which I could not put there.
private void OnDrawCanvas(CanvasControl sender, CanvasDrawEventArgs args)
{
var list = new LinkedList<ImageNode>();
mRootNode.GetTraverseList(list, false);
foreach (var item in list)
{
if (!item.treeVisible)
continue;
if (item.mLayerPixels != null)
{
if (item.mLayer.BlendModeKey == BlendModeType.MULTIPLY)
{
var blendEffect = new BlendEffect()
{
//Background = ???, // what to put????
Foreground = item.mLayerPixels,
Mode = BlendEffectMode.Multiply
};
args.DrawingSession.DrawImage(blendEffect, item.mLayer.Left, item.mLayer.Top);
}
else
{
args.DrawingSession.DrawImage(item.mLayerPixels, item.mLayer.Left, item.mLayer.Top);
}
}
}
}

I ended up creating an offscreen CanvasRenderTarget to do the blending. When all the drawing is done, I create a CanvasBitmap from CanvasRenderTarget which allow me to draw the final result to the UI with args.DrawingSession.DrawImage();

Related

Not all MapIcons get updated on map?

Im adding multiple MapIcons to my UWP app MapControl. I want to change the MapIcon size based map zoom level.
This works almost ok, but when I update marker sizes (call UpdatMarkerSizes()) not all MapIcons get new image.
I have around 200 MapIcons in my map, which I try to update. Is this some performance issue or how I should try to update all MapIcons?
Here is how I initially add MapIcons:
public void AddMapIcons(IReadOnlyCollection<IItem> items)
{
var icon = (CurrentMarkerIconSize == MarkerIconSize.Small) ? _markerSmall : _markerNormal;
foreach (var item in items)
{
var stopIcon = new MapIcon
{
Location = new Geopoint(item.Location),
NormalizedAnchorPoint = new Point(0.5, 0.5),
ZIndex = 5,
CollisionBehaviorDesired = MapElementCollisionBehavior.RemainVisible,
Image = icon
};
MapControl.MapElements.Add(stopIcon);
}
}
And here is my MapIcon Image update functionality, this is called when map zoomlevel is changed.
private void UpdatMarkerSizes(MarkerIconSize newSize)
{
var newImage = (newSize == StopIconSize.Small) ? _markerSmall : _markerNormal;
foreach (var element in MapControl.MapElements)
{
(element as MapIcon).Image = newImage;
}
}
UPDATE 1:
I just noticed that if I also update MapIcon NormalizedAnchorPoint, ZIndex and CollisionBehaviorDesired (set same value again), this issue is not so bad. Previously when I updated my markers (UpdatMarkerSizes()) there was always couple of markers which did not get the new image. Now after this update, I get rarely one or two markers which did not get new image.
Updated code:
private void UpdatMarkerSizes(MarkerIconSize newSize)
{
var newImage = (newSize == StopIconSize.Small) ? _markerSmall : _markerNormal;
foreach (var element in MapControl.MapElements)
{
var mapIconElement = element as MapIcon;
if(mapIconElement != null)
{
mapIconElement.Image = newImage;
mapIconElement.NormalizedAnchorPoint = new Point(0.5, 0.5);
mapIconElement.ZIndex = 5;
mapIconElement.CollisionBehaviorDesired = MapElementCollisionBehavior.RemainVisible;
}
}
}
UPDATE 2:
I think that my solution in update 1 is good enough for now. I have posted new question which is related to MapIcons and their image updating too: Dynamically update collection of MapIcons, update process gets out of sync?

Windows 8 Secondary Tile using Templates

I have a windows store app that currently allows my users to pin and unpin tiles to the start menu. I am able to set a background and logo and text but the problem is I want the tile to instead of a static logo with text I want it to be sort of a live tile.
So it would go from one side with just an image and then flip and the other side would be my app information. I know you are able to do this for regular live tiles and using xml, but I am using c# and would like for this to work in my secondary tile.
Any help on how to go about that would be great.
Here is some code I used to create the secondary tile:
private async void PinButton_OnClick (object sender, RoutedEventArgs e)
{
var item = createdItem.SelectedItem;
if (item != null)
{
var logo = new Uri(item.image);
if (item != null)
{
var smallLogo = new Uri(item.image);
var wideLogo = new Uri(item.image);
}
string tileActivationArguments = logoSecondaryTileId + "WasPinnedAt=" +
DateTime.Now.ToLocalTime();
logoSecondaryTileId = item.ID+ counter.ToString();
counter++;
//Create the tile
SecondaryTile tile = new SecondaryTile(logoSecondaryTileId, item.Code, item.FirstName + " " + item.LastName,
tileActivationArguments, TileOptions.ShowNameOnLogo | TileOptions.ShowNameOnWideLogo, logo);
if (item.SelectedItem is Details)
{
tile.ForegroundText = ForegroundText.Light;
tile.BackgroundColor = Colors.Black;
bool isPinned =
await
tile.RequestCreateForSelectionAsync(GetElementRect((FrameworkElement)sender),
Placement.Default);
if (isPinned)
{
Messages.InvokeToast(Toast.Created);
UnpinButton.Visibility = Visibility.Visible;
pinButton.Visibility = Visibility.Collapsed;
}
else
{
Messages.InvokeToast(Toast.Error);
}
if (SecondaryTile.Exists(logoSecondaryTileId))
{
var dialog = new MessageDialog("Already exists!")
{
Title = "Unable to Pin Tile!"
};
dialog.Commands.Add(new UICommand("Okay", new UICommandInvokedHandler(CommandHandler)));
dialog.CancelCommandIndex = 1;
await dialog.ShowAsync();
}
}
}
}
You just need to define the tile using a template that has a front and back. See here for how to define secondary tiles with templates: Is it possible to use tile templates with secondary tiles in a Windows Store App?. See here for the list of possible templates and which have backs: http://msdn.microsoft.com/en-us/library/windows/apps/hh761491.aspx

WPF Drawing context

In my wpf application i am drawing a lot of geometries as below. My requirement is to change the color of drawingvisual with out redrawing it? any possibilities in wpf?
using (DrawingContext vDrawingContext = vDrawingVisual.RenderOpen())
{
StreamGeometry vGeom = GetCutGeometry(mLength, mWidth);
vDrawingContext.DrawGeometry(mBackGroundBrush, ForeGroundPen, vGeom);
vDrawingContext.Close();
VisualChildren.Add(vDrawingVisual);
}
How could be mBackGroundBrush dyamic colors?
Provided that mBackGroundBrush is a modifiable SolidColorBrush (i.e. it is created in your application and none of the predefined brushes), you could simply change its Color property. That will change the fill color of each drawn geometry with redrawing.
private SolidColorBrush mBackGroundBrush = new SolidColorBrush(Colors.Black);
...
mBackGroundBrush.Color = Colors.Red;
or
mBackGroundBrush.Color = Color.FromArgb(255, 255, 0, 0);
I have done one work around as below. seems working.
///Kept as arefrence while initial drawing phase.
private DrawingVisual mDrawingVisual = null;
if (null != mDrawingVisual)
{
using (DrawingContext vDrawingContext = mDrawingVisual.RenderOpen())
{
DrawingGroup vDrawingGroup = VisualTreeHelper.GetDrawing(mDrawingVisual);
if (null != vDrawingGroup)
{
foreach (Drawing vDrawing in vDrawingGroup.Children)
{
GeometryDrawing vGeometryDrawing = vDrawing as GeometryDrawing;
if (null != vGeometryDrawing)
{
vGeometryDrawing.Brush = mBackGroundBrush;
}
}
}
vDrawingContext.DrawDrawing(vDrawingGroup);
vDrawingContext.Close();
}
}

c# wpf multiple line drawing on canvas with kinect sensor

So first of all let me show you what I want to accomplish:
http://www.youtube.com/watch?NR=1&feature=endscreen&v=FdwZEepJxJ4
There the guy is drawing a long curved line using kinect. I need to make something similar. I had several ideas with just simple code like
layoutGrid.Children.Add(myLine)
This works, but not the way I want.
I was thinking I could use drawingContext to draw lines with:
drawingContext.DrawLine(drawPen, pointprev, point1);
The problem is that by using this drawing method the lines I draw are not kept on the screen. It's like the line is always redrawn. How do I keep my lines?
By the way I also don't understand why the skeleton drawn by the same method gets updated and we don't need to clear the previous lines in the image?
private void WindowLoaded(object sender, RoutedEventArgs e)
{
// Create the drawing group we'll use for drawing
this.drawingGroup = new DrawingGroup();
// Create an image source that we can use in our image control
this.imageSource = new DrawingImage(this.drawingGroup);
// Display the drawing using our image control
Image2.Source = this.imageSource;
************Some other code here*******
}
private void SensorSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
{
*****************Some code to get skeleton data here********
using (DrawingContext dc = this.drawingGroup.Open())
{
if (skeletons.Length != 0)
{
foreach (Skeleton skel in skeletons)
{
if (skel.TrackingState == SkeletonTrackingState.Tracked)
{
this.DrawBonesAndJoints(skel, dc);
}
}
}
}
}
private void DrawBonesAndJoints(Skeleton skeleton, DrawingContext drawingContext)
{
***********************Some code to draw skeleton***************
Joint righthand = skeleton.Joints[JointType.HandRight];
if (init == true)
{
pointprev = SkeletonPointToScreen(righthand.Position);
init = false;
return;
}
Point point1 = SkeletonPointToScreen(righthand.Position);
Pen drawPen = this.trackedBonePen;
drawingContext.DrawLine(drawPen, pointprev, point1);
pointprev = point1;
}
private Point SkeletonPointToScreen(SkeletonPoint skelpoint)
{
// Convert point to depth space.
// We are not using depth directly, but we do want the points in our 640x480 output resolution.
DepthImagePoint depthPoint = this.sensor.CoordinateMapper.MapSkeletonPointToDepthPoint(skelpoint, DepthImageFormat.Resolution640x480Fps30);
return new Point(depthPoint.X, depthPoint.Y);
}
private void DrawBone(Skeleton skeleton, DrawingContext drawingContext, JointType jointType0, JointType jointType1)
{
Joint joint0 = skeleton.Joints[jointType0];
Joint joint1 = skeleton.Joints[jointType1];
Pen drawPen = this.trackedBonePen;
drawingContext.DrawLine(drawPen, this.SkeletonPointToScreen(joint0.Position), this.SkeletonPointToScreen(joint1.Position));
}
How do I keep my lines?
You would have to either use a Polyline or draw a PathGeometry or a StreamGeometry into a DrawingContext by DrawGeometry. The geometry would have to contain one or more polyline segments.

C# Rendering Performance Difference

I am using Dynamic Data Display to draw some plots.
It provides a class called MarkerPointsGraph to render markers to the screen. The OnRender method is called during Render and this in turn calls the appropriate marker's (Circle, Triangle, etc.) render method.
protected override void OnRenderCore(DrawingContext dc, RenderState state)
{
if (DataSource == null) return;
if (Marker == null) return;
var transform = Plotter2D.Viewport.Transform;
DataRect bounds = DataRect.Empty;
using (IPointEnumerator enumerator = DataSource.GetEnumerator(GetContext()))
{
Point point = new Point();
while (enumerator.MoveNext())
{
enumerator.GetCurrent(ref point);
enumerator.ApplyMappings(Marker);
//Point screenPoint = point.Transform(state.Visible, state.Output);
Point screenPoint = point.DataToScreen(transform);
bounds = DataRect.Union(bounds, point);
Marker.Render(dc, screenPoint);
}
}
Viewport2D.SetContentBounds(this, bounds);
}
/// <summary>Renders circle around each point of graph</summary>
public class CirclePointMarker : ShapePointMarker {
public override void Render(DrawingContext dc, Point screenPoint) {
dc.DrawEllipse(Fill, Pen, screenPoint, Size / 2, Size / 2);
}
}
Now, my calling code is as below.
plotter.AddLineGraph(
data.Data,
new Pen
{
Brush = Brushes.Violet,
DashStyle = DashStyles.DashDot,
Thickness = 3
},
GetMarker(data),
new StandardDescription(data.Title));
The main difference I noticed in performance is in the methods below.
Referring to data from the ChartData class
private ShapePointMarker GetMarker(ChartData data)
{
ShapePointMarker marker = null;
switch (data.MarkerShape)
{
case Shapes.Circle:
marker = new CirclePointMarker();
break;
case Shapes.Triangle:
marker = new TrianglePointMarker();
break;
case Shapes.None:
default:
marker = null;
break;
}
if (marker != null)
{
//Referring to stuff here.
marker.Fill = new SolidColorBrush(data.MarkerColor);
marker.Size = data.MarkerSize;
}
return marker;
}
Hardcoding the data in place of the reference.
private ShapePointMarker GetMarker(ChartData data)
{
ShapePointMarker marker = null;
switch (data.MarkerShape)
{
case Shapes.Circle:
marker = new CirclePointMarker();
break;
case Shapes.Triangle:
marker = new TrianglePointMarker();
break;
case Shapes.None:
default:
marker = null;
break;
}
if (marker != null)
{
//Hard coding stuff here.
marker.Fill = Brushes.Red;
marker.Size = 5;
}
return marker;
}
I see an improvement of nearly 100x in the render times merely by changing the reference based data in the GetMarker method to a hard coded value.
What behaviour of C# causes this difference?
EDIT: The MarkerPointsGraph does not create an object of Marker for each point. It merely calls the render method.
You could try freezing your Brush:
if (marker != null)
{
Brush b = new SolidColorBrush(data.MarkerColor);
b.Freeze();
marker.Fill = b;
marker.Size = data.MarkerSize;
}
Brushes.Red is already Frozen that might be why you see the performance increase.
Without knowing too much more about your setup, your issue likely lies in the fact that you are creating a SolidColorBrush in a relatively tight loop (and not disposing them, but that's another issue). That object has a fair amount of overhead associated with creating a new one, whereas using Brushes.Red uses the same instance and so doesn't have the same issues. If you were to create your own brush once I don't think you'd see the same problems.

Categories