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 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- generateDebugAndroidTestSources
- generateDebugSources
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ 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)
-#
gl-react-native 
+#
gl-react-native  
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