Sign in to follow this  
kloffy

Image filters using Tao.Sdl and SDL.NET

Recommended Posts

I was wondering how to use the convolution filters from sdl-gfx in SDL.NET. Here's what I've got:
Surface source = new Surface(new Size(100, 100));

// Blit stuff here

short[] kernel =  { 0, 1, 0, 1, 1, 1, 0, 1, 0 };

byte[] src = ???;
byte[] dst = new byte[src.Length];

SdlGfx.SDL_imageFilterConvolveKernel3x3Divide(src, dst, 3, 3, kernel, 1);

Surface result = new Surface(dst);
So, right now I'm having two questions here: First, how do I get the byte[] from the Surface? (Maybe there is a way of getting it using "source.Pixels"...) Second, am I using SDL_imageFilterConvolveKernel correctly? (To be honest I couldn't make much sense out of the information on the SDL_gfx Library page...)

Share this post


Link to post
Share on other sites
I just realized that the SDL_imageFilterConvolveKernel functions need MMX, so I wrote my own convolution filter. However, it is not optimized and ergo pretty slow. I do not recommend using it.

Edit: Updated code. Still horribly slow...

private static float Weight(short[,] kernel)
{
float weight = 0;

for (int ki = 0; ki < kernel.GetLength(0); ki++)
{
for (int kj = 0; kj < kernel.GetLength(1); kj++)
{
weight += kernel[ki, kj];
}
}

return 1/(float)Math.Max(1, Math.Abs(weight));
}

public static Color GetColor(Surface s, int x, int y)
{
//IntPtr ptr = new IntPtr(s.Pixels.ToInt32() + y * s.Pitch + x * s.BytesPerPixel);

//return s.GetColor(Marshal.ReadInt32(ptr));

return s.GetPixel(new Point(x, y));
}

public static void SetColor(Surface s, int x, int y, Color c)
{
//IntPtr ptr = new IntPtr(s.Pixels.ToInt32() + y * s.Pitch + x * s.BytesPerPixel);

//Marshal.WriteInt32(ptr, s.GetColorValue(c));

s.Draw(new Point(x, y), c);
}

public static Surface Convolve(Surface src, short[,] kernel)
{
return Convolve(src, kernel, Weight(kernel));
}

public static Surface Convolve(Surface src, short[,] kernel, float weight)
{
Surface dst = new Surface(src);

int kw = kernel.GetLength(1);
int kh = kernel.GetLength(0);

int ox = kw / 2, ux = (kw + 1) / 2;
int oy = kh / 2, uy = (kh + 1) / 2;

for (int y = oy; y < src.Height - uy; y++)
{
for (int x = ox; x < src.Width - ux; x++)
{
int r = 0, g = 0, b = 0, a = 0;

for (int kj = 0; kj < kh; kj++)
{
for (int ki = 0; ki < kw; ki++)
{
Color c = GetColor(src, x + ki - ox, y + kj - oy);

int w = kernel[kj, ki];

r += w * c.R;
g += w * c.G;
b += w * c.B;
a += w * c.A;
}
}

r = Bound((int)(r * weight), 0, 255);
g = Bound((int)(g * weight), 0, 255);
b = Bound((int)(b * weight), 0, 255);
a = Bound((int)(a * weight), 0, 255);

SetColor(dst, x, y, Color.FromArgb(a, r, g, b));
}
}

return dst;
}


[Edited by - kloffy on January 1, 2008 9:33:45 AM]

Share this post


Link to post
Share on other sites
Apparently the bottleneck was the polling of the PixelFormat for every GetPixel/SetPixel. For some reason SdlDotNet doesn't buffer the Surface PixelFormat information, so everytime information about the PixelFormat is needed it goes throug the following methods:

        internal Sdl.SDL_Surface SurfaceStruct
{
get
{
if (this.disposed)
{
throw (new ObjectDisposedException(this.ToString()));
}
GC.KeepAlive(this);
return (Sdl.SDL_Surface)Marshal.PtrToStructure(this.Handle,
typeof(Sdl.SDL_Surface));
}
}

internal Sdl.SDL_PixelFormat PixelFormat
{
get
{
if (this.disposed)
{
throw (new ObjectDisposedException(this.ToString()));
}
GC.KeepAlive(this);
return (Sdl.SDL_PixelFormat)Marshal.PtrToStructure(this.SurfaceStruct.format,
typeof(Sdl.SDL_PixelFormat));
}
}


By polling the necessary PixelFormat information only once for the entire filtering process I managed to get down to about 1/7 of the original time it took to run the filter.

        private struct Format
{
public short pitch;
public byte bpp;

public int ashift;
public int rshift;
public int gshift;
public int bshift;

public Format(Surface surface)
{
this.pitch = surface.Pitch;
this.bpp = surface.BytesPerPixel;

this.ashift = surface.AlphaShift;
this.rshift = surface.RedShift;
this.gshift = surface.GreenShift;
this.bshift = surface.BlueShift;
}
}

private static float Weight(short[,] kernel)
{
float weight = 0;

for (int ki = 0; ki < kernel.GetLength(0); ki++)
{
for (int kj = 0; kj < kernel.GetLength(1); kj++)
{
weight += kernel[ki, kj];
}
}

return 1/(float)Math.Max(1, Math.Abs(weight));
}

private static Color GetColor(Surface s, Format format, int x, int y)
{
IntPtr ptr = new IntPtr(s.Pixels.ToInt32() + y * format.pitch + x * format.bpp);

int col = Marshal.ReadInt32(ptr);

int a = (col >> format.ashift) & 0xFF;
int r = (col >> format.rshift) & 0xFF;
int g = (col >> format.gshift) & 0xFF;
int b = (col >> format.bshift) & 0xFF;

return Color.FromArgb(a, r, g, b);
}

private static void SetColor(Surface s, Format format, int x, int y, Color c)
{
IntPtr ptr = new IntPtr(s.Pixels.ToInt32() + y * format.pitch + x * format.bpp);

int col =
c.A << format.ashift |
c.R << format.rshift |
c.G << format.gshift |
c.B << format.bshift;

Marshal.WriteInt32(ptr, col);
}

public static Surface Convolve(Surface src, short[,] kernel)
{
return Convolve(src, kernel, Weight(kernel));
}

public static Surface Convolve(Surface src, short[,] kernel, float weight)
{
Surface dst = new Surface(src);

Format srcFormat = new Format(src);
Format dstFormat = new Format(dst);

int kw = kernel.GetLength(1);
int kh = kernel.GetLength(0);

int ox = kw / 2, ux = (kw + 1) / 2;
int oy = kh / 2, uy = (kh + 1) / 2;

for (int y = oy; y < src.Height - uy; y++)
{
for (int x = ox; x < src.Width - ux; x++)
{
int r = 0, g = 0, b = 0, a = 0;

for (int kj = 0; kj < kh; kj++)
{
for (int ki = 0; ki < kw; ki++)
{
Color c = GetColor(src, srcFormat, x + ki - ox, y + kj - oy);

int w = kernel[kj, ki];

r += w * c.R;
g += w * c.G;
b += w * c.B;
a += w * c.A;
}
}

r = Bound((int)(r * weight), 0, 255);
g = Bound((int)(g * weight), 0, 255);
b = Bound((int)(b * weight), 0, 255);
a = Bound((int)(a * weight), 0, 255);

SetColor(dst, dstFormat, x, y, Color.FromArgb(a, r, g, b));
}
}

return dst;
}

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this