Open campersau opened 4 years ago
DecodeFailed
"occurs when the image fails to load, due to a corrupt image header". Since there is no header corruption here, either the event should not be fired or the documentation needs to be changed.
The image file is of Gray16
pixel format and contains an embedded ICC profile. However, since a specific pixel width is requested, the image goes through a TransformedBitmap
which for some reason changes the pixel format to Gray16Float
. The closest DUCE format for both is Bgr32
.
The ColorConvertedBitmap
that tries to preserve the ICC profile then fails with unsupported pixel format of Gray16Float
, so the code resorts to first converting the bitmap to Bgr32
(note that it would have been fine with Gray16
). The second ColorConvertedBitmap
attempt then fails with invalid color profile, as reported above. I guess that's because it got a grayscale ICC profile for color pixel format.
I agree this is a bug in the framework: the codepath that tries to recover by converting to the closets DUCE format first wrongly assumes that the color context is valid for the new format too.
I would also consider the change of pixel format by the TransformedBitmap
to be unexpected (there are other reports of the issue e.g. here). Either these cases should be documented, or the WIC shouldn't do that.
Last but not least, I am surprised by this transformation chain when the DecodePixelWidth
documentation claims that the JPEG and PNG codecs decode into the required dimensions natively. Why is that not happening?
@campersau There is nothing wrong with the file. You have a few workaround options: If you are in control of the image, either do not include ICC profile in the image or use color pixel format. If not, do not use DecodePixelWidth
or load and scale the image yourself.
I am not sure why you need to use DecodePixelWidth
though, rather than setting the Image.Width
to a fixed value? That seems to cause extra allocations and processing that yields blurry results under higher DPI.
Thanks for your detailed response! DecodeFailed
currently does indeed not fire, it was just my suggestion to do so instead of crashing the app. (And I haven't read the documentation of the event at that time.)
But your response indicates that the issue is somewhere else and that the exception might be avoided entirely which would be even better.
The code was added years ago in https://github.com/NuGet/NuGet.Client/commit/6d112fb8459899b3dc098b1621e08c56c11fa88f, https://github.com/NuGet/NuGet.Client/commit/a66e74ef6aa40ceb032bf52b1684cc3bf57f63f9 and ported to NuGetPackageManager in https://github.com/NuGetPackageExplorer/NuGetPackageExplorer/commit/46488622a7a0921714b5a0df002007ac6c8e5438 Here is the current relevant code: https://github.com/NuGetPackageExplorer/NuGetPackageExplorer/blob/4da26d5ea2e6f1af0fda767236552f47672c041f/PackageExplorer/Converters/IconUrlToImageCacheConverter.cs#L52-L70
The BitmapImage
s are cached in an additional ObjectCache
to improve performance, maybe that's why they were trying to make the images smaller.
I will try to do some memory profiling to see if we can drop DecodePixelWidth
or need to do the scaling on our own.
We also noticed the blurryness and tried to fix that by using this code:
RenderOptions.SetBitmapScalingMode(iconBitmapImage, BitmapScalingMode.HighQuality);
@campersau since you are not using BitmapCacheOption.None
when loading the image, the WPF caches the original bitmap image anyway, so there shouldn't be any need for additional layer of caching. If you do some profiling, I would be curious to see the results.
Another workaround I forgot to mention is using BitmapCreateOptions.IgnoreColorProfile
when initializing the image - it's probably the easiest you can do if you wanted to keep DecodePixelWidth
. Obviously that might potentially give you different color appearance, but since you are dealing with icons rather than photos, it might be acceptable.
RenderOptions.SetBitmapScalingMode(iconBitmapImage, BitmapScalingMode.HighQuality);
This is a blurry option. You can get sharp but pixelated image using NearestNeighbor
. For best quality, I would suggest not throwing away pixels using the DecodePixelWidth
in the first place.
5.0.100-preview.8.20417.9
Windows 10 2004 Build 19041.508
Does the bug reproduce also in WPF for .NET Framework 4.8?:
Yes
Problem description: App crashes with
FileFormatException
when loading specific image usingBitmapImage
withDecodePixelWidth = 32
. This affects .NETFramework as well as .NET Core.Known affected products:
See also https://github.com/NuGetPackageExplorer/NuGetPackageExplorer/issues/1105
Actual behavior: App crashes:
Expected behavior: No crash and invoke
DecodeFailed
event with this exception instead.Minimal repro: