Last active
November 30, 2024 15:24
-
-
Save awxkee/0b39f832b6a81e17f91c9ae6a7dd61c6 to your computer and use it in GitHub Desktop.
Resing methods OpenGL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.android.example.rsmigration | |
| import android.content.Context | |
| import android.graphics.Bitmap | |
| import android.opengl.EGL14 | |
| import android.opengl.EGL15 | |
| import android.opengl.EGLConfig | |
| import android.opengl.EGLContext | |
| import android.opengl.EGLDisplay | |
| import android.opengl.EGLExt | |
| import android.opengl.EGLSurface | |
| import android.opengl.GLES20 | |
| import android.opengl.GLES31 | |
| import android.opengl.GLUtils | |
| import android.os.Build | |
| import android.util.Size | |
| import com.example.simpleegl.BLUR_VERTEX_SHADER | |
| import com.example.simpleegl.TEXTURE_COORDINATES | |
| import com.example.simpleegl.checkGLError | |
| import com.example.simpleegl.createGLES20ShaderProgram | |
| import com.example.simpleegl.isGLES31Supported | |
| import java.nio.ByteBuffer | |
| import java.nio.ByteOrder | |
| import java.nio.FloatBuffer | |
| enum class SamplerMethod { | |
| NEAREST, BILINEAR, LANCZOS | |
| } | |
| enum class Antialiasing(internal val value: Int) { | |
| ANTIALIAS(1), NO_ANTIALIAS(0) | |
| } | |
| var NEAREST_VERT: String = """ | |
| precision mediump float; | |
| uniform sampler2D u_Texture; | |
| uniform vec2 texelSize; | |
| uniform ivec2 sourceSize; | |
| uniform ivec2 targetSize; | |
| varying vec2 vTexCoord; | |
| void main() { | |
| float scale = float(sourceSize.y) / float(targetSize.y); | |
| float centerV = min((vTexCoord.y*float(sourceSize.y) + 0.5) * scale, float(sourceSize.y) - 1.0); | |
| float center = centerV - 0.5; | |
| vec2 sourceOffset = vec2(vTexCoord.x, (centerV - 0.5) * texelSize.y); | |
| vec4 color = texture2D(u_Texture, sourceOffset); | |
| gl_FragColor = color; | |
| } | |
| """.trimIndent() | |
| val NEAREST_HORIZ: String = """ | |
| precision mediump float; | |
| uniform sampler2D u_Texture; | |
| uniform vec2 texelSize; | |
| uniform ivec2 sourceSize; | |
| uniform ivec2 targetSize; | |
| varying vec2 vTexCoord; | |
| void main() { | |
| float scale = float(sourceSize.x) / float(targetSize.x); | |
| float centerV = min((float(vTexCoord.x)*float(sourceSize.x) + 0.5) * scale, float(sourceSize.x) - 1.0); | |
| float center = centerV - 0.5; | |
| vec2 sourceOffset = vec2((centerV - 0.5) * texelSize.x, vTexCoord.y); | |
| vec4 color = texture2D(u_Texture, sourceOffset); | |
| gl_FragColor = color; | |
| } | |
| """.trimIndent() | |
| val BILINEAR_HORIZ: String = """ | |
| precision mediump float; | |
| uniform sampler2D u_Texture; | |
| uniform vec2 texelSize; | |
| uniform ivec2 sourceSize; | |
| uniform ivec2 targetSize; | |
| uniform int antialiasing; | |
| varying vec2 vTexCoord; | |
| float bilinear(float x) { | |
| x = abs(x); | |
| if (x < 1.0) { | |
| return 1.0 - x; | |
| } else { | |
| return 0.0; | |
| } | |
| } | |
| void main() { | |
| float scale = float(sourceSize.x) / float(targetSize.x); | |
| float centerV = min((vTexCoord.x*float(sourceSize.x) + 0.5) * scale, float(sourceSize.x) - 1.0); | |
| float filterRadius = 1.0; | |
| if (antialiasing == 1) { | |
| filterRadius = 1.0 * scale; | |
| } | |
| int start = int(max(floor(centerV - filterRadius), 0.0)); | |
| int end = int(min(ceil(centerV + filterRadius), float(sourceSize.x) - 1.0)); | |
| int length = end - start; | |
| if (length < 0) { | |
| discard; | |
| } | |
| float filterScale = 1.0; | |
| if (antialiasing == 1) { | |
| filterScale = 1.0 / scale; | |
| } | |
| float weightSum = 0.0; | |
| float center = centerV - 0.5; | |
| vec4 color = vec4(0f); | |
| for (int i = start; i < end; i++) { | |
| float xPosition = float(i) * texelSize.x; | |
| float dx = float(i) - center; | |
| float weight = bilinear(dx * xPosition * filterScale); | |
| vec2 sourceOffset = vec2(xPosition, vTexCoord.y); | |
| weightSum += weight; | |
| vec4 wColor = texture2D(u_Texture, sourceOffset) * weight; | |
| color += wColor; | |
| } | |
| if (weightSum != 0.0) { | |
| color = color / weightSum; | |
| } | |
| gl_FragColor = color; | |
| } | |
| """.trimIndent() | |
| val BILINEAR_VERTICAL: String = """ | |
| precision mediump float; | |
| uniform sampler2D u_Texture; | |
| uniform vec2 texelSize; | |
| uniform ivec2 sourceSize; | |
| uniform ivec2 targetSize; | |
| uniform int antialiasing; | |
| varying vec2 vTexCoord; | |
| float bilinear(float x) { | |
| x = abs(x); | |
| if (x < 1.0) { | |
| return 1.0 - x; | |
| } else { | |
| return 0.0; | |
| } | |
| } | |
| void main() { | |
| float scale = float(sourceSize.y) / float(targetSize.y); | |
| float centerV = min((vTexCoord.y*float(sourceSize.y) + 0.5) * scale, float(sourceSize.y) - 1.0); | |
| float filterRadius = 1.0; | |
| if (antialiasing == 1) { | |
| filterRadius = 1.0 * scale; | |
| } | |
| int start = int(max(floor(centerV - filterRadius), 0.0)); | |
| int end = int(min(ceil(centerV + filterRadius), float(sourceSize.y) - 1.0)); | |
| int length = end - start; | |
| if (length < 0) { | |
| discard; | |
| } | |
| float filterScale = 1.0; | |
| if (antialiasing == 1) { | |
| filterScale = 1.0 / scale; | |
| } | |
| float weightSum = 0.0; | |
| float center = centerV - 0.5; | |
| vec4 color = vec4(0f); | |
| for (int i = start; i < end; i++) { | |
| float yPosition = float(i) * texelSize.y; | |
| float dy = float(i) - center; | |
| float weight = bilinear(dy * yPosition * filterScale); | |
| vec2 sourceOffset = vec2(vTexCoord.x, yPosition); | |
| weightSum += weight; | |
| vec4 wColor = texture2D(u_Texture, sourceOffset) * weight; | |
| color += wColor; | |
| } | |
| if (weightSum != 0.0) { | |
| color = color / weightSum; | |
| } | |
| gl_FragColor = color; | |
| } | |
| """.trimIndent() | |
| val LANCZOS_HORIZ: String = """ | |
| precision mediump float; | |
| uniform sampler2D u_Texture; | |
| uniform vec2 texelSize; | |
| uniform ivec2 sourceSize; | |
| uniform ivec2 targetSize; | |
| uniform int antialiasing; | |
| varying vec2 vTexCoord; | |
| float sinc(float x) { | |
| if (x == 0.0) { | |
| return 1.0; | |
| } | |
| return sin(x) / x; | |
| } | |
| float lanczos3(float x) { | |
| float scale_a = 1.0 / 3.0; | |
| if (abs(x) < 3.0) { | |
| float d = 3.14159265358979 * x; | |
| return sinc(d) * sinc(d * scale_a); | |
| } | |
| return 0.0; | |
| } | |
| void main() { | |
| float scale = float(sourceSize.x) / float(targetSize.x); | |
| float centerV = min((vTexCoord.x*float(sourceSize.x) + 0.5) * scale, float(sourceSize.x) - 1.0); | |
| float filterRadius = 1.5; | |
| if (antialiasing == 1) { | |
| filterRadius = 1.5 * scale; | |
| } | |
| int start = int(max(floor(centerV - filterRadius), 0.0)); | |
| int end = int(min(ceil(centerV + filterRadius), float(sourceSize.x) - 1.0)); | |
| int length = end - start; | |
| if (length < 0) { | |
| discard; | |
| } | |
| float filterScale = 1.0; | |
| if (antialiasing == 1) { | |
| filterScale = 1.0 / scale; | |
| } | |
| float weightSum = 0.0; | |
| float center = centerV - 0.5; | |
| vec4 color = vec4(0.0); | |
| for (int i = start; i < end; i++) { | |
| float xPosition = float(i) * texelSize.x; | |
| float dx = float(i) - center; | |
| float weight = lanczos3(dx * xPosition * filterScale); | |
| vec2 sourceOffset = vec2(xPosition, vTexCoord.y); | |
| weightSum += weight; | |
| vec4 wColor = texture2D(u_Texture, sourceOffset) * weight; | |
| color += wColor; | |
| } | |
| if (weightSum != 0.0) { | |
| color = color / weightSum; | |
| } | |
| gl_FragColor = color; | |
| } | |
| """.trimIndent() | |
| val LANCZOS_VERT: String = """ | |
| precision mediump float; | |
| uniform sampler2D u_Texture; | |
| uniform vec2 texelSize; | |
| uniform ivec2 sourceSize; | |
| uniform ivec2 targetSize; | |
| uniform int antialiasing; | |
| varying vec2 vTexCoord; | |
| float sinc(float x) { | |
| if (x == 0.0) { | |
| return 1.0; | |
| } | |
| return sin(x) / x; | |
| } | |
| float lanczos3(float x) { | |
| float scale_a = 1.0 / 3.0; | |
| if (abs(x) < 3.0) { | |
| float d = 3.14159265358979 * x; | |
| return sinc(d) * sinc(d * scale_a); | |
| } | |
| return 0.0; | |
| } | |
| void main() { | |
| float scale = float(sourceSize.y) / float(targetSize.y); | |
| float centerV = min((vTexCoord.y*float(sourceSize.y) + 0.5) * scale, float(sourceSize.y) - 1.0); | |
| float filterRadius = 1.5; | |
| if (antialiasing == 1) { | |
| filterRadius = 1.5 * scale; | |
| } | |
| int start = int(max(floor(centerV - filterRadius), 0.0)); | |
| int end = int(min(ceil(centerV + filterRadius), float(sourceSize.y) - 1.0)); | |
| int length = end - start; | |
| if (length < 0) { | |
| discard; | |
| } | |
| float filterScale = 1.0; | |
| if (antialiasing == 1) { | |
| filterScale = 1.0 / scale; | |
| } | |
| float weightSum = 0.0; | |
| float center = centerV - 0.5; | |
| vec4 color = vec4(0.0); | |
| for (int i = start; i < end; i++) { | |
| float yPosition = float(i) * texelSize.y; | |
| float dy = float(i) - center; | |
| float weight = lanczos3(dy * yPosition * filterScale); | |
| vec2 sourceOffset = vec2(vTexCoord.x, yPosition); | |
| weightSum += weight; | |
| vec4 wColor = texture2D(u_Texture, sourceOffset) * weight; | |
| color += wColor; | |
| } | |
| if (weightSum != 0.0) { | |
| color = color / weightSum; | |
| } | |
| // vec2 sourceOffset = vec2((centerV - 0.5) * texelSize.x, vTexCoord.y); | |
| // vec4 color = texture2D(u_Texture, sourceOffset); | |
| // | |
| // vec2 sourceOffset = vec2((float(start) - 0.5) * texelSize.x, vTexCoord.y); | |
| // vec4 color = texture2D(u_Texture, sourceOffset); | |
| gl_FragColor = color; | |
| } | |
| """.trimIndent() | |
| class OpenGLESResizeRenderPass(private val mContext: Context) { | |
| private var isGLES3Used = false | |
| private var textureIds: IntArray | |
| private var frameBuffers: IntArray | |
| private var eglDisplay: EGLDisplay? = null | |
| private var eglSurface: EGLSurface? = null | |
| private var eglContext: EGLContext? = null | |
| private var nearestHorizontalProgram: Int = -1 | |
| private var nearestVerticalProgram: Int = -1 | |
| private var bilinearHorizontalProgram: Int = -1 | |
| private var bilinearVerticalProgram: Int = -1 | |
| private var lanczosHorizontalProgram: Int = -1 | |
| private var lanczosVerticalProgram: Int = -1 | |
| private var oldWidth: Int = -1 | |
| private var oldHeight: Int = -1 | |
| init { | |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { | |
| isGLES3Used = isGLES31Supported(mContext) | |
| } | |
| // Surface and EGL can be reused where possible | |
| eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY) | |
| EGL14.eglInitialize(eglDisplay!!, null, 0, null, 0) | |
| val configAttribs = intArrayOf( | |
| EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT, | |
| EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, | |
| EGL14.EGL_RED_SIZE, 8, | |
| EGL14.EGL_GREEN_SIZE, 8, | |
| EGL14.EGL_BLUE_SIZE, 8, | |
| EGL14.EGL_ALPHA_SIZE, 8, | |
| EGL14.EGL_NONE | |
| ) | |
| val configs = arrayOfNulls<EGLConfig>(1) | |
| val numConfigs = IntArray(1) | |
| EGL14.eglChooseConfig(eglDisplay!!, configAttribs, 0, configs, 0, 1, numConfigs, 0) | |
| val pbufferAttribs = intArrayOf( | |
| EGL14.EGL_WIDTH, 1, | |
| EGL14.EGL_HEIGHT, 1, | |
| EGL14.EGL_NONE | |
| ) | |
| eglSurface = EGL14.eglCreatePbufferSurface(eglDisplay!!, configs[0], pbufferAttribs, 0) | |
| if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && isGLES3Used) { | |
| val glAttributes = intArrayOf( | |
| EGL15.EGL_CONTEXT_MAJOR_VERSION, 3, | |
| EGL15.EGL_CONTEXT_MINOR_VERSION, 1, | |
| EGL15.EGL_CONTEXT_OPENGL_DEBUG, EGL14.EGL_TRUE, | |
| EGLExt.EGL_CONTEXT_FLAGS_KHR, EGL14.EGL_TRUE, | |
| EGL14.EGL_NONE | |
| ) | |
| eglContext = EGL14.eglCreateContext( | |
| eglDisplay, configs[0], EGL14.EGL_NO_CONTEXT, glAttributes, 0 | |
| ) | |
| } else { | |
| val contextAttribs = intArrayOf(EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE) | |
| eglContext = EGL14.eglCreateContext( | |
| eglDisplay, | |
| configs[0], | |
| EGL14.EGL_NO_CONTEXT, | |
| contextAttribs, | |
| 0 | |
| ) | |
| } | |
| EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext) | |
| bilinearHorizontalProgram = | |
| createGLES20ShaderProgram(BLUR_VERTEX_SHADER, BILINEAR_HORIZ) | |
| if (bilinearHorizontalProgram == -1) { | |
| throw IllegalStateException("Can't compile horizontal bilinear shader") | |
| } | |
| bilinearVerticalProgram = | |
| createGLES20ShaderProgram(BLUR_VERTEX_SHADER, BILINEAR_VERTICAL) | |
| if (bilinearVerticalProgram == -1) { | |
| throw IllegalStateException("Can't compile vertical bilinear shader") | |
| } | |
| lanczosHorizontalProgram = | |
| createGLES20ShaderProgram(BLUR_VERTEX_SHADER, LANCZOS_HORIZ) | |
| if (lanczosHorizontalProgram == -1) { | |
| throw IllegalStateException("Can't compile horizontal lanczos shader") | |
| } | |
| lanczosVerticalProgram = | |
| createGLES20ShaderProgram(BLUR_VERTEX_SHADER, LANCZOS_VERT) | |
| if (lanczosVerticalProgram == -1) { | |
| throw IllegalStateException("Can't compile horizontal lanczos shader") | |
| } | |
| nearestHorizontalProgram = | |
| createGLES20ShaderProgram(BLUR_VERTEX_SHADER, NEAREST_HORIZ) | |
| if (nearestHorizontalProgram == -1) { | |
| throw IllegalStateException("Can't compile horizontal lanczos shader") | |
| } | |
| nearestVerticalProgram = | |
| createGLES20ShaderProgram(BLUR_VERTEX_SHADER, NEAREST_VERT) | |
| if (nearestVerticalProgram == -1) { | |
| throw IllegalStateException("Can't compile horizontal lanczos shader") | |
| } | |
| textureIds = IntArray(3) | |
| GLES20.glGenTextures(3, textureIds, 0) | |
| frameBuffers = IntArray(2) | |
| GLES20.glGenFramebuffers(2, frameBuffers, 0) | |
| EGL14.eglMakeCurrent( | |
| eglDisplay, | |
| EGL14.EGL_NO_SURFACE, | |
| EGL14.EGL_NO_SURFACE, | |
| EGL14.EGL_NO_CONTEXT | |
| ) | |
| } | |
| /** | |
| * This is undefined behaviour to call this method from multiple threads | |
| */ | |
| fun resize( | |
| bitmap: Bitmap, | |
| targetSize: Size, | |
| samplerMethod: SamplerMethod, | |
| antialiasing: Antialiasing | |
| ): Bitmap { | |
| if (eglDisplay == null || eglSurface == null || eglContext == null) { | |
| throw IllegalStateException("Render pass has been already terminated") | |
| } | |
| EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext) | |
| val newBitmap = if (samplerMethod == SamplerMethod.BILINEAR) { | |
| resizeGLES20Convolution( | |
| bitmap, | |
| targetSize, | |
| bilinearHorizontalProgram, | |
| bilinearVerticalProgram, | |
| antialiasing | |
| ) | |
| } else if (samplerMethod == SamplerMethod.LANCZOS) { | |
| resizeGLES20Convolution( | |
| bitmap, | |
| targetSize, | |
| lanczosHorizontalProgram, | |
| lanczosVerticalProgram, | |
| antialiasing | |
| ) | |
| } else { | |
| resizeGLES20Convolution( | |
| bitmap, | |
| targetSize, | |
| nearestHorizontalProgram, | |
| nearestVerticalProgram, | |
| null | |
| ) | |
| } | |
| oldWidth = bitmap.width | |
| oldHeight = bitmap.height | |
| EGL14.eglMakeCurrent( | |
| eglDisplay, | |
| EGL14.EGL_NO_SURFACE, | |
| EGL14.EGL_NO_SURFACE, | |
| EGL14.EGL_NO_CONTEXT | |
| ) | |
| return newBitmap | |
| } | |
| private fun resizeGLES20Convolution( | |
| bitmap: Bitmap, | |
| targetSize: Size, | |
| horizontalProgram: Int, | |
| verticalProgram: Int, | |
| antialiasing: Antialiasing?, | |
| ): Bitmap { | |
| loadBitmap(bitmap, textureIds[0]) | |
| rebindTex(textureIds[1], Size(targetSize.width, bitmap.height)) | |
| rebindTex(textureIds[2], targetSize) | |
| // Horizontal pass | |
| checkGLError { GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffers[0]) } | |
| checkGLError { | |
| GLES20.glFramebufferTexture2D( | |
| GLES20.GL_FRAMEBUFFER, | |
| GLES20.GL_COLOR_ATTACHMENT0, | |
| GLES20.GL_TEXTURE_2D, | |
| textureIds[1], | |
| 0 | |
| ) | |
| } | |
| if (GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) != GLES20.GL_FRAMEBUFFER_COMPLETE) { | |
| throw RuntimeException("Framebuffer is not complete") | |
| } | |
| checkGLError { GLES20.glUseProgram(horizontalProgram) } | |
| val aVertexPositionLocation = | |
| GLES20.glGetAttribLocation(horizontalProgram, "aPosition") | |
| val aTexCoordLocation = GLES20.glGetAttribLocation(horizontalProgram, "aTexCoord") | |
| val uTextureLocation = GLES20.glGetUniformLocation(horizontalProgram, "u_Texture") | |
| val sourceSizeLocation = | |
| GLES20.glGetUniformLocation(horizontalProgram, "sourceSize") | |
| val targetSizeLocation = | |
| GLES20.glGetUniformLocation(horizontalProgram, "targetSize") | |
| val horTexelSizeLocation = | |
| GLES20.glGetUniformLocation(horizontalProgram, "texelSize") | |
| checkGLError { GLES20.glActiveTexture(GLES20.GL_TEXTURE0) } | |
| checkGLError { GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]) } | |
| checkGLError { GLES20.glUniform1i(uTextureLocation, 0) } | |
| checkGLError { GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f) } | |
| checkGLError { GLES20.glViewport(0, 0, bitmap.width, bitmap.height) } | |
| checkGLError { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT or GLES20.GL_DEPTH_BUFFER_BIT) } | |
| val quadVertices = floatArrayOf( | |
| -1.0f, 1.0f, // Top-left corner | |
| -1.0f, -1.0f, // Bottom-left corner | |
| 1.0f, 1.0f, // Top-right corner | |
| 1.0f, -1.0f // Bottom-right corner | |
| ) | |
| val vertexBuffer: FloatBuffer = ByteBuffer | |
| .allocateDirect(quadVertices.size * 4) | |
| .order(ByteOrder.nativeOrder()) | |
| .asFloatBuffer() | |
| vertexBuffer.put(quadVertices).position(0) | |
| val textureCoordinatesBuffer: FloatBuffer = | |
| ByteBuffer.allocateDirect(TEXTURE_COORDINATES.size * 4).run { | |
| order(ByteOrder.nativeOrder()) | |
| asFloatBuffer().apply { | |
| put(TEXTURE_COORDINATES) | |
| position(0) | |
| } | |
| } | |
| GLES20.glVertexAttribPointer( | |
| aVertexPositionLocation, | |
| 2, | |
| GLES20.GL_FLOAT, | |
| false, | |
| 0, | |
| vertexBuffer | |
| ) | |
| GLES20.glVertexAttribPointer( | |
| aTexCoordLocation, | |
| 2, | |
| GLES20.GL_FLOAT, | |
| false, | |
| 0, | |
| textureCoordinatesBuffer | |
| ) | |
| if (antialiasing != null) { | |
| val antialisingLocation = | |
| GLES20.glGetUniformLocation(horizontalProgram, "antialiasing") | |
| GLES20.glUniform1i(antialisingLocation, antialiasing.value) | |
| } | |
| GLES20.glUniform2f( | |
| horTexelSizeLocation, | |
| 1.0f / bitmap.width.toFloat(), | |
| 1.0f / bitmap.height.toFloat() | |
| ) | |
| GLES20.glUniform2i( | |
| sourceSizeLocation, | |
| bitmap.width, | |
| bitmap.height, | |
| ) | |
| GLES20.glUniform2i( | |
| targetSizeLocation, | |
| targetSize.width, | |
| bitmap.height, | |
| ) | |
| GLES20.glEnableVertexAttribArray(aVertexPositionLocation) | |
| GLES20.glEnableVertexAttribArray(aTexCoordLocation) | |
| checkGLError { GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4) } | |
| GLES20.glDisableVertexAttribArray(aVertexPositionLocation) | |
| GLES20.glDisableVertexAttribArray(aTexCoordLocation) | |
| // Vertical pass | |
| checkGLError { GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffers[1]) } | |
| checkGLError { | |
| GLES20.glFramebufferTexture2D( | |
| GLES20.GL_FRAMEBUFFER, | |
| GLES20.GL_COLOR_ATTACHMENT0, | |
| GLES20.GL_TEXTURE_2D, | |
| textureIds[2], | |
| 0 | |
| ) | |
| } | |
| if (GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) != GLES20.GL_FRAMEBUFFER_COMPLETE) { | |
| throw RuntimeException("Framebuffer is not complete") | |
| } | |
| checkGLError { GLES20.glUseProgram(verticalProgram) } | |
| val vVertexPositionLocation = | |
| GLES20.glGetAttribLocation(verticalProgram, "aPosition") | |
| val vTexCoordLocation = GLES20.glGetAttribLocation(verticalProgram, "aTexCoord") | |
| val vuTextureLocation = GLES20.glGetUniformLocation(verticalProgram, "u_Texture") | |
| val vsourceSizeLocation = GLES20.glGetUniformLocation(verticalProgram, "sourceSize") | |
| val vtargetSizeLocation = GLES20.glGetUniformLocation(verticalProgram, "targetSize") | |
| val vhorTexelSizeLocation = | |
| GLES20.glGetUniformLocation(verticalProgram, "texelSize") | |
| checkGLError { GLES20.glActiveTexture(GLES20.GL_TEXTURE0) } | |
| checkGLError { GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[1]) } | |
| checkGLError { GLES20.glUniform1i(vuTextureLocation, 0) } | |
| checkGLError { GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f) } | |
| checkGLError { GLES20.glViewport(0, 0, targetSize.width, bitmap.height) } | |
| checkGLError { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT or GLES20.GL_DEPTH_BUFFER_BIT) } | |
| GLES20.glEnableVertexAttribArray(vVertexPositionLocation) | |
| GLES20.glEnableVertexAttribArray(vTexCoordLocation) | |
| GLES20.glVertexAttribPointer( | |
| vVertexPositionLocation, | |
| 2, | |
| GLES20.GL_FLOAT, | |
| false, | |
| 0, | |
| vertexBuffer | |
| ) | |
| GLES20.glVertexAttribPointer( | |
| vTexCoordLocation, | |
| 2, | |
| GLES20.GL_FLOAT, | |
| false, | |
| 0, | |
| textureCoordinatesBuffer | |
| ) | |
| if (antialiasing != null) { | |
| val vantialisingLocation = | |
| GLES20.glGetUniformLocation(verticalProgram, "antialiasing") | |
| GLES20.glUniform1i(vantialisingLocation, antialiasing.value) | |
| } | |
| GLES20.glUniform2f( | |
| vhorTexelSizeLocation, | |
| 1.0f / targetSize.width.toFloat(), | |
| 1.0f / bitmap.height.toFloat() | |
| ) | |
| GLES20.glUniform2i( | |
| vsourceSizeLocation, | |
| targetSize.width, | |
| bitmap.height, | |
| ) | |
| GLES20.glUniform2i( | |
| vtargetSizeLocation, | |
| targetSize.width, | |
| targetSize.height, | |
| ) | |
| checkGLError { GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4) } | |
| GLES20.glFinish() | |
| GLES20.glDisableVertexAttribArray(vVertexPositionLocation) | |
| GLES20.glDisableVertexAttribArray(vTexCoordLocation) | |
| val buffer = ByteBuffer.allocateDirect(targetSize.width * bitmap.height * 4) | |
| .order(ByteOrder.nativeOrder()) | |
| GLES31.glReadPixels( | |
| 0, | |
| 0, | |
| targetSize.width, | |
| targetSize.height, | |
| GLES31.GL_RGBA, | |
| GLES31.GL_UNSIGNED_BYTE, | |
| buffer | |
| ) | |
| val outputBitmap = | |
| Bitmap.createBitmap(targetSize.width, targetSize.height, Bitmap.Config.ARGB_8888) | |
| outputBitmap.copyPixelsFromBuffer(buffer) | |
| return outputBitmap | |
| } | |
| private fun rebindTex(tex: Int, targetSize: Size) { | |
| GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex) | |
| 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 | |
| ) | |
| GLES20.glTexImage2D( | |
| GLES20.GL_TEXTURE_2D, | |
| 0, | |
| GLES20.GL_RGBA, | |
| targetSize.width, | |
| targetSize.height, | |
| 0, | |
| GLES20.GL_RGBA, | |
| GLES20.GL_UNSIGNED_BYTE, | |
| null | |
| ) | |
| GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0) | |
| } | |
| private fun loadBitmap(bitmap: Bitmap, tex: Int) { | |
| GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex) | |
| GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 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 | |
| ) | |
| GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0) | |
| } | |
| fun terminate() { | |
| if (eglDisplay != null && eglSurface != null && eglContext != null) { | |
| EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext) | |
| if (textureIds.isNotEmpty()) { | |
| GLES20.glDeleteTextures(textureIds.size, textureIds, 0) | |
| textureIds = IntArray(0) | |
| } | |
| if (frameBuffers.isNotEmpty()) { | |
| GLES20.glDeleteFramebuffers(frameBuffers.size, frameBuffers, 0) | |
| frameBuffers = IntArray(0) | |
| } | |
| if (nearestHorizontalProgram != -1) { | |
| GLES20.glDeleteProgram(nearestHorizontalProgram) | |
| nearestHorizontalProgram = -1 | |
| } | |
| if (nearestVerticalProgram != -1) { | |
| GLES20.glDeleteProgram(nearestVerticalProgram) | |
| nearestVerticalProgram = -1 | |
| } | |
| if (bilinearHorizontalProgram != -1) { | |
| GLES20.glDeleteProgram(bilinearHorizontalProgram) | |
| bilinearHorizontalProgram = -1 | |
| } | |
| if (bilinearVerticalProgram != -1) { | |
| GLES20.glDeleteProgram(bilinearVerticalProgram) | |
| bilinearVerticalProgram = -1 | |
| } | |
| if (lanczosHorizontalProgram != -1) { | |
| GLES20.glDeleteProgram(lanczosHorizontalProgram) | |
| lanczosHorizontalProgram = -1 | |
| } | |
| if (lanczosVerticalProgram != -1) { | |
| GLES20.glDeleteProgram(lanczosVerticalProgram) | |
| lanczosVerticalProgram = -1 | |
| } | |
| val egl = eglDisplay | |
| if (egl != null) { | |
| if (eglSurface != null) { | |
| EGL14.eglDestroySurface(egl, eglSurface) | |
| eglSurface = null | |
| } | |
| if (eglContext != null) { | |
| EGL14.eglDestroyContext(egl, eglContext) | |
| eglContext = null | |
| } | |
| EGL14.eglTerminate(egl) | |
| eglDisplay = null | |
| } | |
| } | |
| } | |
| protected fun finalize() { | |
| terminate() | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment