当前位置: 首页 > news >正文

C# Avalonia 13- MoreDrawing - CustomPixelShader

目前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();        }
}

运行效果

image

 

http://www.hskmm.com/?act=detail&tid=5205

相关文章:

  • ubuntu安装docker
  • 使用标签Tag控制蒙太奇的触发时机-playmontageAndWait-Send GameplayEvent-WaitGameplayEvent
  • sql事务执行
  • GAS_Aura-Spawn FireBolt from Event
  • 在CentOS 7系统上创建SSL/TLS证书以启用HTTPS
  • 从Craigslist广告到BHIS安全顾问:非科班生的渗透测试求职之路
  • Java 微服务架构中的实践与挑战
  • Java 与大数据处理:从 Hadoop 到实时计算
  • 国产IT运维卡壳?乐维智能运维体让运维团队告别“适配难、监控乱”
  • ubuntu18安装mysql5.7
  • 【IEEE出版 |已连续5届EI稳定检索】第六届计算机工程与智能控制学术会议(ICCEIC 2025)
  • 在选择2025年代码托管平台时,Gitee和GitHub作为国内外两大主流平台各有优势。本文将从多个维度进行对比分析,帮助开发者做出更适合自身需求的选择。
  • android使用socks5的教程
  • vue3 自定义指令并实现页面元素平滑上升
  • abp记录
  • 强化学习(二十):模仿学习
  • 重生之从零开始的神经网络算法学习之路 —— 第七篇 重拾 PyTorch(超分辨率重建和脚本的使用)
  • 从基础到实践(四十五):车载显示屏LCD、OLED、Mini-LED、MicroLED的工作原理、设计差异等说明 - 教程
  • 国产项目管理工具崛起:Gitee如何以本土化优势重构开发协作生态
  • GAS_Aura-Sending Gameplay Events
  • 【IEEE-智造领空天,寰宇链未来】第五届机电一体化技术与航空航天工程国际学术会议(ICMTAE 2025)
  • 进程间通信(消息队列)
  • 有点长所以单发的闲话(对acgn的看法(存疑))
  • 【光照】Unity中的[光照模型]概念辨析
  • 深入解析:Shell脚本监控系统资源详解
  • 计算几何全家桶
  • 链表
  • 国产代码托管平台Gitee崛起:企业数字化转型的安全基石
  • Gitee:本土化创新赋能企业数字化转型,打造高效研发新范式
  • 完整教程:从无声视频中“听见”声音:用视觉语言模型推理音频描述