Привет, Хабр! Меня зовут Антон, я сделал Android-приложение Watermark Studio для водяных знаков на фото и видео. Расскажу, как оно устроено, какие решения оказались удачными, а какие пришлось компромиссно упрощать. Это не обзор рекламы, а скорее заметки разработчика.
Задача и ограничения
Нужно было приложение, которое ставит знак (текст, логотип, QR-код, сетку) на фото и видео, работает на Android 9 и выше, не отправляет файлы на сервер и нормально чувствует себя на старых телефонах. Отсюда выбор стека: Kotlin, Jetpack Compose, Material 3, Navigation Compose, WorkManager для пакетной обработки, Room для шаблонов и истории. minSdk = 28, compileSdk = 35, сборка с R8.
Фото
Для фото всё довольно прямолинейно: знак рисуется на Bitmap через Canvas. Самое интересное тут не отрисовка, а то, что пользователь хочет видеть результат до сохранения и менять размер и поворот двумя пальцами. Я разделил модель знака (позиция, размер, прозрачность, угол, текст, логотип) и рендерер, который по этой модели собирает итоговую картинку. Один и тот же рендерер используется и для предпросмотра, и для пакетной обработки, поэтому результат на экране совпадает с сохранённым файлом.
Видео
Для видео я взял Media3 Transformer. Знак накладывается как оверлей на каждый кадр, звук при этом сохраняется. Для знака на весь ролик достаточно статического BitmapOverlay, а для движущейся метки нужен оверлей с меняющимся положением. Основные сложности тут не в самой накладке, а в том, что транскодирование на слабых телефонах идёт заметно дольше и требует аккуратной работы с памятью и прогрессом. Пакетная обработка до 10 видео за раз у меня идёт через WorkManager, чтобы задача переживала сворачивание приложения.
Удаление фона у логотипа
Логотипу нужен прозрачный фон, чтобы он нормально смотрелся поверх фото. Для этого я использую небольшую модель силуэтной сегментации в формате ONNX и ONNX Runtime на устройстве. Модель весит около 44 МБ, поэтому не зашита в APK: она скачивается один раз по запросу пользователя, дальше всё работает без интернета. Автоматический результат не всегда идеален, поэтому добавлена ручная подчистка кистью: пользователь стирает или возвращает участки маски. Это сильно улучшило практический результат.
Бесплатный лимит без аккаунтов
Тут пришлось подумать. Бесплатно доступно 5 фото и 1 видео в день, Pro выдаётся на срок. Аккаунты я заводить не хотел: это лишний барьер. Поэтому есть небольшой сервер (Python и SQLite), который хранит дневной счётчик по хешу устройства и проверяет коды активации Pro. Запросы подписываются HMAC-SHA256. Честный минус: секрет для подписи лежит в APK, так что клиентскую часть при желании можно обойти. Для моих целей это приемлемо, но вы должны это понимать, если делаете что-то подобное. Фото и видео на сервер не уходят вообще, только счётчик, активация и проверка обновлений.
Распространение
Пока приложение ставится по APK с сайта, оно само проверяет обновления и предлагает скачать новую версию. Установка только с подтверждения пользователя. Магазины приложений в планах, но на старте мне важнее получить живую обратную связь.
Что бы я сделал иначе
Сразу бы вынес рендерер знака в отдельный модуль и покрыл тестами: на нём держится вся логика.
Раньше бы начал тестировать на слабых телефонах, а не на своём.
Не стал бы класть секрет подписи в клиент, а продумал бы схему получше.
Буду рад вопросам и замечаниям в комментариях, особенно по Media3 на слабых устройствах и по схеме бесплатных лимитов без аккаунтов. Сайт проекта: https://watermarkstudio.ru