search
Twitter Rss Feeds
MicrosoftArticlesForumsGroups
C# .NET
VB.NET
Visual Studio .NET
ADO.NET
Xml/Xslt
VB 6.0
.NET CF
GDI+
LINQ
Deployment
Security
FoxPro
Silverlight / WPF
Entity Framework
RIA Services

Web ProgrammingArticlesForumsGroups
JavaScript
ASP
ASP.NET
Web Services

Non-MicrosoftArticlesForumsGroups
NHibernate
Perl
PHP
Ruby
Java
Linux / Unix
Apple
Open Source

DatabasesArticlesForumsGroups
SQL Server
Access
Oracle
MySQL
Other Databases

OfficeArticlesForumsGroups
Microsoft Excel
Microsoft Word
Microsoft Powerpoint
Publisher
Money

Operating SystemsArticlesForumsGroups
Windows 7
Windows Server
Windows Vista
Windows XP
Windows Update
MAC
Linux / UNIX

Server PlatformsArticlesForumsGroups
Share Point
BizTalk
Site Server
Exhange Server
IIS
Transaction Server

Graphic DesignArticlesForumsGroups
Macromedia Flash
Adobe PhotoShop
Microsoft Expression

OtherArticlesForumsGroups
Subversion / CVS
Ask Dr. Dotnetsky
Active Directory
Networking
Uninstall Virus
Job Openings
Reviews
Search Engines
Resumes

 
Create HTML Image Maps Dynamically With .NET

By Robbe D. Morris

Printer Friendly Version


Robbe & Melisa Morris
I've been working on a financial reporting project lately and came upon the idea of bringing our charts from ChartFX.NET to life.  The idea being that we could dynamically review each pixel's color in the chart in order to generate an HTML image map on the fly.  This would enable us to apply supporting documentation in the form of links or JavaScript functions to perform some sort of action when the user moves their mouse over the chart or clicks a section of it.
.NET offers GDI+ as its tool for reviewing and manipulating images.  We can also implement unsafe code to work with images.  Since we try to avoid code marked as unsafe here, I opted to just use the Bitmap object's .GetPixel and .SetPixel methods instead.  If you have the option to implement unsafe code, you'll want to look into .LockBits and .UnLockBits.  These two methods are known to be faster for working with images.
In a nutshell, the code sample (formatted nicely for reading) below takes in an ArrayList of structs, an image tag element name, and the relative filename of the image.  The struct holds the red, green, and blue desired colors to find in the chart/image and generate the proper area tag.  My first attempt at this was to generate an area tag for each and every pixel.  Naturally, this created a huge amount of HTML for the larger images (400x400) that I was testing with.  The next logical step was to try and reduce the number of tags written by determining areas on a line by line basis in the 400 x 400 grid of pixels.
The code below iterates through the ArrayList of structs, reviews the desired color, searches line by line and pixel by pixel looking for color matches.  If it finds the desired color, it sets a marker and increments that pixel marker until it runs across a different pixel color in the image.  When this occurs, it writes the area tag with that line's coordinates and sets a new marker.  I chose the line by line method of iterating through the pixels in order to support very complex images typically generated from charting software.
For my charts and stress testing, this proved to work pretty well.  You'll find that the slowest part of this process is the .GetPixel method itself.  This is why I make one pass through to collect the colors of the image and store it in a multi-dimensional array sized to match the image height and width.  Then, reference the array by the pixel index each time we process a desired color.
If you would like to see a live demo that process two different 400 x 400 images, click here: Live Demo (opens in new window).  Move your mouse over the various colors and shapes to see the URLs associated with them as well as a JavaScript message box.
As always, we welcome your comments or questions via our message board.  Please take a moment to rate this article.  Rate Article (opens in new window).

ImageMaps.cs
  
 
using System;
using System.Data;
using System.Collections; 
using System.Drawing;
using System.Web;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Text; 

namespace EggHeadCafe.Controls
{
 
 #region Structs
  public struct UrlsForImageColor
  {
    public string Red;
    public string Green;
    public string Blue;
    public string Url;
    public string AltTag;
    public string Target;
    public string OnMouseOver;
  }
 #endregion
 
 public class ImageMaps
 {
 
   public ImageMaps()
   {

   }

   #region Get Map
   public string GetMap(string ImageElementName,string ImageFileName,ArrayList LinksByColor)
   {
     string sRet="";
     string Coords="";
     string CurColor="";
     string CompareColor="";
     bool fFoundColorInPixel=false;
     int hplus=0;
     int Start=0;
     int End=0;
     int y=0;
     int x=0;
     int u=0;

      Color pixelColor;

      StringBuilder sb = new StringBuilder();
		 
      EggHeadCafe.Controls.UrlsForImageColor oLink;

      Bitmap oImage = null;
		
      try
      {
		   
        ImageFileName = System.Web.HttpContext.Current.Server.MapPath(ImageFileName);	 
        oImage = new Bitmap(System.Drawing.Image.FromFile(ImageFileName));
        y = oImage.Height;
        x = oImage.Width;

        string[,] ImagePixels = new string[y,x];

        for(int h = 0; h < y; h++)
        {		 
          for(int w = 0; w < x; w++)
          {
            pixelColor = oImage.GetPixel(h,w);	    
            ImagePixels[h,w] = pixelColor.R.ToString() + "." + pixelColor.G.ToString() + "." + pixelColor.B.ToString();
          }
        }

        sb.Append("<img name=\"" + ImageElementName + "\" src=\"" + ImageFileName + "\" width=\"" + x.ToString());
        sb.Append("\" height=\"" + y.ToString() + "\" border=\"0\" usemap=\"#m_" + ImageElementName + "2\">" + "\n");
        sb.Append("<map name=\"m_" + ImageElementName + "2\">" + "\n");
 
        for(u = 0; u < LinksByColor.Count; u++)
        {
					
          oLink = (EggHeadCafe.Controls.UrlsForImageColor)LinksByColor[u];
					
          CompareColor = oLink.Red + "." +  oLink.Green + "." + oLink.Blue;
 	 
          for(int h = 0; h < y; h++)
          {
						 
            for(int w = 0; w < x; w++)
            {
							
             if (w==0) { Start = w; }
 
             CurColor = ImagePixels[h,w];

             if (CurColor != CompareColor)
             {
								
               if (fFoundColorInPixel == true)
               {
                 Coords = h.ToString() + ","; 
                 Coords += Start.ToString() + ",";
                 Coords += hplus.ToString() + ",";
                 Coords += End.ToString();
                 sb.Append(WriteArea(Coords,oLink));
               }

               Start = w;
               fFoundColorInPixel = false;
               continue;
             }
             else
             {
               fFoundColorInPixel = true;
             }

             End = w;
             hplus = h + 1;

            } // End FOR width

            if (fFoundColorInPixel == true)
             {
               Coords = h.ToString() + ",";
               Coords += Start.ToString() + ",";
               Coords += hplus.ToString() + ",";
               Coords += End.ToString();
               sb.Append(WriteArea(Coords,oLink));
             }

            }  
          }  
 
          sb.Append("</map>" + "\n");
				
			 
          }
          catch (Exception err) 
          {
            throw;
          }
          finally
          {
            sRet = sb.ToString();
            oImage.Dispose();
          }
	  return sRet;
}
		#endregion
 

 #region Write Area
  private string WriteArea(string Coords,EggHeadCafe.Controls.UrlsForImageColor oLink)
  {
    string sRet="";
    sRet = "<area shape=\"rect\" coords=\"" + Coords + "\"";
    if (oLink.Url.Length > 0) { sRet += " href=\"" + oLink.Url + "\" "; }
    if (oLink.AltTag.Length > 0) { sRet += " alt=\"" + oLink.AltTag + "\""; }
    if (oLink.Target.Length > 0) { sRet += " target=\"" + oLink.Target + "\""; }
    if (oLink.OnMouseOver.Length > 0) { sRet += " onmouseover=\"" + oLink.OnMouseOver + "\""; }
    sRet += ">\n";
    return sRet;
  }
 #endregion

  }
}


ImageMaps.aspx
  
<%@ Import Namespace="System.Drawing" %>
<%@ Import Namespace="System.Drawing.Drawing2D" %>
<%@ Import Namespace="System.Drawing.Imaging" %>
<%@ Import Namespace="EggHeadCafe.Controls" %>

<script Language="C#" runat="server">
 
 protected void Page_Load(object sender, EventArgs e)
 {
   EggHeadCafe.Controls.ImageMaps oMap = new EggHeadCafe.Controls.ImageMaps();
   ArrayList Links = new ArrayList();
   EggHeadCafe.Controls.UrlsForImageColor oLink; 
 
   try
   {

     oLink = new EggHeadCafe.Controls.UrlsForImageColor();
     oLink.Red = "255";
     oLink.Green = "0";
     oLink.Blue = "0";
     oLink.Url = "http://www.eggheadcafe.com";
     oLink.AltTag = "my red region";
     oLink.Target = "_blank";
     oLink.OnMouseOver = "";
     oLinks.Add(oLink); 

     oLink = new EggHeadCafe.Controls.UrlsForImageColor();
     oLink.Red = "0";
     oLink.Green = "0";
     oLink.Blue = "0";
     oLink.Url = "http://www.robbemorris.com";
     oLink.AltTag = "my black region";
     oLink.Target = "_blank";
     oLink.OnMouseOver = "";
     Links.Add(oLink); 
                
     Response.Write(oMap.GetMap("Image1","images/pie.gif",Links)); 

    Links.Clear();
 			 
     oLink = new EggHeadCafe.Controls.UrlsForImageColor();
     oLink.Red = "0";
     oLink.Green = "0";
     oLink.Blue = "204";
     oLink.Url = "http://www.eggheadcafe.com";
     oLink.AltTag = "blue region";
     oLink.Target = "_blank";
     oLink.OnMouseOver = "alert('over blue region');";
     Links.Add(oLink); 

     oLink = new EggHeadCafe.Controls.UrlsForImageColor();
     oLink.Red = "255";
     oLink.Green = "0";
     oLink.Blue = "255";
     oLink.Url = "http://www.asp.net";
     oLink.AltTag = "pink region";
     oLink.Target = "_blank";
     oLink.OnMouseOver = "";
     Links.Add(oLink); 

     oLink = new EggHeadCafe.Controls.UrlsForImageColor();
     oLink.Red = "102";
     oLink.Green = "0";
     oLink.Blue = "153";
     oLink.Url = "http://www.yahoo.com";
     oLink.AltTag = "purple region";
     oLink.Target = "_blank";
     oLink.OnMouseOver = "";
     Links.Add(oLink); 

     oLink = new EggHeadCafe.Controls.UrlsForImageColor();
     oLink.Red = "204";
     oLink.Green = "255";
     oLink.Blue = "0";
     oLink.Url = "http://www.google.com";
     oLink.AltTag = "yellow";
     oLink.Target = "_blank";
     oLink.OnMouseOver = "";
     Links.Add(oLink); 

     oLink = new EggHeadCafe.Controls.UrlsForImageColor();
     oLink.Red = "102";
     oLink.Green = "153";
     oLink.Blue = "102";
     oLink.Url = "http://www.rushlimbaugh.com";
     oLink.AltTag = "green region";
     oLink.Target = "_blank";
     oLink.OnMouseOver = "";
     Links.Add(oLink); 

     oLink = new EggHeadCafe.Controls.UrlsForImageColor();
     oLink.Red = "204";
     oLink.Green = "204";
     oLink.Blue = "255";
     oLink.Url = "http://www.foxnews.com";
     oLink.AltTag = "light purple";
     oLink.Target = "_blank";
     oLink.OnMouseOver = "";
     Links.Add(oLink); 
 
     Response.Write(oMap.GetMap("Image2","images/region.gif",Links));
 
   }
     catch (Exception err) { Response.Write(err.Message); }

  }
          
</script>

Robbe has been a Microsoft MVP in C# since 2004.  He is also the co-founder of EggHeadCafe which provides .NET articles, book reviews, software reviews, and software download and purchase advice.


Pete's Blog   |    Pete's Resume   |    Robbe's Blog   |    Robbe's Resume   |    Archive #2   |    Archive #3   |    Dotnetslackers   |    XmlPitStop   |    Advertise   |   Contact Us   |   Privacy   |   Copyright (c) 2000 - 2009 eggheadcafe.com  All rights reserved.