BoltBait Posted July 17, 2016 Share Posted July 17, 2016 So, you want to learn how to write a plugin for paint.net--well, you're in the right place! Getting Ready The first thing you'll need to do is install* CodeLab: Download CodeLab (English) Download CodeLab (Russian) *How to install plugins You may need to configure your system for CodeLab. When installed properly, you'll find CodeLab in the paint.net menu Effects > Advanced > CodeLab. Tutorials Now, here is a list of tutorials to get you started*: All the tutorials you need to get started are here CLICK! (for 10+ tutorials!) These are the old, broken tutorials: Spoiler Overview of how effects work Simple plugins Adding a UI to your effect Creating complex effects Odds and Ends Moving beyond CodeLab to Visual Studio - Using additional IndirectUI rules - How to debug your plugin Adding Help to your plugin Working with extra surfaces Working with Alpha More Sample Code Removing dividing lines from your Visual Studio based Paint.NET Plugin** Adding Tabs to your Visual Studio based Paint.NET Plugin** These tutorials are also old: Spoiler You may also want to read ReMake's "First Steps" tutorials where he leads you by the hand in making various plugins: Lines/Grid Saturation RGB Neon Edges Sponge Sponge (v2) Linocut Color Sketch Sharing Your Work Once you have an effect working the way you want, and you'd like to share it with the other users of paint.net, simply follow these steps: Compress the effect DLL file along with the install.bat file created by CodeLab into a ZIP file. You should find these files on your desktop. Create a thread in the Plugins forum by pressing the Start New Topic button. Fully describe your plugin and attach a few screenshots of what it does. Be sure to include the UI of your effect in the screenshots. Attach the ZIP file to the post using the Choose File button followed by the Attach This File button. If you choose to post your effect's CodeLab script (source code), be sure to paste your CodeLab script inside of a code block (Click the code button <>, select C#, and paste your code into the proper spot. Press the Insert into post button.) Help If you start writing a plugin and you get stuck, post your code in the Plugin Developer's Forum and someone will try and help you figure it out. Just be be sure to paste your CodeLab script inside of a code block (Click the code button <>, select C#, and paste your code into the proper spot. Press the Insert into post button.) Here is a general list of what is and is not possible when writing a plugin: http://forums.getpaint.net/index.php?/topic/14566-p As you can see, plugins are flexible, but they can't do EVERYTHING. If you need more capabilities, maybe what you need is a macro recorder. Try this one: TinyTask. Or, if you're looking for a command line utility to manipulate .PDN files, try https://www.irfanview.com/ There is a plugin that allows it to view Paint.NET files. What About FileType Plugins? How to write a filetype plugin *Those tutorials listed in Bold have been rewritten for CodeLab v6.0! **These tutorials are for Paint.NET v5.0+ plugins 3 Quote Download: BoltBait's Plugin Pack | CodeLab | and a Free Computer Dominos Game Link to comment Share on other sites More sharing options...
Oliver1963 Posted October 9, 2020 Share Posted October 9, 2020 Question: Do I need to first learn C# while writing these, or do the tutorials teach how to use C# too? Quote Link to comment Share on other sites More sharing options...
BoltBait Posted October 9, 2020 Author Share Posted October 9, 2020 38 minutes ago, Oliver1963 said: Question: Do I need to first learn C# while writing these, or do the tutorials teach how to use C# too? The tutorials assume a basic knowledge of C#. But, if you know C, C++, Java, JavaScript, or even Pascal you should be able to follow along just fine. Quote Download: BoltBait's Plugin Pack | CodeLab | and a Free Computer Dominos Game Link to comment Share on other sites More sharing options...
Oliver1963 Posted October 10, 2020 Share Posted October 10, 2020 Thank you for the quick response. I do know C++, thank you for the help 👍 Quote Link to comment Share on other sites More sharing options...
aijaz Posted October 7, 2021 Share Posted October 7, 2021 Dear boltbait May u please write a plugin that will copy the outline of an image to new layer after applying effect->stylize->outline. Quote Link to comment Share on other sites More sharing options...
Ego Eram Reputo Posted October 8, 2021 Share Posted October 8, 2021 Plugins cannot write to any layer except the current one (i.e. they can't create new layers). Quote ebook: Mastering Paint.NET | resources: Plugin Index | Stereogram Tut | proud supporter of Codelab plugins: EER's Plugin Pack | Planetoid | StickMan | WhichSymbol+ | Dr Scott's Markup Renderer | CSV Filetype | dwarf horde plugins: Plugin Browser | ShapeMaker Link to comment Share on other sites More sharing options...
aijaz Posted October 8, 2021 Share Posted October 8, 2021 Can anyone add canny edge detection algorithms as plugin to paint.net Quote Link to comment Share on other sites More sharing options...
Red ochre Posted October 8, 2021 Share Posted October 8, 2021 Not really the place to ask @aijaz I suggest you look through the plug-in index here. My TGMagnitude effect may do what you are seeking, with background opacity set to zero. Additionally there are 'stray pixel' removers if it needs cleaning up. Quote Red ochre Plugin pack.............. Diabolical Drawings ................Real Paintings Link to comment Share on other sites More sharing options...
aijaz Posted October 8, 2021 Share Posted October 8, 2021 @Red Ochre TGM is wonderful. Thanks a lot. Quote Link to comment Share on other sites More sharing options...
DataJuggler Posted January 2, 2022 Share Posted January 2, 2022 (edited) I got my project into Visual Studio, which I now feel at home. I am a little confused about the Rectangles of Interest that Paint.NET uses. My Nuget package DataJuggler.PixelDatabase expects me to load the entire image: PixelDatabase pixelDatabase = PixelDatabaseLoader.LoadPixelDatabase(... You can pass in a path or a bitmap: // Load PixelDatabase by path PixelDatabase pixelDatabase = PixelDatabaseLoader.LoadPixelDatabase(path, StatusUpdate); // Load by Bitmap PixelDatabase pixelDatabase = PixelDatabaseLoader.LoadPixelDatabase(sourceImage, StatusUpdate); // status update is just a method to get pixels updated in long operations I realize Paint.NET uses layers, and I read the plug-in can only effect the active layer, so is there a way to get the full active layer as a bitmap (or Image)? Once a PixelDatabase is loaded, I call ApplyQuery and my package handles updating the entire image. Is there a way to execute code before the image is separated into rectangles of interest? If yes, I can call my ApplyQuery method before rectangles are split, and then in your Render methods I only have to copy pixels to the destination surface. For example, my Create Gradient feature needs to know the size of the entire image. I am not sure how to do that from rectangles of interest. My project and site are free, and I plan on making the plug-in free also, but I can pay you a little for your time if you could guide me how to get started. I think people would find my plug-in useful, as it extends Paint.NET for some pretty useful features. Here is a recent feature added, grayscale: Update the red car to gray, and leave (most) of the rest of the image alone: Bitmap Query Language BQL - If you know SQL you are 90% there. Biggest difference is each criteria is on its own line to make parsing simple. Update Set Grayscale Red Where RedMaxDiff > 0 Y > 761 More features are shown here If you have time to watch a 5-minute video: (the first two minutes has a comic book story and animated intro, you can skip it): World's Greatest Grayscale: Thanks for your time, I have wanted to do this since last year but Paint.NET was still on .NET Framework and my package on .NET5. Now that both are on .NET6 I don't have any excuse not to get this done. Corby / Data Juggler Edited January 2, 2022 by DataJuggler Quote Link to comment Share on other sites More sharing options...
Rick Brewster Posted January 2, 2022 Share Posted January 2, 2022 Just look at this.SrcArgs.Surface from within OnSetRenderInfo() 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...
MJW Posted January 2, 2022 Share Posted January 2, 2022 18 hours ago, DataJuggler said: Is there a way to execute code before the image is separated into rectangles of interest? The source surface is not really separated into ROIs. The two methods that commonly deal with processing the surfaces are, void Render(Surface dst, Surface src, Rectangle rect) and (less often) void OnSetRenderInfo(PropertyBasedEffectConfigToken newToken, RenderArgs DstArgs, RenderArgs SrcArgs) OnSetRenderInfo() is called once per rendering pass, before the calls to Render(). The entire source surface is available as SrcArgs.Surface and the entire destination surface is available as DstArgs.Surface. Render() is called many times, with each call to Render() processing a single ROI. The entire source surface is available as src, and the entire destination surface is available as dst. The ROI is in the rect argument. It isn't part of a surface; it's just the bounds of the ROI, to let the particular call to Render() know what section of the surface is assigned to it. Within Render() you are free to read anywhere in the source surface, but should only write destination pixels that are within the ROI (and you should write all of them). If the algorithm needs to process the whole surface at once, as is sometimes the case, the processing is done in OnSetRenderInfo(). I'm actually not sure if it's kosher to write to the destination surface in OnSetRenderInfo(). I don't think it is. The usual practice is to create an auxiliary surface the first time OnSetRenderInfo() executes, write the desired pixels for the whole image into it in OnSetRenderInfo() during that and all subsequent calls, then copy the results to the destination surface, an ROI at a time, in Render(). If you create an auxillary surface, you should dispose of it in OnDispose(bool disposing). Quote Link to comment Share on other sites More sharing options...
BoltBait Posted January 3, 2022 Author Share Posted January 3, 2022 49 minutes ago, MJW said: I'm actually not sure if it's kosher to write to the destination surface in OnSetRenderInfo(). I don't think it is. Don't do that! 50 minutes ago, MJW said: create an auxiliary surface the first time OnSetRenderInfo() executes, write the desired pixels for the whole image into it in OnSetRenderInfo() during that and all subsequent calls, then copy the results to the destination surface, an ROI at a time, in Render(). Yes, do that! Quote Download: BoltBait's Plugin Pack | CodeLab | and a Free Computer Dominos Game Link to comment Share on other sites More sharing options...
MJW Posted January 3, 2022 Share Posted January 3, 2022 I've never tried this (which is why I forgot to mention it), but I believe that for cases where the rendering requires access to the entire destination surface at the same time, the auxiliary surface can be eliminated and the rendering performed in Render() by specifying EffectRenderingSchedule.None. As I understand it, this causes the rendering to be done in a single call to Render(). According to a comment by @xod, the format for requesting that it be done that way is: public YourNameEffectPlugin () : base (StaticName, StaticIcon, SubmenuName, new EffectOptions () {Flags = EffectFlags.Configurable, RenderingSchedule = EffectRenderingSchedule.None}) { } I hope someone who knows more about this than I do will correct me or provide more details. Even if this is possible, it may be better to do only as much as necessary in OnSetRenderInfo(), then complete whatever can be done in parallel in Render() if the algorithm lends itself to that. Quote Link to comment Share on other sites More sharing options...
null54 Posted January 3, 2022 Share Posted January 3, 2022 12 minutes ago, MJW said: I hope someone who knows more about this than I do will correct me or provide more details. EffectRenderingSchedule.None eliminates the multiple calls to Render() and delivers all of the ROI rectangles at once. Quoting the documentation: Quote /// <summary> /// There is only one call to the Effect's Render method, and all regions of interest are /// delivered at that time. This also means rendering is effectively single threaded. /// </summary> /// <remarks> /// This is the same behavior of the now-obsolete EffectFlags.SingleRenderCall. /// </remarks> You are still restricted to only writing within the region specified by the ROI rectangles, so any algorithm that writes to the entire image must still use a separate work surface. Quote Plugin Pack | PSFilterPdn | Content Aware Fill | G'MIC | Paint Shop Pro Filetype | RAW Filetype | WebP Filetype The small increase in performance you get coding in C++ over C# is hardly enough to offset the headache of coding in the C++ language. ~BoltBait Link to comment Share on other sites More sharing options...
MJW Posted January 3, 2022 Share Posted January 3, 2022 54 minutes ago, null54 said: You are still restricted to only writing within the region specified by the ROI rectangles, so any algorithm that writes to the entire image must still use a separate work surface. But if you get all the ROIs at once, doesn't that imply you can write into all of them? Otherwise, of what use is EffectRenderingSchedule.None? It used to be called SingleRenderCall, or something like that, which also suggests there's a singe call for all the ROIs. EDIT: Oh, I see what the point is. If there's a selection, there will only be ROIs that cover the selected region, so writing into the unselected destination would (or could) still be disallowed. I seem to recall some knowledgeable person saying that now it's actually okay to write outside the selection, but I could be misremembering. In any case, the safe choice is to use an auxiliary surface. If that's so, though, I still don't quite see the usefulness of EffectRenderingSchedule.None. Wouldn't it be better to do the non-parallel computations in OnSetRenderInfo(), and at least get a little parallelism in the copy from the auxiliary buffer to the destination? EDIT 2: I did remember correctly. In response to a comment saying "Writing outside of the selection breaks the undo command," @toe_head2001 said: Quote That was fixed in paint.net 4.1.6. Obviously, writing outside of the selection is still a bad practice, and just wastes compute resources. However, since it's agreed to be bad practice, it should be avoided. Quote Link to comment Share on other sites More sharing options...
Arco Frio Posted December 17, 2023 Share Posted December 17, 2023 (edited) Trying to compile a plugin with CodeLab keeps giving me namespace issues: "(CS0116) A namespace cannot directly contain members such as fields or methods.". Even trying to transfer it to Visual Studio or just trying to compile sample plugins yields no positive results. Any leads as to what I'm doing wrong? Edited December 17, 2023 by Arco Frio Quote Link to comment Share on other sites More sharing options...
BoltBait Posted December 17, 2023 Author Share Posted December 17, 2023 CodeLab uses your .cs filename to create the namespace. Make sure you're not using any special characters in the filename. Quote Download: BoltBait's Plugin Pack | CodeLab | and a Free Computer Dominos Game Link to comment Share on other sites More sharing options...
Arco Frio Posted December 17, 2023 Share Posted December 17, 2023 Well, that might clear up at least one of the scripts, since it had a space in between names (don't have the time to test it right now). But the other one was named just the standard "MyScript.cs". If it's of any help, here's what the script looks like on CodeLab: Spoiler void Render(Surface dst, Surface src, Rectangle rect) { int xTileSize = 0; int yTileSize = 10; int xSpacing = 0; int ySpacing = 2; int xProcessAmount = 1; int yProcessAmount = 1; Rectangle fromRect = Rectangle.FromLTRB(0, yTileSize, rect.Right, rect.Bottom); Rectangle toRect = Rectangle.FromLTRB(0, yTileSize + ySpacing, rect.Right, rect.Bottom + ySpacing); ColorBgra CurrentPixel; for (int y = rect.Top; y < rect.Bottom; y++) { if (IsCancelRequested) return; for (int x = rect.Left; x < rect.Right; x++) { Debug.WriteLine("boop"+y); xProcessAmount++; CurrentPixel = src[x,y]; if (toRect.Contains(x, y)) { int offSetX = x - toRect.Left + fromRect.Left; int offSetY = y - toRect.Top + fromRect.Top; CurrentPixel = src[offSetX, offSetY]; } dst[x,y] = CurrentPixel; } } } This script is a bit pointless for now since I haven't finished it, but by all means I think it should at least compile without errors. Whenever I try to compile it, CodeLab gives me: "Build Error I'm sorry, I was not able to build the DLL. Please check for build errors in the Error List." Checking the error log only gives me an error on a line that doesn't even exist on my code, and the message only appears for a brief moment right after I try to compile/preview: "Error at line -22: The name 'RandomNumberInstanceSeed' does not exist in the current context (CS0103)". Sorry for being needy, I appreciate any help! Quote Link to comment Share on other sites More sharing options...
BoltBait Posted December 18, 2023 Author Share Posted December 18, 2023 42 minutes ago, Arco Frio said: Error at line -22: The name 'RandomNumberInstanceSeed' does not exist in the current context (CS0103) Make sure you're using the latest version of CodeLab (v6.11). I thought I fixed all that Random Number stuff in the latest build. 1 Quote Download: BoltBait's Plugin Pack | CodeLab | and a Free Computer Dominos Game Link to comment Share on other sites More sharing options...
Arco Frio Posted December 18, 2023 Share Posted December 18, 2023 (edited) Yes, my CodeLab version is the latest, I downloaded it just today and am a total beginner on it. Are there any extra steps I should be careful about when installing? All I did was basically download and run the installer. EDIT: Not sure if this helps, but I've noticed the issue only happens when trying to compile a Classic Effect. Any of the other example scripts compile just fine (besides the Filetype example script, which throws "Error at line -97: The name 'Unsafe' does not exist in the current context (CS0103)"). Edited December 18, 2023 by Arco Frio Quote Link to comment Share on other sites More sharing options...
BoltBait Posted December 18, 2023 Author Share Posted December 18, 2023 2 hours ago, Arco Frio said: the Filetype example script, which throws "Error at line -97: The name 'Unsafe' does not exist in the current context (CS0103) Good to know. Thanks. I'm not an expert in filetype plugins and didn't even work in that area on the last release. 2 hours ago, Arco Frio said: Are there any extra steps I should be careful about One thing that we need to work on... If you're wanting to paste a script into CodeLab in order to work on it, be sure to File > New and choose that type of script. This sets up the specific CodeLab tab in order to compile that type of script... THEN paste it in. In other words, you shouldn't paste a classic script into a tab setup for a GPU effect. That won't work. Quote Download: BoltBait's Plugin Pack | CodeLab | and a Free Computer Dominos Game Link to comment Share on other sites More sharing options...
ReMake Posted December 18, 2023 Share Posted December 18, 2023 6 hours ago, BoltBait said: I thought I fixed all that Random Number stuff in the latest build. FYI, the error disappears as soon as you add some control to the UI. 1 Quote Link to comment Share on other sites More sharing options...
Arco Frio Posted December 18, 2023 Share Posted December 18, 2023 (edited) 7 hours ago, BoltBait said: If you're wanting to paste a script into CodeLab in order to work on it, be sure to File > New and choose that type of script. This sets up the specific CodeLab tab in order to compile that type of script... THEN paste it in. Well, i you're talking about the script I showed, I kind of made it from scratch by using another one as a reference. The other I tested besides the Classic Script were just the examples provided by CodeLab itself. 6 hours ago, ReMake said: FYI, the error disappears as soon as you add some control to the UI. I'll try that and see if it lets me compile. EDIT: Yes, that was it! I got to compile it and it worked! Thanks a lot for all the help! Edited December 18, 2023 by Arco Frio Quote Link to comment Share on other sites More sharing options...
Red ochre Posted July 26 Share Posted July 26 Could a link to the NEW system (below) be included on the first post please? (As this thread is pinned and I can never find the new version... well I have now 🙂). https://boltbait.com/pdn/CodeLab/help/tutorial/ The old links are still useful though, thanks for both @BoltBait! Quote Red ochre Plugin pack.............. Diabolical Drawings ................Real Paintings 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.