Krythic Krythic - 1 month ago 17
C# Question

C# Blur Function Not Working

I am trying to implement a blur function that I found online, but for some reason it's drawing the image completely white. I'm pretty sure that the original snippet was not exactly functional to begin with, thus the error.

Here is the simplest form of the code:

public void Blur(int blurSize)
{
// look at every pixel in the blur rectangle
for (int xx = 0; xx < this.Width; xx++)
{
for (int yy = 0; yy < this.Height; yy++)
{
float avgR = 0;
float avgG = 0;
float avgB = 0;
float avgA = 0;
int blurPixelCount = 0;
// average the color of the red, green and blue for each pixel in the
// blur size while making sure you don't go outside the image bounds
for (int x = xx; (x < xx + blurSize && x < this.Width); x++)
{
for (int y = yy; (y < yy + blurSize && y < this.Height); y++)
{
Color pixel = this.GetPixel(x, y);
avgR += pixel.R;
avgG += pixel.G;
avgB += pixel.B;
avgA += pixel.A;
blurPixelCount++;
}
}
avgR = avgR / blurPixelCount;
avgG = avgG / blurPixelCount;
avgB = avgB / blurPixelCount;
avgA = avgA / blurPixelCount;
// now that we know the average for the blur size, set each pixel to that color
for (int x = xx; x < xx + blurSize && x < this.Width; x++)
{
for (int y = yy; y < yy + blurSize && y < this.Height; y++)
{
SetPixel(x, y, new Color(avgR, avgG, avgB, avgA));
}
}
}
}
}


As an example, here is an image before blur:enter image description here

and here it is after the 'attempted' blur(I changed the background to black because of Stackoverflow being white):
enter image description here

I noticed that if blurSize is anything greater than 1, the image just a white smear, like this (using blurSize 2, and background transparency made black by me.):

enter image description here

As you can imagine, anything higher than 2 creates a pure white image. Just in case it's not obvious, this is the effect that I am aiming for:

enter image description here

My question is quite simple, can anyone spot what's wrong with this blurring function?

Update:

As requested, here are is my custom bitmap class:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using GrimoireEngine.Framework.Maths;
using Point = GrimoireEngine.Framework.Maths.Point;
using Rectangle = GrimoireEngine.Framework.Maths.Rectangle;

namespace GrimoireEngine.Framework.Utilities
{
public class GrimoireBitmap
{
public Bitmap Source;
private IntPtr _iptr = IntPtr.Zero;
public BitmapData BitmapData;

public byte[] Pixels { get; set; }
public int Depth { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }

public Color this[int x, int y]
{
get { return GetPixel(x, y); }
set { SetPixel(x, y, value); }
}

public Color this[Point point]
{
get { return GetPixel(point); }
set { SetPixel(point, value); }
}

public bool IsLocked { get; private set; }

public GrimoireBitmap(int width, int height)
{
this.Source = new Bitmap(width, height);
}

public GrimoireBitmap(int width, int height, PixelFormat format)
{
this.Source = new Bitmap(width, height, format);
}

public GrimoireBitmap(Bitmap image)
{
this.Source = image;
}

public GrimoireBitmap(string file)
{
this.Source = new Bitmap(file);
}

/// <summary>
/// Lock bitmap data
/// </summary>
public void LockBits()
{
Width = Source.Width;
Height = Source.Height;
int pixelCount = Width * Height;
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, Width, Height);
Depth = Bitmap.GetPixelFormatSize(Source.PixelFormat);
if (Depth != 8 && Depth != 24 && Depth != 32)
{
throw new ArgumentException("Only 8, 24 and 32 bpp images are supported.");
}
BitmapData = Source.LockBits(rect, ImageLockMode.ReadWrite, Source.PixelFormat);
int step = Depth / 8;
Pixels = new byte[pixelCount * step];
_iptr = BitmapData.Scan0;
Marshal.Copy(_iptr, Pixels, 0, Pixels.Length);
this.IsLocked = true;
}

/// <summary>
/// Unlock bitmap data
/// </summary>
public void UnlockBits()
{
Marshal.Copy(Pixels, 0, _iptr, Pixels.Length);
Source.UnlockBits(BitmapData);
this.IsLocked = false;
}

/// <summary>
/// Get the color of the specified pixel
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public Color GetPixel(int x, int y)
{
Color clr = Color.Transparent;
int cCount = Depth / 8;
int i = ((y * Width) + x) * cCount;

if (i > Pixels.Length - cCount)
throw new IndexOutOfRangeException();

if (Depth == 32) // For 32 bpp get Red, Green, Blue and Alpha
{
byte b = Pixels[i];
byte g = Pixels[i + 1];
byte r = Pixels[i + 2];
byte a = Pixels[i + 3]; // a
clr = Color.FromNonPremultiplied(a, r, g, b);
}
if (Depth == 24) // For 24 bpp get Red, Green and Blue
{
byte b = Pixels[i];
byte g = Pixels[i + 1];
byte r = Pixels[i + 2];
clr = Color.FromNonPremultiplied(r, g, b);
}
if (Depth == 8) // For 8 bpp get color value (Red, Green and Blue values are the same)
{
byte c = Pixels[i];
clr = Color.FromNonPremultiplied(c, c, c);
}
return clr;
}

/// <summary>
///
/// </summary>
/// <param name="point"></param>
/// <returns></returns>
public Color GetPixel(Point point)
{
return GetPixel(point.X, point.Y);
}

/// <summary>
/// Set the color of the specified pixel
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="color"></param>
public void SetPixel(int x, int y, Color color)
{
int i = (((y * Width) + x) * (Depth / 8));
if (Depth == 32) // For 32 bpp set Red, Green, Blue and Alpha
{
Pixels[i] = color.B;
Pixels[i + 1] = color.G;
Pixels[i + 2] = color.R;
Pixels[i + 3] = color.A;
}
if (Depth == 24) // For 24 bpp set Red, Green and Blue
{
Pixels[i] = color.B;
Pixels[i + 1] = color.G;
Pixels[i + 2] = color.R;
}
if (Depth == 8) // For 8 bpp set color value (Red, Green and Blue values are the same)
{
Pixels[i] = color.B;
}
}

/// <summary>
///
/// </summary>
/// <param name="point"></param>
/// <param name="color"></param>
public void SetPixel(Point point, Color color)
{
SetPixel(point.X, point.Y, color);
}

/// <summary>
///
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="color"></param>
public void FillRectangle(int x, int y, int width, int height, Color color)
{
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
SetPixel(i + x, j + y, color);
}
}
}

/// <summary>
///
/// </summary>
/// <param name="rectangle"></param>
/// <param name="color"></param>
public void FillRectangle(Rectangle rectangle, Color color)
{
FillRectangle(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, color);
}

/// <summary>
///
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="bitmap"></param>
public void DrawBitmap(int x, int y, GrimoireBitmap bitmap)
{
for (int i = 0; i < bitmap.Width; i++)
{
for (int j = 0; j < bitmap.Height; j++)
{
SetPixel(x + i, y + j, bitmap.GetPixel(i, j));
}
}
}

/// <summary>
///
/// </summary>
/// <param name="color"></param>
public void Fill(Color color)
{
FillRectangle(0, 0, this.Width, this.Height, color);
}

/// <summary>
///
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
public GrimoireBitmap CopyRegion(int x, int y, int width, int height)
{
GrimoireBitmap bitmap = new GrimoireBitmap(width, height);
bitmap.LockBits();
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
bitmap.SetPixel(i, j, this.GetPixel(x + i, y + j));
}
}
bitmap.UnlockBits();
return bitmap;
}

/// <summary>
///
/// </summary>
/// <param name="rectangle"></param>
/// <returns></returns>
public GrimoireBitmap CopyRegion(Rectangle rectangle)
{
return CopyRegion(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
}

/// <summary>
///
/// </summary>
public void Clear()
{
for (int i = 0; i < this.Width; i++)
{
for (int j = 0; j < this.Height; j++)
{
SetPixel(i, j, Color.Transparent);
}
}
}

/// <summary>
///
/// </summary>
/// <param name="fileName"></param>
public void Save(string fileName)
{
this.Source.Save(fileName);
}

/// <summary>
///
/// </summary>
/// <param name="fileName"></param>
public void Load(string fileName)
{
this.Source = new Bitmap(fileName);
}

/// <summary>
///
/// </summary>
/// <returns></returns>
public Bitmap ToBitmap()
{
return this.Source;
}

/// <summary>
///
/// </summary>
/// <param name="image"></param>
public void FromBitmap(Bitmap image)
{
this.Source = image;
}

/// <summary>
///
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="color"></param>
/// <param name="amount"></param>
public void Blend(int x, int y, int width, int height, Color color, float amount)
{
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
SetPixel(i + x, j + y, Color.Blend(GetPixel(i + x, j + y), color, amount));
}
}
}

/// <summary>
///
/// </summary>
/// <param name="rectangle"></param>
/// <param name="color"></param>
/// <param name="amount"></param>
public void Blend(Rectangle rectangle, Color color, float amount)
{
Blend(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, color, amount);
}

public void Blur(int blurSize)
{
// look at every pixel in the blur rectangle
for (int xx = 0; xx < this.Width; xx++)
{
for (int yy = 0; yy < this.Height; yy++)
{
float avgR = 0;
float avgG = 0;
float avgB = 0;
float avgA = 0;
int blurPixelCount = 0;
// average the color of the red, green and blue for each pixel in the
// blur size while making sure you don't go outside the image bounds
for (int x = xx; (x < xx + blurSize && x < this.Width); x++)
{
for (int y = yy; (y < yy + blurSize && y < this.Height); y++)
{
Color pixel = this.GetPixel(x, y);
avgR += pixel.R;
avgG += pixel.G;
avgB += pixel.B;
avgA += pixel.A;
blurPixelCount++;
}
}
avgR = avgR / blurPixelCount;
avgG = avgG / blurPixelCount;
avgB = avgB / blurPixelCount;
avgA = avgA / blurPixelCount;
// now that we know the average for the blur size, set each pixel to that color
for (int x = xx; x < xx + blurSize && x < this.Width; x++)
{
for (int y = yy; y < yy + blurSize && y < this.Height; y++)
{
SetPixel(x, y, new Color(avgR, avgG, avgB, avgA));
}
}
}
}
}
}
}

Answer

Edit: I modified the code to use System.Drawing.Bitmap. It works fine as it is, here is the result with Blur=5:

enter image description here

Here is the code:

   public void Blur(int blurSize, Bitmap input)
    {
        // look at every pixel in the blur rectangle
        for (int xx = 0; xx < input.Width; xx++)
        {
            for (int yy = 0; yy < input.Height; yy++)
            {
                float avgR = 0;
                float avgG = 0;
                float avgB = 0;
                float avgA = 0;
                int blurPixelCount = 0;
                // average the color of the red, green and blue for each pixel in the
                // blur size while making sure you don't go outside the image bounds
                for (int x = xx; (x < xx + blurSize && x < input.Width); x++)
                {
                    for (int y = yy; (y < yy + blurSize && y < input.Height); y++)
                    {
                        Color pixel = input.GetPixel(x, y);
                        avgR += pixel.R;
                        avgG += pixel.G;
                        avgB += pixel.B;
                        avgA += pixel.A;
                        blurPixelCount++;
                    }
                }
                avgR = avgR / blurPixelCount;
                avgG = avgG / blurPixelCount;
                avgB = avgB / blurPixelCount;
                avgA = avgA / blurPixelCount;
                // now that we know the average for the blur size, set each pixel to that color
                for (int x = xx; x < xx + blurSize && x < input.Width; x++)
                {
                    for (int y = yy; y < yy + blurSize && y < input.Height; y++)
                    {
                        input.SetPixel(x, y, Color.FromArgb((int)avgA, (int)avgR, (int)avgG, (int)avgB));
                    }
                }
            }
        }
    }

So one of the methods such as SetPixel or GetPixel or new Color is not working properly.