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

CGradient 和 CGradientCtrl

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (87投票s)

2002 年 4 月 26 日

14分钟阅读

viewsIcon

269367

downloadIcon

7755

一对用于渲染和编辑彩色渐变的类。

引言

这一对类对于需要颜色渐变或平滑过渡调色板的图形程序非常有用。CGradient 提供了创建颜色渐变的所有有用功能,这些渐变可以渲染到 CPalettesRGBTRIPLES 数组中。CGradientCtrl 是一个用于编辑 CGradient 的控件。

我编写这对类是因为我从未见过有现成的类能完成这项工作,而且它对我自己的 分形生成程序 至关重要,所以我从头开始编写。我只在 Corel DRAW 系列中见过这样的控件,而 CGradientCtrl 是 Corel 版本的克隆。

新版本内容

CGradient

  • 标记点位置以 0-1 的浮点比例定义
  • 渐变可以量化,制作出块状调色板
  • 10 种不同的插值方法可供选择
  • 现在可以选择背景色。

CGradientCtrl

  • 水平和垂直显示模式
  • 键盘支持
  • 标记点可以显示在渐变的左侧、右侧、两侧或两侧都不显示
  • 标记点的平滑拖动
  • 可自定义工具提示
  • 修复了渐变渲染中轻微的块状问题
  • 改进了渐变区域的渲染效果

注意:CGradient 和 CGradientCtrl 2.0 类自 1.0 版本以来进行了显著的修改,因此该类不能与 1.0 类直接替换。

 

CGradientCtrl 演示

我希望这个控件的使用方式相当直观。在演示中,我使用了一个稍微修改过的 Microsoft CFireWnd 来演示 CGradient 如何用于为 8 位位图创建调色板。我还提供了一些包含有趣颜色效果的示例渐变文件。

CGradient 功能

工作原理

我创建了一些术语来描述颜色渐变的功能。

该图显示了渐变的注解图。起始标记点和结束标记点固定在渐变的两个端点,中间是可移动的颜色标记点。每个标记点都有一个关联的颜色,颜色渐变在两个最近的标记点之间进行插值。渐变还有一个可选的背景色,它可以成为渲染调色板中的第一个条目。


插值方法

渐变类现在提供了多种插值方法可供选择

线性插值

在线性插值中,每个通道被单独处理,并且通过加权平均来计算实际的通道值,以在标记点之间移动。该方法可能导致标记点处的过渡看起来相当尖锐。

余弦插值

余弦插值通过使用余弦波的 180° 段来提供比线性插值更平滑的渐变。同样,每个通道都独立于其他通道进行插值。该方法倾向于产生更平滑过渡的调色板,尤其是在标记点周围。

平头开始、平头结束、平头中间插值

 

平头开始
 
平头结束
 
平头中间
 

这三种平头插值方法用恒定颜色填充标记点之间的间隙。平头开始和结束分别使用段的起始和结束标记点的颜色作为恒定颜色,而平头中间插值则取两个标记点的平均值。这些方法使调色板看起来非常块状。

反向插值

反向插值是反向的线性插值!与从第一种颜色开始并淡化到最后一种颜色(对于每个标记点)不同,插值从最后一种颜色开始并反向进行。这种方法使调色板看起来非常零散。

HSL 顺时针、HSL 逆时针、HSL 短路径、HSL 长路径插值

在使用 HSL 插值方法时,颜色是使用色轮进行插值的。饱和度、亮度和色调值是线性插值的。由于色调值是循环类型的值,因此总有两种方法可以从圆上的一个点到达另一个点 - 一条长路径和一条短路径。使用顺时针和逆时针模式,色调总是分别通过顺时针或逆时针路径进行插值。在使用最短和最长模式时,色调总是通过最短或最长路径进行插值。这些模式允许您在标记点之间创建艳丽的彩虹效果。


CPeg 类

每个标记点都有一个索引。结束标记点和背景有特殊的索引。实际值无关紧要,因为我在 "Gradient.h" 文件中 #defined 了所有符号。

标记点 符号 索引(符号是 #defined,因此无关紧要)
背景 背景 -4
开始标记点 STARTPEG -3
结束标记点 ENDPEG -2
-1

标记点的信息存储在 CPeg 对象的 CArray 中。CPeg 是一个包含渐变标记点数据的类。

class CPeg : CObject
{
public:
  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

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

CGradient 类

CGradient 公开继承自 MFC 的 CObject 类

构造与析构

CGradient()

创建一个不带任何标记点的 CGradient,只有开始标记点和结束标记点,它们默认分别设置为黑色和白色。

CGradient(CGradient &gradient)

复制构造函数。

virtual ~CGradient()

析构函数

运算符

CGradient& operator =(CGradient &src)

赋值运算符

属性

int GetPegCount() const

检索渐变中的标记点数量。这不包括开始和结束标记点。

const CPeg GetPeg(int iIndex) const

返回给定索引处的标记点的副本。如果 iIndex固定标记点(STARTPEGENDPEGBACKGROUND),则会返回这些标记点的值。

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

将给定索引处的标记点设置为此颜色和位置信息。函数返回标记点的新索引,以防它被移动。如果 iIndex 指向一个固定标记点(如 BACKGROUNDSTARTPEGENDPEG),则返回 -1。

int SetPeg(int iIndex, CPeg peg)

SetPeg(int, COLORREF, int) 类似,只是标记点是从 CPeg 设置的,而不是位置和颜色数据。

int AddPeg(COLORREF crColour, float iPosition)

添加一个具有颜色 crColour 和位置 iPosition 的标记点。如果 iPosition 小于 0 或大于渐变大小,它将被截断以使其落在渐变边界内。返回值是新标记点的索引。

int AddPeg(CPeg peg)

将一个标记点添加到渐变中,返回值是标记点的索引。

void RemovePeg(int iIndex)

删除索引为 iIndex 的标记点。

int IndexFromPos(float pos)

返回出现在插值段开始处的标记点的索引,该插值段包含 pos

void SetStartPegColour(const COLORREF crColour)
void SetEndPegColour(const COLORREF crColour)
void SetBackgroundColour(const COLORREF crColour)

这三个函数都用于设置各自固定标记点的颜色。

COLORREF GetStartPegColour() const
COLORREF GetEndPegColour() const
COLORREF GetBackgroundColour() const

这三个函数都将返回各自标记点的颜色。

void SetUseBackground(const BOOL bUseBackground)

设置渐变的背景模式。如果 bUseBackgroundTRUE,则调色板的第 0 个条目将设置为背景色;如果 bUseBackgroundFALSE,则第 0 个条目将是渐变区域的第一个颜色。

BOOL GetUseBackground() const

返回背景模式。请参阅 SetUseBackground...

InterpolationMethod GetInterpolationMethod() const

检索渐变使用的插值方法。它可以是

CGradient::Linear,
CGradient::FlatStart,
CGradient::FlatMid,
CGradient::FlatEnd,
CGradient::Cosine,
CGradient::HSLRedBlue,
CGradient::HSLBlueRed,
CGradient::HSLShortest,
CGradient::HSLLongest
CGradient::Reverse

void SetInterpolationMethod(const InterpolationMethod method)

将插值方法设置为上面列出的值之一。

void SetQuantization(const int entries)

SetQuantization 允许您设置调色板应量化的条目数。因此,8 个条目将使调色板有 8 个离散的块。如果 entries 为 -1,则禁用量化。

int GetQuantization() const

返回调色板应量化的条目数。如果返回 -1,则表示未激活量化。

操作

void MakePalette(CPalette *lpPal);

创建一个 Windows 调色板到指针中,并用渐变的 256 种颜色填充调色板。

void Make8BitPalette(RGBTRIPLE *lpPal);

用渐变颜色填充一个包含 256 个 RGBTRIPLE 的数组。

void MakeAllEntries(RGBTRIPLE *lpPal, int iEntryCount);

用颜色渐变填充 iEntryCount 大小的 RGBTRIPLE 数组 lpPal。此函数允许您灵活地创建任何类型的调色板,从 2 色调色板到 65525 色调色板。

COLORREF ColourFromPosition(int iPos);

返回给定位置的渐变颜色。

void Serialize(CArchive &ar);

标准序列化函数。

CGradientCtrl 功能

CGradientCtrl 是一个派生自 CWnd 的控件,允许查看和编辑 CGradient 类。

功能...

  • 完整的键盘支持
  • 现在支持水平和垂直模式。
  • 将可移动标记点显示为一系列箭头,将结束标记点显示为一对正方形。
  • 该控件将渐变显示为垂直列,颜色平滑过渡。
  • 拖动标记点时,渐变会动态更新。
  • 靠得很近的标记点会堆叠在一起
     
  • 标记点可以显示在左侧、右侧、两侧都不显示或两侧都显示
  • 可自定义工具提示支持。

标记点侧

无标记点 - 只读! 左/底部侧标记点 右/顶部侧标记点 两侧标记点

键盘支持

密钥 函数
选项卡 选择下一个标记点
向上、向左 将标记点向上移动至起始位置
向下、向右 将标记点移向结束位置
Home 将标记点移至起始位置
End 将标记点移至结束位置
Del、Backspace 删除标记点
回车、空格 相当于双击标记点
Insert 复制标记点

CGradientCtrl 类

CGradient 控件派生自 MFC 的 CWnd 类

然后,当你开始迭代 2(这是构建迭代的开始)时,你可能想要复制测试用例并将它们重新分类到迭代 2。这还允许对测试用例进行粒度跟踪,并允许你说某个测试用例在一个迭代中是准备好的,但在另一个迭代中不是。同样,如何做到这一点取决于你以及你希望如何报告。 “场景”部分提供了更多细节。

CGradientCtrl();

默认构造函数。

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

在窗口 pParentWnd 中、矩形 rect 内创建控件,并使用 ID nID。返回值表示函数的成功与否。

属性

void SetGradientWidth(int iWidth);

此函数设置绘制渐变时控件的宽度。iWidth 指定控件应有的像素宽度。如果将 GCW_AUTO 传递给 iWidth,控件的宽度将自动调整以最适合其窗口。

int GetGradientWidth();

返回渐变设置为绘制的宽度。该函数可能返回 GCW_AUTO,表示渐变宽度将绘制为最适合控件窗口。

int GetSelIndex();

返回当前选定标记点的索引。此返回值可能为 STARTPEGENDPEGNONE

int SetSelIndex(int iSel, BOOL bUpdate);

将当前选定的标记点设置为索引 iSel。此值必须是介于 0 和可移动标记点数量之间的标记点索引,或者为 STARTPEGENDPEGNONE。如果 bUpdateTRUE,则控件会重绘。返回值是先前选定标记点的索引。

CPeg GetSelPeg();

返回一个代表当前选定标记点的 CPeg 结构。如果当前选定标记点是结束标记点,则 CPeg 类中的位置值将设置为 -1。

CGradient& GetGradient();

返回控件工作的 CGradient 的引用。

void SetGradient(CGradient src)

将源渐变复制到控件嵌入的 CGradient 中。

void ShowTooltips(BOOL bShow = true)

切换工具提示的开启或关闭

CGradient::Orientation GetOrientation() const

返回控件的方向模式。该函数可以返回 CGradient::ForceHorizontalCGradient::ForceVerticalCGradient::Auto。对于 CGradient::Auto,方向会自动选择。因此,如果宽度大于高度,控件将以水平模式显示。

void SetOrientation(CGradient::Orientation orientation)

设置控件的方向模式。可用的模式有 CGradient::ForceHorizontalCGradient::ForceVerticalCGradient::Auto

void SetPegSide(BOOL setrightup, BOOL enable)

设置控件的标记点侧模式。因此,如果 setrightupTRUE,enable 将启用或禁用右侧或顶部侧的标记点绘制。如果 setrightupFALSE,enable 将启用或禁用左侧或底部侧显示的标记点。

BOOL GetPegSide(BOOL rightup) const

检索由 rightup 指定的侧面的标记点侧模式。因此,如果 rightupTRUEGetPegSide 将返回一个 BOOL 值,表示是否要在控件的右侧或上侧显示标记点。

void SetTooltipFormat(const CString format)

将工具提示格式字符串设置为 format 的值。工具提示格式字符串是一段多行标记文本,每行对应一个可能的工具提示。默认的英式工具提示字符串如下所示:

&SELPOS\n位置: &SELPOS 颜色: R &R G &G B &B\n颜色: R &R G &G B &B\n颜色: R &R G &G B &B\n双击添加新标记点

该字符串由一系列标记组成

Token 含义
&SELPOS 显示选定文本的位置。
&R 以十进制(0-255)显示选定标记点的红色分量。
&G 以十进制(0-255)显示选定标记点的绿色分量。
&B 以十进制(0-255)显示选定标记点的蓝色分量。
&HEXR 以两位十六进制(00-FF)显示选定标记点的红色分量。
&HEXG 以两位十六进制(00-FF)显示选定标记点的绿色分量。
&HEXB 以两位十六进制(00-FF)显示选定标记点的蓝色分量。
&FLOATR 以三位小数精度的浮点值(0.0 到 1.0)显示选定标记点的红色分量。
&FLOATG 以三位小数精度的浮点值(0.0 到 1.0)显示选定标记点的绿色分量。
&FLOATB 以三位小数精度的浮点值(0.0 到 1.0)显示选定标记点的蓝色分量。
&& 显示一个安培符 - "&"

每一行都分配给不同的工具提示,以应对不同的情况

Line 用途
1 标记点被拖动时使用
2 鼠标悬停在标记点上时使用。
3 鼠标悬停在开始标记点上时使用
4 鼠标悬停在结束标记点上时使用
5 鼠标悬停在渐变区域时使用

这种标记字符串系统使控件在国际化方面相当中立。标记字符串可以存储在字符串表中,以便自动选择。

CString GetTooltipFormat() const;

返回当前的工具提示格式字符串。

操作

void DeleteSelected(BOOL bUpdate);

删除选定的标记点。如果选定的标记点不是可移动标记点,则不执行任何操作。如果 bUpdateTRUE,则重绘控件。

int MoveSelected(int iNewPos, BOOL bUpdate);

将选定的标记点移动到新位置 iNewPos。如果选定的标记点不是可移动标记点,则不执行任何操作。如果 iNewPos 小于 0 或大于渐变大小。如果 bUpdateTRUE,则重绘控件。返回值是移动后的标记点的新索引。

COLORREF SetColourSelected(COLORREF crNewColour, BOOL bUpdate);

将选定标记点的颜色设置为新的 crNewColour。如果 bUpdateTRUE,则重绘控件。返回值是选定标记点之前的颜色。

通知消息

渐变控件可能会将其父窗口发送任何这些通知消息。CGradientCtrl 使用两个 NMHDRsPegNMHDR 和 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
};

GC_SELCHANGE

值:1

通知选定的标记点已更改。发送一个 PegNMHDR,其中包含有关当前选定标记点的信息。返回值将被忽略。

GC_PEGMOVE

值:2

通知颜色标记点当前正在被移动。发送一个 PegNMHDR,其中包含有关正在拖动的标记点的信息。返回值将被忽略。

GC_PEGMOVED

值:3

通知移动操作刚刚完成。发送一个 PegNMHDR,其中包含有关被拖动的标记点的信息。返回值将被忽略。

GC_PEGREMOVED

值:4

通知标记点已被移除。发送一个 PegNMHDR,其中包含有关被移动的标记点的信息。返回值将被忽略。

GC_CREATEPEG

值:5

通知用户双击了渐变上的空白区域。此事件也可以通过用户按“Insert”键触发。您可能希望弹出一个对话框询问新标记点的颜色,或者像演示一样,创建一个具有该位置渐变颜色的标记点。发送一个 DoubleClickCreateNMHDR,其中包含双击的位置以及该点的颜色。

GC_EDITPEG

值:6

通知用户双击了一个标记点。此事件也可以通过用户按空格键或回车键触发。此消息用于请求宿主显示编辑对话框。发送一个 PegNMHDR,其中包含有关要编辑的标记点的信息。返回值将被忽略。

GC_CHANGE

值:7

当用户对渐变进行任何更改时发送此通知。这是对所有上述通知的通用捕获。发送一个 PegNMHDR,其中包含有关已更改的标记点的信息。返回值将被忽略。

使用 CGradientCtrl

在您的对话框模板中,您需要创建一个自定义控件()。然后您需要配置它。将窗口 ID 设置为类似 IDC_GRADIENT 的值。控件的标题无关紧要。将类设置为 "MFCGradientCtrl"。将样式保留为 0x50010000,如果您希望控件具有演示中所示的客户端边缘,则需要将 ExStyle 设置为 0x200。VC 应该看起来像这样...

现在,在您的对话框的源代码中,在 DoDataExchange 中添加此行

DDX_Control(pDX, IDC_GRADIENT, m_wndGradientCtrl);

完成后,代码应该如下所示

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

在头文件中添加此行

CGradientCtrl m_wndGradientCtrl;

您可以将其放在文件中的任何位置。它应该可以正常工作。我把它放在对话框数据部分正下方。

即将进行的改进

当然,我将努力修复所有报告的错误。很快就会添加透明度支持!

修订

修订版 2.0:添加了插值方法、量化、键盘支持、通知消息、工具提示、水平和垂直方向模式。

修订版 1.0B:修复了演示中的资源泄漏(我认为)。其他小改动。

修订版 1.0A:添加了消息 GC_CHANGE,并成功测试了 CGradientCtrl::Create

英国制造,引以为傲!

抱歉,如果大量使用 colour 而非 color 冒犯了您!:-)

© . All rights reserved.