diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
index 8cc5d7c..bc843c1 100644
--- a/.idea/deploymentTargetSelector.xml
+++ b/.idea/deploymentTargetSelector.xml
@@ -27,6 +27,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 88d99e0..b665d5f 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -20,6 +20,7 @@
+
diff --git a/modules/media3-motion-ext/build.gradle b/modules/media3-motion-ext/build.gradle
new file mode 100644
index 0000000..b6a39fb
--- /dev/null
+++ b/modules/media3-motion-ext/build.gradle
@@ -0,0 +1,53 @@
+plugins {
+ alias(libs.plugins.android.library)
+ id 'maven-publish'
+}
+
+android {
+ namespace 'com.tejpratapsingh.motionlib.media3'
+ compileSdk 36
+
+ defaultConfig {
+ minSdk 28
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles "consumer-rules.pro"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
+ }
+ publishing {
+ singleVariant("release")
+ }
+}
+
+afterEvaluate {
+ publishing {
+ publications {
+ release(MavenPublication) {
+ from components.release
+ }
+ }
+ }
+}
+
+dependencies {
+ implementation project(path: ':modules:core')
+ implementation project(path: ':modules:motionlib')
+
+ implementation libs.androidx.media3.common
+ implementation libs.androidx.media3.exoplayer
+ // Assuming we add media3-effect to libs.versions.toml if missing,
+ // but based on research it should be available or we can use the same version as common
+ implementation "androidx.media3:media3-effect:${libs.versions.media3Version.get()}"
+
+ testImplementation libs.junit
+}
diff --git a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/Media3Utils.kt b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/Media3Utils.kt
new file mode 100644
index 0000000..4857fa0
--- /dev/null
+++ b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/Media3Utils.kt
@@ -0,0 +1,64 @@
+package com.tejpratapsingh.motionlib.media3
+
+import android.graphics.ColorMatrix
+import androidx.annotation.OptIn
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.effect.RgbMatrix
+
+@OptIn(UnstableApi::class)
+object Media3Utils {
+ /**
+ * Converts a Media3 4x4 RGB matrix to an Android 5x4 ColorMatrix.
+ * Media3 matrix is assumed to be a 16-float array representing a 4x4 matrix.
+ * Android ColorMatrix is a 20-float array (4 rows, 5 columns).
+ */
+ fun toColorMatrix(media3Matrix: FloatArray): ColorMatrix {
+ if (media3Matrix.size != 16) {
+ return ColorMatrix()
+ }
+
+ // Media3 matrix (column-major standard for GL):
+ // [ m0 m4 m8 m12 ]
+ // [ m1 m5 m9 m13 ]
+ // [ m2 m6 m10 m14 ]
+ // [ m3 m7 m11 m15 ]
+
+ // Android ColorMatrix (row-major):
+ // [ a b c d e ] -> R' = aR + bG + cB + dA + e
+ // [ f g h i j ] -> G' = fR + gG + hB + iA + j
+ // [ k l m n o ] -> B' = kR + lG + mB + nA + o
+ // [ p q r s t ] -> A' = pR + qG + rB + sA + t
+
+ val colorMatrixArray = FloatArray(20)
+
+ // Row 0 (Red)
+ colorMatrixArray[0] = media3Matrix[0] // m0
+ colorMatrixArray[1] = media3Matrix[4] // m4
+ colorMatrixArray[2] = media3Matrix[8] // m8
+ colorMatrixArray[3] = 0f // d (Alpha contribution to Red)
+ colorMatrixArray[4] = media3Matrix[12] * 255f // e (Offset, normalized to 0-255)
+
+ // Row 1 (Green)
+ colorMatrixArray[5] = media3Matrix[1] // m1
+ colorMatrixArray[6] = media3Matrix[5] // m5
+ colorMatrixArray[7] = media3Matrix[9] // m9
+ colorMatrixArray[8] = 0f // i
+ colorMatrixArray[9] = media3Matrix[13] * 255f // j
+
+ // Row 2 (Blue)
+ colorMatrixArray[10] = media3Matrix[2] // m2
+ colorMatrixArray[11] = media3Matrix[6] // m6
+ colorMatrixArray[12] = media3Matrix[10]// m10
+ colorMatrixArray[13] = 0f // n
+ colorMatrixArray[14] = media3Matrix[14] * 255f // o
+
+ // Row 3 (Alpha) - Identity
+ colorMatrixArray[15] = 0f
+ colorMatrixArray[16] = 0f
+ colorMatrixArray[17] = 0f
+ colorMatrixArray[18] = 1f
+ colorMatrixArray[19] = 0f
+
+ return ColorMatrix(colorMatrixArray)
+ }
+}
diff --git a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/BrightnessEffect.kt b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/BrightnessEffect.kt
new file mode 100644
index 0000000..c9e0f0a
--- /dev/null
+++ b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/BrightnessEffect.kt
@@ -0,0 +1,60 @@
+package com.tejpratapsingh.motionlib.media3.effects
+
+import android.graphics.ColorMatrixColorFilter
+import android.graphics.RenderEffect
+import android.os.Build
+import android.view.View
+import androidx.annotation.OptIn
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.effect.Brightness
+import com.tejpratapsingh.motionlib.core.MotionEffect
+import com.tejpratapsingh.motionlib.core.MotionView
+import com.tejpratapsingh.motionlib.core.animation.Easings
+import com.tejpratapsingh.motionlib.core.animation.Interpolators
+import com.tejpratapsingh.motionlib.core.animation.MotionInterpolator
+import com.tejpratapsingh.motionlib.media3.Media3Utils
+
+/**
+ * A [MotionEffect] that adjusts brightness using [androidx.media3.effect.Brightness].
+ * Animates brightness from [fromBrightness] to [toBrightness].
+ * Brightness ranges from -1 (black) to 1 (white). 0 is no change.
+ */
+@OptIn(UnstableApi::class)
+class BrightnessEffect(
+ override val startFrame: Int,
+ override val endFrame: Int,
+ val fromBrightness: Float = 0.0f,
+ val toBrightness: Float = 1.0f,
+) : MotionEffect {
+ override lateinit var motionView: MotionView
+
+ override fun forFrame(frame: Int): MotionView {
+ if (motionView !is View) return motionView
+ val view = motionView as View
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return motionView
+
+ if (frame !in startFrame..endFrame) {
+ if (frame > endFrame) {
+ view.setRenderEffect(null)
+ }
+ return motionView
+ }
+
+ val brightnessValue = MotionInterpolator.interpolateForRange(
+ interpolator = Interpolators(Easings.LINEAR),
+ currentFrame = frame,
+ frameRange = Pair(startFrame, endFrame),
+ valueRange = Pair(fromBrightness, toBrightness),
+ )
+
+ val brightnessEffect = Brightness(brightnessValue)
+ val matrix = brightnessEffect.getMatrix(0L, false)
+ val colorMatrix = Media3Utils.toColorMatrix(matrix)
+
+ val colorFilter = ColorMatrixColorFilter(colorMatrix)
+ view.setRenderEffect(RenderEffect.createColorFilterEffect(colorFilter))
+
+ return motionView
+ }
+}
diff --git a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/ContrastEffect.kt b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/ContrastEffect.kt
new file mode 100644
index 0000000..c8be0b2
--- /dev/null
+++ b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/ContrastEffect.kt
@@ -0,0 +1,60 @@
+package com.tejpratapsingh.motionlib.media3.effects
+
+import android.graphics.ColorMatrixColorFilter
+import android.graphics.RenderEffect
+import android.os.Build
+import android.view.View
+import androidx.annotation.OptIn
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.effect.Contrast
+import com.tejpratapsingh.motionlib.core.MotionEffect
+import com.tejpratapsingh.motionlib.core.MotionView
+import com.tejpratapsingh.motionlib.core.animation.Easings
+import com.tejpratapsingh.motionlib.core.animation.Interpolators
+import com.tejpratapsingh.motionlib.core.animation.MotionInterpolator
+import com.tejpratapsingh.motionlib.media3.Media3Utils
+
+/**
+ * A [MotionEffect] that adjusts contrast using [androidx.media3.effect.Contrast].
+ * Animates contrast from [fromContrast] to [toContrast].
+ * Contrast 1.0 is no change. 0.0 is uniform gray. > 1.0 increases contrast.
+ */
+@OptIn(UnstableApi::class)
+class ContrastEffect(
+ override val startFrame: Int,
+ override val endFrame: Int,
+ val fromContrast: Float = 1.0f,
+ val toContrast: Float = 2.0f,
+) : MotionEffect {
+ override lateinit var motionView: MotionView
+
+ override fun forFrame(frame: Int): MotionView {
+ if (motionView !is View) return motionView
+ val view = motionView as View
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return motionView
+
+ if (frame !in startFrame..endFrame) {
+ if (frame > endFrame) {
+ view.setRenderEffect(null)
+ }
+ return motionView
+ }
+
+ val contrastValue = MotionInterpolator.interpolateForRange(
+ interpolator = Interpolators(Easings.LINEAR),
+ currentFrame = frame,
+ frameRange = Pair(startFrame, endFrame),
+ valueRange = Pair(fromContrast, toContrast),
+ )
+
+ val contrastEffect = Contrast(contrastValue)
+ val matrix = contrastEffect.getMatrix(0L, false)
+ val colorMatrix = Media3Utils.toColorMatrix(matrix)
+
+ val colorFilter = ColorMatrixColorFilter(colorMatrix)
+ view.setRenderEffect(RenderEffect.createColorFilterEffect(colorFilter))
+
+ return motionView
+ }
+}
diff --git a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/GrayscaleEffect.kt b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/GrayscaleEffect.kt
new file mode 100644
index 0000000..bad64c2
--- /dev/null
+++ b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/GrayscaleEffect.kt
@@ -0,0 +1,46 @@
+package com.tejpratapsingh.motionlib.media3.effects
+
+import android.graphics.ColorMatrixColorFilter
+import android.graphics.RenderEffect
+import android.os.Build
+import android.view.View
+import androidx.annotation.OptIn
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.effect.RgbFilter
+import com.tejpratapsingh.motionlib.core.MotionEffect
+import com.tejpratapsingh.motionlib.core.MotionView
+import com.tejpratapsingh.motionlib.media3.Media3Utils
+
+/**
+ * A [MotionEffect] that applies a grayscale filter using [androidx.media3.effect.RgbFilter].
+ */
+@OptIn(UnstableApi::class)
+class GrayscaleEffect(
+ override val startFrame: Int,
+ override val endFrame: Int,
+) : MotionEffect {
+ override lateinit var motionView: MotionView
+
+ override fun forFrame(frame: Int): MotionView {
+ if (motionView !is View) return motionView
+ val view = motionView as View
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return motionView
+
+ if (frame !in startFrame..endFrame) {
+ if (frame > endFrame) {
+ view.setRenderEffect(null)
+ }
+ return motionView
+ }
+
+ val grayscaleFilter = RgbFilter.createGrayscaleFilter()
+ val matrix = grayscaleFilter.getMatrix(0L, false)
+ val colorMatrix = Media3Utils.toColorMatrix(matrix)
+
+ val colorFilter = ColorMatrixColorFilter(colorMatrix)
+ view.setRenderEffect(RenderEffect.createColorFilterEffect(colorFilter))
+
+ return motionView
+ }
+}
diff --git a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/InvertEffect.kt b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/InvertEffect.kt
new file mode 100644
index 0000000..19a6580
--- /dev/null
+++ b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/InvertEffect.kt
@@ -0,0 +1,46 @@
+package com.tejpratapsingh.motionlib.media3.effects
+
+import android.graphics.ColorMatrixColorFilter
+import android.graphics.RenderEffect
+import android.os.Build
+import android.view.View
+import androidx.annotation.OptIn
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.effect.RgbFilter
+import com.tejpratapsingh.motionlib.core.MotionEffect
+import com.tejpratapsingh.motionlib.core.MotionView
+import com.tejpratapsingh.motionlib.media3.Media3Utils
+
+/**
+ * A [MotionEffect] that applies an inverted color filter using [androidx.media3.effect.RgbFilter].
+ */
+@OptIn(UnstableApi::class)
+class InvertEffect(
+ override val startFrame: Int,
+ override val endFrame: Int,
+) : MotionEffect {
+ override lateinit var motionView: MotionView
+
+ override fun forFrame(frame: Int): MotionView {
+ if (motionView !is View) return motionView
+ val view = motionView as View
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return motionView
+
+ if (frame !in startFrame..endFrame) {
+ if (frame > endFrame) {
+ view.setRenderEffect(null)
+ }
+ return motionView
+ }
+
+ val invertedFilter = RgbFilter.createInvertedFilter()
+ val matrix = invertedFilter.getMatrix(0L, false)
+ val colorMatrix = Media3Utils.toColorMatrix(matrix)
+
+ val colorFilter = ColorMatrixColorFilter(colorMatrix)
+ view.setRenderEffect(RenderEffect.createColorFilterEffect(colorFilter))
+
+ return motionView
+ }
+}
diff --git a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/RgbEffect.kt b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/RgbEffect.kt
new file mode 100644
index 0000000..af8c3e4
--- /dev/null
+++ b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/RgbEffect.kt
@@ -0,0 +1,81 @@
+package com.tejpratapsingh.motionlib.media3.effects
+
+import android.graphics.ColorMatrixColorFilter
+import android.graphics.RenderEffect
+import android.os.Build
+import android.view.View
+import androidx.annotation.OptIn
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.effect.RgbAdjustment
+import com.tejpratapsingh.motionlib.core.MotionEffect
+import com.tejpratapsingh.motionlib.core.MotionView
+import com.tejpratapsingh.motionlib.core.animation.Easings
+import com.tejpratapsingh.motionlib.core.animation.Interpolators
+import com.tejpratapsingh.motionlib.core.animation.MotionInterpolator
+import com.tejpratapsingh.motionlib.media3.Media3Utils
+
+/**
+ * A [MotionEffect] that adjusts RGB scaling using [androidx.media3.effect.RgbAdjustment].
+ */
+@OptIn(UnstableApi::class)
+class RgbEffect(
+ override val startFrame: Int,
+ override val endFrame: Int,
+ val fromRed: Float = 1f,
+ val toRed: Float = 1f,
+ val fromGreen: Float = 1f,
+ val toGreen: Float = 1f,
+ val fromBlue: Float = 1f,
+ val toBlue: Float = 1f,
+) : MotionEffect {
+ override lateinit var motionView: MotionView
+
+ override fun forFrame(frame: Int): MotionView {
+ if (motionView !is View) return motionView
+ val view = motionView as View
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return motionView
+
+ if (frame !in startFrame..endFrame) {
+ if (frame > endFrame) {
+ view.setRenderEffect(null)
+ }
+ return motionView
+ }
+
+ val r = MotionInterpolator.interpolateForRange(
+ interpolator = Interpolators(Easings.LINEAR),
+ currentFrame = frame,
+ frameRange = Pair(startFrame, endFrame),
+ valueRange = Pair(fromRed, toRed),
+ )
+
+ val g = MotionInterpolator.interpolateForRange(
+ interpolator = Interpolators(Easings.LINEAR),
+ currentFrame = frame,
+ frameRange = Pair(startFrame, endFrame),
+ valueRange = Pair(fromGreen, toGreen),
+ )
+
+ val b = MotionInterpolator.interpolateForRange(
+ interpolator = Interpolators(Easings.LINEAR),
+ currentFrame = frame,
+ frameRange = Pair(startFrame, endFrame),
+ valueRange = Pair(fromBlue, toBlue),
+ )
+
+ val rgbAdjustment = RgbAdjustment.Builder()
+ .setRedScale(r)
+ .setGreenScale(g)
+ .setBlueScale(b)
+ .build()
+
+ val matrix = rgbAdjustment.getMatrix(0L, false)
+ val colorMatrix = Media3Utils.toColorMatrix(matrix)
+
+ val colorFilter = ColorMatrixColorFilter(colorMatrix)
+ view.setRenderEffect(RenderEffect.createColorFilterEffect(colorFilter))
+
+ return motionView
+ }
+}
diff --git a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/BrightnessPlugin.kt b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/BrightnessPlugin.kt
new file mode 100644
index 0000000..dd0646d
--- /dev/null
+++ b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/BrightnessPlugin.kt
@@ -0,0 +1,33 @@
+package com.tejpratapsingh.motionlib.media3.plugins
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.ColorMatrixColorFilter
+import android.graphics.Paint
+import androidx.annotation.OptIn
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.effect.Brightness
+import com.tejpratapsingh.motionlib.core.MotionPlugin
+import com.tejpratapsingh.motionlib.media3.Media3Utils
+
+/**
+ * A [MotionPlugin] that adjusts brightness using [androidx.media3.effect.Brightness].
+ * Brightness ranges from -1 (black) to 1 (white). 0 is no change.
+ */
+@OptIn(UnstableApi::class)
+class BrightnessPlugin(val brightness: Float) : MotionPlugin {
+ override fun apply(input: Bitmap): Bitmap {
+ val output = Bitmap.createBitmap(input.width, input.height, input.config ?: Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(output)
+ val paint = Paint()
+
+ val brightnessEffect = Brightness(brightness)
+ val matrix = brightnessEffect.getMatrix(0L, false)
+ val colorMatrix = Media3Utils.toColorMatrix(matrix)
+
+ paint.colorFilter = ColorMatrixColorFilter(colorMatrix)
+ canvas.drawBitmap(input, 0f, 0f, paint)
+
+ return output
+ }
+}
diff --git a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/ContrastPlugin.kt b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/ContrastPlugin.kt
new file mode 100644
index 0000000..7b809a1
--- /dev/null
+++ b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/ContrastPlugin.kt
@@ -0,0 +1,33 @@
+package com.tejpratapsingh.motionlib.media3.plugins
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.ColorMatrixColorFilter
+import android.graphics.Paint
+import androidx.annotation.OptIn
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.effect.Contrast
+import com.tejpratapsingh.motionlib.core.MotionPlugin
+import com.tejpratapsingh.motionlib.media3.Media3Utils
+
+/**
+ * A [MotionPlugin] that adjusts contrast using [androidx.media3.effect.Contrast].
+ * Contrast 1.0 is no change. 0.0 is uniform gray. > 1.0 increases contrast.
+ */
+@OptIn(UnstableApi::class)
+class ContrastPlugin(val contrast: Float) : MotionPlugin {
+ override fun apply(input: Bitmap): Bitmap {
+ val output = Bitmap.createBitmap(input.width, input.height, input.config ?: Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(output)
+ val paint = Paint()
+
+ val contrastEffect = Contrast(contrast)
+ val matrix = contrastEffect.getMatrix(0L, false)
+ val colorMatrix = Media3Utils.toColorMatrix(matrix)
+
+ paint.colorFilter = ColorMatrixColorFilter(colorMatrix)
+ canvas.drawBitmap(input, 0f, 0f, paint)
+
+ return output
+ }
+}
diff --git a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/GrayscalePlugin.kt b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/GrayscalePlugin.kt
new file mode 100644
index 0000000..c389898
--- /dev/null
+++ b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/GrayscalePlugin.kt
@@ -0,0 +1,32 @@
+package com.tejpratapsingh.motionlib.media3.plugins
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.ColorMatrixColorFilter
+import android.graphics.Paint
+import androidx.annotation.OptIn
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.effect.RgbFilter
+import com.tejpratapsingh.motionlib.core.MotionPlugin
+import com.tejpratapsingh.motionlib.media3.Media3Utils
+
+/**
+ * A [MotionPlugin] that applies a grayscale filter using [androidx.media3.effect.RgbFilter].
+ */
+@OptIn(UnstableApi::class)
+class GrayscalePlugin : MotionPlugin {
+ override fun apply(input: Bitmap): Bitmap {
+ val output = Bitmap.createBitmap(input.width, input.height, input.config ?: Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(output)
+ val paint = Paint()
+
+ val grayscaleFilter = RgbFilter.createGrayscaleFilter()
+ val matrix = grayscaleFilter.getMatrix(0L, false)
+ val colorMatrix = Media3Utils.toColorMatrix(matrix)
+
+ paint.colorFilter = ColorMatrixColorFilter(colorMatrix)
+ canvas.drawBitmap(input, 0f, 0f, paint)
+
+ return output
+ }
+}
diff --git a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/InvertPlugin.kt b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/InvertPlugin.kt
new file mode 100644
index 0000000..574ac3a
--- /dev/null
+++ b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/InvertPlugin.kt
@@ -0,0 +1,32 @@
+package com.tejpratapsingh.motionlib.media3.plugins
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.ColorMatrixColorFilter
+import android.graphics.Paint
+import androidx.annotation.OptIn
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.effect.RgbFilter
+import com.tejpratapsingh.motionlib.core.MotionPlugin
+import com.tejpratapsingh.motionlib.media3.Media3Utils
+
+/**
+ * A [MotionPlugin] that applies an inverted color filter using [androidx.media3.effect.RgbFilter].
+ */
+@OptIn(UnstableApi::class)
+class InvertPlugin : MotionPlugin {
+ override fun apply(input: Bitmap): Bitmap {
+ val output = Bitmap.createBitmap(input.width, input.height, input.config ?: Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(output)
+ val paint = Paint()
+
+ val invertedFilter = RgbFilter.createInvertedFilter()
+ val matrix = invertedFilter.getMatrix(0L, false)
+ val colorMatrix = Media3Utils.toColorMatrix(matrix)
+
+ paint.colorFilter = ColorMatrixColorFilter(colorMatrix)
+ canvas.drawBitmap(input, 0f, 0f, paint)
+
+ return output
+ }
+}
diff --git a/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/RgbPlugin.kt b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/RgbPlugin.kt
new file mode 100644
index 0000000..dbfc783
--- /dev/null
+++ b/modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/RgbPlugin.kt
@@ -0,0 +1,41 @@
+package com.tejpratapsingh.motionlib.media3.plugins
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.ColorMatrixColorFilter
+import android.graphics.Paint
+import androidx.annotation.OptIn
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.effect.RgbAdjustment
+import com.tejpratapsingh.motionlib.core.MotionPlugin
+import com.tejpratapsingh.motionlib.media3.Media3Utils
+
+/**
+ * A [MotionPlugin] that adjusts RGB scaling using [androidx.media3.effect.RgbAdjustment].
+ */
+@OptIn(UnstableApi::class)
+class RgbPlugin(
+ val redScale: Float = 1f,
+ val greenScale: Float = 1f,
+ val blueScale: Float = 1f,
+) : MotionPlugin {
+ override fun apply(input: Bitmap): Bitmap {
+ val output = Bitmap.createBitmap(input.width, input.height, input.config ?: Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(output)
+ val paint = Paint()
+
+ val rgbAdjustment = RgbAdjustment.Builder()
+ .setRedScale(redScale)
+ .setGreenScale(greenScale)
+ .setBlueScale(blueScale)
+ .build()
+
+ val matrix = rgbAdjustment.getMatrix(0L, false)
+ val colorMatrix = Media3Utils.toColorMatrix(matrix)
+
+ paint.colorFilter = ColorMatrixColorFilter(colorMatrix)
+ canvas.drawBitmap(input, 0f, 0f, paint)
+
+ return output
+ }
+}
diff --git a/modules/media3-motion-ext/src/test/java/com/tejpratapsingh/motionlib/media3/Media3ExtensionTest.kt b/modules/media3-motion-ext/src/test/java/com/tejpratapsingh/motionlib/media3/Media3ExtensionTest.kt
new file mode 100644
index 0000000..89d4576
--- /dev/null
+++ b/modules/media3-motion-ext/src/test/java/com/tejpratapsingh/motionlib/media3/Media3ExtensionTest.kt
@@ -0,0 +1,55 @@
+package com.tejpratapsingh.motionlib.media3
+
+import com.tejpratapsingh.motionlib.core.MotionEffect
+import com.tejpratapsingh.motionlib.core.MotionPlugin
+import com.tejpratapsingh.motionlib.media3.effects.BrightnessEffect
+import com.tejpratapsingh.motionlib.media3.effects.ContrastEffect
+import com.tejpratapsingh.motionlib.media3.effects.GrayscaleEffect
+import com.tejpratapsingh.motionlib.media3.effects.InvertEffect
+import com.tejpratapsingh.motionlib.media3.effects.RgbEffect
+import com.tejpratapsingh.motionlib.media3.plugins.BrightnessPlugin
+import com.tejpratapsingh.motionlib.media3.plugins.ContrastPlugin
+import com.tejpratapsingh.motionlib.media3.plugins.GrayscalePlugin
+import com.tejpratapsingh.motionlib.media3.plugins.InvertPlugin
+import com.tejpratapsingh.motionlib.media3.plugins.RgbPlugin
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+class Media3ExtensionTest {
+
+ @Test
+ fun testEffectsImplementation() {
+ val brightnessEffect = BrightnessEffect(0, 10)
+ assertTrue(brightnessEffect is MotionEffect)
+
+ val contrastEffect = ContrastEffect(0, 10)
+ assertTrue(contrastEffect is MotionEffect)
+
+ val grayscaleEffect = GrayscaleEffect(0, 10)
+ assertTrue(grayscaleEffect is MotionEffect)
+
+ val invertEffect = InvertEffect(0, 10)
+ assertTrue(invertEffect is MotionEffect)
+
+ val rgbEffect = RgbEffect(0, 10)
+ assertTrue(rgbEffect is MotionEffect)
+ }
+
+ @Test
+ fun testPluginsImplementation() {
+ val brightnessPlugin = BrightnessPlugin(0.5f)
+ assertTrue(brightnessPlugin is MotionPlugin)
+
+ val contrastPlugin = ContrastPlugin(1.5f)
+ assertTrue(contrastPlugin is MotionPlugin)
+
+ val grayscalePlugin = GrayscalePlugin()
+ assertTrue(grayscalePlugin is MotionPlugin)
+
+ val invertPlugin = InvertPlugin()
+ assertTrue(invertPlugin is MotionPlugin)
+
+ val rgbPlugin = RgbPlugin(redScale = 1.2f)
+ assertTrue(rgbPlugin is MotionPlugin)
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index de2c4fe..6b92449 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -32,6 +32,7 @@ include ':modules:motion-store'
include ':modules:motion-video-player'
include ':modules:motion-video-editor'
include ':modules:ml-kit-ext'
+include ':modules:media3-motion-ext'
if (!System.env.JITPACK) {
include ':modules:app'