├── .gitignore
├── LICENSE
├── README.md
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── sample
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── assets
│ └── sample.mp4
│ ├── java
│ └── jp
│ │ └── wasabeef
│ │ └── roundedtextureview
│ │ ├── GLRoundedGeometry.java
│ │ ├── GLTextureView.java
│ │ ├── MainActivity.java
│ │ ├── MultiSampleEGLConfigChooser.java
│ │ └── RoundedTextureView.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
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # OS X files
2 | .DS_Store
3 | Thumbs.db
4 |
5 | # gradle files
6 | .gradle
7 |
8 | # Intellij project files
9 | .idea
10 | *.iml
11 |
12 | # generated files
13 | bin/
14 | gen/
15 | obj/
16 | apk/
17 | target/
18 | build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Proguard folder generated by Eclipse
24 | proguard/
25 |
26 | com_crashlytics_export_strings.xml
27 | crashlytics-build.properties
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RoundedTextureView
2 |
3 | Sample Android code that displays video with rounded corners.
4 |
5 | Use:
6 | - TextureView (GLTextureView)
7 | - OpenGL ES 2.0
8 |
9 |
10 |
--------------------------------------------------------------------------------
/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.0.0-beta6'
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 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasabeef/android-RoundedTextureView/d234fe99ec208600efd569e6613a9ea9d98e3b15/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.11-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 |
--------------------------------------------------------------------------------
/sample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.2"
6 |
7 | defaultConfig {
8 | applicationId "jp.wasabeef.roundedtextureview"
9 | minSdkVersion 14
10 | targetSdkVersion 23
11 | versionCode 1
12 | versionName "1.0"
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 | testCompile 'junit:junit:4.12'
25 | compile 'com.android.support:appcompat-v7:23.2.0'
26 | }
27 |
--------------------------------------------------------------------------------
/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
14 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/sample/src/main/assets/sample.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasabeef/android-RoundedTextureView/d234fe99ec208600efd569e6613a9ea9d98e3b15/sample/src/main/assets/sample.mp4
--------------------------------------------------------------------------------
/sample/src/main/java/jp/wasabeef/roundedtextureview/GLRoundedGeometry.java:
--------------------------------------------------------------------------------
1 | package jp.wasabeef.roundedtextureview;
2 |
3 | import android.graphics.Point;
4 | import android.graphics.RectF;
5 | import android.support.annotation.NonNull;
6 |
7 | /**
8 | * Changed https://github.com/fabrantes/videoroundedcorners
9 | *
10 | * Created by Daichi Furiya on 2015/10/09.
11 | */
12 | public class GLRoundedGeometry {
13 |
14 | private float[] leftTop = new float[2];
15 | private float[] leftBottom = new float[2];
16 | private float[] topLeft = new float[2];
17 | private float[] topRight = new float[2];
18 | private float[] rightTop = new float[2];
19 | private float[] rightBottom = new float[2];
20 | private float[] bottomLeft = new float[2];
21 | private float[] bottomRight = new float[2];
22 |
23 | private float[] innerTopLeft = new float[2];
24 | private float[] innerTopRight = new float[2];
25 | private float[] innerBottomRight = new float[2];
26 | private float[] innerBottomLeft = new float[2];
27 |
28 | private float[] topLeftRadius = new float[2];
29 | private float[] topRightRadius = new float[2];
30 | private float[] bottomRightRadius = new float[2];
31 | private float[] bottomLeftRadius = new float[2];
32 |
33 | public GeometryArrays generateVertexData(RectF radius, RectF viewPortGLBounds,
34 | Point viewPortPxSize) {
35 | return generateVertexData(radius, viewPortGLBounds, viewPortPxSize, 0f);
36 | }
37 |
38 | /**
39 | * Generates a {@link GeometryArrays} object with arrays containing the resulting geometry
40 | * vertices and the corresponding triangle indexes.
41 | *
42 | * @param radius the corner radius of each corner. left is topLeft, top is topRight, right is
43 | * rightBottom and bottom is leftBottom.
44 | * @param viewPortGLBounds the bounds of the GL viewport in GL scalar units.
45 | * @param viewPortPxSize the size of the view port in pixels.
46 | * @param z the z coordinate for the z-plane geometry.
47 | * @return an object with the resulting geometry.
48 | */
49 | public GeometryArrays generateVertexData(RectF radius, RectF viewPortGLBounds,
50 | Point viewPortPxSize, float z) {
51 | final float x0 = viewPortGLBounds.left;
52 | final float x1 = viewPortGLBounds.right;
53 | final float y0 = viewPortGLBounds.bottom;
54 | final float y1 = viewPortGLBounds.top;
55 |
56 | final float leftTopRadius = radius.left;
57 | final float rightTopRadius = radius.top;
58 | final float rightBottomRadius = radius.right;
59 | final float leftBottomRadius = radius.bottom;
60 |
61 | topLeftRadius[0] = leftTopRadius / viewPortPxSize.x * viewPortGLBounds.width();
62 | topLeftRadius[1] = leftTopRadius / viewPortPxSize.y * -viewPortGLBounds.height();
63 | topRightRadius[0] = rightTopRadius / viewPortPxSize.x * viewPortGLBounds.width();
64 | topRightRadius[1] = rightTopRadius / viewPortPxSize.y * -viewPortGLBounds.height();
65 | bottomRightRadius[0] = rightBottomRadius / viewPortPxSize.x * viewPortGLBounds.width();
66 | bottomRightRadius[1] = rightBottomRadius / viewPortPxSize.y * -viewPortGLBounds.height();
67 | bottomLeftRadius[0] = leftBottomRadius / viewPortPxSize.x * viewPortGLBounds.width();
68 | bottomLeftRadius[1] = leftBottomRadius / viewPortPxSize.y * -viewPortGLBounds.height();
69 |
70 | leftTop[0] = x0;
71 | leftTop[1] = y1 - topLeftRadius[1];
72 | leftBottom[0] = x0;
73 | leftBottom[1] = y0 + bottomLeftRadius[1];
74 | topLeft[0] = x0 + topLeftRadius[0];
75 | topLeft[1] = y1;
76 | topRight[0] = x1 - topRightRadius[0];
77 | topRight[1] = y1;
78 | rightTop[0] = x1;
79 | rightTop[1] = y1 - topRightRadius[1];
80 | rightBottom[0] = x1;
81 | rightBottom[1] = y0 + bottomRightRadius[1];
82 | bottomLeft[0] = x0 + bottomLeftRadius[0];
83 | bottomLeft[1] = y0;
84 | bottomRight[0] = x1 - bottomRightRadius[0];
85 | bottomRight[1] = y0;
86 |
87 | innerTopLeft[0] = topLeft[0];
88 | innerTopLeft[1] = leftTop[1];
89 | innerTopRight[0] = topRight[0];
90 | innerTopRight[1] = rightTop[1];
91 | innerBottomLeft[0] = bottomLeft[0];
92 | innerBottomLeft[1] = leftBottom[1];
93 | innerBottomRight[0] = bottomRight[0];
94 | innerBottomRight[1] = rightBottom[1];
95 |
96 | // Each vertex has 5 floats (xyz + uv)
97 | // 5 squares (each has 4 vertices)
98 | // 4 rounded corners (each has X triangles, each triangle has 3 vertices)
99 | final int trianglesPerCorner = 6;
100 | final int floatsPerRoundedCorner = (trianglesPerCorner + 2) * 5;
101 | final int floatsPerSquare = 4 * 5;
102 | final int shortsPerTriangle = 3;
103 | final int shortsPerSquare = 2 * shortsPerTriangle;
104 | final int verticesSize = 5 * floatsPerSquare + 4 * floatsPerRoundedCorner;
105 | final int indicesSize = 5 * shortsPerSquare + 4 * trianglesPerCorner * shortsPerTriangle;
106 | final float[] vertices = new float[verticesSize];
107 | final short[] indices = new short[indicesSize];
108 | final GeometryArrays geoArrays = new GeometryArrays(vertices, indices);
109 |
110 | // Inner center rect
111 | addRect(geoArrays, new float[][] {
112 | innerTopLeft, innerTopRight, innerBottomLeft, innerBottomRight
113 | }, viewPortGLBounds, z);
114 | geoArrays.verticesOffset += floatsPerSquare;
115 | geoArrays.indicesOffset += shortsPerSquare;
116 |
117 | // Left rect
118 | addRect(geoArrays, new float[][] {
119 | leftTop, innerTopLeft, leftBottom, innerBottomLeft
120 | }, viewPortGLBounds, z);
121 | geoArrays.verticesOffset += floatsPerSquare;
122 | geoArrays.indicesOffset += shortsPerSquare;
123 |
124 | // Right rect
125 | addRect(geoArrays, new float[][] {
126 | innerTopRight, rightTop, innerBottomRight, rightBottom
127 | }, viewPortGLBounds, z);
128 | geoArrays.verticesOffset += floatsPerSquare;
129 | geoArrays.indicesOffset += shortsPerSquare;
130 |
131 | // Top rect
132 | addRect(geoArrays, new float[][] {
133 | topLeft, innerTopLeft, topRight, innerTopRight
134 | }, viewPortGLBounds, z);
135 | geoArrays.verticesOffset += floatsPerSquare;
136 | geoArrays.indicesOffset += shortsPerSquare;
137 |
138 | // Bottom rect
139 | addRect(geoArrays, new float[][] {
140 | innerBottomLeft, bottomLeft, innerBottomRight, bottomRight
141 | }, viewPortGLBounds, z);
142 | geoArrays.verticesOffset += floatsPerSquare;
143 | geoArrays.indicesOffset += shortsPerSquare;
144 |
145 | // These assume uniform corners (i.e. same radius on both axis)
146 | // Top left corner
147 | addRoundedCorner(geoArrays, innerTopLeft, topLeftRadius, (float) Math.PI,
148 | (float) (Math.PI / 2.0), trianglesPerCorner, viewPortGLBounds, z);
149 | geoArrays.verticesOffset += floatsPerRoundedCorner;
150 | geoArrays.indicesOffset += trianglesPerCorner * shortsPerTriangle;
151 |
152 | // Top right corner
153 | addRoundedCorner(geoArrays, innerTopRight, topRightRadius, (float) (Math.PI / 2), 0f,
154 | trianglesPerCorner, viewPortGLBounds, z);
155 | geoArrays.verticesOffset += floatsPerRoundedCorner;
156 | geoArrays.indicesOffset += trianglesPerCorner * shortsPerTriangle;
157 |
158 | // Bottom right corner
159 | addRoundedCorner(geoArrays, innerBottomRight, bottomRightRadius, (float) (Math.PI * 3.0 / 2.0),
160 | (float) Math.PI * 2, trianglesPerCorner, viewPortGLBounds, z);
161 | geoArrays.verticesOffset += floatsPerRoundedCorner;
162 | geoArrays.indicesOffset += trianglesPerCorner * shortsPerTriangle;
163 |
164 | // Bottom left corner
165 | addRoundedCorner(geoArrays, innerBottomLeft, bottomLeftRadius, (float) Math.PI,
166 | (float) (Math.PI * 3.0 / 2.0), trianglesPerCorner, viewPortGLBounds, z);
167 |
168 | return new GeometryArrays(vertices, indices);
169 | }
170 |
171 | /**
172 | * Adds the vertices of a rectangle defined by 4 corner points. The array of vertices passed
173 | * in must have the required length to add the geometry points (5 floats for each vertex). Also
174 | * the coordinates of the rect corners should already be in the view port space.
175 | *
176 | * @param geoArrays an object containing the vertex and index data arrays and their current
177 | * offsets.
178 | * @param rectPoints an array of corner points defining the rectangle. index 0 is the x
179 | * coordinate and index 1 the y coordinate.
180 | * @param viewPort the bounds of the current GL viewport, this is used to calculate the texture
181 | * mapping.
182 | * @param z the z coordinate.
183 | */
184 | private void addRect(@NonNull GeometryArrays geoArrays, @NonNull float[][] rectPoints,
185 | @NonNull RectF viewPort, float z) {
186 | final float[] vertices = geoArrays.triangleVertices;
187 | final short[] indices = geoArrays.triangleIndices;
188 | final int indicesOffset = geoArrays.indicesOffset;
189 | final int verticesOffset = geoArrays.verticesOffset;
190 | int rectPointIdx = 0;
191 | for (final float[] rectPoint : rectPoints) {
192 | // 5 values [xyzuv] per vertex
193 | final int currentVertexOffset = verticesOffset + rectPointIdx * 5;
194 |
195 | // XYZ (vertex space coordinates
196 | vertices[currentVertexOffset] = rectPoint[0];
197 | vertices[currentVertexOffset + 1] = rectPoint[1];
198 | vertices[currentVertexOffset + 2] = z;
199 |
200 | // UV (texture mapping)
201 | vertices[currentVertexOffset + 3] = (rectPoint[0] - viewPort.left) / viewPort.width();
202 | vertices[currentVertexOffset + 4] = (rectPoint[1] - viewPort.bottom) / -viewPort.height();
203 |
204 | rectPointIdx++;
205 | }
206 |
207 | // Index our triangles -- tell where each triangle vertex is
208 | final int initialIdx = verticesOffset / 5;
209 | indices[indicesOffset] = (short) (initialIdx);
210 | indices[indicesOffset + 1] = (short) (initialIdx + 1);
211 | indices[indicesOffset + 2] = (short) (initialIdx + 2);
212 | indices[indicesOffset + 3] = (short) (initialIdx + 1);
213 | indices[indicesOffset + 4] = (short) (initialIdx + 2);
214 | indices[indicesOffset + 5] = (short) (initialIdx + 3);
215 | }
216 |
217 | /**
218 | * Adds the vertices of a number of triangles to form a rounded corner. The triangles start at
219 | * some center point and will sweep from a given initial angle up to a final one. The size of
220 | * the triangles is defined by the radius.
221 | *
222 | * The array of vertices passed in must have the required length to add the geometry points
223 | * (5 floats for each vertex). Also the coordinates of the rect corners should already be in
224 | * the view port space.
225 | *
226 | * @param geoArrays an object containing the vertex and index data arrays and their current
227 | * offsets.
228 | * @param center the center point where all triangles will start.
229 | * @param radius the desired radius in the x and y axis, in viewport dimensions.
230 | * @param rads0 the initial angle.
231 | * @param rads1 the final angle.
232 | * @param triangles the amount of triangles to create.
233 | * @param viewPort the bounds of the current GL viewport, this is used to calculate the texture
234 | * mapping.
235 | * @param z the z coordinate.
236 | */
237 | private void addRoundedCorner(@NonNull GeometryArrays geoArrays, @NonNull float[] center,
238 | float[] radius, float rads0, float rads1, int triangles, @NonNull RectF viewPort, float z) {
239 | final float[] vertices = geoArrays.triangleVertices;
240 | final short[] indices = geoArrays.triangleIndices;
241 | final int verticesOffset = geoArrays.verticesOffset;
242 | final int indicesOffset = geoArrays.indicesOffset;
243 | for (int i = 0; i < triangles; i++) {
244 | // final int currentOffset = verticesOffset + i * 15 /* each triangle is 3 * xyzuv */;
245 | final int currentOffset = verticesOffset + i * 5 + (i > 0 ? 2 * 5 : 0);
246 | final float rads = rads0 + (rads1 - rads0) * (i / (float) triangles);
247 | final float radsNext = rads0 + (rads1 - rads0) * ((i + 1) / (float) triangles);
248 | final int triangleEdge2Offset;
249 |
250 | if (i == 0) {
251 | // XYZUV - center point
252 | vertices[currentOffset] = center[0];
253 | vertices[currentOffset + 1] = center[1];
254 | vertices[currentOffset + 2] = z;
255 | vertices[currentOffset + 3] = (vertices[currentOffset] - viewPort.left) / viewPort.width();
256 | vertices[currentOffset + 4] =
257 | (vertices[currentOffset + 1] - viewPort.bottom) / -viewPort.height();
258 |
259 | // XYZUV - triangle edge 1
260 | vertices[currentOffset + 5] = center[0] + radius[0] * (float) Math.cos(rads);
261 | vertices[currentOffset + 6] = center[1] + radius[1] * (float) Math.sin(rads);
262 | vertices[currentOffset + 7] = z;
263 | vertices[currentOffset + 8] =
264 | (vertices[currentOffset + 5] - viewPort.left) / viewPort.width();
265 | vertices[currentOffset + 9] =
266 | (vertices[currentOffset + 6] - viewPort.bottom) / -viewPort.height();
267 |
268 | triangleEdge2Offset = 10;
269 | } else {
270 | triangleEdge2Offset = 0;
271 | }
272 |
273 | // XYZUV - triangle edge 2
274 | final int edge2Offset = currentOffset + triangleEdge2Offset;
275 | vertices[edge2Offset] = center[0] + radius[0] * (float) Math.cos(radsNext);
276 | vertices[edge2Offset + 1] = center[1] + radius[1] * (float) Math.sin(radsNext);
277 | vertices[edge2Offset + 2] = z;
278 | vertices[edge2Offset + 3] = (vertices[edge2Offset] - viewPort.left) / viewPort.width();
279 | vertices[edge2Offset + 4] =
280 | (vertices[edge2Offset + 1] - viewPort.bottom) / -viewPort.height();
281 |
282 | // Index our triangles -- tell where each triangle vertex is
283 | final int initialIdx = verticesOffset / 5;
284 | indices[indicesOffset + i * 3] = (short) (initialIdx);
285 | indices[indicesOffset + i * 3 + 1] = (short) (initialIdx + i + 1);
286 | indices[indicesOffset + i * 3 + 2] = (short) (initialIdx + i + 2);
287 | }
288 | }
289 |
290 | public static class GeometryArrays {
291 | public float[] triangleVertices;
292 | public short[] triangleIndices;
293 | public int verticesOffset = 0;
294 | public int indicesOffset = 0;
295 |
296 | public GeometryArrays(@NonNull float[] vertices, @NonNull short[] indices) {
297 | triangleVertices = vertices;
298 | triangleIndices = indices;
299 | }
300 | }
301 | }
302 |
--------------------------------------------------------------------------------
/sample/src/main/java/jp/wasabeef/roundedtextureview/GLTextureView.java:
--------------------------------------------------------------------------------
1 | package jp.wasabeef.roundedtextureview;
2 |
3 | import android.content.Context;
4 | import android.graphics.SurfaceTexture;
5 | import android.opengl.GLDebugHelper;
6 | import android.util.AttributeSet;
7 | import android.util.Log;
8 | import android.view.TextureView;
9 | import android.view.View;
10 | import java.io.Writer;
11 | import java.lang.ref.WeakReference;
12 | import java.util.ArrayList;
13 | import java.util.List;
14 | import javax.microedition.khronos.egl.EGL10;
15 | import javax.microedition.khronos.egl.EGL11;
16 | import javax.microedition.khronos.egl.EGLConfig;
17 | import javax.microedition.khronos.egl.EGLContext;
18 | import javax.microedition.khronos.egl.EGLDisplay;
19 | import javax.microedition.khronos.egl.EGLSurface;
20 | import javax.microedition.khronos.opengles.GL;
21 | import javax.microedition.khronos.opengles.GL10;
22 |
23 | /**
24 | * See {@link android.opengl.GLSurfaceView}
25 | *
26 | * Created by Daichi Furiya on 2015/10/09.
27 | */
28 | public class GLTextureView extends TextureView
29 | implements TextureView.SurfaceTextureListener, View.OnLayoutChangeListener {
30 |
31 | private final static String TAG = GLTextureView.class.getSimpleName();
32 |
33 | private final static boolean LOG_ATTACH_DETACH = false;
34 | private final static boolean LOG_THREADS = false;
35 | private final static boolean LOG_PAUSE_RESUME = false;
36 | private final static boolean LOG_SURFACE = false;
37 | private final static boolean LOG_RENDERER = false;
38 | private final static boolean LOG_RENDERER_DRAW_FRAME = false;
39 | private final static boolean LOG_EGL = false;
40 |
41 | /**
42 | * The renderer only renders
43 | * when the surface is created, or when {@link #requestRender} is called.
44 | *
45 | * @see #getRenderMode()
46 | * @see #setRenderMode(int)
47 | * @see #requestRender()
48 | */
49 | public final static int RENDERMODE_WHEN_DIRTY = 0;
50 | /**
51 | * The renderer is called
52 | * continuously to re-render the scene.
53 | *
54 | * @see #getRenderMode()
55 | * @see #setRenderMode(int)
56 | */
57 | public final static int RENDERMODE_CONTINUOUSLY = 1;
58 |
59 | /**
60 | * Check glError() after every GL call and throw an exception if glError indicates
61 | * that an error has occurred. This can be used to help track down which OpenGL ES call
62 | * is causing an error.
63 | *
64 | * @see #getDebugFlags
65 | * @see #setDebugFlags
66 | */
67 | public final static int DEBUG_CHECK_GL_ERROR = 1;
68 |
69 | /**
70 | * Log GL calls to the system log at "verbose" level with tag "GLTextureView".
71 | *
72 | * @see #getDebugFlags
73 | * @see #setDebugFlags
74 | */
75 | public final static int DEBUG_LOG_GL_CALLS = 2;
76 |
77 | /**
78 | * Standard View constructor. In order to render something, you
79 | * must call {@link #setRenderer} to register a renderer.
80 | */
81 | public GLTextureView(Context context) {
82 | super(context);
83 | init();
84 | }
85 |
86 | /**
87 | * Standard View constructor. In order to render something, you
88 | * must call {@link #setRenderer} to register a renderer.
89 | */
90 | public GLTextureView(Context context, AttributeSet attrs) {
91 | super(context, attrs);
92 | init();
93 | }
94 |
95 | @Override protected void finalize() throws Throwable {
96 | try {
97 | if (glThread != null) {
98 | // GLThread may still be running if this view was never
99 | // attached to a window.
100 | glThread.requestExitAndWait();
101 | }
102 | } finally {
103 | super.finalize();
104 | }
105 | }
106 |
107 | private void init() {
108 | setSurfaceTextureListener(this);
109 | }
110 |
111 | /**
112 | * Set the glWrapper. If the glWrapper is not null, its
113 | * {@link GLWrapper#wrap(GL)} method is called
114 | * whenever a surface is created. A GLWrapper can be used to wrap
115 | * the GL object that's passed to the renderer. Wrapping a GL
116 | * object enables examining and modifying the behavior of the
117 | * GL calls made by the renderer.
118 | *
119 | * Wrapping is typically used for debugging purposes.
120 | *
121 | * The default value is null.
122 | *
123 | * @param glWrapper the new GLWrapper
124 | */
125 | public void setGLWrapper(GLWrapper glWrapper) {
126 | this.glWrapper = glWrapper;
127 | }
128 |
129 | /**
130 | * Set the debug flags to a new value. The value is
131 | * constructed by OR-together zero or more
132 | * of the DEBUG_CHECK_* constants. The debug flags take effect
133 | * whenever a surface is created. The default value is zero.
134 | *
135 | * @param debugFlags the new debug flags
136 | * @see #DEBUG_CHECK_GL_ERROR
137 | * @see #DEBUG_LOG_GL_CALLS
138 | */
139 | public void setDebugFlags(int debugFlags) {
140 | this.debugFlags = debugFlags;
141 | }
142 |
143 | /**
144 | * Get the current value of the debug flags.
145 | *
146 | * @return the current value of the debug flags.
147 | */
148 | public int getDebugFlags() {
149 | return debugFlags;
150 | }
151 |
152 | /**
153 | * Control whether the EGL context is preserved when the GLTextureView is paused and
154 | * resumed.
155 | *
156 | * If set to true, then the EGL context may be preserved when the GLTextureView is paused.
157 | * Whether the EGL context is actually preserved or not depends upon whether the
158 | * Android device that the program is running on can support an arbitrary number of EGL
159 | * contexts or not. Devices that can only support a limited number of EGL contexts must
160 | * release the EGL context in order to allow multiple applications to share the GPU.
161 | *
162 | * If set to false, the EGL context will be released when the GLTextureView is paused,
163 | * and recreated when the GLTextureView is resumed.
164 | *
165 | *
166 | * The default is false.
167 | *
168 | * @param preserveOnPause preserve the EGL context when paused
169 | */
170 | public void setPreserveEGLContextOnPause(boolean preserveOnPause) {
171 | preserveEGLContextOnPause = preserveOnPause;
172 | }
173 |
174 | /**
175 | * @return true if the EGL context will be preserved when paused
176 | */
177 | public boolean getPreserveEGLContextOnPause() {
178 | return preserveEGLContextOnPause;
179 | }
180 |
181 | /**
182 | * Set the renderer associated with this view. Also starts the thread that
183 | * will call the renderer, which in turn causes the rendering to start.
184 | *
This method should be called once and only once in the life-cycle of
185 | * a GLTextureView.
186 | *
The following GLTextureView methods can only be called before
187 | * setRenderer is called:
188 | *
189 | * - {@link #setEGLConfigChooser(boolean)}
190 | *
- {@link #setEGLConfigChooser(EGLConfigChooser)}
191 | *
- {@link #setEGLConfigChooser(int, int, int, int, int, int)}
192 | *
193 | *
194 | * The following GLTextureView methods can only be called after
195 | * setRenderer is called:
196 | *
197 | * - {@link #getRenderMode()}
198 | *
- {@link #onPause()}
199 | *
- {@link #onResume()}
200 | *
- {@link #queueEvent(Runnable)}
201 | *
- {@link #requestRender()}
202 | *
- {@link #setRenderMode(int)}
203 | *
204 | *
205 | * @param renderer the renderer to use to perform OpenGL drawing.
206 | */
207 | public void setRenderer(Renderer renderer) {
208 | checkRenderThreadState();
209 | if (eglConfigChooser == null) {
210 | eglConfigChooser = new SimpleEGLConfigChooser(true);
211 | }
212 | if (eglContextFactory == null) {
213 | eglContextFactory = new DefaultContextFactory();
214 | }
215 | if (eglWindowSurfaceFactory == null) {
216 | eglWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
217 | }
218 | this.renderer = renderer;
219 | glThread = new GLThread(mThisWeakRef);
220 | glThread.start();
221 | }
222 |
223 | /**
224 | * Install a custom EGLContextFactory.
225 | * If this method is
226 | * called, it must be called before {@link #setRenderer(Renderer)}
227 | * is called.
228 | *
229 | * If this method is not called, then by default
230 | * a context will be created with no shared context and
231 | * with a null attribute list.
232 | */
233 | public void setEGLContextFactory(EGLContextFactory factory) {
234 | checkRenderThreadState();
235 | eglContextFactory = factory;
236 | }
237 |
238 | /**
239 | * Install a custom EGLWindowSurfaceFactory.
240 | *
If this method is
241 | * called, it must be called before {@link #setRenderer(Renderer)}
242 | * is called.
243 | *
244 | * If this method is not called, then by default
245 | * a window surface will be created with a null attribute list.
246 | */
247 | public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) {
248 | checkRenderThreadState();
249 | eglWindowSurfaceFactory = factory;
250 | }
251 |
252 | /**
253 | * Install a custom EGLConfigChooser.
254 | *
If this method is
255 | * called, it must be called before {@link #setRenderer(Renderer)}
256 | * is called.
257 | *
258 | * If no setEGLConfigChooser method is called, then by default the
259 | * view will choose an EGLConfig that is compatible with the current
260 | * android.view.Surface, with a depth buffer depth of
261 | * at least 16 bits.
262 | */
263 | public void setEGLConfigChooser(EGLConfigChooser configChooser) {
264 | checkRenderThreadState();
265 | eglConfigChooser = configChooser;
266 | }
267 |
268 | /**
269 | * Install a config chooser which will choose a config
270 | * as close to 16-bit RGB as possible, with or without an optional depth
271 | * buffer as close to 16-bits as possible.
272 | *
If this method is
273 | * called, it must be called before {@link #setRenderer(Renderer)}
274 | * is called.
275 | *
276 | * If no setEGLConfigChooser method is called, then by default the
277 | * view will choose an RGB_888 surface with a depth buffer depth of
278 | * at least 16 bits.
279 | */
280 | public void setEGLConfigChooser(boolean needDepth) {
281 | setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));
282 | }
283 |
284 | /**
285 | * Install a config chooser which will choose a config
286 | * with at least the specified depthSize and stencilSize,
287 | * and exactly the specified redSize, greenSize, blueSize and alphaSize.
288 | *
If this method is
289 | * called, it must be called before {@link #setRenderer(Renderer)}
290 | * is called.
291 | *
292 | * If no setEGLConfigChooser method is called, then by default the
293 | * view will choose an RGB_888 surface with a depth buffer depth of
294 | * at least 16 bits.
295 | */
296 | public void setEGLConfigChooser(int redSize, int greenSize, int blueSize, int alphaSize,
297 | int depthSize, int stencilSize) {
298 | setEGLConfigChooser(
299 | new ComponentSizeChooser(redSize, greenSize, blueSize, alphaSize, depthSize, stencilSize));
300 | }
301 |
302 | /**
303 | * Inform the default EGLContextFactory and default EGLConfigChooser
304 | * which EGLContext client version to pick.
305 | *
Use this method to create an OpenGL ES 2.0-compatible context.
306 | * Example:
307 | *
308 | * public MyView(Context context) {
309 | * super(context);
310 | * setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context.
311 | * setRenderer(new MyRenderer());
312 | * }
313 | *
314 | * Note: Activities which require OpenGL ES 2.0 should indicate this by
315 | * setting @lt;uses-feature android:glEsVersion="0x00020000" /> in the activity's
316 | * AndroidManifest.xml file.
317 | *
If this method is called, it must be called before {@link #setRenderer(Renderer)}
318 | * is called.
319 | *
This method only affects the behavior of the default EGLContexFactory and the
320 | * default EGLConfigChooser. If
321 | * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then the supplied
322 | * EGLContextFactory is responsible for creating an OpenGL ES 2.0-compatible context.
323 | * If
324 | * {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied
325 | * EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config.
326 | *
327 | * @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0
328 | */
329 | public void setEGLContextClientVersion(int version) {
330 | checkRenderThreadState();
331 | eglContextClientVersion = version;
332 | }
333 |
334 | /**
335 | * Set the rendering mode. When renderMode is
336 | * RENDERMODE_CONTINUOUSLY, the renderer is called
337 | * repeatedly to re-render the scene. When renderMode
338 | * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface
339 | * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY.
340 | *
341 | * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance
342 | * by allowing the GPU and CPU to idle when the view does not need to be updated.
343 | *
344 | * This method can only be called after {@link #setRenderer(Renderer)}
345 | *
346 | * @param renderMode one of the RENDERMODE_X constants
347 | * @see #RENDERMODE_CONTINUOUSLY
348 | * @see #RENDERMODE_WHEN_DIRTY
349 | */
350 | public void setRenderMode(int renderMode) {
351 | glThread.setRenderMode(renderMode);
352 | }
353 |
354 | /**
355 | * Get the current rendering mode. May be called
356 | * from any thread. Must not be called before a renderer has been set.
357 | *
358 | * @return the current rendering mode.
359 | * @see #RENDERMODE_CONTINUOUSLY
360 | * @see #RENDERMODE_WHEN_DIRTY
361 | */
362 | public int getRenderMode() {
363 | return glThread.getRenderMode();
364 | }
365 |
366 | /**
367 | * Request that the renderer render a frame.
368 | * This method is typically used when the render mode has been set to
369 | * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand.
370 | * May be called
371 | * from any thread. Must not be called before a renderer has been set.
372 | */
373 | public void requestRender() {
374 | glThread.requestRender();
375 | }
376 |
377 | /**
378 | * This method is part of the SurfaceHolder.Callback interface, and is
379 | * not normally called or subclassed by clients of GLTextureView.
380 | */
381 | public void surfaceCreated(SurfaceTexture texture) {
382 | glThread.surfaceCreated();
383 | }
384 |
385 | /**
386 | * This method is part of the SurfaceHolder.Callback interface, and is
387 | * not normally called or subclassed by clients of GLTextureView.
388 | */
389 | public void surfaceDestroyed(SurfaceTexture texture) {
390 | // Surface will be destroyed when we return
391 | glThread.surfaceDestroyed();
392 | }
393 |
394 | /**
395 | * This method is part of the SurfaceHolder.Callback interface, and is
396 | * not normally called or subclassed by clients of GLTextureView.
397 | */
398 | public void surfaceChanged(SurfaceTexture texture, int format, int w, int h) {
399 | glThread.onWindowResize(w, h);
400 | }
401 |
402 | /**
403 | * Inform the view that the activity is paused. The owner of this view must
404 | * call this method when the activity is paused. Calling this method will
405 | * pause the rendering thread.
406 | * Must not be called before a renderer has been set.
407 | */
408 | public void onPause() {
409 | glThread.onPause();
410 | }
411 |
412 | /**
413 | * Inform the view that the activity is resumed. The owner of this view must
414 | * call this method when the activity is resumed. Calling this method will
415 | * recreate the OpenGL display and resume the rendering
416 | * thread.
417 | * Must not be called before a renderer has been set.
418 | */
419 | public void onResume() {
420 | glThread.onResume();
421 | }
422 |
423 | /**
424 | * Queue a runnable to be run on the GL rendering thread. This can be used
425 | * to communicate with the Renderer on the rendering thread.
426 | * Must not be called before a renderer has been set.
427 | *
428 | * @param r the runnable to be run on the GL rendering thread.
429 | */
430 | public void queueEvent(Runnable r) {
431 | glThread.queueEvent(r);
432 | }
433 |
434 | /**
435 | * This method is used as part of the View class and is not normally
436 | * called or subclassed by clients of GLTextureView.
437 | */
438 | @Override protected void onAttachedToWindow() {
439 | super.onAttachedToWindow();
440 | if (LOG_ATTACH_DETACH) {
441 | Log.d(TAG, "onAttachedToWindow reattach =" + detached);
442 | }
443 | if (detached && (renderer != null)) {
444 | int renderMode = RENDERMODE_CONTINUOUSLY;
445 | if (glThread != null) {
446 | renderMode = glThread.getRenderMode();
447 | }
448 | glThread = new GLThread(mThisWeakRef);
449 | if (renderMode != RENDERMODE_CONTINUOUSLY) {
450 | glThread.setRenderMode(renderMode);
451 | }
452 | glThread.start();
453 | }
454 | detached = false;
455 | }
456 |
457 | /**
458 | * This method is used as part of the View class and is not normally
459 | * called or subclassed by clients of GLTextureView.
460 | * Must not be called before a renderer has been set.
461 | */
462 | @Override protected void onDetachedFromWindow() {
463 | if (LOG_ATTACH_DETACH) {
464 | Log.d(TAG, "onDetachedFromWindow");
465 | }
466 | if (glThread != null) {
467 | glThread.requestExitAndWait();
468 | }
469 | detached = true;
470 | super.onDetachedFromWindow();
471 | }
472 |
473 | public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
474 | int oldTop, int oldRight, int oldBottom) {
475 | surfaceChanged(getSurfaceTexture(), 0, right - left, bottom - top);
476 | }
477 |
478 | public void addSurfaceTextureListener(SurfaceTextureListener listener) {
479 | surfaceTextureListeners.add(listener);
480 | }
481 |
482 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
483 | surfaceCreated(surface);
484 | surfaceChanged(surface, 0, width, height);
485 |
486 | for (SurfaceTextureListener l : surfaceTextureListeners) {
487 | l.onSurfaceTextureAvailable(surface, width, height);
488 | }
489 | }
490 |
491 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
492 | surfaceChanged(surface, 0, width, height);
493 |
494 | for (SurfaceTextureListener l : surfaceTextureListeners) {
495 | l.onSurfaceTextureSizeChanged(surface, width, height);
496 | }
497 | }
498 |
499 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
500 | surfaceDestroyed(surface);
501 |
502 | for (SurfaceTextureListener l : surfaceTextureListeners) {
503 | l.onSurfaceTextureDestroyed(surface);
504 | }
505 |
506 | return true;
507 | }
508 |
509 | public void onSurfaceTextureUpdated(SurfaceTexture surface) {
510 | requestRender();
511 |
512 | for (SurfaceTextureListener l : surfaceTextureListeners) {
513 | l.onSurfaceTextureUpdated(surface);
514 | }
515 | }
516 |
517 | // ----------------------------------------------------------------------
518 |
519 | /**
520 | * An interface used to wrap a GL interface.
521 | *
Typically
522 | * used for implementing debugging and tracing on top of the default
523 | * GL interface. You would typically use this by creating your own class
524 | * that implemented all the GL methods by delegating to another GL instance.
525 | * Then you could add your own behavior before or after calling the
526 | * delegate. All the GLWrapper would do was instantiate and return the
527 | * wrapper GL instance:
528 | *
529 | * class MyGLWrapper implements GLWrapper {
530 | * GL wrap(GL gl) {
531 | * return new MyGLImplementation(gl);
532 | * }
533 | * static class MyGLImplementation implements GL,GL10,GL11,... {
534 | * ...
535 | * }
536 | * }
537 | *
538 | *
539 | * @see #setGLWrapper(GLWrapper)
540 | */
541 | public interface GLWrapper {
542 | /**
543 | * Wraps a gl interface in another gl interface.
544 | *
545 | * @param gl a GL interface that is to be wrapped.
546 | * @return either the input argument or another GL object that wraps the input argument.
547 | */
548 | GL wrap(GL gl);
549 | }
550 |
551 | /**
552 | * A generic renderer interface.
553 | *
554 | * The renderer is responsible for making OpenGL calls to render a frame.
555 | *
556 | * GLTextureView clients typically create their own classes that implement
557 | * this interface, and then call {@link GLTextureView#setRenderer} to
558 | * register the renderer with the GLTextureView.
559 | *
560 | *
561 | *
562 | *
Developer Guides
563 | *
For more information about how to use OpenGL, read the
564 | * OpenGL developer guide.
565 | *
566 | *
567 | * Threading
568 | * The renderer will be called on a separate thread, so that rendering
569 | * performance is decoupled from the UI thread. Clients typically need to
570 | * communicate with the renderer from the UI thread, because that's where
571 | * input events are received. Clients can communicate using any of the
572 | * standard Java techniques for cross-thread communication, or they can
573 | * use the {@link GLTextureView#queueEvent(Runnable)} convenience method.
574 | *
575 | *
EGL Context Lost
576 | * There are situations where the EGL rendering context will be lost. This
577 | * typically happens when device wakes up after going to sleep. When
578 | * the EGL context is lost, all OpenGL resources (such as textures) that are
579 | * associated with that context will be automatically deleted. In order to
580 | * keep rendering correctly, a renderer must recreate any lost resources
581 | * that it still needs. The {@link #onSurfaceCreated(GL10,
582 | * EGLConfig)} method
583 | * is a convenient place to do this.
584 | *
585 | * @see #setRenderer(Renderer)
586 | */
587 | public interface Renderer {
588 | /**
589 | * Called when the surface is created or recreated.
590 | *
591 | * Called when the rendering thread
592 | * starts and whenever the EGL context is lost. The EGL context will typically
593 | * be lost when the Android device awakes after going to sleep.
594 | *
595 | * Since this method is called at the beginning of rendering, as well as
596 | * every time the EGL context is lost, this method is a convenient place to put
597 | * code to create resources that need to be created when the rendering
598 | * starts, and that need to be recreated when the EGL context is lost.
599 | * Textures are an example of a resource that you might want to create
600 | * here.
601 | *
602 | * Note that when the EGL context is lost, all OpenGL resources associated
603 | * with that context will be automatically deleted. You do not need to call
604 | * the corresponding "glDelete" methods such as glDeleteTextures to
605 | * manually delete these lost resources.
606 | *
607 | *
608 | * @param gl the GL interface. Use instanceof
to
609 | * test if the interface supports GL11 or higher interfaces.
610 | * @param config the EGLConfig of the created surface. Can be used
611 | * to create matching pbuffers.
612 | */
613 | void onSurfaceCreated(GL10 gl, EGLConfig config);
614 |
615 | /**
616 | * Called when the surface changed size.
617 | *
618 | * Called after the surface is created and whenever
619 | * the OpenGL ES surface size changes.
620 | *
621 | * Typically you will set your viewport here. If your camera
622 | * is fixed then you could also set your projection matrix here:
623 | *
624 | * void onSurfaceChanged(GL10 gl, int width, int height) {
625 | * gl.glViewport(0, 0, width, height);
626 | * // for a fixed camera, set the projection too
627 | * float ratio = (float) width / height;
628 | * gl.glMatrixMode(GL10.GL_PROJECTION);
629 | * gl.glLoadIdentity();
630 | * gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
631 | * }
632 | *
633 | *
634 | * @param gl the GL interface. Use instanceof
to
635 | * test if the interface supports GL11 or higher interfaces.
636 | */
637 | void onSurfaceChanged(GL10 gl, int width, int height);
638 |
639 | /**
640 | * Called to draw the current frame.
641 | *
642 | * This method is responsible for drawing the current frame.
643 | *
644 | * The implementation of this method typically looks like this:
645 | *
646 | * void onDrawFrame(GL10 gl) {
647 | * gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
648 | * //... other gl calls to render the scene ...
649 | * }
650 | *
651 | *
652 | * @param gl the GL interface. Use instanceof
to
653 | * test if the interface supports GL11 or higher interfaces.
654 | */
655 | void onDrawFrame(GL10 gl);
656 | }
657 |
658 | /**
659 | * An interface for customizing the eglCreateContext and eglDestroyContext calls.
660 | *
661 | * This interface must be implemented by clients wishing to call
662 | * {@link GLTextureView#setEGLContextFactory(EGLContextFactory)}
663 | */
664 | public interface EGLContextFactory {
665 | EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig);
666 |
667 | void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);
668 | }
669 |
670 | private class DefaultContextFactory implements EGLContextFactory {
671 | private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
672 |
673 | public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
674 | int[] attrib_list = {
675 | EGL_CONTEXT_CLIENT_VERSION, eglContextClientVersion, EGL10.EGL_NONE
676 | };
677 |
678 | return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
679 | eglContextClientVersion != 0 ? attrib_list : null);
680 | }
681 |
682 | public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
683 | if (!egl.eglDestroyContext(display, context)) {
684 | Log.e("DefaultContextFactory", "display:" + display + " context: " + context);
685 | if (LOG_THREADS) {
686 | Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId());
687 | }
688 | EglHelper.throwEglException("eglDestroyContex", egl.eglGetError());
689 | }
690 | }
691 | }
692 |
693 | /**
694 | * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls.
695 | *
696 | * This interface must be implemented by clients wishing to call
697 | * {@link GLTextureView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)}
698 | */
699 | public interface EGLWindowSurfaceFactory {
700 | /**
701 | * @return null if the surface cannot be constructed.
702 | */
703 | EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config,
704 | Object nativeWindow);
705 |
706 | void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface);
707 | }
708 |
709 | private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory {
710 |
711 | public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config,
712 | Object nativeWindow) {
713 | EGLSurface result = null;
714 | try {
715 | result = egl.eglCreateWindowSurface(display, config, nativeWindow, null);
716 | } catch (IllegalArgumentException e) {
717 | // This exception indicates that the surface flinger surface
718 | // is not valid. This can happen if the surface flinger surface has
719 | // been torn down, but the application has not yet been
720 | // notified via SurfaceHolder.Callback.surfaceDestroyed.
721 | // In theory the application should be notified first,
722 | // but in practice sometimes it is not. See b/4588890
723 | Log.e(TAG, "eglCreateWindowSurface", e);
724 | }
725 | return result;
726 | }
727 |
728 | public void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface) {
729 | egl.eglDestroySurface(display, surface);
730 | }
731 | }
732 |
733 | /**
734 | * An interface for choosing an EGLConfig configuration from a list of
735 | * potential configurations.
736 | *
737 | * This interface must be implemented by clients wishing to call
738 | * {@link GLTextureView#setEGLConfigChooser(EGLConfigChooser)}
739 | */
740 | public interface EGLConfigChooser {
741 | /**
742 | * Choose a configuration from the list. Implementors typically
743 | * implement this method by calling
744 | * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the
745 | * EGL specification available from The Khronos Group to learn how to call eglChooseConfig.
746 | *
747 | * @param egl the EGL10 for the current display.
748 | * @param display the current display.
749 | * @return the chosen configuration.
750 | */
751 | EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
752 | }
753 |
754 | private abstract class BaseConfigChooser implements EGLConfigChooser {
755 | public BaseConfigChooser(int[] configSpec) {
756 | mConfigSpec = filterConfigSpec(configSpec);
757 | }
758 |
759 | public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
760 | int[] num_config = new int[1];
761 | if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, num_config)) {
762 | throw new IllegalArgumentException("eglChooseConfig failed");
763 | }
764 |
765 | int numConfigs = num_config[0];
766 |
767 | if (numConfigs <= 0) {
768 | throw new IllegalArgumentException("No configs match configSpec");
769 | }
770 |
771 | EGLConfig[] configs = new EGLConfig[numConfigs];
772 | if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, num_config)) {
773 | throw new IllegalArgumentException("eglChooseConfig#2 failed");
774 | }
775 | EGLConfig config = chooseConfig(egl, display, configs);
776 | if (config == null) {
777 | throw new IllegalArgumentException("No config chosen");
778 | }
779 | return config;
780 | }
781 |
782 | abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs);
783 |
784 | protected int[] mConfigSpec;
785 |
786 | private int[] filterConfigSpec(int[] configSpec) {
787 | if (eglContextClientVersion != 2) {
788 | return configSpec;
789 | }
790 | /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
791 | * And we know the configSpec is well formed.
792 | */
793 | int len = configSpec.length;
794 | int[] newConfigSpec = new int[len + 2];
795 | System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1);
796 | newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE;
797 | newConfigSpec[len] = 0x0004; /* EGL_OPENGL_ES2_BIT */
798 | newConfigSpec[len + 1] = EGL10.EGL_NONE;
799 | return newConfigSpec;
800 | }
801 | }
802 |
803 | /**
804 | * Choose a configuration with exactly the specified r,g,b,a sizes,
805 | * and at least the specified depth and stencil sizes.
806 | */
807 | private class ComponentSizeChooser extends BaseConfigChooser {
808 | public ComponentSizeChooser(int redSize, int greenSize, int blueSize, int alphaSize,
809 | int depthSize, int stencilSize) {
810 | super(new int[] {
811 | EGL10.EGL_RED_SIZE, redSize, EGL10.EGL_GREEN_SIZE, greenSize, EGL10.EGL_BLUE_SIZE,
812 | blueSize, EGL10.EGL_ALPHA_SIZE, alphaSize, EGL10.EGL_DEPTH_SIZE, depthSize,
813 | EGL10.EGL_STENCIL_SIZE, stencilSize, EGL10.EGL_NONE
814 | });
815 | value = new int[1];
816 | this.redSize = redSize;
817 | this.greenSize = greenSize;
818 | this.blueSize = blueSize;
819 | this.alphaSize = alphaSize;
820 | this.depthSize = depthSize;
821 | this.stencilSize = stencilSize;
822 | }
823 |
824 | @Override public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
825 | for (EGLConfig config : configs) {
826 | int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
827 | int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
828 | if ((d >= depthSize) && (s >= stencilSize)) {
829 | int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
830 | int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
831 | int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
832 | int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
833 | if ((r == redSize) && (g == greenSize) && (b == blueSize) && (a == alphaSize)) {
834 | return config;
835 | }
836 | }
837 | }
838 | return null;
839 | }
840 |
841 | private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config, int attribute,
842 | int defaultValue) {
843 |
844 | if (egl.eglGetConfigAttrib(display, config, attribute, value)) {
845 | return value[0];
846 | }
847 | return defaultValue;
848 | }
849 |
850 | private int[] value;
851 | // Subclasses can adjust these values:
852 | protected int redSize;
853 | protected int greenSize;
854 | protected int blueSize;
855 | protected int alphaSize;
856 | protected int depthSize;
857 | protected int stencilSize;
858 | }
859 |
860 | /**
861 | * This class will choose a RGB_888 surface with
862 | * or without a depth buffer.
863 | */
864 | private class SimpleEGLConfigChooser extends ComponentSizeChooser {
865 | public SimpleEGLConfigChooser(boolean withDepthBuffer) {
866 | super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0);
867 | }
868 | }
869 |
870 | /**
871 | * An EGL helper class.
872 | */
873 |
874 | private static class EglHelper {
875 | public EglHelper(WeakReference glTextureViewWeakReference) {
876 | this.glTextureViewWeakRef = glTextureViewWeakReference;
877 | }
878 |
879 | /**
880 | * Initialize EGL for a given configuration spec.
881 | */
882 | public void start() {
883 | if (LOG_EGL) {
884 | Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId());
885 | }
886 | /*
887 | * Get an EGL instance
888 | */
889 | egl = (EGL10) EGLContext.getEGL();
890 |
891 | /*
892 | * Get to the default display.
893 | */
894 | eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
895 |
896 | if (eglDisplay == EGL10.EGL_NO_DISPLAY) {
897 | throw new RuntimeException("eglGetDisplay failed");
898 | }
899 |
900 | /*
901 | * We can now initialize EGL for that display
902 | */
903 | int[] version = new int[2];
904 | if (!egl.eglInitialize(eglDisplay, version)) {
905 | throw new RuntimeException("eglInitialize failed");
906 | }
907 | GLTextureView view = glTextureViewWeakRef.get();
908 | if (view == null) {
909 | eglConfig = null;
910 | eglContext = null;
911 | } else {
912 | eglConfig = view.eglConfigChooser.chooseConfig(egl, eglDisplay);
913 |
914 | /*
915 | * Create an EGL context. We want to do this as rarely as we can, because an
916 | * EGL context is a somewhat heavy object.
917 | */
918 | eglContext = view.eglContextFactory.createContext(egl, eglDisplay, eglConfig);
919 | }
920 | if (eglContext == null || eglContext == EGL10.EGL_NO_CONTEXT) {
921 | eglContext = null;
922 | throwEglException("createContext");
923 | }
924 | if (LOG_EGL) {
925 | Log.w("EglHelper",
926 | "createContext " + eglContext + " tid=" + Thread.currentThread().getId());
927 | }
928 |
929 | eglSurface = null;
930 | }
931 |
932 | /**
933 | * Create an egl surface for the current SurfaceHolder surface. If a surface
934 | * already exists, destroy it before creating the new surface.
935 | *
936 | * @return true if the surface was created successfully.
937 | */
938 | public boolean createSurface() {
939 | if (LOG_EGL) {
940 | Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId());
941 | }
942 | /*
943 | * Check preconditions.
944 | */
945 | if (egl == null) {
946 | throw new RuntimeException("egl not initialized");
947 | }
948 | if (eglDisplay == null) {
949 | throw new RuntimeException("eglDisplay not initialized");
950 | }
951 | if (eglConfig == null) {
952 | throw new RuntimeException("eglConfig not initialized");
953 | }
954 |
955 | /*
956 | * The window size has changed, so we need to create a new
957 | * surface.
958 | */
959 | destroySurfaceImp();
960 |
961 | /*
962 | * Create an EGL surface we can render into.
963 | */
964 | GLTextureView view = glTextureViewWeakRef.get();
965 | if (view != null) {
966 | eglSurface = view.eglWindowSurfaceFactory.createWindowSurface(egl, eglDisplay, eglConfig,
967 | view.getSurfaceTexture());
968 | } else {
969 | eglSurface = null;
970 | }
971 |
972 | if (eglSurface == null || eglSurface == EGL10.EGL_NO_SURFACE) {
973 | int error = egl.eglGetError();
974 | if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
975 | Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
976 | }
977 | return false;
978 | }
979 |
980 | /*
981 | * Before we can issue GL commands, we need to make sure
982 | * the context is current and bound to a surface.
983 | */
984 | if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
985 | /*
986 | * Could not make the context current, probably because the underlying
987 | * TextureView surface has been destroyed.
988 | */
989 | logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", egl.eglGetError());
990 | return false;
991 | }
992 |
993 | return true;
994 | }
995 |
996 | /**
997 | * Create a GL object for the current EGL context.
998 | */
999 | GL createGL() {
1000 |
1001 | GL gl = eglContext.getGL();
1002 | GLTextureView view = glTextureViewWeakRef.get();
1003 | if (view != null) {
1004 | if (view.glWrapper != null) {
1005 | gl = view.glWrapper.wrap(gl);
1006 | }
1007 |
1008 | if ((view.debugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) {
1009 | int configFlags = 0;
1010 | Writer log = null;
1011 | if ((view.debugFlags & DEBUG_CHECK_GL_ERROR) != 0) {
1012 | configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR;
1013 | }
1014 | if ((view.debugFlags & DEBUG_LOG_GL_CALLS) != 0) {
1015 | log = new LogWriter();
1016 | }
1017 | gl = GLDebugHelper.wrap(gl, configFlags, log);
1018 | }
1019 | }
1020 | return gl;
1021 | }
1022 |
1023 | /**
1024 | * Display the current render surface.
1025 | *
1026 | * @return the EGL error code from eglSwapBuffers.
1027 | */
1028 | public int swap() {
1029 | if (!egl.eglSwapBuffers(eglDisplay, eglSurface)) {
1030 | return egl.eglGetError();
1031 | }
1032 | return EGL10.EGL_SUCCESS;
1033 | }
1034 |
1035 | public void destroySurface() {
1036 | if (LOG_EGL) {
1037 | Log.w("EglHelper", "destroySurface() tid=" + Thread.currentThread().getId());
1038 | }
1039 | destroySurfaceImp();
1040 | }
1041 |
1042 | private void destroySurfaceImp() {
1043 | if (eglSurface != null && eglSurface != EGL10.EGL_NO_SURFACE) {
1044 | egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE,
1045 | EGL10.EGL_NO_CONTEXT);
1046 | GLTextureView view = glTextureViewWeakRef.get();
1047 | if (view != null) {
1048 | view.eglWindowSurfaceFactory.destroySurface(egl, eglDisplay, eglSurface);
1049 | }
1050 | eglSurface = null;
1051 | }
1052 | }
1053 |
1054 | public void finish() {
1055 | if (LOG_EGL) {
1056 | Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId());
1057 | }
1058 | if (eglContext != null) {
1059 | GLTextureView view = glTextureViewWeakRef.get();
1060 | if (view != null) {
1061 | view.eglContextFactory.destroyContext(egl, eglDisplay, eglContext);
1062 | }
1063 | eglContext = null;
1064 | }
1065 | if (eglDisplay != null) {
1066 | egl.eglTerminate(eglDisplay);
1067 | eglDisplay = null;
1068 | }
1069 | }
1070 |
1071 | private void throwEglException(String function) {
1072 | throwEglException(function, egl.eglGetError());
1073 | }
1074 |
1075 | public static void throwEglException(String function, int error) {
1076 | String message = formatEglError(function, error);
1077 | if (LOG_THREADS) {
1078 | Log.e("EglHelper",
1079 | "throwEglException tid=" + Thread.currentThread().getId() + " " + message);
1080 | }
1081 | throw new RuntimeException(message);
1082 | }
1083 |
1084 | public static void logEglErrorAsWarning(String tag, String function, int error) {
1085 | Log.w(tag, formatEglError(function, error));
1086 | }
1087 |
1088 | public static String formatEglError(String function, int error) {
1089 | return function + " failed: " + error;
1090 | }
1091 |
1092 | private WeakReference glTextureViewWeakRef;
1093 | EGL10 egl;
1094 | EGLDisplay eglDisplay;
1095 | EGLSurface eglSurface;
1096 | EGLConfig eglConfig;
1097 | EGLContext eglContext;
1098 | }
1099 |
1100 | /**
1101 | * A generic GL Thread. Takes care of initializing EGL and GL. Delegates
1102 | * to a Renderer instance to do the actual drawing. Can be configured to
1103 | * render continuously or on request.
1104 | *
1105 | * All potentially blocking synchronization is done through the
1106 | * glThreadManager object. This avoids multiple-lock ordering issues.
1107 | */
1108 | static class GLThread extends Thread {
1109 | GLThread(WeakReference glTextureViewWeakRef) {
1110 | super();
1111 | width = 0;
1112 | height = 0;
1113 | requestRender = true;
1114 | renderMode = RENDERMODE_CONTINUOUSLY;
1115 | this.glTextureViewWeakRef = glTextureViewWeakRef;
1116 | }
1117 |
1118 | @Override public void run() {
1119 | setName("GLThread " + getId());
1120 | if (LOG_THREADS) {
1121 | Log.i("GLThread", "starting tid=" + getId());
1122 | }
1123 |
1124 | try {
1125 | guardedRun();
1126 | } catch (InterruptedException e) {
1127 | // fall thru and exit normally
1128 | } finally {
1129 | glThreadManager.threadExiting(this);
1130 | }
1131 | }
1132 |
1133 | /*
1134 | * This private method should only be called inside a
1135 | * synchronized(glThreadManager) block.
1136 | */
1137 | private void stopEglSurfaceLocked() {
1138 | if (haveEglSurface) {
1139 | haveEglSurface = false;
1140 | eglHelper.destroySurface();
1141 | }
1142 | }
1143 |
1144 | /*
1145 | * This private method should only be called inside a
1146 | * synchronized(glThreadManager) block.
1147 | */
1148 | private void stopEglContextLocked() {
1149 | if (haveEglContext) {
1150 | eglHelper.finish();
1151 | haveEglContext = false;
1152 | glThreadManager.releaseEglContextLocked(this);
1153 | }
1154 | }
1155 |
1156 | private void guardedRun() throws InterruptedException {
1157 | eglHelper = new EglHelper(glTextureViewWeakRef);
1158 | haveEglContext = false;
1159 | haveEglSurface = false;
1160 | try {
1161 | GL10 gl = null;
1162 | boolean createEglContext = false;
1163 | boolean createEglSurface = false;
1164 | boolean createGlInterface = false;
1165 | boolean lostEglContext = false;
1166 | boolean sizeChanged = false;
1167 | boolean wantRenderNotification = false;
1168 | boolean doRenderNotification = false;
1169 | boolean askedToReleaseEglContext = false;
1170 | int w = 0;
1171 | int h = 0;
1172 | Runnable event = null;
1173 |
1174 | while (true) {
1175 | synchronized (glThreadManager) {
1176 | while (true) {
1177 | if (shouldExit) {
1178 | return;
1179 | }
1180 |
1181 | if (!eventQueue.isEmpty()) {
1182 | event = eventQueue.remove(0);
1183 | break;
1184 | }
1185 |
1186 | // Update the pause state.
1187 | boolean pausing = false;
1188 | if (paused != requestPaused) {
1189 | pausing = requestPaused;
1190 | paused = requestPaused;
1191 | glThreadManager.notifyAll();
1192 | if (LOG_PAUSE_RESUME) {
1193 | Log.i("GLThread", "paused is now " + paused + " tid=" + getId());
1194 | }
1195 | }
1196 |
1197 | // Do we need to give up the EGL context?
1198 | if (shouldReleaseEglContext) {
1199 | if (LOG_SURFACE) {
1200 | Log.i("GLThread", "releasing EGL context because asked to tid=" + getId());
1201 | }
1202 | stopEglSurfaceLocked();
1203 | stopEglContextLocked();
1204 | shouldReleaseEglContext = false;
1205 | askedToReleaseEglContext = true;
1206 | }
1207 |
1208 | // Have we lost the EGL context?
1209 | if (lostEglContext) {
1210 | stopEglSurfaceLocked();
1211 | stopEglContextLocked();
1212 | lostEglContext = false;
1213 | }
1214 |
1215 | // When pausing, release the EGL surface:
1216 | if (pausing && haveEglSurface) {
1217 | if (LOG_SURFACE) {
1218 | Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
1219 | }
1220 | stopEglSurfaceLocked();
1221 | }
1222 |
1223 | // When pausing, optionally release the EGL Context:
1224 | if (pausing && haveEglContext) {
1225 | GLTextureView view = glTextureViewWeakRef.get();
1226 | boolean preserveEglContextOnPause =
1227 | view == null ? false : view.preserveEGLContextOnPause;
1228 | if (!preserveEglContextOnPause
1229 | || glThreadManager.shouldReleaseEGLContextWhenPausing()) {
1230 | stopEglContextLocked();
1231 | if (LOG_SURFACE) {
1232 | Log.i("GLThread", "releasing EGL context because paused tid=" + getId());
1233 | }
1234 | }
1235 | }
1236 |
1237 | // When pausing, optionally terminate EGL:
1238 | if (pausing) {
1239 | if (glThreadManager.shouldTerminateEGLWhenPausing()) {
1240 | eglHelper.finish();
1241 | if (LOG_SURFACE) {
1242 | Log.i("GLThread", "terminating EGL because paused tid=" + getId());
1243 | }
1244 | }
1245 | }
1246 |
1247 | // Have we lost the TextureView surface?
1248 | if ((!hasSurface) && (!waitingForSurface)) {
1249 | if (LOG_SURFACE) {
1250 | Log.i("GLThread", "noticed textureView surface lost tid=" + getId());
1251 | }
1252 | if (haveEglSurface) {
1253 | stopEglSurfaceLocked();
1254 | }
1255 | waitingForSurface = true;
1256 | surfaceIsBad = false;
1257 | glThreadManager.notifyAll();
1258 | }
1259 |
1260 | // Have we acquired the surface view surface?
1261 | if (hasSurface && waitingForSurface) {
1262 | if (LOG_SURFACE) {
1263 | Log.i("GLThread", "noticed textureView surface acquired tid=" + getId());
1264 | }
1265 | waitingForSurface = false;
1266 | glThreadManager.notifyAll();
1267 | }
1268 |
1269 | if (doRenderNotification) {
1270 | if (LOG_SURFACE) {
1271 | Log.i("GLThread", "sending render notification tid=" + getId());
1272 | }
1273 | wantRenderNotification = false;
1274 | doRenderNotification = false;
1275 | renderComplete = true;
1276 | glThreadManager.notifyAll();
1277 | }
1278 |
1279 | // Ready to draw?
1280 | if (readyToDraw()) {
1281 |
1282 | // If we don't have an EGL context, try to acquire one.
1283 | if (!haveEglContext) {
1284 | if (askedToReleaseEglContext) {
1285 | askedToReleaseEglContext = false;
1286 | } else if (glThreadManager.tryAcquireEglContextLocked(this)) {
1287 | try {
1288 | eglHelper.start();
1289 | } catch (RuntimeException t) {
1290 | glThreadManager.releaseEglContextLocked(this);
1291 | throw t;
1292 | }
1293 | haveEglContext = true;
1294 | createEglContext = true;
1295 |
1296 | glThreadManager.notifyAll();
1297 | }
1298 | }
1299 |
1300 | if (haveEglContext && !haveEglSurface) {
1301 | haveEglSurface = true;
1302 | createEglSurface = true;
1303 | createGlInterface = true;
1304 | sizeChanged = true;
1305 | }
1306 |
1307 | if (haveEglSurface) {
1308 | if (this.sizeChanged) {
1309 | sizeChanged = true;
1310 | w = width;
1311 | h = height;
1312 | wantRenderNotification = true;
1313 | if (LOG_SURFACE) {
1314 | Log.i("GLThread", "noticing that we want render notification tid=" + getId());
1315 | }
1316 |
1317 | // Destroy and recreate the EGL surface.
1318 | createEglSurface = true;
1319 |
1320 | this.sizeChanged = false;
1321 | }
1322 | requestRender = false;
1323 | glThreadManager.notifyAll();
1324 | break;
1325 | }
1326 | }
1327 |
1328 | // By design, this is the only place in a GLThread thread where we wait().
1329 | if (LOG_THREADS) {
1330 | Log.i("GLThread", "waiting tid=" + getId() + " haveEglContext: " + haveEglContext
1331 | + " haveEglSurface: " + haveEglSurface + " paused: " + paused + " hasSurface: "
1332 | + hasSurface + " surfaceIsBad: " + surfaceIsBad + " waitingForSurface: "
1333 | + waitingForSurface + " width: " + width + " height: " + height
1334 | + " requestRender: " + requestRender + " renderMode: " + renderMode);
1335 | }
1336 | glThreadManager.wait();
1337 | }
1338 | } // end of synchronized(glThreadManager)
1339 |
1340 | if (event != null) {
1341 | event.run();
1342 | event = null;
1343 | continue;
1344 | }
1345 |
1346 | if (createEglSurface) {
1347 | if (LOG_SURFACE) {
1348 | Log.w("GLThread", "egl createSurface");
1349 | }
1350 | if (!eglHelper.createSurface()) {
1351 | synchronized (glThreadManager) {
1352 | surfaceIsBad = true;
1353 | glThreadManager.notifyAll();
1354 | }
1355 | continue;
1356 | }
1357 | createEglSurface = false;
1358 | }
1359 |
1360 | if (createGlInterface) {
1361 | gl = (GL10) eglHelper.createGL();
1362 |
1363 | glThreadManager.checkGLDriver(gl);
1364 | createGlInterface = false;
1365 | }
1366 |
1367 | if (createEglContext) {
1368 | if (LOG_RENDERER) {
1369 | Log.w("GLThread", "onSurfaceCreated");
1370 | }
1371 | GLTextureView view = glTextureViewWeakRef.get();
1372 | if (view != null) {
1373 | view.renderer.onSurfaceCreated(gl, eglHelper.eglConfig);
1374 | }
1375 | createEglContext = false;
1376 | }
1377 |
1378 | if (sizeChanged) {
1379 | if (LOG_RENDERER) {
1380 | Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
1381 | }
1382 | GLTextureView view = glTextureViewWeakRef.get();
1383 | if (view != null) {
1384 | view.renderer.onSurfaceChanged(gl, w, h);
1385 | }
1386 | sizeChanged = false;
1387 | }
1388 |
1389 | if (LOG_RENDERER_DRAW_FRAME) {
1390 | Log.w("GLThread", "onDrawFrame tid=" + getId());
1391 | }
1392 | {
1393 | GLTextureView view = glTextureViewWeakRef.get();
1394 | if (view != null) {
1395 | view.renderer.onDrawFrame(gl);
1396 | }
1397 | }
1398 | int swapError = eglHelper.swap();
1399 | switch (swapError) {
1400 | case EGL10.EGL_SUCCESS:
1401 | break;
1402 | case EGL11.EGL_CONTEXT_LOST:
1403 | if (LOG_SURFACE) {
1404 | Log.i("GLThread", "egl context lost tid=" + getId());
1405 | }
1406 | lostEglContext = true;
1407 | break;
1408 | default:
1409 | // Other errors typically mean that the current surface is bad,
1410 | // probably because the TextureView surface has been destroyed,
1411 | // but we haven't been notified yet.
1412 | // Log the error to help developers understand why rendering stopped.
1413 | EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError);
1414 |
1415 | synchronized (glThreadManager) {
1416 | surfaceIsBad = true;
1417 | glThreadManager.notifyAll();
1418 | }
1419 | break;
1420 | }
1421 |
1422 | if (wantRenderNotification) {
1423 | doRenderNotification = true;
1424 | }
1425 | }
1426 | } finally {
1427 | /*
1428 | * clean-up everything...
1429 | */
1430 | synchronized (glThreadManager) {
1431 | stopEglSurfaceLocked();
1432 | stopEglContextLocked();
1433 | }
1434 | }
1435 | }
1436 |
1437 | public boolean ableToDraw() {
1438 | return haveEglContext && haveEglSurface && readyToDraw();
1439 | }
1440 |
1441 | private boolean readyToDraw() {
1442 | return (!paused) && hasSurface && (!surfaceIsBad) && (width > 0) && (height > 0) && (
1443 | requestRender || (renderMode == RENDERMODE_CONTINUOUSLY));
1444 | }
1445 |
1446 | public void setRenderMode(int renderMode) {
1447 | if (!((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY))) {
1448 | throw new IllegalArgumentException("renderMode");
1449 | }
1450 | synchronized (glThreadManager) {
1451 | this.renderMode = renderMode;
1452 | glThreadManager.notifyAll();
1453 | }
1454 | }
1455 |
1456 | public int getRenderMode() {
1457 | synchronized (glThreadManager) {
1458 | return renderMode;
1459 | }
1460 | }
1461 |
1462 | public void requestRender() {
1463 | synchronized (glThreadManager) {
1464 | requestRender = true;
1465 | glThreadManager.notifyAll();
1466 | }
1467 | }
1468 |
1469 | public void surfaceCreated() {
1470 | synchronized (glThreadManager) {
1471 | if (LOG_THREADS) {
1472 | Log.i("GLThread", "surfaceCreated tid=" + getId());
1473 | }
1474 | hasSurface = true;
1475 | glThreadManager.notifyAll();
1476 | while ((waitingForSurface) && (!exited)) {
1477 | try {
1478 | glThreadManager.wait();
1479 | } catch (InterruptedException e) {
1480 | Thread.currentThread().interrupt();
1481 | }
1482 | }
1483 | }
1484 | }
1485 |
1486 | public void surfaceDestroyed() {
1487 | synchronized (glThreadManager) {
1488 | if (LOG_THREADS) {
1489 | Log.i("GLThread", "surfaceDestroyed tid=" + getId());
1490 | }
1491 | hasSurface = false;
1492 | glThreadManager.notifyAll();
1493 | while ((!waitingForSurface) && (!exited)) {
1494 | try {
1495 | glThreadManager.wait();
1496 | } catch (InterruptedException e) {
1497 | Thread.currentThread().interrupt();
1498 | }
1499 | }
1500 | }
1501 | }
1502 |
1503 | public void onPause() {
1504 | synchronized (glThreadManager) {
1505 | if (LOG_PAUSE_RESUME) {
1506 | Log.i("GLThread", "onPause tid=" + getId());
1507 | }
1508 | requestPaused = true;
1509 | glThreadManager.notifyAll();
1510 | while ((!exited) && (!paused)) {
1511 | if (LOG_PAUSE_RESUME) {
1512 | Log.i("Main thread", "onPause waiting for paused.");
1513 | }
1514 | try {
1515 | glThreadManager.wait();
1516 | } catch (InterruptedException ex) {
1517 | Thread.currentThread().interrupt();
1518 | }
1519 | }
1520 | }
1521 | }
1522 |
1523 | public void onResume() {
1524 | synchronized (glThreadManager) {
1525 | if (LOG_PAUSE_RESUME) {
1526 | Log.i("GLThread", "onResume tid=" + getId());
1527 | }
1528 | requestPaused = false;
1529 | requestRender = true;
1530 | renderComplete = false;
1531 | glThreadManager.notifyAll();
1532 | while ((!exited) && paused && (!renderComplete)) {
1533 | if (LOG_PAUSE_RESUME) {
1534 | Log.i("Main thread", "onResume waiting for !paused.");
1535 | }
1536 | try {
1537 | glThreadManager.wait();
1538 | } catch (InterruptedException ex) {
1539 | Thread.currentThread().interrupt();
1540 | }
1541 | }
1542 | }
1543 | }
1544 |
1545 | public void onWindowResize(int w, int h) {
1546 | synchronized (glThreadManager) {
1547 | width = w;
1548 | height = h;
1549 | sizeChanged = true;
1550 | requestRender = true;
1551 | renderComplete = false;
1552 | glThreadManager.notifyAll();
1553 |
1554 | // Wait for thread to react to resize and render a frame
1555 | while (!exited && !paused && !renderComplete && ableToDraw()) {
1556 | if (LOG_SURFACE) {
1557 | Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + getId());
1558 | }
1559 | try {
1560 | glThreadManager.wait();
1561 | } catch (InterruptedException ex) {
1562 | Thread.currentThread().interrupt();
1563 | }
1564 | }
1565 | }
1566 | }
1567 |
1568 | public void requestExitAndWait() {
1569 | // don't call this from GLThread thread or it is a guaranteed
1570 | // deadlock!
1571 | synchronized (glThreadManager) {
1572 | shouldExit = true;
1573 | glThreadManager.notifyAll();
1574 | while (!exited) {
1575 | try {
1576 | glThreadManager.wait();
1577 | } catch (InterruptedException ex) {
1578 | Thread.currentThread().interrupt();
1579 | }
1580 | }
1581 | }
1582 | }
1583 |
1584 | public void requestReleaseEglContextLocked() {
1585 | shouldReleaseEglContext = true;
1586 | glThreadManager.notifyAll();
1587 | }
1588 |
1589 | /**
1590 | * Queue an "event" to be run on the GL rendering thread.
1591 | *
1592 | * @param r the runnable to be run on the GL rendering thread.
1593 | */
1594 | public void queueEvent(Runnable r) {
1595 | if (r == null) {
1596 | throw new IllegalArgumentException("r must not be null");
1597 | }
1598 | synchronized (glThreadManager) {
1599 | eventQueue.add(r);
1600 | glThreadManager.notifyAll();
1601 | }
1602 | }
1603 |
1604 | // Once the thread is started, all accesses to the following member
1605 | // variables are protected by the glThreadManager monitor
1606 | private boolean shouldExit;
1607 | private boolean exited;
1608 | private boolean requestPaused;
1609 | private boolean paused;
1610 | private boolean hasSurface;
1611 | private boolean surfaceIsBad;
1612 | private boolean waitingForSurface;
1613 | private boolean haveEglContext;
1614 | private boolean haveEglSurface;
1615 | private boolean shouldReleaseEglContext;
1616 | private int width;
1617 | private int height;
1618 | private int renderMode;
1619 | private boolean requestRender;
1620 | private boolean renderComplete;
1621 | private ArrayList eventQueue = new ArrayList<>();
1622 | private boolean sizeChanged = true;
1623 |
1624 | // End of member variables protected by the glThreadManager monitor.
1625 |
1626 | private EglHelper eglHelper;
1627 |
1628 | /**
1629 | * Set once at thread construction time, nulled out when the parent view is garbage
1630 | * called. This weak reference allows the GLTextureView to be garbage collected while
1631 | * the GLThread is still alive.
1632 | */
1633 | private WeakReference glTextureViewWeakRef;
1634 | }
1635 |
1636 | static class LogWriter extends Writer {
1637 |
1638 | @Override public void close() {
1639 | flushBuilder();
1640 | }
1641 |
1642 | @Override public void flush() {
1643 | flushBuilder();
1644 | }
1645 |
1646 | @Override public void write(char[] buf, int offset, int count) {
1647 | for (int i = 0; i < count; i++) {
1648 | char c = buf[offset + i];
1649 | if (c == '\n') {
1650 | flushBuilder();
1651 | } else {
1652 | builder.append(c);
1653 | }
1654 | }
1655 | }
1656 |
1657 | private void flushBuilder() {
1658 | if (builder.length() > 0) {
1659 | Log.v("GLTextureView", builder.toString());
1660 | builder.delete(0, builder.length());
1661 | }
1662 | }
1663 |
1664 | private StringBuilder builder = new StringBuilder();
1665 | }
1666 |
1667 | private void checkRenderThreadState() {
1668 | if (glThread != null) {
1669 | throw new IllegalStateException("setRenderer has already been called for this instance.");
1670 | }
1671 | }
1672 |
1673 | private static class GLThreadManager {
1674 | private static String TAG = "GLThreadManager";
1675 |
1676 | public synchronized void threadExiting(GLThread thread) {
1677 | if (LOG_THREADS) {
1678 | Log.i("GLThread", "exiting tid=" + thread.getId());
1679 | }
1680 | thread.exited = true;
1681 | if (eglOwner == thread) {
1682 | eglOwner = null;
1683 | }
1684 | notifyAll();
1685 | }
1686 |
1687 | /*
1688 | * Tries once to acquire the right to use an EGL
1689 | * context. Does not block. Requires that we are already
1690 | * in the glThreadManager monitor when this is called.
1691 | *
1692 | * @return true if the right to use an EGL context was acquired.
1693 | */
1694 | public boolean tryAcquireEglContextLocked(GLThread thread) {
1695 | if (eglOwner == thread || eglOwner == null) {
1696 | eglOwner = thread;
1697 | notifyAll();
1698 | return true;
1699 | }
1700 | checkGLESVersion();
1701 | if (multipleGLESContextsAllowed) {
1702 | return true;
1703 | }
1704 | // Notify the owning thread that it should release the context.
1705 | // TODO: implement a fairness policy. Currently
1706 | // if the owning thread is drawing continuously it will just
1707 | // reacquire the EGL context.
1708 | if (eglOwner != null) {
1709 | eglOwner.requestReleaseEglContextLocked();
1710 | }
1711 | return false;
1712 | }
1713 |
1714 | /*
1715 | * Releases the EGL context. Requires that we are already in the
1716 | * glThreadManager monitor when this is called.
1717 | */
1718 | public void releaseEglContextLocked(GLThread thread) {
1719 | if (eglOwner == thread) {
1720 | eglOwner = null;
1721 | }
1722 | notifyAll();
1723 | }
1724 |
1725 | public synchronized boolean shouldReleaseEGLContextWhenPausing() {
1726 | // Release the EGL context when pausing even if
1727 | // the hardware supports multiple EGL contexts.
1728 | // Otherwise the device could run out of EGL contexts.
1729 | return limitedGLESContexts;
1730 | }
1731 |
1732 | public synchronized boolean shouldTerminateEGLWhenPausing() {
1733 | checkGLESVersion();
1734 | return !multipleGLESContextsAllowed;
1735 | }
1736 |
1737 | public synchronized void checkGLDriver(GL10 gl) {
1738 | if (!glesDriverCheckComplete) {
1739 | checkGLESVersion();
1740 | String renderer = gl.glGetString(GL10.GL_RENDERER);
1741 | if (glesVersion < kGLES_20) {
1742 | multipleGLESContextsAllowed = !renderer.startsWith(kMSM7K_RENDERER_PREFIX);
1743 | notifyAll();
1744 | }
1745 | limitedGLESContexts = !multipleGLESContextsAllowed;
1746 | if (LOG_SURFACE) {
1747 | Log.w(TAG, "checkGLDriver renderer = \"" + renderer + "\" multipleContextsAllowed = "
1748 | + multipleGLESContextsAllowed + " limitedGLESContexts = " + limitedGLESContexts);
1749 | }
1750 | glesDriverCheckComplete = true;
1751 | }
1752 | }
1753 |
1754 | private void checkGLESVersion() {
1755 | if (!glesVersionCheckComplete) {
1756 | glesVersionCheckComplete = true;
1757 | }
1758 | }
1759 |
1760 | /**
1761 | * This check was required for some pre-Android-3.0 hardware. Android 3.0 provides
1762 | * support for hardware-accelerated views, therefore multiple EGL contexts are
1763 | * supported on all Android 3.0+ EGL drivers.
1764 | */
1765 | private boolean glesVersionCheckComplete;
1766 | private int glesVersion;
1767 | private boolean glesDriverCheckComplete;
1768 | private boolean multipleGLESContextsAllowed;
1769 | private boolean limitedGLESContexts;
1770 | private static final int kGLES_20 = 0x20000;
1771 | private static final String kMSM7K_RENDERER_PREFIX = "Q3Dimension MSM7500 ";
1772 | private GLThread eglOwner;
1773 | }
1774 |
1775 | private static final GLThreadManager glThreadManager = new GLThreadManager();
1776 |
1777 | private final WeakReference mThisWeakRef = new WeakReference<>(this);
1778 | private GLThread glThread;
1779 | private Renderer renderer;
1780 | private boolean detached;
1781 | private EGLConfigChooser eglConfigChooser;
1782 | private EGLContextFactory eglContextFactory;
1783 | private EGLWindowSurfaceFactory eglWindowSurfaceFactory;
1784 | private GLWrapper glWrapper;
1785 | private int debugFlags;
1786 | private int eglContextClientVersion;
1787 | private boolean preserveEGLContextOnPause;
1788 | private List surfaceTextureListeners = new ArrayList<>();
1789 | }
--------------------------------------------------------------------------------
/sample/src/main/java/jp/wasabeef/roundedtextureview/MainActivity.java:
--------------------------------------------------------------------------------
1 | package jp.wasabeef.roundedtextureview;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 |
6 | public class MainActivity extends AppCompatActivity {
7 |
8 | private static final String TAG = MainActivity.class.getSimpleName();
9 |
10 | @Override protected void onCreate(Bundle savedInstanceState) {
11 | super.onCreate(savedInstanceState);
12 | setContentView(R.layout.activity_main);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/sample/src/main/java/jp/wasabeef/roundedtextureview/MultiSampleEGLConfigChooser.java:
--------------------------------------------------------------------------------
1 | package jp.wasabeef.roundedtextureview;
2 |
3 | import android.util.Log;
4 | import javax.microedition.khronos.egl.EGL10;
5 | import javax.microedition.khronos.egl.EGLConfig;
6 | import javax.microedition.khronos.egl.EGLDisplay;
7 |
8 | /**
9 | * EGL configuration Chooser slightly adapted from:
10 | *
11 | * https://code.google.com/p/gdc2011-android-opengl/source/browse/trunk/src/com/example/gdc11/MultisampleConfigChooser.java
12 | */
13 | public class MultiSampleEGLConfigChooser implements GLTextureView.EGLConfigChooser {
14 | static private final String TAG = MultiSampleEGLConfigChooser.class.getSimpleName();
15 |
16 | private int[] value;
17 |
18 | private boolean usesCoverageAa = false;
19 |
20 | // pixel format
21 | private int r = 8;
22 | private int g = 8;
23 | private int b = 8;
24 | private int a = 8;
25 |
26 | private int depth = 16;
27 | private int stencil = 4;
28 | private int multisample = 4;
29 | private int renderableType = 4;
30 |
31 | public MultiSampleEGLConfigChooser() {
32 | }
33 |
34 | public MultiSampleEGLConfigChooser(int r, int g, int b, int a, int depth, int stencil,
35 | int multisample, int renderableType) {
36 | this.r = r;
37 | this.g = g;
38 | this.b = b;
39 | this.a = a;
40 |
41 | this.depth = depth;
42 | this.stencil = stencil;
43 | this.multisample = multisample;
44 | this.renderableType = renderableType;
45 | }
46 |
47 | @Override public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
48 | // try to find a normal configuration first.
49 | int[] configSpec = {
50 | EGL10.EGL_RED_SIZE, r, EGL10.EGL_GREEN_SIZE, g, EGL10.EGL_BLUE_SIZE, b,
51 | EGL10.EGL_ALPHA_SIZE, a, EGL10.EGL_DEPTH_SIZE, depth, EGL10.EGL_STENCIL_SIZE, stencil,
52 | EGL10.EGL_RENDERABLE_TYPE, renderableType, EGL10.EGL_SAMPLE_BUFFERS, 1, EGL10.EGL_SAMPLES,
53 | multisample, EGL10.EGL_NONE
54 | };
55 | value = new int[1];
56 | if (!egl.eglChooseConfig(display, configSpec, null, 0, value)) {
57 | throw new IllegalArgumentException("eglChooseConfig failed");
58 | }
59 | int numConfigs = value[0];
60 |
61 | // No normal multisampling config was found. Try to create a
62 | // converage multisampling configuration, for the nVidia Tegra2.
63 | // See the EGL_NV_coverage_sample documentation.
64 | if (numConfigs <= 0 && multisample > 1) {
65 | final int EGL_COVERAGE_BUFFERS_NV = 0x30E0;
66 | final int EGL_COVERAGE_SAMPLES_NV = 0x30E1;
67 |
68 | configSpec = new int[] {
69 | EGL10.EGL_RED_SIZE, r, EGL10.EGL_GREEN_SIZE, g, EGL10.EGL_BLUE_SIZE, b,
70 | EGL10.EGL_ALPHA_SIZE, a, EGL10.EGL_DEPTH_SIZE, depth, EGL10.EGL_STENCIL_SIZE, stencil,
71 | EGL10.EGL_RENDERABLE_TYPE, renderableType, EGL_COVERAGE_BUFFERS_NV, 1,
72 | EGL_COVERAGE_SAMPLES_NV, multisample, EGL10.EGL_NONE
73 | };
74 |
75 | if (!egl.eglChooseConfig(display, configSpec, null, 0, value)) {
76 | throw new IllegalArgumentException("2nd eglChooseConfig failed");
77 | }
78 | numConfigs = value[0];
79 |
80 | // Give up, try without multisampling.
81 | if (numConfigs <= 0) {
82 | configSpec = new int[] {
83 | EGL10.EGL_RED_SIZE, r, EGL10.EGL_GREEN_SIZE, g, EGL10.EGL_BLUE_SIZE, b,
84 | EGL10.EGL_ALPHA_SIZE, a, EGL10.EGL_DEPTH_SIZE, depth, EGL10.EGL_STENCIL_SIZE, stencil,
85 | EGL10.EGL_RENDERABLE_TYPE, renderableType, EGL10.EGL_NONE
86 | };
87 |
88 | if (!egl.eglChooseConfig(display, configSpec, null, 0, value)) {
89 | throw new IllegalArgumentException("3rd eglChooseConfig failed");
90 | }
91 | numConfigs = value[0];
92 |
93 | if (numConfigs <= 0) {
94 | throw new IllegalArgumentException("No configs match configSpec");
95 | }
96 | } else {
97 | usesCoverageAa = true;
98 | Log.i(TAG, "usesCoverageAa");
99 | }
100 | }
101 |
102 | // Get all matching configurations.
103 | EGLConfig[] configs = new EGLConfig[numConfigs];
104 | if (!egl.eglChooseConfig(display, configSpec, configs, numConfigs, value)) {
105 | throw new IllegalArgumentException("data eglChooseConfig failed");
106 | }
107 |
108 | // CAUTION! eglChooseConfigs returns configs with higher bit depth
109 | // first: Even though we asked for rgb565 configurations, rgb888
110 | // configurations are considered to be "better" and returned first.
111 | // You need to explicitly filter the data returned by eglChooseConfig!
112 | int index = -1;
113 | for (int i = 0; i < configs.length; ++i) {
114 | if (findConfigAttrib(egl, display, configs[i], EGL10.EGL_RED_SIZE, 0) == r) {
115 | index = i;
116 | break;
117 | }
118 | }
119 | if (index == -1) {
120 | Log.i(TAG, "Did not find sane config, using first");
121 | }
122 | EGLConfig config = configs.length > 0 ? configs[index] : null;
123 | if (config == null) {
124 | throw new IllegalArgumentException("No config chosen");
125 | }
126 | return config;
127 | }
128 |
129 | private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config, int attribute,
130 | int defaultValue) {
131 | if (egl.eglGetConfigAttrib(display, config, attribute, value)) {
132 | return value[0];
133 | }
134 | return defaultValue;
135 | }
136 |
137 | public boolean usesCoverageAa() {
138 | return usesCoverageAa;
139 | }
140 | }
--------------------------------------------------------------------------------
/sample/src/main/java/jp/wasabeef/roundedtextureview/RoundedTextureView.java:
--------------------------------------------------------------------------------
1 | package jp.wasabeef.roundedtextureview;
2 |
3 | import android.content.Context;
4 | import android.graphics.Point;
5 | import android.graphics.RectF;
6 | import android.graphics.SurfaceTexture;
7 | import android.opengl.GLES20;
8 | import android.opengl.Matrix;
9 | import android.support.annotation.NonNull;
10 | import android.util.AttributeSet;
11 | import android.util.Log;
12 | import java.nio.ByteBuffer;
13 | import java.nio.ByteOrder;
14 | import java.nio.FloatBuffer;
15 | import java.nio.ShortBuffer;
16 | import javax.microedition.khronos.egl.EGLConfig;
17 | import javax.microedition.khronos.opengles.GL10;
18 |
19 | /**
20 | * To set adjust the rounded corners use {@link #setCornerRadius(float, float, float, float)}.
21 | *
22 | * Created by Daichi Furiya on 2015/10/09.
23 | */
24 | public class RoundedTextureView extends GLTextureView {
25 |
26 | public interface SurfaceProvider {
27 | void onSurfaceCreated(SurfaceTexture surface);
28 | }
29 |
30 | private GLRenderer glRenderer;
31 |
32 | public RoundedTextureView(Context context) {
33 | this(context, null);
34 | }
35 |
36 | public RoundedTextureView(Context context, AttributeSet attrs) {
37 | super(context, attrs);
38 | setEGLContextClientVersion(2);
39 |
40 | MultiSampleEGLConfigChooser chooser = new MultiSampleEGLConfigChooser();
41 | setEGLConfigChooser(chooser);
42 | glRenderer = new GLRenderer(this);
43 | glRenderer.setUsesCoverageAa(chooser.usesCoverageAa());
44 | setRenderer(glRenderer);
45 | setRenderMode(RENDERMODE_WHEN_DIRTY);
46 | setOpaque(false);
47 | }
48 |
49 | public void setCornerRadius(float radius) {
50 | setCornerRadius(radius, radius, radius, radius);
51 | }
52 |
53 | public void setCornerRadius(float topLeft, float topRight, float bottomRight, float bottomLeft) {
54 | glRenderer.setCornerRadius(topLeft, topRight, bottomRight, bottomLeft);
55 | }
56 |
57 | public void setSurfaceProvider(SurfaceProvider provider) {
58 | glRenderer.setSurfaceProvider(provider);
59 | }
60 |
61 | private static class GLRenderer
62 | implements Renderer, SurfaceTexture.OnFrameAvailableListener {
63 |
64 | private static String TAG = GLRenderer.class.getSimpleName();
65 |
66 | private static final int FLOAT_SIZE_BYTES = 4;
67 | private static final int SHORT_SIZE_BYTES = 2;
68 | private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
69 | private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
70 | private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
71 |
72 | private static final String vertexShader = "" +
73 | "uniform mat4 uMVPMatrix;\n" +
74 | "uniform mat4 uSTMatrix;\n" +
75 | "attribute vec4 aPosition;\n" +
76 | "attribute vec4 aTextureCoord;\n" +
77 | "varying vec2 vTextureCoord;\n" +
78 | "void main() {\n" +
79 | " gl_Position = uMVPMatrix * aPosition;\n" +
80 | " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
81 | "}\n";
82 |
83 | private static final String fragmentShader = "" +
84 | "#extension GL_OES_EGL_image_external : require\n" +
85 | "precision mediump float;\n" +
86 | "varying vec2 vTextureCoord;\n" +
87 | "uniform samplerExternalOES sTexture;\n" +
88 | "void main() {\n" +
89 | " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
90 | "}\n";
91 |
92 | private float[] mvpMatrix = new float[16];
93 | private float[] stMatrix = new float[16];
94 |
95 | private int program;
96 | private int textureID;
97 | private int mvpMatrixHandle;
98 | private int ustMatrixHandle;
99 | private int aPositionHandle;
100 | private int aTextureHandle;
101 |
102 | private static final int GL_TEXTURE_EXTERNAL_OES = 0x00008d65;
103 |
104 | private static final int GL_COVERAGE_BUFFER_BIT_NV = 0x8000;
105 |
106 | private final GLTextureView glTextureView;
107 | private SurfaceTexture surfaceTexture;
108 | private boolean updateSurface = false;
109 |
110 | private float[] triangleVerticesData;
111 | private short[] triangleIndicesData;
112 | private FloatBuffer triangleVertices;
113 | private ShortBuffer triangleIndices;
114 | private RectF roundRadius = new RectF();
115 | private GLRoundedGeometry roundedGeometry;
116 | private final Point viewPortSize = new Point();
117 | private final RectF viewPortGLBounds;
118 | private boolean usesCoverageAa = false;
119 | private SurfaceProvider surfaceProvider;
120 |
121 | public GLRenderer(@NonNull GLTextureView view) {
122 | this(view, new GLRoundedGeometry(), new RectF(-1, 1, 1, -1));
123 | }
124 |
125 | public GLRenderer(@NonNull GLTextureView view, @NonNull GLRoundedGeometry roundedGeometry,
126 | @NonNull RectF viewPortGLBounds) {
127 | glTextureView = view;
128 | this.roundedGeometry = roundedGeometry;
129 | this.viewPortGLBounds = viewPortGLBounds;
130 | viewPortSize.set(1, 1);
131 |
132 | Matrix.setIdentityM(stMatrix, 0);
133 | }
134 |
135 | public void setUsesCoverageAa(boolean usesCoverageAa) {
136 | this.usesCoverageAa = usesCoverageAa;
137 | }
138 |
139 | public void setCornerRadius(float topLeft, float topRight, float bottomRight,
140 | float bottomLeft) {
141 | roundRadius.left = topLeft;
142 | roundRadius.top = topRight;
143 | roundRadius.right = bottomRight;
144 | roundRadius.bottom = bottomLeft;
145 | if (viewPortSize.x > 1) {
146 | updateVertexData();
147 | }
148 | }
149 |
150 | private void updateVertexData() {
151 | final GLRoundedGeometry.GeometryArrays arrays =
152 | roundedGeometry.generateVertexData(roundRadius, viewPortGLBounds, viewPortSize);
153 | triangleVerticesData = arrays.triangleVertices;
154 | triangleIndicesData = arrays.triangleIndices;
155 | if (triangleVertices != null) {
156 | triangleVertices.clear();
157 | } else {
158 | triangleVertices = ByteBuffer.allocateDirect(triangleVerticesData.length * FLOAT_SIZE_BYTES)
159 | .order(ByteOrder.nativeOrder())
160 | .asFloatBuffer();
161 | }
162 | if (triangleIndices != null) {
163 | triangleIndices.clear();
164 | } else {
165 | triangleIndices = ByteBuffer.allocateDirect(triangleIndicesData.length * SHORT_SIZE_BYTES)
166 | .order(ByteOrder.nativeOrder())
167 | .asShortBuffer();
168 | }
169 | triangleVertices.put(triangleVerticesData).position(0);
170 | triangleIndices.put(triangleIndicesData).position(0);
171 | }
172 |
173 | public void setSurfaceProvider(SurfaceProvider provider) {
174 | surfaceProvider = provider;
175 | }
176 |
177 | @Override public void onSurfaceChanged(GL10 glUnused, int width, int height) {
178 | GLES20.glViewport(0, 0, width, height);
179 | viewPortSize.set(width, height);
180 | updateVertexData();
181 | }
182 |
183 | @Override public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
184 | program = createProgram(vertexShader, fragmentShader);
185 | if (program == 0) {
186 | return;
187 | }
188 | aPositionHandle = GLES20.glGetAttribLocation(program, "aPosition");
189 | checkGlError("glGetAttribLocation aPosition");
190 | if (aPositionHandle == -1) {
191 | throw new RuntimeException("Could not get attrib location for aPosition");
192 | }
193 | aTextureHandle = GLES20.glGetAttribLocation(program, "aTextureCoord");
194 | checkGlError("glGetAttribLocation aTextureCoord");
195 | if (aTextureHandle == -1) {
196 | throw new RuntimeException("Could not get attrib location for aTextureCoord");
197 | }
198 |
199 | mvpMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix");
200 | checkGlError("glGetUniformLocation uMVPMatrix");
201 | if (mvpMatrixHandle == -1) {
202 | throw new RuntimeException("Could not get attrib location for uMVPMatrix");
203 | }
204 |
205 | ustMatrixHandle = GLES20.glGetUniformLocation(program, "uSTMatrix");
206 | checkGlError("glGetUniformLocation uSTMatrix");
207 | if (ustMatrixHandle == -1) {
208 | throw new RuntimeException("Could not get attrib location for uSTMatrix");
209 | }
210 |
211 | int[] textures = new int[1];
212 | GLES20.glGenTextures(1, textures, 0);
213 |
214 | textureID = textures[0];
215 | GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureID);
216 | checkGlError("glBindTexture textureID");
217 |
218 | GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
219 | GLES20.GL_LINEAR);
220 | GLES20.glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
221 | GLES20.GL_LINEAR);
222 |
223 | surfaceTexture = new SurfaceTexture(textureID);
224 | surfaceTexture.setOnFrameAvailableListener(this);
225 | if (surfaceProvider != null) {
226 | surfaceProvider.onSurfaceCreated(surfaceTexture);
227 | }
228 |
229 | synchronized (this) {
230 | updateSurface = false;
231 | }
232 | }
233 |
234 | @Override public void onDrawFrame(GL10 glUnused) {
235 | synchronized (this) {
236 | if (updateSurface) {
237 | surfaceTexture.updateTexImage();
238 | surfaceTexture.getTransformMatrix(stMatrix);
239 | updateSurface = false;
240 | }
241 | }
242 |
243 | GLES20.glClearColor(.0f, .0f, .0f, .0f);
244 | int clearMask = GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT;
245 | if (usesCoverageAa) {
246 | // Tegra weirdness
247 | clearMask |= GL_COVERAGE_BUFFER_BIT_NV;
248 | }
249 | GLES20.glClear(clearMask);
250 |
251 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
252 | GLES20.GL_CLAMP_TO_EDGE);
253 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
254 | GLES20.GL_CLAMP_TO_EDGE);
255 |
256 | GLES20.glUseProgram(program);
257 | checkGlError("glUseProgram");
258 |
259 | GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
260 | GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureID);
261 |
262 | triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
263 | GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT, false,
264 | TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
265 | checkGlError("glVertexAttribPointer aPosition");
266 | GLES20.glEnableVertexAttribArray(aPositionHandle);
267 | checkGlError("glEnableVertexAttribArray aPositionHandle");
268 |
269 | triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
270 | GLES20.glVertexAttribPointer(aTextureHandle, 3, GLES20.GL_FLOAT, false,
271 | TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
272 | checkGlError("glVertexAttribPointer aTextureHandle");
273 | GLES20.glEnableVertexAttribArray(aTextureHandle);
274 | checkGlError("glEnableVertexAttribArray aTextureHandle");
275 |
276 | Matrix.setIdentityM(mvpMatrix, 0);
277 | Matrix.scaleM(mvpMatrix, 0, 1f, 1f, 1f);
278 | GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false, mvpMatrix, 0);
279 | GLES20.glUniformMatrix4fv(ustMatrixHandle, 1, false, stMatrix, 0);
280 |
281 | GLES20.glDrawElements(GLES20.GL_TRIANGLES, triangleIndicesData.length, GL10.GL_UNSIGNED_SHORT,
282 | triangleIndices);
283 |
284 | checkGlError("glDrawElements");
285 | GLES20.glFinish();
286 | }
287 |
288 | @Override synchronized public void onFrameAvailable(SurfaceTexture surfaceTexture) {
289 | updateSurface = true;
290 | glTextureView.requestRender();
291 | }
292 |
293 | private static int loadShader(int shaderType, String source) {
294 | int shader = GLES20.glCreateShader(shaderType);
295 | if (shader != 0) {
296 | GLES20.glShaderSource(shader, source);
297 | GLES20.glCompileShader(shader);
298 | int[] compiled = new int[1];
299 | GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
300 | if (compiled[0] == 0) {
301 | Log.e(TAG, "Could not compile shader " + shaderType + ":");
302 | Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
303 | GLES20.glDeleteShader(shader);
304 | shader = 0;
305 | }
306 | }
307 | return shader;
308 | }
309 |
310 | private static int createProgram(String vertexSource, String fragmentSource) {
311 | int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
312 | if (vertexShader == 0) {
313 | return 0;
314 | }
315 | int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
316 | if (pixelShader == 0) {
317 | return 0;
318 | }
319 |
320 | int program = GLES20.glCreateProgram();
321 | if (program != 0) {
322 | GLES20.glAttachShader(program, vertexShader);
323 | checkGlError("glAttachShader");
324 | GLES20.glAttachShader(program, pixelShader);
325 | checkGlError("glAttachShader");
326 | GLES20.glLinkProgram(program);
327 | int[] linkStatus = new int[1];
328 | GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
329 | if (linkStatus[0] != GLES20.GL_TRUE) {
330 | Log.e(TAG, "Could not link program: ");
331 | Log.e(TAG, GLES20.glGetProgramInfoLog(program));
332 | GLES20.glDeleteProgram(program);
333 | program = 0;
334 | }
335 | }
336 | return program;
337 | }
338 |
339 | private static void checkGlError(String op) {
340 | int error;
341 | while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
342 | Log.e(TAG, op + ": glError " + error);
343 | throw new RuntimeException(op + ": glError " + error);
344 | }
345 | }
346 | }
347 | }
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasabeef/android-RoundedTextureView/d234fe99ec208600efd569e6613a9ea9d98e3b15/sample/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasabeef/android-RoundedTextureView/d234fe99ec208600efd569e6613a9ea9d98e3b15/sample/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasabeef/android-RoundedTextureView/d234fe99ec208600efd569e6613a9ea9d98e3b15/sample/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasabeef/android-RoundedTextureView/d234fe99ec208600efd569e6613a9ea9d98e3b15/sample/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wasabeef/android-RoundedTextureView/d234fe99ec208600efd569e6613a9ea9d98e3b15/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | RoundedTextureView
3 |
4 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':sample'
2 |
--------------------------------------------------------------------------------