From fc642a003d417fd568f0ffa866c31b1142891190 Mon Sep 17 00:00:00 2001 From: Doikki Date: Wed, 17 Aug 2022 19:07:11 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=86=E9=A2=91=E6=BB=A4=E9=95=9C=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dkplayer/activity/api/PlayerActivity.kt | 27 +- .../doikki/dkplayer/app/MyApplication.java | 2 - .../dkplayer/widget/render/gl2/EglUtil.java | 133 +++++++ .../gl2/GLFrameBufferObjectRenderer.java | 77 ++++ .../render/gl2/GLFramebufferObject.java | 120 ++++++ .../render/gl2/GLSurfaceRenderView2.java | 91 +++++ .../widget/render/gl2/GLSurfaceTexture.java | 48 +++ .../widget/render/gl2/GLVideoRenderer.java | 193 +++++++++ .../widget/render/gl2/Resolution.java | 39 ++ .../render/gl2/chooser/GLConfigChooser.java | 135 +++++++ .../gl2/contextfactory/GLContextFactory.java | 38 ++ .../render/gl2/filter/GlBilateralFilter.java | 148 +++++++ .../render/gl2/filter/GlBoxBlurFilter.java | 95 +++++ .../render/gl2/filter/GlBrightnessFilter.java | 38 ++ .../gl2/filter/GlBulgeDistortionFilter.java | 80 ++++ .../gl2/filter/GlCGAColorspaceFilter.java | 52 +++ .../render/gl2/filter/GlContrastFilter.java | 41 ++ .../render/gl2/filter/GlCrosshatchFilter.java | 85 ++++ .../render/gl2/filter/GlExposureFilter.java | 38 ++ .../widget/render/gl2/filter/GlFilter.java | 157 ++++++++ .../render/gl2/filter/GlFilterGroup.java | 108 +++++ .../render/gl2/filter/GlGammaFilter.java | 36 ++ .../gl2/filter/GlGaussianBlurFilter.java | 100 +++++ .../render/gl2/filter/GlGrayScaleFilter.java | 20 + .../render/gl2/filter/GlHalftoneFilter.java | 53 +++ .../render/gl2/filter/GlHazeFilter.java | 54 +++ .../gl2/filter/GlHighlightShadowFilter.java | 49 +++ .../widget/render/gl2/filter/GlHueFilter.java | 67 ++++ .../render/gl2/filter/GlInvertFilter.java | 18 + .../gl2/filter/GlLookUpTableFilter.java | 92 +++++ .../render/gl2/filter/GlLuminanceFilter.java | 26 ++ .../filter/GlLuminanceThresholdFilter.java | 39 ++ .../render/gl2/filter/GlMonochromeFilter.java | 54 +++ .../render/gl2/filter/GlOpacityFilter.java | 40 ++ .../render/gl2/filter/GlOverlayFilter.java | 101 +++++ .../render/gl2/filter/GlPixelationFilter.java | 48 +++ .../render/gl2/filter/GlPosterizeFilter.java | 41 ++ .../render/gl2/filter/GlPreviewFilter.java | 74 ++++ .../widget/render/gl2/filter/GlRGBFilter.java | 84 ++++ .../render/gl2/filter/GlSaturationFilter.java | 41 ++ .../render/gl2/filter/GlSepiaFilter.java | 20 + .../render/gl2/filter/GlSharpenFilter.java | 96 +++++ .../render/gl2/filter/GlSolarizeFilter.java | 40 ++ .../gl2/filter/GlSphereRefractionFilter.java | 73 ++++ .../render/gl2/filter/GlSwirlFilter.java | 65 +++ .../GlThreex3TextureSamplingFilter.java | 83 ++++ .../render/gl2/filter/GlToneCurveFilter.java | 371 ++++++++++++++++++ .../render/gl2/filter/GlToneFilter.java | 85 ++++ .../render/gl2/filter/GlVibranceFilter.java | 37 ++ .../render/gl2/filter/GlVignetteFilter.java | 61 +++ .../render/gl2/filter/GlWatermarkFilter.java | 47 +++ .../filter/GlWeakPixelInclusionFilter.java | 46 +++ .../gl2/filter/GlWhiteBalanceFilter.java | 57 +++ .../render/gl2/filter/GlZoomBlurFilter.java | 56 +++ .../src/main/res/values-en/strings.xml | 2 +- .../src/main/res/values/strings.xml | 2 +- 56 files changed, 3917 insertions(+), 6 deletions(-) create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/EglUtil.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLFrameBufferObjectRenderer.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLFramebufferObject.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLSurfaceRenderView2.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLSurfaceTexture.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLVideoRenderer.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/Resolution.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/chooser/GLConfigChooser.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/contextfactory/GLContextFactory.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlBilateralFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlBoxBlurFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlBrightnessFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlBulgeDistortionFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlCGAColorspaceFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlContrastFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlCrosshatchFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlExposureFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlFilterGroup.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlGammaFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlGaussianBlurFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlGrayScaleFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlHalftoneFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlHazeFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlHighlightShadowFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlHueFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlInvertFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlLookUpTableFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlLuminanceFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlLuminanceThresholdFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlMonochromeFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlOpacityFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlOverlayFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlPixelationFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlPosterizeFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlPreviewFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlRGBFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSaturationFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSepiaFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSharpenFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSolarizeFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSphereRefractionFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSwirlFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlThreex3TextureSamplingFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlToneCurveFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlToneFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlVibranceFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlVignetteFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlWatermarkFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlWeakPixelInclusionFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlWhiteBalanceFilter.java create mode 100644 dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlZoomBlurFilter.java diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/activity/api/PlayerActivity.kt b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/activity/api/PlayerActivity.kt index a6d8e83e..35f6ee12 100644 --- a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/activity/api/PlayerActivity.kt +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/activity/api/PlayerActivity.kt @@ -2,6 +2,7 @@ package xyz.doikki.dkplayer.activity.api import android.content.Context import android.content.Intent +import android.graphics.BitmapFactory import android.text.TextUtils import android.view.View import android.widget.EditText @@ -13,11 +14,17 @@ import xyz.doikki.dkplayer.util.IntentKeys import xyz.doikki.dkplayer.util.Utils import xyz.doikki.dkplayer.widget.component.DebugInfoView import xyz.doikki.dkplayer.widget.component.PlayerMonitor -import xyz.doikki.dkplayer.widget.render.gl.GLSurfaceRenderViewFactory +import xyz.doikki.dkplayer.widget.render.gl2.GLSurfaceRenderView2 +import xyz.doikki.dkplayer.widget.render.gl2.filter.GlFilterGroup +import xyz.doikki.dkplayer.widget.render.gl2.filter.GlSepiaFilter +import xyz.doikki.dkplayer.widget.render.gl2.filter.GlSharpenFilter +import xyz.doikki.dkplayer.widget.render.gl2.filter.GlWatermarkFilter import xyz.doikki.videocontroller.StandardVideoController import xyz.doikki.videocontroller.component.* import xyz.doikki.videoplayer.player.BaseVideoView import xyz.doikki.videoplayer.player.VideoView +import xyz.doikki.videoplayer.render.IRenderView +import xyz.doikki.videoplayer.render.RenderViewFactory import xyz.doikki.videoplayer.util.L /** @@ -26,6 +33,10 @@ import xyz.doikki.videoplayer.util.L */ class PlayerActivity : BaseActivity() { + private val renderView by lazy { + GLSurfaceRenderView2(this) + } + override fun getLayoutResId() = R.layout.activity_player override fun initView() { @@ -107,7 +118,19 @@ class PlayerActivity : BaseActivity() { // 临时切换RenderView, 如需全局请通过VideoConfig配置,详见MyApplication if (intent.getBooleanExtra(IntentKeys.CUSTOM_RENDER, false)) { - mVideoView.setRenderViewFactory(GLSurfaceRenderViewFactory.create()) +// mVideoView.setRenderViewFactory(GLSurfaceRenderViewFactory.create()) + mVideoView.setRenderViewFactory(object : RenderViewFactory() { + override fun createRenderView(context: Context?): IRenderView { + return renderView + } + }) + // 设置滤镜 + renderView.setGlFilter(GlFilterGroup( + // 水印 + GlWatermarkFilter(BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)), + GlSepiaFilter(), + GlSharpenFilter() + )) } //临时切换播放核心,如需全局请通过VideoConfig配置,详见MyApplication //使用IjkPlayer解码 diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/app/MyApplication.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/app/MyApplication.java index 753c3ba6..6d6d57ce 100644 --- a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/app/MyApplication.java +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/app/MyApplication.java @@ -32,8 +32,6 @@ public void onCreate() { .setPlayerFactory(ExoMediaPlayerFactory.create()) // 设置自己的渲染view,内部默认TextureView实现 // .setRenderViewFactory(SurfaceRenderViewFactory.create()) - // GLSurfaceView 可对视频加滤镜 -// .setRenderViewFactory(GLSurfaceRenderViewFactory.create()) // 根据手机重力感应自动切换横竖屏,默认false // .setEnableOrientation(true) // 监听系统中其他播放器是否获取音频焦点,实现不与其他播放器同时播放的效果,默认true diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/EglUtil.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/EglUtil.java new file mode 100644 index 00000000..e03e600f --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/EglUtil.java @@ -0,0 +1,133 @@ +package xyz.doikki.dkplayer.widget.render.gl2; + +import static android.opengl.GLES20.GL_ARRAY_BUFFER; +import static android.opengl.GLES20.GL_CLAMP_TO_EDGE; +import static android.opengl.GLES20.GL_LINK_STATUS; +import static android.opengl.GLES20.GL_STATIC_DRAW; +import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER; +import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER; +import static android.opengl.GLES20.GL_TEXTURE_WRAP_S; +import static android.opengl.GLES20.GL_TEXTURE_WRAP_T; +import static android.opengl.GLES20.GL_TRUE; +import static android.opengl.GLES20.glCreateProgram; + +import android.graphics.Bitmap; +import android.opengl.GLES20; +import android.opengl.GLException; +import android.opengl.GLUtils; +import android.util.Log; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; + +import xyz.doikki.dkplayer.BuildConfig; + + +public class EglUtil { + + public static final int NO_TEXTURE = -1; + + private static final int FLOAT_SIZE_BYTES = 4; + + public static int loadShader(final String strSource, final int iType) { + int[] compiled = new int[1]; + int iShader = GLES20.glCreateShader(iType); + GLES20.glShaderSource(iShader, strSource); + GLES20.glCompileShader(iShader); + GLES20.glGetShaderiv(iShader, GLES20.GL_COMPILE_STATUS, compiled, 0); + if (compiled[0] == 0) { + Log.d("Load Shader Failed", "Compilation\n" + GLES20.glGetShaderInfoLog(iShader)); + return 0; + } + return iShader; + } + + public static int createProgram(final int vertexShader, final int pixelShader) throws GLException { + final int program = glCreateProgram(); + if (program == 0) { + throw new RuntimeException("Could not create program"); + } + + GLES20.glAttachShader(program, vertexShader); + GLES20.glAttachShader(program, pixelShader); + + GLES20.glLinkProgram(program); + final int[] linkStatus = new int[1]; + GLES20.glGetProgramiv(program, GL_LINK_STATUS, linkStatus, 0); + if (linkStatus[0] != GL_TRUE) { + GLES20.glDeleteProgram(program); + throw new RuntimeException("Could not link program"); + } + return program; + } + + public static void checkEglError(String operation) { + if (!BuildConfig.DEBUG) return; + int error; + while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { + throw new RuntimeException(operation + ": glError " + error); + } + } + + public static void setupSampler(final int target, final int mag, final int min) { + GLES20.glTexParameterf(target, GL_TEXTURE_MAG_FILTER, mag); + GLES20.glTexParameterf(target, GL_TEXTURE_MIN_FILTER, min); + GLES20.glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + + + public static int createBuffer(final float[] data) { + return createBuffer(toFloatBuffer(data)); + } + + public static int createBuffer(final FloatBuffer data) { + final int[] buffers = new int[1]; + GLES20.glGenBuffers(buffers.length, buffers, 0); + updateBufferData(buffers[0], data); + return buffers[0]; + } + + public static FloatBuffer toFloatBuffer(final float[] data) { + final FloatBuffer buffer = ByteBuffer + .allocateDirect(data.length * FLOAT_SIZE_BYTES) + .order(ByteOrder.nativeOrder()) + .asFloatBuffer(); + buffer.put(data).position(0); + return buffer; + } + + + public static void updateBufferData(final int bufferName, final FloatBuffer data) { + GLES20.glBindBuffer(GL_ARRAY_BUFFER, bufferName); + GLES20.glBufferData(GL_ARRAY_BUFFER, data.capacity() * FLOAT_SIZE_BYTES, data, GL_STATIC_DRAW); + GLES20.glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + public static int loadTexture(final Bitmap img, final int usedTexId, final boolean recycle) { + int[] textures = new int[1]; + if (usedTexId == NO_TEXTURE) { + GLES20.glGenTextures(1, textures, 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, + GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, img, 0); + } else { + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, usedTexId); + GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, img); + textures[0] = usedTexId; + } + if (recycle) { + img.recycle(); + } + return textures[0]; + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLFrameBufferObjectRenderer.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLFrameBufferObjectRenderer.java new file mode 100644 index 00000000..67f368a5 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLFrameBufferObjectRenderer.java @@ -0,0 +1,77 @@ +package xyz.doikki.dkplayer.widget.render.gl2; + +import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT; +import static android.opengl.GLES20.GL_DEPTH_BUFFER_BIT; +import static android.opengl.GLES20.GL_FRAMEBUFFER; + +import android.opengl.GLES20; +import android.opengl.GLSurfaceView; + +import java.util.LinkedList; +import java.util.Queue; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import xyz.doikki.dkplayer.widget.render.gl2.filter.GlFilter; + + +abstract class GLFrameBufferObjectRenderer implements GLSurfaceView.Renderer { + + private GLFramebufferObject framebufferObject; + private GlFilter normalShader; + + private final Queue runOnDraw; + + + GLFrameBufferObjectRenderer() { + runOnDraw = new LinkedList(); + } + + + @Override + public final void onSurfaceCreated(final GL10 gl, final EGLConfig config) { + framebufferObject = new GLFramebufferObject(); + normalShader = new GlFilter(); + normalShader.setup(); + onSurfaceCreated(config); + } + + @Override + public final void onSurfaceChanged(final GL10 gl, final int width, final int height) { + framebufferObject.setup(width, height); + normalShader.setFrameSize(width, height); + onSurfaceChanged(width, height); + } + + @Override + public final void onDrawFrame(final GL10 gl) { + synchronized (runOnDraw) { + while (!runOnDraw.isEmpty()) { + runOnDraw.poll().run(); + } + } + framebufferObject.enable(); + GLES20.glViewport(0, 0, framebufferObject.getWidth(), framebufferObject.getHeight()); + + onDrawFrame(framebufferObject); + + GLES20.glBindFramebuffer(GL_FRAMEBUFFER, 0); + GLES20.glViewport(0, 0, framebufferObject.getWidth(), framebufferObject.getHeight()); + + GLES20.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + normalShader.draw(framebufferObject.getTexName(), null); + + } + + @Override + protected void finalize() throws Throwable { + + } + + public abstract void onSurfaceCreated(EGLConfig config); + + public abstract void onSurfaceChanged(int width, int height); + + public abstract void onDrawFrame(GLFramebufferObject fbo); +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLFramebufferObject.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLFramebufferObject.java new file mode 100644 index 00000000..1a9b9838 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLFramebufferObject.java @@ -0,0 +1,120 @@ +package xyz.doikki.dkplayer.widget.render.gl2; + +import static android.opengl.GLES20.GL_COLOR_ATTACHMENT0; +import static android.opengl.GLES20.GL_DEPTH_ATTACHMENT; +import static android.opengl.GLES20.GL_DEPTH_COMPONENT16; +import static android.opengl.GLES20.GL_FRAMEBUFFER; +import static android.opengl.GLES20.GL_FRAMEBUFFER_BINDING; +import static android.opengl.GLES20.GL_FRAMEBUFFER_COMPLETE; +import static android.opengl.GLES20.GL_LINEAR; +import static android.opengl.GLES20.GL_MAX_RENDERBUFFER_SIZE; +import static android.opengl.GLES20.GL_MAX_TEXTURE_SIZE; +import static android.opengl.GLES20.GL_NEAREST; +import static android.opengl.GLES20.GL_RENDERBUFFER; +import static android.opengl.GLES20.GL_RENDERBUFFER_BINDING; +import static android.opengl.GLES20.GL_RGBA; +import static android.opengl.GLES20.GL_TEXTURE_2D; +import static android.opengl.GLES20.GL_TEXTURE_BINDING_2D; +import static android.opengl.GLES20.GL_UNSIGNED_BYTE; + +import android.opengl.GLES20; + + +public class GLFramebufferObject { + + private int width; + private int height; + private int framebufferName; + private int renderbufferName; + private int texName; + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public int getTexName() { + return texName; + } + + public void setup(final int width, final int height) { + final int[] args = new int[1]; + + GLES20.glGetIntegerv(GL_MAX_TEXTURE_SIZE, args, 0); + if (width > args[0] || height > args[0]) { + throw new IllegalArgumentException("GL_MAX_TEXTURE_SIZE " + args[0]); + } + + GLES20.glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, args, 0); + if (width > args[0] || height > args[0]) { + throw new IllegalArgumentException("GL_MAX_RENDERBUFFER_SIZE " + args[0]); + } + + GLES20.glGetIntegerv(GL_FRAMEBUFFER_BINDING, args, 0); + final int saveFramebuffer = args[0]; + GLES20.glGetIntegerv(GL_RENDERBUFFER_BINDING, args, 0); + final int saveRenderbuffer = args[0]; + GLES20.glGetIntegerv(GL_TEXTURE_BINDING_2D, args, 0); + final int saveTexName = args[0]; + + release(); + + try { + this.width = width; + this.height = height; + + GLES20.glGenFramebuffers(args.length, args, 0); + framebufferName = args[0]; + GLES20.glBindFramebuffer(GL_FRAMEBUFFER, framebufferName); + + GLES20.glGenRenderbuffers(args.length, args, 0); + renderbufferName = args[0]; + GLES20.glBindRenderbuffer(GL_RENDERBUFFER, renderbufferName); + GLES20.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); + GLES20.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbufferName); + + GLES20.glGenTextures(args.length, args, 0); + texName = args[0]; + GLES20.glBindTexture(GL_TEXTURE_2D, texName); + + EglUtil.setupSampler(GL_TEXTURE_2D, GL_LINEAR, GL_NEAREST); + + GLES20.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null); + GLES20.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texName, 0); + + final int status = GLES20.glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + throw new RuntimeException("Failed to initialize framebuffer object " + status); + } + } catch (final RuntimeException e) { + release(); + throw e; + } + + GLES20.glBindFramebuffer(GL_FRAMEBUFFER, saveFramebuffer); + GLES20.glBindRenderbuffer(GL_RENDERBUFFER, saveRenderbuffer); + GLES20.glBindTexture(GL_TEXTURE_2D, saveTexName); + } + + public void release() { + final int[] args = new int[1]; + args[0] = texName; + GLES20.glDeleteTextures(args.length, args, 0); + texName = 0; + args[0] = renderbufferName; + GLES20.glDeleteRenderbuffers(args.length, args, 0); + renderbufferName = 0; + args[0] = framebufferName; + GLES20.glDeleteFramebuffers(args.length, args, 0); + framebufferName = 0; + } + + public void enable() { + GLES20.glBindFramebuffer(GL_FRAMEBUFFER, framebufferName); + } + + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLSurfaceRenderView2.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLSurfaceRenderView2.java new file mode 100644 index 00000000..e381c10f --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLSurfaceRenderView2.java @@ -0,0 +1,91 @@ +package xyz.doikki.dkplayer.widget.render.gl2; + +import android.content.Context; +import android.graphics.Bitmap; +import android.opengl.GLSurfaceView; +import android.util.AttributeSet; +import android.view.View; + +import androidx.annotation.NonNull; + +import xyz.doikki.dkplayer.widget.render.gl2.chooser.GLConfigChooser; +import xyz.doikki.dkplayer.widget.render.gl2.contextfactory.GLContextFactory; +import xyz.doikki.dkplayer.widget.render.gl2.filter.GlFilter; +import xyz.doikki.videoplayer.player.AbstractPlayer; +import xyz.doikki.videoplayer.render.IRenderView; +import xyz.doikki.videoplayer.render.MeasureHelper; + +public class GLSurfaceRenderView2 extends GLSurfaceView implements IRenderView { + + private final GLVideoRenderer renderer; + + public GLSurfaceRenderView2(Context context) { + this(context, null); + } + + public GLSurfaceRenderView2(Context context, AttributeSet attrs) { + super(context, attrs); + setEGLContextFactory(new GLContextFactory()); + setEGLConfigChooser(new GLConfigChooser()); + renderer = new GLVideoRenderer(this); + setRenderer(renderer); + } + + private final MeasureHelper mMeasureHelper = new MeasureHelper(); + + @Override + public void attachToPlayer(@NonNull AbstractPlayer player) { + this.renderer.setPlayer(player); + } + + @Override + public void setVideoSize(int videoWidth, int videoHeight) { + if (videoWidth > 0 && videoHeight > 0) { + mMeasureHelper.setVideoSize(videoWidth, videoHeight); + requestLayout(); + } + } + + @Override + public void setVideoRotation(int degree) { + mMeasureHelper.setVideoRotation(degree); + setRotation(degree); + } + + @Override + public void setScaleType(int scaleType) { + mMeasureHelper.setScreenScale(scaleType); + requestLayout(); + } + + @Override + public View getView() { + return this; + } + + @Override + public Bitmap doScreenShot() { + return null; + } + + @Override + public void release() { + + } + + public void setGlFilter(GlFilter glFilter) { + renderer.setGlFilter(glFilter); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int[] measuredSize = mMeasureHelper.doMeasure(widthMeasureSpec, heightMeasureSpec); + setMeasuredDimension(measuredSize[0], measuredSize[1]); + } + + @Override + public void onPause() { + super.onPause(); + renderer.release(); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLSurfaceTexture.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLSurfaceTexture.java new file mode 100644 index 00000000..7f69fe35 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLSurfaceTexture.java @@ -0,0 +1,48 @@ +package xyz.doikki.dkplayer.widget.render.gl2; + +import android.graphics.SurfaceTexture; + +import xyz.doikki.dkplayer.widget.render.gl2.filter.GlPreviewFilter; + + +class GLSurfaceTexture implements SurfaceTexture.OnFrameAvailableListener { + + private final SurfaceTexture surfaceTexture; + private SurfaceTexture.OnFrameAvailableListener onFrameAvailableListener; + + GLSurfaceTexture(final int texName) { + surfaceTexture = new SurfaceTexture(texName); + surfaceTexture.setOnFrameAvailableListener(this); + } + + void setOnFrameAvailableListener(final SurfaceTexture.OnFrameAvailableListener l) { + onFrameAvailableListener = l; + } + + + int getTextureTarget() { + return GlPreviewFilter.GL_TEXTURE_EXTERNAL_OES; + } + + void updateTexImage() { + surfaceTexture.updateTexImage(); + } + + void getTransformMatrix(final float[] mtx) { + surfaceTexture.getTransformMatrix(mtx); + } + + SurfaceTexture getSurfaceTexture() { + return surfaceTexture; + } + + public void onFrameAvailable(final SurfaceTexture surfaceTexture) { + if (onFrameAvailableListener != null) { + onFrameAvailableListener.onFrameAvailable(this.surfaceTexture); + } + } + + public void release() { + surfaceTexture.release(); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLVideoRenderer.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLVideoRenderer.java new file mode 100644 index 00000000..0512263f --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/GLVideoRenderer.java @@ -0,0 +1,193 @@ +package xyz.doikki.dkplayer.widget.render.gl2; + +import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT; +import static android.opengl.GLES20.GL_LINEAR; +import static android.opengl.GLES20.GL_MAX_TEXTURE_SIZE; +import static android.opengl.GLES20.GL_NEAREST; +import static android.opengl.GLES20.GL_TEXTURE_2D; +import static android.opengl.GLES20.glViewport; + +import android.graphics.SurfaceTexture; +import android.opengl.GLES20; +import android.opengl.Matrix; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import android.view.Surface; + +import javax.microedition.khronos.egl.EGLConfig; + +import xyz.doikki.dkplayer.widget.render.gl2.filter.GlFilter; +import xyz.doikki.dkplayer.widget.render.gl2.filter.GlLookUpTableFilter; +import xyz.doikki.dkplayer.widget.render.gl2.filter.GlPreviewFilter; +import xyz.doikki.videoplayer.player.AbstractPlayer; + +class GLVideoRenderer extends GLFrameBufferObjectRenderer implements SurfaceTexture.OnFrameAvailableListener { + + private static final String TAG = GLVideoRenderer.class.getSimpleName(); + + private GLSurfaceTexture previewTexture; + private boolean updateSurface = false; + + private int texName; + + private final float[] MVPMatrix = new float[16]; + private final float[] ProjMatrix = new float[16]; + private final float[] MMatrix = new float[16]; + private final float[] VMatrix = new float[16]; + private final float[] STMatrix = new float[16]; + + + private GLFramebufferObject filterFramebufferObject; + private GlPreviewFilter previewFilter; + + private GlFilter glFilter; + private boolean isNewFilter; + private final GLSurfaceRenderView2 glPreview; + + private float aspectRatio = 1f; + + private AbstractPlayer player; + + GLVideoRenderer(GLSurfaceRenderView2 glPreview) { + super(); + Matrix.setIdentityM(STMatrix, 0); + this.glPreview = glPreview; + } + + void setGlFilter(final GlFilter filter) { + glPreview.queueEvent(new Runnable() { + @Override + public void run() { + if (glFilter != null) { + glFilter.release(); + if (glFilter instanceof GlLookUpTableFilter) { + ((GlLookUpTableFilter) glFilter).releaseLutBitmap(); + } + glFilter = null; + } + glFilter = filter; + isNewFilter = true; + glPreview.requestRender(); + } + }); + } + + @Override + public void onSurfaceCreated(final EGLConfig config) { + GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + + final int[] args = new int[1]; + + GLES20.glGenTextures(args.length, args, 0); + texName = args[0]; + + + previewTexture = new GLSurfaceTexture(texName); + previewTexture.setOnFrameAvailableListener(this); + + + GLES20.glBindTexture(previewTexture.getTextureTarget(), texName); + // GL_TEXTURE_EXTERNAL_OES + EglUtil.setupSampler(previewTexture.getTextureTarget(), GL_LINEAR, GL_NEAREST); + GLES20.glBindTexture(GL_TEXTURE_2D, 0); + + filterFramebufferObject = new GLFramebufferObject(); + // GL_TEXTURE_EXTERNAL_OES + previewFilter = new GlPreviewFilter(previewTexture.getTextureTarget()); + previewFilter.setup(); + new Handler(Looper.getMainLooper()).post(() -> { + Surface surface = new Surface(previewTexture.getSurfaceTexture()); + player.setSurface(surface); + }); + + Matrix.setLookAtM(VMatrix, 0, + 0.0f, 0.0f, 5.0f, + 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f + ); + + synchronized (this) { + updateSurface = false; + } + + if (glFilter != null) { + isNewFilter = true; + } + + GLES20.glGetIntegerv(GL_MAX_TEXTURE_SIZE, args, 0); + + } + + @Override + public void onSurfaceChanged(final int width, final int height) { + Log.d(TAG, "onSurfaceChanged width = " + width + " height = " + height); + filterFramebufferObject.setup(width, height); + previewFilter.setFrameSize(width, height); + if (glFilter != null) { + glFilter.setFrameSize(width, height); + } + + aspectRatio = (float) width / height; + Matrix.frustumM(ProjMatrix, 0, -aspectRatio, aspectRatio, -1, 1, 5, 7); + Matrix.setIdentityM(MMatrix, 0); + } + + @Override + public void onDrawFrame(final GLFramebufferObject fbo) { + + synchronized (this) { + if (updateSurface) { + previewTexture.updateTexImage(); + previewTexture.getTransformMatrix(STMatrix); + updateSurface = false; + } + } + + if (isNewFilter) { + if (glFilter != null) { + glFilter.setup(); + glFilter.setFrameSize(fbo.getWidth(), fbo.getHeight()); + } + isNewFilter = false; + } + + if (glFilter != null) { + filterFramebufferObject.enable(); + glViewport(0, 0, filterFramebufferObject.getWidth(), filterFramebufferObject.getHeight()); + } + + GLES20.glClear(GL_COLOR_BUFFER_BIT); + + Matrix.multiplyMM(MVPMatrix, 0, VMatrix, 0, MMatrix, 0); + Matrix.multiplyMM(MVPMatrix, 0, ProjMatrix, 0, MVPMatrix, 0); + + previewFilter.draw(texName, MVPMatrix, STMatrix, aspectRatio); + + if (glFilter != null) { + fbo.enable(); + GLES20.glClear(GL_COLOR_BUFFER_BIT); + glFilter.draw(filterFramebufferObject.getTexName(), fbo); + } + } + + @Override + public synchronized void onFrameAvailable(final SurfaceTexture previewTexture) { + updateSurface = true; + glPreview.requestRender(); + } + + void setPlayer(AbstractPlayer player) { + this.player = player; + } + + void release() { + if (glFilter != null) { + glFilter.release(); + } + if (previewTexture != null) { + previewTexture.release(); + } + } + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/Resolution.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/Resolution.java new file mode 100644 index 00000000..bf497c12 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/Resolution.java @@ -0,0 +1,39 @@ +package xyz.doikki.dkplayer.widget.render.gl2; + +import java.io.Serializable; + +public class Resolution implements Serializable { + private final int width; + private final int height; + + public Resolution(int width, int height) { + this.width = width; + this.height = height; + } + + public int width() { + return width; + } + + public int height() { + return height; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Resolution that = (Resolution) o; + + if (height != that.height) return false; + return width == that.width; + } + + @Override + public int hashCode() { + int result = width; + result = 31 * result + height; + return result; + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/chooser/GLConfigChooser.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/chooser/GLConfigChooser.java new file mode 100644 index 00000000..35ac1569 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/chooser/GLConfigChooser.java @@ -0,0 +1,135 @@ +package xyz.doikki.dkplayer.widget.render.gl2.chooser; + +import static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_SIZE; +import static javax.microedition.khronos.egl.EGL10.EGL_BLUE_SIZE; +import static javax.microedition.khronos.egl.EGL10.EGL_DEPTH_SIZE; +import static javax.microedition.khronos.egl.EGL10.EGL_GREEN_SIZE; +import static javax.microedition.khronos.egl.EGL10.EGL_NONE; +import static javax.microedition.khronos.egl.EGL10.EGL_RED_SIZE; +import static javax.microedition.khronos.egl.EGL10.EGL_RENDERABLE_TYPE; +import static javax.microedition.khronos.egl.EGL10.EGL_STENCIL_SIZE; + +import android.opengl.GLSurfaceView; +import android.os.Build; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLDisplay; + + +public class GLConfigChooser implements GLSurfaceView.EGLConfigChooser { + + private final int[] configSpec; + private final int redSize; + private final int greenSize; + private final int blueSize; + private final int alphaSize; + private final int depthSize; + private final int stencilSize; + + private static final int EGL_CONTEXT_CLIENT_VERSION = 2; + + private static final boolean USE_RGB_888 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1; + + public GLConfigChooser() { + this( + USE_RGB_888 ? 8 : 5, + USE_RGB_888 ? 8 : 6, + USE_RGB_888 ? 8 : 5, + 0, + 0, + 0, + EGL_CONTEXT_CLIENT_VERSION + ); + } + + public GLConfigChooser( + final int redSize, + final int greenSize, + final int blueSize, + final int alphaSize, + final int depthSize, + final int stencilSize, + final int version) { + configSpec = filterConfigSpec(new int[]{ + EGL_RED_SIZE, redSize, + EGL_GREEN_SIZE, greenSize, + EGL_BLUE_SIZE, blueSize, + EGL_ALPHA_SIZE, alphaSize, + EGL_DEPTH_SIZE, depthSize, + EGL_STENCIL_SIZE, stencilSize, + EGL_NONE + }, version); + this.redSize = redSize; + this.greenSize = greenSize; + this.blueSize = blueSize; + this.alphaSize = alphaSize; + this.depthSize = depthSize; + this.stencilSize = stencilSize; + } + + private static final int EGL_OPENGL_ES2_BIT = 4; + + private int[] filterConfigSpec(final int[] configSpec, final int version) { + if (version != 2) { + return configSpec; + } + + final int len = configSpec.length; + final int[] newConfigSpec = new int[len + 2]; + System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1); + newConfigSpec[len - 1] = EGL_RENDERABLE_TYPE; + newConfigSpec[len] = EGL_OPENGL_ES2_BIT; + newConfigSpec[len + 1] = EGL_NONE; + return newConfigSpec; + } + + ////////////////////////////////////////////////////////////////////////// + + @Override + public EGLConfig chooseConfig(final EGL10 egl, final EGLDisplay display) { + final int[] num_config = new int[1]; + if (!egl.eglChooseConfig(display, configSpec, null, 0, num_config)) { + throw new IllegalArgumentException("eglChooseConfig failed"); + } + final int config_size = num_config[0]; + if (config_size <= 0) { + throw new IllegalArgumentException("No configs match configSpec"); + } + + final EGLConfig[] configs = new EGLConfig[config_size]; + if (!egl.eglChooseConfig(display, configSpec, configs, config_size, num_config)) { + throw new IllegalArgumentException("eglChooseConfig#2 failed"); + } + final EGLConfig config = chooseConfig(egl, display, configs); + if (config == null) { + throw new IllegalArgumentException("No config chosen"); + } + return config; + } + + private EGLConfig chooseConfig(final EGL10 egl, final EGLDisplay display, final EGLConfig[] configs) { + for (final EGLConfig config : configs) { + final int d = findConfigAttrib(egl, display, config, EGL_DEPTH_SIZE, 0); + final int s = findConfigAttrib(egl, display, config, EGL_STENCIL_SIZE, 0); + if ((d >= depthSize) && (s >= stencilSize)) { + final int r = findConfigAttrib(egl, display, config, EGL_RED_SIZE, 0); + final int g = findConfigAttrib(egl, display, config, EGL_GREEN_SIZE, 0); + final int b = findConfigAttrib(egl, display, config, EGL_BLUE_SIZE, 0); + final int a = findConfigAttrib(egl, display, config, EGL_ALPHA_SIZE, 0); + if ((r == redSize) && (g == greenSize) && (b == blueSize) && (a == alphaSize)) { + return config; + } + } + } + return null; + } + + private int findConfigAttrib(final EGL10 egl, final EGLDisplay display, final EGLConfig config, final int attribute, final int defaultValue) { + final int[] value = new int[1]; + if (egl.eglGetConfigAttrib(display, config, attribute, value)) { + return value[0]; + } + return defaultValue; + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/contextfactory/GLContextFactory.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/contextfactory/GLContextFactory.java new file mode 100644 index 00000000..37269308 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/contextfactory/GLContextFactory.java @@ -0,0 +1,38 @@ +package xyz.doikki.dkplayer.widget.render.gl2.contextfactory; + +import static javax.microedition.khronos.egl.EGL10.EGL_NONE; +import static javax.microedition.khronos.egl.EGL10.EGL_NO_CONTEXT; + +import android.opengl.GLSurfaceView; +import android.util.Log; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; + + +public class GLContextFactory implements GLSurfaceView.EGLContextFactory { + + private static final String TAG = "EContextFactory"; + + private final int EGL_CLIENT_VERSION = 2; + + private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + + @Override + public EGLContext createContext(final EGL10 egl, final EGLDisplay display, final EGLConfig config) { + final int[] attrib_list; + attrib_list = new int[]{EGL_CONTEXT_CLIENT_VERSION, EGL_CLIENT_VERSION, EGL_NONE}; + return egl.eglCreateContext(display, config, EGL_NO_CONTEXT, attrib_list); + } + + @Override + public void destroyContext(final EGL10 egl, final EGLDisplay display, final EGLContext context) { + if (!egl.eglDestroyContext(display, context)) { + Log.e(TAG, "display:" + display + " context: " + context); + throw new RuntimeException("eglDestroyContex" + egl.eglGetError()); + } + } + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlBilateralFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlBilateralFilter.java new file mode 100644 index 00000000..a586808f --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlBilateralFilter.java @@ -0,0 +1,148 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import static android.opengl.GLES20.glUniform1f; + +public class GlBilateralFilter extends GlFilter { + + private static final String VERTEX_SHADER = + "attribute vec4 aPosition;" + + "attribute vec4 aTextureCoord;" + + + "const lowp int GAUSSIAN_SAMPLES = 9;" + + + "uniform highp float texelWidthOffset;" + + "uniform highp float texelHeightOffset;" + + "uniform highp float blurSize;" + + + "varying highp vec2 vTextureCoord;" + + "varying highp vec2 blurCoordinates[GAUSSIAN_SAMPLES];" + + + "void main() {" + + "gl_Position = aPosition;" + + "vTextureCoord = aTextureCoord.xy;" + + + // Calculate the positions for the blur + "int multiplier = 0;" + + "highp vec2 blurStep;" + + "highp vec2 singleStepOffset = vec2(texelHeightOffset, texelWidthOffset) * blurSize;" + + + "for (lowp int i = 0; i < GAUSSIAN_SAMPLES; i++) {" + + "multiplier = (i - ((GAUSSIAN_SAMPLES - 1) / 2));" + + // Blur in x (horizontal) + "blurStep = float(multiplier) * singleStepOffset;" + + "blurCoordinates[i] = vTextureCoord.xy + blurStep;" + + "}" + + "}"; + + private static final String FRAGMENT_SHADER = + "precision mediump float;" + + + "uniform lowp sampler2D sTexture;" + + + "const lowp int GAUSSIAN_SAMPLES = 9;" + + "varying highp vec2 vTextureCoord;" + + "varying highp vec2 blurCoordinates[GAUSSIAN_SAMPLES];" + + + "const mediump float distanceNormalizationFactor = 1.5;" + + + "void main() {" + + "lowp vec4 centralColor = texture2D(sTexture, blurCoordinates[4]);" + + "lowp float gaussianWeightTotal = 0.18;" + + "lowp vec4 sum = centralColor * 0.18;" + + + "lowp vec4 sampleColor = texture2D(sTexture, blurCoordinates[0]);" + + "lowp float distanceFromCentralColor;" + + + "distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);" + + + "lowp float gaussianWeight = 0.05 * (1.0 - distanceFromCentralColor);" + + "gaussianWeightTotal += gaussianWeight;" + + "sum += sampleColor * gaussianWeight;" + + + "sampleColor = texture2D(sTexture, blurCoordinates[1]);" + + "distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);" + + "gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);" + + "gaussianWeightTotal += gaussianWeight;" + + "sum += sampleColor * gaussianWeight;" + + + "sampleColor = texture2D(sTexture, blurCoordinates[2]);" + + "distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);" + + "gaussianWeight = 0.12 * (1.0 - distanceFromCentralColor);" + + "gaussianWeightTotal += gaussianWeight;" + + "sum += sampleColor * gaussianWeight;" + + + "sampleColor = texture2D(sTexture, blurCoordinates[3]);" + + "distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);" + + "gaussianWeight = 0.15 * (1.0 - distanceFromCentralColor);" + + "gaussianWeightTotal += gaussianWeight;" + + "sum += sampleColor * gaussianWeight;" + + + "sampleColor = texture2D(sTexture, blurCoordinates[5]);" + + "distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);" + + "gaussianWeight = 0.15 * (1.0 - distanceFromCentralColor);" + + "gaussianWeightTotal += gaussianWeight;" + + "sum += sampleColor * gaussianWeight;" + + + "sampleColor = texture2D(sTexture, blurCoordinates[6]);" + + "distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);" + + "gaussianWeight = 0.12 * (1.0 - distanceFromCentralColor);" + + "gaussianWeightTotal += gaussianWeight;" + + "sum += sampleColor * gaussianWeight;" + + + "sampleColor = texture2D(sTexture, blurCoordinates[7]);" + + "distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);" + + "gaussianWeight = 0.09 * (1.0 - distanceFromCentralColor);" + + "gaussianWeightTotal += gaussianWeight;" + + "sum += sampleColor * gaussianWeight;" + + + "sampleColor = texture2D(sTexture, blurCoordinates[8]);" + + "distanceFromCentralColor = min(distance(centralColor, sampleColor) * distanceNormalizationFactor, 1.0);" + + "gaussianWeight = 0.05 * (1.0 - distanceFromCentralColor);" + + "gaussianWeightTotal += gaussianWeight;" + + "sum += sampleColor * gaussianWeight;" + + + "gl_FragColor = sum / gaussianWeightTotal;" + + "}"; + + private float texelWidthOffset = 0.004f; + private float texelHeightOffset = 0.004f; + private float blurSize = 1.0f; + + public GlBilateralFilter() { + super(VERTEX_SHADER, FRAGMENT_SHADER); + } + + + public float getTexelWidthOffset() { + return texelWidthOffset; + } + + public void setTexelWidthOffset(final float texelWidthOffset) { + this.texelWidthOffset = texelWidthOffset; + } + + public float getTexelHeightOffset() { + return texelHeightOffset; + } + + public void setTexelHeightOffset(final float texelHeightOffset) { + this.texelHeightOffset = texelHeightOffset; + } + + public float getBlurSize() { + return blurSize; + } + + public void setBlurSize(final float blurSize) { + this.blurSize = blurSize; + } + + @Override + public void onDraw() { + glUniform1f(getHandle("texelWidthOffset"), texelWidthOffset); + glUniform1f(getHandle("texelHeightOffset"), texelHeightOffset); + glUniform1f(getHandle("blurSize"), blurSize); + } + + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlBoxBlurFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlBoxBlurFilter.java new file mode 100644 index 00000000..dce9ba33 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlBoxBlurFilter.java @@ -0,0 +1,95 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + +public class GlBoxBlurFilter extends GlFilter { + + private static final String VERTEX_SHADER = + "attribute vec4 aPosition;" + + "attribute vec4 aTextureCoord;" + + + "uniform highp float texelWidthOffset;" + + "uniform highp float texelHeightOffset;" + + "uniform highp float blurSize;" + + + "varying highp vec2 centerTextureCoordinate;" + + "varying highp vec2 oneStepLeftTextureCoordinate;" + + "varying highp vec2 twoStepsLeftTextureCoordinate;" + + "varying highp vec2 oneStepRightTextureCoordinate;" + + "varying highp vec2 twoStepsRightTextureCoordinate;" + + + "void main() {" + + "gl_Position = aPosition;" + + + "vec2 firstOffset = vec2(1.5 * texelWidthOffset, 1.5 * texelHeightOffset) * blurSize;" + + "vec2 secondOffset = vec2(3.5 * texelWidthOffset, 3.5 * texelHeightOffset) * blurSize;" + + + "centerTextureCoordinate = aTextureCoord.xy;" + + "oneStepLeftTextureCoordinate = centerTextureCoordinate - firstOffset;" + + "twoStepsLeftTextureCoordinate = centerTextureCoordinate - secondOffset;" + + "oneStepRightTextureCoordinate = centerTextureCoordinate + firstOffset;" + + "twoStepsRightTextureCoordinate = centerTextureCoordinate + secondOffset;" + + "}"; + + private static final String FRAGMENT_SHADER = + "precision mediump float;" + + + "uniform lowp sampler2D sTexture;" + + + "varying highp vec2 centerTextureCoordinate;" + + "varying highp vec2 oneStepLeftTextureCoordinate;" + + "varying highp vec2 twoStepsLeftTextureCoordinate;" + + "varying highp vec2 oneStepRightTextureCoordinate;" + + "varying highp vec2 twoStepsRightTextureCoordinate;" + + + "void main() {" + + "lowp vec4 color = texture2D(sTexture, centerTextureCoordinate) * 0.2;" + + "color += texture2D(sTexture, oneStepLeftTextureCoordinate) * 0.2;" + + "color += texture2D(sTexture, oneStepRightTextureCoordinate) * 0.2;" + + "color += texture2D(sTexture, twoStepsLeftTextureCoordinate) * 0.2;" + + "color += texture2D(sTexture, twoStepsRightTextureCoordinate) * 0.2;" + + "gl_FragColor = color;" + + "}"; + + private float texelWidthOffset = 0.003f; + private float texelHeightOffset = 0.003f; + private float blurSize = 1.0f; + + + public GlBoxBlurFilter() { + super(VERTEX_SHADER, FRAGMENT_SHADER); + } + + public float getTexelWidthOffset() { + return texelWidthOffset; + } + + public void setTexelWidthOffset(final float texelWidthOffset) { + this.texelWidthOffset = texelWidthOffset; + } + + public float getTexelHeightOffset() { + return texelHeightOffset; + } + + public void setTexelHeightOffset(final float texelHeightOffset) { + this.texelHeightOffset = texelHeightOffset; + } + + public float getBlurSize() { + return blurSize; + } + + public void setBlurSize(final float blurSize) { + this.blurSize = blurSize; + } + + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("texelWidthOffset"), texelWidthOffset); + GLES20.glUniform1f(getHandle("texelHeightOffset"), texelHeightOffset); + GLES20.glUniform1f(getHandle("blurSize"), blurSize); + } + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlBrightnessFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlBrightnessFilter.java new file mode 100644 index 00000000..39a1a8d9 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlBrightnessFilter.java @@ -0,0 +1,38 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + + +import android.opengl.GLES20; + +/** + * brightness value ranges from -1.0 to 1.0, with 0.0 as the normal level + */ +public class GlBrightnessFilter extends GlFilter { + private static final String BRIGHTNESS_FRAGMENT_SHADER = "" + + "precision mediump float;" + + " varying vec2 vTextureCoord;\n" + + " \n" + + " uniform lowp sampler2D sTexture;\n" + + " uniform lowp float brightness;\n" + + " \n" + + " void main()\n" + + " {\n" + + " lowp vec4 textureColor = texture2D(sTexture, vTextureCoord);\n" + + " \n" + + " gl_FragColor = vec4((textureColor.rgb + vec3(brightness)), textureColor.w);\n" + + " }"; + + public GlBrightnessFilter() { + super(DEFAULT_VERTEX_SHADER, BRIGHTNESS_FRAGMENT_SHADER); + } + + private float brightness = 0f; + + public void setBrightness(float brightness) { + this.brightness = brightness; + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("brightness"), brightness); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlBulgeDistortionFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlBulgeDistortionFilter.java new file mode 100644 index 00000000..1de05887 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlBulgeDistortionFilter.java @@ -0,0 +1,80 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + +public class GlBulgeDistortionFilter extends GlFilter { + + private static final String FRAGMENT_SHADER = + "precision mediump float;" + + + "varying highp vec2 vTextureCoord;" + + "uniform lowp sampler2D sTexture;" + + + "uniform highp vec2 center;" + + "uniform highp float radius;" + + "uniform highp float scale;" + + + "void main() {" + + "highp vec2 textureCoordinateToUse = vTextureCoord;" + + "highp float dist = distance(center, vTextureCoord);" + + "textureCoordinateToUse -= center;" + + "if (dist < radius) {" + + "highp float percent = 1.0 - ((radius - dist) / radius) * scale;" + + "percent = percent * percent;" + + "textureCoordinateToUse = textureCoordinateToUse * percent;" + + "}" + + "textureCoordinateToUse += center;" + + + "gl_FragColor = texture2D(sTexture, textureCoordinateToUse);" + + "}"; + + private float centerX = 0.5f; + private float centerY = 0.5f; + private float radius = 0.25f; + private float scale = 0.5f; + + public GlBulgeDistortionFilter() { + super(DEFAULT_VERTEX_SHADER, FRAGMENT_SHADER); + } + + public float getCenterX() { + return centerX; + } + + public void setCenterX(final float centerX) { + this.centerX = centerX; + } + + public float getCenterY() { + return centerY; + } + + public void setCenterY(final float centerY) { + this.centerY = centerY; + } + + public float getRadius() { + return radius; + } + + public void setRadius(final float radius) { + this.radius = radius; + } + + public float getScale() { + return scale; + } + + public void setScale(final float scale) { + this.scale = scale; + } + + ////////////////////////////////////////////////////////////////////////// + + @Override + public void onDraw() { + GLES20.glUniform2f(getHandle("center"), centerX, centerY); + GLES20.glUniform1f(getHandle("radius"), radius); + GLES20.glUniform1f(getHandle("scale"), scale); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlCGAColorspaceFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlCGAColorspaceFilter.java new file mode 100644 index 00000000..964812b1 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlCGAColorspaceFilter.java @@ -0,0 +1,52 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +public class GlCGAColorspaceFilter extends GlFilter { + + private static final String FRAGMENT_SHADER = + "precision mediump float;" + + + "varying vec2 vTextureCoord;" + + "uniform lowp sampler2D sTexture;" + + + "void main() {" + + "highp vec2 sampleDivisor = vec2(1.0 / 200.0, 1.0 / 320.0);" + + + "highp vec2 samplePos = vTextureCoord - mod(vTextureCoord, sampleDivisor);" + + "highp vec4 color = texture2D(sTexture, samplePos);" + + + "mediump vec4 colorCyan = vec4(85.0 / 255.0, 1.0, 1.0, 1.0);" + + "mediump vec4 colorMagenta = vec4(1.0, 85.0 / 255.0, 1.0, 1.0);" + + "mediump vec4 colorWhite = vec4(1.0, 1.0, 1.0, 1.0);" + + "mediump vec4 colorBlack = vec4(0.0, 0.0, 0.0, 1.0);" + + + "mediump vec4 endColor;" + + "highp float blackDistance = distance(color, colorBlack);" + + "highp float whiteDistance = distance(color, colorWhite);" + + "highp float magentaDistance = distance(color, colorMagenta);" + + "highp float cyanDistance = distance(color, colorCyan);" + + + "mediump vec4 finalColor;" + + + "highp float colorDistance = min(magentaDistance, cyanDistance);" + + "colorDistance = min(colorDistance, whiteDistance);" + + "colorDistance = min(colorDistance, blackDistance);" + + + "if (colorDistance == blackDistance) {" + + "finalColor = colorBlack;" + + "} else if (colorDistance == whiteDistance) {" + + "finalColor = colorWhite;" + + "} else if (colorDistance == cyanDistance) {" + + "finalColor = colorCyan;" + + "} else {" + + "finalColor = colorMagenta;" + + "}" + + + "gl_FragColor = finalColor;" + + "}"; + + + public GlCGAColorspaceFilter() { + super(DEFAULT_VERTEX_SHADER, FRAGMENT_SHADER); + } + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlContrastFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlContrastFilter.java new file mode 100644 index 00000000..f7811d97 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlContrastFilter.java @@ -0,0 +1,41 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + + +import android.opengl.GLES20; + +/** + * Changes the contrast of the image. + * contrast value ranges from 0.0 to 4.0, with 1.0 as the normal level + */ +public class GlContrastFilter extends GlFilter { + + private static final String CONTRAST_FRAGMENT_SHADER = "" + + "precision mediump float;" + + " varying vec2 vTextureCoord;\n" + + " \n" + + " uniform lowp sampler2D sTexture;\n" + + " uniform lowp float contrast;\n" + + " \n" + + " void main()\n" + + " {\n" + + " lowp vec4 textureColor = texture2D(sTexture, vTextureCoord);\n" + + " \n" + + " gl_FragColor = vec4(((textureColor.rgb - vec3(0.5)) * contrast + vec3(0.5)), textureColor.w);\n" + + " }"; + + + public GlContrastFilter() { + super(DEFAULT_VERTEX_SHADER, CONTRAST_FRAGMENT_SHADER); + } + + private float contrast = 1.2f; + + public void setContrast(float contrast) { + this.contrast = contrast; + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("contrast"), contrast); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlCrosshatchFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlCrosshatchFilter.java new file mode 100644 index 00000000..dedddab3 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlCrosshatchFilter.java @@ -0,0 +1,85 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + +public class GlCrosshatchFilter extends GlFilter { + + private static final String CROSSHATCH_FRAGMENT_SHADER = "" + + "precision mediump float;" + + " varying vec2 vTextureCoord;\n" + + " uniform lowp sampler2D sTexture;\n" + + "uniform highp float crossHatchSpacing;\n" + + "uniform highp float lineWidth;\n" + + "const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);\n" + + "void main()\n" + + "{\n" + + "highp float luminance = dot(texture2D(sTexture, vTextureCoord).rgb, W);\n" + + "lowp vec4 colorToDisplay = vec4(1.0, 1.0, 1.0, 1.0);\n" + + "if (luminance < 1.00)\n" + + "{\n" + + "if (mod(vTextureCoord.x + vTextureCoord.y, crossHatchSpacing) <= lineWidth)\n" + + "{\n" + + "colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);\n" + + "}\n" + + "}\n" + + "if (luminance < 0.75)\n" + + "{\n" + + "if (mod(vTextureCoord.x - vTextureCoord.y, crossHatchSpacing) <= lineWidth)\n" + + "{\n" + + "colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);\n" + + "}\n" + + "}\n" + + "if (luminance < 0.50)\n" + + "{\n" + + "if (mod(vTextureCoord.x + vTextureCoord.y - (crossHatchSpacing / 2.0), crossHatchSpacing) <= lineWidth)\n" + + "{\n" + + "colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);\n" + + "}\n" + + "}\n" + + "if (luminance < 0.3)\n" + + "{\n" + + "if (mod(vTextureCoord.x - vTextureCoord.y - (crossHatchSpacing / 2.0), crossHatchSpacing) <= lineWidth)\n" + + "{\n" + + "colorToDisplay = vec4(0.0, 0.0, 0.0, 1.0);\n" + + "}\n" + + "}\n" + + "gl_FragColor = colorToDisplay;\n" + + "}\n"; + + public GlCrosshatchFilter() { + super(DEFAULT_VERTEX_SHADER, CROSSHATCH_FRAGMENT_SHADER); + } + + private float crossHatchSpacing = 0.03f; + private float lineWidth = 0.003f; + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("crossHatchSpacing"), crossHatchSpacing); + GLES20.glUniform1f(getHandle("lineWidth"), lineWidth); + } + + public void setCrossHatchSpacing(float crossHatchSpacing) { + this.crossHatchSpacing = crossHatchSpacing; + } + + public void setLineWidth(float lineWidth) { + this.lineWidth = lineWidth; + } + + @Override + public void setFrameSize(int width, int height) { + super.setFrameSize(width, height); + + float singlePixelSpacing; + if (width != 0) { + singlePixelSpacing = 1.0f / (float) width; + } else { + singlePixelSpacing = 1.0f / 2048.0f; + } + if (crossHatchSpacing < singlePixelSpacing) { + this.crossHatchSpacing = singlePixelSpacing; + } + + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlExposureFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlExposureFilter.java new file mode 100644 index 00000000..d812fdf9 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlExposureFilter.java @@ -0,0 +1,38 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + +/** + * exposure: The adjusted exposure (-10.0 - 10.0, with 0.0 as the default) + */ +public class GlExposureFilter extends GlFilter { + + private static final String EXPOSURE_FRAGMENT_SHADER = "" + + "precision mediump float;" + + " varying vec2 vTextureCoord;\n" + + " \n" + + " uniform lowp sampler2D sTexture;\n" + + " uniform highp float exposure;\n" + + " \n" + + " void main()\n" + + " {\n" + + " highp vec4 textureColor = texture2D(sTexture, vTextureCoord);\n" + + " \n" + + " gl_FragColor = vec4(textureColor.rgb * pow(2.0, exposure), textureColor.w);\n" + + " } "; + + public GlExposureFilter() { + super(DEFAULT_VERTEX_SHADER, EXPOSURE_FRAGMENT_SHADER); + } + + private float exposure = 1f; + + public void setExposure(float exposure) { + this.exposure = exposure; + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("exposure"), exposure); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlFilter.java new file mode 100644 index 00000000..b2ad810e --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlFilter.java @@ -0,0 +1,157 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import static android.opengl.GLES20.GL_FLOAT; +import static android.opengl.GLES20.GL_FRAGMENT_SHADER; +import static android.opengl.GLES20.GL_VERTEX_SHADER; +import static android.opengl.GLES20.glGetAttribLocation; +import static android.opengl.GLES20.glGetUniformLocation; +import static android.opengl.GLES20.glUseProgram; + +import android.content.res.Resources; +import android.opengl.GLES20; + +import java.util.HashMap; + +import xyz.doikki.dkplayer.widget.render.gl2.GLFramebufferObject; +import xyz.doikki.dkplayer.widget.render.gl2.EglUtil; + +public class GlFilter { + + public static final String DEFAULT_UNIFORM_SAMPLER = "sTexture"; + + + protected static final String DEFAULT_VERTEX_SHADER = + "attribute vec4 aPosition;\n" + + "attribute vec4 aTextureCoord;\n" + + "varying highp vec2 vTextureCoord;\n" + + "void main() {\n" + + "gl_Position = aPosition;\n" + + "vTextureCoord = aTextureCoord.xy;\n" + + "}\n"; + + protected static final String DEFAULT_FRAGMENT_SHADER = + "precision mediump float;\n" + + "varying highp vec2 vTextureCoord;\n" + + "uniform lowp sampler2D sTexture;\n" + + "void main() {\n" + + "gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + + "}\n"; + + + private static final float[] VERTICES_DATA = new float[]{ + // X, Y, Z, U, V + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f + }; + + private static final int FLOAT_SIZE_BYTES = 4; + protected static final int VERTICES_DATA_POS_SIZE = 3; + protected static final int VERTICES_DATA_UV_SIZE = 2; + protected static final int VERTICES_DATA_STRIDE_BYTES = (VERTICES_DATA_POS_SIZE + VERTICES_DATA_UV_SIZE) * FLOAT_SIZE_BYTES; + protected static final int VERTICES_DATA_POS_OFFSET = 0 * FLOAT_SIZE_BYTES; + protected static final int VERTICES_DATA_UV_OFFSET = VERTICES_DATA_POS_OFFSET + VERTICES_DATA_POS_SIZE * FLOAT_SIZE_BYTES; + + private final String vertexShaderSource; + private final String fragmentShaderSource; + + private int program; + + private int vertexShader; + private int fragmentShader; + + private int vertexBufferName; + + private final HashMap handleMap = new HashMap(); + + public GlFilter() { + this(DEFAULT_VERTEX_SHADER, DEFAULT_FRAGMENT_SHADER); + } + + public GlFilter(final Resources res, final int vertexShaderSourceResId, final int fragmentShaderSourceResId) { + this(res.getString(vertexShaderSourceResId), res.getString(fragmentShaderSourceResId)); + } + + public GlFilter(final String vertexShaderSource, final String fragmentShaderSource) { + this.vertexShaderSource = vertexShaderSource; + this.fragmentShaderSource = fragmentShaderSource; + } + + public void setup() { + release(); + vertexShader = EglUtil.loadShader(vertexShaderSource, GL_VERTEX_SHADER); + fragmentShader = EglUtil.loadShader(fragmentShaderSource, GL_FRAGMENT_SHADER); + program = EglUtil.createProgram(vertexShader, fragmentShader); + vertexBufferName = EglUtil.createBuffer(VERTICES_DATA); + } + + public void setFrameSize(final int width, final int height) { + } + + + public void release() { + GLES20.glDeleteProgram(program); + program = 0; + GLES20.glDeleteShader(vertexShader); + vertexShader = 0; + GLES20.glDeleteShader(fragmentShader); + fragmentShader = 0; + GLES20.glDeleteBuffers(1, new int[]{vertexBufferName}, 0); + vertexBufferName = 0; + + handleMap.clear(); + } + + public void draw(final int texName, final GLFramebufferObject fbo) { + useProgram(); + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferName); + GLES20.glEnableVertexAttribArray(getHandle("aPosition")); + GLES20.glVertexAttribPointer(getHandle("aPosition"), VERTICES_DATA_POS_SIZE, GL_FLOAT, false, VERTICES_DATA_STRIDE_BYTES, VERTICES_DATA_POS_OFFSET); + GLES20.glEnableVertexAttribArray(getHandle("aTextureCoord")); + GLES20.glVertexAttribPointer(getHandle("aTextureCoord"), VERTICES_DATA_UV_SIZE, GL_FLOAT, false, VERTICES_DATA_STRIDE_BYTES, VERTICES_DATA_UV_OFFSET); + + GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texName); + GLES20.glUniform1i(getHandle("sTexture"), 0); + + onDraw(); + + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + + GLES20.glDisableVertexAttribArray(getHandle("aPosition")); + GLES20.glDisableVertexAttribArray(getHandle("aTextureCoord")); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + } + + protected void onDraw() { + } + + protected final void useProgram() { + glUseProgram(program); + } + + protected final int getVertexBufferName() { + return vertexBufferName; + } + + protected final int getHandle(final String name) { + final Integer value = handleMap.get(name); + if (value != null) { + return value.intValue(); + } + + int location = glGetAttribLocation(program, name); + if (location == -1) { + location = glGetUniformLocation(program, name); + } + if (location == -1) { + throw new IllegalStateException("Could not get attrib or uniform location for " + name); + } + handleMap.put(name, Integer.valueOf(location)); + return location; + } + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlFilterGroup.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlFilterGroup.java new file mode 100644 index 00000000..61c2ad2a --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlFilterGroup.java @@ -0,0 +1,108 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT; +import static android.opengl.GLES20.GL_FRAMEBUFFER; + +import android.opengl.GLES20; +import android.util.Pair; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +import xyz.doikki.dkplayer.widget.render.gl2.GLFramebufferObject; + +public class GlFilterGroup extends GlFilter { + + private final Collection filters; + + private final ArrayList> list = new ArrayList>(); + + public GlFilterGroup(final GlFilter... glFilters) { + this(Arrays.asList(glFilters)); + } + + public GlFilterGroup(final Collection glFilters) { + filters = glFilters; + } + + @Override + public void setup() { + super.setup(); + + if (filters != null) { + final int max = filters.size(); + int count = 0; + + for (final GlFilter shader : filters) { + shader.setup(); + final GLFramebufferObject fbo; + if ((count + 1) < max) { + fbo = new GLFramebufferObject(); + } else { + fbo = null; + } + list.add(Pair.create(shader, fbo)); + count++; + } + } + } + + @Override + public void release() { + for (final Pair pair : list) { + if (pair.first != null) { + pair.first.release(); + } + if (pair.second != null) { + pair.second.release(); + } + } + list.clear(); + super.release(); + } + + @Override + public void setFrameSize(final int width, final int height) { + super.setFrameSize(width, height); + + for (final Pair pair : list) { + if (pair.first != null) { + pair.first.setFrameSize(width, height); + } + if (pair.second != null) { + pair.second.setup(width, height); + } + } + } + + private int prevTexName; + + @Override + public void draw(final int texName, final GLFramebufferObject fbo) { + prevTexName = texName; + for (final Pair pair : list) { + if (pair.second != null) { + if (pair.first != null) { + pair.second.enable(); + GLES20.glClear(GL_COLOR_BUFFER_BIT); + + pair.first.draw(prevTexName, pair.second); + } + prevTexName = pair.second.getTexName(); + + } else { + if (fbo != null) { + fbo.enable(); + } else { + GLES20.glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + if (pair.first != null) { + pair.first.draw(prevTexName, fbo); + } + } + } + } + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlGammaFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlGammaFilter.java new file mode 100644 index 00000000..4fb8a2b4 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlGammaFilter.java @@ -0,0 +1,36 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + +public class GlGammaFilter extends GlFilter { + private static final String GAMMA_FRAGMENT_SHADER = "" + + "precision mediump float;" + + " varying vec2 vTextureCoord;\n" + + " \n" + + " uniform lowp sampler2D sTexture;\n" + + " uniform lowp float gamma;\n" + + " \n" + + " void main()\n" + + " {\n" + + " lowp vec4 textureColor = texture2D(sTexture, vTextureCoord);\n" + + " \n" + + " gl_FragColor = vec4(pow(textureColor.rgb, vec3(gamma)), textureColor.w);\n" + + " }"; + + public GlGammaFilter() { + super(DEFAULT_VERTEX_SHADER, GAMMA_FRAGMENT_SHADER); + } + + private float gamma = 1.2f; + + public void setGamma(float gamma) { + this.gamma = gamma; + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("gamma"), gamma); + } + + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlGaussianBlurFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlGaussianBlurFilter.java new file mode 100644 index 00000000..f8c3fe9c --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlGaussianBlurFilter.java @@ -0,0 +1,100 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + +public class GlGaussianBlurFilter extends GlFilter { + + private static final String VERTEX_SHADER = + "attribute vec4 aPosition;" + + "attribute vec4 aTextureCoord;" + + + "const lowp int GAUSSIAN_SAMPLES = 9;" + + + "uniform highp float texelWidthOffset;" + + "uniform highp float texelHeightOffset;" + + "uniform highp float blurSize;" + + + "varying highp vec2 blurCoordinates[GAUSSIAN_SAMPLES];" + + + "void main() {" + + "gl_Position = aPosition;" + + "highp vec2 vTextureCoord = aTextureCoord.xy;" + + + // Calculate the positions for the blur + "int multiplier = 0;" + + "highp vec2 blurStep;" + + "highp vec2 singleStepOffset = vec2(texelHeightOffset, texelWidthOffset) * blurSize;" + + + "for (lowp int i = 0; i < GAUSSIAN_SAMPLES; i++) {" + + "multiplier = (i - ((GAUSSIAN_SAMPLES - 1) / 2));" + + // Blur in x (horizontal) + "blurStep = float(multiplier) * singleStepOffset;" + + "blurCoordinates[i] = vTextureCoord.xy + blurStep;" + + "}" + + "}"; + + private static final String FRAGMENT_SHADER = + "precision mediump float;" + + + "const lowp int GAUSSIAN_SAMPLES = 9;" + + "varying highp vec2 blurCoordinates[GAUSSIAN_SAMPLES];" + + + "uniform lowp sampler2D sTexture;" + + + "void main() {" + + "lowp vec4 sum = vec4(0.0);" + + + "sum += texture2D(sTexture, blurCoordinates[0]) * 0.05;" + + "sum += texture2D(sTexture, blurCoordinates[1]) * 0.09;" + + "sum += texture2D(sTexture, blurCoordinates[2]) * 0.12;" + + "sum += texture2D(sTexture, blurCoordinates[3]) * 0.15;" + + "sum += texture2D(sTexture, blurCoordinates[4]) * 0.18;" + + "sum += texture2D(sTexture, blurCoordinates[5]) * 0.15;" + + "sum += texture2D(sTexture, blurCoordinates[6]) * 0.12;" + + "sum += texture2D(sTexture, blurCoordinates[7]) * 0.09;" + + "sum += texture2D(sTexture, blurCoordinates[8]) * 0.05;" + + + "gl_FragColor = sum;" + + "}"; + + private float texelWidthOffset = 0.01f; + private float texelHeightOffset = 0.01f; + private float blurSize = 0.2f; + + public GlGaussianBlurFilter() { + super(VERTEX_SHADER, FRAGMENT_SHADER); + } + + public float getTexelWidthOffset() { + return texelWidthOffset; + } + + public void setTexelWidthOffset(final float texelWidthOffset) { + this.texelWidthOffset = texelWidthOffset; + } + + public float getTexelHeightOffset() { + return texelHeightOffset; + } + + public void setTexelHeightOffset(final float texelHeightOffset) { + this.texelHeightOffset = texelHeightOffset; + } + + public float getBlurSize() { + return blurSize; + } + + public void setBlurSize(final float blurSize) { + this.blurSize = blurSize; + } + + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("texelWidthOffset"), texelWidthOffset); + GLES20.glUniform1f(getHandle("texelHeightOffset"), texelHeightOffset); + GLES20.glUniform1f(getHandle("blurSize"), blurSize); + } + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlGrayScaleFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlGrayScaleFilter.java new file mode 100644 index 00000000..373ecc08 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlGrayScaleFilter.java @@ -0,0 +1,20 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + + +public class GlGrayScaleFilter extends GlFilter { + + private static final String FRAGMENT_SHADER = + "precision mediump float;" + + "varying vec2 vTextureCoord;" + + "uniform lowp sampler2D sTexture;" + + "const highp vec3 weight = vec3(0.2125, 0.7154, 0.0721);" + + "void main() {" + + "float luminance = dot(texture2D(sTexture, vTextureCoord).rgb, weight);" + + "gl_FragColor = vec4(vec3(luminance), 1.0);" + + "}"; + + public GlGrayScaleFilter() { + super(DEFAULT_VERTEX_SHADER, FRAGMENT_SHADER); + } + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlHalftoneFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlHalftoneFilter.java new file mode 100644 index 00000000..78a9d19b --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlHalftoneFilter.java @@ -0,0 +1,53 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + +public class GlHalftoneFilter extends GlFilter { + + private static final String HALFTONE_FRAGMENT_SHADER = "" + + "precision mediump float;" + + " varying vec2 vTextureCoord;\n" + + + " uniform lowp sampler2D sTexture;\n" + + + "uniform highp float fractionalWidthOfPixel;\n" + + "uniform highp float aspectRatio;\n" + + + "const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);\n" + + + "void main()\n" + + "{\n" + + " highp vec2 sampleDivisor = vec2(fractionalWidthOfPixel, fractionalWidthOfPixel / aspectRatio);\n" + + " highp vec2 samplePos = vTextureCoord - mod(vTextureCoord, sampleDivisor) + 0.5 * sampleDivisor;\n" + + " highp vec2 textureCoordinateToUse = vec2(vTextureCoord.x, (vTextureCoord.y * aspectRatio + 0.5 - 0.5 * aspectRatio));\n" + + " highp vec2 adjustedSamplePos = vec2(samplePos.x, (samplePos.y * aspectRatio + 0.5 - 0.5 * aspectRatio));\n" + + " highp float distanceFromSamplePoint = distance(adjustedSamplePos, textureCoordinateToUse);\n" + + " lowp vec3 sampledColor = texture2D(sTexture, samplePos).rgb;\n" + + " highp float dotScaling = 1.0 - dot(sampledColor, W);\n" + + " lowp float checkForPresenceWithinDot = 1.0 - step(distanceFromSamplePoint, (fractionalWidthOfPixel * 0.5) * dotScaling);\n" + + " gl_FragColor = vec4(vec3(checkForPresenceWithinDot), 1.0);\n" + + "}"; + + public GlHalftoneFilter() { + super(DEFAULT_VERTEX_SHADER, HALFTONE_FRAGMENT_SHADER); + } + + private float fractionalWidthOfPixel = 0.01f; + private float aspectRatio = 1f; + + public void setFractionalWidthOfAPixel(float fractionalWidthOfAPixel) { + this.fractionalWidthOfPixel = fractionalWidthOfAPixel; + } + + @Override + public void setFrameSize(int width, int height) { + super.setFrameSize(width, height); + aspectRatio = (float) height / (float) width; + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("fractionalWidthOfPixel"), fractionalWidthOfPixel); + GLES20.glUniform1f(getHandle("aspectRatio"), aspectRatio); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlHazeFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlHazeFilter.java new file mode 100644 index 00000000..62630a4c --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlHazeFilter.java @@ -0,0 +1,54 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + + + +public class GlHazeFilter extends GlFilter { + + private static final String FRAGMENT_SHADER = + "precision mediump float;" + + "varying highp vec2 vTextureCoord;" + + "uniform lowp sampler2D sTexture;" + + "uniform lowp float distance;" + + "uniform highp float slope;" + + + "void main() {" + + "highp vec4 color = vec4(1.0);" + + + "highp float d = vTextureCoord.y * slope + distance;" + + + "highp vec4 c = texture2D(sTexture, vTextureCoord);" + + "c = (c - d * color) / (1.0 -d);" + + "gl_FragColor = c;" + // consider using premultiply(c); + "}"; + + private float distance = 0.2f; + private float slope = 0.0f; + + public GlHazeFilter() { + super(DEFAULT_VERTEX_SHADER, FRAGMENT_SHADER); + } + + public float getDistance() { + return distance; + } + + public void setDistance(final float distance) { + this.distance = distance; + } + + public float getSlope() { + return slope; + } + + public void setSlope(final float slope) { + this.slope = slope; + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("distance"), distance); + GLES20.glUniform1f(getHandle("slope"), slope); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlHighlightShadowFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlHighlightShadowFilter.java new file mode 100644 index 00000000..a0abacb0 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlHighlightShadowFilter.java @@ -0,0 +1,49 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + +public class GlHighlightShadowFilter extends GlFilter { + + private static final String HIGHLIGHT_SHADOW_FRAGMENT_SHADER = "" + + "precision mediump float;" + + " uniform lowp sampler2D sTexture;\n" + + " varying vec2 vTextureCoord;\n" + + " \n" + + " uniform lowp float shadows;\n" + + " uniform lowp float highlights;\n" + + " \n" + + " const mediump vec3 luminanceWeighting = vec3(0.3, 0.3, 0.3);\n" + + " \n" + + " void main()\n" + + " {\n" + + " lowp vec4 source = texture2D(sTexture, vTextureCoord);\n" + + " mediump float luminance = dot(source.rgb, luminanceWeighting);\n" + + " \n" + + " mediump float shadow = clamp((pow(luminance, 1.0/(shadows+1.0)) + (-0.76)*pow(luminance, 2.0/(shadows+1.0))) - luminance, 0.0, 1.0);\n" + + " mediump float highlight = clamp((1.0 - (pow(1.0-luminance, 1.0/(2.0-highlights)) + (-0.8)*pow(1.0-luminance, 2.0/(2.0-highlights)))) - luminance, -1.0, 0.0);\n" + + " lowp vec3 result = vec3(0.0, 0.0, 0.0) + ((luminance + shadow + highlight) - 0.0) * ((source.rgb - vec3(0.0, 0.0, 0.0))/(luminance - 0.0));\n" + + " \n" + + " gl_FragColor = vec4(result.rgb, source.a);\n" + + " }"; + + public GlHighlightShadowFilter() { + super(DEFAULT_VERTEX_SHADER, HIGHLIGHT_SHADOW_FRAGMENT_SHADER); + } + + private float shadows = 1f; + private float highlights = 0f; + + public void setShadows(float shadows) { + this.shadows = shadows; + } + + public void setHighlights(float highlights) { + this.highlights = highlights; + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("shadows"), shadows); + GLES20.glUniform1f(getHandle("highlights"), highlights); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlHueFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlHueFilter.java new file mode 100644 index 00000000..5f6b2249 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlHueFilter.java @@ -0,0 +1,67 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + +public class GlHueFilter extends GlFilter { + + private static final String HUE_FRAGMENT_SHADER = "" + + "precision highp float;\n" + + " varying vec2 vTextureCoord;\n" + + "\n" + + " uniform lowp sampler2D sTexture;\n" + + "uniform mediump float hueAdjust;\n" + + "const highp vec4 kRGBToYPrime = vec4 (0.299, 0.587, 0.114, 0.0);\n" + + "const highp vec4 kRGBToI = vec4 (0.595716, -0.274453, -0.321263, 0.0);\n" + + "const highp vec4 kRGBToQ = vec4 (0.211456, -0.522591, 0.31135, 0.0);\n" + + "\n" + + "const highp vec4 kYIQToR = vec4 (1.0, 0.9563, 0.6210, 0.0);\n" + + "const highp vec4 kYIQToG = vec4 (1.0, -0.2721, -0.6474, 0.0);\n" + + "const highp vec4 kYIQToB = vec4 (1.0, -1.1070, 1.7046, 0.0);\n" + + "\n" + + "void main ()\n" + + "{\n" + + " // Sample the input pixel\n" + + " highp vec4 color = texture2D(sTexture, vTextureCoord);\n" + + "\n" + + " // Convert to YIQ\n" + + " highp float YPrime = dot (color, kRGBToYPrime);\n" + + " highp float I = dot (color, kRGBToI);\n" + + " highp float Q = dot (color, kRGBToQ);\n" + + "\n" + + " // Calculate the hue and chroma\n" + + " highp float hue = atan (Q, I);\n" + + " highp float chroma = sqrt (I * I + Q * Q);\n" + + "\n" + + " // Make the user's adjustments\n" + + " hue += (-hueAdjust); //why negative rotation?\n" + + "\n" + + " // Convert back to YIQ\n" + + " Q = chroma * sin (hue);\n" + + " I = chroma * cos (hue);\n" + + "\n" + + " // Convert back to RGB\n" + + " highp vec4 yIQ = vec4 (YPrime, I, Q, 0.0);\n" + + " color.r = dot (yIQ, kYIQToR);\n" + + " color.g = dot (yIQ, kYIQToG);\n" + + " color.b = dot (yIQ, kYIQToB);\n" + + "\n" + + " // Save the result\n" + + " gl_FragColor = color;\n" + + "}\n"; + + public GlHueFilter() { + super(DEFAULT_VERTEX_SHADER, HUE_FRAGMENT_SHADER); + } + + private float hue = 90f; + + public void setHue(float hue) { + this.hue = hue; + } + + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("hueAdjust"), hue); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlInvertFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlInvertFilter.java new file mode 100644 index 00000000..7ac3739a --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlInvertFilter.java @@ -0,0 +1,18 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + + + +public class GlInvertFilter extends GlFilter { + private static final String FRAGMENT_SHADER = + "precision mediump float;" + + "varying vec2 vTextureCoord;" + + "uniform lowp sampler2D sTexture;" + + "void main() {" + + "lowp vec4 color = texture2D(sTexture, vTextureCoord);" + + "gl_FragColor = vec4((1.0 - color.rgb), color.w);" + + "}"; + + public GlInvertFilter() { + super(DEFAULT_VERTEX_SHADER, FRAGMENT_SHADER); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlLookUpTableFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlLookUpTableFilter.java new file mode 100644 index 00000000..db35135c --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlLookUpTableFilter.java @@ -0,0 +1,92 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.opengl.GLES20; + +import xyz.doikki.dkplayer.widget.render.gl2.EglUtil; + + + + +public class GlLookUpTableFilter extends GlFilter { + + private final static String FRAGMENT_SHADER = + "precision mediump float;" + + "uniform mediump sampler2D lutTexture; \n" + + "uniform lowp sampler2D sTexture; \n" + + "varying highp vec2 vTextureCoord; \n" + + "vec4 sampleAs3DTexture(vec3 uv) {\n" + + " float width = 16.;\n" + + " float sliceSize = 1.0 / width;\n" + + " float slicePixelSize = sliceSize / width;\n" + + " float sliceInnerSize = slicePixelSize * (width - 1.0);\n" + + " float zSlice0 = min(floor(uv.z * width), width - 1.0);\n" + + " float zSlice1 = min(zSlice0 + 1.0, width - 1.0);\n" + + " float xOffset = slicePixelSize * 0.5 + uv.x * sliceInnerSize;\n" + + " float s0 = xOffset + (zSlice0 * sliceSize);\n" + + " float s1 = xOffset + (zSlice1 * sliceSize);\n" + + " vec4 slice0Color = texture2D(lutTexture, vec2(s0, uv.y));\n" + + " vec4 slice1Color = texture2D(lutTexture, vec2(s1, uv.y));\n" + + " float zOffset = mod(uv.z * width, 1.0);\n" + + " vec4 result = mix(slice0Color, slice1Color, zOffset);\n" + + " return result;\n" + + "}\n" + + "void main() {\n" + + " vec4 pixel = texture2D(sTexture, vTextureCoord);\n" + + " vec4 gradedPixel = sampleAs3DTexture(pixel.rgb);\n" + + " gradedPixel.a = pixel.a;\n" + + " pixel = gradedPixel;\n" + + " gl_FragColor = pixel;\n " + + "}"; + + public GlLookUpTableFilter(Bitmap bitmap) { + super(DEFAULT_VERTEX_SHADER, FRAGMENT_SHADER); + this.lutTexture = bitmap; + hTex = EglUtil.NO_TEXTURE; + } + + + public GlLookUpTableFilter(Resources resources, int fxID) { + super(DEFAULT_VERTEX_SHADER, FRAGMENT_SHADER); + this.lutTexture = BitmapFactory.decodeResource(resources, fxID); + hTex = EglUtil.NO_TEXTURE; + } + + private int hTex; + + private Bitmap lutTexture; + + @Override + public void onDraw() { + int offsetDepthMapTextureUniform = getHandle("lutTexture"); + GLES20.glActiveTexture(GLES20.GL_TEXTURE3); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, hTex); + GLES20.glUniform1i(offsetDepthMapTextureUniform, 3); + } + + @Override + public void setup() { + super.setup(); + loadTexture(); + } + + private void loadTexture() { + if (hTex == EglUtil.NO_TEXTURE) { + hTex = EglUtil.loadTexture(lutTexture, EglUtil.NO_TEXTURE, false); + } + } + + public void releaseLutBitmap() { + if (lutTexture != null && !lutTexture.isRecycled()) { + lutTexture.recycle(); + lutTexture = null; + } + } + + public void reset() { + hTex = EglUtil.NO_TEXTURE; + hTex = EglUtil.loadTexture(lutTexture, EglUtil.NO_TEXTURE, false); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlLuminanceFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlLuminanceFilter.java new file mode 100644 index 00000000..21092cc9 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlLuminanceFilter.java @@ -0,0 +1,26 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +public class GlLuminanceFilter extends GlFilter { + + private static final String LUMINANCE_FRAGMENT_SHADER = "" + + "precision mediump float;" + + "\n" + + " varying vec2 vTextureCoord;\n" + + "\n" + + " uniform lowp sampler2D sTexture;\n" + + "\n" + + "// Values from \"Graphics Shaders: Theory and Practice\" by Bailey and Cunningham\n" + + "const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);\n" + + "\n" + + "void main()\n" + + "{\n" + + " lowp vec4 textureColor = texture2D(sTexture, vTextureCoord);\n" + + " float luminance = dot(textureColor.rgb, W);\n" + + " \n" + + " gl_FragColor = vec4(vec3(luminance), textureColor.a);\n" + + "}"; + + public GlLuminanceFilter() { + super(DEFAULT_VERTEX_SHADER, LUMINANCE_FRAGMENT_SHADER); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlLuminanceThresholdFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlLuminanceThresholdFilter.java new file mode 100644 index 00000000..f42ab4a6 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlLuminanceThresholdFilter.java @@ -0,0 +1,39 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + +public class GlLuminanceThresholdFilter extends GlFilter { + + private static final String LUMINANCE_THRESHOLD_FRAGMENT_SHADER = "" + + "precision mediump float;" + + "varying highp vec2 vTextureCoord;\n" + + "\n" + + "uniform lowp sampler2D sTexture;\n" + + "uniform highp float threshold;\n" + + "\n" + + "const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);\n" + + "\n" + + "void main()\n" + + "{\n" + + " highp vec4 textureColor = texture2D(sTexture, vTextureCoord);\n" + + " highp float luminance = dot(textureColor.rgb, W);\n" + + " highp float thresholdResult = step(threshold, luminance);\n" + + " \n" + + " gl_FragColor = vec4(vec3(thresholdResult), textureColor.w);\n" + + "}"; + + public GlLuminanceThresholdFilter() { + super(DEFAULT_VERTEX_SHADER, LUMINANCE_THRESHOLD_FRAGMENT_SHADER); + } + + private float threshold = 0.5f; + + public void setThreshold(float threshold) { + this.threshold = threshold; + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("threshold"), threshold); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlMonochromeFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlMonochromeFilter.java new file mode 100644 index 00000000..4cf2327c --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlMonochromeFilter.java @@ -0,0 +1,54 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + + + +public class GlMonochromeFilter extends GlFilter { + + private static final String FRAGMENT_SHADER = + "precision lowp float;" + + + "varying highp vec2 vTextureCoord;" + + "uniform lowp sampler2D sTexture;" + + "uniform float intensity;" + + + "const mediump vec3 luminanceWeighting = vec3(0.2125, 0.7154, 0.0721);" + + + "void main() {" + + + "lowp vec4 textureColor = texture2D(sTexture, vTextureCoord);" + + "float luminance = dot(textureColor.rgb, luminanceWeighting);" + + + "lowp vec4 desat = vec4(vec3(luminance), 1.0);" + + + "lowp vec4 outputColor = vec4(" + + "(desat.r < 0.5 ? (2.0 * desat.r * 0.6) : (1.0 - 2.0 * (1.0 - desat.r) * (1.0 - 0.6)))," + + "(desat.g < 0.5 ? (2.0 * desat.g * 0.45) : (1.0 - 2.0 * (1.0 - desat.g) * (1.0 - 0.45)))," + + "(desat.b < 0.5 ? (2.0 * desat.b * 0.3) : (1.0 - 2.0 * (1.0 - desat.b) * (1.0 - 0.3)))," + + "1.0" + + ");" + + + "gl_FragColor = vec4(mix(textureColor.rgb, outputColor.rgb, intensity), textureColor.a);" + + "}"; + + private float intensity = 1.0f; + + public GlMonochromeFilter() { + super(DEFAULT_VERTEX_SHADER, FRAGMENT_SHADER); + } + + public float getIntensity() { + return intensity; + } + + public void setIntensity(float intensity) { + this.intensity = intensity; + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("intensity"), intensity); + } + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlOpacityFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlOpacityFilter.java new file mode 100644 index 00000000..64f9b87b --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlOpacityFilter.java @@ -0,0 +1,40 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + +/** + * Adjusts the alpha channel of the incoming image + * opacity: The value to multiply the incoming alpha channel for each pixel by (0.0 - 1.0, with 1.0 as the default) + */ +public class GlOpacityFilter extends GlFilter { + + private static final String OPACITY_FRAGMENT_SHADER = "" + + "precision mediump float;" + + " varying highp vec2 vTextureCoord;\n" + + " \n" + + " uniform lowp sampler2D sTexture;\n" + + " uniform lowp float opacity;\n" + + " \n" + + " void main()\n" + + " {\n" + + " lowp vec4 textureColor = texture2D(sTexture, vTextureCoord);\n" + + " \n" + + " gl_FragColor = vec4(textureColor.rgb, textureColor.a * opacity);\n" + + " }\n"; + + public GlOpacityFilter() { + super(DEFAULT_VERTEX_SHADER, OPACITY_FRAGMENT_SHADER); + } + + private float opacity = 1f; + + public void setOpacity(float opacity) { + this.opacity = opacity; + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("opacity"), opacity); + } + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlOverlayFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlOverlayFilter.java new file mode 100644 index 00000000..1729a9e4 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlOverlayFilter.java @@ -0,0 +1,101 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.opengl.GLES20; +import android.opengl.GLUtils; + +import xyz.doikki.dkplayer.widget.render.gl2.Resolution; + + + + +public abstract class GlOverlayFilter extends GlFilter { + + private final int[] textures = new int[1]; + + private Bitmap bitmap = null; + + protected Resolution inputResolution = new Resolution(1280, 720); + + public GlOverlayFilter() { + super(DEFAULT_VERTEX_SHADER, FRAGMENT_SHADER); + } + + private final static String FRAGMENT_SHADER = + "precision mediump float;\n" + + "varying vec2 vTextureCoord;\n" + + "uniform lowp sampler2D sTexture;\n" + + "uniform lowp sampler2D oTexture;\n" + + "void main() {\n" + + " lowp vec4 textureColor = texture2D(sTexture, vTextureCoord);\n" + + " lowp vec4 textureColor2 = texture2D(oTexture, vTextureCoord);\n" + + " \n" + + " gl_FragColor = mix(textureColor, textureColor2, textureColor2.a);\n" + + "}\n"; + + public void setResolution(Resolution resolution) { + this.inputResolution = resolution; + } + + @Override + public void setFrameSize(int width, int height) { + super.setFrameSize(width, height); + setResolution(new Resolution(width, height)); + } + + private void createBitmap() { + releaseBitmap(bitmap); + bitmap = Bitmap.createBitmap(inputResolution.width(), inputResolution.height(), Bitmap.Config.ARGB_8888); + } + + @Override + public void setup() { + super.setup();// 1 + GLES20.glGenTextures(1, textures, 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); + + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + + createBitmap(); + } + + @Override + public void onDraw() { + if (bitmap == null) { + createBitmap(); + } + if (bitmap.getWidth() != inputResolution.width() || bitmap.getHeight() != inputResolution.height()) { + createBitmap(); + } + + bitmap.eraseColor(Color.argb(0, 0, 0, 0)); + Canvas bitmapCanvas = new Canvas(bitmap); + bitmapCanvas.scale(1, -1, bitmapCanvas.getWidth() / 2, bitmapCanvas.getHeight() / 2); + drawCanvas(bitmapCanvas); + + int offsetDepthMapTextureUniform = getHandle("oTexture");// 3 + + GLES20.glActiveTexture(GLES20.GL_TEXTURE3); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); + + if (bitmap != null && !bitmap.isRecycled()) { + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, bitmap, 0); + } + + GLES20.glUniform1i(offsetDepthMapTextureUniform, 3); + } + + protected abstract void drawCanvas(Canvas canvas); + + public static void releaseBitmap(Bitmap bitmap) { + if (bitmap != null && !bitmap.isRecycled()) { + bitmap.recycle(); + bitmap = null; + } + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlPixelationFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlPixelationFilter.java new file mode 100644 index 00000000..30cc2fb9 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlPixelationFilter.java @@ -0,0 +1,48 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + +public class GlPixelationFilter extends GlFilter { + + private static final String PIXELATION_FRAGMENT_SHADER = "" + + "precision highp float;\n" + + + "varying highp vec2 vTextureCoord;\n" + + + "uniform float imageWidthFactor;\n" + + "uniform float imageHeightFactor;\n" + + "uniform lowp sampler2D sTexture;\n" + + "uniform float pixel;\n" + + + "void main()\n" + + "{\n" + + " vec2 uv = vTextureCoord.xy;\n" + + " float dx = pixel * imageWidthFactor;\n" + + " float dy = pixel * imageHeightFactor;\n" + + " vec2 coord = vec2(dx * floor(uv.x / dx), dy * floor(uv.y / dy));\n" + + " vec3 tc = texture2D(sTexture, coord).xyz;\n" + + " gl_FragColor = vec4(tc, 1.0);\n" + + "}"; + + public GlPixelationFilter() { + super(DEFAULT_VERTEX_SHADER, PIXELATION_FRAGMENT_SHADER); + } + + private final float pixel = 1f; + private float imageWidthFactor = 1f / 720; + private float imageHeightFactor = 1f / 720; + + @Override + public void setFrameSize(int width, int height) { + super.setFrameSize(width, height); + imageWidthFactor = 1f / width; + imageHeightFactor = 1f / height; + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("pixel"), pixel); + GLES20.glUniform1f(getHandle("imageWidthFactor"), imageWidthFactor); + GLES20.glUniform1f(getHandle("imageHeightFactor"), imageHeightFactor); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlPosterizeFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlPosterizeFilter.java new file mode 100644 index 00000000..d2409b9d --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlPosterizeFilter.java @@ -0,0 +1,41 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + +public class GlPosterizeFilter extends GlFilter { + + private static final String POSTERIZE_FRAGMENT_SHADER = "" + + "precision mediump float;" + + " varying vec2 vTextureCoord;\n" + + "\n" + + "uniform lowp sampler2D sTexture;\n" + + "uniform highp float colorLevels;\n" + + "\n" + + "void main()\n" + + "{\n" + + " highp vec4 textureColor = texture2D(sTexture, vTextureCoord);\n" + + " \n" + + " gl_FragColor = floor((textureColor * colorLevels) + vec4(0.5)) / colorLevels;\n" + + "}"; + + public GlPosterizeFilter() { + super(DEFAULT_VERTEX_SHADER, POSTERIZE_FRAGMENT_SHADER); + } + + private int colorLevels = 10; + + public void setColorLevels(int colorLevels) { + if (colorLevels < 0) { + this.colorLevels = 0; + } else if (colorLevels > 256) { + this.colorLevels = 256; + } else { + this.colorLevels = colorLevels; + } + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("colorLevels"), colorLevels); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlPreviewFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlPreviewFilter.java new file mode 100644 index 00000000..5e8c3b4a --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlPreviewFilter.java @@ -0,0 +1,74 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import static android.opengl.GLES20.GL_ARRAY_BUFFER; +import static android.opengl.GLES20.GL_FLOAT; +import static android.opengl.GLES20.GL_TEXTURE0; +import static android.opengl.GLES20.GL_TEXTURE_2D; +import static android.opengl.GLES20.GL_TRIANGLE_STRIP; + +import android.opengl.GLES20; + + + +public class GlPreviewFilter extends GlFilter { + + public static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65; + + private static final String VERTEX_SHADER = + "uniform mat4 uMVPMatrix;\n" + + "uniform mat4 uSTMatrix;\n" + + "uniform float uCRatio;\n" + + + "attribute vec4 aPosition;\n" + + "attribute vec4 aTextureCoord;\n" + + "varying highp vec2 vTextureCoord;\n" + + + "void main() {\n" + + "vec4 scaledPos = aPosition;\n" + + "scaledPos.x = scaledPos.x * uCRatio;\n" + + "gl_Position = uMVPMatrix * scaledPos;\n" + + "vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" + + "}\n"; + + private final int texTarget; + + public GlPreviewFilter(final int texTarget) { + super(VERTEX_SHADER, createFragmentShaderSourceOESIfNeed(texTarget)); + this.texTarget = texTarget; + } + + private static String createFragmentShaderSourceOESIfNeed(final int texTarget) { + if (texTarget == GL_TEXTURE_EXTERNAL_OES) { + return new StringBuilder() + .append("#extension GL_OES_EGL_image_external : require\n") + .append(DEFAULT_FRAGMENT_SHADER.replace("sampler2D", "samplerExternalOES")) + .toString(); + } + return DEFAULT_FRAGMENT_SHADER; + } + + public void draw(final int texName, final float[] mvpMatrix, final float[] stMatrix, final float aspectRatio) { + useProgram(); + + GLES20.glUniformMatrix4fv(getHandle("uMVPMatrix"), 1, false, mvpMatrix, 0); + GLES20.glUniformMatrix4fv(getHandle("uSTMatrix"), 1, false, stMatrix, 0); + GLES20.glUniform1f(getHandle("uCRatio"), aspectRatio); + + GLES20.glBindBuffer(GL_ARRAY_BUFFER, getVertexBufferName()); + GLES20.glEnableVertexAttribArray(getHandle("aPosition")); + GLES20.glVertexAttribPointer(getHandle("aPosition"), VERTICES_DATA_POS_SIZE, GL_FLOAT, false, VERTICES_DATA_STRIDE_BYTES, VERTICES_DATA_POS_OFFSET); + GLES20.glEnableVertexAttribArray(getHandle("aTextureCoord")); + GLES20.glVertexAttribPointer(getHandle("aTextureCoord"), VERTICES_DATA_UV_SIZE, GL_FLOAT, false, VERTICES_DATA_STRIDE_BYTES, VERTICES_DATA_UV_OFFSET); + + GLES20.glActiveTexture(GL_TEXTURE0); + GLES20.glBindTexture(texTarget, texName); + GLES20.glUniform1i(getHandle(DEFAULT_UNIFORM_SAMPLER), 0); + + GLES20.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + GLES20.glDisableVertexAttribArray(getHandle("aPosition")); + GLES20.glDisableVertexAttribArray(getHandle("aTextureCoord")); + GLES20.glBindBuffer(GL_ARRAY_BUFFER, 0); + GLES20.glBindTexture(GL_TEXTURE_2D, 0); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlRGBFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlRGBFilter.java new file mode 100644 index 00000000..b02e38e4 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlRGBFilter.java @@ -0,0 +1,84 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + +/** + * Adjusts the individual RGB channels of an image + * red: Normalized values by which each color channel is multiplied. The range is from 0.0 up, with 1.0 as the default. + * green: + * blue: + */ +public class GlRGBFilter extends GlFilter { + + private static final String RGB_FRAGMENT_SHADER = "" + + "precision mediump float;" + + " varying vec2 vTextureCoord;\n" + + " \n" + + " uniform lowp sampler2D sTexture;\n" + + " uniform highp float red;\n" + + " uniform highp float green;\n" + + " uniform highp float blue;\n" + + " uniform lowp float brightness;\n" + + " uniform lowp float saturation;\n" + + " uniform lowp float contrast;\n" + + " \n" + + " const mediump vec3 luminanceWeighting = vec3(0.2125, 0.7154, 0.0721);\n" + + " \n" + + " void main()\n" + + " {\n" + + " highp vec4 textureColor = texture2D(sTexture, vTextureCoord);\n" + + " lowp vec4 textureOtherColor = texture2D(sTexture, vTextureCoord);\n" + + " lowp float luminance = dot(textureOtherColor.rgb, luminanceWeighting);\n" + + " lowp vec3 greyScaleColor = vec3(luminance);\n" + + " \n" + + " gl_FragColor = vec4(textureColor.r * red, textureColor.g * green, textureColor.b * blue, 1.0);\n" + + " gl_FragColor = vec4((textureOtherColor.rgb + vec3(brightness)), textureOtherColor.w);\n" + + " gl_FragColor = vec4(mix(greyScaleColor, textureColor.rgb, saturation), textureOtherColor.w);\n" + + " gl_FragColor = vec4(((textureOtherColor.rgb - vec3(0.5)) * contrast + vec3(0.5)), textureOtherColor.w);\n" + + " }\n"; + + public GlRGBFilter() { + super(DEFAULT_VERTEX_SHADER, RGB_FRAGMENT_SHADER); + } + + private float red = 1f; + private float green = 1f; + private float blue = 1f; + private float brightness = 0f; + private float saturation = 1f; + private float contrast = 1.2f; + + public void setRed(float red) { + this.red = red; + } + + public void setGreen(float green) { + this.green = green; + } + + public void setBlue(float blue) { + this.blue = blue; + } + + public void setBrightness(float brightness) { + this.brightness = brightness; + } + + public void setSaturation(float saturation) { + this.saturation = saturation; + } + + public void setContrast(float contrast) { + this.contrast = contrast; + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("red"), red); + GLES20.glUniform1f(getHandle("green"), green); + GLES20.glUniform1f(getHandle("blue"), blue); + GLES20.glUniform1f(getHandle("brightness"), brightness); + GLES20.glUniform1f(getHandle("saturation"), saturation); + GLES20.glUniform1f(getHandle("contrast"), contrast); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSaturationFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSaturationFilter.java new file mode 100644 index 00000000..2045e281 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSaturationFilter.java @@ -0,0 +1,41 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + +public class GlSaturationFilter extends GlFilter { + private static final String SATURATION_FRAGMENT_SHADER = "" + + "precision mediump float;" + + " varying vec2 vTextureCoord;\n" + + " \n" + + " uniform lowp sampler2D sTexture;\n" + + " uniform lowp float saturation;\n" + + " \n" + + " const mediump vec3 luminanceWeighting = vec3(0.2125, 0.7154, 0.0721);\n" + + " \n" + + " void main()\n" + + " {\n" + + " lowp vec4 textureColor = texture2D(sTexture, vTextureCoord);\n" + + " lowp float luminance = dot(textureColor.rgb, luminanceWeighting);\n" + + " lowp vec3 greyScaleColor = vec3(luminance);\n" + + " \n" + + " gl_FragColor = vec4(mix(greyScaleColor, textureColor.rgb, saturation), textureColor.w);\n" + + " \n" + + " }"; + + + public GlSaturationFilter() { + super(DEFAULT_VERTEX_SHADER, SATURATION_FRAGMENT_SHADER); + } + + private float saturation = 1f; + + public void setSaturation(float saturation) { + this.saturation = saturation; + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("saturation"), saturation); + } + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSepiaFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSepiaFilter.java new file mode 100644 index 00000000..cd684fa3 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSepiaFilter.java @@ -0,0 +1,20 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + + +public class GlSepiaFilter extends GlFilter { + private static final String FRAGMENT_SHADER = + "precision mediump float;" + + "varying vec2 vTextureCoord;" + + "uniform lowp sampler2D sTexture;" + + "const highp vec3 weight = vec3(0.2125, 0.7154, 0.0721);" + + "void main() {" + + " vec4 FragColor = texture2D(sTexture, vTextureCoord);\n" + + " gl_FragColor.r = dot(FragColor.rgb, vec3(.393, .769, .189));\n" + + " gl_FragColor.g = dot(FragColor.rgb, vec3(.349, .686, .168));\n" + + " gl_FragColor.b = dot(FragColor.rgb, vec3(.272, .534, .131));\n" + + "}"; + + public GlSepiaFilter() { + super(DEFAULT_VERTEX_SHADER, FRAGMENT_SHADER); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSharpenFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSharpenFilter.java new file mode 100644 index 00000000..0eebdc18 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSharpenFilter.java @@ -0,0 +1,96 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + + + +public class GlSharpenFilter extends GlFilter { + + private static final String VERTEX_SHADER = + "attribute vec4 aPosition;" + + "attribute vec4 aTextureCoord;" + + + "uniform float imageWidthFactor;" + + "uniform float imageHeightFactor;" + + "uniform float sharpness;" + + + "varying highp vec2 textureCoordinate;" + + "varying highp vec2 leftTextureCoordinate;" + + "varying highp vec2 rightTextureCoordinate;" + + "varying highp vec2 topTextureCoordinate;" + + "varying highp vec2 bottomTextureCoordinate;" + + + "varying float centerMultiplier;" + + "varying float edgeMultiplier;" + + + "void main() {" + + "gl_Position = aPosition;" + + + "mediump vec2 widthStep = vec2(imageWidthFactor, 0.0);" + + "mediump vec2 heightStep = vec2(0.0, imageHeightFactor);" + + + "textureCoordinate = aTextureCoord.xy;" + + "leftTextureCoordinate = textureCoordinate - widthStep;" + + "rightTextureCoordinate = textureCoordinate + widthStep;" + + "topTextureCoordinate = textureCoordinate + heightStep;" + + "bottomTextureCoordinate = textureCoordinate - heightStep;" + + + "centerMultiplier = 1.0 + 4.0 * sharpness;" + + "edgeMultiplier = sharpness;" + + "}"; + + private static final String FRAGMENT_SHADER = + "precision highp float;" + + + "uniform lowp sampler2D sTexture;" + + + "varying highp vec2 textureCoordinate;" + + "varying highp vec2 leftTextureCoordinate;" + + "varying highp vec2 rightTextureCoordinate;" + + "varying highp vec2 topTextureCoordinate;" + + "varying highp vec2 bottomTextureCoordinate;" + + + "varying float centerMultiplier;" + + "varying float edgeMultiplier;" + + + "void main() {" + + "mediump vec3 textureColor = texture2D(sTexture, textureCoordinate).rgb;" + + "mediump vec3 leftTextureColor = texture2D(sTexture, leftTextureCoordinate).rgb;" + + "mediump vec3 rightTextureColor = texture2D(sTexture, rightTextureCoordinate).rgb;" + + "mediump vec3 topTextureColor = texture2D(sTexture, topTextureCoordinate).rgb;" + + "mediump vec3 bottomTextureColor = texture2D(sTexture, bottomTextureCoordinate).rgb;" + + + "gl_FragColor = vec4((textureColor * centerMultiplier - (leftTextureColor * edgeMultiplier + rightTextureColor * edgeMultiplier + topTextureColor * edgeMultiplier + bottomTextureColor * edgeMultiplier)), texture2D(sTexture, bottomTextureCoordinate).w);" + + "}"; + + private float imageWidthFactor = 0.004f; + private float imageHeightFactor = 0.004f; + private float sharpness = 1.f; + + public GlSharpenFilter() { + super(VERTEX_SHADER, FRAGMENT_SHADER); + } + + public float getSharpness() { + return sharpness; + } + + public void setSharpness(final float sharpness) { + this.sharpness = sharpness; + } + + + @Override + public void setFrameSize(final int width, final int height) { + imageWidthFactor = 1f / width; + imageHeightFactor = 1f / height; + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("imageWidthFactor"), imageWidthFactor); + GLES20.glUniform1f(getHandle("imageHeightFactor"), imageHeightFactor); + GLES20.glUniform1f(getHandle("sharpness"), sharpness); + } + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSolarizeFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSolarizeFilter.java new file mode 100644 index 00000000..7f380588 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSolarizeFilter.java @@ -0,0 +1,40 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + +public class GlSolarizeFilter extends GlFilter { + + private static final String SOLATIZE_FRAGMENT_SHADER = "" + + "precision mediump float;" + + " varying vec2 vTextureCoord;\n" + + "\n" + + " uniform lowp sampler2D sTexture;\n" + + " uniform highp float threshold;\n" + + "\n" + + " const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);\n" + + "\n" + + "void main()\n" + + "{\n" + + " highp vec4 textureColor = texture2D(sTexture, vTextureCoord);\n" + + " highp float luminance = dot(textureColor.rgb, W);\n" + + " highp float thresholdResult = step(luminance, threshold);\n" + + " highp vec3 finalColor = abs(thresholdResult - textureColor.rgb);\n" + + " \n" + + " gl_FragColor = vec4(finalColor, textureColor.w);\n" + + "}"; + + public GlSolarizeFilter() { + super(DEFAULT_VERTEX_SHADER, SOLATIZE_FRAGMENT_SHADER); + } + + private float threshold = 0.5f; + + public void setThreshold(float threshold) { + this.threshold = threshold; + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("threshold"), threshold); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSphereRefractionFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSphereRefractionFilter.java new file mode 100644 index 00000000..dd00e59f --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSphereRefractionFilter.java @@ -0,0 +1,73 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + + + +public class GlSphereRefractionFilter extends GlFilter { + + private static final String FRAGMENT_SHADER = + "precision mediump float;" + + + "varying vec2 vTextureCoord;" + + "uniform lowp sampler2D sTexture;" + + "uniform highp vec2 center;" + + "uniform highp float radius;" + + "uniform highp float aspectRatio;" + + "uniform highp float refractiveIndex;" + + + "void main() {" + + "highp vec2 textureCoordinateToUse = vec2(vTextureCoord.x, (vTextureCoord.y * aspectRatio + 0.5 - 0.5 * aspectRatio));" + + "highp float distanceFromCenter = distance(center, textureCoordinateToUse);" + + "lowp float checkForPresenceWithinSphere = step(distanceFromCenter, radius);" + + + "distanceFromCenter = distanceFromCenter / radius;" + + + "highp float normalizedDepth = radius * sqrt(1.0 - distanceFromCenter * distanceFromCenter);" + + "highp vec3 sphereNormal = normalize(vec3(textureCoordinateToUse - center, normalizedDepth));" + + + "highp vec3 refractedVector = refract(vec3(0.0, 0.0, -1.0), sphereNormal, refractiveIndex);" + + + "gl_FragColor = texture2D(sTexture, (refractedVector.xy + 1.0) * 0.5) * checkForPresenceWithinSphere;" + + "}"; + + private float centerX = 0.5f; + private float centerY = 0.5f; + private float radius = 0.5f; + private float aspectRatio = 1.0f; + private float refractiveIndex = 0.71f; + + public GlSphereRefractionFilter() { + super(DEFAULT_VERTEX_SHADER, FRAGMENT_SHADER); + } + + public void setCenterX(float centerX) { + this.centerX = centerX; + } + + public void setCenterY(float centerY) { + this.centerY = centerY; + } + + public void setRadius(float radius) { + this.radius = radius; + } + + public void setAspectRatio(float aspectRatio) { + this.aspectRatio = aspectRatio; + } + + public void setRefractiveIndex(float refractiveIndex) { + this.refractiveIndex = refractiveIndex; + } + + ////////////////////////////////////////////////////////////////////////// + + @Override + public void onDraw() { + GLES20.glUniform2f(getHandle("center"), centerX, centerY); + GLES20.glUniform1f(getHandle("radius"), radius); + GLES20.glUniform1f(getHandle("aspectRatio"), aspectRatio); + GLES20.glUniform1f(getHandle("refractiveIndex"), refractiveIndex); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSwirlFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSwirlFilter.java new file mode 100644 index 00000000..88aca2d6 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlSwirlFilter.java @@ -0,0 +1,65 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.graphics.PointF; +import android.opengl.GLES20; + +public class GlSwirlFilter extends GlFilter { + + private static final String SWIRL_FRAGMENT_SHADER = "" + + "precision mediump float;" + + " varying vec2 vTextureCoord;\n" + + "\n" + + " uniform lowp sampler2D sTexture;\n" + + "\n" + + "uniform highp vec2 center;\n" + + "uniform highp float radius;\n" + + "uniform highp float angle;\n" + + "\n" + + "void main()\n" + + "{\n" + + "highp vec2 textureCoordinateToUse = vTextureCoord;\n" + + "highp float dist = distance(center, vTextureCoord);\n" + + "if (dist < radius)\n" + + "{\n" + + "textureCoordinateToUse -= center;\n" + + "highp float percent = (radius - dist) / radius;\n" + + "highp float theta = percent * percent * angle * 8.0;\n" + + "highp float s = sin(theta);\n" + + "highp float c = cos(theta);\n" + + "textureCoordinateToUse = vec2(dot(textureCoordinateToUse, vec2(c, -s)), dot(textureCoordinateToUse, vec2(s, c)));\n" + + "textureCoordinateToUse += center;\n" + + "}\n" + + "\n" + + "gl_FragColor = texture2D(sTexture, textureCoordinateToUse );\n" + + "\n" + + "}\n"; + + public GlSwirlFilter() { + super(DEFAULT_VERTEX_SHADER, SWIRL_FRAGMENT_SHADER); + } + + private float angle = 1.0f; + private float radius = 0.5f; + private PointF center = new PointF(0.5f, 0.5f); + + public void setAngle(float angle) { + this.angle = angle; + } + + public void setRadius(float radius) { + this.radius = radius; + } + + public void setCenter(PointF center) { + this.center = center; + } + + @Override + public void onDraw() { + GLES20.glUniform2f(getHandle("center"), center.x, center.y); + GLES20.glUniform1f(getHandle("radius"), radius); + GLES20.glUniform1f(getHandle("angle"), angle); + } + + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlThreex3TextureSamplingFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlThreex3TextureSamplingFilter.java new file mode 100644 index 00000000..f2c0fbe6 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlThreex3TextureSamplingFilter.java @@ -0,0 +1,83 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + + + +public class GlThreex3TextureSamplingFilter extends GlFilter { + private static final String THREE_X_THREE_TEXTURE_SAMPLING_VERTEX_SHADER = + "attribute vec4 aPosition;" + + "attribute vec4 aTextureCoord;" + + + "uniform highp float texelWidth;" + + "uniform highp float texelHeight;" + + + "varying highp vec2 textureCoordinate;" + + "varying highp vec2 leftTextureCoordinate;" + + "varying highp vec2 rightTextureCoordinate;" + + + "varying highp vec2 topTextureCoordinate;" + + "varying highp vec2 topLeftTextureCoordinate;" + + "varying highp vec2 topRightTextureCoordinate;" + + + "varying highp vec2 bottomTextureCoordinate;" + + "varying highp vec2 bottomLeftTextureCoordinate;" + + "varying highp vec2 bottomRightTextureCoordinate;" + + + "void main() {" + + "gl_Position = aPosition;" + + + "vec2 widthStep = vec2(texelWidth, 0.0);" + + "vec2 heightStep = vec2(0.0, texelHeight);" + + "vec2 widthHeightStep = vec2(texelWidth, texelHeight);" + + "vec2 widthNegativeHeightStep = vec2(texelWidth, -texelHeight);" + + + "textureCoordinate = aTextureCoord.xy;" + + "leftTextureCoordinate = textureCoordinate - widthStep;" + + "rightTextureCoordinate = textureCoordinate + widthStep;" + + + "topTextureCoordinate = textureCoordinate - heightStep;" + + "topLeftTextureCoordinate = textureCoordinate - widthHeightStep;" + + "topRightTextureCoordinate = textureCoordinate + widthNegativeHeightStep;" + + + "bottomTextureCoordinate = textureCoordinate + heightStep;" + + "bottomLeftTextureCoordinate = textureCoordinate - widthNegativeHeightStep;" + + "bottomRightTextureCoordinate = textureCoordinate + widthHeightStep;" + + "}"; + + private float texelWidth; + private float texelHeight; + + public GlThreex3TextureSamplingFilter(String fragmentShaderSource) { + super(THREE_X_THREE_TEXTURE_SAMPLING_VERTEX_SHADER, fragmentShaderSource); + } + + public float getTexelWidth() { + return texelWidth; + } + + public void setTexelWidth(float texelWidth) { + this.texelWidth = texelWidth; + } + + public float getTexelHeight() { + return texelHeight; + } + + public void setTexelHeight(float texelHeight) { + this.texelHeight = texelHeight; + } + + @Override + public void setFrameSize(final int width, final int height) { + texelWidth = 1f / width; + texelHeight = 1f / height; + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("texelWidth"), texelWidth); + GLES20.glUniform1f(getHandle("texelHeight"), texelHeight); + } + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlToneCurveFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlToneCurveFilter.java new file mode 100644 index 00000000..56fbd4c0 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlToneCurveFilter.java @@ -0,0 +1,371 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.graphics.Point; +import android.graphics.PointF; +import android.opengl.GLES20; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedList; + + +public class GlToneCurveFilter extends GlFilter { + + private final static String FRAGMENT_SHADER = + "precision mediump float;\n" + + " varying highp vec2 vTextureCoord;\n" + + " uniform lowp sampler2D sTexture;\n" + + " uniform mediump sampler2D toneCurveTexture;\n" + + "\n" + + " void main()\n" + + " {\n" + + " lowp vec4 textureColor = texture2D(sTexture, vTextureCoord);\n" + + " lowp float redCurveValue = texture2D(toneCurveTexture, vec2(textureColor.r, 0.0)).r;\n" + + " lowp float greenCurveValue = texture2D(toneCurveTexture, vec2(textureColor.g, 0.0)).g;\n" + + " lowp float blueCurveValue = texture2D(toneCurveTexture, vec2(textureColor.b, 0.0)).b;\n" + + "\n" + + " gl_FragColor = vec4(redCurveValue, greenCurveValue, blueCurveValue, textureColor.a);\n" + + " }"; + + private PointF[] rgbCompositeControlPoints; + private PointF[] redControlPoints; + private PointF[] greenControlPoints; + private PointF[] blueControlPoints; + + private ArrayList rgbCompositeCurve; + private ArrayList redCurve; + private ArrayList greenCurve; + private ArrayList blueCurve; + + private final LinkedList runOnDraw; + + private final int[] textures = new int[1]; + + private byte[] toneCurveByteArray; + + + public GlToneCurveFilter(InputStream input) { + super(DEFAULT_VERTEX_SHADER, FRAGMENT_SHADER); + PointF[] defaultCurvePoints = new PointF[]{new PointF(0.0f, 0.0f), new PointF(0.5f, 0.5f), new PointF(1.0f, 1.0f)}; + rgbCompositeControlPoints = defaultCurvePoints; + redControlPoints = defaultCurvePoints; + greenControlPoints = defaultCurvePoints; + blueControlPoints = defaultCurvePoints; + + runOnDraw = new LinkedList<>(); + + setFromCurveFileInputStream(input); + + setRgbCompositeControlPoints(rgbCompositeControlPoints); + setRedControlPoints(redControlPoints); + setGreenControlPoints(greenControlPoints); + setBlueControlPoints(blueControlPoints); + + } + + @Override + public void setup() { + super.setup();// 1 + GLES20.glGenTextures(1, textures, 0); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); + + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); + GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + + while (!runOnDraw.isEmpty()) { + runOnDraw.removeFirst().run(); + } + } + + @Override + public void onDraw() { + + int offsetDepthMapTextureUniform = getHandle("toneCurveTexture");// 3 + + GLES20.glActiveTexture(GLES20.GL_TEXTURE3); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); + + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 256 /*width*/, 1 /*height*/, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, ByteBuffer.wrap(toneCurveByteArray)); + + GLES20.glUniform1i(offsetDepthMapTextureUniform, 3); + } + + private void setFromCurveFileInputStream(InputStream input) { + try { + int version = readShort(input); + int totalCurves = readShort(input); + + ArrayList curves = new ArrayList(totalCurves); + float pointRate = 1.0f / 255; + + for (int i = 0; i < totalCurves; i++) { + // 2 bytes, Count of points in the curve (short integer toAndroidFormat 2...19) + short pointCount = readShort(input); + + PointF[] points = new PointF[pointCount]; + + // point count * 4 + // Curve points. Each curve point is a pair of short integers where + // the first number is the output getNode (vertical coordinate on the + // Curves dialog graph) and the second is the input getNode. All coordinates have range 0 to 255. + for (int j = 0; j < pointCount; j++) { + short y = readShort(input); + short x = readShort(input); + + points[j] = new PointF(x * pointRate, y * pointRate); + } + + curves.add(points); + } + input.close(); + + rgbCompositeControlPoints = curves.get(0); + redControlPoints = curves.get(1); + greenControlPoints = curves.get(2); + blueControlPoints = curves.get(3); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private short readShort(InputStream input) throws IOException { + return (short) (input.read() << 8 | input.read()); + } + + private void setRgbCompositeControlPoints(PointF[] points) { + rgbCompositeControlPoints = points; + rgbCompositeCurve = createSplineCurve(rgbCompositeControlPoints); + updateToneCurveTexture(); + } + + private void setRedControlPoints(PointF[] points) { + redControlPoints = points; + redCurve = createSplineCurve(redControlPoints); + updateToneCurveTexture(); + } + + private void setGreenControlPoints(PointF[] points) { + greenControlPoints = points; + greenCurve = createSplineCurve(greenControlPoints); + updateToneCurveTexture(); + } + + private void setBlueControlPoints(PointF[] points) { + blueControlPoints = points; + blueCurve = createSplineCurve(blueControlPoints); + updateToneCurveTexture(); + } + + private void runOnDraw(final Runnable runnable) { + synchronized (runOnDraw) { + runOnDraw.addLast(runnable); + } + } + + private void updateToneCurveTexture() { + runOnDraw(new Runnable() { + @Override + public void run() { + GLES20.glActiveTexture(GLES20.GL_TEXTURE1); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]); + + if ((redCurve.size() >= 256) && (greenCurve.size() >= 256) && (blueCurve.size() >= 256) && (rgbCompositeCurve.size() >= 256)) { + toneCurveByteArray = new byte[256 * 4]; + for (int currentCurveIndex = 0; currentCurveIndex < 256; currentCurveIndex++) { + // BGRA for upload to texture + toneCurveByteArray[currentCurveIndex * 4 + 2] = (byte) ((int) Math.min(Math.max(currentCurveIndex + blueCurve.get(currentCurveIndex) + rgbCompositeCurve.get(currentCurveIndex), 0), 255) & 0xff); + toneCurveByteArray[currentCurveIndex * 4 + 1] = (byte) ((int) Math.min(Math.max(currentCurveIndex + greenCurve.get(currentCurveIndex) + rgbCompositeCurve.get(currentCurveIndex), 0), 255) & 0xff); + toneCurveByteArray[currentCurveIndex * 4] = (byte) ((int) Math.min(Math.max(currentCurveIndex + redCurve.get(currentCurveIndex) + rgbCompositeCurve.get(currentCurveIndex), 0), 255) & 0xff); + toneCurveByteArray[currentCurveIndex * 4 + 3] = (byte) (255 & 0xff); + } + + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 256 /*width*/, 1 /*height*/, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, ByteBuffer.wrap(toneCurveByteArray)); + } +// Buffer pixels! +// GLES20.glTexImage2D(int target, +// int level, +// int internalformat, +// int width, +// int height, +// int border, +// int format, +// int type, +// java.nio.Buffer pixels); + } + }); + } + + private ArrayList createSplineCurve(PointF[] points) { + if (points == null || points.length <= 0) { + return null; + } + + // Sort the array + PointF[] pointsSorted = points.clone(); + Arrays.sort(pointsSorted, new Comparator() { + @Override + public int compare(PointF point1, PointF point2) { + if (point1.x < point2.x) { + return -1; + } else if (point1.x > point2.x) { + return 1; + } else { + return 0; + } + } + }); + + // Convert toAndroidFormat (0, 1) to (0, 255). + Point[] convertedPoints = new Point[pointsSorted.length]; + for (int i = 0; i < points.length; i++) { + PointF point = pointsSorted[i]; + convertedPoints[i] = new Point((int) (point.x * 255), (int) (point.y * 255)); + } + + ArrayList splinePoints = createSplineCurve2(convertedPoints); + + // If we have a first point like (0.3, 0) we'll be missing some points at the beginning + // that should be 0. + Point firstSplinePoint = splinePoints.get(0); + if (firstSplinePoint.x > 0) { + for (int i = firstSplinePoint.x; i >= 0; i--) { + splinePoints.add(0, new Point(i, 0)); + } + } + + // Insert points similarly at the end, if necessary. + Point lastSplinePoint = splinePoints.get(splinePoints.size() - 1); + if (lastSplinePoint.x < 255) { + for (int i = lastSplinePoint.x + 1; i <= 255; i++) { + splinePoints.add(new Point(i, 255)); + } + } + + // Prepare the spline points. + ArrayList preparedSplinePoints = new ArrayList(splinePoints.size()); + for (Point newPoint : splinePoints) { + Point origPoint = new Point(newPoint.x, newPoint.x); + + float distance = (float) Math.sqrt(Math.pow((origPoint.x - newPoint.x), 2.0) + Math.pow((origPoint.y - newPoint.y), 2.0)); + + if (origPoint.y > newPoint.y) { + distance = -distance; + } + + preparedSplinePoints.add(distance); + } + + return preparedSplinePoints; + } + + private ArrayList createSplineCurve2(Point[] points) { + ArrayList sdA = createSecondDerivative(points); + + // Is [points count] equal to [sdA count]? +// int n = [points count]; + int n = sdA.size(); + if (n < 1) { + return null; + } + double[] sd = new double[n]; + + // From NSMutableArray to sd[n]; + for (int i = 0; i < n; i++) { + sd[i] = sdA.get(i); + } + + + ArrayList output = new ArrayList(n + 1); + + for (int i = 0; i < n - 1; i++) { + Point cur = points[i]; + Point next = points[i + 1]; + + for (int x = cur.x; x < next.x; x++) { + double t = (double) (x - cur.x) / (next.x - cur.x); + + double a = 1 - t; + double b = t; + double h = next.x - cur.x; + + double y = a * cur.y + b * next.y + (h * h / 6) * ((a * a * a - a) * sd[i] + (b * b * b - b) * sd[i + 1]); + + if (y > 255.0) { + y = 255.0; + } else if (y < 0.0) { + y = 0.0; + } + + output.add(new Point(x, (int) Math.round(y))); + } + } + + // If the last point is (255, 255) it doesn't get added. + if (output.size() == 255) { + output.add(points[points.length - 1]); + } + return output; + } + + private ArrayList createSecondDerivative(Point[] points) { + int n = points.length; + if (n <= 1) { + return null; + } + + double[][] matrix = new double[n][3]; + double[] result = new double[n]; + matrix[0][1] = 1; + // What about matrix[0][1] and matrix[0][0]? Assuming 0 for now (Brad L.) + matrix[0][0] = 0; + matrix[0][2] = 0; + + for (int i = 1; i < n - 1; i++) { + Point P1 = points[i - 1]; + Point P2 = points[i]; + Point P3 = points[i + 1]; + + matrix[i][0] = (double) (P2.x - P1.x) / 6; + matrix[i][1] = (double) (P3.x - P1.x) / 3; + matrix[i][2] = (double) (P3.x - P2.x) / 6; + result[i] = (double) (P3.y - P2.y) / (P3.x - P2.x) - (double) (P2.y - P1.y) / (P2.x - P1.x); + } + + // What about result[0] and result[n-1]? Assuming 0 for now (Brad L.) + result[0] = 0; + result[n - 1] = 0; + + matrix[n - 1][1] = 1; + // What about matrix[n-1][0] and matrix[n-1][2]? For now, assuming they are 0 (Brad L.) + matrix[n - 1][0] = 0; + matrix[n - 1][2] = 0; + + // solving pass1 (up->down) + for (int i = 1; i < n; i++) { + double k = matrix[i][0] / matrix[i - 1][1]; + matrix[i][1] -= k * matrix[i - 1][2]; + matrix[i][0] = 0; + result[i] -= k * result[i - 1]; + } + // solving pass2 (down->up) + for (int i = n - 2; i >= 0; i--) { + double k = matrix[i][2] / matrix[i + 1][1]; + matrix[i][1] -= k * matrix[i + 1][0]; + matrix[i][2] = 0; + result[i] -= k * result[i + 1]; + } + + ArrayList output = new ArrayList(n); + for (int i = 0; i < n; i++) output.add(result[i] / matrix[i][1]); + + return output; + } + + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlToneFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlToneFilter.java new file mode 100644 index 00000000..02fd7530 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlToneFilter.java @@ -0,0 +1,85 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + + + +public class GlToneFilter extends GlThreex3TextureSamplingFilter { + + private static final String FRAGMENT_SHADER = + "precision highp float;\n" + + + "uniform lowp sampler2D sTexture;\n" + + + "varying vec2 textureCoordinate;\n" + + "varying vec2 leftTextureCoordinate;\n" + + "varying vec2 rightTextureCoordinate;\n" + + + "varying vec2 topTextureCoordinate;\n" + + "varying vec2 topLeftTextureCoordinate;\n" + + "varying vec2 topRightTextureCoordinate;\n" + + + "varying vec2 bottomTextureCoordinate;\n" + + "varying vec2 bottomLeftTextureCoordinate;\n" + + "varying vec2 bottomRightTextureCoordinate;\n" + + +// "uniform highp float intensity;" + + "uniform highp float threshold;" + + "uniform highp float quantizationLevels;" + + + "const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);" + + + "void main() {\n" + + "vec4 textureColor = texture2D(sTexture, textureCoordinate);" + + + "float bottomLeftIntensity = texture2D(sTexture, bottomLeftTextureCoordinate).r;" + + "float topRightIntensity = texture2D(sTexture, topRightTextureCoordinate).r;" + + "float topLeftIntensity = texture2D(sTexture, topLeftTextureCoordinate).r;" + + "float bottomRightIntensity = texture2D(sTexture, bottomRightTextureCoordinate).r;" + + "float leftIntensity = texture2D(sTexture, leftTextureCoordinate).r;" + + "float rightIntensity = texture2D(sTexture, rightTextureCoordinate).r;" + + "float bottomIntensity = texture2D(sTexture, bottomTextureCoordinate).r;" + + "float topIntensity = texture2D(sTexture, topTextureCoordinate).r;" + + "float h = -topLeftIntensity - 2.0 * topIntensity - topRightIntensity + bottomLeftIntensity + 2.0 * bottomIntensity + bottomRightIntensity;" + + "float v = -bottomLeftIntensity - 2.0 * leftIntensity - topLeftIntensity + bottomRightIntensity + 2.0 * rightIntensity + topRightIntensity;" + + + "float mag = length(vec2(h, v));" + + "vec3 posterizedImageColor = floor((textureColor.rgb * quantizationLevels) + 0.5) / quantizationLevels;" + + "float thresholdTest = 1.0 - step(threshold, mag);" + + "gl_FragColor = vec4(posterizedImageColor * thresholdTest, textureColor.a);" + + "}"; + + private float threshold = 0.2f; + private float quantizationLevels = 10f; + + + public GlToneFilter() { + super(FRAGMENT_SHADER); + } + + ////////////////////////////////////////////////////////////////////////// + + public float getThreshold() { + return threshold; + } + + public void setThreshold(final float threshold) { + this.threshold = threshold; + } + + public float getQuantizationLevels() { + return quantizationLevels; + } + + public void setQuantizationLevels(final float quantizationLevels) { + this.quantizationLevels = quantizationLevels; + } + + ////////////////////////////////////////////////////////////////////////// + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("threshold"), threshold); + GLES20.glUniform1f(getHandle("quantizationLevels"), quantizationLevels); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlVibranceFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlVibranceFilter.java new file mode 100644 index 00000000..ae75cfaf --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlVibranceFilter.java @@ -0,0 +1,37 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + +public class GlVibranceFilter extends GlFilter { + + private static final String VIBRANCE_FRAGMENT_SHADER = "" + + "precision mediump float;" + + " varying vec2 vTextureCoord;\n" + + "\n" + + " uniform lowp sampler2D sTexture;\n" + + " uniform lowp float vibrance;\n" + + "\n" + + "void main() {\n" + + " lowp vec4 color = texture2D(sTexture, vTextureCoord);\n" + + " lowp float average = (color.r + color.g + color.b) / 3.0;\n" + + " lowp float mx = max(color.r, max(color.g, color.b));\n" + + " lowp float amt = (mx - average) * (-vibrance * 3.0);\n" + + " color.rgb = mix(color.rgb, vec3(mx), amt);\n" + + " gl_FragColor = color;\n" + + "}"; + + public GlVibranceFilter() { + super(DEFAULT_VERTEX_SHADER, VIBRANCE_FRAGMENT_SHADER); + } + + private float vibrance = 0f; + + public void setVibrance(float vibrance) { + this.vibrance = vibrance; + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("vibrance"), vibrance); + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlVignetteFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlVignetteFilter.java new file mode 100644 index 00000000..6d2d39f2 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlVignetteFilter.java @@ -0,0 +1,61 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + + + +public class GlVignetteFilter extends GlFilter { + + private static final String FRAGMENT_SHADER = + "precision mediump float;" + + + "varying vec2 vTextureCoord;" + + "uniform lowp sampler2D sTexture;" + + + "uniform lowp vec2 vignetteCenter;" + + "uniform highp float vignetteStart;" + + "uniform highp float vignetteEnd;" + + + "void main() {" + + "lowp vec3 rgb = texture2D(sTexture, vTextureCoord).rgb;" + + "lowp float d = distance(vTextureCoord, vec2(vignetteCenter.x, vignetteCenter.y));" + + "lowp float percent = smoothstep(vignetteStart, vignetteEnd, d);" + + "gl_FragColor = vec4(mix(rgb.x, 0.0, percent), mix(rgb.y, 0.0, percent), mix(rgb.z, 0.0, percent), 1.0);" + + "}"; + + private final float vignetteCenterX = 0.5f; + private final float vignetteCenterY = 0.5f; + private float vignetteStart = 0.2f; + private float vignetteEnd = 0.85f; + + public GlVignetteFilter() { + super(DEFAULT_VERTEX_SHADER, FRAGMENT_SHADER); + } + + + public float getVignetteStart() { + return vignetteStart; + } + + public void setVignetteStart(final float vignetteStart) { + this.vignetteStart = vignetteStart; + } + + public float getVignetteEnd() { + return vignetteEnd; + } + + public void setVignetteEnd(final float vignetteEnd) { + this.vignetteEnd = vignetteEnd; + } + + ////////////////////////////////////////////////////////////////////////// + + @Override + public void onDraw() { + GLES20.glUniform2f(getHandle("vignetteCenter"), vignetteCenterX, vignetteCenterY); + GLES20.glUniform1f(getHandle("vignetteStart"), vignetteStart); + GLES20.glUniform1f(getHandle("vignetteEnd"), vignetteEnd); + } + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlWatermarkFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlWatermarkFilter.java new file mode 100644 index 00000000..3f94f71d --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlWatermarkFilter.java @@ -0,0 +1,47 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.graphics.Bitmap; +import android.graphics.Canvas; + +public class GlWatermarkFilter extends GlOverlayFilter { + + private final Bitmap bitmap; + private Position position = Position.LEFT_TOP; + + public GlWatermarkFilter(Bitmap bitmap) { + this.bitmap = bitmap; + } + + + public GlWatermarkFilter(Bitmap bitmap, Position position) { + this.bitmap = bitmap; + this.position = position; + } + + @Override + protected void drawCanvas(Canvas canvas) { + if (bitmap != null && !bitmap.isRecycled()) { + switch (position) { + case LEFT_TOP: + canvas.drawBitmap(bitmap, 0, 0, null); + break; + case LEFT_BOTTOM: + canvas.drawBitmap(bitmap, 0, canvas.getHeight() - bitmap.getHeight(), null); + break; + case RIGHT_TOP: + canvas.drawBitmap(bitmap, canvas.getWidth() - bitmap.getWidth(), 0, null); + break; + case RIGHT_BOTTOM: + canvas.drawBitmap(bitmap, canvas.getWidth() - bitmap.getWidth(), canvas.getHeight() - bitmap.getHeight(), null); + break; + } + } + } + + public enum Position { + LEFT_TOP, + LEFT_BOTTOM, + RIGHT_TOP, + RIGHT_BOTTOM + } +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlWeakPixelInclusionFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlWeakPixelInclusionFilter.java new file mode 100644 index 00000000..e41e39d5 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlWeakPixelInclusionFilter.java @@ -0,0 +1,46 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + + + +public class GlWeakPixelInclusionFilter extends GlThreex3TextureSamplingFilter { + + private static final String FRAGMENT_SHADER = + "precision lowp float;\n" + + + "uniform lowp sampler2D sTexture;\n" + + + "varying vec2 textureCoordinate;\n" + + "varying vec2 leftTextureCoordinate;\n" + + "varying vec2 rightTextureCoordinate;\n" + + + "varying vec2 topTextureCoordinate;\n" + + "varying vec2 topLeftTextureCoordinate;\n" + + "varying vec2 topRightTextureCoordinate;\n" + + + "varying vec2 bottomTextureCoordinate;\n" + + "varying vec2 bottomLeftTextureCoordinate;\n" + + "varying vec2 bottomRightTextureCoordinate;\n" + + + "void main() {\n" + + "float bottomLeftIntensity = texture2D(sTexture, bottomLeftTextureCoordinate).r;" + + "float topRightIntensity = texture2D(sTexture, topRightTextureCoordinate).r;" + + "float topLeftIntensity = texture2D(sTexture, topLeftTextureCoordinate).r;" + + "float bottomRightIntensity = texture2D(sTexture, bottomRightTextureCoordinate).r;" + + "float leftIntensity = texture2D(sTexture, leftTextureCoordinate).r;" + + "float rightIntensity = texture2D(sTexture, rightTextureCoordinate).r;" + + "float bottomIntensity = texture2D(sTexture, bottomTextureCoordinate).r;" + + "float topIntensity = texture2D(sTexture, topTextureCoordinate).r;" + + "float centerIntensity = texture2D(sTexture, textureCoordinate).r;" + + + "float pixelIntensitySum = bottomLeftIntensity + topRightIntensity + topLeftIntensity + bottomRightIntensity + leftIntensity + rightIntensity + bottomIntensity + topIntensity + centerIntensity;" + + "float sumTest = step(1.5, pixelIntensitySum);" + + "float pixelTest = step(0.01, centerIntensity);" + + + "gl_FragColor = vec4(vec3(sumTest * pixelTest), 1.0);" + + "}"; + + public GlWeakPixelInclusionFilter() { + super(FRAGMENT_SHADER); + } + +} diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlWhiteBalanceFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlWhiteBalanceFilter.java new file mode 100644 index 00000000..ee58e544 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlWhiteBalanceFilter.java @@ -0,0 +1,57 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.opengl.GLES20; + +public class GlWhiteBalanceFilter extends GlFilter { + + private static final String WHITE_BALANCE_FRAGMENT_SHADER = "" + + " uniform lowp sampler2D sTexture;\n" + + " varying vec2 vTextureCoord;\n" + + + " \n" + + "uniform lowp float temperature;\n" + + "uniform lowp float tint;\n" + + "\n" + + "const lowp vec3 warmFilter = vec3(0.93, 0.54, 0.0);\n" + + "\n" + + "const mediump mat3 RGBtoYIQ = mat3(0.299, 0.587, 0.114, 0.596, -0.274, -0.322, 0.212, -0.523, 0.311);\n" + + "const mediump mat3 YIQtoRGB = mat3(1.0, 0.956, 0.621, 1.0, -0.272, -0.647, 1.0, -1.105, 1.702);\n" + + "\n" + + "void main()\n" + + "{\n" + + " lowp vec4 source = texture2D(sTexture, vTextureCoord);\n" + + " \n" + + " mediump vec3 yiq = RGBtoYIQ * source.rgb; //adjusting tint\n" + + " yiq.b = clamp(yiq.b + tint*0.5226*0.1, -0.5226, 0.5226);\n" + + " lowp vec3 rgb = YIQtoRGB * yiq;\n" + + "\n" + + " lowp vec3 processed = vec3(\n" + + " (rgb.r < 0.5 ? (2.0 * rgb.r * warmFilter.r) : (1.0 - 2.0 * (1.0 - rgb.r) * (1.0 - warmFilter.r))), //adjusting temperature\n" + + " (rgb.g < 0.5 ? (2.0 * rgb.g * warmFilter.g) : (1.0 - 2.0 * (1.0 - rgb.g) * (1.0 - warmFilter.g))), \n" + + " (rgb.b < 0.5 ? (2.0 * rgb.b * warmFilter.b) : (1.0 - 2.0 * (1.0 - rgb.b) * (1.0 - warmFilter.b))));\n" + + "\n" + + " gl_FragColor = vec4(mix(rgb, processed, temperature), source.a);\n" + + "}"; + + public GlWhiteBalanceFilter() { + super(DEFAULT_VERTEX_SHADER, WHITE_BALANCE_FRAGMENT_SHADER); + } + + private float temperature = 5000f; + private float tint = 0f; + + public void setTemperature(final float temperature) { + this.temperature = temperature < 5000 ? (float) (0.0004 * (temperature - 5000.0)) : (float) (0.00006 * (temperature - 5000.0)); + } + + public void setTint(final float tint) { + this.tint = (float) (tint / 100.0); + } + + @Override + public void onDraw() { + GLES20.glUniform1f(getHandle("temperature"), temperature); + GLES20.glUniform1f(getHandle("tint"), tint); + } + +} \ No newline at end of file diff --git a/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlZoomBlurFilter.java b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlZoomBlurFilter.java new file mode 100644 index 00000000..40870c93 --- /dev/null +++ b/dkplayer-sample/src/main/java/xyz/doikki/dkplayer/widget/render/gl2/filter/GlZoomBlurFilter.java @@ -0,0 +1,56 @@ +package xyz.doikki.dkplayer.widget.render.gl2.filter; + +import android.graphics.PointF; +import android.opengl.GLES20; + +public class GlZoomBlurFilter extends GlFilter { + + private static final String ZOOM_BLUR_FRAGMENT_SHADER = "" + + "precision mediump float;" + + " varying vec2 vTextureCoord;\n" + + "\n" + + "uniform lowp sampler2D sTexture;\n" + + "\n" + + "uniform highp vec2 blurCenter;\n" + + "uniform highp float blurSize;\n" + + "\n" + + "void main()\n" + + "{\n" + + " // TODO: Do a more intelligent scaling based on resolution here\n" + + " highp vec2 samplingOffset = 1.0/100.0 * (blurCenter - vTextureCoord) * blurSize;\n" + + " \n" + + " lowp vec4 fragmentColor = texture2D(sTexture, vTextureCoord) * 0.18;\n" + + " fragmentColor += texture2D(sTexture, vTextureCoord + samplingOffset) * 0.15;\n" + + " fragmentColor += texture2D(sTexture, vTextureCoord + (2.0 * samplingOffset)) * 0.12;\n" + + " fragmentColor += texture2D(sTexture, vTextureCoord + (3.0 * samplingOffset)) * 0.09;\n" + + " fragmentColor += texture2D(sTexture, vTextureCoord + (4.0 * samplingOffset)) * 0.05;\n" + + " fragmentColor += texture2D(sTexture, vTextureCoord - samplingOffset) * 0.15;\n" + + " fragmentColor += texture2D(sTexture, vTextureCoord - (2.0 * samplingOffset)) * 0.12;\n" + + " fragmentColor += texture2D(sTexture, vTextureCoord - (3.0 * samplingOffset)) * 0.09;\n" + + " fragmentColor += texture2D(sTexture, vTextureCoord - (4.0 * samplingOffset)) * 0.05;\n" + + " \n" + + " gl_FragColor = fragmentColor;\n" + + "}\n"; + + private PointF blurCenter = new PointF(0.5f, 0.5f); + private float blurSize = 1f; + + public GlZoomBlurFilter() { + super(DEFAULT_VERTEX_SHADER, ZOOM_BLUR_FRAGMENT_SHADER); + } + + public void setBlurCenter(PointF blurCenter) { + this.blurCenter = blurCenter; + } + + public void setBlurSize(float blurSize) { + this.blurSize = blurSize; + } + + @Override + public void onDraw() { + GLES20.glUniform2f(getHandle("blurCenter"), blurCenter.x, blurCenter.y); + GLES20.glUniform1f(getHandle("blurSize"), blurSize); + } + +} diff --git a/dkplayer-sample/src/main/res/values-en/strings.xml b/dkplayer-sample/src/main/res/values-en/strings.xml index 33b30fb4..f3289ba5 100644 --- a/dkplayer-sample/src/main/res/values-en/strings.xml +++ b/dkplayer-sample/src/main/res/values-en/strings.xml @@ -9,7 +9,7 @@ LIVE music DEFINITION - Custom RenderView,video overlay + Custom RenderView,filter,watermark Custom IjkMediaPlayer Custom ExoMediaPlayer SCREEN SHOT diff --git a/dkplayer-sample/src/main/res/values/strings.xml b/dkplayer-sample/src/main/res/values/strings.xml index 46a95459..57d34e65 100644 --- a/dkplayer-sample/src/main/res/values/strings.xml +++ b/dkplayer-sample/src/main/res/values/strings.xml @@ -8,7 +8,7 @@ 直播 音乐 清晰度切换 - 自定义RenderView,视频加水印 + 自定义RenderView,滤镜,水印 自定义IjkMediaPlayer 自定义ExoMediaPlayer 截图