public Image Pixelate(Image sourceImage, int pixelSize) { if (sourceImage == null || !(sourceImage is Bitmap)) { return(sourceImage); } if (sourceImage.Width < pixelSize) { pixelSize = sourceImage.Width; } if (sourceImage.Height < pixelSize) { pixelSize = sourceImage.Height; } using (IFastBitmap dest = FastBitmap.CreateCloneOf(sourceImage)) { using (IFastBitmap src = FastBitmap.Create(sourceImage as Bitmap)) { FastBitmap.MixColors(dest, src, pixelSize); } Bitmap bmp = new Bitmap(dest.Width, dest.Height, sourceImage.PixelFormat); using (Graphics graphics = Graphics.FromImage(bmp)) { dest.DrawTo(graphics, Point.Empty); } return(bmp); } }
private void ApplyBoxBlur(Bitmap destinationBitmap, int range) { // We only need one fastbitmap as we use it as source and target (the reading is done for one line H/V, writing after "parsing" one line H/V) using (IFastBitmap fastBitmap = FastBitmap.Create(destinationBitmap)) { FastBitmapOperator.ApplyBoxBlur(fastBitmap, range); } }
/// <summary> /// Implements the Apply code for the Brightness Filet /// </summary> /// <param name="graphics"></param> /// <param name="applyBitmap"></param> /// <param name="rect"></param> /// <param name="renderMode"></param> public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) { Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); if (applyRect.Width == 0 || applyRect.Height == 0) { // nothing to do return; } GraphicsState state = graphics.Save(); if (Invert) { graphics.SetClip(applyRect); graphics.ExcludeClip(rect); } using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(applyBitmap, applyRect)) { Color highlightColor = GetFieldValueAsColor(FieldType.FILL_COLOR); for (int y = fastBitmap.Top; y < fastBitmap.Bottom; y++) { for (int x = fastBitmap.Left; x < fastBitmap.Right; x++) { Color color = fastBitmap.GetColorAt(x, y); color = Color.FromArgb(color.A, Math.Min(highlightColor.R, color.R), Math.Min(highlightColor.G, color.G), Math.Min(highlightColor.B, color.B)); fastBitmap.SetColorAt(x, y, color); } } fastBitmap.DrawTo(graphics, applyRect.Location); } graphics.Restore(state); }
public static void ApplyBoxBlur(IFastBitmap fastBitmap, int range) { // Range must be odd! if ((range & 1) == 0) { range++; } if (range <= 1) { return; } // Box blurs are frequently used to approximate a Gaussian blur. // By the central limit theorem, if applied 3 times on the same image, a box blur approximates the Gaussian kernel to within about 3%, yielding the same result as a quadratic convolution kernel. // This might be true, but the GDI+ BlurEffect doesn't look the same, a 2x blur is more simular and we only make 2x Box-Blur. // (Might also be a mistake in our blur, but for now it looks great) if (fastBitmap.hasAlphaChannel) { BoxBlurHorizontalAlpha(fastBitmap, range); BoxBlurVerticalAlpha(fastBitmap, range); BoxBlurHorizontalAlpha(fastBitmap, range); BoxBlurVerticalAlpha(fastBitmap, range); } else { BoxBlurHorizontal(fastBitmap, range); BoxBlurVertical(fastBitmap, range); BoxBlurHorizontal(fastBitmap, range); BoxBlurVertical(fastBitmap, range); } }
public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) { int pixelSize = GetFieldValueAsInt(FieldType.PIXEL_SIZE); Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); if (pixelSize <= 1 || rect.Width == 0 || rect.Height == 0) { // Nothing to do return; } if (rect.Width < pixelSize) { pixelSize = rect.Width; } if (rect.Height < pixelSize) { pixelSize = rect.Height; } using (IFastBitmap dest = FastBitmap.CreateCloneOf(applyBitmap, rect)) { using (IFastBitmap src = FastBitmap.Create(applyBitmap, rect)) { List <Color> colors = new List <Color>(); int halbPixelSize = pixelSize / 2; for (int y = src.Top - halbPixelSize; y < src.Bottom + halbPixelSize; y = y + pixelSize) { for (int x = src.Left - halbPixelSize; x <= src.Right + halbPixelSize; x = x + pixelSize) { colors.Clear(); for (int yy = y; yy < y + pixelSize; yy++) { if (yy >= src.Top && yy < src.Bottom) { for (int xx = x; xx < x + pixelSize; xx++) { if (xx >= src.Left && xx < src.Right) { colors.Add(src.GetColorAt(xx, yy)); } } } } Color currentAvgColor = Colors.Mix(colors); for (int yy = y; yy <= y + pixelSize; yy++) { if (yy >= src.Top && yy < src.Bottom) { for (int xx = x; xx <= x + pixelSize; xx++) { if (xx >= src.Left && xx < src.Right) { dest.SetColorAt(xx, yy, currentAvgColor); } } } } } } } dest.DrawTo(graphics, rect.Location); } }
/// <summary> /// Apply BoxBlur to the fastBitmap /// </summary> /// <param name="fastBitmap">IFastBitmap to blur</param> /// <param name="range">Must be ODD!</param> public static void ApplyBoxBlur(IFastBitmap fastBitmap, int range) { // Range must be odd! if ((range & 1) == 0) { range++; } if (range <= 1) { return; } // Box blurs are frequently used to approximate a Gaussian blur. // By the central limit theorem, if applied 3 times on the same image, a box blur approximates the Gaussian kernel to within about 3%, yielding the same result as a quadratic convolution kernel. // This might be true, but the GDI+ BlurEffect doesn't look the same, a 2x blur is more simular and we only make 2x Box-Blur. // (Might also be a mistake in our blur, but for now it looks great) if (fastBitmap.hasAlphaChannel) { BoxBlurHorizontalAlpha(fastBitmap, range); BoxBlurVerticalAlpha(fastBitmap, range); BoxBlurHorizontalAlpha(fastBitmap, range); BoxBlurVerticalAlpha(fastBitmap, range); } else { BoxBlurHorizontal(fastBitmap, range); BoxBlurVertical(fastBitmap, range); BoxBlurHorizontal(fastBitmap, range); BoxBlurVertical(fastBitmap, range); } }
public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) { int blurRadius = GetFieldValueAsInt(FieldType.BLUR_RADIUS); Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); if (applyRect.Width == 0 || applyRect.Height == 0) { return; } GraphicsState state = graphics.Save(); if (Invert) { graphics.SetClip(applyRect); graphics.ExcludeClip(rect); } if (GDIplus.IsBlurPossible(blurRadius)) { GDIplus.DrawWithBlur(graphics, applyBitmap, applyRect, null, null, blurRadius, false); } else { using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(applyBitmap, applyRect)) { ImageHelper.ApplyBoxBlur(fastBitmap, blurRadius); fastBitmap.DrawTo(graphics, applyRect); } } graphics.Restore(state); }
public static Rectangle FindAutoCropRectangle(Image image, int cropDifference) { Rectangle cropRectangle = Rectangle.Empty; Rectangle currentRectangle = Rectangle.Empty; List <Point> checkPoints = new List <Point>(); // Top Left checkPoints.Add(new Point(0, 0)); // Bottom Left checkPoints.Add(new Point(0, image.Height - 1)); // Top Right checkPoints.Add(new Point(image.Width - 1, 0)); // Bottom Right checkPoints.Add(new Point(image.Width - 1, image.Height - 1)); using (IFastBitmap fastBitmap = FastBitmap.Create((Bitmap)image)) { // find biggest area foreach (Point checkPoint in checkPoints) { currentRectangle = FastBitmapOperator.FindAutoCropRectangle(fastBitmap, checkPoint, cropDifference); if (currentRectangle.Width * currentRectangle.Height > cropRectangle.Width * cropRectangle.Height) { cropRectangle = currentRectangle; } } } return(cropRectangle); }
private static void BoxBlurVerticalAlpha(IFastBitmap targetFastBitmap, int range) { if (!targetFastBitmap.hasAlphaChannel) { throw new NotSupportedException("BoxBlurVerticalAlpha should be called for bitmaps with alpha channel"); } int w = targetFastBitmap.Width; int halfRange = range / 2; Color[] newColors = new Color[targetFastBitmap.Height]; int oldPixelOffset = -(halfRange + 1) * w; int newPixelOffset = (halfRange) * w; byte[] tmpColor = new byte[4]; for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) { int hits = 0; int a = 0; int r = 0; int g = 0; int b = 0; for (int y = targetFastBitmap.Top - halfRange; y < targetFastBitmap.Bottom; y++) { int oldPixel = y - halfRange - 1; if (oldPixel >= targetFastBitmap.Top) { targetFastBitmap.GetColorAt(x, oldPixel, tmpColor); a -= tmpColor[FastBitmap.COLOR_INDEX_A]; r -= tmpColor[FastBitmap.COLOR_INDEX_R]; g -= tmpColor[FastBitmap.COLOR_INDEX_G]; b -= tmpColor[FastBitmap.COLOR_INDEX_B]; hits--; } int newPixel = y + halfRange; if (newPixel < targetFastBitmap.Bottom) { //int colorg = pixels[index + newPixelOffset]; targetFastBitmap.GetColorAt(x, newPixel, tmpColor); a += tmpColor[FastBitmap.COLOR_INDEX_A]; r += tmpColor[FastBitmap.COLOR_INDEX_R]; g += tmpColor[FastBitmap.COLOR_INDEX_G]; b += tmpColor[FastBitmap.COLOR_INDEX_B]; hits++; } if (y >= targetFastBitmap.Top) { newColors[y - targetFastBitmap.Top] = Color.FromArgb((byte)(a / hits), (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); } } for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) { targetFastBitmap.SetColorAt(x, y, newColors[y - targetFastBitmap.Top]); } } }
/// <summary> /// Factory for creating a FastBitmap as a destination /// </summary> /// <param name="newSize"></param> /// <param name="pixelFormat"></param> /// <param name="backgroundColor"></param> /// <returns>IFastBitmap</returns> public static IFastBitmap CreateEmpty(Size newSize, PixelFormat pixelFormat, Color backgroundColor) { Bitmap destination = ImageHelper.CreateEmpty(newSize.Width, newSize.Height, pixelFormat, backgroundColor, 96f, 96f); IFastBitmap fastBitmap = Create(destination); fastBitmap.NeedsDispose = true; return(fastBitmap); }
/// <summary> /// BoxBlurVertical is a private helper method for the BoxBlur /// </summary> /// <param name="targetFastBitmap">BitmapBuffer which previously was created with BoxBlurHorizontal</param> /// <param name="range">Range must be odd!</param> private static void BoxBlurVerticalAlpha(IFastBitmap targetFastBitmap, int range) { if (!targetFastBitmap.HasAlphaChannel) { throw new NotSupportedException("BoxBlurVerticalAlpha should be called for bitmaps with alpha channel"); } var halfRange = range / 2; var newColors = new Color[targetFastBitmap.Height]; var tmpColor = new byte[4]; for (var x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) { var hits = 0; var a = 0; var r = 0; var g = 0; var b = 0; for (var y = targetFastBitmap.Top - halfRange; y < targetFastBitmap.Bottom; y++) { var oldPixel = y - halfRange - 1; if (oldPixel >= targetFastBitmap.Top) { targetFastBitmap.GetColorAt(x, oldPixel, tmpColor); a -= tmpColor[FastBitmapBase.ColorIndexA]; r -= tmpColor[FastBitmapBase.ColorIndexR]; g -= tmpColor[FastBitmapBase.ColorIndexG]; b -= tmpColor[FastBitmapBase.ColorIndexB]; hits--; } var newPixel = y + halfRange; if (newPixel < targetFastBitmap.Bottom) { //int colorg = pixels[index + newPixelOffset]; targetFastBitmap.GetColorAt(x, newPixel, tmpColor); a += tmpColor[FastBitmapBase.ColorIndexA]; r += tmpColor[FastBitmapBase.ColorIndexR]; g += tmpColor[FastBitmapBase.ColorIndexG]; b += tmpColor[FastBitmapBase.ColorIndexB]; hits++; } if (y >= targetFastBitmap.Top) { newColors[y - targetFastBitmap.Top] = Color.FromArgb((byte)(a / hits), (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); } } for (var y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) { targetFastBitmap.SetColorAt(x, y, ref newColors[y - targetFastBitmap.Top]); } } }
/// <summary> /// BoxBlurHorizontal is a private helper method for the BoxBlur, only for IFastBitmaps with alpha channel /// </summary> /// <param name="targetFastBitmap">Target BitmapBuffer</param> /// <param name="range">Range must be odd!</param> private static void BoxBlurHorizontalAlpha(IFastBitmap targetFastBitmap, int range) { if (!targetFastBitmap.HasAlphaChannel) { throw new NotSupportedException("BoxBlurHorizontalAlpha should be called for bitmaps with alpha channel"); } var halfRange = range / 2; var newColors = new Color[targetFastBitmap.Width]; var tmpColor = new byte[4]; for (var y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) { var hits = 0; var a = 0; var r = 0; var g = 0; var b = 0; for (var x = targetFastBitmap.Left - halfRange; x < targetFastBitmap.Right; x++) { var oldPixel = x - halfRange - 1; if (oldPixel >= targetFastBitmap.Left) { targetFastBitmap.GetColorAt(oldPixel, y, tmpColor); a -= tmpColor[FastBitmapBase.ColorIndexA]; r -= tmpColor[FastBitmapBase.ColorIndexR]; g -= tmpColor[FastBitmapBase.ColorIndexG]; b -= tmpColor[FastBitmapBase.ColorIndexB]; hits--; } var newPixel = x + halfRange; if (newPixel < targetFastBitmap.Right) { targetFastBitmap.GetColorAt(newPixel, y, tmpColor); a += tmpColor[FastBitmapBase.ColorIndexA]; r += tmpColor[FastBitmapBase.ColorIndexR]; g += tmpColor[FastBitmapBase.ColorIndexG]; b += tmpColor[FastBitmapBase.ColorIndexB]; hits++; } if (x >= targetFastBitmap.Left) { newColors[x - targetFastBitmap.Left] = Color.FromArgb((byte)(a / hits), (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); } } for (var x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) { targetFastBitmap.SetColorAt(x, y, ref newColors[x - targetFastBitmap.Left]); } } }
private static void BoxBlurHorizontalAlpha(IFastBitmap targetFastBitmap, int range) { if (!targetFastBitmap.hasAlphaChannel) { throw new NotSupportedException("BoxBlurHorizontalAlpha should be called for bitmaps with alpha channel"); } int halfRange = range / 2; Color[] newColors = new Color[targetFastBitmap.Width]; byte[] tmpColor = new byte[4]; for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) { int hits = 0; int a = 0; int r = 0; int g = 0; int b = 0; for (int x = targetFastBitmap.Left - halfRange; x < targetFastBitmap.Right; x++) { int oldPixel = x - halfRange - 1; if (oldPixel >= targetFastBitmap.Left) { targetFastBitmap.GetColorAt(oldPixel, y, tmpColor); a -= tmpColor[FastBitmap.COLOR_INDEX_A]; r -= tmpColor[FastBitmap.COLOR_INDEX_R]; g -= tmpColor[FastBitmap.COLOR_INDEX_G]; b -= tmpColor[FastBitmap.COLOR_INDEX_B]; hits--; } int newPixel = x + halfRange; if (newPixel < targetFastBitmap.Right) { targetFastBitmap.GetColorAt(newPixel, y, tmpColor); a += tmpColor[FastBitmap.COLOR_INDEX_A]; r += tmpColor[FastBitmap.COLOR_INDEX_R]; g += tmpColor[FastBitmap.COLOR_INDEX_G]; b += tmpColor[FastBitmap.COLOR_INDEX_B]; hits++; } if (x >= targetFastBitmap.Left) { newColors[x - targetFastBitmap.Left] = Color.FromArgb((byte)(a / hits), (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); } } for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) { targetFastBitmap.SetColorAt(x, y, newColors[x - targetFastBitmap.Left]); } } }
public static Bitmap Pixelate(Bitmap sourceImage, int pixelSize) { pixelSize = Math.Min(pixelSize, sourceImage.Width); pixelSize = Math.Min(pixelSize, sourceImage.Height); Bitmap result = sourceImage.CreateEmptyBitmap(); using (IFastBitmap src = FastBitmap.Create(sourceImage, new Rectangle(0, 0, sourceImage.Width, sourceImage.Height))) using (IFastBitmap dest = FastBitmap.Create(result)) { List <Color> colors = new List <Color>(); int halbPixelSize = pixelSize / 2; for (int y = src.Top - halbPixelSize; y < src.Bottom + halbPixelSize; y = y + pixelSize) { for (int x = src.Left - halbPixelSize; x <= src.Right + halbPixelSize; x = x + pixelSize) { colors.Clear(); for (int yy = y; yy < y + pixelSize; yy++) { if (yy >= src.Top && yy < src.Bottom) { for (int xx = x; xx < x + pixelSize; xx++) { if (xx >= src.Left && xx < src.Right) { colors.Add(src.GetColorAt(xx, yy)); } } } } Color currentAvgColor = ColorHelpers.Mix(colors); for (int yy = y; yy <= y + pixelSize; yy++) { if (yy >= src.Top && yy < src.Bottom) { for (int xx = x; xx <= x + pixelSize; xx++) { if (xx >= src.Left && xx < src.Right) { dest.SetColorAt(xx, yy, currentAvgColor); } } } } } } } return(result); }
public Image Blur(Image sourceImage, int radius) { if (sourceImage == null) { return(sourceImage); } using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(sourceImage)) { FastBitmapOperator.ApplyBoxBlur(fastBitmap, radius); Bitmap bmp = new Bitmap(sourceImage.Width, sourceImage.Height, sourceImage.PixelFormat); using (Graphics graphics = Graphics.FromImage(bmp)) { fastBitmap.DrawTo(graphics, Point.Empty); } return(bmp); } }
//tiles = 16 means 16 x 16 atlas public List <Bitmap> Atlas2dInto1d(Bitmap atlas2d, int tiles, int atlassizezlimit) { IFastBitmap orig = d_FastBitmapFactory(); orig.bmp = atlas2d; int tilesize = atlas2d.Width / tiles; int atlasescount = Math.Max(1, (tiles * tiles * tilesize) / atlassizezlimit); List <Bitmap> atlases = new List <Bitmap>(); orig.Lock(); //256 x 1 IFastBitmap atlas1d = null; for (int i = 0; i < tiles * tiles; i++) { int x = i % tiles; int y = i / tiles; int tilesinatlas = (tiles * tiles / atlasescount); if (i % tilesinatlas == 0) { if (atlas1d != null) { atlas1d.Unlock(); atlases.Add(atlas1d.bmp); } atlas1d = d_FastBitmapFactory(); atlas1d.bmp = new Bitmap(tilesize, atlassizezlimit); atlas1d.Lock(); } for (int xx = 0; xx < tilesize; xx++) { for (int yy = 0; yy < tilesize; yy++) { int c = orig.GetPixel(x * tilesize + xx, y * tilesize + yy); atlas1d.SetPixel(xx, (i % tilesinatlas) * tilesize + yy, c); } } } atlas1d.Unlock(); atlases.Add(atlas1d.bmp); orig.Unlock(); return(atlases); }
public override bool ProcessCapture(ISurface surface, ICaptureDetails captureDetails) { LOG.DebugFormat("Changing surface to grayscale!"); using (IFastBitmap bbb = FastBitmap.Create(surface.Image as Bitmap)) { bbb.Lock(); for (int y = 0; y < bbb.Height; y++) { for (int x = 0; x < bbb.Width; x++) { Color color = bbb.GetColorAt(x, y); int luma = (int)((0.3 * color.R) + (0.59 * color.G) + (0.11 * color.B)); color = Color.FromArgb(luma, luma, luma); bbb.SetColorAt(x, y, color); } } } return(true); }
public static void MixColors(IFastBitmap dest, IFastBitmap src, int pixelSize) { List <Color> colors = new List <Color>(); int halbPixelSize = pixelSize / 2; for (int y = src.Top - halbPixelSize; y < src.Bottom + halbPixelSize; y = y + pixelSize) { for (int x = src.Left - halbPixelSize; x <= src.Right + halbPixelSize; x = x + pixelSize) { colors.Clear(); for (int yy = y; yy < y + pixelSize; yy++) { if (yy >= src.Top && yy < src.Bottom) { for (int xx = x; xx < x + pixelSize; xx++) { if (xx >= src.Left && xx < src.Right) { colors.Add(src.GetColorAt(xx, yy)); } } } } Color currentAvgColor = Colors.Mix(colors); for (int yy = y; yy <= y + pixelSize; yy++) { if (yy >= src.Top && yy < src.Bottom) { for (int xx = x; xx <= x + pixelSize; xx++) { if (xx >= src.Left && xx < src.Right) { dest.SetColorAt(xx, yy, currentAvgColor); } } } } } } }
/// <summary> /// Private helper method for the FindAutoCropRectangle /// </summary> /// <param name="fastBitmap">IFastBitmap</param> /// <param name="referenceColor">color for reference</param> /// <param name="cropDifference">int</param> /// <returns>NativeRect</returns> private static NativeRect FindAutoCropRectangle(this IFastBitmap fastBitmap, Color referenceColor, int cropDifference) { var cropRectangle = NativeRect.Empty; var min = new NativePoint(int.MaxValue, int.MaxValue); var max = new NativePoint(int.MinValue, int.MinValue); if (cropDifference > 0) { for (var y = 0; y < fastBitmap.Height; y++) { for (var x = 0; x < fastBitmap.Width; x++) { var currentColor = fastBitmap.GetColorAt(x, y); var diffR = Math.Abs(currentColor.R - referenceColor.R); var diffG = Math.Abs(currentColor.G - referenceColor.G); var diffB = Math.Abs(currentColor.B - referenceColor.B); if ((diffR + diffG + diffB) / 3 <= cropDifference) { continue; } if (x < min.X) { min = min.ChangeX(x); } if (y < min.Y) { min = min.ChangeY(y); } if (x > max.X) { max = max.ChangeX(x); } if (y > max.Y) { max = max.ChangeY(y); } } } } else { for (var y = 0; y < fastBitmap.Height; y++) { for (var x = 0; x < fastBitmap.Width; x++) { var currentColor = fastBitmap.GetColorAt(x, y); if (!referenceColor.Equals(currentColor)) { continue; } if (x < min.X) { min = min.ChangeX(x); } if (y < min.Y) { min = min.ChangeY(y); } if (x > max.X) { max = max.ChangeX(x); } if (y > max.Y) { max = max.ChangeY(y); } } } } if (!(NativePoint.Empty.Equals(min) && max.Equals(new NativePoint(fastBitmap.Width - 1, fastBitmap.Height - 1))) && !(min.X == int.MaxValue || min.Y == int.MaxValue || max.X == int.MinValue || min.X == int.MinValue)) { cropRectangle = new NativeRect(min.X, min.Y, max.X - min.X + 1, max.Y - min.Y + 1); } return(cropRectangle); }
public static Rectangle FindAutoCropRectangle(IFastBitmap fastBitmap, Point colorPoint, int cropDifference) { Rectangle cropRectangle = Rectangle.Empty; Color referenceColor = fastBitmap.GetColorAt(colorPoint.X, colorPoint.Y); Point min = new Point(int.MaxValue, int.MaxValue); Point max = new Point(int.MinValue, int.MinValue); if (cropDifference > 0) { for (int y = 0; y < fastBitmap.Height; y++) { for (int x = 0; x < fastBitmap.Width; x++) { Color currentColor = fastBitmap.GetColorAt(x, y); int diffR = Math.Abs(currentColor.R - referenceColor.R); int diffG = Math.Abs(currentColor.G - referenceColor.G); int diffB = Math.Abs(currentColor.B - referenceColor.B); if (((diffR + diffG + diffB) / 3) > cropDifference) { if (x < min.X) { min.X = x; } if (y < min.Y) { min.Y = y; } if (x > max.X) { max.X = x; } if (y > max.Y) { max.Y = y; } } } } } else { for (int y = 0; y < fastBitmap.Height; y++) { for (int x = 0; x < fastBitmap.Width; x++) { Color currentColor = fastBitmap.GetColorAt(x, y); if (referenceColor.Equals(currentColor)) { if (x < min.X) { min.X = x; } if (y < min.Y) { min.Y = y; } if (x > max.X) { max.X = x; } if (y > max.Y) { max.Y = y; } } } } } if (!(Point.Empty.Equals(min) && max.Equals(new Point(fastBitmap.Width - 1, fastBitmap.Height - 1)))) { if (!(min.X == int.MaxValue || min.Y == int.MaxValue || max.X == int.MinValue || min.X == int.MinValue)) { cropRectangle = new Rectangle(min.X, min.Y, max.X - min.X + 1, max.Y - min.Y + 1); } } return(cropRectangle); }
private static void BoxBlurVertical(IFastBitmap targetFastBitmap, int range) { var halfRange = range / 2; Parallel.For(targetFastBitmap.Left, targetFastBitmap.Right, x => { unsafe { var readColor = stackalloc byte[4]; var averages = stackalloc byte[range << 2]; var hits = 0; var a = 0; var r = 0; var g = 0; var b = 0; for (var y = targetFastBitmap.Top; y < targetFastBitmap.Top + halfRange; y++) { targetFastBitmap.GetColorAt(x, y, readColor); a += readColor[FastBitmapBase.ColorIndexA]; r += readColor[FastBitmapBase.ColorIndexR]; g += readColor[FastBitmapBase.ColorIndexG]; b += readColor[FastBitmapBase.ColorIndexB]; hits++; } for (var y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) { var topSide = y - halfRange - 1; if (topSide >= targetFastBitmap.Top) { // Get value at the top side of range targetFastBitmap.GetColorAt(x, topSide, readColor); a -= readColor[FastBitmapBase.ColorIndexA]; r -= readColor[FastBitmapBase.ColorIndexR]; g -= readColor[FastBitmapBase.ColorIndexG]; b -= readColor[FastBitmapBase.ColorIndexB]; hits--; } var bottomSide = y + halfRange; if (bottomSide < targetFastBitmap.Bottom) { targetFastBitmap.GetColorAt(x, bottomSide, readColor); a += readColor[FastBitmapBase.ColorIndexA]; r += readColor[FastBitmapBase.ColorIndexR]; g += readColor[FastBitmapBase.ColorIndexG]; b += readColor[FastBitmapBase.ColorIndexB]; hits++; } var writeLocation = (y % range) << 2; averages[writeLocation + FastBitmapBase.ColorIndexA] = (byte)(a / hits); averages[writeLocation + FastBitmapBase.ColorIndexR] = (byte)(r / hits); averages[writeLocation + FastBitmapBase.ColorIndexG] = (byte)(g / hits); averages[writeLocation + FastBitmapBase.ColorIndexB] = (byte)(b / hits); if (topSide >= targetFastBitmap.Top) { // Write the value from the calculated avarages var readLocation = (topSide % range) << 2; targetFastBitmap.SetColorAt(x, topSide, averages, readLocation); } } } }); }
private static void BoxBlurHorizontal(IFastBitmap targetFastBitmap, int range) { var halfRange = range / 2; Parallel.For(targetFastBitmap.Top, targetFastBitmap.Bottom, y => { unsafe { var averages = stackalloc byte[range << 2]; var readColor = stackalloc byte[4]; var a = 0; var r = 0; var g = 0; var b = 0; var hits = halfRange; for (var x = targetFastBitmap.Left; x < targetFastBitmap.Left + halfRange; x++) { targetFastBitmap.GetColorAt(x, y, readColor); a += readColor[FastBitmapBase.ColorIndexA]; r += readColor[FastBitmapBase.ColorIndexR]; g += readColor[FastBitmapBase.ColorIndexG]; b += readColor[FastBitmapBase.ColorIndexB]; } for (var x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) { var leftSide = x - halfRange - 1; if (leftSide >= targetFastBitmap.Left) { // Get value at the left side of range targetFastBitmap.GetColorAt(leftSide, y, readColor); a -= readColor[FastBitmapBase.ColorIndexA]; r -= readColor[FastBitmapBase.ColorIndexR]; g -= readColor[FastBitmapBase.ColorIndexG]; b -= readColor[FastBitmapBase.ColorIndexB]; hits--; } var rightSide = x + halfRange; if (rightSide < targetFastBitmap.Right) { targetFastBitmap.GetColorAt(rightSide, y, readColor); a += readColor[FastBitmapBase.ColorIndexA]; r += readColor[FastBitmapBase.ColorIndexR]; g += readColor[FastBitmapBase.ColorIndexG]; b += readColor[FastBitmapBase.ColorIndexB]; hits++; } var writeLocation = (x % range) << 2; averages[writeLocation + FastBitmapBase.ColorIndexA] = (byte)(a / hits); averages[writeLocation + FastBitmapBase.ColorIndexR] = (byte)(r / hits); averages[writeLocation + FastBitmapBase.ColorIndexG] = (byte)(g / hits); averages[writeLocation + FastBitmapBase.ColorIndexB] = (byte)(b / hits); if (leftSide >= targetFastBitmap.Left) { // Now we can write the value from the calculated avarages var readLocation = (leftSide % range) << 2; targetFastBitmap.SetColorAt(leftSide, y, averages, readLocation); } } } }); }
/// <summary> /// Private helper method for the FindAutoCropRectangle /// </summary> /// <param name="fastBitmap"></param> /// <param name="colorPoint"></param> /// <param name="cropDifference"></param> /// <returns>Rectangle</returns> private static Rectangle FindAutoCropRectangle(IFastBitmap fastBitmap, Point colorPoint, int cropDifference) { Rectangle cropRectangle = Rectangle.Empty; Color referenceColor = fastBitmap.GetColorAt(colorPoint.X, colorPoint.Y); Point min = new Point(int.MaxValue, int.MaxValue); Point max = new Point(int.MinValue, int.MinValue); if (cropDifference > 0) { for (int y = 0; y < fastBitmap.Height; y++) { for (int x = 0; x < fastBitmap.Width; x++) { Color currentColor = fastBitmap.GetColorAt(x, y); int diffR = Math.Abs(currentColor.R - referenceColor.R); int diffG = Math.Abs(currentColor.G - referenceColor.G); int diffB = Math.Abs(currentColor.B - referenceColor.B); if (((diffR + diffG + diffB) / 3) <= cropDifference) { continue; } if (x < min.X) min.X = x; if (y < min.Y) min.Y = y; if (x > max.X) max.X = x; if (y > max.Y) max.Y = y; } } } else { for (int y = 0; y < fastBitmap.Height; y++) { for (int x = 0; x < fastBitmap.Width; x++) { Color currentColor = fastBitmap.GetColorAt(x, y); if (!referenceColor.Equals(currentColor)) { continue; } if (x < min.X) min.X = x; if (y < min.Y) min.Y = y; if (x > max.X) max.X = x; if (y > max.Y) max.Y = y; } } } if (!(Point.Empty.Equals(min) && max.Equals(new Point(fastBitmap.Width - 1, fastBitmap.Height - 1)))) { if (!(min.X == int.MaxValue || min.Y == int.MaxValue || max.X == int.MinValue || min.X == int.MinValue)) { cropRectangle = new Rectangle(min.X, min.Y, max.X - min.X + 1, max.Y - min.Y + 1); } } return cropRectangle; }
/// <summary> /// BoxBlurVertical is a private helper method for the BoxBlur /// </summary> /// <param name="targetFastBitmap">BitmapBuffer which previously was created with BoxBlurHorizontal</param> /// <param name="range">Range must be odd!</param> private static void BoxBlurVerticalAlpha(IFastBitmap targetFastBitmap, int range) { if (!targetFastBitmap.hasAlphaChannel) { throw new NotSupportedException("BoxBlurVerticalAlpha should be called for bitmaps with alpha channel"); } int halfRange = range / 2; Color[] newColors = new Color[targetFastBitmap.Height]; byte[] tmpColor = new byte[4]; for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) { int hits = 0; int a = 0; int r = 0; int g = 0; int b = 0; for (int y = targetFastBitmap.Top - halfRange; y < targetFastBitmap.Bottom; y++) { int oldPixel = y - halfRange - 1; if (oldPixel >= targetFastBitmap.Top) { targetFastBitmap.GetColorAt(x, oldPixel, tmpColor); a -= tmpColor[FastBitmap.COLOR_INDEX_A]; r -= tmpColor[FastBitmap.COLOR_INDEX_R]; g -= tmpColor[FastBitmap.COLOR_INDEX_G]; b -= tmpColor[FastBitmap.COLOR_INDEX_B]; hits--; } int newPixel = y + halfRange; if (newPixel < targetFastBitmap.Bottom) { //int colorg = pixels[index + newPixelOffset]; targetFastBitmap.GetColorAt(x, newPixel, tmpColor); a += tmpColor[FastBitmap.COLOR_INDEX_A]; r += tmpColor[FastBitmap.COLOR_INDEX_R]; g += tmpColor[FastBitmap.COLOR_INDEX_G]; b += tmpColor[FastBitmap.COLOR_INDEX_B]; hits++; } if (y >= targetFastBitmap.Top) { newColors[y - targetFastBitmap.Top] = Color.FromArgb((byte)(a / hits), (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); } } for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) { targetFastBitmap.SetColorAt(x, y, newColors[y - targetFastBitmap.Top]); } } }
/// <summary> /// BoxBlurHorizontal is a private helper method for the BoxBlur, only for IFastBitmaps with alpha channel /// </summary> /// <param name="targetFastBitmap">Target BitmapBuffer</param> /// <param name="range">Range must be odd!</param> private static void BoxBlurHorizontalAlpha(IFastBitmap targetFastBitmap, int range) { if (!targetFastBitmap.hasAlphaChannel) { throw new NotSupportedException("BoxBlurHorizontalAlpha should be called for bitmaps with alpha channel"); } int halfRange = range / 2; Color[] newColors = new Color[targetFastBitmap.Width]; byte[] tmpColor = new byte[4]; for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) { int hits = 0; int a = 0; int r = 0; int g = 0; int b = 0; for (int x = targetFastBitmap.Left - halfRange; x < targetFastBitmap.Right; x++) { int oldPixel = x - halfRange - 1; if (oldPixel >= targetFastBitmap.Left) { targetFastBitmap.GetColorAt(oldPixel, y, tmpColor); a -= tmpColor[FastBitmap.COLOR_INDEX_A]; r -= tmpColor[FastBitmap.COLOR_INDEX_R]; g -= tmpColor[FastBitmap.COLOR_INDEX_G]; b -= tmpColor[FastBitmap.COLOR_INDEX_B]; hits--; } int newPixel = x + halfRange; if (newPixel < targetFastBitmap.Right) { targetFastBitmap.GetColorAt(newPixel, y, tmpColor); a += tmpColor[FastBitmap.COLOR_INDEX_A]; r += tmpColor[FastBitmap.COLOR_INDEX_R]; g += tmpColor[FastBitmap.COLOR_INDEX_G]; b += tmpColor[FastBitmap.COLOR_INDEX_B]; hits++; } if (x >= targetFastBitmap.Left) { newColors[x - targetFastBitmap.Left] = Color.FromArgb((byte)(a / hits), (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); } } for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) { targetFastBitmap.SetColorAt(x, y, newColors[x - targetFastBitmap.Left]); } } }
/// <summary> /// Get the image /// </summary> public Bitmap GetQuantizedImage(int allowedColorCount) { if (allowedColorCount > 256) { throw new ArgumentOutOfRangeException("Quantizing muss be done to get less than 256 colors"); } if (colorCount < allowedColorCount) { // Simple logic to reduce to 8 bit LOG.Info("Colors in the image are already less as whished for, using simple copy to indexed image, no quantizing needed!"); return(SimpleReindex()); } // preprocess the colors CalculateMoments(); LOG.Info("Calculated the moments..."); Int32 next = 0; Single[] volumeVariance = new Single[MAXCOLOR]; // processes the cubes for (Int32 cubeIndex = 1; cubeIndex < allowedColorCount; ++cubeIndex) { // if cut is possible; make it if (Cut(cubes[next], cubes[cubeIndex])) { volumeVariance[next] = cubes[next].Volume > 1 ? CalculateVariance(cubes[next]) : 0.0f; volumeVariance[cubeIndex] = cubes[cubeIndex].Volume > 1 ? CalculateVariance(cubes[cubeIndex]) : 0.0f; } else { // the cut was not possible, revert the index volumeVariance[next] = 0.0f; cubeIndex--; } next = 0; Single temp = volumeVariance[0]; for (Int32 index = 1; index <= cubeIndex; ++index) { if (volumeVariance[index] > temp) { temp = volumeVariance[index]; next = index; } } if (temp <= 0.0) { allowedColorCount = cubeIndex + 1; break; } } Int32[] lookupRed = new Int32[MAXCOLOR]; Int32[] lookupGreen = new Int32[MAXCOLOR]; Int32[] lookupBlue = new Int32[MAXCOLOR]; tag = new byte[MAXVOLUME]; // precalculates lookup tables for (int k = 0; k < allowedColorCount; ++k) { Mark(cubes[k], k, tag); long weight = Volume(cubes[k], weights); if (weight > 0) { lookupRed[k] = (int)(Volume(cubes[k], momentsRed) / weight); lookupGreen[k] = (int)(Volume(cubes[k], momentsGreen) / weight); lookupBlue[k] = (int)(Volume(cubes[k], momentsBlue) / weight); } else { lookupRed[k] = 0; lookupGreen[k] = 0; lookupBlue[k] = 0; } } reds = new Int32[allowedColorCount + 1]; greens = new Int32[allowedColorCount + 1]; blues = new Int32[allowedColorCount + 1]; sums = new Int32[allowedColorCount + 1]; LOG.Info("Starting bitmap reconstruction..."); using (FastChunkyBitmap dest = FastBitmap.Create(resultBitmap) as FastChunkyBitmap) { using (IFastBitmap src = FastBitmap.Create(sourceBitmap)) { IFastBitmapWithBlend srcBlend = src as IFastBitmapWithBlend; Dictionary <Color, byte> lookup = new Dictionary <Color, byte>(); byte bestMatch; for (int y = 0; y < src.Height; y++) { for (int x = 0; x < src.Width; x++) { Color color; if (srcBlend != null) { // WithoutAlpha, this makes it possible to ignore the alpha color = srcBlend.GetBlendedColorAt(x, y); } else { color = src.GetColorAt(x, y); } // Check if we already matched the color if (!lookup.ContainsKey(color)) { // If not we need to find the best match // First get initial match bestMatch = dest.GetColorIndexAt(x, y); bestMatch = tag[bestMatch]; Int32 bestDistance = 100000000; for (int lookupIndex = 0; lookupIndex < allowedColorCount; lookupIndex++) { Int32 foundRed = lookupRed[lookupIndex]; Int32 foundGreen = lookupGreen[lookupIndex]; Int32 foundBlue = lookupBlue[lookupIndex]; Int32 deltaRed = color.R - foundRed; Int32 deltaGreen = color.G - foundGreen; Int32 deltaBlue = color.B - foundBlue; Int32 distance = deltaRed * deltaRed + deltaGreen * deltaGreen + deltaBlue * deltaBlue; if (distance < bestDistance) { bestDistance = distance; bestMatch = (byte)lookupIndex; } } lookup.Add(color, bestMatch); } else { // Already matched, so we just use the lookup bestMatch = lookup[color]; } reds[bestMatch] += color.R; greens[bestMatch] += color.G; blues[bestMatch] += color.B; sums[bestMatch]++; dest.SetColorIndexAt(x, y, bestMatch); } } } } // generates palette ColorPalette imagePalette = resultBitmap.Palette; Color[] entries = imagePalette.Entries; for (Int32 paletteIndex = 0; paletteIndex < allowedColorCount; paletteIndex++) { if (sums[paletteIndex] > 0) { reds[paletteIndex] /= sums[paletteIndex]; greens[paletteIndex] /= sums[paletteIndex]; blues[paletteIndex] /= sums[paletteIndex]; } entries[paletteIndex] = Color.FromArgb(255, reds[paletteIndex], greens[paletteIndex], blues[paletteIndex]); } resultBitmap.Palette = imagePalette; // Make sure the bitmap is not disposed, as we return it. Bitmap tmpBitmap = resultBitmap; resultBitmap = null; return(tmpBitmap); }
/// <summary> /// Reindex the 24/32 BPP (A)RGB image to a 8BPP /// </summary> /// <returns>Bitmap</returns> public Bitmap SimpleReindex() { List <Color> colors = new List <Color>(); Dictionary <Color, byte> lookup = new Dictionary <Color, byte>(); using (FastChunkyBitmap bbbDest = FastBitmap.Create(resultBitmap) as FastChunkyBitmap) { bbbDest.Lock(); using (IFastBitmap bbbSrc = FastBitmap.Create(sourceBitmap)) { IFastBitmapWithBlend bbbSrcBlend = bbbSrc as IFastBitmapWithBlend; bbbSrc.Lock(); byte index; for (int y = 0; y < bbbSrc.Height; y++) { for (int x = 0; x < bbbSrc.Width; x++) { Color color; if (bbbSrcBlend != null) { color = bbbSrcBlend.GetBlendedColorAt(x, y); } else { color = bbbSrc.GetColorAt(x, y); } if (lookup.ContainsKey(color)) { index = lookup[color]; } else { colors.Add(color); index = (byte)(colors.Count - 1); lookup.Add(color, index); } bbbDest.SetColorIndexAt(x, y, index); } } } } // generates palette ColorPalette imagePalette = resultBitmap.Palette; Color[] entries = imagePalette.Entries; for (Int32 paletteIndex = 0; paletteIndex < 256; paletteIndex++) { if (paletteIndex < colorCount) { entries[paletteIndex] = colors[paletteIndex]; } else { entries[paletteIndex] = Color.Black; } } resultBitmap.Palette = imagePalette; // Make sure the bitmap is not disposed, as we return it. Bitmap tmpBitmap = resultBitmap; resultBitmap = null; return(tmpBitmap); }
/// <summary> /// See <see cref="IColorQuantizer.Prepare"/> for more details. /// </summary> public WuQuantizer(Bitmap sourceBitmap) { this.sourceBitmap = sourceBitmap; // Make sure the color count variables are reset BitArray bitArray = new BitArray((int)Math.Pow(2, 24)); colorCount = 0; // creates all the cubes cubes = new WuColorCube[MAXCOLOR]; // initializes all the cubes for (Int32 cubeIndex = 0; cubeIndex < MAXCOLOR; cubeIndex++) { cubes[cubeIndex] = new WuColorCube(); } // resets the reference minimums cubes[0].RedMinimum = 0; cubes[0].GreenMinimum = 0; cubes[0].BlueMinimum = 0; // resets the reference maximums cubes[0].RedMaximum = MAXSIDEINDEX; cubes[0].GreenMaximum = MAXSIDEINDEX; cubes[0].BlueMaximum = MAXSIDEINDEX; weights = new Int64[SIDESIZE, SIDESIZE, SIDESIZE]; momentsRed = new Int64[SIDESIZE, SIDESIZE, SIDESIZE]; momentsGreen = new Int64[SIDESIZE, SIDESIZE, SIDESIZE]; momentsBlue = new Int64[SIDESIZE, SIDESIZE, SIDESIZE]; moments = new Single[SIDESIZE, SIDESIZE, SIDESIZE]; Int32[] table = new Int32[256]; for (Int32 tableIndex = 0; tableIndex < 256; ++tableIndex) { table[tableIndex] = tableIndex * tableIndex; } // Use a bitmap to store the initial match, which is just as good as an array and saves us 2x the storage using (IFastBitmap sourceFastBitmap = FastBitmap.Create(sourceBitmap)) { IFastBitmapWithBlend sourceFastBitmapWithBlend = sourceFastBitmap as IFastBitmapWithBlend; sourceFastBitmap.Lock(); using (FastChunkyBitmap destinationFastBitmap = FastBitmap.CreateEmpty(sourceBitmap.Size, PixelFormat.Format8bppIndexed, Color.White) as FastChunkyBitmap) { destinationFastBitmap.Lock(); for (int y = 0; y < sourceFastBitmap.Height; y++) { for (int x = 0; x < sourceFastBitmap.Width; x++) { Color color; if (sourceFastBitmapWithBlend == null) { color = sourceFastBitmap.GetColorAt(x, y); } else { color = sourceFastBitmapWithBlend.GetBlendedColorAt(x, y); } // To count the colors int index = color.ToArgb() & 0x00ffffff; // Check if we already have this color if (!bitArray.Get(index)) { // If not, add 1 to the single colors colorCount++; bitArray.Set(index, true); } Int32 indexRed = (color.R >> 3) + 1; Int32 indexGreen = (color.G >> 3) + 1; Int32 indexBlue = (color.B >> 3) + 1; weights[indexRed, indexGreen, indexBlue]++; momentsRed[indexRed, indexGreen, indexBlue] += color.R; momentsGreen[indexRed, indexGreen, indexBlue] += color.G; momentsBlue[indexRed, indexGreen, indexBlue] += color.B; moments[indexRed, indexGreen, indexBlue] += table[color.R] + table[color.G] + table[color.B]; // Store the initial "match" Int32 paletteIndex = (indexRed << 10) + (indexRed << 6) + indexRed + (indexGreen << 5) + indexGreen + indexBlue; destinationFastBitmap.SetColorIndexAt(x, y, (byte)(paletteIndex & 0xff)); } } resultBitmap = destinationFastBitmap.UnlockAndReturnBitmap(); } } }