目前Avalonia无法继承Effect类重写,因为构造函数是internal。我们重写一个GrayscaleImage实现灰化。

 GrayscaleImage类

    public class GrayscaleImage : Control
{
public static readonly StyledProperty<IImage?> SourceProperty =
AvaloniaProperty.Register<GrayscaleImage, IImage?>(nameof(Source)); public static readonly StyledProperty<bool> IsGrayscaleProperty =
AvaloniaProperty.Register<GrayscaleImage, bool>(nameof(IsGrayscale), true); public static readonly StyledProperty<Stretch> StretchProperty =
AvaloniaProperty.Register<GrayscaleImage, Stretch>(nameof(Stretch), Stretch.Uniform); public IImage? Source
{
get => GetValue(SourceProperty);
set => SetValue(SourceProperty, value);
} public bool IsGrayscale
{
get => GetValue(IsGrayscaleProperty);
set => SetValue(IsGrayscaleProperty, value);
} public Stretch Stretch
{
get => GetValue(StretchProperty);
set => SetValue(StretchProperty, value);
} static GrayscaleImage()
{
AffectsRender<GrayscaleImage>(IsGrayscaleProperty, SourceProperty, StretchProperty);
AffectsMeasure<GrayscaleImage>(SourceProperty);
} protected override Size MeasureOverride(Size availableSize)
{
if (Source is Bitmap bitmap)
{
var sourceSize = new Size(bitmap.PixelSize.Width, bitmap.PixelSize.Height); if (double.IsInfinity(availableSize.Width) && double.IsInfinity(availableSize.Height))
return sourceSize; if (Stretch == Stretch.None)
return sourceSize; return CalculateStretchedSize(sourceSize, availableSize, Stretch);
} return base.MeasureOverride(availableSize);
} private Size CalculateStretchedSize(Size sourceSize, Size availableSize, Stretch stretch)
{
double scaleX = 1.0;
double scaleY = 1.0; bool isConstrainedWidth = !double.IsInfinity(availableSize.Width);
bool isConstrainedHeight = !double.IsInfinity(availableSize.Height); if ((isConstrainedWidth || isConstrainedHeight) && sourceSize.Width > 0 && sourceSize.Height > 0)
{
scaleX = isConstrainedWidth ? availableSize.Width / sourceSize.Width : scaleX;
scaleY = isConstrainedHeight ? availableSize.Height / sourceSize.Height : scaleY; if (stretch == Stretch.Uniform)
{
scaleX = scaleY = Math.Min(scaleX, scaleY);
}
else if (stretch == Stretch.UniformToFill)
{
scaleX = scaleY = Math.Max(scaleX, scaleY);
} return new Size(sourceSize.Width * scaleX, sourceSize.Height * scaleY);
} return sourceSize;
} public override void Render(DrawingContext context)
{
base.Render(context); if (Source is not Bitmap bitmap || Bounds.Width <= 0 || Bounds.Height <= 0)
return; context.Custom(new GrayscaleDrawOperation(new Rect(Bounds.Size), bitmap, IsGrayscale, Stretch));
} private sealed class GrayscaleDrawOperation : ICustomDrawOperation
{
private readonly Rect BoundsRect;
private readonly Bitmap BitmapSource;
private readonly bool ApplyGrayscale;
private readonly Stretch StretchMode; public GrayscaleDrawOperation(Rect boundsRect, Bitmap bitmap, bool applyGrayscale, Stretch stretch)
{
BoundsRect = boundsRect;
BitmapSource = bitmap;
ApplyGrayscale = applyGrayscale;
StretchMode = stretch;
} public Rect Bounds => BoundsRect; public void Dispose()
{
} public bool HitTest(Point point) => BoundsRect.Contains(point); public bool Equals(ICustomDrawOperation? other)
{
return other is GrayscaleDrawOperation op &&
op.BitmapSource == BitmapSource &&
op.BoundsRect == BoundsRect &&
op.ApplyGrayscale == ApplyGrayscale &&
op.StretchMode == StretchMode;
} public void Render(ImmediateDrawingContext context)
{
var leaseFeature = context.TryGetFeature<ISkiaSharpApiLeaseFeature>();
if (leaseFeature is null)
return; using var lease = leaseFeature.Lease();
var canvas = lease.SkCanvas; using var skImage = ToSkImage(BitmapSource);
using var paint = new SKPaint(); if (ApplyGrayscale)
{
float[] colorMatrix = {
0.299f, 0.587f, 0.114f, 0, 0,
0.299f, 0.587f, 0.114f, 0, 0,
0.299f, 0.587f, 0.114f, 0, 0,
0, 0, 0, 1, 0
};
paint.ColorFilter = SKColorFilter.CreateColorMatrix(colorMatrix);
} var sourceSize = new SKSize(skImage.Width, skImage.Height);
var destRect = CalculateDestinationRect(BoundsRect, sourceSize, StretchMode); canvas.Save();
canvas.DrawImage(skImage, destRect, paint);
canvas.Restore();
} private SKRect CalculateDestinationRect(Rect bounds, SKSize sourceSize, Stretch stretch)
{
double sourceWidth = sourceSize.Width;
double sourceHeight = sourceSize.Height;
double destWidth = bounds.Width;
double destHeight = bounds.Height; if (stretch == Stretch.None)
{
return new SKRect(0, 0, (float)sourceWidth, (float)sourceHeight);
} if (stretch == Stretch.Fill)
{
return new SKRect(0, 0, (float)destWidth, (float)destHeight);
} double scaleX = destWidth / sourceWidth;
double scaleY = destHeight / sourceHeight; if (stretch == Stretch.Uniform)
{
double scale = Math.Min(scaleX, scaleY);
double scaledWidth = sourceWidth * scale;
double scaledHeight = sourceHeight * scale;
double x = (destWidth - scaledWidth) / 2;
double y = (destHeight - scaledHeight) / 2; return new SKRect((float)x, (float)y, (float)(x + scaledWidth), (float)(y + scaledHeight));
} if (stretch == Stretch.UniformToFill)
{
double scale = Math.Max(scaleX, scaleY);
double scaledWidth = sourceWidth * scale;
double scaledHeight = sourceHeight * scale;
double x = (destWidth - scaledWidth) / 2;
double y = (destHeight - scaledHeight) / 2; return new SKRect((float)x, (float)y, (float)(x + scaledWidth), (float)(y + scaledHeight));
} return new SKRect(0, 0, (float)destWidth, (float)destHeight);
} private static SKImage ToSkImage(Bitmap bitmap)
{
using var memoryStream = new MemoryStream();
bitmap.Save(memoryStream);
memoryStream.Position = 0;
return SKImage.FromEncodedData(memoryStream);
}
}
}

CustomPixelShader.axaml代码

<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Height="386" Width="268"
x:Class="AvaloniaUI.CustomPixelShader"
Title="CustomPixelShader">
<StackPanel>
<GrayscaleImage Margin="5" Source="avares://AvaloniaUI/Resources/Images/harpsichord.jpg" IsGrayscale="{Binding #chkEffect.IsChecked}">
</GrayscaleImage> <CheckBox Name="chkEffect" Margin="5" Content="Effect enabled" IsChecked="True"></CheckBox>
</StackPanel>
</Window>

CustomPixelShader.axaml.cs代码

using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Media; namespace AvaloniaUI; public partial class CustomPixelShader : Window
{
public CustomPixelShader()
{
InitializeComponent();
}
}

运行效果

C# Avalonia 13- MoreDrawing - CustomPixelShader的更多相关文章

  1. 【.NET6+WPF+Avalonia】开发支持跨平台的WPF应用程序以及基于ubuntu系统的演示

    前言:随着跨平台越来越流行,.net core支持跨平台至今也有好几年的光景了.但是目前基于.net的跨平台,大多数还是在使用B/S架构的跨平台上:至于C/S架构,大部分人可能会选择QT进行开发,或者 ...

  2. .NET跨平台框架选择之一 - Avalonia UI

    本文阅读目录 1. Avalonia UI简介 Avalonia UI文档教程:https://docs.avaloniaui.net/docs/getting-started 随着跨平台越来越流行, ...

  3. TechEmpower 13轮测试中的ASP.NET Core性能测试

    应用性能直接影响到托管服务的成本,因此公司在开发应用时需要格外注意应用所使用的Web框架,初创公司尤其如此.此外,糟糕的应用性能也会影响到用户体验,甚至会因此受到相关搜索引擎的降级处罚.在选择框架时, ...

  4. .NET平台开源项目速览(13)机器学习组件Accord.NET框架功能介绍

    Accord.NET Framework是在AForge.NET项目的基础上封装和进一步开发而来.因为AForge.NET更注重与一些底层和广度,而Accord.NET Framework更注重与机器 ...

  5. 转:ORA-15186: ASMLIB error function = [asm_open], error = [1], 2009-05-24 13:57:38

    转:ORA-15186: ASMLIB error function = [asm_open], error = [1], 2009-05-24 13:57:38http://space.itpub. ...

  6. IIS启动失败,启动Windows Process Activation Service时,出现错误13:数据无效 ;HTTP 错误 401.2 - Unauthorized 由于身份验证头无效,您无权查看此页

    因为修改过管理员账号的密码后重启服务器导致IIS无法启动,出现已下异常 1.解决:"启动Windows Process Activation Service时,出现错误13:数据无效&quo ...

  7. CSharpGL(13)用GLSL实现点光源(point light)和平行光源(directional light)的漫反射(diffuse reflection)

    CSharpGL(13)用GLSL实现点光源(point light)和平行光源(directional light)的漫反射(diffuse reflection) 2016-08-13 由于CSh ...

  8. ABP(现代ASP.NET样板开发框架)系列之13、ABP领域层——数据过滤器(Data filters)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之13.ABP领域层——数据过滤器(Data filters) ABP是“ASP.NET Boilerplate P ...

  9. 推荐13款javascript模板引擎

    javaScript 在生成各种页面内容时如果能结合一些模板技术,可以让逻辑和数据之间更加清晰,本文介绍 X 款 JavaScript 的模板引擎.(排名不分先后顺序) 1. Mustache 基于j ...

  10. ASP.NET Core 中文文档 第三章 原理(13)管理应用程序状态

    原文:Managing Application State 作者:Steve Smith 翻译:姚阿勇(Dr.Yao) 校对:高嵩 在 ASP.NET Core 中,有多种途径可以对应用程序的状态进行 ...

随机推荐

  1. 解决Xamarin.Android 软键盘弹出挡住Edittext的问题

    Window.SetSoftInputMode(SoftInput.AdjustPan);

  2. zsh 卡慢 解决方案

    首先,到对应的theme目录下的主题 git svn hg 之类的全部注释掉. git config --add oh-my-zsh.hide-status 1 使用这个命令关闭git的检查. ~/. ...

  3. LINGO 解线性方程 例子

    简介 没有什么比一个例子更好讲解Lingo的了,不行那就两个... ... Question 已知某种商品6个仓库的存货量,8个客户对该商品的需求量,单位商品运价如下所示,试确定6个仓库到8个客户的商 ...

  4. java BorderFrame.java

    简介边框的设置 简单 lowerd 低于设计的平面 Riased 高于设计的平面 Etched 嵌入 Line 线条形式 Matte 粗线条似乎? Empty 没有边框 code /* * @Auth ...

  5. POLIR-Lawsuits: 法院审核通过后迟迟不立案可以提起上诉 + 当法院迟迟不立案怎么办

    法院审核通过后迟迟不立案可以提起上诉. 法院一般需要在当事人起诉后七日内决定是否立案,如果拖着不立案可以问明原因, 要求法院在规定期限内立案,如果裁定不予受理,原告对裁定不服的,可以提起上诉. 立案: ...

  6. Unity 编辑器格子工具

    using UnityEditor; using UnityEngine; public class CustomGridWindow3 : EditorWindow { private Textur ...

  7. Unity UGUI源码 URP源码 地址

    UGUI源码 地址:https://github.com/Unity-Technologies/uGUI 文档:https://docs.unity3d.com/Packages/com.unity. ...

  8. Django缓存机制详解:从配置到实战应用

    一.缓存基础与环境准备 什么是缓存? 缓存是指保存计算密集型操作的结果,当再次需要该结果时直接从缓存中获取,而无需重新计算.在 Django 中,缓存可以应用于不同粒度: 整个网站缓存 特定视图缓存 ...

  9. P4017 最大食物链计数(拓扑排序)

    测试链接:https://www.luogu.com.cn/record/224653356 思路 利用出度是否为0,判断是否是食物链顶端,利用dp的思想,累加前面的路径 题解 拓扑排序 +stl队列 ...

  10. “同声传译”还是“全文翻译”?为何HotSpot虚拟机仍要保留解释器?

    Java虚拟机采用的是基于栈的指令集架构,这意味着Java虚拟机主要通过解释执行基于栈的字节码来运行Java程序.尽管Java虚拟机采取了一些优化措施,如栈顶缓存(Stack Top Cache),将 ...