Skip to content

fix: create plugins using media3 lib#73

Open
tejpratap46 wants to merge 1 commit into
mainfrom
feat/media3-ext
Open

fix: create plugins using media3 lib#73
tejpratap46 wants to merge 1 commit into
mainfrom
feat/media3-ext

Conversation

@tejpratap46

@tejpratap46 tejpratap46 commented Jun 14, 2026

Copy link
Copy Markdown
Owner

Closes #72

Summary by CodeRabbit

  • New Features
    • Added Media3 motion extension module with brightness, contrast, grayscale, invert, and RGB adjustment effects for real-time video and motion processing.
    • Added corresponding bitmap processing plugins for offline color filter application to images.
    • Supports frame-based animation with parameter interpolation across configurable frame ranges.

@cloudflare-workers-and-pages

Copy link
Copy Markdown
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
motion-lib 6fbcddd Commit Preview URL

Branch Preview URL
Jun 14 2026, 06:41 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
sdui 6fbcddd Commit Preview URL

Branch Preview URL
Jun 14 2026, 06:41 PM

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

A new media3-motion-ext Android library module is introduced, registered in settings.gradle and .idea project files, with a build.gradle configuring Maven publishing and Media3 dependencies. The module provides Media3Utils.toColorMatrix for converting Media3 4×4 matrices to Android ColorMatrix, five MotionEffect classes (Brightness, Contrast, Grayscale, Invert, Rgb) that apply per-frame animated RenderEffect filters, five MotionPlugin classes that apply the same filters to bitmaps, and JUnit interface conformance tests.

Changes

media3-motion-ext module

Layer / File(s) Summary
Module setup and Gradle wiring
settings.gradle, modules/media3-motion-ext/build.gradle, .idea/gradle.xml, .idea/deploymentTargetSelector.xml
Registers :modules:media3-motion-ext as a Gradle subproject, configures the Android library namespace, SDK levels, Java 11, Maven publishing, and Media3/internal project dependencies; updates IDE module and deployment target lists.
Media3Utils matrix conversion utility
modules/media3-motion-ext/src/main/java/.../Media3Utils.kt
Adds Media3Utils.toColorMatrix which validates a 16-float Media3 column-major 4×4 matrix, remaps it to a 20-float Android 4×5 row-major ColorMatrix, scales translation offsets by 255, and fills an identity alpha row.
MotionEffect implementations
modules/media3-motion-ext/src/main/java/.../effects/BrightnessEffect.kt, ContrastEffect.kt, GrayscaleEffect.kt, InvertEffect.kt, RgbEffect.kt
Adds five MotionEffect classes that per-frame linearly interpolate their respective Media3 effect parameters, convert the result via Media3Utils, and apply or clear a RenderEffect ColorMatrixColorFilter on the backing View, guarding on SDK level (≥ S) and MotionView type.
MotionPlugin implementations
modules/media3-motion-ext/src/main/java/.../plugins/BrightnessPlugin.kt, ContrastPlugin.kt, GrayscalePlugin.kt, InvertPlugin.kt, RgbPlugin.kt
Adds five MotionPlugin classes each implementing apply(Bitmap): Bitmap by building a Media3 effect, converting its matrix via Media3Utils into a ColorMatrixColorFilter, and drawing the filtered input onto a new output Bitmap via Canvas.
Interface conformance tests
modules/media3-motion-ext/src/test/java/.../Media3ExtensionTest.kt
Adds Media3ExtensionTest with two JUnit methods that instantiate all five effects and all five plugins and assert each is an instance of MotionEffect or MotionPlugin respectively.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 Hoppity-hop, a new module arrives,
With brightness and contrast that colorfully thrives!
Grayscale and invert, inverted with care,
RGB channels float through the pixel-filled air.
Media3 matrices, mapped row by row—
The rabbit commits, and now colors shall glow! 🎨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: create plugins using media3 lib' directly describes the main changes—adding new Media3-based plugins and effects throughout the new media3-motion-ext module.
Linked Issues check ✅ Passed The PR successfully implements all coding requirements from issue #72: it creates multiple Media3-based effects (BrightnessEffect, ContrastEffect, GrayscaleEffect, InvertEffect, RgbEffect), plugins (BrightnessPlugin, ContrastPlugin, GrayscalePlugin, InvertPlugin, RgbPlugin), and utility code (Media3Utils) using androidx.media3 APIs.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the Media3 migration objective: the new media3-motion-ext module, IDE configuration updates, and build configuration changes are all necessary supporting infrastructure for the new Media3 effects and plugins.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/media3-ext

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
lyrics 6fbcddd Commit Preview URL

Branch Preview URL
Jun 14 2026, 06:42 PM

@amazon-q-developer amazon-q-developer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

This PR adds media3 library integration for motion effects. Critical issues identified that must be fixed before merge:

Critical Fixes Required:

  1. ArrayIndexOutOfBoundsException risk in Media3Utils.toColorMatrix - missing bounds checks before accessing array indices 12-14
  2. UninitializedPropertyAccessException risk in all effect classes - missing initialization checks for lateinit motionView property
  3. Performance regressions in plugin classes - repeated object allocations on every frame/apply call

Verification Needed:
The matrix conversion logic in Media3Utils needs validation to ensure the column-major to row-major transformation is mathematically correct, especially the offset scaling by 255f.

Please address the critical crash risks before merging.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

Comment on lines +39 to +53
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 Logic Error: Missing bounds check before array access will cause ArrayIndexOutOfBoundsException. Add validation to prevent crashes when accessing media3Matrix indices 12, 13, and 14.

Suggested change
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 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] = if (media3Matrix.size > 12) media3Matrix[12] * 255f else 0f // 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] = if (media3Matrix.size > 13) media3Matrix[13] * 255f else 0f // 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] = if (media3Matrix.size > 14) media3Matrix[14] * 255f else 0f // o

Comment on lines +31 to +33
override fun forFrame(frame: Int): MotionView {
if (motionView !is View) return motionView
val view = motionView as View

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 Crash Risk: Missing lateinit initialization check causes UninitializedPropertyAccessException. Add check before accessing motionView property to prevent runtime crashes.

Suggested change
override fun forFrame(frame: Int): MotionView {
if (motionView !is View) return motionView
val view = motionView as View
override fun forFrame(frame: Int): MotionView {
if (!::motionView.isInitialized || motionView !is View) return motionView
val view = motionView as View

Comment on lines +33 to +35
override fun forFrame(frame: Int): MotionView {
if (motionView !is View) return motionView
val view = motionView as View

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 Crash Risk: Missing lateinit initialization check causes UninitializedPropertyAccessException. Add check before accessing motionView property to prevent runtime crashes.

Suggested change
override fun forFrame(frame: Int): MotionView {
if (motionView !is View) return motionView
val view = motionView as View
override fun forFrame(frame: Int): MotionView {
if (!::motionView.isInitialized || motionView !is View) return motionView
val view = motionView as View

Comment on lines +24 to +26
val brightnessEffect = Brightness(brightness)
val matrix = brightnessEffect.getMatrix(0L, false)
val colorMatrix = Media3Utils.toColorMatrix(matrix)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 Performance Regression: Creating new Brightness instance and matrix conversion on every frame causes unnecessary allocations. Cache the colorMatrix instance if brightness doesn't change.

Comment on lines +27 to +34
val rgbAdjustment = RgbAdjustment.Builder()
.setRedScale(redScale)
.setGreenScale(greenScale)
.setBlueScale(blueScale)
.build()

val matrix = rgbAdjustment.getMatrix(0L, false)
val colorMatrix = Media3Utils.toColorMatrix(matrix)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 Performance Regression: Creating new RgbAdjustment and matrix conversion on every apply call causes unnecessary allocations. Initialize the colorMatrix once in the constructor to avoid repeated overhead.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new module, media3-motion-ext, which integrates Media3-based effects and plugins (such as brightness, contrast, grayscale, invert, and RGB adjustments) into the motion library. The review feedback highlights several critical issues and optimization opportunities: first, RenderEffect should be cleared whenever a frame is outside the active range to support bidirectional scrubbing correctly; second, constant effects and plugins should cache their filters or effects to avoid high allocation overhead; third, a potential crash when processing HARDWARE bitmaps in plugins must be resolved by falling back to ARGB_8888; and finally, the media3-effect dependency should be declared in the version catalog rather than using string interpolation.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +37 to +42
if (frame !in startFrame..endFrame) {
if (frame > endFrame) {
view.setRenderEffect(null)
}
return motionView
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

When scrubbing backwards or jumping to a frame before startFrame, the RenderEffect will persist on the view because it is only cleared when frame > endFrame. To support bidirectional playback and scrubbing correctly, clear the RenderEffect whenever the frame is outside the active range. Additionally, checking if view.renderEffect is not null before clearing avoids unnecessary invalidations.

Suggested change
if (frame !in startFrame..endFrame) {
if (frame > endFrame) {
view.setRenderEffect(null)
}
return motionView
}
if (frame !in startFrame..endFrame) {
if (view.renderEffect != null) {
view.setRenderEffect(null)
}
return motionView
}

Comment on lines +36 to +41

if (frame !in startFrame..endFrame) {
if (frame > endFrame) {
view.setRenderEffect(null)
}
return motionView

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

When scrubbing backwards or jumping to a frame before startFrame, the RenderEffect will persist on the view because it is only cleared when frame > endFrame. To support bidirectional playback and scrubbing correctly, clear the RenderEffect whenever the frame is outside the active range. Additionally, checking if view.renderEffect is not null before clearing avoids unnecessary invalidations.

        if (frame !in startFrame..endFrame) {
            if (view.renderEffect != null) {
                view.setRenderEffect(null)
            }
            return motionView
        }

Comment on lines +39 to +44
if (frame !in startFrame..endFrame) {
if (frame > endFrame) {
view.setRenderEffect(null)
}
return motionView
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

When scrubbing backwards or jumping to a frame before startFrame, the RenderEffect will persist on the view because it is only cleared when frame > endFrame. To support bidirectional playback and scrubbing correctly, clear the RenderEffect whenever the frame is outside the active range. Additionally, checking if view.renderEffect is not null before clearing avoids unnecessary invalidations.

Suggested change
if (frame !in startFrame..endFrame) {
if (frame > endFrame) {
view.setRenderEffect(null)
}
return motionView
}
if (frame !in startFrame..endFrame) {
if (view.renderEffect != null) {
view.setRenderEffect(null)
}
return motionView
}

Comment on lines +18 to +46
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
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Since GrayscaleEffect is a constant effect (its matrix does not change per frame), we can cache the RenderEffect instance to avoid allocating multiple objects (such as GrayscaleFilter, FloatArray, ColorMatrix, ColorMatrixColorFilter, and RenderEffect) on every single frame. This significantly improves performance and prevents GC overhead during animations. Additionally, clearing the RenderEffect when the frame is outside the active range ensures correct behavior during bidirectional scrubbing.

class GrayscaleEffect(
    override val startFrame: Int,
    override val endFrame: Int,
) : MotionEffect {
    override lateinit var motionView: MotionView

    private var cachedRenderEffect: RenderEffect? = null

    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 (view.renderEffect != null) {
                view.setRenderEffect(null)
            }
            return motionView
        }

        if (cachedRenderEffect == null) {
            val grayscaleFilter = RgbFilter.createGrayscaleFilter()
            val matrix = grayscaleFilter.getMatrix(0L, false)
            val colorMatrix = Media3Utils.toColorMatrix(matrix)
            val colorFilter = ColorMatrixColorFilter(colorMatrix)
            cachedRenderEffect = RenderEffect.createColorFilterEffect(colorFilter)
        }

        view.setRenderEffect(cachedRenderEffect)

        return motionView
    }
}

Comment on lines +18 to +46
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
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Since InvertEffect is a constant effect (its matrix does not change per frame), we can cache the RenderEffect instance to avoid allocating multiple objects (such as InvertedFilter, FloatArray, ColorMatrix, ColorMatrixColorFilter, and RenderEffect) on every single frame. This significantly improves performance and prevents GC overhead during animations. Additionally, clearing the RenderEffect when the frame is outside the active range ensures correct behavior during bidirectional scrubbing.

class InvertEffect(
    override val startFrame: Int,
    override val endFrame: Int,
) : MotionEffect {
    override lateinit var motionView: MotionView

    private var cachedRenderEffect: RenderEffect? = null

    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 (view.renderEffect != null) {
                view.setRenderEffect(null)
            }
            return motionView
        }

        if (cachedRenderEffect == null) {
            val invertedFilter = RgbFilter.createInvertedFilter()
            val matrix = invertedFilter.getMatrix(0L, false)
            val colorMatrix = Media3Utils.toColorMatrix(matrix)
            val colorFilter = ColorMatrixColorFilter(colorMatrix)
            cachedRenderEffect = RenderEffect.createColorFilterEffect(colorFilter)
        }

        view.setRenderEffect(cachedRenderEffect)

        return motionView
    }
}

Comment on lines +17 to +33
@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
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If the input bitmap has a HARDWARE configuration (which is common when loaded via modern image loading libraries with hardware acceleration), calling Canvas(output) with a bitmap created using input.config will crash with an IllegalArgumentException. To prevent this, check if the config is HARDWARE and fallback to ARGB_8888. Additionally, since the contrast parameter is constant for a given plugin instance, we can cache the ColorMatrixColorFilter using lazy to avoid recreating it on every apply call.

class ContrastPlugin(val contrast: Float) : MotionPlugin {
    private val colorFilter by lazy {
        val contrastEffect = Contrast(contrast)
        val matrix = contrastEffect.getMatrix(0L, false)
        val colorMatrix = Media3Utils.toColorMatrix(matrix)
        ColorMatrixColorFilter(colorMatrix)
    }

    override fun apply(input: Bitmap): Bitmap {
        val config = if (input.config == Bitmap.Config.HARDWARE) Bitmap.Config.ARGB_8888 else (input.config ?: Bitmap.Config.ARGB_8888)
        val output = Bitmap.createBitmap(input.width, input.height, config)
        val canvas = Canvas(output)
        val paint = Paint().apply {
            colorFilter = this@ContrastPlugin.colorFilter
        }

        canvas.drawBitmap(input, 0f, 0f, paint)

        return output
    }
}

Comment on lines +17 to +32
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
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If the input bitmap has a HARDWARE configuration (which is common when loaded via modern image loading libraries with hardware acceleration), calling Canvas(output) with a bitmap created using input.config will crash with an IllegalArgumentException. To prevent this, check if the config is HARDWARE and fallback to ARGB_8888. Additionally, since the grayscale filter is constant, we can cache the ColorMatrixColorFilter using lazy to avoid recreating it on every apply call.

class GrayscalePlugin : MotionPlugin {
    private val colorFilter by lazy {
        val grayscaleFilter = RgbFilter.createGrayscaleFilter()
        val matrix = grayscaleFilter.getMatrix(0L, false)
        val colorMatrix = Media3Utils.toColorMatrix(matrix)
        ColorMatrixColorFilter(colorMatrix)
    }

    override fun apply(input: Bitmap): Bitmap {
        val config = if (input.config == Bitmap.Config.HARDWARE) Bitmap.Config.ARGB_8888 else (input.config ?: Bitmap.Config.ARGB_8888)
        val output = Bitmap.createBitmap(input.width, input.height, config)
        val canvas = Canvas(output)
        val paint = Paint().apply {
            colorFilter = this@GrayscalePlugin.colorFilter
        }

        canvas.drawBitmap(input, 0f, 0f, paint)

        return output
    }
}

Comment on lines +17 to +32
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
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If the input bitmap has a HARDWARE configuration (which is common when loaded via modern image loading libraries with hardware acceleration), calling Canvas(output) with a bitmap created using input.config will crash with an IllegalArgumentException. To prevent this, check if the config is HARDWARE and fallback to ARGB_8888. Additionally, since the inverted filter is constant, we can cache the ColorMatrixColorFilter using lazy to avoid recreating it on every apply call.

class InvertPlugin : MotionPlugin {
    private val colorFilter by lazy {
        val invertedFilter = RgbFilter.createInvertedFilter()
        val matrix = invertedFilter.getMatrix(0L, false)
        val colorMatrix = Media3Utils.toColorMatrix(matrix)
        ColorMatrixColorFilter(colorMatrix)
    }

    override fun apply(input: Bitmap): Bitmap {
        val config = if (input.config == Bitmap.Config.HARDWARE) Bitmap.Config.ARGB_8888 else (input.config ?: Bitmap.Config.ARGB_8888)
        val output = Bitmap.createBitmap(input.width, input.height, config)
        val canvas = Canvas(output)
        val paint = Paint().apply {
            colorFilter = this@InvertPlugin.colorFilter
        }

        canvas.drawBitmap(input, 0f, 0f, paint)

        return output
    }
}

Comment on lines +17 to +41
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
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If the input bitmap has a HARDWARE configuration (which is common when loaded via modern image loading libraries with hardware acceleration), calling Canvas(output) with a bitmap created using input.config will crash with an IllegalArgumentException. To prevent this, check if the config is HARDWARE and fallback to ARGB_8888. Additionally, since the RGB scaling parameters are constant for a given plugin instance, we can cache the ColorMatrixColorFilter using lazy to avoid recreating it on every apply call.

class RgbPlugin(
    val redScale: Float = 1f,
    val greenScale: Float = 1f,
    val blueScale: Float = 1f,
) : MotionPlugin {
    private val colorFilter by lazy {
        val rgbAdjustment = RgbAdjustment.Builder()
            .setRedScale(redScale)
            .setGreenScale(greenScale)
            .setBlueScale(blueScale)
            .build()
        val matrix = rgbAdjustment.getMatrix(0L, false)
        val colorMatrix = Media3Utils.toColorMatrix(matrix)
        ColorMatrixColorFilter(colorMatrix)
    }

    override fun apply(input: Bitmap): Bitmap {
        val config = if (input.config == Bitmap.Config.HARDWARE) Bitmap.Config.ARGB_8888 else (input.config ?: Bitmap.Config.ARGB_8888)
        val output = Bitmap.createBitmap(input.width, input.height, config)
        val canvas = Canvas(output)
        val paint = Paint().apply {
            colorFilter = this@RgbPlugin.colorFilter
        }

        canvas.drawBitmap(input, 0f, 0f, paint)

        return output
    }
}

Comment on lines +48 to +50
// 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()}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Instead of using string interpolation with libs.versions.media3Version.get(), it is more idiomatic and cleaner to define the media3-effect dependency in your libs.versions.toml version catalog and reference it directly as a type-safe accessor.

    implementation libs.androidx.media3.effect

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
modules/media3-motion-ext/src/test/java/com/tejpratapsingh/motionlib/media3/Media3ExtensionTest.kt (1)

23-53: Replace tautological interface checks with behavior assertions.

From lines 23–53, assertTrue(x is MotionEffect/MotionPlugin) checks are guaranteed by the explicit interface implementation in class declarations (: MotionEffect, : MotionPlugin), providing no regression protection. Test actual behavior instead: verify frame boundary handling (startFrame/endFrame constraints) for effects, and bitmap transformation correctness for plugins.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@modules/media3-motion-ext/src/test/java/com/tejpratapsingh/motionlib/media3/Media3ExtensionTest.kt`
around lines 23 - 53, Replace the tautological interface checks in the
testEffectsImplementation() and testPluginsImplementation() methods with
behavior assertions that test actual functionality. For the effect objects
(BrightnessEffect, ContrastEffect, GrayscaleEffect, InvertEffect, RgbEffect),
verify frame boundary handling by asserting constraints on startFrame and
endFrame values. For the plugin objects (BrightnessPlugin, ContrastPlugin,
GrayscalePlugin, InvertPlugin, RgbPlugin), test bitmap transformation
correctness by verifying that the plugins properly modify image properties or
output. Remove the trivial isinstance checks (assertTrue(x is
MotionEffect/MotionPlugin)) and add meaningful assertions that exercise the
actual behavior of each effect and plugin.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@modules/media3-motion-ext/build.gradle`:
- Line 50: The media3-effect dependency uses direct string interpolation with
version variables instead of referencing the version catalog like the other
media3 artifacts (media3-common and media3-exoplayer). Add a new entry for
media3-effect in the gradle/libs.versions.toml file under the [libraries]
section following the same pattern as the other androidx.media3 entries, then
update the implementation dependency from the interpolated version string to use
the catalog reference format (libs.androidx.media3.effect) to maintain
consistency across all media3 dependencies.

In
`@modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/BrightnessEffect.kt`:
- Line 56: Effect implementations are overwriting each other because they call
setRenderEffect() which replaces any existing effect instead of composing them.
In BrightnessEffect.kt at line 56, ContrastEffect.kt at line 56, and
GrayscaleEffect.kt at line 42, replace the direct setRenderEffect() call with a
composition strategy that chains effects together. Retrieve the existing
RenderEffect from the view (if any), use RenderEffect.createChainEffect() to
compose the new effect with the existing one, and then apply the composed result
via setRenderEffect(). This ensures multiple effects on the same view are all
visible in the final render.

In
`@modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/BrightnessPlugin.kt`:
- Line 20: The elvis operator in the Bitmap.createBitmap call at
BrightnessPlugin.kt line 20 only protects against null configs but allows
HARDWARE config to propagate, which causes a crash when creating a Canvas since
hardware bitmaps are immutable. Replace the condition `input.config ?:
Bitmap.Config.ARGB_8888` with an explicit check that filters out HARDWARE config
by verifying that the input config is not null and not equal to
Bitmap.Config.HARDWARE before using it; otherwise default to
Bitmap.Config.ARGB_8888. Apply this same fix at ContrastPlugin.kt line 20,
GrayscalePlugin.kt line 19, InvertPlugin.kt line 19, and RgbPlugin.kt line 23.

---

Nitpick comments:
In
`@modules/media3-motion-ext/src/test/java/com/tejpratapsingh/motionlib/media3/Media3ExtensionTest.kt`:
- Around line 23-53: Replace the tautological interface checks in the
testEffectsImplementation() and testPluginsImplementation() methods with
behavior assertions that test actual functionality. For the effect objects
(BrightnessEffect, ContrastEffect, GrayscaleEffect, InvertEffect, RgbEffect),
verify frame boundary handling by asserting constraints on startFrame and
endFrame values. For the plugin objects (BrightnessPlugin, ContrastPlugin,
GrayscalePlugin, InvertPlugin, RgbPlugin), test bitmap transformation
correctness by verifying that the plugins properly modify image properties or
output. Remove the trivial isinstance checks (assertTrue(x is
MotionEffect/MotionPlugin)) and add meaningful assertions that exercise the
actual behavior of each effect and plugin.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4c4aed41-8d1b-4d48-ad38-4970ed810e32

📥 Commits

Reviewing files that changed from the base of the PR and between 8859ed5 and 6fbcddd.

📒 Files selected for processing (16)
  • .idea/deploymentTargetSelector.xml
  • .idea/gradle.xml
  • modules/media3-motion-ext/build.gradle
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/Media3Utils.kt
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/BrightnessEffect.kt
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/ContrastEffect.kt
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/GrayscaleEffect.kt
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/InvertEffect.kt
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/RgbEffect.kt
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/BrightnessPlugin.kt
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/ContrastPlugin.kt
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/GrayscalePlugin.kt
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/InvertPlugin.kt
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/RgbPlugin.kt
  • modules/media3-motion-ext/src/test/java/com/tejpratapsingh/motionlib/media3/Media3ExtensionTest.kt
  • settings.gradle

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()}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify media3-effect artifact availability at version 1.10.0
curl -s "https://repo1.maven.org/maven2/androidx/media3/media3-effect/1.10.0/" | grep -q "media3-effect-1.10.0.aar" && echo "✓ media3-effect:1.10.0 exists" || echo "✗ media3-effect:1.10.0 not found"

# Check if media3-effect is in the version catalog
rg -n "media3.effect|media3-effect" gradle/libs.versions.toml

Repository: tejpratap46/AndroidVideoMotion

Length of output: 107


🏁 Script executed:

#!/bin/bash
# Find available versions of media3-effect in Maven Central
echo "=== Checking available media3-effect versions ==="
curl -s "https://repo1.maven.org/maven2/androidx/media3/media3-effect/" | grep -oP '">[\d.]+/<' | tr -d '">' | sort -V | tail -10

# Check the gradle/libs.versions.toml file
echo -e "\n=== Checking gradle/libs.versions.toml for media3 references ==="
rg -A 5 "media3" gradle/libs.versions.toml || echo "No media3 references found"

# Read the build.gradle file around line 50
echo -e "\n=== Checking build.gradle around line 50 ==="
sed -n '40,60p' modules/media3-motion-ext/build.gradle

Repository: tejpratap46/AndroidVideoMotion

Length of output: 1570


🏁 Script executed:

#!/bin/bash
# Better verification of media3-effect artifact existence using Maven metadata
echo "=== Checking media3-effect in Maven Central ==="
curl -s "https://repo1.maven.org/maven2/androidx/media3/media3-effect/maven-metadata.xml" | grep -oP '<version>\K[^<]+' | tail -5

# Also check specifically for 1.10.0
echo -e "\n=== Checking for media3-effect 1.10.0 directly ==="
curl -s -I "https://repo1.maven.org/maven2/androidx/media3/media3-effect/1.10.0/media3-effect-1.10.0.aar" | head -1

# Check what other media3 artifacts are available at that version
echo -e "\n=== Other media3 artifacts at version 1.10.0 ==="
curl -s "https://repo1.maven.org/maven2/androidx/media3/" | grep -oP '>media3-[^/<]+' | sort -u | head -10

Repository: tejpratap46/AndroidVideoMotion

Length of output: 241


🏁 Script executed:

#!/bin/bash
# Verify the artifact truly exists with Python for more reliable checking
python3 << 'PY'
import urllib.request
import urllib.error

url = "https://repo1.maven.org/maven2/androidx/media3/media3-effect/1.10.0/media3-effect-1.10.0.aar"
try:
    response = urllib.request.urlopen(url, timeout=5)
    print(f"✓ media3-effect:1.10.0 exists (HTTP {response.status})")
except urllib.error.HTTPError as e:
    print(f"✗ media3-effect:1.10.0 not found (HTTP {e.code})")
except Exception as e:
    print(f"? Could not verify: {e}")
PY

# Also verify other media3 artifacts at same version to confirm pattern
echo -e "\n=== Verifying media3-common at 1.10.0 ==="
python3 << 'PY'
import urllib.request
url = "https://repo1.maven.org/maven2/androidx/media3/media3-common/1.10.0/media3-common-1.10.0.aar"
try:
    response = urllib.request.urlopen(url, timeout=5)
    print(f"✓ media3-common:1.10.0 exists")
except:
    print(f"✗ media3-common:1.10.0 not found")
PY

Repository: tejpratap46/AndroidVideoMotion

Length of output: 304


Add media3-effect to the version catalog for consistency.

Line 50 uses string interpolation to construct the dependency, while lines 46-47 reference the version catalog (libs.androidx.media3.common, libs.androidx.media3.exoplayer). Add media3-effect to gradle/libs.versions.toml under [libraries] and update the dependency to use the catalog reference instead, matching the pattern for other media3 artifacts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/media3-motion-ext/build.gradle` at line 50, The media3-effect
dependency uses direct string interpolation with version variables instead of
referencing the version catalog like the other media3 artifacts (media3-common
and media3-exoplayer). Add a new entry for media3-effect in the
gradle/libs.versions.toml file under the [libraries] section following the same
pattern as the other androidx.media3 entries, then update the implementation
dependency from the interpolated version string to use the catalog reference
format (libs.androidx.media3.effect) to maintain consistency across all media3
dependencies.

val colorMatrix = Media3Utils.toColorMatrix(matrix)

val colorFilter = ColorMatrixColorFilter(colorMatrix)
view.setRenderEffect(RenderEffect.createColorFilterEffect(colorFilter))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

All effect classes overwrite each other via setRenderEffect.

All five effect implementations call view.setRenderEffect(...) which replaces any existing RenderEffect rather than composing. When multiple effects target the same view in a single frame (e.g., brightness + contrast), only the last-applied effect is visible.

  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/BrightnessEffect.kt#L56-L56: replace setRenderEffect(createColorFilterEffect(...)) with a composition strategy using RenderEffect.createChainEffect().
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/ContrastEffect.kt#L56-L56: same composition fix needed.
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/GrayscaleEffect.kt#L42-L42: same composition fix needed.
  • (Also applies to InvertEffect and RgbEffect implementations not shown in this review batch.)
📍 Affects 3 files
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/BrightnessEffect.kt#L56-L56 (this comment)
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/ContrastEffect.kt#L56-L56
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/GrayscaleEffect.kt#L42-L42
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/effects/BrightnessEffect.kt`
at line 56, Effect implementations are overwriting each other because they call
setRenderEffect() which replaces any existing effect instead of composing them.
In BrightnessEffect.kt at line 56, ContrastEffect.kt at line 56, and
GrayscaleEffect.kt at line 42, replace the direct setRenderEffect() call with a
composition strategy that chains effects together. Retrieve the existing
RenderEffect from the view (if any), use RenderEffect.createChainEffect() to
compose the new effect with the existing one, and then apply the composed result
via setRenderEffect(). This ensures multiple effects on the same view are all
visible in the final render.

@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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify all plugin sites using input.config directly in Bitmap.createBitmap
rg -n "Bitmap\\.createBitmap\\(input\\.width, input\\.height, input\\.config \\?: Bitmap\\.Config\\.ARGB_8888\\)" \
  modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/*.kt

Repository: tejpratap46/AndroidVideoMotion

Length of output: 1140


🏁 Script executed:

#!/bin/bash
# Check BrightnessPlugin implementation to see how output bitmap is used
cat -n modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/BrightnessPlugin.kt

Repository: tejpratap46/AndroidVideoMotion

Length of output: 1519


🌐 Web query:

Bitmap.Config.HARDWARE Android Canvas drawing compatibility

💡 Result:

In Android, Bitmap.Config.HARDWARE creates bitmaps that are stored exclusively in graphics memory [1][2]. These bitmaps are inherently immutable [1][3] and are intended strictly for display purposes, such as being drawn to a screen [1]. Compatibility with Canvas drawing is subject to strict limitations: 1. Software Rendering Restriction: You cannot draw a HARDWARE bitmap onto a Canvas that is not hardware-accelerated [4][5][6]. Attempting to do so will result in an IllegalStateException with the message "Software rendering doesn't support hardware bitmaps" [7][8][5]. 2. Immutable Property: Because HARDWARE bitmaps are immutable, they cannot be used as the backing store for a Canvas [9]. You cannot instantiate a new Canvas using a HARDWARE bitmap (e.g., new Canvas(hardwareBitmap)) because that operation requires the target bitmap to be mutable [9][3]. 3. Common Workarounds: - If you need to perform software-based drawing or manipulation, you must copy the HARDWARE bitmap into a software-backed format first, such as Bitmap.Config.ARGB_8888, using the copy method: hardwareBitmap.copy(Bitmap.Config.ARGB_8888, false) [10]. - If you are experiencing this crash in a view context (e.g., inside onDraw), ensure the View is not forced into software rendering mode using setLayerType(View.LAYER_TYPE_SOFTWARE, null) or an activity-level flag, unless you have specifically converted the bitmap to a software-compatible format [5]. - For applications using image loading libraries (like Glide or Coil), configure them to disallow hardware bitmaps if you intend to perform further transformations on the retrieved images [7][11]. In summary, HARDWARE bitmaps are optimized for read-only display tasks and are not compatible with general Canvas drawing or offscreen rendering workflows [1][9].

Citations:


Prevent Bitmap.Config.HARDWARE from being used as the output bitmap.

At line 20, the elvis operator input.config ?: Bitmap.Config.ARGB_8888 only protects against null configs. If the input bitmap has Bitmap.Config.HARDWARE, it propagates directly to the output, which then fails when creating a Canvas on line 21. Hardware bitmaps are immutable and incompatible with Canvas drawing—this will crash with "Software rendering doesn't support hardware bitmaps."

Replace input.config with an explicit check that filters out HARDWARE config:

Suggested fix
-        val output = Bitmap.createBitmap(input.width, input.height, input.config ?: Bitmap.Config.ARGB_8888)
+        val safeConfig = when (input.config) {
+            null, Bitmap.Config.HARDWARE -> Bitmap.Config.ARGB_8888
+            else -> input.config
+        }
+        val output = Bitmap.createBitmap(input.width, input.height, safeConfig)

Apply the same fix at:

  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/ContrastPlugin.kt Line 20
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/GrayscalePlugin.kt Line 19
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/InvertPlugin.kt Line 19
  • modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/RgbPlugin.kt Line 23
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val output = Bitmap.createBitmap(input.width, input.height, input.config ?: Bitmap.Config.ARGB_8888)
val safeConfig = when (input.config) {
null, Bitmap.Config.HARDWARE -> Bitmap.Config.ARGB_8888
else -> input.config
}
val output = Bitmap.createBitmap(input.width, input.height, safeConfig)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@modules/media3-motion-ext/src/main/java/com/tejpratapsingh/motionlib/media3/plugins/BrightnessPlugin.kt`
at line 20, The elvis operator in the Bitmap.createBitmap call at
BrightnessPlugin.kt line 20 only protects against null configs but allows
HARDWARE config to propagate, which causes a crash when creating a Canvas since
hardware bitmaps are immutable. Replace the condition `input.config ?:
Bitmap.Config.ARGB_8888` with an explicit check that filters out HARDWARE config
by verifying that the input config is not null and not equal to
Bitmap.Config.HARDWARE before using it; otherwise default to
Bitmap.Config.ARGB_8888. Apply this same fix at ContrastPlugin.kt line 20,
GrayscalePlugin.kt line 19, InvertPlugin.kt line 19, and RgbPlugin.kt line 23.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use Media3 API for effects and plugins

1 participant