BoltBait.com

CodeLab Tutorial Part 1 - Simple

How to create CodeLab plugins that do not require a UI



In order to follow this tutorial, you'll need the CodeLab plugin and at least a basic knowledge of C#. You can download the CodeLab plugin here: http://BoltBait.com/pdn/codelab/.

This tutorial was written in response to the following PM that I received:


From: FireFly
I know enough (says "pyro" who reviewed some of my code) C# to start making some simple plugins using codelab, but I have absolutely know idea where to start. I attempted to make a 'dot at center' (off enormators effects) because it sounded the easiest. Could you suggest an easier one to start with? If you could offer any other help, it would also be appreciated.

Thanks, FireFly.

Here is my response:

I learn best by code examples, so I recommend looking at the source code to EVERY plugin you can find. There are many good examples here on this site. Look around.

First, you must learn C# at least to a basic level. Now, if you have expirence with C, C++, or even Pascal, you'll probably do fine. If not, you must first learn C#.

Ready to start? OK, Good!

The key to CodeLab scripts (and all effects really) is that there is a loop that loops through all of the pixels on the destination canvas--that is, the canvas where we will be drawing. It is your responsibility to write to every pixel on the destination canvas. You need to write either the corresponding pixel from the source canvas (the canvas you see on the screen before your effect is run) or a modified pixel that you create out of some algorithm.



Some effects are very simple, like "dot at center" and some are very complex, like Ink Sketch.

As for your "dot at center" problem, let me walk you through that one.

OK, starting with CodeLab's default script:

#region UICode
int Amount1=0;	//[0,100]Slider 1 Description
int Amount2=0;	//[0,100]Slider 2 Description
int Amount3=0;	//[0,100]Slider 3 Description
#endregion

void Render(Surface dst, Surface src, Rectangle rect)
{
    // Delete any of these lines you don't need
    Rectangle selection = EnvironmentParameters.GetSelection(src.Bounds).GetBoundsInt();
    int CenterX = ((selection.Right - selection.Left) / 2)+selection.Left;
    int CenterY = ((selection.Bottom - selection.Top) / 2)+selection.Top;
    ColorBgra PrimaryColor = (ColorBgra)EnvironmentParameters.PrimaryColor;
    ColorBgra SecondaryColor = (ColorBgra)EnvironmentParameters.SecondaryColor;
    int BrushWidth = (int)EnvironmentParameters.BrushWidth;

    ColorBgra CurrentPixel;
    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        for (int x = rect.Left; x < rect.Right; x++)
        {
            CurrentPixel = src[x,y];
            // TODO: Add pixel processing code here
            // Access RGBA values this way, for example:
            // CurrentPixel.R = (byte)PrimaryColor.R;
            // CurrentPixel.G = (byte)PrimaryColor.G;
            // CurrentPixel.B = (byte)PrimaryColor.B;
            // CurrentPixel.A = (byte)PrimaryColor.A;
            dst[x,y] = CurrentPixel;
        }
    }
}


The default script simply copies all pixels from the source canvas to the destination canvas. Effectively, it does nothing.

The main part of the Render function is the nested loops. The loops are for processing each pixel of the selection one at a time. The "for y" loop steps through each row, from top to bottom. Inside of that, the "for x" loop steps through each pixel on the current row from left to right. When you are inside of the x loop, you are working on a single pixel on the destination canvas. Then, each time you finish an X loop, it processes the next row in the Y loop.

Let's get started. We won't be needing a user interface (UI) for our effect, so let's delete these lines:

#region UICode
int Amount1=0;	//[0,100]Slider 1 Description
int Amount2=0;	//[0,100]Slider 2 Description
int Amount3=0;	//[0,100]Slider 3 Description
#endregion

We won't be needing the secondary color or the brush width, so (per the comment in the file) we can delete these lines:

ColorBgra SecondaryColor = (ColorBgra)EnvironmentParameters.SecondaryColor;
int BrushWidth = (int)EnvironmentParameters.BrushWidth;


Next we need to see when we are in the center of the selection. Notice that the default script computes the center point for us in the following lines:

int CenterX = ((selection.Right - selection.Left) / 2)+selection.Left;
int CenterY = ((selection.Bottom - selection.Top) / 2)+selection.Top;


Now, as we loop through the destination canvas using x and y, we need to add a test to see when we are processing the center point pixel. We can do that this way:

if ((y == CenterY) && (x == CenterX))

Once we know that we are working on the center point, just replace the source pixel with a pixel of the primary color. The default script has what we need in a comment. All we have to do is uncomment some lines in the default script:

CurrentPixel.R = (byte)PrimaryColor.R;
CurrentPixel.G = (byte)PrimaryColor.G;
CurrentPixel.B = (byte)PrimaryColor.B;
CurrentPixel.A = (byte)PrimaryColor.A;

So, if you're following along, your script should now look like this:

void Render(Surface dst, Surface src, Rectangle rect)
{
    // Delete any of these lines you don't need
    Rectangle selection = EnvironmentParameters.GetSelection(src.Bounds).GetBoundsInt();
    int CenterX = ((selection.Right - selection.Left) / 2)+selection.Left;
    int CenterY = ((selection.Bottom - selection.Top) / 2)+selection.Top;
    ColorBgra PrimaryColor = (ColorBgra)EnvironmentParameters.PrimaryColor;

    ColorBgra CurrentPixel;
    for(int y = rect.Top; y < rect.Bottom; y++)
    {
        for (int x = rect.Left; x < rect.Right; x++)
        {
            CurrentPixel = src[x,y];
            // TODO: Add pixel processing code here
            if ((y == CenterY) && (x == CenterX))
            {
            // Access RGBA values this way, for example:
                CurrentPixel.R = (byte)PrimaryColor.R;
                CurrentPixel.G = (byte)PrimaryColor.G;
                CurrentPixel.B = (byte)PrimaryColor.B;
                CurrentPixel.A = (byte)PrimaryColor.A;
            }
            dst[x,y] = CurrentPixel;
        }
    }
}


And, we're done. Put that script into CodeLab and press the Build DLL button and you've made your first effect plugin!

Here is something to keep in mind:

Your Render function is actually called by Paint.NET many times in order to complete the effect. "Why?" you ask... well, Paint.NET breaks up your current selection into smaller squares that I like to call "work units". Paint.NET then calls your Render function once for each work unit. Paint.NET does this to be efficient. If you have 2 or 4 CPU's, it will balance the workload among them so that your effect finishes faster. Here is an example of how Paint.NET may break up your selection into ROI units. Each colored band represents one ROI:


I hope this helps!


By the way, now that you've finished reading part 1, you're probably ready for part 2:

How to Write an Effect Plugin (Part 2 of 4 - Intermediate)


More Information

Here is some more information that you may find useful:
CodeLab Effects Design Overview
Sample Code for User Interface Elements
Using the Code Editor
Designing a User Interface for Your Effect
Building a DLL File
CodeLab Help File
Check for CodeLab Updates
Learn C#

 

News


CodeLab 2.19 Released
(April 15, 2017)
This latest release of CodeLab for Paint.NET includes the Notepad++ editor and a full WYSIWYG help editor.
More...

HTML Editor 1.5 Released
(March 31, 2016)
This latest release is a complete rewrite adding a wysiwyg editor mode and a much improved UI.
More...

Double-Six Dominoes 3.0
(September 25, 2015)
This long-awaited refresh of the most popular dominoes game on Download.com is now available!
More...