diff --git a/Examples/AdvancedEffects/android/app/build.gradle b/Examples/AdvancedEffects/android/app/build.gradle index 56c9895c5e64c69c4f4d34fc506629e8102fe1d2..f51be09f32344e2f84a283c6b7ccb9bc33bf120c 100644 --- a/Examples/AdvancedEffects/android/app/build.gradle +++ b/Examples/AdvancedEffects/android/app/build.gradle @@ -74,7 +74,7 @@ android { dependencies { compile fileTree(dir: "libs", include: ["*.jar"]) compile "com.android.support:appcompat-v7:23.0.1" - compile "com.facebook.react:react-native:0.17.+" + compile "com.facebook.react:react-native:0.18.+" compile project(':RNGL') } diff --git a/Examples/AdvancedEffects/package.json b/Examples/AdvancedEffects/package.json index d3b5ae1780f52d5186ca097c712a543096cef46a..528d8cf2d6dee756e79db8e7d97d5f96a74b57da 100644 --- a/Examples/AdvancedEffects/package.json +++ b/Examples/AdvancedEffects/package.json @@ -3,12 +3,14 @@ "version": "0.0.1", "private": true, "scripts": { - "start": "react-native start" + "start": "node node_modules/react-native/local-cli/cli.js start" }, "dependencies": { - "gl-react": "^2.0.2", + "fbjs": "^0.6.0", + "gl-react": "^2.1.0", "gl-react-native": "file:../..", "glsl-transitions": "^2015.11.8", - "react-native": "^0.17.0" + "react": "^0.14.5", + "react-native": "0.18.0-rc" } } diff --git a/Examples/AdvancedEffects/src/index.js b/Examples/AdvancedEffects/src/index.js index 9b8b63ec9cc37be61bc7a3f5eef472720b2d94d8..db9a122300b627ae36378a0d4f273bb9be435abe 100644 --- a/Examples/AdvancedEffects/src/index.js +++ b/Examples/AdvancedEffects/src/index.js @@ -1,4 +1,3 @@ -require("gl-react/react-native"); const React = require("react-native"); const { StyleSheet, diff --git a/Examples/Hearts/android/app/build.gradle b/Examples/Hearts/android/app/build.gradle index 95a70110814aa55b8ba3768811a1ae6fe70d7597..9da914dd6f98489b28e8bee19923b0b777d7f55a 100644 --- a/Examples/Hearts/android/app/build.gradle +++ b/Examples/Hearts/android/app/build.gradle @@ -74,6 +74,6 @@ android { dependencies { compile fileTree(dir: "libs", include: ["*.jar"]) compile "com.android.support:appcompat-v7:23.0.1" - compile "com.facebook.react:react-native:0.17.+" + compile "com.facebook.react:react-native:0.18.+" compile project(":RNGL") } diff --git a/Examples/Hearts/package.json b/Examples/Hearts/package.json index a6ca214c5bb12a0754e059ac40769b36a2db23bf..9c82504106e7129db99489d7deb314d48dc8a687 100644 --- a/Examples/Hearts/package.json +++ b/Examples/Hearts/package.json @@ -3,13 +3,15 @@ "version": "0.0.1", "private": true, "scripts": { - "start": "react-native start" + "start": "node node_modules/react-native/local-cli/cli.js start" }, "dependencies": { "crypto": "0.0.3", - "gl-react": "^2.0.6", + "fbjs": "^0.6.0", + "gl-react": "^2.1.0", "gl-react-native": "file:../..", - "react-native": "^0.17.0", + "react": "^0.14.5", + "react-native": "0.18.0-rc", "seedrandom": "gre/seedrandom#released" } } diff --git a/Examples/Hearts/src/Heart.js b/Examples/Hearts/src/Heart.js index 7d758475d294c9eda57b7e8eb0fbdf617e5800e5..0810192c42294419760ae8f300500c841e1842bf 100644 --- a/Examples/Hearts/src/Heart.js +++ b/Examples/Hearts/src/Heart.js @@ -1,5 +1,5 @@ import GL from "gl-react"; -const {React} = GL; +import React from "react"; const {PropTypes} = React; const shaders = GL.Shaders.create({ diff --git a/Examples/Hearts/src/index.js b/Examples/Hearts/src/index.js index f857d9c8ff136eb741a54314b377c94753663e8c..f4ce09e559dfcc850d359dc31431e57c9af00798 100644 --- a/Examples/Hearts/src/index.js +++ b/Examples/Hearts/src/index.js @@ -1,4 +1,3 @@ -require("gl-react/react-native"); const React = require("react-native"); const { StyleSheet, diff --git a/Examples/Simple/android/app/app.iml b/Examples/Simple/android/app/app.iml index c6bbb953db43a7d0fc9ddaeca229a9bfcb829397..9732579ae160d4f3fa7f94a885c46f1598265cfb 100644 --- a/Examples/Simple/android/app/app.iml +++ b/Examples/Simple/android/app/app.iml @@ -77,7 +77,7 @@ - + @@ -96,14 +96,14 @@ - + - + @@ -112,6 +112,7 @@ + diff --git a/Examples/Simple/android/app/build.gradle b/Examples/Simple/android/app/build.gradle index c24a17d0e0ec291322bb3dbe10d753c0010c5d0d..d3660d6017fd4608d571fc72dfda3a8f67fb3c91 100644 --- a/Examples/Simple/android/app/build.gradle +++ b/Examples/Simple/android/app/build.gradle @@ -74,8 +74,10 @@ android { dependencies { compile fileTree(dir: "libs", include: ["*.jar"]) compile "com.android.support:appcompat-v7:23.0.1" - compile "com.facebook.react:react-native:0.17.+" + compile "com.facebook.react:react-native:0.18.+" compile project(":RNMaterialKit") compile project(":RNGL") + compile project(':react-native-fs') + } diff --git a/Examples/Simple/android/app/src/main/java/com/simple/MainActivity.java b/Examples/Simple/android/app/src/main/java/com/simple/MainActivity.java index 1822c6c017c5a67d63910691aa0330d6a9f54cb4..a50883ebf62cbcfa3b8ab469af0bc3dd9b84f3c2 100644 --- a/Examples/Simple/android/app/src/main/java/com/simple/MainActivity.java +++ b/Examples/Simple/android/app/src/main/java/com/simple/MainActivity.java @@ -11,6 +11,7 @@ import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler; import com.facebook.react.shell.MainReactPackage; import com.github.xinthink.rnmk.ReactMaterialKitPackage; import com.projectseptember.RNGL.RNGLPackage; +import com.rnfs.RNFSPackage; public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler { @@ -28,6 +29,7 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand .setJSMainModuleName("index.android") .addPackage(new MainReactPackage()) .addPackage(new RNGLPackage()) + .addPackage(new RNFSPackage()) .addPackage(new ReactMaterialKitPackage()) .setUseDeveloperSupport(BuildConfig.DEBUG) .setInitialLifecycleState(LifecycleState.RESUMED) diff --git a/Examples/Simple/android/settings.gradle b/Examples/Simple/android/settings.gradle index eac886a4cb3dc83c9e8f7b46a373491a6f04ff31..06488e776cfc1e208a7deb2092b0a5ce228fd61a 100644 --- a/Examples/Simple/android/settings.gradle +++ b/Examples/Simple/android/settings.gradle @@ -6,4 +6,7 @@ include ':RNMaterialKit' project(':RNMaterialKit').projectDir = file('../node_modules/react-native-material-kit/android') include ':RNGL' -project(':RNGL').projectDir = file('../../../android') \ No newline at end of file +project(':RNGL').projectDir = file('../../../android') + +include ':react-native-fs' +project(':react-native-fs').projectDir = new File(settingsDir, '../node_modules/react-native-fs/android') \ No newline at end of file diff --git a/Examples/Simple/ios/Simple.xcodeproj/project.pbxproj b/Examples/Simple/ios/Simple.xcodeproj/project.pbxproj index d0fc18e51ce394e8cb12ce9e16a26f89348b3b50..33690a40fd8686ce239f87f930da8e3737aa2872 100644 --- a/Examples/Simple/ios/Simple.xcodeproj/project.pbxproj +++ b/Examples/Simple/ios/Simple.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; 34607B601C132B22009203B1 /* libRCTMaterialKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 34607B581C132B1C009203B1 /* libRCTMaterialKit.a */; }; 3461EB4B1C132AEC0003E4A2 /* libRNGL.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3461EB4A1C132AD30003E4A2 /* libRNGL.a */; }; + 34C9905A1C3530D6002F49FC /* libRNFS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 34C990581C3530CE002F49FC /* libRNFS.a */; }; 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; /* End PBXBuildFile section */ @@ -104,6 +105,13 @@ remoteGlobalIDString = 4107012F1ACB723B00C6AA39; remoteInfo = RNGL; }; + 34C990571C3530CE002F49FC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 34C990481C3530CE002F49FC /* RNFS.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = F12AFB9B1ADAF8F800E0535D; + remoteInfo = RNFS; + }; 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; @@ -142,6 +150,7 @@ 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; 34607B4F1C132B1C009203B1 /* RCTMaterialKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTMaterialKit.xcodeproj; path = "../node_modules/react-native-material-kit/iOS/RCTMaterialKit.xcodeproj"; sourceTree = ""; }; 3461EB3B1C132AD30003E4A2 /* RNGL.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNGL.xcodeproj; path = "../node_modules/gl-react-native/ios/RNGL.xcodeproj"; sourceTree = ""; }; + 34C990481C3530CE002F49FC /* RNFS.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNFS.xcodeproj; path = "../node_modules/react-native-fs/RNFS.xcodeproj"; sourceTree = ""; }; 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -158,6 +167,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 34C9905A1C3530D6002F49FC /* libRNFS.a in Frameworks */, 34607B601C132B22009203B1 /* libRCTMaterialKit.a in Frameworks */, 3461EB4B1C132AEC0003E4A2 /* libRNGL.a in Frameworks */, 146834051AC3E58100842450 /* libReact.a in Frameworks */, @@ -287,6 +297,14 @@ name = Products; sourceTree = ""; }; + 34C990491C3530CE002F49FC /* Products */ = { + isa = PBXGroup; + children = ( + 34C990581C3530CE002F49FC /* libRNFS.a */, + ); + name = Products; + sourceTree = ""; + }; 78C398B11ACF4ADC00677621 /* Products */ = { isa = PBXGroup; children = ( @@ -298,6 +316,7 @@ 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( + 34C990481C3530CE002F49FC /* RNFS.xcodeproj */, 34607B4F1C132B1C009203B1 /* RCTMaterialKit.xcodeproj */, 3461EB3B1C132AD30003E4A2 /* RNGL.xcodeproj */, 146833FF1AC3E56700842450 /* React.xcodeproj */, @@ -453,6 +472,10 @@ ProductGroup = 146834001AC3E56700842450 /* Products */; ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; }, + { + ProductGroup = 34C990491C3530CE002F49FC /* Products */; + ProjectRef = 34C990481C3530CE002F49FC /* RNFS.xcodeproj */; + }, { ProductGroup = 3461EB3C1C132AD30003E4A2 /* Products */; ProjectRef = 3461EB3B1C132AD30003E4A2 /* RNGL.xcodeproj */; @@ -537,6 +560,13 @@ remoteRef = 3461EB491C132AD30003E4A2 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 34C990581C3530CE002F49FC /* libRNFS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRNFS.a; + remoteRef = 34C990571C3530CE002F49FC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; diff --git a/Examples/Simple/package.json b/Examples/Simple/package.json index 91a0abead785e069ad64a63b1cbbfd782cceb7fc..f8262c5dc7fee2b6afb97bfd2c3081128afaca77 100644 --- a/Examples/Simple/package.json +++ b/Examples/Simple/package.json @@ -3,12 +3,15 @@ "version": "0.0.1", "private": true, "scripts": { - "start": "react-native start" + "start": "node node_modules/react-native/local-cli/cli.js start" }, "dependencies": { + "fbjs": "^0.6.0", + "gl-react": "^2.1.0", "gl-react-native": "file:../..", - "gl-react": "^2.0.8", - "react-native": "^0.17.0", - "react-native-material-kit": "^0.2.4" + "react": "^0.14.5", + "react-native": "0.18.0-rc", + "react-native-fs": "^1.1.0", + "react-native-material-kit": "gre/react-native-material-kit#no-peerDependencies" } } diff --git a/Examples/Simple/src/Blur.js b/Examples/Simple/src/Blur.js index 26a5d3c05e58c356964d1cb26abcd53e86ab49e8..0f3ae20055e5043277f6520383534f8860ec2000 100644 --- a/Examples/Simple/src/Blur.js +++ b/Examples/Simple/src/Blur.js @@ -1,5 +1,5 @@ const GL = require("gl-react"); -const React = GL.React; +const React = require("react"); const Blur1D = require("./Blur1D"); module.exports = GL.createComponent(({ width, height, factor, children }) => diff --git a/Examples/Simple/src/Blur1D.js b/Examples/Simple/src/Blur1D.js index feb2f21e30eb11b6e0bbab6e1a8b6aa3ccad25f8..95579e08c310faab8fbd9afdda0a21d0e6d67cbd 100644 --- a/Examples/Simple/src/Blur1D.js +++ b/Examples/Simple/src/Blur1D.js @@ -1,5 +1,5 @@ const GL = require("gl-react"); -const React = GL.React; +const React = require("react"); const shaders = GL.Shaders.create({ blur1D: { diff --git a/Examples/Simple/src/HelloGL.js b/Examples/Simple/src/HelloGL.js index fc08b5140f88fd6d5943794bd5e61d0c62774754..475087db222a15e0b27881915b3115a6f4ed6feb 100644 --- a/Examples/Simple/src/HelloGL.js +++ b/Examples/Simple/src/HelloGL.js @@ -1,5 +1,5 @@ const GL = require("gl-react"); -const React = GL.React; +const React = require("react"); const shaders = GL.Shaders.create({ helloGL: { diff --git a/Examples/Simple/src/HueRotate.js b/Examples/Simple/src/HueRotate.js index d520d5f8f287d7ef4230ab14b836bb94d85d691a..1c85254ea7b7f3a6c57039e46e99dfa1a2f30e45 100644 --- a/Examples/Simple/src/HueRotate.js +++ b/Examples/Simple/src/HueRotate.js @@ -1,5 +1,5 @@ const GL = require("gl-react"); -const React = GL.React; +const React = require("react"); const shaders = GL.Shaders.create({ hueRotate: { diff --git a/Examples/Simple/src/PieProgress.js b/Examples/Simple/src/PieProgress.js index 012c4593a49d0b2c130da08a405996a070f90068..a7a8d57201225969bf6cd82d0fc8b03a85689a52 100644 --- a/Examples/Simple/src/PieProgress.js +++ b/Examples/Simple/src/PieProgress.js @@ -1,5 +1,5 @@ const GL = require("gl-react"); -const React = GL.React; +const React = require("react"); const shaders = GL.Shaders.create({ pieProgress: { diff --git a/Examples/Simple/src/Saturation.js b/Examples/Simple/src/Saturation.js index c8088ab9b9745af097a7cfefca9e85415f1497f1..f24ec3f1fa0812a2c4403b29021f70da87c72e82 100644 --- a/Examples/Simple/src/Saturation.js +++ b/Examples/Simple/src/Saturation.js @@ -1,5 +1,5 @@ const GL = require("gl-react"); -const React = GL.React; +const React = require("react"); const shaders = GL.Shaders.create({ saturation: { diff --git a/Examples/Simple/src/index.js b/Examples/Simple/src/index.js index d3552462034736bf8521f76ee552320797df698a..0756153ae148f9858f58622e123bbea16de9469c 100644 --- a/Examples/Simple/src/index.js +++ b/Examples/Simple/src/index.js @@ -1,5 +1,3 @@ -require("gl-react/react-native"); - const React = require("react-native"); const { StyleSheet, @@ -22,6 +20,8 @@ const { MKButton, } = require("react-native-material-kit"); +const RNFS = require("react-native-fs"); + const HelloGL = require("./HelloGL"); const Saturation = require("./Saturation"); const HueRotate = require("./HueRotate"); @@ -82,7 +82,8 @@ class Simple extends Component { switch1: false, switch2: false, switch3: false, - captured: null + captured: null, + captureConfig: null }; this.onCapture1 = this.onCapture1.bind(this); @@ -97,9 +98,17 @@ class Simple extends Component { } onCapture1 () { - this.refs.helloGL.captureFrame().then(data64 => { - this.setState({ captured: data64 }); - }); + const captureConfig = { + quality: Math.round((Math.random() * 100))/100, + type: Math.random() < 0.5 ? "jpg": "png", + format: Math.random() < 0.5 ? "base64" : "file" + }; + if (captureConfig.format === "file") { + captureConfig.filePath = RNFS.DocumentDirectoryPath+"/hellogl_capture.png"; + } + this.refs.helloGL + .captureFrame(captureConfig) + .then(captured => this.setState({ captured, captureConfig })); } render () { @@ -113,7 +122,8 @@ class Simple extends Component { switch1, switch2, switch3, - captured + captured, + captureConfig } = this.state; return @@ -128,9 +138,28 @@ class Simple extends Component { - {captured && } + {captured && + } - {captured && {captured.slice(0, 100)}} + {captureConfig && + + + format={captureConfig.format} + + + type={captureConfig.type} + + + quality={captureConfig.quality+""} + + + } + {captured && + + {captured.slice(0, 100)} + } diff --git a/Examples/Tests/android/app/build.gradle b/Examples/Tests/android/app/build.gradle index bc93951aef3f45930b51658adb38e35bff52532a..eddf4565e6cd90f5f658e9f24fc2eb2ed7047075 100644 --- a/Examples/Tests/android/app/build.gradle +++ b/Examples/Tests/android/app/build.gradle @@ -74,7 +74,7 @@ android { dependencies { compile fileTree(dir: "libs", include: ["*.jar"]) compile "com.android.support:appcompat-v7:23.0.1" - compile "com.facebook.react:react-native:0.17.+" + compile "com.facebook.react:react-native:0.18.+" compile project(":RNGL") } diff --git a/Examples/Tests/package.json b/Examples/Tests/package.json index 5e12bf40bc25bd3c5d4c6ac546ad36512812627f..cec5049ad9ddd7bc0b945c199e6d48e569795a4d 100644 --- a/Examples/Tests/package.json +++ b/Examples/Tests/package.json @@ -3,11 +3,13 @@ "version": "0.0.1", "private": true, "scripts": { - "start": "react-native start" + "start": "node node_modules/react-native/local-cli/cli.js start" }, "dependencies": { + "fbjs": "^0.6.0", + "gl-react": "^2.1.0", "gl-react-native": "file:../..", - "gl-react": "^2.0.2", - "react-native": "^0.17.0" + "react": "^0.14.5", + "react-native": "0.18.0-rc" } } diff --git a/Examples/Tests/src/Add.js b/Examples/Tests/src/Add.js index cbb560c36d33a2b9e0ec573d0cde53a5303c3a3f..d25edd8f3a18d67461d0fac9a601cca94f08f32c 100644 --- a/Examples/Tests/src/Add.js +++ b/Examples/Tests/src/Add.js @@ -1,5 +1,5 @@ const GL = require("gl-react"); -const React = GL.React; +const React = require("react"); const shaders = GL.Shaders.create({ add: { diff --git a/Examples/Tests/src/Blur.js b/Examples/Tests/src/Blur.js index d10b83b7983ae1ab0d0ef9e16a986a3449aa818c..8433c9c817e8d3364610bec48e940db3790493a8 100644 --- a/Examples/Tests/src/Blur.js +++ b/Examples/Tests/src/Blur.js @@ -1,5 +1,5 @@ const GL = require("gl-react"); -const React = GL.React; +const React = require("react"); const { PropTypes } = React; diff --git a/Examples/Tests/src/Blur1D.js b/Examples/Tests/src/Blur1D.js index df5067fd33f18ec473a2677841d7147363802093..b9adbfed2dfc20c64e09e0944d90db514382881e 100644 --- a/Examples/Tests/src/Blur1D.js +++ b/Examples/Tests/src/Blur1D.js @@ -1,6 +1,5 @@ const GL = require("gl-react"); -const React = GL.React; - +const React = require("react"); const { PropTypes } = React; diff --git a/Examples/Tests/src/Copy.js b/Examples/Tests/src/Copy.js index c49efeb28ed845b8003919d01aa489039c8b0383..33be68e7da4ab5ef437db6c869e10899cb66d274 100644 --- a/Examples/Tests/src/Copy.js +++ b/Examples/Tests/src/Copy.js @@ -1,5 +1,5 @@ const GL = require("gl-react"); -const React = GL.React; +const React = require("react"); const shaders = GL.Shaders.create({ Copy: { diff --git a/Examples/Tests/src/Display2.js b/Examples/Tests/src/Display2.js index 6aac82fd4c8381033ca411d434f58891994f582f..003fd182219bb30a4a4837382710102ad86c24ff 100644 --- a/Examples/Tests/src/Display2.js +++ b/Examples/Tests/src/Display2.js @@ -1,5 +1,5 @@ const GL = require("gl-react"); -const React = GL.React; +const React = require("react"); const shaders = GL.Shaders.create({ display2: { diff --git a/Examples/Tests/src/HelloGL.js b/Examples/Tests/src/HelloGL.js index e72df0e66b87297423133ab6b8ad5bd53ba1c859..d944c7c7a0774e134b5bffea412afdc082987280 100644 --- a/Examples/Tests/src/HelloGL.js +++ b/Examples/Tests/src/HelloGL.js @@ -1,5 +1,5 @@ const GL = require("gl-react"); -const React = GL.React; +const React = require("react"); const shaders = GL.Shaders.create({ helloGL: { diff --git a/Examples/Tests/src/Layer.js b/Examples/Tests/src/Layer.js index 30752f5b0e717e00f249fbf0b103eee4d4fab8b3..5bef44b8124903900631610e5380d0af86e9a7a2 100644 --- a/Examples/Tests/src/Layer.js +++ b/Examples/Tests/src/Layer.js @@ -1,5 +1,5 @@ const GL = require("gl-react"); -const React = GL.React; +const React = require("react"); const shaders = GL.Shaders.create({ layer: { diff --git a/Examples/Tests/src/Multiply.js b/Examples/Tests/src/Multiply.js index e4cef5510f1f1c4b8c84a83ec77887ca977b0b2e..fde8023f05f9e1dfa3b5c9c794178e62cda69509 100644 --- a/Examples/Tests/src/Multiply.js +++ b/Examples/Tests/src/Multiply.js @@ -1,5 +1,5 @@ const GL = require("gl-react"); -const React = GL.React; +const React = require("react"); const shaders = GL.Shaders.create({ multiply: { diff --git a/Examples/Tests/src/TransparentNonPremultiplied.js b/Examples/Tests/src/TransparentNonPremultiplied.js index df8d7c322a8af669859daea65c49f8cf1286e3fb..5bc8110fe33b64779d7d7ca6979bf6e0d9bdaa0c 100644 --- a/Examples/Tests/src/TransparentNonPremultiplied.js +++ b/Examples/Tests/src/TransparentNonPremultiplied.js @@ -1,5 +1,5 @@ const GL = require("gl-react"); -const React = GL.React; +const React = require("react"); const shaders = GL.Shaders.create({ TransparentNonPremultiplied: { diff --git a/Examples/Tests/src/index.js b/Examples/Tests/src/index.js index ff197d05d344e5feaafc0071ac68b1506524bc35..c53f6c1b61b818753854383ff64a6b723eac945d 100644 --- a/Examples/Tests/src/index.js +++ b/Examples/Tests/src/index.js @@ -1,4 +1,3 @@ -require("gl-react/react-native"); const React = require("react-native"); const { Text, diff --git a/Examples/android/RNGL.iml b/Examples/android/RNGL.iml deleted file mode 100644 index d90b763d79676e58f0ed979346315239bc64de94..0000000000000000000000000000000000000000 --- a/Examples/android/RNGL.iml +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index 8f4ce7fb974596db2c3f6d985567c963972cf1a2..b63fffd41c00b6ae68a4bd12330d943ea54fcf8a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ **[Gitbook documentation](http://projectseptemberinc.gitbooks.io/gl-react/content/) / [Github](https://github.com/ProjectSeptemberInc/gl-react-native/) / [gl-react](https://github.com/ProjectSeptemberInc/gl-react/)** / [#gl-react on reactiflux](https://discordapp.com/channels/102860784329052160/106102146109325312) -# icon gl-react-native ![](https://img.shields.io/badge/react--native-%200.17.x-05F561.svg) +# icon gl-react-native ![](https://img.shields.io/badge/react--native-%200.18.x-05F561.svg) ![](https://img.shields.io/badge/gl--react-%202.1.x-05F561.svg) OpenGL bindings for React Native to implement complex effects over images and components, in the descriptive VDOM paradigm. diff --git a/android/build.gradle b/android/build.gradle index 9c3c76cc7b63fd015b37b1f9bf4f7b5f7aaab3e7..60b53fb02b6252173d7302aad0e3375dbb163929 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -30,5 +30,5 @@ repositories { } dependencies { - compile 'com.facebook.react:react-native:0.17.+' + compile 'com.facebook.react:react-native:0.18.+' } diff --git a/android/src/main/java/com/projectseptember/RNGL/CaptureConfig.java b/android/src/main/java/com/projectseptember/RNGL/CaptureConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..4900e7beb95b42204358dfba65caf095146ea62f --- /dev/null +++ b/android/src/main/java/com/projectseptember/RNGL/CaptureConfig.java @@ -0,0 +1,63 @@ +package com.projectseptember.RNGL; + +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableMap; + + +public class CaptureConfig { + String format; + String type; + String filePath; + Double quality; + + public CaptureConfig(String format, String type, String filePath, Double quality) { + this.format = format; + this.type = type; + this.filePath = filePath; + this.quality = quality; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CaptureConfig that = (CaptureConfig) o; + + if (format != null ? !format.equals(that.format) : that.format != null) return false; + if (type != null ? !type.equals(that.type) : that.type != null) return false; + if (filePath != null ? !filePath.equals(that.filePath) : that.filePath != null) + return false; + return !(quality != null ? !quality.equals(that.quality) : that.quality != null); + + } + + @Override + public int hashCode() { + int result = format != null ? format.hashCode() : 0; + result = 31 * result + (type != null ? type.hashCode() : 0); + result = 31 * result + (filePath != null ? filePath.hashCode() : 0); + result = 31 * result + (quality != null ? quality.hashCode() : 0); + return result; + } + + public static CaptureConfig fromMap (ReadableMap map) { + return new CaptureConfig( + map.getString("format"), + map.getString("type"), + map.getString("filePath"), + map.getDouble("quality") + ); + } + + public WritableMap toMap () { + WritableMap map = Arguments.createMap(); + map.putString("format", format); + map.putString("type", type); + map.putString("filePath", filePath); + map.putDouble("quality", quality); + return map; + } + +} diff --git a/android/src/main/java/com/projectseptember/RNGL/GLCanvas.java b/android/src/main/java/com/projectseptember/RNGL/GLCanvas.java index 38a49caf0411e8ee260f15578de729ed75c3945b..83502910383f92f757ee83f55c1629cdab54bef4 100644 --- a/android/src/main/java/com/projectseptember/RNGL/GLCanvas.java +++ b/android/src/main/java/com/projectseptember/RNGL/GLCanvas.java @@ -27,6 +27,7 @@ import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.events.RCTEventEmitter; import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; @@ -69,7 +70,11 @@ public class GLCanvas extends GLSurfaceView private Map fbos; private ExecutorSupplier executorSupplier; private final Queue mRunOnDraw = new LinkedList<>(); - private boolean captureFrameRequested = false; + + private List captureConfigs = new ArrayList<>(); + private float pixelRatio; + + private float displayDensity; public GLCanvas(ThemedReactContext context, ExecutorSupplier executorSupplier) { super(context); @@ -78,6 +83,10 @@ public class GLCanvas extends GLSurfaceView rnglContext = context.getNativeModule(RNGLContext.class); setEGLContextClientVersion(2); + DisplayMetrics dm = reactContext.getResources().getDisplayMetrics(); + displayDensity = dm.density; + pixelRatio = dm.density; + setEGLConfigChooser(8, 8, 8, 8, 16, 0); getHolder().setFormat(PixelFormat.RGB_888); setZOrderOnTop(true); @@ -104,7 +113,7 @@ public class GLCanvas extends GLSurfaceView if (!shaders.containsKey(id)) { GLShaderData shaderData = rnglContext.getShader(id); if (shaderData == null) return null; - shaders.put(id, new GLShader(shaderData)); + shaders.put(id, new GLShader(shaderData, id, rnglContext)); } return shaders.get(id); } @@ -123,6 +132,12 @@ public class GLCanvas extends GLSurfaceView @Override public void onSurfaceChanged(GL10 gl, int width, int height) {} + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + syncSize(w, h, pixelRatio); + } + @Override public void onDrawFrame(GL10 gl) { runAll(mRunOnDraw); @@ -156,18 +171,76 @@ public class GLCanvas extends GLSurfaceView if (shouldRenderNow) { this.render(); deferredRendering = false; - if (captureFrameRequested) { - captureFrameRequested = false; - Bitmap capture = createSnapshot(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - capture.compress(Bitmap.CompressFormat.PNG, 100, baos); - String frame = "data:image/png;base64,"+ - Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT); - dispatchOnCaptureFrame(frame); + if (captureConfigs.size() > 0) { + capture(); // FIXME: maybe we should schedule this? } } } + private void capture () { + Bitmap capture = createSnapshot(); + ReactContext reactContext = (ReactContext)getContext(); + RCTEventEmitter eventEmitter = reactContext.getJSModule(RCTEventEmitter.class); + + for (CaptureConfig config : captureConfigs) { + String result = null, error = null; + boolean isPng = config.type.equals("png"); + boolean isJpeg = !isPng && (config.type.equals("jpg")||config.type.equals("jpeg")); + boolean isWebm = !isPng && !isJpeg && config.type.equals("webm"); + boolean isBase64 = config.format.equals("base64"); + boolean isFile = !isBase64 && config.format.equals("file"); + + Bitmap.CompressFormat compressFormat = + isPng ? Bitmap.CompressFormat.PNG : + isJpeg ? Bitmap.CompressFormat.JPEG : + isWebm ? Bitmap.CompressFormat.WEBP : + null; + + int quality = (int)(100 * config.quality); + + if (compressFormat == null) { + error = "Unsupported capture type '"+config.type+"'"; + } + else if (isBase64) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + capture.compress(compressFormat, quality, baos); + String frame = "data:image/png;base64,"+ + Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT); + baos.close(); + result = frame; + } + catch (Exception e) { + e.printStackTrace(); + error = "Could not capture as base64: "+e.getMessage(); + } + } + else if (isFile) { + try { + FileOutputStream fileOutputStream = new FileOutputStream(config.filePath); + capture.compress(compressFormat, quality, fileOutputStream); + fileOutputStream.close(); + result = "file://"+config.filePath; + } + catch (Exception e) { + e.printStackTrace(); + error = "Could not write file: "+e.getMessage(); + } + } + else { + error = "Unsupported capture format '"+config.format+"'"; + } + + WritableMap response = Arguments.createMap(); + response.putMap("config", config.toMap()); + if (error != null) response.putString("error", error); + if (result != null) response.putString("result", result); + eventEmitter.receiveEvent(getId(), "captureFrame", response); + } + + captureConfigs = new ArrayList<>(); + } + private boolean haveRemainingToPreload() { for (Uri uri: imagesToPreload) { if (!preloaded.contains(uri)) { @@ -242,8 +315,13 @@ public class GLCanvas extends GLSurfaceView execute(new Runnable() { public void run() { // FIXME: maybe should set a flag so we don't do it twice?? - if (!syncData()) - requestSyncData(); + try { + if (!syncData()) + requestSyncData(); + } + catch (GLShaderCompilationFailed e) { + // This is ignored. It will be handled by RNGLContext.shaderFailedToCompile + } } }); } @@ -472,8 +550,8 @@ public class GLCanvas extends GLSurfaceView if (arraySizeForType(type) != arr.size()) { shader.runtimeException( "uniform '"+uniformName+ - "': Invalid array size: "+arr.size()+ - ". Expected: "+arraySizeForType(type)); + "': Invalid array size: "+arr.size()+ + ". Expected: "+arraySizeForType(type)); } uniformsFloatBuffer.put(uniformName, parseAsFloatArray(arr)); break; @@ -488,8 +566,8 @@ public class GLCanvas extends GLSurfaceView if (arraySizeForType(type) != arr2.size()) { shader.runtimeException( "uniform '"+uniformName+ - "': Invalid array size: "+arr2.size()+ - ". Expected: "+arraySizeForType(type)); + "': Invalid array size: "+arr2.size()+ + ". Expected: "+arraySizeForType(type)); } uniformsIntBuffer.put(uniformName, parseAsIntArray(arr2)); break; @@ -497,7 +575,7 @@ public class GLCanvas extends GLSurfaceView default: shader.runtimeException( "uniform '"+uniformName+ - "': type not supported: "+type); + "': type not supported: "+type); } } @@ -511,9 +589,9 @@ public class GLCanvas extends GLSurfaceView for (String uniformName: uniformTypes.keySet()) { if (!uniformsFloat.containsKey(uniformName) && - !uniformsInteger.containsKey(uniformName) && - !uniformsFloatBuffer.containsKey(uniformName) && - !uniformsIntBuffer.containsKey(uniformName)) { + !uniformsInteger.containsKey(uniformName) && + !uniformsFloatBuffer.containsKey(uniformName) && + !uniformsIntBuffer.containsKey(uniformName)) { shader.runtimeException("All defined uniforms must be provided. Missing '"+uniformName+"'"); } } @@ -525,8 +603,8 @@ public class GLCanvas extends GLSurfaceView uniformsIntBuffer, uniformsFloatBuffer, textures, - data.width, - data.height, + (int)(data.width * data.pixelRatio), + (int)(data.height * data.pixelRatio), data.fboId, contextChildren, children); @@ -538,7 +616,7 @@ public class GLCanvas extends GLSurfaceView .order(ByteOrder.nativeOrder()) .asFloatBuffer(); for (int i=0; i { private ExecutorSupplier executorSupplier; + @ReactProp(name="pixelRatio") + public void setPixelRatio (GLCanvas view, float pixelRatio) { + view.setPixelRatio(pixelRatio); + } + @ReactProp(name="nbContentTextures") public void setNbContentTextures (GLCanvas view, int nbContentTextures) { view.setNbContentTextures(nbContentTextures); @@ -89,7 +94,7 @@ public class GLCanvasManager extends SimpleViewManager { Assertions.assertNotNull(args); switch (commandType) { case COMMAND_CAPTURE_FRAME: { - canvas.requestCaptureFrame(); + canvas.requestCaptureFrame(CaptureConfig.fromMap(args.getMap(0))); return; } default: diff --git a/android/src/main/java/com/projectseptember/RNGL/GLData.java b/android/src/main/java/com/projectseptember/RNGL/GLData.java index 61ad242ed20bfa5f898eceaea72dccdc726a4887..57964af70bc946a3e683319da5320c1d74ffadd0 100644 --- a/android/src/main/java/com/projectseptember/RNGL/GLData.java +++ b/android/src/main/java/com/projectseptember/RNGL/GLData.java @@ -10,17 +10,19 @@ public class GLData { final Integer shader; final ReadableMap uniforms; - final Integer width; - final Integer height; + final Double width; + final Double height; + final Double pixelRatio; final Integer fboId; final List contextChildren; final List children; - public GLData(Integer shader, ReadableMap uniforms, Integer width, Integer height, Integer fboId, List contextChildren, List children) { + public GLData(Integer shader, ReadableMap uniforms, Double width, Double height, Double pixelRatio, Integer fboId, List contextChildren, List children) { this.shader = shader; this.uniforms = uniforms; this.width = width; this.height = height; + this.pixelRatio = pixelRatio; this.fboId = fboId; this.contextChildren = contextChildren; this.children = children; @@ -37,11 +39,12 @@ public class GLData { public static GLData fromMap (ReadableMap map) { Integer shader = map.getInt("shader"); ReadableMap uniforms = map.getMap("uniforms"); - Integer width = (int) map.getDouble("width"); - Integer height = (int) map.getDouble("height"); + Double width = map.getDouble("width"); + Double height = map.getDouble("height"); + Double pixelRatio = map.getDouble("pixelRatio"); Integer fboId = map.getInt("fboId"); List children = fromArray(map.getArray("children")); List contextChildren = fromArray(map.getArray("contextChildren")); - return new GLData(shader, uniforms, width, height, fboId, contextChildren, children); + return new GLData(shader, uniforms, width, height, pixelRatio, fboId, contextChildren, children); } } diff --git a/android/src/main/java/com/projectseptember/RNGL/GLShader.java b/android/src/main/java/com/projectseptember/RNGL/GLShader.java index e19dab7c8af9e0183fe2264b16463ebde5d436d7..15f95f6725225b966f44babcf41e77f3631ffb56 100644 --- a/android/src/main/java/com/projectseptember/RNGL/GLShader.java +++ b/android/src/main/java/com/projectseptember/RNGL/GLShader.java @@ -21,29 +21,30 @@ public class GLShader { private int pointerLoc; // The "pointer" attribute is used to iterate over vertex private Map uniformLocations; // The uniform locations cache - public GLShader(String name, String vert, String frag) { - this.name = name; - this.vert = vert; - this.frag = frag; - } + private Integer id; + private RNGLContext rnglContext; + private GLShaderCompilationFailed compilationFailed; - public GLShader(GLShaderData data) { + public GLShader(GLShaderData data, Integer id, RNGLContext rnglContext) { this.name = data.name; this.vert = data.vert; this.frag = data.frag; + this.id = id; + this.rnglContext = rnglContext; } @Override protected void finalize() throws Throwable { super.finalize(); if (buffer != null) { + // TODO: need to check if this works properly glDeleteProgram(program); glDeleteBuffers(1, buffer, 0); } } public void runtimeException (String msg) { - throw new RuntimeException("Shader '"+name+"': "+msg); + throw new GLShaderCompilationFailed(name, msg); } public void bind () { @@ -64,7 +65,7 @@ public class GLShader { glGetProgramiv(program, GL_VALIDATE_STATUS, validSuccess, 0); if (validSuccess[0] == GL_FALSE) { glGetProgramInfoLog(program); - runtimeException("Validation failed " + glGetProgramInfoLog(program)); + runtimeException(glGetProgramInfoLog(program)); } } @@ -133,7 +134,7 @@ public class GLShader { int compileSuccess[] = new int[1]; glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, compileSuccess, 0); if (compileSuccess[0] == GL_FALSE) { - runtimeException("failed to compile: " + glGetShaderInfoLog(shaderHandle)); + runtimeException(glGetShaderInfoLog(shaderHandle)); return -1; } return shaderHandle; @@ -157,7 +158,7 @@ public class GLShader { this.uniformLocations = locations; } - private void makeProgram () { + private void makeProgram () throws GLShaderCompilationFailed { int vertex = compileShader(vert, GL_VERTEX_SHADER); if (vertex == -1) return; @@ -172,7 +173,7 @@ public class GLShader { int[] linkSuccess = new int[1]; glGetProgramiv(program, GL_LINK_STATUS, linkSuccess, 0); if (linkSuccess[0] == GL_FALSE) { - runtimeException("Linking failed "+glGetProgramInfoLog(program)); + runtimeException(glGetProgramInfoLog(program)); } glUseProgram(program); @@ -208,7 +209,18 @@ public class GLShader { } public boolean ensureCompile() { - if (!isReady()) makeProgram(); + if (!isReady()) { + if (compilationFailed != null) throw compilationFailed; + try { + makeProgram(); + rnglContext.shaderSucceedToCompile(id, uniformTypes); + } + catch (GLShaderCompilationFailed e) { + compilationFailed = e; + rnglContext.shaderFailedToCompile(id, e); + throw e; + } + } return isReady(); } } diff --git a/android/src/main/java/com/projectseptember/RNGL/GLShaderCompilationFailed.java b/android/src/main/java/com/projectseptember/RNGL/GLShaderCompilationFailed.java new file mode 100644 index 0000000000000000000000000000000000000000..2fc50dcf7dfb0ad52c8da1e8ab37d55d70b20156 --- /dev/null +++ b/android/src/main/java/com/projectseptember/RNGL/GLShaderCompilationFailed.java @@ -0,0 +1,12 @@ +package com.projectseptember.RNGL; + +public class GLShaderCompilationFailed extends RuntimeException { + public final String shaderName; + public final String compileError; + + public GLShaderCompilationFailed(String shaderName, String compileError) { + super("Shader '"+shaderName+"': "+compileError); + this.compileError = compileError; + this.shaderName = shaderName; + } +} diff --git a/android/src/main/java/com/projectseptember/RNGL/RNGLContext.java b/android/src/main/java/com/projectseptember/RNGL/RNGLContext.java index 4c78d9857f8e9aa736878a51b41303205c2e93cb..58173724cc03e72af6c7be03d7b1dc346d9c0d9e 100644 --- a/android/src/main/java/com/projectseptember/RNGL/RNGLContext.java +++ b/android/src/main/java/com/projectseptember/RNGL/RNGLContext.java @@ -1,9 +1,15 @@ package com.projectseptember.RNGL; +import android.util.Log; + +import static android.opengl.GLES20.*; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableMap; import java.util.HashMap; import java.util.Map; @@ -20,6 +26,7 @@ public class RNGLContext extends ReactContextBaseJavaModule { private Map shaders = new HashMap<>(); private Map fbos = new HashMap<>(); + private Map onCompileCallbacks = new HashMap<>(); public RNGLContext (ReactApplicationContext reactContext) { super(reactContext); @@ -35,9 +42,67 @@ public class RNGLContext extends ReactContextBaseJavaModule { } @ReactMethod - public void addShader (final Integer id, final ReadableMap config) { + public void addShader (final Integer id, final ReadableMap config, final Callback onCompile) { final String frag = config.getString("frag"); final String name = config.getString("name"); shaders.put(id, new GLShaderData(name, STATIC_VERT, frag)); + if (onCompile != null) { + onCompileCallbacks.put(id, onCompile); + } + } + + @ReactMethod + public void removeShader (final Integer id) { + GLShaderData shader = shaders.remove(id); + if (shader == null) { + throw new Error("removeShader("+id+"): shader does not exist"); + } + } + + public void shaderFailedToCompile(Integer id, GLShaderCompilationFailed e) { + Callback onCompile = onCompileCallbacks.get(id); + if (onCompile == null) { + Log.e("RNGLContext", e.getMessage()); + } + else { + onCompile.invoke(e.compileError); + } + } + + public void shaderSucceedToCompile(Integer id, Map uniformTypes) { + Callback onCompile = onCompileCallbacks.get(id); + onCompileCallbacks.remove(id); + if (onCompile != null) { + WritableMap res = Arguments.createMap(); + WritableMap uniforms = Arguments.createMap(); + for (String key : uniformTypes.keySet()) { + uniforms.putString(key, glTypeString(uniformTypes.get(key))); + } + res.putMap("uniforms", uniforms); + onCompile.invoke(null, res); + } + } + + static String glTypeString (int type) { + switch (type) { + case GL_FLOAT: return "float"; + case GL_FLOAT_VEC2: return "vec2"; + case GL_FLOAT_VEC3: return "vec3"; + case GL_FLOAT_VEC4: return "vec4"; + case GL_INT: return "int"; + case GL_INT_VEC2: return "ivec2"; + case GL_INT_VEC3: return "ivec3"; + case GL_INT_VEC4: return "ivec4"; + case GL_BOOL: return "bool"; + case GL_BOOL_VEC2: return "bvec2"; + case GL_BOOL_VEC3: return "bvec3"; + case GL_BOOL_VEC4: return "bvec4"; + case GL_FLOAT_MAT2: return "mat2"; + case GL_FLOAT_MAT3: return "mat3"; + case GL_FLOAT_MAT4: return "mat4"; + case GL_SAMPLER_2D: return "sampler2D"; + case GL_SAMPLER_CUBE: return "samplerCube"; + } + return ""; } } diff --git a/ios/CaptureConfig.h b/ios/CaptureConfig.h new file mode 100644 index 0000000000000000000000000000000000000000..d9fa9bd31b990732c5fd7ff179423d7b089e1b66 --- /dev/null +++ b/ios/CaptureConfig.h @@ -0,0 +1,19 @@ +#import + +@interface CaptureConfig: NSObject + +@property (nonatomic, copy) NSString *format; +@property (nonatomic, copy) NSString *type; +@property (nonatomic, copy) NSString *filePath; +@property (nonatomic, copy) NSNumber *quality; + +-(instancetype)initWithFormat: (NSString *)format + withType: (NSString *)type + withQuality: (NSNumber *)quality + withFilePath: (NSString *)filePath; + +- (bool) isEqualToCaptureConfig: (CaptureConfig *)other; + +- (NSDictionary *) dictionary; + +@end diff --git a/ios/CaptureConfig.m b/ios/CaptureConfig.m new file mode 100644 index 0000000000000000000000000000000000000000..f7ef07bfe039b0704ab5cdbaf57c8b1bd2c31989 --- /dev/null +++ b/ios/CaptureConfig.m @@ -0,0 +1,38 @@ +#import "CaptureConfig.h" + +@implementation CaptureConfig + +-(instancetype)initWithFormat: (NSString *)format + withType: (NSString *)type + withQuality: (NSNumber *)quality + withFilePath: (NSString *)filePath +{ + if ((self = [super init])) { + self.format = format; + self.type = type; + self.quality = quality; + self.filePath = filePath; + } + return self; +} + + +- (bool) isEqualToCaptureConfig: (CaptureConfig *)other +{ + return [self.format isEqualToString:other.format] && + [self.type isEqualToString:other.type] && + [self.quality isEqualToNumber:other.quality] && + [self.filePath isEqualToString:other.filePath]; +} + +- (NSDictionary *) dictionary +{ + return @{ + @"format": self.format, + @"type": self.type, + @"quality": self.quality, + @"filePath": self.filePath + }; +} + +@end diff --git a/ios/GLCanvas.h b/ios/GLCanvas.h index 4f04cdedf19f9201de7ded178c951825c969a010..9084b4b2419943e2e84d2ccf84d146cbd54df14c 100644 --- a/ios/GLCanvas.h +++ b/ios/GLCanvas.h @@ -1,5 +1,6 @@ #import #import "GLData.h" +#import "CaptureConfig.h" #import "RCTComponent.h" @interface GLCanvas: GLKView @@ -11,6 +12,7 @@ @property (nonatomic) BOOL visibleContent; @property (nonatomic) NSNumber *nbContentTextures; @property (nonatomic) NSNumber *renderId; +@property (nonatomic) NSNumber *pixelRatio; @property (nonatomic) NSArray *imagesToPreload; @property (nonatomic, copy) RCTBubblingEventBlock onGLProgress; @property (nonatomic, copy) RCTBubblingEventBlock onGLLoad; @@ -18,6 +20,6 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge; -- (void) requestCaptureFrame; +- (void) requestCaptureFrame:(CaptureConfig *)config; @end diff --git a/ios/GLCanvas.m b/ios/GLCanvas.m index 392d4dd143ebc3e97fd7393bc27f61050b76a4e0..551aea383b36c07742976717ab4bf0c9066653b1 100644 --- a/ios/GLCanvas.m +++ b/ios/GLCanvas.m @@ -41,28 +41,29 @@ NSArray* diff (NSArray* a, NSArray* b) { @implementation GLCanvas { RCTBridge *_bridge; - + GLRenderData *_renderData; - - BOOL _captureFrameRequested; - + NSArray *_contentData; NSArray *_contentTextures; NSDictionary *_images; // This caches the currently used images (imageSrc -> GLReactImage) - + BOOL _opaque; // opaque prop (if false, the GLCanvas will become transparent) - + BOOL _deferredRendering; // This flag indicates a render has been deferred to the next frame (when using contents) - + GLint defaultFBO; - + NSMutableArray *_preloaded; BOOL _dirtyOnLoad; BOOL _neverRendered; - + NSTimer *animationTimer; - + BOOL _needSync; + + NSMutableArray *_captureConfigs; + BOOL _captureScheduled; } - (instancetype)initWithBridge:(RCTBridge *)bridge @@ -71,23 +72,44 @@ NSArray* diff (NSArray* a, NSArray* b) { _bridge = bridge; _images = @{}; _preloaded = [[NSMutableArray alloc] init]; - _captureFrameRequested = false; + _captureConfigs = [[NSMutableArray alloc] init]; + _captureScheduled = false; _dirtyOnLoad = true; _neverRendered = true; self.context = [bridge.rnglContext getContext]; - self.contentScaleFactor = RCTScreenScale(); } return self; } RCT_NOT_IMPLEMENTED(-init) +- (void)dealloc +{ + _bridge = nil; + _images = nil; + _preloaded = nil; + _captureConfigs = nil; + _contentData = nil; + _contentTextures = nil; + _data = nil; + _renderData = nil; + if (animationTimer) { + [animationTimer invalidate]; + animationTimer = nil; + } +} + //// Props Setters -- (void) requestCaptureFrame +- (void) requestCaptureFrame: (CaptureConfig *)config { - _captureFrameRequested = true; [self setNeedsDisplay]; + for (CaptureConfig *existing in _captureConfigs) { + if ([existing isEqualToCaptureConfig:config]) { + return; + } + } + [_captureConfigs addObject:config]; } -(void)setImagesToPreload:(NSArray *)imagesToPreload @@ -141,6 +163,12 @@ RCT_NOT_IMPLEMENTED(-init) } } +- (void)setPixelRatio:(NSNumber *)pixelRatio +{ + self.contentScaleFactor = [pixelRatio floatValue]; + [self setNeedsDisplay]; +} + - (void)setData:(GLData *)data { _data = data; @@ -161,37 +189,39 @@ RCT_NOT_IMPLEMENTED(-init) [self setNeedsDisplay]; } -- (void)syncData +- (bool)syncData:(NSError **)error { @autoreleasepool { - + NSDictionary *prevImages = _images; NSMutableDictionary *images = [[NSMutableDictionary alloc] init]; - + GLRenderData * (^traverseTree) (GLData *data); __block __weak GLRenderData * (^weak_traverseTree)(GLData *data); weak_traverseTree = traverseTree = ^GLRenderData *(GLData *data) { NSNumber *width = data.width; NSNumber *height = data.height; + NSNumber *pixelRatio = data.pixelRatio; int fboId = [data.fboId intValue]; - + NSMutableArray *contextChildren = [[NSMutableArray alloc] init]; for (GLData *child in data.contextChildren) { GLRenderData *node = weak_traverseTree(child); if (node == nil) return nil; [contextChildren addObject:node]; } - + NSMutableArray *children = [[NSMutableArray alloc] init]; for (GLData *child in data.children) { GLRenderData *node = weak_traverseTree(child); if (node == nil) return nil; [children addObject:node]; } - + GLShader *shader = [_bridge.rnglContext getShader:data.shader]; if (shader == nil) return nil; - + if (![shader ensureCompiles:error]) return nil; + NSDictionary *uniformTypes = [shader uniformTypes]; NSMutableDictionary *uniforms = [[NSMutableDictionary alloc] init]; NSMutableDictionary *textures = [[NSMutableDictionary alloc] init]; @@ -199,8 +229,8 @@ RCT_NOT_IMPLEMENTED(-init) for (NSString *uniformName in data.uniforms) { id value = [data.uniforms objectForKey:uniformName]; GLenum type = [uniformTypes[uniformName] intValue]; - - + + if (type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE) { uniforms[uniformName] = [NSNumber numberWithInt:units++]; if ([value isEqual:[NSNull null]]) { @@ -227,7 +257,7 @@ RCT_NOT_IMPLEMENTED(-init) if (!src) { RCTLogError(@"texture uniform '%@': Invalid uri format '%@'", uniformName, value); } - + GLImage *image = images[src]; if (image == nil) { image = prevImages[src]; @@ -253,42 +283,41 @@ RCT_NOT_IMPLEMENTED(-init) uniforms[uniformName] = value; } } - + int maxTextureUnits; glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits); if (units > maxTextureUnits) { RCTLogError(@"Maximum number of texture reach. got %i >= max %i", units, maxTextureUnits); } - + for (NSString *uniformName in shader.uniformTypes) { if (uniforms[uniformName] == nil) { RCTLogError(@"All defined uniforms must be provided. Missing '%@'", uniformName); } } - + return [[GLRenderData alloc] initWithShader:shader withUniforms:uniforms withTextures:textures - withWidth:width - withHeight:height + withWidth:(int)([width floatValue] * [pixelRatio floatValue]) + withHeight:(int)([height floatValue] * [pixelRatio floatValue]) withFboId:fboId withContextChildren:contextChildren withChildren:children]; }; - + GLRenderData *res = traverseTree(_data); if (res != nil) { - _needSync = false; _renderData = traverseTree(_data); _images = images; for (NSString *src in diff([prevImages allKeys], [images allKeys])) { [_preloaded removeObject:src]; } + return true; } else { - // the data is not ready, retry in one tick - [self setNeedsDisplay]; + return false; } } } @@ -305,7 +334,7 @@ RCT_NOT_IMPLEMENTED(-init) UIView *v = [view.subviews count] == 1 ? view.subviews[0] : view; - imgData = [GLImageData genPixelsWithView:v]; + imgData = [GLImageData genPixelsWithView:v withPixelRatio:self.contentScaleFactor]; } else { imgData = nil; } @@ -352,21 +381,30 @@ RCT_NOT_IMPLEMENTED(-init) - (void)drawRect:(CGRect)rect { self.layer.opaque = _opaque; - + if (_neverRendered) { _neverRendered = false; glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); } - + if (_needSync) { - [self syncData]; + NSError *error; + BOOL syncSuccessful = [self syncData:&error]; + BOOL errorCanBeRecovered = error==nil || (error.code != GLLinkingFailure && error.code != GLCompileFailure); + if (!syncSuccessful && errorCanBeRecovered) { + // something failed but is recoverable, retry in one tick + [self performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:NO]; + } + else { + _needSync = false; + } } - + if ([self haveRemainingToPreload]) { return; } - + BOOL needsDeferredRendering = [_nbContentTextures intValue] > 0 && !_autoRedraw; if (needsDeferredRendering && !_deferredRendering) { _deferredRendering = true; @@ -375,21 +413,63 @@ RCT_NOT_IMPLEMENTED(-init) else { _deferredRendering = false; [self render]; - if (_captureFrameRequested) { - _captureFrameRequested = false; + if (!_captureScheduled && [_captureConfigs count] > 0) { + _captureScheduled = true; [self performSelectorOnMainThread:@selector(capture) withObject:nil waitUntilDone:NO]; } } } --(void)capture +-(void) capture { + _captureScheduled = false; + if (!self.onGLCaptureFrame) return; + UIImage *frameImage = [self snapshot]; - NSData *frameData = UIImagePNGRepresentation(frameImage); - NSString *frame = - [NSString stringWithFormat:@"data:image/png;base64,%@", - [frameData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]]; - if (self.onGLCaptureFrame) self.onGLCaptureFrame(@{ @"frame": frame }); + + for (CaptureConfig *config in _captureConfigs) { + id result; + id error; + + BOOL isPng = [config.type isEqualToString:@"png"]; + BOOL isJpeg = !isPng && ([config.type isEqualToString:@"jpeg"] || [config.type isEqualToString:@"jpg"]); + + BOOL isBase64 = [config.format isEqualToString:@"base64"]; + BOOL isFile = !isBase64 && [config.format isEqualToString:@"file"]; + + NSData *frameData = + isPng ? UIImagePNGRepresentation(frameImage) : + isJpeg ? UIImageJPEGRepresentation(frameImage, [config.quality floatValue]) : + nil; + + if (!frameData) { + error = [NSString stringWithFormat:@"Unsupported capture type '%@'", config.type]; + } + else if (isBase64) { + NSString *base64 = [frameData base64EncodedStringWithOptions: NSDataBase64Encoding64CharacterLineLength]; + result = [NSString stringWithFormat:@"data:image/%@;base64,%@", config.type, base64]; + } + else if (isFile) { + NSError *e; + if (![frameData writeToFile:config.filePath options:0 error:&e]) { + error = [NSString stringWithFormat:@"Could not write file: %@", e.localizedDescription]; + } + else { + result = [NSString stringWithFormat:@"file://%@", config.filePath]; + } + } + else { + error = [NSString stringWithFormat:@"Unsupported capture format '%@'", config.format]; + } + + NSMutableDictionary *response = [[NSMutableDictionary alloc] init]; + response[@"config"] = [config dictionary]; + if (error) response[@"error"] = error; + if (result) response[@"result"] = result; + self.onGLCaptureFrame(response); + } + + _captureConfigs = [[NSMutableArray alloc] init]; } - (void)render @@ -397,22 +477,21 @@ RCT_NOT_IMPLEMENTED(-init) GLRenderData *rd = _renderData; if (!rd) return; RCT_PROFILE_BEGIN_EVENT(0, @"GLCanvas render", nil); - + @autoreleasepool { - CGFloat scale = RCTScreenScale(); - + void (^recDraw) (GLRenderData *renderData); __block __weak void (^weak_recDraw) (GLRenderData *renderData); weak_recDraw = recDraw = ^void(GLRenderData *renderData) { - float w = [renderData.width floatValue] * scale; - float h = [renderData.height floatValue] * scale; - + int w = renderData.width; + int h = renderData.height; + for (GLRenderData *child in renderData.contextChildren) weak_recDraw(child); - + for (GLRenderData *child in renderData.children) weak_recDraw(child); - + if (renderData.fboId == -1) { glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); glViewport(0, 0, w, h); @@ -422,42 +501,42 @@ RCT_NOT_IMPLEMENTED(-init) [fbo setShapeWithWidth:w withHeight:h]; [fbo bind]; } - + [renderData.shader bind]; - + for (NSString *uniformName in renderData.textures) { GLTexture *texture = renderData.textures[uniformName]; int unit = [((NSNumber *)renderData.uniforms[uniformName]) intValue]; [texture bind:unit]; } - + for (NSString *uniformName in renderData.uniforms) { [renderData.shader setUniform:uniformName withValue:renderData.uniforms[uniformName]]; } - + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); glDrawArrays(GL_TRIANGLES, 0, 6); }; - + // DRAWING THE SCENE - + [self syncContentTextures]; - + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); glEnable(GL_BLEND); recDraw(rd); glDisable(GL_BLEND); glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); glBindBuffer(GL_ARRAY_BUFFER, 0); - + if (_dirtyOnLoad && ![self haveRemainingToPreload]) { _dirtyOnLoad = false; [self dispatchOnLoad]; } } - + RCT_PROFILE_END_EVENT(0, @"gl", nil); } diff --git a/ios/GLCanvasManager.m b/ios/GLCanvasManager.m index 48c0212dcccc77b973e56263a1a8406c5694503f..c2fd5d64a0369f94f705cdf79e0e136163f69093 100644 --- a/ios/GLCanvasManager.m +++ b/ios/GLCanvasManager.m @@ -1,6 +1,7 @@ #import "GLCanvasManager.h" #import "GLCanvas.h" #import "RCTConvert+GLData.h" +#import "RCTConvert+CaptureConfig.h" #import "RCTUIManager.h" #import "RCTLog.h" #import @@ -17,17 +18,23 @@ RCT_EXPORT_MODULE(); return self; } +- (dispatch_queue_t)methodQueue +{ + return self.bridge.uiManager.methodQueue; +} + RCT_EXPORT_VIEW_PROPERTY(nbContentTextures, NSNumber); RCT_EXPORT_VIEW_PROPERTY(opaque, BOOL); RCT_EXPORT_VIEW_PROPERTY(autoRedraw, BOOL); RCT_EXPORT_VIEW_PROPERTY(data, GLData); RCT_EXPORT_VIEW_PROPERTY(renderId, NSNumber); +RCT_EXPORT_VIEW_PROPERTY(pixelRatio, NSNumber); RCT_EXPORT_VIEW_PROPERTY(imagesToPreload, NSArray); RCT_EXPORT_VIEW_PROPERTY(onGLLoad, RCTBubblingEventBlock); RCT_EXPORT_VIEW_PROPERTY(onGLProgress, RCTBubblingEventBlock); RCT_EXPORT_VIEW_PROPERTY(onGLCaptureFrame, RCTBubblingEventBlock); -RCT_EXPORT_METHOD(capture: (nonnull NSNumber *)reactTag) +RCT_EXPORT_METHOD(capture: (nonnull NSNumber *)reactTag withConfig:(id)config) { [self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { UIView *view = viewRegistry[reactTag]; @@ -36,7 +43,7 @@ RCT_EXPORT_METHOD(capture: (nonnull NSNumber *)reactTag) } else { GLCanvas *glCanvas = (GLCanvas *)view; - [glCanvas requestCaptureFrame]; + [glCanvas requestCaptureFrame:[RCTConvert CaptureConfig:config]]; } }]; } diff --git a/ios/GLData.h b/ios/GLData.h index 4523918aff428a0fe32dfc80ffc7fc1a20184429..273330cd00ea301648bd8e8c2087f17b1a4f8639 100644 --- a/ios/GLData.h +++ b/ios/GLData.h @@ -8,6 +8,7 @@ @property (nonatomic) NSDictionary *uniforms; @property (nonatomic) NSNumber *width; @property (nonatomic) NSNumber *height; +@property (nonatomic) NSNumber *pixelRatio; @property (nonatomic) NSNumber *fboId; @property (nonatomic) NSArray *contextChildren; @property (nonatomic) NSArray *children; @@ -16,6 +17,7 @@ withUniforms: (NSDictionary *)uniforms withWidth: (NSNumber *)width withHeight: (NSNumber *)height + withPixelRatio: (NSNumber *)pixelRatio withFboId: (NSNumber *)fboId withContextChildren: (NSArray *)contextChildren withChildren: (NSArray *)children; diff --git a/ios/GLData.m b/ios/GLData.m index 246685431b6c4b4b8181950600ab8f28282ac212..faf9f36ea6030abca7a80639249ec234cfff27a9 100644 --- a/ios/GLData.m +++ b/ios/GLData.m @@ -6,6 +6,7 @@ withUniforms: (NSDictionary *)uniforms withWidth: (NSNumber *)width withHeight: (NSNumber *)height + withPixelRatio: (NSNumber *)pixelRatio withFboId: (NSNumber *)fboId withContextChildren: (NSArray *)contextChildren withChildren: (NSArray *)children @@ -15,6 +16,7 @@ self.uniforms = uniforms; self.width = width; self.height = height; + self.pixelRatio = pixelRatio; self.fboId = fboId; self.contextChildren = contextChildren; self.children = children; diff --git a/ios/GLImageData.h b/ios/GLImageData.h index c2010f350abb18fc995d53dc2a50f3198f1ad32e..5131ad06123a94391a4548abdc38fe7b8402fdb5 100644 --- a/ios/GLImageData.h +++ b/ios/GLImageData.h @@ -8,7 +8,7 @@ + (GLImageData*) empty; + (GLImageData*) genPixelsWithImage: (UIImage *)image; -+ (GLImageData*) genPixelsWithView: (UIView *)view; ++ (GLImageData*) genPixelsWithView: (UIView *)view withPixelRatio:(float)pixelRatio; - (instancetype)initWithData: (GLubyte *)data withWidth:(int)width withHeight:(int)height; diff --git a/ios/GLImageData.m b/ios/GLImageData.m index 5ef0c814898a0129aba4d123a24a59ee077d86f6..8b301a9c1de868a8d7b38d23070438cd0a57715c 100644 --- a/ios/GLImageData.m +++ b/ios/GLImageData.m @@ -1,6 +1,5 @@ #import "GLImageData.h" -#import "RCTUtils.h" #import "RCTLog.h" // This structure aims to be used in an immutable way @@ -55,16 +54,16 @@ GLImageData *EMPTY_PIXELS; return [[GLImageData alloc] initWithData:data withWidth:width withHeight:height]; } -+ (GLImageData *)genPixelsWithView: (UIView *)view ++ (GLImageData *)genPixelsWithView: (UIView *)view withPixelRatio:(float)pixelRatio { - float width = RCTScreenScale() * view.bounds.size.width; - float height = RCTScreenScale() * view.bounds.size.height; + float width = pixelRatio * view.bounds.size.width; + float height = pixelRatio * view.bounds.size.height; GLubyte *data = (GLubyte *)malloc(4 * width * height); CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef ctx = CGBitmapContextCreate(data, width, height, 8, 4 * width, colourSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); CGColorSpaceRelease(colourSpace); CGContextClearRect(ctx, CGRectMake(0.0, 0.0, width, height)); - CGContextScaleCTM(ctx, RCTScreenScale(), RCTScreenScale()); + CGContextScaleCTM(ctx, pixelRatio, pixelRatio); [view.layer renderInContext:ctx]; CGContextRelease(ctx); return [[GLImageData alloc] initWithData:data withWidth:width withHeight:height]; diff --git a/ios/GLRenderData.h b/ios/GLRenderData.h index 44ecf9d2fe03aa86509f21c650e658791bcde221..ae3ab257515de575a81b73e2f0a7b491fa38c004 100644 --- a/ios/GLRenderData.h +++ b/ios/GLRenderData.h @@ -7,8 +7,8 @@ @property (nonatomic) GLShader *shader; @property (nonatomic) NSDictionary *uniforms; @property (nonatomic) NSDictionary *textures; -@property (nonatomic) NSNumber *width; -@property (nonatomic) NSNumber *height; +@property (nonatomic) int width; +@property (nonatomic) int height; @property (nonatomic) int fboId; @property (nonatomic) NSArray *contextChildren; @property (nonatomic) NSArray *children; @@ -16,8 +16,8 @@ -(instancetype) initWithShader: (GLShader *)shader withUniforms:(NSDictionary *)uniforms withTextures: (NSDictionary *)textures - withWidth: (NSNumber *)width - withHeight: (NSNumber *)height + withWidth: (int)width + withHeight: (int)height withFboId: (int)fboId withContextChildren: (NSArray *)contextChildren withChildren: (NSArray *)children; diff --git a/ios/GLRenderData.m b/ios/GLRenderData.m index f4b6721c6ec01c8eeecf037813ed5a5f8e02633f..902b14a29c079ea260324829b3fcf2e149a30d0b 100644 --- a/ios/GLRenderData.m +++ b/ios/GLRenderData.m @@ -6,8 +6,8 @@ -(instancetype) initWithShader: (GLShader *)shader withUniforms:(NSDictionary *)uniforms withTextures: (NSDictionary *)textures - withWidth: (NSNumber *)width - withHeight: (NSNumber *)height + withWidth: (int)width + withHeight: (int)height withFboId: (int)fboId withContextChildren: (NSArray *)contextChildren withChildren: (NSArray *)children diff --git a/ios/GLShader.h b/ios/GLShader.h index 2983552720cba3f4efdcc0c36ad32c67209842a0..a187a1fba4cd355ffb40a6e4b6acfcc0d91d0909 100644 --- a/ios/GLShader.h +++ b/ios/GLShader.h @@ -1,6 +1,13 @@ #import #import "RCTBridgeModule.h" +NS_ENUM(NSInteger) { + GLContextFailure = 87001, + GLLinkingFailure = 87002, + GLCompileFailure = 87003, + GLNotAProgram = 87004 +}; + @interface GLShader: NSObject @property EAGLContext *context; @@ -18,10 +25,7 @@ */ - (void) bind; -/** - * Check the shader validity - */ -- (void) validate; +- (bool) ensureCompiles: (NSError**)error; /** * Set the value of an uniform diff --git a/ios/GLShader.m b/ios/GLShader.m index 2271400bec1d2e384a581f018e31d39c225e17b7..37e5c10c84287e550dfcbb19816c19e37272646c 100644 --- a/ios/GLShader.m +++ b/ios/GLShader.m @@ -5,7 +5,7 @@ #import "RCTConvert.h" #import "GLShader.h" -GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shaderType) { +GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shaderType, NSError **error) { GLuint shaderHandle = glCreateShader(shaderType); @@ -20,8 +20,10 @@ GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shade if (compileSuccess == GL_FALSE) { GLchar messages[256]; glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]); - NSString *messageString = [NSString stringWithUTF8String:messages]; - RCTLogError(@"Shader '%@' failed to compile: %@", shaderName, messageString); + *error = [[NSError alloc] + initWithDomain:[NSString stringWithUTF8String:messages] + code:GLCompileFailure + userInfo:nil]; return -1; } @@ -41,6 +43,7 @@ GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shade GLint pointerLoc; // The "pointer" attribute is used to iterate over vertex NSDictionary *_uniformTypes; // The types of the GLSL uniforms (N.B: array are not supported) NSDictionary *_uniformLocations; // The uniform locations cache + NSError *_error; } - (instancetype)initWithContext: (EAGLContext*)context withName:(NSString *)name withVert:(NSString *)vert withFrag:(NSString *)frag @@ -51,7 +54,10 @@ GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shade _context = context; _vert = vert; _frag = frag; - [self makeProgram]; + NSError *error; + if (![self makeProgram:&error]) { + _error = error; + } } return self; } @@ -60,12 +66,21 @@ GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shade { glDeleteProgram(program); glDeleteBuffers(1, &buffer); + _name = nil; + _context = nil; + _vert = nil; + _frag = nil; + _uniformLocations = nil; + _uniformTypes = nil; + program = 0; + buffer = 0; + pointerLoc = 0; } -- (bool) ensureContext +- (bool) ensureContext: (NSError **)error { - if (![EAGLContext setCurrentContext:_context]) { - RCTLogError(@"Shader '%@': Failed to set current OpenGL context", _name); + if (!_context || ![EAGLContext setCurrentContext:_context]) { + *error = [[NSError alloc] initWithDomain:@"Failed to set current OpenGL context" code:GLContextFailure userInfo:nil]; return false; } return true; @@ -73,11 +88,6 @@ GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shade - (void) bind { - if (![self ensureContext]) return; - if ( glIsProgram(program) != GL_TRUE ){ - RCTLogError(@"Shader '%@': not a program!", _name); - return; - } glUseProgram(program); glBindBuffer(GL_ARRAY_BUFFER, buffer); glEnableVertexAttribArray(pointerLoc); @@ -307,19 +317,6 @@ GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shade } } -- (void) validate -{ - glValidateProgram(program); - GLint validSuccess; - glGetProgramiv(program, GL_VALIDATE_STATUS, &validSuccess); - if (validSuccess == GL_FALSE) { - GLchar messages[256]; - glGetProgramInfoLog(program, sizeof(messages), 0, &messages[0]); - NSString *messageString = [NSString stringWithUTF8String:messages]; - RCTLogError(@"Shader '%@': Validation failed %@", _name, messageString); - } -} - - (void) computeMeta { NSMutableDictionary *uniforms = @{}.mutableCopy; @@ -342,15 +339,29 @@ GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shade _uniformLocations = locations; } -- (void) makeProgram +- (bool) ensureCompiles: (NSError **)error { - if (![self ensureContext]) return; + if (![self ensureContext:error]) { + return false; + } + if (!glIsProgram(program)) { + *error = [[NSError alloc] initWithDomain:@"not a program" code:GLNotAProgram userInfo:nil]; + return false; + } + if (_error == nil) return true; + *error = _error; + return false; +} - GLuint vertex = compileShader(_name, _vert, GL_VERTEX_SHADER); - if (vertex == -1) return; +- (bool) makeProgram: (NSError **)error +{ + if (![self ensureContext:error]) return false; - GLuint fragment = compileShader(_name, _frag, GL_FRAGMENT_SHADER); - if (fragment == -1) return; + GLuint vertex = compileShader(_name, _vert, GL_VERTEX_SHADER, error); + if (vertex == -1) return false; + + GLuint fragment = compileShader(_name, _frag, GL_FRAGMENT_SHADER, error); + if (fragment == -1) return false; program = glCreateProgram(); glAttachShader(program, vertex); @@ -362,9 +373,11 @@ GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shade if (linkSuccess == GL_FALSE) { GLchar messages[256]; glGetProgramInfoLog(program, sizeof(messages), 0, &messages[0]); - NSString *messageString = [NSString stringWithUTF8String:messages]; - RCTLogError(@"Shader '%@': Linking failed %@", _name, messageString); - return; + *error = [[NSError alloc] + initWithDomain:[NSString stringWithUTF8String:messages] + code:GLLinkingFailure + userInfo:nil]; + return false; } glUseProgram(program); @@ -384,6 +397,8 @@ GLuint compileShader (NSString* shaderName, NSString* shaderString, GLenum shade 1.0, 1.0 }; glBufferData(GL_ARRAY_BUFFER, sizeof(buf), buf, GL_STATIC_DRAW); + + return true; } diff --git a/ios/RCTConvert+CaptureConfig.h b/ios/RCTConvert+CaptureConfig.h new file mode 100644 index 0000000000000000000000000000000000000000..d2767147dc1712f0d68ba322e8b80189d688e7d4 --- /dev/null +++ b/ios/RCTConvert+CaptureConfig.h @@ -0,0 +1,8 @@ +#import "RCTConvert.h" +#import "CaptureConfig.h" + +@interface RCTConvert (CaptureConfig) + ++ (CaptureConfig *)CaptureConfig:(id)json; + +@end diff --git a/ios/RCTConvert+CaptureConfig.m b/ios/RCTConvert+CaptureConfig.m new file mode 100644 index 0000000000000000000000000000000000000000..f72159daac51c454617fcbbab4abdd025f666b2f --- /dev/null +++ b/ios/RCTConvert+CaptureConfig.m @@ -0,0 +1,22 @@ +// +// RCTConvert+CaptureConfig.m +// RNGL +// +// Created by Gaetan Renaudeau on 30/12/15. +// +// + +#import "RCTConvert+CaptureConfig.h" + +@implementation RCTConvert (CaptureConfig) + ++ (CaptureConfig *)CaptureConfig:(id)json +{ + return [[CaptureConfig alloc] + initWithFormat:[RCTConvert NSString:json[@"format"]] + withType:[RCTConvert NSString:json[@"type"]] + withQuality:[RCTConvert NSNumber:json[@"quality"]] + withFilePath:[RCTConvert NSString:json[@"filePath"]]]; +} + +@end diff --git a/ios/RCTConvert+GLData.m b/ios/RCTConvert+GLData.m index e65f49cd84f828339c39663fbdfffb8900124148..786873ef66800a0b0a25d9eb1b5765fbe5f84c25 100644 --- a/ios/RCTConvert+GLData.m +++ b/ios/RCTConvert+GLData.m @@ -10,6 +10,7 @@ NSDictionary *uniforms = [self NSDictionary:json[@"uniforms"]]; NSNumber *width = [self NSNumber:json[@"width"]]; NSNumber *height = [self NSNumber:json[@"height"]]; + NSNumber *pixelRatio = [self NSNumber:json[@"pixelRatio"]]; NSNumber *fboId = [self NSNumber:json[@"fboId"]]; NSArray *contextChildrenJSON = [self NSArray: json[@"contextChildren"]]; NSArray *childrenJSON = [self NSArray: json[@"children"]]; @@ -30,6 +31,7 @@ withUniforms: uniforms withWidth: width withHeight: height + withPixelRatio: pixelRatio withFboId: fboId withContextChildren: contextChildren withChildren: children]; diff --git a/ios/RNGL.xcodeproj/project.pbxproj b/ios/RNGL.xcodeproj/project.pbxproj index 359f9cef099c9001e68458554b875375d70dfca8..3624ab06f36f5d5fe480d97cbf73dcb081eefafd 100644 --- a/ios/RNGL.xcodeproj/project.pbxproj +++ b/ios/RNGL.xcodeproj/project.pbxproj @@ -18,6 +18,8 @@ 346089D81BEFD0A500C90DB5 /* GLTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = 346089CB1BEFD0A500C90DB5 /* GLTexture.m */; }; 346089D91BEFD0A500C90DB5 /* RCTConvert+GLData.m in Sources */ = {isa = PBXBuildFile; fileRef = 346089CD1BEFD0A500C90DB5 /* RCTConvert+GLData.m */; }; 346089DA1BEFD0A500C90DB5 /* RNGLContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 346089CF1BEFD0A500C90DB5 /* RNGLContext.m */; }; + 34C990421C34939C002F49FC /* RCTConvert+CaptureConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 34C990411C34939C002F49FC /* RCTConvert+CaptureConfig.m */; }; + 34C990471C349422002F49FC /* CaptureConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 34C990461C349422002F49FC /* CaptureConfig.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -48,13 +50,17 @@ 346089C61BEFD0A500C90DB5 /* GLRenderData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLRenderData.h; sourceTree = ""; }; 346089C71BEFD0A500C90DB5 /* GLRenderData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GLRenderData.m; sourceTree = ""; }; 346089C81BEFD0A500C90DB5 /* GLShader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLShader.h; sourceTree = ""; }; - 346089C91BEFD0A500C90DB5 /* GLShader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GLShader.m; sourceTree = ""; }; + 346089C91BEFD0A500C90DB5 /* GLShader.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = GLShader.m; sourceTree = ""; tabWidth = 2; }; 346089CA1BEFD0A500C90DB5 /* GLTexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLTexture.h; sourceTree = ""; }; 346089CB1BEFD0A500C90DB5 /* GLTexture.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = GLTexture.m; sourceTree = ""; tabWidth = 2; }; 346089CC1BEFD0A500C90DB5 /* RCTConvert+GLData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+GLData.h"; sourceTree = ""; }; 346089CD1BEFD0A500C90DB5 /* RCTConvert+GLData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+GLData.m"; sourceTree = ""; }; 346089CE1BEFD0A500C90DB5 /* RNGLContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNGLContext.h; sourceTree = ""; }; 346089CF1BEFD0A500C90DB5 /* RNGLContext.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = RNGLContext.m; sourceTree = ""; tabWidth = 2; }; + 34C990401C34939C002F49FC /* RCTConvert+CaptureConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+CaptureConfig.h"; sourceTree = ""; }; + 34C990411C34939C002F49FC /* RCTConvert+CaptureConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+CaptureConfig.m"; sourceTree = ""; tabWidth = 2; }; + 34C990451C34941C002F49FC /* CaptureConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CaptureConfig.h; sourceTree = ""; }; + 34C990461C349422002F49FC /* CaptureConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = CaptureConfig.m; sourceTree = ""; tabWidth = 2; }; 4107012F1ACB723B00C6AA39 /* libRNGL.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNGL.a; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -92,6 +98,10 @@ 346089CB1BEFD0A500C90DB5 /* GLTexture.m */, 346089CC1BEFD0A500C90DB5 /* RCTConvert+GLData.h */, 346089CD1BEFD0A500C90DB5 /* RCTConvert+GLData.m */, + 34C990461C349422002F49FC /* CaptureConfig.m */, + 34C990451C34941C002F49FC /* CaptureConfig.h */, + 34C990401C34939C002F49FC /* RCTConvert+CaptureConfig.h */, + 34C990411C34939C002F49FC /* RCTConvert+CaptureConfig.m */, 346089CE1BEFD0A500C90DB5 /* RNGLContext.h */, 346089CF1BEFD0A500C90DB5 /* RNGLContext.m */, 410701301ACB723B00C6AA39 /* Products */, @@ -161,10 +171,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 34C990421C34939C002F49FC /* RCTConvert+CaptureConfig.m in Sources */, 346089D31BEFD0A500C90DB5 /* GLFBO.m in Sources */, 346089DA1BEFD0A500C90DB5 /* RNGLContext.m in Sources */, 346089D21BEFD0A500C90DB5 /* GLData.m in Sources */, 346089D91BEFD0A500C90DB5 /* RCTConvert+GLData.m in Sources */, + 34C990471C349422002F49FC /* CaptureConfig.m in Sources */, 346089D51BEFD0A500C90DB5 /* GLImageData.m in Sources */, 346089D41BEFD0A500C90DB5 /* GLImage.m in Sources */, 346089D61BEFD0A500C90DB5 /* GLRenderData.m in Sources */, diff --git a/ios/RNGLContext.m b/ios/RNGLContext.m index 689bb50e5b53997b4e755e360697e2d5ca1c7716..909aac30db9295724f8e82436ed9c1e7d483ce7c 100644 --- a/ios/RNGLContext.m +++ b/ios/RNGLContext.m @@ -45,9 +45,73 @@ RCT_EXPORT_MODULE() return _context; } +- (void)_addShader:(nonnull NSNumber *)id +withConfig:(NSDictionary *)config +withOnCompile:(RCTResponseSenderBlock)onCompile +{ + NSString *frag = [RCTConvert NSString:config[@"frag"]]; + NSString *name = [RCTConvert NSString:config[@"name"]]; + if (!frag) { + RCTLogError(@"Shader '%@': missing frag field", name); + return; + } + GLShader *shader = [[GLShader alloc] initWithContext:_context withName:name withVert:fullViewportVert withFrag:frag]; + NSError *error; + bool success = [shader ensureCompiles:&error]; + if (onCompile) { + if (!success) { + onCompile(@[error.domain]); + } + else { + onCompile(@[[NSNull null], + @{ + @"uniforms": shader.uniformTypes + }]); + } + } + else { + if (!success) { + RCTLogError(@"Shader '%@': %@", name, error.domain); + } + } + _shaders[id] = shader; +} + static NSString* fullViewportVert = @"attribute vec2 position;varying vec2 uv;void main() {gl_Position = vec4(position,0.0,1.0);uv = vec2(0.5, 0.5) * (position+vec2(1.0, 1.0));}"; -RCT_EXPORT_METHOD(addShader:(nonnull NSNumber *)id withConfig:(NSDictionary *)config) { +NSString* glTypeString (int type) { + switch (type) { + case GL_FLOAT: return @"float"; + case GL_FLOAT_VEC2: return @"vec2"; + case GL_FLOAT_VEC3: return @"vec3"; + case GL_FLOAT_VEC4: return @"vec4"; + case GL_INT: return @"int"; + case GL_INT_VEC2: return @"ivec2"; + case GL_INT_VEC3: return @"ivec3"; + case GL_INT_VEC4: return @"ivec4"; + case GL_BOOL: return @"bool"; + case GL_BOOL_VEC2: return @"bvec2"; + case GL_BOOL_VEC3: return @"bvec3"; + case GL_BOOL_VEC4: return @"bvec4"; + case GL_FLOAT_MAT2: return @"mat2"; + case GL_FLOAT_MAT3: return @"mat3"; + case GL_FLOAT_MAT4: return @"mat4"; + case GL_SAMPLER_2D: return @"sampler2D"; + case GL_SAMPLER_CUBE: return @"samplerCube"; + } + return @""; +} +NSDictionary* glTypesString (NSDictionary *types) { + NSMutableDictionary *dict = types.mutableCopy; + for (NSString *key in [dict allKeys]) { + dict[key] = glTypeString([dict[key] intValue]); + } + return dict; +} + +RCT_EXPORT_METHOD(addShader:(nonnull NSNumber *)id + withConfig:(NSDictionary *)config + withOnCompile:(RCTResponseSenderBlock)onCompile) { NSString *frag = [RCTConvert NSString:config[@"frag"]]; NSString *name = [RCTConvert NSString:config[@"name"]]; if (!frag) { @@ -55,9 +119,36 @@ RCT_EXPORT_METHOD(addShader:(nonnull NSNumber *)id withConfig:(NSDictionary *)co return; } GLShader *shader = [[GLShader alloc] initWithContext:_context withName:name withVert:fullViewportVert withFrag:frag]; + NSError *error; + bool success = [shader ensureCompiles:&error]; + if (onCompile) { + if (!success) { + onCompile(@[error.domain]); + } + else { + onCompile(@[[NSNull null], + @{ + @"uniforms": glTypesString(shader.uniformTypes) + }]); + } + } + else { + if (!success) { + RCTLogError(@"Shader '%@': %@", name, error.domain); + } + } _shaders[id] = shader; } +RCT_EXPORT_METHOD(removeShader:(nonnull NSNumber *)id) { + GLShader *shader = [_shaders objectForKey:id]; + if (!shader) { + RCTLogError(@"removeShader(%@): shader does not exist", id); + return; + } + [_shaders removeObjectForKey:id]; +} + @end @implementation RCTBridge (RNGLContext) diff --git a/package.json b/package.json index f17fd230da915f2edf3459288a5f10a75dbbafa2..615c6b37ef4b1bcf995d6d1bf3cfb7a10c8024e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gl-react-native", - "version": "2.17.8", + "version": "2.18.0-rc", "description": "OpenGL bindings for react-native to implement complex effects over images and components, in the descriptive VDOM paradigm", "repository": { "type": "git", @@ -19,15 +19,10 @@ "author": "Project September ", "license": "MIT", "peerDependencies": { - "react-native": ">= 0.17.0 <0.18.0", - "gl-react": ">= 2.0.3 <2.1.0" + "react-native": "0.18.0-rc", + "gl-react": "^2.1.0" }, "dependencies": { "invariant": "2.2.0" - }, - "devDependencies": { - "babel-eslint": "^4.1.6", - "eslint": "^1.9.0", - "eslint-plugin-react": "^3.8.0" } } diff --git a/src/GLCanvas.captureFrame.android.js b/src/GLCanvas.captureFrame.android.js index ceb938beba2c6e1c81e63891e1c628177c09af48..f16ee537b4935ae3c43b85e82d2b553c02b0892b 100644 --- a/src/GLCanvas.captureFrame.android.js +++ b/src/GLCanvas.captureFrame.android.js @@ -12,4 +12,4 @@ See README install instructions. React.NativeModules.UIManager.GLCanvas is %s`, GLCanvas); const {Commands} = GLCanvas; -module.exports = handle => UIManager.dispatchViewManagerCommand(handle, Commands.captureFrame, []); +module.exports = (handle, config) => UIManager.dispatchViewManagerCommand(handle, Commands.captureFrame, [ config ]); diff --git a/src/GLCanvas.captureFrame.ios.js b/src/GLCanvas.captureFrame.ios.js index 24a716e17bf8feb7de298e610f8242c5ace886da..80c2a53b862191b7f4f9dd590829a5b2dd194ba4 100644 --- a/src/GLCanvas.captureFrame.ios.js +++ b/src/GLCanvas.captureFrame.ios.js @@ -10,4 +10,4 @@ See README install instructions. React.NativeModules.GLCanvasManager is %s`, GLCanvasManager); -module.exports = handle => GLCanvasManager.capture(handle); +module.exports = (handle, config) => GLCanvasManager.capture(handle, config); diff --git a/src/GLCanvas.js b/src/GLCanvas.js index 17d7b40209eed2615c434ca35abfa084224fbc55..df14ef0c3ab28c4967b830bf914b43f282a7951b 100644 --- a/src/GLCanvas.js +++ b/src/GLCanvas.js @@ -1,3 +1,4 @@ +const invariant = require("invariant"); const React = require("react-native"); const { Component, @@ -6,6 +7,9 @@ const { const captureFrame = require("./GLCanvas.captureFrame"); +const serializeOption = config => +config.format + ":" + config.type + ":" + config.quality; + const GLCanvasNative = requireNativeComponent("GLCanvas", GLCanvas, { nativeOnly: { onGLChange: true, @@ -25,21 +29,85 @@ function defer() { } class GLCanvas extends Component { - captureFrame (cb) { - const promise = ( - this._pendingCaptureFrame || // use pending capture OR create a new captureFrame pending - (captureFrame(React.findNodeHandle(this.refs.native)), this._pendingCaptureFrame = defer()) - ).promise; - if (typeof cb === "function") { - console.warn("GLSurface: callback parameter of captureFrame is deprecated, use the returned promise instead"); // eslint-disable-line no-console - promise.then(cb); + + componentWillMount () { + this._pendingCaptureFrame = {}; + } + + componentWillUnmount () { + Object.keys(this._pendingCaptureFrame).forEach(key => + this._pendingCaptureFrame[key].reject(new Error("GLCanvas is unmounting"))); + this._pendingCaptureFrame = null; + } + + addPendingCaptureFrame (config) { + const key = serializeOption(config); + return this._pendingCaptureFrame[key] || ( + (captureFrame(React.findNodeHandle(this.refs.native), config), + this._pendingCaptureFrame[key] = defer()) + ); + } + + captureFrame (configArg) { + let config; + if (configArg) { + invariant(typeof configArg==="object", "captureFrame takes an object option in parameter"); + let nb = 0; + if ("format" in configArg) { + invariant( + typeof configArg.format === "string", + "captureFrame({format}): format must be a string (e.g: 'base64'), Got: '%s'", + configArg.format); + if (configArg.format === "file") invariant( + typeof configArg.filePath === "string" && configArg.filePath, + "captureFrame({filePath}): filePath must be defined when using 'file' format and be an non-empty string, Got: '%s'", + configArg.filePath); + nb ++; + } + if ("type" in configArg) { + invariant( + typeof configArg.type === "string", + "captureFrame({type}): type must be a string (e.g: 'png', 'jpg'), Got: '%s'", + configArg.type); + nb ++; + } + if ("quality" in configArg) { + invariant( + typeof configArg.quality === "number" && + configArg.quality >= 0 && + configArg.quality <= 1, + "captureFrame({quality}): quality must be a number between 0 and 1, Got: '%s'", + configArg.quality); + nb ++; + } + if ("filePath" in configArg) { + nb ++; + } + const keys = Object.keys(configArg); + invariant(keys.length === nb, "captureFrame(config): config must be an object with {format, type, quality, filePath}, found some invalid keys in '%s'", keys); + config = configArg; } - return promise; + return this.addPendingCaptureFrame({ + format: "base64", + type: "png", + quality: 1, + filePath: "", + ...config + }).promise; } - onGLCaptureFrame = ({ nativeEvent: {frame} }) => { - this._pendingCaptureFrame.resolve(frame); - this._pendingCaptureFrame = undefined; + + onGLCaptureFrame = ({ nativeEvent: { error, result, config } }) => { + const key = serializeOption(config); + invariant(key in this._pendingCaptureFrame, "capture '%s' is not scheduled in this._pendingCaptureFrame", key); + if (error) { + this._pendingCaptureFrame[key].reject(error); + } + else { + this._pendingCaptureFrame[key].resolve(result); + } + delete this._pendingCaptureFrame[key]; }; + render () { const { width, height, onLoad, onProgress, eventsThrough, ...restProps } = this.props; return props.scale || PixelRatio.get(); function renderVcontent (width, height, id, children, { visibleContent }) { const childrenStyle = { @@ -40,4 +42,4 @@ function renderVcontainer ({ style, width, height, visibleContent, eventsThrough ; } -module.exports = createSurface(renderVcontainer, renderVcontent, renderVGL); +module.exports = createSurface(renderVcontainer, renderVcontent, renderVGL, getPixelRatio); diff --git a/src/index.js b/src/index.js index 98ec5c7f1054b92d5353a5601b549794921a5ad0..5fcd1398fb084f98dbc040f11231551de2ce2cb6 100644 --- a/src/index.js +++ b/src/index.js @@ -10,8 +10,8 @@ See README install instructions. React.NativeModules.RNGLContext is %s`, RNGLContext); // Hook Shaders to RNGLContext -Shaders.list().map(id => RNGLContext.addShader(id, Shaders.get(id))); -Shaders.on("add", (id, shader) => RNGLContext.addShader(id, shader)); +Shaders.on("add", (id, shader, onCompile) => RNGLContext.addShader(id, shader, onCompile)); +Shaders.on("remove", id => RNGLContext.removeShader(id)); module.exports = { Surface