Image manipulation in C# WPF/Silverlight
March 7, 2010 1 Comment
In my previous post, I demonstrated how to manipulate an image in WPF – the editable bitmap class is called WriteableImage. There is a class with the same name in Silverlight, but unfortunately, they’re not really similar. I’ve extended my extensions to also work under Silverlight – so that the same code can be used in C#/WPF and Silverlight.
The following helper class works the same in C#/WPF and in Silverlight;
using System;
using System.Windows;
using System.Windows.Media.Imaging;
namespace ImageEvolvatron.Core
{
public static class WriteableBitmapHelper
{
#if SILVERLIGHT
public static WriteableBitmap CreateBitmap(int width, int height)
{
return new WriteableBitmap(width, height);
}
public static void SetEachPixelColour(
this WriteableBitmap bitmap,
Func<Point, PixelColor> colourFunc)
{
Point point = new Point(0, 0);
int[] pixels = bitmap.Pixels;
int pixelPos = 0;
for (int y = 0; y < bitmap.PixelHeight; y++)
{
point.Y = y;
for (int x = 0; x < bitmap.PixelWidth; x++)
{
point.X = x;
PixelColor color = colourFunc(point);
pixels[pixelPos++] = color.AsInt;
}
}
}
#else
public static WriteableBitmap CreateBitmap(int width, int height)
{
return new WriteableBitmap(
width,
height,
96,
96,
System.Windows.Media.PixelFormats.Bgr32,
null);
}
public static void SetEachPixelColour(
this WriteableBitmap bitmap,
Func<Point, PixelColor> colourFunc)
{
// See http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.writeablebitmap.aspx
unsafe
{
// Get a pointer to the back buffer.
bitmap.Lock();
Point point = new Point(0, 0);
int backBuffer = (int)bitmap.BackBuffer;
for (int y = 0; y < bitmap.PixelHeight; y++)
{
point.Y = y;
for (int x = 0; x < bitmap.PixelWidth; x++)
{
point.X = x;
// Find the address of the pixel to draw.
PixelColor color = colourFunc(point);
// Assign the color data to the pixel.
*((int*)backBuffer) = color.AsInt;
// Step to the next pixel - 4 bytes per pixel
backBuffer += 4;
}
}
// The entire bitmap has been regenerated
bitmap.AddDirtyRect(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight));
bitmap.Unlock();
}
}
#endif
}
}
The PixelColor source;
using System.Runtime.InteropServices;
namespace ImageEvolvatron.Core
{
[StructLayout(LayoutKind.Sequential)]
public struct PixelColor
{
public byte Blue;
public byte Green;
public byte Red;
public byte Alpha;
public static PixelColor FromArgb(byte red, byte green, byte blue)
{
return
new PixelColor
{
Blue = blue,
Red = red,
Green = green,
Alpha = 255
};
}
public int AsInt
{
get
{
return
Alpha << 24 // Alpha
| Red << 16 // R
| Green << 8 // G
| Blue << 0; // B
}
}
}
}
This code, either in Silverlight or C#/WPF, works to produce the image shown;
Actual Silverlight/C# code:
public static BitmapSource ShowDistanceFromCenter(int width, int height)
{
Point imageCenter = new Point(width / 2.0, height / 2.0);
WriteableBitmap bitmap = WriteableBitmapHelper.CreateBitmap(width, height);
double maxDistance = Distance(imageCenter.X, imageCenter.Y);
bitmap.SetEachPixelColour(
point =>
{
double dist = Distance(
point.X - imageCenter.X,
point.Y - imageCenter.Y);
byte core = Convert.ToByte(255 * dist / maxDistance);
PixelColor color = PixelColor.FromArgb(core, core, core);
if (Math.Round(point.X) == Math.Round(imageCenter.X))
{
color.Red = (byte)(255 - color.Red);
}
if (Math.Round(point.Y) == Math.Round(imageCenter.Y))
{
color.Blue = (byte)(255 - color.Blue);
}
return color;
});
return bitmap;
}
private static double Distance(double dx, double dy)
{
return Math.Sqrt((dx * dx) + (dy * dy));
}
Pingback: 2010 in review « Mattias Fagerlund's Coding Blog