In a simplified form, you can use context.Response.BinaryWrite()to write an array of bytes to the response stream. Usually you call this from a custom HTTP handler that maps to a particular mime type - ie * .jpg or * .png, etc.
The following block of code shows one way to create a simple capcha image and returns it as an array of bytes that can be used context.Response.BinaryWrite()
private Byte[] GenerateImage()
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
Byte[] rand = new Byte[200];
rng.GetBytes(rand);
int i = 0;
Bitmap bmp = new Bitmap(_imageWidth, _imageHeight, PixelFormat.Format24bppRgb);
Bitmap cloneBmp = null;
Graphics g = null;
LinearGradientBrush backgroundBrush = null;
LinearGradientBrush textBrush = null;
SolidBrush[] circleBrush = new SolidBrush[3];
Font font = null;
GraphicsPath path = null;
try
{
g = Graphics.FromImage(bmp);
g.SmoothingMode = SmoothingMode.AntiAlias;
Rectangle r = new Rectangle(0, 0, _imageWidth, _imageHeight);
backgroundBrush = new LinearGradientBrush(
new RectangleF(0, 0, _imageWidth, _imageHeight),
Color.FromArgb(rand[i++] / 2 + 128, rand[i++] / 2 + 128, 255),
Color.FromArgb(255, rand[i++] / 2 + 128, rand[i++] / 2 + 128),
rand[i++] * 360 / 256);
g.FillRectangle(backgroundBrush, r);
for (int br = 0; br < circleBrush.Length; br++)
{
circleBrush[br] = new SolidBrush(Color.FromArgb(128, rand[i++], rand[i++], rand[i++]));
}
for (int circle = 0; circle < 30; circle++)
{
int radius = rand[i++] % 10;
g.FillEllipse(circleBrush[circle % 2],
rand[i++] * _imageWidth / 256,
rand[i++] * _imageHeight / 256,
radius, radius);
}
font = new Font("Tahoma", _imageHeight / 2, FontStyle.Regular);
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
path = new GraphicsPath();
path.AddString(_challengeKey, font.FontFamily, (int)font.Style, font.Size, r, format);
textBrush = new LinearGradientBrush(
new RectangleF(0, 0, _imageWidth, _imageHeight),
Color.FromArgb(rand[i] % 128, rand[i] % 128, rand[i++] % 128),
Color.FromArgb(rand[i] % 128, rand[i] % 128, rand[i++] % 128),
rand[i++] * 360 / 256);
g.FillPath(textBrush, path);
cloneBmp = (Bitmap)bmp.Clone();
int distortionSeed = rand[i++];
double distortion = distortionSeed > 128 ? 5 + (distortionSeed - 128) % 5 : -5 - distortionSeed % 5;
for (int y = 0; y < _imageHeight; y++)
{
for (int x = 0; x < _imageWidth; x++)
{
int newX = (int)(x + (distortion * Math.Sin(Math.PI * y / 96.0)));
int newY = (int)(y + (distortion * Math.Cos(Math.PI * x / 64.0)));
if (newX < 0 || newX >= _imageWidth)
{
newX = 0;
}
if (newY < 0 || newY >= _imageHeight)
{
newY = 0;
}
bmp.SetPixel(x, y, cloneBmp.GetPixel(newX, newY));
}
}
MemoryStream stream = new MemoryStream();
bmp.Save(stream, ImageFormat.Jpeg);
return stream.ToArray();
}
finally
{
if (backgroundBrush != null)
{
backgroundBrush.Dispose();
}
if (textBrush != null)
{
textBrush.Dispose();
}
for (int br = 0; br < circleBrush.Length; br++)
{
if (circleBrush[br] != null)
{
circleBrush[br].Dispose();
}
}
if (font != null)
{
font.Dispose();
}
if (path != null)
{
path.Dispose();
}
if (g != null)
{
g.Dispose();
}
if (bmp != null)
{
bmp.Dispose();
}
if (cloneBmp != null)
{
cloneBmp.Dispose();
}
}
}
Here is what your http handler might look like. Note that this is very simple and will do the job, but does not include image caching code for the client or server side that you want to ultimately add to the performance.
public class ImageHandler : IHttpHandler
{
public bool IsReusable
{
get
{
return true;
}
}
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = MimeTypeConstants.JPG;
context.Response.Clear();
context.Response.BinaryWrite(GenerateImage());
context.Response.End();
}
}
Enjoy it!