【问题标题】:When should I use PixelFormats.Pbgra32 in WPF application?什么时候应该在 WPF 应用程序中使用 PixelFormats.Pbgra32?
【发布时间】:2016-11-28 23:47:19
【问题描述】:

考虑以下设置:我在磁盘上有一些图像。我无法修改这些图像,因为它们属于另一个项目。我刚读过。

我需要对它们进行处理以在我的应用中看起来最佳。我的意思是像素着色器处理。 当我完成处理后,输出图像将用作某种 ListView 或其他控件中的缩略图。

我选择PixelFormats.Bgra32 作为输出格式,因为为它构建像素着色器非常简单。 但是我读到PixelFormats.Pbgra32 是 Windows 原生使用的,因此速度更快。

在图像上使用淡入淡出效果时是否仍然更快?在托管代码中预乘通道而不是将其留给框架是否有意义?

【问题讨论】:

  • 显示器没有 Alpha 通道,因此预乘像素格式可以更快,因为不需要后乘。但是实现淡入淡出效果的着色器必须做更多的工作来恢复原始 RGB 值并重新应用新的 alpha。准确性也大大降低。很难想象它会更好,试试吧。
  • 我猜你是对的,尽管我已经多次看到“使用 Pbgra32”。我的猜测是,如果直接渲染位图,这是一种优化 - 如果 WPF 只是将所有渲染发送到合成它们的渲染队列,它怎么能直接渲染。所有 WPF 控件都使用 32 位颜色画笔,但话又说回来,如果画笔中的 alpha 字节为 0xff,则渲染器可能会决定跳过一个步骤。

标签: c# wpf


【解决方案1】:

所以我如何测试它(使用 /unsafe 编译):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

namespace BgraVsPbgra {

    public partial class MainWindow : Window {

        readonly WrapPanel Panel;
        readonly TextBlock Output;
        readonly PixelFormat FN = PixelFormats.Bgra32;
        readonly PixelFormat FP = PixelFormats.Pbgra32;
        readonly List<long> TN = new List<long>();
        readonly List<long> TP = new List<long>();
        DateTime T0;
        long DT => (DateTime.Now - T0).Ticks;
        DateTime Now => DateTime.Now;

        public MainWindow() {
            InitializeComponent();
            SizeToContent = SizeToContent.WidthAndHeight;
            Title = "Bgra32 vs Pbgra Benchmark";
            Panel = new WrapPanel {
                Width = 512,
                Height = 512
            };
            Output = new TextBlock {
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Center,
                Foreground = new SolidColorBrush(Colors.White)
            };
            (Content as Grid).Children.Add(Panel);
            (Content as Grid).Children.Add(Output);
            Dispatcher.BeginInvoke(new Action(() => Start()), DispatcherPriority.SystemIdle);
        }

        private async void Start() {
            Output.Text += "Bgra32 vs Pbgra32 benchmark started.\r\n\r\n";
            var t0 = DateTime.Now;
            var n = 3;
            var i = 16384;
            for (var p = 1; p <= n; p++) {
                Output.Text += $"\r\nPass {p}/{n}.\r\n\r\n";
                await Benchmark(i / 4, 0.75, true);
                await Benchmark(i / 4, 0.75, false);
                await Benchmark(i / 4, 1, true);
                await Benchmark(i / 4, 1, false);
            }
            var t = (DateTime.Now - t0).TotalSeconds;
            Output.Text += $"\r\nDone in {t:0.0}s.\r\n";
        }

        private async Task Benchmark(int n = 256, double opacity = 1, bool cheat = false) {
            Output.Text += $"Benchmarking with opacity = {opacity}...   ";
            if (cheat) Output.Text += "CHEATING!...   ";
            //Output.Text += "\r\n";
            for (int i = 0; i < n; i++) await Dispatcher.BeginInvoke(new Action(async () => await DrawTestAsync(opacity, cheat)), DispatcherPriority.Send);
            Output.Text += ($"Pbgra32 advantage: {(TN.Average() / TP.Average() * 100d - 100d):0.0}%\r\n");
        }

        async Task DrawTestAsync(double opacity = 1, bool cheat = false) {
            await Dispatcher.BeginInvoke(new Action(() => {
                Panel.Children.Clear();
                for (int i = 0; i < 8; i++) {
                    T0 = Now; Panel.Children.Add(new Image { Source = CreateBitmap(FP, cheat), Width = 128, Height = 128, Opacity = opacity }); TP.Add(DT);
                    T0 = Now; Panel.Children.Add(new Image { Source = CreateBitmap(FN, cheat), Width = 128, Height = 128, Opacity = opacity }); TN.Add(DT);
                }
            }), DispatcherPriority.Send);

        }



        BitmapSource CreateBitmap(PixelFormat pixelFormat, bool cheat = false) {
            var bitmap = new WriteableBitmap(256, 256, 96, 96, pixelFormat, null);
            bitmap.Lock();
            unsafe
            {
                var pixels = (uint*)bitmap.BackBuffer;
                if (pixelFormat == FN || cheat)
                    for (uint y = 0; y < 256; y++) for (uint x = 0; x < 256; x++) pixels[x + y * 256] = 0x80000000u | (x << 16) | y;
                else
                    for (uint y = 0; y < 256; y++) for (uint x = 0; x < 256; x++) pixels[x + y * 256] = 0x80000000u | (x / 2 << 16) | y / 2;
            }
            bitmap.Unlock();
            bitmap.Freeze();
            return bitmap;
        }

    }

}

我得到了相当随机的结果,但大多数时候使用 Pbgra32 像素格式似乎比使用 Bgra32 像素格式快一点(比如 3%)。

当使用Pbgra32 格式时,必须对每个通道进行一些额外的操作,但额外的 2 个划分似乎没有任何可衡量的差异。

因此,如果您想让位图速度提高大约 3%,请使用 Pbgra32 格式并预乘像 pr = r * a / 255 这样的通道,这可能看起来很丑:

uint pixel = a << 24 | r * a / 255 << 16 | g * a / 255 << 8 | b * a / 255;

并且不要创建pixel 函数,否则会影响性能。说到性能,不安全的像素操作似乎比使用安全的 CopyPixels() / WritePixels() 方法快 2 到 3 倍。

不,我不会使用Pbgra32 为位图创建位图或像素着色器。我的看法是:不值得。如果我错了,请纠正我。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 2011-04-15
    • 2017-04-10
    • 2012-03-19
    • 2018-05-12
    • 2018-12-11
    相关资源
    最近更新 更多