user70192
user70192

Reputation: 14214

Writing an Image dynamically

I have three values that are being generated dynamically in the code-behind of my web form. These three values will be shown to the user. While this may sound odd, I do NOT want the user to be able to copy and paste these values. Because of this, I would like to write out the values via an image that is build dynamically.

My question is, is there a way to dynamically create an image and write it out to the response? I would prefer not to create an image, save it to the server, and then pass a url back to the page. I would really like to write out the binary content along with the response. Is this possible? If so, can someone please explain/show how?

Upvotes: 3

Views: 1253

Answers (3)

Doug
Doug

Reputation: 5328

In the simplist form you can use context.Response.BinaryWrite() to write a byte array to the response stream. Typically you would call this from a custom http handler that is mapped to a particular mime type - i.e. *.jpg or *.png etc..

The following block of code shows one way to create a simple capcha image and return it as a byte array which can be consumed by 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++)
      {
        // Adds a simple wave
        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. Please note this is very basic and will get the job done, but does not include any image caching code, for client side or server side, which you will want to eventually add for 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!

Upvotes: 3

Ed B
Ed B

Reputation: 6054

Yes, it's pretty easy to create an image from text:

http://chiragrdarji.wordpress.com/2008/05/09/generate-image-from-text-using-c-or-convert-text-in-to-image-using-c/

Use a generic handler (.ashx) file to output an image. Then link to the handler from your page..and it will show up as an image.

Upvotes: 0

lc.
lc.

Reputation: 116528

As far as sending the binary content inline with the page, you might want to take a look at using data URIs (pros and cons are mentioned on the Wikipedia page).

Upvotes: 0

Related Questions