Skip to content

New filtering stage #50

@noobie-iv

Description

@noobie-iv

@zvezdochiot, @plzombie

Если планируем новую ветку с промежуточными фильтрами, надо решить, как жить дальше.

Как было раньше (в старых ST, за 2008 год):

  • Исходные файлы идентифицируются по ImageId. Внутри Id - всего лишь полный путь к файлу, так что это уникальный идентификатор.
  • В ходе работы делается нарезка на страницы, которые все равно реально представлены одним изображением. Поэтому id завернут внутрь PageId, который хранит ImageId и дополнительно номер страницы.
  • Все фильтры, кроме последнего, не меняют исходное изображение, а только масштабируют, поворачивают, и подрезают. Все это умеет делать Qt на лету при рисовании QImage. Поэтому от фильтра к фильтру передается исходный QImage, а каждый фильтр только правит матрицы преобразований и контур подрезки, а Qt их рисует, когда надо - такая обработка происходит очень быстро.

После выбора новой страницы обработка идет в три стадии Обработка изображения, Обновление эскиза, Прорисовка эскиза:

  • Обработка изображения

    • Обработка происходит в Task::process()
    • Главное окно собирает список задач (у каждой задачи есть указатель на следующую, или нуль).
    • Получается цепочка LoadFileTask -> fix_orientation::Task -> page_split::Task -> ... -> null.
    • LoadFileTask загружает исходный файл, и далее по цепочке передается Id, QImage и Transformation.
    • Каждый фильтр в цепочке правит либо трансформацию (ориентация, поворот), либо подрезку (страницы, поля).
    • По завершении результаты (изображение, трансформация и подрезка) передаются в центральное изображение, где Qt и рисует исходную картинку - повернутую и подрезанную.
  • Обновление эскиза

    • Обработка происходит в CacheDrivenTask::process()
    • Первая стадия сигналит списку эскизов (ThumbnailSequence), и тот запускает вторую цепочку из CacheDrivenTask.
    • В ней нет файловых операций, она только возвращает результирующую трансформацию для Id.
    • В список эскизов помещается обновленный легковесный Item c Id внутри.
  • Прорисовка эскиза

    • Прорисовка происходит в обработчике события paint()
    • Эскиз, которому надо прорисоваться, лезет в кеш (ThumbnailPixmapCache) по PageId.
    • Кеш для PageId генерирует уникальное имя файла для записи на диск, создает и записывает туда изображение, и возвращает эскизу для прорисовки.
    • Уникальное имя получается дописыванием к исходному имени хеша, полученного из полного пути (который как раз есть в Id). Уникальный путь дает уникальных хеш и имя эскиза для записи.

Важно, что прорисовка эскизов оптимизирована. Как и с главной картинкой, на всех стадиях кроме последней, используется одно и то же изображение. Это можно посмотреть во время работы ST в каталоге out\cache\thumbs. На всех стадиях в кеше хранится одна копия эскиза, потому имя файла кеш генерирует по одному и тому же Id. А зрительно эскизы отличаются потому, что после загрузки из кеша эскиз еще кое-что дорисовывает поверх сам.

Исключение - последняя стадия, output. На этой стадии сначала Task::process() создает новое окончательное изображение, и записывает его в каталог out под именем out_file_path. А потом создает новый эскиз, у которого Id подменен на новый - PageId(ImageId(out_file_path)). И цепочка CacheDrivenTask::process() тоже возвращает новый идентификатор - PageId(ImageId(out_file_path)). После этого при рисовании эскиза в кеше появится новая запись, потому что хеш пути поменялся, и на стадии output эскиз становится другой.

Как стало в ST/STA/STU

Если попробовать поменять изображение в какой-нибудь из промежуточных стадий (как в STD) - ломается логика работы цепочек Task/CacheDrivenTask и кеша, которые в оригинале максимально легковесны. Возможно, поэтому во всех поздних версиях ST обработку изображения затолкали на вкладки последней стадии, чтобы не переделывать базовую логику.

Как сделано в STEX

После появления в середине обработки стадии dewarp понадобилось обновлять эскизы, если сделана развертка (а если только поворот - то не надо). Поэтому кеш был доработан: элементам ThumbId внутри него добавлены флаги isAffineTransform. Эти флаги отличают распрямленные изображения от обычных, и кеш генерирует для них другие имена (см. метод ThumbnailPixmapCache::Impl::getThumbFilePath). Поэтому для развернутых изображений кеш, начиная со стадии dewarp и до конца, создает дополнительные файлы эскизов, которые не смешиваются с эскизами предыдущих стадий.

Что должно быть теперь?

Теперь планируется в еще одной новой стадии менять изображение по дороге. Разумеется, нужно менять и эскиз. Какие есть варианты? Сходу могу предложить:

  • Просто генерировать в кеше для разных стадий разные имена файлов: img_1_XXX, img_2_XXX, img_3_XXX. Просто и дубово. Появятся лишние чтения - записи.
  • Ввести параметр "версия эскиза", и передавать ее по цепочкам. Какой фильтр нахулиганил - увеличивает версию, и в кеше генерируется новый файл. Это аналог решения из STEX. Видимо, средне-сложно.
  • Принудительно записывать новый файл на диск, если фильтр поменял изображение, и дальше передавать уже его Id. Придется в каталоге out завести подкаталоги для вредных фильтров. Это аналог решения в стадии output. Видимо, самое сложное.
    • В отладочной версии сейчас после распрямления видны жуткие тормоза, от стадии dewarp и до самого конца. Видимо, повторные генерации изображения тормозят. Решение с промежуточной записью должно вроде ускорить дело.
    • Теоретически, тут можно и внешнюю обработку добавить. Внешняя программа как раз через этот промежуточный сохраненный файл и обменивалась бы с ST.

Metadata

Metadata

Assignees

No one assigned

    Labels

    disputeDebateenhancementNew feature or requestquestionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions