From 1a3bdce4318c76760326355558c2d44504224169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Thu, 5 Nov 2015 13:20:09 +0100 Subject: [PATCH] Fixes #15 : implement captureFrame() --- Examples/Simple/Button.js | 42 ++++++++++++++++++++++++++ Examples/Simple/index.ios.js | 16 +++++++++- RNGL/GLCanvas.h | 2 ++ RNGL/GLCanvas.m | 33 +++++++++++++++++++- RNGL/GLCanvasManager.m | 2 ++ src/GLCanvas.js | 58 ++++++++++++++++++++++++++++++++++++ src/View.js | 11 ++----- 7 files changed, 153 insertions(+), 11 deletions(-) create mode 100644 Examples/Simple/Button.js create mode 100644 src/GLCanvas.js diff --git a/Examples/Simple/Button.js b/Examples/Simple/Button.js new file mode 100644 index 0000000..1cf8b93 --- /dev/null +++ b/Examples/Simple/Button.js @@ -0,0 +1,42 @@ +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 ( + + + {children} + + + ); + } +} + +Button.propTypes = { +}; + +module.exports = Button; diff --git a/Examples/Simple/index.ios.js b/Examples/Simple/index.ios.js index 7b2453a..84f8449 100644 --- a/Examples/Simple/index.ios.js +++ b/Examples/Simple/index.ios.js @@ -20,6 +20,7 @@ const PieProgress = require("./PieProgress"); const OneFingerResponse = require("./OneFingerResponse"); const AnimatedHelloGL = require("./AnimatedHelloGL"); const Blur = require("./Blur"); +const Button = require("./Button"); class Simple extends React.Component { constructor (props) { @@ -33,7 +34,15 @@ class Simple extends React.Component { switch1: false, switch2: false, switch3: false, + captured: null }; + this.onCapture1 = this.onCapture1.bind(this); + } + + onCapture1 () { + this.refs.helloGL.captureFrame(data64 => { + this.setState({ captured: data64 }); + }); } render () { @@ -46,6 +55,7 @@ class Simple extends React.Component { switch1, switch2, switch3, + captured } = this.state; return @@ -56,7 +66,11 @@ class Simple extends React.Component { 1. Hello GL - + + + + {captured && } + 2. Saturate an Image diff --git a/RNGL/GLCanvas.h b/RNGL/GLCanvas.h index a4ffb00..835fe5a 100644 --- a/RNGL/GLCanvas.h +++ b/RNGL/GLCanvas.h @@ -7,12 +7,14 @@ @property (nonatomic) BOOL opaque; @property (nonatomic) BOOL autoRedraw; @property (nonatomic) BOOL eventsThrough; +@property (nonatomic) int captureNextFrameId; @property (nonatomic) BOOL visibleContent; @property (nonatomic) NSNumber *nbContentTextures; @property (nonatomic) NSNumber *renderId; @property (nonatomic) NSArray *imagesToPreload; @property (nonatomic, assign) BOOL onProgress; @property (nonatomic, assign) BOOL onLoad; +@property (nonatomic, assign) BOOL onChange; - (instancetype)initWithBridge:(RCTBridge *)bridge withContext:(EAGLContext *)context; diff --git a/RNGL/GLCanvas.m b/RNGL/GLCanvas.m index 26161de..fa7720b 100644 --- a/RNGL/GLCanvas.m +++ b/RNGL/GLCanvas.m @@ -47,6 +47,8 @@ NSString* srcResource (id res) BOOL _preloadingDone; NSTimer *animationTimer; + + int _lastCaptureId; } - (instancetype)initWithBridge:(RCTBridge *)bridge @@ -57,6 +59,7 @@ NSString* srcResource (id res) _images = @{}; _preloaded = [[NSMutableArray alloc] init]; _preloadingDone = false; + _lastCaptureId = 0; self.context = context; self.contentScaleFactor = RCTScreenScale(); } @@ -135,6 +138,12 @@ RCT_NOT_IMPLEMENTED(-init) _nbContentTextures = nbContentTextures; } +- (void)setCaptureNextFrameId:(int)captureNextFrameId +{ + _captureNextFrameId = captureNextFrameId; + [self setNeedsDisplay]; +} + //// Sync methods (called from props setters) - (void) syncEventsThrough @@ -290,6 +299,7 @@ RCT_NOT_IMPLEMENTED(-init) { self.layer.opaque = _opaque; [self syncEventsThrough]; + __weak GLCanvas *weakSelf = self; if (!_preloadingDone) { glClearColor(0.0, 0.0, 0.0, 0.0); @@ -299,13 +309,27 @@ RCT_NOT_IMPLEMENTED(-init) BOOL needsDeferredRendering = _nbContentTextures > 0 && !_autoRedraw; if (needsDeferredRendering && !_deferredRendering) { dispatch_async(dispatch_get_main_queue(), ^{ + if (!weakSelf) return; _deferredRendering = true; - [self setNeedsDisplay]; + [weakSelf setNeedsDisplay]; }); } else { [self render]; _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) [_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 diff --git a/RNGL/GLCanvasManager.m b/RNGL/GLCanvasManager.m index 4c598f6..7e8cdb3 100644 --- a/RNGL/GLCanvasManager.m +++ b/RNGL/GLCanvasManager.m @@ -23,11 +23,13 @@ RCT_EXPORT_VIEW_PROPERTY(opaque, BOOL); RCT_EXPORT_VIEW_PROPERTY(autoRedraw, BOOL); RCT_EXPORT_VIEW_PROPERTY(eventsThrough, BOOL); RCT_EXPORT_VIEW_PROPERTY(visibleContent, BOOL); +RCT_EXPORT_VIEW_PROPERTY(captureNextFrameId, int); RCT_EXPORT_VIEW_PROPERTY(data, GLData); RCT_EXPORT_VIEW_PROPERTY(renderId, NSNumber); RCT_EXPORT_VIEW_PROPERTY(imagesToPreload, NSArray); RCT_EXPORT_VIEW_PROPERTY(onLoad, BOOL); RCT_EXPORT_VIEW_PROPERTY(onProgress, BOOL); +RCT_EXPORT_VIEW_PROPERTY(onChange, BOOL); - (UIView *)view { diff --git a/src/GLCanvas.js b/src/GLCanvas.js new file mode 100644 index 0000000..2cb43e7 --- /dev/null +++ b/src/GLCanvas.js @@ -0,0 +1,58 @@ +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 ; + } +} + +module.exports = GLCanvas; diff --git a/src/View.js b/src/View.js index d04365b..975ed9c 100644 --- a/src/View.js +++ b/src/View.js @@ -2,14 +2,12 @@ const {createView} = require("gl-react-core"); const React = require("react-native"); const Shaders = require("./Shaders"); const Uniform = require("./Uniform"); +const GLCanvas = require("./GLCanvas"); const { - requireNativeComponent, View, } = React; -const GLCanvas = requireNativeComponent("GLCanvas", null); - const renderVcontent = function (width, height, id, children, { visibleContent }) { const childrenStyle = { position: "absolute", @@ -23,12 +21,7 @@ const renderVcontent = function (width, height, id, children, { visibleContent } }; const renderVGL = function (props) { - const { width, height, ...restProps } = props; - return ; + return ; }; const renderVcontainer = function ({ style, width, height }, contents, renderer) { -- 2.26.2