我一直在对程序进行逆向工程,最近遇到了一个函数,该函数旨在创建一种半透明外观的颜色,用于文本选择。它通过将RGB转换为YUV,更改Y(亮度)分量,然后再转换回RGB来实现此目的。
uint32_t CalcSelectionColor(uint32_t bgr)
{
double r,g,b;
double y,u,v;
r = (bgr >> 0) & 0xFF;
g = (bgr >> 8) & 0xFF;
b = (bgr >> 16) & 0xFF;
/* RGB to YUV */
y = 0.299*r + 0.587*g + 0.114*b;
u = (b-y) * 0.565 * 0.5;
v = (r-y) * 0.713 * 0.5;
/* lower brightness? */
y = 255.0 - y;
/* YUV to RGB */
r = y + 1.403*v;
g = y - 0.344*u - 0.714*v;
b = y + 1.77*u;
return ((uint8_t)(b) << 16) | ((uint8_t)(g) << 8) | ((uint8_t)(r));
}
作为对计算机图形学知识非常了解的人,我想更详细地介绍一下转换之间的作用以及更广泛意义上的实际预期效果。这是调整颜色或其他东西亮度的常用方法吗?如果我输入0x00FF00,则得到的结果是0x1E9D1E
此代码中使用的公式类似于从RGB到YUV并返回的Julien转换:
Transformation from RGB to YUV:
Y = 0.299R + 0.587G + 0.114B
U'= (B-Y)*0.565
V'= (R-Y)*0.713
Transformation from YUV to RGB:
R = Y + 1.403V'
G = Y - 0.344U' - 0.714V'
B = Y + 1.770U'
但是,您的代码中的公式有些不同。尽管后向变换相同,但前向变换对于U和V分量都有一个附加的乘数0.5。还有一个亮度分量的微不足道的操作]
y = 255.0 - y
只是反转亮度。那么,这里发生了什么?
如果使用普通的Julien RGB-> YUV变换,则将颜色表示为亮度Y和两个色调分量U和V的组合,这两个分量定义了此图所示的颜色:
但是,在您的代码中,U和V分量也都乘以0.5。这意味着,在此UV平面上,您从任何给定的颜色移近两倍于原点(0,0)。例如,如果初始颜色是具有UV坐标(-0.4,0.3)的A,那么您将获得具有UV坐标(-0.2,0.15)的新颜色B。同样,颜色C(0.2,-0.3)变为颜色D(0.1,-0.15):
之后,您将颜色的亮度反转,使深色变成明亮,而深色变成黑暗。这就是您的代码的效果。
这不是很普遍,但这是一种非常好的方法。诸如HSL / HSV之类的常用模型不能正确表示强度,并且会出现一些奇怪的分段线性的东西,并带有色相/颜色。 YUV是一个非常好的色彩空间,代表沿一个轴的强度和垂直平面中的色度(色相/颜色)。
[通常不修改也不要调整(至少钳位)调整Y和V的情况下,通常会修改Y,这有点可疑,因为在极端情况下(Y = 0黑色,Y =全白),U和V的范围有限(端点上完全没有范围) 。否则,应用它们会将您带到RGB立方体之外,并在您返回RGB时导致虚假的裁剪结果。但是这里的技巧非常聪明。代码为inverting Y,同时保持色度固定,因此U和V的输入范围限制接近黑色,将自动确保它们在输出中大致正确,反之亦然。
如Alex所指出的,此处的代码还将色度值减半,从而降低了色彩饱和度。这可能是为了避免上述剪裁问题,但这不是必需的。但这也许也是预期的视觉效果的一部分。
所以,TL; DR:效果是反转强度/亮度和饱和度减半。