点阵字库在JAVA中的实现_weixin_30333885的博客-程序员宅基地

技术标签: java  

前言:为什么要使用点阵字库

在某些场合,系统自带的字库并不能令人满意,或者,在你需要特别的字体时,你希望能附带上该字库。

那为什么又需要点阵字库呢?因为在使用较小的字体的时候,点阵字库能更清晰,同时,由于点阵字库并不包含路径等信息,因此,字库文件的大小也很小,便于携带。

如何生成点阵字库

在此,我并没有打算在此文中说明如何生成点阵字库,其实有很多现成的点阵字库可以选择,当然,目前都是使用 TrueType 字库,但很多比较老的程序都会带有点阵字库,你可以直接拿来使用,或者,网上也有很多生成点阵字库的工具,当然,你甚至可以自己写一个小程序来生成点阵字库。

我们这里将直接使用 UCODS 自带的字库来举例子,你也可以使用《特大点阵字库制作软件》来生成一个点阵字库。

点阵字库的组成

点阵字库的组成基本上都类似,除了有些点阵字库可能会在文件头上附加一些版权信息外,基本上没有其它变化,另外,他们在点阵信息的压缩上都会采用相同的方式。下面我们举个例子看看:

3.gif

上图是一个 12*12 大小的“我”字,从栅格中可以看到,每个点的位置上只会有“有”和“无”两种状态,由左至右。因此,上面的点阵信息可以如下描述:

第 1 行

0

0

0

0

1

0

1

0

1

0

0

0

第 2 行

1

1

1

1

0

0

1

0

0

1

0

0

第 3 行

0

0

0

1

0

0

1

0

0

0

0

0

第 4 行

1

1

1

1

1

1

1

1

1

1

1

0

第 5 行

0

0

0

1

0

0

1

0

0

0

0

0

第 6 行

……

由于每个点的信息只有 0 和 1 两种状态,因此,为了节省存储空间,我们使用位的存储点的信息。我们知道,一个字节有 8 位,因此,上面的一个点阵字,可以用若干个字节来进行描述。

当然,不同的点阵大小,每一行的点不一样多,并不能保证他们总是 8 的倍数,因此,最后不足 8 个字节的仍然占用一个字节,且从最高位向最低位排列,不足的地方补 0 。

1 : 00001010 1000 →补位→ 00001010 10000000 → Ox0a 0x80
2 : 11110010 0100 →补位→ 11110010 01000000 → 0xf2 0x40
3 : 00010010 0000 →补位→ 00010010 00000000 → 0x12 0x00
4 : 11111111 1110 →补位→ 11111111 11100000 → 0xff 0xe0
5 : 00010010 0000 →补位→ 00010010 00000000 → 0x12 0x00
……

由上可知,对于一个 12*12 的汉字来说,一个字需要占用 2 × 12 = 24 个字节。我们已经知道了每一个字的存储方式,那么,在字库中,这些字是按照什么顺序来存储的呢?

我们知道,不同的语言使用不同的字符集,对于汉字来说,有很多种字符集如 GB2312 、 BIG5 、 GBK 等,当然,还有更大的字符集如 UTF-8 、 UNICODE 等,在我们的程序中,我们必须确定一种字符集,为了简单起见,我们以 GB2312 举例子。(关于各字符集的说明,自己可以在网上搜索一下)

GB2312 规定“对任意一个图形字符都采用两个字节表示,每个字节均采用七位编码表示”,习惯上称第一个字节为“高字节”,第二个字节为“低字节”。 GB2312-80 包含了大部分常用的一、二级汉字,和 9 区的符号。该字符集是几乎所有的中文系统和国际化的软件都支持的中文字符集,这也是最基本的中文字符集。其编码范围是高位 0xa1 - 0xfe ,低位也是 0xa1-0xfe ;汉字从 0xb0a1 开始,结束于 0xf7fe 。

GB2312 将代码表分为 94 个区,对应第一字节( 0xa1-0xfe );每个区 94 个位( 0xa1-0xfe ),对应第二字节,两个字节的值分别为区号值和位号值加 32 ( 2OH ),因此也称为区位码。 01-09 区为符号、数字区, 16-87 区为汉字区( 0xb0-0xf7 ), 10-15 区、 88-94 区是有待进一步标准化的空白区。 GB2312 将收录的汉字分成两级:第一级是常用汉字计 3755 个,置于 16-55 区,按汉语拼音字母 / 笔形顺序排列;第二级汉字是次常用汉字计 3008 个,置于 56-87 区,按部首 / 笔画顺序排列。故而 GB2312 最多能表示 6763 个汉字。

仍然是 12*12 点阵的“我”字为例,“我”的编码为 0xCED2 ,因此,在字库文件中,“我”字的偏移为: ((0xCE-0xA1)*94+(0xD2-0xA1))*24 字节 / 字 =102696 ,其后的连续 24 字节即为“我”字的点阵信息。

public class CharCode {
public CharCode() {
String str = "我";
try {
byte[] b = str.getBytes("GB2312");
for (int i = 0; i < b.length; i++) {
System.out.println(Integer.toHexString(b[i]));
}
} catch (Exception ex) {
}
}

public static void main(String[] args) {
CharCode charcode = new CharCode();
}
}

将点阵信息还原为汉字

由于我们已经能够顺利的找到汉字的点阵数据,因此,我们只需要简单的将点阵信息还原即可。还原的方法很简单,只需要按照上述格式逆向操作就行了:

“我”字的信息第一个字节为 Ox0a ,从高位向低位依次取当前位上的数据,如果为 1 ,表示该点需要着色, 0 则表示该点为空,为了取位的方便,我们可以直接定义一个数组来与该字节异或:

public final static int[] verify = {128, 64, 32, 16, 8, 4, 2, 1};
然后,作如下判断:
for (int w = 0; w < 8; w++) {
if ((b & verify[w]) == verify[w]) { //需要着色
//着色
}
}

下面,我们以一段小的程序来完成该工作:

import java.io.*;
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;

public class Test extends JFrame {
byte[] dotfont;
BufferedImage imgCH;
int[] verify = {128, 64, 32, 16, 8, 4, 2, 1};
String test = "点阵汉字的测试";
int imgWidth = 300;
int imgHeight = 200;

public Test() {
super("DotFont");

File file = new File("gb.dat");
try {
FileInputStream fis = new FileInputStream(file);
dotfont = new byte[fis.available()];
fis.read(dotfont);
fis.close();
} catch (FileNotFoundException ex) {
} catch (IOException ex) {
}

this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(300, 240);
this.show();
}

void createCH(byte[] ch, int off) {
int q1 = ch[off] & 0xff;
int q2 = ch[off + 1] & 0xff;
int offset = (q1 - 0xa1) * 94 * 24;
q2 -= 0xa1;
offset += q2 * 24;
imgCH = new BufferedImage(12, 12, BufferedImage.TYPE_INT_RGB);
for (int h = 0; h < 12; h++) {
byte b = dotfont[offset++];
for (int w = 0; w < 8; w++) {
if ((b & verify[w]) == verify[w]) {
imgCH.setRGB(w, h, 0xffffffff);
} else {
imgCH.setRGB(w, h, 0);
}
}
b = dotfont[offset++];
for (int w = 0; w < 4; w++) {
if ((b & verify[w]) == verify[w]) {
imgCH.setRGB(w + 8, h, 0xffffffff);
} else {
imgCH.setRGB(w + 8, h, 0);
}
}
}
}

public void paint(Graphics g) {
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
byte[] an = str2bytes(test);
int offset = 0;
int x = 10, y = 34;
while (y < imgHeight && offset < an.length) {
int b = an[offset] & 0xff;
if (b > 0x7f) {
createCH(an, offset);
g.drawImage(imgCH, x, y, null);
x += 12;
offset += 2;
} else { //英文暂时不考虑
x += 6;
offset++;
}
if (x > imgWidth) {
x = 10;
y += 14;
}
}
}

byte[] str2bytes(String s) {
if (null == s || "".equals(s)) {
return null;
}
byte[] abytes = null;
try {
abytes = s.getBytes("gb2312");
} catch (UnsupportedEncodingException ex) {
}
return abytes;
}

public static void main(String[] args) {
new Test();
}
}

在上述代码中, gb.dat 是直接从 UCDOS 中自带的 12 点阵字库,当然,你也可以使用其它工具生成,为了提高效率,我们直接将整个字库加载到内存中。 str2bytes(String s)是用来将字符串转换成为 GB2312 编码格式的二进制字节数组,在 createCH(byte[] ch, int off) 中,我们创建了一个空白的图片,然后根据字库的点阵信息,来决定是否使用 setRGB()方法为该点着色,最后,将创建的图片画在程序的画布上。

当然,以上程序仅仅是演示,并没有考虑效率的问题和对英文字库的处理

编译并运行该程序,你会得出如下的界面:

1.gif

至此,我们的点阵字库显示程序已经可以正常工作了,在这里,其实我们还可以玩一点小小的技巧,定义一个颜色数组

int[] color = {0xbbff00, 0xaaff11, 0x99ff22, 0x88ff33, 0x77ff44, 0x66ff55, 0x55ff66, 0x44ff77, 0x33ff88, 0x22ff99, 0x11ffaa, 0x00ffbb};

然后,在使用 setRGB()时根据当前行的位置来使用数组中相对索引的颜色:

imgCH.setRGB(w + 8, h, 0xffffffff);

改为

imgCH.setRGB(w + 8, h, color[h]);

这个小小的修改并不会降低代码的执行效率,并且,你将会得到如下图的效果:

2.gif

代码由此下载

转载于:https://www.cnblogs.com/jqyp/archive/2011/04/26/2029900.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30333885/article/details/97887506

智能推荐

unittest中case的管理及运用_up1292的博客-程序员宅基地

import unittestclass Testdemo(unittest.TestCase): def test_01(self): print("这是第一个case") # @unittest.skip('test_02') # test_02不执行 def test_02(self): print("这是第二个case...

FF05期末作业成品代码——美食汇-美食菜谱(5页) HTML+CSS+JS网页设计期末课程大作业_优质内容贡献的博客-程序员宅基地

HTML5期末大作业:美食网站设计——美食汇-美食菜谱(5页) HTML+CSS+JS网页设计期末课程大作业作品介绍1.网页作品简介 :HTML期末大学生网页设计作业 A+水平 ,喜欢的可以下载,文章页支持手机PC响应式布局。2.网页作品编辑:作品下载后可使用任意HTML编辑软件(如:DW、HBuilder、NotePAD 、Vscode 、Sublime 、Webstorm、 Notepad++ 等任意HTML软件编辑修改网页)。3.网页作品技术:Div+CSS、鼠标滑过特效、Table、导航栏效

DBMS_XPLAN.DISPLAY_CURSOR()看执行计划_lwei_998的博客-程序员宅基地

<br />我们可以很容易的得到一个SQL的执行计划。<br />如果一个SQL已经执行过了,我们怎么查看他真实的执行计划呢。<br />如果知道已执行SQL的 SQL_ID,或HASH_VALUE.<br />在10g中利用DBMS_XPLAN.DISPLAY_CURSOR()可以很方便地查询到已执行SQL的执行计划。<br /><br />SQL> desc dbms_xplan<br />FUNCTION DISPLAY_CURSOR RETURNS DBMS_XPLAN_TYPE_TABLE<br

list常用函数的例子_qq64602632的博客-程序员宅基地

[cpp] view plaincopyprint?void test_list_assign()  {      std::listint> c1;      std::listint> c2;      c1.push_back(10);      c1.push_back(20);      c1.push_back(30);   

数据结构——链栈及其操作_dgbxssz959266的博客-程序员宅基地

1 #include&lt;iostream&gt; 2 using namespace std; 3 4 typedef int Status; 5 typedef int ElemType; 6 #define OK 1 7 #define ERROR 0 8 9 10 //链栈的存储结构11 typedef struct Stack...

Linux学习系列-轮询函数_努努要要的博客-程序员宅基地_轮询函数

理解这三个轮询函数差异的关键在于理解其轮询的文件描述符(socket也是文件)的数据结构。select轮询函数 函数定义:int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exeptfds, struct timeval *timeout);// fd_set操作宏void FD_SET(int fd, fd_s

随便推点

PyQT项目优化---添加多线程数控制_Giser_D的博客-程序员宅基地

就是筑龙网的爬虫项目优化,添加多线程控制数,避免卡顿# -*- coding: utf-8 -*-# Form implementation generated from reading ui file 'ZL.ui'## Created by: PyQt5 UI code generator 5.13.0## WARNING! All changes made in this fi...

Java 技术新手入门_weixin_33686714的博客-程序员宅基地

Java 技术是什么? Java技术既是一种高级的面向对象的编程语言,也是一个平台。Java 技术基于 Java 虚拟机(Java virtualmachine,JVM)的概念 —— 这是语言与底层软件和硬件之间的一种转换器。Java 语言的所有实现都必须实现 JVM,从而使 Java程序可以在有 JVM 的任何系统上运行。 ...

基于企业微信的流量增长_微薇 微盛·企微管家的博客-程序员宅基地

私域流量运营可划分为四个步骤:营销获客、留存促活、销售转化、客户管理,构建成基于企业微信的完整私域增长闭环1、裂变获客规模化裂变获客,快速增长私域流量池,大部分私域玩家停留在第一个层面,无法规模化获客是大家共同面临的问题最常见的是好友裂变,企业微信好友裂变原理是:以企业微信客服号作为裂变载体、承接客户载体,所有的传播载体上展现的都是企业微信客服号,通过奖品福利、课程优惠、书籍产品等吸引用户,让用户扫码添加企业微信客服号为好友,然后不断传播裂变用户具体的参与路径是:用户看到活动海报

16. 最接近的三数之和(Python)_濯君的博客-程序员宅基地

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).来源:力扣(LeetCode)链接:h...

【剖析】为什么c语言从main函数开始执行程序_feixs1的博客-程序员宅基地_c++总是从main函数开始执行

今天学习c++,突然想到为什么程序要从main函数开始执行。也忘记老师有没有讲过,甚至有些遗忘。担心之余开始前往csdn进行查询学习,终于在一个大神博客学习到了,现在分享给大家。main只是开发工具所规定的一个特殊函数名称而已。它既不是程序的入口,也不是必须要有的函数。程序的入口点记录在可执行文件中的一个数据,该数据标明程序从哪个位置开始执行,这个数据是连接程序的时候由link.exe确定的,可以把程序的入口点 指定为任意函数,甚至可以自己编辑可执行文件修改程序的入口点。在默认情况下,link.e

达梦(DM8)数据库支持数据类型总结_在奋斗的大道的博客-程序员宅基地_达梦数据库布尔类型

一、数值类型NUMERIC[精度,标度]:用于存储零、正负定点数。精度范围1至38 NUMBER类型:跟NUMERIC相同 DECIMAL/DEC类型:跟NUMERIC相似 BIT类型:用于存储整数数据1、0或null INTEGER/INT类型:用于存储有符号整数,精度为10 PLS_INTEGER类型:与INTEGER相同 BIGINT类型:用于存储有符号整数,精度为19,标度为0 TINYINT类型:用于存储有符号整数,精度为3,标度为0。取值范围为:-128~+127。 BYTE

推荐文章

热门文章

相关标签