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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 |
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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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
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
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/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 |
--------------------------------------------------------------------------------