渐变变得简单






4.80/5 (149投票s)
一个用于交互式设计渐变的工具。
引言
我必须承认,我只是对渐变画刷有了初步的了解,然后就匆忙开始编码了。但是,直到我开始玩这个工具,我才真正对渐变有了好的理解。在那时,我意识到我需要回去对这个主题做进一步的研究。所以,我一直在研究、升级、研究……你懂的。而且,因为我正在升级 Gradiator 应用程序,所以我想在这次研究/升级过程中分享我学到的东西。
在最新的升级中,我
- 更改了用户界面,希望使其更容易使用
- 添加了画刷管理器,可以加载/保存您喜欢的画刷
- 添加了颜色选择器,可以加载/保存颜色调色板
- 添加了状态栏,可以修改位置和大小,查看当前形状类型,以及循环切换形状
我不会深入解释图形、GDI+,甚至渐变;相反,我将简要概述每个重要概念,然后开始讲解如何创建本文中看到的渐变。其中许多都很简单,有些我走了弯路,有些我真的要抓耳挠腮,但总的来说,我已经能够创建一些相当不错的图形,您也可以。首先,我们需要对概念有一个基本的了解,所以本节将致力于此。
- 概念
- 渐变简介
- 线性渐变画刷
- 路径渐变画刷
- 混合
- 颜色混合
- Alpha 混合
- 使用 Gradiator
- 一个示例
- 参考文献和链接
我需要在此声明,对于某些应用程序,GDI+ 可能会很慢,因为它目前还没有利用许多图形卡内置的硬件加速功能。希望将来这个问题会得到解决。但在此之前,我们可以做一些事情来确保我们充分利用 GDI+。
- 使用双缓冲。
- 只重绘需要重绘的区域。
- 避免在
Panel
或PictureBox
等控件中绘制,而是使用父控件的客户端区域。 - 如果可以使用
DrawImageUnscaled
而不是DrawImage
,那么自动缩放功能很方便,但会消耗资源。
我确信还有其他提高性能的技巧,但这些是我个人觉得有帮助的。
在我使用/开发此应用程序的过程中,我添加了一些我认为有用且节省时间的功能。这个应用程序还有很多可以改进的地方,如果您有任何建议或想添加内容,请随时提出。我提供了默认和 Web 颜色调色板,以及一些有趣的画刷调色板。如果您想出了任何您觉得有趣的画刷,我很想看看。Barney 说:“分享”:)
一如既往,我希望这篇文章具有指导意义,应用程序能派上用场。如果您从中获得的知识和我撰写它时一样多,那么我们都发财了。
渐变简介
我对渐变的理解是,颜色在一个定义的角度上从一种颜色过渡到另一种颜色。韦氏词典的定义:“以均匀的斜率上升或下降”。
这里将介绍两种渐变类型:线性渐变画刷和径向渐变画刷。
图 1 中的渐变是最简单的线性渐变类型。它从白色“开始”,以 0 度角过渡到红色。绘制上述渐变的代码是
private void label1_Paint(object sender, PaintEventArgs e)
{
LinearGradientBrush lgb =
new LinearGradientBrush(label1.ClientRectangle, clr1, clr2, 0f, true);
e.Graphics.FillRectangle(lgb, label1.ClientRectangle);
lgb.Dispose();
}
图 2 中的渐变是最简单的径向渐变类型。它也从一种颜色开始过渡到另一种颜色,但轴线从一个中心点和一个中心颜色开始,然后向外过渡到结束颜色,同样是白色和红色,就像上面的图一样。绘制图 2 中渐变的代码是
private void label1_Paint(object sender, PaintEventArgs e)
{
GraphicsPath gp = new GraphicsPath();
gp.AddEllipse(label1.ClientRectangle);
PathGradientBrush pgb = new PathGradientBrush(gp);
pgb.CenterPoint = new PointF(label1.ClientRectangle.Width / 2,
label1.ClientRectangle.Height / 2);
pgb.CenterColor = Color.White;
pgb.SurroundColors = new Color[] { Color.Red };
e.Graphics.FillPath(pgb, gp);
pgb.Dispose();
gp.Dispose();
}
线性渐变画刷
现在我们已经了解了基础知识,我们可以了解更多关于渐变的功能。可以用来改变渐变绘制方式的两个有趣的方法是:SetSigmaShapeBell
和 SetTriangularShape
。
图 3 和图 4 显示了使用 SetSigmaShapeBell
和 SetBlendTriangularShape
方法的结果,以标签的中间为焦点,衰减率为 50%。从图中可以看出,钟形方法比其三角形对应物将中心颜色分布在更宽的范围内。
这两种画刷的构造函数都有参数,允许确定焦点和从该点开始的衰减率。下面的示例代码展示了如何使用线性渐变画刷绘制图 4 中的渐变。
private void label1_Paint(object sender, PaintEventArgs e)
{
LinearGradientBrush lgb =
new LinearGradientBrush(label1.ClientRectangle, clr1, clr2, 0f, true);
lgb.SetBlendTriangularShape(.5f, 1.0f);
e.Graphics.FillRectangle(lgb, label1.ClientRectangle);
lgb.Dispose();
}
我们以前见过这段代码,只是增加了高亮显示的文本。第一个参数定义焦点,第二个参数定义衰减比例。在这种情况下,我使用了 100% 的衰减率以获得戏剧性的/视觉效果。
您还可以使用一个附加属性来控制整体亮度和红绿蓝的比例,即伽马校正属性。有关伽马校正的详细讨论,请参阅此链接。
路径渐变画刷
路径渐变画刷使用与线性画刷相同的方法,行为也类似,但结果看起来不同,因为路径渐变画刷是径向工作的。在这里值得一提的是,在设置焦点时,焦点从外边缘(从 0 开始)向中心(到 1.0)工作。要设置中心点,请使用路径渐变的中心点属性。
在图 5 的左侧,我们使用 Set
Blend
TriangularShape
方法,并将焦点设置为 50%,焦点比例仍为 100%;在右侧,焦点已移至左上角。使用 Set
Blend
TriangularShape
和 SetSigmaShapeBell
方法以及 FocusScales
(这与两种方法的焦点参数类似)定义了渐变的缩放。注意:奇怪的是,FocusScales
的工作方式与其等效项正好相反,它们从中心(0)开始向外(1)工作。
用于生成图 5 左侧绘图的代码如下所示
private void label1_Paint(object sender, PaintEventArgs e)
{
GraphicsPath gp = new GraphicsPath();
gp.AddEllipse(label1.ClientRectangle);
PathGradientBrush pgb = new PathGradientBrush(gp);
pgb.CenterPoint = new PointF(label1.ClientRectangle.Width / 2,
label1.ClientRectangle.Height / 2);
pgb.CenterColor = Color.White;
pgb.SurroundColors = new Color[] { Color.Red };
pgb.SetBlendTriangularShape(.5f, 1.0f);
pgb.FocusScales = new PointF(0f, 0f);
e.Graphics.FillPath(pgb, gp);
pgb.Dispose();
gp.Dispose();
}
作为使用 Set
Blend
TriangularShape
方法和 FocusScales
属性的示例,我们将使用图 5 的左侧绘图。如果我们使用上面的代码,但将 FocusScales
属性更改为(.5f, .5f),焦点将是形状中心向外 75% 的位置。
混合
通过混合,我们可以定义自定义渐变,从而控制渐变的渲染方式。混合允许我们覆盖正常的混合行为,并在路径上设置点,我们在这些点定义备用的衰减率。我们仍然从起始颜色过渡到结束颜色,但正如您在下图中所看到的,在各个位置的衰减率可以更精确地控制渲染过程。
这里需要定义几个术语
- 位置 - 渐变路径上的一个点
- 因子 - 在定义位置的衰减率
在图 6 中,我已经编号了位置,结合下面的列表,您将更好地理解混合是如何工作的。这段代码用于生成图 6 的右侧绘图。
private void label1_Paint(object sender, PaintEventArgs e)
{
GraphicsPath gp = new GraphicsPath();
gp.AddEllipse(label1.ClientRectangle);
PathGradientBrush pgb = new PathGradientBrush(gp);
pgb.FocusScales = new PointF(0f, 0f);
Blend blnd = new Blend();
blnd.Positions = new float[] { 0f, .25f, .5f, .75f, 1f };
blnd.Factors = new float[] { 1f, 0f, 1f, 0f, 1f };
pgb.Blend = blnd;
e.Graphics.FillPath(pgb, gp);
pgb.Dispose();
gp.Dispose();
}
两种渐变都使用了相同的值绘制,根据我们到目前为止学到的知识,结果是可预测的。唯一的区别是 PathGradientBrush
,位置从中心点向外辐射,在指定的位置,您可以看到进行的过渡。
注意:这里有三件非常重要的事情
- 位置和因子的数量必须相同。
- 第一个位置必须是 0,最后一个位置必须是 1。
- 位置、因子和颜色的数量不应超过图形点列表中点的数量。如果超过,我将关闭颜色混合,或者 GDI+ 会抛出异常。(正在处理中。)
如果其中任何一个条件不满足,GDI+ 将抛出异常。
颜色混合
颜色混合的工作方式与常规混合非常相似,只是在每个位置,我们都可以使用颜色而不是衰减值。此时可以做一个有趣的观察,如果我们把点 2 移动到虚线所在的位置,这将把红色的结束位置移到箭头所在的位置,从而有效地使点 3 无用,这基本上是我们期望它行为的方式。但是,如果两个对象中的任何一个半透明,那么结果组合中的颜色就是两种颜色的混合,并且本身就是 alpha 混合。Alpha 混合将在下一节讨论。
图 7 显示了一个使用线性渐变画刷颜色混合功能的形状示例。您可以看到,每个位置的颜色都是彩虹的不同颜色,这些颜色均匀分布在 0 度渐变路径上的位置。下面的代码片段用于绘制图 7。
private void label1_Paint(object sender, PaintEventArgs e)
{
GraphicsPath gp = new GraphicsPath();
gp.AddEllipse(label1.ClientRectangle);
PathGradientBrush pgb = new PathGradientBrush(gp);
pgb.CenterPoint = new PointF(label1.ClientRectangle.Width / 2,
label1.ClientRectangle.Height / 2);
pgb.CenterColor = Color.White;
pgb.SurroundColors = new Color[] { Color.Red,
Color.Orange,
Color.Yellow,
Color.Green,
Color.Blue,
Color.Indigo,
Color.Violet };
pgb.SetBlendTriangularShape(.5f, 1.0f);
pgb.FocusScales = new PointF(0f, 0f);
e.Graphics.FillPath(pgb, gp);
pgb.Dispose();
gp.Dispose();
}
Alpha 混合
当我搜索 alpha 混合的良好解释时,我偶然发现 Toshihiro Horie 撰写的文章《Alpha Blending Tutorial》中的内容,它几乎说明了一切。
什么是 alpha 混合?它是一种将两种图像的颜色混合在一起形成最终图像的方法。彩虹出现在瀑布上的景象就是 alpha 混合的一个自然发生的例子。如果您将彩虹本身视为一张图像,将背景瀑布视为另一张图像,那么最终图像可以通过 alpha 混合这两张图像来形成。像素艺术家和程序员有时称 alpha 混合为“半透明”——它是同一回事。此外,当混合因子随时间动态变化时,alpha 混合被称为“交叉淡入淡出”或“溶解”。
要在您的绘图中添加 alpha 混合,只需调整 Color.FromArgb
方法调用中的第一个参数。这是 alpha 通道参数,其值可以从 0 到 255,0 表示完全透明,255 表示不透明。使用 alpha 混合时,事情会变得更有趣。它为问题增加了另一个维度。原因是当形状重叠时,它会形成两种颜色和每种颜色的不透明度级别的混合。我可以将其类比为一位艺术家在他调色板上混合颜色以形成新颜色。同样,您可以调整每种颜色的水平……添加一点白色,少一点黑色,等等。
使用 Gradiator
我在开发这个应用程序的过程中使用了它,并把它当作一种涂鸦板。我有一个想法,苦思冥想一段时间,然后开始涂鸦,去抽根烟,再想一会儿,然后保存它,尝试一下,然后回去调整,直到我得到我想要的东西。问题是,我越使用 Gradiator,我的技能就越好,我遇到了看起来更好的东西,但我就是无法最终确定那个该死的程序。关于对话框就是一个完美的例子,因为我想让它反映出渐变所能实现的效果。看看它,窗体上只有四个对象,两个是文本,另外两个是背景和按钮。
概述
简而言之,我们在这里做的是创建画刷来渲染我们的形状。当我们第一次创建形状时,分配给它的默认画刷是实心画刷,带有白色背景和边框。您可以使用工作区右侧选项卡控件中的属性编辑器来更改画刷的属性。我无法将所有需要的控件放在一个区域并留出足够的工作区空间,而且我也不想要一堆浮动窗口,所以我妥协了,按照我的设想划分了功能,最终得到了三个选项卡,它们是
- 通用属性 - 此选项卡包含所选画刷的通用属性
- 画刷 - 画刷管理
- 混合 - 混合属性编辑器
形状编辑器
我认为关于形状编辑器本身不需要说太多,它只是一个基本的图形编辑器,允许您创建、移动和调整一些基本形状的大小。
创建形状:单击所需的形状,将鼠标移动到画布区域,在要开始形状的位置按下鼠标左键,然后拖动直到达到结束点,然后释放鼠标按钮。
选择形状:单击形状边界内的任何位置。我选择形状时做了一些不同的事情,这在某些情况下很麻烦,但对于大多数编辑来说是一个非常好的功能。在我的选择逻辑中,如果您有两个对象,其中一个坐在另一个上面,我选择较小的那个;如果它们相同,我选择最后一个绘制的(z 顺序的顶部),但如果顶部的形状不透明,那么您将看不到下面的形状,除非您将其带到顶部,通过选择它并按鼠标右键选择“置于顶层”选项。我这样做是因为当我处理两个对象时,它允许我在定位背景中的形状时看到我在做什么。一旦您开始使用 Gradiator,您就会欣赏这个选项。
移动形状:按住鼠标左键不放,在形状边界内的任何位置,然后将其移动到新位置。
调整对象大小:按住鼠标左键不放,在调整大小的矩形内,然后拖动到新的形状。目前您只能向正方向调整大小。我仍在研究这个问题。
画刷管理器
画刷管理器是您创建并希望将来使用的画刷的存储库。您可以
- 创建一个新的画刷调色板。
- 加载调色板 - 启动时,在启动目录中加载一个名为“default palette.xml”的默认调色板。
- 导入调色板,将其附加到当前调色板。
- 将当前调色板保存到文件。
要使用调色板中的画刷,请使用形状编辑器创建形状原语,如果尚未选择,请选择它,然后单击画刷管理器列表中任意一个缩略图图像以将其与原语关联。这些原语,无论是实心的还是半透明的,都可以相互叠加以生成复合图像。现在,要保存一个画刷,只需选择它并右键单击,选择“保存画刷”选项,它将添加到画刷管理器的列表中。退出应用程序之前请务必保存。
混合编辑器
在这里,您可以真正发挥艺术才能,创作出精美的图像。我绝不是艺术家,但在花了一点时间研究混合之后,我更容易可视化我想要做什么以及如何实现本文所示的图像(即使是盲目的松鼠偶尔也能找到坚果)。
我在这里能给您的唯一真正建议就是玩它!
要使用编辑器,请选择一个对象,转到“混合”选项卡,然后通过复选框打开或关闭混合并选择所需的混合类型,常规混合是默认设置。混合仅在使用线性渐变或路径渐变画刷时可用,原因显而易见。混合的类型决定了哪些参数有意义,因为
LinearGradientBrush
- 位置和因子PathGradientBrush
- 颜色和位置
颜色选择器
对于颜色选择,我决定设计一种激活选择器的方法。为此,我在每个颜色属性旁边放了一个颜色样本。在某些情况下,这有点麻烦,但总的来说,它被证明是一个很好的解决方案。要选择颜色,请转到颜色选择器并选择一种颜色,然后对于您要修改的属性,单击样本。要将颜色从属性样本传输到颜色选择器中的主颜色,请右键单击属性样本。启动时,在启动目录中加载了一个名为“color palette.xml”的默认颜色调色板。
一个例子
刚开始的时候,我有一个小技巧,如果我感到困惑,我就会在我感到困惑的区域选择一个颜色/位置组合,并剧烈地改变颜色。这将给我一个大致的了解,混合覆盖了什么以及颜色分布(如果涉及不透明度)。这里的重点是,如果您不理解我解释的方式,或者我解释错了,请进入渐变,创建一个形状,并将其与此画刷关联,然后更改颜色或只是玩玩它。我尝试了细边框、粗边框以及许多不同的东西,然后才做到这一点。边缘的阴影让我费尽心思(我告诉过你我不是艺术家)。
所以,这是我创建这个的时候的想法
- 像画框一样,这样我就可以在这个区域叠加东西,或者根据需要将其混合
- 客户端区域和边框之间的平滑过渡
- 边框阴影,希望给它增加一点 3D 效果
好吧,我们开始吧。在我喝一杯脉动饮料并快速抽根烟的时候,你可以启动 Gradiator,或者不启动(抽吧,你有就抽)。然后我们开始。
好了,我回来了。
那么,让我们从中心开始,然后向外延伸
- 起始位置(0):让我们从浅/粉蓝色开始。
- 82:我们想 abrupt 地结束这个过渡,所以我们渐变到钢蓝色/灰色,稍微深一点,这样就不那么单调了。
- 85:我们想要一个深色的阴影区域,使其看起来凹陷。(85 - 82 = 3)所以我们有一个 3 像素的阴影区域。
- 86:在深色阴影区域之后,我们想要一个浅色区域,回到钢灰色,以表示它是突起的,并开始边缘的混合。
- 92:此时,我们已经从钢色渐变,并且我们想将其变暗,使颜色褪色。如果我们跳过这一步,过渡将是线性的,看起来像一个斜坡,而不是像上面示例中那样具有弯曲边缘的外观。
可以做出几点观察
- 如果我们更改前两种颜色并使其半透明,那么它将是一个真正的边框,因为客户端区域是半透明的!
- 注意透视。与顶部和底部相比,看看侧面的边框大小。如果它是正方形的,那就不是问题了。只是需要记住的一点。
好了,差不多了……祝你好运!
参考文献和链接
本文中的所有插图均使用 Gradiator 应用程序生成。
- CP 上有一篇关于 alpha 混合的文章:C# 透明度教程 - 第一部分,作者:Joe Pardue。
- 这是一篇非常好的文章,概述了本文中用于颜色混合的概念:使用纯色和渐变绘制概述。
- 如果您有兴趣了解颜色基础知识,这个网站很棒:RGB World。
- 颜色工具 - 参考图表、样本、软件等.
- 光和颜色的物理学.
- 一个适合初学者的好网站,涵盖了 GDI+ 的基础知识.
- 两个概念上与此类似的应用程序;都名为 GradientMaker:Robin Studio: Gradient Maker 和 Gradient Maker 应用程序。
- 一本关于图形编程的极佳书籍,总的来说,是《Graphics Programming with GDI+》,作者 Mahesh Chand,Microsoft .NET Development Series,ISBN 0-321-16077-0。