Сергей Галездинов дата публикации 20-01-2009 10:26 Альтернатива TGIFImage
Наверняка многие из нас сталкивались с проблемой: необходимо отобразить в программе некий анимированный объект. Одно из решений — использовать компонент Animate и AVI ролик. Однако найти нужный ролик сложно, да и создать/отредактировать не легче. Второе решение — использовать анимированную графику (коей на данный момент очень много) — и gif тут признанный лидер (хотя, конечно, этот формат не отвечает современным веяниям. В частности никакого альфа-канала и максимум 256 цветная палитра). Есть еще набирающие обороты форматы APNG и MNG, но с анимацией в этих форматах также туго, как с AVI.
Поискав нативные компоненты для отображения gif-анимации в Delphi (то есть не просто поддержка формата, а именно вычленение свойств анимации и ее отображение), можно сказать, что выбор весьма невелик. Это TGifImage от Anders Melander (который стал настолько популярен, что уже входит в поставку с делфи (начиная, кажется, с 2007. в 2009 точно есть)) и TjvGIFImage из известного пакета компонент JEDI. В целом компоненты справляются со своей задачей. Они умеют извлекать, отображать и корректно сохранять информацию из gif-анимаций. И если нужно отобразить небольшое число анимаций, то на этом прочтение статьи можно закончить:) Проблемы начинаются, когда нужно отображать достаточно большое число анимированных объектов или часто извлекать отдельные кадры для последующей обработки. Оба компонента страдают большим недостатком — обе реализации потребляют непомерное число GDI ресурсов и, при интенсивном использовании, дают ощутимые утечки и со временем они могут стать причиной падения вашей программы с ошибкой EOutOfResources.
Используя TGifImage в Qip Infium мы столкнулись с проблемой прожорливого потребления и утечки GDI ресурсов — ведь смайлы у нас в формате gif и используются они весьма интенсивно. Сначала было введено ограничение в 20 смайлов на закладку и ряд ухищрений, чтобы потребление GDI было меньше, однако проблему утечки это не решало. И в конце концов, я решил поискать альтернативные решения. Это должна была быть библиотека, не использующая лишних dll (дабы можно было контролировать весь процесс работы, ловить баги внутри dll — не самая лучшая перспектива) и не страдающая описанным выше недугом.
В процессе поиска я наткнулся на замечательнейшую библиотеку — Vampire Imaging Library. Это кроссплатформенная библиотека на языке Object Pascal, поддерживающая большое число форматов и обладающая поистине широкими возможностями. Настолько стройной, продуманной архитектуры в библиотеках я практически не видел. Одним словом, я влюбился в нее с первого взгляда:) И, хотя библиотека позиционируется скорее для игровых разработчиков, для VCL она тоже вполне пригодна. И поддержка GIF формата заявлена. Так что выбор пал на нее. Однако, как оказалось тогда, поддержка gif была весьма далека от тех требований, что я ставил изначально. Потребление GDI было нулевым, однако не было полной поддержки GIF, не было возможности извлечения информации о кадрах и ни о какой полноценной интеграции в VCL не было и речи. Но библиотека настолько хороша и продумана, что грех было отказываться от такой красоты. Поэтому я решил внести свою лепту в библиотеку и довести поддержку GIF (а заодно и интеграцию с VCL) до ума.
Результат долгой работы вы можете наблюдать здесь, скачав последний релиз (также можно скачать последний срез исходников из svn). Я постарался сделать переход к этой библиотеке от TGifImage максимально простым. Теперь достаточно подключить модуль ImagingComponents1 в uses и вы можете загружать в TImage любую gif анимацию и наслаждаться ею в рантайме2 :) Перечислю преимущества и недостатки моей реализации.
Преимущества:
- Отстутствует потребление GDI, поскольку хранение не в TBitmap, а в массиве цветов/палитре (можно использовать как DIB секцию). GDI потребляются только при отрисовке и сразу же освобождаются.
- Гораздо более низкое потребление памяти при загрузке нескольких экземпляров одного и того же gif за счет собственного механизма подсчета ссылок.
- Честно украденный из TjvGifImage и слегка переработанный алгоритм вкупе с рядом ухищрений по буфферизации отрисовки показывает также более низкое потребление процессора.
- Более корректное отображение анимации. К примеру, полностью поддерживается TrueColor gif (не показатель, конечно, но тем не менее).
- Анимация работает также в формах внутри dll.
- Имеется возможность масштабирования как отдельных фреймов, так и всей анимации в целом.
Недостатки:
- Поскольку TImage не особо предназначен для анимации, то приходится мириться с возникновением артефактов при отрисовке. Когда они могут возникнуть можете прочитать в комментариях к методу Draw. К сожалению, я не смог найти нормального способа, чтобы избавиться от них самой библиотекой. Однако, есть несколько способов от нее избавиться, вручную задав цвет фона или подложку в виде битмапа (см. свойства BackgroundColor, BackgroundBitmap и BackgroundSharing). Моих знаний не хватило, чтобы подобрать такие условия, чтобы обновление бекграунда проходило корректно. Может, вам удастся?
- При сохранении измененного gif могут потеряться заголовки, которые библиотека игнорирует — комментарии, Application Extension, отличный от NETSCAPE 2.0; а также может иметь больший размер на диске, потому что библиотека при работе с форматом GIF оперирует только 8-битным представлением цвета. Опять таки, это можно исправить, сохраняя не через библиотеку, а через TFileStream к примеру.
- Нет работы с ClipboardFormat. Мне это было как-то ненужно, а лентяй я страшный. :о)
- Возможны баги и глюки, поскольку используется довольно много полушаманских способов, дабы достичь самоанимации в TImage. Плюс достаточно много кода работает с указателями и сам автор говорит, что библиотека пока не thread-safe (но для отображения gif анимации это и не нужно при аккуратном использовании). Я проводил тестирование на 20 000 самых разнообразных gif анимаций, но вполне могу появиться такие страшные гифки, не отвечающие стандартам, от которых библиотека может загнуться (если вы такую встретите — просьба сообщить здесь).
Должен предостеречь вас. Если у вас нет тяжелого наследия в виде горы кода, закладывающегося на особенности VCL, то использовать самоанимацию в TImage я бы крайне не советовал. Вместо этого лучше сделать свой контрол и, слегка поправив в классе TImagingGraphic, реализовать отрисовку самому. Подскажу как: достаточно в общем-то по умолчанию присваивать свойству SelfAnimated значение False (но тогда расчет интервалов надо будет делать самому), либо переделать реакцию на смену кадра в PaintTriggered. В папке с модулем вы найдете модуль RVVampireGifAnimate, позволяющий работать с гиф анимацией в TRichView – пример того, как можно работать с классом самому (в TRichView расчет интервалов свой).
Кроме того, проникнув духом библиотеки, базовый класс написан «на вырост» — для реализации форматов MNG и APNG. Поддержка MNG в библиотеке уже есть, APNG планируется. И может быть даже кто-то заинтересуется этими форматами и сделает наконец бесплатный полнофункциональный редактор для создания анимаций в формате APNG и/или MNG :о).
Прошу не забрасывать меня помидорами, а на все огрехи показывать и тыкать меня носом в мануалы:) Исправление ошибок/расширение функционала только приветствуется. Мы эту библиотеку уже используем достаточно широко, чего и вам советую;)
Естественно, текущая реализация не финальная. Следите за изменениями библиотеки, и, конечно же, любая помощь проекту только приветствуется ;)
В прилагаемом архиве вы найдете 3 демо-проекта, демонстрирующих преимущества моей реализации (при запуске TImageSample(TGIFImage).exe будьте готовы к вылету программы с кучей ошибок. Просто поверьте на слово, что TGIFImage не справится с отображением такого количества анимаций:))
P.S. Хотел выложить статью к юбилею королевства, но релиз библиотеки несколько затянулся. Всех с наступающим новым годом!
1Замечу, что подключать нужно модуль из папки Extras\Contrib\ExtraGIF\
2Если загрузка идет через TGraphic и только потом загружается в TImage, то пользуйтесь классом TImagingGifGraphic или его символическим близнецом TGifImage (добавлен для более безболезненного перехода от TGifImage, если он уже используется в проекте и не используются какие-то специфические свойства)
К статье прилагаются примеры
[GDI, рисование на канве] [Анимация] [Растры, BMP]
Обсуждение материала [ 14-11-2011 07:11 ] 10 сообщений |