Jump to content
How to Install Plugins ×

Point Blur Effect


Boude

Recommended Posts

PointBlurSample.png

 

PointBlur.zip

 

Latest version: 1.4.9

 

Hello everyone,

I'm back. Not with some easy to make image. No, I've got something you might actually use: Point Blur.

Point Blur is a simple effect, doing the reverse of Zoom Blur. Whereas Zoom Blur blurs away from a certain point, Point Blur blurs to this point, hence Point Blur.

A couple of therms used in replies:

Point: the pixel, user defined, that's the centre of the point blur.

Current pixel: a plugin steps through each pixel, the "current pixel" is the pixel, he is calculating the colour for.

Step loop: the loop used to step through the pixels between current pixel and point.

A glance under the hood, requires some knowledge of mathematics:

Point Blur works by averaging the colours of the pixels in the line of that pixel and the point chosen.

For example:

Current Pixel = 0,0 (Coordinates (X,Y))

Point Chosen = 3,0

Pixel 1,0 = Black

Pixel 2,0 = White

Current Pixel is the average of the colours of the pixels in the line of itself and the point chosen (1,0 and 2,0), so it will be gray. Not all pixels are so simple, but with some application of trigonometric functions, it works.

The new render method uses the distance from the current pixel to affect the colour; longer distance, less effect on current pixel.

Enjoy.

 

Source:

Spoiler
using System.Collections.Generic;
using System.Drawing;
using PaintDotNet;
using PaintDotNet.Effects;
using PaintDotNet.IndirectUI;
using PaintDotNet.PropertySystem;
using System;

namespace PointBlur
{
public class PointBlur : PropertyBasedEffect
{
	public static string StaticName { get { return "Point Blur"; } }

	public static Bitmap StaticImage { get { return new Bitmap(16, 16); } }

	public static string StaticSubmenuName { get { return SubmenuNames.Blurs; } }

	public enum PropertyNames
	{
		Point,
		Length,
		AlphaMultiplier,
		Quality,
		Old
	}

	public PointBlur()
		: base(StaticName, StaticImage, StaticSubmenuName, EffectFlags.Configurable)
	{

	}
	//Properties from UI
	protected override PropertyCollection OnCreatePropertyCollection()
	{
		List<Property> props = new List<Property>();			
		props.Add(new DoubleVectorProperty(PropertyNames.Point, new Pair<double, double>(0, 0), new Pair<double, double>(-1, -1), new Pair<double, double>(1, 1)));
		props.Add(new Int32Property(PropertyNames.Length, 10, 0, 300));
		props.Add(new Int32Property(PropertyNames.Quality, 3, 1, 5));
		props.Add(new BooleanProperty(PropertyNames.Old, false));
		props.Add(new DoubleProperty(PropertyNames.AlphaMultiplier, 1, 1, 3));
		return new PropertyCollection(props);
	}
	//UI Creation (using IndirectUI)
	protected override ControlInfo OnCreateConfigUI(PropertyCollection props)
	{
		ControlInfo configUI = CreateDefaultConfigUI(props);
		ImageResource img = ImageResource.FromImage(EnvironmentParameters.SourceSurface.CreateAliasedBitmap());
		configUI.SetPropertyControlValue(PropertyNames.Point, ControlInfoPropertyNames.DisplayName, "Set convention point");
		configUI.SetPropertyControlValue(PropertyNames.Point, ControlInfoPropertyNames.StaticImageUnderlay, img);

		configUI.SetPropertyControlValue(PropertyNames.Length, ControlInfoPropertyNames.DisplayName, "Blur length");

		configUI.SetPropertyControlValue(PropertyNames.Quality, ControlInfoPropertyNames.DisplayName, "Quality");

		configUI.SetPropertyControlValue(PropertyNames.AlphaMultiplier, ControlInfoPropertyNames.DisplayName, "Alpha Multiplier");

		configUI.SetPropertyControlValue(PropertyNames.Old, ControlInfoPropertyNames.DisplayName, "Old");

		configUI.SetPropertyControlValue(PropertyNames.Old, ControlInfoPropertyNames.Description, "Render method selection, new one has higher quality (Old ignores Alpha Multiplier).");
		return configUI;
	}
	//Variables used in code
	Point point;
	int length;
	double quality;
	bool old;
	double alphaMultiplier;
	//Receiving variables used
	protected override void OnSetRenderInfo(PropertyBasedEffectConfigToken newToken, RenderArgs dstArgs, RenderArgs srcArgs)
	{
		Pair<double, double> tempPoint = (Pair<double, double>)newToken.GetProperty(PropertyNames.Point).Value;

		Rectangle bounds = EnvironmentParameters.GetSelection(EnvironmentParameters.SourceSurface.Bounds).GetBoundsInt();

		double x = ((tempPoint.First + 1) * bounds.Width / 2) + bounds.Left;
		double y = ((tempPoint.Second + 1) * bounds.Height / 2) + bounds.Top;

		point = new Point((int)x, (int)y);

		length = (int)newToken.GetProperty(PropertyNames.Length).Value;

		quality = (double)((double)1 / (int)newToken.GetProperty(PropertyNames.Quality).Value);

		alphaMultiplier = (double)newToken.GetProperty(PropertyNames.AlphaMultiplier).Value;

		old = (bool)newToken.GetProperty(PropertyNames.Old).Value;
		base.OnSetRenderInfo(newToken, dstArgs, srcArgs);
	}
	//Starting the render
	protected unsafe override void OnRender(Rectangle[] rois, int startIndex, int length)
	{
		for (int i = startIndex; i < startIndex + length; ++i)
		{
			Rectangle rect = rois[i];
			if (old)
				RenderOld(DstArgs.Surface, SrcArgs.Surface, rect);
			else
				RenderNew(DstArgs.Surface, SrcArgs.Surface, rect);
		}
	}
	//The old render method
	void RenderOld(Surface dst, Surface src, Rectangle rect)
	{
		Rectangle selection = EnvironmentParameters.GetSelection(src.Bounds).GetBoundsInt();
		for (int y = rect.Top; y < rect.Bottom; y++)
		{
			for (int x = rect.Left; x < rect.Right; x++)
			{
				//Angle from current pixel to point
				double angle = Math.Atan2(point.Y - y, point.X - x);
				angle = (Math.PI * 1.5) - angle;
				//The colours used (all pixels colours between current pixel and point are added up to each other and stored in this array, later divided to end up with an average)
				double[] colors = new double[4];
				//The number I divide with (the plural is a remnant of a bad thought, removed by debugging)
				double dividers = 0;

				//Looping through pixels between current pixel and point (quality = 1/(quality from UI))
				for (double i = 1; i < length; i += quality)
				{
					//Pixel coords of which colours are added up
					int xPix = (int)((Math.Sin(angle) * i) + x).Clamp(src.Bounds.Left, src.Bounds.Right - 1);
					int yPix = (int)((Math.Cos(angle) * i) + y).Clamp(src.Bounds.Top, src.Bounds.Bottom - 1);

					colors[0] += src[xPix, yPix].A;
					colors[1] += src[xPix, yPix].R;
					colors[2] += src[xPix, yPix].B;
					colors[3] += src[xPix, yPix].G;
					dividers++;
				}
				//setting colours, bad structure remnant of bad assumptions
				ColorBgra pixel = new ColorBgra();
				double color = colors[0] / dividers;
				pixel.A = (byte)color;
				color = colors[1] / dividers;
				pixel.R = (byte)color;
				color = colors[2] / dividers;
				pixel.B = (byte)color;
				color = colors[3] / dividers;
				pixel.G = (byte)color;
				dst[x, y] = pixel;
			}
		}
	}

	//The new render method
	void RenderNew(Surface dst, Surface src, Rectangle rect)
	{
		Rectangle selection = EnvironmentParameters.GetSelection(src.Bounds).GetBoundsInt();
		for (int y = rect.Top; y < rect.Bottom; y++)
		{
			for (int x = rect.Left; x < rect.Right; x++)
			{
				//Angle from current pixel to point
				double angle = Math.Atan2(point.Y - y, point.X - x);
				angle = (Math.PI * 1.5) - angle;
				//The colours used (all pixels colours between current pixel and point are added up to each other and stored in this array, later divided to end up with an average)
				double[] colors = new double[4];
				//The number I divide with (the plural is a remnant of a bad thought, removed by debugging)
				double dividers = 0;

				//Looping through pixels between current pixel and point (quality = 1/(quality from UI))
				for (double i = 1; i < length; i += quality)
				{
					//Pixel coords of which colours are added up
					int xPix = (int)((Math.Sin(angle) * i) + x).Clamp(src.Bounds.Left, src.Bounds.Right - 1);
					int yPix = (int)((Math.Cos(angle) * i) + y).Clamp(src.Bounds.Top, src.Bounds.Bottom - 1);
					double multiplier = length - i;

					colors[0] += src[xPix, yPix].A * multiplier;
					colors[1] += src[xPix, yPix].R * multiplier;
					colors[2] += src[xPix, yPix].B * multiplier;
					colors[3] += src[xPix, yPix].G * multiplier;
					dividers += multiplier;
				}
				//setting colours, bad structure remnant of bad assumptions
				ColorBgra pixel = new ColorBgra();
				double color = colors[0] * alphaMultiplier / dividers;
				pixel.A = (byte)color.Clamp(0, 255);
				color = colors[1] / dividers;
				pixel.R = (byte)color;
				color = colors[2] / dividers;
				pixel.B = (byte)color;
				color = colors[3] / dividers;
				pixel.G = (byte)color;
				dst[x, y] = pixel;
			}
		}
	}
}
}

 

 

yhsjjie.png

History repeats itself, because nobody was paying attention the first time.

Link to comment
Share on other sites

Thank you so much for this! I had been hoping for a blur that actually blur to a vanishing point! Nice name for the plug-in! :coffee:

Officially retired from this forum. Have a nice day.

Link to comment
Share on other sites

Thanks guys, this plugin was the reason I started learning C#. I missed it, so now we're united.

If anyone has any functions, he would look to see implemented in the plugin, feel free to ask.

BTW, if anyone would like a look at the source, feel free to ask. Note: no comments.

Edited by Boude

yhsjjie.png

History repeats itself, because nobody was paying attention the first time.

Link to comment
Share on other sites

Ok guys, thanks for your great response. I might add some functions this weekend, if I have nothing better to do. (Surprisingly I also have a life)

@n d: Source added, even some comments (lucky guy). I don't know how experienced you're with C#, so if you've any questions, feel free to ask.

I actually copied half my code from MadJik's codelab to indirectUI (somewhere in plugin development), so you might want to take a sneak peak there as well.

yhsjjie.png

History repeats itself, because nobody was paying attention the first time.

Link to comment
Share on other sites

You dont get the point correctly :roll: i got a screenie bout it

http://cookies.dcsrvdls.com/images/screenie.jpg

this:

double x = (tempPoint.First + 1) * bounds.Width / 2;
double y = (tempPoint.Second + 1) * bounds.Height / 2;

should be more like:

double x = ((tempPoint.First + 1) * bounds.Width / 2) + bounds.Left;
double y = ((tempPoint.Second + 1) * bounds.Height / 2) + bounds.Top;

i think

Edited by Cookies
CookiesSigPDN2.png
Link to comment
Share on other sites

@Cookies: Thanks for pointing that out. Will fix it on next update.

After some research and another mind destroying one hour trip in the tube, I came across something one might call a bug (no errors, don't worry). On next update distance from pixel in the "step loop" will affect how much they affect the colour of "current pixel". In plain common English, it will look better.

Also possible new icon (instead of blank bitmap) and I increase the possibility of updating this weekend (now assumption worthy).

Even more, when I'm explaining stuff the plugin does, I notice to use certain words to describe aspects of the plugin, some of these are now described in the main post (top of page 1).

Edit:

Forget weekend, updated now.

Updates:

point now set correctly

added alpha multiplier, for less lossy alpha increase

better render method, old one still included, but no longer updated

Edited by Boude

yhsjjie.png

History repeats itself, because nobody was paying attention the first time.

Link to comment
Share on other sites

  • 3 weeks later...

Nice one: I remember making a request for someone to develop this type of plugin over 2 years ago :)

This should be very useful.

Thanks.

-CJ

PKRM.png

PKRG.pngPKRT.pngPKRD.pngPKRP.png

Link to comment
Share on other sites

Thanks guys, this plugin is only a logical "spin-off" of zoom blur. So requests are logical, I can remember at least one other (negative zoom blur).

@cj: This plugin is pretty much exactly what you asked for, so great minds think the same.

While thinking about this, I had this idea: what if the plugin would blur in perspective (like atmospheric perspective, objects (read: "pixels") close to the vanishing point are blurred more then objects further away, of course this would be optional. So what do you think?

yhsjjie.png

History repeats itself, because nobody was paying attention the first time.

Link to comment
Share on other sites

certainly would an interesting take on this already great plugin. would look forward to seeing how that version turns out. LOL!

ciao OMA

thanks for the great plugin Boude..

great to see you back Oma..have admired your work immensely..albeit from afar..hope to see more from you soon.. B)

regards j.d.

Link to comment
Share on other sites

After some messing around with my code, I found that a perspective blur wasn't possible (not without a huge workaround). The result isn't as good as you might expect. So you'll have to do with the current version.

yhsjjie.png

History repeats itself, because nobody was paying attention the first time.

Link to comment
Share on other sites

  • 4 months later...

Cool but :/... it needs an icon I am new in the forums but i have been using paint.NET For a long time and htis was my first plug-in it needs an icon so I made some sort of it here you go!

image.png

maybe is not perfect but at least is an icon :XD:

Fantastic effect!

Edited by Ego Eram Reputo
Fixed malformed URL
Link to comment
Share on other sites

  • 1 year later...

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...