Жук в Canvas.drawBitmap на андроиде.

Jul 20, 2015 23:07

Оригинал взят у droids_life в Жук в Canvas.drawBitmap на андроиде.



Занятный жук всплыл при переработке кода шашек. Там был алгоритм рендеринга подсветки полей, куда может ходить шашечка. Использовалась т.н. «off-screen» битовая карта для промежуточных результатов. Схема несложная:

for (клетка in поля для хода)
{
    отрисовать клетку в битовую карту;
    canvas.drawBitmap(...);
}

Сия свистопляска нужна для случая, когда шашечка может прийти в конечную клетку несколькими путями. Тогда мы красим метку окончания хода в несколько цветов. Эта покраска делается равными (по углу) сегментами окружности, маскированными картинкой от нашего дизайнера.

Маска (зелёное - нулевая альфа):


Сегменты без маскирования:


Итоговый выхлоп:


В игре:


Если б разноцветности не было, то можно просто рисовать чёрно-белую картинку с матричным преобразованием цвета (см. ColorMatrixColorFilter). После переработки кода по какой-то причине вышеозначенный схематоз сломался: все клетки стали рисоваться, как последняя в цикле. Как будто содержимое битовой карты на каждый вызов drawBitmap было одним и тем же. Как выяснилось, причина проблемы появилась ещё в 2011 и не исправлена до сих пор.

Начиная с версии андроида 3.0 все приложения, которые в своём манифесте в targetSdkVersion ставят 14, автоматом получают рендеринг пользовательского интерфейса через видеокарту. Про трансляцию методов класса Canvas в соотв. OpenGL ES вызовы очень интересно рассказывал Romain Guy на Google IO 2011.

Создавать на каждый кадр, а уж тем более на каждую клетку, по битовой карте - непозволительное расточительство. Поэтому в памяти была одна картинка, в которую всё и рисовалось. Кроме флагов в манифесте есть ещё условия неполучения аппаратного ускорения. И они были соблюдены в шашках. А после переработки кода получилось так, что включилось аппаратное ускорение, и рендеринг через объект класса Canvas переставал быть моментальным (т.н. immediate mode) - все команды на отрисовку начали идти в буфер, который потом выполняла нить рендеринга. Соответственно каждый вызов drawBitmap запоминал в качестве своего аргумента ссылку на одну и ту же битовую карту, содержимое которой менялось (кстати, тоже через Canvas, созданный с ней в качестве аргумента конструктора).

Простейшее решение, явно запретить аппаратный рендеринг пользовательского интерфейса, не сработало на аппаратах с большим экраном - анимации лютейшим образом тормозили. Что интересно, на старичках с андроидом версии 2.3 и экраном 320x480 всё было замечательно. Видимо, в гугле таким же образом пришли к реализации рисования UI через OpenGL (упёрлись в пропускную способность памяти?): экраны телефонов расли, да и планшеты надо было окучивать.

В итоге переписал алгоритм на рендеринг без промежуточного буфера - запользовал класс BitmapShader при отрисовке сегментов окружности.

android, засады

Previous post Next post
Up