-
-
Notifications
You must be signed in to change notification settings - Fork 122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a base Win2D-compatible canvas effect type #449
Comments
I think this using var _ = GetCanvasImageLock(); is an anti-pattern: it's way too easy to forget to do this, and also very easy to get confused as to when it's necessary. What race conditions are you worried about? These things are generally only used single-threaded, and if the D2D factory is created multithreaded then you're generally fine. And if you are using a It might be better to establish a pattern like with I use in Paint.NET's public enum CanvasEffectInvalidationType // or, you know, some better name could be chosen
{
Recreate,
Update
}
public abstract unsafe class CanvasEffect : ICanvasImage // ICanvasImageInterop
{
...
protected abstract ICanvasImage CreateCanvasImage();
protected virtual void UpdateCanvasImage();
protected void InvalidateCanvasImage(CanvasEffectInvalidationType invalidationType = CanvasEffectInvalidationType.Recreate);
...
} When creating the Then when implementing the derived sample effect, you'd have it like this: public sealed partial class SampleEffect : CanvasEffect
{
private GaussianBlurEffect? blurEffect;
protected override ICanvasImage CreateCanvasImage()
{
this.blurEffect = new GaussianBlurEffect();
return this.blurEffect;
}
protected override void UpdateCanvasImage()
{
this.blurEffect.BlurAmount = this.blurAmount;
}
private float blurAmount;
public float BlurAmount
{
get => this.blurAmount;
set
{
this.blurAmount = value;
InvalidateCanvasImage(CanvasEffectInvalidationType.Update);
}
}
} This is much simpler to work with. You don't have to worry about checking whether This pattern is also much more performant when you have a complicated effect graph. Recreating the graph every time one little property changes would be very expensive. And the pattern from your code above adds a little complexity to every location that wants to modify the |
Also, a derived class does not have to implement |
Right. Yeah maybe the thread-safety thing was not actually needed 🤔 Also I'm thinking it'd be nice to have a What about something like this: public enum CanvasEffectInvalidationType
{
Creation,
Update
}
public abstract unsafe class CanvasEffect : ICanvasImage // ICanvasImageInterop
{
protected abstract ICanvasImage CreateCanvasImage();
protected virtual void UpdateCanvasImage();
protected void InvalidateCanvasImage(CanvasEffectInvalidationType invalidationType = CanvasEffectInvalidationType.Update);
protected void SetAndInvalidateCanvasImage<T>(ref T storage, T value, CanvasEffectInvalidationType invalidationType = CanvasEffectInvalidationType.Update);
} Then you could do: public sealed partial class SampleEffect : CanvasEffect
{
private GaussianBlurEffect? blurEffect;
protected override ICanvasImage CreateCanvasImage()
{
this.blurEffect = new GaussianBlurEffect();
return this.blurEffect;
}
protected override void UpdateCanvasImage()
{
this.blurEffect.BlurAmount = this.blurAmount;
}
private float blurAmount;
public float BlurAmount
{
get => this.blurAmount;
set => SetAndInvalidateCanvasImage(ref this.blurAmount, value);
}
} And then I guess next year once C# 12 lands: public sealed partial class SampleEffect : CanvasEffect
{
private GaussianBlurEffect? blurEffect;
protected override ICanvasImage CreateCanvasImage()
{
this.blurEffect = new GaussianBlurEffect();
return this.blurEffect;
}
protected override void UpdateCanvasImage()
{
this.blurEffect.BlurAmount = this.blurAmount;
}
public float BlurAmount
{
get => field;
set => SetAndInvalidateCanvasImage(ref field, value);
}
} Which is very very nice 😄 What do you think? |
Description
This issue tracks designing and adding a base type that would allow developers to "package" their own custom Win2D-compatible effects. This is crucial to allow consumers of those effect to benefit from an easy to use API surface that's abstracted from the internal implementation details of the effect. For instance, consumers shouldn't need to see or directly use transform mappings or constant buffers. Instead, the packaged effect would expose the correct properties to modify the effect-specific state in a nice way.
Proposed API (work in progress)
A few key points:
GetBounds
methods are simply forwarding the logic to the wrappedICanvasImage
objectICanvasImageInterop
is internal and explicitly implemented, as it's a COM interface and not a public API.CreateCanvasImage
is used to create the effect graph from the implementing type.CanvasEffect
takes care of caching this.InvalidateCnavasImage
is used by derived types to signal when the effect graph is no longer valid, and the cached value should be discarded. This will causeCanvasEffect
to callCreateCanvasImage
again the next time an image is needed.Example use
Here's a very simple wrapper packaged effect:
The text was updated successfully, but these errors were encountered: