/// <summary> /// Performs a vertical fast box blur on the collection of colors. /// <see href="http://blog.ivank.net/fastest-gaussian-blur.html"/> /// </summary> /// <param name="sourceColors">The source colors.</param> /// <param name="radius">The radius to which to blur.</param> /// <param name="fixGamma">Whether to blur the colors using the linear color space.</param> /// <returns> /// The <see cref="T:Color[,]"/>. /// </returns> private static Color[,] BoxBlurVertical(Color[,] sourceColors, int radius, bool fixGamma) { int width = sourceColors.GetLength(0); int height = sourceColors.GetLength(1); Color[,] destination = new Color[width, height]; // For each column for (int y = 0; y < height; y++) { // For each row for (int x = 0; x < width; x++) { int fy = Math.Max(0, y - radius); int ty = Math.Min(width, y + radius + 1); int red = 0; int green = 0; int blue = 0; int alpha = 0; for (int yy = fy; yy < ty; yy++) { Color sourceColor = sourceColors[x, yy]; if (fixGamma) { sourceColor = PixelOperations.ToLinear(sourceColor); } red += sourceColor.R; green += sourceColor.G; blue += sourceColor.B; alpha += sourceColor.A; } int divider = ty - fy; red /= divider; green /= divider; blue /= divider; alpha /= divider; Color destinationColor = Color.FromArgb(alpha, red, green, blue); if (fixGamma) { destinationColor = PixelOperations.ToSRGB(destinationColor); } destination[x, y] = destinationColor; } } return(destination); }
/// <summary> /// Processes the given kernel to produce an array of pixels representing a bitmap. /// </summary> /// <param name="source">The image to process.</param> /// <param name="kernel">The Gaussian kernel to use when performing the method</param> /// <param name="fixGamma">Whether to process the image using the linear color space.</param> /// <returns>A processed bitmap.</returns> public Color[,] ProcessKernel(Color[,] source, double[,] kernel, bool fixGamma) { var width = source.GetLength(0); var height = source.GetLength(1); var kernelLength = kernel.GetLength(0); var radius = kernelLength >> 1; var kernelSize = kernelLength * kernelLength; var threshold = Threshold; var destination = new Color[width, height]; // For each line Parallel.For( 0, height, y => { // For each pixel for (var x = 0; x < width; x++) { // The number of kernel elements taken into account int processedKernelSize; // Colour sums double blue; double alpha; double divider; double green; var red = green = blue = alpha = divider = processedKernelSize = 0; // For each kernel row for (var i = 0; i < kernelLength; i++) { var ir = i - radius; var offsetY = y + ir; // Skip the current row if (offsetY < 0) { continue; } // Outwith the current bounds so break. if (offsetY >= height) { break; } // For each kernel column for (var j = 0; j < kernelLength; j++) { var jr = j - radius; var offsetX = x + jr; // Skip the column if (offsetX < 0) { continue; } if (offsetX < width) { // ReSharper disable once AccessToDisposedClosure var sourceColor = source[offsetX, offsetY]; if (fixGamma) { sourceColor = PixelOperations.ToLinear(sourceColor); } var k = kernel[i, j]; divider += k; red += k * sourceColor.R; green += k * sourceColor.G; blue += k * sourceColor.B; alpha += k * sourceColor.A; processedKernelSize++; } } } // Check to see if all kernel elements were processed if (processedKernelSize == kernelSize) { // All kernel elements are processed; we are not on the edge. divider = Divider; } else { // We are on an edge; do we need to use dynamic divider or not? if (!UseDynamicDividerForEdges) { // Apply the set divider. divider = Divider; } } // Check and apply the divider if ((long)divider != 0) { red /= divider; green /= divider; blue /= divider; alpha /= divider; } // Add any applicable threshold. red += threshold; green += threshold; blue += threshold; alpha += threshold; var destinationColor = Color.FromArgb(alpha.ToByte(), red.ToByte(), green.ToByte(), blue.ToByte()); if (fixGamma) { destinationColor = PixelOperations.ToSRGB(destinationColor); } destination[x, y] = destinationColor; } }); return(destination); }
/// <summary> /// Processes the given kernel to produce an array of pixels representing a bitmap. /// </summary> /// <param name="source">The image to process.</param> /// <param name="kernel">The Gaussian kernel to use when performing the method</param> /// <param name="fixGamma">Whether to process the image using the linear color space.</param> /// <returns>A processed bitmap.</returns> public Bitmap ProcessKernel(Bitmap source, double[,] kernel, bool fixGamma) { int width = source.Width; int height = source.Height; Bitmap destination = new Bitmap(width, height, PixelFormat.Format32bppPArgb); destination.SetResolution(source.HorizontalResolution, source.VerticalResolution); using (FastBitmap sourceBitmap = new FastBitmap(source)) { using (FastBitmap destinationBitmap = new FastBitmap(destination)) { int kernelLength = kernel.GetLength(0); int radius = kernelLength >> 1; int kernelSize = kernelLength * kernelLength; int threshold = this.Threshold; // For each line Parallel.For( 0, height, y => { // For each pixel for (int x = 0; x < width; x++) { // The number of kernel elements taken into account int processedKernelSize; // Colour sums double blue; double alpha; double divider; double green; double red = green = blue = alpha = divider = processedKernelSize = 0; // For each kernel row for (int i = 0; i < kernelLength; i++) { int ir = i - radius; int offsetY = y + ir; // Skip the current row if (offsetY < 0) { continue; } // Outwith the current bounds so break. if (offsetY >= height) { break; } // For each kernel column for (int j = 0; j < kernelLength; j++) { int jr = j - radius; int offsetX = x + jr; // Skip the column if (offsetX < 0) { continue; } if (offsetX < width) { // ReSharper disable once AccessToDisposedClosure Color sourceColor = sourceBitmap.GetPixel(offsetX, offsetY); if (fixGamma) { sourceColor = PixelOperations.ToLinear(sourceColor); } double k = kernel[i, j]; divider += k; red += k * sourceColor.R; green += k * sourceColor.G; blue += k * sourceColor.B; alpha += k * sourceColor.A; processedKernelSize++; } } } // Check to see if all kernel elements were processed if (processedKernelSize == kernelSize) { // All kernel elements are processed; we are not on the edge. divider = this.Divider; } else { // We are on an edge; do we need to use dynamic divider or not? if (!this.UseDynamicDividerForEdges) { // Apply the set divider. divider = this.Divider; } } // Check and apply the divider if ((long)divider != 0) { red /= divider; green /= divider; blue /= divider; alpha /= divider; } // Add any applicable threshold. red += threshold; green += threshold; blue += threshold; alpha += threshold; Color destinationColor = Color.FromArgb(alpha.ToByte(), red.ToByte(), green.ToByte(), blue.ToByte()); if (fixGamma) { destinationColor = PixelOperations.ToSRGB(destinationColor); } // ReSharper disable once AccessToDisposedClosure destinationBitmap.SetPixel(x, y, destinationColor); } }); } } source.Dispose(); return(destination); }
internal static Bitmap ResizeLanczos(Bitmap source, int width, int height, Rectangle destinationRectangle, bool fixGamma = true) { int sourceWidth = source.Width; int sourceHeight = source.Height; int startX = destinationRectangle.X; int startY = destinationRectangle.Y; int endX = destinationRectangle.Width + startX; int endY = destinationRectangle.Height + startY; Bitmap destination = new Bitmap(width, height, PixelFormat.Format32bppPArgb); destination.SetResolution(source.HorizontalResolution, source.VerticalResolution); using (FastBitmap sourceBitmap = new FastBitmap(source)) { using (FastBitmap destinationBitmap = new FastBitmap(destination)) { // Scaling factors double widthFactor = sourceWidth / (double)destinationRectangle.Width; double heightFactor = sourceHeight / (double)destinationRectangle.Height; // Width and height decreased by 1 int maxHeight = sourceHeight - 1; int maxWidth = sourceWidth - 1; // For each column Parallel.For( startY, endY, y => { if (y >= 0 && y < height) { // Y coordinates of source points. double originY = ((y - startY) * heightFactor) - 0.5; int originY1 = (int)originY; double dy = originY - originY1; // For each row. for (int x = startX; x < endX; x++) { if (x >= 0 && x < width) { // X coordinates of source points. double originX = ((x - startX) * widthFactor) - 0.5f; int originX1 = (int)originX; double dx = originX - originX1; // Destination color components double r = 0; double g = 0; double b = 0; double a = 0; for (int n = -3; n < 6; n++) { // Get Y cooefficient double k1 = Interpolation.LanczosKernel3(dy - n); int originY2 = originY1 + n; if (originY2 < 0) { originY2 = 0; } if (originY2 > maxHeight) { originY2 = maxHeight; } for (int m = -3; m < 6; m++) { // Get X cooefficient double k2 = k1 * Interpolation.LanczosKernel3(m - dx); int originX2 = originX1 + m; if (originX2 < 0) { originX2 = 0; } if (originX2 > maxWidth) { originX2 = maxWidth; } // ReSharper disable once AccessToDisposedClosure Color sourceColor = sourceBitmap.GetPixel(originX2, originY2); if (fixGamma) { sourceColor = PixelOperations.ToLinear(sourceColor); } r += k2 * sourceColor.R; g += k2 * sourceColor.G; b += k2 * sourceColor.B; a += k2 * sourceColor.A; } } Color destinationColor = Color.FromArgb( a.ToByte(), r.ToByte(), g.ToByte(), b.ToByte()); if (fixGamma) { destinationColor = PixelOperations.ToSRGB(destinationColor); } // ReSharper disable once AccessToDisposedClosure destinationBitmap.SetPixel(x, y, destinationColor); } } } }); } } source.Dispose(); return(destination); }
public static Bitmap ResizeBilinear(Bitmap source, int width, int height, Rectangle destinationRectangle, bool fixGamma) { int sourceWidth = source.Width; int sourceHeight = source.Height; int startX = destinationRectangle.X; int startY = destinationRectangle.Y; int endX = destinationRectangle.Width + startX; int endY = destinationRectangle.Height + startY; // Scaling factors double widthFactor = sourceWidth / (double)destinationRectangle.Width; double heightFactor = sourceHeight / (double)destinationRectangle.Height; // Width and height decreased by 1 int maxHeight = sourceHeight - 1; int maxWidth = sourceWidth - 1; Bitmap destination = new Bitmap(width, height, PixelFormat.Format32bppPArgb); destination.SetResolution(source.HorizontalResolution, source.VerticalResolution); using (FastBitmap sourceBitmap = new FastBitmap(source)) { using (FastBitmap destinationBitmap = new FastBitmap(destination)) { // For each column Parallel.For( startY, endY, y => { if (y >= 0 && y < height) { // Y coordinates of source points double originY = (y - startY) * heightFactor; int originY1 = (int)originY; int originY2 = (originY1 == maxHeight) ? originY1 : originY1 + 1; double dy1 = originY - originY1; double dy2 = 1.0 - dy1; // Get temp pointers int temp1 = originY1; int temp2 = originY2; // For every column. for (int x = startX; x < endX; x++) { if (x >= 0 && x < width) { // X coordinates of source points double originX = (x - startX) * widthFactor; int originX1 = (int)originX; int originX2 = (originX1 == maxWidth) ? originX1 : originX1 + 1; double dx1 = originX - originX1; double dx2 = 1.0 - dx1; // Get four pixels to sample from. Color sourceColor1 = sourceBitmap.GetPixel(originX1, temp1); Color sourceColor2 = sourceBitmap.GetPixel(originX2, temp1); Color sourceColor3 = sourceBitmap.GetPixel(originX1, temp2); Color sourceColor4 = sourceBitmap.GetPixel(originX2, temp2); if (fixGamma) { sourceColor1 = PixelOperations.ToLinear(sourceColor1); sourceColor2 = PixelOperations.ToLinear(sourceColor2); sourceColor3 = PixelOperations.ToLinear(sourceColor3); sourceColor4 = PixelOperations.ToLinear(sourceColor4); } // Get four points in red channel. int p1 = sourceColor1.R; int p2 = sourceColor2.R; int p3 = sourceColor3.R; int p4 = sourceColor4.R; int r = (int)((dy2 * ((dx2 * p1) + (dx1 * p2))) + (dy1 * ((dx2 * p3) + (dx1 * p4)))); // Get four points in green channel. p1 = sourceColor1.G; p2 = sourceColor2.G; p3 = sourceColor3.G; p4 = sourceColor4.G; int g = (int)((dy2 * ((dx2 * p1) + (dx1 * p2))) + (dy1 * ((dx2 * p3) + (dx1 * p4)))); // Get four points in blue channel p1 = sourceColor1.B; p2 = sourceColor2.B; p3 = sourceColor3.B; p4 = sourceColor4.B; int b = (int)((dy2 * ((dx2 * p1) + (dx1 * p2))) + (dy1 * ((dx2 * p3) + (dx1 * p4)))); // Get four points in alpha channel p1 = sourceColor1.A; p2 = sourceColor2.A; p3 = sourceColor3.A; p4 = sourceColor4.A; int a = (int)((dy2 * ((dx2 * p1) + (dx1 * p2))) + (dy1 * ((dx2 * p3) + (dx1 * p4)))); Color destinationColor = Color.FromArgb( a.ToByte(), r.ToByte(), g.ToByte(), b.ToByte()); if (fixGamma) { destinationColor = PixelOperations.ToSRGB(destinationColor); } destinationBitmap.SetPixel(x, y, destinationColor); } } } }); } } source.Dispose(); return(destination); }
public static Bitmap ResizeBicubicHighQuality(Bitmap source, int width, int height, Rectangle destinationRectangle, bool fixGamma) { int sourceWidth = source.Width; int sourceHeight = source.Height; int startX = destinationRectangle.X; int startY = destinationRectangle.Y; int endX = destinationRectangle.Width + startX; int endY = destinationRectangle.Height + startY; // Scaling factors double widthFactor = sourceWidth / (double)destinationRectangle.Width; double heightFactor = sourceHeight / (double)destinationRectangle.Height; // Width and height decreased by 1 int maxHeight = sourceHeight - 1; int maxWidth = sourceWidth - 1; Bitmap destination = new Bitmap(width, height, PixelFormat.Format32bppPArgb); destination.SetResolution(source.HorizontalResolution, source.VerticalResolution); // The radius for pre blurring the images. // We only apply this for very small images. int radius = 0; if (width <= 150 && height <= 150) { radius = 4; } using (FastBitmap sourceBitmap = new FastBitmap(source)) { using (FastBitmap destinationBitmap = new FastBitmap(destination)) { // For each column Parallel.For( startY, endY, y => { if (y >= 0 && y < height) { // Y coordinates of source points. double originY = ((y - startY) * heightFactor) - 0.5; int originY1 = (int)originY; double dy = originY - originY1; // Houses colors for blurring. Color[,] sourceColors = new Color[4, 4]; // For each row. for (int x = startX; x < endX; x++) { if (x >= 0 && x < width) { // X coordinates of source points. double originX = ((x - startX) * widthFactor) - 0.5f; int originX1 = (int)originX; double dx = originX - originX1; // Destination color components double r = 0; double g = 0; double b = 0; double a = 0; for (int yy = -1; yy < 3; yy++) { int originY2 = originY1 + yy; if (originY2 < 0) { originY2 = 0; } if (originY2 > maxHeight) { originY2 = maxHeight; } for (int xx = -1; xx < 3; xx++) { int originX2 = originX1 + xx; if (originX2 < 0) { originX2 = 0; } if (originX2 > maxWidth) { originX2 = maxWidth; } Color sourceColor = sourceBitmap.GetPixel(originX2, originY2); sourceColors[xx + 1, yy + 1] = sourceColor; } } // Blur the colors. if (radius > 0) { sourceColors = BoxBlur(sourceColors, radius, fixGamma); } // Do the resize. for (int yy = -1; yy < 3; yy++) { // Get Y cooefficient double kernel1 = Interpolation.BiCubicBSplineKernel(dy - yy); for (int xx = -1; xx < 3; xx++) { // Get X cooefficient double kernel2 = kernel1 * Interpolation.BiCubicBSplineKernel(xx - dx); Color sourceColor = sourceColors[xx + 1, yy + 1]; if (fixGamma) { sourceColor = PixelOperations.ToLinear(sourceColor); } r += kernel2 * sourceColor.R; g += kernel2 * sourceColor.G; b += kernel2 * sourceColor.B; a += kernel2 * sourceColor.A; } } Color destinationColor = Color.FromArgb( a.ToByte(), r.ToByte(), g.ToByte(), b.ToByte()); if (fixGamma) { destinationColor = PixelOperations.ToSRGB(destinationColor); } destinationBitmap.SetPixel(x, y, destinationColor); } } } }); } } source.Dispose(); return(destination); }