【发布时间】:2011-11-10 23:53:55
【问题描述】:
我想在基于 Java AWT 的应用程序中实现一个简单的位图字体绘制。应用程序使用Graphics 对象,我想在其中实现一个简单的算法:
1) 加载一个文件(可能使用ImageIO.read(new File(fileName))),它是 1 位 PNG,看起来像这样:
即它是 8*8 字符的 16*16(或 16*many,如果我想支持 Unicode)矩阵。黑色对应背景色,白色对应前景。
2) 逐个字符地绘制字符串,将该位图的相关部分传送到目标Graphics。到目前为止,我只成功了这样的事情:
int posX = ch % 16;
int posY = ch / 16;
int fontX = posX * CHAR_WIDTH;
int fontY = posY * CHAR_HEIGHT;
g.drawImage(
font,
dx, dy, dx + CHAR_WIDTH, dy + CHAR_HEIGHT,
fontX, fontY, fontX + CHAR_WIDTH, fontY + CHAR_HEIGHT,
null
);
它可以工作,但是,可惜的是,它会按原样显示文本,即我不能用所需的前景色和背景色替换黑色和白色,我什至不能使背景透明。
所以,问题是:在 Java 中是否有一种简单(且快速!)的方式来将一个 1 位位图的一部分传输到另一个位图,并在传输过程中对其进行着色(即用一种给定的颜色替换所有 0 像素并且全部 1 个像素与另一个像素)?
我研究了几个解决方案,但在我看来,它们都不是最理想的:
- 使用自定义着色 BufferedImageOp,如 this solution 中所述 - 它应该可以工作,但在每次 blit 操作之前重新着色位图似乎效率很低。
- 使用多个 32 位 RGBA PNG,黑色像素的 Alpha 通道设置为 0,前景设置为最大值。每个所需的前景色都应该有自己的预渲染位图。通过这种方式,我可以使背景透明并在 blitting 之前将其单独绘制为一个矩形,然后使用我的字体选择一个位图,用所需的颜色预着色并在该矩形上绘制它的一部分。对我来说似乎是一个巨大的矫枉过正 - 是什么让这个选项更糟 - 它将前景色的数量限制在相对较小的数量(即我可以实际加载并保存数百或数千个位图,而不是数百万)
- 捆绑和加载自定义字体,如this solution 中所述,可以工作,但据我在Font#createFont 文档中看到的,AWT 的
Font似乎只适用于基于矢量的字体,而不适用于基于位图的字体.
可能已经有任何库实现了此类功能?或者是时候让我切换到某种更高级的图形库,比如lwjgl?
基准测试结果
我在一个简单的测试中测试了几个算法:我有 2 个字符串,每个字符串 71 个字符,并且一个接一个地连续绘制它们,就在同一个地方:
for (int i = 0; i < N; i++) {
cv.putString(5, 5, STR, Color.RED, Color.BLUE);
cv.putString(5, 5, STR2, Color.RED, Color.BLUE);
}
然后我测量所用时间并计算速度:每秒字符串和每秒字符数。到目前为止,我测试的各种实现产生了以下结果:
- 位图字体,16*16字符位图:10991个字符串/秒,780391个字符/秒
- 位图字体,预分割图像:11048 个字符串/秒,784443 个字符/秒
- g.drawString():8952 个字符串/秒,635631 个字符/秒
- 彩色位图字体,使用 LookupOp 和 ByteLookupTable 着色:404 个字符串/秒,28741 个字符/秒
【问题讨论】:
-
“它应该可以工作,但它似乎效率很低”我相信分析而不是意见。
-
+1 表示基准测试结果。所以 64 美元的问题是:任何渲染 28K+ 字符/秒的方法是否达到最低规格。对于这个用例?
-
我当前的目标是 80*50 字符控制台的 25..30 FPS。理想情况下是 80*50*30 ~ 120K 字符/秒。 28K 会在全控制台动画重绘时提供大约 7 FPS 的效果。我接受了peek at what Notch does at his LD16 game,似乎有一些原始位数组处理魔法。我会尝试更多地挖掘这种可能性。
标签: java fonts bitmap awt lwjgl