public static PinnedBitmap Convolute(Bitmap source, double[,] kernel) { var kernelHeight = kernel.GetUpperBound(0) + 1; var kernelWidth = kernel.GetUpperBound(1) + 1; if ((kernelWidth % 2) == 0 || (kernelHeight % 2) == 0) { throw new InvalidOperationException("Invalid kernel size"); } using (var pinnedSource = PinnedBitmap.FromBitmap(source)) { var width = source.Width; var height = source.Height; var result = new PinnedBitmap(width, height); var index = 0; var halfKernelWidth = kernelWidth >> 1; var halfKernelHeight = kernelHeight >> 1; for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { var a = 0.0; var r = 0.0; var g = 0.0; var b = 0.0; for (var kernelX = -halfKernelWidth; kernelX <= halfKernelWidth; kernelX++) { var pixelX = kernelX + x; if (pixelX < 0) { pixelX = 0; } else if (pixelX >= width) { pixelX = width - 1; } for (var kernelY = -halfKernelHeight; kernelY <= halfKernelHeight; kernelY++) { var pixelY = kernelY + y; if (pixelY < 0) { pixelY = 0; } else if (pixelY >= height) { pixelY = height - 1; } var col = pinnedSource.Data[pixelY * width + pixelX]; var k = kernel[kernelY + halfKernelWidth, kernelX + halfKernelHeight]; a += ((col >> 24) & 0x000000FF) * k; r += ((col >> 16) & 0x000000FF) * k; g += ((col >> 8) & 0x000000FF) * k; b += ((col) & 0x000000FF) * k; } } var alphaInt = (int)a; var alpha = (byte)((alphaInt > 255) ? 255 : ((alphaInt < 0) ? 0 : alphaInt)); if (alpha == 1) { alpha = 0; } var redInt = (int)r; var red = (byte)((redInt > 255) ? 255 : ((redInt < 0) ? 0 : redInt)); var greenInt = (int)g; var green = (byte)((greenInt > 255) ? 255 : ((greenInt < 0) ? 0 : greenInt)); var blueInt = (int)b; var blue = (byte)((blueInt > 255) ? 255 : ((blueInt < 0) ? 0 : blueInt)); result.Data[index++] = (alpha << 24) | (red << 16) | (green << 8) | blue; } } return(result); } }