Commit 1a3bdce4 authored by Gaëtan Renaudeau's avatar Gaëtan Renaudeau

Fixes #15 : implement captureFrame()

parent 40ca889f
const React = require("react-native");
const {
StyleSheet,
Component,
View,
Text,
TouchableOpacity
} = React;
const styles = StyleSheet.create({
root: {
backgroundColor: "#ddd",
borderRadius: 4,
borderColor: "#ccc",
borderWidth: 1,
borderStyle: "solid",
width: 150,
padding: 10
},
text: {
color: "#333"
}
});
class Button extends Component {
render () {
const { children, width, ...rest } = this.props;
return (
<TouchableOpacity {...rest}>
<View style={[ {width}, styles.root ]}>
<Text style={styles.text}>{children}</Text>
</View>
</TouchableOpacity>
);
}
}
Button.propTypes = {
};
module.exports = Button;
...@@ -20,6 +20,7 @@ const PieProgress = require("./PieProgress"); ...@@ -20,6 +20,7 @@ const PieProgress = require("./PieProgress");
const OneFingerResponse = require("./OneFingerResponse"); const OneFingerResponse = require("./OneFingerResponse");
const AnimatedHelloGL = require("./AnimatedHelloGL"); const AnimatedHelloGL = require("./AnimatedHelloGL");
const Blur = require("./Blur"); const Blur = require("./Blur");
const Button = require("./Button");
class Simple extends React.Component { class Simple extends React.Component {
constructor (props) { constructor (props) {
...@@ -33,7 +34,15 @@ class Simple extends React.Component { ...@@ -33,7 +34,15 @@ class Simple extends React.Component {
switch1: false, switch1: false,
switch2: false, switch2: false,
switch3: false, switch3: false,
captured: null
}; };
this.onCapture1 = this.onCapture1.bind(this);
}
onCapture1 () {
this.refs.helloGL.captureFrame(data64 => {
this.setState({ captured: data64 });
});
} }
render () { render () {
...@@ -46,6 +55,7 @@ class Simple extends React.Component { ...@@ -46,6 +55,7 @@ class Simple extends React.Component {
switch1, switch1,
switch2, switch2,
switch3, switch3,
captured
} = this.state; } = this.state;
return <ScrollView style={styles.container}> return <ScrollView style={styles.container}>
...@@ -56,7 +66,11 @@ class Simple extends React.Component { ...@@ -56,7 +66,11 @@ class Simple extends React.Component {
<Text style={styles.demoTitle}>1. Hello GL</Text> <Text style={styles.demoTitle}>1. Hello GL</Text>
<View style={styles.demo}> <View style={styles.demo}>
<HelloGL width={256} height={171} /> <HelloGL width={256} height={171} ref="helloGL" />
<View style={{ paddingTop: 20, alignItems: "center", flexDirection: "row" }}>
<Button onPress={this.onCapture1}>captureFrame()</Button>
{captured && <Image source={{ uri:captured }} style={{ marginLeft: 20, width: 51, height: 34 }} />}
</View>
</View> </View>
<Text style={styles.demoTitle}>2. Saturate an Image</Text> <Text style={styles.demoTitle}>2. Saturate an Image</Text>
......
...@@ -7,12 +7,14 @@ ...@@ -7,12 +7,14 @@
@property (nonatomic) BOOL opaque; @property (nonatomic) BOOL opaque;
@property (nonatomic) BOOL autoRedraw; @property (nonatomic) BOOL autoRedraw;
@property (nonatomic) BOOL eventsThrough; @property (nonatomic) BOOL eventsThrough;
@property (nonatomic) int captureNextFrameId;
@property (nonatomic) BOOL visibleContent; @property (nonatomic) BOOL visibleContent;
@property (nonatomic) NSNumber *nbContentTextures; @property (nonatomic) NSNumber *nbContentTextures;
@property (nonatomic) NSNumber *renderId; @property (nonatomic) NSNumber *renderId;
@property (nonatomic) NSArray *imagesToPreload; @property (nonatomic) NSArray *imagesToPreload;
@property (nonatomic, assign) BOOL onProgress; @property (nonatomic, assign) BOOL onProgress;
@property (nonatomic, assign) BOOL onLoad; @property (nonatomic, assign) BOOL onLoad;
@property (nonatomic, assign) BOOL onChange;
- (instancetype)initWithBridge:(RCTBridge *)bridge - (instancetype)initWithBridge:(RCTBridge *)bridge
withContext:(EAGLContext *)context; withContext:(EAGLContext *)context;
......
...@@ -47,6 +47,8 @@ NSString* srcResource (id res) ...@@ -47,6 +47,8 @@ NSString* srcResource (id res)
BOOL _preloadingDone; BOOL _preloadingDone;
NSTimer *animationTimer; NSTimer *animationTimer;
int _lastCaptureId;
} }
- (instancetype)initWithBridge:(RCTBridge *)bridge - (instancetype)initWithBridge:(RCTBridge *)bridge
...@@ -57,6 +59,7 @@ NSString* srcResource (id res) ...@@ -57,6 +59,7 @@ NSString* srcResource (id res)
_images = @{}; _images = @{};
_preloaded = [[NSMutableArray alloc] init]; _preloaded = [[NSMutableArray alloc] init];
_preloadingDone = false; _preloadingDone = false;
_lastCaptureId = 0;
self.context = context; self.context = context;
self.contentScaleFactor = RCTScreenScale(); self.contentScaleFactor = RCTScreenScale();
} }
...@@ -135,6 +138,12 @@ RCT_NOT_IMPLEMENTED(-init) ...@@ -135,6 +138,12 @@ RCT_NOT_IMPLEMENTED(-init)
_nbContentTextures = nbContentTextures; _nbContentTextures = nbContentTextures;
} }
- (void)setCaptureNextFrameId:(int)captureNextFrameId
{
_captureNextFrameId = captureNextFrameId;
[self setNeedsDisplay];
}
//// Sync methods (called from props setters) //// Sync methods (called from props setters)
- (void) syncEventsThrough - (void) syncEventsThrough
...@@ -290,6 +299,7 @@ RCT_NOT_IMPLEMENTED(-init) ...@@ -290,6 +299,7 @@ RCT_NOT_IMPLEMENTED(-init)
{ {
self.layer.opaque = _opaque; self.layer.opaque = _opaque;
[self syncEventsThrough]; [self syncEventsThrough];
__weak GLCanvas *weakSelf = self;
if (!_preloadingDone) { if (!_preloadingDone) {
glClearColor(0.0, 0.0, 0.0, 0.0); glClearColor(0.0, 0.0, 0.0, 0.0);
...@@ -299,13 +309,27 @@ RCT_NOT_IMPLEMENTED(-init) ...@@ -299,13 +309,27 @@ RCT_NOT_IMPLEMENTED(-init)
BOOL needsDeferredRendering = _nbContentTextures > 0 && !_autoRedraw; BOOL needsDeferredRendering = _nbContentTextures > 0 && !_autoRedraw;
if (needsDeferredRendering && !_deferredRendering) { if (needsDeferredRendering && !_deferredRendering) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
if (!weakSelf) return;
_deferredRendering = true; _deferredRendering = true;
[self setNeedsDisplay]; [weakSelf setNeedsDisplay];
}); });
} }
else { else {
[self render]; [self render];
_deferredRendering = false; _deferredRendering = false;
if (_captureNextFrameId > _lastCaptureId) {
_lastCaptureId ++;
int id = _lastCaptureId;
dispatch_async(dispatch_get_main_queue(), ^{ // snapshot not allowed in render tick. defer it.
if (!weakSelf) return;
UIImage *frameImage = [weakSelf snapshot];
NSData *frameData = UIImagePNGRepresentation(frameImage);
NSString *frame =
[NSString stringWithFormat:@"data:image/png;base64,%@",
[frameData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]];
[weakSelf dispatchOnCapture:frame withId:id];
});
}
} }
} }
...@@ -433,4 +457,11 @@ RCT_NOT_IMPLEMENTED(-init) ...@@ -433,4 +457,11 @@ RCT_NOT_IMPLEMENTED(-init)
[_bridge.eventDispatcher sendInputEventWithName:@"progress" body:event]; [_bridge.eventDispatcher sendInputEventWithName:@"progress" body:event];
} }
- (void)dispatchOnCapture: (NSString *)frame withId:(int)id
{
NSDictionary *event = @{ @"target": self.reactTag, @"frame": frame, @"id":@(id) };
// FIXME: using onChange is a hack before we use the new system to directly call callbacks. we will replace with: self.onCaptureNextFrame(...)
[_bridge.eventDispatcher sendInputEventWithName:@"change" body:event];
}
@end @end
...@@ -23,11 +23,13 @@ RCT_EXPORT_VIEW_PROPERTY(opaque, BOOL); ...@@ -23,11 +23,13 @@ RCT_EXPORT_VIEW_PROPERTY(opaque, BOOL);
RCT_EXPORT_VIEW_PROPERTY(autoRedraw, BOOL); RCT_EXPORT_VIEW_PROPERTY(autoRedraw, BOOL);
RCT_EXPORT_VIEW_PROPERTY(eventsThrough, BOOL); RCT_EXPORT_VIEW_PROPERTY(eventsThrough, BOOL);
RCT_EXPORT_VIEW_PROPERTY(visibleContent, BOOL); RCT_EXPORT_VIEW_PROPERTY(visibleContent, BOOL);
RCT_EXPORT_VIEW_PROPERTY(captureNextFrameId, int);
RCT_EXPORT_VIEW_PROPERTY(data, GLData); RCT_EXPORT_VIEW_PROPERTY(data, GLData);
RCT_EXPORT_VIEW_PROPERTY(renderId, NSNumber); RCT_EXPORT_VIEW_PROPERTY(renderId, NSNumber);
RCT_EXPORT_VIEW_PROPERTY(imagesToPreload, NSArray); RCT_EXPORT_VIEW_PROPERTY(imagesToPreload, NSArray);
RCT_EXPORT_VIEW_PROPERTY(onLoad, BOOL); RCT_EXPORT_VIEW_PROPERTY(onLoad, BOOL);
RCT_EXPORT_VIEW_PROPERTY(onProgress, BOOL); RCT_EXPORT_VIEW_PROPERTY(onProgress, BOOL);
RCT_EXPORT_VIEW_PROPERTY(onChange, BOOL);
- (UIView *)view - (UIView *)view
{ {
......
const React = require("react-native");
const {
Component,
requireNativeComponent
} = React;
const GLCanvasNative = requireNativeComponent("GLCanvas", GLCanvas);
class GLCanvas extends Component {
constructor (props) {
super(props);
this.state = {
captureNextFrameId: 0 // the current id to send to the ObjC part.
};
this._captureId = 1; // track the current id to use for captures. it get incremented when the frame is obtained.
this._captureListeners = { [this._captureId]: [] }; // callbacks by capture id
this._needsCapture = false;
this.handleCapture = this.handleCapture.bind(this);
this.onCaptureFrame = this.onCaptureFrame.bind(this);
}
captureFrame (cb) {
this._captureListeners[this._captureId].push(cb);
this.requestCapture();
}
onCaptureFrame ({ nativeEvent: {frame, id} }) {
if (id in this._captureListeners) {
this._captureListeners[id].forEach(listener => listener(frame));
delete this._captureListeners[id];
}
this._captureId ++;
this._captureListeners[this._captureId] = [];
}
requestCapture () {
if (this._needsCapture) return;
this._needsCapture = true;
requestAnimationFrame(this.handleCapture);
}
handleCapture () {
if (!this._needsCapture) return;
this._needsCapture = false;
this.setState({ captureNextFrameId: this._captureId });
}
render () {
const { width, height, ...restProps } = this.props;
const { captureNextFrameId } = this.state;
return <GLCanvasNative
ref="native"
{...restProps}
style={{ width, height }}
captureNextFrameId={captureNextFrameId}
onChange={this.onCaptureFrame} // FIXME using onChange is a current workaround before we migrate to react-native custom callbacks. later, replace with onCaptureNextFrame
/>;
}
}
module.exports = GLCanvas;
...@@ -2,14 +2,12 @@ const {createView} = require("gl-react-core"); ...@@ -2,14 +2,12 @@ const {createView} = require("gl-react-core");
const React = require("react-native"); const React = require("react-native");
const Shaders = require("./Shaders"); const Shaders = require("./Shaders");
const Uniform = require("./Uniform"); const Uniform = require("./Uniform");
const GLCanvas = require("./GLCanvas");
const { const {
requireNativeComponent,
View, View,
} = React; } = React;
const GLCanvas = requireNativeComponent("GLCanvas", null);
const renderVcontent = function (width, height, id, children, { visibleContent }) { const renderVcontent = function (width, height, id, children, { visibleContent }) {
const childrenStyle = { const childrenStyle = {
position: "absolute", position: "absolute",
...@@ -23,12 +21,7 @@ const renderVcontent = function (width, height, id, children, { visibleContent } ...@@ -23,12 +21,7 @@ const renderVcontent = function (width, height, id, children, { visibleContent }
}; };
const renderVGL = function (props) { const renderVGL = function (props) {
const { width, height, ...restProps } = props; return <GLCanvas ref="canvas" {...props} />;
return <GLCanvas
key="native"
{...restProps}
style={{ width, height }}
/>;
}; };
const renderVcontainer = function ({ style, width, height }, contents, renderer) { const renderVcontainer = function ({ style, width, height }, contents, renderer) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment