在安卓中结合使用GLSurfaceView和Canvas使用_笔记_可能有错

续:https://blog.csdn.net/cjzjolly/article/details/81667087

《使用GLSurfaceView实现涂鸦画板功能》

 

在上文中实现的OpenGL画线效果比较粗糙,但Canvas做绘制线条这方面的比较好用而且方便;但是Canvas移动图形实在是太慢了,所以我试着把两者结合起来使用——Canvas做线条绘制,当需要缩放和移动画面时,使用OpenGL加速。

 

一、坐标的摆弄:

默认长宽比为16比9的情况下,OpenGL的坐标如图所示:

 

无论什么分辨率,GLSurfaceView都会把视口设定成如上图所示的坐标,然后用百分比的方式读取

而Canvas的坐标如下所示(以1920*1080像素为例):

 

 

为了方便坐标对齐,我决定使用GLSurfaceView的右下角象限进行绘图,并且为了有最大到200%的的放大率,需要先预先把Z轴设定为-2f,也就是把整个画面往前“推”一下,此时坐标情况如下:

 

然后通过gl.glTranslatef(-2f * ratio, 2f, -2f) 平移画面到右下象限,此时的坐标如下:

此时,通过下面的一系列换算,即可把Canvas坐标投射到GLSurfaceView的坐标系中(在还没漫游过的情况下):

glX = canvasX / canvas宽 * 16 / 9 * 4

glY = - canvasY / canvas宽  * 4

 

 

 

 

二、缩放

缩放方面,Canvas和GLSurfaceView完全是不同的逻辑,从上一章的知识中可以发现,GLSurfaceView从默认的视距到2倍的视距时,坐标系在视口大小一样的情况下,单位数变成了2倍,也意味就像现实世界中的近大远小法则——等同速度的物体,越靠近观察者,视觉上移动速度越快。即:

当视距为1倍时,物体移动1个单位,在视口中实际显示的是移动1个单位;

当视距为2倍时,物体移动1个单位,在视口中实际显示的只会是移动1/2个单位。

而在Canvas中,无论怎么缩放,坐标轴是不会发生任何变化的。

 

那我们针对某一个缩放中心缩放一定的比例之后,如何使得两个模式显示的画面依然可以同步起来呢?

 

我们以Canvas中的画面,以(1920,1080)缩小到原来的50%来考虑这个问题:

设定Scale为0.5。

 

 

 

       此时已知Canvas已缩放Scale(0.5)的大小,那么我们的OpenGL画布需要推更远来使得画面看起来更小,例如变为原来的一半大小即Z轴的负数变大两倍,所以我们需要把Z轴的值调整为 -2f * (1 / scale),此时,你会看到OpenGL画布的内容以屏幕中心为缩放中心,所有图案缩小面积为原来的二分之一。

       但此时,OpenGL显示的内容虽然和Canvas显示的内容大小已经一样,但是方位却产生了错位,必须进行纠正。

       此时可以认为OpenGL的矩形x轴已经移动了(1-scale)比例那么多的位置量,那么如果我们现在要把它重新退回x为0的位置,则Canvas的方式下需要偏移(-1920 * (1 - scale))的单位量,而由于OpenGL存在Z轴,所以移动量要按照Z轴的距离乘以那么多倍(见第一章),所以OpenGL方式下需要偏移(-1920 * (1 - scale)) *  Z轴大小。而我们的缩放中心是1920,则相当于要右移OpenGL矩形到目标方位的话,需要(1920 * 2 - 1920 * (1 - scale))*  Z轴大小,然后再除以OpenGL视口的宽度得到比例值,从而最终得到确切的缩放后的偏移量。

glDx  = (缩放中心x * 2 -1920 * (1 - scale)) *  Z轴大小 / OpenGL视口宽度

glDy  = (缩放中心y * 2 -1080 * (1 - scale)) *  Z轴大小 / OpenGL视口宽度

 

 

效果如下:

 

测试用的Demo代码:

https://download.csdn.net/download/cjzjolly/11038207

要在 GLSurfaceView 显示 YUV 数据,你需要将 YUV 数据转换为 RGB 数据,然后将 RGB 数据传递给 OpenGL ES,最后在 GLSurfaceView 显示。这个过程可以通过 JNI 来完成。 以下是一个简单的示例代码: 1. Java 代码: ``` public class YuvRenderer implements GLSurfaceView.Renderer { private static final String TAG = "YuvRenderer"; private int mTextureId; private int mProgram; private int mPositionHandle; private int mTexCoordHandle; private int mYuvWidth; private int mYuvHeight; private ByteBuffer mYuvBuffer; public YuvRenderer() { mYuvWidth = 0; mYuvHeight = 0; mYuvBuffer = null; } public void setYuvData(int width, int height, byte[] yuvData) { mYuvWidth = width; mYuvHeight = height; mYuvBuffer = ByteBuffer.wrap(yuvData); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); GLES20.glDisable(GLES20.GL_DEPTH_TEST); mProgram = createProgram(); mPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition"); mTexCoordHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord"); int textureUniformHandle = GLES20.glGetUniformLocation(mProgram, "uTexture"); int[] textureIds = new int[1]; GLES20.glGenTextures(1, textureIds, 0); mTextureId = textureIds[0]; GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId); 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, mYuvWidth / 2, mYuvHeight / 2, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); GLES20.glUseProgram(mProgram); GLES20.glVertexAttribPointer(mPositionHandle, 2, GLES20.GL_FLOAT, false, 0, createVertexBuffer()); GLES20.glVertexAttribPointer(mTexCoordHandle, 2, GLES20.GL_FLOAT, false, 0, createTexCoordBuffer()); GLES20.glEnableVertexAttribArray(mPositionHandle); GLES20.glEnableVertexAttribArray(mTexCoordHandle); GLES20.glUniform1i(textureUniformHandle, 0); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { GLES20.glViewport(0, 0, width, height); } @Override public void onDrawFrame(GL10 gl) { if (mYuvBuffer == null) { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); return; } GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); byte[] yuvData = mYuvBuffer.array(); int[] rgbData = new int[mYuvWidth * mYuvHeight]; YuvUtils.convertYUV420ToRGB8888(yuvData, rgbData, mYuvWidth, mYuvHeight); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId); GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, mYuvWidth / 2, mYuvHeight / 2, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, ByteBuffer.wrap(rgbData)); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); } private int createProgram() { int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_CODE); int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_CODE); int program = GLES20.glCreateProgram(); GLES20.glAttachShader(program, vertexShader); GLES20.glAttachShader(program, fragmentShader); GLES20.glLinkProgram(program); return program; } private int loadShader(int shaderType, String shaderCode) { int shader = GLES20.glCreateShader(shaderType); GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); return shader; } private FloatBuffer createVertexBuffer() { float[] vertexData = new float[] { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, }; ByteBuffer byteBuffer = ByteBuffer.allocateDirect(vertexData.length * 4); byteBuffer.order(ByteOrder.nativeOrder()); FloatBuffer buffer = byteBuffer.asFloatBuffer(); buffer.put(vertexData); buffer.position(0); return buffer; } private FloatBuffer createTexCoordBuffer() { float[] texCoordData = new float[] { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, }; ByteBuffer byteBuffer = ByteBuffer.allocateDirect(texCoordData.length * 4); byteBuffer.order(ByteOrder.nativeOrder()); FloatBuffer buffer = byteBuffer.asFloatBuffer(); buffer.put(texCoordData); buffer.position(0); return buffer; } private static final String VERTEX_SHADER_CODE = "attribute vec4 aPosition;\n" + "attribute vec2 aTexCoord;\n" + "varying vec2 vTexCoord;\n" + "void main() {\n" + " gl_Position = aPosition;\n" + " vTexCoord = aTexCoord;\n" + "}"; private static final String FRAGMENT_SHADER_CODE = "precision mediump float;\n" + "uniform sampler2D uTexture;\n" + "varying vec2 vTexCoord;\n" + "void main() {\n" + " gl_FragColor = texture2D(uTexture, vTexCoord);\n" + "}"; } ``` 2. JNI 代码: ``` JNIEXPORT void JNICALL Java_com_example_yuvrenderer_YuvRenderer_setYuvData(JNIEnv *env, jobject obj, jint width, jint height, jbyteArray yuvData) { jclass clazz = env->GetObjectClass(obj); jfieldID yuvWidthField = env->GetFieldID(clazz, "mYuvWidth", "I"); jint yuvWidth = env->GetIntField(obj, yuvWidthField); jfieldID yuvHeightField = env->GetFieldID(clazz, "mYuvHeight", "I"); jint yuvHeight = env->GetIntField(obj, yuvHeightField); jbyte* yuvDataPtr = env->GetByteArrayElements(yuvData, NULL); jsize yuvDataSize = env->GetArrayLength(yuvData); if (yuvWidth != width || yuvHeight != height) { env->SetIntField(obj, yuvWidthField, width); env->SetIntField(obj, yuvHeightField, height); jclass byteBufferClazz = env->FindClass("java/nio/ByteBuffer"); jmethodID allocateDirectMethod = env->GetStaticMethodID(byteBufferClazz, "allocateDirect", "(I)Ljava/nio/ByteBuffer;"); jobject yuvBuffer = env->CallStaticObjectMethod(byteBufferClazz, allocateDirectMethod, yuvDataSize); env->SetObjectField(obj, env->GetFieldID(clazz, "mYuvBuffer", "Ljava/nio/ByteBuffer;"), yuvBuffer); } jobject yuvBuffer = env->GetObjectField(obj, env->GetFieldID(clazz, "mYuvBuffer", "Ljava/nio/ByteBuffer;")); env->GetDirectBufferAddress(yuvBuffer); memcpy(yuvBufferPtr, yuvDataPtr, yuvDataSize); env->ReleaseByteArrayElements(yuvData, yuvDataPtr, JNI_ABORT); } ``` 这个示例代码假设 YUV 数据是 NV21 格式的,你需要根据你的 YUV 数据格式进行相应的修改。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值