6

在我看来,一个常见的问题是:字符编码与位图字体相结合。大多数多语言编码在不同的字符类型之间有很大的空间,甚至还有很多未使用的代码点。因此,如果我想使用它们,我会浪费大量内存(不仅是为了保存多字节文本——我的意思是专门用于我的位图字体中的空格)——而且 VRAM 非常有价值......所以似乎唯一合理的事情是:在我的纹理上使用自定义映射,即 UTF-8 字符(这样就不会浪费空间)。但是:这项工作似乎与使用自己的专有字符编码相同(因此在我的纹理中也有自己的字符顺序)。在我的特殊情况下,我获得了 4096 个不同字符的纹理空间,并且需要字符来显示拉丁语言和日语(它与只支持通用 cjk 代码页的 utf-8 混为一谈)。有人遇到过类似的问题吗(我真的很想知道,如果没有的话)?如果已经有任何方法?

编辑:这里描述了同样的问题http://www.tonypottier.info/Unicode_And_Japanese_Kanji/但它没有提供如何将这些位图字体映射保存到 utf-8 空间效率的真正解决方案。因此,欢迎任何进一步的帮助!

编辑2:

非常感谢您的回答。对不起,我的问题描述得不够清楚。

我真正想解决的是:CJK Unicode 范围超过 20000 个字符。但是正确显示日语文本只需要大约 2000 个字符的子集。这些字符分布在从 U+4E00 到 U+9FA5 的范围内。所以我需要以某种方式将这些 Unicode 代码点(只有 2000 用于日语)转换为我创建的纹理的坐标(我也可以像我想要的那样对字符进行排序)。

即 U+4E03 是日文字符,但 U+4E04、U+4E05、U+4E06 不是。那么 U+4E07 也是一个日文字符。所以最简单的解决方案,我可以看到:在字符 U+4E03 之后在我的纹理中留下三个空格(或者在那里写上不必要的字符 U+4E04、U+4E05、U+4E06)然后写 U+4E07。但这会浪费太多纹理空间(20000 个字符,即使只需要 2000 个字符)。所以我希望能够只放入我的纹理:“...U+4E03,U+4E07...”。但是我不知道如何编写我的 displayText 函数——因为我不知道我想显示的字形的纹理坐标在哪里。会有一个哈希图或类似的东西,但我不知道如何存储这些数据(为每个字符编写类似 ...{U+4E03, 128}, {U+4E07, 129}...填充hasmap)。

对于问题: 1)没有特定的格式 - 所以我将自己编写 displayText 函数。2)没有理由反对 unicode - 它只是我的位图字体的 CJK 范围问题。3)我认为,这通常与平台和语言无关,但在我的情况下,我在 Mac OS X/iOS 上使用 C++ 和 OpenGL。

非常感谢您的帮助!如果您对此有任何进一步的想法,那真的对我有很大帮助!

4

6 回答 6

3

你想解决的真正问题是什么?

是 UTF-8 编码的字符串每个字符占用三个字节吗?如果是,请切换到 UTF-16。否则不要责怪UTF-8。(说明:UTF-8 只是一种将整数序列转换为字节序列的算法。它与代码页中的字符分组无关。这反过来又是Unicode 代码点的用途。)

Unicode代码点是否分布在许多“代码页”上(其中“代码页”是指由256个相邻的Unicode代码点组成的块)?如果是,请发明一个从 Unicode 代码点 (0x000000 - 0x10FFFF) 到较小整数集的映射。就内存而言,这应该花费不超过 4 字节乘以您真正需要的字符数。查找时间大约是 24 次内存访问、24 次整数比较和 24 次分支指令。(实际上,这将是树形映射中的二进制搜索。)如果这太昂贵,您可以使用基于哈希表的映射。

是别的吗?那么请给我们一些例子,以更好地理解您的问题。

据我了解,您可能应该编写一个小型实用程序,该程序将您要在应用程序中使用的一组 Unicode 代码点作为输入,然后生成用于显示文本的代码和数据。这就提出了以下问题:

  1. 您必须使用特定的位图字体格式还是自己编写displayText函数?
  2. 是否有任何理由反对对所有字符串使用 Unicode 并将它们转换为您的位图优化编码,只是在您呈现文本时?编码转换当然是displayText方法内部的,对正常的应用程序代码不可见。
  3. 只是出于兴趣:问题是否特定于某种编程语言或环境?

更新

我假设您的主要问题是这样的一些功能:

Rectangle position(int codepoint)

如果我必须这样做,我会首先为每个字符创建一个位图。位图的文件名将是代码点,因此可以轻松地重新生成“大图”,以防万一您找到更多需要的字符。准备工作包括以下步骤:

  1. 加载所有位图并确定它们的尺寸。此步骤的结果是从整数到(宽度,高度)对的映射。
  2. 为大图中的角色图像计算一个好的布局,并记住每个角色的放置位置。保存大图。将代码点到 (x, y, width, height) 的映射保存到另一个文件。这可以是文本文件,或者如果您没有磁盘空间,则可以是二进制文件。细节无所谓。

然后该displayText函数将按如下方式工作:

void displayText(int x, int y, String s) {
  for (char c : s.toCharArray()) { // TODO: handle code points correctly
    int codepoint = c;
    Rectangle position = positions.get(codepoint);
    if (position != null) {
      // draw bitmap
      x += position.width;
    }
  }
}

Map<Integer, Rectangle> positions = loadPositionsFromFile();

现在剩下的唯一问题是如何使用尽可能少的内存在内存中表示此映射,并且仍然足够快。当然,这取决于您的编程语言。

内存中的表示可以是一些包含 x、y、宽度、高度的数组。对于每个元素,一个 16 位整数就足够了。无论如何,您可能只需要 8 位的宽度和高度。然后,另一个数组会将代码点映射到索引positionData(如果代码点不可用,则映射到某个特殊值)。这将是一个由 20000 个 16 位整数组成的数组,因此总而言之,您有:

  • 2000 * ( 2 + 2 + 1 + 1 positionX) = 12000 字节positionYpositionWidthpositionHeight
  • codepointToIndexInPositionArrays如果使用数组而不是映射,则20000 * 2 = 40000 字节。

与位图本身的大小相比,这应该足够小。而且由于数组不会改变,它们可以在只读内存中。

于 2010-12-27T00:48:03.303 回答
2

我相信对这些数据进行编码的最有效(无损)方法是使用Huffman 编码来存储您的文档信息。这是一个经典的信息论问题。您需要执行映射以从压缩空间到角色空间。

此技术将根据每个文档的字符频率(或您选择将其应用于的任何域/文档)尽可能有效地压缩您的文档。只会存储您使用的字符,并且它们将以与使用频率成正比的有效方式存储。

我认为解决这个问题的最好方法是使用现有的实现(UTF16、UTF8...),这比实现自己的 Huffman 编码更不容易出错,以节省一点空间。磁盘空间和带宽很便宜,而激怒客户或经理的错误则不然。我相信霍夫曼编码在理论上将是最有效(无损)的编码,但对于这个应用程序来说不是最实用的。不过请查看链接,这可能对其中一些概念有所帮助。

-Brian J. Stinar-

于 2010-12-28T19:45:06.847 回答
1

这篇论文已经过时了,不再是 1980 年了,几乎所有显示应用都不需要搜索比特。在开发应用程序时,例如 iPhone,您必须计划跨多种语言的 l10n,因此仅为日语节省一些位是没有意义的。

日本仍在使用 Shift-JIS,因为就像中国的 GB18030、香港的 BIG5 等一样,他们拥有一个庞大、稳定且高效的资源池,已经锁定在语言环境编码中。迁移到 Unicode 需要重写大量的框架工具和随之而来的额外测试。

如果你看一下 iPod,它只支持拉丁文、中文、日文和韩文,跳过泰文和其他脚本,从而节省了一些成本。随着内存价格的下降和 iPhone 存储空间的增加,Apple 已经能够添加对更多脚本的支持。

UTF-8 是一种节省空间的方式,使用 UTF-8 进行存储,并转换为 UCS-2 或更高版本,以便于操作和显示。Shift-JIS 和 Unicode 之间的差异非常小。

于 2010-12-30T13:23:48.700 回答
1

UTF-8 通常是一种非常有效的编码。如果您的应用程序主要关注亚洲和其他具有多字节字符集的地区,那么您可能会从使用 UTF-16 中获益更多。您当然可以编写自己的编码,但它不会为您节省那么多数据,并且会为您提供大量工作。

如果你真的需要压缩你的数据(我想知道是否以及为什么),你最好使用一些算法来压缩你的 UTF 数据。大多数算法在较大的数据块上工作效率更高,但也有用于压缩小块文本的算法。我认为如果您探索这些而不是定义自己的编码,您将节省大量时间。

于 2010-12-22T08:35:42.980 回答
0

您可以使用多个位图并按需加载它们,而不是尝试包含所有可能字符的单个位图。

于 2010-12-26T10:08:07.707 回答
0

仅中文就有超过 4096 个字符,我说的不是标点符号,而是用来组成单词的字符。来自维基百科

康熙字典中包含的汉字数量约为 47,035 个,尽管其中有大量是历史上积累的很少使用的变体。

即使其中许多很少使用,即使不需要 90%,您仍然会耗尽您的配额。(我认为现代文本中使用的实际数字大约是 10 - 20k。)

如果您事先知道需要使用哪些字符,那么最好的办法可能是创建一个 Unicode 代码点的间接表来索引到您的纹理。然后你只需要在你的纹理中放置你实际使用的尽可能多的字符。我相信 Flash(和一些 PDF)在内部会做这样的事情。

于 2010-12-22T08:14:49.310 回答