Click here to Skip to main content
15,879,474 members
Articles / Programming Languages / C#
Article

Create Custom Color Maps in C#

Rate me:
Please Sign up or sign in to vote.
3.39/5 (12 votes)
15 May 20077 min read 112.5K   2.3K   49   8
This article shows how to create various custom color maps

Introduction

In C#, there is a default ColorMap class, defined in the System.Drawing.Imaging namespace. This class defines a mapping between existing colors and the new colors to which they are to be converted. When the map is applied, any pixel of the old color is converted to the new color. This class is useful for image processing applications.

However, in some graphics and chart applications, you may need the custom color maps to achieve specific visual effects. These color maps are simply tables or lists of colors that are organized in some desired fashion. The surface, patch, and image objects can be associated with a custom color map. This article shows you how to create such custom color maps.

Background

To create a custome color map in C#, you need to construct a color map with an m x 4 color map matrix. Each row of this matrix represents ARGB values. The row index can represent the y data of a 2D chart or the height (the z data) of a 3D surface plot. For a given color map matrix with m rows, the color data values can be linearly scaled to the color map.

For example, if you want to use the color map to represent the y coordinates of a 2D graphics object, you can use the YMin and YMax to linearly transform the y data values to indices where each index identifies an ARGB row (i.e., a color) in the color map matrix. The mathematical transformation of the color index values is described by the formula:

Image 1

<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"><formulas /><f eqn="if lineDrawn pixelLineWidth 0"><f eqn="sum @0 1 0"><f eqn="sum 0 0 @1"><f eqn="prod @2 1 2"><f eqn="prod @3 21600 pixelWidth"><f eqn="prod @3 21600 pixelHeight"><f eqn="sum @0 0 1"><f eqn="prod @6 1 2"><f eqn="prod @7 21600 pixelWidth"><f eqn="sum @8 21600 0"><f eqn="prod @7 21600 pixelHeight"><f eqn="sum @10 21600 0"></formulas /><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"><lock aspectratio="t" v:ext="edit"><shape id="_x0000_i1025" style="WIDTH: 252.75pt; HEIGHT: 54.75pt" type="#_x0000_t75"><imagedata src="file:///C:\DOCUME~1\jhxu\LOCALS~1\Temp\msohtml1\01\clip_image001.wmz">

Here y is the individual value of Y data and m is the length of the color map matrix. This allows you to use the entire range of colors in the color map over the plotted data. For 3D graphics objects and 3D surface charts, the y data should be replaced with the z data.

Now we can implement the colormap matrix class. Let's use a custom colormap named "Spring" as an example to illustrate how easily it is to create a custom colormap.

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">using System; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">using System.Drawing; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">using System.Drawing.Drawing2D; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">namespace Example1_8 </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{ </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">public class ColorMap </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{ </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">private int colormapLength = 64; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">private int alphaValue = 255; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">public ColorMap() </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{ </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">} </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">public ColorMap(int colorLength) </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{ </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">colormapLength = colorLength; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">} </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">public ColorMap(int colorLength, int alpha) </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{ </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">colormapLength = colorLength; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">alphaValue = alpha; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">} </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">public int[,] Spring() </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{ </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int[,] cmap = new int[colormapLength, 4]; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">float[] spring = new float[colormapLength]; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">for (int i = 0; i < colormapLength; i++) </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{ </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">spring[i] = 1.0f * i / (colormapLength - 1); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap[i, 0] = alphaValue; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap[i, 1] = 255; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap[i, 2] = (int)(255 * spring[i]); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap[i, 3] = 255 - cmap[i, 1]; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">} </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">return cmap; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">}</p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">......</p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">}</p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">}</p>

In this class, there are three constructors. If you use

ColorMap cm = new ColorMap();

to create a new ColorMap object, the default parameters colormapLength = 64 and alphaValue = 255 will be used. Here colormapLength is the length of the color map matrix and the alphaValue is the color transparency parameter. The default alphaValue of 255 represents an opaque color. The following constructor

ColorMap cm = new ColorMap(32);

overrides the colormapLength with the input parameter 32, and the alphaValue remains the default value of 255. You can override both parameters by calling the ColorMap class with the following code snippet:

COlorMap cm = new ColorMap(32, 100);

This sets colormapLength = 32 and alphaValue = 100.

I have add eight commonly used custom colormaps to the ColorMap class. You can easily add more custom colormaps following the procedure described here.

<h2>Using the code</h2>

The ColorMap class can be used in your C# applications. The follwoing Form1 class demonstrate how to draw various color bars using the ColorMap class. Here is the code snippet of the Form1 class:

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">using System; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">using System.Drawing; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">using System.Windows.Forms; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">namespace Example1_8 </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{ </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">public partial class Form1 : Form </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{ </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">public Form1() </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{ </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">InitializeComponent(); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">SetStyle(ControlStyles.ResizeRedraw, true); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">This.BackColor = Color.White; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">this.Width = 340; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">this.Height = 340; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">} </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">protected override void OnPaint(PaintEventArgs e) </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{ </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">Graphics g = e.Graphics; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int width = 30; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int height = 128; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int y = 10; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">// Create opaque color maps with alpha = 255: </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">ColorMap cm = new ColorMap(); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">Font aFont = new Font("Arial", 20, FontStyle.Bold); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">g.DrawString("OPAQUE COLOR", aFont, Brushes.Black, 10, 60); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10, y, width, height, cm, "Spring"); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 40, y, width, height, cm, "Summer"); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 2 * 40, y, width, height, cm, "Autumn"); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 3 * 40, y, width, height, cm, "Winter"); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 4 * 40, y, width, height, cm, "Jet"); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 5 * 40, y, width, height, cm, "Gray"); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 6 * 40, y, width, height, cm, "Hot"); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 7 * 40, y, width, height, cm, "Cool"); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">y = y + 150; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">// Create transparent color maps with alpha = 150: </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">ColorMap cm1 = new ColorMap(64, 150); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">g.DrawString("TRANSPARENT COLOR", aFont, Brushes.Black, 10, 210); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10, y, width, height, cm1, "Spring"); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 40, y, width, height, cm1, "Summer"); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 2 * 40, y, width, height, cm1, "Autumn"); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 3 * 40, y, width, height, cm1, "Winter"); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 4 * 40, y, width, height, cm1, "Jet"); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 5 * 40, y, width, height, cm1, "Gray"); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 6 * 40, y, width, height, cm1, "Hot"); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">DrawColorBar(g, 10 + 7 * 40, y, width, height, cm1, "Cool"); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">} </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in" />

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">private void DrawColorBar(Graphics g, int x, int y, </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int width, int height, ColorMap map, string str) </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{ </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int[,] cmap = new int[64, 4]; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">switch(str) </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{ </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">case "Jet": </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap = map.Jet(); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">break; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">case "Hot": </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap = map.Hot(); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">break; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">case "Gray": </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap = map.Gray(); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">break; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">case "Cool": </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap = map.Cool(); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">break; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">case "Summer": </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap = map.Summer(); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">break; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">case "Autumn": </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap = map.Autumn(); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">break; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">case "Spring": </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap = map.Spring(); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">break; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">case "Winter": </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap = map.Winter(); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">break; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">} </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int ymin = 0; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int ymax = 32; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int dy = height / (ymax - ymin); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int m = 64; </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">for (int i = 0; i < 32; i++) </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">{ </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int colorIndex = (int)((i - ymin) * </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">m / (ymax - ymin)); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">SolidBrush aBrush = new SolidBrush(Color.FromArgb( </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap[colorIndex, 0], cmap[colorIndex, 1], </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">cmap[colorIndex, 2], cmap[colorIndex, 3])); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">g.FillRectangle(aBrush, x, y + i * dy, width, dy); </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">} </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">} </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">} </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">} </p>

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in" />

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in" />

<p>Inside the DrawColorBar method, we draw a color bar by dividing it into 32 sub-rectangles. We thenassign the y data from 0 to 31. The switch statement selects a specified color map matrix. The following code snippet</p>

<p />

<p class="Code" style="MARGIN: 0in -67.7pt 0pt 0.25in">int colorIndex = (int)((i - ymin) * m / (ymax - ymin)); </p>

<p style="MARGIN: 6pt 0in">computes the index of the color map matrix using the Y data. Then we create a SolidBrush object using this color map matrix.</p>

<p style="MARGIN: 6pt 0in">Inside the OnPaint method, we create two ColorMap objects, cm and cm1. cm uses the default parameters: colormapLength = 64 and alphaValue = 255; i.e. the opaque color. The parameters of cm1 are reassigned to colormapLength = 64 and alphaValue = 150, indicating that the color becomes transparent. </p>

<p style="MARGIN: 6pt 0in">This project produces the output shown in the following screenshot, which shows eight different color maps defined in the ColorMap class.</p>
<img src="Colormap/fig1-18.gif" /> 
<p>This project is from the examples of the new book "Practical C# Charts and Graphics", where you can find more advanced chart and graphics programming for real-world .NET applications. For more information, please visit my website at <a href="http://www.authors.unicadpublish.com/~jack_xu">www.authors.unicadpublish.com/~jack_xu</a></p>
<h2>About the Author</h2> 
<p>Dr. Jack Xu has a Ph.D in theoretical physics. He has over 15 years programming experience in Basic, Fortran, C, C++, Matlab, and C#, specializing in numerical computation methods, algorithms, physical modeling, computer-aided design (CAD) development, graphics user interface, and 3D graphics. Currently, he is responsible for developing commercial CAD tools based on Microsoft .NET framework.</p>

<p>Please read my other articles:</p>

<p><a href="JackXu.asp">"Draw US Flag using C# and GDI+"</a></p>

<p><a href="ColorShading.asp">"Create a Custom Color Shading in C#"</a></p>

<p><a href="Sphere.asp">"Spherical Coordinates in C#"</a></p>

<p />

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey18-Feb-12 3:24
professionalManoj Kumar Choubey18-Feb-12 3:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.