自从文章《GDI+ 在Delphi程序的应用 -- 可调节的文字阴影特效》发表后,不少人问我怎样实现文字描边。由于我只是个业余编程爱好者,加上文化底蕴差,只要涉及算法和编程理论方面的东西,我就无能为力了,所以直到目前,我也不知道具体的描边算法是怎样的(网上搜索过N次,也没找到答案,可能这方面的东西是要卖钱的)。
因问得人多了,有时我也思索和研究一下,总算找了个方法可以实现,虽然同专业的图像软件(如PhotoShop)文字描边效果相比差强人意,但可以凑合凑合,作为研究心得,将代码贴在这里备查。
在《GDI+ 在Delphi程序的应用 -- 可调节的文字阴影特效》一文的内容的基础上,对文字阴影效果代码进行了改进和扩充,扩充的功能有2点:一是由原来只能产生黑色阴影扩充为任意颜色阴影;二是可以对阴影进行扩展。有了这2个功能,利用阴影效果也就可以进行文字描边了,推而广之,也可实现图像的描边。下面是具体的代码内容:
-
-
procedureBackImage(Data:TBitmapData;Dest:Pointer;Color:TARGB);
-
asm
- pushesi
- pushedi
-
movesi,[eax+16]
-
movedi,edx
-
movedx,ecx
-
andedx,0FFFFFFh
-
movecx,[eax]
-
imulecx,[eax+4]
- cld
-
@Loop:
-
or[esi],edx
-
movsd
- loop@Loop
- popedi
- popesi
-
end;
-
-
-
procedureMakeExpand(Data:TBitmapData;Source,ExpMatrix:Pointer;
- MatrixSize:LongWord);
-
var
- Radius,mSize,rSize:LongWord;
- x,y:LongWord;
- Width,Height:Integer;
- Matrix:Pointer;
- Stride:LongWord;
-
asm
- pushesi
- pushedi
- pushebx
-
movesi,edx
-
movedi,[eax+16]
-
addedi,3
- add ecx, 3
-
movMatrix,ecx
- movecx,MatrixSize
- movedx,ecx
- dececx
- movebx,[eax]
- subebx,ecx
-
movWidth,ebx
-
movebx,[eax+4]
- subebx,ecx
-
movHeight,ebx
-
shrecx,1
-
movRadius,ecx
-
moveax,[eax+8]
- movStride,eax
- movmSize,eax
-
shledx,2
-
submSize,edx
-
addeax,4
- imuleax,ecx
-
addeax,3
-
addesi,eax
-
shlecx,3
-
movrSize,ecx
-
movy,0
-
@yLoop:
-
movx,0
-
@xLoop:
-
test[esi],0ffh
-
jz@NextPixel
-
test[esi-4],0ffh
-
jz@001
-
test[esi+4],0ffh
- jz@001
- movebx,Stride
- test[esi+ebx],0ffh
- jz@001
- negebx
- test[esi+ebx],0ffh
- jnz@NextPixel
-
@001:
-
pushedi
-
movebx,Matrix
-
movedx,MatrixSize
-
@Loop3:
-
movecx,MatrixSize
-
@Loop4:
-
mov al,[ebx]
- cmp al,[edi]
-
jb @002
-
mov[edi],al
-
@002:
-
addedi,4
-
addebx,4
-
loop@Loop4
-
addedi,mSize
- decedx
-
jnz @Loop3
-
popedi
-
@NextPixel:
-
addedi,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(Data:TBitmapData;Buf:Pointer;Radius:LongWord);
-
var
-
Gauss:arrayofInteger;
- Q:Double;
- x,y,n,z:Integer;
- p:PInteger;
-
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;
- MakeShadow(Data,Buf,Gauss,n,z);
-
end;
-
procedureGdipBorder(Data:TBitmapData;Buf:Pointer;Expand:LongWord;Color:TARGB);
-
var
- bmp:TGpBitmap;
- bg:TGpGraphics;
- Data1:TBitmapData;
- Size:Integer;
-
begin
-
Size:=Expandshl1+1;
-
bmp:=TGpBitmap.Create(Size,Size,pf32bppARGB);
-
bg:=TGpGraphics.Create(bmp);
-
try
-
-
bg.SmoothingMode:=smAntiAlias;
-
bg.PixelOffsetMode:=pmHalf;
-
bg.FillEllipse(Brushs[Color],0,0,Size,Size);
-
Data1:=bmp.LockBits(GpRect(0,0,Size,Size),[imRead],pf32bppARGB);
-
try
-
-
MakeExpand(Data,Buf,Data1.Scan0,Size);
-
finally
-
bmp.UnlockBits(Data1);
-
end;
-
finally
-
bg.Free;
-
bmp.Free;
-
end;
-
end;
-
procedureDrawShadow(constg:TGpGraphics;constBitmap:TGpBitmap;
-
constlayoutRect:TGpRectF;ShadowSize,Distance:LongWord;
- Angle:Single;Color:TARGB;Expand:LongWord);
-
var
- dr,sr:TGpRectF;
- Data:TBitmapData;
- Buf:Pointer;
- SaveScan0:Pointer;
-
begin
-
Data:=Bitmap.LockBits(GpRect(0,0,Bitmap.Width,Bitmap.Height),
- [imRead,imWrite],pf32bppARGB);
-
GetMem(Buf,Data.Height*Data.Stride);
-
try
- BackImage(Data,Buf,Color);
-
ifExpand>ShadowSizethen
- Expand:=ShadowSize;
-
ifExpand<>0then
-
ifExpand<>ShadowSizethen
-
begin
-
SaveScan0:=Data.Scan0;
-
Data.Scan0:=Buf;
- GdipBorder(Data,SaveScan0,Expand,Color);
-
Data.Scan0:=SaveScan0;
-
endelse
- GdipBorder(Data,Buf,Expand,Color);
-
ifExpand<>ShadowSizethen
- GdipShadow(Data,Buf,ShadowSize-Expand);
-
finally
- FreeMem(Buf);
-
Bitmap.UnlockBits(Data);
-
end;
-
sr:=GpRect(0.0,0.0,Data.Width,Data.Height);
-
-
-
dr:=GpRect(layoutRect.Point,sr.Size);
-
-
Offset(dr,Cos(PI*Angle/180)*Distance-ShadowSize-1,
-
Sin(PI*Angle/180)*Distance-ShadowSize-1);
-
-
g.DrawImage(Bitmap,dr,sr.X,sr.Y,sr.Width,sr.Height,utPixel);
-
end;
-
-
-
-
-
-
procedureDrawShadowString(constg:TGpGraphics;conststr:WideString;
-
constfont:TGpFont;constlayoutRect:TGpRectF;
-
ShadowSize,Distance:LongWord;Angle:Single=60;
-
Color:TARGB=$C0000000;Expand:LongWord=0;
-
constformat:TGpStringFormat=nil);overload;
-
var
- Bmp:TGpBitmap;
- Bg:TGpGraphics;
-
begin
-
-
Bmp:=TGpBitmap.Create(Round(layoutRect.Width+0.5)+ShadowSizeshl1+2,
-
Round(layoutRect.Height+0.5)+ShadowSizeshl1+2,
- pf32bppARGB);
-
Bg:=TGpGraphics.Create(Bmp);
-
try
-
Bg.TextRenderingHint:=thAntiAlias;
-
-
-
Bg.DrawString(str,font,Brushs[Colorand$FF000000],
-
GpRect(ShadowSize+1,ShadowSize+1,
-
layoutRect.Width,layoutRect.Height),format);
- DrawShadow(g,Bmp,layoutRect,ShadowSize,Distance,Angle,Color,Expand);
-
finally
-
Bg.Free;
-
Bmp.Free;
-
end;
-
end;
-
-
procedureDrawShadowString(constg:TGpGraphics;conststr:WideString;
-
constfont:TGpFont;constorigin:TGpPointF;
-
ShadowSize,Distance:LongWord;Angle:Single=60;
-
Color:TARGB=$C0000000;Expand:LongWord=0;
-
constformat:TGpStringFormat=nil);overload;
-
begin
-
DrawShadowString(g,str,font,g.MeasureString(str,font,origin,format),
- ShadowSize,Distance,Angle,Color,Expand,format);
-
end;
上面代码中MakeShadow过程的代码在《GDI+ 在Delphi程序的应用 -- 可调节的文字阴影特效》一文中,本文没有贴出。由于代码中已经有了较详细的注释,故不再解释。下面贴出测试代码:
-
procedureTextPaint(g:TGpGraphics);
-
var
- brush:TGpLinearGradientBrush;
- font:TGpFont;
- fontFamily:TGpFontFamily;
- r:TGpRect;
-
begin
-
fontFamily:=TGpFontFamily.Create('华文行楷');
-
font:=TGpFont.Create(fontFamily,55,[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,60,$C0000000,1);
-
DrawShadowString(g,'文字阴影特效',font,GpPoint(10,r.Height/3),1,0,60,$FFFF0000,1);
-
-
-
g.TextRenderingHint:=thAntiAlias;
-
g.DrawString('文字阴影特效',font,Brushs.White,10,r.Height/3);
-
font.Free;
-
fontFamily.Free;
-
Brush.Free;
-
end;
以下是测试代码效果图,图一和图二都是文字描边(1个像素的边框)加阴影效果,其中图一没进行阴影扩展,即上面的15行的代码最后一个参数为0,图二是加了1个像素的阴影扩展效果(上述代码的“正宗”输出):
图一
图二
利用改进的阴影效果,不仅可实现文字描边,也可显示类似立体文字的效果(改变显示距离),上面测试代码中,被注释的2句代码输出效果如下:
图三
至于图像的描边,似乎没有文字的描边效果好,究其原因,主要是图像的轮廓看起来好像是圆润平滑的,其实有很多半影锯齿,在Photoshop中,通过先选区后描边,可能对选区边缘作了处理,所以效果相当好(专业的软件,肯定有很好的算法)。下面是我对一张小图片作的描边处理代码和输出效果图:
-
-
-
-
-
procedureDrawImageBorder(constg:TGpGraphics;constImage:TGpImage;
- x,y:Single;BorderWidth:LongWord;Color:TARGB=kcWhite;
-
Expand:LongWord=0;constAttributes:TGpImageAttributes=nil);
-
var
- Bmp:TGpBitmap;
- Bg:TGpGraphics;
- ColorMatrix:TColorMatrix;
- Attr:TGpImageAttributes;
- layoutRect:TGpRectF;
-
begin
-
Bmp:=TGpBitmap.Create(Image.Width+BorderWidthshl1+2,
-
Image.Height+BorderWidthshl1+2,
- pf32bppARGB);
-
Bg:=TGpGraphics.Create(Bmp);
- Attr:=Attributes;
-
ifAttr=nilthen
-
Attr:=TGpImageAttributes.Create;
-
try
-
FillChar(ColorMatrix,Sizeof(TColorMatrix),0);
-
ColorMatrix[3,3]:=1;
-
ColorMatrix[4,4]:=1;
-
-
Attr.SetColorMatrix(ColorMatrix);
-
layoutRect:=GpRect(x,y,Image.Width,Image.Height);
-
Bg.DrawImage(Image,
-
GpRect(BorderWidth+1,BorderWidth+1,layoutRect.Width,layoutRect.Height),
-
0,0,layoutRect.Width,layoutRect.Height,utPixel,Attr);
-
DrawShadow(g,Bmp,layoutRect,BorderWidth,0,0,Color,BorderWidth-Expand);
-
finally
-
ifAttributes<>nilthen
-
Attr.ClearColorMatrix
-
else
-
Attr.Free;
-
Bg.Free;
-
Bmp.Free;
-
end;
-
end;
-
procedureImagePaint(g:TGpGraphics);
-
var
- brush:TGpLinearGradientBrush;
- r:TGpRect;
- Image:TGpImage;
- Attributes:TGpImageAttributes;
-
begin
-
r:=GpRect(Form1.PaintBox1.ClientRect);
-
brush:=TGpLinearGradientBrush.Create(r,kcBlue,kcAliceBlue,90);
-
g.FillRectangle(Brush,r);
-
Image:=TGpImage.Create('..\..\Media\Watermark.bmp');
-
-
g.TranslateTransform(20,r.Height/3);
-
g.DrawImage(Image,0,0,Image.Width,Image.Height);
-
-
Attributes:=TGpImageAttributes.Create;
-
Attributes.SetColorKey($ff00ff00,$ff00ff00);
-
-
g.TranslateTransform(Image.Width+20,0);
-
DrawImageBorder(g,Image,0,0,2,kcWhite,0,Attributes);
-
g.DrawImage(Image,GpRect(0.0,0,Image.Width,Image.Height),
-
0.0,0.0,Image.Width,Image.Height,utPixel,Attributes);
-
-
g.TranslateTransform(Image.Width+20,0);
-
DrawImageBorder(g,Image,0,0,5,kcWhite,3,Attributes);
-
g.DrawImage(Image,GpRect(0.0,0,Image.Width,Image.Height),
-
0.0,0.0,Image.Width,Image.Height,utPixel,Attributes);
-
Attributes.Free;
-
Brush.Free;
-
Image.Free;
-
end;
图四
上面的效果图中,左边是原图,中间是2个像素的描边图,右边是5个像素的描边图,其中有3像素的模糊扩散。从图中可以看出,我以$ff00ff00为透明色处理图像四个角后,在中间和右边的描边图中,还是很明显的看到四个角有很淡的绿色,正是这个原因,在中间图的圆角描边有明显的锯齿。
最后作几点说明:
1、本文纯属业余学习和研究的心得,并非什么正宗的算法;
2、因为本文代码是学习时即兴写的,并非优化代码,而且是以过程形式出现的,有兴趣的朋友可以自己进行优化改进,写成类或者元件更好(由于算法和功能都不是很完善,所以我没写成类的形式);
3、例子中的GDI+版本系本人自己改写的,与网上流通的版本不完全兼容,如需使用本版本,请参照《GDI+ for VCL基础 -- GDI+ 与 VCL 》一文的下载地址,并请留意后面的修改说明。
4、如有好的建议,请来信:maozefa@hotmail.com
更新(2008-8-5 12:50):在MakeExpand过程中,是按图象逐点用位图画笔矩阵填充的,每个像素点都要进行矩阵大小的操作,最小的1像素扩展的矩阵大小为3 * 3,可见扩展速度是不大理想的。今天对代码作了一点修改,对每个象素点都进行了判断,如果是边界像素,则作画笔矩阵填充,否则直接跳过,这样一来,速度应该提高不少(没作测试,增加的代码用红色标出,有兴趣者可以测试)。
分享到:
相关推荐
GDI+(Graphics Device Interface Plus)是微软为Windows应用程序提供的一种强大的图形处理库,它扩展了原有的GDI(Graphics Device Interface),增加了更多的图形绘制功能和更丰富的API接口,使得开发者能够创建出...
总的来说,GDI+ Delphi接口单元为Delphi开发者提供了一个强大而易用的工具,使他们能够充分利用GDI+的强大功能,创建出具有高质量图形和图像效果的应用程序。这个接口简化了与底层系统交互的过程,降低了开发复杂性...
通过GDI+,程序员可以轻松地在Windows应用程序中实现复杂的图形操作,如画线、画圆、填充区域、处理图像以及生成高质量的文字输出。 首先,GDI+的核心类`System.Drawing.Graphics`是进行所有图形绘制的基础。这个类...
在Windows编程领域,GDI(Graphics Device Interface)和GDI+是两种图形设备接口,用于在应用程序中绘制图形、文本以及处理图像。本教程将深入探讨如何在MFC(Microsoft Foundation Classes)框架下利用GDI与GDI+...
GDI++,全称Graphics Device Interface Plus Plus,是Windows平台上的一种图形用户界面开发库,它扩展了标准的GDI(Graphics Device Interface)功能,提供了更强大的2D图形处理能力。GDI++通常用于Windows应用程序...
**正文** Delphi GDI+库是一个用于...总的来说,Delphi GDI+库为Delphi开发者提供了一套强大的图形处理工具,极大地扩展了Delphi在图形界面设计和动画制作上的可能性,是提升应用程序视觉效果和用户体验的重要手段。
C# GDI+ 是一种强大的图形处理库,用于在Windows应用程序中创建和操作图形。GDI+ 是微软.NET Framework的一部分,提供了丰富的功能,包括绘制线条、曲线、形状、文本、图像处理以及更多。本示例提供了全面的C# GDI+...
GDI+(Graphics Device Interface Plus)是Windows操作系统中用于图形绘制的一个强大库,它扩展了传统的GDI(Graphics Device Interface)功能,提供了更多的图形处理能力,包括矢量图形、位图操作、颜色控制以及...
GDI+(Graphics Device Interface Plus)是Windows操作系统中用于图形绘制和图像处理的一个高级API,它是对传统GDI(Graphics Device Interface)的扩展和增强。GDI+提供了一套面向对象的接口,使得开发者能够更加...
9. **高级特效**:如模糊、阴影、发光等可以通过GDI+实现,为应用程序添加丰富的视觉效果。 为了配置和使用GDI+,开发者通常需要: 1. **添加引用**:在VC项目中,需要添加GDI+的库文件引用,如gdiplus.lib。 2. ...
在"GDI+应用程序图形界面设计"的资料包中,你可以找到关于GDI+开发和使用的全面资源。以下是一些核心知识点: 1. **图形对象**:GDI+中的图形对象包括画刷、笔、字体和图像等,它们是绘制图形的基础。例如,你可以...
GDI+库文件是Windows XP操作系统中用于图形设备接口(Graphics Device ...尽管现在有Direct2D和Windows Presentation Foundation(WPF)等更先进的图形框架,但GDI+仍然在许多现有的Windows应用程序中发挥着作用。
在易语言中实现GDI或GDI+的文字描边是一项重要的技术,它可以让文本在界面上更加突出,提升视觉效果。文字描边通常是通过在原始文字的基础上,沿着文字边缘描绘出一条颜色不同的轮廓来实现的。这种效果在各种软件...
在标签中,“gdi+”和“gdi”都提到了,表明资源涵盖了GDI+的基础以及可能与传统GDI的交互。"c_gdi+"和"gdi+_c++"则特指了用C++语言来操作GDI+的实例。 压缩包内的文件名称“www.pudn.com.txt”可能是来源于一个...
在Delphi编程环境中,自定义窗体描边是一项常见的需求,它可以让你的程序窗口看起来更加独特和专业。本文将深入探讨如何在Delphi中实现自定义窗体描边的演示。 首先,我们要理解窗体(Form)是Delphi应用程序的基本...
在IT领域,C#是一种广泛使用的编程语言,尤其在Windows应用程序开发中占据着重要地位。GDI+(Graphics Device Interface Plus)是微软提供的一种图形设备接口,它为开发者提供了丰富的图形绘制、图像处理和文本渲染...
在Delphi中使用GDI+库,可以让你的应用程序拥有与C#或其他.NET框架中的GDI+相媲美的图形处理能力。 `GDIPAPI.pas`文件通常包含了GDI+的基本API函数声明,这些函数是GDI+的核心,允许开发者进行图形绘制、颜色处理、...
GDI+(Graphics Device Interface Plus)是微软推出的一种图形设备接口的增强版本,它为Windows应用程序提供了丰富的图形绘制和图像处理功能。这本书“GDI+程序设计”由Eric White撰写,中文版由杨浩和张哲峰翻译,...
GDI+是Microsoft为C/C++开发者提供的一个基于类的应用程序编程接口(API),允许开发者在Windows平台上创建丰富的图形界面和格式化文本。GDI+主要面向Windows XP及后续版本的操作系统,并且也支持Win64平台。 #### ...
GDI+(Graphics Device Interface Plus)是Windows操作系统中用于图形绘制和图像处理的一个API,它扩展了传统的GDI(Graphics Device Interface),提供了更多的图形处理功能和更好的性能。GDI+适用于C++开发者,...