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;
}
}
}
}
}