Gradient Color Picker V2 revises the earlier implementation by revising the user interface and eliminating a need to determine the color of a pixel at a specific screen location.
Table of Contents
The symbol returns the reader to the top of the Table of Contents.
Introduction
This article revises an earlier implementation of the Gradient Color Picker [^]. The incentive for the revision were reader comments regarding the earlier implementation.
The only areas of change were in the positioning of the user interface components and the implementation of the gradient_PAN and the button_PAN panels. The original article should be referenced for details.
Implementation
gradient_PAN
When either the start or end color is chosen, the fill_gradient_PAN method is invoked.
bool fill_gradient_PAN ( )
{
if ( have_end_color && have_start_color )
{
gradient_PAN.Visible = true;
gradient_PAN.Invalidate ( );
number_of_colors_LAB.Visible = true;
number_of_colors_NUD.Value = number_of_colors;
number_of_colors_NUD.Visible = true;
generate_BUT.Visible = true;
}
return ( true );
}
This method insures that both the start and end colors have been chosen. If so, the method executes the following:
gradient_PAN.Visible = true;
gradient_PAN.Invalidate ( );
The gradient_PAN has the PAN_OnPaint event handler attached.
void PAN_OnPaint ( object sender,
PaintEventArgs e )
{
base.OnPaint ( e );
e.Graphics.FillRectangle (
new LinearGradientBrush (
gradient_PAN.ClientRectangle,
start_color,
end_color,
0.0F ),
gradient_PAN.ClientRectangle );
}
The PAN_OnPaint event hander is very simple. All it does is create a LinearGradientBrush [^] and uses it to fill the gradient_PAN client rectangle.
In this revision the left side of the gradient_PAN is aligned horizontally with the horizontal center of the start_color_BUT and the right side of the gradient_PAN is aligned horizontally with the horizontal center of the end_color_BUT. The number of colors label and the numeric up/down control have been moved to midway between the start- and end-color buttons.
button_PAN
When the Generate button is clicked, the balance of the tool's GUI is rendered. In most cases this entails making various objects visible. However, generating the button_PAN panel is somewhat more complex.
bool fill_button_PAN ( )
{
int right_most = 0;
int spacing = 0;
int top = 2;
colors.Clear ( );
colors = get_linear_gradient_colors ( start_color,
end_color,
number_of_colors );
foreach ( Control control in button_PAN.Controls )
{
if ( control is Button )
{
control.Click -= new EventHandler (
gradient_BUT_Click );
}
}
button_PAN.Controls.Clear ( );
buttons.Clear ( );
spacing = ( button_PAN.Size.Width -
( BUTTON_WIDTH * number_of_colors ) ) /
( number_of_colors - 1 );
for ( int i = 0; ( i < number_of_colors ); i++ )
{
Button button = new Button ( );
int left = ( i * ( spacing + BUTTON_WIDTH ) );
button.FlatStyle = FlatStyle.Popup;
button.Location = new Point ( left, top );
button.Size = BUTTON_SIZE;
button.Click += new EventHandler (
gradient_BUT_Click );
right_most = button.Location.X + button.Size.Width;
button.BackColor = colors [ i ];
button.UseVisualStyleBackColor = false;
buttons.Add ( button );
}
if ( right_most < ( button_PAN.Size.Width - EPSILON ) )
{
int pixels = 1;
int start = 0;
start = buttons.Count -
( button_PAN.Size.Width - right_most );
for ( int i = start; ( i < buttons.Count ); i++ )
{
Point location = buttons [ i ].Location;
location.X += pixels++;
buttons [ i ].Location = location;
}
}
for ( int i = 0; ( i < buttons.Count ); i++ )
{
Button button = buttons [ i ];
button_PAN.Controls.Add ( button );
}
button_PAN.Visible = true;
reset_BUT.Visible = true;
copy_format_GB.Visible = true;
ascending_PB.Visible = true;
copy_left_to_right_BUT.Visible = true;
descending_PB.Visible = true;
copy_right_to_left_BUT.Visible = true;
return ( true );
}
The major change in fill_button_PAN is the elimination of the dependency on the contents of the gradient_PAN to determine the background color of the buttons in the button_PAN. To achieve this, the get_linear_gradient_colors method is invoked to fill a list of colors whose members will be assigned to each button as its background color.
List < Color > get_linear_gradient_colors (
Color start_color,
Color end_color,
int number_of_colors )
{
List < Color > list = new List < Color > ( 0 );
float count = ( float ) number_of_colors - 1.0F;
float start_R = ( float ) start_color.R;
float difference_R = ( start_R -
( float ) end_color.R ) /
count;
float start_G = ( float ) start_color.G;
float difference_G = ( start_G -
( float ) end_color.G ) /
count;
float start_B = ( float ) start_color.B;
float difference_B = ( start_B -
( float ) end_color.B ) /
count;
for ( int i = 0; ( i < number_of_colors ); i++ )
{
int B = MinMax ( start_B, difference_B, ( float ) i );
int G = MinMax ( start_G, difference_G, ( float ) i );
int R = MinMax ( start_R, difference_R, ( float ) i );
list.Add ( Color.FromArgb ( R, G, B ) );
}
return ( list );
}
The method MinMax was introduced to avoid repeating the same logic over and over again.
int MinMax ( float start,
float difference,
float i )
{
int result = ( int ) ( start - ( i * difference ) + 0.5F );
return ( Math.Max ( 0, Math.Min ( 255, result ) ) );
}
In this revision, the colors list and get_linear_gradient_colors replace generate_back_color.
Acknowlegments
The authors of the readers comments are:
- steve-redTrans
- BillWoodruff
I thank both for their comments that caused this article to be written.
References
Conclusion
This article has revised a tool that provides developers with the ability to pick colors from a linear color gradient.
Development Environment
The Gradient Color Picker was developed in the following environment:
Microsoft Windows 7 Professional SP 1 |
Microsoft Visual Studio 2008 Professional SP1 |
Microsoft Visual C# 2008 |
Microsoft .Net Framework Version 3.5 SP1 |
History
05/05/2020 | Original article |
05/28/2021 | Revised article |
In 1964, I was in the US Coast Guard when I wrote
my first program. It was written in RPG (note no suffixing numbers). Programs and data were entered using punched cards. Turnaround was about 3 hours. So much for the "good old days!"
In 1970, when assigned to Washington DC, I started my MS in Mechanical Engineering. I specialized in Transportation. Untold hours in statistical theory and practice were required, forcing me to use the university computer and learn the FORTRAN language, still using punched cards!
In 1973, I was employed by the Norfolk VA Police Department as a crime analyst for the
High Intensity Target program. There, I was still using punched cards!
In 1973, I joined Computer Sciences Corporation (CSC). There, for the first time, I was introduced to a terminal with the ability to edit, compile, link, and test my programs on-line. CSC also gave me the opportunity to discuss technical issues with some of the brightest minds I've encountered during my career.
In 1975, I moved to San Diego to head up an IR&D project,
BIODAB. I returned to school (UCSD) and took up Software Engineering at the graduate level. After BIODAB, I headed up a team that fixed a
stalled project. I then headed up one of the two most satisfying projects of my career, the
Automated Flight Operations Center at Ft. Irwin, CA.
I left Anteon Corporation (the successor to CSC on a major contract) and moved to Pensacola, FL. For a small company I built their firewall, given free to the company's customers. An opportunity to build an
air traffic controller trainer arose. This was the other most satisfying project of my career.
Today, I consider myself capable.