65.9K
CodeProject 正在变化。 阅读更多。
Home

已知颜色调色板工具

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.37/5 (9投票s)

2010 年 11 月 26 日

CPOL

6分钟阅读

viewsIcon

46746

downloadIcon

1206

提供一个有助于从已知颜色中选择颜色的工具。

Known Colors Palette

引言

颜色是互联网的呼吸。本文讨论了一些颜色调色板,然后介绍了一个可用于选择网页合适颜色的工具。

背景

颜色历史

在计算的早期历史中,颜色并不是成功应用程序的重要属性。大多数计算机终端都有绿色或琥珀色的显示屏。此外,大多数终端都是面向文本的,每行显示固定数量的字符,每屏显示固定数量的行。在很大程度上,运行在计算机终端上的大多数应用程序都是命令行解释器、文本编辑器和调制解调器控制应用程序。虽然有图形终端可用,但它们非常昂贵,并且仅在专业环境中使用。

1973年,施乐帕洛阿尔托研究中心(PARC)开发了个人工作站,这是现代个人计算机的前身。1975年,PARC推出了图形用户界面,其熟悉的桌面和图标。为了支持这些,PARC开发了位图图形。

1981年,随着个人计算机的推出,IBM还推出了彩色图形适配器(CGA),应用程序开发人员可以使用十六种颜色。

CGA Color Palette

1984年,IBM推出了增强图形适配器(EGA),具有64种颜色。

EGA Color Palette

然后,在1987年,IBM推出了视频图形阵列(VGA)。VGA调色板提供了256种颜色。

Default VGA Palette

随着X窗口的引入,一个名为X11颜色名称的调色板可用了。X11颜色名称与早期颜色调色板最重要的区别在于,可以通过指定其名称或RGB三元组来选择颜色。

最后,随着.NET Framework 1.1的推出,已知颜色调色板由微软正式化。尽管此调色板包含微软用于显示各种系统实体的一些颜色(例如,ActiveBorderAppWorkspace、Control Dark 等),但在枚举已知颜色时,我们可以排除这些Microsoft系统颜色

需求

在测试颜色命中测试用户控件时,我需要一个测试图像。我决定显示美国时区就足够了。因为我准备的文章将在互联网上查看,所以我决定使用已知颜色为图像着色。

我使用Microsoft Paint创建图像。在图像准备过程中,我偶然发现了该工具的颜色?编辑颜色...对话框中的一些限制。

  • 显示的颜色不是已知颜色。
  • 最初显示的颜色不是已知颜色。
  • 当鼠标悬停在颜色上时,对话框不会显示颜色名称。

为了解决这两个问题,我构建了已知颜色调色板工具。对该工具的要求包括:

  • 显示的颜色应为已知颜色。
  • 当鼠标悬停在颜色块上时,应显示颜色名称。
  • 颜色可以按已知颜色名称或按已知颜色的 ARGB 值进行排序。
  • 当鼠标单击颜色块时,将显示已知颜色的 ARGB 值以及各个 ARGB 分量值。
  • 应提供一个上下文菜单,允许用户执行排序、将工具置于最顶层,并允许将相应的颜色信息复制到剪贴板。

已知颜色调色板工具

已知颜色调色板工具基于微软作为 System.Drawing 程序集一部分提供的KnownColor 枚举。微软的枚举(除了系统颜色之外)等同于CSS 颜色名称。在枚举微软已知颜色名称时,会排除那些微软系统颜色。

  // ************************** get_known_colors_from_Microsoft

  // http://www.dreamincode.net/code/snippet1405.htm

  private List < Color > get_known_colors_from_Microsoft ( )
      {
      List < Color >  colors = new List<Color> ( );
      string [ ]      color_names = 
                          Enum.GetNames ( typeof ( 
                                              KnownColor ) );

      foreach ( string color_name in color_names )
          {
          KnownColor known_color = ( KnownColor ) Enum.Parse ( 
                                       typeof ( KnownColor ), 
                                       color_name );

          if ( ( known_color > KnownColor.Transparent ) &&
               ( known_color < KnownColor.ButtonFace ) )
              {
              colors.Add ( Color.FromName ( color_name ) );
              }
          }

      return ( colors );
      }

颜色枚举后的第一个任务是创建两个面板,一个面板按名称显示颜色,另一个面板按 ARGB 显示颜色。这两个面板都会被创建然后隐藏。我创建了两个单独的面板来提高切换显示时的性能。

  // ****************************** create_colors_by_name_panel

  private void create_colors_by_name_panel ( )
      {
      NameComparer  nc = new NameComparer ( );

      known_colors.Sort ( nc );
      colors_by_name_panel = populate_panel ( );
      this.Controls.Add ( colors_by_name_panel );
      colors_by_name_panel.Visible = false;
      }

  // ****************************** create_colors_by_ARGB_panel

  private void create_colors_by_ARGB_panel ( )
      {
      ARGBComparer  ac = new ARGBComparer ( );

      known_colors.Sort ( ac );
      colors_by_ARGB_panel = populate_panel ( );
      this.Controls.Add ( colors_by_ARGB_panel );
      colors_by_ARGB_panel.Visible = false;
      }

实现了两个类,NameComparer ARGBComparer,用于执行比较。

  // ******************************************* class NameComparer

  public class NameComparer : IComparer < Color >
      {

      // ************************************************** Compare

      public int Compare ( Color color_1,
                           Color color_2 )
          {

          if ( color_1 == null )
              {
              if ( color_2 == null )
                  {
                                      // If color_1 is null and 
                                      // color_2 is null, they're
                                      // equal 
                  return ( 0 );
                  }
              else
                  {
                                      // If color_1 is null and 
                                      // color_2 is not null, 
                                      // color_2 is greater. 
                  return ( -1 );
                  }
              }
          else
              {
                                      // If color_1 is not null...
              if ( color_2 == null )
                                      // ...and color_2 is null, 
                                      // color_1 is greater.
                  {
                  return ( 1 );
                  }
              else
                  {
                                      // ...and color_2 is not 
                                      // null, compare the names
                                      // of the two colors
                  return ( color_1.Name.CompareTo ( 
                               color_2.Name ) );
                  }
              }
          }
          
      } // class NameComparer

  // ******************************************* class ARGBComparer

  public class ARGBComparer : IComparer < Color >
      {

      // ************************************************** Compare

      public int Compare ( Color  color_1,
                           Color  color_2 )
          {

          if ( color_1 == null )
              {
              if ( color_2 == null )
                  {
                                      // If color_1 is null and 
                                      // color_2 is null, they're
                                      // equal 
                  return ( 0 );
                  }
              else
                  {
                                      // If color_1 is null and 
                                      // color_2 is not null, 
                                      // color_2 is greater. 
                  return ( -1 );
                  }
              }
          else
              {
                                      // If color_1 is not null...
              if ( color_2 == null )
                                      // ...and color_2 is null, 
                                      // color_1 is greater.
                  {
                  return ( 1 );
                  }
              else
                  {
                                      // ...and color_2 is not 
                                      // null, compare the ARGBs
                                      // of the two colors
                  uint  ARGB_1;
                  uint  ARGB_2;

                  ARGB_1 = Utilities.ColorToUIntARGB ( color_1 );
                  ARGB_2 = Utilities.ColorToUIntARGB ( color_2 );
                  
                  return ( ARGB_1.CompareTo ( ARGB_2 ) );
                  }
              }
          }
          
      } // class ARGBComparer

当已知颜色列表排序后,它将用于填充面板。

  // ******************************************* populate_panel

  private Panel populate_panel ( )
      {
      Point   color_square_location;
      Size    color_square_size;
      int     column = 0;
      Panel   panel = new Panel ( );

      panel.Location = new Point ( PANEL_LEFT, PANEL_TOP );
      panel.Size = new Size ( PANEL_WIDTH, PANEL_HEIGHT );
      
      color_square_location = new Point ( INITIAL_LEFT, 
                                          INITIAL_TOP );
      color_square_size = new Size ( COLOR_SQUARE_EDGE,
                                     COLOR_SQUARE_EDGE );

      foreach ( Color color in known_colors )
          {
          Button   color_square = new Button ( );
          ToolTip  tooltip = new ToolTip ( );

          color_square.Location = color_square_location;
          color_square.Size = color_square_size;
          color_square.BackColor = color;
          color_square.Click += new System.EventHandler (
                                    color_square_BUT_Click );
          tooltip.SetToolTip ( color_square, color.Name );
          tooltip.AutomaticDelay = TOOLTIP_DELAY;
          
          panel.Controls.Add ( color_square );
          column++;

          if ( column >= COLOR_SQUARES_PER_ROW )
              {
              column = 0;
              
              color_square_location.X = INITIAL_LEFT;
              color_square_location.Y += 
                  COLOR_SQUARE_EDGE + COLOR_SQUARE_SEPARATION;
              }
          else
              {
              color_square_location.X +=
                  COLOR_SQUARE_EDGE + COLOR_SQUARE_SEPARATION;
              }
          }
      
      return ( panel );
      }

在初始化时,变量 sort_by_name true ,并且显示按名称排序的面板。

  // ******************************************* initialize_GUI

  private void initialize_GUI ( )
      {

      if ( sort_by_name )
          {
          byNameToolStripMenuItem.Checked = true;
          sortByNameToolStripMenuItem.Checked = true;
          byARGBToolStripMenuItem.Checked = false;
          sortByARGBToolStripMenuItem.Checked = false;
          
          colors_by_name_panel.Visible = true;
          colors_by_ARGB_panel.Visible = false;
          }
      else
          {
          byNameToolStripMenuItem.Checked = false;
          sortByNameToolStripMenuItem.Checked = false;
          byARGBToolStripMenuItem.Checked = true;
          sortByARGBToolStripMenuItem.Checked = true;
          
          colors_by_name_panel.Visible = false;
          colors_by_ARGB_panel.Visible = true;
          }
      }

如果用户切换显示,变量 sort_by_name 将变为 false ,并显示按 ARGB 排序的面板。

选择颜色时,将显示有关该颜色的信息。

Known Color Selected

一旦选择了一个颜色,无论是单击颜色块还是按 Ctrl-S,颜色信息都会被复制到剪贴板。对于已知颜色 DarkRed,复制到剪贴板的信息是:

{Name=DarkRed,ARGB=(255,139,0,0)=#FF8B0000=(FF,8B,00,00)} 

虽然传输到剪贴板与在 Microsoft Paint 中设置颜色不同,但它确实提供了一种捕获数据并将其复制到 Microsoft Paint 的方法。如果我能弄清楚如何对 Microsoft Paint 进行子类化,我将修改已知颜色调色板工具以直接与 Microsoft Paint 交互。任何关于此升级的建议都将不胜感激。

修订

正如一位读者指出的那样,工具开发时的屏幕分辨率可能与工具执行时的屏幕分辨率不同。在 1024x768 下看起来正常的内容,在其他分辨率下可能看起来不太好。更糟糕的是,当工具在更高分辨率下收缩时,该工具可能会变得无用。

为了解决这个问题,我在 800x600 的屏幕分辨率下重新开发了该工具。完成后,我添加了代码来初始化两个屏幕分辨率乘数,一个用于水平调整,另一个用于垂直调整。

  // ************************ initialize_resolution_multipliers

  private void initialize_resolution_multipliers ( )
      {
      
      if ( ( SystemInformation.PrimaryMonitorSize.Height >
             CREATED_IN_SCREEN_HEIGHT ) ||
           ( SystemInformation.PrimaryMonitorSize.Width >
             CREATED_IN_SCREEN_WIDTH ) ) 
          {
          screen_height_multiplier = 
              ( float ) SystemInformation.
                            PrimaryMonitorSize.Height /
              ( float ) CREATED_IN_SCREEN_HEIGHT;
          screen_height_multiplier *= 4.0F;
          screen_height_multiplier /= 5.0F;

          screen_width_multiplier = 
              ( float ) SystemInformation.
                            PrimaryMonitorSize.Width /
              ( float ) CREATED_IN_SCREEN_WIDTH;
          screen_width_multiplier *= 4.0F;
          screen_width_multiplier /= 5.0F;

          color_square_edge = 
              ( int ) ( ( float ) color_square_edge * 
                        screen_width_multiplier );
          color_square_separation = 
              ( int ) ( ( float ) color_square_separation * 
                        screen_width_multiplier );

          resolution_changed = true;
          }
      }

除了乘数之外,两个全局变量 color_square_edge color_square_separation 被赋予了调整后的值。这两个变量替换了 populate_panel 方法(见上文)中的常量 COLOR_SQUARE_EDGE COLOR_SQUARE_SEPARATION

一旦这些乘数被初始化(注意,如果执行分辨率为 800x600,两者都将是 1.0F),实际的调整将等待 Load 事件触发,该事件反过来执行 adjust_GUI_to_resolution 方法。

  // ********************************** KnownColorsPalette_Load

  private void KnownColorsPalette_Load ( object       sender, 
                                         EventArgs    e )
      {
      adjust_GUI_to_resolution ( );
      }

如果执行分辨率与开发分辨率不同(即,变量 resolution_changed true),则 adjust_GUI_to_resolution 方法会修改每个控件的位置、大小和字体大小。

  // ********************************* adjust_GUI_to_resolution

  // http://cshark.wordpress.com/2009/06/01/
  //    how-to-change-form-size-depending-on-screen-resolution/

  private void adjust_GUI_to_resolution ( )
      {
      
      if ( resolution_changed ) 
          {
          float   font_size;
          int     height;
          int     width;
          float   x;
          float   y;

          font_size = this.Font.Size * 
                      screen_height_multiplier;
          height = ( int ) ( ( float ) this.Height * 
                             screen_height_multiplier );
          width = ( int ) ( ( float ) this.Width * 
                            screen_height_multiplier );
          x = this.Location.X * screen_width_multiplier;
          y = this.Location.Y * screen_height_multiplier;
          
          this.Height = height;
          this.Width = width;
          this.Font = new Font ( this.Font.FontFamily, 
                                 font_size, 
                                 this.Font.Style );

          foreach ( Control control in this.Controls )
              {
              font_size = control.Font.Size * 
                          screen_height_multiplier;
              height = ( int ) ( ( float ) control.Height * 
                                 screen_height_multiplier );
              width = ( int ) ( ( float ) control.Width * 
                                screen_height_multiplier );
              x = control.Location.X * screen_width_multiplier;
              y = control.Location.Y * screen_height_multiplier;

              control.Width = ( int ) Math.Ceiling ( ( double ) width );
              control.Height = ( int ) Math.Ceiling ( ( double ) height );
              control.Location = new Point ( ( int ) x, 
                                             ( int ) y );
              control.Font = new Font ( control.Font.FontFamily, 
                                        ( int ) font_size, 
                                        control.Font.Style );
              }                
          } 
      }

工具提示和菜单项的字体大小存在一个问题,我仍在研究中。当我有了解决方案后,我将再次修改本文。

参考文献

历史

  • 2010/11/26 - 修订文章以回应读者意见并纠正排版和逻辑错误
  • 2010/11/29 - 修改了工具,使其能够响应与开发分辨率不同的执行屏幕分辨率
© . All rights reserved.