Экономия места для японской кодировки?
На мой взгляд, общая проблема: кодировка символов в сочетании с растровым шрифтом. Большинство многоязычных кодировок имеют огромное пространство между различными типами символов и даже большим количеством неиспользуемых кодовых точек. Поэтому, если я хочу использовать их, я трачу много памяти (не только для сохранения многобайтового текста - я имею в виду специально для пробелов в моем растровом шрифте) - а VRAM в основном очень ценный... Так что кажется единственно разумным быть: Использование пользовательского сопоставления на моей текстуре для символов UTF-8 (чтобы не было пустого места). НО: Это усилие похоже на использование собственной проприетарной кодировки символов (так же как и собственного порядка символов в моей текстуре). В моем конкретном случае я получил текстурное пространство для 4096 различных символов, и мне нужны символы для отображения латинского и японского языков (это путаница с utf-8, которая поддерживает только общие кодовые страницы cjk). Была ли у кого-нибудь похожая проблема (мне действительно интересно, если нет)? Если есть уже какой-либо подход?
Редактировать: та же проблема описана здесь http://www.tonypottier.info/Unicode_And_Japanese_Kanji/ но она не дает реального решения, как сохранить эти отображения растрового шрифта в эффективном пространстве utf-8. Так что любая дальнейшая помощь приветствуется!
Edit2:
Большое спасибо за ответ. Мне жаль, что моя проблема не была достаточно ясно описана.
Что я действительно хочу решить, так это: диапазон 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) Я думаю, это обычно не зависит от платформы и языка, но в моем случае я использую C++ с OpenGL на Mac OS X/iOS.
Большое спасибо за Вашу помощь! Если у вас есть какие-либо дальнейшие идеи для этого, это действительно очень мне поможет!
6 ответов
Какую реальную проблему вы хотите решить?
Это то, что строка в кодировке UTF-8 занимает три байта на символ? Если да, переключитесь на UTF-16. В противном случае не вините UTF-8. (Пояснение: UTF-8 - это просто алгоритм для преобразования последовательности целых чисел в последовательность байтов. Он не имеет ничего общего с группировкой символов в кодовых страницах. Это, в свою очередь, то, для чего нужны кодовые точки Unicode.)
Является ли то, что кодовые точки Unicode распределены по многим "кодовым страницам" (где "кодовая страница" означает блок из 256 смежных кодовых точек Unicode)? Если да, придумайте отображение из кодовых точек Unicode (0x000000 - 0x10FFFF) в меньший набор целых чисел. С точки зрения памяти это должно стоить не больше, чем 4 байта, умноженное на количество символов, которое вам действительно нужно. Время поиска составило бы приблизительно 24 обращения к памяти, 24 сравнения целых чисел и 24 инструкции ветвления. (На самом деле, это будет бинарный поиск в древовидной карте.) И если это слишком дорого, вы можете использовать отображение на основе хеш-таблицы.
Это что-то еще? Тогда, пожалуйста, приведите несколько примеров, чтобы лучше понять вашу проблему.
Насколько я понимаю, вам, вероятно, следует написать небольшую служебную программу, которая принимает в качестве входных данных набор кодовых точек Unicode, которые вы хотите использовать в своем приложении, а затем генерирует код и данные для отображения текстов. Это поднимает вопросы:
- Вы должны использовать определенный формат растрового шрифта, или вы напишите
displayText
функционировать себя? - Есть ли какая-либо причина против использования Unicode для всех строк и преобразования их в кодировку, оптимизированную для растровых изображений, только на время, когда вы визуализируете текст? Преобразование кодировки, конечно, будет внутренним
displayText
метод и не виден нормальному коду приложения. - Просто из интереса: специфична ли проблема для определенного языка программирования или среды?
Обновление:
Я предполагаю, что вашей главной проблемой является какая-то функция, подобная этой:
Rectangle position(int codepoint)
Если бы мне пришлось это сделать, я бы начал с одного растрового изображения для каждого символа. Имя файла растрового изображения будет кодовой точкой, так что "большая картинка" может быть легко восстановлена, на случай, если вы найдете еще несколько символов, которые вам нужны. Подготовка состоит из следующих этапов:
- Загрузите все растровые изображения и определите их размеры. Результатом этого шага является карта из целых чисел в пары (ширина, высота).
- Вычислите хороший макет для изображений персонажей в большой картине и запомните, где был размещен каждый персонаж. Сохранить большую картинку. Сохраните отображение из кодовых точек в (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, width, height. Для каждого элемента достаточно 16-битного целого числа. И, вероятно, вам нужно всего лишь 8 бит для ширины и высоты в любом случае. Затем другой массив отобразит кодовую точку на индекс в positionData
(или какое-то специальное значение, если кодовая точка недоступна). Это будет массив из 20000 16-битных целых чисел, так что в итоге вы получите:
- 2000 * (2 + 2 + 1 + 1) = 12000 байт для
positionX
,positionY
,positionWidth
а такжеpositionHeight
- 20000 * 2 = 40000 байт для
codepointToIndexInPositionArrays
, если вы используете массив вместо карты.
По сравнению с размером самого растрового изображения, оно должно быть достаточно маленьким. А поскольку массивы не меняются, они могут находиться в памяти только для чтения.
Я считаю, что наиболее эффективным (без потерь) методом для кодирования этих данных будет использование кодировки Хаффмана для хранения информации о вашем документе. Это классическая проблема теории информации. Вам нужно будет выполнить сопоставление, чтобы перейти из сжатого пространства в пространство символов.
Этот метод позволяет максимально эффективно сжимать ваш документ, основываясь на частоте символов для каждого документа (или любого домена / документов, к которым вы хотите применить его). Будут сохраняться только те символы, которые вы используете, и они будут сохраняться эффективным способом, прямо пропорциональным частоте их использования.
Я думаю, что лучший способ решить эту проблему - использовать существующую реализацию (UTF16, UTF8...). Это будет намного менее подвержено ошибкам, чем реализация собственного кода Хаффмана, чтобы сэкономить немного места. Дисковое пространство и пропускная способность дешевы, ошибки, которые раздражают клиентов или менеджеров, - нет. Я уверен, что теоретически кодирование Хаффмана будет наиболее эффективным (без потерь), но не самым практичным для этого приложения. Проверьте ссылку, хотя, это может помочь с некоторыми из этих концепций.
Брайан Дж. Стинар
UTF-8 обычно очень эффективная кодировка. Если ваше приложение ориентировано в основном на Азию и другие регионы с многобайтовыми наборами символов, вы можете получить больше пользы от использования UTF-16. Конечно, вы могли бы написать свою собственную кодировку, но это не сэкономит вам столько данных и обеспечит вам много работы.
Если вам действительно нужно сжать ваши данные (и мне интересно, если и почему), вы могли бы лучше всего использовать какой-то алгоритм для сжатия ваших UTF-данных. Большинство алгоритмов работают более эффективно на больших блоках данных, но есть также алгоритмы сжатия небольших кусков текста. Я думаю, вы сэкономите много времени, если будете исследовать их вместо определения собственной кодировки.
Бумага в значительной степени устарела, это уже не 1980 год, поиск фрагментов не является обязательным требованием практически любого приложения для отображения. При разработке приложения, например, iPhone, вы должны планировать l10n для нескольких языков, так что экономить несколько бит только на японском языке немного бессмысленно.
Япония все еще использует Shift-JIS, потому что, как и Китай с GB18030, Гонконг с BIG5 и т. Д., Они имеют большой, стабильный и эффективный пул ресурсов, уже заблокированный в кодировках локалей. Миграция на Unicode требует переписывания значительного количества инструментов инфраструктуры и дополнительного тестирования, которое следует за этим.
Если вы посмотрите на iPod, он экономит биты, поддерживая только латиницу, китайский, японский и корейский языки, пропуская тайские и другие скрипты. Когда цены на память упали, а объемы хранения увеличились с iPhone, Apple смогла добавить поддержку большего количества сценариев.
UTF-8 - это способ сэкономить место, использовать UTF-8 для хранения и конвертировать в UCS-2 или выше для более удобной манипуляции и отображения. Различия между Shift-JIS и Unicode действительно довольно незначительны.
Вы можете использовать несколько растровых изображений и загружать их по требованию, вместо одного растрового изображения, которое пытается охватить все возможные символы.
Один только китайский имеет более 4096 символов, и я говорю не о пунктуации, а о символах, которые используются для формирования слов. Из Википедии:
Количество китайских иероглифов, содержащихся в словаре Канси, составляет приблизительно 47 535, хотя большое количество из них - редко используемые варианты, накопленные за всю историю.
Несмотря на то, что многие из них используются редко, даже если бы 90% не были необходимы, вы все равно исчерпали бы свою квоту. (Я думаю, что фактическое число, используемое в современном тексте, где-то около 10 - 20 тыс.)
Если вы заранее знаете, какие символы вам понадобятся, лучше всего использовать лучшую ставку для создания таблицы косвенных кодов Unicode для индексов в вашей текстуре. Тогда вам нужно всего лишь добавить столько символов в вашу текстуру, сколько вы фактически используете. Я считаю, что Flash (и некоторые PDF-файлы) делают что-то подобное внутри.