`
sogotobj
  • 浏览: 639186 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

GDI+ 在Delphi程序的应用 -- 可调节的文字阴影特效

阅读更多

利用GDI+输出文字阴影效果有多种方法,最简单的就是第一次输出有偏移的灰色文字,第二次输出正常文字。下面是仿C#文字输出例子里的代码片断,输出了这种带阴影的文字:

serifFontFamily:=TGpFontFamily.GenericSerif;
//Loadthefontswewanttouse
titleFont:=TGpFont.Create(serifFontFamily,60);
//Loadtheimagetobeusedforthetexturedtextfromtheexe'sresourcefork
textImage:=TGpBitmap.Create('....mediamarble.jpg');
textTextureBrush:
=TGpTextureBrush.Create(textImage);
//Setupshadowbrush-makeittranslucent
titleShadowBrush:=TGpSolidBrush.Create(ARGB(70,kcBlack));

//Drawatexturedstring
s:='GraphicsSamples';
g.DrawString(s,titleFont,titleShadowBrush,
15,25);
g.DrawString(s,titleFont,textTextureBrush,
10,20);

效果图如下(作了适当缩小):

显然,这种文字阴影效果不太令人满意,没有那种半阴影的效果。

网上介绍了一种“借助GDI+的反走样能力生成透明的阴影与半阴影”的文字阴影方法,其原理是将要输出的文字输出按一定比例缩小,以某种半灰调输出到一个按同样比例缩小的内存位图中,然后设置画布插值模式为高质量双三次插值法,再将位图放大到实际画布大小输出,因为双三次插值放大使文本的边缘产生Alpha模糊,这样就出现阴影与半影效果,下面是这种方法的Delphi代码:

procedurePaintText(g:TGpGraphics);
var
brush:TGpLinearGradientBrush;
font:TGpFont;
fontFamily:TGpFontFamily;
r:TGpRect;
bmp:TGpBitmap;
bg:TGpGraphics;
m:TGpMatrix;
begin
fontFamily:
=TGpFontFamily.Create('TimesNewRoman');
font:
=TGpFont.Create(fontFamily,50,[fsBold],utPixel);
r:
=GpRect(Form1.PaintBox1.ClientRect);
brush:
=TGpLinearGradientBrush.Create(r,kcBlue,kcAliceBlue,90);
//填充渐变背景
g.FillRectangle(brush,r);
//建立内存位图,其大小是画布的1/4
bmp:=TGpBitmap.Create(r.Widthshr2,r.Heightshr2,g);
bg:
=TGpGraphics.Create(bmp);
bg.TextRenderingHint:
=thAntiAlias;
//按1/4缩放输出阴影文字,并平移(3,3)
m:=TGpMatrix.Create(0.25,0,0,0.25,3,3);
bg.SetTransform(m);
bg.DrawString(
'文字阴影特效',font,Brushs[ARGB(128,0,0,0)],10,r.Height/3);
//设置插值模式为高质量双三次插值法
g.InterpolationMode:=imHighQualityBicubic;
g.TextRenderingHint:
=thAntiAlias;
//放大输出阴影位图到画布
g.DrawImage(bmp,r,0,0,bmp.Width,bmp.Height,utPixel);
//输出正常文字
g.DrawString('文字阴影特效',font,Brushs.White,10,r.Height/3);
m.Free;
bg.Free;
bmp.Free;
brush.Free;
font.Free;
fontFamily.Free;
end;

代码中已经作了注释,就不再详细讲解,下面是运行效果截图,图的上部分是带阴影的文字输出截图,下部分是单独的阴影输出截图:

从效果图,特别是从上半部分截图看,文字的阴影效果要比前面简单的文字阴影效果好,其阴影边缘有一定的半影效果;但是从下半部分单独的文字阴影输出图看,还是觉得不尽人意。首先是边缘模糊效果不太明显,其次是阴影半影部分,也就是边缘模糊部分有太强的放大痕迹,再次就是此方法无多大调节余地,想得到更明显的半影效果的办法就是进一步缩小阴影文字比例,但由此带来的锯齿状显然更突出。看来,想要得到好的文字阴影效果,如PhotoShop式样中的投影效果,得另辟途径。

经过研究,PhotoShop式样中的投影效果其实就是一种高斯模糊效果。一般的图像高斯模糊是对图像各像素的RGB用高斯卷积矩阵进行卷积处理(关于高斯模糊请看我的文章《GDI+ 在Delphi程序的应用 -- 图像卷积操作及高斯模糊》),而要处理文字阴影效果只需要建立一个透明的32位ARGB格式内存位图,将文字用一定的阴影色调输出到位图,然后用高斯模糊矩阵对位图的Alpha字节进行卷积处理,就可达到很好的效果。下面是函数代码:

//卷积处理阴影效果。Data:GDI+位图数据,要求32位ARGB格式; Source: 复制的源
//ConvolMatrix:卷积矩阵;MatrixSize:矩阵大小,Nuclear:卷积核(必须大于0)
procedure MakeShadow(Data: TBitmapData; Source: Pointer;
ConvolMatrix: array of Integer; MatrixSize, Nuclear: LongWord);
var
Radius,mSize,rSize:LongWord;
x,y:LongWord;
Width,Height:Integer;
Matrix:Pointer;
asm
pushesi
pushedi
pushebx

movesi,edx
//esi=Source+3(Alphabyte)
addesi,3
movedi,[eax
+16]//edi=Data.Scan0
movMatrix,ecx//Matrix=ConvolMatrix
movecx,MatrixSize
movedx,ecx
dececx
movebx,[eax]
subebx,ecx
movWidth,ebx
//Width=Data.Width-(MatrixSize-1)
movebx,[eax+4]
subebx,ecx
movHeight,ebx
//Height=Data.Height-(MatrixSize-1)
shrecx,1
movRadius,ecx
//Radius=MatrixSize/2
moveax,[eax+8]
movmSize,eax
shledx,
2
submSize,edx
//mSize=Data.Stride-MatrixSize*4
addeax,4
imuleax,ecx
addedi,eax
//edi=edi+(Data.Stride*Radius+Radius*4)
addedi,3//edi+=3(Alphabyte)
shlecx,3
movrSize,ecx
//rSize=Radius*2*4
movebx,Nuclear//ebx=Nuclear

movy,
0//for(y=0;y<Height;y++)
@yLoop://{
movx,0//for(x=0;x<Width;x++)
@xLoop://{
pushesi//Save(esi)
pushedi//Save(edi)
movedi,Matrix//edi=Matrix

xoreax,eax
//eax=0
//用卷积矩阵处理Alpha字节
movecx,MatrixSize//for(I=0;I<MatrixSize;I++)
@Loop3://{
pushecx
movecx,MatrixSize
//for(J=0;J<=MatrixSize;J++)
@Loop4://{
movzxedx,[esi]//edx=*esi(Alphabyte)
imuledx,[edi]
addeax,edx
//eax+=edx**edi
addesi,4//esi+=4
addedi,4//edi++
loop@Loop4//}
addesi,mSize//esi+=mSize
popecx
loop@Loop3
//}
cdq
idivebx
//eax/=ebx
popedi//Result(edi)
mov[edi],al//*edi=al
addedi,4//edi+=4
popesi//Reset(esi)esi+=4
addesi,4

incx
moveax,x
cmpeax,Width
jl@xLoop
//}
addesi,rSize
addedi,rSize
incy
moveax,y
cmpeax,Height
jl@yLoop
//}

popebx
popedi
popesi
end;

procedureGdipShadow(Bmp:TGpBitmap;Radius:LongWord);
var
Data:TBitmapData;
Gauss:arrayofInteger;
Q:Double;
x,y,n,z:Integer;
p:PInteger;
Buf:Pointer;
begin
//根据半径计算高斯模糊矩阵
Q:=Radius/2;
ifQ=0thenQ:=0.1;
n:
=Radiusshl1+1;
SetLength(Gauss,n
*n);
p:
=@Gauss[0];
z:
=0;
forx:=-RadiustoRadiusdo
fory:=-RadiustoRadiusdo
begin
p
^:=Round(Exp(-(x*x+y*y)/(2.0*Q*Q))/(2.0*PI*Q*Q)*1000.0);
Inc(z,p
^);
Inc(p);
end;
Data:
=Bmp.LockBits(GpRect(0,0,Bmp.Width,Bmp.Height),[imRead,imWrite],pf32bppARGB);
GetMem(Buf,Data.Height
*Data.Stride);
try
//备份源数据
Move(Data.Scan0^,Buf^,Data.Height*Data.Stride);
//高斯卷积处理阴影效果
MakeShadow(Data,Buf, Gauss,n,z);
finally
FreeMem(Buf);
Bmp.UnlockBits(Data);
end;
end;

//计算并输出文字阴影效果
//g:文字输出的画布;str要输出的文字;font:字体;layoutRect:限定的文字输出范围
//ShadowSize:阴影大小;Distance:阴影距离;
//Angle:阴影输出角度(左边平行处为0度。顺时针方向)
//ShadowAlpha:阴影文字的不透明度;format:文字输出格式
procedureDrawShadowString(constg:TGpGraphics;conststr:WideString;
constfont:TGpFont;constlayoutRect:TGpRectF;
ShadowSize,Distance:LongWord;Angle:Single
=60;
ShadowAlpha:Byte
=192;constformat:TGpStringFormat=nil);overload;
var
Bmp:TGpBitmap;
Bg:TGpGraphics;
dr,sr:TGpRectF;
begin
sr:
=GpRect(ShadowSizeshl1,ShadowSizeshl1,layoutRect.Width,layoutRect.Height);
//建立透明的32位ARGB阴影位图,其大小为layoutRect长、宽度+ShadowSize*4
Bmp:=TGpBitmap.Create(Round(sr.Width)+ShadowSizeshl2,
Round(sr.Height)
+ShadowSizeshl2,pf32bppARGB);
Bg:
=TGpGraphics.Create(Bmp);
try
Bg.TextRenderingHint:
=thAntiAlias;
//以不透明度为ShadowAlpha的黑色画刷,
//在2倍ShadowSize偏移处输出文字到位图画布,
Bg.DrawString(str,font,Brushs[ARGB(ShadowAlpha,kcBlack)],sr,format);
//处理文字阴影效果
GdipShadow(Bmp,ShadowSize);
dr:
=layoutRect;
//根据角度计算阴影位图在目标画布的偏移量
Offset(dr,Cos(pi*Angle/180)*Distance,
Sin(pi
*Angle/180)*Distance);
//扩大源和目标矩形,以输出边缘半影部分
Inflate(dr,ShadowSize,ShadowSize);
Inflate(sr,ShadowSize,ShadowSize);
//输出阴影位图到目标画布
g.DrawImage(Bmp,dr,sr.X,sr.Y,sr.Width,sr.Height,utPixel);
finally
Bg.Free;
Bmp.Free;
end;
end;
//计算并输出文字阴影效果,除以输出点origin替代上面布局矩形外,其他参数同上
procedureDrawShadowString(constg:TGpGraphics;conststr:WideString;
constfont:TGpFont;constorigin:TGpPointF;
ShadowSize,Distance:LongWord;Angle:Single
=60;
ShadowAlpha:Byte
=192;constformat:TGpStringFormat=nil);overload;
begin
DrawShadowString(g,str,font,g.MeasureString(str,font,origin,format),
ShadowSize,Distance,Angle,ShadowAlpha,format);
end;

代码中已经含比较详细的注释,就不再讲解了,本文介绍的函数最大的特点就是阴影的大小、间隔距离、输出角度以及不透明度可根据需要调整;阴影效果也很好,边缘模糊均匀,线条圆润平滑,可与一般的PhotoShop文字阴影效果相媲美;由于核心函数MakeShadow代码采用BASM,且只处理了像素的Alpha字节,边界处理也省略了,因此处理速度还是较满意的。为了照顾需要pascal代码的朋友,下面给出该函数的pascal版本,速度比BASM版本慢很多,不过,通过其中的代码和注释可以加深了解该函数的原理:

//卷积处理阴影效果。Data:GDI+位图数据,要求32位ARGB格式;Source:复制的源
//ConvolMatrix:卷积矩阵;MatrixSize:矩阵大小,Nuclear:卷积核(必须大于0)
procedureMakeShadow(Data:TBitmapData;Source:Pointer;
ConvolMatrix:arrayofInteger;MatrixSize,Nuclear:LongWord);
var
x,y,I,J:Integer;
Width,Height,mSize,rSize:Integer;
v,Radius,Count:Integer;
pd,ps,ps1:PByte;
begin
Radius:
=MatrixSizeshr1;
Width:
=Data.Width-Radiusshl1;
Height:
=Data.Height-Radiusshl1;
mSize:
=Data.Stride-MatrixSizeshl2;
rSize:
=Radiusshl3;
Count:
=MatrixSize*MatrixSize;
//pd指向目标偏移地址为卷积半径后像素的Alpha字节
//为简化过程,不处理以Radius为半径的边界像素
pd:=Data.Scan0;
Inc(pd,Radius
*Data.Stride+Radius*4+3);
//ps指向源首像素地址的Alpha字节,也就是目标首像素第一个卷积乘数的像素点
ps:=Source;
Inc(ps,
3);
fory:=1toHeightdo
begin
forx:=1toWidthdo
begin
ps1:
=ps;
v:
=0;
forI:=0tocount-1do
begin
if(I<>0)and(ImodMatrixSize=0)then
Inc(ps1,mSize);
Inc(v,ConvolMatrix[I]
*ps1^);//Alpha字节卷积求和
Inc(ps1,4);
end;
v:
=vdivNuclear;//卷积和/卷积核
pd^:=v;
inc(pd,
4);
Inc(ps,
4);
end;
Inc(ps,rSize);
Inc(pd,rSize);
end;
end;

下面给出演示代码和效果图:

procedureTextPaint(g:TGpGraphics);
var
brush:TGpLinearGradientBrush;
font:TGpFont;
fontFamily:TGpFontFamily;
r:TGpRect;
begin
fontFamily:
=TGpFontFamily.Create('TimesNewRoman'{'华文行楷'});
font:
=TGpFont.Create(fontFamily,50,[fsBold],utPixel);
r:
=GpRect(Form1.PaintBox1.ClientRect);
brush:
=TGpLinearGradientBrush.Create(r,kcBlue,kcAliceBlue,90);
g.FillRectangle(Brush,r);
DrawShadowString(g,
'文字阴影特效',font,GpPoint(10,r.Height/3),5,10);
g.TextRenderingHint:
=thAntiAlias;
g.DrawString(
'文字阴影特效',font,Brushs.White,10,r.Height/3);
brush.Free;
font.Free;
fontFamily.Free;
end;

效果图也和上面一样分上下两部分,以便效果比较,并给出了2种字体的文字输出,其中下图为华文行楷字体。

下面是华文彩云字体,五色渐变画刷文字:

由于本文代码没有做更多条件下的测试,可能存在BUG,而且算法也有待提出改进意见,请朋友们不吝指教,来信请寄maozefa@hotmail.com

本例子中的GDI+版本系本人自己改写的,与网上流通的版本不完全兼容,如需使用本版本,请参照《GDI+ for VCL基础 -- GDI+ 与 VCL 》一文的下载地址,并请留意后面的修改说明。

后记1(2007.13.30):刚才发现函数中果然存在一点BUG,原因是把数据源备份地址通过GDI+的TBitmapData结构的保留字段作为参数传递给MakeShadow函数,原以为该保留字段可以使用的,没想到GDI+ DLL内部可能使用了该字段(看来,保留字段还是不要使用的好,呵呵),导致设置某些字体,或者字体大小,或者字体风格时随机出现阴影位图清零,而无文字阴影输出的BUG,现已经修改本文代码,请朋友们谅解并提出宝贵意见。

分享到:
评论

相关推荐

    GDI+入门指导书------经典

    GDI+入门指导书------经典 非常适合于GDI+初学者

    GDI++ for DELPHI

    GDI++ for DELPHI是一个专为DELPHI开发者设计的图形设备接口(GDI)扩展库,它提供了丰富的图形绘制功能,使开发者能够利用更高级的图形处理技术来增强应用程序的界面美观度和交互性。GDI++是基于Windows API的GDI...

    GDI+特效---对话框

    通过以上介绍,我们可以看到GDI+为Windows应用程序的界面设计带来了更多可能性,而对话框作为用户交互的重要组成部分,利用GDI+的特效可以提升用户体验。实践这些知识,你将能够创建出富有视觉吸引力且功能完善的...

    GDI+_for_VCL基础

    GDI+ for VCL基础是关于在Delphi和C++Builder中使用GDI+图形库进行图形绘制和图像处理的专题。GDI+是微软在Windows XP系统中引入的一个增强图形设备接口,它扩展了传统的GDI(Graphics Device Interface),提供了更...

    GDI+图像程序设计(PDF & 源码 -电子工业出版社)

    本书适合于开发GDI+图形应用程序的初、中级程序员阅读,书中给出了大量用C#语言编写的可重用示例代码,可以使读者更快地掌握书中所介绍的各种知识和概念。本书也可以作为大专院校相关课程的重要辅导教材。 【编辑...

    Delphi使用GDI+制作任意图片形状窗口

    为了实现上述功能,我们需要在Delphi中创建一个新的VCL Forms应用程序,然后导入GDI+的相关单元。在窗体的`OnCreate`事件中初始化GDI+,在`OnPaint`事件中绘制图片和处理透明度,在`OnNCCalcSize`事件中计算并设置...

    在Delphi XE10.2版本可用的GDI+库

    在Delphi XE10.2版本中,开发者可以利用GDI+库来增强图形处理功能,提升应用程序的视觉效果。GDI+是Microsoft提供的一个图形设备接口,它基于GDI(Graphics Device Interface),但提供了更多的图形绘制和图像处理...

    delphi gdi+遮罩动画字

    在本文中,我们将深入探讨如何在Delphi编程环境中利用GDI+库来创建遮罩动画文字效果。Delphi是一款强大的Windows应用程序开发工具,而GDI+(Graphics Device Interface Plus)是微软提供的图形处理库,提供了丰富的...

    Delphi GDI+

    Delphi GDI+是Delphi开发者用于构建高质量图形用户界面的强大工具集,它通过提供矢量图形、位图处理、文字渲染、颜色管理和图形变换等功能,极大地扩展了原生GDI的功能,帮助开发者创建出更为美观且功能丰富的应用...

    GDI+图形程序设计.zip

    GDI+提供了丰富的绘图功能,包括二维几何图形、曲线、文本、图像以及颜色管理等,使得开发者能够创建出具有高质量视觉效果的应用程序。 这本书《GDI+程序设计》显然是一个深入探讨GDI+技术的教程,它可能包含了GDI+...

    GDI+程序设计 GDI+程序设计 GDI+程序设计 GDI+程序设计

    GDI+(Graphics Device Interface Plus)是微软Windows操作系统中用于图形设备接口的增强版本,它为开发者提供了一套丰富的绘图工具,使得在Windows应用程序中创建、管理和显示图形变得更加便捷和高效。GDI+不仅继承...

    GDI+程序设计(清华大学)

    书中不仅介绍了GDI+的基础知识,还着重讲解了如何将GDI+技术应用于实际开发中,尤其适合那些希望在应用程序中实现高级图形处理和自定义控件开发的中高级程序员。 另外,书中提到了.NET Framework对编程的变革作用,...

    GDI+图形程序设计

    10. **高级绘图效果**:GDI+支持模糊、渐变、阴影等特效,让开发者能够创建出具有视觉冲击力的图形界面。 这本书深入浅出地讲解了GDI+的各种功能和用法,不仅介绍了基本的绘图操作,还涵盖了高级特性和实践技巧。...

    gdi++实现图像压缩-图像裁剪和缩放-图像格式转换-图像dpi修改.rar

    在Windows平台上,C++开发图形应用时,GDI++(也称为GDI+)是一个强大的图形库,它提供了丰富的功能,包括图像处理、绘制、文本渲染等。本压缩包文件包含的是利用GDI++实现的一些关键图像操作,如图像压缩、图像裁剪...

    ( Delphi 7 )使用GDI+进行图形缩放、拖动,多种图片格式支持,仅简单示例

    使用GDI+进行图形缩放、拖动,多种图片格式支持,仅简单示例。 问题源贴:http://bbs.csdn.net/topics/390638094

    GDI+游戏GDI+游戏GDI+游戏GDI+游戏GDI+游戏GDI+游戏

    GDI+(Graphics Device Interface Plus)是Windows操作系统中用于图形绘制和图像处理的一个高级API,它是.NET Framework的一部分,尤其在C#编程中被广泛应用于创建图形界面和游戏开发。在这个"飞机大战"的小游戏中,...

    delphi 与 GDI+

    《Delphi与GDI+深度探索》 在编程领域,Delphi是一款强大的对象 Pascal 编程工具,以其高效、易用而备受开发者喜爱。而GDI+(Graphics Device Interface Plus)是微软推出的一种图形设备接口,它扩展了传统的GDI...

    Delphi使用GDI+绘图测试程序

    在Delphi中,结合GDI+可以实现复杂的图形效果,如阴影、透明、旋转、缩放等。此外,GDI+还支持路径绘制、贝塞尔曲线、图像处理(如裁剪、旋转、滤镜等)。通过深入研究和实践,开发者可以充分利用GDI+的潜力,创造出...

    GDI+程序设计 (中文版pdf)

    《GDI+程序设计》是由Eric White撰写的一本详细介绍了GDI+在.NET框架中图形编程应用的书籍,由清华大学出版社出版。GDI+是微软公司针对.NET环境所提供的图形设备接口,它取代了之前的GDI,并引入了大量新的命名空间...

    GDI+开发包 GDI+ SDK

    GDI+(Graphics Device Interface Plus)是微软推出的一种图形设备接口的增强版本,它为Windows应用程序提供了丰富的2D图形、图像处理和文本渲染能力。GDI+开发包,即GDI+ SDK,是用于帮助开发者利用GDI+功能进行...

Global site tag (gtag.js) - Google Analytics