在二维数组中创建线性渐变

Posted

技术标签:

【中文标题】在二维数组中创建线性渐变【英文标题】:Creating a linear gradient in 2D array 【发布时间】:2009-02-06 18:15:57 【问题描述】:

我有一个类似于 2D 位图的数组,比如说 500*500 个值。我正在尝试在数组上创建线性渐变,因此生成的位图看起来像这样(灰度): (来源:showandtell-graphics.com)

输入将是要填充的数组、两个点(如 Photoshop/GIMP 中渐变工具的起点和终点)以及将使用的值范围。

我目前最好的结果是这样的:

alt text http://img222.imageshack.us/img222/1733/gradientfe3.png

...这与我想要实现的目标相去甚远。它看起来更像是径向渐变。

创建这种渐变的最简单方法是什么?我打算用 C++ 实现它,但我想要一些通用算法。

【问题讨论】:

【参考方案1】:

这确实是一个数学问题,因此它是否真的“属于” Stack Overflow 可能存在争议,但无论如何:您需要将图像中每个点的坐标投影到渐变轴上并使用该坐标来确定颜色。

在数学上,我的意思是:

    假设起点是 (x1, y1),终点是 (x2, y2) 计算A = (x2 - x1)B = (y2 - y1) 计算起点C1 = A * x1 + B * y1,终点C2 = A * x2 + B * y2C2应大于C1) 对于图像中的每个点,计算C = A * x + B * y

    如果C <= C1,使用起始颜色;如果C >= C2,使用结束颜色;否则,使用加权平均值:

    (start_color * (C2 - C) + end_color * (C - C1))/(C2 - C1)

我做了一些快速测试来检查这是否基本有效。

【讨论】:

也许我很笨,但我做不到。它适用于水平和垂直渐变,但不适用于有角度的渐变。无论如何 +1。 刚刚仔细检查,在 Mathematica 中,它对我测试的所有角度都非常有效,所以我相当肯定公式本身是有效的。你遇到了什么麻烦? @David Zaslavsky 你能告诉我在第 3 节和第 4 节中你的线性渐变绘制算法发生了什么吗?不知道是什么C,C1和C2(是那里计算的?) @ka_ka8 C 是在您希望渐变运行的方向上增加的坐标。 C1C2 是该坐标的值,对应于起点和终点。【参考方案2】:

在您的示例图像中,看起来您有一个径向渐变。这是我对您需要的步骤的即兴数学解释。抱歉,其他答案在实施方面更好。

    定义一个线性函数(如 y = x + 1),其域(即 x)从您想要开始的颜色到您想要结束的颜色。您可以根据 Ox0 到 OxFFFFFF(对于 24 位颜色)的范围来考虑这一点。如果您想处理亮度等问题,则必须对范围(即 y 值)进行一些技巧。 接下来,您需要在您拥有的矩阵上映射一个向量,因为这定义了颜色将更改的方向。此外,线性函数定义的颜色值将分配给沿向量的每个点。向量的起点和终点也定义了 1 中域的最小值和最大值。您可以将向量视为渐变的一条线。 对于矩阵中的每个单元格,可以从向量中为颜色分配一个值,其中单元格的垂线与向量相交。请参见下图,其中 c 是单元格的位置, 。是交点。如果你假装那个颜色在。是红色,那么这就是您将分配给单元格的内容。
| C | | 向量:____.______________ | |

【讨论】:

我在考虑这些方面的事情,但我希望我能找到一些更简单的解决方案。我花了一个半小时得出正确的公式来计算 A->B 线上的正确点 :) 好东西,如果你有机会,把它贴在你的问题中。我相信有人会觉得它很有用。【参考方案3】:

我将发布我的解决方案。

int ColourAt( int x, int y )

  float imageX = (float)x / (float)BUFFER_WIDTH;
  float imageY = (float)y / (float)BUFFER_WIDTH;

  float xS = xStart / (float)BUFFER_WIDTH;
  float yS = yStart / (float)BUFFER_WIDTH;
  float xE = xEnd / (float)BUFFER_WIDTH;
  float yE = yEnd / (float)BUFFER_WIDTH;
  float xD = xE - xS;
  float yD = yE - yS;

  float mod = 1.0f / ( xD * xD + yD * yD );

  float gradPos = ( ( imageX - xS ) * xD + ( imageY - yS ) * yD ) * mod;

  float mag = gradPos > 0 ? gradPos < 1.0f ? gradPos : 1.0f : 0.0f;

  int colour = (int)( 255 * mag );
  colour |= ( colour << 16 ) + ( colour << 8 );
  return colour;

为了加快速度,缓存派生的“方向”值(提示:预乘以 mag)。

【讨论】:

【参考方案4】:

这个问题有两个部分。

    给定两种颜色 A 和 B 以及某个百分比 p,确定从 A 到 B 的“百分比”是什么颜色。

    给定平面上的一个点,求该点在给定线上的正交投影。

第 2 部分中的给定线是您的渐变线。给定任意点 P,将其投影到渐变线上。假设它的投影是 R。然后计算出 R 距离渐变段的起点有多远,作为渐变段长度的百分比。在上面第 1 部分的函数中使用这个百分比。这就是 P 应该的颜色。

请注意,与其他人所说的相反,从第 1 部分开始,您不能只将颜色视为函数中的常规数字。这几乎肯定不会达到您想要的效果。您所做的取决于您使用的色彩空间。如果你想要一个 RGB 渐变,那么你必须分别查看红色、绿色和蓝色的颜色分量。

例如,如果您想要“介于”纯红色和蓝色之间的颜色,那么您正在处理的是十六进制表示法

ff 00 00

00 00 ff

可能你想要的颜色是这样的

80 00 80

这是一种漂亮的紫色。您必须分别平均每个颜色分量。如果您尝试直接对十六进制数 0xff0000 和 0x0000ff 进行平均,则会得到 0x7F807F,它是中等灰色。我猜这至少可以解释上面图片的部分问题。

或者,如果您在 HSV 颜色空间中,您可能只想调整色调组件,而让其他组件保持原样。

【讨论】:

我只使用灰度,这大大简化了颜色混合。

以上是关于在二维数组中创建线性渐变的主要内容,如果未能解决你的问题,请参考以下文章

如何将多个线性渐变添加到 CSS 背景?如果

渐变色

CSS中的渐变——线性渐变

CSS3中线性渐变,怎么让它在div中,往两边渐变

如何给SVG填充和描边应用线性渐变

Canvas 线性渐变原理详解