Пока реализовывал поддержку рамок для Йокселя, кое-что накопал. Итак, самое главное требование для реализации быстрой отрисовки очень просто: минимум обращений к GDI. И все будет хорошо.
На практике это означает примерно следующее.
У Мокселя есть пара стилей рамок, для которых в стандартном GDI нет предопределенных стилей. Первоначально отрисовку таких рамок я реализовал при помощи обычных «сплошных» (solid) перьев, отрисовывая каждый штрих самостоятельно. В результате на более-менее сложных таблицах тормоза возникали просто неимоверные. Количество вызовов GDI зашкаливало за десятки тысяч. Пришлось перейти на использование пользовательских стилей для перьев. Перо с пользовательским стилем создается через функцию Ext Create Pen?:
В качестве dwPenStyle передаем стиль PS_USERSTYLE, а в lpStyle – массив, определяющий стиль (длина штриха, длина промежутка и т.д.) Правда, PS_USERSTYLE поддерживается только начиная с Windows NT. В результате перехода на перья с пользовательскими стилями количество обращений к GDI сократилось в несколько раз.
В случае с Йокселем реализация проста. Есть функция Get Pen?, принимающая несколько параметров – свойств пера. Параметры складываются в некоторую простую структуру. Далее структура используется в качестве ключа в обычном std::map. Если элемент с таким ключом отсутствует – перо создается и добавляется в кеш. Если элемент есть – он просто сразу возвращается. Как правило, такой метод производительность повышает просто радикально.
Возьмем такую таблицу
Эта таблица легко и просто отрисовывается в Excel. При прокрутке не возникает вообще никаких тормозов. А вот в Йокселе подтормаживание было очень ощутимо. Все объяснялось тем, что Йоксель отрисовывал рамки для каждой ячейки отдельно. Т.е. для таблицы 10х10 ячеек – 100 ячеек – 100 * 4 рамки = 400 рамок отрисовывалось отдельными вызовами GDI. Для исправления ситуации алгоритм отрисовки рамок был существенно изменен. При перерисовке ячеек заполняются две матрицы с данными о рамках: горизонтальные рамки и вертикальные рамки. Далее эти матрицы просто обходятся и анализируются. При этом, если несколько соседних ячеек имеют одинаковый стиль рамки, то рамка для них всех будет отрисована за один вызов GDI. В случае вышеуказанной таблицы 10х10 ячеек понадобится всего 20 вызовов функций рисования. Итого получаем ускорение в 20 раз.
Более того, улучшается качество отрисовки. Рассмотрим пример:
Здесь мы видим ряд ячеек, для каждой из которых установлен одинаковый стиль нижней рамки: пунктирная линия средней толщины. По этому рисунку четко видно, что Моксель рисует рамки индивидуально для каждой ячейки. Из-за этого на стыках ячеек можно видеть некоторые артефакты: удлиненные штрихи или укороченные промежутки. А вот для Йокселя стиль линии соблюдается четко по всей длине.
Собственно, использованный стиль практически единственный, на котором подобные артефакты хорошо заметны. На других стилях артефакты конечно тоже встречаются, но уже не так заметны. Поэтому для Мокселя подобный стиль рисования сходит с рук. Для Йокселя это уже становится важным, т.к. Йоксель теперь поддерживает и стили Excel, которые при моксельной отрисовке выглядят очень плохо.