Реализация наложения фильтров на фотографии в мобильном приложении
Ленты-карусели с превью фильтров — как в Instagram. Пользователь листает пальцем, в реальном времени видит результат. Если предпросмотр подтормаживает или применение финального фильтра занимает 2+ секунды, UX разрушен. Вся техника здесь — про скорость.
Как работают фильтры на уровне GPU
Фильтр — это попиксельное преобразование: берём RGBA-значение каждого пикселя, применяем математику, записываем новое значение. Наивная реализация на CPU для 12 Мп фото займёт 200–400 мс. GPU обрабатывает те же пиксели параллельно за 5–15 мс.
iOS. Два пути: CIFilter (Core Image) и Metal шейдеры.
CIFilter — проще, встроен в систему. Для большинства фотофильтров хватает: CIColorMonochrome, CIPhotoEffectChrome, CIVignette, CIColorCurves (iOS 16+). Строим цепочку фильтров через filter.outputImage → следующий фильтр. CIContext(options: [.useSoftwareRenderer: false]) обязателен — иначе рендер на CPU.
Metal нужен, когда хочется эффектов, которых нет в CIFilter: дуотон с кастомными цветами, zonal adjustments, LUT-фильтры высокого разрешения. Пишем fragment shader на MSL:
fragment float4 duotoneFilter(VertexOut in [[stage_in]],
texture2d<float> texture [[texture(0)]]) {
float4 color = texture.sample(sampler, in.texCoord);
float luma = dot(color.rgb, float3(0.299, 0.587, 0.114));
return mix(shadowColor, highlightColor, luma);
}
Android. GPUImage (библиотека) — порт популярных iOS-фильтров на OpenGL ES. GPUImageFilter, GPUImageFilterGroup для цепочек. Для предпросмотра — GPUImageView показывает результат напрямую на SurfaceTexture без промежуточного Bitmap. В новых проектах смотрим в сторону Vulkan через RenderScript Intrinsics Replacement Toolkit.
Flutter. image (pub.dev) — CPU-обработка, медленно для превью. Для реального времени — ColorFiltered виджет с ColorFilter.matrix() для базовых коррекций. Сложные фильтры — через Flutter Texture + нативный OpenGL/Metal код на платформенной стороне.
LUT-фильтры: профессиональный подход
Look Up Table — 3D-таблица 64×64×64 (или 16×16×16 для мобильных). Каждый RGB-вход даёт RGB-выход через интерполяцию. Дизайнер создаёт LUT в Lightroom/Photoshop, разработчик подключает как текстуру в шейдер. На iOS — CIColorCube или CIColorCubeWithColorSpace. Одна LUT-текстура заменяет набор цветокорректирующих операций.
Превью карусели без тормозов
Генерируем превью заранее: при загрузке экрана создаём уменьшенную копию (300×300 px) через UIGraphicsImageRenderer, прогоняем через каждый фильтр, кешируем в NSCache. Пользователь листает карусель — картинки уже готовы. Финальный рендер в полном разрешении запускается только после выбора фильтра.
Сохранение настроек фильтра
Пользователь выбрал «Хром» с интенсивностью 0.7 — при возврате к посту это значение должно восстановиться. Сохраняем не готовое изображение, а параметры фильтра (filterName, intensity) — это позволяет редактировать фото после сохранения черновика без потери качества.
Финальный рендер в полном разрешении запускаем только перед публикацией или экспортом. Это сохраняет оригинал нетронутым и даёт возможность отменить фильтр в любой момент.
Сроки
Набор из 6–10 CIFilter-фильтров с карусельным превью — 3–4 дня. LUT-система с кастомными шейдерами Metal/OpenGL — 5–7 дней.







