├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── LICENSE ├── README-en.md ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── io │ │ └── github │ │ └── kenneycode │ │ └── fusion │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── test.mp4 │ │ ├── test.png │ │ ├── test_image_0.png │ │ └── test_lut.png │ ├── java │ │ └── io │ │ │ └── github │ │ │ └── kenneycode │ │ │ └── fusion │ │ │ ├── demo │ │ │ ├── MainActivity.kt │ │ │ ├── SampleActivity.kt │ │ │ └── fragment │ │ │ │ ├── SampleBasicUsage.kt │ │ │ │ ├── SampleCamera.kt │ │ │ │ ├── SampleGLSurfaceViewUsage0.kt │ │ │ │ ├── SampleImageOffscreenRender.kt │ │ │ │ ├── SampleMVPMatrix.kt │ │ │ │ ├── SampleVideo.kt │ │ │ │ └── SampleVideoOffscreenRender.kt │ │ │ └── util │ │ │ └── Utils.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_sample.xml │ │ ├── fragment_sample_common.xml │ │ ├── fragment_sample_fusion_gl_texture_view.xml │ │ ├── fragment_sample_gl_surface_view.xml │ │ ├── fragment_sample_offscreen.xml │ │ └── layout_sample_list_item.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── io │ └── github │ └── kenneycode │ └── fusion │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── libfusion ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── io │ │ └── github │ │ └── kenneycode │ │ └── fusion │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── io │ │ │ └── github │ │ │ └── kenneycode │ │ │ └── fusion │ │ │ ├── common │ │ │ ├── Common.kt │ │ │ ├── Constants.kt │ │ │ ├── DataKeys.kt │ │ │ ├── Ref.kt │ │ │ ├── Shader.kt │ │ │ └── Size.kt │ │ │ ├── context │ │ │ ├── FusionEGL.kt │ │ │ ├── FusionGLThread.kt │ │ │ ├── GLContext.kt │ │ │ ├── InPlaceGLContext.kt │ │ │ └── SimpleGLContext.kt │ │ │ ├── framebuffer │ │ │ ├── FrameBuffer.kt │ │ │ └── FrameBufferPool.kt │ │ │ ├── input │ │ │ ├── FusionCamera.kt │ │ │ ├── FusionImage.kt │ │ │ ├── FusionVideo.kt │ │ │ ├── FusionVideoDecoder.kt │ │ │ └── InputReceiver.kt │ │ │ ├── output │ │ │ ├── FusionBitmap.kt │ │ │ ├── FusionVideoEncoder.kt │ │ │ └── FusionView.kt │ │ │ ├── parameter │ │ │ ├── AttributeParameter.kt │ │ │ ├── BitmapTexture2DParameter.kt │ │ │ ├── FloatArrayParameter.kt │ │ │ ├── FloatParameter.kt │ │ │ ├── IntParameter.kt │ │ │ ├── Mat4Parameter.kt │ │ │ ├── OESTextureParameter.kt │ │ │ ├── Parameter.kt │ │ │ ├── Texture2DParameter.kt │ │ │ └── UniformParameter.kt │ │ │ ├── process │ │ │ ├── RenderChain.kt │ │ │ ├── RenderGraph.kt │ │ │ └── RenderPipeline.kt │ │ │ ├── program │ │ │ ├── GLProgram.kt │ │ │ └── GLProgramPool.kt │ │ │ ├── renderer │ │ │ ├── CropRenderer.kt │ │ │ ├── DisplayRenderer.kt │ │ │ ├── GLRenderer.kt │ │ │ ├── GaussianBlurRenderer.kt │ │ │ ├── LUTRenderer.kt │ │ │ ├── OESConvertRenderer.kt │ │ │ ├── Renderer.kt │ │ │ ├── ScaleRenderer.kt │ │ │ ├── SimpleRenderer.kt │ │ │ └── TextureRenderer.kt │ │ │ ├── texture │ │ │ ├── Texture.kt │ │ │ └── TexturePool.kt │ │ │ └── util │ │ │ ├── BitmapUtil.kt │ │ │ ├── GLUtil.kt │ │ │ └── Util.kt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── io │ └── github │ └── kenneycode │ └── fusion │ └── ExampleUnitTest.kt └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # IntelliJ 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/tasks.xml 39 | .idea/gradle.xml 40 | .idea/assetWizardSettings.xml 41 | .idea/dictionaries 42 | .idea/libraries 43 | .idea/caches 44 | 45 | # Keystore files 46 | # Uncomment the following line if you do not want to check your keystore files in. 47 | #*.jks 48 | 49 | # External native build folder generated in Android Studio 2.2 and later 50 | .externalNativeBuild 51 | 52 | # Google Services (e.g. APIs or Firebase) 53 | google-services.json 54 | 55 | # Freeline 56 | freeline.py 57 | freeline/ 58 | freeline_project_description.json 59 | 60 | # fastlane 61 | fastlane/report.xml 62 | fastlane/Preview.html 63 | fastlane/screenshots 64 | fastlane/test_output 65 | fastlane/readme.md 66 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 |
35 | 36 | 37 | 38 | xmlns:android 39 | 40 | ^$ 41 | 42 | 43 | 44 |
45 |
46 | 47 | 48 | 49 | xmlns:.* 50 | 51 | ^$ 52 | 53 | 54 | BY_NAME 55 | 56 |
57 |
58 | 59 | 60 | 61 | .*:id 62 | 63 | http://schemas.android.com/apk/res/android 64 | 65 | 66 | 67 |
68 |
69 | 70 | 71 | 72 | .*:name 73 | 74 | http://schemas.android.com/apk/res/android 75 | 76 | 77 | 78 |
79 |
80 | 81 | 82 | 83 | name 84 | 85 | ^$ 86 | 87 | 88 | 89 |
90 |
91 | 92 | 93 | 94 | style 95 | 96 | ^$ 97 | 98 | 99 | 100 |
101 |
102 | 103 | 104 | 105 | .* 106 | 107 | ^$ 108 | 109 | 110 | BY_NAME 111 | 112 |
113 |
114 | 115 | 116 | 117 | .* 118 | 119 | http://schemas.android.com/apk/res/android 120 | 121 | 122 | 123 |
124 |
125 | 126 | 127 | 128 | .* 129 | 130 | .* 131 | 132 | 133 | BY_NAME 134 | 135 |
136 |
137 |
138 |
139 |
140 |
-------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 kenney 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README-en.md: -------------------------------------------------------------------------------- 1 | ### What is fusion? 2 | 3 | `fusion` is an `OpenGL ES` effects rendering library on `Android` written by `kotlin`, which is similar to `GPUImage` on `IOS`. 4 | 5 | - `fusion` highly abstracts the ` input / output` and rendering process, hides the complex and trivial `OpenGL API`. Developers without `OpenGL` knowledge can use `fusion` easily 6 | - organize and manage the renderers by `chain/graph` through `RenderChain / RenderGraph`, and unify input and output through `RrenderPipline`. 7 | - support image/video/camera as input, and video decode/encode, camera logic. 8 | - support image/video offscreen rendering to save the effects. 9 | - support auto reuse `texture`/`frame buffer`/`program` to reduce the usage of graphic memory. 10 | - support `GL thread` and `EGL` to easily create `GL` enviroment. 11 | - support display the rendered result by the built-in view, and also support `GLSurfaceView` . 12 | - supports common rendering effects, and developers can inherit `SimpleRenderer/RenderChain/RenderGraph` to achieve complex effects, or implement `Renderer` interface. 13 | 14 | continuously updating... 15 | 16 | ### Integration: 17 | 18 | add the following code to the root `gradle` of your project: 19 | 20 | ``` 21 | allprojects { 22 | repositories { 23 | ... 24 | maven { url 'https://www.jitpack.io' } 25 | } 26 | } 27 | ``` 28 | 29 | then add the following code to the `gradle` of your module: 30 | 31 | ``` 32 | dependencies { 33 | implementation 'com.github.kenneycode:fusion:1.2.0' 34 | } 35 | ``` 36 | 37 | ### Basic usage of image rendering 38 | 39 | ```java 40 | // create RenderChain and add some renderers 41 | val renderer = RenderChain() 42 | .addRenderer(ScaleRenderer().apply { setFlip(false, true); setScale(0.8f) }) 43 | .addRenderer(CropRenderer().apply { setCropRect(0.1f, 0.9f, 0.8f, 0.2f) }) 44 | .addRenderer(LUTRenderer().apply { setLUTImage(Util.decodeBitmapFromAssets("test_lut.png")!!); setLUTStrength(0.8f) }) 45 | .addRenderer(GaussianBlurRenderer().apply { setBlurRadius(10) }) 46 | 47 | // create RenderPipeline,connecting input, renderer and ouput 48 | renderPipeline = RenderPipeline 49 | .input(FusionImage(Util.decodeBitmapFromAssets("test.png")!!)) 50 | .renderWith(renderer) 51 | .useContext(fusionView) 52 | .output(fusionView) 53 | 54 | // start processing 55 | renderPipeline.start() 56 | ``` 57 | 58 | ### Basic usage of video rendering 59 | 60 | ```java 61 | // create RenderChain and add some renderers 62 | val renderer = RenderChain() 63 | .addRenderer(OES2RGBARenderer()) 64 | .addRenderer(LUTRenderer().apply { setLUTImage(Util.decodeBitmapFromAssets("test_lut.png")!!); setLUTStrength(0.8f) }) 65 | .addRenderer(GaussianBlurRenderer().apply { setBlurRadius(10) }) 66 | 67 | // create RenderPipeline,connecting input, renderer and ouput 68 | renderPipeline = RenderPipeline 69 | .input(FusionVideo("/sdcard/test.mp4")) 70 | .renderWith(renderer) 71 | .useContext(fusionView) 72 | .output(fusionView) 73 | 74 | // start processing 75 | renderPipeline.start() 76 | ``` 77 | ### Basic usage of camera rendering 78 | 79 | ```kotlin 80 | // create RenderChain and add some renderers 81 | val renderer = RenderChain() 82 | .addRenderer(OESConvertRenderer()) 83 | .addRenderer(LUTRenderer().apply { setLUTImage(BitmapUtil.decodeBitmapFromAssets("test_lut.png")!!); setLUTStrength(0.8f) }) 84 | 85 | // configs for constructing fusion camera 86 | val fusionCameraConfig = FusionCamera.Config().apply { 87 | windowRotation = activity!!.windowManager.defaultDisplay.rotation 88 | desiredPreviewSize = Size(1080, 1920) 89 | } 90 | 91 | // create RenderPipeline,connecting input, renderer and ouput 92 | renderPipeline = RenderPipeline 93 | .input(FusionCamera(fusionCameraConfig)) 94 | .renderWith(renderer) 95 | .useContext(fusionView) 96 | .output(fusionView) 97 | 98 | // start processing 99 | renderPipeline.start() 100 | ``` 101 | for more usages please see the demo. 102 | 103 | thank you! -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [english README please click here](./README-en.md) 2 | 3 | ### fusion是什么? 4 | 5 | `Android`上的`OpenGL ES`特效渲染库,类似`IOS`上的 `GPUImage`. 6 | 7 | - 高度抽象了输入输出及渲染过程,隐藏了复杂繁琐的`OpenGL API`,即使不会`OpenGL`也能轻松上手。 8 | - 统一渲染过程,通过`RenderChain`/`RenderGraph`将渲染器按`chain`/`graph`进行组织管理,并通过`RenderPipline`统一输入输出。 9 | - 支持图片/视频/相机输入,自带视频编解码和相机逻辑。 10 | - 支持图片/视频离屏渲染用于保存。 11 | - 支持`texture/frame buffer/program`自动回收复用。 12 | - 封装了`GL`线程及`EGL`环境,可通过`GLThread`及`EGL`帮助快速创建`GL`环境。 13 | - 自带渲染显示`View`,也可以使用系统的`GLSurfaceView`。 14 | - 自带常用渲染效果,可继承`SimpleRenderer/RenderChain/RenderGraph`实现复杂效果,也可自行实现`Renderer`接口。 15 | 16 | 持续更新中... 17 | 18 | ### 引入方法 19 | 20 | 根`gradle`中添加: 21 | 22 | ``` 23 | allprojects { 24 | repositories { 25 | ... 26 | maven { url 'https://www.jitpack.io' } 27 | } 28 | } 29 | ``` 30 | 31 | 要引入的`module`中添加: 32 | 33 | ``` 34 | dependencies { 35 | implementation 'com.github.kenneycode:fusion:1.2.0' 36 | } 37 | ``` 38 | 39 | ### 图片渲染基本用法 40 | 41 | ```kotlin 42 | // 创建RenderChain并添加一些renderer 43 | val renderer = RenderChain() 44 | .addRenderer(ScaleRenderer().apply { setFlip(false, true); setScale(0.8f) }) 45 | .addRenderer(CropRenderer().apply { setCropRect(0.1f, 0.9f, 0.8f, 0.2f) }) 46 | .addRenderer(LUTRenderer().apply { setLUTImage(Util.decodeBitmapFromAssets("test_lut.png")!!); setLUTStrength(0.8f) }) 47 | .addRenderer(GaussianBlurRenderer().apply { setBlurRadius(10) }) 48 | 49 | // 创建RenderPipeline,连接输入、渲染器与输出 50 | renderPipeline = RenderPipeline 51 | .input(FusionImage(Util.decodeBitmapFromAssets("test.png")!!)) 52 | .renderWith(renderer) 53 | .useContext(fusionView) 54 | .output(fusionView) 55 | 56 | // 开始处理 57 | renderPipeline.start() 58 | ``` 59 | 60 | ### 视频渲染基本用法 61 | 62 | ```kotlin 63 | // 创建RenderChain并添加一些renderer 64 | val renderer = RenderChain() 65 | .addRenderer(OES2RGBARenderer()) 66 | .addRenderer(LUTRenderer().apply { setLUTImage(Util.decodeBitmapFromAssets("test_lut.png")!!); setLUTStrength(0.8f) }) 67 | .addRenderer(GaussianBlurRenderer().apply { setBlurRadius(10) }) 68 | 69 | // 创建RenderPipeline,连接输入、渲染器与输出 70 | renderPipeline = RenderPipeline 71 | .input(FusionVideo("/sdcard/test.mp4")) 72 | .renderWith(renderer) 73 | .useContext(fusionView) 74 | .output(fusionView) 75 | 76 | // 开始处理 77 | renderPipeline.start() 78 | ``` 79 | ### 相机渲染基本用法 80 | 81 | ```kotlin 82 | // 创建RenderChain并添加一些renderer 83 | val renderer = RenderChain() 84 | .addRenderer(OESConvertRenderer()) 85 | .addRenderer(LUTRenderer().apply { setLUTImage(BitmapUtil.decodeBitmapFromAssets("test_lut.png")!!); setLUTStrength(0.8f) }) 86 | 87 | // 相机配置 88 | val fusionCameraConfig = FusionCamera.Config().apply { 89 | windowRotation = activity!!.windowManager.defaultDisplay.rotation 90 | desiredPreviewSize = Size(1080, 1920) 91 | } 92 | 93 | // 创建RenderPipeline,连接输入、渲染器与输出 94 | renderPipeline = RenderPipeline 95 | .input(FusionCamera(fusionCameraConfig)) 96 | .renderWith(renderer) 97 | .useContext(fusionView) 98 | .output(fusionView) 99 | 100 | // 开始处理 101 | renderPipeline.start() 102 | ``` 103 | 104 | 更多用法请查看demo。 105 | 106 | 谢谢! -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android-extensions' 3 | apply plugin: 'kotlin-android' 4 | 5 | android { 6 | compileSdkVersion 28 7 | defaultConfig { 8 | applicationId "io.github.kenneycode.fusion.demo" 9 | minSdkVersion 18 10 | targetSdkVersion 28 11 | versionCode 1 12 | versionName "1.0" 13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | implementation fileTree(dir: 'libs', include: ['*.jar']) 25 | implementation 'androidx.appcompat:appcompat:1.0.2' 26 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 27 | implementation project(path: ':libfusion') 28 | implementation 'com.android.support:recyclerview-v7:28.0.0' 29 | testImplementation 'junit:junit:4.12' 30 | androidTestImplementation 'androidx.test:runner:1.1.1' 31 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 32 | compile "androidx.core:core-ktx:+" 33 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 34 | } 35 | repositories { 36 | mavenCentral() 37 | } 38 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/github/kenneycode/fusion/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * @see [Testing documentation](http://d.android.com/tools/testing) 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | 23 | assertEquals("io.github.kenneycode.fusion.demo", appContext.packageName) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/assets/test.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenneycode/fusion/8b2959cf39fa7c988a3e3758686027cede1d1007/app/src/main/assets/test.mp4 -------------------------------------------------------------------------------- /app/src/main/assets/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenneycode/fusion/8b2959cf39fa7c988a3e3758686027cede1d1007/app/src/main/assets/test.png -------------------------------------------------------------------------------- /app/src/main/assets/test_image_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenneycode/fusion/8b2959cf39fa7c988a3e3758686027cede1d1007/app/src/main/assets/test_image_0.png -------------------------------------------------------------------------------- /app/src/main/assets/test_lut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenneycode/fusion/8b2959cf39fa7c988a3e3758686027cede1d1007/app/src/main/assets/test_lut.png -------------------------------------------------------------------------------- /app/src/main/java/io/github/kenneycode/fusion/demo/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.demo 2 | 3 | import android.Manifest 4 | import android.content.Intent 5 | import androidx.appcompat.app.AppCompatActivity 6 | 7 | import android.os.Bundle 8 | import android.view.LayoutInflater 9 | import android.view.View 10 | import android.view.ViewGroup 11 | import android.widget.Button 12 | import androidx.recyclerview.widget.LinearLayoutManager 13 | import androidx.recyclerview.widget.RecyclerView 14 | import io.github.kenneycode.fusion.demo.SimpleActivity.Companion.KEY_SAMPLE_INDEX 15 | import io.github.kenneycode.fusion.util.BitmapUtil 16 | import androidx.core.app.ActivityCompat 17 | import android.content.pm.PackageManager 18 | import androidx.core.content.ContextCompat 19 | import io.github.kenneycode.fusion.util.Utils 20 | 21 | 22 | 23 | 24 | /** 25 | * 26 | * Coded by kenney 27 | * 28 | * http://www.github.com/kenneycode/fusion 29 | * 30 | * fusion demo 31 | * 32 | */ 33 | 34 | class MainActivity : AppCompatActivity() { 35 | 36 | override fun onCreate(savedInstanceState: Bundle?) { 37 | super.onCreate(savedInstanceState) 38 | setContentView(R.layout.activity_main) 39 | BitmapUtil.context = applicationContext 40 | Utils.copyAssetsFiles(this, "test.mp4", "/sdcard/test.mp4") 41 | checkPermissions() 42 | val samplesList = findViewById(R.id.list) 43 | val layoutManager = LinearLayoutManager(this) 44 | layoutManager.orientation = LinearLayoutManager.VERTICAL 45 | samplesList.layoutManager = layoutManager 46 | samplesList.adapter = MyAdapter() 47 | 48 | } 49 | 50 | 51 | private fun checkPermissions() { 52 | val permissions = mutableListOf() 53 | if (ContextCompat.checkSelfPermission(application, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { 54 | permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE) 55 | } 56 | if (ContextCompat.checkSelfPermission(application, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { 57 | permissions.add(Manifest.permission.CAMERA) 58 | } 59 | if (permissions.isNotEmpty()) { 60 | ActivityCompat.requestPermissions(this, permissions.toTypedArray(), 1) 61 | } 62 | } 63 | 64 | inner class MyAdapter : RecyclerView.Adapter() { 65 | 66 | override fun onCreateViewHolder(p0: ViewGroup, p1: Int): VH { 67 | val view = LayoutInflater.from(p0.context).inflate(R.layout.layout_sample_list_item, p0, false) 68 | return VH(view) 69 | } 70 | 71 | override fun getItemCount(): Int { 72 | return SimpleActivity.samples.size 73 | } 74 | 75 | override fun onBindViewHolder(p0: VH, p1: Int) { 76 | p0.button.text = getString(SimpleActivity.samples[p1].first!!) 77 | p0.button.setOnClickListener { 78 | val intent = Intent(this@MainActivity, SimpleActivity::class.java) 79 | intent.putExtra(KEY_SAMPLE_INDEX, p1) 80 | this@MainActivity.startActivity(intent) 81 | } 82 | } 83 | 84 | } 85 | 86 | inner class VH(itemView : View) : RecyclerView.ViewHolder(itemView) { 87 | var button : Button = itemView.findViewById(R.id.button) 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/kenneycode/fusion/demo/SampleActivity.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.demo 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.core.util.Pair 6 | import io.github.kenneycode.fusion.demo.fragment.* 7 | 8 | /** 9 | * 10 | * Coded by kenney 11 | * 12 | * http://www.github.com/kenneycode/fusion 13 | * 14 | * demo list activity 15 | * 16 | */ 17 | 18 | class SimpleActivity : AppCompatActivity() { 19 | 20 | companion object { 21 | 22 | const val KEY_SAMPLE_INDEX = "KEY_SAMPLE_INDEX" 23 | 24 | val samples = listOf( 25 | Pair(R.string.sample_0, SampleBasicUsage::class.java), 26 | Pair(R.string.sample_1, SampleGLSurfaceViewUsage0::class.java), 27 | Pair(R.string.sample_2, SampleMVPMatrix::class.java), 28 | Pair(R.string.sample_3, SampleVideo::class.java), 29 | Pair(R.string.sample_4, SampleImageOffscreenRender::class.java), 30 | Pair(R.string.sample_5, SampleVideoOffscreenRender::class.java), 31 | Pair(R.string.sample_6, SampleCamera::class.java) 32 | ) 33 | 34 | } 35 | 36 | override fun onCreate(savedInstanceState: Bundle?) { 37 | super.onCreate(savedInstanceState) 38 | setContentView(R.layout.activity_sample) 39 | val sampleIndex = intent.getIntExtra(KEY_SAMPLE_INDEX, -1) 40 | title = getString(samples[sampleIndex].first!!) 41 | val transaction = supportFragmentManager.beginTransaction() 42 | transaction.replace(R.id.content, samples[sampleIndex].second!!.newInstance()) 43 | transaction.commit() 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/kenneycode/fusion/demo/fragment/SampleBasicUsage.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.demo.fragment 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | import io.github.kenneycode.fusion.demo.R 9 | import io.github.kenneycode.fusion.util.BitmapUtil 10 | import io.github.kenneycode.fusion.framebuffer.FrameBufferPool 11 | 12 | import io.github.kenneycode.fusion.input.FusionImage 13 | import io.github.kenneycode.fusion.process.RenderChain 14 | import io.github.kenneycode.fusion.process.RenderPipeline 15 | import io.github.kenneycode.fusion.program.GLProgramPool 16 | import io.github.kenneycode.fusion.renderer.* 17 | import io.github.kenneycode.fusion.texture.TexturePool 18 | import kotlinx.android.synthetic.main.fragment_sample_fusion_gl_texture_view.* 19 | 20 | /** 21 | * 22 | * Coded by kenney 23 | * 24 | * http://www.github.com/kenneycode/fusion 25 | * 26 | * 基本用法 27 | * 28 | */ 29 | 30 | class SampleBasicUsage : Fragment() { 31 | 32 | private lateinit var renderPipeline: RenderPipeline 33 | 34 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 35 | return inflater.inflate(R.layout.fragment_sample_fusion_gl_texture_view, container, false) 36 | } 37 | 38 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 39 | 40 | // 创建RenderChain并添加一些renderer 41 | val renderer = RenderChain() 42 | .addRenderer(ScaleRenderer().apply { setScale(0.8f) }) 43 | .addRenderer(CropRenderer().apply { setCropRect(0.1f, 0.9f, 0.8f, 0.2f) }) 44 | .addRenderer(LUTRenderer().apply { setLUTImage(BitmapUtil.decodeBitmapFromAssets("test_lut.png")!!); setLUTStrength(0.8f) }) 45 | .addRenderer(GaussianBlurRenderer().apply { setBlurRadius(10) }) 46 | .addRenderer(TextureRenderer().apply { setBitmap(BitmapUtil.decodeBitmapFromAssets("test_image_0.png")!!); setRenderRect(-1f, 1f, -0.5f, 0.8f) }) 47 | 48 | // 创建RenderPipeline,连接输入、渲染器与输出 49 | renderPipeline = RenderPipeline 50 | .input(FusionImage(BitmapUtil.decodeBitmapFromAssets("test.png")!!)) 51 | .renderWith(renderer) 52 | .useContext(fusionView) 53 | .output(fusionView) 54 | 55 | // 开始处理 56 | renderPipeline.start() 57 | 58 | } 59 | 60 | override fun onDestroy() { 61 | TexturePool.release() 62 | FrameBufferPool.release() 63 | GLProgramPool.release() 64 | renderPipeline.release() 65 | super.onDestroy() 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/kenneycode/fusion/demo/fragment/SampleCamera.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.demo.fragment 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | import io.github.kenneycode.fusion.common.Size 9 | import io.github.kenneycode.fusion.demo.R 10 | import io.github.kenneycode.fusion.util.BitmapUtil 11 | import io.github.kenneycode.fusion.framebuffer.FrameBufferPool 12 | import io.github.kenneycode.fusion.input.FusionCamera 13 | 14 | import io.github.kenneycode.fusion.process.RenderChain 15 | import io.github.kenneycode.fusion.process.RenderPipeline 16 | import io.github.kenneycode.fusion.program.GLProgramPool 17 | import io.github.kenneycode.fusion.renderer.* 18 | import io.github.kenneycode.fusion.texture.TexturePool 19 | import kotlinx.android.synthetic.main.fragment_sample_fusion_gl_texture_view.* 20 | 21 | /** 22 | * 23 | * Coded by kenney 24 | * 25 | * http://www.github.com/kenneycode/fusion 26 | * 27 | * 相机渲染demo 28 | * 29 | */ 30 | 31 | class SampleCamera : Fragment() { 32 | 33 | private lateinit var renderPipeline: RenderPipeline 34 | 35 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 36 | return inflater.inflate(R.layout.fragment_sample_fusion_gl_texture_view, container, false) 37 | } 38 | 39 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 40 | 41 | // 创建RenderChain并添加一些renderer 42 | val renderer = RenderChain() 43 | .addRenderer(OESConvertRenderer()) 44 | .addRenderer(LUTRenderer().apply { setLUTImage(BitmapUtil.decodeBitmapFromAssets("test_lut.png")!!); setLUTStrength(0.8f) }) 45 | 46 | // 相机配置 47 | val fusionCameraConfig = FusionCamera.Config().apply { 48 | windowRotation = activity!!.windowManager.defaultDisplay.rotation 49 | desiredPreviewSize = Size(1080, 1920) 50 | } 51 | 52 | // 创建RenderPipeline,连接输入、渲染器与输出 53 | renderPipeline = RenderPipeline 54 | .input(FusionCamera(fusionCameraConfig)) 55 | .renderWith(renderer) 56 | .useContext(fusionView) 57 | .output(fusionView) 58 | 59 | // 开始处理 60 | renderPipeline.start() 61 | 62 | } 63 | 64 | override fun onDestroy() { 65 | TexturePool.release() 66 | FrameBufferPool.release() 67 | GLProgramPool.release() 68 | renderPipeline.release() 69 | super.onDestroy() 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/kenneycode/fusion/demo/fragment/SampleGLSurfaceViewUsage0.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.demo.fragment 2 | 3 | import android.opengl.GLSurfaceView 4 | import android.os.Bundle 5 | import android.util.Log 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.fragment.app.Fragment 10 | import io.github.kenneycode.fusion.common.DataKeys 11 | import io.github.kenneycode.fusion.demo.R 12 | import io.github.kenneycode.fusion.util.BitmapUtil 13 | import io.github.kenneycode.fusion.framebuffer.FrameBufferPool 14 | import io.github.kenneycode.fusion.process.RenderGraph 15 | import io.github.kenneycode.fusion.program.GLProgramPool 16 | import io.github.kenneycode.fusion.renderer.DisplayRenderer 17 | import io.github.kenneycode.fusion.renderer.SimpleRenderer 18 | import io.github.kenneycode.fusion.texture.TexturePool 19 | import java.nio.ByteBuffer 20 | import javax.microedition.khronos.egl.EGLConfig 21 | import javax.microedition.khronos.opengles.GL10 22 | 23 | /** 24 | * 25 | * Coded by kenney 26 | * 27 | * http://www.github.com/kenneycode/fusion 28 | * 29 | * 基本GLSurfaceView用法0 30 | * 31 | */ 32 | 33 | class SampleGLSurfaceViewUsage0 : Fragment() { 34 | 35 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 36 | return inflater.inflate(R.layout.fragment_sample_gl_surface_view, container, false) 37 | } 38 | 39 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 40 | 41 | view.findViewById(R.id.glSurfaceView).apply { 42 | 43 | setEGLContextClientVersion(3) 44 | setEGLConfigChooser(8, 8, 8, 8, 0, 0) 45 | setRenderer(object : GLSurfaceView.Renderer { 46 | 47 | private var surfaceWidth = 0 48 | private var surfaceHeight = 0 49 | private lateinit var renderGraph: RenderGraph 50 | 51 | 52 | override fun onDrawFrame(gl: GL10?) { 53 | Log.e("debug", "onDrawFrame()") 54 | // 执行渲染 55 | renderGraph.update(mutableMapOf( 56 | DataKeys.KEY_DISPLAY_WIDTH to surfaceWidth, 57 | DataKeys.KEY_DISPLAY_HEIGHT to surfaceHeight 58 | )) 59 | renderGraph.render() 60 | 61 | } 62 | 63 | override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) { 64 | surfaceWidth = width 65 | surfaceHeight = height 66 | } 67 | 68 | override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) { 69 | 70 | // 创建一个简单渲染器 71 | val simpleRenderer = SimpleRenderer() 72 | 73 | // 创建一个显示渲染器 74 | val displayRenderer = DisplayRenderer().apply { 75 | setFlip(false, true) 76 | } 77 | 78 | // 创建RenderGraph 79 | renderGraph = RenderGraph() 80 | .addRenderer(simpleRenderer, "simpleRenderer") 81 | .addRenderer(displayRenderer, "displayRenderer") 82 | .connectRenderer("simpleRenderer", "displayRenderer").apply { 83 | init() 84 | } 85 | 86 | // 创建图片输入源 87 | val bitmap = BitmapUtil.decodeBitmapFromAssets("test.png")!! 88 | val buffer = ByteBuffer.allocate(bitmap.width * bitmap.height * 4) 89 | bitmap.copyPixelsToBuffer(buffer) 90 | buffer.position(0) 91 | renderGraph.setInput(TexturePool.obtainTexture(bitmap.width, bitmap.height).apply { 92 | retain = true 93 | setData(buffer) 94 | }) 95 | 96 | } 97 | 98 | }) 99 | 100 | renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY 101 | } 102 | 103 | } 104 | 105 | override fun onDestroy() { 106 | TexturePool.release() 107 | FrameBufferPool.release() 108 | GLProgramPool.release() 109 | super.onDestroy() 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/kenneycode/fusion/demo/fragment/SampleImageOffscreenRender.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.demo.fragment 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | import io.github.kenneycode.fusion.demo.R 9 | import io.github.kenneycode.fusion.util.BitmapUtil 10 | import io.github.kenneycode.fusion.framebuffer.FrameBufferPool 11 | import io.github.kenneycode.fusion.input.FusionImage 12 | import io.github.kenneycode.fusion.output.FusionBitmap 13 | import io.github.kenneycode.fusion.process.RenderChain 14 | import io.github.kenneycode.fusion.process.RenderPipeline 15 | import io.github.kenneycode.fusion.program.GLProgramPool 16 | import io.github.kenneycode.fusion.renderer.CropRenderer 17 | import io.github.kenneycode.fusion.renderer.GaussianBlurRenderer 18 | import io.github.kenneycode.fusion.renderer.LUTRenderer 19 | import io.github.kenneycode.fusion.renderer.ScaleRenderer 20 | import io.github.kenneycode.fusion.texture.TexturePool 21 | import kotlinx.android.synthetic.main.fragment_sample_offscreen.* 22 | 23 | /** 24 | * 25 | * Coded by kenney 26 | * 27 | * http://www.github.com/kenneycode/fusion 28 | * 29 | * 离屏渲染 30 | * 31 | */ 32 | 33 | class SampleImageOffscreenRender : Fragment() { 34 | 35 | 36 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 37 | return inflater.inflate(R.layout.fragment_sample_offscreen, container, false) 38 | } 39 | 40 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 41 | 42 | button.setOnClickListener { startSample() } 43 | 44 | } 45 | 46 | private fun startSample() { 47 | 48 | val sourceImagePath = "test.png" 49 | 50 | // 创建RenderChain并添加一些renderer 51 | val renderer = RenderChain() 52 | .addRenderer(ScaleRenderer().apply { setFlip(false, true); setScale(0.8f) }) 53 | .addRenderer(CropRenderer().apply { setCropRect(0.1f, 0.9f, 0.8f, 0.2f) }) 54 | .addRenderer(LUTRenderer().apply { setLUTImage(BitmapUtil.decodeBitmapFromAssets("test_lut.png")!!); setLUTStrength(0.8f) }) 55 | .addRenderer(GaussianBlurRenderer().apply { setBlurRadius(10) }) 56 | 57 | // 输出bitmap 58 | val output = FusionBitmap() 59 | 60 | // 创建RenderPipeline,连接输入、渲染器与输出 61 | val renderPipeline = RenderPipeline 62 | .input(FusionImage(BitmapUtil.decodeBitmapFromAssets(sourceImagePath)!!)) 63 | .renderWith(renderer) 64 | .output(output) 65 | 66 | // 开始处理 67 | renderPipeline.start() 68 | 69 | // 等待RenderPipeline执行完成 70 | renderPipeline.flush() 71 | 72 | activity?.runOnUiThread { tips.text = "bitmap已生成!" } 73 | 74 | } 75 | 76 | override fun onDestroy() { 77 | TexturePool.release() 78 | FrameBufferPool.release() 79 | GLProgramPool.release() 80 | super.onDestroy() 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/kenneycode/fusion/demo/fragment/SampleMVPMatrix.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.demo.fragment 2 | 3 | import android.opengl.GLSurfaceView 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import androidx.fragment.app.Fragment 9 | import io.github.kenneycode.fusion.common.DataKeys 10 | import io.github.kenneycode.fusion.demo.R 11 | import io.github.kenneycode.fusion.util.BitmapUtil 12 | import io.github.kenneycode.fusion.framebuffer.FrameBufferPool 13 | 14 | import io.github.kenneycode.fusion.process.RenderChain 15 | import io.github.kenneycode.fusion.program.GLProgramPool 16 | import io.github.kenneycode.fusion.renderer.DisplayRenderer 17 | import io.github.kenneycode.fusion.renderer.SimpleRenderer 18 | import io.github.kenneycode.fusion.texture.TexturePool 19 | import io.github.kenneycode.fusion.util.GLUtil 20 | import java.nio.ByteBuffer 21 | import javax.microedition.khronos.egl.EGLConfig 22 | import javax.microedition.khronos.opengles.GL10 23 | 24 | /** 25 | * 26 | * Coded by kenney 27 | * 28 | * http://www.github.com/kenneycode/fusion 29 | * 30 | * MVP matrix demo 31 | * 32 | */ 33 | 34 | class SampleMVPMatrix : Fragment() { 35 | 36 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 37 | return inflater.inflate(R.layout.fragment_sample_gl_surface_view, container, false) 38 | } 39 | 40 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 41 | 42 | view.findViewById(R.id.glSurfaceView).apply { 43 | 44 | setEGLContextClientVersion(3) 45 | setEGLConfigChooser(8, 8, 8, 8, 0, 0) 46 | setRenderer(object : GLSurfaceView.Renderer { 47 | 48 | private var surfaceWidth = 0 49 | private var surfaceHeight = 0 50 | private lateinit var renderChain: RenderChain 51 | 52 | 53 | override fun onDrawFrame(gl: GL10?) { 54 | 55 | // 执行渲染 56 | renderChain.update(mutableMapOf( 57 | DataKeys.KEY_DISPLAY_WIDTH to surfaceWidth, 58 | DataKeys.KEY_DISPLAY_HEIGHT to surfaceHeight 59 | )) 60 | renderChain.render() 61 | 62 | } 63 | 64 | override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) { 65 | surfaceWidth = width 66 | surfaceHeight = height 67 | } 68 | 69 | override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) { 70 | 71 | // 创建一个简单渲染器 72 | val simpleRenderer = SimpleRenderer().apply { 73 | setFlip(false, true) 74 | val mvpMatrix = GLUtil.createMVPMatrix( 75 | rotateY = 45f 76 | ) 77 | setMVPMatrix(mvpMatrix) 78 | } 79 | 80 | // 创建一个显示渲染器 81 | val displayRenderer = DisplayRenderer() 82 | 83 | // 创建RenderChain 84 | renderChain = RenderChain() 85 | .addRenderer(simpleRenderer) 86 | .addRenderer(displayRenderer).apply { 87 | init() 88 | } 89 | 90 | // 创建图片输入源 91 | val bitmap = BitmapUtil.decodeBitmapFromAssets("test.png")!! 92 | val buffer = ByteBuffer.allocate(bitmap.width * bitmap.height * 4) 93 | bitmap.copyPixelsToBuffer(buffer) 94 | buffer.position(0) 95 | renderChain.setInput(TexturePool.obtainTexture(bitmap.width, bitmap.height).apply { 96 | retain = true 97 | setData(buffer) 98 | }) 99 | 100 | } 101 | }) 102 | } 103 | 104 | } 105 | 106 | override fun onDestroy() { 107 | TexturePool.release() 108 | FrameBufferPool.release() 109 | GLProgramPool.release() 110 | super.onDestroy() 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/kenneycode/fusion/demo/fragment/SampleVideo.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.demo.fragment 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | import io.github.kenneycode.fusion.demo.R 9 | import io.github.kenneycode.fusion.util.BitmapUtil 10 | import io.github.kenneycode.fusion.framebuffer.FrameBufferPool 11 | 12 | import io.github.kenneycode.fusion.input.FusionVideo 13 | import io.github.kenneycode.fusion.process.RenderChain 14 | import io.github.kenneycode.fusion.process.RenderPipeline 15 | import io.github.kenneycode.fusion.program.GLProgramPool 16 | import io.github.kenneycode.fusion.renderer.* 17 | import io.github.kenneycode.fusion.texture.TexturePool 18 | import kotlinx.android.synthetic.main.fragment_sample_fusion_gl_texture_view.* 19 | 20 | /** 21 | * 22 | * Coded by kenney 23 | * 24 | * http://www.github.com/kenneycode/fusion 25 | * 26 | * 视频渲染demo 27 | * 28 | */ 29 | 30 | class SampleVideo : Fragment() { 31 | 32 | private lateinit var renderPipeline: RenderPipeline 33 | 34 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 35 | return inflater.inflate(R.layout.fragment_sample_fusion_gl_texture_view, container, false) 36 | } 37 | 38 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 39 | 40 | // 创建RenderChain并添加一些renderer 41 | val renderer = RenderChain() 42 | .addRenderer(OESConvertRenderer()) 43 | .addRenderer(LUTRenderer().apply { setLUTImage(BitmapUtil.decodeBitmapFromAssets("test_lut.png")!!); setLUTStrength(0.8f) }) 44 | .addRenderer(GaussianBlurRenderer().apply { setBlurRadius(20) }) 45 | 46 | // 创建RenderPipeline,连接输入、渲染器与输出 47 | renderPipeline = RenderPipeline 48 | .input(FusionVideo("/sdcard/test.mp4")) 49 | .renderWith(renderer) 50 | .useContext(fusionView) 51 | .output(fusionView) 52 | 53 | // 开始处理 54 | renderPipeline.start() 55 | 56 | } 57 | 58 | override fun onDestroy() { 59 | TexturePool.release() 60 | FrameBufferPool.release() 61 | GLProgramPool.release() 62 | renderPipeline.release() 63 | super.onDestroy() 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/kenneycode/fusion/demo/fragment/SampleVideoOffscreenRender.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.demo.fragment 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | import io.github.kenneycode.fusion.demo.R 9 | import io.github.kenneycode.fusion.util.BitmapUtil 10 | import io.github.kenneycode.fusion.framebuffer.FrameBufferPool 11 | import io.github.kenneycode.fusion.input.FusionVideoDecoder 12 | import io.github.kenneycode.fusion.output.FusionVideoEncoder 13 | import io.github.kenneycode.fusion.process.RenderChain 14 | import io.github.kenneycode.fusion.process.RenderPipeline 15 | import io.github.kenneycode.fusion.program.GLProgramPool 16 | import io.github.kenneycode.fusion.renderer.* 17 | import io.github.kenneycode.fusion.texture.TexturePool 18 | import kotlinx.android.synthetic.main.fragment_sample_offscreen.* 19 | 20 | /** 21 | * 22 | * Coded by kenney 23 | * 24 | * http://www.github.com/kenneycode/fusion 25 | * 26 | * 视频离屏渲染 27 | * 28 | */ 29 | 30 | class SampleVideoOffscreenRender : Fragment() { 31 | 32 | 33 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 34 | return inflater.inflate(R.layout.fragment_sample_offscreen, container, false) 35 | } 36 | 37 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 38 | 39 | button.setOnClickListener { startSample() } 40 | 41 | } 42 | 43 | private fun startSample() { 44 | 45 | val sourceVideoPath = "/sdcard/test.mp4" 46 | val outputVideoPath = "/sdcard/test_encoder.mp4" 47 | 48 | // 创建RenderChain并添加一些renderer 49 | val renderer = RenderChain() 50 | .addRenderer(OESConvertRenderer()) 51 | .addRenderer(LUTRenderer().apply { setLUTImage(BitmapUtil.decodeBitmapFromAssets("test_lut.png")!!); setLUTStrength(0.8f) }) 52 | .addRenderer(GaussianBlurRenderer().apply { setBlurRadius(10) }) 53 | 54 | // 视频解码器 55 | val fusionVideoDecoder = FusionVideoDecoder(sourceVideoPath) 56 | 57 | // 创建RenderPipeline,连接输入、渲染器与输出 58 | val renderPipeline = RenderPipeline 59 | .input(fusionVideoDecoder) 60 | .renderWith(renderer) 61 | .output(FusionVideoEncoder(outputVideoPath)) 62 | 63 | fusionVideoDecoder.setCallback(object : FusionVideoDecoder.Callback { 64 | 65 | override fun onStart() { 66 | } 67 | 68 | override fun onEnd() { 69 | renderPipeline.release() 70 | activity?.runOnUiThread { tips.text = "视频已生成! path = $outputVideoPath" } 71 | } 72 | 73 | }) 74 | 75 | // 开始处理 76 | renderPipeline.start() 77 | 78 | } 79 | 80 | override fun onDestroy() { 81 | TexturePool.release() 82 | FrameBufferPool.release() 83 | GLProgramPool.release() 84 | super.onDestroy() 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/kenneycode/fusion/util/Utils.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.util 2 | 3 | import android.content.Context 4 | import java.io.File 5 | import java.io.FileOutputStream 6 | 7 | class Utils { 8 | 9 | companion object { 10 | 11 | fun copyAssetsFiles(context: Context, source: String, dst: String): Boolean { 12 | try { 13 | val fileNames = context.assets.list(source) 14 | if (fileNames!!.isNotEmpty()) { 15 | val file = File(dst) 16 | file.mkdirs() 17 | for (fileName in fileNames) { 18 | copyAssetsFiles(context, "$source/$fileName", "$dst/$fileName") 19 | } 20 | } else { 21 | val ins = context.assets.open(source) 22 | val fos = FileOutputStream(File(dst)) 23 | val buffer = ByteArray(1024) 24 | var byteCount = 0 25 | while (true) { 26 | byteCount = ins.read(buffer) 27 | if (byteCount == -1) { 28 | break 29 | } 30 | fos.write(buffer, 0, byteCount) 31 | } 32 | fos.flush() 33 | ins.close() 34 | fos.close() 35 | } 36 | } catch (e: Exception) { 37 | e.printStackTrace() 38 | } 39 | return true 40 | } 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_sample.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_sample_common.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_sample_fusion_gl_texture_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_sample_gl_surface_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_sample_offscreen.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenneycode/fusion/8b2959cf39fa7c988a3e3758686027cede1d1007/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenneycode/fusion/8b2959cf39fa7c988a3e3758686027cede1d1007/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenneycode/fusion/8b2959cf39fa7c988a3e3758686027cede1d1007/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenneycode/fusion/8b2959cf39fa7c988a3e3758686027cede1d1007/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenneycode/fusion/8b2959cf39fa7c988a3e3758686027cede1d1007/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenneycode/fusion/8b2959cf39fa7c988a3e3758686027cede1d1007/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenneycode/fusion/8b2959cf39fa7c988a3e3758686027cede1d1007/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenneycode/fusion/8b2959cf39fa7c988a3e3758686027cede1d1007/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenneycode/fusion/8b2959cf39fa7c988a3e3758686027cede1d1007/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenneycode/fusion/8b2959cf39fa7c988a3e3758686027cede1d1007/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | fusion demo 3 | 基本用法 4 | GLSurfaceView用法0 5 | MVP matrix 6 | 视频渲染 7 | 图片离屏渲染 8 | 视频离屏渲染 9 | 相机渲染 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/io/github/kenneycode/fusion/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see [Testing documentation](http://d.android.com/tools/testing) 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, (2 + 2).toLong()) 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext.kotlin_version = '1.3.60' 5 | repositories { 6 | google() 7 | jcenter() 8 | 9 | } 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:3.5.0' 12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 13 | 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | google() 22 | jcenter() 23 | maven { url 'https://www.jitpack.io' } 24 | } 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | 21 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenneycode/fusion/8b2959cf39fa7c988a3e3758686027cede1d1007/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Nov 19 14:28:21 CST 2019 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-5.4.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /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 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 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 Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /libfusion/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /libfusion/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android-extensions' 3 | apply plugin: 'kotlin-android' 4 | 5 | android { 6 | compileSdkVersion 28 7 | 8 | 9 | defaultConfig { 10 | minSdkVersion 18 11 | targetSdkVersion 28 12 | versionCode 1 13 | versionName "1.0" 14 | 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | consumerProguardFiles 'consumer-rules.pro' 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | 26 | } 27 | 28 | dependencies { 29 | implementation fileTree(dir: 'libs', include: ['*.jar']) 30 | implementation 'com.github.kenneycode:VideoStudio:1.0.0' 31 | implementation 'androidx.appcompat:appcompat:1.0.2' 32 | testImplementation 'junit:junit:4.12' 33 | androidTestImplementation 'androidx.test:runner:1.1.1' 34 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 35 | compile "androidx.core:core-ktx:+" 36 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 37 | } 38 | repositories { 39 | mavenCentral() 40 | } 41 | -------------------------------------------------------------------------------- /libfusion/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenneycode/fusion/8b2959cf39fa7c988a3e3758686027cede1d1007/libfusion/consumer-rules.pro -------------------------------------------------------------------------------- /libfusion/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /libfusion/src/androidTest/java/io/github/kenneycode/fusion/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * @see [Testing documentation](http://d.android.com/tools/testing) 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | 23 | assertEquals("io.github.kenneycode.fusion.test", appContext.packageName) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /libfusion/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/common/Common.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.common 2 | 3 | import android.opengl.GLES20.glGetError 4 | import java.lang.AssertionError 5 | 6 | /** 7 | * 8 | * Coded by kenney 9 | * 10 | * http://www.github.com/kenneycode/fusion 11 | * 12 | * common逻辑 13 | * 14 | */ 15 | 16 | val DEBUG = true 17 | 18 | /** 19 | * 20 | * 检查GL error,在DEBUG==true的情况下如果检查到GL error会throw AssertionError 21 | * 22 | * @param block 要检查的代码块 23 | * 24 | */ 25 | fun glCheck(block: () -> Unit) { 26 | block() 27 | if (DEBUG) { 28 | val error = glGetError() 29 | if (error != 0) { 30 | throw AssertionError("GL checkout error = $error") 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/common/Constants.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.common 2 | 3 | import android.opengl.Matrix 4 | 5 | /** 6 | * 7 | * Coded by kenney 8 | * 9 | * http://www.github.com/kenneycode/fusion 10 | * 11 | * 常用常量 12 | * 13 | */ 14 | 15 | class Constants { 16 | 17 | companion object { 18 | 19 | val SIMPLE_VERTEX_SHADER = 20 | "precision mediump float;\n" + 21 | "attribute vec4 a_position;\n" + 22 | "attribute vec2 a_textureCoordinate;\n" + 23 | "varying vec2 v_textureCoordinate;\n" + 24 | "void main() {\n" + 25 | " v_textureCoordinate = a_textureCoordinate;\n" + 26 | " gl_Position = a_position;\n" + 27 | "}" 28 | 29 | val MVP_VERTEX_SHADER = 30 | "precision mediump float;\n" + 31 | "attribute vec4 a_position;\n" + 32 | "attribute vec2 a_textureCoordinate;\n" + 33 | "uniform mat4 u_mvpMatrix;\n" + 34 | "varying vec2 v_textureCoordinate;\n" + 35 | "void main() {\n" + 36 | " v_textureCoordinate = a_textureCoordinate;\n" + 37 | " gl_Position = u_mvpMatrix * a_position;\n" + 38 | "}" 39 | 40 | val SIMPLE_FRAGMENT_SHADER = 41 | "precision mediump float;\n" + 42 | "varying vec2 v_textureCoordinate;\n" + 43 | "uniform sampler2D u_texture;\n" + 44 | "void main() {\n" + 45 | " gl_FragColor = texture2D(u_texture, v_textureCoordinate);\n" + 46 | "}" 47 | 48 | val OES_VERTEX_SHADER = 49 | "precision mediump float;\n" + 50 | "attribute vec4 a_position;\n" + 51 | "attribute vec4 a_textureCoordinate;\n" + 52 | "varying vec2 v_textureCoordinate;\n" + 53 | "uniform mat4 u_stMatrix;\n" + 54 | "void main() {\n" + 55 | " v_textureCoordinate = (u_stMatrix * a_textureCoordinate).xy;\n" + 56 | " gl_Position = a_position;\n" + 57 | "}" 58 | 59 | val OES_FRAGMENT_SHADER = 60 | "#extension GL_OES_EGL_image_external : require\n" + 61 | "precision mediump float;\n" + 62 | "varying vec2 v_textureCoordinate;\n" + 63 | "uniform samplerExternalOES u_texture;\n" + 64 | "void main() {\n" + 65 | " gl_FragColor = texture2D(u_texture, v_textureCoordinate);\n" + 66 | "}" 67 | 68 | val LUT_FRAGMENT_SHADER = 69 | "precision highp float;\n" + 70 | "uniform sampler2D u_texture;\n" + 71 | "uniform sampler2D u_lutTexture;\n" + 72 | "varying vec2 v_textureCoordinate;\n" + 73 | "uniform float strength;\n" + 74 | "\n" + 75 | "void main()\n" + 76 | "{\n" + 77 | " vec4 color = texture2D(u_texture, v_textureCoordinate);\n" + 78 | " float blueColor = color.b * 63.0;\n" + 79 | " \n" + 80 | " vec2 quad1;\n" + 81 | " quad1.y = floor(floor(blueColor) / 8.0);\n" + 82 | " quad1.x = floor(blueColor) - (quad1.y * 8.0);\n" + 83 | " \n" + 84 | " vec2 quad2;\n" + 85 | " quad2.y = floor(ceil(blueColor) / 8.0);\n" + 86 | " quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n" + 87 | " \n" + 88 | " vec2 texPos1;\n" + 89 | " texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);\n" + 90 | " texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);\n" + 91 | " \n" + 92 | " vec2 texPos2;\n" + 93 | " texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r);\n" + 94 | " texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g);\n" + 95 | " \n" + 96 | " vec4 newColor1 = texture2D(u_lutTexture, texPos1);\n" + 97 | " vec4 newColor2 = texture2D(u_lutTexture, texPos2);\n" + 98 | " \n" + 99 | " vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n" + 100 | " newColor = mix(color, vec4(newColor.rgb, color.w), strength);\n" + 101 | " gl_FragColor = newColor;\n" + 102 | "}" 103 | 104 | val GAUSSIAN_BLUR_FRAGMENT_SHADER = 105 | "precision mediump float;\n" + 106 | "varying vec2 v_textureCoordinate;\n" + 107 | "uniform sampler2D u_texture;\n" + 108 | "uniform float weights[6];\n" + 109 | "uniform float hOffset;\n" + 110 | "uniform float vOffset;\n" + 111 | "uniform int orientation;\n" + 112 | "void main() {\n" + 113 | "\tvec4 color = texture2D(u_texture, v_textureCoordinate) * weights[0];\n" + 114 | "\tif (orientation == 0) {\n" + 115 | "\t\tfor (int i = 1; i < 6; ++i) {\n" + 116 | "\t\t\tcolor += texture2D(u_texture, vec2(v_textureCoordinate.x - float(i) * hOffset, v_textureCoordinate.y)) * weights[i];\n" + 117 | "\t\t\tcolor += texture2D(u_texture, vec2(v_textureCoordinate.x + float(i) * hOffset, v_textureCoordinate.y)) * weights[i];\n" + 118 | "\t\t}\n" + 119 | "\t} else {\n" + 120 | "\t\tfor (int i = 1; i < 6; ++i) {\n" + 121 | "\t\t\tcolor += texture2D(u_texture, vec2(v_textureCoordinate.x, v_textureCoordinate.y - float(i) * vOffset)) * weights[i];\n" + 122 | "\t\t\tcolor += texture2D(u_texture, vec2(v_textureCoordinate.x, v_textureCoordinate.y + float(i) * vOffset)) * weights[i];\n" + 123 | "\t\t}\n" + 124 | "\t}\n" + 125 | " gl_FragColor = color;\n" + 126 | "}" 127 | 128 | val SIMPLE_VERTEX = floatArrayOf(-1f, -1f, -1f, 1f, 1f, 1f, -1f, -1f, 1f, 1f, 1f, -1f) 129 | val SIMPLE_VERTEX_FLIP_X = floatArrayOf(1f, -1f, 1f, 1f, -1f, 1f, 1f, -1f, -1f, 1f, -1f, -1f) 130 | val SIMPLE_VERTEX_FLIP_Y = floatArrayOf(-1f, 1f, -1f, -1f, 1f, -1f, -1f, 1f, 1f, -1f, 1f, 1f) 131 | val SIMPLE_VERTEX_FLIP_XY = floatArrayOf(1f, 1f, 1f, -1f, -1f, -1f, 1f, 1f, -1f, -1f, -1f, 1f) 132 | val SIMPLE_TEXTURE_COORDINATE = floatArrayOf(0f, 0f, 0f, 1f, 1f, 1f, 0f, 0f, 1f, 1f, 1f, 0f) 133 | val IDENTITY_MATRIX: FloatArray 134 | get() = FloatArray(16).apply { 135 | Matrix.setIdentityM(this, 0) 136 | } 137 | 138 | val POSITION_PARAM_KEY = "a_position" 139 | val TEXTURE_COORDINATE_PARAM_KEY = "a_textureCoordinate" 140 | val MVP_MATRIX_PARAM_KEY = "u_mvpMatrix" 141 | 142 | val INVALID_LOCATION = -1 143 | 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/common/DataKeys.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.common 2 | 3 | class DataKeys { 4 | 5 | companion object { 6 | 7 | const val ST_MATRIX = "ST_MATRIX" 8 | const val KEY_DISPLAY_WIDTH = "KEY_DISPLAY_WIDTH" 9 | const val KEY_DISPLAY_HEIGHT = "KEY_DISPLAY_HEIGHT" 10 | const val KEY_SOURCE_VIDEO_FPS = "KEY_SOURCE_VIDEO_FPS" 11 | const val KEY_SOURCE_VIDEO_WIDTH = "KEY_SOURCE_VIDEO_WIDTH" 12 | const val KEY_SOURCE_VIDEO_HEIGHT = "KEY_SOURCE_VIDEO_HEIGHT" 13 | 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/common/Ref.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.common 2 | 3 | /** 4 | * 5 | * Coded by kenney 6 | * 7 | * http://www.github.com/kenneycode/fusion 8 | * 9 | * 引用计数类 10 | * 11 | */ 12 | 13 | open class Ref { 14 | 15 | protected var refCount = 1 16 | 17 | /** 18 | * 19 | * 增加count个引用计数 20 | * 21 | * @param count 要增加的引用计数 22 | * 23 | */ 24 | fun increaseRef(count: Int = 1) { 25 | refCount += count 26 | } 27 | 28 | /** 29 | * 30 | * 减少1个引用计数 31 | * 32 | */ 33 | open fun decreaseRef() { 34 | --refCount 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/common/Shader.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.common 2 | 3 | /** 4 | * 5 | * Coded by kenney 6 | * 7 | * http://www.github.com/kenneycode/fusion 8 | * 9 | * Shader包装类,包括vertex shader和fragment shader 10 | * 11 | */ 12 | 13 | class Shader constructor(var vertexShader: String = Constants.SIMPLE_VERTEX_SHADER, var fragmentShader: String = Constants.SIMPLE_FRAGMENT_SHADER) { 14 | 15 | override fun equals(other: Any?): Boolean { 16 | if (this === other) { 17 | return true 18 | } 19 | if (javaClass != other?.javaClass) { 20 | return false 21 | } 22 | other as Shader 23 | if (vertexShader != other.vertexShader) { 24 | return false 25 | } 26 | if (fragmentShader != other.fragmentShader) { 27 | return false 28 | } 29 | return true 30 | } 31 | 32 | override fun hashCode(): Int { 33 | var result = vertexShader.hashCode() 34 | result = 31 * result + fragmentShader.hashCode() 35 | return result 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/common/Size.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.common 2 | 3 | /** 4 | * 5 | * Coded by kenney 6 | * 7 | * http://www.github.com/kenneycode/fusion 8 | * 9 | * Size类 10 | * 11 | */ 12 | 13 | class Size(var width: Int, var height: Int) { 14 | 15 | override fun equals(other: Any?): Boolean { 16 | if (this === other) { 17 | return true 18 | } 19 | if (javaClass != other?.javaClass) { 20 | return false 21 | } 22 | other as Size 23 | if (width != other.width) { 24 | return false 25 | } 26 | if (height != other.height) { 27 | return false 28 | } 29 | return true 30 | } 31 | 32 | override fun hashCode(): Int { 33 | var result = width 34 | result = 31 * result + height 35 | return result 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/context/FusionEGL.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.context 2 | 3 | import android.opengl.EGL14 4 | import android.opengl.EGLConfig 5 | import android.opengl.EGLContext 6 | import android.opengl.EGLExt 7 | import android.view.Surface 8 | 9 | /** 10 | * 11 | * Coded by kenney 12 | * 13 | * http://www.github.com/kenneycode/fusion 14 | * 15 | * EGL封装类 16 | * 17 | */ 18 | 19 | class FusionEGL { 20 | 21 | var eglContext = EGL14.EGL_NO_CONTEXT 22 | 23 | private var eglDisplay = EGL14.EGL_NO_DISPLAY 24 | private var eglSurface = EGL14.EGL_NO_SURFACE 25 | private var previousDisplay = EGL14.EGL_NO_DISPLAY 26 | private var previousDrawSurface = EGL14.EGL_NO_SURFACE 27 | private var previousReadSurface = EGL14.EGL_NO_SURFACE 28 | private var previousContext = EGL14.EGL_NO_CONTEXT 29 | 30 | /** 31 | * 32 | * 初始化 33 | * 34 | * @param surface 用于创建window surface的surface,如果传null则创建pbuffer surface 35 | * @param shareContext 共享的GL Context 36 | * 37 | */ 38 | fun init(surface: Surface?, shareContext: EGLContext = EGL14.EGL_NO_CONTEXT) { 39 | eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY) 40 | val version = IntArray(2) 41 | EGL14.eglInitialize(eglDisplay, version, 0, version, 1) 42 | val attribList = intArrayOf(EGL14.EGL_RED_SIZE, 8, EGL14.EGL_GREEN_SIZE, 8, EGL14.EGL_BLUE_SIZE, 8, EGL14.EGL_ALPHA_SIZE, 8, EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT or EGLExt.EGL_OPENGL_ES3_BIT_KHR, EGL14.EGL_NONE, 0, EGL14.EGL_NONE) 43 | val eglConfig = arrayOfNulls(1) 44 | val numConfigs = IntArray(1) 45 | EGL14.eglChooseConfig( 46 | eglDisplay, 47 | attribList, 0, 48 | eglConfig, 0, 49 | eglConfig.size, 50 | numConfigs, 0 51 | ) 52 | eglContext = EGL14.eglCreateContext( 53 | eglDisplay, eglConfig[0], shareContext, 54 | intArrayOf(EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE), 0 55 | ) 56 | val surfaceAttribs = intArrayOf(EGL14.EGL_NONE) 57 | eglSurface = if (surface == null) { 58 | EGL14.eglCreatePbufferSurface(eglDisplay, eglConfig[0], surfaceAttribs, 0) 59 | } else { 60 | EGL14.eglCreateWindowSurface(eglDisplay, eglConfig[0], surface, surfaceAttribs, 0) 61 | } 62 | } 63 | 64 | /** 65 | * 66 | * 将EGL环境绑定到调用线程 67 | * 68 | */ 69 | fun bind() { 70 | previousDisplay = EGL14.eglGetCurrentDisplay() 71 | previousDrawSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW) 72 | previousReadSurface = EGL14.eglGetCurrentSurface(EGL14.EGL_READ) 73 | previousContext = EGL14.eglGetCurrentContext() 74 | EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext) 75 | } 76 | 77 | /** 78 | * 79 | * 将之前的EGL环境恢复 80 | * 81 | */ 82 | fun unbind() { 83 | EGL14.eglMakeCurrent(previousDisplay, previousDrawSurface, previousReadSurface, previousContext) 84 | } 85 | 86 | /** 87 | * 88 | * 交换显示buffer 89 | * 90 | */ 91 | fun swapBuffers(): Boolean { 92 | return EGL14.eglSwapBuffers(eglDisplay, eglSurface) 93 | } 94 | 95 | /** 96 | * 97 | * 释放资源 98 | * 99 | */ 100 | fun release() { 101 | if (eglDisplay !== EGL14.EGL_NO_DISPLAY) { 102 | EGL14.eglMakeCurrent(eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT) 103 | EGL14.eglDestroySurface(eglDisplay, eglSurface) 104 | EGL14.eglDestroyContext(eglDisplay, eglContext) 105 | EGL14.eglReleaseThread() 106 | EGL14.eglTerminate(eglDisplay) 107 | } 108 | eglDisplay = EGL14.EGL_NO_DISPLAY 109 | eglContext = EGL14.EGL_NO_CONTEXT 110 | eglSurface = EGL14.EGL_NO_SURFACE 111 | } 112 | 113 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/context/FusionGLThread.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.context 2 | 3 | import android.opengl.EGL14 4 | import android.opengl.EGLContext 5 | import android.os.Handler 6 | import android.os.HandlerThread 7 | import android.view.Surface 8 | import java.util.concurrent.Semaphore 9 | 10 | /** 11 | * 12 | * Coded by kenney 13 | * 14 | * http://www.github.com/kenneycode/fusion 15 | * 16 | * 包含GL Context和线程 17 | * 18 | */ 19 | 20 | class FusionGLThread { 21 | 22 | private val handlerThread : HandlerThread = HandlerThread("FusionGLThread") 23 | private lateinit var handler : Handler 24 | lateinit var egl : FusionEGL 25 | 26 | /** 27 | * 28 | * 初始化 29 | * 30 | * @param surface 用于创建window surface的surface,如果传null则创建pbuffer surface 31 | * @param shareContext 共享的GL Context 32 | * 33 | */ 34 | fun init(surface: Surface? = null, shareContext: EGLContext = EGL14.EGL_NO_CONTEXT) { 35 | handlerThread.start() 36 | handler = Handler(handlerThread.looper) 37 | egl = FusionEGL() 38 | egl.init(surface, shareContext) 39 | handler.post { 40 | egl.bind() 41 | } 42 | } 43 | 44 | /** 45 | * 46 | * 异步执行一个任务 47 | * 48 | * @param task 要执行的任务 49 | * @param render 是否需要渲染 50 | * 51 | */ 52 | fun post(task : () -> Unit, render: Boolean = false) { 53 | handler.post { 54 | task.invoke() 55 | if (render) { 56 | egl.swapBuffers() 57 | } 58 | } 59 | } 60 | 61 | /** 62 | * 63 | * 同步执行一个任务 64 | * 65 | * @param task 要执行的任务 66 | * @param render 是否需要渲染 67 | * 68 | */ 69 | fun execute(task : () -> Unit, render: Boolean = false) { 70 | val semaphore = Semaphore(0) 71 | handler.post { 72 | task.invoke() 73 | if (render) { 74 | egl.swapBuffers() 75 | } 76 | semaphore.release() 77 | } 78 | semaphore.acquire() 79 | } 80 | 81 | fun getEGLContext(): EGLContext { 82 | return egl.eglContext 83 | } 84 | 85 | /** 86 | * 87 | * 交换显示buffer 88 | * 89 | */ 90 | fun swapBuffers() { 91 | egl.swapBuffers() 92 | } 93 | 94 | /** 95 | * 96 | * 停止线程和释放资源 97 | * 98 | */ 99 | fun release() { 100 | handler.post { 101 | egl.release() 102 | handlerThread.looper.quit() 103 | } 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/context/GLContext.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.context 2 | 3 | import android.opengl.EGLContext 4 | 5 | /** 6 | * 7 | * Coded by kenney 8 | * 9 | * http://www.github.com/kenneycode/fusion 10 | * 11 | * OpenGL Context 接口 12 | * 13 | */ 14 | 15 | interface GLContext { 16 | 17 | /** 18 | * 19 | * 将task在此OpenGL Context中执行 20 | * 21 | * @param task 要执行的task 22 | * 23 | */ 24 | fun runOnGLContext(task: () -> Unit) 25 | 26 | fun getEGLContext(): EGLContext 27 | 28 | fun release() 29 | 30 | } 31 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/context/InPlaceGLContext.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.context 2 | 3 | import android.opengl.EGL14 4 | import android.opengl.EGLContext 5 | 6 | /** 7 | * 8 | * Coded by kenney 9 | * 10 | * http://www.github.com/kenneycode/fusion 11 | * 12 | * 原地执行的GL Context类 13 | * 14 | */ 15 | 16 | class InPlaceGLContext : GLContext { 17 | 18 | override fun getEGLContext(): EGLContext { 19 | return EGL14.eglGetCurrentContext() 20 | } 21 | /** 22 | * 23 | * 将task在此OpenGL Context中执行 24 | * 25 | * @param task 要执行的task 26 | * 27 | */ 28 | override fun runOnGLContext(task: () -> Unit) { 29 | task.invoke() 30 | } 31 | 32 | override fun release() { 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/context/SimpleGLContext.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.context 2 | 3 | import android.opengl.EGL14 4 | import android.opengl.EGLContext 5 | import android.view.Surface 6 | 7 | /** 8 | * 9 | * Coded by kenney 10 | * 11 | * http://www.github.com/kenneycode/fusion 12 | * 13 | * 普通GL Context 14 | * 15 | */ 16 | 17 | class SimpleGLContext(surface: Surface? = null) : GLContext { 18 | 19 | private val glThread = FusionGLThread() 20 | 21 | init { 22 | glThread.init(surface) 23 | } 24 | 25 | override fun getEGLContext(): EGLContext { 26 | return EGL14.eglGetCurrentContext() 27 | } 28 | 29 | /** 30 | * 31 | * 将task在此OpenGL Context中执行 32 | * 33 | * @param task 要执行的task 34 | * 35 | */ 36 | override fun runOnGLContext(task: () -> Unit) { 37 | glThread.post(task) 38 | } 39 | 40 | override fun release() { 41 | glThread.release() 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/framebuffer/FrameBuffer.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.framebuffer 2 | 3 | import android.opengl.GLES11Ext 4 | import android.opengl.GLES20.* 5 | import io.github.kenneycode.fusion.common.Ref 6 | import io.github.kenneycode.fusion.common.glCheck 7 | 8 | import io.github.kenneycode.fusion.texture.Texture 9 | import io.github.kenneycode.fusion.util.GLUtil 10 | import io.github.kenneycode.fusion.util.Util 11 | 12 | /** 13 | * 14 | * Coded by kenney 15 | * 16 | * http://www.github.com/kenneycode/fusion 17 | * 18 | * FrameBuffer类,可以做为一帧渲染的输入/输出 19 | * 20 | */ 21 | 22 | class FrameBuffer : Ref() { 23 | 24 | var frameBuffer = GL_NONE 25 | var attachedTexture: Texture? = null 26 | 27 | /** 28 | * 29 | * 将texture attach到此frame buffer上 30 | * 31 | * @param texture 要attach的texture 32 | * 33 | */ 34 | fun attachTexture(texture: Texture) { 35 | attachedTexture?.decreaseRef() 36 | attachedTexture = texture 37 | if (frameBuffer == GL_NONE) { 38 | frameBuffer = GLUtil.createFrameBuffer() 39 | } 40 | bind() 41 | Util.assert(texture.width > 0 && texture.height > 0 && (texture.type == GL_TEXTURE_2D || texture.type == GLES11Ext.GL_TEXTURE_EXTERNAL_OES) && glIsTexture(texture.texture) && frameBuffer > 0, "texture error") 42 | glCheck { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture.type, texture.texture, 0) } 43 | } 44 | 45 | /** 46 | * 47 | * 绑定此frame buffer,绑定后将渲染到此frame buffer上 48 | * 49 | */ 50 | fun bind() { 51 | glCheck { glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer) } 52 | } 53 | 54 | /** 55 | * 56 | * 解绑定此frame buffer,绑定后将渲染到此frame buffer上 57 | * 58 | */ 59 | fun unbind() { 60 | glCheck { glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE) } 61 | } 62 | 63 | /** 64 | * 65 | * 减少引用计数,当引用计数为0时放回frame buffer cache 66 | * 67 | */ 68 | override fun decreaseRef() { 69 | super.decreaseRef() 70 | if (refCount <= 0) { 71 | FrameBufferPool.releaseFrameBuffer(this) 72 | } 73 | } 74 | 75 | /** 76 | * 77 | * 释放资源 78 | * 79 | */ 80 | fun release() { 81 | GLUtil.deleteFrameBuffer(frameBuffer) 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/framebuffer/FrameBufferPool.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.framebuffer 2 | 3 | /** 4 | * 5 | * Coded by kenney 6 | * 7 | * http://www.github.com/kenneycode/fusion 8 | * 9 | * FrameBuffer缓存池 10 | * 11 | */ 12 | 13 | object FrameBufferPool { 14 | 15 | private val cache = mutableListOf() 16 | 17 | /** 18 | * 19 | * 获取frame buffer 20 | * 21 | * @return frame buffer 22 | * 23 | */ 24 | fun obtainFrameBuffer(): FrameBuffer { 25 | if (cache.isEmpty()) { 26 | cache.add(FrameBuffer()) 27 | } 28 | return cache.removeAt(0) 29 | } 30 | 31 | /** 32 | * 33 | * 向cache中归还frame buffer 34 | * 35 | * @param frameBuffer 归还的frame buffer 36 | * 37 | */ 38 | fun releaseFrameBuffer(frameBuffer: FrameBuffer) { 39 | cache.add(frameBuffer) 40 | } 41 | 42 | fun release() { 43 | cache.forEach { 44 | it.release() 45 | } 46 | cache.clear() 47 | } 48 | 49 | } 50 | 51 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/input/FusionCamera.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.input 2 | 3 | import android.graphics.SurfaceTexture 4 | import android.hardware.Camera 5 | import android.opengl.GLES11Ext 6 | import android.view.Surface 7 | import io.github.kenneycode.fusion.common.DataKeys 8 | import io.github.kenneycode.fusion.common.Size 9 | import io.github.kenneycode.fusion.context.GLContext 10 | import io.github.kenneycode.fusion.process.RenderPipeline 11 | import io.github.kenneycode.fusion.texture.Texture 12 | import io.github.kenneycode.fusion.util.GLUtil 13 | 14 | /** 15 | * 16 | * Coded by kenney 17 | * 18 | * http://www.github.com/kenneycode/fusion 19 | * 20 | * 相机输入源 21 | * 22 | */ 23 | 24 | class FusionCamera(private val config: Config) : RenderPipeline.Input { 25 | 26 | class Config { 27 | 28 | var cameraFacing = Camera.CameraInfo.CAMERA_FACING_FRONT 29 | var windowRotation = 0 30 | var desiredPreviewSize = Size(0, 0) 31 | 32 | } 33 | 34 | private lateinit var cameraTexture: Texture 35 | private lateinit var camera: Camera 36 | private lateinit var surfaceTexture: SurfaceTexture 37 | private lateinit var inputReceiver: InputReceiver 38 | private lateinit var finalPreviewSize: Size 39 | 40 | override fun start(data: MutableMap) { 41 | camera.startPreview() 42 | } 43 | 44 | override fun setInputReceiver(inputReceiver: InputReceiver) { 45 | this.inputReceiver = inputReceiver 46 | } 47 | 48 | override fun onInit(glContext: GLContext, data: MutableMap) { 49 | val cameraId = getCameraId(config.cameraFacing) 50 | camera = Camera.open(cameraId) 51 | setPreviewSize(camera.parameters) 52 | val info = Camera.CameraInfo() 53 | Camera.getCameraInfo(cameraId, info) 54 | setCameraDisplayOrientation(cameraId) 55 | cameraTexture = Texture(finalPreviewSize.width, finalPreviewSize.height, GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLUtil.createOESTexture()).apply { retain = true } 56 | surfaceTexture = SurfaceTexture(cameraTexture.texture).apply { 57 | setOnFrameAvailableListener { 58 | it.updateTexImage() 59 | val transformMatrix = FloatArray(16) 60 | it.getTransformMatrix(transformMatrix) 61 | inputReceiver.onInputReady( 62 | cameraTexture, 63 | mutableMapOf().apply { put(DataKeys.ST_MATRIX, transformMatrix) } 64 | ) 65 | } 66 | } 67 | camera.setPreviewTexture(surfaceTexture) 68 | } 69 | 70 | override fun onUpdate(data: MutableMap) { 71 | } 72 | 73 | private fun getCameraId(facing : Int) : Int { 74 | val numberOfCameras = Camera.getNumberOfCameras() 75 | for (i in 0 until numberOfCameras) { 76 | val info = Camera.CameraInfo() 77 | Camera.getCameraInfo(i, info) 78 | if (info.facing == facing) { 79 | return i 80 | } 81 | } 82 | return -1 83 | } 84 | 85 | private fun setPreviewSize(parameters: Camera.Parameters) { 86 | parameters.supportedPreviewSizes.forEach { 87 | // desiredPreviewSize是竖屏时的宽高 88 | if (it.height == config.desiredPreviewSize.width && it.width == config.desiredPreviewSize.height) { 89 | finalPreviewSize = Size(it.height, it.width) 90 | parameters.setPreviewSize(it.width, it.height) 91 | return 92 | } 93 | } 94 | finalPreviewSize = Size(parameters.supportedPreviewSizes.first().height, parameters.supportedPreviewSizes.first().width) 95 | parameters.setPreviewSize(parameters.supportedPreviewSizes.first().width, parameters.supportedPreviewSizes.first().height) 96 | } 97 | 98 | private fun setCameraDisplayOrientation(cameraId: Int) { 99 | val info = Camera.CameraInfo() 100 | Camera.getCameraInfo(cameraId, info) 101 | var degrees = 0 102 | when (config.windowRotation) { 103 | Surface.ROTATION_0 -> degrees = 0 104 | Surface.ROTATION_90 -> degrees = 90 105 | Surface.ROTATION_180 -> degrees = 180 106 | Surface.ROTATION_270 -> degrees = 270 107 | } 108 | 109 | var rotation = 0 110 | if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { 111 | rotation = (info.orientation + degrees) % 360; 112 | rotation = (360 - rotation) % 360 113 | } else { 114 | rotation = (info.orientation - degrees + 360) % 360; 115 | } 116 | camera.setDisplayOrientation(rotation) 117 | } 118 | 119 | override fun onRelease() { 120 | camera.stopPreview() 121 | camera.release() 122 | cameraTexture.release() 123 | } 124 | 125 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/input/FusionImage.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.input 2 | 3 | import android.graphics.Bitmap 4 | import io.github.kenneycode.fusion.context.GLContext 5 | 6 | import io.github.kenneycode.fusion.process.RenderPipeline 7 | import io.github.kenneycode.fusion.texture.Texture 8 | import io.github.kenneycode.fusion.texture.TexturePool 9 | import io.github.kenneycode.fusion.util.BitmapUtil 10 | import java.nio.ByteBuffer 11 | 12 | /** 13 | * 14 | * Coded by kenney 15 | * 16 | * http://www.github.com/kenneycode/fusion 17 | * 18 | * 图片输入源 19 | * 20 | */ 21 | 22 | class FusionImage(private val image: Bitmap) : RenderPipeline.Input, InputReceiver { 23 | 24 | override fun onInputReady(input: Texture, data: MutableMap) { 25 | TODO("not implemented") //To change body of created functions use File | Settings | File Templates. 26 | } 27 | 28 | private lateinit var inputReceiver: InputReceiver 29 | private lateinit var imageTexture: Texture 30 | 31 | override fun setInputReceiver(inputReceiver: InputReceiver) { 32 | this.inputReceiver = inputReceiver 33 | } 34 | 35 | override fun onInit(glContext: GLContext, data: MutableMap) { 36 | val buffer = ByteBuffer.allocate(image.width * image.height * 4) 37 | BitmapUtil.flipBitmap(image, false, true).copyPixelsToBuffer(buffer) 38 | buffer.position(0) 39 | imageTexture = TexturePool.obtainTexture(image.width, image.height).apply { 40 | retain = true 41 | setData(buffer) 42 | } 43 | } 44 | 45 | override fun onUpdate(data: MutableMap) { 46 | } 47 | 48 | override fun start(data: MutableMap) { 49 | inputReceiver.onInputReady(imageTexture) 50 | } 51 | 52 | override fun onRelease() { 53 | imageTexture.decreaseRef() 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/input/FusionVideo.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.input 2 | 3 | import android.graphics.SurfaceTexture 4 | import android.media.MediaPlayer 5 | import android.opengl.GLES11Ext 6 | import android.view.Surface 7 | import io.github.kenneycode.fusion.common.DataKeys 8 | import io.github.kenneycode.fusion.common.Size 9 | import io.github.kenneycode.fusion.context.GLContext 10 | import io.github.kenneycode.fusion.process.RenderPipeline 11 | import io.github.kenneycode.fusion.texture.Texture 12 | import io.github.kenneycode.fusion.util.GLUtil 13 | import javax.microedition.khronos.opengles.GL11Ext 14 | 15 | /** 16 | * 17 | * Coded by kenney 18 | * 19 | * http://www.github.com/kenneycode/fusion 20 | * 21 | * 视频输入源 22 | * 23 | */ 24 | 25 | class FusionVideo(private val videoPath: String) : RenderPipeline.Input { 26 | 27 | private val videoPlayer = MediaPlayer() 28 | private var decodeOutputTexture = 0 29 | private val transformMatrix = FloatArray(16) 30 | private lateinit var surfaceTexture: SurfaceTexture 31 | private lateinit var surface: Surface 32 | private lateinit var videoSize: Size 33 | private lateinit var inputReceiver: InputReceiver 34 | 35 | override fun onInit(glContext: GLContext, data: MutableMap) { 36 | decodeOutputTexture = GLUtil.createOESTexture() 37 | surfaceTexture = SurfaceTexture(decodeOutputTexture).apply { 38 | setOnFrameAvailableListener { 39 | it.updateTexImage() 40 | it.getTransformMatrix(transformMatrix) 41 | inputReceiver.onInputReady( 42 | Texture(videoSize.width, videoSize.height, GLES11Ext.GL_TEXTURE_EXTERNAL_OES, decodeOutputTexture).apply { retain = true }, 43 | mutableMapOf().apply { put(DataKeys.ST_MATRIX, transformMatrix) } 44 | ) 45 | } 46 | } 47 | surface = Surface(surfaceTexture) 48 | videoPlayer.setDataSource(videoPath) 49 | videoPlayer.setSurface(surface) 50 | videoPlayer.isLooping = true 51 | videoPlayer.prepare() 52 | videoSize = Size(videoPlayer.videoWidth, videoPlayer.videoHeight) 53 | } 54 | 55 | override fun onUpdate(data: MutableMap) { 56 | } 57 | 58 | override fun setInputReceiver(inputReceiver: InputReceiver) { 59 | this.inputReceiver = inputReceiver 60 | } 61 | 62 | override fun start(data: MutableMap) { 63 | videoPlayer.start() 64 | } 65 | 66 | override fun onRelease() { 67 | surfaceTexture.release() 68 | surface.release() 69 | videoPlayer.release() 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/input/FusionVideoDecoder.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.input 2 | 3 | import android.graphics.SurfaceTexture 4 | import android.media.MediaMetadataRetriever 5 | import android.media.MediaPlayer 6 | import android.opengl.GLES11Ext 7 | import android.os.Handler 8 | import android.os.HandlerThread 9 | import android.view.Surface 10 | import io.github.kenneycode.fusion.common.DataKeys 11 | import io.github.kenneycode.fusion.common.Size 12 | import io.github.kenneycode.fusion.context.GLContext 13 | import io.github.kenneycode.fusion.process.RenderPipeline 14 | import io.github.kenneycode.fusion.texture.Texture 15 | import io.github.kenneycode.fusion.util.GLUtil 16 | import io.github.kenneycode.videostudio.VideoDecoder 17 | import javax.microedition.khronos.opengles.GL11Ext 18 | 19 | /** 20 | * 21 | * Coded by kenney 22 | * 23 | * http://www.github.com/kenneycode/fusion 24 | * 25 | * 视频解码器 26 | * 27 | */ 28 | 29 | class FusionVideoDecoder(private val videoPath: String) : RenderPipeline.Input { 30 | 31 | private lateinit var decodeHandler: Handler 32 | private val videoDecoder = VideoDecoder() 33 | private var decodeOutputTexture = 0 34 | private val transformMatrix = FloatArray(16) 35 | private lateinit var surfaceTexture: SurfaceTexture 36 | private lateinit var videoSize: Size 37 | private lateinit var inputReceiver: InputReceiver 38 | private var callback: Callback? = null 39 | 40 | override fun onInit(glContext: GLContext, data: MutableMap) { 41 | decodeHandler = Handler(HandlerThread("DecodeHT").apply { start() }.looper) 42 | decodeOutputTexture = GLUtil.createOESTexture() 43 | surfaceTexture = SurfaceTexture(decodeOutputTexture).apply { 44 | setOnFrameAvailableListener { 45 | it.updateTexImage() 46 | it.getTransformMatrix(transformMatrix) 47 | inputReceiver.onInputReady( 48 | Texture(videoSize.width, videoSize.height, decodeOutputTexture, GLES11Ext.GL_TEXTURE_EXTERNAL_OES).apply { retain = true }, 49 | mutableMapOf().apply { put(DataKeys.ST_MATRIX, transformMatrix) } 50 | ) 51 | } 52 | } 53 | videoDecoder.init(videoPath, surfaceTexture) 54 | videoSize = Size(videoDecoder.getVideoWidth(), videoDecoder.getVideoHeight()) 55 | MediaMetadataRetriever().let { 56 | it.setDataSource(videoPath) 57 | data[DataKeys.KEY_SOURCE_VIDEO_WIDTH] = it.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH).toInt() 58 | data[DataKeys.KEY_SOURCE_VIDEO_HEIGHT] = it.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT).toInt() 59 | } 60 | } 61 | 62 | override fun onUpdate(data: MutableMap) { 63 | } 64 | 65 | override fun setInputReceiver(inputReceiver: InputReceiver) { 66 | this.inputReceiver = inputReceiver 67 | } 68 | 69 | override fun start(data: MutableMap) { 70 | decodeHandler.post { 71 | callback?.onStart() 72 | while(videoDecoder.decode()) { } 73 | callback?.onEnd() 74 | } 75 | } 76 | 77 | fun setCallback(callback: Callback) { 78 | this.callback = callback 79 | } 80 | 81 | override fun onRelease() { 82 | decodeHandler.looper.quit() 83 | surfaceTexture.release() 84 | videoDecoder.release() 85 | inputReceiver.onRelease() 86 | } 87 | 88 | interface Callback { 89 | 90 | fun onStart() 91 | fun onEnd() 92 | 93 | } 94 | 95 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/input/InputReceiver.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.input 2 | 3 | import io.github.kenneycode.fusion.context.GLContext 4 | import io.github.kenneycode.fusion.texture.Texture 5 | 6 | interface InputReceiver { 7 | 8 | fun onInit(glContext: GLContext, data: MutableMap) { } 9 | fun onInputReady(input: Texture, data: MutableMap = mutableMapOf()) 10 | fun onRelease() { } 11 | 12 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/output/FusionBitmap.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.output 2 | 3 | import android.graphics.Bitmap 4 | import io.github.kenneycode.fusion.input.InputReceiver 5 | import io.github.kenneycode.fusion.texture.Texture 6 | import io.github.kenneycode.fusion.util.GLUtil 7 | 8 | /** 9 | * 10 | * Coded by kenney 11 | * 12 | * http://www.github.com/kenneycode/fusion 13 | * 14 | * bitmap输出 15 | * 16 | */ 17 | 18 | class FusionBitmap : InputReceiver { 19 | 20 | var bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) 21 | 22 | override fun onInputReady(input: Texture, data: MutableMap) { 23 | bitmap = GLUtil.texture2Bitmap(input) 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/output/FusionVideoEncoder.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.output 2 | 3 | import android.opengl.EGL14 4 | import io.github.kenneycode.fusion.common.DataKeys 5 | import io.github.kenneycode.fusion.context.GLContext 6 | import io.github.kenneycode.fusion.input.InputReceiver 7 | import io.github.kenneycode.fusion.texture.Texture 8 | import io.github.kenneycode.videostudio.VideoEncoder 9 | 10 | /** 11 | * 12 | * Coded by kenney 13 | * 14 | * http://www.github.com/kenneycode/fusion 15 | * 16 | * 视频编码器输入源 17 | * 18 | */ 19 | 20 | class FusionVideoEncoder(private val videoPath: String) : InputReceiver { 21 | 22 | private val videoEncoder = VideoEncoder() 23 | private var index = 0 24 | 25 | override fun onInit(glContext: GLContext, data: MutableMap) { 26 | videoEncoder.init( 27 | videoPath, 28 | data[DataKeys.KEY_SOURCE_VIDEO_WIDTH] as Int, 29 | data[DataKeys.KEY_SOURCE_VIDEO_HEIGHT] as Int, 30 | glContext.getEGLContext() 31 | ) 32 | } 33 | 34 | override fun onInputReady(input: Texture, data: MutableMap) { 35 | videoEncoder.encodeFrame(input.texture, (index++) * 40 * 1000000L) 36 | } 37 | 38 | override fun onRelease() { 39 | videoEncoder.encodeFrame(0, 0) 40 | videoEncoder.release() 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/output/FusionView.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.output 2 | 3 | import android.content.Context 4 | import android.graphics.SurfaceTexture 5 | import android.opengl.EGL14 6 | import android.opengl.EGLContext 7 | import android.util.AttributeSet 8 | import android.view.Surface 9 | import android.view.TextureView 10 | import io.github.kenneycode.fusion.common.Constants 11 | import io.github.kenneycode.fusion.common.Constants.Companion.SIMPLE_FRAGMENT_SHADER 12 | import io.github.kenneycode.fusion.context.FusionGLThread 13 | import io.github.kenneycode.fusion.context.GLContext 14 | import io.github.kenneycode.fusion.input.InputReceiver 15 | import io.github.kenneycode.fusion.renderer.DisplayRenderer 16 | import io.github.kenneycode.fusion.texture.Texture 17 | import io.github.kenneycode.fusion.util.GLUtil 18 | import java.util.* 19 | 20 | /** 21 | * 22 | * Coded by kenney 23 | * 24 | * http://www.github.com/kenneycode/fusion 25 | * 26 | * 渲染显示View 27 | * 28 | */ 29 | 30 | class FusionView : TextureView, InputReceiver, GLContext { 31 | 32 | private var glThread: FusionGLThread? = null 33 | private var displayRenderer = DisplayRenderer(Constants.SIMPLE_VERTEX_SHADER, SIMPLE_FRAGMENT_SHADER) 34 | private val pendingTasks = LinkedList<() -> Unit>() 35 | private var surfaceWidth = 0 36 | private var surfaceHeight = 0 37 | private var initialized = false 38 | 39 | constructor(context: Context?) : super(context) 40 | constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) 41 | constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) 42 | 43 | init { 44 | surfaceTextureListener = object : SurfaceTextureListener { 45 | 46 | private var surface: Surface? = null 47 | 48 | override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture?, width: Int, height: Int) { 49 | } 50 | 51 | override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) { 52 | } 53 | 54 | override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean { 55 | surface?.release() 56 | return true 57 | } 58 | 59 | override fun onSurfaceTextureAvailable(surfaceTexture: SurfaceTexture?, width: Int, height: Int) { 60 | surfaceWidth = width 61 | surfaceHeight = height 62 | glThread = FusionGLThread().apply { 63 | surface = Surface(surfaceTexture) 64 | init(surface) 65 | pendingTasks.forEach { task -> 66 | post(task) 67 | } 68 | } 69 | } 70 | 71 | } 72 | } 73 | 74 | override fun onInputReady(input: Texture, data: MutableMap) { 75 | if (!initialized) { 76 | displayRenderer.init() 77 | } 78 | displayRenderer.setDisplaySize(surfaceWidth, surfaceHeight) 79 | displayRenderer.setInput(input) 80 | displayRenderer.render() 81 | glThread?.swapBuffers() 82 | } 83 | 84 | /** 85 | * 86 | * 通知渲染输出目标输入已经准备好了 87 | * 88 | * @param textures 输入textures 89 | * 90 | */ 91 | 92 | /** 93 | * 94 | * 在该view对应的GL Context中执行一个任务 95 | * 96 | * @param task 要执行的任务 97 | * 98 | */ 99 | override fun runOnGLContext(task: () -> Unit) { 100 | post { 101 | glThread.let { 102 | if (it != null) { 103 | it.post(task) 104 | } else { 105 | pendingTasks.addLast(task) 106 | } 107 | } 108 | } 109 | } 110 | 111 | override fun getEGLContext(): EGLContext { 112 | return glThread?.getEGLContext() ?: EGL14.EGL_NO_CONTEXT 113 | } 114 | 115 | /** 116 | * 117 | * detached from window回调,此时销毁资源 118 | * 119 | */ 120 | public override fun onDetachedFromWindow() { 121 | super.onDetachedFromWindow() 122 | glThread?.let { 123 | it.post({ 124 | displayRenderer.release() 125 | it.release() 126 | }) 127 | } 128 | } 129 | 130 | override fun release() { 131 | } 132 | 133 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/parameter/AttributeParameter.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.parameter 2 | 3 | import android.opengl.GLES20 4 | import java.nio.ByteBuffer 5 | import java.nio.ByteOrder 6 | 7 | import android.opengl.GLES20.GL_FLOAT 8 | import android.opengl.GLES20.glEnableVertexAttribArray 9 | import android.opengl.GLES20.glVertexAttribPointer 10 | import io.github.kenneycode.fusion.common.glCheck 11 | import io.github.kenneycode.fusion.util.Util 12 | 13 | /** 14 | * 15 | * Coded by kenney 16 | * 17 | * http://www.github.com/kenneycode/fusion 18 | * 19 | * Shader attribute参数 20 | * 21 | */ 22 | 23 | class AttributeParameter(key: String, private var value: FloatArray, private val componentCount: Int = 2) : Parameter(key) { 24 | 25 | /** 26 | * 27 | * 绑定参数 28 | * 29 | * @param program GL Program 30 | * 31 | */ 32 | override fun bind(program: Int) { 33 | if (location < 0) { 34 | glCheck { location = GLES20.glGetAttribLocation(program, key) } 35 | } 36 | Util.assert(location >= 0, "glGetAttribLocation wrong location") 37 | onBind(location) 38 | } 39 | 40 | override fun onBind(location: Int) { 41 | val vertexBuffer = ByteBuffer.allocateDirect(value.size * java.lang.Float.SIZE / 8) 42 | .order(ByteOrder.nativeOrder()) 43 | .asFloatBuffer() 44 | .apply { 45 | put(value) 46 | position(0) 47 | } 48 | glCheck { glVertexAttribPointer(location, componentCount, GL_FLOAT, false, 0, vertexBuffer) } 49 | glCheck { glEnableVertexAttribArray(location) } 50 | } 51 | 52 | override fun update(value: Any) { 53 | (value as? FloatArray)?.let { 54 | this.value = it 55 | } 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/parameter/BitmapTexture2DParameter.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.parameter 2 | 3 | import android.graphics.Bitmap 4 | import android.opengl.GLES20.* 5 | import android.util.Log 6 | import io.github.kenneycode.fusion.util.GLUtil 7 | import io.github.kenneycode.fusion.util.Util 8 | 9 | /** 10 | * 11 | * Coded by kenney 12 | * 13 | * http://www.github.com/kenneycode/fusion 14 | * 15 | * Bitmap shader uniform sampler2D 参数 16 | * 17 | */ 18 | 19 | class BitmapTexture2DParameter(key: String, private var value: Bitmap, index: Int) : Texture2DParameter(key, 0, index) { 20 | 21 | override fun onBind(location: Int) { 22 | Util.assert(!value.isRecycled, "bitmap is recycled") 23 | super.update(GLUtil.bitmap2Texture(value, true)) 24 | super.onBind(location) 25 | } 26 | 27 | override fun update(value: Any) { 28 | (value as? Bitmap)?.let { 29 | if (this.value != it) { 30 | GLUtil.deleteTexture(getValue() as Int) 31 | } 32 | this.value = it 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/parameter/FloatArrayParameter.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.parameter 2 | 3 | import android.opengl.GLES20.* 4 | import io.github.kenneycode.fusion.common.glCheck 5 | 6 | /** 7 | * 8 | * Coded by kenney 9 | * 10 | * http://www.github.com/kenneycode/fusion 11 | * 12 | * Shader float数组参数 13 | * 14 | */ 15 | 16 | class FloatArrayParameter(key: String, private var value: FloatArray) : UniformParameter(key) { 17 | 18 | override fun onBind(location: Int) { 19 | glCheck { glUniform1fv(location, value.size, value, 0) } 20 | } 21 | 22 | override fun update(value: Any) { 23 | (value as? FloatArray)?.let { 24 | this.value = it 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/parameter/FloatParameter.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.parameter 2 | 3 | import android.opengl.GLES20.* 4 | import io.github.kenneycode.fusion.common.glCheck 5 | 6 | /** 7 | * 8 | * Coded by kenney 9 | * 10 | * http://www.github.com/kenneycode/fusion 11 | * 12 | * Shader float参数 13 | * 14 | */ 15 | 16 | class FloatParameter(key: String, private var value: Float) : UniformParameter(key) { 17 | 18 | override fun onBind(location: Int) { 19 | glCheck { glUniform1f(location, value) } 20 | } 21 | 22 | override fun update(value: Any) { 23 | (value as? Float)?.let { 24 | this.value = it 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/parameter/IntParameter.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.parameter 2 | 3 | import android.opengl.GLES20.glUniform1i 4 | import io.github.kenneycode.fusion.common.glCheck 5 | 6 | /** 7 | * 8 | * Coded by kenney 9 | * 10 | * http://www.github.com/kenneycode/fusion 11 | * 12 | * Shader int参数 13 | * 14 | */ 15 | 16 | class IntParameter(key: String, private var value: Int) : UniformParameter(key) { 17 | 18 | override fun onBind(location: Int) { 19 | glCheck { glUniform1i(location, value) } 20 | } 21 | 22 | override fun update(value: Any) { 23 | (value as? Int)?.let { 24 | this.value = it 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/parameter/Mat4Parameter.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.parameter 2 | 3 | import android.opengl.GLES20.glUniformMatrix4fv 4 | import io.github.kenneycode.fusion.common.glCheck 5 | 6 | /** 7 | * 8 | * Coded by kenney 9 | * 10 | * http://www.github.com/kenneycode/fusion 11 | * 12 | * 4*4 Matrix 参数 13 | * 14 | */ 15 | 16 | class Mat4Parameter(key : String, private var value : FloatArray) : UniformParameter(key) { 17 | 18 | override fun onBind(location: Int) { 19 | glCheck { glUniformMatrix4fv(location, 1, false, value, 0) } 20 | } 21 | 22 | override fun update(value: Any) { 23 | (value as? FloatArray)?.let { 24 | this.value = it 25 | } 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/parameter/OESTextureParameter.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.parameter 2 | 3 | import android.opengl.GLES11Ext 4 | import android.opengl.GLES20.* 5 | import android.opengl.GLES30 6 | import io.github.kenneycode.fusion.common.glCheck 7 | 8 | /** 9 | * 10 | * Coded by kenney 11 | * 12 | * http://www.github.com/kenneycode/fusion 13 | * 14 | * OES纹理参数 15 | * 16 | */ 17 | 18 | class OESTextureParameter(key : String, private var value : Int) : UniformParameter(key) { 19 | 20 | override fun onBind(location: Int) { 21 | glCheck { glActiveTexture(GLES30.GL_TEXTURE0) } 22 | glCheck { glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, value) } 23 | glCheck { glUniform1i(location, 0) } 24 | } 25 | 26 | override fun update(value: Any) { 27 | (value as? Int)?.let { 28 | this.value = it 29 | } 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/parameter/Parameter.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.parameter 2 | 3 | import io.github.kenneycode.fusion.common.Constants 4 | 5 | /** 6 | * 7 | * Coded by kenney 8 | * 9 | * http://www.github.com/kenneycode/fusion 10 | * 11 | * Shader参数基类 12 | * 13 | */ 14 | 15 | abstract class Parameter(val key : String) { 16 | 17 | protected var location: Int = Constants.INVALID_LOCATION 18 | 19 | /** 20 | * 21 | * 绑定参数,外部的调用入口 22 | * 23 | * @param program GL Program 24 | * 25 | */ 26 | abstract fun bind(program: Int) 27 | 28 | /** 29 | * 30 | * 绑定参数,给子类的回调 31 | * 32 | * @param location 参数location 33 | * 34 | */ 35 | protected abstract fun onBind(location: Int) 36 | 37 | protected open fun getValue(): Any { return 0 } 38 | 39 | /** 40 | * 41 | * 更新参数 42 | * 43 | * @param value 新值 44 | * 45 | */ 46 | open fun update(value: Any) {} 47 | 48 | override fun equals(other: Any?): Boolean { 49 | if (this === other) { 50 | return true 51 | } 52 | if (javaClass != other?.javaClass) { 53 | return false 54 | } 55 | other as Parameter 56 | if (key != other.key) { 57 | return false 58 | } 59 | if (location != other.location) { 60 | return false 61 | } 62 | return true 63 | } 64 | 65 | override fun hashCode(): Int { 66 | var result = key.hashCode() 67 | result = 31 * result + location 68 | return result 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/parameter/Texture2DParameter.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.parameter 2 | 3 | import android.opengl.GLES20.* 4 | import android.util.Log 5 | import io.github.kenneycode.fusion.common.glCheck 6 | import io.github.kenneycode.fusion.util.GLUtil 7 | import io.github.kenneycode.fusion.util.Util 8 | 9 | /** 10 | * 11 | * Coded by kenney 12 | * 13 | * http://www.github.com/kenneycode/fusion 14 | * 15 | * Shader uniform sampler2D参数 16 | * 17 | */ 18 | 19 | open class Texture2DParameter(key: String, private var value: Int, private val index: Int) : UniformParameter(key) { 20 | 21 | override fun onBind(location: Int) { 22 | glCheck { glActiveTexture(GL_TEXTURE0 + index) } 23 | glCheck { glBindTexture(GL_TEXTURE_2D, value) } 24 | glCheck { glUniform1i(location, index) } 25 | } 26 | 27 | override fun getValue(): Any { 28 | return value 29 | } 30 | 31 | override fun update(value: Any) { 32 | (value as? Int)?.let { 33 | this.value = it 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/parameter/UniformParameter.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.parameter 2 | 3 | import android.opengl.GLES20 4 | import io.github.kenneycode.fusion.common.glCheck 5 | import io.github.kenneycode.fusion.util.Util 6 | 7 | /** 8 | * 9 | * Coded by kenney 10 | * 11 | * http://www.github.com/kenneycode/fusion 12 | * 13 | * Shader uniform参数基类 14 | * 15 | */ 16 | 17 | abstract class UniformParameter(key : String) : Parameter(key) { 18 | 19 | /** 20 | * 21 | * 绑定参数 22 | * 23 | * @param program GL Program 24 | * 25 | */ 26 | override fun bind(program: Int) { 27 | if (location < 0) { 28 | glCheck { location = GLES20.glGetUniformLocation(program, key) } 29 | } 30 | Util.assert(location >= 0, "glGetUniformLocation error") 31 | onBind(location) 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/process/RenderChain.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.process 2 | 3 | import io.github.kenneycode.fusion.renderer.Renderer 4 | import io.github.kenneycode.fusion.texture.Texture 5 | import io.github.kenneycode.fusion.util.Util 6 | 7 | /** 8 | * 9 | * Coded by kenney 10 | * 11 | * http://www.github.com/kenneycode/fusion 12 | * 13 | * 渲染过程管理类,将Renderer按单链的方式连接,并执行渲染过程 14 | * 15 | */ 16 | 17 | open class RenderChain : Renderer { 18 | 19 | private val renderGraph = RenderGraph() 20 | private var tailRendererId: String? = null 21 | 22 | /** 23 | * 24 | * 初始化,会对chain中所有Node都调用其初始化方法 25 | * 26 | */ 27 | override fun init() { 28 | renderGraph.init() 29 | } 30 | 31 | /** 32 | * 33 | * 更新数据,会对chain中所有Node都调用其更新数据的方法 34 | * 35 | * @param data 数据 36 | * 37 | * @return 是否需要执行当前渲染 38 | * 39 | */ 40 | override fun update(data: MutableMap): Boolean { 41 | return renderGraph.update(data) 42 | } 43 | 44 | /** 45 | * 46 | * 添加一个Renderer 47 | * 48 | * @param renderer Renderer 49 | * 50 | * @return 返回此RenderChain 51 | * 52 | */ 53 | fun addRenderer(renderer: Renderer, id: String = Util.genId(renderer)): RenderChain { 54 | renderGraph.addRenderer(renderer, id) 55 | tailRendererId?.let { 56 | renderGraph.connectRenderer(it, id) 57 | } 58 | tailRendererId = id 59 | return this 60 | } 61 | 62 | /** 63 | * 64 | * 删除一个 renderer 65 | * 66 | * @param id renderer id 67 | * 68 | */ 69 | fun removeRenderer(id: String) { 70 | renderGraph.removeRenderer(id) 71 | } 72 | 73 | /** 74 | * 75 | * 设置输入 76 | * 77 | * @param frameBuffer 输入FrameBuffer 78 | * 79 | */ 80 | override fun setInput(texture: Texture) { 81 | renderGraph.setInput(texture) 82 | } 83 | 84 | /** 85 | * 86 | * 设置输入 87 | * 88 | * @param frameBuffers 输入FrameBuffer 89 | */ 90 | override fun setInput(textures: List) { 91 | renderGraph.setInput(textures) 92 | } 93 | 94 | override fun getOutput(): Texture? { 95 | return renderGraph.getOutput() 96 | } 97 | 98 | /** 99 | * 100 | * 设置输出 101 | * 102 | * @param frameBuffer 输出FrameBuffer 103 | * 104 | */ 105 | override fun setOutput(texture: Texture?) { 106 | renderGraph.setOutput(texture) 107 | } 108 | 109 | /** 110 | * 111 | * 执行渲染 112 | * 113 | * @return 输出FrameBuffer 114 | */ 115 | override fun render() { 116 | return renderGraph.render() 117 | } 118 | 119 | fun getInput(): List { 120 | return renderGraph.getInput() 121 | } 122 | 123 | /** 124 | * 125 | * 释放资源 126 | * 127 | */ 128 | override fun release() { 129 | renderGraph.release() 130 | } 131 | 132 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/process/RenderGraph.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.process 2 | 3 | import io.github.kenneycode.fusion.renderer.Renderer 4 | import io.github.kenneycode.fusion.texture.Texture 5 | import io.github.kenneycode.fusion.texture.TexturePool 6 | import io.github.kenneycode.fusion.util.Util 7 | import java.util.* 8 | 9 | /** 10 | * 11 | * Coded by kenney 12 | * 13 | * http://www.github.com/kenneycode/fusion 14 | * 15 | * 渲染过程管理类,将Renderer按指定规则连接成Graph,并执行渲染过程 16 | * 17 | */ 18 | 19 | class RenderGraph : Renderer { 20 | 21 | private var input = mutableListOf() 22 | private var output: Texture? = null 23 | private val rendererNodeMap = HashMap() 24 | private val startNodes = mutableListOf() 25 | 26 | /** 27 | * 28 | * 添加renderer 29 | * 30 | * @param renderer 根Renderer 31 | * 32 | * @return 返回此RenderGraph 33 | * 34 | */ 35 | fun addRenderer(renderer: Renderer, id: String = Util.genId(renderer)): RenderGraph { 36 | rendererNodeMap[id] = Node(renderer, id).also { 37 | startNodes.add(it) 38 | } 39 | return this 40 | } 41 | 42 | /** 43 | * 44 | * 为一个Renderer连接一个后续Renderer 45 | * 46 | * @param pre 前一个Renderer id 47 | * @param next 后一个Renderer id 48 | * 49 | * @return 返回此RenderGraph 50 | * 51 | */ 52 | fun connectRenderer(preId: String, nextId: String): RenderGraph { 53 | val preNode = rendererNodeMap[preId]!! 54 | val nextNode = rendererNodeMap[nextId]!! 55 | preNode.addNext(nextNode) 56 | nextNode.layer = if (preNode.layer + 1 > nextNode.layer) { 57 | preNode.layer + 1 58 | } else { 59 | nextNode.layer 60 | } 61 | startNodes.remove(nextNode) 62 | return this 63 | } 64 | 65 | /** 66 | * 67 | * 初始化,会对Graph中所有Node都调用其初始化方法 68 | * 69 | */ 70 | override fun init() { 71 | layerTraversal(startNodes) { node -> 72 | node.renderer.init() 73 | } 74 | } 75 | 76 | /** 77 | * 78 | * 更新数据,会对Graph中所有Node都调用其更新数据的方法 79 | * 80 | * @param data 数据 81 | * 82 | * @return 是否需要执行当前渲染 83 | * 84 | */ 85 | override fun update(data: MutableMap): Boolean { 86 | layerTraversal(startNodes) { node -> 87 | node.needRender = node.renderer.update(data) 88 | } 89 | return true 90 | } 91 | 92 | /** 93 | * 94 | * 设置输入 95 | * 96 | * @param frameBuffer 输入FrameBuffer 97 | * 98 | */ 99 | override fun setInput(texture: Texture) { 100 | setInput(listOf(texture)) 101 | } 102 | 103 | /** 104 | * 105 | * 设置输入 106 | * 107 | * @param frameBuffers 输入FrameBuffer 108 | */ 109 | override fun setInput(textures: List) { 110 | input.apply { 111 | clear() 112 | addAll(textures) 113 | } 114 | } 115 | 116 | override fun getOutput(): Texture? { 117 | return output 118 | } 119 | 120 | /** 121 | * 122 | * 设置输出 123 | * 124 | * @param frameBuffer 输出FrameBuffer 125 | * 126 | */ 127 | override fun setOutput(texture: Texture?) { 128 | output = texture 129 | } 130 | 131 | /** 132 | * 133 | * 执行渲染 134 | * 135 | * @return 输出FrameBuffer 136 | * 137 | */ 138 | override fun render() { 139 | output = renderByLayer(input) 140 | } 141 | 142 | /** 143 | * 144 | * 按层序渲染 145 | * 146 | * @param input 输入textures 147 | * 148 | * @return 输出texture 149 | * 150 | */ 151 | private fun renderByLayer(input: List): Texture? { 152 | var intermediateTexture: Texture? = null 153 | input.forEach { it.increaseRef() } 154 | startNodes.forEach { it.input.addAll(input) } 155 | layerTraversal(startNodes) { node -> 156 | intermediateTexture = if (node.needRender) { 157 | node.renderer.setInput(node.input) 158 | node.renderer.setOutput( 159 | if (node.nextNodes.isEmpty()) { 160 | output 161 | } else { 162 | TexturePool.obtainTexture(node.input.first().width, node.input.first().height) 163 | } 164 | ) 165 | node.renderer.render() 166 | node.renderer.getOutput() 167 | } else { 168 | node.input.first().apply { increaseRef() } 169 | } 170 | node.input.forEach { 171 | it.decreaseRef() 172 | } 173 | node.input.clear() 174 | intermediateTexture?.let { outputTexture -> 175 | if (node.nextNodes.isNotEmpty()) { 176 | outputTexture.increaseRef(node.nextNodes.size - 1) 177 | node.nextNodes.forEach { nextNode -> 178 | nextNode.input.add(outputTexture) 179 | } 180 | } 181 | } 182 | } 183 | return intermediateTexture 184 | } 185 | 186 | /** 187 | * 188 | * 层序遍历 189 | * 190 | * @param rootNode 根node 191 | * @param nodeProcessor 节点处理器 192 | * 193 | */ 194 | private fun layerTraversal(startNodes: MutableList, nodeProcessor: (node: Node) -> Unit) { 195 | val traversalQueue = LinkedList() 196 | val queuedNodes = mutableSetOf() 197 | val layerNodes = mutableListOf>() 198 | traversalQueue.addAll(startNodes) 199 | while (!traversalQueue.isEmpty()) { 200 | val node = traversalQueue.removeFirst() 201 | if (node.layer >= layerNodes.size) { 202 | layerNodes.add(mutableListOf()) 203 | } 204 | layerNodes[node.layer].add(node); 205 | node.nextNodes.forEach { nextNode -> 206 | if (!queuedNodes.contains(nextNode)) { 207 | traversalQueue.addLast(nextNode) 208 | queuedNodes.add(nextNode) 209 | } 210 | } 211 | } 212 | layerNodes.forEach { layer -> 213 | layer.forEach { node -> 214 | nodeProcessor(node) 215 | } 216 | } 217 | } 218 | 219 | fun getInput(): List { 220 | return input 221 | } 222 | 223 | /** 224 | * 225 | * 从 graph 中删除一个 renderer 226 | * 如果被删除的 node 没有前置节点,直接删除 227 | * 如果被删除的 node 只有一个前置节点,则直接将前置节点与后置节点连接 228 | * 否则删除该 node 的同时其所有连接关系 229 | * 230 | * @param id renderer id 231 | * 232 | */ 233 | fun removeRenderer(id: String) { 234 | if (!startNodes.removeAll { it.id == id }) { 235 | val preNodes = mutableListOf() 236 | val nextNodes = rendererNodeMap[id]!!.nextNodes 237 | layerTraversal(startNodes) { node -> 238 | if (node.nextNodes.any { it.id == id }) { 239 | preNodes.add(node) 240 | } 241 | } 242 | if (preNodes.size == 1) { 243 | preNodes.first().nextNodes.clear() 244 | preNodes.first().nextNodes.addAll(nextNodes) 245 | } else { 246 | preNodes.forEach { preNode -> 247 | preNode.nextNodes.removeAll { it.id == id } 248 | } 249 | } 250 | } 251 | rendererNodeMap.remove(id) 252 | } 253 | 254 | /** 255 | * 256 | * 重置 graph node 的连接关系 257 | * 258 | */ 259 | fun reset() { 260 | layerTraversal(startNodes) { node -> 261 | node.nextNodes.clear() 262 | } 263 | startNodes.clear() 264 | } 265 | 266 | /** 267 | * 268 | * 释放资源 269 | * 270 | */ 271 | override fun release() { 272 | layerTraversal(startNodes) { node -> 273 | node.renderer.release() 274 | } 275 | } 276 | 277 | /** 278 | * 279 | * Graph Node 280 | * 281 | */ 282 | private open inner class Node(val renderer: Renderer, val id: String, var layer: Int = 0) { 283 | 284 | var needRender = true 285 | var input = mutableListOf() 286 | var nextNodes= mutableListOf() 287 | 288 | fun addNext(nextNode: Node) { 289 | nextNodes.add(nextNode) 290 | } 291 | 292 | } 293 | 294 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/process/RenderPipeline.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.process 2 | 3 | import io.github.kenneycode.fusion.context.GLContext 4 | import io.github.kenneycode.fusion.context.SimpleGLContext 5 | import io.github.kenneycode.fusion.input.InputReceiver 6 | import io.github.kenneycode.fusion.renderer.Renderer 7 | import io.github.kenneycode.fusion.texture.Texture 8 | import io.github.kenneycode.fusion.texture.TexturePool 9 | import java.util.concurrent.Semaphore 10 | 11 | /** 12 | * 13 | * Coded by kenney 14 | * 15 | * http://www.github.com/kenneycode/fusion 16 | * 17 | * 渲染管线 18 | * 19 | */ 20 | 21 | class RenderPipeline private constructor() : InputReceiver { 22 | 23 | companion object { 24 | 25 | fun input(input: Input): RenderPipeline { 26 | return RenderPipeline().apply { 27 | input.setInputReceiver(this) 28 | this.input = input 29 | } 30 | } 31 | 32 | } 33 | 34 | private var glContext: GLContext? = null 35 | private lateinit var input: Input 36 | private lateinit var output: InputReceiver 37 | private lateinit var renderer: Renderer 38 | 39 | fun renderWith(renderer: Renderer): RenderPipeline { 40 | this.renderer = renderer 41 | return this 42 | } 43 | 44 | fun useContext(glContext: GLContext): RenderPipeline { 45 | this.glContext = glContext 46 | return this 47 | } 48 | 49 | fun output(output: InputReceiver): RenderPipeline { 50 | this.output = output 51 | return this 52 | } 53 | 54 | fun init(data: MutableMap = mutableMapOf()) { 55 | input.onInit(glContext!!, data) 56 | output.onInit(glContext!!, data) 57 | renderer.init() 58 | } 59 | 60 | fun update(data: MutableMap = mutableMapOf()) { 61 | input.onUpdate(data) 62 | renderer.update(data) 63 | } 64 | 65 | fun render(input: Texture) { 66 | val outputTexture = TexturePool.obtainTexture(input.width, input.height) 67 | renderer.setInput(input) 68 | renderer.setOutput(outputTexture) 69 | renderer.render() 70 | renderer.getOutput()?.let { 71 | output.onInputReady(it) 72 | } 73 | outputTexture.decreaseRef() 74 | } 75 | 76 | fun start(data: MutableMap = mutableMapOf()) { 77 | executeOnGLContext { 78 | init() 79 | input.start(data) 80 | } 81 | } 82 | 83 | override fun onInputReady(input: Texture, data: MutableMap) { 84 | update(data) 85 | render(input) 86 | } 87 | 88 | private fun executeOnGLContext(task: () -> Unit) { 89 | if (glContext == null) { 90 | glContext = SimpleGLContext() 91 | } 92 | glContext?.runOnGLContext { 93 | task() 94 | } 95 | } 96 | 97 | 98 | /** 99 | * 100 | * 触发一次更新和渲染 101 | * 102 | */ 103 | fun refresh() { 104 | 105 | } 106 | 107 | /** 108 | * 109 | * 等待RenderPipeline执行完成 110 | * 111 | */ 112 | fun flush() { 113 | val lock = Semaphore(0) 114 | executeOnGLContext { 115 | lock.release() 116 | } 117 | lock.acquire() 118 | } 119 | 120 | fun release() { 121 | executeOnGLContext { 122 | input.onRelease() 123 | output.onRelease() 124 | renderer.release() 125 | glContext?.release() 126 | } 127 | } 128 | 129 | interface Input { 130 | 131 | fun start(data: MutableMap) 132 | fun setInputReceiver(inputReceiver: InputReceiver) 133 | fun onInit(glContext: GLContext, data: MutableMap) 134 | fun onUpdate(data: MutableMap) 135 | fun onRelease() 136 | 137 | } 138 | 139 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/program/GLProgram.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.program 2 | 3 | import android.opengl.GLES20.* 4 | import android.util.Log 5 | import io.github.kenneycode.fusion.common.Ref 6 | import io.github.kenneycode.fusion.common.Shader 7 | import io.github.kenneycode.fusion.common.glCheck 8 | import io.github.kenneycode.fusion.parameter.Parameter 9 | 10 | import io.github.kenneycode.fusion.util.Util 11 | 12 | /** 13 | * 14 | * Coded by kenney 15 | * 16 | * http://www.github.com/kenneycode/fusion 17 | * 18 | * GL Program类,可通过GLProgramCache获取 19 | * 20 | */ 21 | 22 | class GLProgram(val shader: Shader) : Ref() { 23 | 24 | var program = 0 25 | 26 | /** 27 | * 28 | * 初始化方法,重复调用不会初始化多次 29 | * 30 | */ 31 | fun init() { 32 | 33 | if (!glIsProgram(program)) { 34 | 35 | program = glCreateProgram() 36 | 37 | var vertexShader = GL_NONE 38 | glCheck { vertexShader = glCreateShader(GL_VERTEX_SHADER) } 39 | var fragmentShader = GL_NONE 40 | glCheck { fragmentShader = glCreateShader(GL_FRAGMENT_SHADER) } 41 | 42 | glCheck { glShaderSource(vertexShader, shader!!.vertexShader) } 43 | glCheck { glShaderSource(fragmentShader, shader!!.fragmentShader) } 44 | 45 | glCheck { glCompileShader(vertexShader) } 46 | glCheck { 47 | glGetShaderInfoLog(vertexShader).let { 48 | if (it.isNotEmpty()) { 49 | Log.e("debug", "vertex shader info log = $it") 50 | } 51 | } 52 | } 53 | 54 | glCheck { glCompileShader(fragmentShader) } 55 | glCheck { 56 | glGetShaderInfoLog(fragmentShader).let { 57 | if (it.isNotEmpty()) { 58 | Log.e("debug", "fragment shader info log = $it") 59 | } 60 | } 61 | } 62 | 63 | glCheck { glAttachShader(program, vertexShader) } 64 | glCheck { glAttachShader(program, fragmentShader) } 65 | 66 | glCheck { glLinkProgram(program) } 67 | glCheck { 68 | glGetProgramInfoLog(program).let { 69 | if (it.isNotEmpty()) { 70 | Log.e("debug", "program info log = $it") 71 | } 72 | } 73 | } 74 | 75 | glCheck { glDeleteShader(vertexShader) } 76 | glCheck { glDeleteShader(fragmentShader) } 77 | 78 | } 79 | 80 | } 81 | 82 | /** 83 | * 84 | * 将参数绑定到此GL Program 85 | * 86 | * @param parameters 要绑定的参数 87 | * 88 | */ 89 | fun bindParameters(parameters: Collection) { 90 | glUseProgram(program) 91 | parameters.forEach { p -> 92 | p.bind(program) 93 | } 94 | } 95 | 96 | /** 97 | * 98 | * 减少引用计数,当引用计数为0时放回GLProgramCache 99 | * 100 | */ 101 | override fun decreaseRef() { 102 | super.decreaseRef() 103 | if (refCount == 0) { 104 | GLProgramPool.releaseGLProgram(this) 105 | } 106 | } 107 | 108 | /** 109 | * 110 | * 释放资源 111 | * 112 | */ 113 | fun release() { 114 | if (glIsProgram(program)) { 115 | glDeleteProgram(program) 116 | } 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/program/GLProgramPool.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.program 2 | 3 | import io.github.kenneycode.fusion.common.Shader 4 | 5 | /** 6 | * 7 | * Coded by kenney 8 | * 9 | * http://www.github.com/kenneycode/fusion 10 | * 11 | * GL Program缓存池 12 | * 13 | */ 14 | 15 | object GLProgramPool { 16 | 17 | private val cache = mutableMapOf() 18 | 19 | /** 20 | * 21 | * 获取指定Shader对应的GL Program,如果缓存池中没有,将创建 22 | * 23 | * @param shader shader 24 | * 25 | * @return Shader对应的GL Program 26 | * 27 | */ 28 | fun obtainGLProgram(shader: Shader): GLProgram { 29 | if (!cache.containsKey(shader)) { 30 | cache[shader] = GLProgram(shader) 31 | } else { 32 | cache[shader]!!.increaseRef() 33 | } 34 | return cache[shader]!! 35 | } 36 | 37 | /** 38 | * 39 | * 将GL Program放回cache 40 | * 41 | * @param glProgram 要放回的GL Program 42 | * 43 | */ 44 | fun releaseGLProgram(glProgram: GLProgram) { 45 | cache[glProgram.shader] = glProgram 46 | } 47 | 48 | /** 49 | * 50 | * 释放资源 51 | * 52 | */ 53 | fun release() { 54 | cache.values.forEach { glProgram -> 55 | glProgram.release() 56 | } 57 | cache.clear() 58 | } 59 | 60 | } 61 | 62 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/renderer/CropRenderer.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.renderer 2 | 3 | import android.graphics.RectF 4 | 5 | /** 6 | * 7 | * Coded by kenney 8 | * 9 | * http://www.github.com/kenneycode/fusion 10 | * 11 | * 裁剪renderer 12 | * 13 | */ 14 | 15 | class CropRenderer : SimpleRenderer() { 16 | 17 | private val cropRect = RectF(0f, 1f, 1f, 0f) 18 | 19 | /** 20 | * 21 | * 设置纹理裁剪框(在纹理坐标系下) 22 | * 23 | * @param left 裁剪框左坐标 24 | * @param top 裁剪框上坐标 25 | * @param right 裁剪框右坐标 26 | * @param bottom 裁剪框下坐标 27 | * 28 | */ 29 | fun setCropRect(left: Float, top: Float, right: Float, bottom: Float) { 30 | cropRect.left = left 31 | cropRect.top = top 32 | cropRect.right = right 33 | cropRect.bottom = bottom 34 | } 35 | 36 | override fun bindParameters() { 37 | setTextureCoordinates(floatArrayOf(cropRect.left, cropRect.bottom, cropRect.left, cropRect.top, cropRect.right, cropRect.top, cropRect.left, cropRect.bottom, cropRect.right, cropRect.top, cropRect.right, cropRect.bottom)) 38 | super.bindParameters() 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/renderer/DisplayRenderer.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.renderer 2 | 3 | import android.graphics.RectF 4 | import android.opengl.GLES20.* 5 | import io.github.kenneycode.fusion.common.Constants 6 | import io.github.kenneycode.fusion.common.DataKeys 7 | import io.github.kenneycode.fusion.common.glCheck 8 | 9 | /** 10 | * 11 | * Coded by kenney 12 | * 13 | * http://www.github.com/kenneycode/fusion 14 | * 15 | * 显示渲染器,渲染到0号FrameBuffer,通常是屏幕 16 | * 17 | */ 18 | 19 | class DisplayRenderer(vertexShader: String = Constants.MVP_VERTEX_SHADER, fragmentShader: String = Constants.SIMPLE_FRAGMENT_SHADER) : SimpleRenderer(vertexShader, fragmentShader) { 20 | 21 | enum class DisplayMode { 22 | 23 | CenterCrop, 24 | FitCenter 25 | 26 | } 27 | 28 | private var displayWidth: Int = 0 29 | private var displayHeight: Int = 0 30 | private var displayMode = DisplayMode.CenterCrop 31 | 32 | fun setDisplaySize(width: Int, height: Int) { 33 | displayWidth = width 34 | displayHeight = height 35 | } 36 | 37 | fun setDisplayMode(displayMode: DisplayMode) { 38 | this.displayMode = displayMode 39 | } 40 | 41 | override fun update(data: MutableMap): Boolean { 42 | setDisplaySize(data[DataKeys.KEY_DISPLAY_WIDTH] as Int, data[DataKeys.KEY_DISPLAY_HEIGHT] as Int) 43 | return true 44 | } 45 | 46 | override fun bindParameters() { 47 | val inputWidth = input.first().width 48 | val inputHeight = input.first().height 49 | val displayRect = RectF(-1f, 1f, 1f, -1f) 50 | val textureRect = RectF(0f, 1f, 1f, 0f) 51 | val inputRatio = inputWidth.toFloat() / inputHeight 52 | val displayRatio = displayWidth.toFloat() / displayHeight 53 | when (displayMode) { 54 | DisplayMode.CenterCrop -> { 55 | if (inputRatio < displayRatio) { 56 | textureRect.left = 0f 57 | textureRect.right = 1f 58 | textureRect.top = 1 - (1 - inputRatio / displayRatio) / 2 59 | textureRect.bottom = (1 - inputRatio / displayRatio) / 2 60 | } else { 61 | textureRect.top = 1f 62 | textureRect.bottom = 0f 63 | textureRect.left = (1 - 1 / inputRatio * displayRatio) / 2 64 | textureRect.right = 1 - (1 - 1 / inputRatio * displayRatio) / 2 65 | } 66 | } 67 | DisplayMode.FitCenter -> { 68 | if (inputRatio < displayRatio) { 69 | displayRect.bottom = -1f 70 | displayRect.top = 1f 71 | displayRect.left = -1 + (2f - 2f * inputRatio) / 2 72 | displayRect.right = 1f - (displayRect.left - (-1)) 73 | } else { 74 | displayRect.left = -1f 75 | displayRect.right = 1f 76 | displayRect.bottom = -1f + (2f - 2f / inputRatio) / 2 77 | displayRect.top = 1f - (displayRect.bottom - (-1)) 78 | } 79 | } 80 | } 81 | setPositions( 82 | floatArrayOf( 83 | displayRect.left, displayRect.bottom, 84 | displayRect.left, displayRect.top, 85 | displayRect.right, displayRect.top, 86 | displayRect.left, displayRect.bottom, 87 | displayRect.right, displayRect.top, 88 | displayRect.right, displayRect.bottom 89 | ) 90 | ) 91 | setTextureCoordinates( 92 | floatArrayOf( 93 | textureRect.left, textureRect.bottom, 94 | textureRect.left, textureRect.top, 95 | textureRect.right, textureRect.top, 96 | textureRect.left, textureRect.bottom, 97 | textureRect.right, textureRect.top, 98 | textureRect.right, textureRect.bottom 99 | ) 100 | ) 101 | super.bindParameters() 102 | } 103 | 104 | override fun bindOutput() { 105 | glCheck { glBindFramebuffer(GL_FRAMEBUFFER, 0) } 106 | glCheck { glViewport(0, 0, displayWidth, displayHeight) } 107 | } 108 | 109 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/renderer/GLRenderer.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.renderer 2 | 3 | import android.graphics.Bitmap 4 | 5 | /** 6 | * 7 | * Coded by kenney 8 | * 9 | * http://www.github.com/kenneycode/fusion 10 | * 11 | * GL渲染器接口 12 | * 13 | */ 14 | 15 | interface GLRenderer : Renderer { 16 | 17 | /** 18 | * 19 | * 初始化参数 20 | * 21 | */ 22 | fun initParameter() 23 | 24 | /** 25 | * 26 | * 设置attribute float数组 27 | * 28 | * @param key attribute参数名 29 | * @param value float数组 30 | * @param componentCount 每个顶点的成份数(一维,二维..) 31 | * 32 | */ 33 | fun setAttributeFloats(key: String, value: FloatArray, componentCount: Int = 2) 34 | 35 | /** 36 | * 37 | * 设置顶点坐标,默认是二维坐标 38 | * 39 | * @param positions 顶点坐标数组 40 | * 41 | */ 42 | fun setPositions(positions: FloatArray, componentCount: Int = 2) 43 | 44 | /** 45 | * 46 | * 设置纹理坐标 47 | * 48 | * @param textureCoordinates 纹理坐标数组 49 | * 50 | */ 51 | fun setTextureCoordinates(textureCoordinates: FloatArray) 52 | 53 | /** 54 | * 55 | * 设置 bitmap 纹理参数 56 | * 57 | * @param key 纹理参数名 58 | * @param value bitmap 59 | * 60 | */ 61 | fun setUniformBitmapTexture2D(key: String, value: Bitmap, index: Int = 0) 62 | 63 | /** 64 | * 65 | * 设置纹理参数 66 | * 67 | * @param key 纹理参数名 68 | * @param value 纹理id 69 | * 70 | */ 71 | fun setUniformTexture2D(key: String, value: Int, index: Int = 0) 72 | 73 | /** 74 | * 75 | * 设置OES纹理参数 76 | * 77 | * @param key 纹理参数名 78 | * @param value 纹理id 79 | * 80 | */ 81 | fun setUniformOESTexture(key: String, value: Int) 82 | 83 | /** 84 | * 85 | * 设置4*4 Matrix参数 86 | * 87 | * @param key 纹理参数名 88 | * @param value 4*4 Matrix 89 | * 90 | */ 91 | fun setUniformMat4(key: String, value: FloatArray) 92 | 93 | /** 94 | * 95 | * 设置float参数 96 | * 97 | * @param key float参数名 98 | * @param value float参数 99 | * 100 | */ 101 | fun setUniformFloat(key: String, value: Float) 102 | 103 | /** 104 | * 105 | * 设置float数组参数 106 | * 107 | * @param key float数组参数名 108 | * @param value float数组参数 109 | * 110 | */ 111 | fun setUniformFloatArray(key: String, value: FloatArray) 112 | 113 | /** 114 | * 115 | * 设置int参数 116 | * 117 | * @param key int参数名 118 | * @param value int参数 119 | * 120 | */ 121 | fun setUniformInt(key: String, value: Int) 122 | 123 | /** 124 | * 125 | * 设置MVP 126 | * 127 | * @param value 4*4 MVP Matrix 128 | * 129 | */ 130 | fun setMVPMatrix(value: FloatArray) 131 | 132 | /** 133 | * 134 | * @param flipX 水平翻转 135 | * @param flipY 垂直翻转 136 | * 137 | */ 138 | fun setFlip(flipX: Boolean, flipY: Boolean) 139 | 140 | /** 141 | * 142 | * 绑定输入 143 | * 144 | */ 145 | fun bindInput() 146 | 147 | /** 148 | * 149 | * 绑定输出 150 | * 151 | */ 152 | fun bindOutput() 153 | 154 | /** 155 | * 156 | * 绑定参数 157 | * 158 | */ 159 | fun bindParameters() 160 | 161 | /** 162 | * 163 | * 解绑输入 164 | * 165 | */ 166 | fun unBindInput() 167 | 168 | } 169 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/renderer/GaussianBlurRenderer.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.renderer 2 | 3 | import io.github.kenneycode.fusion.common.Constants 4 | import io.github.kenneycode.fusion.process.RenderChain 5 | import io.github.kenneycode.fusion.texture.Texture 6 | 7 | /** 8 | * 9 | * Coded by kenney 10 | * 11 | * http://www.github.com/kenneycode/fusion 12 | * 13 | * 高斯模糊 renderer 14 | * 15 | */ 16 | 17 | class GaussianBlurRenderer : RenderChain() { 18 | 19 | companion object { 20 | 21 | class ParameterKey { 22 | 23 | companion object { 24 | val WEIGHTS = "weights" 25 | val H_OFFSET = "hOffset" 26 | val V_OFFSET = "vOffset" 27 | val ORIENTATION = "orientation" 28 | } 29 | 30 | } 31 | 32 | private val HALF_SAMPLE_POINTS = 5 33 | 34 | } 35 | 36 | private val hRenderer = SimpleRenderer(Constants.SIMPLE_VERTEX_SHADER, Constants.GAUSSIAN_BLUR_FRAGMENT_SHADER) 37 | private val vRenderer = SimpleRenderer(Constants.SIMPLE_VERTEX_SHADER, Constants.GAUSSIAN_BLUR_FRAGMENT_SHADER) 38 | private var blurRadius = 3 39 | 40 | override fun init() { 41 | val weights = normalize(calculateWeights(5f)) 42 | hRenderer.apply { 43 | setUniformInt(ParameterKey.ORIENTATION, 0) 44 | setUniformFloatArray(ParameterKey.WEIGHTS, weights) 45 | addRenderer(this) 46 | } 47 | vRenderer.apply { 48 | setUniformInt(ParameterKey.ORIENTATION, 1) 49 | setUniformFloatArray(ParameterKey.WEIGHTS, weights) 50 | addRenderer(this) 51 | } 52 | super.init() 53 | } 54 | 55 | /** 56 | * 57 | * 设置模糊半径 58 | * 59 | * @param blurRadius 模糊半径 60 | * 61 | */ 62 | fun setBlurRadius(blurRadius: Int) { 63 | this.blurRadius = blurRadius 64 | } 65 | 66 | override fun render() { 67 | val inputWidth = getInput().first().width 68 | val inputHeight = getInput().first().height 69 | hRenderer.setUniformFloat(ParameterKey.H_OFFSET, blurRadius.toFloat() / inputWidth / HALF_SAMPLE_POINTS) 70 | hRenderer.setUniformFloat(ParameterKey.V_OFFSET, blurRadius.toFloat() / inputHeight / HALF_SAMPLE_POINTS) 71 | vRenderer.setUniformFloat(ParameterKey.H_OFFSET, blurRadius.toFloat() / inputWidth / HALF_SAMPLE_POINTS) 72 | vRenderer.setUniformFloat(ParameterKey.V_OFFSET, blurRadius.toFloat() / inputHeight / HALF_SAMPLE_POINTS) 73 | super.render() 74 | } 75 | 76 | private fun calculateWeights(sigma: Float): FloatArray { 77 | val weights = FloatArray(6) 78 | weights[0] = gaussianFunc(0f, 0f, sigma) 79 | for (i in 1 until 6) { 80 | weights[i] = gaussianFunc(i.toFloat(), 0f, sigma) 81 | } 82 | return weights 83 | } 84 | 85 | private fun normalize(weights: FloatArray): FloatArray { 86 | var sum = weights[0] 87 | for (i in 1 until weights.size) { 88 | sum += weights[i] * 2 89 | } 90 | for (i in weights.indices) { 91 | weights[i] /= sum 92 | } 93 | return weights 94 | } 95 | 96 | private fun gaussianFunc(x: Float, y: Float, sigma: Float): Float { 97 | return ((1f / (2.0 * Math.PI * sigma * sigma)) * Math.pow(Math.E, -(x * x + y * y) / (2.0 * sigma))).toFloat() 98 | } 99 | 100 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/renderer/LUTRenderer.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.renderer 2 | 3 | import android.graphics.Bitmap 4 | import io.github.kenneycode.fusion.common.Constants 5 | import io.github.kenneycode.fusion.common.glCheck 6 | import io.github.kenneycode.fusion.util.GLUtil 7 | 8 | /** 9 | * 10 | * Coded by kenney 11 | * 12 | * http://www.github.com/kenneycode/fusion 13 | * 14 | * LUT renderer 15 | * 16 | */ 17 | 18 | class LUTRenderer : SimpleRenderer(Constants.SIMPLE_VERTEX_SHADER, Constants.LUT_FRAGMENT_SHADER) { 19 | 20 | companion object { 21 | 22 | class ParameterKey { 23 | 24 | companion object { 25 | val STRENGTH = "strength" 26 | val LUT_TEXTURE = "u_lutTexture" 27 | } 28 | 29 | } 30 | 31 | } 32 | 33 | private lateinit var lutBitmap: Bitmap 34 | 35 | override fun init() { 36 | super.init() 37 | glCheck { setUniformFloat(ParameterKey.STRENGTH, 1f) } 38 | glCheck { setUniformTexture2D(ParameterKey.LUT_TEXTURE, GLUtil.bitmap2Texture(lutBitmap), 1) } 39 | } 40 | 41 | fun setLUTImage(lutBitmap: Bitmap) { 42 | this.lutBitmap = lutBitmap 43 | } 44 | 45 | fun setLUTStrength(strength: Float) { 46 | glCheck { setUniformFloat(ParameterKey.STRENGTH, strength) } 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/renderer/OESConvertRenderer.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.renderer 2 | 3 | import io.github.kenneycode.fusion.common.Constants 4 | import io.github.kenneycode.fusion.common.DataKeys 5 | import io.github.kenneycode.fusion.common.glCheck 6 | 7 | /** 8 | * 9 | * Coded by kenney 10 | * 11 | * http://www.github.com/kenneycode/fusion 12 | * 13 | * OES纹理转TEXTURE_2D纹理 14 | * 15 | */ 16 | 17 | class OESConvertRenderer : SimpleRenderer(Constants.OES_VERTEX_SHADER, Constants.OES_FRAGMENT_SHADER) { 18 | 19 | override fun bindInput() { 20 | glCheck { setUniformOESTexture("u_texture", input.first().texture) } 21 | } 22 | 23 | override fun update(data: MutableMap): Boolean { 24 | glCheck { setUniformMat4("u_stMatrix", data[DataKeys.ST_MATRIX] as? FloatArray ?: Constants.IDENTITY_MATRIX) } 25 | return true 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/renderer/Renderer.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.renderer 2 | 3 | import io.github.kenneycode.fusion.framebuffer.FrameBuffer 4 | import io.github.kenneycode.fusion.texture.Texture 5 | 6 | /** 7 | * 8 | * Coded by kenney 9 | * 10 | * http://www.github.com/kenneycode/fusion 11 | * 12 | * 渲染器接口 13 | * 14 | */ 15 | 16 | interface Renderer { 17 | 18 | /** 19 | * 20 | * 初始化 21 | * 22 | */ 23 | fun init() 24 | 25 | /** 26 | * 27 | * 更新数据 28 | * 29 | * @param data 传入的数据 30 | * 31 | * @return 是否执行当次渲染 32 | * 33 | */ 34 | fun update(data: MutableMap): Boolean 35 | 36 | /** 37 | * 38 | * 设置单个输入 39 | * 40 | * @param texture 输入texture 41 | * 42 | */ 43 | fun setInput(texture: Texture) 44 | 45 | /** 46 | * 47 | * 设置多个输入 48 | * 49 | * @param textures texture list 50 | * 51 | */ 52 | fun setInput(textures: List) 53 | 54 | /** 55 | * 56 | * 设置输出 57 | * 58 | * @param texture texture 59 | * 60 | */ 61 | fun setOutput(texture: Texture?) 62 | 63 | /** 64 | * 65 | * 获取输出 66 | * 67 | * @return 输出texture 68 | * 69 | */ 70 | fun getOutput(): Texture? 71 | 72 | /** 73 | * 74 | * 渲染 75 | * 76 | */ 77 | fun render() 78 | 79 | /** 80 | * 81 | * 释放资源 82 | * 83 | */ 84 | fun release() 85 | 86 | } 87 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/renderer/ScaleRenderer.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.renderer 2 | 3 | import android.opengl.GLES20 4 | import io.github.kenneycode.fusion.common.glCheck 5 | import io.github.kenneycode.fusion.framebuffer.FrameBufferPool 6 | import io.github.kenneycode.fusion.texture.TexturePool 7 | 8 | /** 9 | * 10 | * Coded by kenney 11 | * 12 | * http://www.github.com/kenneycode/fusion 13 | * 14 | * 缩放renderer 15 | * 16 | */ 17 | 18 | class ScaleRenderer : SimpleRenderer() { 19 | 20 | // 缩放比例 21 | private var scale = 1.0f 22 | 23 | override fun bindOutput() { 24 | val outputWidth = (scale * input.first().width).toInt() 25 | val outputHeight = (scale * input.first().height).toInt() 26 | getOutput()?.let { output -> 27 | val frameBuffer = FrameBufferPool.obtainFrameBuffer() 28 | if (output.width != outputWidth || output.height != outputHeight) { 29 | output.decreaseRef() 30 | val texture = TexturePool.obtainTexture(outputWidth, outputHeight).apply { 31 | setOutput(this) 32 | } 33 | frameBuffer.attachTexture(texture) 34 | } else { 35 | frameBuffer.attachTexture(output) 36 | } 37 | frameBuffer.bind() 38 | glCheck { GLES20.glViewport(0, 0, output.width, output.height) } 39 | } 40 | } 41 | 42 | fun setScale(scale: Float) { 43 | this.scale = scale 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/renderer/SimpleRenderer.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.renderer 2 | 3 | 4 | import android.graphics.Bitmap 5 | import android.opengl.GLES20.* 6 | import android.util.Log 7 | import io.github.kenneycode.fusion.common.Constants 8 | import io.github.kenneycode.fusion.common.Shader 9 | import io.github.kenneycode.fusion.common.glCheck 10 | import io.github.kenneycode.fusion.framebuffer.FrameBufferPool 11 | import io.github.kenneycode.fusion.program.GLProgram 12 | import io.github.kenneycode.fusion.program.GLProgramPool 13 | 14 | import io.github.kenneycode.fusion.parameter.* 15 | import io.github.kenneycode.fusion.texture.Texture 16 | import io.github.kenneycode.fusion.util.GLUtil 17 | import io.github.kenneycode.fusion.util.Util 18 | 19 | /** 20 | * 21 | * Coded by kenney 22 | * 23 | * http://www.github.com/kenneycode/fusion 24 | * 25 | * 简单渲染器,可继承此类进行扩展 26 | * 27 | */ 28 | 29 | open class SimpleRenderer(vertexShader: String = Constants.MVP_VERTEX_SHADER, fragmentShader: String = Constants.SIMPLE_FRAGMENT_SHADER) : GLRenderer { 30 | 31 | private val shader = Shader(vertexShader, fragmentShader) 32 | private lateinit var glProgram: GLProgram 33 | private val parameters = HashMap() 34 | private var vertexCount = 0 35 | protected val input = mutableListOf() 36 | private var output: Texture? = null 37 | 38 | /** 39 | * 40 | * 初始化 41 | * 42 | */ 43 | override fun init() { 44 | glProgram = GLProgramPool.obtainGLProgram(shader).apply { 45 | init() 46 | } 47 | initParameter() 48 | } 49 | 50 | /** 51 | * 52 | * 初始化参数 53 | * 54 | */ 55 | override fun initParameter() { 56 | } 57 | 58 | /** 59 | * 60 | * 设置attribute float数组 61 | * 62 | * @param key attribute参数名 63 | * @param value float数组 64 | * @param componentCount 每个顶点的成份数(一维,二维..) 65 | * 66 | */ 67 | override fun setAttributeFloats(key: String, value: FloatArray, componentCount: Int) { 68 | setParameter(key, value) { 69 | AttributeParameter(key, value, componentCount) 70 | } 71 | } 72 | 73 | /** 74 | * 75 | * 设置顶点坐标,默认是二维坐标 76 | * 77 | * @param positions 顶点坐标数组 78 | * 79 | */ 80 | override fun setPositions(positions: FloatArray, componentCount: Int) { 81 | setAttributeFloats(Constants.POSITION_PARAM_KEY, positions, componentCount) 82 | vertexCount = positions.size / componentCount 83 | } 84 | 85 | /** 86 | * 87 | * 设置纹理坐标 88 | * 89 | * @param textureCoordinates 纹理坐标数组 90 | * 91 | */ 92 | override fun setTextureCoordinates(textureCoordinates: FloatArray) { 93 | setAttributeFloats(Constants.TEXTURE_COORDINATE_PARAM_KEY, textureCoordinates, 2) 94 | } 95 | 96 | /** 97 | * 98 | * 设置纹理参数 99 | * 100 | * @param key 纹理参数名 101 | * @param value 纹理id 102 | * 103 | */ 104 | override fun setUniformTexture2D(key: String, value: Int, index: Int) { 105 | setParameter(key, value) { 106 | Texture2DParameter(key, value, index) 107 | } 108 | } 109 | 110 | /** 111 | * 112 | * 设置 bitmap 纹理参数 113 | * 114 | * @param key 纹理参数名 115 | * @param value bitmap 116 | * 117 | */ 118 | override fun setUniformBitmapTexture2D(key: String, value: Bitmap, index: Int) { 119 | setParameter(key, value) { 120 | BitmapTexture2DParameter(key, value, index) 121 | } 122 | } 123 | 124 | /** 125 | * 126 | * 设置OES纹理参数 127 | * 128 | * @param key 纹理参数名 129 | * @param value 纹理id 130 | * 131 | */ 132 | override fun setUniformOESTexture(key: String, value: Int) { 133 | setParameter(key, value) { 134 | OESTextureParameter(key, value) 135 | } 136 | } 137 | 138 | /** 139 | * 140 | * 设置4*4 Matrix参数 141 | * 142 | * @param key 纹理参数名 143 | * @param value 4*4 Matrix 144 | * 145 | */ 146 | override fun setUniformMat4(key: String, value: FloatArray) { 147 | setParameter(key, value) { 148 | Mat4Parameter(key, value) 149 | } 150 | } 151 | 152 | /** 153 | * 154 | * 设置float参数 155 | * 156 | * @param key float参数名 157 | * @param value float参数 158 | * 159 | */ 160 | override fun setUniformFloat(key: String, value: Float) { 161 | setParameter(key, value) { 162 | FloatParameter(key, value) 163 | } 164 | } 165 | 166 | /** 167 | * 168 | * 设置int参数 169 | * 170 | * @param key int参数名 171 | * @param value int参数 172 | * 173 | */ 174 | override fun setUniformInt(key: String, value: Int) { 175 | setParameter(key, value) { 176 | IntParameter(key, value) 177 | } 178 | } 179 | 180 | override fun setUniformFloatArray(key: String, value: FloatArray) { 181 | setParameter(key, value) { 182 | FloatArrayParameter(key, value) 183 | } 184 | } 185 | 186 | /** 187 | * 188 | * 设置MVP 189 | * 190 | * @param value 4*4 MVP Matrix 191 | * 192 | */ 193 | override fun setMVPMatrix(value: FloatArray) { 194 | setUniformMat4(Constants.MVP_MATRIX_PARAM_KEY, value) 195 | } 196 | 197 | /** 198 | * 199 | * 设置参数,如果参数已存在则更新,不存在则创建 200 | * 201 | * @param key 参数名 202 | * @param value 参数值 203 | * @param parameterCreator 参数创建器 204 | * 205 | */ 206 | private fun setParameter(key: String, value: Any, parameterCreator: () -> Parameter) { 207 | parameters[key]?.update(value) ?: let { 208 | parameters[key] = parameterCreator() 209 | } 210 | } 211 | 212 | /** 213 | * 214 | * 设置渲染翻转 215 | * 216 | * @param flipX 水平翻转 217 | * @param flipY 垂直翻转 218 | * 219 | */ 220 | override fun setFlip(flipX: Boolean, flipY: Boolean) { 221 | if (!flipX && !flipY) { 222 | setPositions(Constants.SIMPLE_VERTEX) 223 | } else if (flipX && !flipY) { 224 | setPositions(Constants.SIMPLE_VERTEX_FLIP_X) 225 | } else if (!flipX && flipY) { 226 | setPositions(Constants.SIMPLE_VERTEX_FLIP_Y) 227 | } else { 228 | setPositions(Constants.SIMPLE_VERTEX_FLIP_XY) 229 | } 230 | } 231 | 232 | /** 233 | * 234 | * 绑定输入 235 | * 236 | */ 237 | override fun bindInput() { 238 | if (input.isNotEmpty()) { 239 | var textureIndex = '0' 240 | var i = 0 241 | while (i < input.size) { 242 | var textureKey = "u_texture" 243 | if (i > 0) { 244 | textureKey += textureIndex 245 | } 246 | setUniformTexture2D(textureKey, input[i].texture) 247 | ++i 248 | ++textureIndex 249 | } 250 | } 251 | } 252 | 253 | /** 254 | * 255 | * 绑定输出 256 | * 257 | */ 258 | override fun bindOutput() { 259 | output?.let { outputTexture -> 260 | FrameBufferPool.obtainFrameBuffer().apply { 261 | attachTexture(outputTexture) 262 | bind() 263 | Util.assert(outputTexture.width > 0 && outputTexture.height > 0, "output size error") 264 | glCheck { glViewport(0, 0, outputTexture.width, outputTexture.height) } 265 | } 266 | } 267 | } 268 | 269 | /** 270 | * 271 | * 绑定渲染状态 272 | * 273 | */ 274 | protected fun bindRenderState() { 275 | glCheck { glEnable(GL_BLEND) } 276 | glCheck { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) } 277 | } 278 | 279 | /** 280 | * 281 | * 绑定参数 282 | * 283 | */ 284 | override fun bindParameters() { 285 | checkDefaultParameters() 286 | glProgram.bindParameters(parameters.values) 287 | } 288 | 289 | /** 290 | * 291 | * 给一些预定的参数设置默认值 292 | * 293 | */ 294 | private fun checkDefaultParameters() { 295 | if (GLUtil.hasAttribute(glProgram.program, Constants.POSITION_PARAM_KEY) && !parameters.containsKey(Constants.POSITION_PARAM_KEY)) { 296 | setPositions(Constants.SIMPLE_VERTEX) 297 | } 298 | if (GLUtil.hasAttribute(glProgram.program, Constants.TEXTURE_COORDINATE_PARAM_KEY) && !parameters.containsKey(Constants.TEXTURE_COORDINATE_PARAM_KEY)) { 299 | setTextureCoordinates(Constants.SIMPLE_TEXTURE_COORDINATE) 300 | } 301 | if (GLUtil.hasUniform(glProgram.program, Constants.MVP_MATRIX_PARAM_KEY) && !parameters.containsKey(Constants.MVP_MATRIX_PARAM_KEY)) { 302 | setMVPMatrix(Constants.IDENTITY_MATRIX) 303 | } 304 | } 305 | 306 | /** 307 | * 308 | * 执行渲染(draw call) 309 | * 310 | */ 311 | private fun performRendering() { 312 | glCheck { glDrawArrays(GL_TRIANGLES, 0, vertexCount) } 313 | } 314 | 315 | /** 316 | * 317 | * 解绑输入 318 | * 319 | */ 320 | override fun unBindInput() { 321 | input.clear() 322 | glCheck { glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE) } 323 | } 324 | 325 | /** 326 | * 327 | * 更新数据 328 | * 329 | * @param data 传入的数据 330 | * 331 | * @return 是否执行当次渲染 332 | * 333 | */ 334 | override fun update(data: MutableMap): Boolean { 335 | return true 336 | } 337 | 338 | /** 339 | * 340 | * 设置单个输入 341 | * 342 | * @param texture 输入texture 343 | * 344 | */ 345 | override fun setInput(texture: Texture) { 346 | setInput(listOf(texture)) 347 | } 348 | 349 | /** 350 | * 351 | * 设置多个输入 352 | * 353 | * @param textures 输入texture list 354 | * 355 | */ 356 | override fun setInput(textures: List) { 357 | input.addAll(textures) 358 | } 359 | 360 | /** 361 | * 362 | * 获取输出 363 | * 364 | * @return 输出texture 365 | * 366 | */ 367 | override fun getOutput(): Texture? { 368 | return output 369 | } 370 | 371 | /** 372 | * 373 | * 设置输出 374 | * 375 | * @param texture 输出texture 376 | * 377 | */ 378 | override fun setOutput(texture: Texture?) { 379 | output = texture 380 | } 381 | 382 | /** 383 | * 384 | * 渲染前回调 385 | * 386 | */ 387 | protected open fun beforeRender() { 388 | glCheck { glClearColor(0f, 1f, 0f, 1f) } 389 | glCheck { glClear(GL_COLOR_BUFFER_BIT) } 390 | } 391 | 392 | /** 393 | * 394 | * 渲染后回调 395 | * 396 | */ 397 | protected fun afterRender() { 398 | unBindInput() 399 | } 400 | 401 | /** 402 | * 403 | * 渲染 404 | * 405 | * 406 | */ 407 | override fun render() { 408 | bindInput() 409 | bindOutput() 410 | bindParameters() 411 | bindRenderState() 412 | beforeRender() 413 | performRendering() 414 | afterRender() 415 | } 416 | 417 | /** 418 | * 419 | * 释放资源 420 | * 421 | */ 422 | override fun release() { 423 | if (this::glProgram.isInitialized) { 424 | glProgram.decreaseRef() 425 | } 426 | } 427 | 428 | } 429 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/renderer/TextureRenderer.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.renderer 2 | 3 | import android.graphics.Bitmap 4 | import io.github.kenneycode.fusion.texture.Texture 5 | 6 | /** 7 | * 8 | * Coded by kenney 9 | * 10 | * http://www.github.com/kenneycode/fusion 11 | * 12 | * 纹理renderer 13 | * 14 | */ 15 | 16 | class TextureRenderer : SimpleRenderer() { 17 | 18 | fun setBitmap(bitmap: Bitmap) { 19 | setUniformBitmapTexture2D("u_texture", bitmap) 20 | } 21 | 22 | fun setTexture(texture: Texture) { 23 | setUniformTexture2D("u_texture", texture.texture) 24 | } 25 | 26 | /** 27 | * 28 | * 设置从 texture 中取内容的 rect 29 | * 坐标轴及坐标范围与纹理坐标系相同 30 | * 31 | * @param left 左边坐标 32 | * @param top 上边坐标 33 | * @param right 右边坐标 34 | * @param bottom 下边坐标 35 | * 36 | */ 37 | fun setTextureRect(left: Float, top: Float, right: Float, bottom: Float) { 38 | setTextureCoordinates(floatArrayOf(left, bottom, left, top, right, top, left, bottom, right, top, right, bottom)) 39 | } 40 | 41 | /** 42 | * 43 | * 设置渲染到 frame buffer 的 rect 44 | * 坐标轴及坐标范围与顶点坐标系相同 45 | * 46 | * @param left 左边坐标 47 | * @param top 上边坐标 48 | * @param right 右边坐标 49 | * @param bottom 下边坐标 50 | * 51 | */ 52 | fun setRenderRect(left: Float, top: Float, right: Float, bottom: Float) { 53 | setPositions(floatArrayOf(left, bottom, left, top, right, top, left, bottom, right, top, right, bottom)) 54 | } 55 | 56 | override fun beforeRender() { 57 | 58 | } 59 | 60 | override fun bindInput() { 61 | 62 | } 63 | 64 | override fun bindOutput() { 65 | getOutput()?.decreaseRef() 66 | setOutput(input.first().apply { increaseRef() }) 67 | super.bindOutput() 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/texture/Texture.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.texture 2 | 3 | import android.opengl.GLES11Ext.GL_TEXTURE_EXTERNAL_OES 4 | import android.opengl.GLES20.* 5 | import io.github.kenneycode.fusion.util.GLUtil 6 | import io.github.kenneycode.fusion.common.Ref 7 | import io.github.kenneycode.fusion.common.glCheck 8 | import io.github.kenneycode.fusion.util.Util 9 | import java.nio.Buffer 10 | 11 | /** 12 | * 13 | * Coded by kenney 14 | * 15 | * http://www.github.com/kenneycode/fusion 16 | * 17 | * Texture封装类 18 | * 19 | */ 20 | 21 | class Texture(val width: Int, val height: Int, val type: Int = GL_TEXTURE_2D, var texture: Int = GL_NONE) : Ref() { 22 | 23 | var retain = false 24 | 25 | fun init() { 26 | if (texture == GL_NONE) { 27 | texture = GLUtil.createTexture(type) 28 | glCheck { glBindTexture(type, texture) } 29 | glCheck { glTexImage2D(type, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null) } 30 | glCheck { glBindTexture(type, 0) } 31 | } 32 | } 33 | 34 | /** 35 | * 36 | * 绑定此FrameBuffer,绑定后将渲染到此FrameBuffer上 37 | * 38 | * @param width 宽度 39 | * @param height 高度 40 | * @param externalTexture 外部纹理,当指定外部纹理时,外部纹理会附着到此FrameBuffer上 41 | * 42 | */ 43 | fun setData(data: Buffer) { 44 | Util.assert(width > 0 && height > 0 && (type == GL_TEXTURE_2D || type == GL_TEXTURE_EXTERNAL_OES) && texture != GL_NONE, "texture error") 45 | glCheck { glBindTexture(type, texture) } 46 | glCheck { glTexImage2D(type, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data) } 47 | glCheck { glBindTexture(type, 0) } 48 | } 49 | 50 | /** 51 | * 52 | * 减少引用计数,当引用计数为0时放回FrameBufferCache 53 | * 54 | */ 55 | override fun decreaseRef() { 56 | if (!retain) { 57 | super.decreaseRef() 58 | if (refCount <= 0) { 59 | TexturePool.releaseTexture(this) 60 | } 61 | } 62 | } 63 | 64 | fun release() { 65 | if (texture != GL_NONE) { 66 | GLUtil.deleteTexture(texture) 67 | texture = 0 68 | } 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/texture/TexturePool.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.texture 2 | 3 | import android.opengl.GLES20.GL_TEXTURE_2D 4 | import io.github.kenneycode.fusion.common.Size 5 | import io.github.kenneycode.fusion.util.Util 6 | 7 | /** 8 | * 9 | * Coded by kenney 10 | * 11 | * http://www.github.com/kenneycode/fusion 12 | * 13 | * Texture缓存池 14 | * 15 | */ 16 | 17 | object TexturePool { 18 | 19 | private val cache = mutableMapOf>>() 20 | 21 | /** 22 | * 23 | * 获取指定尺寸的FrameBuffer,如果cache中不存在,会创建 24 | * 25 | * @param width 宽度 26 | * @param height 高度 27 | * 28 | * @return 对应尺寸的FrameBuffer 29 | * 30 | */ 31 | fun obtainTexture(width: Int, height: Int, type: Int = GL_TEXTURE_2D): Texture { 32 | Util.assert(width > 0 && height > 0, "texture size error") 33 | if (!cache.containsKey(type)) { 34 | cache[type] = mutableMapOf() 35 | } 36 | val size = Size(width, height) 37 | if (!cache[type]!!.containsKey(size)) { 38 | cache[type]!![size] = mutableListOf() 39 | } 40 | if (cache[type]!![size]!!.isEmpty()) { 41 | cache[type]!![size]!!.add(Texture(width, height, type).apply { 42 | init() 43 | }) 44 | } else { 45 | cache[type]!![size]!!.first().increaseRef() 46 | } 47 | return cache[type]!![size]!!.removeAt(0) 48 | } 49 | 50 | /** 51 | * 52 | * 向cache中归还FrameBuffer 53 | * 54 | * @param texture 归还的texture 55 | * 56 | */ 57 | fun releaseTexture(texture: Texture) { 58 | cache[texture.type]?.get(Size(texture.width, texture.height))?.add(texture) 59 | } 60 | 61 | fun release() { 62 | cache.entries.forEach { 63 | it.value.entries.forEach { textures -> 64 | textures.value.forEach { texture -> 65 | texture.release() 66 | } 67 | } 68 | } 69 | cache.clear() 70 | } 71 | 72 | } 73 | 74 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/util/BitmapUtil.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.util 2 | 3 | import android.content.Context 4 | import android.graphics.Bitmap 5 | import android.graphics.BitmapFactory 6 | import android.graphics.Matrix 7 | import java.io.IOException 8 | 9 | /** 10 | * 11 | * Coded by kenney 12 | * 13 | * http://www.github.com/kenneycode/fusion 14 | * 15 | * bitmap 相关 utils 16 | * 17 | */ 18 | 19 | class BitmapUtil { 20 | 21 | companion object { 22 | 23 | lateinit var context: Context 24 | 25 | fun decodeBitmapFromAssets(filename: String): Bitmap? { 26 | val options = BitmapFactory.Options() 27 | options.inSampleSize = 1 28 | try { 29 | return BitmapFactory.decodeStream(context.assets.open(filename)) 30 | } catch (e: IOException) { 31 | e.printStackTrace() 32 | } 33 | 34 | return null 35 | } 36 | 37 | fun flipBitmap(bitmap: Bitmap, flipX: Boolean, flipY: Boolean): Bitmap { 38 | val resultBitmap = Bitmap.createBitmap( 39 | bitmap, 40 | 0, 41 | 0, 42 | bitmap.width, 43 | bitmap.height, 44 | Matrix().apply { 45 | setScale( 46 | if (flipX) { -1f } else { 1f }, 47 | if (flipY) { -1f } else { 1f } 48 | ) 49 | }, 50 | false 51 | ) 52 | return if (resultBitmap != bitmap) { 53 | bitmap.recycle() 54 | resultBitmap 55 | } else { 56 | bitmap 57 | } 58 | 59 | } 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/util/GLUtil.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.util 2 | 3 | import android.graphics.Bitmap 4 | import android.opengl.GLES11Ext 5 | import android.opengl.GLES20.* 6 | import java.nio.ByteBuffer 7 | import android.opengl.Matrix 8 | import io.github.kenneycode.fusion.common.Constants 9 | import io.github.kenneycode.fusion.common.glCheck 10 | import io.github.kenneycode.fusion.texture.Texture 11 | 12 | /** 13 | * 14 | * Coded by kenney 15 | * 16 | * http://www.github.com/kenneycode/fusion 17 | * 18 | * GL util类 19 | * 20 | */ 21 | 22 | class GLUtil { 23 | 24 | companion object { 25 | 26 | /** 27 | * 28 | * 创建一个纹理 29 | * 30 | * @return 纹理id 31 | */ 32 | fun createTexture(type: Int = GL_TEXTURE_2D): Int { 33 | val textures = IntArray(1) 34 | glCheck { glGenTextures(1, textures, 0) } 35 | glCheck { glBindTexture(type, textures[0]) } 36 | glCheck { glTexParameteri(type, GL_TEXTURE_MIN_FILTER, GL_LINEAR) } 37 | glCheck { glTexParameteri(type, GL_TEXTURE_MAG_FILTER, GL_LINEAR) } 38 | glCheck { glTexParameteri(type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) } 39 | glCheck { glTexParameteri(type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) } 40 | glCheck { glBindTexture(type, 0) } 41 | return textures[0] 42 | } 43 | 44 | /** 45 | * 46 | * 创建一个OES纹理 47 | * 48 | * @return 纹理id 49 | */ 50 | fun createOESTexture() : Int { 51 | val textures = IntArray(1) 52 | glCheck { glGenTextures(textures.size, textures, 0) } 53 | glCheck { glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[0]) } 54 | glCheck { glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) } 55 | glCheck { glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) } 56 | glCheck { glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR) } 57 | glCheck { glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR) } 58 | glCheck { glBindTexture(GL_TEXTURE_2D, 0) } 59 | return textures[0] 60 | } 61 | 62 | /** 63 | * 64 | * 删除一个纹理 65 | * 66 | * @param texture 纹理id 67 | */ 68 | fun deleteTexture(texture: Int) { 69 | glCheck { glDeleteTextures(1, intArrayOf(texture), 0) } 70 | } 71 | 72 | /** 73 | * 74 | * 创建一个FrameBuffer 75 | * 76 | * @return frame buffer id 77 | */ 78 | fun createFrameBuffer(): Int { 79 | val frameBuffer = IntArray(1) 80 | glCheck { glGenFramebuffers(1, frameBuffer, 0) } 81 | return frameBuffer[0] 82 | } 83 | 84 | /** 85 | * 86 | * 删除一个FrameBuffer 87 | * 88 | * @param frameBuffer frame buffer id 89 | */ 90 | fun deleteFrameBuffer(frameBuffer: Int) { 91 | val temp = intArrayOf(frameBuffer) 92 | glCheck { glDeleteFramebuffers(1, temp, 0) } 93 | } 94 | 95 | fun bitmap2Texture(bitmap: Bitmap): Int { 96 | val texture = createTexture(GL_TEXTURE_2D) 97 | glCheck { glBindTexture(GL_TEXTURE_2D, texture) } 98 | val b = ByteBuffer.allocate(bitmap.width * bitmap.height * 4) 99 | bitmap.copyPixelsToBuffer(b) 100 | b.position(0) 101 | glCheck { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) } 102 | glCheck { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) } 103 | glCheck { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) } 104 | glCheck { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) } 105 | glCheck { glTexImage2D( 106 | GL_TEXTURE_2D, 0, GL_RGBA, bitmap.width, 107 | bitmap.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, b) } 108 | return texture 109 | } 110 | 111 | fun bitmap2Texture(bitmap: Bitmap, flipY: Boolean = false): Int { 112 | val texture = createTexture(GL_TEXTURE_2D) 113 | glCheck { glBindTexture(GL_TEXTURE_2D, texture) } 114 | val b = ByteBuffer.allocate(bitmap.width * bitmap.height * 4) 115 | if (flipY) { 116 | BitmapUtil.flipBitmap(bitmap, false, true) 117 | } else { 118 | bitmap 119 | }.copyPixelsToBuffer(b) 120 | b.position(0) 121 | glCheck { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) } 122 | glCheck { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) } 123 | glCheck { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) } 124 | glCheck { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) } 125 | glCheck { glTexImage2D( 126 | GL_TEXTURE_2D, 0, GL_RGBA, bitmap.width, 127 | bitmap.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, b) } 128 | return texture 129 | } 130 | 131 | /** 132 | * 133 | * 将bitmap转换为纹理 134 | * 135 | * @param texture 纹理id 136 | * @param bitmap bitmap 137 | * 138 | */ 139 | fun bitmap2Texture(texture: Int, bitmap: Bitmap) { 140 | glCheck { glBindTexture(GL_TEXTURE_2D, texture) } 141 | val b = ByteBuffer.allocate(bitmap.width * bitmap.height * 4) 142 | bitmap.copyPixelsToBuffer(b) 143 | b.position(0) 144 | glCheck { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) } 145 | glCheck { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) } 146 | glCheck { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) } 147 | glCheck { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) } 148 | glCheck { glTexImage2D( 149 | GL_TEXTURE_2D, 0, GL_RGBA, bitmap.width, 150 | bitmap.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, b) } 151 | } 152 | 153 | /** 154 | * 155 | * 将纹理转换为bitmap 156 | * 157 | * @param texture 纹理 158 | * 159 | */ 160 | fun texture2Bitmap(texture: Texture): Bitmap { 161 | return texture2Bitmap(texture.texture, texture.width, texture.height) 162 | } 163 | 164 | /** 165 | * 166 | * 将纹理转换为bitmap 167 | * 168 | * @param texture 纹理id 169 | * @param width 宽度 170 | * @param height 高度 171 | * 172 | */ 173 | fun texture2Bitmap(texture: Int, width: Int, height: Int): Bitmap { 174 | val buffer = ByteBuffer.allocate(width * height * 4) 175 | val frameBuffers = IntArray(1) 176 | glCheck { glGenFramebuffers(frameBuffers.size, frameBuffers, 0) } 177 | glCheck { glBindTexture(GL_TEXTURE_2D, texture) } 178 | glCheck { glBindFramebuffer(GL_FRAMEBUFFER, frameBuffers[0]) } 179 | glCheck { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0) } 180 | glCheck { glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer) } 181 | val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) 182 | buffer.position(0) 183 | bitmap.copyPixelsFromBuffer(buffer) 184 | glCheck { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0) } 185 | glCheck { glBindFramebuffer(GL_FRAMEBUFFER, 0) } 186 | glCheck { glDeleteFramebuffers(frameBuffers.size, frameBuffers, 0) } 187 | return bitmap 188 | } 189 | 190 | /** 191 | * 192 | * 将OES纹理转换为bitmap 193 | * 194 | * @param texture 纹理id 195 | * @param width 宽度 196 | * @param height 高度 197 | * 198 | */ 199 | fun oesTexture2Bitmap(texture: Int, width: Int, height: Int): Bitmap { 200 | val buffer = ByteBuffer.allocate(width * height * 4) 201 | val frameBuffers = IntArray(1) 202 | glCheck { glGenFramebuffers(frameBuffers.size, frameBuffers, 0) } 203 | glCheck { glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture) } 204 | glCheck { glBindFramebuffer(GL_FRAMEBUFFER, frameBuffers[0]) } 205 | glCheck { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture, 0) } 206 | glCheck { glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer) } 207 | val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) 208 | buffer.position(0) 209 | bitmap.copyPixelsFromBuffer(buffer) 210 | glCheck { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0, 0) } 211 | glCheck { glBindFramebuffer(GL_FRAMEBUFFER, 0) } 212 | glCheck { glDeleteFramebuffers(frameBuffers.size, frameBuffers, 0) } 213 | return bitmap 214 | } 215 | 216 | fun createMVPMatrix( 217 | translateX: Float = 0f, translateY: Float = 0f, translateZ: Float = 0f, 218 | rotateX: Float = 0f, rotateY: Float = 0f, rotateZ: Float = 0f, 219 | scaleX: Float = 1f, scaleY: Float = 1f, scaleZ: Float = 1f, 220 | cameraPositionX: Float = 0f, cameraPositionY: Float = 0f, cameraPositionZ: Float = 10f, 221 | lookAtX: Float = 0f, lookAtY: Float = 0f, lookAtZ: Float = 0f, 222 | cameraUpX: Float = 0f, cameraUpY: Float = 1f, cameraUpZ: Float = 0f, 223 | nearPlaneLeft: Float = -1f, nearPlaneTop: Float = 1f, nearPlaneRight: Float = 1f, nearPlaneBottom: Float = -1f, 224 | nearPlane: Float = 5f, 225 | farPlane: Float = 15f 226 | ): FloatArray { 227 | val modelMatrix = createModelMatrix( 228 | translateX, translateY, translateZ, 229 | rotateX, rotateY, rotateZ, 230 | scaleX, scaleY, scaleZ 231 | ) 232 | val viewMatrix = createViewMatrix( 233 | cameraPositionX, cameraPositionY, cameraPositionZ, 234 | lookAtX, lookAtY, lookAtZ, 235 | cameraUpX, cameraUpY, cameraUpZ 236 | ) 237 | val projectionMatrix = createProjectionMatrix( 238 | nearPlaneLeft, nearPlaneTop, nearPlaneRight, nearPlaneBottom, 239 | nearPlane, 240 | farPlane 241 | ) 242 | return Constants.IDENTITY_MATRIX.apply { 243 | Matrix.multiplyMM(this, 0, viewMatrix, 0, modelMatrix, 0) 244 | Matrix.multiplyMM(this, 0, projectionMatrix, 0, this, 0) 245 | } 246 | } 247 | 248 | fun createModelMatrix( 249 | translateX: Float, translateY: Float, translateZ: Float, 250 | rotateX: Float, rotateY: Float, rotateZ: Float, 251 | scaleX: Float, scaleY: Float, scaleZ: Float 252 | ): FloatArray { 253 | val translateMatrix = Constants.IDENTITY_MATRIX 254 | val rotateMatrix = Constants.IDENTITY_MATRIX 255 | val scaleMatrix = Constants.IDENTITY_MATRIX 256 | Matrix.translateM(translateMatrix, 0, translateX, translateY, translateZ) 257 | Matrix.rotateM(rotateMatrix, 0, rotateX, 1f, 0f, 0f) 258 | Matrix.rotateM(rotateMatrix, 0, rotateY, 0f, 1f, 0f) 259 | Matrix.rotateM(rotateMatrix, 0, rotateZ, 0f, 0f, 1f) 260 | Matrix.scaleM(scaleMatrix, 0, scaleX, scaleY, scaleZ) 261 | return Constants.IDENTITY_MATRIX.apply { 262 | Matrix.multiplyMM(this, 0, rotateMatrix, 0, scaleMatrix, 0) 263 | Matrix.multiplyMM(this, 0, this, 0, translateMatrix, 0) 264 | } 265 | } 266 | 267 | fun createViewMatrix( 268 | cameraPositionX: Float, cameraPositionY: Float, cameraPositionZ: Float, 269 | lookAtX: Float, lookAtY: Float, lookAtZ: Float, 270 | cameraUpX: Float, cameraUpY: Float, cameraUpZ: Float 271 | ): FloatArray { 272 | return Constants.IDENTITY_MATRIX.apply { 273 | Matrix.setLookAtM( 274 | this, 275 | 0, 276 | cameraPositionX, cameraPositionY, cameraPositionZ, 277 | lookAtX, lookAtY, lookAtZ, 278 | cameraUpX, cameraUpY, cameraUpZ 279 | ) 280 | } 281 | } 282 | 283 | fun createProjectionMatrix( 284 | nearPlaneLeft: Float, nearPlaneTop: Float, nearPlaneRight: Float, nearPlaneBottom: Float, 285 | nearPlane: Float, 286 | farPlane: Float 287 | ): FloatArray { 288 | return Constants.IDENTITY_MATRIX.apply { 289 | Matrix.frustumM( 290 | this, 291 | 0, 292 | nearPlaneLeft, nearPlaneRight, nearPlaneBottom, nearPlaneTop, 293 | nearPlane, 294 | farPlane 295 | ) 296 | } 297 | } 298 | 299 | fun hasAttribute(program: Int, attributeName: String): Boolean { 300 | var ret = false 301 | glCheck { ret = glGetAttribLocation(program, attributeName) >= 0 } 302 | return ret 303 | } 304 | 305 | fun hasUniform(program: Int, attributeName: String): Boolean { 306 | var ret = false 307 | glCheck { ret = glGetUniformLocation(program, attributeName) >= 0 } 308 | return ret 309 | } 310 | 311 | } 312 | 313 | } 314 | -------------------------------------------------------------------------------- /libfusion/src/main/java/io/github/kenneycode/fusion/util/Util.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion.util 2 | 3 | import java.lang.AssertionError 4 | import java.lang.RuntimeException 5 | 6 | /** 7 | * 8 | * Coded by kenney 9 | * 10 | * http://www.github.com/kenneycode/fusion 11 | * 12 | * util类 13 | * 14 | */ 15 | 16 | class Util { 17 | 18 | companion object { 19 | 20 | fun assert(v: Boolean, msg: String = "") { 21 | if (!v) { 22 | throw AssertionError(msg) 23 | } 24 | } 25 | 26 | fun genId(obj: Any): String { 27 | return "${obj.javaClass.simpleName}@${Integer.toHexString(System.identityHashCode(obj))}" 28 | } 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /libfusion/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | libfusion 3 | 4 | -------------------------------------------------------------------------------- /libfusion/src/test/java/io/github/kenneycode/fusion/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.kenneycode.fusion 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see [Testing documentation](http://d.android.com/tools/testing) 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, (2 + 2).toLong()) 16 | } 17 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':libfusion' 2 | rootProject.name='fusion' 3 | --------------------------------------------------------------------------------