渐变色拾取器






4.77/5 (7投票s)
本文介绍了一个工具,它使开发者能够从线性颜色渐变中拾取颜色。
目录
简介
我不确定有多少次我需要生成一个由两种或更多颜色线性渐变组成的颜色数组。在为我网站的 COVID-19 部分构建动画图形时,我需要生成多个色阶。例如
由于任务变得繁重,我决定开发一个工具来协助。结果就是渐变拾色器。
用户界面
轴向颜色渐变(有时也称为线性颜色渐变)由两个点和每个点的颜色指定。沿这些点之间直线的颜色使用线性插值计算。
我想要一个工具,它能提供一个由线性颜色渐变派生出的颜色列表。颜色列表将被放置在剪贴板中。该工具需要提供以下功能。
- 接受两种颜色,并用线性颜色渐变填充一个面板 (gradient_PAN);接受要从线性颜色渐变中提取的颜色数量。最小颜色数量为 3(少于三种颜色不需要使用工具);最大颜色数量为 20(受限于工具客户端区域的大小)。
- 当用户点击 生成 时,工具会绘制彩色按钮,这些按钮的颜色与相关线性渐变的颜色相同(即,线性渐变中按钮正上方的颜色)。
- 用户现在可以选择将颜色值捕获到剪贴板。这些选项包括
- 用户可以选择用于剪贴板条目的颜色格式;默认格式为 RGB 十六进制。
- 通过点击任何单个颜色按钮,该按钮的颜色将被复制到剪贴板。两个末端按钮的颜色与最初指定的颜色相同。如果点击示例中的第六个彩色按钮,复制到剪贴板的值为
'#FF6666' - 通过点击 从左到右复制,剪贴板将填充所有彩色按钮的逗号分隔颜色列表,从左到右。对于示例,复制到剪贴板的值为
'#FFBBBB','#FFA7A7','#FF9797','#FF8787','#FF7777','#FF6666',
'#FF5656','#FF4545','#FF3535','#FF2525','#FF1414','#FF0000' - 通过点击 从右到左复制,剪贴板将填充所有彩色按钮的逗号分隔颜色列表,从右到左。对于示例,复制到剪贴板的值为
'#FF0000','#FF1414','#FF2525','#FF3535','#FF4545','#FF5656',
'#FF6666','#FF7777','#FF8787','#FF9797','#FFA7A7','#FFBBBB'
- 如果用户点击 重置,用户返回到步骤 1;如果用户点击 退出,应用程序退出。
实现
与任何事件驱动的应用程序一样,初始化大部分时间都花在建立图形用户界面上。在渐变拾色器的情况下,实际上只有两个重要的组件:gradient_PAN 和 button_PAN 面板。
gradient_PAN
当选择起始颜色或结束颜色时,会调用 fill_gradient_PAN 方法。此方法确保起始和结束颜色按钮都已访问,并且起始和结束颜色都已选择。如果满足条件,该方法会执行以下操作
gradient_PAN.Visible = true;
gradient_PAN.Invalidate ( );
gradient_PAN 附加了 PAN_OnPaint 事件处理程序。
// *********************************************** PAN_OnPaint
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 );
} // PAN_OnPaint
PAN_OnPaint 事件处理程序非常简单。它所做的只是创建一个 LinearGradientBrush [^] 并用它填充 gradient_PAN 客户端矩形。
button_PAN
当点击 生成 按钮时,工具的其余 GUI 将被渲染。在大多数情况下,这需要使各种对象可见。然而,生成 button_PAN 面板则要复杂一些。
// ******************************************* fill_button_PAN
bool fill_button_PAN ( )
{
int right_most = 0;
int spacing = 0;
int top = 2;
Point UL = new Point ( 0, 0 ) ;
// remove existing event
// handlers from buttons in
// the button_PAN
foreach ( Control control in button_PAN.Controls )
{
if ( control is Button )
{
control.Click -= new EventHandler (
gradient_BUT_Click );
}
}
// remove any existing buttons
// from the button_PAN
button_PAN.Controls.Clear ( );
// clear the buttons list
buttons.Clear ( );
// compute initial spacing
// between buttons
spacing = ( gradient_PAN.Size.Width -
( BUTTON_WIDTH * number_of_colors ) ) /
( number_of_colors - 1 );
// create gradient buttons and
// add them to buttons list
for ( int i = 0; ( i < number_of_colors ); i++ )
{
Button button = new Button ( );
int left = ( i * ( spacing + BUTTON_WIDTH ) );
// want no borders
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;
buttons.Add ( button );
}
// the spacing may not be
// large enough to cause the
// buttons to completely fill
// the button panel to the
// right; here we correct the
// inter-button spacing;
// EPSILON is currently 3
if ( right_most < ( gradient_PAN.Size.Width - EPSILON ) )
{
int pixels = 1;
int start = 0;
// start is expected to be
// greater than zero
start = buttons.Count -
( gradient_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;
}
}
// set the button BackColor;
// copy the button from the
// buttons List to the
// button_PAN
for ( int i = 0; ( i < buttons.Count ); i++ )
{
Button button = buttons [ i ];
// color the button based upon
// its current location in the
// buttons panel
if ( i == 0 )
{
button.BackColor = start_color;
}
else if ( i == ( number_of_colors - 1 ) )
{
button.BackColor = end_color;
}
else
{
generate_back_color ( ref button );
}
button.UseVisualStyleBackColor = false;
// place button in button_PAN
button_PAN.Controls.Add ( button );
}
button_PAN.Visible = true;
reset_BUT.Visible = true;
initialize_miscellaneous_controls ( );
return ( true );
} // fill_button_PAN
fill_button_PAN 执行以下任务
- 由于应用程序可能会多次执行,因此有必要移除附加到每个按钮的所有事件处理程序。此外,所有按钮都将从 button_PAN 中移除,并且所有按钮都将从 buttons 列表中移除。
- 计算初始 spacing。请注意,由于舍入误差,计算值可能不是按钮之间的最终间距。
- 创建按钮并沿 button_PAN 面板进行间隔,同时记录最右侧按钮的右侧位置。
- 修正按钮之间的间距,使按钮沿 button_PAN 等距排列。此步骤是为了确保按钮颜色准确描绘 gradient_PAN 中的颜色。
- 到目前为止,按钮数据已存储在 buttons 列表中。现在按钮被放置到 button_PAN 中。由于最终位置已知,因此调用 generate_back_color 方法最终为每个按钮分配 BackColor。
- 最后,使其余控件可见。
generate_back_color 方法首先计算 gradient_PAN 垂直中心点的坐标,该点在水平方向上与 button_PAN 中的按钮居中。此点在工具的客户端矩形坐标中定义。
在调用 Win32 get_pixel_color_at_location 方法之前,必须将该点转换为屏幕坐标。
// *************************************** generate_back_color
bool generate_back_color ( ref Button button )
{
Point point;
Point screen_point;
point = new Point (
( ( button_PAN.Location.X +
button.Location.X ) +
( button.Size.Width / 2 ) ),
( gradient_PAN.Location.Y +
( gradient_PAN.Size.Height / 2 ) ) );
screen_point = PointToScreen ( point );
button.BackColor =
Win32API.get_pixel_color_at_location ( screen_point );
return ( true );
} // generate_back_color
Win32 get_pixel_color_at_location 方法使用 GetPixel 对一个像素大小的 Bitmap 进行操作。位于指定位置的像素被复制 (BitBlt [^]) 到 screen_pixel Bitmap 中。这带来了四个好处:该方法在多显示器环境下使用时不会引发异常;它比 GetPixel 更快;使用的 Bitmap 只有一像素高和宽;并且 Bitmap 局部于此方法。get_pixel_color_at_location 方法采用以下形式
// **************************************************** BitBlt
[ DllImport ( "gdi32.dll",
EntryPoint = "BitBlt" ) ]
public static extern bool BitBlt ( IntPtr hdcDest,
int nXDest,
int nYDest,
int nWidth,
int nHeight,
IntPtr hdcSrc,
int nXSrc,
int nYSrc,
int dwRop );
:
:
// ******************************* get_pixel_color_at_location
public static Color get_pixel_color_at_location (
Point screen_location )
{
Color color;
Bitmap screen_pixel = new Bitmap (
1,
1,
PixelFormat.Format32bppArgb );
using ( Graphics destination = Graphics.FromImage (
screen_pixel ) )
{
using ( Graphics source = Graphics.FromHwnd (
IntPtr.Zero ) )
{
IntPtr source_DC = source.GetHdc ( );
IntPtr destination_DC = destination.GetHdc ( );
BitBlt ( destination_DC,
0,
0,
1,
1,
source_DC,
screen_location.X,
screen_location.Y,
( int ) CopyPixelOperation.SourceCopy );
}
}
color = screen_pixel.GetPixel ( 0, 0 );
screen_pixel.Dispose ( );
return ( color );
}
} // class Win32API
参考文献
结论
本文介绍了一个工具,它使开发者能够从线性颜色渐变中拾取颜色。
开发环境
渐变色拾取器是在以下环境中开发的
Microsoft Windows 7 专业版 SP 1 |
Microsoft Visual Studio 2008 专业版 SP1 |
Microsoft Visual C# 2008 |
Microsoft .Net Framework Version 3.5 SP1 |
历史
05/05/2020 | 原文 |