RGB颜色空间以R(Red:红)、G(Green:绿)、B(Blue:蓝)三种基本色为基础,进行不同程度的叠加,产生丰富而广泛的颜色。
最能反映人眼感知的是灰度图像,要获取清晰的前景色,可转换为灰度图后来计算前景色。
色相环(colorcircle):是指一种圆形排列的色相光谱(SPECTRUM),色彩是按照光谱在自然中出现的顺序来排列的;暖色(WARMCOLOR)位于包含红色和黄色的半圆之内,冷色则包含在绿色和紫色的那个半圆内。
互补色:当两个颜色的色相,在色相环中的夹角为180°时,这两个颜色互为补色,也就是“互补色”。
通过RGB可方便地计算器互补色:用255减去对应的RGB后,得到的颜色即为互补色。
对比色:当两个颜色的色相,在色相环中的夹角大于130°时,这两个颜色就有明显的对比关系了,夹角越大,对比关系越强。
灰度图,每个像素点只能有一个值表示颜色,它的像素值在0到255之间,0是黑色,255是白色,中间值是一些不同等级的灰色,可以说灰度是黑与白之间的过渡色。
通过RGB,可方便计算出对应的灰度:
(299*R + 587*G + 114*B)/1000
一个图像经过上面公式计算后,即得到人眼感知的灰度图;当期望在背景色上显示清晰的前景色(文本)时,即可根据背景的灰度确定使用黑色或白色。
透明度混合(Alpha blending)是把透明度混合进个颜色中,方便颜色计算:
DestinationColor.rgb = (SourceColor.rgb * SourceColor.a) + (DestinationColor.rgb * (1 - SourceColor.a))
最常见的像素表示格式是RGBA8888,即(r,g,b,a),每个通道8位。为了表示方便,alpha通道一般记成正规化为0~1的浮点数:
如红色60%透明度就是(255, 0, 0, 153),正规化后为(255, 0, 0, 0.6);而 Premultiplied Alpha 则是把RGB通道乘以透明度也就是(r*a, g*a, b*a, a)
,也就是变成(153, 0, 0, 0.6)。
透明通道在渲染的时候通过Alpha Blending产生作用,混合后的颜色计算公式(如一个透明度为
a
s
a_s
as的颜色
C
s
C_s
Cs,渲染到颜色
C
d
C_d
Cd上):
C
o
=
a
s
∗
C
s
+
(
1
−
a
s
)
∗
C
d
C_o = a_s*C_s + (1 − a_s)*C_d
Co=as∗Cs+(1−as)∗Cd
如果颜色以Premultiplied Alpha形式存储(
C
s
C_s
Cs已经乘以透明度了),则混合公式变成了:
C
o
=
C
s
′
+
(
1
−
a
s
)
∗
C
d
C_o = C_s^′ + (1 − a_s)*C_d
Co=Cs′+(1−as)∗Cd
Premultiplied Alpha后的像素格式变得不直观,但混合的时候可以少一次乘法,这可以提高一些效率;除此之外最主要的是:没有Premultiplied Alph的纹理无法进行Texture Filtering(纹理过滤,除非使用最近邻插值):
Premultiplied Alpha最重要的意义是使得带透明度的图片纹理可以正常的进行线性插值;这样旋转、缩放或者非整数的纹理坐标才能正常显示,否则在透明像素边缘附近产生奇怪的颜色。
go中通过image.At
获取对应位置的颜色,然后通过color.RGBA()
返回对应的R,G,B值;但其值是Premultiplied Alpha的,要转换成灰度,需要:
(19595*r + 38470*g + 7471*b + 1<<15) >> 24
。
若只是把图片转换成黑白的,可直接使用color.GrayModel.Convert
来方便实现。
要根据背景色来确定文本颜色(以黑、白为例),提取文字显示位置处的颜色,转换为灰度值后,根据其值确定是黑色或白色。
func calcTextColor(dc *gg.Context, x, y float64) color.Color {
cur := dc.Image().At(int(x), int(y))
//gray := color.GrayModel.Convert(cur)
r, g, b, _ := cur.RGBA()
lum := (19595*r + 38470*g + 7471*b + 1<<15) >> 24
var txtColor color.Color
if lum > 158 {
txtColor = color.NRGBA{A: 255}
} else {
txtColor = color.NRGBA{R: 255, G: 255, B: 255, A: 255}
}
return txtColor
}
要更好地处理文本颜色,可多取几个点平均,或者每个文本处获取对应的显示颜色。
除计算文本颜色,便于显示文本外;还可以通过文本蒙版的方式显示:
func AddText(dc *gg.Context, txt string, tX int, tY int) {
w, h := dc.MeasureString(txt)
dc.SetHexColor("#CCCCCCCC")
minY := math.Max(0.0, float64(tY)-h)
dc.DrawRectangle(float64(tX), minY, w, h)
dc.Fill()
dc.SetHexColor("#333333")
dc.DrawString(txt, float64(tX), minY+h)
}
通过画一个与文本相同大小的底框(填充深色),然后在上面显示文本(浅色)。