半月前,回了个关于双色渐变填充矩形的帖(见http://topic.csdn.net/u/20091030/12/74cc8a21-1b63-4051-818c-d4c871b292f6.html),事后,闲来无事,于是,按那个回帖的思路,写了个任意角度双色渐变填充矩形的函数,感觉不错,比GDI+渐变填充速度快不少,又将其扩展为任意角度多色(ARGB)渐变填充矩形、文字,后来灵机一动,不如把这个由简入繁、由浅入深写的代码放到BLOG中,即可实现代码分享,满足实用要求,又可给初学者一个学习参考,于是先后发布了3篇BLOG文章:《实现任意角度渐变填充(一) -- 双色渐变填充矩形》
《实现任意角度渐变填充(一) -- 多色渐变填充矩形》
《实现任意角度渐变填充(三) -- 多色渐变填充文字》 文章写完后,却又发现,其实这些代码完全可以改编扩展为其它任意封闭图形,包括Windows区域的填充,但究竟是写成一个个单独的函数好,还是归并为一个类(例如一个扩展填充功能的TCanvas派生类)好呢?这2种方式各有优缺点,因此拿不定主意,特地在此征求各位的意见,当然,也包括对上述文章中代码的改进意见。 以下贴上上述文章中的2张界面截图:4个不同角度多色填充矩形的拼图:3色和5色填充文字(已消除锯齿):
《实现任意角度渐变填充(一) -- 多色渐变填充矩形》
《实现任意角度渐变填充(三) -- 多色渐变填充文字》 文章写完后,却又发现,其实这些代码完全可以改编扩展为其它任意封闭图形,包括Windows区域的填充,但究竟是写成一个个单独的函数好,还是归并为一个类(例如一个扩展填充功能的TCanvas派生类)好呢?这2种方式各有优缺点,因此拿不定主意,特地在此征求各位的意见,当然,也包括对上述文章中代码的改进意见。 以下贴上上述文章中的2张界面截图:4个不同角度多色填充矩形的拼图:3色和5色填充文字(已消除锯齿):
你看过Photoshop中的渐变填充没,那个效果用GDI+要如何实现呢》
带alpha通道的透明色的渐变色彩的产生方法:起始色彩为rgbaFrom, 终点色彩为rgbato,中间某个偏移的色彩rgba应该为:
rgbaf.premultiply;
rgbat.premultiply;
假设偏移比例为s(1> s> 0)
r = rf + s * (rt - rf)
g = gf + s * (gt - gf)
b = bf + s * (bt - bf)
a = af + s * (at - af)rgba.demutiply;楼主可以用带透明度的色彩试一下,然后和gdi+作一下对比。支持下楼主的这种共享行为~
GradientPathBrush.SetSurroundColors.
这个画刷的路径得是一个矩形。当然更复杂的也可以,这个是最简单的方法。
错了,应该是PathGradientBrush.呵呵~~~
我意思是但凡带透明度的色彩的渐变,需要在渐变之前premutiply一下,然后按照比例去计算渐变色彩,计算完成之后,还要把结果demutiply一下。否则的话,rgb3个通道的色彩会偏低,导致视觉上看起来偏暗色。
begin
// p^ = alpha * (color2 - color1) + color1
p.Alpha := ((I * (c2.Alpha - c1.Alpha)) shr 24) +c1.Alpha;
p.Red := ((I * (c2.Red - c1.Red)) shr 24) + c1.Red;
p.Green := ((I * (c2.Green - c1.Green)) shr 24) + c1.Green;
p.Blue := ((I * (c2.Blue - c1.Blue)) shr 24) + c1.Blue;
Inc(I, Delta);
Inc(p);
end; 貌似你在预先生成渐变色彩的时候,没有premutiply。且计算完成之后没有demutiply。
建议你用透明色渐变填充下,然后比对下gdi+的效果。
第一次是预生成渐变色彩缓冲区,也就是你引用的这段代码,其中的I就是你所说的偏移比例,只不过是扩大了0x1000000后用定点数处理的,这与你的r = rf + s * (rt - rf) 是完全一样的;
第二次是在实际填充时处理的,以双色填充为例,如果起始填充色或者结束填充的Alpha分量不等于255,其填充代码为:
// Alpha填充
if Data.AlphaBlend then
begin
for y := 1 to Data.Height do
begin
x0 := y0;
p := p0;
for x := 1 to Data.Width do
begin
c := ColorBuf[x0 shr 8]; // 取颜色缓冲区的颜色
p.Alpha := ((c.Alpha * (c.Alpha - p.Alpha)) shr 8) + p.Alpha;
p.Red := ((c.Alpha * (c.Red - p.Red)) shr 8) + p.Red;
p.Green := ((c.Alpha * (c.Green - p.Green)) shr 8) + p.Green;
p.Blue := ((c.Alpha * (c.Blue - p.Blue)) shr 8) + p.Blue;
Inc(Integer(p), Data.ScanDelta);
Inc(x0, Data.xDelta);
end;
Inc(Integer(p0), Data.ScanOffset);
Inc(y0, Data.yDelta);
end;
end
这上面的代码算法也是与你说的r = rf + s * (rt - rf)是一致的啊,不知还需要在哪里再premutiply
按照你的建议,我用GDI+和我写的多色填充代码进行了比较,下面是比较的代码和截图图,为了方便比较,分别用45度和135度画成对称的,左边是GDI+画的,右边是我的填充代码画的,都是用128的Alpha。
var
g: TGpGraphics;
brush: TGpLinearGradientBrush;
begin
brush := TGpLinearGradientBrush.Create(GpRect(0, 0, 200, 200), 0, 0, 45);
Brush.SetInterpolationColors([$80008000, $80FFFF00, $80FF0000, $800000FF, $80FFA500], [0, 0.25, 0.5, 0.75, 1]);
g := TGpGraphics.Create(Canvas.Handle);
g.FillRectangle(brush, 0, 0, 200, 200);
g.Free;
LinearGradientFillRect(Canvas, Rect(200, 0, 400, 200),
[$80008000, $80FFFF00, $80FF0000, $800000FF, $80FFA500], [0, 0.25, 0.5, 0.75, 1], 135);
end;
你把interpolate color换成alpha值不一样的再试一下。比如ARGB(200, 10, 200, 50)到ARGB(60, 200, 10, 20)就是渐变色之间的透明度也有差距的那种。
demultiply就是从有效值反向取到运算值的过程。这里渐变注重的应该是视觉效果上的渐变,也就是有效值的渐变。如果采用运算值当作有效值来渐变,其效果肯定会在预期的视觉效果之外。