Some Win2D effects not working with Composition APIs (unsupported?) - c#

I'm working with some Win2D effects and the Composition APIs in a UWP application and I'm having some issues with some of the available effects.
Here's a working method I use to blur some content behind a target element:
public static SpriteVisual GetAttachedBlur<T>(
[NotNull] this T element, float blur, int ms) where T : FrameworkElement
{
// Get the visual and the compositor
Visual visual = element.GetVisual();
Compositor compositor = visual.Compositor;
// Create the blur effect and the effect factory
GaussianBlurEffect blurEffect = new GaussianBlurEffect
{
Name = "Blur",
BlurAmount = blur,
BorderMode = EffectBorderMode.Hard,
Optimization = EffectOptimization.Balanced,
Source = new CompositionEffectSourceParameter("source")
};
CompositionEffectFactory effectFactory = compositor.CreateEffectFactory(blurEffect);
// Setup the rest of the effect
CompositionEffectBrush effectBrush = effectFactory.CreateBrush();
effectBrush.SetSourceParameter("source", compositor.CreateBackdropBrush());
// Assign the effect to a brush and display it
SpriteVisual sprite = compositor.CreateSpriteVisual();
sprite.Brush = effectBrush;
sprite.Size = new Vector2((float)element.ActualWidth, (float)element.ActualHeight);
ElementCompositionPreview.SetElementChildVisual(element, sprite);
return sprite;
}
Now, it works perfectly fine, but if I try to replace that GaussianBlurEffect effect with for example a DirectionalBlurEffect, I get an exception when I call the CreateEffectFactory method, saying that the input effect is not supported.
Now, I looked at the documentation and it seems that both those effects have the [NoComposition] attribution, so my first question is:
If both of the effects are unsupported ([NoComposition] attribute), why is it that the GaussianBlurEffect works fine and the DirectionalBlurEffect doesn't?
And the second question I have:
Is there another way to use/apply that DirectionalBlurEffect? There are quite a few Win2D effects that are marked as [NoComposition] that I would like to use, is there a workaround for that or do I just have to give up on them?
Thanks for your help!

Looks like the Win2D documentation wasn't updated, and right now only theGaussianBlurEffect is supported (see here)

Related

How Can I Make A VideoEffect Display Different Data Each Time It Is Added To A MediaClip?

Okay, I have a media composition and have added to it 3 media clips...
MediaComposition composition = new MediaComposition();
MediaClip clip1 = await MediaClip.CreateFromFileAsync(file);
MediaClip clip2 = await MediaClip.CreateFromFileAsync(file);
MediaClip clip3 = await MediaClip.CreateFromFileAsync(file);
composition.Clips.Add(clip1);
composition.Clips.Add(clip2;
composition.Clips.Add(clip3);
Now I want each clip to display a distinct caption and to accomplish this I have written a custom video effect that uses the DrawTextLayout method of the Win2D API. Let me be clear, I don't want to do this with overlays! But since video effects are built using the factory design pattern, how would I go about loading different data on each instance of the video effect?
Here is the core of MyVideoEffect:
public void ProcessFrame(ProcessVideoFrameContext context)
{
using (CanvasBitmap inputBitmap = CanvasBitmap.CreateFromDirect3D11Surface(canvasDevice, context.InputFrame.Direct3DSurface))
using (CanvasRenderTarget renderTarget = CanvasRenderTarget.CreateFromDirect3D11Surface(canvasDevice, context.OutputFrame.Direct3DSurface))
using (CanvasDrawingSession ds = renderTarget.CreateDrawingSession())
{
CanvasTextFormat textFormat = new CanvasTextFormat();
CanvasTextLayout textLayout = new CanvasTextLayout(canvasDevice, text, textFormat, width, height);
ds.DrawTextLayout(someTextLayout, x, y, Colors.Yellow);
}
}
Here, the "text" parameter should have a different value each time.
How can I specify the unique value when adding the video effect?
composition.Clips[0].VideoEffectDefinitions.Add(new VideoEffectDefinition(typeof(MyVideoEffect).FullName));
composition.Clips[1].VideoEffectDefinitions.Add(new VideoEffectDefinition(typeof(MyVideoEffect).FullName));
composition.Clips[2].VideoEffectDefinitions.Add(new VideoEffectDefinition(typeof(MyVideoEffect).FullName));
Is there an elegant way of doing this hopefully without having to resort to timers?

UWP Masking an image using another image as a mask

Does anybody know of any ways to use an image as a mask for another image in UWP, the only masking function I can see is CompositionMaskBrush which I don't believe can achieve what I want.
An example of what I'm looking to achieve is the following.
I have a solid black PNG in the shape of a mobile phone case, the user adds their own image which is then clipped and masked to the dimensions of the solid black PNG - Resulting in the image below.
Any help whatsoever would be greatly appreciated. I've spent quite a while browsing for a solution.
Example Image Here
Just posting for anybody else who needs and answer to this, but I finally managed to find a solution using Win2D and an Imageloader.
Here is a link to the ImageLoader. Note that I had to roll back a few versions in order make it work how the documentation states. The link below is to the version that I'm using. Anything later than this version will not work with the sample code I'm going to post.
https://www.nuget.org/packages/Robmikh.Util.CompositionImageLoader/0.4.0-alpha
private Compositor _compositor;
private IImageLoader _imageLoader;
private CompositionEffectFactory _effectFactory;
private async void InitMask()
{
// Store our Compositor and create our ImageLoader.
_compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
_imageLoader = ImageLoaderFactory.CreateImageLoader(_compositor);
// Setup our effect definition. First is the CompositeEffect that will take
// our sources and produce the intersection of the images (because we selected
// the DestinationIn mode for the effect). Next we take our CompositeEffect
// and make it the source of our next effect, the InvertEffect. This will take
// the intersection image and invert the colors. Finally we take that combined
// effect and put it through a HueRotationEffect, were we can adjust the colors
// using the Angle property (which we will animate below).
IGraphicsEffect graphicsEffect = new HueRotationEffect
{
Name = "hueEffect",
Angle = 0.0f,
Source = new InvertEffect
{
Source = new CompositeEffect
{
Mode = CanvasComposite.DestinationIn,
Sources =
{
new CompositionEffectSourceParameter("image"),
new CompositionEffectSourceParameter("mask")
}
}
}
};
// Create our effect factory using the effect definition and mark the Angle
// property as adjustable/animatable.
_effectFactory = _compositor.CreateEffectFactory(graphicsEffect, new string[] { "hueEffect.Angle" });
// Create MangedSurfaces for both our base image and the mask we'll be using.
// The mask is a transparent image with a white circle in the middle. This is
// important since the CompositeEffect will use just the circle for the
// intersectionsince the rest is transparent.
var managedImageSurface = await _imageLoader.CreateManagedSurfaceFromUriAsync(new Uri("http://sendus.pics/uploads/" + ImagePass + "/0.png", UriKind.Absolute));
//var managedImageSurface = await _imageLoader.CreateManagedSurfaceFromUriAsync(new Uri("ms-appx:///Assets/colour.jpg", UriKind.Absolute));
var managedMaskSurface = await _imageLoader.CreateManagedSurfaceFromUriAsync(new Uri("ms-appx:///" + MaskImage, UriKind.Absolute));
// Create brushes from our surfaces.
var imageBrush = _compositor.CreateSurfaceBrush(managedImageSurface.Surface);
var maskBrush = _compositor.CreateSurfaceBrush(managedMaskSurface.Surface);
// Create an setup our effect brush.Assign both the base image and mask image
// brushes as source parameters in the effect (with the same names we used in
// the effect definition). If we wanted, we could create many effect brushes
// and use different images in all of them.
var effectBrush = _effectFactory.CreateBrush();
effectBrush.SetSourceParameter("image", imageBrush);
effectBrush.SetSourceParameter("mask", maskBrush);
// All that's left is to create a visual, assign the effect brush to the Brush
// property, and attach it into the tree...
var visual = _compositor.CreateSpriteVisual();
visual.Size = new Vector2(MaskH, MaskW);
visual.Offset = new Vector3(0, 300, 0);
visual.Brush = effectBrush;
ElementCompositionPreview.SetElementChildVisual(this, visual);
}

Xamarin iOS - Mapkit ArgumentNullException: Value cannot be null

My question is similar to this but I am not sure how my code relates to the answers given in the other post.
I am using MapKit in Xamarin iOS to create a custom map for my Xamarin iOS project. I have a few different custom things happening at the moment, and am using Polygons annotations and now circles that are added to my map.
I have just started implementing adding MKCircle to my map, but when I try to add Circle Overlays to my map I am receiving this error:
System.ArgumentNullException: Value cannot be null. Parameter name: polygon
I think it is being I trying to return the same overlay to two renderers, but I am not sure how to ammend this. Here is my code:
for(int i=0; i < hazards.Count; i++) //This adds 3 circles in my example
{
LatLong ltlng = JsonConvert.DeserializeObject<LatLong>(hazards[i].coordinates);
coords[i].Latitude = Convert.ToDouble(ltlng.latitude);
coords[i].Longitude = Convert.ToDouble(ltlng.longitude);
var overlay = MKCircle.Circle(coords[i], Convert.ToDouble(hazards[i].radius));
nativeMap.AddOverlay(overlay); //this is the suspected problem
}
And my renderer code here:
MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlayWrapper)
{
if (!Equals(overlayWrapper, null))
{
var overlay = ObjCRuntime.Runtime.GetNSObject(overlayWrapper.Handle) as IMKOverlay;
polygonRenderer = new MKPolygonRenderer(overlay as MKPolygon)
{
FillColor = UIColor.Red,
StrokeColor = UIColor.Blue,
Alpha = 0.4f,
LineWidth = 9
};
}
return polygonRenderer;
}
Do I need to add something to my renderer code like this?:
circleRenderer = new MKCircleRenderer(overlay as MKCircle){};
It appears all your overlays are MKCircle based:
var overlay = MKCircle.Circle(coords[i]
In your GetOverlayRenderer you casting all overlays received as MKPolygon objects which will result in a null object.
polygonRenderer = new MKPolygonRenderer(overlay as MKPolygon)
You are then trying to create a MKPolygonRenderer render for each of your overlays which would not work if you actually did have an MKCircle-based overlay.
If all your overlays are MKCircle based, then yes use:
new MKCircleRenderer(overlay as MKCircle){};

How to ensure UWP app is always full screen on launch?

Is there a way (either C# or XAML) I can maximize a UWP app window even after I resized and closed it previously on desktop?
I have tried with ApplicationViewWindowingMode.FullScreen but this makes the app go entire full screen and covers the Windows Taskbar too.
You can use another value PreferredLaunchViewSize from ApplicationViewWindowingMode and then set ApplicationView.PreferredLaunchViewSize but the key is to find out what the size is going to be.
Theoretically, you could use a really big number and window would just extend to the max it could be. However, it's probably safer to just calculate the screen dimensions in effective pixels.
So if you just call the following method before InitializeComponent(); on your main Page, it should maximize the window on startup.
private static void MaximizeWindowOnLoad()
{
// Get how big the window can be in epx.
var bounds = ApplicationView.GetForCurrentView().VisibleBounds;
ApplicationView.PreferredLaunchViewSize = new Size(bounds.Width, bounds.Height);
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.PreferredLaunchViewSize;
}
Note the app somehow remembers these settings even after you uninstalled it. If you ever want to change back to the default behavior (app starts up with the previous window size), simply call ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.Auto; once and remove all the code.
Update
Looks like in the latest Windows 10 build, ApplicationView.GetForCurrentView().VisibleBounds no longer returns the full window size in effective pixels anymore. So we now need a new way to calculate it.
Turns out it's quite straightforward since the DisplayInformation class also gives us the screen resolution as well as the scale factor.
The following is the updated code -
public MainPage()
{
MaximizeWindowOnLoad();
InitializeComponent();
void MaximizeWindowOnLoad()
{
var view = DisplayInformation.GetForCurrentView();
// Get the screen resolution (APIs available from 14393 onward).
var resolution = new Size(view.ScreenWidthInRawPixels, view.ScreenHeightInRawPixels);
// Calculate the screen size in effective pixels.
// Note the height of the Windows Taskbar is ignored here since the app will only be given the maxium available size.
var scale = view.ResolutionScale == ResolutionScale.Invalid ? 1 : view.RawPixelsPerViewPixel;
var bounds = new Size(resolution.Width / scale, resolution.Height / scale);
ApplicationView.PreferredLaunchViewSize = new Size(bounds.Width, bounds.Height);
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.PreferredLaunchViewSize;
}
}
If you want to MAXIMISE your app on launch you can use the following:
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.Maximized;
But be sure to put it into the Loaded Event for your Page or it will not work!
I've too few points to comment directly. None of the above resized to a maximized view for me (or the below single-line ApplicationViewWindowingMode.Maximized method), but I have used some of the answers to come up with something that worked for me. It is still very clunky however. The screen size given in 'DisplayInformation' is too big to allow the page to be resized directly to it. Trying to do it didn't work and I had to take 60 off height and width to get it to return 'true', therefore I have the following bit of nonsense which worked, maybe it will help someone else find a better answer. It goes in the page/window loaded event. Nothing else needs to be added elsewhere.
private void Page_Loaded(object sender, RoutedEventArgs e)
{
var view = ApplicationView.GetForCurrentView();
var displayInfo = DisplayInformation.GetForCurrentView();
double x = ActualWidth;
double y = ActualHeight;
bool answer = true;
// Get the screen resolution (APIs available from 14393 onward).
var resolution = new Size(displayInfo.ScreenWidthInRawPixels-60, displayInfo.ScreenHeightInRawPixels-60);
answer = view.TryResizeView(resolution); //This should return true if the resize is successful
if (answer)
{
x = displayInfo.ScreenWidthInRawPixels - 60;
y = displayInfo.ScreenHeightInRawPixels - 60;
}
answer = true;
while (answer == true)
{
x++;
answer = view.TryResizeView(new Size { Width = x, Height = y });
}
x = x - 1;
answer = true;
while (answer == true)
{
y++;
answer = view.TryResizeView(new Size { Width = x, Height = y });
}
Adding the following line to the OnLaunched event under App.xaml.cs did it for me.
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.FullScreen;
NOTE: Make sure to add it before the following line
Window.Current.Activate();
If you like to go fullscreen at the runtime use the following line.
ApplicationView.GetForCurrentView().TryEnterFullScreenMode();
I have this one liner that works as I expected Justins code to, but for some reason, when using Justins answer, my window would not be maximized... But then I changed something that did make it maximized but I lost all my fluent design such as Acrylic and RevealHighlite...
So I came up with this one liner which keeps all of my fluent design principles happy:
ApplicationView.GetForCurrentView().TryEnterFullScreenMode();
Something to note:
I did try Justins answer, and I am using his method of MaximizeWindowOnLoad() which I have called straight after the initializeComponent();
Full overview:
public class()
{
this.InitializeComponent();
MaximizeWindowOnLoad();
}
private static void MaximizeWindowOnLoad()
{
ApplicationView.GetForCurrentView().TryEnterFullScreenMode();
}

Apply easing function to animation behind code

I managed to build my storyboard behind code. I don't know how to add easing functions though. I am looking for something like:
DoubleAnimation FadelnTBAnimation = new DoubleAnimation();
FadelnTBAnimation.To = 0;
FadelnTBAnimation.BeginTime = TimeSpan.FromSeconds(0);
FadelnTBAnimation.Duration = new Duration(TimeSpan.FromSeconds(1));
FadelnTBAnimation.EasingFunction = EasingMode.EaseInOut; // this line gives an error
How could I apply easing functions with c#?
The reason why I find useful to build the storyboard with code Is because I am applying the same animation to several objects and sometimes it does not work when I bind the target property in XAML.
You need to create an instance of IEasingFunction (http://msdn.microsoft.com/en-us/library/system.windows.media.animation.ieasingfunction.aspx). There is a list of implementation classes at the bottom of that documentation entry, the most common of which is probably CubicEase or QuadraticEase.
There is a difference between the easing-function and the easing-mode.
Here is a short example for Win-8 (not WPF):
SineEase easingFunction = new SineEase();
easingFunction.EasingMode = EasingMode.EaseIn;
animation.EasingFunction = easingFunction;
A simple way to add the easing function in your case would be to just add it to the double animation.
FadelnTBAnimation.EasingFunction = new QuarticEase(); // for example

Categories

Resources