dotnet / wpf

WPF is a .NET Core UI framework for building Windows desktop applications.
MIT License
7.07k stars 1.17k forks source link

WebP with alpha image is broken #1436

Open neelabo opened 5 years ago

neelabo commented 5 years ago

Problem description:

WebP can now be displayed on Windows 10, but when trying to display transparent WebP with WPF, the transparent part becomes broken display.

sample

However, BitmapImage using DecodePixelWidth different from the source is displayed correctly.

I submitted a similar report as an issue with the .NET Framework.

Minimal repro:

jairbubbles commented 4 years ago

Indeed, same behavior on .NET Framework, the .webp version is broken while the .png version displays correctly.

This code:

<UniformGrid Rows="1">
     <Image Source="https://www.gstatic.com/webp/gallery3/1_webp_ll.webp"></Image>
     <Image Source="https://www.gstatic.com/webp/gallery3/1_webp_ll.png"></Image>
</UniformGrid>

Will produce:

image

(Windows version 1903 too)

h82258652 commented 4 years ago

https://github.com/dotnet/wpf/blob/ac9d1b7a6b0ee7c44fd2875a1174b820b3940619/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Imaging/BitmapDecoder.cs There is no webp decoder. So wpf use the UnknownBitmapDecoder and it can not handle the alpha in webp format.

weltkante commented 4 years ago

There is no webp decoder. So wpf use the UnknownBitmapDecoder

Thats a contradiction, I think you mean that there's no explicit C#/.NET class for it but (I assume) it just falls back to WIC and lets Windows resolve the decoder. All decoders are native implementations just some have explicit wrappers in .NET - but if you look at their implementation they all do the same as the UnknownBitmapDecoder and forward to base class

So the fact that there is no named class for it doesn't really mean anything.

weltkante commented 4 years ago

@grubioe /cc @vatsan-madhavan can we get that reevaluated? ~If this is a bug in WIC or (external to WPF) WEBP decoder it would be really annoying if its not forwarded to the right Windows team just because the WPF team planned it for "future"~

[edit] its a bug in WPF but it'd still be nice to reevaluate and fix in 5.0 timeframe instead of leaving it on "future"

jairbubbles commented 4 years ago

The thumbnail appears correctly on Windows explorer. I don't know if it ends up using the same decoder though.

weltkante commented 4 years ago

Unless someone beats me to it I'll test with native WIC when I get home to determine if this is a WPF bug or WIC bug, for some reason my Windows 10 / 1909 installation at work doesn't have the webp decoder (I get an exception when referencing the image in WPF) and Windows doesn't offer it as optional feature either. ~I'm pretty sure I've seen it yesterday on my home machine when I happened to scroll through optional features.~

[edit] apparently its not an optional feature, you can find it under "apps and features" as installed application but it isn't uninstallable. ~Still no idea how to install webp support on my work machine, would have assumed it gets installed automatically with the 1903 or 1909 update.~

[edit] apparently you can install it from the store

weltkante commented 4 years ago

The bug is in WPF, probably in native code which hasn't been open sourced yet. WIC can load the image just fine, test code to load the image via WIC attached.

In fact I also figured out what happens, if you call new BitmapImage(new Uri(...)).Format on the webp image it reports Bgr32 which is wrong. WPF simply ignores the alpha channel even though WIC reports that the image has an alpha channel. The webp image has color artifacts in transparent places so it looks weird when they aren't transparent. (Its probably an optimization of the webp encoding because color in transparent regions is irrelevant so it doesn't bother to encode it and just leaves it at whatever is convenient.)

example code to load image via WIC ```csharp private void LoadImage() { // Referencing SharpDX.Direct2D1 nuget package using (var factory = new SharpDX.WIC.ImagingFactory2()) using (var decoder = new SharpDX.WIC.BitmapDecoder(factory, "1_webp_a.webp", SharpDX.WIC.DecodeOptions.CacheOnLoad)) { Debug.Assert(decoder.FrameCount == 1); using (var frame = decoder.GetFrame(0)) { Debug.Assert(frame.PixelFormat == SharpDX.WIC.PixelFormat.Format32bppRGBA); var size = frame.Size; var stride = size.Width * 4; var pixels = new byte[stride * size.Height]; frame.CopyPixels(pixels, stride); for (int iy = 0; iy < size.Height; iy++) { for (int ix = 0; ix < size.Width; ix++) { // convert from RGBA to BGRA Swap(ref pixels[iy * stride + ix * 4 + 0], ref pixels[iy * stride + ix * 4 + 2]); // uncomment to reproduce the bug, WPF seems to ignore the alpha channel // which you can simulate by setting alpha to 255 (opaque) //pixels[iy * stride + ix * 4 + 3] = 255; } } var bitmap = new WriteableBitmap(size.Width, size.Height, 96, 96, PixelFormats.Bgra32, null); bitmap.Lock(); try { bitmap.WritePixels(new Int32Rect(0, 0, size.Width, size.Height), pixels, stride, 0); } finally { bitmap.Unlock(); } ImageControl.Source = bitmap; } } } private static void Swap(ref byte lhs, ref byte rhs) { var tmp = lhs; lhs = rhs; rhs = tmp; } ```
vatsan-madhavan commented 4 years ago

@arpitmathur can we bring this one up in our bug triage again for discussion ?

More than just WebP, I’d like us to think about how we are going to support emerging media formats for which native WIC support is lacking. We may want to find out how Office does this today, for e.g. to see if their approach is reusable for us.

{It could be that we wait till Jan to dig into this.}

jairbubbles commented 4 years ago

Would be nice to have native support for .gif. I usually use https://github.com/XamlAnimatedGif/WpfAnimatedGif for this.

icetech233 commented 1 year ago

我也发现了 这个 bug ,怎么处理 !??

miloush commented 1 year ago

@icetech233 See https://github.com/dotnet/wpf/issues/1436#issuecomment-562269664 for a workaround. Basically you need to convert the image into a known pixel format. You can let WIC do it:

var decoder = BitmapDecoder.Create(new Uri("1_webp_a.webp", UriKind.Relative), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
var converted = new FormatConvertedBitmap(decoder.Frames[0], PixelFormats.Bgra32, null, 0);

Content = new Image { Source = converted };

The WebP decoder returns GUID_WICPixelFormat32bppRGBA which is not supported by WPF so it fallbacks to Default. I suggested extending the supported pixel formats in #4570

But I also agree with @vatsan-madhavan we need an easy way to plug in managed only/non-WIC image decoders and encoders.

oktagon2 commented 1 year ago

Based on the example in learn.microsoft I coded the following xaml:

<Window x:Class="WpfApplicationForStackOverflow1.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:WpfApplicationForStackOverflow1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800"
        Background="Aquamarine">
    <StackPanel>
        <Image Name="Image1" Width="400">
            <Image.Source>
                <BitmapImage DecodePixelWidth="997"
                             UriSource="https://www.gstatic.com/webp/gallery3/1_webp_ll.webp" />
            </Image.Source> 
        </Image>
    </StackPanel>
</Window>

It works. The background is transparent. The DecodePixelWidth seems to have a healthy effect. Unfortunately not for every value. I prefer prime numbers like 997.