GameOverlay.Net
GameOverlay.Net copied to clipboard
Random "Unsupported Image Format" Exception at graphics.CreateImage(string)
I have to load a lot of images (250+) for my overlay, and graphics.CreateImage is very slow, so i want to show a kind of load screen to the user, images are loaded in an extra Task, started in the SetupGraphics event handler:
public async Task LoadImagesAsync(Graphics graphic)
{
await Task.Run(() =>
{
foreach (var image in imageFileList)
{
string name = Path.GetFileNameWithoutExtension(image);
lock (lockObject)
{
try
{
images.Add(name, graphic.CreateImage(image));
}
catch (Exception ex)
{
break;
}
}
}
});
}
I get random "Unsupported Image Format" exceptions, on completely different images, sometimes it goes through without a problem, sometimes after 10 images or after 220 I get these random exceptions.
Images are transparent PNG Files, relatively small: 12x18 px,
- ex {"Unsupported Image Format!"} System.Exception
+ Data {System.Collections.ListDictionaryInternal} System.Collections.IDictionary {System.Collections.ListDictionaryInternal}
HResult -2146233088 int
HasBeenThrown true bool
HelpLink null string
+ InnerException null System.Exception
Message "Unsupported Image Format!" string
SerializationStackTraceString " at GameOverlay.Drawing.Imaging.ImageDecoder.Decode(RenderTarget device, BitmapDecoder decoder)\r\n at GameOverlay.Drawing.Image.LoadBitmapFromMemory(RenderTarget device, Byte[] bytes)\r\n at GameOverlay.Drawing.Graphics.CreateImage(String path)\r\n at iNavSim.OSD.<>c__DisplayClass14_0.<LoadImagesAsync>b__0() in C:\\Users\\andi__000\\Source\\Repos\\iNavSim\\src\\OSD.cs:line 151" string
SerializationWatsonBuckets null object
Source "GameOverlay" string
StackTrace " bei GameOverlay.Drawing.Imaging.ImageDecoder.Decode(RenderTarget device, BitmapDecoder decoder)\r\n bei GameOverlay.Drawing.Image.LoadBitmapFromMemory(RenderTarget device, Byte[] bytes)\r\n bei GameOverlay.Drawing.Graphics.CreateImage(String path)\r\n bei iNavSim.OSD.<>c__DisplayClass14_0.<LoadImagesAsync>b__0() in C:\\Users\\andi__000\\Source\\Repos\\iNavSim\\src\\OSD.cs: Zeile151" string
+ TargetSite {SharpDX.Direct2D1.Bitmap Decode(SharpDX.Direct2D1.RenderTarget, SharpDX.WIC.BitmapDecoder)} System.Reflection.MethodBase {System.Reflection.RuntimeMethodInfo}
_HResult -2146233088 int
+ _data {System.Collections.ListDictionaryInternal} System.Collections.IDictionary {System.Collections.ListDictionaryInternal}
_dynamicMethods null object[]
_exceptionMethod null System.Reflection.MethodBase
_helpURL null string
+ _innerException null System.Exception
_ipForWatsonBuckets 0x00007ffa72106af0 System.UIntPtr
_message "Unsupported Image Format!" string
_remoteStackTraceString null string
_source null string
+ _stackTrace {byte[192]} byte[]
_stackTraceString null string
_watsonBuckets null byte[]
_xcode -532462766 int
_xptrs 0x0000000000000000 System.IntPtr
+ Statische Member
Ok i found the issue:
public static Bitmap Decode(RenderTarget device, BitmapDecoder decoder)
{
var frame = decoder.GetFrame(0);
var converter = new FormatConverter(Image.ImageFactory);
foreach (var format in _pixelFormatEnumerator)
{
try
{
converter.Initialize(frame, format);
var bmp = Bitmap.FromWicBitmap(device, converter);
TryCatch(() => converter.Dispose());
TryCatch(() => frame.Dispose());
return bmp;
}
catch
{
TryCatch(() => converter.Dispose());
converter = new FormatConverter(Image.ImageFactory);
}
}
TryCatch(() => converter.Dispose());
TryCatch(() => frame.Dispose());
throw new Exception("Unsupported Image Format!");
}
This wild mix of try ... catch and a strange use of IDisposable does not work reasonably with tasks and is extremely slow. Try ... catch is intended for exceptions, and should never be used for program flow control. Yes I know, there is no reasonable way to determine the pixel format beforehand.
I have reduced the whole ImageDecoder.cs to the following code and adapted the constructors of Image and the Grapics.createImage(...) methods accordingly.
using System;
using SharpDX.Direct2D1;
using SharpDX.WIC;
using Bitmap = SharpDX.Direct2D1.Bitmap;
namespace GameOverlay.Drawing.Imaging
{
internal static class ImageDecoder
{
public static Bitmap Decode(RenderTarget device, BitmapDecoder decoder, Guid format)
{
using (var frame = decoder.GetFrame(0))
{
using (var converter = new FormatConverter(Image.ImageFactory))
{
converter.Initialize(frame, format);
return Bitmap.FromWicBitmap(device, converter);
}
}
}
}
}
This runs so fast that I don't need an extra task anymore, here are my results of the fast performance test with 273 transparent PNGs with 13x18 pixels:
Testcode:
private void LoadImages(Graphics graphics)
{
DateTime start = DateTime.Now;
foreach (var file in Directory.GetFiles("img_folder"))
{
string name = Path.GetFileNameWithoutExtension(file);
//Original
_images.Add(name, graphics.CreateImage(file));
// New
_images.Add(name, graphic.CreateImage(file, PixelFormat.Format64bppPRGBA));
}
TimeSpan duration = DateTime.Now - start;
}
Result (No kidding!): Original: 37.76 seconds New Code: 143 Millisesonds
That's a speedup of 264 times!
Would you create a PR for this? The existing trial and error method shouldn't be removed. Accepting an extra parameter is fine. You could also fastpath for the required format in the existing code
you can determine the format beforehand. i don't know how. that's why i ended up with this
Did this ever get merged?
There was no PR so nothing got merged