首页  编辑  

字符画软件的四个关键技术

Tags: /超级猛料/Picture.图形图像编程/其他/   Date Created:

字符画软件的四个关键技术

第一个关键技术:汉字库读取技术

  使用汉字库技术可以做到和操作系统无关性,我们先了解一下点阵字库的基本原理

如下所示,下面是一个"字"的点阵图,在16点阵字库中一个汉字为16x16点,每一行使用两个字节表示,如下面示例第一行的十六进制为:0x02和0x00,所以,一个汉字在16点阵字库中需要占用2x16个字节,24点阵字库需要3x24个字节,下面我们仅以16点阵字库为例,其他点阵类似。

██████ █████████

███████ ████████

██            ██

██ ██████████ ██

█ ██████████ ███

███        █████

█████████ ██████

████████ ███████

███████ █████ ██

               █

███████ ████████

███████ ████████

███████ ████████

███████ ████████

█████ █ ████████

██████ █████████

下面的函数返回指定字符串的字符画文本

function Get16(const AWord,AForeground,ABackground:string):string;

   function GetBit(const c,n:byte):integer;

   begin

       result:=(c shr n) and 1;

   end;

var

   iLen        :integer;

   iFileSize   :integer;

   s           :string;

   k,l,i,p     :integer;

   cw:array[0..31] of char;

   qu_ma,wei_ma:integer;

   File16      :file;

begin

   iLen:=length(AWord);

   AssignFile(File16,piProgramInfo.Path+'HZK16');

   FileMode := fmOpenRead;

   try

       Reset(File16,1);

   finally

       FileMode:=fmOpenReadWrite;

   end;

   iFileSize:=FileSize(File16);

   try

       for l:=1 to iLen div 2 do

       begin

           k:=l*2-1;

           // 如果不是汉字,往前进一位

           while k<=iLen do

           begin

               if ByteType(AWord,k)=mbLeadByte then break;

               inc(k);

           end;

           if k>iLen then break;

           if ((ord(AWord[k]) and $80)<>0) then

           begin

               qu_ma:=ord(AWord[k])-161;

               wei_ma:=ord(AWord[k+1])-161;

               if (94*qu_ma+wei_ma)*32+32>iFileSize then continue;

               try

                   seek(File16,(94*qu_ma+wei_ma)*32);

               except

                   myMessageBox('fseek call fail!');

                   exit;

               end;

               BlockRead(File16,cw,32);

               for i:=0 to 15 do

               begin

                   for p:=7 downto 0 do

                   begin

                       if GetBit(ord(cw[i*2]),p)=1 then s:=s+AForeground

                       else                            s:=s+ABackground;

                   end;

                   for p:=7 downto 0 do

                   begin

                       if GetBit(ord(cw[i*2+1]),p)=1 then s:=s+AForeground

                       else                              s:=s+ABackground;

                   end;

                   s:=s+#13#10;

               end;

           end;

       end;

   finally

       CloseFile(File16);

   end;

   result:=s;

end;

第二个关键技术:使用系统字库进行转换

  其实使用系统字库是极为自由的方式,因为这样我们完全不必关心字库的技术,这一切都交给系统好了,让我们充分利用系统资源。

  如果我们定义一个设备,然后设定好设备的各种属性,包括宽度、高度、字体、颜色等,然后在上面绘制文本就可以了,要转换为字符画,只需要把设备上的点阵信息转换为文本即可。

配合 CreateFontIndirect 函数,使用 DrawText 可以绘制丰富的文本效果。实现完整的字符画效果

下面是十二号宋体的转换结果

█████ ██████

█          █

  ████████ █

██       ███

██████ █████

█████ ██████

           █

█████ ██████

█████ ██████

█████ ██████

███   ██████

████████████

下面是九号@黑体的转换结果

████████████

██  ███ ████

██ ████ ████

██ █ ██ ████

██ █  █ ████

█  █       █

   █ ██ ██ █

██ █ ██ ██ █

██ █ ██ ████

██ ████ ████

██  ███ ████

████████████

第三个关键技术:图片转换为文本

  要把图像转换为文本,这其中有一个很大的困难,就是文本没有颜色,所以我们特别引进了一个概念:文本灰度,就是把不同字母在屏幕上显示的大小排序,得到一张灰度表,用这个灰度表来转换图片,可以达到比较好的效果。

下面的函数可以把一个位图转换成文本,ABit 是位图,AGray 是灰度

function ImageToText(ABit:TBitmap;const AGray:string):string;

var

   x,y         :integer;

   s           :string;

   pColor      :Longint;

   R,G,B       :byte;

   iGray       :integer;

   sGrayPer    :string;                

   iGrayLen    :integer;              

   iIndex      :integer;              

begin

   s:='';

   sGrayPer:=AGray;

   iGrayLen:=Length(sGrayPer);

   for y:=0 to ABit.Height-1 do

   begin

       for x:=0 to ABit.Width-1 do

       begin

           pColor:=ABit.Canvas.Pixels[x,y];

           R:=pColor and $FF;

           G:=(pColor shr 8) and $FF;

           B:=(pColor shr 16) and $FF;

           iGray:=HiByte(R*77+G*151+B*28);          

           iIndex:=(iGray*iGrayLen div 255);

           if iIndex<1 then iIndex:=1;

           if iIndex>iGrayLen then iIndex:=iGrayLen;

           s:=s+sGrayPer[iIndex];

       end;

       s:=s+Crlf;

   end;

   result:=s;

end;

这是一个常用且效果比较好的灰度:"MNHQ$OC?7>!":-';. "

第四个关键技术:把文本转换为图像

  要把文本转换为图片,必须获取两个重要参数:转换后的宽和高,要取得这两个参数,我们可以使用 GetTextExtentPoint32 函数,该函数的定义如下:

function GetTextExtentPoint32(DC: HDC; Str: PChar; Count: Integer; var Size: TSize): BOOL;

DC 传入设备句柄

Str 为文本内容

Count 为文本的长度(字节)

Size 返回宽和高

在实际应用中,往往被转换的文本有多行,且每一行的长度不定,

所以我们还需要在生成图像前进行一遍预扫,以便获得完整的图像大小

下面演示了文本转换为图像的代码

////////////////////////////////////////////////////////////////////////////////

// 功能     : 把文本转换为位图

// AOwner   : 窗体参数

// AText    : 要转换的文本

// AFont    : 文本的字体

// ABitmap  : 转换后的位图对象

// 日期     : 2003.12.15

////////////////////////////////////////////////////////////////////////////////

procedure TextToBitmap(AOwner:TObject;const AText:TStrings;AFont:TFont;ABitmap:TBitmap);

var

   i               :integer;

   iWidth,iHeight  :integer;

   iCharHeight     :integer;

   s               :string;

   r               :TRect;

   size            :TSize;

   lblTemp         :TLabel;

begin

   iWidth:=0;

   iHeight:=0;

   lblTemp:=TLabel.Create(nil);

   r.Top:=0;

   try

       lblTemp.Visible:=false;

       lblTemp.Parent:=TWinControl(AOwner);

       lblTemp.Font.Assign(AFont);

       ABitmap.Canvas.Brush.Style:=bsClear;

       ABitmap.Canvas.Pen.Color:=rgb(0,0,0);

       ABitmap.Canvas.Brush.Color:=RGB(255,255,255);

       ABitmap.Canvas.Font.Assign(AFont);

       // 下面代码用户获得文本的最大宽度和高度

       for i:=0 to AText.Count-1 do

       begin

           s:=AText.Strings[i];

           if s='' then s:=' ';

           lblTemp.Caption:=s;

           GetTextExtentPoint32(lblTemp.Canvas.Handle,pchar(lblTemp.Caption),lblTemp.GetTextLen,size);

           if iWidth<size.cx then iWidth:=Size.cx;

           iHeight:=iHeight+Size.cy;

       end;

       // 获得一个字符的高度

       GetTextExtentPoint32(lblTemp.Canvas.Handle,pchar('   '),length('   '),size);

       iCharHeight:=size.cy;

       ABitmap.Width:=iWidth;

       ABitmap.Height:=iHeight;

       for i:=0 to AText.Count-1 do

       begin

           s:=AText.Strings[i];

           r.Left:=0;

           r.Right:=ABitmap.Width;

           r.Bottom:=r.Bottom+iCharHeight;

           DrawText(ABitmap.Canvas.Handle,PChar(s),length(s),r,0);

           r.Top:=r.Top+iCharHeight;

       end;

   finally

       lblTemp.Free;

   end;

end;

2003.12.15

凌丽软件工作室 wosens.com