Click here to Skip to main content
15,349,648 members
Articles / Desktop Programming / MFC
Posted 25 Apr 2002


131 bookmarked

CGradient and CGradientCtrl

Rate me:
Please Sign up or sign in to vote.
4.97/5 (90 votes)
29 Jan 200314 min read
A pair of classes for rendering and editing colourful washes


This pair of classes is useful for graphics programs which need colour washes, or need smooth flowing palettes. CGradient provides all the useful functions for creating colour washes which can be rendered into CPalettes or arrays of RGBTRIPLES. CGradientCtrl is a control for editing the CGradient.

I wrote this pair of classes because I've never seen anything available which does the job, and as it was kind of crucial to my Fractal Generating Program, I started coding from scratch . The only place I've ever seen a control like this is in the Corel DRAW series, and the CGradientCtrl is clone of Corel's version.

New in This Version


  • Peg positions defined on a float scale of 0 - 1
  • The gradient can be quantized to make a blocky palette
  • A choice of 10 different interpolation methods
  • Background can now be selected.


  • Horizontal and Vertical Display modes
  • Keyboard support
  • Pegs can be shown on the left side of the gradient, right side, both or neither
  • Smooth dragging of pegs
  • Customizable Tooltips
  • Fixed slight blockiness in the rendering of the gradient
  • Better rendering of the wash

Note: The CGradient and CGradientCtrl 2.0 classes have been manipulated quite significantly since version 1.0, so the class cannot be a drop-replacment to the 1.0 class.


CGradientCtrl Demo

Image 1

I hope that the control is reasonably obvious how to use. In the demo I used a slightly modified version of the Microsoft CFireWnd to demonstrate how CGradient can be used to paletize 8-bit bitmaps. I've also provided a few sample gradient files containing some interesting colour effects.

CGradient Features

How it works

I have used some terms which I created myself to describe features of a colour wash.

Image 2

The diagram shows an annotated diagram of a gradient. The start peg and the end peg are fixed at either end of the gradient, with the movable colour pegs in-between them. Every peg has an associated colour, and the colour wash interpolated between the two the nearest pegs. The gradient also has an optional background colour which can become the 1st entry in rendered palettes.

Interpolation Methods

The gradient class now provides a choice of interpolation methods:

Linear Interpolation

Image 3

With linear interpolation each channel is treated individually, and the actual channel values are calculated by making weighted averages to move between pegs. The method can make the transition over a peg look rather sharp.

Cosine Interpolation

Image 4

Cosine interpolation provides smoother gradients than linear interpolation by using 180° segments of the cosine wave. Again each channel is interpolated independently of the others. This method tends to lead to a smoother flowing palettes especially around the pegs.

Flat Start, Flat End, Flat Middle Interpolation


Image 5

Flat Start
Image 6
Flat End
Image 7
Flat Middle

The three flat interpolation methods fill the gap between pegs with a constant colour. Flat Start and End use the colours of the start and end pegs of a segment respectively as the constant colour, and Flat Middle interpolation takes an average of the two pegs. These methods make the palette look really blocky.

Reverse Interpolation

Image 8

Reverse interpolation is linear interpolation in reverse! Instead of starting with the first colour and fading through to the last for each peg, interpolation starts from the last colour and works in reverse. This method makes the palette look really cut up.

HSL Clockwise, HSL Anticlockwise, HSL Shortest, HSL Longest Interpolation

With the HSL interpolation methods, colour is interpolated using the colour wheel. Saturation, luminance and hue values are interpolated linearly. Because the hue value is a circular type value, there are always two ways of getting from one point on the circle to the other - a long route and a short route. With clockwise and anticlockwise modes the hue is always interpolated by the clockwise or anticlockwise route respectively. And with shortest and longest modes the hue is always interpolated by the shortest or longest routes. These modes allow you to create a gaudy rainbow effect between pegs.

CPeg Class

Every peg has an index. The end pegs and the background have special indexes. The actual value is irrelevant as I have #defined all the symbols in the "Gradient.h" file.

PegSymbolIndex (irrelevant as the symbols are #defined)

The information of the pegs is stored in a CArray of CPegs. The CPeg is a class which contains the data for a gradient peg.

class CPeg : CObject
  CPeg();                                 // Default Constructor
  CPeg(const CPeg &src);                  // Copy Constructor
  CPeg& operator = (const CPeg &src);     // Assignment operator
  void Serialize(CArchive &ar);	          // Used to save the data to a file
  const UINT GetID() const {return id;};  // Returns the unique id of the peg
  DECLARE_SERIAL(CPeg)     // Handy MFC thingy
  COLORREF colour;         // The colour of the peg
  float position;          // The position of the peg

  UINT id;                 // The unique ID of the peg
  static UINT uniqueID;    // Used to create new  ids for fresh pegs

CGradient Class

CGradient is publicly derived from the MFC CObject class

Construction & Destruction


Creates a CGradient with no pegs except the start peg and the end peg which are assigned by default to black and white respectively.

CGradient(CGradient &gradient)

Copy constructor.

virtual ~CGradient()



CGradient& operator =(CGradient &src)

Assignment operator


int GetPegCount() const

Retrieves the number of pegs in the gradient. This does not include the start and end pegs.

const CPeg GetPeg(int iIndex) const

Returns a copy of a peg from a given index. If iIndex is a fixed peg (STARTPEG, ENDPEG or BACKGROUND), the pegs for these will be returned.

int SetPeg(int iIndex, COLORREF crColour, int iPosition)

Will set a peg of a given index with this colour and position info. The function returns the new index of the peg, in case it has been moved. -1 is returned if iIndex refers to a fixed peg such as BACKGROUND, STARTPEG or ENDPEG.

int SetPeg(int iIndex, CPeg peg)

Similar to SetPeg(int, COLORREF, int), except the pegs are set from a CPeg not position and colour data.

int AddPeg(COLORREF crColour, float iPosition)

Adds a peg with colour crColour and position iPosition. If iPosition is less than 0 or more than the gradient size, it will be truncated so that it falls within the bounds of the gradient. The return value is the index of the new peg.

int AddPeg(CPeg peg)

Add a peg to the gradient, the return value is the index of the peg.

void RemovePeg(int iIndex)

Deletes a peg at index iIndex.

int IndexFromPos(float pos)

Returns the index of the peg which appears at the beginning of the interpolation segment, which pos fall inside.

void SetStartPegColour(const COLORREF crColour)<BR>void SetEndPegColour(const COLORREF crColour)<BR>void SetBackgroundColour(const COLORREF crColour)

All three set the colour of their respective fixed pegs.

COLORREF GetStartPegColour() const<BR>COLORREF GetEndPegColour() const<BR>COLORREF GetBackgroundColour() const

All three will return the colour of their respective pegs.

void SetUseBackground(const BOOL bUseBackground)

Sets the Gradient's Background mode. If bUseBackground is TRUE 0'th entry in a palette will be set to the bacground colour, if bUseBackground is FALSE, the 0'th entry will be the first colour in the wash.

BOOL GetUseBackground() const

Return the background mode. See SetUseBackground...

InterpolationMethod GetInterpolationMethod() const

Retrieves the interpolation method used on the gradient. It can be

CGradient::HSLLongest or

void SetInterpolationMethod(const InterpolationMethod method)

Sets the interpolation method to and of the values listed above.

void SetQuantization(const int entries)

SetQuantization allow you to set a number of entries that the palette should be quantized to. So with 8 entries the palette will have 8 discrete blocks in it. If entries is -1 quantization is disabled.

int GetQuantization() const

Returns the number of entries the palette should be quantized to. If -1 is returned, quantization is not active.


void MakePalette(CPalette *lpPal);

Creates a windows palette into the pointer, and fills the palette with the 256 colours from the gradient.

void Make8BitPalette(RGBTRIPLE *lpPal);

Fills an array of 256 RGBTRIPLEs with the gradient colours.

void MakeAllEntries(RGBTRIPLE *lpPal, int iEntryCount);

Fill the array of RGBTRIPLEs lpPal of size iEntryCount with the colour gradient. This function allows you to flexible create any type of palette from 2-colour palettes to 65525 palettes.

COLORREF ColourFromPosition(int iPos);

Returns the colour of the gradient at a given position.

void Serialize(CArchive &ar);

Standard serialization function.

CGradientCtrl Features

The CGradientCtrl is a CWnd derived control that allows the viewing and editing of the CGradient class.


  • Full keyboard support
  • Now runs in horizontal and vertical modes.
  • Displays the movable pegs as series of arrows, and the end pegs as a pair of squares.
  • The control shows the gradients as a vertical column, with a smooth movement of colour.
  • As pegs are dragged, the gradient is updated dynamically.
  • Pegs which are close together are stacked up:
     Image 9
  • Pegs can be shown on the left side, right side, none or both sides
  • Customizable Tooltip support.

Peg Sides

Image 10Image 11Image 12Image 13
No Pegs - Read only!Left/Bottom side pegsRight/Top side pegsBoth side pegs

Keyboard Support

TabSelect Next Peg
Up, LeftSlide peg up towards start
Down, RightSlide peg towards end
HomeSlide peg to the start
EndSlide peg to end
Del, BackspaceDelete Peg
Return, SpaceEquivalent to double clicking on a peg
InsertDuplicates a peg

CGradientCtrl Class

CGradient control is derived from the MFC CWnd class



Default constructor.

BOOL Create(const RECT& rect, CWnd* pParentWnd, UINT nID);

Creates a control in the window pParentWnd, in the rectangle rect, and with the ID nID. The return value is the success of the function.


void SetGradientWidth(int iWidth);

The function sets the width that the gradient is drawn with the control. iWidth specifies the number of pixels width the control should be. If GCW_AUTO is passed to iWidth, the width of control will be automatically adjusted to best fit the control into it's window.

int GetGradientWidth();

Returns the width that gradient is set to draw to. The function may return GCW_AUTO which means that the gradient width will be drawn so that it is best fitted into the control window.

int GetSelIndex();

Returns the index of the currently selected peg. This value return may be STARTPEG, ENDPEG or NONE.

int SetSelIndex(int iSel, BOOL bUpdate);

Sets the currently  selected peg to the index iSel. This value must be either a peg index between 0, and the number of movable pegs, or STARTPEG, ENDPEG, or NONE. If bUpdate is TRUE, the control is redrawn. The return value is the index of the previously selected peg.

CPeg GetSelPeg();

Returns an CPeg structure representing the currently selected peg. If the currently selected peg is an end peg, the position value in the CPeg class will be set to -1.

CGradient& GetGradient();

Returns a reference to the CGradient from which the control works.

void SetGradient(CGradient src)

Copies the source gradient into the controls embedded CGradient.

void ShowTooltips(BOOL bShow = true)

Switches tooltips on, or off

CGradient::Orientation GetOrientation() const

Returns the orientation mode of the control. The function can return CGradient::ForceHorizontal, CGradient::ForceVertical or CGradient::Auto. For CGradient::Auto, the orientation is automatically selected. So if the width is greater that the height, the control will be shown in horizontal mode.

void SetOrientation(CGradient::Orientation orientation)

Sets the orientation mode of the control. The modes CGradient::ForceHorizontal, CGradient::ForceVertical or CGradient::Auto are available.

void SetPegSide(BOOL setrightup, BOOL enable)

Sets the peg side mode for the control. So if setrightup is TRUE, enable will enable disable peg drawing on the right or top side. With right FALSE, enable will enable or disable pegs displayed on the left or bottom side.

BOOL GetPegSide(BOOL rightup) const

Retrieves the peg side mode for the side specified by rightup. So with rightup TRUE, GetPegSide will return a BOOL for whether pegs are to be displayed on the right or upper side of the control.

void SetTooltipFormat(const CString format)

Sets the tooltip format string to the value of format. A tool-tip format string is a piece of multi-line tokened text with a line associate with each possible tool-tip. So the default British tool-tip string looks like this:

&SELPOS\nPosition: &SELPOS Colour: R &R G &G B &B\nColour: R &R G &G B &B\nColour: R &R G &G B &B\nDouble Click to Add a New Peg

The string is made up of a series of tokens:

&SELPOSDisplays the position of  the selected text.
&RDisplays the red component of the selected peg in decimal (0-255).
&GDisplays the green component of the selected peg in decimal (0-255).
&BDisplays the blue component of the selected peg in decimal (0-255).
&HEXRDisplays the red component of the selected peg in two digit hex (00-FF).
&HEXGDisplays the green component of the selected peg in two digit hex (00-FF).
&HEXBDisplays the blue component of the selected peg in two digit hex (00-FF).
&FLOATRDisplays the red component of the selected peg as a floating point value between 0.0 and 1.0 to three decimal place accuracy.
&FLOATGDisplays the green component of the selected peg as a floating point value between 0.0 and 1.0 to three decimal place accuracy.
&FLOATBDisplays the blue component of the selected peg as a floating point value between 0.0 and 1.0 to three decimal place accuracy.
&&Displays an ampersand - "&"

Each line is assigned to a different tool-tip for a different situation:

1Used when the peg is being dragged
2Used when the mouse hovers over a peg.
3Used when the mouse hovers over then start peg
4Used when the mouse hovers over then end peg
5Used when the mouse hovers over the gradient area

This system of token strings allows the control to be fairly neutral with respect to internationalization. The token strings can be stored in string table so that they can be auto-selected.

CString GetTooltipFormat() const;

Returns the current tool-tip format string.


void DeleteSelected(BOOL bUpdate);

Deletes the selected peg. No operation is performed if the selected peg is not a movable peg. If bUpdate is TRUE, the control is redrawn.

int MoveSelected(int iNewPos, BOOL bUpdate);

Moves the selected peg to a new position iNewPos. No operation is performed if the selected peg is not a movable peg. If iNewPos is less than 0 or greater than the gradient size. If bUpdate is TRUE, the control is redrawn. The return value is the new index of the moved peg.

COLORREF SetColourSelected(COLORREF crNewColour, BOOL bUpdate);

Sets the colour of the selected peg to new crNewColour. If bUpdate is TRUE, the control is redrawn. The return value is the previous colour of the selected peg.

Notification Messages

The gradient control may send any of these notification messages to it's parent window. The CGradientCtrl uses two NMHDRs: PegNMHDR, and DoubleClickCreateNMHDR

// Standard NMHDR used for all the notifications except GC_DBL_CREATE
struct PegNMHDR
	NMHDR nmhdr;		// Standard NMHDR
	CPeg peg;		// A copy of the CPeg in quesyion
	int index;		// The index of the peg in question

// NMHDR used in the GC_DBL_CREATE
struct PegCreateNMHDR
	NMHDR nmhdr;		// Standard NMHDR
	float position;		// The position at which a peg should be created
	COLORREF colour;	// The colour at the position


Value: 1

Notification that the the selected peg has changed. A PegNMHDR is sent containing information about the currently selected peg. The return value is ignored.


Value: 2

Notification that a colour peg is currently being moved. A PegNMHDR is sent containing information about the peg being dragged. The returned value is ignored.


Value: 3

Notification that a movement operation has just completed. A PegNMHDR is sent containing information about the peg dragged. The returned value is ignored.


Value: 4

Notification that a peg has been removed. A PegNMHDR is sent containing information about the moved peg. The returned value is ignored.


Value: 5

Notification that the user has double clicked on an empty spot on the gradient. This event can can also be triggered by the user pressing the "Insert" key. You may want to bring up a dialog asking for the colour of the new peg, or as in the case of the demo create a peg of a with the gradient colour in the position. A DoubleClickCreateNMHDR is sent containing the position that was double clicked, and colour present at that point.


Value: 6

Notification that the user has double clicked on a peg. This event can can also be triggered by the user pressing the Space Bar or the Return key. This message is sent to request the host to show an editing dialog. A PegNMHDR is sent containing information about the peg to be edited. The returned value is ignored.


Value: 7

Notification is sent when the user makes any change to the gradient. This is a catch-all for all the above notifications. A PegNMHDR is sent containing information about the peg that has changed. The returned value is ignored.

Using CGradientCtrl

Into your dialog template you'll need to create a custom control (Image 14). Then you'll need to configure it. Set the windows ID to something like IDC_GRADIENT. The caption of the control is irrelevant. Set the class to "MFCGradientCtrl". Leave the style on 0x50010000, and if you want the control to have a client edge as shown in the demo you'll need to set the ExStyle to 0x200. VC should look something like this...

Image 15

Now in the source for your dialog, in DoDataExchange add this line:

DDX_Control(pDX, IDC_GRADIENT, m_wndGradientCtrl);

The code should look like this when you've finished

void CFooDlg::DoDataExchange(CDataExchange* pDX)
    DDX_Control(pDX, IDC_GRADIENT, m_wndGradientCtrl); //<-- Add this line

In the header file add this line:

CGradientCtrl m_wndGradientCtrl;

You can put it anywhere in the file. It will should work fine. I put mine right beneath the dialog data section.

Forthcoming Improvements

Of course I will endeavour to fix all the bugs that get reported. Transparency support would be nice soon!


Revision 2.0: Added Interpolation methods, quantization, keyboard support, notification messages, tooltips, horizontal and vertical orientation modes.

Revision 1.0B: Fixed resource leak in demo (I think). Other minor changes.

Revision 1.0A: Added message GC_CHANGE, and tested CGradientCtrl::Create with successful results.

British and Proud!

Sorry if the abundant use of colour rather than color offends you! :-)


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


About the Author

Joel Holdsworth
Web Developer
United Kingdom United Kingdom
Joel Holdsworth is a Mechanical Engineering student studying at Imperial College London. He has been programming for many years now. His favourite activities include bulding things, as well as building gadgets and also building stuff. That might be Mechanical Stuff, or Electronic stuff, or indeed Computer stuff. And when it comes to building stuff with computers Joel loves to mess around with C++ and C# in VS 2005, as well as pretty much anything computer related.

Comments and Discussions

GeneralGreat article/source code. Pin
fkhanoom4-Dec-09 8:21
Memberfkhanoom4-Dec-09 8:21 
QuestionPotential GDI leak? Pin
RedFraggle4-Nov-08 4:08
MemberRedFraggle4-Nov-08 4:08 
GeneralVery nice, here are some code beautification suggestions Pin
Andreas Schniertshauer10-May-06 22:44
MemberAndreas Schniertshauer10-May-06 22:44 
GeneralSuggestion Pin
lano110615-Dec-05 8:04
Memberlano110615-Dec-05 8:04 
GeneralRe: Suggestion Pin
Joel Holdsworth15-Dec-05 10:18
MemberJoel Holdsworth15-Dec-05 10:18 
GeneralThese classes are fantastic! Pin
lano110612-Dec-05 7:51
Memberlano110612-Dec-05 7:51 
GeneralSetGradientWidth() Pin
pcmaan13-Jun-05 23:35
Memberpcmaan13-Jun-05 23:35 
GeneralRe: SetGradientWidth() Pin
Joel Holdsworth13-Jun-05 23:48
MemberJoel Holdsworth13-Jun-05 23:48 
GeneralRe: SetGradientWidth() Pin
pcmaan14-Jun-05 3:34
Memberpcmaan14-Jun-05 3:34 
GeneralCooooool! Pin
Sergi Díaz13-Apr-04 5:40
MemberSergi Díaz13-Apr-04 5:40 
GeneralRe: Tracking tooltip doesn't track. Pin
Joel Holdsworth25-Mar-04 6:33
MemberJoel Holdsworth25-Mar-04 6:33 
GeneralRe: Tracking tooltip doesn't track. Pin
e_roman25-Mar-04 22:15
Membere_roman25-Mar-04 22:15 
GeneralRe: Tracking tooltip doesn't track. Pin
David Stranz5-Nov-04 12:31
MemberDavid Stranz5-Nov-04 12:31 
GeneralTracking tooltip doesn't track. Pin
e_roman25-Mar-04 6:23
Membere_roman25-Mar-04 6:23 
QuestionTransparency support? Pin
khan200019-Mar-04 13:40
Memberkhan200019-Mar-04 13:40 
AnswerRe: Transparency support? Pin
Joel Holdsworth20-Mar-04 22:36
MemberJoel Holdsworth20-Mar-04 22:36 
Generalnice job! Pin
luminus_ak31-Jan-04 13:15
Memberluminus_ak31-Jan-04 13:15 
GeneralNice control Pin
igorcerovsky6-Dec-03 7:29
Memberigorcerovsky6-Dec-03 7:29 
GeneralRe: Nice control Pin
Joel Holdsworth6-Dec-03 7:34
MemberJoel Holdsworth6-Dec-03 7:34 
GeneralRe: Nice control Pin
igorcerovsky7-Dec-03 4:50
Memberigorcerovsky7-Dec-03 4:50 
GeneralPhotoshop Pin
NormDroid23-Oct-03 4:37
professionalNormDroid23-Oct-03 4:37 
Generalsmall demo fix Pin
Midnight4898-Oct-03 10:57
MemberMidnight4898-Oct-03 10:57 
GeneralRe: small demo fix Pin
Philippe Lhoste8-Jun-04 2:49
MemberPhilippe Lhoste8-Jun-04 2:49 
GeneralRe: small demo fix Pin
Joel Holdsworth8-Jun-04 2:56
MemberJoel Holdsworth8-Jun-04 2:56 
GeneralRe: small demo fix Pin
Philippe Lhoste8-Jun-04 3:29
MemberPhilippe Lhoste8-Jun-04 3:29 

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.