3

I am trying to decode a Base64 image and place it into a WPF image source. However, the code I am using has an error of:

No imaging component suitable to complete this operation was found.

error

I have double-checked that the Base64 string I have is, in fact, a correct Base64 encoding by using an online Base64 Decoder so I know its not that.

My code:

byte[] binaryData = Convert.FromBase64String(desc.Icon_Path);
MemoryStream ms = new MemoryStream(binaryData, 0, binaryData.Length);
ms.Write(binaryData, 0, binaryData.Length);
System.Drawing.Image image = System.Drawing.Image.FromStream(ms, true);
icon.Source = ToWpfImage(image);
ms.Dispose();

public BitmapImage ToWpfImage(System.Drawing.Image img)
{
  MemoryStream ms = new MemoryStream();
  img.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);

  BitmapImage ix = new BitmapImage();
  ix.BeginInit();
  ix.CacheOption = BitmapCacheOption.OnLoad;
  ix.StreamSource = ms;
  ix.EndInit();
  return ix;
}

What could I be doing incorrect?

StealthRT
  • 10,108
  • 40
  • 183
  • 342
  • 2
    I dont think you need the ms.Write line do you? If you do you'll need to set ms.position = 0 after the write. This is because after the write the stream position will be at the end. – Mike Hixson Jun 24 '14 at 01:07
  • Do you have to convert the Base64 String to a System.Drawing.Image? – Jamleck Jun 24 '14 at 06:34
  • As a side note, I also found that replacing ImageFormat.Bmp with ImageFormat.Png seemed to fix the issue. – Jamleck Jun 24 '14 at 07:22
  • Does the base64 string contain an encoded image buffer (e.g. a JPEG) or raw pixels? – Clemens Jun 24 '14 at 07:27
  • @MikeHixson Not sure why it didn't post this when I commented on your post above - that did end up working. I also asked that you put that as an answer so that I can give you credit. – StealthRT Jun 24 '14 at 14:43
  • Glad to help. I just submitted my answer. – Mike Hixson Jun 24 '14 at 16:57

4 Answers4

6

Given that the Base64 string contains an encoded image buffer that can be decoded by one of WPF's BitmapDecoders, you do not need more code than this:

public static BitmapSource BitmapFromBase64(string b64string)
{
    var bytes = Convert.FromBase64String(b64string);

    using (var stream = new MemoryStream(bytes))
    {
        return BitmapFrame.Create(stream,
            BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
    }
}
Clemens
  • 123,504
  • 12
  • 155
  • 268
1

The LoadImage function from here and Convert.FromBase64String is what you need. See the example below.

// MainWindow.xaml
<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="400" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <!--Using data binding-->
        <Image Grid.Row="0" Source="{Binding ImageSource}" />
        <!--Using Code behind-->
        <Image Grid.Row="1" x:Name="Icon" />
    </Grid>
</Window>

Below is the code behind.

// MainWindow.xaml.cs
using System;
using System.ComponentModel;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();    
        var mainWindowVm = new MainWindowViewModel();
        DataContext = mainWindowVm;

        byte[] buffer = File.ReadAllBytes(@"C:\temp\icon.jpg");
        string base64String = Convert.ToBase64String(buffer, 0, buffer.Length);

        // Option 1
        byte[] binaryData = Convert.FromBase64String(base64String);
        Icon.Source = MainWindowViewModel.LoadImage(binaryData);    
        // Option 2
        mainWindowVm.SetImageSource(Convert.FromBase64String(base64String));    
        // Option 3
        // mainWindowVm.SetImageSource(File.ReadAllBytes(@"C:\temp\icon.jpg"));
    }
}

public class MainWindowViewModel : INotifyPropertyChanged
{
    private ImageSource _imageSource;

    public ImageSource ImageSource
    {
        get { return _imageSource; }
        set
        {
            _imageSource = value;
            OnPropertyChanged("ImageSource");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void SetImageSource(byte[] imageData)
    {
        // You can also call LoadImage from here.
        var image = new BitmapImage();
        image.BeginInit();
        image.StreamSource = new MemoryStream(imageData);
        image.EndInit();    
        ImageSource = image;
    }

    public static BitmapImage LoadImage(byte[] imageData)
    {
        if (imageData == null || imageData.Length == 0) return null;
        var image = new BitmapImage();
        using (var mem = new MemoryStream(imageData))
        {
            mem.Position = 0;
            image.BeginInit();
            image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
            image.CacheOption = BitmapCacheOption.OnLoad;
            image.UriSource = null;
            image.StreamSource = mem;
            image.EndInit();
        }
        image.Freeze();
        return image;
    }

    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (null != handler)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}
Community
  • 1
  • 1
Jamleck
  • 1,017
  • 7
  • 12
1

I dont think you need the ms.Write line do you? If you do you'll need to set ms.position = 0 after the write. This is because after the write the stream position will be at the end.

Mike Hixson
  • 5,071
  • 1
  • 19
  • 24
0

I wrote a converter that reads the base64 part and returns a byte array. WPF Image control can handle this by default and will present the image from that bunch of bytes.

using System.Globalization;
using System.Linq;
using System.Windows.Data;

public class DataUrlToByteArrayConverter : IValueConverter
{
    public object Convert( object value, Type targetType, object parameter, CultureInfo culture )
    {
        if ( value is not string s ) return Binding.DoNothing;
        if ( s.StartsWith( "data:" ) && s.Contains( ";base64," ) )
        { // we have a data-url with base64 encoding
            var base64str = s.Split( ";base64," ).Last();
            return System.Convert.FromBase64String( base64str );
        }
        return Binding.DoNothing;
    }

    public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
    {
        throw new NotImplementedException();
    }
}

and you can use it in the view

<Window.Resources>
    <conv:DataUrlToByteArrayConverter x:Key="DataUrlToByteArray" />
</Window.Resources>

<Image Source="{Binding Image, Converter={StaticResource DataUrlToByteArray}}" />

Sir Rufo
  • 18,395
  • 2
  • 39
  • 73