Триангуляция данных пути из OpenType.js с помощью Earcut

У меня есть пример использования, когда мне нужно отобразить значительное количество (~ 50 000 глифов) четких, масштабируемых текстовых строк на элементе холста. лучшее решение, которое я пробовал до сих пор, включает в себя триангуляцию текста, нарисованного на элементе холста (текст был нарисован с использованием метода fillText), загрузку форм матрицы и Float32Array треугольников, представляющих эту строку, в графический процессор через WebGL. Используя этот метод, я смог отрендерить 100 000 глифов со скоростью около 30 кадров в секунду. Глифы становятся блочными при очень высоких уровнях масштабирования, но это нормально для моего варианта использования.

Однако этот метод имеет накладные расходы около ~ 250 мс на строку, поскольку я сначала рисую строку в элементе холста в памяти, читаю данные пикселей, превращаю растровое изображение в вектор, а затем триангулирую векторные данные. Поискав в Интернете решения, я наткнулся на два интересных проекта с открытым исходным кодом:

Итак, теперь я хочу переписать свое первоначальное доказательство концепции, чтобы использовать OpenType и Earcut. OpenType для передачи данных кривой в Earcut и Earcut для триангуляции этих данных и возврата массива, представляющего точку для каждого треугольника.

Моя проблема заключается в том, я не могу понять, как получить данные, предоставляемые OpenType, и преобразовать их в формат, который принимает Earcut. Может ли кто-нибудь оказать помощь в этом?

Дополнительная информация:

В этом вопросе StackOverflow содержится отличная информация, но отсутствуют некоторые детали реализации: Текст лучшего качества в WebGL< /а>. Я полагаю, что я пытаюсь выполнить подход «Шрифт как геометрия», описанный в первом ответе.


person Synthesize    schedule 27.05.2018    source источник
comment
Это похоже на проблему XY; зачем вам нужно столько глифов, отображаемых одновременно на одном холсте?   -  person Mike 'Pomax' Kamermans    schedule 28.05.2018
comment
Спасибо за комментарий. Мой вариант использования — веб-приложение для управления проектами, где каждая задача может иметь пять разных строк на максимальном уровне масштабирования, и пользователь может перемещаться/масштабировать расписание. Уже есть отбраковка, поэтому глифы за кадром не отображаются, а также система уровня детализации, поэтому при увеличении масштаба отображается больше деталей. Однако текст отображается при относительно низком уровне масштабирования с видимой одной строкой описания задачи. Обычно более 1000 задач отображаются одновременно.   -  person Synthesize    schedule 29.05.2018
comment
Я думаю, вопрос был в том, нужен ли вам WebGL? Обычный 2D-холст использует то же аппаратное ускорение и имеет более сложные алгоритмы заполнения контуров без необходимости разбивать кривые на сегменты и выполнять триангуляцию.   -  person riv    schedule 29.05.2018
comment
Поэтому моей первой попыткой было использовать два элемента холста. Один с контекстом WebGL и один с CanvasRenderingContext2D. Весь текст рисовался на CanvasRenderingContext2D, который затем накладывался на основной холст контекста GL. Это отлично работало в Chrome и iOS Safari, однако рисование текста было значительно медленнее в Firefox и Edge с большим количеством строк (использовалось 5000 в качестве тестового примера), что в конечном итоге не помогло нам.   -  person Synthesize    schedule 29.05.2018


Ответы (1)


Вы можете создать путь, используя Font.getPath. Путь состоит из инструкций перехода, перехода к линии, перехода к кривой, перехода к квадроциклу и закрытия, доступ к которым осуществляется через path.commands. Конечно, сначала вам нужно будет преобразовать инструкции кривой Безье в небольшие сегменты.

Когда у вас есть набор закрытых путей, вам нужно определить, какие из них являются дырами. Внутренние контуры будут ориентированы в направлении, противоположном внешним, и вы можете назначить их наименьшему внешнему контуру, содержащему их. Когда у вас есть группы «внешних контуров и набор отверстий», вы сможете передать их в библиотеку Earcut.

Это простая реализация, которая предполагает отсутствие пересечений. Для меня это работало очень хорошо для большинства шрифтов, за исключением очень немногих «причудливых» шрифтов с пересекающимися путями.

Вот рабочий пример: https://jsbin.com/gecakub/edit?html,js,output

Вместо того, чтобы создавать сетки для каждой строки, вы также можете создавать их для отдельных символов, а затем размещать их самостоятельно, используя данные кернинга из библиотеки.

Редактировать: это решение будет работать только для шрифтов TTF, хотя его можно легко настроить для CCF (.otf), игнорируя ориентацию пути и используя лучшую проверку «путь A находится внутри пути B», если шрифт имеет пересекающиеся пути.

person riv    schedule 29.05.2018
comment
Благодарю вас! Это потрясающий ответ и именно то, что я искал. - person Synthesize; 29.05.2018
comment
В шрифтах OpenType контуры CFF используют номер пересечения для определения отверстий, тогда как контуры TTF используют направление пути для определения отверстий, а контуры SVG буквально используют то, что вы хотите, просто установите fillrule соответствующим образом для каждого глифа. Итак, я не знаю, игнорирует ли OpenType.js эти различия и просто переписывает пути, или же он учитывает исходный тип глифа, но если вы знаете, какой из этих двух, то стоило бы обновить бит, где вы объясняете, как чтобы определить, какие подпути могут представлять дыры - person Mike 'Pomax' Kamermans; 04.06.2018
comment
Ах, это хороший момент, я использовал только шрифты TTF, понятия не имею, выполняет ли библиотека какую-либо обработку для других типов шрифтов (я использовал FreeType в своем исходном проекте, поэтому я еще меньше знаю об OpenType.js). Это решение определенно сломается на контурах, где направление намотки не имеет значения, но это можно исправить, заменив проверку направления более надежным кодом, чтобы проверить, находится ли путь X внутри пути Y (хотя будет невозможно обрабатывать самопересечения) . - person riv; 04.06.2018