// The MIT License // Copyright © 2020 Roger Cabo Ashauer // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, // sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // https://de.wikipedia.org/wiki/MIT-Lizenz // This solution is based on Fast image convolutions by Wojciech Jarosz. // http://elynxsdk.free.fr/ext-docs/Blur/Fast_box_blur.pdf // And Ivan Kutskir // http://blog.ivank.net/fastest-gaussian-blur.html // And Mike Demyl // https://github.com/mdymel // https://github.com/mdymel/superfastblur using System.Collections.Generic; using System.Threading.Tasks; using Unity.Collections; using UnityEngine; namespace DA_Assets.Tools { public static class ParallelGaussianBlurExtensions { public static void Blur(this Texture2D texture2D, float step = 0.5f) { ParallelGaussianBlur bl = new ParallelGaussianBlur(texture2D, step); bl.GaussianBlur(); } } public class ParallelGaussianBlur { public ParallelGaussianBlur(Texture2D tex, float step) { this._tex2D = tex; this._radial = step; } private readonly ParallelOptions _pOptions = new ParallelOptions { MaxDegreeOfParallelism = 8 }; private NativeArray _RawTextureData = new NativeArray(); private Texture2D _tex2D; private float _radial; private int _width; private int _height; private int[] m_red; private int[] m_green; private int[] m_blue; private int[] m_alpha; public void GaussianBlur() { _RawTextureData = _tex2D.GetRawTextureData(); _width = _tex2D.width; _height = _tex2D.height; _width = _tex2D.width; _height = _tex2D.height; m_red = new int[_width * _height]; m_green = new int[_width * _height]; m_blue = new int[_width * _height]; m_alpha = new int[_width * _height]; Parallel.For(0, _width * _height, _pOptions, i => { m_red[i] = _RawTextureData[i].r; m_green[i] = _RawTextureData[i].g; m_blue[i] = _RawTextureData[i].b; m_alpha[i] = _RawTextureData[i].a; }); int[] newAlpha = new int[_width * _height]; int[] newRed = new int[_width * _height]; int[] newGreen = new int[_width * _height]; int[] newBlue = new int[_width * _height]; Parallel.Invoke( () => gaussBlur_4(m_alpha, newAlpha, _radial), () => gaussBlur_4(m_red, newRed, _radial), () => gaussBlur_4(m_green, newGreen, _radial), () => gaussBlur_4(m_blue, newBlue, _radial)); Parallel.For(0, _width * _height, _pOptions, i => { if (newAlpha[i] > 255) newAlpha[i] = 255; if (newRed[i] > 255) newRed[i] = 255; if (newGreen[i] > 255) newGreen[i] = 255; if (newBlue[i] > 255) newBlue[i] = 255; if (newAlpha[i] < 0) newAlpha[i] = 0; if (newRed[i] < 0) newRed[i] = 0; if (newGreen[i] < 0) newGreen[i] = 0; if (newBlue[i] < 0) newBlue[i] = 0; _RawTextureData[i] = new Color32((byte)newRed[i], (byte)newGreen[i], (byte)newBlue[i], (byte)newAlpha[i]); }); _tex2D.Apply(); } private int[] boxesForGauss(float sigma, int n) { float wIdeal = Mathf.Sqrt((12 * sigma * sigma / n) + 1); int wl = (int)Mathf.Floor(wIdeal); if (wl % 2 == 0) { wl--; } int wu = wl + 2; float mIdeal = (float)(12 * sigma * sigma - n * wl * wl - 4 * n * wl - 3 * n) / (-4 * wl - 4); float m = Mathf.Round(mIdeal); List sizes = new List(); for (int i = 0; i < n; i++) { sizes.Add(i < m ? wl : wu); } return sizes.ToArray(); } private void gaussBlur_4(int[] colorChannel, int[] destChannel, float r) { int[] bxs = boxesForGauss(r, 3); boxBlur_4(colorChannel, destChannel, _width, _height, (bxs[0] - 1) / 2); boxBlur_4(destChannel, colorChannel, _width, _height, (bxs[1] - 1) / 2); boxBlur_4(colorChannel, destChannel, _width, _height, (bxs[2] - 1) / 2); } private void boxBlur_4(int[] colorChannel, int[] destChannel, int w, int h, int r) { for (int i = 0; i < colorChannel.Length; i++) { destChannel[i] = colorChannel[i]; } boxBlurH_4(destChannel, colorChannel, w, h, r); boxBlurT_4(colorChannel, destChannel, w, h, r); } private void boxBlurH_4(int[] colorChannel, int[] dest, int w, int h, int radial) { float iar = (float)1 / (radial + radial + 1); Parallel.For(0, h, _pOptions, i => { int ti = i * w; int li = ti; int ri = ti + radial; int fv = colorChannel[ti]; int lv = colorChannel[ti + w - 1]; int val = (radial + 1) * fv; for (int j = 0; j < radial; j++) { val += colorChannel[ti + j]; } for (int j = 0; j <= radial; j++) { val += colorChannel[ri++] - fv; dest[ti++] = (int)Mathf.Round(val * iar); } for (int j = radial + 1; j < w - radial; j++) { val += colorChannel[ri++] - dest[li++]; dest[ti++] = (int)Mathf.Round(val * iar); } for (int j = w - radial; j < w; j++) { val += lv - colorChannel[li++]; dest[ti++] = (int)Mathf.Round(val * iar); } }); } private void boxBlurT_4(int[] colorChannel, int[] dest, int w, int h, int r) { float iar = (float)1 / (r + r + 1); Parallel.For(0, w, _pOptions, i => { int ti = i; int li = ti; int ri = ti + r * w; int fv = colorChannel[ti]; int lv = colorChannel[ti + w * (h - 1)]; int val = (r + 1) * fv; for (int j = 0; j < r; j++) { val += colorChannel[ti + j * w]; } for (int j = 0; j <= r; j++) { val += colorChannel[ri] - fv; dest[ti] = (int)Mathf.Round(val * iar); ri += w; ti += w; } for (int j = r + 1; j < h - r; j++) { val += colorChannel[ri] - colorChannel[li]; dest[ti] = (int)Mathf.Round(val * iar); li += w; ri += w; ti += w; } for (int j = h - r; j < h; j++) { val += lv - colorChannel[li]; dest[ti] = (int)Mathf.Round(val * iar); li += w; ti += w; } }); } } }