Jump to content

How it's made (Sponge effect)


ReMake

Recommended Posts

How to create a plugin of the Sponge effect in CodeLab I wrote in tutorial CodeLab - First Steps (Sponge). In the end of this tutorial I recommended to apply the Oil Painting effect before you call the Sponge effect.

 

When I read BoltBait's topic How to Write an Effect Plugin (Part 7 - Extra Surface), I understood how to create a plugin which combines two effects - Clouds and Oil Painting.

 

EDIT: The first variant of this tutorial was based on BoltBait's How to Write an Effect Plugin (Part 7 - Extra Surface) tutorial. In this variant the Clouds effect was processed in OnSetRenderInfo function. toe_head2001 suggested to combine both effects in the Render loop. It has allowed to increase speed of effect. I have updated this tutorial.

 

Start CodeLab (Effects-> Advanced-> CodeLab). Choose New in the menu File. In a dialogue box Script Editor click a No button. The dialogue box New Source (Template) will be presented to you. In the drop-down list choose Clouds and in the drop-down list Blend choose Multiply. Click the Generate Code button. We will get the following script of effect:

// Name:
// Submenu:
// Author:
// Title:
// Desc:
// Keywords:
// URL:
// Help:
#region UICode
IntSliderControl Amount1 = 250; // [2,1000] Scale
DoubleSliderControl Amount2 = 0.5; // [0,1] Roughness
BinaryPixelOp Amount3 = LayerBlendModeUtil.CreateCompositionOp(LayerBlendMode.Normal); // Blend Mode
ReseedButtonControl Amount4 = 0; // [255] Reseed
#endregion

// Setup for calling the Render Clouds effect
private CloudsEffect cloudsEffect = new CloudsEffect();
private PropertyCollection cloudsProps;

// Setup for using Multiply blend op
private BinaryPixelOp multiplyOp = LayerBlendModeUtil.CreateCompositionOp(LayerBlendMode.Multiply);

// Here is the main render loop function
void Render(Surface dst, Surface src, Rectangle rect)
{
    // Call the Render Clouds function
    cloudsProps = cloudsEffect.CreatePropertyCollection();
    PropertyBasedEffectConfigToken CloudsParameters = new PropertyBasedEffectConfigToken(cloudsProps);
    CloudsParameters.SetPropertyValue(CloudsEffect.PropertyNames.Scale, Amount1);
    CloudsParameters.SetPropertyValue(CloudsEffect.PropertyNames.Power, Amount2);
    CloudsParameters.SetPropertyValue(CloudsEffect.PropertyNames.BlendMode, Amount3);
    CloudsParameters.SetPropertyValue(CloudsEffect.PropertyNames.Seed, (int)Amount4);
    cloudsEffect.SetRenderInfo(CloudsParameters, new RenderArgs(dst), new RenderArgs(src));
    // Call the Clouds function using Black and White
    cloudsEffect.Render(new Rectangle[1] {rect},0,1);

    // Now in the main render loop, the dst canvas has a render of clouds
    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        if (IsCancelRequested) return;
        for (int x = rect.Left; x < rect.Right; x++)
        {
            ColorBgra CurrentPixel = dst[x,y];

            // TODO: Add additional pixel processing code here

            CurrentPixel = multiplyOp.Apply(src[x,y], CurrentPixel);


            dst[x,y] = CurrentPixel;
        }
    }
}

Open the designer interface (File -> User Interface Designer), select in it the Scale control and rename it to Brush Size. Set the maximum value to 100, set the default value to 25. Click the Update button. Select the second control. Select Integer Slider in the Control type box. Set the minimum value to 0, the maximum value to 10 and the default value to 0. Click the Update button. Remove the third and fourth controls - we don't need them. Select Integer Slider in the Control type box. Type Intensity in the Control name box. Set the maximum value to 255 and click the Add button. Select Integer Slider in the Control type box. Type Smoothness in the Control name box. Set the minimum value to 1, set the default value to 2 and the maximum value to 8. Click the Add button. Last two controls will be necessary for us later. Click button OK.

Comment out or delete the following line:

CloudsParameters.SetPropertyValue(CloudsEffect.PropertyNames.BlendOp, Amount3);

it isn't need to us any more.

In the line

CloudsParameters.SetPropertyValue(CloudsEffect.PropertyNames.Seed, (int)Amount4);

write 255 instead of (int) Amount4.

Since we changed the type of the variable Amount2, we will have to make a change in the next line:

CloudsParameters.SetPropertyValue(CloudsEffect.PropertyNames.Power, Amount2);

replacing Amount2 to (10 - Amount2) / 10.0

 

Let's convert our clouds to the black-and-white contrast image. For this purpose color components of pixel (R, G, В) will be necessary for us. If the components value is less than 128, we will set it to 0, otherwise - 255. Insert into the script the following block of codes instead of the comment //TODO:

            int A = CurrentPixel.A;

            // Convert to black-and-white contrast image
            int R = CurrentPixel.R < 128 ? 0 : 255;
            int G = CurrentPixel.G < 128 ? 0 : 255;
            int B = CurrentPixel.B < 128 ? 0 : 255;

Now let's eliminate light areas in this image and restore our pixel from the updated values of the color components. Insert into the script the following block of codes before blend operation:

            // Eliminate light areas
            A = 255 - Math.Min(Math.Min(R, G), В);

            // Restore pixel        
            CurrentPixel.R = Int32Util.ClampToByte(R);
            CurrentPixel.G = Int32Util.ClampToByte(G);
            CurrentPixel.B = Int32Util.ClampToByte(В);
            CurrentPixel.A = Int32Util.ClampToByte(A + Amount3 - 255);

Fill the commented lines at the top of the script, for example, like these:

// Name: Sponge
// Submenu: Artistic
// Author: ReMake
// Title: Sponge v1.2    ReMake 2015
// Desc: The Paint.NET effect which simulates Sponge painting
// Keywords: paint.net|effect|sponge
// URL: http://www.getpaint.net/redirect/plugins.html

If your effect has to have the help, add one more comment:

// Help: The Paint.NET effect which simulates Sponge painting.

Save this script with the Sponge_1 name and create a DLL file with the same name. Try how your effect works. Try to apply different settings for controls.

 

Notice, visually appreciable change of intensity comes through each three-four units of variable Amount3. Let's remember it.

 

Below are the most effective parameters ranges of our effect:
 - Brush Size - 20...32
 - Intensity - 200...240

Let's modify our script with the received results. The code block of the user interface will look like this:

#region UICode
IntSliderControl Amount1 = 3; // [1,10] Brush Size
IntSliderControl Amount2 = 7; // [0,10] Roughness
IntSliderControl Amount3 = 5; // [0,15] Intensity
IntSliderControl Amount4 = 2; // [1,8] Smoothness
#endregion

Change the following line in the script:

CloudsParameters.SetPropertyValue(CloudsEffect.PropertyNames.Scale, Amount1 + 20);
CloudsParameters.SetPropertyValue(CloudsEffect.PropertyNames.Power, (10 - Amount2) / 10.0);
CurrentPixel.A = Int32Util.ClampToByte(A + Amount3*3 - 240);

For the further work we will need an auxiliary surface for our effect. Add in the script the next lines after UI code block:

// Declare auxiliary surface
private Surface aux;

Our final script should look like this:

// Name: Sponge
// Submenu: Artistic
// Author: ReMake
// Title: Sponge v1.2    ReMake 2015
// Desc: The Paint.NET effect which simulates Sponge painting
// Keywords: paint.net|effect|sponge
// URL: http://www.getpaint.net/redirect/plugins.html
// Help: The Paint.NET effect which simulates Sponge painting.
#region UICode
IntSliderControl Amount1 = 3; // [1,10] Brush Size
IntSliderControl Amount2 = 7; // [0,10] Roughness
IntSliderControl Amount3 = 5; // [0,15] Intensity
IntSliderControl Amount4 = 3; // [1,8] Smoothness
#endregion

// Declare auxiliary surface
private Surface aux;

// Setup for calling the Render Clouds effect
private CloudsEffect cloudsEffect = new CloudsEffect();
private PropertyCollection cloudsProps;

// Setup for using Multiply blend op
private BinaryPixelOp multiplyOp = LayerBlendModeUtil.CreateCompositionOp(LayerBlendMode.Multiply);

// Here is the main render loop function
void Render(Surface dst, Surface src, Rectangle rect)
{
    // Call the Render Clouds function
    cloudsProps = cloudsEffect.CreatePropertyCollection();
    PropertyBasedEffectConfigToken CloudsParameters = new PropertyBasedEffectConfigToken(cloudsProps);
    CloudsParameters.SetPropertyValue(CloudsEffect.PropertyNames.Scale, Amount1 + 20);
    CloudsParameters.SetPropertyValue(CloudsEffect.PropertyNames.Power, (10 - Amount2) / 10.0);
    CloudsParameters.SetPropertyValue(CloudsEffect.PropertyNames.Seed, 255);
    cloudsEffect.SetRenderInfo(CloudsParameters, new RenderArgs(dst), new RenderArgs(src));
    // Call the Clouds function using Black and White
    cloudsEffect.Render(new Rectangle[1] {rect},0,1);

    // Now in the main render loop, the dst canvas has a render of clouds
    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        if (IsCancelRequested) return;
        for (int x = rect.Left; x < rect.Right; x++)
        {
            ColorBgra CurrentPixel = dst[x,y];

            int A = CurrentPixel.A;

            // Convert to black-and-white contrast image
            int R = CurrentPixel.R < 128 ? 0 : 255;
            int G = CurrentPixel.G < 128 ? 0 : 255;
            int B = CurrentPixel.B < 128 ? 0 : 255;
          
            // Eliminate light areas
            A = 255 - Math.Min(Math.Min(R, G), В);

            // Restore pixel		
            CurrentPixel.R = Int32Util.ClampToByte(R);
            CurrentPixel.G = Int32Util.ClampToByte(G);
            CurrentPixel.B = Int32Util.ClampToByte(В);
            CurrentPixel.A = Int32Util.ClampToByte(A + Amount3 * 3 - 240);

            CurrentPixel = multiplyOp.Apply(src[x,y], CurrentPixel);

            dst[x,y] = CurrentPixel;
        }
    }
}

Save this script with the Sponge name - maybe it will be necessary to you in the future.

Now let's prepare our script to moving into Visual Studio. Open the dialogue box Building (File -> Save As DLL...).

 

Building.png

 

Click the Select icon link and select the icon (for example, like this: Sponge_1.png). Click the View source check box and click the Build button.

You will see a dialogue box containing all the source code that CodeLab prepared for you.

 

FullSourceCode.png

 

Our script is ready to moving into Visual Studio. Copy this code (click on button Paste.png) and move it to a Visual Studio project how it's written in BoltBait's tutorial: CodeLab Tutorial Part 5 - Beyond CodeLab.

 

Let's continue. Find the function named OnSetRenderInfo. Currently it looks like this:

        protected override void OnSetRenderInfo(PropertyBasedEffectConfigToken newToken, RenderArgs dstArgs, RenderArgs srcArgs)
        {
            Amount1 = newToken.GetProperty<Int32Property>(PropertyNames.Amount1).Value;
            Amount2 = newToken.GetProperty<Int32Property>(PropertyNames.Amount2).Value;
            Amount3 = newToken.GetProperty<Int32Property>(PropertyNames.Amount3).Value;
            Amount4 = newToken.GetProperty<Int32Property>(PropertyNames.Amount4).Value;

            base.OnSetRenderInfo(newToken, dstArgs, srcArgs);
        }

We will use the following line to create the auxiliary surface:

aux = new Surface(srcArgs.Size);

Place this line before the base.OnSetRenderInfo call.
 

Return to CodeLab and open in it the dialogue box New Source (Template). In the drop-down list choose Oil Painting and click the Generate Code button. Copy from the script this code block:

    // Setup for calling the Oil Painting effect
    OilPaintingEffect oilpaintEffect = new OilPaintingEffect();
    PropertyCollection oilpaintProps = oilpaintEffect.CreatePropertyCollection();
    PropertyBasedEffectConfigToken oilpaintParameters = new PropertyBasedEffectConfigToken(oilpaintProps);
    oilpaintParameters.SetPropertyValue(OilPaintingEffect.PropertyNames.BrushSize, Amount1);
    oilpaintParameters.SetPropertyValue(OilPaintingEffect.PropertyNames.Coarseness, Amount2);
    oilpaintEffect.SetRenderInfo(oilpaintParameters, new RenderArgs(dst), new RenderArgs(src));
    // Call the Oil Painting function
    oilpaintEffect.Render(new Rectangle[1] {rect},0,1);

    // Now in the main render loop, the dst canvas has an oil painted version of the src canvas

and paste it into Visual Studio before the comment // Call the Render Clouds function

In the line

oilpaintParameters.SetPropertyValue(OilPaintingEffect.PropertyNames.BrushSize, Amount1);

change Amount1 to Amount4 and in the line

oilpaintParameters.SetPropertyValue(OilPaintingEffect.PropertyNames.Coarseness, Amount2);

change Amount2 to 3:

oilpaintParameters.SetPropertyValue(OilPaintingEffect.PropertyNames.BrushSize, Amount4);
oilpaintParameters.SetPropertyValue(OilPaintingEffect.PropertyNames.Coarseness, 3);

In the line

cloudsEffect.SetRenderInfo(CloudsParameters, new RenderArgs(dst), new RenderArgs(src));

change dst to aux, and src to dst.

cloudsEffect.SetRenderInfo(CloudsParameters, new RenderArgs(aux), new RenderArgs(dst));

Now we should move Clouds from an auxiliary surface to the destination canvas. For this in the line

ColorBgra CurrentPixel = dst[x, y];

change dst to aux:

ColorBgra CurrentPixel = aux[x, y];

It's necessary to mix current pixel with appropriate pixel on the destination canvas. For this purpose replace a line

CurrentPixel = multiplyOp.Apply(src[x,y], CurrentPixel);

with the line

CurrentPixel = multiplyOp.Apply(CurrentPixel, dst[x, y]);

Our final render function should look like this:

void Render(Surface dst, Surface src, Rectangle rect)
{
    // Setup for calling the Oil Painting effect
    OilPaintingEffect oilpaintEffect = new OilPaintingEffect();
    PropertyCollection oilpaintProps = oilpaintEffect.CreatePropertyCollection();
    PropertyBasedEffectConfigToken oilpaintParameters = new PropertyBasedEffectConfigToken(oilpaintProps);
    oilpaintParameters.SetPropertyValue(OilPaintingEffect.PropertyNames.BrushSize, Amount4);
    oilpaintParameters.SetPropertyValue(OilPaintingEffect.PropertyNames.Coarseness, 3);
    oilpaintEffect.SetRenderInfo(oilpaintParameters, new RenderArgs(dst), new RenderArgs(src));
    // Call the Oil Painting function
    oilpaintEffect.Render(new Rectangle[1] { rect }, 0, 1);

    // Now in the main render loop, the dst canvas has an oil painted version of the src canvas

    // Call the Render Clouds function
    cloudsProps = cloudsEffect.CreatePropertyCollection();
    PropertyBasedEffectConfigToken CloudsParameters = new PropertyBasedEffectConfigToken(cloudsProps);
    CloudsParameters.SetPropertyValue(CloudsEffect.PropertyNames.Scale, Amount1 + 20);
    CloudsParameters.SetPropertyValue(CloudsEffect.PropertyNames.Power, (10 - Amount2) / 10.0);
    CloudsParameters.SetPropertyValue(CloudsEffect.PropertyNames.Seed, 255);
    cloudsEffect.SetRenderInfo(CloudsParameters, new RenderArgs(aux), new RenderArgs(dst));
    // Call the Clouds function using Black and White
    cloudsEffect.Render(new Rectangle[1] {rect},0,1);

    // Now in the main render loop, the dst canvas has a render of clouds

    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        if (IsCancelRequested) return;
        for (int x = rect.Left; x < rect.Right; x++)
        {
            ColorBgra CurrentPixel = aux[x, y];

            int A = CurrentPixel.A;

            // Convert to black-and-white contrast image
            int R = CurrentPixel.R < 128 ? 0 : 255;
            int G = CurrentPixel.G < 128 ? 0 : 255;
            int B = CurrentPixel.B < 128 ? 0 : 255;

            // Eliminate light areas
            A = 255 - Math.Min(Math.Min(R, G), В);

            // Restore pixel		
            CurrentPixel.R = Int32Util.ClampToByte(R);
            CurrentPixel.G = Int32Util.ClampToByte(G);
            CurrentPixel.B = Int32Util.ClampToByte(В);
            CurrentPixel.A = Int32Util.ClampToByte(A + Amount3 * 3 - 240);

            CurrentPixel = multiplyOp.Apply(CurrentPixel, dst[x, y]);

            dst[x, y] = CurrentPixel;
        }
    }
}

Save this solution and build your DLL from within Visual Studio. Try this effect in Paint.NET.

The user interface of our new effect looks like this.

SpongeUI_en.png

 

The source image
551001.jpg

 

And this is result of effect's work.

After.jpg

 

Thanks BoltBait for his excellent tutorials.

Edited by toe_head2001
Formatting
  • Like 1
  • Upvote 1
Link to comment
Share on other sites

Brilliant tutorial ReMake! :star:

Thank you so much for writing it. It will be a fabulous resource for plugin authors wishing to migrate from CodeLab to Visual Studio.

Bravo!

  • Upvote 1
Link to comment
Share on other sites

These three lines:

int R = (int)(double)((CurrentPixel.R < 128) ? 0 : 255);
int G = (int)(double)((CurrentPixel.G < 128) ? 0 : 255);
int B = (int)(double)((CurrentPixel.B < 128) ? 0 : 255);

 

Why do you have these casts here? You only have two possible values: 0 or 255. They are already compatible int values, so no need for '(int)'. And I have no idea why you have the '(double)'.

 

Much cleaner code without the useless casts. If you wanted to, you can also remove the () around the inline If statements.

int R = CurrentPixel.R < 128 ? 0 : 255;
int G = CurrentPixel.G < 128 ? 0 : 255;
int B = CurrentPixel.B < 128 ? 0 : 255;

 

 

Why put the Clouds effect code in OnSetRenderInfo? You lose the advantage of using ROIS. Both can work perfectly fine in Render. Please tell me the rationale for this decision.

  • Upvote 1

(September 25th, 2023)  Sorry about any broken images in my posts. I am aware of the issue.

bp-sig.png
My Gallery  |  My Plugin Pack

Layman's Guide to CodeLab

Link to comment
Share on other sites

...Much cleaner code without the useless casts. If you wanted to, you can also remove the () around the inline If statements...

 

I agree, fixed. It's more elegant code. I was not very attentive.

 

 

Why put the Clouds effect code in OnSetRenderInfo? You lose the advantage of using ROIS. Both can work perfectly fine in Render. Please tell me the rationale for this decision.

 

From CodeLab Tutorial Part 3 - Complex by BoltBait:

 

...When designing a complex effect, you need to keep in mind the limitations of CodeLab and the Paint.NET effect system: Complex effects can call one built-in effect,...

 

I don't know how to combine both effects in Render loop. Give me an example, please. I will be very thankful to you.

 

  • Like 1
Link to comment
Share on other sites

BoltBait's been smoking something. :P

I have two in my Blur Fill effect. In this case they are daisy-chained, but they don't have to be.

 

I take the 'enlargedSurface' which was prepared in OnSetRenderInfo, and input it into Gaussian Blur. It outputs to 'bluredSurface'.

Then I take 'bluredSurface', and input it into Brightness and Contrast. It outputs to 'lightSurface'.

 

'lightSurface' is what I use in my x,y Loop.

 

 

Here's the reduced example:

void Render(Surface dst, Surface src, Rectangle rect)
{
	// Setup for calling the Gaussian Blur effect
	GaussianBlurEffect blurEffect = new GaussianBlurEffect();
	PropertyCollection blurProps = blurEffect.CreatePropertyCollection();
	PropertyBasedEffectConfigToken BlurParameters = new PropertyBasedEffectConfigToken(blurProps);
	BlurParameters.SetPropertyValue(GaussianBlurEffect.PropertyNames.Radius, Amount1);
	blurEffect.SetRenderInfo(BlurParameters, new RenderArgs(bluredSurface), new RenderArgs(enlargedSurface));
	// Call the Gaussian Blur function
	blurEffect.Render(new Rectangle[1] { rect }, 0, 1);

	// Setup for calling the Brightness and Contrast Adjustment function
	BrightnessAndContrastAdjustment bacAdjustment = new BrightnessAndContrastAdjustment();
	PropertyCollection bacProps = bacAdjustment.CreatePropertyCollection();
	PropertyBasedEffectConfigToken bacParameters = new PropertyBasedEffectConfigToken(bacProps);
	bacParameters.SetPropertyValue(BrightnessAndContrastAdjustment.PropertyNames.Brightness, Amount2);
	bacParameters.SetPropertyValue(BrightnessAndContrastAdjustment.PropertyNames.Contrast, 0);
	bacAdjustment.SetRenderInfo(bacParameters, new RenderArgs(lightSurface), new RenderArgs(bluredSurface));
	// Call the Brightness and Contrast Adjustment function
	bacAdjustment.Render(new Rectangle[1] { rect }, 0, 1);

	for (int y = rect.Top; y < rect.Bottom; y++)
	{
		if (IsCancelRequested) return;
		for (int x = rect.Left; x < rect.Right; x++)
		{
			dst[x, y] = normalOp.Apply(lightSurface[x, y], src[x, y]);
		}
	}
}
You can see the full file here:

https://github.com/toehead2001/pdn-blur-fill/blob/master/BlurFill/BlurFill.cs

Edited by toe_head2001
  • Upvote 1

(September 25th, 2023)  Sorry about any broken images in my posts. I am aware of the issue.

bp-sig.png
My Gallery  |  My Plugin Pack

Layman's Guide to CodeLab

Link to comment
Share on other sites

 

BoltBait's been smoking something. :P

I have two in my Blur Fill effect. In this case they are daisy-chained, but they don't have to be.

 

I think BoltBait was talking about a limitation in CodeLab, where you can't create your own surfaces.

 

EDIT: I took this out, because I thought maybe I was wrong, but I think it's correct, so I put it back in:

I think, though, that even in CodeLab you could call multiple effects, provided that all but the first were adjustment-type effects (not necessarily in the Adjustment menu), which only use a single pixel as input to modify the same pixel. You could, I believe, call the effect with the dst surface as input and output. Perhaps I'm mistaken, but offhand I don't see why that wouldn't work.

Edited by MJW
Link to comment
Share on other sites

I think BoltBait was talking about a limitation in CodeLab, where you can't create your own surfaces.

You can create your own surfaces in CodeLab, it's just that anything you do with them will have poor performance. I did this with my first release of Text Window, and then wised-up and started using OnSetRenderInfo for that sort of thing.

 

You could, I believe, call the effect with the dst surface as input and output. Perhaps I'm mistaken, but offhand I don't see why that wouldn't work.

 

I tried this a few months ago, and it didn't work... at least for what I was doing. I don't remember the details though.

(September 25th, 2023)  Sorry about any broken images in my posts. I am aware of the issue.

bp-sig.png
My Gallery  |  My Plugin Pack

Layman's Guide to CodeLab

Link to comment
Share on other sites

It seems to work for the case I tried. The following plugin has an initial blur, followed by a brightness and contrast adjustment, and finally, an optional color inversion which is done in the render loop. The loop is only executed if the invert is done, since otherwise dst already contains the final image. There's a bit a flicker when the controls are moved, but the result is correct.

 

Here is the code:

Hidden Content:
// Name: Gaussian Blur with Brightness and Contrast
// Submenu: Blurs
// Author: MJW
// Title: Gaussian Blur with Brighness and Contrast
// Desc: Gaussian Blur with Brightness and Contrast (test multiple built-in effects)
// Keywords: Gaussian blur contrast brightness test multiple built-in effects
// URL:
// Help:
#region UICode
IntSliderControl Amount1 = 1; // [0,100] Blur Radius
IntSliderControl Amount2 = 0; // [-100,100] Brightness
IntSliderControl Amount3 = 0; // [-100,100] Contrast
CheckboxControl Amount4 = false; // [0,1] Invert Colors
#endregion


// Here is the main render loop function
void Render(Surface dst, Surface src, Rectangle rect)
{
    // Setup for calling the Gaussian Blur effect
    GaussianBlurEffect blurEffect = new GaussianBlurEffect();
    PropertyCollection blurProps = blurEffect.CreatePropertyCollection();
    PropertyBasedEffectConfigToken BlurParameters = new PropertyBasedEffectConfigToken(blurProps);
    BlurParameters.SetPropertyValue(GaussianBlurEffect.PropertyNames.Radius, Amount1);
    blurEffect.SetRenderInfo(BlurParameters, new RenderArgs(dst), new RenderArgs(src));
    // Call the Gaussian Blur function
    blurEffect.Render(new Rectangle[1] {rect}, 0, 1);  
    
    // Adjust brightness and contrast.
    BrightnessAndContrastAdjustment bacAdjustment = new BrightnessAndContrastAdjustment();
    PropertyCollection bacProps = bacAdjustment.CreatePropertyCollection();
    PropertyBasedEffectConfigToken bacParameters = new PropertyBasedEffectConfigToken(bacProps);
    bacParameters.SetPropertyValue(BrightnessAndContrastAdjustment.PropertyNames.Brightness, Amount2);
    bacParameters.SetPropertyValue(BrightnessAndContrastAdjustment.PropertyNames.Contrast, Amount3);
    bacAdjustment.SetRenderInfo(bacParameters, new RenderArgs(dst), new RenderArgs(dst));
    // Call the Brightness and Contrast Adjustment function
    bacAdjustment.Render(new Rectangle[1] {rect}, 0, 1);

    // Now in the main render loop, the dst canvas has a blurred version of the src canvas
    // The dst canvas has the contrast adjusted.
    // No need to do any more unless Invert is selected.
    if (Amount4)
    {
        for (int y = rect.Top; y < rect.Bottom; y++)
        {
            if (IsCancelRequested) return;
            for (int x = rect.Left; x < rect.Right; x++)
            {
                ColorBgra CurrentPixel = dst[x, y];   
                CurrentPixel = ColorBgra.FromUInt32(0x00ffffff ^ CurrentPixel.Bgra);    
                dst[x, y] = CurrentPixel;
            }
        }
    }
}


Link to comment
Share on other sites

Hmm, I tried using the same surface on my Blur Fill plugin, and I get a nasty banding effect. This is the same problem I was having a few months ago, as I recall now.

 

surface-test.png

(September 25th, 2023)  Sorry about any broken images in my posts. I am aware of the issue.

bp-sig.png
My Gallery  |  My Plugin Pack

Layman's Guide to CodeLab

Link to comment
Share on other sites

For which effect are you trying to reuse the same surface as both the source and the destination? You should be able to do so with the brightness adjustment, but not with the enlargement or blur. The only way I know of for a CodeLab plugin to do the Blur Fill (at least without additional surfaces) is if the blur and enlargement could somehow be done in a single step, because in both operations a pixel's value depends on other pixels, which may or may not have already been modified. (I can't immediately see how it could be done in CodeLab even with additional surfaces, but I'd need to think about it more carefully before saying it couldn't.)

Edited by MJW
Link to comment
Share on other sites

...because in both operations a pixel's valued depends on other pixels, and those pixels may or may not have already been modified

 

Yeah, I knew the pre-enlarged surface and the enlarged surface needed to be different, so I left those as is. The issue was caused by using the same surface in the Blur effect. Thanks for that explanation, I didn't realize that about Gaussian Blur.

(September 25th, 2023)  Sorry about any broken images in my posts. I am aware of the issue.

bp-sig.png
My Gallery  |  My Plugin Pack

Layman's Guide to CodeLab

Link to comment
Share on other sites

Hello, Kalama.

 

I tried to use both effects in the Render loop - it really work. Thanks for your hint. I will modify tutorial later. Maybe tomorrow. For this I will need some time.

Link to comment
Share on other sites

  • 2 years later...

Hi @ReMake     I've been doing your tutorials to familiarize myself with CodeLab and Visual Studio.  I've already done @BoltBait's tutorials, and up to this point, everything has come out perfect.

After following @BoltBait instructions here, I am unable to build the DLL because I still have (2) error messages concerning the 'MultiplyBlendOp' lines.

 

Here is a screenshot:

 

spongelesson.png

 

I have looked at 'suggested fixes' and nothing works to get rid of the red squiggly lines. I also have a small grey line in code above the red ones.

 

When I did BoltBait's ColorBalance example, it worked fine and I was able to build the DLL.

 

Please keep in mind that I do not know C# at this time.  I was simply trying to to get more familiar using these.

 

Any help would be appreciated.  Thanks!

 

Here's a dropbox link to my VS folder if you need it.     https://www.dropbox.com/sh/7j3skswsqscr9b3/AADmTb4MxKw1GnKSBFDhUrTla?dl=0

 

 

Link to comment
Share on other sites

I think you're missing a reference.

 

Add another reference to your project, click the "Browse" tab. Browse over to the "C:\Program Files\Paint.NET\" folder.

Select the file "PaintDotNet.Data.DLL". Click OK.

 

You added references for Base, Core, and Effects.  You just forgot Data.

 

  • Like 1
  • Upvote 1
Link to comment
Share on other sites

You can also use the CodeLab v4 beta; it can automatically create the Visual Studio solution for you.

 

But maybe you want to learn the "nuts and bolts" of things, and therefore want to do it manually.

  • Like 1

(September 25th, 2023)  Sorry about any broken images in my posts. I am aware of the issue.

bp-sig.png
My Gallery  |  My Plugin Pack

Layman's Guide to CodeLab

Link to comment
Share on other sites

Thank you @BoltBait and @toe_head2001

 

Yes, I'm taking the 'long route' on purpose.  I've learned quite a few things already.

 

I will try your solution BB, and post whether it worked.  :)

Link to comment
Share on other sites

YEAH!  It worked!  @BoltBait   ?  I had a sneaky suspicion that I needed to add a file under 'References'.

 

But I did not need that file for your ColorBalance lesson.  How would I have known to add that particular file for Sponge effect?

 

Or do I need the 'Data" file all the time now?  And it did not give me the icon I embedded.  I got the icon when I did ColorBalance lesson.

Edited by lynxster4
added extra info
Link to comment
Share on other sites

Yes, BoltBait is right, you did not have enough references. The idea proposed toe_head2001 I really like - is a lot easier.

Here is a list of necessary and sufficient references for this effect:

PaintDotNet.Base

PaintDotNet.Core

PaintDotNet.Data

PaintDotNet.Effects

System

System.Drawing

System.Windows.Forms

 

Good luck.

 

  • Like 1
Link to comment
Share on other sites

16 minutes ago, lynxster4 said:

YEAH!  It worked!  @BoltBait   ?  I had a sneaky suspicion that I needed to add a file under 'References'.

 

But I did not need that file for your ColorBalance lesson.  How would I have known to add that particular file for Sponge effect?

 

Or do I need the 'Data" file all the time now?  And it did not give me the icon I embedded.  I got the icon when I did ColorBalance lesson.

 

It depends.

 

Here's the thing... different parts of Paint.NET are defined in different DLL's.  The ColorBalance lesson didn't need a blending operation, so it didn't need PaintDotNet.Data (which is where Paint.NET has implemented all of the blending operations).

 

It doesn't hurt to have it added to the references. In fact, the new Create VS Project function of CodeLab 4.0 always adds ALL of the DLL's, just in case you need them later.

Link to comment
Share on other sites

1 hour ago, BoltBait said:

Here's the thing... different parts of Paint.NET are defined in different DLL's.  The ColorBalance lesson didn't need a blending operation, so it didn't need PaintDotNet.Data (which is where Paint.NET has implemented all of the blending operations).

 

It doesn't hurt to have it added to the references. In fact, the new Create VS Project function of CodeLab 4.0 always adds ALL of the DLL's, just in case you need them later.

 

I appreciate the explanation.  I still have a couple more CodeLab tuts to do, then who knows?  

 

But as @toe_head2001 said, I'm doing it this way to learn the 'nuts and bolts'.  I know so much more than I did two weeks ago.

 

And thanks @ReMake.   The VS Solution button will surely come in handy when I'm ready to dabble on my own.

 

  • Upvote 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...