Android GLSurfaceView图像显示优化完整方案

  • 项目结构
    app/
    ├── src/main/
    │ ├── java/com/example/glimageviewer/
    │ │ ├── GLImageRenderer.kt # OpenGL渲染器
    │ │ ├── GLImageView.kt # 自定义GLSurfaceView
    │ │ ├── ImageFormat.kt # 图像格式定义
    │ │ ├── MainActivity.kt # 主Activity
    │ │ └── utils/
    │ │ ├── ImageUtils.kt # 图像处理工具
    │ │ └── ShaderUtils.kt # 着色器工具
    │ ├── res/
    │ │ ├── layout/
    │ │ │ ├── activity_main.xml # 主布局
    │ │ │ └── layout_gl_image.xml # GL图像显示布局
    │ │ └── values/
    │ │ └── colors.xml
    │ └── assets/
    │ └── shaders/
    │ ├── vertex_shader.glsl # 顶点着色器
    │ └── fragment_shader.glsl # 片段着色器

  • 核心代码实现

    1. 图像格式定义 (ImageFormat.kt)
    package com.example.glimageviewer
    
    /**
     * 图像格式枚举
     */
    enum class ImageFormat(val value: Int) {
        GRAY(0),        // 8位灰度图
        RGB(1),         // 24位RGB (R-G-B)
        BGR(2),         // 24位BGR (B-G-R)
        YUV420(3),      // YUV420格式 (Y平面 + U平面 + V平面)
        YUYV(4),        // YUYV交错格式
        NV21(5),        // NV21格式 (Y平面 + VU交错)
        NV12(6);        // NV12格式 (Y平面 + UV交错)
        
        companion object {
            fun fromValue(value: Int): ImageFormat {
                return values().find { it.value == value } ?: RGB
            }
        }
    }
    
    /**
     * 图像数据类
     */
    data class ImageData(
        val data: ByteBuffer,
        val width: Int,
        val height: Int,
        val format: ImageFormat,
        val timestamp: Long = System.currentTimeMillis()
    )
    
  1. 着色器工具类 (ShaderUtils.kt)

    package com.example.glimageviewer.utils
    
    import android.opengl.GLES30
    import android.util.Log
    
    object ShaderUtils {
        private const val TAG = "ShaderUtils"
        
        /**
         * 加载着色器
         */
        fun loadShader(type: Int, shaderCode: String): Int {
            val shader = GLES30.glCreateShader(type)
            GLES30.glShaderSource(shader, shaderCode)
            GLES30.glCompileShader(shader)
            
            // 检查编译状态
            val compiled = IntArray(1)
            GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0)
            if (compiled[0] == 0) {
                val error = GLES30.glGetShaderInfoLog(shader)
                Log.e(TAG, "Shader compilation failed: $error")
                GLES30.glDeleteShader(shader)
                return 0
            }
            return shader
        }
        
        /**
         * 创建着色器程序
         */
        fun createProgram(vertexShaderCode: String, fragmentShaderCode: String): Int {
            val vertexShader = loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)
            val fragmentShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode)
            
            if (vertexShader == 0 || fragmentShader == 0) {
                return 0
            }
            
            val program = GLES30.glCreateProgram()
            GLES30.glAttachShader(program, vertexShader)
            GLES30.glAttachShader(program, fragmentShader)
            GLES30.glLinkProgram(program)
            
            // 检查链接状态
            val linked = IntArray(1)
            GLES30.glGetProgramiv(program, GLES30.GL_LINK_STATUS, linked, 0)
            if (linked[0] == 0) {
                val error = GLES30.glGetProgramInfoLog(program)
                Log.e(TAG, "Program linking failed: $error")
                GLES30.glDeleteProgram(program)
                return 0
            }
            
            // 清理着色器
            GLES30.glDeleteShader(vertexShader)
            GLES30.glDeleteShader(fragmentShader)
            
            return program
        }
    }
    
  2. 图像处理工具类 (ImageUtils.kt)

    package com.example.glimageviewer.utils
    
    import android.graphics.Bitmap
    import android.graphics.Color
    import java.nio.ByteBuffer
    
    object ImageUtils {
        
        /**
         * Bitmap转RGB ByteBuffer
         */
        fun bitmapToRGB(bitmap: Bitmap): ByteBuffer {
            val rgbData = ByteArray(bitmap.width * bitmap.height * 3)
            var index = 0
            
            for (y in 0 until bitmap.height) {
                for (x in 0 until bitmap.width) {
                    val pixel = bitmap.getPixel(x, y)
                    rgbData[index++] = Color.red(pixel).toByte()
                    rgbData[index++] = Color.green(pixel).toByte()
                    rgbData[index++] = Color.blue(pixel).toByte()
                }
            }
            
            val buffer = ByteBuffer.allocateDirect(rgbData.size)
            buffer.put(rgbData)
            buffer.position(0)
            return buffer
        }
        
        /**
         * Bitmap转BGR ByteBuffer
         */
        fun bitmapToBGR(bitmap: Bitmap): ByteBuffer {
            val bgrData = ByteArray(bitmap.width * bitmap.height * 3)
            var index = 0
            
            for (y in 0 until bitmap.height) {
                for (x in 0 until bitmap.width) {
                    val pixel = bitmap.getPixel(x, y)
                    bgrData[index++] = Color.blue(pixel).toByte()
                    bgrData[index++] = Color.green(pixel).toByte()
                    bgrData[index++] = Color.red(pixel).toByte()
                }
            }
            
            val buffer = ByteBuffer.allocateDirect(bgrData.size)
            buffer.put(bgrData)
            buffer.position(0)
            return buffer
        }
        
        /**
         * 创建测试图像数据
         */
        fun createTestImage(width: Int, height: Int, format: ImageFormat): ByteBuffer {
            return when (format) {
                ImageFormat.GRAY -> createGrayTestImage(width, height)
                ImageFormat.RGB -> createRGBTestImage(width, height)
                ImageFormat.BGR -> createBGRTestImage(width, height)
                ImageFormat.YUV420 -> createYUV420TestImage(width, height)
                ImageFormat.YUYV -> createYUYVTestImage(width, height)
                ImageFormat.NV21 -> createNV21TestImage(width, height)
                ImageFormat.NV12 -> createNV12TestImage(width, height)
            }
        }
        
        private fun createGrayTestImage(width: Int, height: Int): ByteBuffer {
            val data = ByteArray(width * height)
            for (y in 0 until height) {
                for (x in 0 until width) {
                    val gray = ((x + y) * 255 / (width + height)).toByte()
                    data[y * width + x] = gray
                }
            }
            val buffer = ByteBuffer.allocateDirect(data.size)
            buffer.put(data)
            buffer.position(0)
            return buffer
        }
        
        private fun createRGBTestImage(width: Int, height: Int): ByteBuffer {
            val data = ByteArray(width * height * 3)
            var index = 0
            for (y in 0 until height) {
                for (x in 0 until width) {
                    data[index++] = (x * 255 / width).toByte()      // R
                    data[index++] = (y * 255 / height).toByte()     // G
                    data[index++] = 128.toByte()                    // B
                }
            }
            val buffer = ByteBuffer.allocateDirect(data.size)
            buffer.put(data)
            buffer.position(0)
            return buffer
        }
        
        private fun createBGRTestImage(width: Int, height: Int): ByteBuffer {
            val data = ByteArray(width * height * 3)
            var index = 0
            for (y in 0 until height) {
                for (x in 0 until width) {
                    data[index++] = 128.toByte()                    // B
                    data[index++] = (y * 255 / height).toByte()     // G
                    data[index++] = (x * 255 / width).toByte()      // R
                }
            }
            val buffer = ByteBuffer.allocateDirect(data.size)
            buffer.put(data)
            buffer.position(0)
            return buffer
        }
        
        private fun createYUV420TestImage(width: Int, height: Int): ByteBuffer {
            val ySize = width * height
            val uvSize = width * height / 4
            val data = ByteArray(ySize + uvSize * 2)
            
            // Y分量
            for (y in 0 until height) {
                for (x in 0 until width) {
                    val yValue = ((x + y) * 255 / (width + height)).toByte()
                    data[y * width + x] = yValue
                }
            }
            
            // U分量
            for (i in 0 until uvSize) {
                data[ySize + i] = 128.toByte()
            }
            
            // V分量
            for (i in 0 until uvSize) {
                data[ySize + uvSize + i] = 128.toByte()
            }
            
            val buffer = ByteBuffer.allocateDirect(data.size)
            buffer.put(data)
            buffer.position(0)
            return buffer
        }
        
        private fun createYUYVTestImage(width: Int, height: Int): ByteBuffer {
            val data = ByteArray(width * height * 2)
            var index = 0
            for (y in 0 until height) {
                for (x in 0 until width step 2) {
                    data[index++] = (x * 255 / width).toByte()      // Y1
                    data[index++] = 128.toByte()                    // U
                    data[index++] = (y * 255 / height).toByte()     // Y2
                    data[index++] = 128.toByte()                    // V
                }
            }
            val buffer = ByteBuffer.allocateDirect(data.size)
            buffer.put(data)
            buffer.position(0)
            return buffer
        }
        
        private fun createNV21TestImage(width: Int, height: Int): ByteBuffer {
            val ySize = width * height
            val vuSize = width * height / 2
            val data = ByteArray(ySize + vuSize)
            
            // Y分量
            for (y in 0 until height) {
                for (x in 0 until width) {
                    val yValue = ((x + y) * 255 / (width + height)).toByte()
                    data[y * width + x] = yValue
                }
            }
            
            // VU分量
            for (i in 0 until vuSize step 2) {
                data[ySize + i] = 128.toByte()      // V
                data[ySize + i + 1] = 128.toByte()  // U
            }
            
            val buffer = ByteBuffer.allocateDirect(data.size)
            buffer.put(data)
            buffer.position(0)
            return buffer
        }
        
        private fun createNV12TestImage(width: Int, height: Int): ByteBuffer {
            val ySize = width * height
            val uvSize = width * height / 2
            val data = ByteArray(ySize + uvSize)
            
            // Y分量
            for (y in 0 until height) {
                for (x in 0 until width) {
                    val yValue = ((x + y) * 255 / (width + height)).toByte()
                    data[y * width + x] = yValue
                }
            }
            
            // UV分量
            for (i in 0 until uvSize step 2) {
                data[ySize + i] = 128.toByte()      // U
                data[ySize + i + 1] = 128.toByte()  // V
            }
            
            val buffer = ByteBuffer.allocateDirect(data.size)
            buffer.put(data)
            buffer.position(0)
            return buffer
        }
    }
    
  3. OpenGL渲染器 (GLImageRenderer.kt)

    package com.example.glimageviewer
    
    import android.opengl.GLES30
    import android.opengl.GLSurfaceView
    import android.opengl.Matrix
    import android.util.Log
    import com.example.glimageviewer.utils.ShaderUtils
    import java.nio.ByteBuffer
    import java.nio.ByteOrder
    import java.nio.FloatBuffer
    import javax.microedition.khronos.egl.EGLConfig
    import javax.microedition.khronos.opengles.GL10
    
    /**
     * OpenGL ES 3.0 图像渲染器
     * 支持多种图像格式:GRAY, RGB, BGR, YUV420, YUYV, NV21, NV12
     */
    class GLImageRenderer : GLSurfaceView.Renderer {
        
        companion object {
            private const val TAG = "GLImageRenderer"
            
            // 顶点坐标 (全屏四边形)
            private val VERTEX_COORDS = floatArrayOf(
                -1.0f,  1.0f, 0.0f,  // 左上
                -1.0f, -1.0f, 0.0f,  // 左下
                 1.0f,  1.0f, 0.0f,  // 右上
                 1.0f, -1.0f, 0.0f   // 右下
            )
            
            // 纹理坐标
            private val TEX_COORDS = floatArrayOf(
                0.0f, 0.0f,  // 左下
                0.0f, 1.0f,  // 左上
                1.0f, 0.0f,  // 右下
                1.0f, 1.0f   // 右上
            )
        }
        
        // OpenGL对象
        private var program = 0
        private val textures = IntArray(3) // 最多3个纹理 (YUV需要3个)
        private var vertexBuffer: FloatBuffer? = null
        private var texCoordBuffer: FloatBuffer? = null
        
        // Uniform位置
        private var uMVPMatrixLocation = 0
        private var uFormatLocation = 0
        private var uTexture0Location = 0
        private var uTexture1Location = 0
        private var uTexture2Location = 0
        
        // 图像数据
        private var imageData: ImageData? = null
        private val mvpMatrix = FloatArray(16)
        
        // 线程安全
        private val lock = Object()
        
        init {
            initBuffers()
        }
        
        private fun initBuffers() {
            // 初始化顶点缓冲区
            vertexBuffer = ByteBuffer.allocateDirect(VERTEX_COORDS.size * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
            vertexBuffer?.put(VERTEX_COORDS)?.position(0)
            
            // 初始化纹理坐标缓冲区
            texCoordBuffer = ByteBuffer.allocateDirect(TEX_COORDS.size * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
            texCoordBuffer?.put(TEX_COORDS)?.position(0)
        }
        
        override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
            Log.d(TAG, "onSurfaceCreated")
            
            // 设置背景色
            GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
            
            // 创建着色器程序
            program = ShaderUtils.createProgram(VERTEX_SHADER_CODE, FRAGMENT_SHADER_CODE)
            if (program == 0) {
                Log.e(TAG, "Failed to create shader program")
                return
            }
            
            // 获取Uniform位置
            uMVPMatrixLocation = GLES30.glGetUniformLocation(program, "uMVPMatrix")
            uFormatLocation = GLES30.glGetUniformLocation(program, "uFormat")
            uTexture0Location = GLES30.glGetUniformLocation(program, "uTexture0")
            uTexture1Location = GLES30.glGetUniformLocation(program, "uTexture1")
            uTexture2Location = GLES30.glGetUniformLocation(program, "uTexture2")
            
            // 设置纹理单元
            GLES30.glUseProgram(program)
            GLES30.glUniform1i(uTexture0Location, 0)
            GLES30.glUniform1i(uTexture1Location, 1)
            GLES30.glUniform1i(uTexture2Location, 2)
            
            // 创建纹理
            GLES30.glGenTextures(3, textures, 0)
            for (i in 0..2) {
                GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textures[i])
                GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR)
                GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR)
                GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE)
                GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE)
            }
            
            // 初始化矩阵
            Matrix.setIdentityM(mvpMatrix, 0)
        }
        
        override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
            Log.d(TAG, "onSurfaceChanged: ${width}x${height}")
            
            GLES30.glViewport(0, 0, width, height)
            
            // 计算宽高比
            val ratio = width.toFloat() / height.toFloat()
            
            // 设置正交投影矩阵
            if (width > height) {
                Matrix.orthoM(mvpMatrix, 0, -ratio, ratio, -1f, 1f, -1f, 1f)
            } else {
                Matrix.orthoM(mvpMatrix, 0, -1f, 1f, -1f / ratio, 1f / ratio, -1f, 1f)
            }
        }
        
        override fun onDrawFrame(gl: GL10?) {
            GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
            
            if (program == 0) return
            
            GLES30.glUseProgram(program)
            
            // 传递MVP矩阵
            GLES30.glUniformMatrix4fv(uMVPMatrixLocation, 1, false, mvpMatrix, 0)
            
            // 获取当前图像数据
            val currentImageData = synchronized(lock) { imageData }
            
            if (currentImageData != null) {
                // 传递格式
                GLES30.glUniform1i(uFormatLocation, currentImageData.format.value)
                
                // 绑定纹理数据
                bindTextureData(currentImageData)
                
                // 绑定顶点坐标
                val aPosition = GLES30.glGetAttribLocation(program, "aPosition")
                GLES30.glEnableVertexAttribArray(aPosition)
                GLES30.glVertexAttribPointer(aPosition, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer)
                
                // 绑定纹理坐标
                val aTexCoord = GLES30.glGetAttribLocation(program, "aTexCoord")
                GLES30.glEnableVertexAttribArray(aTexCoord)
                GLES30.glVertexAttribPointer(aTexCoord, 2, GLES30.GL_FLOAT, false, 0, texCoordBuffer)
                
                // 绘制
                GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
                
                // 禁用顶点数组
                GLES30.glDisableVertexAttribArray(aPosition)
                GLES30.glDisableVertexAttribArray(aTexCoord)
            }
        }
        
        /**
         * 更新图像数据
         */
        fun updateImage(imageData: ImageData) {
            synchronized(lock) {
                this.imageData = imageData
            }
        }
        
        /**
         * 根据格式绑定纹理数据
         */
        private fun bindTextureData(imageData: ImageData) {
            when (imageData.format) {
                ImageFormat.GRAY -> bindGrayTexture(imageData)
                ImageFormat.RGB -> bindRGBTexture(imageData)
                ImageFormat.BGR -> bindBGRTexture(imageData)
                ImageFormat.YUV420 -> bindYUV420Texture(imageData)
                ImageFormat.YUYV -> bindYUYVTexture(imageData)
                ImageFormat.NV21 -> bindNV21Texture(imageData)
                ImageFormat.NV12 -> bindNV12Texture(imageData)
            }
        }
        
        private fun bindGrayTexture(imageData: ImageData) {
            GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textures[0])
            GLES30.glTexImage2D(
                GLES30.GL_TEXTURE_2D, 0, GLES30.GL_LUMINANCE,
                imageData.width, imageData.height, 0,
                GLES30.GL_LUMINANCE, GLES30.GL_UNSIGNED_BYTE, imageData.data
            )
        }
        
        private fun bindRGBTexture(imageData: ImageData) {
            GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textures[0])
            GLES30.glTexImage2D(
                GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGB,
                imageData.width, imageData.height, 0,
                GLES30.GL_RGB, GLES30.GL_UNSIGNED_BYTE, imageData.data
            )
        }
        
        private fun bindBGRTexture(imageData: ImageData) {
            GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textures[0])
            GLES30.glTexImage2D(
                GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGB,
                imageData.width, imageData.height, 0,
                GLES30.GL_RGB, GLES30.GL_UNSIGNED_BYTE, imageData.data
            )
        }
        
        private fun bindYUV420Texture(imageData: ImageData) {
            val ySize = imageData.width * imageData.height
            val uvSize = ySize / 4
            
            // Y分量
            GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textures[0])
            imageData.data.position(0)
            GLES30.glTexImage2D(
                GLES30.GL_TEXTURE_2D, 0, GLES30.GL_LUMINANCE,
                imageData.width, imageData.height, 0,
                GLES30.GL_LUMINANCE, GLES30.GL_UNSIGNED_BYTE, imageData.data
            )
            
            // U分量
            val uBuffer = ByteBuffer.allocateDirect(uvSize)
            imageData.data.position(ySize)
            for (i in 0 until uvSize) {
                uBuffer.put(imageData.data.get())
            }
            uBuffer.position(0)
            
            GLES30.glActiveTexture(GLES30.GL_TEXTURE1)
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textures[1])
            GLES30.glTexImage2D(
                GLES30.GL_TEXTURE_2D, 0, GLES30.GL_LUMINANCE,
                imageData.width / 2, imageData.height / 2, 0,
                GLES30.GL_LUMINANCE, GLES30.GL_UNSIGNED_BYTE, uBuffer
            )
            
            // V分量
            val vBuffer = ByteBuffer.allocateDirect(uvSize)
            imageData.data.position(ySize + uvSize)
            for (i in 0 until uvSize) {
                vBuffer.put(imageData.data.get())
            }
            vBuffer.position(0)
            
            GLES30.glActiveTexture(GLES30.GL_TEXTURE2)
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textures[2])
            GLES30.glTexImage2D(
                GLES30.GL_TEXTURE_2D, 0, GLES30.GL_LUMINANCE,
                imageData.width / 2, imageData.height / 2, 0,
                GLES30.GL_LUMINANCE, GLES30.GL_UNSIGNED_BYTE, vBuffer
            )
        }
        
        private fun bindYUYVTexture(imageData: ImageData) {
            GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textures[0])
            imageData.data.position(0)
            GLES30.glTexImage2D(
                GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA,
                imageData.width / 2, imageData.height, 0,
                GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, imageData.data
            )
        }
        
        private fun bindNV21Texture(imageData: ImageData) {
            val ySize = imageData.width * imageData.height
            val vuSize = ySize / 2
            
            // Y分量
            GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textures[0])
            imageData.data.position(0)
            GLES30.glTexImage2D(
                GLES30.GL_TEXTURE_2D, 0, GLES30.GL_LUMINANCE,
                imageData.width, imageData.height, 0,
                GLES30.GL_LUMINANCE, GLES30.GL_UNSIGNED_BYTE, imageData.data
            )
            
            // VU分量
            val vuBuffer = ByteBuffer.allocateDirect(vuSize)
            imageData.data.position(ySize)
            for (i in 0 until vuSize) {
                vuBuffer.put(imageData.data.get())
            }
            vuBuffer.position(0)
            
            GLES30.glActiveTexture(GLES30.GL_TEXTURE1)
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textures[1])
            GLES30.glTexImage2D(
                GLES30.GL_TEXTURE_2D, 0, GLES30.GL_LUMINANCE_ALPHA,
                imageData.width / 2, imageData.height / 2, 0,
                GLES30.GL_LUMINANCE_ALPHA, GLES30.GL_UNSIGNED_BYTE, vuBuffer
            )
        }
        
        private fun bindNV12Texture(imageData: ImageData) {
            val ySize = imageData.width * imageData.height
            val uvSize = ySize / 2
            
            // Y分量
            GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textures[0])
            imageData.data.position(0)
            GLES30.glTexImage2D(
                GLES30.GL_TEXTURE_2D, 0, GLES30.GL_LUMINANCE,
                imageData.width, imageData.height, 0,
                GLES30.GL_LUMINANCE, GLES30.GL_UNSIGNED_BYTE, imageData.data
            )
            
            // UV分量
            val uvBuffer = ByteBuffer.allocateDirect(uvSize)
            imageData.data.position(ySize)
            for (i in 0 until uvSize) {
                uvBuffer.put(imageData.data.get())
            }
            uvBuffer.position(0)
            
            GLES30.glActiveTexture(GLES30.GL_TEXTURE1)
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textures[1])
            GLES30.glTexImage2D(
                GLES30.GL_TEXTURE_2D, 0, GLES30.GL_LUMINANCE_ALPHA,
                imageData.width / 2, imageData.height / 2, 0,
                GLES30.GL_LUMINANCE_ALPHA, GLES30.GL_UNSIGNED_BYTE, uvBuffer
            )
        }
        
        /**
         * 释放资源
         */
        fun release() {
            if (program != 0) {
                GLES30.glDeleteProgram(program)
                program = 0
            }
            GLES30.glDeleteTextures(3, textures, 0)
        }
        
        companion object {
             // 顶点着色器
    	    private val VERTEX_SHADER_CODE = "#version 300 es\n" +
    	            "uniform mat4 uMVPMatrix;\n" +
    	            "layout(location=0) in vec4 aPosition;\n" +
    	            "layout(location=1) in vec2 aTexCoord;\n" +
    	            "out vec2 vTexCoord;\n" +
    	            "\n" +
    	            "void main() {\n" +
    	            "    gl_Position = uMVPMatrix * aPosition;\n" +
    	            "    vTexCoord = aTexCoord;\n" +
    	            "}"
    
    	    // 片段着色器
    	    private val FRAGMENT_SHADER_CODE = "#version 300 es\n" +
    	            "precision mediump float;\n" +
    	            "\n" +
    	            "uniform int uFormat;\n" +
    	            "uniform sampler2D uTexture0;\n" +
    	            "uniform sampler2D uTexture1;\n" +
    	            "uniform sampler2D uTexture2;\n" +
    	            "\n" +
    	            "in vec2 vTexCoord;\n" +
    	            "out vec4 outColor;\n" +
    	            "\n" +
    	            "// 格式常量\n" +
    	            "#define FORMAT_GRAY 0\n" +
    	            "#define FORMAT_YUYV 1\n" +
    	            "#define FORMAT_YUV420 2\n" +
    	            "#define FORMAT_BGR 3\n" +
    	            "          \n" +
    	            "\n" +
    	            "\n" +
    	            "// YUV转RGB转换函数 (BT.601标准)\n" +
    	            "vec4 yuvToRgb(float y, float u, float v) {\n" +
    	            "    y = 1.1643 * (y - 0.0625);\n" +
    	            "    u = u - 0.5;\n" +
    	            "    v = v - 0.5;\n" +
    	            "    \n" +
    	            "    float r = clamp(y + 1.5958 * v, 0.0, 1.0);\n" +
    	            "    float g = clamp(y - 0.39173 * u - 0.81290 * v, 0.0, 1.0);\n" +
    	            "    float b = clamp(y + 2.017 * u, 0.0, 1.0);\n" +
    	            "    \n" +
    	            "    return vec4(r, g, b, 1.0);\n" +
    	            "}\n" +
    	            "\n" +
    	            "// 处理YUYV格式\n" +
    	            "vec4 processYUYV() {\n" +
    	            "    vec2 realCoord = vec2(vTexCoord.x * 2.0, vTexCoord.y);\n" +
    	            "    vec4 yuyv = texture(uTexture0, vec2(realCoord.x * 0.5, realCoord.y));\n" +
    	            "    \n" +
    	            "    float y, u, v;\n" +
    	            "    if (fract(realCoord.x) < 0.5) {\n" +
    	            "        y = yuyv.r;\n" +
    	            "        u = yuyv.g;\n" +
    	            "        v = yuyv.a;\n" +
    	            "    } else {\n" +
    	            "        y = yuyv.b;\n" +
    	            "        u = yuyv.g;\n" +
    	            "        v = yuyv.a;\n" +
    	            "    }\n" +
    	            "    \n" +
    	            "    return yuvToRgb(y, u, v);\n" +
    	            "}\n" +
    	            "\n" +
    	            "void main() {\n" +
    	            "    switch(uFormat) {\n" +
    	            "        case FORMAT_GRAY:\n" +
    	            "            float gray = texture(uTexture0, vTexCoord).r;\n" +
    	            "            outColor = vec4(gray, gray, gray, 1.0);\n" +
    	            "            break;\n" +
    	            "            \n" +
    	            "        case FORMAT_YUYV:\n" +
    	            "            outColor = processYUYV();\n" +
    	            "            break;\n" +
    	            "            \n" +
    	            "         case FORMAT_YUV420:\n" +
    	            "            float y = texture(uTexture0, vTexCoord).r;\n" +
    	            "            float u = texture(uTexture1, vTexCoord).r;\n" +
    	            "            float v = texture(uTexture2, vTexCoord).r;\n" +
    	            "            outColor = yuvToRgb(y, u, v);\n" +
    	            "            break;    \n" +
    	            "            \n" +
    	            "        case FORMAT_BGR:\n" +
    	            "            vec4 rgbColor = texture(uTexture0, vTexCoord);\n" +
    	            "            outColor = vec4(rgbColor.b, rgbColor.g, rgbColor.r, 1.0);\n" +
    	            "            break;\n" +
    	            "            \n" +
    	            "        default:\n" +
    	            "            outColor = vec4(1.0, 0.0, 0.0, 1.0);\n" +
    	            "    }\n" +
    	            "}"
        }
    }
    
  4. 自定义GLSurfaceView (GLImageView.kt)

    package com.example.glimageviewer
    
    import android.content.Context
    import android.graphics.PixelFormat
    import android.opengl.GLSurfaceView
    import android.util.AttributeSet
    import android.util.Log
    
    /**
     * 自定义GLSurfaceView用于图像显示
     */
    class GLImageView @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0
    ) : GLSurfaceView(context, attrs, defStyleAttr) {
        
        companion object {
            private const val TAG = "GLImageView"
        }
        
        private var renderer: GLImageRenderer? = null
        
        init {
            initGLSurfaceView()
        }
        
        private fun initGLSurfaceView() {
            // 设置OpenGL ES 3.0
            setEGLContextClientVersion(3)
            
            // 设置EGL配置
            setEGLConfigChooser(8, 8, 8, 8, 16, 0)
            
            // 设置像素格式
            holder.setFormat(PixelFormat.TRANSLUCENT)
            
            // 创建渲染器
            renderer = GLImageRenderer()
            setRenderer(renderer)
            
            // 设置渲染模式为按需渲染
            renderMode = RENDERMODE_WHEN_DIRTY
            
            Log.d(TAG, "GLImageView initialized")
        }
        
        /**
         * 更新图像数据
         */
        fun updateImage(imageData: ImageData) {
            renderer?.updateImage(imageData)
            requestRender()
        }
        
        /**
         * 更新图像数据 (便捷方法)
         */
        fun updateImage(data: ByteBuffer, width: Int, height: Int, format: ImageFormat) {
            val imageData = ImageData(data, width, height, format)
            updateImage(imageData)
        }
        
        /**
         * 释放资源
         */
        fun release() {
            renderer?.release()
            renderer = null
        }
        
        override fun onDetachedFromWindow() {
            release()
            super.onDetachedFromWindow()
        }
    }
    
  5. 主Activity (MainActivity.kt)

    package com.example.glimageviewer
    
    import android.os.Bundle
    import android.util.Log
    import android.widget.Button
    import android.widget.Spinner
    import android.widget.TextView
    import androidx.appcompat.app.AppCompatActivity
    import com.example.glimageviewer.utils.ImageUtils
    import java.nio.ByteBuffer
    
    class MainActivity : AppCompatActivity() {
        
        companion object {
            private const val TAG = "MainActivity"
            private const val TEST_WIDTH = 640
            private const val TEST_HEIGHT = 480
        }
        
        private lateinit var glImageView: GLImageView
        private lateinit var formatSpinner: Spinner
        private lateinit var updateButton: Button
        private lateinit var infoTextView: TextView
        
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            
            initViews()
            setupListeners()
        }
        
        private fun initViews() {
            glImageView = findViewById(R.id.glImageView)
            formatSpinner = findViewById(R.id.formatSpinner)
            updateButton = findViewById(R.id.updateButton)
            infoTextView = findViewById(R.id.infoTextView)
            
            // 显示初始信息
            updateInfo("GLSurfaceView图像显示器已初始化")
        }
        
        private fun setupListeners() {
            updateButton.setOnClickListener {
                updateTestImage()
            }
        }
        
        private fun updateTestImage() {
            val selectedFormat = when (formatSpinner.selectedItemPosition) {
                0 -> ImageFormat.GRAY
                1 -> ImageFormat.RGB
                2 -> ImageFormat.BGR
                3 -> ImageFormat.YUV420
                4 -> ImageFormat.YUYV
                5 -> ImageFormat.NV21
                6 -> ImageFormat.NV12
                else -> ImageFormat.RGB
            }
            
            try {
                val testData = ImageUtils.createTestImage(TEST_WIDTH, TEST_HEIGHT, selectedFormat)
                glImageView.updateImage(testData, TEST_WIDTH, TEST_HEIGHT, selectedFormat)
                
                val info = "格式: ${selectedFormat.name}, 尺寸: ${TEST_WIDTH}x${TEST_HEIGHT}, 时间: ${System.currentTimeMillis()}"
                updateInfo(info)
                
                Log.d(TAG, "Updated image: $info")
            } catch (e: Exception) {
                Log.e(TAG, "Failed to update image", e)
                updateInfo("更新图像失败: ${e.message}")
            }
        }
        
        private fun updateInfo(info: String) {
            infoTextView.text = info
        }
        
        override fun onDestroy() {
            glImageView.release()
            super.onDestroy()
        }
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菠萝加点糖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值