├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── dandy │ │ └── demo │ │ └── gltextureviewdemo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── GLESDemo │ │ │ └── SimpleTriangle │ │ │ ├── gles_triangle.frag │ │ │ ├── gles_triangle.mat │ │ │ └── gles_triangle.vert │ ├── java │ │ └── com │ │ │ └── dandy │ │ │ ├── demo │ │ │ └── gltextureviewdemo │ │ │ │ └── MainActivity.java │ │ │ ├── gldemo │ │ │ └── glestextureview │ │ │ │ ├── DemoGlesTextureView.java │ │ │ │ ├── GLESRendererImpl.java │ │ │ │ ├── MatrixAid.java │ │ │ │ └── Triangle.java │ │ │ ├── helper │ │ │ ├── android │ │ │ │ ├── FileHelper.java │ │ │ │ ├── LogHelper.java │ │ │ │ └── SDCardHelper.java │ │ │ ├── gles │ │ │ │ ├── CommonUtils.java │ │ │ │ ├── IGLESRenderer.java │ │ │ │ ├── MaterialAider.java │ │ │ │ └── ShaderHelper.java │ │ │ └── java │ │ │ │ ├── JFileHelper.java │ │ │ │ └── PendingThreadAider.java │ │ │ └── module │ │ │ └── gles │ │ │ └── textureview │ │ │ ├── GLESTVThread.java │ │ │ └── GLESTextureView.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── dandy │ └── demo │ └── gltextureviewdemo │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GLTextureViewDemo 2 | A sample that use a TextureView to show gles operation 3 | 一个简单的展示opengl的TextureView 4 | http://flycatdeng.iteye.com/admin/blogs/2359280 5 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | defaultConfig { 7 | applicationId "com.dandy.demo.gltextureviewdemo" 8 | minSdkVersion 19 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:25.2.0' 28 | testCompile 'junit:junit:4.12' 29 | } 30 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\flycatdeng\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/dandy/demo/gltextureviewdemo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.dandy.demo.gltextureviewdemo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.dandy.demo.gltextureviewdemo", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/assets/GLESDemo/SimpleTriangle/gles_triangle.frag: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | varying vec4 vColor; //接收从顶点着色器过来的参数 3 | 4 | void main() 5 | { 6 | gl_FragColor = vColor;//给此片元颜色值 7 | } -------------------------------------------------------------------------------- /app/src/main/assets/GLESDemo/SimpleTriangle/gles_triangle.mat: -------------------------------------------------------------------------------- 1 | Version=1.0 2 | VertexFile=gles_triangle.vert 3 | FragmentFile=gles_triangle.frag 4 | -------------------------------------------------------------------------------- /app/src/main/assets/GLESDemo/SimpleTriangle/gles_triangle.vert: -------------------------------------------------------------------------------- 1 | uniform mat4 uMVPMatrix; //总变换矩阵 2 | attribute vec3 aPosition; //顶点位置 3 | attribute vec4 aColor; //顶点颜色 4 | varying vec4 vColor; //用于传递给片元着色器的变量 5 | 6 | void main() 7 | { 8 | gl_Position = uMVPMatrix * vec4(aPosition,1); //根据总变换矩阵计算此次绘制此顶点位置 9 | vColor = aColor;//将接收的颜色传递给片元着色器 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/dandy/demo/gltextureviewdemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.dandy.demo.gltextureviewdemo; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | 6 | import com.dandy.gldemo.glestextureview.DemoGlesTextureView; 7 | 8 | public class MainActivity extends AppCompatActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | DemoGlesTextureView view=new DemoGlesTextureView(this); 14 | // setContentView(R.layout.activity_main); 15 | setContentView(view); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/dandy/gldemo/glestextureview/DemoGlesTextureView.java: -------------------------------------------------------------------------------- 1 | package com.dandy.gldemo.glestextureview; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | import com.dandy.module.gles.textureview.GLESTextureView; 7 | 8 | /** 9 | * 调用方法类似于GLSurfaceView的了 10 | * 11 | * @author flycatdeng 12 | * 13 | */ 14 | public class DemoGlesTextureView extends GLESTextureView { 15 | protected static final String TAG = "DemoGlesTextureView"; 16 | 17 | public DemoGlesTextureView(Context context) { 18 | super(context); 19 | init(context); 20 | } 21 | 22 | public DemoGlesTextureView(Context context, AttributeSet attrs) { 23 | super(context, attrs); 24 | init(context); 25 | } 26 | 27 | private void init(Context context) { 28 | GLESRendererImpl renerer = new GLESRendererImpl(context); 29 | setRenderer(renerer); 30 | // setRenderMode(RENDERMODE_CONTINUOUSLY); 31 | setRenderMode(RENDERMODE_WHEN_DIRTY); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/dandy/gldemo/glestextureview/GLESRendererImpl.java: -------------------------------------------------------------------------------- 1 | package com.dandy.gldemo.glestextureview; 2 | 3 | import android.content.Context; 4 | import android.opengl.GLES20; 5 | 6 | import com.dandy.helper.android.LogHelper; 7 | import com.dandy.helper.gles.IGLESRenderer; 8 | 9 | /** 10 | * GLESRendererImpl可以被GLSurfaceView的Renderer和GLESTextureView复用 11 | * 12 | * @author flycatdeng 13 | * 14 | */ 15 | public class GLESRendererImpl implements IGLESRenderer { 16 | private static final String TAG = "GLESRendererImpl"; 17 | private Context mContext; 18 | private Triangle mTriangle; 19 | 20 | public GLESRendererImpl(Context context) { 21 | mContext = context; 22 | } 23 | 24 | @Override 25 | public void onSurfaceCreated() { 26 | LogHelper.d(TAG, LogHelper.getThreadName()); 27 | // 设置屏幕背景色RGBA 28 | GLES20.glClearColor(1, 0, 0, 1.0f); 29 | GLES20.glEnable(GLES20.GL_DEPTH_TEST); 30 | mTriangle = new Triangle(mContext); 31 | } 32 | 33 | @Override 34 | public void onSurfaceChanged(int width, int height) { 35 | LogHelper.d(TAG, LogHelper.getThreadName()); 36 | // 设置视窗大小及位置 37 | GLES20.glViewport(0, 0, width, height); 38 | mTriangle.onSurfaceChanged(width, height); 39 | } 40 | 41 | @Override 42 | public void onResume() { 43 | // do something 44 | } 45 | 46 | @Override 47 | public void onPause() { 48 | // do something 49 | } 50 | 51 | @Override 52 | public void onDrawFrame() {// 绘制 53 | // 清除深度缓冲与颜色缓冲(就是清除缓存) 54 | GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); 55 | mTriangle.onDrawSelf(); 56 | } 57 | 58 | @Override 59 | public void onDestroy() { 60 | // do something 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/dandy/gldemo/glestextureview/MatrixAid.java: -------------------------------------------------------------------------------- 1 | package com.dandy.gldemo.glestextureview; 2 | 3 | import android.opengl.Matrix; 4 | 5 | /** 6 | * 7 | * @author dengchukun 2016年11月26日 8 | */ 9 | public class MatrixAid { 10 | public float[] mProjMatrix = new float[16];// 4x4矩阵 投影用 11 | public float[] mVMatrix = new float[16];// 摄像机位置朝向9参数矩阵 12 | public float[] mMMatrix = new float[16];// 具体物体的移动旋转矩阵,旋转、平移 13 | public float[] mMVPMatrix;// 最后起作用的总变换矩阵 14 | 15 | public float[] getFianlMatrix(float[] spec) { 16 | mMVPMatrix = new float[16]; 17 | Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, spec, 0); 18 | Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0); 19 | return mMVPMatrix; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/dandy/gldemo/glestextureview/Triangle.java: -------------------------------------------------------------------------------- 1 | package com.dandy.gldemo.glestextureview; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.nio.ByteOrder; 5 | import java.nio.FloatBuffer; 6 | 7 | import android.content.Context; 8 | import android.opengl.GLES20; 9 | import android.opengl.Matrix; 10 | 11 | import com.dandy.helper.android.LogHelper; 12 | import com.dandy.helper.gles.MaterialAider; 13 | import com.dandy.helper.gles.ShaderHelper; 14 | 15 | /** 16 | * 这个三角形及其用到的材质都是自写的,没有用到OpenGL内建变量,就连总变换矩阵,顶点坐标以及颜色等都是由代码传进去的 17 | * 18 | * @author dengchukun 2016年11月26日 19 | */ 20 | public class Triangle { 21 | private Context mContext; 22 | private MatrixAid mMatrix = new MatrixAid(); 23 | private int mVertexCount = 0; 24 | // 之所以要有缓存数据是因为绘制每一帧图像的时候,如果缓存已经有数据了,就不需要每次都将顶点数据通过GL传进去了,这样就可以省好多IO带宽 25 | private FloatBuffer mVertexBuffer;// 顶点坐标数据缓冲 26 | private FloatBuffer mColorBuffer;// 顶点着色数据缓冲 27 | private int mProgram;// 自定义渲染管线程序id 28 | private int muMVPMatrixHandle;// 总变换矩阵引用id 29 | private int maPositionHandle; // 顶点位置属性引用id 30 | private int maColorHandle; // 顶点颜色属性引用id 31 | 32 | public Triangle(Context context) { 33 | mContext = context; 34 | // 初始化顶点坐标与着色数据 35 | initVertexData(); 36 | // 初始化shader 37 | initShader(); 38 | } 39 | 40 | /** 41 | *
 42 |      * 
 43 |      * 
44 | */ 45 | private void initVertexData() { 46 | // 顶点坐标数据的初始化 47 | mVertexCount = 3; 48 | final float UNIT_SIZE = 0.2f; 49 | float vertices[] = new float[]// 首先将顶点此属性数据一次存放入数组,这里是顶点坐标 50 | { // 51 | -4 * UNIT_SIZE, 0, 0, // 第1个顶点的XYZ坐标值 52 | 0, -4 * UNIT_SIZE, 0, // // 第2个顶点的XYZ坐标值 53 | 4 * UNIT_SIZE, 0, 0// // 第3个顶点的XYZ坐标值 54 | }; 55 | ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);// 开辟对应容量的缓冲 56 | vbb.order(ByteOrder.nativeOrder());// 设置字节顺序为本地操作系统顺序 57 | mVertexBuffer = vbb.asFloatBuffer();// 浮点型缓冲 58 | mVertexBuffer.put(vertices);// 将数组中的顶点数据送入缓冲 59 | mVertexBuffer.position(0);// 设置缓冲的其实位置 60 | 61 | float colors[] = new float[]// 62 | { // 63 | 1, 1, 1, 0, // 64 | 0, 0, 1, 0, // 65 | 0, 1, 0, 0// 66 | }; 67 | ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length * 4); 68 | cbb.order(ByteOrder.nativeOrder()); 69 | mColorBuffer = cbb.asFloatBuffer(); 70 | mColorBuffer.put(colors); 71 | mColorBuffer.position(0); 72 | } 73 | 74 | /** 75 | *
 76 |      * 
 77 |      * 
78 | */ 79 | private void initShader() { 80 | MaterialAider mat = new MaterialAider(mContext); 81 | mat.setMaterialName("GLESDemo/SimpleTriangle/gles_triangle.mat"); 82 | mProgram = ShaderHelper.getProgramFromAsset(mContext, mat); 83 | // 获取程序中顶点位置属性引用id 84 | maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition"); 85 | // 获取程序中顶点颜色属性引用id 86 | maColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor"); 87 | // 获取程序中总变换矩阵引用id 88 | muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); 89 | } 90 | 91 | /** 92 | *
 93 |      * 
 94 |      * 
95 | */ 96 | public void onDrawSelf() { 97 | LogHelper.d("Triangle", "onDrawSelf"); 98 | // 制定使用某套shader程序 99 | GLES20.glUseProgram(mProgram); 100 | // 初始化变换矩阵 101 | Matrix.setRotateM(mMatrix.mMMatrix, 0, 0, 0, 1, 0); 102 | // 设置沿Z轴正向位移1 103 | Matrix.translateM(mMatrix.mMMatrix, 0, 0, 0, 1); 104 | // 设置绕x轴旋转 105 | Matrix.rotateM(mMatrix.mMMatrix, 0, 30, 1, 0, 0); 106 | // 107 | GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMatrix.getFianlMatrix(mMatrix.mMMatrix), 0); 108 | // 主要分为三步,1.获取着色器对应属性变量的引用 109 | // 2.通过引用将缓存中的数据传入管线 110 | // 3.启用传入的数据 111 | // 为画笔指定顶点位置数据 112 | // glVertexAttribPointer将顶点坐标数据以及顶点颜色数据传送进渲染管线,以备渲染时在顶点着色器中使用 113 | GLES20.glVertexAttribPointer(// 将顶点位置数据传送进渲染管线 114 | maPositionHandle, // 顶点位置属性引用 115 | 3, // 每顶点一组的数据个数(这里是X、Y、Z坐标,所以是3) 116 | GLES20.GL_FLOAT, // 数据类型 117 | false, // 是否格式化 118 | 3 * 4, // 每组数据的尺寸,这里每组3个浮点数值(XYZ坐标),每个浮点数4个字节所以是3*4=12个字节 119 | mVertexBuffer// 存放了数据的缓存 120 | ); 121 | GLES20.glVertexAttribPointer(maColorHandle, 4, GLES20.GL_FLOAT, false, 4 * 4, mColorBuffer); 122 | // 允许顶点位置数据数组 123 | // glEnableVertexAttribArray启用顶点位置数据和顶点颜色数据 124 | GLES20.glEnableVertexAttribArray(maPositionHandle); 125 | GLES20.glEnableVertexAttribArray(maColorHandle); 126 | // 绘制三角形 127 | GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mVertexCount); 128 | } 129 | 130 | /** 131 | *
132 |      * 
133 |      * 
134 | * 135 | * @param width 136 | * @param height 137 | */ 138 | public void onSurfaceChanged(int width, int height) { 139 | // 计算GLSurfaceView的宽高比 140 | float ratio = (float) width / height; 141 | // 调用此方法计算产生透视投影矩阵 142 | Matrix.frustumM(mMatrix.mProjMatrix, // 存储生成矩阵元素的float[]类型数组 143 | 0, // 填充起始偏移量 144 | -ratio, ratio, // near面的left、right 145 | -1, 1, // near面的bottom、top 146 | 1, 10// near面、far面与视点的距离 147 | ); 148 | // 调用此方法产生摄像机9参数位置矩阵 149 | Matrix.setLookAtM(mMatrix.mVMatrix, // 存储生成矩阵元素的float[]类型数组 150 | 0, // 填充起始偏移量 151 | 0, 0, 3, // 摄像机位置的XYZ坐标 152 | 0f, 0f, 0f, // 观察目标点XYZ坐标 153 | 0.0f, 1.0f, 0.0f// up向量在XYZ轴上的分量 154 | ); 155 | 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /app/src/main/java/com/dandy/helper/android/FileHelper.java: -------------------------------------------------------------------------------- 1 | package com.dandy.helper.android; 2 | 3 | import java.io.File; 4 | import java.io.FileNotFoundException; 5 | import java.io.FileOutputStream; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | 9 | import android.content.Context; 10 | import android.graphics.Bitmap; 11 | 12 | import com.dandy.helper.java.JFileHelper; 13 | 14 | /** 15 | * 文件帮助类,和IO帮助类 16 | * 17 | * @author dengchukun 2016年11月18日 18 | */ 19 | public class FileHelper { 20 | 21 | private static final String TAG = FileHelper.class.getSimpleName(); 22 | 23 | /** 24 | * 从assets目录下拷贝整个文件夹,不管是文件夹还是文件都能拷贝 25 | * 26 | * @param context 27 | * 上下文 28 | * @param rootDirFullPath 29 | * 文件目录,要拷贝的目录如assets目录下有一个SBClock文件夹:SBClock 30 | * @param targetDirFullPath 31 | * 目标文件夹位置如:/sdcrad/SBClock 32 | */ 33 | public static boolean copyFolderFromAssets(Context context, String rootDirFullPath, String targetDirFullPath) { 34 | LogHelper.d(TAG, "copyFolderFromAssets " + "rootDirFullPath-" + rootDirFullPath + " targetDirFullPath-" + targetDirFullPath); 35 | File file = new File(targetDirFullPath); 36 | if (file.exists()) { 37 | LogHelper.d(TAG, LogHelper.getThreadName() + "file exists"); 38 | } else { 39 | boolean success = file.mkdirs(); 40 | LogHelper.d(TAG, "copyFolderFromAssets mkdir status: " + success + " isSDCardExist()-" + SDCardHelper.isSDCardExist()); 41 | if (!success) { 42 | return false; 43 | } 44 | } 45 | try { 46 | String[] listFiles = context.getAssets().list(rootDirFullPath);// 遍历该目录下的文件和文件夹 47 | for (String string : listFiles) {// 看起子目录是文件还是文件夹,这里只好用.做区分了 48 | LogHelper.d(TAG, "name-" + rootDirFullPath + "/" + string); 49 | if (isFileByName(string)) {// 文件 50 | copyFileFromAssets(context, rootDirFullPath + "/" + string, targetDirFullPath + "/" + string); 51 | } else {// 文件夹 52 | String childRootDirFullPath = rootDirFullPath + "/" + string; 53 | String childTargetDirFullPath = targetDirFullPath + "/" + string; 54 | new File(childTargetDirFullPath).mkdirs(); 55 | copyFolderFromAssets(context, childRootDirFullPath, childTargetDirFullPath); 56 | } 57 | } 58 | return true; 59 | } catch (IOException e) { 60 | LogHelper.d(TAG, LogHelper.getThreadName() + "IOException-" + e.getMessage()); 61 | e.printStackTrace(); 62 | return false; 63 | } 64 | } 65 | 66 | private static boolean isFileByName(String string) { 67 | if (string.contains(".")) { 68 | return true; 69 | } 70 | return false; 71 | } 72 | 73 | /** 74 | * 从assets目录下拷贝文件 75 | * 76 | * @param context 77 | * 上下文 78 | * @param assetsFilePath 79 | * 文件的路径名如:SBClock/0001cuteowl/cuteowl_dot.png 80 | * @param targetFileFullPath 81 | * 目标文件路径如:/sdcard/SBClock/0001cuteowl/cuteowl_dot.png 82 | */ 83 | public static void copyFileFromAssets(Context context, String assetsFilePath, String targetFileFullPath) { 84 | LogHelper.d(TAG, LogHelper.getThreadName()); 85 | InputStream assestsFileImputStream; 86 | try { 87 | assestsFileImputStream = context.getAssets().open(assetsFilePath); 88 | JFileHelper.copyFile(assestsFileImputStream, targetFileFullPath); 89 | } catch (IOException e) { 90 | LogHelper.d(TAG, LogHelper.getThreadName() + "IOException-" + e.getMessage()); 91 | e.printStackTrace(); 92 | } 93 | } 94 | 95 | /** 96 | * 从assets目录下获取文本文件内容 97 | * 98 | * @param context 99 | * 上下文 100 | * @param fileAssetPath 101 | * 文本文件路径 102 | * @return 103 | */ 104 | public static String getFileContentFromAsset(Context context, String fileAssetPath) { 105 | InputStream ins; 106 | try { 107 | ins = context.getAssets().open(fileAssetPath); 108 | byte[] contentByte = new byte[ins.available()]; 109 | ins.read(contentByte); 110 | return new String(contentByte); 111 | } catch (Exception e1) { 112 | e1.printStackTrace(); 113 | } 114 | return ""; 115 | } 116 | 117 | public static InputStream getInputStreamFromAsset(Context context, String fileAssetPath) { 118 | LogHelper.d(TAG, LogHelper.getThreadName()); 119 | InputStream ins = null; 120 | try { 121 | ins = context.getAssets().open(fileAssetPath); 122 | } catch (IOException e) { 123 | e.printStackTrace(); 124 | LogHelper.d(TAG, LogHelper.getThreadName() + " e=" + e.getMessage()); 125 | } 126 | return ins; 127 | } 128 | 129 | /** 130 | *
131 |      * 保存一张图片
132 |      * 
133 | * 134 | * @param bitmap 135 | * @param path 136 | * @param needRecycleBitmap 137 | */ 138 | public static void saveImage(Bitmap bitmap, String path, boolean needRecycleBitmap) { 139 | FileOutputStream fos = null; 140 | try { 141 | fos = new FileOutputStream(path);// 注意app的sdcard读写权限问题 142 | } catch (FileNotFoundException e) { 143 | e.printStackTrace(); 144 | LogHelper.d(TAG, LogHelper.getThreadName() + " FileNotFoundException e=" + e.getMessage()); 145 | } 146 | bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);// 压缩成png,100%显示效果 147 | try { 148 | fos.flush(); 149 | } catch (IOException e) { 150 | e.printStackTrace(); 151 | } 152 | if (needRecycleBitmap && !bitmap.isRecycled()) { 153 | bitmap.recycle(); 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /app/src/main/java/com/dandy/helper/android/LogHelper.java: -------------------------------------------------------------------------------- 1 | package com.dandy.helper.android; 2 | 3 | import android.app.ActivityManager; 4 | import android.content.Context; 5 | import android.util.Log; 6 | import android.widget.Toast; 7 | 8 | /** 9 | * 打印帮助类 10 | * 11 | * @author dengchukun 12 | * 13 | */ 14 | public class LogHelper { 15 | private static final String ROOT_TAG = "dengck"; 16 | private static boolean sIsLogDebug = true; 17 | private static String sRootTag = ROOT_TAG; 18 | private static boolean sIsToastDebug = true; 19 | 20 | /** 21 | * 打印log详细信息 相见LogDemo类和MainActivity类 最好是每个方法中都调用此方法 22 | */ 23 | public static void d(String tag, String content) { 24 | if (sIsLogDebug) { 25 | Log.d(sRootTag + "_" + tag, content); 26 | } 27 | } 28 | 29 | // class DetailLogDemo 30 | /** 31 | * 打印一段字符串 32 | * 33 | * @param content 34 | */ 35 | public static void printLog(String content) { 36 | Log.d(sRootTag, content); 37 | } 38 | 39 | /** 40 | * 打印线程名称 41 | */ 42 | public static void printProcessName(Context context, String content) { 43 | int pid = android.os.Process.myPid(); 44 | ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 45 | for (ActivityManager.RunningAppProcessInfo appProcess : mActivityManager.getRunningAppProcesses()) { 46 | if (appProcess.pid == pid) { 47 | LogHelper.printLog(content + appProcess.processName); 48 | } 49 | } 50 | } 51 | 52 | /** 53 | * 得到调用此方法的线程的线程名 54 | * 55 | * @return 56 | */ 57 | public static String getThreadName() { 58 | if (!sIsLogDebug) { 59 | return ""; 60 | } 61 | StringBuffer sb = new StringBuffer(); 62 | try { 63 | sb.append(Thread.currentThread().getName()); 64 | sb.append("-> "); 65 | sb.append(Thread.currentThread().getStackTrace()[3].getMethodName()); 66 | sb.append("()"); 67 | sb.append(" "); 68 | } catch (Exception e) { 69 | } 70 | return sb.toString(); 71 | } 72 | 73 | public static boolean isLogDebug() { 74 | return sIsLogDebug; 75 | } 76 | 77 | public static void setLogDebug(boolean isLogDebug) { 78 | sIsLogDebug = isLogDebug; 79 | } 80 | 81 | public static String getRootTag() { 82 | return sRootTag; 83 | } 84 | 85 | public static void setRootTag(String rootTag) { 86 | sRootTag = rootTag; 87 | } 88 | 89 | public static void showToast(Context context, String content) { 90 | if (sIsToastDebug) { 91 | Toast.makeText(context, content, Toast.LENGTH_SHORT).show(); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/com/dandy/helper/android/SDCardHelper.java: -------------------------------------------------------------------------------- 1 | package com.dandy.helper.android; 2 | 3 | import android.os.Environment; 4 | import android.os.StatFs; 5 | 6 | public class SDCardHelper { 7 | 8 | /** 9 | * 得到SD卡的目录路径 10 | */ 11 | public static String getSDCardDirPath() { 12 | return Environment.getExternalStorageDirectory().getAbsolutePath(); 13 | } 14 | 15 | /** 16 | * 判断SD卡是否可用 17 | * 18 | * @return 19 | */ 20 | public static boolean isSDCardExist() { 21 | if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 22 | return true; 23 | } 24 | return false; 25 | } 26 | 27 | /** 28 | * 获取SD卡的剩余容量 单位byte 29 | * 30 | * @return 31 | */ 32 | @SuppressWarnings("deprecation") 33 | public static long getSDCardAllSize() { 34 | if (isSDCardExist()) { 35 | StatFs stat = new StatFs(getSDCardDirPath()); 36 | // 获取空闲的数据块的数量 37 | long availableBlocks = (long) stat.getAvailableBlocks() - 4; 38 | // 获取单个数据块的大小(byte) 39 | long freeBlocks = stat.getAvailableBlocks(); 40 | return freeBlocks * availableBlocks; 41 | } 42 | return 0; 43 | } 44 | 45 | /** 46 | * 获取指定路径所在空间的剩余可用容量字节数,单位byte 47 | * 48 | * @param filePath 49 | * @return 容量字节 SDCard可用空间,内部存储可用空间 50 | */ 51 | @SuppressWarnings("deprecation") 52 | public static long getFreeBytes(String filePath) { 53 | // 如果是sd卡的下的路径,则获取sd卡可用容量 54 | if (filePath.startsWith(getSDCardDirPath())) { 55 | filePath = getSDCardDirPath(); 56 | } else {// 如果是内部存储的路径,则获取内存存储的可用容量 57 | filePath = Environment.getDataDirectory().getAbsolutePath(); 58 | } 59 | StatFs stat = new StatFs(filePath); 60 | long availableBlocks = (long) stat.getAvailableBlocks() - 4; 61 | return stat.getBlockSize() * availableBlocks; 62 | } 63 | 64 | /** 65 | * 获取系统存储路径 66 | * 67 | * @return 68 | */ 69 | public static String getRootDirectoryPath() { 70 | return Environment.getRootDirectory().getAbsolutePath(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/dandy/helper/gles/CommonUtils.java: -------------------------------------------------------------------------------- 1 | package com.dandy.helper.gles; 2 | 3 | import com.dandy.helper.android.LogHelper; 4 | 5 | import android.app.ActivityManager; 6 | import android.content.Context; 7 | import android.content.pm.ConfigurationInfo; 8 | import android.opengl.GLES20; 9 | 10 | /** 11 | * 12 | * @author dengchukun 2016年11月23日 13 | */ 14 | public class CommonUtils { 15 | 16 | private static final String TAG = CommonUtils.class.getSimpleName(); 17 | 18 | /** 19 | *
20 |      * 检查设备是否支持OpenGL ES 2.0
21 |      * check whether the device support OpenGL ES 2.0
22 |      * 
23 | * 24 | * @param context 25 | * @return 26 | */ 27 | public static boolean isSupportEs2(Context context) { 28 | ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 29 | ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo(); 30 | boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000; 31 | LogHelper.d(TAG, LogHelper.getThreadName() + " supportsEs2=" + supportsEs2); 32 | return supportsEs2; 33 | } 34 | 35 | /** 36 | * 检查每一步操作是否有错误的方法 37 | * 38 | * @param op 39 | * TAG 40 | */ 41 | public static void checkGlError(String op) { 42 | int error; 43 | while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { 44 | LogHelper.d("ES20_ERROR", op + ": glError " + error); 45 | throw new RuntimeException(op + ": glError " + error); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/dandy/helper/gles/IGLESRenderer.java: -------------------------------------------------------------------------------- 1 | package com.dandy.helper.gles; 2 | 3 | /** 4 | *
 5 |  * GLES里用到的用于渲染的一个接口。
 6 |  * 如果是GLSurfaceView要用到,则其对应的GLSurfaceView.Renderer可以来调用IGLESRenderer的实现类来实现逻辑
 7 |  * 如果是TextureView要用到,则使用自定义的一个线程里调用IGLESRenderer的实现类来做一个类似于GLSurfaceView.Renderer的操作
 8 |  * 所以IGLESRenderer中的方法都要在GL线程里运行(TextureView创建一个线程,把它当做一个GL线程)
 9 |  * 
10 | * 11 | * @author flycatdeng 12 | * 13 | */ 14 | public interface IGLESRenderer { 15 | /** 16 | *
17 |      * Surface创建好之后
18 |      * 
19 | */ 20 | public void onSurfaceCreated(); 21 | 22 | /** 23 | *
24 |      * 界面大小有更改
25 |      * 
26 | * 27 | * @param width 28 | * @param height 29 | */ 30 | public void onSurfaceChanged(int width, int height); 31 | 32 | /** 33 | *
34 |      * 绘制每一帧
35 |      * 
36 | */ 37 | public void onDrawFrame(); 38 | 39 | /** 40 | *
41 |      * Activity的onResume时的操作
42 |      * 
43 | */ 44 | public void onResume(); 45 | 46 | /** 47 | *
48 |      * Activity的onPause时的操作
49 |      * 
50 | */ 51 | public void onPause(); 52 | 53 | /** 54 | *
55 |      * Activity的onDestroy时的操作
56 |      * 
57 | */ 58 | public void onDestroy(); 59 | 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/dandy/helper/gles/MaterialAider.java: -------------------------------------------------------------------------------- 1 | package com.dandy.helper.gles; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.HashMap; 6 | import java.util.Properties; 7 | 8 | import android.content.Context; 9 | import android.text.TextUtils; 10 | 11 | import com.dandy.helper.android.FileHelper; 12 | import com.dandy.helper.android.LogHelper; 13 | 14 | /** 15 | * need the key word new to create a MaterialAid object 16 | * 17 | * @author dandy 18 | * 19 | */ 20 | public class MaterialAider { 21 | private static final String TAG = MaterialAider.class.getSimpleName(); 22 | private String mMaterialName = "origin.mat"; 23 | private String mMaterialDirectory = ""; 24 | private Properties property = new Properties(); 25 | private Context mContext; 26 | private HashMap mMaterialUniformMap = new HashMap(); 27 | 28 | class Key { 29 | public static final String VERTEX_FILE = "VertexFile"; 30 | public static final String FRAGMENT_FILE = "FragmentFile"; 31 | public static final String VERSION = "Version"; 32 | public static final String UNIFORM = "Uniform"; 33 | } 34 | 35 | /** 36 | * Initialize the MaterialAid object 37 | *

38 | * the default material file will not be changed until we invoke setMaterialName 39 | * 40 | * @param context 41 | */ 42 | public MaterialAider(Context context) { 43 | mContext = context; 44 | // try { 45 | // property.load(FileHelper.getInputStreamFromAsset(mContext, mMaterialName)); 46 | // } catch (IOException e) { 47 | // e.printStackTrace(); 48 | // } 49 | } 50 | 51 | /** 52 | * set the material file we want to use 53 | * 54 | * @param name 55 | */ 56 | public void setMaterialName(String name) { 57 | LogHelper.d(TAG, LogHelper.getThreadName() + " name-" + name + " mContext=" + mContext); 58 | mMaterialName = name; 59 | try { 60 | property.load(FileHelper.getInputStreamFromAsset(mContext, mMaterialName)); 61 | int lastSep = name.lastIndexOf(File.separator); 62 | if (lastSep != -1) { 63 | mMaterialDirectory = name.substring(0, lastSep); 64 | } 65 | LogHelper.d(TAG, LogHelper.getThreadName() + " name-" + name + " mMaterialDirectory=" + mMaterialDirectory); 66 | } catch (IOException e) { 67 | e.printStackTrace(); 68 | LogHelper.d(TAG, LogHelper.getThreadName() + " e=" + e.getMessage()); 69 | } 70 | } 71 | 72 | /** 73 | * get the material file name under assets 74 | * 75 | * @return 76 | */ 77 | public String getMaterialName() { 78 | return mMaterialName; 79 | } 80 | 81 | /** 82 | * get the vertex shader file name in the material file 83 | * 84 | * @return 85 | */ 86 | public String getMaterialVertexName() { 87 | String vertexFile = property.getProperty(Key.VERTEX_FILE); 88 | // LogHelper.d(TAG, LogHelper.getThreadName() + " vertexFile-" + vertexFile); 89 | if (TextUtils.isEmpty(mMaterialDirectory)) { 90 | return vertexFile; 91 | } 92 | return mMaterialDirectory + File.separator + vertexFile; 93 | } 94 | 95 | /** 96 | * get the fragment shader file name in the material file 97 | * 98 | * @return 99 | */ 100 | public String getMaterialFragmentName() { 101 | String fragmentFile = property.getProperty(Key.FRAGMENT_FILE); 102 | // LogHelper.d(TAG, LogHelper.getThreadName() + " fragmentFile-" + fragmentFile); 103 | if (TextUtils.isEmpty(mMaterialDirectory)) { 104 | return fragmentFile; 105 | } 106 | return mMaterialDirectory + File.separator + fragmentFile; 107 | } 108 | 109 | /** 110 | * get the version of the material file 111 | * 112 | * @return 113 | */ 114 | public String getMaterialVersion() { 115 | String version = property.getProperty(Key.VERSION); 116 | // LogHelper.d(TAG, LogHelper.getThreadName() + " version-" + version); 117 | return version; 118 | } 119 | 120 | public void setMaterialProperty(String key, float value) { 121 | if (key.startsWith("u")) {// uniform 122 | mMaterialUniformMap.put(key, String.valueOf(value)); 123 | } else if (key.startsWith("a")) {// attribute 124 | 125 | } 126 | } 127 | 128 | public HashMap getMaterialUniformMapFromClient() { 129 | // LogHelper.d(TAG, LogHelper.getThreadName() + " mMaterialUniformMap-" + mMaterialUniformMap.toString()); 130 | return mMaterialUniformMap; 131 | } 132 | 133 | public HashMap getMaterialUniformKeyAndType() { 134 | HashMap result = new HashMap(); 135 | String[] typeKeys = getMaterialUniforms(); 136 | if (typeKeys.length == 0) { 137 | // LogHelper.d(TAG, LogHelper.getThreadName() + " there is no uniform key"); 138 | return null; 139 | } 140 | for (int i = 0; i < typeKeys.length; i++) { 141 | String typeKey = typeKeys[i]; 142 | // LogHelper.d(TAG, LogHelper.getThreadName() + " typeKey-" + typeKey); 143 | String[] typeAndKey = typeKey.split("_"); 144 | // LogHelper.d(TAG, LogHelper.getThreadName() + " typeAndKey[0]-" + typeAndKey[0] + " typeAndKey[1]-" + typeAndKey[1]); 145 | String type = typeAndKey[0]; 146 | String key = typeAndKey[1]; 147 | result.put(key, type); 148 | } 149 | // LogHelper.d(TAG, LogHelper.getThreadName() + " result-" + result.toString()); 150 | return result; 151 | } 152 | 153 | public String[] getMaterialUniforms() { 154 | // LogHelper.d(TAG, LogHelper.getThreadName()); 155 | String uniform = getMaterialUniformFromFile(); 156 | String[] typeKey = uniform.split(","); 157 | // LogHelper.d(TAG, LogHelper.getThreadName() + " typeKey length-" + typeKey.length); 158 | return typeKey; 159 | } 160 | 161 | public String getMaterialUniformFromFile() { 162 | String uniform = property.getProperty(Key.UNIFORM); 163 | // LogHelper.d(TAG, LogHelper.getThreadName() + " uniform-" + uniform); 164 | return uniform; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /app/src/main/java/com/dandy/helper/gles/ShaderHelper.java: -------------------------------------------------------------------------------- 1 | package com.dandy.helper.gles; 2 | 3 | import java.io.IOException; 4 | import java.util.Properties; 5 | 6 | import android.annotation.SuppressLint; 7 | import android.content.Context; 8 | import android.opengl.GLES20; 9 | 10 | import com.dandy.helper.android.FileHelper; 11 | import com.dandy.helper.android.LogHelper; 12 | 13 | @SuppressLint("NewApi") 14 | public class ShaderHelper { 15 | private static final String TAG = ShaderHelper.class.getSimpleName(); 16 | 17 | /** 18 | * 19 | * @param shaderType 20 | * shader的类型 GLES20.GL_VERTEX_SHADER(顶点) GLES20.GL_FRAGMENT_SHADER(片元) 21 | * @param source 22 | * shader的脚本字符串 23 | * @return 24 | */ 25 | public static int getShader(int shaderType, String source) { 26 | // 创建一个新shader 27 | int shader = GLES20.glCreateShader(shaderType); 28 | if (shader == 0) {// 创建shader不成功 29 | throw new RuntimeException(TAG + LogHelper.getThreadName() + " Error creating vertex shader."); 30 | } // 若创建成功则加载shader 31 | // 加载shader的源代码 32 | GLES20.glShaderSource(shader, source); 33 | // 编译shader 34 | GLES20.glCompileShader(shader); 35 | // 存放编译成功shader数量的数组 36 | int[] compiled = new int[1]; 37 | // 获取Shader的编译情况 38 | GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); 39 | if (compiled[0] == 0) {// 若编译失败则显示错误日志并删除此shader 40 | LogHelper.d(TAG + "_ES20_ERROR", "Could not compile shader " + shaderType + ":"); 41 | LogHelper.d(TAG + "_ES20_ERROR", GLES20.glGetShaderInfoLog(shader)); 42 | GLES20.glDeleteShader(shader); 43 | shader = 0; 44 | } 45 | return shader; 46 | } 47 | 48 | /** 49 | * 通过顶点着色器字符串得到顶点着色器ID 50 | * 51 | * @param vertexSource 52 | * 顶点着色器字符串 53 | * @return 54 | */ 55 | public static int getVertexShader(String vertexSource) { 56 | return getShader(GLES20.GL_VERTEX_SHADER, vertexSource); 57 | } 58 | 59 | /** 60 | * 通过片元着色器字符串得到片元着色器ID 61 | * 62 | * @param fragmentSource 63 | * 片元着色器字符串 64 | * @return 65 | */ 66 | public static int getFragmentShader(String fragmentSource) { 67 | return getShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); 68 | } 69 | 70 | /** 71 | * 创建shader在gpu上运行的程序,从assets目录下获取 72 | * 73 | * @param vertexSourcePath 74 | * 顶点着色器字符串在assets目录下的路径 75 | * @param fragmentSourcePath 76 | * 片元着色器字符串在assets目录下的路径 77 | * @return 78 | */ 79 | public static int getProgramFromAsset(Context context, MaterialAider material) { 80 | String vertexSourcePath = material.getMaterialVertexName(); 81 | String fragmentSourcePath = material.getMaterialFragmentName(); 82 | return getProgramFromAsset(context, vertexSourcePath, fragmentSourcePath); 83 | } 84 | 85 | /** 86 | * 创建shader在gpu上运行的程序,从assets目录下获取 87 | * 88 | * @param vertexSourcePath 89 | * 顶点着色器字符串在assets目录下的路径 90 | * @param fragmentSourcePath 91 | * 片元着色器字符串在assets目录下的路径 92 | * @return 93 | */ 94 | public static int getProgramFromAsset(Context context, String materialPath) { 95 | String vertexSourcePath = "origin.vert"; 96 | String fragmentSourcePath = "origin.frag"; 97 | Properties property = new Properties(); 98 | try { 99 | property.load(FileHelper.getInputStreamFromAsset(context, materialPath)); 100 | vertexSourcePath = property.getProperty("VertextFile"); 101 | fragmentSourcePath = property.getProperty("FragmentFile"); 102 | } catch (IOException e) { 103 | e.printStackTrace(); 104 | } 105 | return getProgramFromAsset(context, vertexSourcePath, fragmentSourcePath); 106 | } 107 | 108 | /** 109 | * 创建shader在gpu上运行的程序,从assets目录下获取 110 | * 111 | * @param vertexSourcePath 112 | * 顶点着色器字符串在assets目录下的路径 113 | * @param fragmentSourcePath 114 | * 片元着色器字符串在assets目录下的路径 115 | * @return 116 | */ 117 | public static int getProgramFromAsset(Context context, String vertexSourcePath, String fragmentSourcePath) { 118 | String vertexSource = FileHelper.getFileContentFromAsset(context, vertexSourcePath); 119 | String fragmentSource = FileHelper.getFileContentFromAsset(context, fragmentSourcePath); 120 | // LogHelper.d(TAG, LogHelper.getThreadName() + "vertexSource-" + vertexSource); 121 | // LogHelper.d(TAG, LogHelper.getThreadName() + "fragmentSource-" + fragmentSource); 122 | return getProgram(vertexSource, fragmentSource); 123 | } 124 | 125 | /** 126 | * 创建shader在gpu上运行的程序 127 | * 128 | * @param vertexSource 129 | * 顶点着色器字符串 130 | * @param fragmentSource 131 | * 片元着色器字符串 132 | * @return 133 | */ 134 | public static int getProgram(String vertexSource, String fragmentSource) { 135 | int vertexShader = getVertexShader(vertexSource); 136 | int fragmentShader = getFragmentShader(fragmentSource); 137 | return getProgram(vertexShader, fragmentShader); 138 | } 139 | 140 | /** 141 | * 创建shader在gpu上运行的程序 142 | * 143 | * @param vertexShader 144 | * 顶点着色器的ID 145 | * @param fragShader 146 | * 片元着色器的ID 147 | * @return 148 | */ 149 | public static int getProgram(int vertexShader, int fragShader) { 150 | if (vertexShader == 0) { 151 | return 0; 152 | } 153 | if (fragShader == 0) { 154 | return 0; 155 | } 156 | // 创建程序 157 | int program = GLES20.glCreateProgram();// 创建程序,在GPU上跑,返回其ID 158 | if (program == 0) {// 创建程序失败 159 | throw new RuntimeException("Error creating program."); 160 | } // 创建程序成功 161 | // 加入着色器 162 | GLES20.glAttachShader(program, vertexShader);// 向程序中加入顶点着色器 163 | CommonUtils.checkGlError("glAttachShader vertexShader"); 164 | GLES20.glAttachShader(program, fragShader);// 向程序中加入片元点着色器 165 | CommonUtils.checkGlError("glAttachShader fragShader"); 166 | // 链接程序 167 | GLES20.glLinkProgram(program); 168 | int[] linkStatus = new int[1]; // 存放链接成功program数量的数组 169 | GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); // 获取program的链接情况 170 | // 若链接失败则报错并删除程序 171 | if (linkStatus[0] != GLES20.GL_TRUE) { 172 | LogHelper.d(TAG + "_ES20_ERROR", "Could not link program: "); 173 | LogHelper.d(TAG + "_ES20_ERROR", GLES20.glGetProgramInfoLog(program)); 174 | GLES20.glDeleteProgram(program); 175 | program = 0; 176 | throw new RuntimeException("Error creating program."); 177 | } 178 | LogHelper.d(TAG, LogHelper.getThreadName() + "program ID=" + program); 179 | return program; 180 | } 181 | 182 | } 183 | -------------------------------------------------------------------------------- /app/src/main/java/com/dandy/helper/java/JFileHelper.java: -------------------------------------------------------------------------------- 1 | package com.dandy.helper.java; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.Closeable; 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.FileNotFoundException; 8 | import java.io.FileOutputStream; 9 | import java.io.FileWriter; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.io.InputStreamReader; 13 | import java.io.OutputStream; 14 | import java.nio.charset.Charset; 15 | import java.util.Locale; 16 | 17 | import com.dandy.helper.android.LogHelper; 18 | 19 | public class JFileHelper { 20 | 21 | private static final String TAG = JFileHelper.class.getSimpleName(); 22 | 23 | /** 24 | * 输入流转字节数组 25 | * 26 | * @param in 27 | * @return 28 | * @throws IOException 29 | */ 30 | public static void copyStream(InputStream is, OutputStream out) throws IOException { 31 | try { 32 | int len = 0; 33 | byte[] buffer = new byte[1024]; 34 | while ((len = is.read(buffer)) > 0) { 35 | out.write(buffer, 0, len); 36 | } 37 | out.flush(); 38 | } finally { 39 | closeIOStream(is, out); 40 | } 41 | } 42 | 43 | /** 44 | * 关闭io流 45 | * 46 | * @param closeable 47 | */ 48 | public static void closeIOStream(Closeable... closeable) { 49 | if (closeable == null) { 50 | return; 51 | } 52 | for (Closeable ca : closeable) { 53 | try { 54 | if (ca == null) { 55 | continue; 56 | } 57 | ca.close(); 58 | ca = null; 59 | } catch (Exception e) { 60 | e.printStackTrace(); 61 | } 62 | } 63 | } 64 | 65 | /** 66 | * 判断某个路径的文件是否存在 67 | * 68 | * @param path 69 | * @return 70 | */ 71 | public static boolean isFileExit(String path) { 72 | File f = new File(path); 73 | if (f.exists()) { 74 | return true; 75 | } else { 76 | return false; 77 | } 78 | } 79 | 80 | /** 81 | * 流转字符串方法 82 | * 83 | * @param is 84 | * @return 85 | */ 86 | public static String convertStreamToString(InputStream is) { 87 | BufferedReader reader = new BufferedReader(new InputStreamReader(is, Charset.defaultCharset())); 88 | StringBuilder sb = new StringBuilder(); 89 | String line = null; 90 | try { 91 | while ((line = reader.readLine()) != null) { 92 | sb.append(line); 93 | } 94 | } catch (IOException e) { 95 | e.printStackTrace(); 96 | } finally { 97 | try { 98 | is.close(); 99 | } catch (IOException e) { 100 | e.printStackTrace(); 101 | } 102 | } 103 | return sb.toString(); 104 | } 105 | 106 | /** 107 | * 返回该文件的大小,例如1.3GB 108 | * 109 | * @param fileLength 110 | * 文件长度,可以由File.getLength()得到 111 | * @return 112 | */ 113 | public static String formatFileLength(long fileLength) { 114 | long kb = 1024; 115 | long mb = kb * 1024; 116 | long gb = mb * 1024; 117 | 118 | if (fileLength >= gb) { 119 | return String.format(Locale.getDefault(), "%.1f GB", (float) fileLength / gb); 120 | } else if (fileLength >= mb) { 121 | float f = (float) fileLength / mb; 122 | return String.format(Locale.getDefault(), f > 100 ? "%.0f MB" : "%.1f MB", f); 123 | } else if (fileLength >= kb) { 124 | float f = (float) fileLength / kb; 125 | return String.format(Locale.getDefault(), f > 100 ? "%.0f KB" : "%.1f KB", f); 126 | } else { 127 | return String.format(Locale.getDefault(), "%d B", fileLength); 128 | } 129 | } 130 | 131 | /** 132 | * 复制文件 133 | * 134 | * @param ins 135 | * 该文件的输入流 136 | * @param destFileFullPath 137 | * 要存放的文件的路径 138 | * @return 139 | */ 140 | public static boolean copyFile(InputStream ins, String destFileFullPath) { 141 | LogHelper.d(TAG, LogHelper.getThreadName() + "destFileFullPath-" + destFileFullPath); 142 | FileOutputStream fos = null; 143 | try { 144 | File file = new File(destFileFullPath); 145 | LogHelper.d(TAG, LogHelper.getThreadName() + "开始读入"); 146 | fos = new FileOutputStream(file); 147 | LogHelper.d(TAG, LogHelper.getThreadName() + "开始写出"); 148 | byte[] buffer = new byte[8192]; 149 | int count = 0; 150 | LogHelper.d(TAG, LogHelper.getThreadName() + "准备循环了"); 151 | while ((count = ins.read(buffer)) > 0) { 152 | fos.write(buffer, 0, count); 153 | } 154 | LogHelper.d(TAG, LogHelper.getThreadName() + "已经创建该文件"); 155 | return true; 156 | } catch (Exception e) { 157 | e.printStackTrace(); 158 | LogHelper.d(TAG, LogHelper.getThreadName() + e.getMessage()); 159 | return false; 160 | } finally { 161 | try { 162 | if (fos != null) { 163 | fos.close(); 164 | } 165 | if (ins != null) { 166 | ins.close(); 167 | } 168 | } catch (IOException e) { 169 | e.printStackTrace(); 170 | LogHelper.d(TAG, LogHelper.getThreadName() + e.getMessage()); 171 | } 172 | } 173 | } 174 | 175 | public static boolean copyFolder(String srcFolderFullPath, String destFolderFullPath) { 176 | LogHelper.d(TAG, LogHelper.getThreadName() + "srcFolderFullPath-" + srcFolderFullPath + " destFolderFullPath-" + destFolderFullPath); 177 | try { 178 | boolean success = (new File(destFolderFullPath)).mkdirs(); // 如果文件夹不存在 179 | // 则建立新文件夹 180 | if (!success) { 181 | return false; 182 | } 183 | File file = new File(srcFolderFullPath); 184 | String[] files = file.list(); 185 | File temp = null; 186 | for (int i = 0; i < files.length; i++) { 187 | if (srcFolderFullPath.endsWith(File.separator)) { 188 | temp = new File(srcFolderFullPath + files[i]); 189 | } else { 190 | temp = new File(srcFolderFullPath + File.separator + files[i]); 191 | } 192 | if (temp.isFile()) { 193 | FileInputStream input = new FileInputStream(temp); 194 | copyFile(input, destFolderFullPath + "/" + (temp.getName()).toString()); 195 | } 196 | if (temp.isDirectory()) {// 如果是子文件夹 197 | copyFolder(srcFolderFullPath + "/" + files[i], destFolderFullPath + "/" + files[i]); 198 | } 199 | } 200 | } catch (Exception e) { 201 | e.printStackTrace(); 202 | LogHelper.d(TAG, LogHelper.getThreadName() + e.getMessage()); 203 | return false; 204 | } 205 | return true; 206 | } 207 | 208 | public static String getFileContent(String filePath) { 209 | try { 210 | FileInputStream fins = new FileInputStream(filePath); 211 | return getFileContent(fins); 212 | } catch (FileNotFoundException e) { 213 | e.printStackTrace(); 214 | } 215 | return ""; 216 | } 217 | 218 | public static String getFileContent(InputStream ins) { 219 | try { 220 | byte[] contentByte = new byte[ins.available()]; 221 | ins.read(contentByte); 222 | return new String(contentByte); 223 | } catch (IOException e) { 224 | e.printStackTrace(); 225 | } 226 | return ""; 227 | } 228 | 229 | public static boolean copyContentToFile(String content, String destFileFullPath) { 230 | FileWriter fWriter = null; 231 | try { 232 | fWriter = new FileWriter(destFileFullPath); 233 | fWriter.write(content); 234 | fWriter.flush(); 235 | return true; 236 | } catch (IOException e1) { 237 | e1.printStackTrace(); 238 | } finally { 239 | try { 240 | fWriter.close(); 241 | } catch (IOException e) { 242 | e.printStackTrace(); 243 | return false; 244 | } 245 | } 246 | return false; 247 | } 248 | 249 | public static InputStream getInputStreamFromPath(String path) { 250 | LogHelper.d(TAG, LogHelper.getThreadName()); 251 | InputStream ins = null; 252 | // File file = new File(path); 253 | try { 254 | ins = new FileInputStream(path); 255 | } catch (FileNotFoundException e) { 256 | e.printStackTrace(); 257 | } 258 | return ins; 259 | } 260 | 261 | public static void removeFile(String file_path) { 262 | try { 263 | File f = new File(file_path); 264 | if (!f.exists()) { 265 | return; 266 | } 267 | f.delete(); 268 | } catch (Exception e) { 269 | } 270 | } 271 | 272 | /** 273 | *

274 |      * 在某个大的文件夹中删除指定名称的所有文件,
275 |      * 例如这个文件夹下不同的子文件夹下都有一个叫a.txt的文件,那么此时可以用这个方法来删除这个a.txt
276 |      * 
277 | * 278 | * @param targetFolderFullPath 279 | * 目标文件夹 280 | * @param fileSimpleName 281 | * 要删除的文件的名称(不要全路劲) 282 | */ 283 | public static void deleteAppointedFilesInDirectory(String targetFolderFullPath, String fileSimpleName) { 284 | File file = new File(targetFolderFullPath); 285 | if (!file.exists()) {// 文件夹不存在,不用查找 286 | LogHelper.d(TAG, LogHelper.getThreadName() + "file does not exist"); 287 | return; 288 | } 289 | String[] files = file.list(); 290 | File temp = null; 291 | if (files == null || files.length == 0) {// 文件夹下没有子文件或子文件夹,不用查找 292 | LogHelper.d(TAG, LogHelper.getThreadName() + "files.length == 0"); 293 | return; 294 | } 295 | for (int i = 0; i < files.length; i++) { 296 | if (!files[i].equals(fileSimpleName)) {// 如果你的文件名或者文件夹的名称都和目标名称不一致了,那这个就不用判断了,直接判断下一个 297 | continue; 298 | } 299 | if (targetFolderFullPath.endsWith(File.separator)) { 300 | temp = new File(targetFolderFullPath + files[i]); 301 | } else { 302 | temp = new File(targetFolderFullPath + File.separator + files[i]); 303 | } 304 | if (temp.isFile()) {// 是文件,而且名称相同,删除 305 | temp.delete(); 306 | // deleteAppointedFile(targetFolderFullPath + "/" + (temp.getName()).toString()); 307 | } 308 | if (temp.isDirectory()) {// 如果是子文件夹 309 | deleteAppointedFilesInDirectory(targetFolderFullPath + "/" + files[i], fileSimpleName); 310 | } 311 | } 312 | } 313 | 314 | /** 315 | *
316 |      * 在某个大的文件夹中删除指定名称的所有文件夹,
317 |      * 例如这个文件夹下不同的子文件夹下都有一个叫.svn的文件夹,那么此时可以用这个方法来删除这个.svn夹
318 |      * 
319 | * 320 | * @param targetFolderFullPath 321 | * 目标文件夹 322 | * @param directorySimpleName 323 | * 要删除的文件夹的名称(不要全路劲) 324 | */ 325 | public static void deleteAppointedDirectorysInDirectory(String targetFolderFullPath, String directorySimpleName) { 326 | File file = new File(targetFolderFullPath); 327 | if (file.getName().equals(directorySimpleName)) {// 如果该文件夹已经是要删除的文件夹名称了,直接删除这个文件夹 328 | LogHelper.d(TAG, LogHelper.getThreadName() + " file.getName()=" + file.getName() + " directorySimpleName=" + directorySimpleName); 329 | deleteFolder(targetFolderFullPath); 330 | return; 331 | } 332 | String[] files = file.list(); 333 | File temp = null; 334 | if (files == null || files.length == 0) {// 文件夹下没有子文件或子文件夹,不用查找 335 | LogHelper.d(TAG, LogHelper.getThreadName() + "files.length == 0"); 336 | return; 337 | } 338 | for (int i = 0; i < files.length; i++) { 339 | if (!files[i].equals(directorySimpleName)) {// 如果你的文件名或者文件夹的名称都和目标名称不一致了,那这个就不用判断了,直接判断下一个 340 | continue; 341 | } 342 | if (targetFolderFullPath.endsWith(File.separator)) { 343 | temp = new File(targetFolderFullPath + files[i]); 344 | } else { 345 | temp = new File(targetFolderFullPath + File.separator + files[i]); 346 | } 347 | if (temp.isFile()) {// 是文件,而且名称相同,删除 348 | continue; 349 | } 350 | if (temp.isDirectory()) {// 如果是子文件夹 351 | deleteAppointedDirectorysInDirectory(targetFolderFullPath + File.separator + files[i], directorySimpleName); 352 | } 353 | } 354 | } 355 | 356 | /** 357 | * 删除某个文件夹 358 | * 359 | * @param targetFolderFullPath 360 | * 要删除的文件夹的路径 361 | * @return 362 | */ 363 | public static boolean deleteFolder(String targetFolderFullPath) { 364 | LogHelper.d(TAG, LogHelper.getThreadName() + "targetFolderFullPath-" + targetFolderFullPath); 365 | File file = new File(targetFolderFullPath); 366 | if (!file.exists()) { 367 | LogHelper.d(TAG, LogHelper.getThreadName() + "file does not exist"); 368 | return true; 369 | } 370 | String[] files = file.list(); 371 | File temp = null; 372 | if (files == null) { 373 | LogHelper.d(TAG, LogHelper.getThreadName() + "files == null"); 374 | return true; 375 | } 376 | if (files.length == 0) { 377 | LogHelper.d(TAG, LogHelper.getThreadName() + "files.length == 0"); 378 | boolean success = file.delete(); 379 | return success; 380 | } 381 | for (int i = 0; i < files.length; i++) { 382 | if (targetFolderFullPath.endsWith(File.separator)) { 383 | temp = new File(targetFolderFullPath + files[i]); 384 | } else { 385 | temp = new File(targetFolderFullPath + File.separator + files[i]); 386 | } 387 | if (temp.isFile()) { 388 | deleteFile(targetFolderFullPath + File.separator + (temp.getName()).toString()); 389 | } 390 | if (temp.isDirectory()) {// 如果是子文件夹 391 | deleteFolder(targetFolderFullPath + File.separator + files[i]); 392 | } 393 | } 394 | boolean success = file.delete(); 395 | return success; 396 | } 397 | 398 | /** 399 | * 删除某个文件 400 | * 401 | * @param targetFileFullPath 402 | * 要删除的文件的路径 403 | */ 404 | public static void deleteFile(String targetFileFullPath) { 405 | File file = new File(targetFileFullPath); 406 | file.delete(); 407 | } 408 | } 409 | -------------------------------------------------------------------------------- /app/src/main/java/com/dandy/helper/java/PendingThreadAider.java: -------------------------------------------------------------------------------- 1 | package com.dandy.helper.java; 2 | 3 | import java.util.LinkedList; 4 | 5 | /** 6 | * 若在某些条件下才能运行的一些行为,提前运行则失去了意义,所以此时可以将这些事件保存起来,等条件满足之后再全部按顺序运行 7 | * 8 | * @author flycatdeng 9 | * 10 | */ 11 | public class PendingThreadAider { 12 | LinkedList mRunOnDraw = new LinkedList(); 13 | 14 | public void runPendings() { 15 | while (!mRunOnDraw.isEmpty()) { 16 | mRunOnDraw.removeFirst().run(); 17 | } 18 | } 19 | 20 | public void addToPending(final Runnable runnable) { 21 | synchronized (mRunOnDraw) { 22 | mRunOnDraw.addLast(runnable); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/dandy/module/gles/textureview/GLESTVThread.java: -------------------------------------------------------------------------------- 1 | package com.dandy.module.gles.textureview; 2 | 3 | import javax.microedition.khronos.egl.EGL10; 4 | import javax.microedition.khronos.egl.EGLConfig; 5 | import javax.microedition.khronos.egl.EGLContext; 6 | import javax.microedition.khronos.egl.EGLDisplay; 7 | import javax.microedition.khronos.egl.EGLSurface; 8 | 9 | import android.graphics.SurfaceTexture; 10 | import android.opengl.GLUtils; 11 | 12 | import com.dandy.helper.android.LogHelper; 13 | import com.dandy.helper.gles.IGLESRenderer; 14 | import com.dandy.helper.java.PendingThreadAider; 15 | 16 | /** 17 | *
 18 |  * TextureView中要用的GLThread
 19 |  * 一个很好的EGL相关学习参考
 20 |  * 一般在Android中使用OpenGL ES,总是会从GLSurfaceView和Renderer开始,只需要提供一个合适的SurfaceHolder,就可以完成整个环境初始化,并进行绘制。
 21 |  * GLSurfaceView和Renderer事实上只是在本文描述的基础上封装了一些便利的功能,便于开发者开发,比如渲染同步、状态控制、主(渲染)循环等
 22 |  * 
23 | * 24 | * @author flycatdeng 25 | */ 26 | class GLESTVThread extends Thread { 27 | private static final String TAG = "GLESTVThread"; 28 | private SurfaceTexture mSurfaceTexture; 29 | private EGL10 mEgl; 30 | private EGLDisplay mEglDisplay = EGL10.EGL_NO_DISPLAY;// 显示设备 31 | private EGLSurface mEglSurface = EGL10.EGL_NO_SURFACE; 32 | private EGLContext mEglContext = EGL10.EGL_NO_CONTEXT; 33 | // private GL mGL; 34 | private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; 35 | private static final int EGL_OPENGL_ES2_BIT = 4; 36 | private IGLESRenderer mRenderer; 37 | private PendingThreadAider mPendingThreadAider = new PendingThreadAider(); 38 | private boolean mNeedRenderring = true; 39 | private Object LOCK = new Object(); 40 | private boolean mIsPaused = false; 41 | 42 | public GLESTVThread(SurfaceTexture surface, IGLESRenderer renderer) { 43 | mSurfaceTexture = surface; 44 | mRenderer = renderer; 45 | } 46 | 47 | @Override 48 | public void run() { 49 | LogHelper.d(TAG, LogHelper.getThreadName()); 50 | initGLESContext(); 51 | mRenderer.onSurfaceCreated(); 52 | while (mNeedRenderring) { 53 | mPendingThreadAider.runPendings();// 执行未执行的,或要执行的事件。(后期可以开放以便模仿GLSurfaceView的queueEvent(Runnable r)) 54 | mRenderer.onDrawFrame();// 绘制 55 | // 一帧完成之后,调用eglSwapBuffers(EGLDisplay dpy, EGLContext ctx)来显示 56 | mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);// 这一句不能少啊,少了就GG了,一片空白 57 | // 1.凡是onPause都要停止,2.如果是onResume的状态,如果是循环刷新则会继续下一次循环,否则会暂停等待调用requestRender() 58 | if (mIsPaused) { 59 | pauseWhile(); 60 | } else if (mRendererMode == GLESTextureView.RENDERMODE_WHEN_DIRTY) { 61 | pauseWhile(); 62 | } 63 | } 64 | destoryGLESContext(); 65 | } 66 | 67 | private void initGLESContext() { 68 | LogHelper.d(TAG, LogHelper.getThreadName()); 69 | mEgl = (EGL10) EGLContext.getEGL(); 70 | mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);// 获取显示设备 71 | if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { 72 | throw new RuntimeException("eglGetdisplay failed : " + GLUtils.getEGLErrorString(mEgl.eglGetError())); 73 | } 74 | 75 | int[] version = new int[2]; 76 | if (!mEgl.eglInitialize(mEglDisplay, version)) {// //version中存放EGL 版本号,int[0]为主版本号,int[1]为子版本号 77 | throw new RuntimeException("eglInitialize failed : " + GLUtils.getEGLErrorString(mEgl.eglGetError())); 78 | } 79 | 80 | // 构造需要的特性列表 81 | int[] configAttribs = { // 82 | EGL10.EGL_BUFFER_SIZE, 32,// 83 | EGL10.EGL_ALPHA_SIZE, 8, // 指定Alpha大小,以下四项实际上指定了像素格式 84 | EGL10.EGL_BLUE_SIZE, 8, // 指定B大小 85 | EGL10.EGL_GREEN_SIZE, 8,// 指定G大小 86 | EGL10.EGL_RED_SIZE, 8,// 指定RGB中的R大小(bits) 87 | EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,// 指定渲染api类别,这里或者是硬编码的4,或者是EGL14.EGL_OPENGL_ES2_BIT 88 | EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT, EGL10.EGL_NONE// 总是以EGL10.EGL_NONE结尾 89 | }; 90 | 91 | int[] numConfigs = new int[1]; 92 | EGLConfig[] configs = new EGLConfig[1]; 93 | // eglChooseConfig(display, attributes, configs, num, configNum); 94 | // 用于获取满足attributes的所有config,参数1、2其意明显,参数3用于存放输出的configs,参数4指定最多输出多少个config,参数5由EGL系统写入,表明满足attributes的config一共有多少个 95 | if (!mEgl.eglChooseConfig(mEglDisplay, configAttribs, configs, 1, numConfigs)) {// 获取所有满足attributes的configs,并选择一个 96 | throw new RuntimeException("eglChooseConfig failed : " + GLUtils.getEGLErrorString(mEgl.eglGetError())); 97 | } 98 | 99 | int[] contextAttribs = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};// attrib_list,目前可用属性只有EGL_CONTEXT_CLIENT_VERSION, 1代表OpenGL ES 1.x, 100 | // 2代表2.0。同样在Android4.2之前,没有EGL_CONTEXT_CLIENT_VERSION这个属性,只能使用硬编码0x3098代替 101 | mEglContext = mEgl.eglCreateContext(mEglDisplay, configs[0], EGL10.EGL_NO_CONTEXT, // share_context,是否有context共享,共享的contxt之间亦共享所有数据。EGL_NO_CONTEXT代表不共享 102 | contextAttribs);// 创建context 103 | mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, configs[0], mSurfaceTexture,// 负责对Android Surface的管理 104 | null// Surface属性 105 | );// 获取显存,create a new EGL window surface 106 | if (mEglSurface == EGL10.EGL_NO_SURFACE || mEglContext == EGL10.EGL_NO_CONTEXT) { 107 | int error = mEgl.eglGetError(); 108 | if (error == EGL10.EGL_BAD_NATIVE_WINDOW) { 109 | throw new RuntimeException("eglCreateWindowSurface returned EGL_BAD_NATIVE_WINDOW. "); 110 | } 111 | throw new RuntimeException("eglCreateWindowSurface failed : " + GLUtils.getEGLErrorString(mEgl.eglGetError())); 112 | } 113 | 114 | if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {// 设置为当前的渲染环境 115 | throw new RuntimeException("eglMakeCurrent failed : " + GLUtils.getEGLErrorString(mEgl.eglGetError())); 116 | } 117 | // mGL = mEglContext.getGL(); 118 | } 119 | 120 | private void pauseWhile() { 121 | synchronized (LOCK) { 122 | try { 123 | LOCK.wait(); 124 | } catch (InterruptedException e) { 125 | e.printStackTrace(); 126 | } 127 | } 128 | } 129 | 130 | private void destoryGLESContext() { 131 | LogHelper.d(TAG, LogHelper.getThreadName()); 132 | mEgl.eglDestroyContext(mEglDisplay, mEglContext); 133 | mEgl.eglDestroySurface(mEglDisplay, mEglSurface); 134 | mEglContext = EGL10.EGL_NO_CONTEXT; 135 | mEglSurface = EGL10.EGL_NO_SURFACE; 136 | } 137 | 138 | public void onPause() { 139 | mRenderer.onPause(); 140 | mIsPaused = true; 141 | } 142 | 143 | public void onResume() { 144 | mRenderer.onResume(); 145 | mIsPaused = false; 146 | requestRender(); 147 | } 148 | 149 | public void onSurfaceChanged(final int width, final int height) { 150 | mPendingThreadAider.addToPending(new Runnable() {// 在GL线程中执行 151 | 152 | @Override 153 | public void run() { 154 | mRenderer.onSurfaceChanged(width, height); 155 | } 156 | }); 157 | } 158 | 159 | private int mRendererMode = GLESTextureView.RENDERMODE_CONTINUOUSLY; 160 | 161 | public void setRenderMode(int mode) { 162 | mRendererMode = mode; 163 | } 164 | 165 | public void requestRender() { 166 | synchronized (LOCK) { 167 | LOCK.notifyAll(); 168 | } 169 | } 170 | 171 | public void onDestroy() { 172 | mNeedRenderring = false; 173 | mRenderer.onDestroy(); 174 | destoryGLESContext(); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /app/src/main/java/com/dandy/module/gles/textureview/GLESTextureView.java: -------------------------------------------------------------------------------- 1 | package com.dandy.module.gles.textureview; 2 | 3 | import android.content.Context; 4 | import android.graphics.SurfaceTexture; 5 | import android.util.AttributeSet; 6 | import android.view.TextureView; 7 | 8 | import com.dandy.helper.gles.IGLESRenderer; 9 | 10 | /** 11 | *
 12 |  * 一个类似于GLSurfaceView的TextureView,用于显示opengl
 13 |  *  {@link #setRenderer(IGLESRenderer)}类似于GLSurfaceView的setRenderer(Renderer)
 14 |  *  {{@link #setRenderMode(int)}类似于GLSurfaceView的setRenderMode(int)
 15 |  *  详细调用可模仿com.dandy.gldemo.glestextureview.DemoGlesTextureView
 16 |  *  没事可看参照1或者参照2
 17 |  * 
18 | * 19 | * @author flycatdeng 20 | * 21 | */ 22 | public class GLESTextureView extends TextureView implements TextureView.SurfaceTextureListener { 23 | public final static int RENDERMODE_WHEN_DIRTY = 0; 24 | public final static int RENDERMODE_CONTINUOUSLY = 1; 25 | private GLESTVThread mGLThread; 26 | private IGLESRenderer mRenderer; 27 | private int mRendererMode = RENDERMODE_CONTINUOUSLY; 28 | 29 | public GLESTextureView(Context context) { 30 | super(context); 31 | init(context); 32 | } 33 | 34 | public GLESTextureView(Context context, AttributeSet attrs) { 35 | super(context, attrs); 36 | init(context); 37 | } 38 | 39 | /** 40 | *
 41 |      * 类似于GLSurfaceView的setRenderer
 42 |      * 
43 | */ 44 | public void setRenderer(IGLESRenderer renderer) { 45 | mRenderer = renderer; 46 | } 47 | 48 | /** 49 | *
 50 |      * 类似于GLSurfaceView的setRenderMode
 51 |      * 渲染模式,是循环刷新,还是请求的时候刷新
 52 |      * 
53 | */ 54 | public void setRenderMode(int mode) { 55 | mRendererMode = mode; 56 | } 57 | 58 | /** 59 | * Request that the renderer render a frame. This method is typically used when the render mode has been set to {@link #RENDERMODE_WHEN_DIRTY}, so 60 | * that frames are only rendered on demand. May be called from any thread. Must not be called before a renderer has been set. 61 | */ 62 | public void requestRender() { 63 | if (mRendererMode != RENDERMODE_WHEN_DIRTY) { 64 | return; 65 | } 66 | mGLThread.requestRender(); 67 | } 68 | 69 | private void init(Context context) { 70 | setSurfaceTextureListener(this); 71 | } 72 | 73 | @Override 74 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 75 | mGLThread = new GLESTVThread(surface, mRenderer);// 创建一个线程,作为GL线程 76 | mGLThread.setRenderMode(mRendererMode); 77 | mGLThread.start(); 78 | mGLThread.onSurfaceChanged(width, height); 79 | } 80 | 81 | @Override 82 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 83 | return false; 84 | } 85 | 86 | @Override 87 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 88 | mGLThread.onSurfaceChanged(width, height); 89 | } 90 | 91 | @Override 92 | public void onSurfaceTextureUpdated(SurfaceTexture surface) { 93 | 94 | } 95 | 96 | public void onResume() { 97 | if (mGLThread != null) { 98 | mGLThread.onResume(); 99 | } 100 | } 101 | 102 | public void onPause() { 103 | if (mGLThread != null) { 104 | mGLThread.onPause(); 105 | } 106 | } 107 | 108 | public void onDestroy() { 109 | if (mGLThread != null) { 110 | mGLThread.onDestroy(); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flycatdeng/GLTextureViewDemo/f57140aa28190607f3083f08516cdb31229c70c5/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flycatdeng/GLTextureViewDemo/f57140aa28190607f3083f08516cdb31229c70c5/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flycatdeng/GLTextureViewDemo/f57140aa28190607f3083f08516cdb31229c70c5/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flycatdeng/GLTextureViewDemo/f57140aa28190607f3083f08516cdb31229c70c5/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flycatdeng/GLTextureViewDemo/f57140aa28190607f3083f08516cdb31229c70c5/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | GLTextureViewDemo 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/dandy/demo/gltextureviewdemo/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.dandy.demo.gltextureviewdemo; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.2' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flycatdeng/GLTextureViewDemo/f57140aa28190607f3083f08516cdb31229c70c5/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------