From 701e71ae11faaf4c0834c6ace090e90759c8f27f Mon Sep 17 00:00:00 2001 From: pedroSG94 Date: Fri, 24 Jul 2026 20:46:36 +0200 Subject: [PATCH 01/10] refactor textfilterrender --- .../com/pedro/streamer/utils/FilterMenu.kt | 4 +- .../render/filters/object/TextFilterRender.kt | 80 ++++++++++++++++ .../object/TextObjectFilterRender.java | 91 ------------------- 3 files changed, 82 insertions(+), 93 deletions(-) create mode 100644 encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/TextFilterRender.kt delete mode 100644 encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/TextObjectFilterRender.java diff --git a/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt b/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt index 2870445fa..af3d810c3 100644 --- a/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt +++ b/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt @@ -72,7 +72,7 @@ import com.pedro.encoder.input.gl.render.filters.ZebraFilterRender import com.pedro.encoder.input.gl.render.filters.`object`.GifObjectFilterRender import com.pedro.encoder.input.gl.render.filters.`object`.ImageObjectFilterRender import com.pedro.encoder.input.gl.render.filters.`object`.SurfaceFilterRender -import com.pedro.encoder.input.gl.render.filters.`object`.TextObjectFilterRender +import com.pedro.encoder.input.gl.render.filters.`object`.TextFilterRender import com.pedro.encoder.utils.gl.TranslateTo import com.pedro.library.view.GlInterface import com.pedro.streamer.R @@ -323,7 +323,7 @@ class FilterMenu(private val context: Context) { return true } R.id.text -> { - val textObjectFilterRender = TextObjectFilterRender() + val textObjectFilterRender = TextFilterRender() glInterface.setFilter(textObjectFilterRender) textObjectFilterRender.setText("Hello world", 22f, Color.RED) textObjectFilterRender.setScale(50f, 50f) diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/TextFilterRender.kt b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/TextFilterRender.kt new file mode 100644 index 000000000..f7333d820 --- /dev/null +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/TextFilterRender.kt @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2024 pedroSG94. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pedro.encoder.input.gl.render.filters.`object` + +import android.graphics.Color +import android.graphics.Typeface +import android.opengl.GLES20 +import android.os.Build +import androidx.annotation.RequiresApi +import com.pedro.encoder.utils.gl.TextStreamObject + +/** + * Created by pedro on 27/07/18. + */ + +@Deprecated("Use TextFilterRender instead") +typealias TextObjectFilterRender = TextFilterRender + +@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) +class TextFilterRender : BaseObjectFilterRender() { + private var text: String = "" + private var textSize = 0f + private var textColor = 0 + private var backgroundColor = 0 + private var typeface: Typeface? = null + + init { + streamObject = TextStreamObject() + } + + override fun drawFilter() { + super.drawFilter() + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, streamObjectTextureId[0]) + //Set alpha. 0f if no image loaded. + GLES20.glUniform1f(uAlphaHandle, if (streamObjectTextureId[0] == -1) 0f else alpha) + } + + @JvmOverloads + fun setText( + text: String, textSize: Float, textColor: Int, + backgroundColor: Int = Color.TRANSPARENT, typeface: Typeface? = null + ) { + this.text = text + this.textSize = textSize + this.textColor = textColor + this.backgroundColor = backgroundColor + this.typeface = typeface + (streamObject as TextStreamObject).load(text, textSize, textColor, backgroundColor, typeface) + shouldLoad = true + } + + fun addText(text: String) { + setText(this.text + text, textSize, textColor, backgroundColor, typeface) + } + + fun updateColor(textColor: Int) { + setText(text, textSize, textColor, backgroundColor, typeface) + } + + fun updateTypeface(typeface: Typeface?) { + setText(text, textSize, textColor, backgroundColor, typeface) + } + + fun updateTextSize(textSize: Float) { + setText(text, textSize, textColor, backgroundColor, typeface) + } +} diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/TextObjectFilterRender.java b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/TextObjectFilterRender.java deleted file mode 100644 index e38d33d13..000000000 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/TextObjectFilterRender.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2024 pedroSG94. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.pedro.encoder.input.gl.render.filters.object; - -import android.graphics.Color; -import android.graphics.Typeface; -import android.opengl.GLES20; -import android.os.Build; - -import androidx.annotation.RequiresApi; - -import com.pedro.encoder.utils.gl.TextStreamObject; - -/** - * Created by pedro on 27/07/18. - */ - -@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) -public class TextObjectFilterRender extends BaseObjectFilterRender { - - private String text; - private float textSize; - private int textColor; - private int backgroundColor; - private Typeface typeface; - - public TextObjectFilterRender() { - super(); - streamObject = new TextStreamObject(); - } - - @Override - protected void drawFilter() { - super.drawFilter(); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, streamObjectTextureId[0]); - //Set alpha. 0f if no image loaded. - GLES20.glUniform1f(uAlphaHandle, streamObjectTextureId[0] == -1 ? 0f : alpha); - } - - public void setText(String text, float textSize, int textColor) { - setText(text, textSize, textColor, Color.TRANSPARENT, null); - } - - public void setText(String text, float textSize, int textColor, int backgroundColor) { - setText(text, textSize, textColor, backgroundColor, null); - } - - public void setText(String text, float textSize, int textColor, Typeface typeface) { - setText(text, textSize, textColor, Color.TRANSPARENT, typeface); - } - - public void setText(String text, float textSize, int textColor, int backgroundColor, Typeface typeface) { - this.text = text; - this.textSize = textSize; - this.textColor = textColor; - this.backgroundColor = backgroundColor; - this.typeface = typeface; - ((TextStreamObject) streamObject).load(text, textSize, textColor, backgroundColor, typeface); - shouldLoad = true; - } - - public void addText(String text) { - setText(this.text + text, textSize, textColor, backgroundColor, typeface); - } - - public void updateColor(int textColor) { - setText(this.text + text, textSize, textColor, backgroundColor, typeface); - } - - public void updateTypeface(Typeface typeface) { - setText(this.text + text, textSize, textColor, backgroundColor, typeface); - } - - public void updateTextSize(float textSize) { - setText(this.text + text, textSize, textColor, backgroundColor, typeface); - } -} From 4643ba3f991f1a46c178342315f90e815d6ac66f Mon Sep 17 00:00:00 2001 From: pedroSG94 Date: Fri, 24 Jul 2026 21:23:00 +0200 Subject: [PATCH 02/10] moving filters to kotlin --- .../com/pedro/streamer/utils/FilterMenu.kt | 8 +- .../render/filters/object/GifFilterRender.kt | 51 ++++++++++ .../filters/object/GifObjectFilterRender.java | 54 ---------- ...FilterRender.java => ImageFilterRender.kt} | 41 ++++---- .../filters/object/SurfaceFilterRender.java | 99 ------------------- .../filters/object/SurfaceFilterRender.kt | 81 +++++++++++++++ 6 files changed, 155 insertions(+), 179 deletions(-) create mode 100644 encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/GifFilterRender.kt delete mode 100644 encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/GifObjectFilterRender.java rename encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/{ImageObjectFilterRender.java => ImageFilterRender.kt} (54%) delete mode 100644 encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/SurfaceFilterRender.java create mode 100644 encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/SurfaceFilterRender.kt diff --git a/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt b/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt index af3d810c3..0c2fb6666 100644 --- a/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt +++ b/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt @@ -69,8 +69,8 @@ import com.pedro.encoder.input.gl.render.filters.SnowFilterRender import com.pedro.encoder.input.gl.render.filters.SwirlFilterRender import com.pedro.encoder.input.gl.render.filters.TemperatureFilterRender import com.pedro.encoder.input.gl.render.filters.ZebraFilterRender -import com.pedro.encoder.input.gl.render.filters.`object`.GifObjectFilterRender -import com.pedro.encoder.input.gl.render.filters.`object`.ImageObjectFilterRender +import com.pedro.encoder.input.gl.render.filters.`object`.GifFilterRender +import com.pedro.encoder.input.gl.render.filters.`object`.ImageFilterRender import com.pedro.encoder.input.gl.render.filters.`object`.SurfaceFilterRender import com.pedro.encoder.input.gl.render.filters.`object`.TextFilterRender import com.pedro.encoder.utils.gl.TranslateTo @@ -207,7 +207,7 @@ class FilterMenu(private val context: Context) { } R.id.gif -> { try { - val gifObjectFilterRender = GifObjectFilterRender() + val gifObjectFilterRender = GifFilterRender() gifObjectFilterRender.setGif(context.resources.openRawResource(R.raw.banana)) glInterface.setFilter(gifObjectFilterRender) gifObjectFilterRender.setScale(50f, 50f) @@ -225,7 +225,7 @@ class FilterMenu(private val context: Context) { return true } R.id.image -> { - val imageObjectFilterRender = ImageObjectFilterRender() + val imageObjectFilterRender = ImageFilterRender() glInterface.setFilter(imageObjectFilterRender) imageObjectFilterRender.setImage( BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher) diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/GifFilterRender.kt b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/GifFilterRender.kt new file mode 100644 index 000000000..e457b7b2c --- /dev/null +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/GifFilterRender.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2024 pedroSG94. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pedro.encoder.input.gl.render.filters.`object` + +import android.opengl.GLES20 +import android.os.Build +import androidx.annotation.RequiresApi +import com.pedro.encoder.utils.gl.GifStreamObject +import java.io.IOException +import java.io.InputStream + +/** + * Created by pedro on 27/07/18. + */ + +@Deprecated("Use GifFilterRender instead") +typealias GifObjectFilterRender = GifFilterRender + +@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) +class GifFilterRender : BaseObjectFilterRender() { + init { + streamObject = GifStreamObject() + } + + override fun drawFilter() { + super.drawFilter() + val position: Int = (streamObject as GifStreamObject).updateFrame(streamObjectTextureId.size) + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, streamObjectTextureId[position]) + //Set alpha. 0f if no image loaded. + GLES20.glUniform1f(uAlphaHandle, if (streamObjectTextureId[position] == -1) 0f else alpha) + } + + @Throws(IOException::class) + fun setGif(inputStream: InputStream?) { + (streamObject as GifStreamObject).load(inputStream) + shouldLoad = true + } +} diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/GifObjectFilterRender.java b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/GifObjectFilterRender.java deleted file mode 100644 index 2a711b14a..000000000 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/GifObjectFilterRender.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2024 pedroSG94. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.pedro.encoder.input.gl.render.filters.object; - -import android.opengl.GLES20; -import android.os.Build; - -import androidx.annotation.RequiresApi; - -import com.pedro.encoder.utils.gl.GifStreamObject; - -import java.io.IOException; -import java.io.InputStream; - -/** - * Created by pedro on 27/07/18. - */ - -@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) -public class GifObjectFilterRender extends BaseObjectFilterRender { - - public GifObjectFilterRender() { - super(); - streamObject = new GifStreamObject(); - } - - @Override - protected void drawFilter() { - super.drawFilter(); - int position = ((GifStreamObject) streamObject).updateFrame(streamObjectTextureId.length); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, streamObjectTextureId[position]); - //Set alpha. 0f if no image loaded. - GLES20.glUniform1f(uAlphaHandle, streamObjectTextureId[position] == -1 ? 0f : alpha); - } - - public void setGif(InputStream inputStream) throws IOException { - ((GifStreamObject) streamObject).load(inputStream); - shouldLoad = true; - } -} diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/ImageObjectFilterRender.java b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/ImageFilterRender.kt similarity index 54% rename from encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/ImageObjectFilterRender.java rename to encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/ImageFilterRender.kt index 989daa408..6aa85a3ca 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/ImageObjectFilterRender.java +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/ImageFilterRender.kt @@ -13,39 +13,36 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.pedro.encoder.input.gl.render.filters.`object` -package com.pedro.encoder.input.gl.render.filters.object; - -import android.graphics.Bitmap; -import android.opengl.GLES20; -import android.os.Build; - -import androidx.annotation.RequiresApi; - -import com.pedro.encoder.utils.gl.ImageStreamObject; +import android.graphics.Bitmap +import android.opengl.GLES20 +import android.os.Build +import androidx.annotation.RequiresApi +import com.pedro.encoder.utils.gl.ImageStreamObject /** * Created by pedro on 27/07/18. */ -@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) -public class ImageObjectFilterRender extends BaseObjectFilterRender { +@Deprecated("Use ImageFilterRender instead") +typealias ImageObjectFilterRender = ImageFilterRender - public ImageObjectFilterRender() { - super(); - streamObject = new ImageStreamObject(); +@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) +class ImageFilterRender : BaseObjectFilterRender() { + init { + streamObject = ImageStreamObject() } - @Override - protected void drawFilter() { - super.drawFilter(); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, streamObjectTextureId[0]); + override fun drawFilter() { + super.drawFilter() + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, streamObjectTextureId[0]) //Set alpha. 0f if no image loaded. - GLES20.glUniform1f(uAlphaHandle, streamObjectTextureId[0] == -1 ? 0f : alpha); + GLES20.glUniform1f(uAlphaHandle, if (streamObjectTextureId[0] == -1) 0f else alpha) } - public void setImage(Bitmap bitmap) { - ((ImageStreamObject) streamObject).load(bitmap); - shouldLoad = true; + fun setImage(bitmap: Bitmap?) { + (streamObject as ImageStreamObject).load(bitmap) + shouldLoad = true } } diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/SurfaceFilterRender.java b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/SurfaceFilterRender.java deleted file mode 100644 index 693eddd46..000000000 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/SurfaceFilterRender.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2024 pedroSG94. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.pedro.encoder.input.gl.render.filters.object; - -import android.content.Context; -import android.graphics.SurfaceTexture; -import android.opengl.GLES11Ext; -import android.opengl.GLES20; -import android.os.Build; -import android.os.Handler; -import android.os.Looper; -import android.view.Surface; - -import androidx.annotation.RequiresApi; - -import com.pedro.encoder.R; -import com.pedro.encoder.utils.gl.GlUtil; - -/** - * Created by pedro on 18/07/18. - */ - -@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) -public class SurfaceFilterRender extends BaseObjectFilterRender { - - public interface SurfaceReadyCallback { - void surfaceReady(SurfaceTexture surfaceTexture); - } - - private SurfaceTexture surfaceTexture; - private Surface surface; - private final SurfaceReadyCallback surfaceReadyCallback; - - public SurfaceFilterRender() { - this(null); - } - - public SurfaceFilterRender(SurfaceReadyCallback surfaceReadyCallback) { - super(); - this.surfaceReadyCallback = surfaceReadyCallback; - } - - @Override - protected void initGlFilter(Context context) { - fragment = R.raw.surface_fragment; - super.initGlFilter(context); - GlUtil.createExternalTextures(streamObjectTextureId.length, streamObjectTextureId, 0); - surfaceTexture = new SurfaceTexture(streamObjectTextureId[0]); - surfaceTexture.setDefaultBufferSize(getWidth(), getHeight()); - surface = new Surface(surfaceTexture); - if (surfaceReadyCallback != null) { - new Handler(Looper.getMainLooper()).post(() -> surfaceReadyCallback.surfaceReady(surfaceTexture)); - } - } - - @Override - protected void drawFilter() { - surfaceTexture.updateTexImage(); - super.drawFilter(); - GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, streamObjectTextureId[0]); - //Set alpha. 0f if no image loaded. - GLES20.glUniform1f(uAlphaHandle, streamObjectTextureId[0] == -1 ? 0f : alpha); - } - - @Override - public void release() { - super.release(); - if (surfaceTexture != null) surfaceTexture.release(); - if (surface != null) surface.release(); - } - - /** - * This texture must be renderer using an api called on main thread to avoid possible errors - */ - public SurfaceTexture getSurfaceTexture() { - return surfaceTexture; - } - - /** - * This surface must be renderer using an api called on main thread to avoid possible errors - */ - public Surface getSurface() { - return surface; - } -} \ No newline at end of file diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/SurfaceFilterRender.kt b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/SurfaceFilterRender.kt new file mode 100644 index 000000000..af79bcdc3 --- /dev/null +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/SurfaceFilterRender.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2024 pedroSG94. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pedro.encoder.input.gl.render.filters.`object` + +import android.content.Context +import android.graphics.SurfaceTexture +import android.opengl.GLES11Ext +import android.opengl.GLES20 +import android.os.Build +import android.os.Handler +import android.os.Looper +import android.view.Surface +import androidx.annotation.RequiresApi +import com.pedro.encoder.R +import com.pedro.encoder.utils.gl.GlUtil + +/** + * Created by pedro on 18/07/18. + */ +@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) +class SurfaceFilterRender @JvmOverloads constructor(private val surfaceReadyCallback: SurfaceReadyCallback? = null) : BaseObjectFilterRender() { + interface SurfaceReadyCallback { + fun surfaceReady(surfaceTexture: SurfaceTexture?) + } + + /** + * This texture must be renderer using an api called on main thread to avoid possible errors + */ + var surfaceTexture: SurfaceTexture? = null + private set + + /** + * This surface must be renderer using an api called on main thread to avoid possible errors + */ + var surface: Surface? = null + private set + + override fun initGlFilter(context: Context?) { + fragment = R.raw.surface_fragment + super.initGlFilter(context) + GlUtil.createExternalTextures(streamObjectTextureId.size, streamObjectTextureId, 0) + val surfaceTexture = SurfaceTexture(streamObjectTextureId[0]) + surfaceTexture.setDefaultBufferSize(width, height) + this.surfaceTexture = surfaceTexture + surface = Surface(surfaceTexture) + surfaceReadyCallback?.let { + Handler(Looper.getMainLooper()).post { + surfaceReadyCallback.surfaceReady( + surfaceTexture + ) + } + } + } + + override fun drawFilter() { + surfaceTexture?.updateTexImage() + super.drawFilter() + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, streamObjectTextureId[0]) + //Set alpha. 0f if no image loaded. + GLES20.glUniform1f(uAlphaHandle, if (streamObjectTextureId[0] == -1) 0f else alpha) + } + + override fun release() { + super.release() + surfaceTexture?.release() + surface?.release() + } +} \ No newline at end of file From 6c9728d5cb11fcb78e437081fd937fd9e677cb96 Mon Sep 17 00:00:00 2001 From: pedroSG94 Date: Fri, 24 Jul 2026 21:45:55 +0200 Subject: [PATCH 03/10] finish filters refactor --- .../com/pedro/streamer/utils/FilterMenu.kt | 3 +- .../object/BaseObjectFilterRender.java | 206 ------------------ .../filters/object/BaseObjectFilterRender.kt | 180 +++++++++++++++ .../render/filters/object/GifFilterRender.kt | 7 +- .../filters/object/ImageFilterRender.kt | 7 +- .../filters/object/SurfaceFilterRender.kt | 12 +- .../render/filters/object/TextFilterRender.kt | 11 +- 7 files changed, 203 insertions(+), 223 deletions(-) delete mode 100644 encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.java create mode 100644 encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.kt diff --git a/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt b/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt index 0c2fb6666..a80b72b03 100644 --- a/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt +++ b/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt @@ -306,7 +306,8 @@ class FilterMenu(private val context: Context) { return true } R.id.surface_filter -> { - val surfaceFilterRender = SurfaceFilterRender { surfaceTexture -> //You can render this filter with other api that draw in a surface. for example you can use VLC + //You can render this filter with other api that draw in a surface. for example you can use VLC + val surfaceFilterRender = SurfaceFilterRender { surfaceTexture -> mediaPlayer = MediaPlayer.create(context, R.raw.big_bunny_240p) mediaPlayer?.setSurface(Surface(surfaceTexture)) mediaPlayer?.isLooping = true diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.java b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.java deleted file mode 100644 index 9837077b2..000000000 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (C) 2024 pedroSG94. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.pedro.encoder.input.gl.render.filters.object; - -import android.content.Context; -import android.graphics.PointF; -import android.opengl.GLES20; -import android.opengl.Matrix; -import android.os.Build; - -import androidx.annotation.RequiresApi; - -import com.pedro.encoder.R; -import com.pedro.encoder.input.gl.Sprite; -import com.pedro.encoder.input.gl.TextureLoader; -import com.pedro.encoder.input.gl.render.filters.BaseFilterRender; -import com.pedro.encoder.utils.gl.GlUtil; -import com.pedro.encoder.utils.gl.StreamObjectBase; -import com.pedro.encoder.utils.gl.TranslateTo; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; - -/** - * Created by pedro on 03/08/18. - */ - -@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) -abstract public class BaseObjectFilterRender extends BaseFilterRender { - - //rotation matrix - private final float[] squareVertexDataFilter = { - // X, Y, Z, U, V - -1f, -1f, 0f, 0f, 0f, //bottom left - 1f, -1f, 0f, 1f, 0f, //bottom right - -1f, 1f, 0f, 0f, 1f, //top left - 1f, 1f, 0f, 1f, 1f, //top right - }; - - private int program = -1; - private int aPositionHandle = -1; - private int aTextureHandle = -1; - private int aTextureObjectHandle = -1; - private int uMVPMatrixHandle = -1; - private int uSTMatrixHandle = -1; - private int uSamplerHandle = -1; - private int uObjectHandle = -1; - protected int uAlphaHandle = -1; - - protected int fragment = R.raw.object_fragment; - private final FloatBuffer squareVertexObject; - - protected int[] streamObjectTextureId = new int[] { -1 }; - protected TextureLoader textureLoader = new TextureLoader(); - protected StreamObjectBase streamObject; - private final Sprite sprite = new Sprite(); - protected float alpha = 1f; - protected boolean shouldLoad = false; - - public BaseObjectFilterRender() { - squareVertex = ByteBuffer.allocateDirect(squareVertexDataFilter.length * FLOAT_SIZE_BYTES) - .order(ByteOrder.nativeOrder()) - .asFloatBuffer(); - squareVertex.put(squareVertexDataFilter).position(0); - float[] vertices = sprite.getTransformedVertices(); - squareVertexObject = ByteBuffer.allocateDirect(vertices.length * FLOAT_SIZE_BYTES) - .order(ByteOrder.nativeOrder()) - .asFloatBuffer(); - squareVertexObject.put(vertices).position(0); - Matrix.setIdentityM(MVPMatrix, 0); - Matrix.setIdentityM(STMatrix, 0); - } - - @Override - protected void initGlFilter(Context context) { - String vertexShader = GlUtil.getStringFromRaw(context, R.raw.object_vertex); - String fragmentShader = GlUtil.getStringFromRaw(context, fragment); - - program = GlUtil.createProgram(vertexShader, fragmentShader); - aPositionHandle = GLES20.glGetAttribLocation(program, "aPosition"); - aTextureHandle = GLES20.glGetAttribLocation(program, "aTextureCoord"); - aTextureObjectHandle = GLES20.glGetAttribLocation(program, "aTextureObjectCoord"); - uMVPMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix"); - uSTMatrixHandle = GLES20.glGetUniformLocation(program, "uSTMatrix"); - uSamplerHandle = GLES20.glGetUniformLocation(program, "uSampler"); - uObjectHandle = GLES20.glGetUniformLocation(program, "uObject"); - uAlphaHandle = GLES20.glGetUniformLocation(program, "uAlpha"); - } - - @Override - protected void drawFilter() { - if (shouldLoad) { - releaseTexture(); - streamObjectTextureId = textureLoader.load(streamObject.getBitmaps()); - shouldLoad = false; - } - - GLES20.glUseProgram(program); - - squareVertex.position(SQUARE_VERTEX_DATA_POS_OFFSET); - GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT, false, - SQUARE_VERTEX_DATA_STRIDE_BYTES, squareVertex); - GLES20.glEnableVertexAttribArray(aPositionHandle); - - squareVertex.position(SQUARE_VERTEX_DATA_UV_OFFSET); - GLES20.glVertexAttribPointer(aTextureHandle, 2, GLES20.GL_FLOAT, false, - SQUARE_VERTEX_DATA_STRIDE_BYTES, squareVertex); - GLES20.glEnableVertexAttribArray(aTextureHandle); - - squareVertexObject.position(SQUARE_VERTEX_DATA_POS_OFFSET); - GLES20.glVertexAttribPointer(aTextureObjectHandle, 2, GLES20.GL_FLOAT, false, - 2 * FLOAT_SIZE_BYTES, squareVertexObject); - GLES20.glEnableVertexAttribArray(aTextureObjectHandle); - - GLES20.glUniformMatrix4fv(uMVPMatrixHandle, 1, false, MVPMatrix, 0); - GLES20.glUniformMatrix4fv(uSTMatrixHandle, 1, false, STMatrix, 0); - //Sampler - GLES20.glUniform1i(uSamplerHandle, 0); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, previousTexId); - //Object - GLES20.glUniform1i(uObjectHandle, 1); - GLES20.glActiveTexture(GLES20.GL_TEXTURE1); - } - - @Override - protected void disableResources() { - GlUtil.disableResources(aTextureHandle, aTextureObjectHandle, aPositionHandle); - } - - @Override - public void release() { - GLES20.glDeleteProgram(program); - releaseTexture(); - sprite.reset(); - } - - private void releaseTexture() { - GLES20.glDeleteTextures(streamObjectTextureId.length, streamObjectTextureId, 0); - streamObjectTextureId = new int[] { -1 }; - } - - public void setAlpha(float alpha) { - this.alpha = alpha; - } - - public void setScale(float scaleX, float scaleY) { - sprite.scale(scaleX, scaleY); - squareVertexObject.put(sprite.getTransformedVertices()).position(0); - } - - public void setPosition(float x, float y) { - sprite.translate(x, y); - squareVertexObject.put(sprite.getTransformedVertices()).position(0); - } - - public void setPosition(TranslateTo positionTo) { - sprite.translate(positionTo); - squareVertexObject.put(sprite.getTransformedVertices()).position(0); - } - - public PointF getScale() { - return sprite.getScale(); - } - - public PointF getPosition() { - return sprite.getTranslation(); - } - - public void setRotation(int angle) { - sprite.setRotation(angle); - } - - public int getRotation() { - return sprite.getRotation(); - } - - public void setDefaultScale(int streamWidth, int streamHeight) { - sprite.scale(streamObject.getWidth() * 100 / streamWidth, - streamObject.getHeight() * 100 / streamHeight); - squareVertexObject.put(sprite.getTransformedVertices()).position(0); - } - - public PointF getOriginalScale() { - if (streamObject != null) { - return new PointF(streamObject.getWidth(), streamObject.getHeight()); - } else { - return new PointF(0, 0); - } - } -} diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.kt b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.kt new file mode 100644 index 000000000..665d2c4fa --- /dev/null +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.kt @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2024 pedroSG94. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.pedro.encoder.input.gl.render.filters.`object` + +import android.content.Context +import android.graphics.PointF +import android.opengl.GLES20 +import android.opengl.Matrix +import android.os.Build +import androidx.annotation.RequiresApi +import com.pedro.encoder.R +import com.pedro.encoder.input.gl.Sprite +import com.pedro.encoder.input.gl.TextureLoader +import com.pedro.encoder.input.gl.render.filters.BaseFilterRender +import com.pedro.encoder.utils.gl.GlUtil +import com.pedro.encoder.utils.gl.StreamObjectBase +import com.pedro.encoder.utils.gl.TranslateTo +import java.nio.ByteBuffer +import java.nio.ByteOrder +import java.nio.FloatBuffer + +/** + * Created by pedro on 03/08/18. + */ +@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) +abstract class BaseObjectFilterRender : BaseFilterRender() { + //rotation matrix + private val squareVertexDataFilter = floatArrayOf( + // X, Y, Z, U, V + -1f, -1f, 0f, 0f, 0f, //bottom left + 1f, -1f, 0f, 1f, 0f, //bottom right + -1f, 1f, 0f, 0f, 1f, //top left + 1f, 1f, 0f, 1f, 1f, //top right + ) + + private var program = -1 + private var aPositionHandle = -1 + private var aTextureHandle = -1 + private var aTextureObjectHandle = -1 + private var uMVPMatrixHandle = -1 + private var uSTMatrixHandle = -1 + private var uSamplerHandle = -1 + private var uObjectHandle = -1 + protected var uAlphaHandle: Int = -1 + + protected var fragment: Int = R.raw.object_fragment + private val squareVertexObject: FloatBuffer + + protected var streamObjectTextureId: IntArray = intArrayOf(-1) + protected var textureLoader: TextureLoader = TextureLoader() + protected var streamObject: StreamObjectBase? = null + private val sprite = Sprite() + var alpha: Float = 1f + protected var shouldLoad: Boolean = false + + init { + squareVertex = ByteBuffer.allocateDirect(squareVertexDataFilter.size * FLOAT_SIZE_BYTES) + .order(ByteOrder.nativeOrder()) + .asFloatBuffer() + squareVertex.put(squareVertexDataFilter).position(0) + val vertices = sprite.getTransformedVertices() + squareVertexObject = ByteBuffer.allocateDirect(vertices.size * FLOAT_SIZE_BYTES) + .order(ByteOrder.nativeOrder()) + .asFloatBuffer() + squareVertexObject.put(vertices).position(0) + Matrix.setIdentityM(MVPMatrix, 0) + Matrix.setIdentityM(STMatrix, 0) + } + + override fun initGlFilter(context: Context) { + val vertexShader = GlUtil.getStringFromRaw(context, R.raw.object_vertex) + val fragmentShader = GlUtil.getStringFromRaw(context, fragment) + + program = GlUtil.createProgram(vertexShader, fragmentShader) + aPositionHandle = GLES20.glGetAttribLocation(program, "aPosition") + aTextureHandle = GLES20.glGetAttribLocation(program, "aTextureCoord") + aTextureObjectHandle = GLES20.glGetAttribLocation(program, "aTextureObjectCoord") + uMVPMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix") + uSTMatrixHandle = GLES20.glGetUniformLocation(program, "uSTMatrix") + uSamplerHandle = GLES20.glGetUniformLocation(program, "uSampler") + uObjectHandle = GLES20.glGetUniformLocation(program, "uObject") + uAlphaHandle = GLES20.glGetUniformLocation(program, "uAlpha") + } + + override fun drawFilter() { + if (shouldLoad) { + releaseTexture() + streamObject?.bitmaps?.let { streamObjectTextureId = textureLoader.load(it) } + shouldLoad = false + } + + GLES20.glUseProgram(program) + + squareVertex.position(SQUARE_VERTEX_DATA_POS_OFFSET) + GLES20.glVertexAttribPointer( + aPositionHandle, 3, GLES20.GL_FLOAT, false, + SQUARE_VERTEX_DATA_STRIDE_BYTES, squareVertex + ) + GLES20.glEnableVertexAttribArray(aPositionHandle) + + squareVertex.position(SQUARE_VERTEX_DATA_UV_OFFSET) + GLES20.glVertexAttribPointer( + aTextureHandle, 2, GLES20.GL_FLOAT, false, + SQUARE_VERTEX_DATA_STRIDE_BYTES, squareVertex + ) + GLES20.glEnableVertexAttribArray(aTextureHandle) + + squareVertexObject.position(SQUARE_VERTEX_DATA_POS_OFFSET) + GLES20.glVertexAttribPointer( + aTextureObjectHandle, 2, GLES20.GL_FLOAT, false, + 2 * FLOAT_SIZE_BYTES, squareVertexObject + ) + GLES20.glEnableVertexAttribArray(aTextureObjectHandle) + + GLES20.glUniformMatrix4fv(uMVPMatrixHandle, 1, false, MVPMatrix, 0) + GLES20.glUniformMatrix4fv(uSTMatrixHandle, 1, false, STMatrix, 0) + //Sampler + GLES20.glUniform1i(uSamplerHandle, 0) + GLES20.glActiveTexture(GLES20.GL_TEXTURE0) + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, previousTexId) + //Object + GLES20.glUniform1i(uObjectHandle, 1) + GLES20.glActiveTexture(GLES20.GL_TEXTURE1) + } + + override fun disableResources() { + GlUtil.disableResources(aTextureHandle, aTextureObjectHandle, aPositionHandle) + } + + override fun release() { + GLES20.glDeleteProgram(program) + releaseTexture() + sprite.reset() + } + + private fun releaseTexture() { + GLES20.glDeleteTextures(streamObjectTextureId.size, streamObjectTextureId, 0) + streamObjectTextureId = intArrayOf(-1) + } + + fun setScale(scaleX: Float, scaleY: Float) { + sprite.scale(scaleX, scaleY) + squareVertexObject.put(sprite.getTransformedVertices()).position(0) + } + + fun setPosition(x: Float, y: Float) { + sprite.translate(x, y) + squareVertexObject.put(sprite.getTransformedVertices()).position(0) + } + + fun setPosition(positionTo: TranslateTo) { + sprite.translate(positionTo) + squareVertexObject.put(sprite.getTransformedVertices()).position(0) + } + + val scale: PointF + get() = sprite.scale + + val position: PointF + get() = sprite.translation + + var rotation: Int + get() = sprite.rotation + set(angle) { + sprite.setRotation(angle) + } +} diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/GifFilterRender.kt b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/GifFilterRender.kt index e457b7b2c..083a8df09 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/GifFilterRender.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/GifFilterRender.kt @@ -26,11 +26,12 @@ import java.io.InputStream * Created by pedro on 27/07/18. */ -@Deprecated("Use GifFilterRender instead") -typealias GifObjectFilterRender = GifFilterRender +@Deprecated("Use GifFilterRender instead", ReplaceWith("GifFilterRender")) +@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) +open class GifObjectFilterRender : GifFilterRender() @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) -class GifFilterRender : BaseObjectFilterRender() { +open class GifFilterRender : BaseObjectFilterRender() { init { streamObject = GifStreamObject() } diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/ImageFilterRender.kt b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/ImageFilterRender.kt index 6aa85a3ca..69791fb47 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/ImageFilterRender.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/ImageFilterRender.kt @@ -25,11 +25,12 @@ import com.pedro.encoder.utils.gl.ImageStreamObject * Created by pedro on 27/07/18. */ -@Deprecated("Use ImageFilterRender instead") -typealias ImageObjectFilterRender = ImageFilterRender +@Deprecated("Use ImageFilterRender instead", ReplaceWith("ImageFilterRender")) +@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) +open class ImageObjectFilterRender : ImageFilterRender() @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) -class ImageFilterRender : BaseObjectFilterRender() { +open class ImageFilterRender : BaseObjectFilterRender() { init { streamObject = ImageStreamObject() } diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/SurfaceFilterRender.kt b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/SurfaceFilterRender.kt index af79bcdc3..fca6c543d 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/SurfaceFilterRender.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/SurfaceFilterRender.kt @@ -31,9 +31,9 @@ import com.pedro.encoder.utils.gl.GlUtil * Created by pedro on 18/07/18. */ @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) -class SurfaceFilterRender @JvmOverloads constructor(private val surfaceReadyCallback: SurfaceReadyCallback? = null) : BaseObjectFilterRender() { - interface SurfaceReadyCallback { - fun surfaceReady(surfaceTexture: SurfaceTexture?) +open class SurfaceFilterRender @JvmOverloads constructor(private val surfaceReadyCallback: SurfaceReadyCallback? = null) : BaseObjectFilterRender() { + fun interface SurfaceReadyCallback { + fun surfaceReady(surfaceTexture: SurfaceTexture) } /** @@ -48,7 +48,7 @@ class SurfaceFilterRender @JvmOverloads constructor(private val surfaceReadyCall var surface: Surface? = null private set - override fun initGlFilter(context: Context?) { + override fun initGlFilter(context: Context) { fragment = R.raw.surface_fragment super.initGlFilter(context) GlUtil.createExternalTextures(streamObjectTextureId.size, streamObjectTextureId, 0) @@ -58,9 +58,7 @@ class SurfaceFilterRender @JvmOverloads constructor(private val surfaceReadyCall surface = Surface(surfaceTexture) surfaceReadyCallback?.let { Handler(Looper.getMainLooper()).post { - surfaceReadyCallback.surfaceReady( - surfaceTexture - ) + surfaceReadyCallback.surfaceReady(surfaceTexture) } } } diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/TextFilterRender.kt b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/TextFilterRender.kt index f7333d820..1616503e5 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/TextFilterRender.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/TextFilterRender.kt @@ -26,11 +26,12 @@ import com.pedro.encoder.utils.gl.TextStreamObject * Created by pedro on 27/07/18. */ -@Deprecated("Use TextFilterRender instead") -typealias TextObjectFilterRender = TextFilterRender +@Deprecated("Use TextFilterRender instead", ReplaceWith("TextFilterRender")) +@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) +open class TextObjectFilterRender : TextFilterRender() @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) -class TextFilterRender : BaseObjectFilterRender() { +open class TextFilterRender : BaseObjectFilterRender() { private var text: String = "" private var textSize = 0f private var textColor = 0 @@ -62,6 +63,10 @@ class TextFilterRender : BaseObjectFilterRender() { shouldLoad = true } + fun setText(text: String, textSize: Float, textColor: Int, typeface: Typeface?) { + setText(text, textSize, textColor, Color.TRANSPARENT, typeface) + } + fun addText(text: String) { setText(this.text + text, textSize, textColor, backgroundColor, typeface) } From 971a6de90253571960c795d579b4b720512e110c Mon Sep 17 00:00:00 2001 From: pedroSG94 Date: Fri, 24 Jul 2026 22:37:56 +0200 Subject: [PATCH 04/10] fix filters rotation --- .../encoder/input/gl/render/filters/AndroidViewFilterRender.kt | 1 - .../input/gl/render/filters/object/BaseObjectFilterRender.kt | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt index 896756882..8aca84abc 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt @@ -221,7 +221,6 @@ class AndroidViewFilterRender : BaseFilterRender() { continue } - sprite.calculateDefaultScale(previewWidth.toFloat(), previewHeight.toFloat()) val canvasPosition = sprite.getCanvasPosition(previewWidth.toFloat(), previewHeight.toFloat()) val canvasScale = sprite.getCanvasScale(previewWidth.toFloat(), previewHeight.toFloat()) val rotationAxis = sprite.rotationAxis diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.kt b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.kt index 665d2c4fa..3a7573dfd 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.kt @@ -175,6 +175,7 @@ abstract class BaseObjectFilterRender : BaseFilterRender() { var rotation: Int get() = sprite.rotation set(angle) { - sprite.setRotation(angle) + sprite.rotation = angle + squareVertexObject.put(sprite.getTransformedVertices()).position(0) } } From 36fde6128cb7590815a6ece15418ff12daf5de71 Mon Sep 17 00:00:00 2001 From: pedroSG94 Date: Fri, 24 Jul 2026 22:47:50 +0200 Subject: [PATCH 05/10] unify AndroidViewFilterRender sprite --- .../encoder/input/gl/AndroidViewSprite.java | 173 ------------------ .../render/filters/AndroidViewFilterRender.kt | 34 +++- 2 files changed, 25 insertions(+), 182 deletions(-) delete mode 100644 encoder/src/main/java/com/pedro/encoder/input/gl/AndroidViewSprite.java diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/AndroidViewSprite.java b/encoder/src/main/java/com/pedro/encoder/input/gl/AndroidViewSprite.java deleted file mode 100644 index 62e6db744..000000000 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/AndroidViewSprite.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2024 pedroSG94. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.pedro.encoder.input.gl; - -import android.graphics.PointF; -import android.view.View; - -import com.pedro.encoder.utils.gl.TranslateTo; - -/** - * Created by pedro on 3/2/22. - */ -public class AndroidViewSprite { - - private PointF scale; - private PointF position; - private PointF rotationAxis; - private int rotation; - private View view; - - public AndroidViewSprite() { - reset(); - } - - public void setView(View view) { - this.view = view; - rotationAxis = new PointF(view.getMeasuredWidth() / 2f, view.getMeasuredHeight() / 2f); - } - - /** - * @param deltaX Position x in percent - * @param deltaY Position x in percent - */ - public void translate(float deltaX, float deltaY) { - position.x = deltaX; - position.y = deltaY; - } - - /** - * @param translation Predefined position - */ - public void translate(TranslateTo translation) { - switch (translation) { - case CENTER: - this.position.x = 50f - scale.x / 2f; - this.position.y = 50f - scale.y / 2f; - break; - case BOTTOM: - this.position.x = 50f - scale.x / 2f; - this.position.y = 100f - scale.y; - break; - case TOP: - this.position.x = 50f - scale.x / 2f; - this.position.y = 0f; - break; - case LEFT: - this.position.x = 0f; - this.position.y = 50f - scale.y / 2f; - break; - case RIGHT: - this.position.x = 100f - scale.x; - this.position.y = 50f - scale.y / 2f; - break; - case TOP_LEFT: - this.position.x = 0f; - this.position.y = 0f; - break; - case TOP_RIGHT: - this.position.x = 100f - scale.x; - this.position.y = 0f; - break; - case BOTTOM_LEFT: - this.position.x = 0f; - this.position.y = 100f - scale.y; - break; - case BOTTOM_RIGHT: - this.position.x = 100f - scale.x; - this.position.y = 100f - scale.y; - break; - default: - break; - } - } - - /** - * @param deltaX Scale x in percent - * @param deltaY Scale y in percent - */ - public void scale(float deltaX, float deltaY) { - //keep old position - position.x /= deltaX / scale.x; - position.y /= deltaY / scale.y; - //set new scale. - scale = new PointF(deltaX, deltaY); - } - - /** - * @return Scale in percent - */ - public PointF getScale() { - return scale; - } - - /** - * @return Position in percent - */ - public PointF getTranslation() { - return position; - } - - public int getRotation() { - return rotation; - } - - public PointF getRotationAxis() { - return rotationAxis; - } - - public void setRotation(int rotation) { - if (rotation < 0) { - this.rotation = 0; - } else if (rotation > 360) { - this.rotation = 360; - } else { - this.rotation = rotation; - } - } - - public void reset() { - scale = new PointF(0f, 0f); - position = new PointF(0f, 0f); - } - - /** - * Traduce position in percent to work with canvas. - */ - public PointF getCanvasPosition(float previewX, float previewY) { - return new PointF(previewX * position.x / 100f, previewY * position.y / 100f); - } - - /** - * Traduce scale in percent to work with canvas. - */ - public PointF getCanvasScale(float previewX, float previewY) { - float scaleFactorX = 100f * (float) view.getWidth() / previewX; - float scaleFactorY = 100f * (float) view.getHeight() / previewY; - return new PointF(scale.x / scaleFactorX, scale.y / scaleFactorY); - } - - /** - * Calculate default scale if none is indicated after load the filter. - */ - public void calculateDefaultScale(float previewX, float previewY) { - if (scale.x == 0f && scale.y == 0f) { - scale.x = 100f * (float) view.getWidth() / previewX; - scale.y = 100f * (float) view.getHeight() / previewY; - } - } -} diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt index 8aca84abc..8a597752c 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt @@ -30,7 +30,7 @@ import android.view.View import androidx.annotation.RequiresApi import com.pedro.common.TimeUtils import com.pedro.encoder.R -import com.pedro.encoder.input.gl.AndroidViewSprite +import com.pedro.encoder.input.gl.Sprite import com.pedro.encoder.utils.gl.GlUtil import com.pedro.encoder.utils.gl.TranslateTo import kotlinx.coroutines.CoroutineScope @@ -75,7 +75,6 @@ class AndroidViewFilterRender : BaseFilterRender() { field = value value?.let { it.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED) - sprite.setView(it) startRender() } } @@ -88,7 +87,7 @@ class AndroidViewFilterRender : BaseFilterRender() { */ var isHardwareMode: Boolean = true get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && field - private val sprite: AndroidViewSprite + private val sprite = Sprite() private val frameAvailable = AtomicBoolean(false) @Volatile var targetFps = 30 @@ -104,7 +103,6 @@ class AndroidViewFilterRender : BaseFilterRender() { squareVertex.put(squareVertexDataFilter).position(0) Matrix.setIdentityM(MVPMatrix, 0) Matrix.setIdentityM(STMatrix, 0) - sprite = AndroidViewSprite() } override fun initGlFilter(context: Context) { @@ -195,9 +193,28 @@ class AndroidViewFilterRender : BaseFilterRender() { var rotation: Int get() = sprite.rotation set(rotation) { - sprite.setRotation(rotation) + sprite.rotation = rotation } + /** + * Translate position in percent to work with canvas. + */ + private fun getCanvasPosition(): PointF { + val position = sprite.translation + return PointF(previewWidth * position.x / 100f, previewHeight * position.y / 100f) + } + + /** + * Translate scale in percent to work with canvas. + */ + private fun getCanvasScale(view: View): PointF { + if (view.width <= 0 || view.height <= 0) return PointF(0f, 0f) + val scale = sprite.scale + val scaleFactorX = 100f * view.width / previewWidth + val scaleFactorY = 100f * view.height / previewHeight + return PointF(scale.x / scaleFactorX, scale.y / scaleFactorY) + } + private fun startRender() { job = CoroutineScope(Dispatchers.IO).launch { while (isActive) { @@ -221,15 +238,14 @@ class AndroidViewFilterRender : BaseFilterRender() { continue } - val canvasPosition = sprite.getCanvasPosition(previewWidth.toFloat(), previewHeight.toFloat()) - val canvasScale = sprite.getCanvasScale(previewWidth.toFloat(), previewHeight.toFloat()) - val rotationAxis = sprite.rotationAxis + val canvasPosition = getCanvasPosition() + val canvasScale = getCanvasScale(view) val rotation = sprite.rotation canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR) canvas.translate(canvasPosition.x, canvasPosition.y) canvas.scale(canvasScale.x, canvasScale.y) - canvas.rotate(rotation.toFloat(), rotationAxis.x, rotationAxis.y) + canvas.rotate(rotation.toFloat(), view.width / 2f, view.height / 2f) try { view.draw(canvas) From ca7968d6265aeae8d0ba7e0c3d1114d97d2ec8c2 Mon Sep 17 00:00:00 2001 From: pedroSG94 Date: Fri, 24 Jul 2026 22:53:45 +0200 Subject: [PATCH 06/10] fix androidviewfilter rotation --- .../encoder/input/gl/render/filters/AndroidViewFilterRender.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt index 8a597752c..b32703615 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt @@ -193,7 +193,8 @@ class AndroidViewFilterRender : BaseFilterRender() { var rotation: Int get() = sprite.rotation set(rotation) { - sprite.rotation = rotation + //revert rotation to rotate exactly like object filters + sprite.rotation = -rotation } /** From 80904b6f58231ef54960dc3c99d70565f2c55dc2 Mon Sep 17 00:00:00 2001 From: pedroSG94 Date: Fri, 24 Jul 2026 23:15:15 +0200 Subject: [PATCH 07/10] refactor spritegesturecontroller --- .../com/pedro/streamer/utils/FilterMenu.kt | 8 +- .../com/pedro/encoder/input/gl/Sprite.java | 20 ++++- .../input/gl/SpriteGestureController.java | 80 +++++-------------- .../render/filters/AndroidViewFilterRender.kt | 2 +- .../filters/object/BaseObjectFilterRender.kt | 9 +-- 5 files changed, 46 insertions(+), 73 deletions(-) diff --git a/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt b/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt index a80b72b03..5cd124a63 100644 --- a/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt +++ b/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt @@ -212,7 +212,7 @@ class FilterMenu(private val context: Context) { glInterface.setFilter(gifObjectFilterRender) gifObjectFilterRender.setScale(50f, 50f) gifObjectFilterRender.setPosition(TranslateTo.BOTTOM) - spriteGestureController.setBaseObjectFilterRender(gifObjectFilterRender) //Optional + spriteGestureController.setSprite(gifObjectFilterRender.sprite) //Optional } catch (_: IOException) { } return true } @@ -232,7 +232,7 @@ class FilterMenu(private val context: Context) { ) imageObjectFilterRender.setScale(50f, 50f) imageObjectFilterRender.setPosition(TranslateTo.CENTER) - spriteGestureController.setBaseObjectFilterRender(imageObjectFilterRender) //Optional + spriteGestureController.setSprite(imageObjectFilterRender.sprite) //Optional spriteGestureController.setPreventMoveOutside(false) //Optional return true } @@ -316,7 +316,7 @@ class FilterMenu(private val context: Context) { glInterface.setFilter(surfaceFilterRender) //Video is 360x240 so select a percent to keep aspect ratio (50% x 33.3% screen) surfaceFilterRender.setScale(50f, 33.3f) - spriteGestureController.setBaseObjectFilterRender(surfaceFilterRender) //Optional + spriteGestureController.setSprite(surfaceFilterRender.sprite) //Optional return true } R.id.temperature -> { @@ -329,7 +329,7 @@ class FilterMenu(private val context: Context) { textObjectFilterRender.setText("Hello world", 22f, Color.RED) textObjectFilterRender.setScale(50f, 50f) textObjectFilterRender.setPosition(TranslateTo.CENTER) - spriteGestureController.setBaseObjectFilterRender(textObjectFilterRender) //Optional + spriteGestureController.setSprite(textObjectFilterRender.sprite) //Optional return true } R.id.zebra -> { diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/Sprite.java b/encoder/src/main/java/com/pedro/encoder/input/gl/Sprite.java index 1a6eb9305..62c134b0d 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/Sprite.java +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/Sprite.java @@ -50,6 +50,7 @@ public class Sprite { private PointF scale; private PointF position; private int rotation; + private volatile boolean dirty = true; public Sprite() { reset(); @@ -62,6 +63,7 @@ public Sprite() { public void translate(float deltaX, float deltaY) { position.x = deltaX; position.y = deltaY; + dirty = true; } /** @@ -108,6 +110,7 @@ public void translate(TranslateTo translation) { default: break; } + dirty = true; } /** @@ -120,24 +123,36 @@ public void scale(float deltaX, float deltaY) { position.y /= deltaY / scale.y; //set new scale. scale = new PointF(deltaX, deltaY); + dirty = true; } public void setRotation(int angle) { rotation = angle; + dirty = true; + } + + /** + * @return true if the sprite changed since the last call. Used to know if the vertices related + * with this sprite must be calculated again. + */ + public boolean consumeDirty() { + boolean dirty = this.dirty; + this.dirty = false; + return dirty; } /** * @return Scale in percent */ public PointF getScale() { - return scale; + return new PointF(scale.x, scale.y); } /** * @return Position in percent */ public PointF getTranslation() { - return position; + return new PointF(position.x, position.y); } public int getRotation() { @@ -148,6 +163,7 @@ public void reset() { scale = new PointF(100f, 100f); position = new PointF(0f, 0f); rotation = 0; + dirty = true; } /** diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/SpriteGestureController.java b/encoder/src/main/java/com/pedro/encoder/input/gl/SpriteGestureController.java index 2dc3b6c11..b39486a72 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/SpriteGestureController.java +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/SpriteGestureController.java @@ -35,39 +35,23 @@ @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) public class SpriteGestureController { - private BaseObjectFilterRender baseObjectFilterRender; - private AndroidViewFilterRender androidViewFilterRender; + private Sprite sprite; private float lastDistance; private boolean preventMoveOutside = true; public SpriteGestureController() { } - public SpriteGestureController(BaseObjectFilterRender sprite) { - this.baseObjectFilterRender = sprite; + public SpriteGestureController(Sprite sprite) { + this.sprite = sprite; } - public SpriteGestureController(AndroidViewFilterRender sprite) { - this.androidViewFilterRender = sprite; - } - - public BaseFilterRender getFilterRender() { - return androidViewFilterRender == null ? baseObjectFilterRender : androidViewFilterRender; - } - - public void setBaseObjectFilterRender(BaseObjectFilterRender baseObjectFilterRender) { - this.baseObjectFilterRender = baseObjectFilterRender; - this.androidViewFilterRender = null; - } - - public void setBaseObjectFilterRender(AndroidViewFilterRender androidViewFilterRender) { - this.androidViewFilterRender = androidViewFilterRender; - this.baseObjectFilterRender = null; + public void setSprite(Sprite sprite) { + this.sprite = sprite; } public void stopListener() { - this.androidViewFilterRender = null; - this.baseObjectFilterRender = null; + sprite = null; } public void setPreventMoveOutside(boolean preventMoveOutside) { @@ -75,34 +59,24 @@ public void setPreventMoveOutside(boolean preventMoveOutside) { } public boolean spriteTouched(View view, MotionEvent motionEvent) { - if (baseObjectFilterRender == null && androidViewFilterRender == null) return false; + Sprite sprite = this.sprite; + if (sprite == null) return false; float xPercent = motionEvent.getX() * 100 / view.getWidth(); float yPercent = motionEvent.getY() * 100 / view.getHeight(); - PointF scale; - PointF position; - if (baseObjectFilterRender != null) { - scale = baseObjectFilterRender.getScale(); - position = baseObjectFilterRender.getPosition(); - } else { - scale = androidViewFilterRender.getScale(); - position = androidViewFilterRender.getPosition(); - } + PointF scale = sprite.getScale(); + PointF position = sprite.getTranslation(); boolean xTouched = xPercent >= position.x && xPercent <= position.x + scale.x; boolean yTouched = yPercent >= position.y && yPercent <= position.y + scale.y; return xTouched && yTouched; } public void moveSprite(View view, MotionEvent motionEvent) { - if (baseObjectFilterRender == null && androidViewFilterRender == null) return; + Sprite sprite = this.sprite; + if (sprite == null) return; if (motionEvent.getPointerCount() == 1) { float xPercent = motionEvent.getX() * 100 / view.getWidth(); float yPercent = motionEvent.getY() * 100 / view.getHeight(); - PointF scale; - if (baseObjectFilterRender != null) { - scale = baseObjectFilterRender.getScale(); - } else { - scale = androidViewFilterRender.getScale(); - } + PointF scale = sprite.getScale(); if (preventMoveOutside) { float x = xPercent - scale.x / 2.0F; float y = yPercent - scale.y / 2.0F; @@ -118,39 +92,23 @@ public void moveSprite(View view, MotionEvent motionEvent) { if (y + scale.y > 100.0F) { y = 100.0F - scale.y; } - if (baseObjectFilterRender != null) { - baseObjectFilterRender.setPosition(x, y); - } else { - androidViewFilterRender.setPosition(x, y); - } + sprite.translate(x, y); } else { - if (baseObjectFilterRender != null) { - baseObjectFilterRender.setPosition(xPercent - scale.x / 2f, yPercent - scale.y / 2f); - } else { - androidViewFilterRender.setPosition(xPercent - scale.x / 2f, yPercent - scale.y / 2f); - } + sprite.translate(xPercent - scale.x / 2f, yPercent - scale.y / 2f); } } } public void scaleSprite(MotionEvent motionEvent) { - if (baseObjectFilterRender == null && androidViewFilterRender == null) return; + Sprite sprite = this.sprite; + if (sprite == null) return; if (motionEvent.getPointerCount() > 1) { float distance = CameraHelper.getFingerSpacing(motionEvent); float percent = distance >= lastDistance ? 1 : -1; - PointF scale; - if (baseObjectFilterRender != null) { - scale = baseObjectFilterRender.getScale(); - } else { - scale = androidViewFilterRender.getScale(); - } + PointF scale = sprite.getScale(); float newScaleX = scale.x + percent; float newScaleY = scale.y + percent; - if (baseObjectFilterRender != null) { - baseObjectFilterRender.setScale(newScaleX, newScaleY); - } else { - androidViewFilterRender.setScale(newScaleX, newScaleY); - } + sprite.scale(newScaleX, newScaleY); lastDistance = distance; } } diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt index b32703615..0dd20e386 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt @@ -87,7 +87,7 @@ class AndroidViewFilterRender : BaseFilterRender() { */ var isHardwareMode: Boolean = true get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && field - private val sprite = Sprite() + val sprite = Sprite() private val frameAvailable = AtomicBoolean(false) @Volatile var targetFps = 30 diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.kt b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.kt index 3a7573dfd..99f08d0e0 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.kt @@ -62,7 +62,7 @@ abstract class BaseObjectFilterRender : BaseFilterRender() { protected var streamObjectTextureId: IntArray = intArrayOf(-1) protected var textureLoader: TextureLoader = TextureLoader() protected var streamObject: StreamObjectBase? = null - private val sprite = Sprite() + val sprite = Sprite() var alpha: Float = 1f protected var shouldLoad: Boolean = false @@ -96,6 +96,9 @@ abstract class BaseObjectFilterRender : BaseFilterRender() { } override fun drawFilter() { + if (sprite.consumeDirty()) { + squareVertexObject.put(sprite.getTransformedVertices()).position(0) + } if (shouldLoad) { releaseTexture() streamObject?.bitmaps?.let { streamObjectTextureId = textureLoader.load(it) } @@ -153,17 +156,14 @@ abstract class BaseObjectFilterRender : BaseFilterRender() { fun setScale(scaleX: Float, scaleY: Float) { sprite.scale(scaleX, scaleY) - squareVertexObject.put(sprite.getTransformedVertices()).position(0) } fun setPosition(x: Float, y: Float) { sprite.translate(x, y) - squareVertexObject.put(sprite.getTransformedVertices()).position(0) } fun setPosition(positionTo: TranslateTo) { sprite.translate(positionTo) - squareVertexObject.put(sprite.getTransformedVertices()).position(0) } val scale: PointF @@ -176,6 +176,5 @@ abstract class BaseObjectFilterRender : BaseFilterRender() { get() = sprite.rotation set(angle) { sprite.rotation = angle - squareVertexObject.put(sprite.getTransformedVertices()).position(0) } } From e3e9ef89f100367e5956be5f6085119e971109e4 Mon Sep 17 00:00:00 2001 From: pedroSG94 Date: Fri, 24 Jul 2026 23:21:37 +0200 Subject: [PATCH 08/10] androidview filter to view filter --- .../com/pedro/streamer/utils/FilterMenu.kt | 44 +++++++++---------- .../input/gl/SpriteGestureController.java | 3 -- ...iewFilterRender.kt => ViewFilterRender.kt} | 11 +++-- 3 files changed, 30 insertions(+), 28 deletions(-) rename encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/{AndroidViewFilterRender.kt => ViewFilterRender.kt} (96%) diff --git a/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt b/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt index 5cd124a63..f2da6a46d 100644 --- a/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt +++ b/app/src/main/java/com/pedro/streamer/utils/FilterMenu.kt @@ -28,7 +28,7 @@ import android.view.View import androidx.annotation.RequiresApi import com.pedro.encoder.input.gl.SpriteGestureController import com.pedro.encoder.input.gl.render.filters.AnalogTVFilterRender -import com.pedro.encoder.input.gl.render.filters.AndroidViewFilterRender +import com.pedro.encoder.input.gl.render.filters.ViewFilterRender import com.pedro.encoder.input.gl.render.filters.BasicDeformationFilterRender import com.pedro.encoder.input.gl.render.filters.BeautyFilterRender import com.pedro.encoder.input.gl.render.filters.BlackFilterRender @@ -113,9 +113,9 @@ class FilterMenu(private val context: Context) { //Set view size to allow rendering view.measure(sizeSpecWidth, sizeSpecHeight) view.layout(0, 0, previewSize.x, previewSize.y) - val androidViewFilterRender = AndroidViewFilterRender() - androidViewFilterRender.view = view - glInterface.setFilter(androidViewFilterRender) + val viewFilterRender = ViewFilterRender() + viewFilterRender.view = view + glInterface.setFilter(viewFilterRender) return true } R.id.basic_deformation -> { @@ -207,12 +207,12 @@ class FilterMenu(private val context: Context) { } R.id.gif -> { try { - val gifObjectFilterRender = GifFilterRender() - gifObjectFilterRender.setGif(context.resources.openRawResource(R.raw.banana)) - glInterface.setFilter(gifObjectFilterRender) - gifObjectFilterRender.setScale(50f, 50f) - gifObjectFilterRender.setPosition(TranslateTo.BOTTOM) - spriteGestureController.setSprite(gifObjectFilterRender.sprite) //Optional + val gifFilterRender = GifFilterRender() + gifFilterRender.setGif(context.resources.openRawResource(R.raw.banana)) + glInterface.setFilter(gifFilterRender) + gifFilterRender.setScale(50f, 50f) + gifFilterRender.setPosition(TranslateTo.BOTTOM) + spriteGestureController.setSprite(gifFilterRender.sprite) //Optional } catch (_: IOException) { } return true } @@ -225,14 +225,14 @@ class FilterMenu(private val context: Context) { return true } R.id.image -> { - val imageObjectFilterRender = ImageFilterRender() - glInterface.setFilter(imageObjectFilterRender) - imageObjectFilterRender.setImage( + val imageFilterRender = ImageFilterRender() + glInterface.setFilter(imageFilterRender) + imageFilterRender.setImage( BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher) ) - imageObjectFilterRender.setScale(50f, 50f) - imageObjectFilterRender.setPosition(TranslateTo.CENTER) - spriteGestureController.setSprite(imageObjectFilterRender.sprite) //Optional + imageFilterRender.setScale(50f, 50f) + imageFilterRender.setPosition(TranslateTo.CENTER) + spriteGestureController.setSprite(imageFilterRender.sprite) //Optional spriteGestureController.setPreventMoveOutside(false) //Optional return true } @@ -324,12 +324,12 @@ class FilterMenu(private val context: Context) { return true } R.id.text -> { - val textObjectFilterRender = TextFilterRender() - glInterface.setFilter(textObjectFilterRender) - textObjectFilterRender.setText("Hello world", 22f, Color.RED) - textObjectFilterRender.setScale(50f, 50f) - textObjectFilterRender.setPosition(TranslateTo.CENTER) - spriteGestureController.setSprite(textObjectFilterRender.sprite) //Optional + val textFilterRender = TextFilterRender() + glInterface.setFilter(textFilterRender) + textFilterRender.setText("Hello world", 22f, Color.RED) + textFilterRender.setScale(50f, 50f) + textFilterRender.setPosition(TranslateTo.CENTER) + spriteGestureController.setSprite(textFilterRender.sprite) //Optional return true } R.id.zebra -> { diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/SpriteGestureController.java b/encoder/src/main/java/com/pedro/encoder/input/gl/SpriteGestureController.java index b39486a72..e7c94255a 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/SpriteGestureController.java +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/SpriteGestureController.java @@ -23,9 +23,6 @@ import androidx.annotation.RequiresApi; -import com.pedro.encoder.input.gl.render.filters.AndroidViewFilterRender; -import com.pedro.encoder.input.gl.render.filters.BaseFilterRender; -import com.pedro.encoder.input.gl.render.filters.object.BaseObjectFilterRender; import com.pedro.encoder.input.video.CameraHelper; /** diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/ViewFilterRender.kt similarity index 96% rename from encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt rename to encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/ViewFilterRender.kt index 0dd20e386..301ae30dd 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/AndroidViewFilterRender.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/ViewFilterRender.kt @@ -49,8 +49,13 @@ import kotlin.time.Duration.Companion.milliseconds /** * Created by pedro on 4/02/18. */ + +@Deprecated("Use ViewFilterRender instead", ReplaceWith("ViewFilterRender")) +@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) +open class AndroidViewFilterRender : ViewFilterRender() + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) -class AndroidViewFilterRender : BaseFilterRender() { +open class ViewFilterRender : BaseFilterRender() { //rotation matrix private val squareVertexDataFilter = floatArrayOf( // X, Y, Z, U, V @@ -222,8 +227,8 @@ class AndroidViewFilterRender : BaseFilterRender() { val sleepRate = 1000 / targetFps val startTimestamp = TimeUtils.getCurrentTimeMillis() - val surface = this@AndroidViewFilterRender.surface - val view = this@AndroidViewFilterRender.view + val surface = this@ViewFilterRender.surface + val view = this@ViewFilterRender.view if (surface == null || view == null) { val sleep = sleepRate - (TimeUtils.getCurrentTimeMillis() - startTimestamp) delay(sleep.milliseconds) From 095f93c123208a63adf308740609cc2e549837f4 Mon Sep 17 00:00:00 2001 From: pedroSG94 Date: Fri, 24 Jul 2026 23:40:58 +0200 Subject: [PATCH 09/10] fix view filter rotation and add setview kdoc --- .../gl/render/filters/ViewFilterRender.kt | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/ViewFilterRender.kt b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/ViewFilterRender.kt index 301ae30dd..fd14999e9 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/ViewFilterRender.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/ViewFilterRender.kt @@ -74,14 +74,21 @@ open class ViewFilterRender : BaseFilterRender() { private var uSamplerViewHandle = -1 private var viewId = intArrayOf(-1) + /** + * The view to render. This filter never measures or layouts the view so you must do it before + * set it here, in other case nothing is rendered. + * Example using the encoder resolution to render the view in fullscreen: + * + * val widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY) + * val heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY) + * view.measure(widthSpec, heightSpec) + * view.layout(0, 0, width, height) + */ var view: View? = null set(value) { stopRender() field = value - value?.let { - it.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED) - startRender() - } + value?.let { startRender() } } private var surfaceTexture: SurfaceTexture? = null private var surface: Surface? = null @@ -198,8 +205,7 @@ open class ViewFilterRender : BaseFilterRender() { var rotation: Int get() = sprite.rotation set(rotation) { - //revert rotation to rotate exactly like object filters - sprite.rotation = -rotation + sprite.rotation = rotation } /** @@ -246,7 +252,8 @@ open class ViewFilterRender : BaseFilterRender() { val canvasPosition = getCanvasPosition() val canvasScale = getCanvasScale(view) - val rotation = sprite.rotation + //revert rotation to rotate exactly like object filters + val rotation = -sprite.rotation canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR) canvas.translate(canvasPosition.x, canvasPosition.y) From 3b8fd8df721f41c1eb5ca0c0bf3ef2943f32ce13 Mon Sep 17 00:00:00 2001 From: pedroSG94 Date: Fri, 24 Jul 2026 23:49:26 +0200 Subject: [PATCH 10/10] get back java compatibility on baseobject --- .../render/filters/object/BaseObjectFilterRender.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.kt b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.kt index 99f08d0e0..e56b0bf14 100644 --- a/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.kt +++ b/encoder/src/main/java/com/pedro/encoder/input/gl/render/filters/object/BaseObjectFilterRender.kt @@ -54,17 +54,17 @@ abstract class BaseObjectFilterRender : BaseFilterRender() { private var uSTMatrixHandle = -1 private var uSamplerHandle = -1 private var uObjectHandle = -1 - protected var uAlphaHandle: Int = -1 + @JvmField protected var uAlphaHandle: Int = -1 - protected var fragment: Int = R.raw.object_fragment + @JvmField protected var fragment: Int = R.raw.object_fragment private val squareVertexObject: FloatBuffer - protected var streamObjectTextureId: IntArray = intArrayOf(-1) - protected var textureLoader: TextureLoader = TextureLoader() - protected var streamObject: StreamObjectBase? = null + @JvmField protected var streamObjectTextureId: IntArray = intArrayOf(-1) + @JvmField protected var textureLoader: TextureLoader = TextureLoader() + @JvmField protected var streamObject: StreamObjectBase? = null val sprite = Sprite() var alpha: Float = 1f - protected var shouldLoad: Boolean = false + @JvmField protected var shouldLoad: Boolean = false init { squareVertex = ByteBuffer.allocateDirect(squareVertexDataFilter.size * FLOAT_SIZE_BYTES)