-
Posts
17 -
Joined
-
Last visited
-
Days Won
3
Content Type
Events
Profiles
Forums
Blogs
Gallery
Downloads
Posts posted by pascal
-
-
Version 1.1
I made some small tweaks for overall improvement.
- Now uses Parallel.ForEach instead of tasks with locks, thanks to @NinthDesertDude and @null54.
- Small UI tweaks.
- Pre-calculates the pixel properties it has to sort on.
- More efficient sorting using arrays and vectors.
- 1
- 1
-
Pixel Sort
This plugin can be used to create a glitchy effect by sorting pixels.
- Use the interval parameters to change the intensity of the effect.
- It can sort in either horizontal or vertical direction.
- It can sort based on multiple properties, such as value, hue, saturation or individual color channels.
Note: if multiple pixels have the same property, the order might appear to be random.
Download
Located in: Effects > Distort > Pixel Sort
Code
Spoiler#region UICodes ListBoxControl<Sorter> SORTER = 0; // Sorter|RGB ListBoxControl<Direction> DIRECTION = 0; // Direction|Row IntSliderControl MIN = 60; // [0,255] Interval min IntSliderControl MAX = 180; // [0,255] Interval max ListBoxControl<Interval> INTERVAL = 0; // Interval|Threshold|Edge detection CheckboxControl INVERT = false; // Invert interval CheckboxControl FLIP = false; // {!RANDOM} Flip sorting order CheckboxControl RANDOM = false; // Random sorting order ReseedButtonControl SEED = 0; // {RANDOM} Reseed #endregion private enum Sorter { RGB, Red, Green, Blue, Alpha, Hue, Saturation, Value, Lightness, Luma, Minimum, Random } private enum Direction { Row, Column } private enum Interval { Threshold, Edge } private readonly Comparer<Vector2Int32> comparer = Comparer<Vector2Int32>.Create((a, b) => a.Y.CompareTo(b.Y)); private bool[,] mask = null; private int[,] sortMap = null; private Random random = null; public void PreRender(Surface dst, Surface src) { Rectangle selection = EnvironmentParameters.SelectionBounds; Surface wrk = new Surface(selection.Size); Surface wrkDst = new Surface(wrk.Size); wrk.CopySurface(src, selection); wrkDst.CopySurface(wrk); this.random = new Random(SEED); this.mask = GetIntervalMask(wrk, MIN, MAX, INTERVAL); Func<ColorBgra, int> mapper = GetMapper(SORTER); this.sortMap = GetSortMap(wrk, mapper, FLIP); Action<Surface, Surface>[] actions = DIRECTION switch { Direction.Row => GetSortActionsX(wrk.Height), _ => GetSortActionsY(wrk.Width) }; System.Threading.Tasks.Parallel.ForEach(actions, action => { if (IsCancelRequested) return; action(wrk, wrkDst); }); dst.CopySurface(wrkDst, selection.Location); } private Func<ColorBgra, int> GetMapper(Sorter sorter) { Func<ColorBgra, int> result = sorter switch { Sorter.Red => c => c.R, Sorter.Green => c => c.G, Sorter.Blue => c => c.B, Sorter.Hue => c => HsvColor.FromColor(c.ToColor()).Hue, Sorter.Saturation => c => HsvColor.FromColor(c.ToColor()).Saturation, Sorter.Alpha => c => c.A, Sorter.Value => Value, Sorter.Lightness => Lightness, Sorter.Luma => Luma, Sorter.Minimum => Minimum, Sorter.Random => c => random.Next(), _ => Average }; return result; } private Action<Surface, Surface>[] GetSortActionsX(int rows) { var actions = new Action<Surface, Surface>[rows]; for (int y = 0; y < rows; y++) { int tmp = y; actions[y] = (s, d) => SortRow(s, d, tmp); } return actions; } private Action<Surface, Surface>[] GetSortActionsY(int columns) { var actions = new Action<Surface, Surface>[columns]; for (int x = 0; x < columns; x++) { int tmp = x; actions[x] = (s, d) => SortColumn(s, d, tmp); } return actions; } private void SortRow(Surface src, Surface dst, int y) { for (int x = 0; x < src.Width;) { int start = NextTrueX(x, y, src.Width); int end = NextFalseX(start, y, src.Width); if (IsCancelRequested) return; if (start < end - 1) { SortRowSection(y, start, end, src, dst); } x = end + 1; } } private void SortColumn(Surface src, Surface dst, int x) { for (int y = 0; y < src.Height;) { int start = NextTrueY(x, y, src.Height); int end = NextFalseY(x, start, src.Height); if (IsCancelRequested) return; if (start < end - 1) { SortColumnSection(x, start, end, src, dst); } y = end + 1; } } private int NextTrueX(int x, int y, int maxX) { while (!mask[x,y]) { x++; if (x >= maxX) { return maxX - 1; } } return x; } private int NextFalseX(int x, int y, int maxX) { while (mask[x,y]) { x++; if (x >= maxX) { return maxX; } } return x; } private int NextTrueY(int x, int y, int maxY) { while (!mask[x,y]) { y++; if (y >= maxY) { return maxY - 1; } } return y; } private int NextFalseY(int x, int y, int maxY) { while (mask[x,y]) { y++; if (y >= maxY) { return maxY; } } return y; } private bool[,] GetIntervalMask(Surface src, int min, int max, Interval interval) { return interval switch { Interval.Edge => GetEdgeMask(src, min, max), _ => GetThresholdMask(src, min, max) }; } private bool[,] GetThresholdMask(Surface src, int min, int max) { bool[,] result = new bool[src.Width, src.Height]; min *= 3; max *= 3; for (int y = 0; y < src.Height; y++) { for (int x = 0; x < src.Width; x++) { float l = Average(src[x,y]); result[x,y] = (min <= l && l <= max) ^ INVERT; } } return result; } private bool[,] GetEdgeMask(Surface src, int min, int max) { bool[,] result = new bool[src.Width, src.Height]; for (int y = 0; y < src.Height; y++) { for (int x = 0; x < src.Width; x++) { if (!src.Bounds.Contains(x + 1, y + 1)) { result[x,y] = (min <= 127 && 127 <= max) ^ INVERT; continue; } ColorBgra cp = src[x,y]; ColorBgra np = src[x + 1, y + 1]; int r = np.R - cp.R; int g = np.G - cp.G; int b = np.B - cp.B; int mx = Math.Max(Math.Max(r, g), b); int mn = Math.Min(Math.Min(r, g), b); int m = Math.Abs(mn) > Math.Abs(mx) ? mn : mx; int l = Math.Clamp(127 + m * 3, 0, 255); result[x,y] = (min <= l && l <= max) ^ INVERT; } } return result; } private void SortRowSection(int y, int x0, int x1, Surface src, Surface dst) { int size = x1 - x0; Vector2Int32[] array = new Vector2Int32[size]; for (int x = x0; x < x1; x++) { array[x - x0] = new Vector2Int32(x, this.sortMap[x, y]); } if (RANDOM && random.Next(0, 2) == 0) Array.Sort(array, Reverse(comparer)); else Array.Sort(array, comparer); if (IsCancelRequested) return; for (int x = x0; x < x1; x++) { Vector2Int32 p = array[x - x0]; dst[x, y] = src[p.X, y]; } } private void SortColumnSection(int x, int y0, int y1, Surface src, Surface dst) { int size = y1 - y0; Vector2Int32[] array = new Vector2Int32[size]; for (int y = y0; y < y1; y++) { array[y - y0] = new Vector2Int32(y, this.sortMap[x, y]); } if (RANDOM && random.Next(0, 2) == 0) Array.Sort(array, Reverse(comparer)); else Array.Sort(array, comparer); if (IsCancelRequested) return; for (int y = y0; y < y1; y++) { Vector2Int32 p = array[y - y0]; dst[x, y] = src[x, p.X]; } } private int[,] GetSortMap(Surface src, Func<ColorBgra, int> mapper, bool flip) { var result = new int[src.Width, src.Height]; for (int y = 0; y < src.Height; y++) { for (int x = 0; x < src.Width; x++) { int val = mapper(src[x,y]); if (flip) val = -val; result[x,y] = val; } } return result; } private int Average(ColorBgra c) { return c.R + c.G + c.B; } private int Lightness(ColorBgra c) { return Value(c) + Minimum(c); } private int Value(ColorBgra c) { return Math.Max(c.R, Math.Max(c.G, c.B)); } private int Minimum(ColorBgra c) { return Math.Min(c.R, Math.Min(c.G, c.B)); } private int Luma(ColorBgra c) { return 2126 * c.R + 7152 * c.G + 722 * c.B; } private Comparer<T> Reverse<T>(Comparer<T> comp) { return Comparer<T>.Create((a, b) => comp.Compare(b, a)); } public void Render(Surface dst, Surface src, Rectangle rect) {}
- 2
- 1
- 2
-
Version 2 available
- Changed the layout to make it more clear in which order things are applied.
- Contrast now only applies to the color-mapped layer.
- Added more blending modes.
- Made the mix slider independent of blending modes.
- Improved overall code.
- 1
- 2
-
Smooth Julia Fractal
This plugin is able to render any fractal from the Julia set. As opposed to my previous plugin Advanced Julia Fractal, this new plugin makes use of normalized iteration counting to blend smoothly between colors. Increasing Iterations and Quality will result in a high quality fractal. The exposure controls can be used to fix the brightness. As always, you can use my other plugin Duotone Gradient Mapping to add coloring.
Download
Located in: Effects > Render > Smooth Julia Fractal
Code
Spoiler#region UICode DoubleSliderControl CONSTANT_A = 0; // [-2,2] Real constant DoubleSliderControl CONSTANT_B = 0; // [-2,2] Imaginary constant DoubleSliderControl ZOOM = 20; // [1,1000] Zoom DoubleSliderControl OFFSET_X = 0; // [-2,2] Offset X DoubleSliderControl OFFSET_Y = 0; // [-2,2] Offset Y IntSliderControl ITER = 100; // [1,500] Iterations DoubleSliderControl MIN_V = 0.0; // [-10.0, 0.0] Min exposure DoubleSliderControl MAX_V = 1.0; // [0.001, 100.0] Max exposure IntSliderControl QUALITY = 0; // [0,5] Quality #endregion void Render(Surface dst, Surface src, Rectangle rect) { Rectangle selection = EnvironmentParameters.SelectionBounds; ColorBgra currentPixel; int samples = 1 + QUALITY * 2; int qmin = -(samples / 2); int qmax = samples + qmin; double zoom = ZOOM * ZOOM; double multiplier = (MAX_V - MIN_V) * 255.0 / (double) (samples * ITER); double min = MIN_V * 255.0; double[] sampleOffsets = new double[samples]; for (int i = 0; i < samples; i++) { sampleOffsets[i] = (i + qmin) / (samples * zoom); } for (int y = rect.Top; y < rect.Bottom; y++) { if (IsCancelRequested) return; for (int x = rect.Left; x < rect.Right; x++) { currentPixel = src[x,y]; double value = 0.0; double fx = (x - selection.Left - selection.Width / 2.0) / zoom; double fy = (y - selection.Top - selection.Height / 2.0) / zoom; foreach (double offset in sampleOffsets) { double newX = fx + offset + OFFSET_X; double newY = fy + offset + OFFSET_Y; value += smoothFractalValue(newX, newY, ITER); } double mapped = value * multiplier + min; byte lightness = (byte) Math.Clamp(mapped, 0.0, 255.0); currentPixel.R = lightness; currentPixel.G = lightness; currentPixel.B = lightness; dst[x,y] = currentPixel; } } } double smoothFractalValue(double a, double b, int n) { double smoothValue = 0.0; for (int i = 0; i < n && a * a + b * b < 8.0; i++) { double p = a; a = a * a - b * b + CONSTANT_A; b = 2 * p * b + CONSTANT_B; smoothValue += Math.Exp(-Math.Sqrt(a * a + b * b)); } return smoothValue; }
- 2
- 2
-
Version 5 available
- The new version is more accurate than before. It converts the RGB values to the LMS color space and takes gamma correction into account.
- Protanomaly, deuteranomaly, tritanomaly and achromatomaly are removed from the plugin because those were just blending between normal vision and the corresponding deficiency.
- The deficiency 'Blue-Cone Monochromacy' was added as a second type of monochromacy.
- 2
- 1
-
Right now my attachment quota is 711.35kB / 1MB which is 69%, I'll check after this reply
Edit: Thanks for fixing it. I assume it is a bug though. Like the attachments weren't properly deleted from my profile after editing the post
-
Hi @Pixey. The problem is, those attachment do not show up in the actual post itself. The attachments are not in the post, yet the post says the limit is reached.
-
Hi,
I recently wanted to edit a post and change some attachments in that post. However, it tells me the maximum amount of attachments for that post has been reached, even though I removed all of the attachments in the post. It also gives me a link to see all the attachments I have ever uploaded, but there seems to be no option to delete any attachments. On top of that, it says the specific attachments are still in the post, even though this is not the case.
Is there a way I can remove any of my unused or old attachments?
- 1
-
Hello.
This plugin can be used to generate a Voronoi texture. It comes with color options, different render options and some other settings that I considered to be useful.
The plugin can be found in Effects > Render > Voronoi
Preview
Code
Spoiler// Name: Voronoi // Submenu: Render // Author: pascal // Title: Render Voronoi // Version: 1.0 // Desc: Generate a Voronoi texture // Keywords: // URL: // Help: #region UICode ColorWheelControl col1 = ColorBgra.FromBgr(0,0,0); // Cell Color ColorWheelControl col2 = ColorBgra.FromBgr(255,255,255); // Line Color ListBoxControl mode = 0; // Mode|Cells|Lines|Crystals|Solid IntSliderControl amount = 15; //[1,100] Count IntSliderControl size = 200; //[1,1000] Size IntSliderControl detail = 0; //[0,5] Detail IntSliderControl bright = 0; //[-100,100] Bias IntSliderControl contr = 0; //[-100,100] Contrast IntSliderControl seed = 0; //[0,1000] Seed #endregion //global variables tile[,] tiles; Rectangle bnds; int tilesize; int cols, rows; void PreRender(Surface dst, Surface src) { bnds = EnvironmentParameters.SelectionBounds; //divide canvas into tiles tilesize = (int)Math.Ceiling(bnds.Width / (float)amount); cols = amount + 4; rows = (int)Math.Ceiling(bnds.Height * amount / (float)bnds.Width) + 4; tiles = new tile[cols, rows]; //generate random point for each tile and store tile for(int i = 0; i < cols; i++) { for(int j = 0; j < rows; j++) { Random random = new Random(seed++*(j+1)*(i+1)); int tleft = (i-2)*tilesize; int tright = (i-1)*tilesize; int ttop = (j-2)*tilesize; int tbottom = (j-1)*tilesize; int randx = random.Next(tleft,tright); int randy = random.Next(ttop,tbottom); rect tilebounds = new rect(tleft,tright,ttop,tbottom); tile t = new tile(tilebounds,new point(randx, randy)); tiles[i,j] = t; } } } void Render(Surface dst, Surface src, Rectangle rect) { for(int y = rect.Top; y < rect.Bottom; y++) { for(int x = rect.Left; x < rect.Right; x++) { //get the corresponding tile index int ix = clamp(x / tilesize + 2, 2, cols - 4); int iy = clamp(y / tilesize + 2, 2, rows - 4); var points = new point[25]; //get the 25 nearest points based on the surrounding tiles for(int i = -2; i <= 2; i++) { for(int j = -2; j <= 2; j++) { tile currentTile = tiles[ix + i, iy + j]; points[(i + 2) + 5 * (j + 2)] = currentTile.point; } } //calculate the distance to each point int[] dist = new int[points.Count()]; for(int i = 0; i < dist.Count(); i++) { int diffX = points[i].x - x; int diffY = points[i].y - y; dist[i] = diffX*diffX + diffY*diffY; } //sort the distances and store each index in idx[] int[] idx = Enumerable.Range(0, dist.Count()).ToArray(); Array.Sort<int>(idx, (a, b) => dist[a].CompareTo(dist[b])); double col = 0; //LINE MODE if(mode == 1) { //get 3 nearest points var p1 = points[idx[detail]]; var p2 = points[idx[detail+1]]; var p3 = points[idx[detail+2]]; //main line double distanceSquared = distanceToMidLine(x, y, p1, p2); if(distanceSquared <= size/50.0) { //smooth line edges col = Math.Max(col, map((float)distanceSquared, size/50f, 0, 0, 1)); } //fill in gaps in some lines distanceSquared = distanceToMidLine(x, y, p1, p3); if(distanceSquared <= size/50.0) { col = Math.Max(col, map((float)distanceSquared, size/50f, 0, 0, 1)); } //round line ends var e = midPoint(p1, p2, p3); double distToEndSquared = (e.x - x) * (e.x - x) + (e.y - y) * (e.y - y); if(distToEndSquared <= size/50.0) { //draw circle at line edge col = Math.Max(col, map(distToEndSquared, size/50.0, 0, 0, 1)); } } //CRYSTAL MODE else if(mode == 2) { //closest 2 points var p1 = points[idx[detail]]; var p2 = points[idx[detail+1]]; int dist1 = (p1.x - x) * (p1.x - x) + (p1.y - y) * (p1.y - y); int dist2 = (p2.x - x) * (p2.x - x) + (p2.y - y) * (p2.y - y); //difference in distance int diff = dist2 - dist1; col = 1f - Math.Max(0, Math.Min(1, diff / 25f / size)); } //SOLID MODE else if (mode == 3) { //get closest point var p1 = points[idx[detail]]; //random value with closest point as seed Random random = new Random(p1.x * p1.y); float diff = (float)random.NextDouble(); col = Math.Max(0, Math.Min(1, diff)); } //CELLS MODE else { //get closest point var p = points[idx[detail]]; //calculated distance squared int distX = p.x - x; int distY = p.y - y; int diff = distX*distX + distY*distY; //value based on distance col = Math.Max(0, Math.Min(1, diff / 25f / size)); } //apply bias (brightness) and contrast col = brightness(col, bright/100f); col = contrast(col, contr/50f); //calculate color values byte R = limit(col*col2.R + (1f-col)*col1.R); byte G = limit(col*col2.G + (1f-col)*col1.G); byte B = limit(col*col2.B + (1f-col)*col1.B); dst[x, y] = ColorBgra.FromBgr(B, G, R); } } } byte limit(double val) { //limit byte between 0 and 255 return Math.Max((byte)0, Math.Min((byte)255, (byte)val)); } int clamp(int val, int min, int max) { //clamp value between min and max return Math.Max(min, Math.Min(val, max)); } double map(double val, double min0, double max0, double min1, double max1) { //map value to a new domain return min1 + (val - min0) / (max0 - min0) * (max1 - min1); } double brightness(double val, double bri) { //apply a brightness effect to the value if(bri == 0f) return val; if(bri > 0) { return 1 - Math.Pow(1 - val, 1 + bri); } else { return Math.Pow(val, 1 - bri); } } double contrast(double val, double con) { //apply a contrast effect to the value if(con == 0f) return val; if(con > 0) { if(val < 0.5) { return Math.Pow(2 * val, 1 + con) / 2.0; } else { return 1 - Math.Pow(-2 * val + 2, 1 + con) / 2.0; } } else { if(val < 0.5) { return 0.5 - Math.Pow(-2 * val + 1, 1 - con) / 2.0; } else { return 0.5 + Math.Pow(2 * val - 1, 1 - con) / 2.0; } } } point midPoint(point p1, point p2, point p3) { //matrix calculations to find the midpoint of a circle through 3 points int m1 = (p1.x * p1.x + p1.y * p1.y); int m2 = (p2.x * p2.x + p2.y * p2.y); int m3 = (p3.x * p3.x + p3.y * p3.y); int xm1 = m1 * p2.y + m2 * p3.y + m3 * p1.y - m1 * p3.y - m2 * p1.y - m3 * p2.y; int xm2 = p1.x * p2.y + p2.x * p3.y + p3.x * p1.y - p1.x * p3.y - p2.x * p1.y - p3.x * p2.y; int x = (int)(xm1 / (float)xm2 / 2f); int ym1 = m1 * p2.x + m2 * p3.x + m3 * p1.x - m1 * p3.x - m2 * p1.x - m3 * p2.x; int y = (int)(ym1 / (float)xm2 / -2f); return new point(x, y); } double distanceToMidLine(int x, int y, point p1, point p2) { //calculate middle point double mx = (p1.x + p2.x) / 2.0; double my = (p1.y + p2.y) / 2.0; if(p1.x == p2.x) return (y - my) * (y - my); if(p1.y == p2.y) return (x - mx) * (x - mx); //generate line double a = (p2.x - p1.x) / (double)(p2.y - p1.y); double b = my + a * mx; //calculate distance form pixel to line double top = (a * x + y - b); return top * top / (a * a + 1); } class rect { public int left, right, top, bottom; public rect(int l, int r, int t, int b) { left = l; right = r; top = t; bottom = b; } } class point { public int x, y; public point(int a, int b) { x = a; y = b; } } class tile { public rect bounds; public point point; public tile(rect b, point p) { bounds = b; point = p; } }
- 2
- 2
-
Hello.
It has been a while, but I'm back with another plugin: Perlin Waves.
This plugin uses Perlin noise to generate a wave texture. It comes with a lot of configurable options.
I hope you like it!
Download: perlinwaves.zip
11-11-2021 update: bug fix and changed alpha settings
Effect can be found in Effects > Render > Perlin Waves
Preview:
Code:
A part of the code comes from this Perlin noise implementation
Spoiler// Name: Perlin Waves // Submenu: Render // Author: pascal // Title: Generate Perlin Waves // Version: 1.0 // Desc: // Keywords: // URL: // Help: #region UICode ColorWheelControl col1 = ColorBgra.FromBgr(0, 0, 0); // Line Color ColorWheelControl col2 = ColorBgra.FromBgr(255, 255, 255); // Background Color IntSliderControl zoom = 50; // [1,200] Zoom IntSliderControl rotate = 0; // [0,1000] Move IntSliderControl width = 10; // [1,1000] Thickness IntSliderControl seed = 1; // [1,1000] Seed IntSliderControl smooth = 2; // [0,2] Interpolation IntSliderControl height = 50; // [0,100] Height IntSliderControl contr = 0; // [0,100] Contrast ListBoxControl alpha = 0; // Alpha Mode|Overlay|Preserve|Preserve Inverted|Transparent|Transparent Inverted|Blend|Blend Inverted|Clip|Clip Inverted #endregion class vector{ public float x; public float y; } void Render(Surface dst, Surface src, Rectangle rect) { for(int y = rect.Top; y < rect.Bottom; y++) { for(int x = rect.Left; x < rect.Right; x++) { if(IsCancelRequested) return; float val = (perlin(x / (float)zoom, y / (float)zoom) + 1) / 2f; float col = 0; float w = width / 1000f; float h = contrastR(height / 100f, 2); if(val > h - w && val < h + w) { if(val < h) { col = contrast(map(val, h - w, h, 0, 1), contr * contr / 100f); } else { col = contrast(map(val, h + w, h, 0, 1), contr * contr / 100f); } } ColorBgra cp = new ColorBgra(); cp.R = limit(col * col1.R + (1 - col) * col2.R); cp.G = limit(col * col1.G + (1 - col) * col2.G); cp.B = limit(col * col1.B + (1 - col) * col2.B); switch(alpha) { case 1: cp.A = src[x, y].A; break; case 2: cp.A = limit(255-src[x, y].A); break; case 3: cp = col1; cp.A = limit(col * 255); break; case 4: cp = col2; cp.A = limit((1 - col) * 255); break; case 5: cp = col1; cp.A = limit(col * 255); cp = blend(cp, src[x, y]); break; case 6: cp = col2; cp.A = limit((1 - col) * 255); cp = blend(cp, src[x, y]); break; case 7: cp = col1; cp.A = limit(col * src[x, y].A); break; case 8: cp = col2; cp.A = limit((1 - col) * src[x, y].A); break; default: cp.A = (byte)255; break; } dst[x, y] = cp; } } } byte limit(float x) { return (byte)Math.Max(0, Math.Min(255, x)); } float map(float val, float min0, float max0, float min1, float max1) { return min1 + (val - min0) / (max0 - min0) * (max1 - min1); } float contrast(float val, float con) { if(val < 0.5) { return (float)(Math.Pow(2 * val, 1 + con) / 2); } else { return (float)(1 - Math.Pow(-2 * val + 2, 1 + con) / 2); } } float contrastR(float val, float con) { if(con == 0f) return val; if(val < 0.5) { return (float)(0.5 - Math.Pow(-2 * val + 1, 1 + con) / 2); } else { return (float)(0.5 + Math.Pow(2 * val - 1, 1 + con) / 2); } } ColorBgra blend(ColorBgra a, ColorBgra b) { if(a.A == 0 && b.A == 0) return ColorBgra.FromBgra(0, 0, 0, 0); float alpha = a.A / 255f; float alphaB = b.A / 255f; float alpha0 = alpha + alphaB * (1 - alpha); byte R = limit((a.R * alpha + b.R * alphaB * (1 - alpha)) / alpha0); byte G = limit((a.G * alpha + b.G * alphaB * (1 - alpha)) / alpha0); byte B = limit((a.B * alpha + b.B * alphaB * (1 - alpha)) / alpha0); byte A = limit(alpha0 * 255); return ColorBgra.FromBgra(B, G, R, A); } vector randomGradient(int ix, int iy) { uint w = (uint)seed; uint s = w / 2; uint a = (uint)ix; uint b = (uint)iy; a *= 3284157443; b ^= a << (int) s | a >> (int)(w - s); b *= 1911520717; a ^= b << (int) s | b >> (int)(w - s); a *= 2048419325; float random = (float)(a * (3.14159265 / ~(~0u >> 1)) + rotate / 1000f * Math.Tau); vector v = new vector(); v.x = (float)Math.Sin(random); v.y = (float)Math.Cos(random); return v; } float dotGridGradient(int ix, int iy, float x, float y) { vector gradient = randomGradient(ix, iy); float dx = x - (float)ix; float dy = y - (float)iy; return (dx*gradient.x + dy*gradient.y); } float interpolate(float a0, float a1, float w) { if (0.0 > w) return a0; if (1.0 < w) return a1; switch(smooth){ case 0: return (float)((a1 - a0) * w + a0); case 1: return (float)((a1 - a0) * (3.0 - w * 2.0) * w * w + a0); default: return (float)((a1 - a0) * ((w * (w * 6.0 - 15.0) + 10.0) * w * w * w) + a0); } } float perlin(float x, float y) { int x0 = (int)x; int x1 = x0 + 1; int y0 = (int)y; int y1 = y0 + 1; float sx = x - (float)x0; float sy = y - (float)y0; float n0, n1, ix0, ix1, value; n0 = dotGridGradient(x0, y0, x, y); n1 = dotGridGradient(x1, y0, x, y); ix0 = interpolate(n0, n1, sx); n0 = dotGridGradient(x0, y1, x, y); n1 = dotGridGradient(x1, y1, x, y); ix1 = interpolate(n0, n1, sx); value = interpolate(ix0, ix1, sy); return value; }
- 3
- 1
- 3
-
This plugin can be used to render Julia set fractals using a variety of settings. Adding color options would have made the UI too big, but it can be achieved using my Duotone Gradient Mapping plugin.
I hope you enjoy this one!
Download:
Can be found in Effects > Render > Advanced Julia Fractal
Previews:
Code:
(Made in a few hours, so there is probably a lot to improve)
// Name: Advanced Julia Fractal // Submenu: Render // Author: Pascal // Title: Advanced Julia Fractal // Version: 1.0.0 // Desc: // Keywords: // URL: // Help: #region UICode IntSliderControl ca = 0; //[-300,300] Real part of constant IntSliderControl cb = 0; //[-300,300] Imaginary part of constant IntSliderControl zoom = 5000; // [1,100000] Zoom IntSliderControl xoff = 0; // [-10000,10000] Offset X IntSliderControl yoff = 0; // [-10000,10000] Offset Y IntSliderControl iterations = 50; //[1,200] Iterations IntSliderControl type = 0; //[0,3] Render type CheckboxControl innerCol = false; //Inner color CheckboxControl invert = false; //Invert CheckboxControl trans = false; //Transparency CheckboxControl invertTrans = false; //Invert Transparency IntSliderControl thresh = 0; //[0,1000] Clean IntSliderControl bright = 0; //[-100,100] Brightness IntSliderControl contr = 0; //[0,100] Contrast CheckboxControl alpha = false; //Preserve alpha #endregion public void Render(Surface dst, Surface src, Rectangle rect) { Rectangle selection = EnvironmentParameters.SelectionBounds; int sw = selection.Right-selection.Left; int sh = selection.Bottom-selection.Top; int dw = selection.Left; int dh = selection.Top; ColorBgra CurrentPixel; for (int y = rect.Top; y < rect.Bottom; y++) { if (IsCancelRequested) return; for (int x = rect.Left; x < rect.Right; x++) { if (IsCancelRequested) return; CurrentPixel = src[x,y]; double a = x-dw; double b = y-dh; double ca = this.ca/100f; double cb = this.cb/200f; double dx = (double)xoff; double dy = (double)yoff; double zoom = Math.Pow(this.zoom/100000f,2)*100000f; float val = fractal( (a-sw/2)/zoom + dx/10000f, (b-sh/2)/zoom + dy/10000f, ca, cb, iterations, type ); double thresh = this.thresh/1000f; if(val<thresh || innerCol && val >= (iterations-1)/(float)iterations){ val=0; } if(invert){ val = 1-val; } val+= bright/100f; val = limit(val,0,1); val = contrast(val,contr); val = limit(val,0,1); CurrentPixel.R = (byte)limit(val*255,0,255); CurrentPixel.G = (byte)limit(val*255,0,255); CurrentPixel.B = (byte)limit(val*255,0,255); byte A = CurrentPixel.A; CurrentPixel.A = 255; if(trans){ if(!invert^invertTrans){ CurrentPixel.A = CurrentPixel.R; } else{ CurrentPixel.A = (byte)(255-CurrentPixel.R); } } if(alpha){ CurrentPixel.A = (byte)(CurrentPixel.A*A/255); } dst[x,y] = CurrentPixel; } } } private float fractal(double x, double y,double ca, double cb, int n, int type){ if(type == 0 && x*x+y*y<n && n>0){ return fractal(x*x-y*y+ca,2*x*y+cb,ca,cb,--n,type); } if(type == 1 && Math.Abs(x+y)<2 && n>0){ return fractal(x*x-y*y+ca,2*x*y+cb,ca,cb,--n,type); } if(type == 2 && y<2 && n>0){ return fractal(x*x-y*y+ca,2*x*y+cb,ca,cb,--n,type); } if(type == 3 && x<2 && n>0){ return fractal(x*x-y*y+ca,2*x*y+cb,ca,cb,--n,type); } else{ return 1-n/(float)iterations; } } private float limit(float v, int min, int max){ if(v>max)v=max; else if(v<min)v=min; return v; } private float contrast(float x, double c){ double y; c = Math.Pow(c/2,3); if(x <= .5f){ y = .5f * Math.Pow(2*x, (c/500)+1); } else{ y = 1 - .5f * Math.Pow(2 - 2*x, (c/500)+1); } return (float)y; }
- 3
- 6
-
No need, for some reason it works now that I tried again. Very weird.
-
Yes, I know that. I can edit my post, but I can’t save the edit.
-
Not sure if this is the right place to ask, but I want to edit my post of a plugin I made. I made an improved version and want to edit the original post. The ‘Edit Topic’ just doesn’t do anything for some reason, does anyone know why?
-
[April 22th 2023] Version 2 available!
Back with my second plugin. This time I made a plugin that can be used to easily and quickly create a gradient mapping effect using 2 colors. I know pyrochild already made a gradient mapping tool, but this is just a simplified version using only 2 colors for dark and light tones. I added some options for post processing as well, like contrast and blending mode.
Download
Location: Effects > Color > Duotone Gradient Map
Code
Spoiler#region UICode ColorWheelControl DARK = ColorBgra.FromBgr(0, 0, 0); // Dark ColorWheelControl LIGHT = ColorBgra.FromBgr(255, 255, 255); // Light IntSliderControl CONTRAST = 0; // [-100,100] Contrast ListBoxControl<BlendMode> BLEND = 0; // Blending mode|Normal IntSliderControl MIX = 100; // [0,100] Mix #endregion enum BlendMode { Normal, Multiply, Screen, Darken, Lighten, Overlay, Additive } Func<byte, byte, byte> blender = null; void PreRender(Surface dst, Surface src) { blender = BLEND switch { BlendMode.Multiply => BlendMult, BlendMode.Screen => BlendScreen, BlendMode.Darken => BlendDarken, BlendMode.Lighten => BlendLighten, BlendMode.Overlay => BlendOverlay, BlendMode.Additive => BlendAdd, _ => BlendNormal }; } void Render(Surface dst, Surface src, Rectangle rect) { double mix = MIX / 100.0; double contrast = CONTRAST / 10.0; ColorBgra c; for (int y = rect.Top; y < rect.Bottom; y++) { if (IsCancelRequested) return; for (int x = rect.Left; x < rect.Right; x++) { c = src[x,y]; double value = Contrast((c.R + c.G + c.B) / 765.0, contrast); ColorBgra n = Mix(DARK, LIGHT, value); n = ApplyBlend(c, n, blender); n = Mix(c, n, mix); n.A = c.A; dst[x,y] = n; } } } double Contrast(double x, double p) { if (p == 0) { return x; } if (p > 0) { return x < 0.5 ? 0.5 * Math.Pow(2.0 * x, p + 1.0) : 1.0 - 0.5 * Math.Pow(2.0 - 2.0 * x, p + 1.0); } else { return x < 0.5 ? 0.5 - 0.5 * Math.Pow(1.0 - 2.0 * x, 1.0 - p) : 0.5 + 0.5 * Math.Pow(2.0 * x - 1.0, 1.0 - p); } } ColorBgra Mix(ColorBgra a, ColorBgra b, double value) { return ColorBgra.FromBgr( Mix(a.B, b.B, value), Mix(a.G, b.G, value), Mix(a.R, b.R, value) ); } byte Mix(byte a, byte b, double value) { double output = (1.0 - value) * a + value * b; return (byte) Math.Clamp(output, 0.0, 255.0); } ColorBgra ApplyBlend(ColorBgra a, ColorBgra b, Func<byte, byte, byte> f) { return ColorBgra.FromBgr( f(a.B, b.B), f(a.G, b.G), f(a.R, b.R) ); } byte BlendNormal(byte a, byte b) { return b; } byte BlendDarken(byte a, byte b) { return Math.Min(a, b); } byte BlendLighten(byte a, byte b) { return Math.Max(a, b); } byte BlendMult(byte a, byte b) { return (byte) Math.Clamp(a * b * 0.0039215686, 0.0, 255.0); } byte BlendScreen(byte a, byte b) { return (byte) (255 - BlendMult((byte) (255 - a), (byte) (255 - b))); } byte BlendOverlay(byte a, byte b) { return (byte) (a < 127 ? 2 * BlendMult(a, b) : 2 * BlendScreen(a, b)); } byte BlendAdd(byte a, byte b) { return (byte) Math.Clamp(a + b, 0, 255); }
- 2
- 1
- 1
-
[June 28th 2022] Version 5 available!
This plugin can be used to simulate certain colorblindness types such as: protanopia, deuteranopia, tritanopia, achromatopsia and blue-cone monochromacy.
Download:
Can be found in Effects > Color > Colorblindness
Note: if you have an older version installed, remove it manually from the Paint.NET Effects folder. The old version is called colorblindness4.dll.
The new version does not include the version number in the name.
Preview:
Code:
Spoiler#region UICode ListBoxControl<Deficiency> type = Deficiency.Default; //Color blindness type|Protanopia|Deuteranopia|Tritanopia|Achromatopsia|Blue-Cone Monochromacy|Normal Vision #endregion enum Deficiency { [LmsMatrix( 0, 1.05118294, -0.05116099, 0, 1, 0, 0, 0, 1)] Protanopia, [LmsMatrix( 1, 0, 0, 0.9513092, 0, 0.04866992, 0, 0, 1)] Deuteranopia, [LmsMatrix( 1, 0, 0, 0, 1, 0, -0.86744736, 1.86727089, 0)] Tritanopia, [LmsMatrix( 0.4, 0.6, 0, 0.4, 0.6, 0, 0.4, 0.6, 0)] Achromatopsia, [LmsMatrix( 0, 0, 1, 0, 0, 1, 0, 0, 1 )] Blue_Cone_Monochromacy, [LmsMatrix( 1, 0, 0, 0, 1, 0, 0, 0, 1)] Default } static double[,] toLmsMatrix = toMatrix(3, 3, 0.31399022, 0.63951294, 0.04649755, 0.15537241, 0.75789446, 0.08670142, 0.01775239, 0.10944209, 0.87256922); static double[,] toLinearRgbMatrix = toMatrix(3, 3, 5.47221206, -4.6419601, 0.16963708, -1.1252419, 2.29317094, -0.1678952, 0.02980165, -0.19318073, 1.16364789); class LmsMatrix: Attribute { public double[,] matrix {get; private set;} internal LmsMatrix(params double[] matrix) { this.matrix = toMatrix(3, 3, matrix); } public static LmsMatrix getMatrix(Deficiency d) { return (LmsMatrix) Attribute.GetCustomAttribute(deficiencyOf(d), typeof(LmsMatrix)); } static MemberInfo deficiencyOf(Deficiency d) { return typeof(Deficiency).GetField(Enum.GetName(typeof(Deficiency), d)); } } static double[,] toLinear(params byte[] rgb) { double[,] linear = new double[rgb.Length, 1]; for (int i = 0; i < linear.Length; i++) { linear[i, 0] = Math.Clamp(rgb[i] <= 0.04045 * 255.0 ? (rgb[i] / 255.0) / 12.92 : Math.Pow((rgb[i] / 255.0 + 0.055) / 1.055, 2.4), 0.0, 1.0); } return linear; } static byte[] fromLinear(double[,] linear) { byte[] normal = new byte[linear.GetLength(0)]; for (int i = 0; i < normal.Length; i++) { double l = Math.Clamp(linear[i, 0], 0.0, 1.0); normal[i] = Math.Clamp((byte) (l <= 0.0031308 ? l * 255.0 * 12.92 : 255.0 * (1.055 * Math.Pow(l, 0.41666) - 0.055)), (byte) 0, (byte) 255); } return normal; } static double[,] multiplyMatrix(double[,] a, double[,] b) { int rA = a.GetLength(0); int cA = a.GetLength(1); int cB = b.GetLength(1); double[,] result = new double[rA, cB]; for (int i = 0; i < rA; i++) { for (int j = 0; j < cB; j++) { double temp = 0; for (int k = 0; k < cA; k++) { temp += a[i, k] * b[k, j]; } result[i, j] = temp; } } return result; } static double[,] toMatrix(int rows, int columns, params double[] values) { double[,] result = new double[rows, columns]; for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { result[i, j] = values[j + i * columns]; } } return result; } static double[,] getColorBlindnessMatrix(double[,] lmsMatrix) { return multiplyMatrix( multiplyMatrix( toLinearRgbMatrix, lmsMatrix), toLmsMatrix ); } static ColorBgra applyColorBlindnessMatrix(double[,] matrix, ColorBgra pixel) { byte[] rgb = new byte[]{pixel.R, pixel.G, pixel.B}; byte[] newRgb = fromLinear( multiplyMatrix( matrix, toLinear(rgb) ) ); return ColorBgra.FromBgra(newRgb[2], newRgb[1], newRgb[0], pixel.A); } void Render(Surface dst, Surface src, Rectangle rect) { double[,] matrix = getColorBlindnessMatrix(LmsMatrix.getMatrix(type).matrix); for (int y = rect.Top; y < rect.Bottom; y++) { if (IsCancelRequested) return; for (int x = rect.Left; x < rect.Right; x++) { dst[x,y] = applyColorBlindnessMatrix(matrix, src[x,y]); } } }
- 2
- 4
Pixel Sort [v2 2023-05-18]
in Plugins - Publishing ONLY!
Posted · Edited by pascal
Version 2 available!
Added some extra features.
- Now contains more properties to sort on, including complete randomness. It includes all of the following: RGB, Red, Green, Blue, Alpha, Hue, Saturation, Value, Lightness, Luma, Minimum and Random. The previous Value setting is now RGB, with the current Value being the value of the pixel's HSV color.
- Added Edge detection as an extra interval function besides Threshold. This can be used to keep the overall shapes of objects in the image.
- Re-ordered the UI