【问题标题】:Converting Icon object to and from byte[] causes quality loss将 Icon 对象与 byte[] 相互转换会导致质量损失
【发布时间】:2018-10-07 14:23:51
【问题描述】:

我正在制作一个 WPF 应用程序,其中 Image 有一个来自 Icon 对象的 ImageSource

为了将其保存在磁盘上,我将它们转换为 byte[],而 ImageSource 的 getter 将 byte[] 转换回 ImageSource

问题,这样做的时候,结果看起来完全错误,似乎只有几种颜色可以保存。

这是一个完整的 WPF 解决方案,可让您亲眼看看它看起来有多可怕:

XAML

<Window x:Class="POCicon.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        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"
        xmlns:local="clr-namespace:POCicon"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:MyViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Image Source="{Binding WithConversion}" MaxHeight="32" MaxWidth="32" Stretch="Fill" ></Image>
        <Image Grid.Column="1" Source="{Binding NoConversion}" MaxHeight="32" MaxWidth="32" Stretch="Fill" ></Image>
    </Grid>
</Window>

C# 视图模型

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace POCicon
{
    public class MyViewModel
    {
        private System.Drawing.Icon Icon;
        public MyViewModel()
        {
            Icon= System.Drawing.Icon.ExtractAssociatedIcon("C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe");
        }
        public ImageSource WithConversion
        {
            get
            {
                byte[] byteArray;
                using (MemoryStream serializer = new MemoryStream())
                {
                    Icon.Save(serializer);
                    byteArray = serializer.ToArray();
                }
                using (MemoryStream deserializer = new MemoryStream(byteArray))
                {
                    BitmapImage image = new BitmapImage();
                    image.BeginInit();
                    // image.CreateOptions = BitmapCreateOptions.PreservePixelFormat; //totally useless unfortunately
                    image.StreamSource = deserializer;
                    image.EndInit();
                    return image;
                }
            }
        }

        public ImageSource NoConversion
        {
            get
            {
                var imgSource = Imaging.CreateBitmapSourceFromHIcon(Icon.Handle, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
                return imgSource;
            }
        }

    }
}

有没有办法修复这种方法,或者通过另一条路线获得所需的结果?要求是它在某些时候必须是 Icon 对象,并且最终成为 byte[]

非常感谢

【问题讨论】:

  • (编辑:这是对已删除评论的回复)是的,我也试过了。把它放在我上面写的解决方案中,结果看起来一样,有质量损失,但我不明白它来自哪里。
  • 将 Icon 转换为 ImageSource 时,没问题,但我需要 byte[] 将它们保存在磁盘上。
  • 不能先通过CreateBitmapSourceFromHIcon创建一个BitmapSource,然后编码成字节数组吗?
  • 试一试也无妨。你知道如何从 BitmapSource 到数组吗?

标签: c# .net wpf type-conversion icons


【解决方案1】:

不要使用Icon.Save(),而是使用Icon.ToBitmap()从图标创建System.Drawing.Bitmap,然后保存:

var icon = System.Drawing.Icon.ExtractAssociatedIcon(
    @"C:\Program Files (x86)\JetBrains\dotPeek\v1.1\Bin\dotpeek32.exe");
var bitmap = icon.ToBitmap();

byte[] buffer;

using (var memoryStream = new MemoryStream())
{
    bitmap.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
    buffer = memoryStream.ToArray();
}

或者,在转换为 BitmapSource 后创建 byte[]

var icon = System.Drawing.Icon.ExtractAssociatedIcon(
    @"C:\Program Files (x86)\JetBrains\dotPeek\v1.1\Bin\dotpeek32.exe");
var bitmapSource = Imaging.CreateBitmapSourceFromHIcon(
    icon.Handle, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());

var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));

byte[] buffer;

using (var memoryStream = new MemoryStream())
{
    encoder.Save(memoryStream);
    buffer = memoryStream.ToArray();
}

【讨论】:

  • 确实如此。您的解决方案解决了我的问题。选择的答案。
  • 另请注意使用 icon.ToBitmap 的替代方法。
  • 非常感谢您的宝贵时间,克莱门斯!
  • 对于未来的读者,我比较了 Clemens 建议的两种方法的性能。我从单个 Icon 转换为 byte[],然后返回到 ImageSource。第一种方法(Icon.ToBitmap())需要 2 毫秒,中间有一个字节 [1620]。第二种方法 (Imaging.CreateBitmapSourceFromHIcon(...)) 需要 52 毫秒,中间字节 [1776]。在屏幕上看到它们,我没有注意到清晰度或颜色的差异。
猜你喜欢
  • 1970-01-01
  • 2019-02-20
  • 1970-01-01
  • 2020-09-21
  • 2014-10-23
  • 1970-01-01
  • 1970-01-01
  • 2020-12-31
  • 2012-04-06
相关资源
最近更新 更多