John J Scott Posted December 19, 2023 Share Posted December 19, 2023 I added System.Drawing.Common as a dependency to my project so I could load PNG files. However, this causes loading my Paint.NET filetype plugin to crash with a System.ExecutionEngineException exception. (Net7-Windows) I suspect there's a clash between System.Drawing.Common for Net7 and System.Drawing for Net4.8. What is the preferred way to load a PNG file in Net7? There seem to be several third party libraries, but a recommendation from the horses mouth would be a great place to start without following several dead ends. Can the Paint.NET API be leveraged to do this simply? Cheers John Quote Link to comment Share on other sites More sharing options...
BoltBait Posted December 19, 2023 Share Posted December 19, 2023 9 minutes ago, John J Scott said: Can the Paint.NET API be leveraged to do this simply? Already done. What type of plugin are you writing, an Effect or a Filetype? Where are you trying to load the PNG from, an embedded resource or a file on disk? If an effect, are you writing a Classic effect or a GPU accelerated effect? Quote Download: BoltBait's Plugin Pack | CodeLab | and a Free Computer Dominos Game Link to comment Share on other sites More sharing options...
Rick Brewster Posted December 19, 2023 Share Posted December 19, 2023 You don't need to add System.Drawing.Common. Paint.NET already brings in the .NET runtime+framework, and the Windows Desktop frameworks. Just make sure you're referencing the WindowsDesktop package with these lines at the top of your .csproj <PropertyGroup> <UseWPF>true</UseWPF> <ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets> <UseWindowsForms>true</UseWindowsForms> </PropertyGroup> Also if you need to load PNGs, don't bother with System.Drawing. It's garbage. Use IPngFileType instead https://paintdotnet.github.io/apidocs/api/PaintDotNet.IPngFileType.html . @null54 is using this in one of his plugins. It lets you use PDN's built-in PNG FileType, which does A LOT of things with regards to handling metadata and other weird things you don't want to get buried in figuring out. You get IPngFileType through your Services property, e.g. this.Services.GetService<IPngFileType>() Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
Rick Brewster Posted December 19, 2023 Share Posted December 19, 2023 There is also IJpegFileType https://paintdotnet.github.io/apidocs/api/PaintDotNet.IJpegFileType.html Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
John J Scott Posted December 19, 2023 Author Share Posted December 19, 2023 Thanks all! It's validating a filetype plugin for hardware compressed textures. In C++: 1. Load in PNG 2. Compress to BC1 (aka DXT1) 3. Save as DDS 4. Save in my custom format. 5. Load my custom format and make sure it's the same (including all mips) 6. Decompress DDS with DirectXTex and save as PNG In C#: 1. Load DDS 2. Load my custom format in C# and make sure it's the same 3. Decompress custom format to BGRA as part of the filetype plugin 4. Compare my BGRA to the decompressed PNG to make sure they are the same. Similar process with ETC, but using KTX images not DDS. ASTC and PVRTC are on the radar, but that's about it. All this is external to Paint.NET - I just use it as my viewer. As such, I don't have access to services for IPngFileType. Even if I had a Document, I seem to need to set each pixel individually, which seems inefficient BitmapLayer layer = ( BitmapLayer )document.Layers[ 0 ]; for( int32 x = 0; x < layer.Width; x++ ) { for( int32 y = 0; y < layer.Height; y++ ) { reference_bgra[x + ( y * layer.Width)] = ( int32 )layer.Surface[x, y].Bgra; } } In the end, I opted for LoadFromWICFile: private static int32[] LoadPNGImage( string fullPNGPath ) { ScratchImage scratch_image = TexHelper.Instance.LoadFromWICFile( fullPNGPath + ".png", WIC_FLAGS.NONE ); Image image = scratch_image.GetImage( 0 ); int32[] reference_bgra = new int32[image.Width * image.Height]; Marshal.Copy( image.Pixels, reference_bgra, 0, image.Width * image.Height ); return reference_bgra; } I'm all ears for better approaches Cheers John Quote Link to comment Share on other sites More sharing options...
Rick Brewster Posted December 20, 2023 Share Posted December 20, 2023 The problem there is you're marshaling data through a managed array (int32[]). You can use something like NativeMemory to allocate unmanaged/unsafe memory and copy to/from directly using NativeMemory.Copy(). My preferred method would be: 1. Allocate your output buffer using NativeMemory.Alloc() 2. Wrap it in a class that holds the pointer and calls NativeMemory.Free() in its finalizer. I would just derive from SafeHandleZeroOrMinusOneIsInvalid, the runtime has special support for this class (for SafeHandles in general). This makes it substantially less likely you'll leak memory, and makes it very clear how to free the memory (a naked pointer has no allocator attached to it). 3. Use layer.Surface.AsRegionPtr() to obtain a RegionPtr<ColorBgra>, which is a low-level primitive kind of like Span<T> except for 2D bitmap-like regions of memory (pointer, width, height, stride). 4. Create a RegionPtr<ColorBgra> for your output buffer, being sure to pass the object from 2 in as the "owner" 5. Use CopyTo() to copy between the RegionPtr's 6. Return that wrapper class you created in step 2 (instead of the int32[]) Edit: See next comment. But, the general idea here is still the right one: don't manually copy pixel by pixel. Use a bulk-copy mechanism. And be sure to respect the stride of a bitmap. Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
Solution Rick Brewster Posted December 20, 2023 Solution Share Posted December 20, 2023 8 hours ago, John J Scott said: Can the Paint.NET API be leveraged to do this simply? Since you said you're not using this inside of Paint.NET then the answer is no. You cannot use Paint.NET's DLLs outside of Paint.NET. You can only use them from a plugin inside of Paint.NET. Both practically and legally speaking. Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.