Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Fixed
- Fixed 16 KB page-size compatibility by replacing the editor photo filters native library.

## [1.13.1] - 2026-02-14
### Changed
Expand Down
1 change: 0 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ dependencies {
implementation(libs.androidx.documentfile)
implementation(libs.androidx.media3.exoplayer)
implementation(libs.sanselan)
implementation(libs.androidphotofilters)
implementation(libs.androidsvg.aar)
implementation(libs.gestureviews)
implementation(libs.subsamplingscaleimageview)
Expand Down
25 changes: 0 additions & 25 deletions app/lint-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -210,31 +210,6 @@
column="17"/>
</issue>

<issue
id="Aligned16KB"
message="The native library `arm64-v8a/libNativeImageProcessor.so` (from `com.github.naveensingh:androidphotofilters:193f2ae509`) is not 16 KB aligned">
<location
file="$GRADLE_USER_HOME/caches/9.4.1/transforms/51a908bb5df44a6eafbf9daeee56e27b/transformed/jetified-androidphotofilters-193f2ae509/jni/arm64-v8a/libNativeImageProcessor.so"/>
</issue>

<issue
id="Aligned16KB"
message="The native library `arm64-v8a/libNativeImageProcessor.so` (from `com.github.naveensingh:androidphotofilters:193f2ae509`) is not 16 KB aligned">
<location
file="$GRADLE_USER_HOME/caches/9.4.1/transforms/51a908bb5df44a6eafbf9daeee56e27b/transformed/jetified-androidphotofilters-193f2ae509/jni/arm64-v8a/libNativeImageProcessor.so"/>
</issue>

<issue
id="Aligned16KB"
message="The native library `arm64-v8a/libNativeImageProcessor.so` (from `com.github.naveensingh:androidphotofilters:193f2ae509`) is not 16 KB aligned"
errorLine1="androidphotofilters = { module = &quot;com.github.naveensingh:androidphotofilters&quot;, version.ref = &quot;androidphotofilters&quot; }"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="$HOME/work/Gallery/Gallery/gradle/libs.versions.toml"
line="69"
column="24"/>
</issue>

<issue
id="DiscouragedApi"
message="Fixed screen orientations will be ignored in most cases, starting from Android 16. Android is moving toward a model where apps are expected to adapt to various orientations, display sizes, and aspect ratios."
Expand Down
313 changes: 313 additions & 0 deletions app/src/main/kotlin/com/zomato/photofilters/FilterPack.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
@file:Suppress(
"MagicNumber",
"LongMethod",
"LongParameterList",
"ReturnCount",
"TooManyFunctions",
"UnusedParameter"
)

package com.zomato.photofilters

import android.content.Context
import com.zomato.photofilters.geometry.Point
import com.zomato.photofilters.imageprocessors.Filter
import com.zomato.photofilters.imageprocessors.subfilters.BrightnessSubFilter
import com.zomato.photofilters.imageprocessors.subfilters.ColorOverlaySubFilter
import com.zomato.photofilters.imageprocessors.subfilters.ContrastSubFilter
import com.zomato.photofilters.imageprocessors.subfilters.SaturationSubFilter
import com.zomato.photofilters.imageprocessors.subfilters.ToneCurveSubFilter
import com.zomato.photofilters.imageprocessors.subfilters.VignetteSubFilter

object FilterPack {
/***
* the filter pack,
* @param context
* @return list of filters
*/
fun getFilterPack(context: Context): List<Filter> {
val filters: MutableList<Filter> = ArrayList()
filters.add(getAweStruckVibeFilter(context))
filters.add(getClarendon(context))
filters.add(getOldManFilter(context))
filters.add(getMarsFilter(context))
filters.add(getRiseFilter(context))
filters.add(getAprilFilter(context))
filters.add(getAmazonFilter(context))
filters.add(getStarLitFilter(context))
filters.add(getNightWhisperFilter(context))
filters.add(getLimeStutterFilter(context))
filters.add(getHaanFilter(context))
filters.add(getBlueMessFilter(context))
filters.add(getAdeleFilter(context))
filters.add(getCruzFilter(context))
filters.add(getMetropolis(context))
filters.add(getAudreyFilter(context))
return filters
}

fun getStarLitFilter(context: Context): Filter {
val rgbKnots = arrayOfNulls<Point>(8)
rgbKnots[0] = Point(0f, 0f)
rgbKnots[1] = Point(34f, 6f)
rgbKnots[2] = Point(69f, 23f)
rgbKnots[3] = Point(100f, 58f)
rgbKnots[4] = Point(150f, 154f)
rgbKnots[5] = Point(176f, 196f)
rgbKnots[6] = Point(207f, 233f)
rgbKnots[7] = Point(255f, 255f)
val filter = Filter()
filter.name = "Starlit"
filter.addSubFilter(ToneCurveSubFilter(rgbKnots, null, null, null))
return filter
}

fun getBlueMessFilter(context: Context): Filter {
val redKnots = arrayOfNulls<Point>(8)
redKnots[0] = Point(0f, 0f)
redKnots[1] = Point(86f, 34f)
redKnots[2] = Point(117f, 41f)
redKnots[3] = Point(146f, 80f)
redKnots[4] = Point(170f, 151f)
redKnots[5] = Point(200f, 214f)
redKnots[6] = Point(225f, 242f)
redKnots[7] = Point(255f, 255f)
val filter = Filter()
filter.name = "BlueMess"
filter.addSubFilter(ToneCurveSubFilter(null, redKnots, null, null))
filter.addSubFilter(BrightnessSubFilter(30))
filter.addSubFilter(ContrastSubFilter(1f))
return filter
}

fun getAweStruckVibeFilter(context: Context): Filter {
val rgbKnots = arrayOfNulls<Point>(5)
rgbKnots[0] = Point(0f, 0f)
rgbKnots[1] = Point(80f, 43f)
rgbKnots[2] = Point(149f, 102f)
rgbKnots[3] = Point(201f, 173f)
rgbKnots[4] = Point(255f, 255f)

val redKnots = arrayOfNulls<Point>(5)
redKnots[0] = Point(0f, 0f)
redKnots[1] = Point(125f, 147f)
redKnots[2] = Point(177f, 199f)
redKnots[3] = Point(213f, 228f)
redKnots[4] = Point(255f, 255f)


val greenKnots = arrayOfNulls<Point>(6)
greenKnots[0] = Point(0f, 0f)
greenKnots[1] = Point(57f, 76f)
greenKnots[2] = Point(103f, 130f)
greenKnots[3] = Point(167f, 192f)
greenKnots[4] = Point(211f, 229f)
greenKnots[5] = Point(255f, 255f)


val blueKnots = arrayOfNulls<Point>(7)
blueKnots[0] = Point(0f, 0f)
blueKnots[1] = Point(38f, 62f)
blueKnots[2] = Point(75f, 112f)
blueKnots[3] = Point(116f, 158f)
blueKnots[4] = Point(171f, 204f)
blueKnots[5] = Point(212f, 233f)
blueKnots[6] = Point(255f, 255f)

val filter = Filter()
filter.name = "Struck"
filter.addSubFilter(ToneCurveSubFilter(rgbKnots, redKnots, greenKnots, blueKnots))
return filter
}

fun getLimeStutterFilter(context: Context): Filter {
val blueKnots = arrayOfNulls<Point>(3)
blueKnots[0] = Point(0f, 0f)
blueKnots[1] = Point(165f, 114f)
blueKnots[2] = Point(255f, 255f)
val filter = Filter()
filter.name = "Lime"
filter.addSubFilter(ToneCurveSubFilter(null, null, null, blueKnots))
return filter
}

fun getNightWhisperFilter(context: Context): Filter {
val rgbKnots = arrayOfNulls<Point>(3)
rgbKnots[0] = Point(0f, 0f)
rgbKnots[1] = Point(174f, 109f)
rgbKnots[2] = Point(255f, 255f)

val redKnots = arrayOfNulls<Point>(4)
redKnots[0] = Point(0f, 0f)
redKnots[1] = Point(70f, 114f)
redKnots[2] = Point(157f, 145f)
redKnots[3] = Point(255f, 255f)

val greenKnots = arrayOfNulls<Point>(3)
greenKnots[0] = Point(0f, 0f)
greenKnots[1] = Point(109f, 138f)
greenKnots[2] = Point(255f, 255f)

val blueKnots = arrayOfNulls<Point>(3)
blueKnots[0] = Point(0f, 0f)
blueKnots[1] = Point(113f, 152f)
blueKnots[2] = Point(255f, 255f)

val filter = Filter()
filter.name = "Whisper"
filter.addSubFilter(ContrastSubFilter(1.5f))
filter.addSubFilter(ToneCurveSubFilter(rgbKnots, redKnots, greenKnots, blueKnots))
return filter
}

fun getAmazonFilter(context: Context): Filter {
val blueKnots = arrayOfNulls<Point>(6)
blueKnots[0] = Point(0f, 0f)
blueKnots[1] = Point(11f, 40f)
blueKnots[2] = Point(36f, 99f)
blueKnots[3] = Point(86f, 151f)
blueKnots[4] = Point(167f, 209f)
blueKnots[5] = Point(255f, 255f)
val filter = Filter("Amazon")
filter.addSubFilter(ContrastSubFilter(1.2f))
filter.addSubFilter(ToneCurveSubFilter(null, null, null, blueKnots))
return filter
}

fun getAdeleFilter(context: Context): Filter {
val filter = Filter("Adele")
filter.addSubFilter(SaturationSubFilter(-100f))
return filter
}

fun getCruzFilter(context: Context): Filter {
val filter = Filter("Cruz")
filter.addSubFilter(SaturationSubFilter(-100f))
filter.addSubFilter(ContrastSubFilter(1.3f))
filter.addSubFilter(BrightnessSubFilter(20))
return filter
}

fun getMetropolis(context: Context): Filter {
val filter = Filter("Metropolis")
filter.addSubFilter(SaturationSubFilter(-1f))
filter.addSubFilter(ContrastSubFilter(1.7f))
filter.addSubFilter(BrightnessSubFilter(70))
return filter
}

fun getAudreyFilter(context: Context): Filter {
val filter = Filter("Audrey")
val redKnots = arrayOfNulls<Point>(3)
redKnots[0] = Point(0f, 0f)
redKnots[1] = Point(124f, 138f)
redKnots[2] = Point(255f, 255f)

filter.addSubFilter(SaturationSubFilter(-100f))
filter.addSubFilter(ContrastSubFilter(1.3f))
filter.addSubFilter(BrightnessSubFilter(20))
filter.addSubFilter(ToneCurveSubFilter(null, redKnots, null, null))
return filter
}

fun getRiseFilter(context: Context): Filter {
val blueKnots = arrayOfNulls<Point>(4)
blueKnots[0] = Point(0f, 0f)
blueKnots[1] = Point(39f, 70f)
blueKnots[2] = Point(150f, 200f)
blueKnots[3] = Point(255f, 255f)

val redKnots = arrayOfNulls<Point>(4)
redKnots[0] = Point(0f, 0f)
redKnots[1] = Point(45f, 64f)
redKnots[2] = Point(170f, 190f)
redKnots[3] = Point(255f, 255f)

val filter = Filter("Rise")
filter.addSubFilter(ContrastSubFilter(1.9f))
filter.addSubFilter(BrightnessSubFilter(60))
filter.addSubFilter(VignetteSubFilter(context, 200))
filter.addSubFilter(ToneCurveSubFilter(null, redKnots, null, blueKnots))
return filter
}

fun getMarsFilter(context: Context): Filter {
val filter = Filter("Mars")
filter.addSubFilter(ContrastSubFilter(1.5f))
filter.addSubFilter(BrightnessSubFilter(10))
return filter
}

fun getAprilFilter(context: Context): Filter {
val blueKnots = arrayOfNulls<Point>(4)
blueKnots[0] = Point(0f, 0f)
blueKnots[1] = Point(39f, 70f)
blueKnots[2] = Point(150f, 200f)
blueKnots[3] = Point(255f, 255f)

val redKnots = arrayOfNulls<Point>(4)
redKnots[0] = Point(0f, 0f)
redKnots[1] = Point(45f, 64f)
redKnots[2] = Point(170f, 190f)
redKnots[3] = Point(255f, 255f)

val filter = Filter("April")
filter.addSubFilter(ContrastSubFilter(1.5f))
filter.addSubFilter(BrightnessSubFilter(5))
filter.addSubFilter(VignetteSubFilter(context, 150))
filter.addSubFilter(ToneCurveSubFilter(null, redKnots, null, blueKnots))
return filter
}

fun getHaanFilter(context: Context): Filter {
val greenKnots = arrayOfNulls<Point>(3)
greenKnots[0] = Point(0f, 0f)
greenKnots[1] = Point(113f, 142f)
greenKnots[2] = Point(255f, 255f)

val filter = Filter("Haan")
filter.addSubFilter(ContrastSubFilter(1.3f))
filter.addSubFilter(BrightnessSubFilter(60))
filter.addSubFilter(VignetteSubFilter(context, 200))
filter.addSubFilter(ToneCurveSubFilter(null, null, greenKnots, null))
return filter
}

fun getOldManFilter(context: Context): Filter {
val filter = Filter("OldMan")
filter.addSubFilter(BrightnessSubFilter(30))
filter.addSubFilter(SaturationSubFilter(0.8f))
filter.addSubFilter(ContrastSubFilter(1.3f))
filter.addSubFilter(VignetteSubFilter(context, 100))
filter.addSubFilter(ColorOverlaySubFilter(100, .2f, .2f, .1f))
return filter
}

fun getClarendon(context: Context): Filter {
val redKnots = arrayOfNulls<Point>(4)
redKnots[0] = Point(0f, 0f)
redKnots[1] = Point(56f, 68f)
redKnots[2] = Point(196f, 206f)
redKnots[3] = Point(255f, 255f)


val greenKnots = arrayOfNulls<Point>(4)
greenKnots[0] = Point(0f, 0f)
greenKnots[1] = Point(46f, 77f)
greenKnots[2] = Point(160f, 200f)
greenKnots[3] = Point(255f, 255f)


val blueKnots = arrayOfNulls<Point>(4)
blueKnots[0] = Point(0f, 0f)
blueKnots[1] = Point(33f, 86f)
blueKnots[2] = Point(126f, 220f)
blueKnots[3] = Point(255f, 255f)

val filter = Filter("Clarendon")
filter.addSubFilter(ContrastSubFilter(1.5f))
filter.addSubFilter(BrightnessSubFilter(-10))
filter.addSubFilter(ToneCurveSubFilter(null, redKnots, greenKnots, blueKnots))
return filter
}
}
5 changes: 5 additions & 0 deletions app/src/main/kotlin/com/zomato/photofilters/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# AndroidPhotoFilters compatibility shim

This package keeps the small AndroidPhotoFilters API surface used by Gallery while avoiding the upstream prebuilt native library.

The filter API and preset definitions are based on `naveensingh/AndroidPhotoFilters` / `com.github.naveensingh:androidphotofilters` (Apache-2.0). The pixel operations formerly delegated to `libNativeImageProcessor.so` are implemented in Kotlin so the APK no longer ships the 4 KB-aligned native library that fails 16 KB page-size compatibility checks.
Loading
Loading