Skip to content

Instantly share code, notes, and snippets.

@nikeee
Last active August 29, 2015 13:57
Show Gist options
  • Save nikeee/9710214 to your computer and use it in GitHub Desktop.
Save nikeee/9710214 to your computer and use it in GitHub Desktop.
class BitmapExtensions
{
public static unsafe Bitmap StackBlur(this Bitmap sourceImage, int radius)
{
int width = sourceImage.Width;
int height = sourceImage.Height;
int size = radius << 1;
var destinationImage = new Bitmap(width, height);
var lockRect = new Rectangle(0, 0, width, height);
BitmapData sourceData = sourceImage.LockBits(lockRect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
BitmapData destinationData = destinationImage.LockBits(lockRect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
int pointerInc = destinationData.Stride; - 3;
Parallel.For(0, height, (int y) =>
{
byte* sourcePointer = (byte*)sourceData.Scan0 + y * sourceData.Stride;
byte* destinationPointer = (byte*)destinationData.Scan0 + y * destinationData.Stride;
int alpha = 0;
int red = 0;
int green = 0;
int blue = 0;
var colors = new Queue<byte[]>(size);
for (int i = 0; i < Math.Min(radius, width); i++)
{
var color = new byte[4]; // may use stackalloc here, but Generics do not support pointer types
blue += color[3] = *sourcePointer;
green += color[2] = *++sourcePointer;
red += color[1] = *++sourcePointer;
alpha += color[0] = *++sourcePointer;
++sourcePointer;
colors.Enqueue(color);
}
for (int x = 0; x < width; x++)
{
if (colors.Count == size)
{
byte[] subtract = colors.Dequeue();
alpha -= subtract[0];
red -= subtract[1];
green -= subtract[2];
blue -= subtract[3];
}
if (x + radius < width)
{
var color = new byte[4]; // may use stackalloc here, but Generics do not support pointer types
blue += color[3] = *(sourcePointer);
green += color[2] = *(++sourcePointer);
red += color[1] = *(++sourcePointer);
alpha += color[0] = *(++sourcePointer);
++sourcePointer;
colors.Enqueue(color);
}
*destinationPointer = (byte)(blue / colors.Count);
*(++destinationPointer) = (byte)(green / colors.Count);
*(++destinationPointer) = (byte)(red / colors.Count);
*(++destinationPointer) = (byte)(alpha / colors.Count);
++destinationPointer;
}
});
Parallel.For(0, width, (int x) =>
{
byte* sourcePointer = (byte*)destinationData.Scan0 + x * 4;
byte* destinationPointer = (byte*)destinationData.Scan0 + x * 4;
int alpha = 0;
int red = 0;
int green = 0;
int blue = 0;
var colors = new Queue<byte[]>(size);
for (int i = 0; i < Math.Min(radius, height); i++)
{
var color = new byte[4]; // may use stackalloc here, but Generics do not support pointer types
blue += color[3] = *(sourcePointer);
green += color[2] = *(++sourcePointer);
red += color[1] = *(++sourcePointer);
alpha += color[0] = *(++sourcePointer);
colors.Enqueue(color);
sourcePointer += pointerInc;
}
for (int y = 0; y < height; y++)
{
if (colors.Count == size)
{
byte[] subtract = colors.Dequeue();
alpha -= subtract[0];
red -= subtract[1];
green -= subtract[2];
blue -= subtract[3];
}
if (y + radius < height)
{
var color = new byte[4]; // may use stackalloc here, but Generics do not support pointer types
blue += color[3] = *(sourcePointer);
green += color[2] = *(++sourcePointer);
red += color[1] = *(++sourcePointer);
alpha += color[0] = *(++sourcePointer);
colors.Enqueue(color);
sourcePointer += pointerInc;
}
*destinationPointer = (byte)(blue / colors.Count);
*(++destinationPointer) = (byte)(green / colors.Count);
*(++destinationPointer) = (byte)(red / colors.Count);
*(++destinationPointer) = (byte)(alpha / colors.Count);
destinationPointer += pointerInc;
}
});
sourceImage.UnlockBits(sourceData);
destinationImage.UnlockBits(destinationData);
return destinationImage;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment