19 using System.Threading.Tasks;
53 return FilterSRGB(image, scale);
62 for (
int i = 0; i < boxes.Length; i++)
66 image = box.
Filter(image, 1);
78 private static int[] BoxesForGauss(
double standardDeviation,
int count)
80 double idealWeight = Math.Sqrt((12 * standardDeviation * standardDeviation / count) + 1);
82 int lowerWeight = (int)Math.Floor(idealWeight);
84 if (lowerWeight % 2 == 0)
89 int upperWeight = lowerWeight + 2;
91 double idealSize = (12 * standardDeviation * standardDeviation - count * lowerWeight * lowerWeight - 4 * count * lowerWeight - 3 * count) / (-4 * lowerWeight - 4);
93 int m = (int)Math.Round(idealSize);
95 int[] sizes =
new int[count];
97 for (
int i = 0; i < count; i++)
99 sizes[i] = i < m ? lowerWeight : upperWeight;
105 private RasterImage FilterSRGBExact(RasterImage image,
double standardDeviation)
107 IntPtr intermediateData = System.Runtime.InteropServices.Marshal.AllocHGlobal(image.Width * image.Height * (image.HasAlpha ? 4 : 3));
108 IntPtr tbrData = System.Runtime.InteropServices.Marshal.AllocHGlobal(image.Width * image.Height * (image.HasAlpha ? 4 : 3));
109 GC.AddMemoryPressure(2 * image.Width * image.Height * (image.HasAlpha ? 4 : 3));
111 int width = image.
Width;
112 int height = image.Height;
114 int kernelSize = (int)Math.Ceiling(standardDeviation * 3);
116 double[] kernel =
new double[kernelSize * 2 + 1];
120 for (
int i = 0; i <= kernelSize; i++)
122 kernel[i] = Math.Exp(-(kernelSize - i) * (kernelSize - i) / (2 * standardDeviation * standardDeviation));
126 kernel[2 * kernelSize - i] = kernel[i];
128 total += kernel[i] * 2;
136 for (
int i = 0; i < kernel.Length; i++)
141 int pixelSize = image.HasAlpha ? 4 : 3;
142 int stride = image.Width * pixelSize;
146 double size = Math.Sqrt((
double)image.Width * image.Height);
152 else if (size <= 512)
154 threads = Math.Min(4, Environment.ProcessorCount);
158 threads = Math.Min(8, Environment.ProcessorCount);
163 byte* input = (
byte*)image.ImageDataAddress;
164 byte* intermediate = (
byte*)intermediateData;
165 byte* output = (
byte*)tbrData;
173 for (
int x = 0; x < width; x++)
180 for (
int targetX = 0; targetX <= kernelSize * 2; targetX++)
182 int tX = Math.Min(Math.Max(0, x + targetX - kernelSize), width - 1);
184 double a = input[y * stride + tX * 4 + 3] / 255.0;
186 weight += kernel[targetX] * a;
188 R += kernel[targetX] * input[y * stride + tX * 4] * a;
189 G += kernel[targetX] * input[y * stride + tX * 4 + 1] * a;
190 B += kernel[targetX] * input[y * stride + tX * 4 + 2] * a;
195 intermediate[y * stride + x * 4] = (byte)Math.Min(255, Math.Max(0, R / weight));
196 intermediate[y * stride + x * 4 + 1] = (byte)Math.Min(255, Math.Max(0, G / weight));
197 intermediate[y * stride + x * 4 + 2] = (byte)Math.Min(255, Math.Max(0, B / weight));
198 intermediate[y * stride + x * 4 + 3] = (byte)Math.Min(255, Math.Max(0, weight * 255));
202 intermediate[y * stride + x * 4] = 0;
203 intermediate[y * stride + x * 4 + 1] = 0;
204 intermediate[y * stride + x * 4 + 2] = 0;
205 intermediate[y * stride + x * 4 + 3] = 0;
214 for (
int x = 0; x < width; x++)
220 for (
int targetX = 0; targetX <= kernelSize * 2; targetX++)
222 int tX = Math.Min(Math.Max(0, x + targetX - kernelSize), width - 1);
224 R += kernel[targetX] * input[y * stride + tX * 3];
225 G += kernel[targetX] * input[y * stride + tX * 3 + 1];
226 B += kernel[targetX] * input[y * stride + tX * 3 + 2];
229 intermediate[y * stride + x * 3] = (byte)Math.Min(255, Math.Max(0, R));
230 intermediate[y * stride + x * 3 + 1] = (byte)Math.Min(255, Math.Max(0, G));
231 intermediate[y * stride + x * 3 + 2] = (byte)Math.Min(255, Math.Max(0, B));
242 for (
int x = 0; x < width; x++)
251 for (
int targetY = 0; targetY <= kernelSize * 2; targetY++)
253 int tY = Math.Min(Math.Max(0, y + targetY - kernelSize), height - 1);
255 double a = intermediate[tY * stride + x * 4 + 3] / 255.0;
257 weight += kernel[targetY] * a;
259 R += kernel[targetY] * intermediate[tY * stride + x * 4] * a;
260 G += kernel[targetY] * intermediate[tY * stride + x * 4 + 1] * a;
261 B += kernel[targetY] * intermediate[tY * stride + x * 4 + 2] * a;
266 output[y * stride + x * 4] = (byte)Math.Min(255, Math.Max(0, R / weight));
267 output[y * stride + x * 4 + 1] = (byte)Math.Min(255, Math.Max(0, G / weight));
268 output[y * stride + x * 4 + 2] = (byte)Math.Min(255, Math.Max(0, B / weight));
269 output[y * stride + x * 4 + 3] = (byte)Math.Min(255, Math.Max(0, weight * 255));
273 output[y * stride + x * 4] = 0;
274 output[y * stride + x * 4 + 1] = 0;
275 output[y * stride + x * 4 + 2] = 0;
276 output[y * stride + x * 4 + 3] = 0;
285 for (
int x = 0; x < width; x++)
291 for (
int targetY = 0; targetY <= kernelSize * 2; targetY++)
293 int tY = Math.Min(Math.Max(0, y + targetY - kernelSize), height - 1);
295 R += kernel[targetY] * intermediate[tY * stride + x * 3];
296 G += kernel[targetY] * intermediate[tY * stride + x * 3 + 1];
297 B += kernel[targetY] * intermediate[tY * stride + x * 3 + 2];
300 output[y * stride + x * 3] = (byte)Math.Min(255, Math.Max(0, R));
301 output[y * stride + x * 3 + 1] = (byte)Math.Min(255, Math.Max(0, G));
302 output[y * stride + x * 3 + 2] = (byte)Math.Min(255, Math.Max(0, B));
311 for (
int y = 0; y < height; y++)
316 for (
int y = 0; y < height; y++)
323 ParallelOptions options =
new ParallelOptions() { MaxDegreeOfParallelism = threads };
325 Parallel.For(0, height, options, yLoop);
326 Parallel.For(0, height, options, xLoop);
330 System.Runtime.InteropServices.Marshal.FreeHGlobal(intermediateData);
331 GC.RemoveMemoryPressure(image.Width * image.Height * (image.HasAlpha ? 4 : 3));
333 DisposableIntPtr disp =
new DisposableIntPtr(tbrData);
335 return new RasterImage(ref disp, image.Width, image.Height, image.HasAlpha, image.Interpolate);