Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Skia for V4! 🥳 #2727

Merged
merged 91 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from 87 commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
065521e
feat: Add Skia support (`toSkImage()`)
mrousavy Apr 5, 2024
cffb5fa
feat: Add `frame.getPlaformBuffer()`
mrousavy Apr 5, 2024
93acf26
Try use getPlatformBuffer
mrousavy Apr 5, 2024
bce81a0
Update Frame.ts
mrousavy Apr 5, 2024
ced1084
fix: Fix `getPlatformBuffer()`
mrousavy Apr 5, 2024
9e60dbf
fix: Fix buffer access
mrousavy Apr 5, 2024
33d63ae
fix: Always support `rgb` and `yuv`, remove `native`
mrousavy Apr 6, 2024
ded8475
feat: Build Skia example renderer
mrousavy Apr 6, 2024
8691edb
fix: Render a bit better
mrousavy Apr 6, 2024
a12149a
fix: Use atomic updates now
mrousavy Apr 6, 2024
d50a870
fix: Remove SK flags
mrousavy Apr 8, 2024
02a3c9f
fix: Clear and copy Frame
mrousavy Apr 8, 2024
88455b3
fix: Use bigint
mrousavy Apr 8, 2024
8ab7754
fix: Use `ImageProxy` and close it to avoid stalling
mrousavy Apr 8, 2024
3e7042e
Only do copy on iOS
mrousavy Apr 8, 2024
d1f7e08
fix: Add throw method signature
mrousavy Apr 8, 2024
81d1b47
feat: Box into `DrawableFrame`
mrousavy Apr 8, 2024
552ab81
feat: Create `SkiaCameraCanvas`
mrousavy Apr 8, 2024
b9e346b
feat: Allow disabling `preview` use-case
mrousavy Apr 8, 2024
979e0f4
feat: Catch some errors
mrousavy Apr 8, 2024
a2c63ab
Update FrameHostObject.cpp
mrousavy Apr 8, 2024
3a9811e
fix: Use proper layouts
mrousavy Apr 8, 2024
49df0d6
Update useSkiaFrameProcessor.ts
mrousavy Apr 8, 2024
ede9ebf
fix: Use main Coroutine Scope
mrousavy Apr 8, 2024
aac6135
fix: Fix wrong changes because we now properly abort changes
mrousavy Apr 8, 2024
528845a
chore: Format
mrousavy Apr 8, 2024
f4481d8
fix: Upgrade react-native-worklets-core
mrousavy Apr 8, 2024
8e013ae
Merge branch 'v4' into v4-skia
mrousavy Apr 9, 2024
04150be
fix: Add `focusRequiresPreview` error to iOS
mrousavy Apr 9, 2024
2f3610b
chore: Use latest react-native-worklets-core
mrousavy Apr 9, 2024
7aaef18
fix: Fix buffer orientation on iOS (it's currently always `.up`
mrousavy Apr 9, 2024
1fe118c
fix: Only pass function into native, no object
mrousavy Apr 9, 2024
346af67
fix: Format code
mrousavy Apr 9, 2024
d54f84e
fix: Fix styling
mrousavy Apr 9, 2024
b43bad1
fix: Fix pointer-events
mrousavy Apr 9, 2024
d2a92e8
Update Camera.tsx
mrousavy Apr 9, 2024
eefe074
Update useSkiaFrameProcessor.ts
mrousavy Apr 9, 2024
5b3886c
Update Worklets
mrousavy Apr 9, 2024
7859435
Draw radial face blur
mrousavy Apr 9, 2024
4ce9a1f
circle that spins
mrousavy Apr 9, 2024
87140d1
fix: Proxify
mrousavy Apr 9, 2024
be49a74
fix: Also use worklets proxy lazily
mrousavy Apr 9, 2024
dd33d91
fix: Try multiple error handlers
mrousavy Apr 9, 2024
efd00f8
fix: Also use Skia Proxy
mrousavy Apr 9, 2024
664f573
Add docs to the objects
mrousavy Apr 9, 2024
2f80193
fix: Use `createModuleProxy`
mrousavy Apr 9, 2024
88568e0
fix: Also re-create surface if height is different
mrousavy Apr 10, 2024
a3657ee
perf: Remove unneeded mutex locks as Frame only locks refCount
mrousavy Apr 10, 2024
a208724
Update FRAME_PROCESSORS_CREATE_OVERVIEW.mdx
mrousavy Apr 10, 2024
46b9941
fix: Remove unneeded CPU copy
mrousavy Apr 10, 2024
f9e1eba
fix: Catch PixelFormat error and fallback to default format
mrousavy Apr 10, 2024
a813793
fix: Throw proper PixelFormat error with more info
mrousavy Apr 10, 2024
f390315
Update CameraError.swift
mrousavy Apr 10, 2024
ae6cf65
Update Frame.java
mrousavy Apr 10, 2024
af60cbc
fix: Use `jsi::Scope`
mrousavy Apr 10, 2024
49b8d12
chore: Update react-native-worklets-core
mrousavy Apr 10, 2024
8729d1a
fix: Dispose previous Surface
mrousavy Apr 11, 2024
d2e0bb9
Update useSkiaFrameProcessor.ts
mrousavy Apr 11, 2024
8da98a1
fix: Fix surface re-creating each time
mrousavy Apr 11, 2024
739138b
chore: Bump to latest RN Skia with the features we need
mrousavy Apr 11, 2024
fb8f721
feat: Clean everything on unmount
mrousavy Apr 11, 2024
4fe5982
Add CPU copy again. On iOS, we need it.
mrousavy Apr 11, 2024
107850b
fix: Fix Android build
mrousavy Apr 11, 2024
56d457b
fix: Fix Android build again
mrousavy Apr 11, 2024
9a83756
Update ModuleProxy.ts
mrousavy Apr 15, 2024
cbee439
fix: Use type imports if possible
mrousavy Apr 15, 2024
04a0dcb
fix: Add RN Skia patch
mrousavy Apr 15, 2024
264dd4b
Add react-native-fast-tflite
mrousavy Apr 15, 2024
37db0f5
fix: Configure for tflite files as well
mrousavy Apr 15, 2024
be92f5a
Revert "fix: Configure for tflite files as well"
mrousavy Apr 15, 2024
90d16b5
Revert "Add react-native-fast-tflite"
mrousavy Apr 15, 2024
81bbb18
Update @shopify+react-native-skia+1.2.0.patch
mrousavy Apr 15, 2024
938d328
Add `useSkiaFrameProcessor` to ESLint
mrousavy Apr 15, 2024
304610b
Update yarn.lock
mrousavy Apr 16, 2024
1922bbf
fix: Upgrade to latest Skia to use NativeBuffer API now
mrousavy Apr 17, 2024
ebc04ba
fix: Use `CVPixelBuffer` instead of `CMSampleBuffer`
mrousavy Apr 17, 2024
ab432f9
fix: Fix package.json
mrousavy Apr 17, 2024
c3c13d5
chore: Update to latest react-native-worklets-core
mrousavy Apr 17, 2024
d70087f
fix: Use thread-local storage for SkSurface
mrousavy Apr 17, 2024
2b16bf6
Update Podfile.lock
mrousavy Apr 17, 2024
59a1c01
fix: Migrate over to new Worklets API
mrousavy Apr 17, 2024
d5169ad
Update FRAME_PROCESSORS.mdx
mrousavy Apr 17, 2024
f9b333d
Update useSkiaFrameProcessor.ts
mrousavy Apr 17, 2024
e4ca0cc
fix: Run destroy in interaction manager
mrousavy Apr 17, 2024
3cc8407
fix: Safely remove surfaces
mrousavy Apr 17, 2024
0b317db
fix: Update RNWC to fix terminated threads using dead `thread_local` …
mrousavy Apr 17, 2024
5d8ce85
Merge branch 'v4' into v4-skia
mrousavy Apr 17, 2024
0e79415
fix: Install hierarchy fitter in `CameraView`
mrousavy Apr 17, 2024
ac8ad60
chore: Remove Skia example from example app
mrousavy Apr 17, 2024
36ca3a7
Update CameraPage.tsx
mrousavy Apr 17, 2024
b7ecaa9
fix: Simplify default pixel format
mrousavy Apr 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/ISSUE_TEMPLATE/BUILD_ERROR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ body:
render: json
placeholder: >
"dependencies": {
"react-native": "^0.72.3",
"react-native-reanimated": "^3.4.2",
"react-native-vision-camera": "^3.0.0",
"react-native-worklets-core": "^0.2.0",
"react-native": "^0.73.4",
"react-native-reanimated": "^3.9.0",
"react-native-vision-camera": "^4.0.0",
"react-native-worklets-core": "^1.0.0",
...
},
validations:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/build-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ jobs:
run: yarn install --frozen-lockfile
- name: Install node_modules for example/
run: yarn install --frozen-lockfile --cwd example
- name: Remove react-native-worklets-core
run: yarn remove react-native-worklets-core --cwd example
- name: Remove worklets, skia and reanimated
run: yarn remove react-native-worklets-core @shopify/react-native-skia react-native-reanimated --cwd ..

- name: Restore Gradle cache
uses: actions/cache@v4
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/build-ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ jobs:
${{ runner.os }}-yarn-
- name: Install node_modules for example/
run: yarn install --frozen-lockfile --cwd ..
- name: Remove react-native-worklets-core
run: yarn remove react-native-worklets-core --cwd ..
- name: Remove worklets, skia and reanimated
run: yarn remove react-native-worklets-core @shopify/react-native-skia react-native-reanimated --cwd ..

- name: Restore buildcache
uses: mikehardy/buildcache-action@v2
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/guides/FRAME_PROCESSORS.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Because they are written in JS, Frame Processors are simple, powerful, extensibl

### react-native-worklets-core

Frame Processors require [react-native-worklets-core](https://github.com/margelo/react-native-worklets-core) 0.2.0 or higher. Install it:
Frame Processors require [react-native-worklets-core](https://github.com/margelo/react-native-worklets-core) 1.0.0 or higher. Install it:

```sh
npm i react-native-worklets-core
Expand Down Expand Up @@ -147,10 +147,10 @@ const onDraw = useDrawCallback((canvas) => {

### Call functions

And you can also call back to the React-JS thread by using `createRunInJsFn(...)`:
And you can also call back to the React-JS thread by using `createRunOnJS(...)`:

```tsx
const onFaceDetected = Worklets.createRunInJsFn((face: Face) => {
const onFaceDetected = Worklets.createRunOnJS((face: Face) => {
navigation.push("FiltersPage", { face: face })
})

Expand Down
10 changes: 5 additions & 5 deletions docs/docs/guides/FRAME_PROCESSORS_CREATE_OVERVIEW.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Return values will automatically be converted to JS values, assuming they are re
```java
@Nullable
@Override
public Object callback(@NonNull Frame frame, @Nullable Map<String, Object> arguments) {
public Object callback(@NonNull Frame frame, @Nullable Map<String, Object> arguments) throws Throwable {
return "cat";
}
```
Expand All @@ -74,7 +74,7 @@ You can also manipulate the buffer and return it (or a copy of it) by returning
```java
@Nullable
@Override
public Object callback(@NonNull Frame frame, @Nullable Map<String, Object> arguments) {
public Object callback(@NonNull Frame frame, @Nullable Map<String, Object> arguments) throws Throwable {
Frame resizedFrame = new Frame(/* ... */);
return resizedFrame;
}
Expand Down Expand Up @@ -112,11 +112,11 @@ To let the user know that something went wrong you can use Exceptions:
```java
@Nullable
@Override
public Object callback(@NonNull Frame frame, @Nullable Map<String, Object> arguments) {
public Object callback(@NonNull Frame frame, @Nullable Map<String, Object> arguments) throws Throwable {
if (arguments != null && arguments.get("codeType") instanceof String) {
// ...
} else {
throw new Exception("codeType property has to be a string!");
throw new RuntimeException("codeType property has to be a string!");
}
}
```
Expand Down Expand Up @@ -149,7 +149,7 @@ For example, a realtime video chat application might use WebRTC to send the fram
```java
@Nullable
@Override
public Object callback(@NonNull Frame frame, @Nullable Map<String, Object> arguments) {
public Object callback(@NonNull Frame frame, @Nullable Map<String, Object> arguments) throws Throwable {
if (arguments == null) {
return null;
}
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/guides/FRAME_PROCESSORS_TIPS.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ If you use native Frame Processor Plugins, make sure they are optimized for real

## ESLint react-hooks plugin

If you are using the [react-hooks ESLint plugin](https://www.npmjs.com/package/eslint-plugin-react-hooks), make sure to add `useFrameProcessor` to `additionalHooks` inside your ESLint config so dependencies are detected properly. (See ["advanced configuration"](https://www.npmjs.com/package/eslint-plugin-react-hooks#advanced-configuration))
If you are using the [react-hooks ESLint plugin](https://www.npmjs.com/package/eslint-plugin-react-hooks), make sure to add `useFrameProcessor` and `useSkiaFrameProcessor` to `additionalHooks` inside your ESLint config so dependencies are detected properly. (See ["advanced configuration"](https://www.npmjs.com/package/eslint-plugin-react-hooks#advanced-configuration))

## Technical

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/guides/FRAME_PROCESSOR_CREATE_PLUGIN_ANDROID.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public class FaceDetectorFrameProcessorPlugin extends FrameProcessorPlugin {

@Nullable
@Override
public Object callback(@NonNull Frame frame, @Nullable Map<String, Object> arguments) {
public Object callback(@NonNull Frame frame, @Nullable Map<String, Object> arguments) throws Throwable {
// highlight-next-line
// code goes here
return null;
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/guides/SETUP.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ npx expo install react-native-vision-camera

VisionCamera requires **iOS 12 or higher**, and **Android-SDK version 26 or higher**. See [Troubleshooting](/docs/guides/troubleshooting) if you're having installation issues.

> **(Optional)** If you want to use [Frame Processors](/docs/guides/frame-processors), you need to install [react-native-worklets-core](https://github.com/margelo/react-native-worklets-core) 0.2.0 or higher.
> **(Optional)** If you want to use [Frame Processors](/docs/guides/frame-processors), you need to install [react-native-worklets-core](https://github.com/margelo/react-native-worklets-core) 1.0.0 or higher.

## Updating manifests

Expand Down
3 changes: 2 additions & 1 deletion package/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,13 @@ module.exports = {
],
'@typescript-eslint/no-non-null-assertion': 'error',
'@typescript-eslint/no-unnecessary-condition': 'error',
'@typescript-eslint/consistent-type-imports': 'warn',

// react hooks
'react-hooks/exhaustive-deps': [
'error',
{
additionalHooks: '(useDerivedValue|useAnimatedStyle|useAnimatedProps|useWorkletCallback|useFrameProcessor)',
additionalHooks: '(useDerivedValue|useAnimatedStyle|useAnimatedProps|useWorkletCallback|useFrameProcessor|useSkiaFrameProcessor)',
},
],
},
Expand Down
2 changes: 1 addition & 1 deletion package/VisionCamera.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Pod::Spec.new do |s|
s.source = { :git => "https://github.com/mrousavy/react-native-vision-camera.git", :tag => "#{s.version}" }

s.pod_target_xcconfig = {
"GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) SK_METAL=1 SK_GANESH=1 VISION_CAMERA_ENABLE_FRAME_PROCESSORS=#{hasWorklets}",
"GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) VISION_CAMERA_ENABLE_FRAME_PROCESSORS=#{hasWorklets}",
"OTHER_SWIFT_FLAGS" => "$(inherited) #{hasWorklets ? "-D VISION_CAMERA_ENABLE_FRAME_PROCESSORS" : ""}",
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17",
"HEADER_SEARCH_PATHS" => "\"$(PODS_TARGET_SRCROOT)/cpp/\"/** "
Expand Down
114 changes: 73 additions & 41 deletions package/android/src/main/cpp/frameprocessor/FrameHostObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ std::vector<jsi::PropNameID> FrameHostObject::getPropertyNames(jsi::Runtime& rt)
// Conversion
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("toString")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("toArrayBuffer")));
result.push_back(jsi::PropNameID::forUtf8(rt, std::string("getNativeBuffer")));
}

return result;
Expand All @@ -60,6 +61,40 @@ std::vector<jsi::PropNameID> FrameHostObject::getPropertyNames(jsi::Runtime& rt)
jsi::Value FrameHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) {
auto name = propName.utf8(runtime);

// Properties
if (name == "isValid") {
return jsi::Value(this->frame && this->frame->getIsValid());
}
if (name == "width") {
return jsi::Value(this->frame->getWidth());
}
if (name == "height") {
return jsi::Value(this->frame->getHeight());
}
if (name == "isMirrored") {
return jsi::Value(this->frame->getIsMirrored());
}
if (name == "orientation") {
auto orientation = this->frame->getOrientation();
auto string = orientation->getUnionValue();
return jsi::String::createFromUtf8(runtime, string->toStdString());
}
if (name == "pixelFormat") {
auto pixelFormat = this->frame->getPixelFormat();
auto string = pixelFormat->getUnionValue();
return jsi::String::createFromUtf8(runtime, string->toStdString());
}
if (name == "timestamp") {
return jsi::Value(static_cast<double>(this->frame->getTimestamp()));
}
if (name == "bytesPerRow") {
return jsi::Value(this->frame->getBytesPerRow());
}
if (name == "planesCount") {
return jsi::Value(this->frame->getPlanesCount());
}

// Internal Methods
if (name == "incrementRefCount") {
jsi::HostFunctionType incrementRefCount = JSI_FUNC {
// Increment retain count by one.
Expand All @@ -76,19 +111,34 @@ jsi::Value FrameHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pr
};
return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "decrementRefCount"), 0, decrementRefCount);
}
if (name == "toString") {
jsi::HostFunctionType toString = JSI_FUNC {

// Conversion methods
if (name == "getNativeBuffer") {
jsi::HostFunctionType getNativeBuffer = JSI_FUNC {
if (!this->frame) {
return jsi::String::createFromUtf8(runtime, "[closed frame]");
throw jsi::JSError(runtime, "Cannot get Platform Buffer - this Frame is already closed!");
}
auto width = this->frame->getWidth();
auto height = this->frame->getHeight();
auto format = this->frame->getPixelFormat();
auto formatString = format->getUnionValue();
auto str = std::to_string(width) + " x " + std::to_string(height) + " " + formatString->toString() + " Frame";
return jsi::String::createFromUtf8(runtime, str);
#if __ANDROID_API__ >= 26
AHardwareBuffer* hardwareBuffer = this->frame->getHardwareBuffer();
AHardwareBuffer_acquire(hardwareBuffer);
uintptr_t pointer = reinterpret_cast<uintptr_t>(hardwareBuffer);
jsi::HostFunctionType deleteFunc = [=](jsi::Runtime& runtime, const jsi::Value& thisArg, const jsi::Value* args,
size_t count) -> jsi::Value {
AHardwareBuffer_release(hardwareBuffer);
return jsi::Value::undefined();
};

jsi::Object buffer(runtime);
buffer.setProperty(runtime, "pointer", jsi::BigInt::fromUint64(runtime, pointer));
buffer.setProperty(runtime, "delete",
jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "delete"), 0, deleteFunc));
return buffer;
#else
throw jsi::JSError(runtime, "Cannot get Platform Buffer - getNativeBuffer() requires HardwareBuffers, which are "
"only available on Android API 26 or above. Set your app's minSdk version to 26 and try again.");
#endif
};
return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "toString"), 0, toString);
return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "getNativeBuffer"), 0, getNativeBuffer);
}
if (name == "toArrayBuffer") {
jsi::HostFunctionType toArrayBuffer = JSI_FUNC {
Expand Down Expand Up @@ -144,37 +194,19 @@ jsi::Value FrameHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pr
};
return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "toArrayBuffer"), 0, toArrayBuffer);
}

if (name == "isValid") {
return jsi::Value(this->frame && this->frame->getIsValid());
}
if (name == "width") {
return jsi::Value(this->frame->getWidth());
}
if (name == "height") {
return jsi::Value(this->frame->getHeight());
}
if (name == "isMirrored") {
return jsi::Value(this->frame->getIsMirrored());
}
if (name == "orientation") {
auto orientation = this->frame->getOrientation();
auto string = orientation->getUnionValue();
return jsi::String::createFromUtf8(runtime, string->toStdString());
}
if (name == "pixelFormat") {
auto pixelFormat = this->frame->getPixelFormat();
auto string = pixelFormat->getUnionValue();
return jsi::String::createFromUtf8(runtime, string->toStdString());
}
if (name == "timestamp") {
return jsi::Value(static_cast<double>(this->frame->getTimestamp()));
}
if (name == "bytesPerRow") {
return jsi::Value(this->frame->getBytesPerRow());
}
if (name == "planesCount") {
return jsi::Value(this->frame->getPlanesCount());
if (name == "toString") {
jsi::HostFunctionType toString = JSI_FUNC {
if (!this->frame) {
return jsi::String::createFromUtf8(runtime, "[closed frame]");
}
auto width = this->frame->getWidth();
auto height = this->frame->getHeight();
auto format = this->frame->getPixelFormat();
auto formatString = format->getUnionValue();
auto str = std::to_string(width) + " x " + std::to_string(height) + " " + formatString->toString() + " Frame";
return jsi::String::createFromUtf8(runtime, str);
};
return jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "toString"), 0, toString);
}

// fallback to base implementation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,16 @@ jsi::Value FrameProcessorPluginHostObject::get(jsi::Runtime& runtime, const jsi:
runtime, jsi::PropNameID::forUtf8(runtime, "call"), 2,
[=](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
// Frame is first argument
auto frameHostObject = arguments[0].asObject(runtime).asHostObject<FrameHostObject>(runtime);
auto frameHolder = arguments[0].asObject(runtime);
std::shared_ptr<FrameHostObject> frameHostObject;
if (frameHolder.isHostObject<FrameHostObject>(runtime)) {
// User directly passed FrameHostObject
frameHostObject = frameHolder.getHostObject<FrameHostObject>(runtime);
} else {
// User passed a wrapper, e.g. DrawableFrame which contains the FrameHostObject as a hidden property
jsi::Object actualFrame = frameHolder.getPropertyAsObject(runtime, "__frame");
frameHostObject = actualFrame.asHostObject<FrameHostObject>(runtime);
}
auto frame = frameHostObject->frame;

// Options are second argument (possibly undefined)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ std::vector<jsi::PropNameID> VisionCameraProxy::getPropertyNames(jsi::Runtime& r
return result;
}

void VisionCameraProxy::setFrameProcessor(int viewTag, jsi::Runtime& runtime, const jsi::Object& object) {
_javaProxy->cthis()->setFrameProcessor(viewTag, runtime, object);
void VisionCameraProxy::setFrameProcessor(int viewTag, jsi::Runtime& runtime, const std::shared_ptr<jsi::Function>& function) {
_javaProxy->cthis()->setFrameProcessor(viewTag, runtime, function);
}

void VisionCameraProxy::removeFrameProcessor(int viewTag) {
Expand All @@ -70,8 +70,9 @@ jsi::Value VisionCameraProxy::get(jsi::Runtime& runtime, const jsi::PropNameID&
runtime, jsi::PropNameID::forUtf8(runtime, "setFrameProcessor"), 1,
[this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
auto viewTag = arguments[0].asNumber();
auto object = arguments[1].asObject(runtime);
this->setFrameProcessor(static_cast<int>(viewTag), runtime, object);
auto frameProcessor = arguments[1].asObject(runtime).asFunction(runtime);
auto sharedFunction = std::make_shared<jsi::Function>(std::move(frameProcessor));
this->setFrameProcessor(static_cast<int>(viewTag), runtime, sharedFunction);
return jsi::Value::undefined();
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class VisionCameraProxy : public jsi::HostObject {
jsi::Value get(jsi::Runtime& runtime, const jsi::PropNameID& name) override;

private:
void setFrameProcessor(int viewTag, jsi::Runtime& runtime, const jsi::Object& frameProcessor);
void setFrameProcessor(int viewTag, jsi::Runtime& runtime, const std::shared_ptr<jsi::Function>& frameProcessor);
void removeFrameProcessor(int viewTag);
jsi::Value initFrameProcessorPlugin(jsi::Runtime& runtime, const std::string& name, const jsi::Object& options);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,10 @@ JVisionCameraProxy::~JVisionCameraProxy() {
#endif
}

void JVisionCameraProxy::setFrameProcessor(int viewTag, jsi::Runtime& runtime, const jsi::Object& frameProcessorObject) {
void JVisionCameraProxy::setFrameProcessor(int viewTag, jsi::Runtime& runtime, const std::shared_ptr<jsi::Function>& function) {
#if VISION_CAMERA_ENABLE_FRAME_PROCESSORS
auto frameProcessorType = frameProcessorObject.getProperty(runtime, "type").asString(runtime).utf8(runtime);
auto worklet = std::make_shared<RNWorklet::JsiWorklet>(runtime, frameProcessorObject.getProperty(runtime, "frameProcessor"));

jni::local_ref<JFrameProcessor::javaobject> frameProcessor;
if (frameProcessorType == "frame-processor") {
frameProcessor = JFrameProcessor::create(worklet, _workletContext);
} else {
throw std::runtime_error("Unknown FrameProcessor.type passed! Received: " + frameProcessorType);
}
auto worklet = std::make_shared<RNWorklet::JsiWorklet>(runtime, function);
jni::local_ref<JFrameProcessor::javaobject> frameProcessor = JFrameProcessor::create(worklet, _workletContext);

auto setFrameProcessorMethod = javaClassLocal()->getMethod<void(int, alias_ref<JFrameProcessor::javaobject>)>("setFrameProcessor");
setFrameProcessorMethod(_javaPart, viewTag, frameProcessor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class JVisionCameraProxy : public jni::HybridClass<JVisionCameraProxy> {
~JVisionCameraProxy();
static void registerNatives();

void setFrameProcessor(int viewTag, jsi::Runtime& runtime, const jsi::Object& frameProcessor);
void setFrameProcessor(int viewTag, jsi::Runtime& runtime, const std::shared_ptr<jsi::Function>& frameProcessor);
void removeFrameProcessor(int viewTag);
jni::local_ref<JFrameProcessorPlugin::javaobject> initFrameProcessorPlugin(const std::string& name,
jni::local_ref<JMap<jstring, jobject>> options);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.mrousavy.camera

import com.facebook.react.bridge.ReadableMap
import com.mrousavy.camera.core.FocusRequiresPreviewError
import com.mrousavy.camera.extensions.px
import com.mrousavy.camera.utils.runOnUiThreadAndWait

suspend fun CameraView.focus(pointMap: ReadableMap) {
val x = pointMap.getDouble("x")
val y = pointMap.getDouble("y")
val previewView = previewView ?: throw FocusRequiresPreviewError()

val point = runOnUiThreadAndWait {
previewView.meteringPointFactory.createPoint(x.toFloat().px, y.toFloat().px)
Expand Down
Loading
Loading