Сохранение истории в графическом редакторе?

Здравствуйте. Вот задался вопросом — как корректно сохранять историю в графическом редакторе (как Гимп) для использования в UNDO/REDO. Сам вижу три варианта:

1. Сохранять список действий.

Например, нарисовал линию такого-то цвета в таких координатах, применил фильтр «Блур», повернул изображение. В случае использования UNDO проходятся все действия от первого до необходимого.
Преимущество: Занимает очень мало места в памяти. При сохранении проекта можно с легкостью восстановить всю историю. При желании можно даже хранить полное дерево (например сделали действие A, B, C, D, E, вернулись к С, сделали F, G. Итоговый путь получился ABCFG, но можно как в системе контроля версий сохранить и ветку DE)
Недостаток: При использовании Undo или загрузке ранее сохраненного изображения проходится весь путь, а это может играть существенное время и если при обычном простом редактировании такой проход может занять каких-то 100-200 мс, что некритично, так при использовании тяжелых фильтров может замедлиться до десятков секунд.

2. Сохранять полный дамп проекта в bitmap

Каждое действие всю картинку мы сохраняем в память как готовый рисунок.
Преимущество: Очень быстро работает. Сделали undo/redo и просто показали из памяти тот рисунок, который у нас был (уже отрендеренный). Даже тяжелый фильтр, что угодно.
Недостаток: Занимает очень много места в памяти. Допустим, у нас рисунок размером 1k*1k. В каждом пикселе мы храним 32 бита информации (по 8 бит на r,g,b и 8 бит на альфаканал), то есть 4 байта, то есть такой битмап у нас будет занимать в идеале 4 метра, а история из 64 действий — 64*4 = 256 мб в памяти. А если допустить, что у нас графический редактор поддерживает слои и пусть их будет хотя бы пять… Аналогично, проекты с историей хранить тяжело.

Gimp

В Гимпе по косвенным признакам я опеределил, что используется второй подход. Признаки такие:

1. История ограничена

2. Тяжелые фильтры при ундо-редо возвращают статус кво очень быстро.

3. Совмещенный подход

Конечно, есть альтернатива. Храним историю первым способом, а последних, например, 8 действий храним вторым способом, т.к. редко используется такой дальний undo. Сохранять на винт можно только действия, из-за чего проект с множеством изменений будет долго открываться, но допустимо весить. Или еще добавить последнюю битмап дату.
Преимущества обоих подходов
Недостаток — значительно утяжеляется логика приложения. Фактически — надо реализовывать два механизма истории.

Тема для обсуждения

Что скажете вы? Может, есть еще какой-то подход. Если нет, то какой из подходов предпочли бы вы (в идеальном случае и в случае очень сжатых сроков)? Почему?
  • Вопрос задан
  • 3146 просмотров
Решения вопроса 1
По моему третий способ было бы логично дополнить некими ключевыми кадрами (по аналогии с видео), по сути bitmap, которые бы сохранялись после тяжелых фильтров и хранились бы не в оперативе, а на винте (во временной папке). Т.е. как Вы и описали, несколько последних действий в оперативке в виде bitmap. История назад в виде списка действий от ключевых кадров (тяжелых фильтров). Да и такие ключевые кадры было бы логично хранить не в сыром виде, а хоть в том же png. Это позволит не так значительно тормозить при откате на далекую историю и экономить место на винте за счет сжатия png.

А вообще, идею с ключевыми кадрами можно было бы вообще полностью применить вместо третьего варианта. Т.е. мелкие действия хранить именно как действия, а более тяжелые, как ключевые кадры в виде bitmap в оперативке. Если более тяжелое действие отодвигается в прошлое, то паковать его в png и сбрасывать на винт.

И… по поводу слоёв… у нас же undo обычно применяется к конкретному действию на конкретном слое, а не ко всем слоям одновременно. Конечно есть live-фильтры, но это отдельная история. Я к тому, что число слоёв не увеличивает кол-во bitmap пропорционально.
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
Интересно что-то типа хранить несколько последних итераций, а на остальные делать патчи типа diff фоновым процессом.
Ответ написан
Milfgard
@Milfgard
А нельзя в каких-то случаях выстроить последовательность, обратную первой?

В смысле, бывают обратимые изменения, а бывают необратимые. Логично хранить ключевой кадр после необратимых, а от основного состояния достраиваться до ближайшего обратимого в обратном порядке, используя историю на уже имеющийся кадр, проходя путь с конца, а не с начала.
Ответ написан
Milfgard
@Milfgard
А если использовать геометрическаю распределённость? Сохранять не ключевые кадры целиком, а только картинки из области изменений, а потом и накладывать их друг на друга?

То есть каждый мазок кисточкой — это сохранение битмэпа в пределах тех ячеек сетки, где он прошел, а не всей картины.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы