Skip to content

Commit

Permalink
fix(Android): use SkCanvas from Skia api in export (#5)
Browse files Browse the repository at this point in the history
* chore(Android): improve build

* chore(Android): refactor imports

* chore: fix import

* fix(Android): use SkCanvas from Skia api in export

* chore: formatting

* chore: remove unused file and update pod
  • Loading branch information
fdecampredon authored Jun 25, 2024
1 parent 228c2d2 commit b5b7846
Show file tree
Hide file tree
Showing 12 changed files with 150 additions and 167 deletions.
114 changes: 33 additions & 81 deletions android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,6 @@ file(GLOB libfbjni_include_DIRS "${build_DIR}/fbjni-*-headers.jar/")
# Consume shared libraries and headers from prefabs
find_package(fbjni REQUIRED CONFIG)
find_package(ReactAndroid REQUIRED CONFIG)
find_package(shopify_react-native-skia REQUIRED CONFIG)
find_package(react-native-reanimated REQUIRED CONFIG)

set(JSI_LIB ReactAndroid::jsi)
message("-- JSI : " ${JSI_LIB})
set(FBJNI_LIBRARY fbjni::fbjni)
message("-- FBJNI : " ${FBJNI_LIBRARY})
set(REACT_LIB ReactAndroid::react_nativemodule_core)
message("-- REACT : " ${REACT_LIB})
set(TURBOMODULES_LIB "ReactAndroid::turbomodulejsijni")
message("-- TURBO : " ${TURBOMODULES_LIB})
set(RNSKIA_LIB shopify_react-native-skia::rnskia)
message("-- RNSKIA : " ${RNSKIA_LIB})
set(REANIMATED_LIB react-native-reanimated::reanimated)
message("-- REANIMATED : " ${REANIMATED_LIB})


add_library(${PACKAGE_NAME}
SHARED
Expand Down Expand Up @@ -65,88 +49,56 @@ include_directories(
# React native
"${NODE_MODULES_DIR}/react-native/ReactCommon/callinvoker"
"${NODE_MODULES_DIR}/react-native/ReactCommon/jsi"
"${NODE_MODULES_DIR}/react-native/ReactCommon/runtimeexecutor"
"${NODE_MODULES_DIR}/react-native/ReactCommon"
"${NODE_MODULES_DIR}/react-native/ReactCommon/react/nativemodule/core"
"${NODE_MODULES_DIR}/react-native/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni"

# include skia headers
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/rnskia"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/skia/include/config/"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/skia/include/core/"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/skia/include/effects/"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/skia/include/utils/"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/skia/include/pathops/"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/skia/modules/"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/skia/modules/skparagraph/include/"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/skia/modules/skresources/include/"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/skia/include/"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/skia"

"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/api"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/jsi"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/rnskia"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/rnskia/values"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/rnskia/dom"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/rnskia/dom/base"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/rnskia/dom/nodes"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/rnskia/dom/props"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/cpp/utils"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/android/cpp/jni/include"
"${NODE_MODULES_DIR}/@shopify/react-native-skia/android/cpp/rnskia-android"

${libfbjni_include_DIRS}
)


# Import prebuilt skia library
set(SKIA_LIBS_PATH "${NODE_MODULES_DIR}/@shopify/react-native-skia/libs/android/${ANDROID_ABI}")

set(SKIA_LIB "skia")
add_library(skia STATIC IMPORTED)
set_property(TARGET skia PROPERTY IMPORTED_LOCATION "${SKIA_LIBS_PATH}/libskia.a")

set(SKIA_SVG "svg")
add_library(svg STATIC IMPORTED)
set_property(TARGET svg PROPERTY IMPORTED_LOCATION "${SKIA_LIBS_PATH}/libsvg.a")

set(SKIA_SKSHAPER_LIB "skshaper")
add_library(skshaper STATIC IMPORTED)
set_property(TARGET skshaper PROPERTY IMPORTED_LOCATION "${SKIA_LIBS_PATH}/libskshaper.a")

set(SKIA_MODULE_SKSG_LIB "sksg")
add_library(sksg STATIC IMPORTED)
set_property(TARGET sksg PROPERTY IMPORTED_LOCATION "${SKIA_LIBS_PATH}/libsksg.a")

set(SKIA_MODULE_SKUNICODE_LIB "skunicode")
add_library(skunicode STATIC IMPORTED)
set_property(TARGET skunicode PROPERTY IMPORTED_LOCATION "${SKIA_LIBS_PATH}/libskunicode.a")


# Android Log lib
find_library(LOG_LIB log)

find_library(
LOG_LIB
log
)

add_definitions(-DGL_GLEXT_PROTOTYPES)
add_definitions(-DEGL_EGLEXT_PROTOTYPES)

# Link
target_link_libraries(
${PACKAGE_NAME}
${LOG_LIB}
${REANIMATED_LIB}
${FBJNI_LIBRARY}
${REACT_LIB}
${JSI_LIB}
${TURBOMODULES_LIB}
${SKIA_SKSHAPER_LIB}
${SKIA_SVG}
${SKIA_MODULE_SKSG_LIB}
${SKIA_MODULE_SKUNICODE_LIB}
${SKIA_LIB}
${RNSKIA_LIB}
android
fbjni::fbjni
ReactAndroid::react_nativemodule_core
ReactAndroid::jsi
ReactAndroid::reactnativejni
ReactAndroid::runtimeexecutor
ReactAndroid::turbomodulejsijni
-ljnigraphics
-lGLESv2
-lEGL
-landroid
)

find_package(shopify_react-native-skia REQUIRED CONFIG)
target_link_libraries(
${PACKAGE_NAME}
shopify_react-native-skia::rnskia
)

get_target_property(RN_SKIA_INCLUDE_DIR shopify_react-native-skia::rnskia INTERFACE_INCLUDE_DIRECTORIES)
include_directories("${RN_SKIA_INCLUDE_DIR}/react-native-skia")


set(RN_SKIA_DIR "${NODE_MODULES_DIR}/@shopify/react-native-skia")
file(GLOB skiaLibraries "${RN_SKIA_DIR}/libs/android/${ANDROID_ABI}/*.a")
foreach(skiaLibrary ${skiaLibraries})
target_link_libraries(${PACKAGE_NAME} "${skiaLibrary}")
endforeach()

find_package(react-native-reanimated REQUIRED CONFIG)
target_link_libraries(
${PACKAGE_NAME}
react-native-reanimated::reanimated
)
38 changes: 30 additions & 8 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
google()
mavenCentral()
}
Expand Down Expand Up @@ -111,12 +114,20 @@ android {
excludes = [
"META-INF",
"META-INF/**",
"**/librnskia.so",
"**/libc++_shared.so",
"**/libfbjni.so",
"**/libjsi.so",
"**/libreact_nativemodule_core.so",
"**/libfolly_json.so",
"**/libfolly_runtime.so",
"**/libglog.so",
"**/libhermes.so",
"**/libhermes-executor-debug.so",
"**/libhermes_executor.so",
"**/libreactnativejni.so",
"**/libturbomodulejsijni.so",
"**/libc++_shared.so",
"**/libfbjni.so"
"**/libreact_nativemodule_core.so",
"**/libjscexecutor.so",
"**/libruntimeexecutor.so"
]
}

Expand Down Expand Up @@ -161,10 +172,7 @@ repositories {

def MEDIA3_VERSION = "1.2.1"
dependencies {
// For < 0.71, this will be from the local maven repo
// For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+"
implementation 'com.facebook.react:react-android:+'
implementation(project(":shopify_react-native-skia")) {
exclude group: "com.facebook.react", module: "react-native"
}
Expand All @@ -175,6 +183,20 @@ dependencies {
implementation "androidx.media3:media3-exoplayer-hls:${MEDIA3_VERSION}"
}

tasks.whenTaskAdded { task ->
if (task.name.contains("configureCMakeDebug")) {
rootProject.getTasksByName("packageReactNdkDebugLibs", true).forEach {
task.dependsOn(it)
}
}

if (task.name.contains("configureCMakeRel")) {
rootProject.getTasksByName("packageReactNdkReleaseLibs", true).forEach {
task.dependsOn(it)
}
}
}

if (isNewArchitectureEnabled()) {
react {
jsRootDir = file("../src/")
Expand Down
4 changes: 2 additions & 2 deletions android/cpp/JNIHelpers.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#pragma once

#include <JniSkiaManager.h>
#include <RNSkPlatformContext.h>
#include <fbjni/fbjni.h>
#include <react-native-skia/JniSkiaManager.h>
#include <react-native-skia/RNSkPlatformContext.h>

namespace RNSkiaVideo {

Expand Down
2 changes: 1 addition & 1 deletion android/cpp/VideoComposition.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#pragma once

#include <RNSkPlatformContext.h>
#include <fbjni/ByteBuffer.h>
#include <fbjni/fbjni.h>
#include <jni.h>
#include <jsi/jsi.h>

namespace RNSkiaVideo {

Expand Down
59 changes: 19 additions & 40 deletions android/cpp/VideoCompositionExporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

#include "VideoCompositionExporter.h"
#include <EGL/eglext.h>
#include <JsiSkCanvas.h>
#include <gpu/ganesh/SkSurfaceGanesh.h>
#include <gpu/ganesh/gl/GrGLBackendSurface.h>
#include <react-native-skia/JsiSkCanvas.h>
#include <react-native-skia/include/gpu/ganesh/SkSurfaceGanesh.h>
#include <react-native-skia/include/gpu/ganesh/gl/GrGLBackendSurface.h>

#include "JNIHelpers.h"

Expand All @@ -18,7 +18,8 @@ jsi::Value VideoCompositionExporter::exportVideoComposition(
jsi::Runtime& runtime, jsi::Object jsComposition, jsi::Object options,
std::shared_ptr<reanimated::WorkletRuntime> workletRuntime,
std::shared_ptr<reanimated::ShareableWorklet> drawFrame,
jsi::Function onSuccess, jsi::Function onError) {
jsi::Function onSuccess, jsi::Function onError,
std::shared_ptr<RNSkia::JsiSkSurface> jsiSurface) {

auto composition = VideoComposition::fromJSIObject(runtime, jsComposition);
auto outPath =
Expand All @@ -38,7 +39,7 @@ jsi::Value VideoCompositionExporter::exportVideoComposition(
global_ref<VideoCompositionExporter::JavaPart> exporter =
VideoCompositionExporter::create(composition, outPath, width, height,
frameRate, bitRate, encoderName,
workletRuntime, drawFrame);
workletRuntime, drawFrame, jsiSurface);

auto sharedSuccessCallback =
std::make_shared<jsi::Function>(std::move(onSuccess));
Expand Down Expand Up @@ -80,10 +81,11 @@ global_ref<VideoCompositionExporter::JavaPart> VideoCompositionExporter::create(
int width, int height, int frameRate, int bitRate,
std::optional<std::string> encoderName,
std::shared_ptr<reanimated::WorkletRuntime> workletRuntime,
std::shared_ptr<reanimated::ShareableWorklet> drawFrame) {
std::shared_ptr<reanimated::ShareableWorklet> drawFrame,
std::shared_ptr<RNSkia::JsiSkSurface> jsiSurface) {

auto hybridData = makeHybridData(std::make_unique<VideoCompositionExporter>(
width, height, workletRuntime, drawFrame));
width, height, workletRuntime, drawFrame, jsiSurface));
auto jExporter = newObjectJavaArgs(
hybridData, composition, outPath, width, height, frameRate, bitRate,
encoderName.has_value() ? encoderName.value() : nullptr);
Expand All @@ -96,11 +98,13 @@ global_ref<VideoCompositionExporter::JavaPart> VideoCompositionExporter::create(
VideoCompositionExporter::VideoCompositionExporter(
int width, int height,
std::shared_ptr<reanimated::WorkletRuntime> workletRuntime,
std::shared_ptr<reanimated::ShareableWorklet> drawFrame) {
std::shared_ptr<reanimated::ShareableWorklet> drawFrame,
std::shared_ptr<RNSkia::JsiSkSurface> jsiSurface) {
this->width = width;
this->height = height;
this->drawFrameWorklet = drawFrame;
this->workletRuntime = workletRuntime;
this->jsiSurface = jsiSurface;
}

void VideoCompositionExporter::registerNatives() {
Expand All @@ -124,30 +128,7 @@ void VideoCompositionExporter::start(
void VideoCompositionExporter::makeSkiaSharedContextCurrent() {
if (surface == nullptr) {
surface = SkiaOpenGLSurfaceFactory::makeOffscreenSurface(width, height);
jsiCanvas = std::make_shared<JsiSkCanvas>(
JNIHelpers::getSkiaPlatformContext(), surface->getCanvas());

jsiCanvasProxy =
std::make_shared<jsi::Object>(workletRuntime->getJSIRuntime());
auto& jsiCanvasMap = jsiCanvas->getExportedFunctionMap();
for (auto& entry : jsiCanvasMap) {
auto propName = entry.first;
auto func = std::bind(entry.second, jsiCanvas, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4);
jsiCanvasProxy->setProperty(
workletRuntime->getJSIRuntime(), propName.c_str(),
jsi::Function::createFromHostFunction(
workletRuntime->getJSIRuntime(),
jsi::PropNameID::forUtf8(workletRuntime->getJSIRuntime(),
propName),
0,
[=](jsi::Runtime& runtime, const jsi::Value& thisValue,
const jsi::Value* arguments, size_t count) -> jsi::Value {
return func(workletRuntime->getJSIRuntime(), thisValue,
arguments, count);
}));
}
jsiSurface->setObject(surface);
}
SkiaOpenGLHelper::makeCurrent(
&ThreadContextHolder::ThreadSkiaOpenGLContext,
Expand All @@ -160,19 +141,18 @@ int VideoCompositionExporter::renderFrame(
&ThreadContextHolder::ThreadSkiaOpenGLContext,
ThreadContextHolder::ThreadSkiaOpenGLContext.gl1x1Surface);
auto currentTime = jsi::Value(time);
auto result = jsi::Object(workletRuntime->getJSIRuntime());
auto& runtime = workletRuntime->getJSIRuntime();
auto result = jsi::Object(runtime);
auto platformContext = JNIHelpers::getSkiaPlatformContext();
for (auto& entry : *frames) {
auto id = entry.first->toStdString();
auto frame = entry.second;
auto jsFrame = frame->toJS(workletRuntime->getJSIRuntime());
result.setProperty(workletRuntime->getJSIRuntime(), id.c_str(),
std::move(jsFrame));
auto jsFrame = frame->toJS(runtime);
result.setProperty(runtime, id.c_str(), std::move(jsFrame));
}
surface->getCanvas()->clear(SkColors::kTransparent);

auto canvasProxy = jsiCanvasProxy.get();
workletRuntime->runGuarded(drawFrameWorklet, *canvasProxy, currentTime, result);
workletRuntime->runGuarded(drawFrameWorklet, currentTime, result);

GrAsDirectContext(surface->recordingContext())->flushAndSubmit();
GrBackendTexture texture = SkSurfaces::GetBackendTexture(
Expand All @@ -189,8 +169,7 @@ int VideoCompositionExporter::renderFrame(
void VideoCompositionExporter::release() {
jThis = nullptr;
surface = nullptr;
jsiCanvas = nullptr;
jsiCanvasProxy = nullptr;
jsiSurface = nullptr;
}

void VideoCompositionExporter::onComplete() {
Expand Down
Loading

0 comments on commit b5b7846

Please sign in to comment.