搜索
bottom↓
回复: 15

求助 DELPHI如何提取输入汉字的点阵数据

[复制链接]

出0入0汤圆

发表于 2009-3-19 13:53:11 | 显示全部楼层 |阅读模式
DELPHI如何提取输入汉字的点阵数据

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出330入0汤圆

发表于 2009-3-20 10:38:56 | 显示全部楼层
1、找一个字模,比如UCDOS里常用的HZK16.dat(16*16汉字字模)。
2、取模方法:HZK16.dat是用比特(bit)来存储汉字点阵,具体读取方法需要了解一下汉字的区位码及该字库文件的存储信息。

(原文件名:16汉字模.JPG)
3、定义一个数组,如:32个元素,为什么是32?是针对16*16点阵的字形,需要用到32字节:横向16个点,纵向16个点,所以16*16=256,每个点用一位来表示,现在就是256位。而8位=1字节,所以256/8=32字节。同理:如果是24×16点阵字形,那么24*16/8 = 48字节,你需要定义一个48个元素的数组。
4、用二进制(Binary)方式打开字模文件,放进第3步定义的数组。注意取模要用到区号和位号:(94 * l_QuHao + l_WeiHao) * 32 + 1


转一段读取12点阵汉字的方法: (C语言版本,用的字库是hzk12.dat)
void ShowChineseWords(char*word,int x,int y,int color)
{
FILE *fps;
int i,j;
unsigned char buf[32];
unsigned int upattern;
char wd[2];
long offset;
setcolor(color);
fps=fopen("c:\\dos_fonts\\hzk12","rb");
for(j=0;j<strlen(word);j++)
{
if(word[j]>0&&word[j]<128){
if(word[j]>=32){wd[0]=word[j];wd[1]='\0';outtextxy(x,y+3,wd);x+=12;}
continue;}
else{
offset=(long)((*(word+j)+96)*94+*(word+j+1)+96-95)*24;
fseek(fps,offset,SEEK_SET);
fread(buf,sizeof(unsigned char),24,fps);
for(i=0;i<24;i+=2)
{
upattern=(buf<<8)|buf[i+1];
setlinestyle(USERBIT_LINE,upattern,NORM_WIDTH);
line(x+15,y+i/2,x,y+i/2);
}
j++;
}
x=x+16;
}
fclose(fps);
setlinestyle(0,0,1);
}
关键在于这三句(其它的不须太在意):
offset=(long)((*(word+j)+96)*94+*(word+j+1)+96-95)*24;
fseek(fps,offset,SEEK_SET);
fread(buf,sizeof(unsigned char),24,fps);
对于HZK16,HZK32等,公式有所变化,关键在于最后的系数24的变化,比如HZK16应为32,而HZK32应为128(这个是我推断得知,没有试一下,可以自己试一下)

出330入0汤圆

发表于 2009-3-20 12:14:06 | 显示全部楼层
转一篇delphi的文章
如何读取汉字库并以图形显示

    我需要把写字板中的文字转换成二进制矩阵位图文件以存放在单片机中供led显示屏显示,经过反复摸索,发现如果用windows下的矢量图形是不可能的,因为不能确切知道某字的矢量图函数,而且如果读到canvas中取点一是太慢,二是一般led显示屏的显示面积很小,在计算机中不可能使点一一对应,所以想到可以用dos下的点阵字库进行转换,我用的是16*16的字库,只是举个例子。
只要知道某个汉字的两个asscii码。具体程序如下,我只是把读出来的点阵显示出来。
procedure form1.button1click();
var pstr:array[0..1]of char;
getstr:array[0..31]of byte;
temp,dis:byte;
binf:file;
qh,wh:byte;
offset:longint;
j,i,x,y:integer;
begin
pstr:='姜';
qh:=ord(pstr[0]);
wh:=ord(pstr[1]);
qh:=qh-$a0;
wh:=wh-$a0;
offset:=(94*(qh-1)+(wh-1))*32;
assignfile(binf,'d:\hzk16.dat');//关联外部文件
reset(binf,1);//关联
seek(binf,offset);
blockread(binf,getstr,sizeof(getstr));
//close(binf);//关闭文件

form1.image1.Canvas.Brush.Color :=clred;
x:=0;
y:=0;
i:=0;
j:=0;
while(i<=31) do
begin
temp:=getstr;
for j:=0 to 7 do
begin
dis:=temp and 128;
dis:=dis shr 7;
if dis=1 then
form1.Image1.Canvas.Rectangle(x*10,y*10,x*10+11,y*10+11);//画点
     ///////
     inc(x);
     if x>15 then
     begin
     x:=0;
     inc(y);
     end;//end_if
temp:=temp shl 1;
end;//for
inc(i);
end;//end_while
end;

出330入0汤圆

发表于 2009-3-20 13:03:04 | 显示全部楼层
继续“汉字点阵数据”终结者系列:

点阵字库(来自百度知道)
    一般我们使用16*16的点阵宋体字库,所谓16*16,是每一个汉字在纵、横各16点的区域内显示的。不过后来又有了HZK12、HZK24,HZK32和HZK48字库及黑体、楷体和隶书字库。
    虽然汉字库种类繁多,但都是按照区位的顺序排列的。前一个字节为该汉字的区号,后一个字节为该字的位号。每一个区记录94个汉字,位号则为该字在该区中的位置。因此,汉字在汉字库中的具体位置计算公式为:94*(区号-1)+位号-1。减1是因为数组是以0为开始而区号位号是以1为开始的。
    这仅为以汉字为单位该汉字在汉字库中的位置,那么,如何得到以字节为单位得到该汉字在汉字库中的位置呢?只需乘上一个汉字字模占用的字节数即可,即:(94*(区号-1)+位号-1)*一个汉字字模占用字节数,而按每种汉字库的汉字大小不同又会得到不同的结果。
    以16*16点阵字库为例,计算公式则为:(94*(区号-1)+(位号-1))*32。
    汉字库从该位置起的32字节信息即记录了该字的字模信息。
    了解点阵汉字及汉字库的构成原理后,显示汉字就变得简单。以16*16点阵字库为例,通常的方法是:将文件工作指针移到需要的汉字字模处、将汉字库文件读入一2*16数组再用for循环一位位地显示。
#include "graphics.h"
#include "stdio.h"
main()
{
  int i=VGA,j=VGAHI,k;
  unsigned char mat[16][2],chinease[3]="我";
  FILE *HZK;
  if((HZK=fopen("hzk16","rb"))==NULL)exit(0);
  initgraph(&i,&j,"");
  i=chinease[0]-0xa0;j=chinease[1]-0xa0; /*获得区码与位码*/
  fseek(HZK,(94*(i-1)+(j-1))*32l,SEEK_SET);
  fread(mat,32,1,HZK);
  for(j=0;j<16;j++)
   for(i=0;i<2;i++)
    for(k=0;k<8;k++)
     if(mat[j]&(0x80>>k)) /*测试为1的位则显示*/
       putpixel(i*8+k,j,WHITE);

  getch();
  closegraph();
  fclose(HZK);
}

======================================
完整的C版本
#include <stdio.h>
#include <alloc.h>
#include <ctype.h>
#include <dos.h>
#include <dir.h>
#include <io.h>
#include <graphics.h>
#define GETADR(n,str) (str *) calloc (n,sizeof(str)) // 申请N个指定字符串长度的地址
#define W16 16
#define C16 32
unsigned char bit[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};

FILE *fi;
char *dot,str[20];

main(int argc,char *argv[])
{
  int i,j,nn,xc,yc,MaxX,MaxY,menul[1000];
  int graphdriver=DETECT,graphmode;
  if(argc!=2)
  { //检测是否输入了2个参数
    puts("LT16<???.N4>");
    exit(0);
  }
  dot=GETADR(C16,char); //获取16个char长度的内存地址
  fnmerge(str,"","",argv[1],".N4"); //建立str 为路径 ,文件名是 第一个参数.N4 的文件(这里可能有bug,str没初始化)
  if((fi=fopen(str,"r"))==NULL)
  { //打开这个文件用于输入
    puts("Can't open DATA.");
    exit(0);
  }
  fscanf(fi,"%d",&nn); //读取一个整数
  if(nn<=0||nn>1000) //这个数字必须在 0~1000之间
    exit(0);
  for (i=0;i<nn;i++)
   fscanf(fi,"%d",&menul); //根据这个数字来决定继续读取的数字数量
  fclose(fi);
  if((fi=fopen("CLIB16.DOT","rb"))==NULL)
  { //打开 CLIB16.DOT 这个文件
    puts("Can't open CLIB16.DOT");
    exit(0);
  }

  initgraph(&graphdriver,&graphmode,""); // 初始化图形模式
  MaxX=getmaxx(); // 获得屏幕最大坐标范围
  MaxY=getmaxy();
  xc=0;
  yc=20;
  for(j=0;j<nn;j++)
  {
    i=readlibdot(menul[j]); //读取点阵数据
    putchar16(xc,yc); //显示到对应的坐标系
    xc+=20;
    if(xc>MaxX-20)
    {
      yc+=20;
      xc=0;
      if(yc>MaxY-20)
      {
        getch();
        clearviewport();
        yc=0;
      }
    }
  }
  getch(); //按任意键继续
  closegraph(); //关闭图形模式
}

readlibdot(int j)
{
  int i0,j1,j2;
  long order,k1;
  j1=j/100;
  j2=j-j1*100;
  if(j1>=9)
  j1=j1-6;
  k1=94*j1+j2-95;
  order=C16*k1;
  fseek(fi,order,SEEK_SET);
  i0=fread(dot,C16,1,fi);
  return(i0);
}
putchar16(int bx,int by) //显示readlibdot 得到的汉字(保存在dot 里面)的每个象素点阵
{
  int i,j,kk,k,x1,y1;
  unsigned char marker;
  kk=0;
  y1=by;
  for(i=0;i<16;i++,y1++)
  {
    for(j=0;j<2;j++)
    {
      x1=bx+j*8;
      marker=*(dot+kk);
      kk++;
      for(k=0;k<8;k++)
      {
        if(!(marker&bit[k]))
        continue;
        putpixel(x1+k,y1,YELLOW);
      }
    } 
  }
  return;
}

===================
吃完饭后继续回来终结。。。

出330入0汤圆

发表于 2009-3-20 14:26:29 | 显示全部楼层
转:如何在没有汉化的Turbo C 集成开发环境下编制显示汉字的程序呢?
    解决这一编程问题,我们首先必须了解有关汉字编码及字库的知识。根据对汉字使用频率的研究,可把汉字分成高频字(约100 个),常用字(约3000 个),次常用字(约4000 个),罕见字(约8000 个)和死字(约45000 个),即正常使用的汉字达15000 个。我国1981 年公布了《通讯用汉字字符集(基本集)及其交换码标准》GB2312-80 方案,把高频字、常用字、和次常用字集合成汉字基本字符集(共6763 个),在该集中按汉字使用的频度,又将其分为一级汉字3755 个(按拼音排序)、二级汉字3008 个(按部首排序),再加上西文字母、数字、图形符号等700 个。

汉字编码:
区位码
    国家标准的汉字字符集(GB2312—80)在汉字操作系统中是以汉字库的形式提供的。汉字库结构作了统一规定,即将字库分成94 个区,每个区有94 个汉字(以位作区别)每一个汉字在汉字库中有确定的区和位编号(用两个字节),这就是所谓的区位码(区位码的第一个字节表示区号,第二个字节表示位号,因而只要知道了区位码,就可知道该汉字在字库中的地址,每个汉字在字库中是以点阵字模形式存储的,如一般采用16×16点阵形式,每个点用一个二进位表示,存1 的点,当显示时,可以在屏上显示一个亮点,存0 的点,则在屏上不显示,这样把存某字的16×16 点阵信息直接用来在显示器上按上述原则显示,则将出现对应的汉字。

内码
    汉字使用两字节表示,国家制定了统一标准,称为国标码。国标码规定,每个字节使用后面7 位,第一位为0。为了区别于英文的ASCII 码,国标码在计算机上使用的时候,规定汉字每个字节第一位设置为1,以表示该两字节为汉字,称为内码。以“大”字为例子:
    国标码3473H: 0 0 1 1 0 1 0 0 0 1 1 1 0 0 1 1
      内码B4F3H: 1 0 1 1 0 1 0 0 1 1 1 1 0 0 1 1
    国标码与内码有一定的转换公式,即16 进制的区位码,两个字节各加80H,就成为了国标码。汉字字模在字库中存放的位置根据汉字的区位码来确定,内码是汉字在机内的表示。由于区位码和内码存在固定的转换关系,所以当在支持汉字输入的系统中,键盘输入的汉字内码即在程序中存在,将其转换为区位码,再从字库中找到对应的汉字字模,然后再用有关的位操作和循环语句,对每个字节的每一位进行判断,如同过滤一样,如果某位是1,则按设置的颜色在屏幕的相应位置画点(用graphics.h 中的显示象素点的函数putpixel()),若某位为0,则不画点,这样就可按预先设置的颜色在相应位置显示出该汉字来。

内码到区位码的转换
若汉字内码为十六进制数h2h1l2l1,则区号qh 相位号wh 分别为:
qh= h2h1-0xa0;
wh= l2l1-0xa0;
若用十进制表示内码为dld2,则
qh=dl-l60;
wh=d2-160;
即区位码qw 为:
qw=100*(d1-160)十(d2-160);
反过来,若已经知道了区位码qw。则也可求得区号和位号:
qh=qw/100;
wh=qw-100*qh;
因而该汉字在汉字库中离起点的偏移位置(以字节为单位),可计算为:
offset=(94*(qh-1)+(wh-1))* 32;
    注意:字库中每1 区有94 个字符。这样,就可以找寻到文件的偏移量,读出一个char bytes[32]数组。这样bytes 数组中则存了要显示汉字的16×16 点阵字模,然后将字模按行扫描的办法,通过循环用putpixel()函数在屏幕设定位置显示出象点,因而组合成一个显示的汉字。

出0入0汤圆

发表于 2009-3-20 15:53:56 | 显示全部楼层
谢谢zcllom 星罗棋布
有空尝试下,再次感谢.

出0入0汤圆

发表于 2009-3-20 16:17:23 | 显示全部楼层
直接用Windows API获得CANVAS生成点阵
懂得Win32很容易实现的
访问Canvas用SacnLine,用Pixel是没有任何效率可言的

出330入0汤圆

发表于 2009-3-20 22:47:04 | 显示全部楼层
楼上说的有道理,
现在的流行做法是把字画到bitmap上,然后扫描点阵,这样可以做一些特殊的字体,如16*12。。。

出0入0汤圆

 楼主| 发表于 2009-3-22 23:41:04 | 显示全部楼层
学习了啊 呵呵

出0入0汤圆

 楼主| 发表于 2009-3-22 23:55:21 | 显示全部楼层
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Image1: TImage;
    FontDialog1: TFontDialog;
    Edit1: TEdit;
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    procedure sendpicture(kuan, gao: byte);
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.sendpicture(kuan:byte;gao:byte);//发送一幅图片 取得点陣数据
var
x,y,i,j:integer;
b,b1:byte;
r:byte;
c:integer;
display:string;
DIS1:integer;
begin
  for j:=1 to 1 do
  for y:=0 to gao-1 do
  for x:=1 to kuan do
  begin
    b1:=01;
    b:=0;
    //for i:=(x-1)*8  to (x-1)*8+7 do
    for i:=(x-1)*8+7 downto (x-1)*8   do
    begin
      c:=getpixel(form1.image1.canvas.handle,i,y);//取点(颜色值)
      r:=GetRvalue(c);//红色分量
//    g:=Getgvalue(c);//绿色分量
      if j=1 then
      begin
        if r>0 then b:=b or b1;
      end else
      if j=2 then
      begin
//       if g>0 then b:=b or b1;
      end;
      b1:=b1 shl 1;
    end;
    display:=inttohex(b,2);
    display:='0x'+display+',';
    memo1.Lines.Add(display);
    //sentdata(chr(b));
  end;
  memo1.Lines.Add('};');
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  image1.Canvas.Font:=fontdialog1.Font;
  image1.Canvas.Font.Color:=clred;
  image1.Canvas.Brush.Color:=clblack;
  image1.Canvas.Pen.Color:=clblack;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  form1.Image1.Canvas.Rectangle(form1.image1.clientrect);
  form1.Image1.Canvas.TextOut(0,0,form1.Edit1.Text);
  memo1.Lines.Clear;
  memo1.Lines.Add('{ /*'+edit1.Text+'*/');
  sendpicture(2,16)
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  if form1.FontDialog1.Execute then
  begin
    image1.Canvas.Font.Name:=fontdialog1.Font.Name;
    image1.Canvas.Font.Color:=fontdialog1.Font.Color;
    image1.Canvas.Font.Style:=fontdialog1.Font.Style;
    image1.Canvas.Font.Size:=fontdialog1.Font.Size;
  end;
end;

end.

这是一个16*16点阵提取的DELPHI源程序  大家帮忙
分析下

出0入0汤圆

发表于 2009-4-9 16:33:23 | 显示全部楼层
路过

出0入0汤圆

发表于 2009-4-21 20:28:39 | 显示全部楼层
非常感谢,我也很想知道,请问如果我要做64*64点阵的上位机呢?是不是要调用计算机里16*16的汉字字模?还是有其他的办法吗??谢谢~~~~~~~~~~~~~

出0入0汤圆

发表于 2009-7-31 09:23:52 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-7-16 20:26:53 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-10-22 20:36:06 | 显示全部楼层
MARK

出0入0汤圆

发表于 2010-10-22 21:05:26 | 显示全部楼层
Mark
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-23 20:25

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表