/// <summary> /// Returns the color count from the palette of the given image. /// </summary> /// <param name="image"> /// The <see cref="System.Drawing.Image"/> to get the colors from. /// </param> /// <returns> /// The <see cref="int"/> representing the color count. /// </returns> public static int GetColorCount(Image image) { ConcurrentDictionary<Color, Color> colors = new ConcurrentDictionary<Color, Color>(); int width = image.Width; int height = image.Height; using (FastBitmap fastBitmap = new FastBitmap(image)) { Parallel.For( 0, height, y => { for (int x = 0; x < width; x++) { // ReSharper disable once AccessToDisposedClosure Color color = fastBitmap.GetPixel(x, y); colors.TryAdd(color, color); } }); } int count = colors.Count; colors.Clear(); return count; }
/// <summary> /// Processes the given bitmap to apply the threshold. /// </summary> /// <param name="source"> /// The image to process. /// </param> /// <returns> /// A processed bitmap. /// </returns> public Bitmap ProcessFilter(Bitmap source) { int width = source.Width; int height = source.Height; using (FastBitmap sourceBitmap = new FastBitmap(source)) { Parallel.For( 0, height, y => { for (int x = 0; x < width; x++) { // ReSharper disable AccessToDisposedClosure Color color = sourceBitmap.GetPixel(x, y); sourceBitmap.SetPixel(x, y, color.B >= this.threshold ? Color.White : Color.Black); // ReSharper restore AccessToDisposedClosure } }); } return source; }
/// <summary> /// Adjusts the alpha component of the given image. /// </summary> /// <param name="source"> /// The <see cref="Image"/> source to adjust. /// </param> /// <param name="percentage"> /// The percentage value between 0 and 100 for adjusting the opacity. /// </param> /// <param name="rectangle">The rectangle to define the bounds of the area to adjust the opacity. /// If null then the effect is applied to the entire image.</param> /// <returns> /// The <see cref="Bitmap"/> with the alpha component adjusted. /// </returns> /// <exception cref="ArgumentOutOfRangeException"> /// Thrown if the percentage value falls outside the acceptable range. /// </exception> public static Bitmap Alpha(Image source, int percentage, Rectangle? rectangle = null) { if (percentage > 100 || percentage < 0) { throw new ArgumentOutOfRangeException(nameof(percentage), "Percentage should be between 0 and 100."); } float factor = (float)percentage / 100; int width = source.Width; int height = source.Height; // Traditional examples using a color matrix alter the rgb values also. using (FastBitmap bitmap = new FastBitmap(source)) { // Loop through the pixels. Parallel.For( 0, height, y => { for (int x = 0; x < width; x++) { // ReSharper disable AccessToDisposedClosure Color color = bitmap.GetPixel(x, y); bitmap.SetPixel(x, y, Color.FromArgb(Convert.ToInt32(color.A * factor), color.R, color.G, color.B)); // ReSharper restore AccessToDisposedClosure } }); } return (Bitmap)source; }
/// <summary> /// Applies the given image mask to the source. /// </summary> /// <param name="source"> /// The source <see cref="Image"/>. /// </param> /// <param name="mask"> /// The mask <see cref="Image"/>. /// </param> /// <exception cref="ArgumentException"> /// Thrown if the two images are of different size. /// </exception> /// <returns> /// The masked <see cref="Bitmap"/>. /// </returns> public static Bitmap ApplyMask(Image source, Image mask) { if (mask.Size != source.Size) { throw new ArgumentException(); } int width = mask.Width; int height = mask.Height; Bitmap toMask = new Bitmap(source); toMask.SetResolution(source.HorizontalResolution, source.VerticalResolution); // Loop through and replace the alpha channel using (FastBitmap maskBitmap = new FastBitmap(mask)) { using (FastBitmap sourceBitmap = new FastBitmap(toMask)) { Parallel.For( 0, height, y => { for (int x = 0; x < width; x++) { // ReSharper disable AccessToDisposedClosure Color maskColor = maskBitmap.GetPixel(x, y); Color sourceColor = sourceBitmap.GetPixel(x, y); if (sourceColor.A != 0) { sourceBitmap.SetPixel(x, y, Color.FromArgb(maskColor.A, sourceColor.R, sourceColor.G, sourceColor.B)); } // ReSharper restore AccessToDisposedClosure } }); } } // Ensure the background is cleared out on non alpha supporting formats. Bitmap clear = new Bitmap(width, height, PixelFormat.Format32bppPArgb); clear.SetResolution(source.HorizontalResolution, source.VerticalResolution); using (Graphics graphics = Graphics.FromImage(clear)) { graphics.SmoothingMode = SmoothingMode.AntiAlias; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.Clear(Color.Transparent); graphics.DrawImageUnscaled(toMask, 0, 0, width, height); } toMask.Dispose(); return clear; }
//tiles = 16 means 16 x 16 atlas public List<Bitmap> Atlas2dInto1d(Bitmap atlas2d, int tiles, int atlassizezlimit) { FastBitmap orig = new FastBitmap(); 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 FastBitmap 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 = new FastBitmap(); 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 static bool CompareColors(this Image image, Image other) { if (image == null || other == null) { throw new ArgumentNullException(image == null ? "image" : "other"); } if (image.Size != other.Size) { return false; } FastBitmap fastImage = new FastBitmap(image); FastBitmap fastOther = new FastBitmap(other); try { fastImage.Lock(); fastOther.Lock(); for (int x = 0; x < image.Width; x++) { for (int y = 0; y < image.Height; y++) { // We use ToArgb because Colors are compared on more than their ARGB values - see Color class documentation on MSDN if (fastImage.GetPixel(x, y).ToArgb() != fastOther.GetPixel(x, y).ToArgb()) { return false; } } } } finally { fastImage.Unlock(); fastOther.Unlock(); } return true; }
/// <summary> /// Traces the edges of a given <see cref="Image"/>. /// </summary> /// <param name="source"> /// The source <see cref="Image"/>. /// </param> /// <param name="destination"> /// The destination <see cref="Image"/>. /// </param> /// <param name="threshold"> /// The threshold (between 0 and 255). /// </param> /// <returns> /// The a new instance of <see cref="Bitmap"/> traced. /// </returns> public static Bitmap Trace(Image source, Image destination, byte threshold = 0) { int width = source.Width; int height = source.Height; // Grab the edges converting to greyscale, and invert the colors. ConvolutionFilter filter = new ConvolutionFilter(new SobelEdgeFilter(), true); using (Bitmap temp = filter.Process2DFilter(source)) { destination = new InvertMatrixFilter().TransformImage(temp, destination); // Darken it slightly to aid detection destination = Adjustments.Brightness(destination, -5); } // Loop through and replace any colors more white than the threshold // with a transparent one. using (FastBitmap destinationBitmap = new FastBitmap(destination)) { Parallel.For( 0, height, y => { for (int x = 0; x < width; x++) { // ReSharper disable AccessToDisposedClosure Color color = destinationBitmap.GetPixel(x, y); if (color.B >= threshold) { destinationBitmap.SetPixel(x, y, Color.Transparent); } // ReSharper restore AccessToDisposedClosure } }); } // Darken it again to average out the color. destination = Adjustments.Brightness(destination, -5); return (Bitmap)destination; }
/// <summary> /// Adjust the gamma (intensity of the light) component of the given image. /// </summary> /// <param name="source"> /// The <see cref="Image"/> source to adjust. /// </param> /// <param name="value"> /// The value to adjust the gamma by (typically between .2 and 5). /// </param> /// <returns> /// The <see cref="Bitmap"/> with the gamma adjusted. /// </returns> /// <exception cref="ArgumentOutOfRangeException"> /// Thrown if the value falls outside the acceptable range. /// </exception> public static Bitmap Gamma(Image source, float value) { if (value > 5 || value < .1) { throw new ArgumentOutOfRangeException(nameof(value), "Value should be between .1 and 5."); } byte[] ramp = new byte[256]; for (int x = 0; x < 256; ++x) { byte val = ((255.0 * Math.Pow(x / 255.0, value)) + 0.5).ToByte(); ramp[x] = val; } int width = source.Width; int height = source.Height; using (FastBitmap bitmap = new FastBitmap(source)) { Parallel.For( 0, height, y => { for (int x = 0; x < width; x++) { // ReSharper disable once AccessToDisposedClosure Color composite = bitmap.GetPixel(x, y); Color linear = Color.FromArgb(composite.A, ramp[composite.R], ramp[composite.G], ramp[composite.B]); // ReSharper disable once AccessToDisposedClosure bitmap.SetPixel(x, y, linear); } }); } return (Bitmap)source; }
/// <summary> /// Converts an image from a linear color-space to the equivalent sRGB color-space. /// </summary> /// <param name="source"> /// The <see cref="Image"/> source to convert. /// </param> /// <returns> /// The <see cref="Bitmap"/>. /// </returns> public static Bitmap ToSRGB(Image source) { // Create only once and lazily. byte[] ramp = SRGBBytes.Value; int width = source.Width; int height = source.Height; using (FastBitmap bitmap = new FastBitmap(source)) { Parallel.For( 0, height, y => { for (int x = 0; x < width; x++) { // ReSharper disable once AccessToDisposedClosure Color composite = bitmap.GetPixel(x, y); Color linear = Color.FromArgb(composite.A, ramp[composite.R], ramp[composite.G], ramp[composite.B]); // ReSharper disable once AccessToDisposedClosure bitmap.SetPixel(x, y, linear); } }); } return (Bitmap)source; }
private static Bitmap CropBitmapAndUnlok(FastBitmap bmp, Color backgroundColor) { int y = 0; int x; Color c = backgroundColor; int backgroundArgb = backgroundColor.ToArgb(); // Crop top while (y < bmp.Height && IsBackgroundColor(c, backgroundArgb)) { c = bmp.GetPixel(0, y); if (IsBackgroundColor(c, backgroundArgb)) { for (x = 1; x < bmp.Width; x++) { c = bmp.GetPixelNext(); if (c.A != 0 && c.ToArgb() != backgroundArgb) break; } } if (IsBackgroundColor(c, backgroundArgb)) y++; } int minY = y; if (minY > 3) minY -= 3; else minY = 0; // Crop left x = 0; c = backgroundColor; while (x < bmp.Width && IsBackgroundColor(c, backgroundArgb)) { for (y = minY; y < bmp.Height; y++) { c = bmp.GetPixel(x, y); if (!IsBackgroundColor(c, backgroundArgb)) break; } if (IsBackgroundColor(c, backgroundArgb)) x++; } int minX = x; if (minX > 3) minX -= 3; else minX -= 0; // Crop bottom y = bmp.Height - 1; c = backgroundColor; while (y > minY && IsBackgroundColor(c, backgroundArgb)) { c = bmp.GetPixel(0, y); if (IsBackgroundColor(c, backgroundArgb)) { for (x = 1; x < bmp.Width; x++) { c = bmp.GetPixelNext(); if (!IsBackgroundColor(c, backgroundArgb)) break; } } if (IsBackgroundColor(c, backgroundArgb)) y--; } int maxY = y + 7; if (maxY >= bmp.Height) maxY = bmp.Height - 1; // Crop right x = bmp.Width - 1; c = backgroundColor; while (x > minX && IsBackgroundColor(c, backgroundArgb)) { for (y = minY; y < bmp.Height; y++) { c = bmp.GetPixel(x, y); if (!IsBackgroundColor(c, backgroundArgb)) break; } if (IsBackgroundColor(c, backgroundArgb)) x--; } int maxX = x + 7; if (maxX >= bmp.Width) maxX = bmp.Width - 1; bmp.UnlockImage(); Bitmap bmpImage = bmp.GetBitmap(); if (bmpImage.Width > 1 && bmpImage.Height > 1 && maxX - minX > 0 && maxY - minY > 0) { Bitmap bmpCrop = bmpImage.Clone(new Rectangle(minX, minY, maxX - minX, maxY - minY), bmpImage.PixelFormat); return bmpCrop; } return (Bitmap)bmpImage.Clone(); }
/// <summary> /// Processes the given bitmap to apply the current instance of <see cref="IEdgeFilter"/>. /// </summary> /// <param name="source">The image to process.</param> /// <returns>A processed bitmap.</returns> public Bitmap ProcessFilter(Image source) { int width = source.Width; int height = source.Height; int maxWidth = width + 1; int maxHeight = height + 1; int bufferedWidth = width + 2; int bufferedHeight = height + 2; Bitmap destination = new Bitmap(width, height, PixelFormat.Format32bppPArgb); Bitmap input = new Bitmap(bufferedWidth, bufferedHeight, PixelFormat.Format32bppPArgb); destination.SetResolution(source.HorizontalResolution, source.VerticalResolution); input.SetResolution(source.HorizontalResolution, source.VerticalResolution); using (Graphics graphics = Graphics.FromImage(input)) { // Fixes an issue with transparency not converting properly. graphics.Clear(Color.Transparent); Rectangle destinationRectangle = new Rectangle(0, 0, bufferedWidth, bufferedHeight); Rectangle rectangle = new Rectangle(0, 0, width, height); // If it's greyscale apply a colormatrix to the image. using (ImageAttributes attributes = new ImageAttributes()) { if (this.greyscale) { attributes.SetColorMatrix(ColorMatrixes.GreyScale); } // We use a trick here to detect right to the edges of the image. // flip/tile the image with a pixel in excess in each direction to duplicate pixels. // Later on we draw pixels without that excess. using (TextureBrush tb = new TextureBrush(source, rectangle, attributes)) { tb.WrapMode = WrapMode.TileFlipXY; tb.TranslateTransform(1, 1); graphics.FillRectangle(tb, destinationRectangle); } } } try { double[,] horizontalFilter = this.edgeFilter.HorizontalGradientOperator; int kernelLength = horizontalFilter.GetLength(0); int radius = kernelLength >> 1; using (FastBitmap sourceBitmap = new FastBitmap(input)) { using (FastBitmap destinationBitmap = new FastBitmap(destination)) { // Loop through the pixels. Parallel.For( 0, bufferedHeight, y => { for (int x = 0; x < bufferedWidth; x++) { double rX = 0; double gX = 0; double bX = 0; // Apply each matrix multiplier to the color components for each pixel. for (int fy = 0; fy < kernelLength; fy++) { int fyr = fy - radius; int offsetY = y + fyr; // Skip the current row if (offsetY < 0) { continue; } // Outwith the current bounds so break. if (offsetY >= bufferedHeight) { break; } for (int fx = 0; fx < kernelLength; fx++) { int fxr = fx - radius; int offsetX = x + fxr; // Skip the column if (offsetX < 0) { continue; } if (offsetX < bufferedWidth) { // ReSharper disable once AccessToDisposedClosure Color currentColor = sourceBitmap.GetPixel(offsetX, offsetY); double r = currentColor.R; double g = currentColor.G; double b = currentColor.B; rX += horizontalFilter[fy, fx] * r; gX += horizontalFilter[fy, fx] * g; bX += horizontalFilter[fy, fx] * b; } } } // Apply the equation and sanitize. byte red = rX.ToByte(); byte green = gX.ToByte(); byte blue = bX.ToByte(); Color newColor = Color.FromArgb(red, green, blue); if (y > 0 && x > 0 && y < maxHeight && x < maxWidth) { // ReSharper disable once AccessToDisposedClosure destinationBitmap.SetPixel(x - 1, y - 1, newColor); } } }); } } } finally { // We created a new image. Cleanup. input.Dispose(); } return destination; }
/// <summary> /// Adjust the gamma (intensity of the light) component of the given image. /// </summary> /// <param name="source"> /// The <see cref="Image"/> source to adjust. /// </param> /// <param name="value"> /// The value to adjust the gamma by (typically between .2 and 5). /// </param> /// <returns> /// The <see cref="Bitmap"/> with the gamma adjusted. /// </returns> /// <exception cref="ArgumentOutOfRangeException"> /// Thrown if the value falls outside the acceptable range. /// </exception> public static Bitmap Gamma(Image source, float value) { if (value > 5 || value < .1) { throw new ArgumentOutOfRangeException("value", "Value should be between .1 and 5."); } int width = source.Width; int height = source.Height; Bitmap destination = new Bitmap(width, height); destination.SetResolution(source.HorizontalResolution, source.VerticalResolution); byte[] ramp = new byte[256]; for (int x = 0; x < 256; ++x) { byte val = ((255.0 * Math.Pow(x / 255.0, value)) + 0.5).ToByte(); ramp[x] = val; } using (FastBitmap fastSource = new FastBitmap(source)) { using (FastBitmap fastDestination = new FastBitmap(destination)) { Parallel.For( 0, height, y => { for (int x = 0; x < width; x++) { // ReSharper disable once AccessToDisposedClosure Color color = fastSource.GetPixel(x, y); byte r = ramp[color.R]; byte g = ramp[color.G]; byte b = ramp[color.B]; // ReSharper disable once AccessToDisposedClosure fastDestination.SetPixel(x, y, Color.FromArgb(color.A, r, g, b)); } }); } } //Rectangle rectangle = new Rectangle(0, 0, width, height); //using (Graphics graphics = Graphics.FromImage(destination)) //{ // using (ImageAttributes attributes = new ImageAttributes()) // { // attributes.SetGamma(value); // graphics.DrawImage(source, rectangle, 0, 0, width, height, GraphicsUnit.Pixel, attributes); // } //} source.Dispose(); return destination; }
/// <summary> /// Applies the halftone filter. /// </summary> /// <param name="source"> /// The <see cref="Bitmap"/> to apply the filter to. /// </param> /// <returns> /// The <see cref="Bitmap"/> with the filter applied. /// </returns> public Bitmap ApplyFilter(Bitmap source) { // TODO: Make this class implement an interface? Bitmap padded = null; Bitmap cyan = null; Bitmap magenta = null; Bitmap yellow = null; Bitmap keyline = null; Bitmap newImage = null; try { int sourceWidth = source.Width; int sourceHeight = source.Height; int width = source.Width + this.distance; int height = source.Height + this.distance; // Draw a slightly larger image, flipping the top/left pixels to prevent // jagged edge of output. padded = new Bitmap(width, height, PixelFormat.Format32bppPArgb); padded.SetResolution(source.HorizontalResolution, source.VerticalResolution); using (Graphics graphicsPadded = Graphics.FromImage(padded)) { graphicsPadded.Clear(Color.White); Rectangle destinationRectangle = new Rectangle(0, 0, sourceWidth + this.distance, source.Height + this.distance); using (TextureBrush tb = new TextureBrush(source)) { tb.WrapMode = WrapMode.TileFlipXY; tb.TranslateTransform(this.distance, this.distance); graphicsPadded.FillRectangle(tb, destinationRectangle); } } // Calculate min and max widths/heights. Rectangle rotatedBounds = this.GetBoundingRectangle(width, height); int minY = -(rotatedBounds.Height + height); int maxY = rotatedBounds.Height + height; int minX = -(rotatedBounds.Width + width); int maxX = rotatedBounds.Width + width; Point center = Point.Empty; // Yellow oversaturates the output. int offset = this.distance; float yellowMultiplier = this.distance * 1.587f; float magentaMultiplier = this.distance * 2.176f; float multiplier = this.distance * 2.2f; float max = this.distance * (float)Math.Sqrt(2); float magentaMax = this.distance * (float)Math.Sqrt(1.4545); // Bump up the keyline max so that black looks black. float keylineMax = max * (float)Math.Sqrt(2); // Color sampled process colours from Wikipedia pages. // Keyline brush is declared separately. Brush cyanBrush = new SolidBrush(Color.FromArgb(0, 183, 235)); Brush magentaBrush = new SolidBrush(Color.FromArgb(255, 0, 144)); Brush yellowBrush = new SolidBrush(Color.FromArgb(255, 239, 0)); // Create our images. cyan = new Bitmap(width, height, PixelFormat.Format32bppPArgb); magenta = new Bitmap(width, height, PixelFormat.Format32bppPArgb); yellow = new Bitmap(width, height, PixelFormat.Format32bppPArgb); keyline = new Bitmap(width, height, PixelFormat.Format32bppPArgb); newImage = new Bitmap(sourceWidth, sourceHeight, PixelFormat.Format32bppPArgb); // Ensure the correct resolution is set. cyan.SetResolution(source.HorizontalResolution, source.VerticalResolution); magenta.SetResolution(source.HorizontalResolution, source.VerticalResolution); yellow.SetResolution(source.HorizontalResolution, source.VerticalResolution); keyline.SetResolution(source.HorizontalResolution, source.VerticalResolution); newImage.SetResolution(source.HorizontalResolution, source.VerticalResolution); // Check bounds against this. Rectangle rectangle = new Rectangle(0, 0, width, height); using (Graphics graphicsCyan = Graphics.FromImage(cyan)) using (Graphics graphicsMagenta = Graphics.FromImage(magenta)) using (Graphics graphicsYellow = Graphics.FromImage(yellow)) using (Graphics graphicsKeyline = Graphics.FromImage(keyline)) { // Set the quality properties. graphicsCyan.PixelOffsetMode = PixelOffsetMode.Half; graphicsMagenta.PixelOffsetMode = PixelOffsetMode.Half; graphicsYellow.PixelOffsetMode = PixelOffsetMode.Half; graphicsKeyline.PixelOffsetMode = PixelOffsetMode.Half; graphicsCyan.SmoothingMode = SmoothingMode.AntiAlias; graphicsMagenta.SmoothingMode = SmoothingMode.AntiAlias; graphicsYellow.SmoothingMode = SmoothingMode.AntiAlias; graphicsKeyline.SmoothingMode = SmoothingMode.AntiAlias; graphicsCyan.CompositingQuality = CompositingQuality.HighQuality; graphicsMagenta.CompositingQuality = CompositingQuality.HighQuality; graphicsYellow.CompositingQuality = CompositingQuality.HighQuality; graphicsKeyline.CompositingQuality = CompositingQuality.HighQuality; // Set up the canvas. graphicsCyan.Clear(Color.White); graphicsMagenta.Clear(Color.White); graphicsYellow.Clear(Color.White); graphicsKeyline.Clear(Color.White); // This is too slow. The graphics object can't be called within a parallel // loop so we have to do it old school. :( using (FastBitmap sourceBitmap = new FastBitmap(padded)) { for (int y = minY; y < maxY; y += offset) { for (int x = minX; x < maxX; x += offset) { Color color; CmykColor cmykColor; float brushWidth; // Cyan Point rotatedPoint = ImageMaths.RotatePoint(new Point(x, y), this.cyanAngle, center); int angledX = rotatedPoint.X; int angledY = rotatedPoint.Y; if (rectangle.Contains(new Point(angledX, angledY))) { color = sourceBitmap.GetPixel(angledX, angledY); cmykColor = color; brushWidth = Math.Min((cmykColor.C / 100f) * multiplier, max); graphicsCyan.FillEllipse(cyanBrush, angledX, angledY, brushWidth, brushWidth); } // Magenta rotatedPoint = ImageMaths.RotatePoint(new Point(x, y), this.magentaAngle, center); angledX = rotatedPoint.X; angledY = rotatedPoint.Y; if (rectangle.Contains(new Point(angledX, angledY))) { color = sourceBitmap.GetPixel(angledX, angledY); cmykColor = color; brushWidth = Math.Min((cmykColor.M / 100f) * magentaMultiplier, magentaMax); graphicsMagenta.FillEllipse(magentaBrush, angledX, angledY, brushWidth, brushWidth); } // Yellow rotatedPoint = ImageMaths.RotatePoint(new Point(x, y), this.yellowAngle, center); angledX = rotatedPoint.X; angledY = rotatedPoint.Y; if (rectangle.Contains(new Point(angledX, angledY))) { color = sourceBitmap.GetPixel(angledX, angledY); cmykColor = color; brushWidth = Math.Min((cmykColor.Y / 100f) * yellowMultiplier, max); graphicsYellow.FillEllipse(yellowBrush, angledX, angledY, brushWidth, brushWidth); } // Keyline rotatedPoint = ImageMaths.RotatePoint(new Point(x, y), this.keylineAngle, center); angledX = rotatedPoint.X; angledY = rotatedPoint.Y; if (rectangle.Contains(new Point(angledX, angledY))) { color = sourceBitmap.GetPixel(angledX, angledY); cmykColor = color; brushWidth = Math.Min((cmykColor.K / 100f) * multiplier, keylineMax); // Just using black is too dark. Brush keylineBrush = new SolidBrush(CmykColor.FromCmykColor(0, 0, 0, cmykColor.K)); graphicsKeyline.FillEllipse(keylineBrush, angledX, angledY, brushWidth, brushWidth); } } } } // Set our white background. using (Graphics graphics = Graphics.FromImage(newImage)) { graphics.Clear(Color.White); } // Blend the colors now to mimic adaptive blending. using (FastBitmap cyanBitmap = new FastBitmap(cyan)) using (FastBitmap magentaBitmap = new FastBitmap(magenta)) using (FastBitmap yellowBitmap = new FastBitmap(yellow)) using (FastBitmap keylineBitmap = new FastBitmap(keyline)) using (FastBitmap destinationBitmap = new FastBitmap(newImage)) { Parallel.For( offset, height, y => { for (int x = offset; x < width; x++) { // ReSharper disable AccessToDisposedClosure Color cyanPixel = cyanBitmap.GetPixel(x, y); Color magentaPixel = magentaBitmap.GetPixel(x, y); Color yellowPixel = yellowBitmap.GetPixel(x, y); Color keylinePixel = keylineBitmap.GetPixel(x, y); // Negate the offset. int xBack = x - offset; int yBack = y - offset; CmykColor blended = cyanPixel.AddAsCmykColor(magentaPixel, yellowPixel, keylinePixel); if (rectangle.Contains(new Point(xBack, yBack))) { destinationBitmap.SetPixel(xBack, yBack, blended); } // ReSharper restore AccessToDisposedClosure } }); } } padded.Dispose(); cyan.Dispose(); magenta.Dispose(); yellow.Dispose(); keyline.Dispose(); source.Dispose(); source = newImage; } catch { if (padded != null) { padded.Dispose(); } if (cyan != null) { cyan.Dispose(); } if (magenta != null) { magenta.Dispose(); } if (yellow != null) { yellow.Dispose(); } if (keyline != null) { keyline.Dispose(); } if (newImage != null) { newImage.Dispose(); } } return source; }
/// <summary> /// Applies the oil painting filter. /// </summary> /// <param name="source"> /// The source. /// </param> /// <returns> /// The <see cref="Bitmap"/>. /// </returns> public Bitmap ApplyFilter(Bitmap source) { // TODO: Make this class implement an interface? int width = source.Width; int height = source.Height; int radius = this.brushSize >> 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)) { Parallel.For( 0, height, y => { for (int x = 0; x < width; x++) { int maxIntensity = 0; int maxIndex = 0; int[] intensityBin = new int[this.levels]; int[] blueBin = new int[this.levels]; int[] greenBin = new int[this.levels]; int[] redBin = new int[this.levels]; byte sourceAlpha = 255; for (int i = 0; i <= radius; 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 (int fx = 0; fx <= radius; fx++) { int jr = fx - radius; int offsetX = x + jr; // Skip the column if (offsetX < 0) { continue; } if (offsetX < width) { // ReSharper disable once AccessToDisposedClosure Color color = sourceBitmap.GetPixel(offsetX, offsetY); byte sourceBlue = color.B; byte sourceGreen = color.G; byte sourceRed = color.R; sourceAlpha = color.A; int currentIntensity = (int)Math.Round(((sourceBlue + sourceGreen + sourceRed) / 3.0 * (this.levels - 1)) / 255.0); intensityBin[currentIntensity] += 1; blueBin[currentIntensity] += sourceBlue; greenBin[currentIntensity] += sourceGreen; redBin[currentIntensity] += sourceRed; if (intensityBin[currentIntensity] > maxIntensity) { maxIntensity = intensityBin[currentIntensity]; maxIndex = currentIntensity; } } } } byte blue = Math.Abs(blueBin[maxIndex] / maxIntensity).ToByte(); byte green = Math.Abs(greenBin[maxIndex] / maxIntensity).ToByte(); byte red = Math.Abs(redBin[maxIndex] / maxIntensity).ToByte(); // ReSharper disable once AccessToDisposedClosure destinationBitmap.SetPixel(x, y, Color.FromArgb(sourceAlpha, red, green, blue)); } }); } } return destination; }
public void TestGetPixel() { Bitmap original = GenerateRandomBitmap(64, 64); Bitmap copy = original.Clone(new Rectangle(0, 0, 64, 64), original.PixelFormat); FastBitmap fastOriginal = new FastBitmap(original); fastOriginal.Lock(); for (int y = 0; y < original.Height; y++) { for (int x = 0; x < original.Width; x++) { Assert.AreEqual(fastOriginal.GetPixel(x, y).ToArgb(), copy.GetPixel(x, y).ToArgb(), "Calls to FastBitmap.GetPixel() must return the same value as returned by Bitmap.GetPixel()"); } } fastOriginal.Unlock(); }
public static Image ReplaceColor(this Image image, Color oldColor, Color newColor) { if (image == null) { throw new ArgumentNullException("image"); } Image clone = image.Clone() as Image; // If they try to replace a color with itself (sneaky, sneaky!) then just return our cloned image immediately if (oldColor.ToArgb() == newColor.ToArgb()) { return clone; } FastBitmap fastBitmap = new FastBitmap(clone); fastBitmap.Lock(); for (int x = 0; x < image.Width; x++) { for (int y = 0; y < image.Height; y++) { // We use ToArgb because Colors are compared on more than their ARGB values - see Color class documentation on MSDN if (fastBitmap.GetPixel(x, y).ToArgb() == oldColor.ToArgb()) { fastBitmap.SetPixel(x, y, newColor); } } } fastBitmap.Unlock(); return fastBitmap.Image; }
public Bitmap GetImageTransparentRgba(int index) { if (ImagesRgba == null || ImagesRgba.Count <= index) { return null; } if (IsDrawnRgba(index) == false) { DrawRgbaImage(index); } Bitmap img = ImagesRgba[index].Image.Clone() as Bitmap; Color bg = Color.Fuchsia; FastBitmap fb = new FastBitmap(img); fb.LockImage(); for (int x = 0; x < img.Width; x++) { for (int y = 0; y < img.Height; y++) { if (fb.GetPixel(x, y) == bg) { fb.SetPixel(x, y, Color.Transparent); } } } fb.UnlockImage(); return img; }
public Bitmap GetImageTransparentPal(int index) { if (ImagesPal == null || ImagesPal.Count <= index) { return null; } if (IsDrawnPal(index) == false) { if (DrawPalImage(index) == false) { // TODO: Exception? return null; } } Bitmap img = ImagesPal[index].Image.Clone() as Bitmap; Color bg = Palette[0]; FastBitmap fb = new FastBitmap(img); fb.LockImage(); for (int x = 0; x < img.Width; x++) { for (int y = 0; y < img.Height; y++) { if (fb.GetPixel(x, y) == bg) { fb.SetPixel(x, y, Color.Transparent); } } } fb.UnlockImage(); return img; }
public void TestFastBitmapUnlockedGetAccessException() { Bitmap bitmap = new Bitmap(64, 64); FastBitmap fastBitmap = new FastBitmap(bitmap); fastBitmap.GetPixel(0, 0); }
public void TestFastBitmapGetPixelBoundsException() { Bitmap bitmap = new Bitmap(64, 64); FastBitmap fastBitmap = new FastBitmap(bitmap); fastBitmap.Lock(); try { fastBitmap.GetPixel(-1, -1); Assert.Fail("When trying to access a coordinate that is out of bounds via GetPixel, an exception must be thrown"); } catch (ArgumentOutOfRangeException) { } try { fastBitmap.GetPixel(fastBitmap.Width, 0); Assert.Fail("When trying to access a coordinate that is out of bounds via GetPixel, an exception must be thrown"); } catch (ArgumentOutOfRangeException) { } try { fastBitmap.GetPixel(0, fastBitmap.Height); Assert.Fail("When trying to access a coordinate that is out of bounds via GetPixel, an exception must be thrown"); } catch (ArgumentOutOfRangeException) { } fastBitmap.GetPixel(fastBitmap.Width - 1, fastBitmap.Height - 1); }
public Bitmap GetImageTransparentPal(int index) { if (ImagesPal == null || ImagesPal.Count <= index) { return null; } if (IsDrawnPal(index) == false) { if (DrawPalImage(index) == false) { throw new Exception("Failed to draw pal-image on index #" + index); } } var img = ImagesPal[index].Image.Clone() as Bitmap; if (img == null) { throw new Exception("Invalid pal image on index #" + index); } var bg = Palette[0]; var fb = new FastBitmap(img); fb.LockImage(); for (var x = 0; x < img.Width; x++) { for (var y = 0; y < img.Height; y++) { if (fb.GetPixel(x, y) == bg) { fb.SetPixel(x, y, Color.Transparent); } } } fb.UnlockImage(); return img; }
public Bitmap GetImageTransparentRgba(int index) { if (ImagesRgba == null || ImagesRgba.Count <= index) { return null; } if (IsDrawnRgba(index) == false) { DrawRgbaImage(index); } var img = ImagesRgba[index].Image.Clone() as Bitmap; if (img == null) { throw new Exception("Invalid rgba image on index #" + index); } var bg = Color.Fuchsia; var fb = new FastBitmap(img); fb.LockImage(); for (var x = 0; x < img.Width; x++) { for (var y = 0; y < img.Height; y++) { if (fb.GetPixel(x, y) == bg) { fb.SetPixel(x, y, Color.Transparent); } } } fb.UnlockImage(); return img; }
public static int CountColors(this Image image) { if (image == null) { throw new ArgumentNullException("image"); } HashSet<int> colors = new HashSet<int>(); FastBitmap fastBitmap = new FastBitmap(image); fastBitmap.Lock(); // Loop through all the pixels of the image // and add the colors to the HashSet - duplicates // are automatically ignored by that collection type for (int x = 0; x < image.Width; x++) { for (int y = 0; y < image.Height; y++) { // We use ToArgb because Colors are compared on more than their ARGB values - see Color class documentation on MSDN colors.Add(fastBitmap.GetPixel(x, y).ToArgb()); } } fastBitmap.Unlock(); return colors.Count; }
private FastBitmap ApplyMask(byte[] mask, FastBitmap img) { Bitmap result = new Bitmap(img.Width, img.Height); FastBitmap fb = new FastBitmap(result); fb.LockBits(); img.LockBits(); int n = 0; for (int j = 0; j < img.Height; j++) { for (int i = 0; i < img.Width; i++) { if (mask[n] > VibeModel.BackgroundByte) { fb.SetPixel(i, j, img.GetPixel(i, j)); } n++; } } img.UnlockBits(); fb.UnlockBits(); return fb; }