diff --git a/apps/docs/docs/canvas/canvas.md b/apps/docs/docs/canvas/canvas.md index 25c4eff90d..b9ae60c4c8 100644 --- a/apps/docs/docs/canvas/canvas.md +++ b/apps/docs/docs/canvas/canvas.md @@ -13,7 +13,7 @@ Behind the scenes, it is using its own React renderer. |:-----|:---------|:-----------------| | style? | `ViewStyle` | View style | | ref? | `Ref` | Reference to the `SkiaView` object | -| mode? | `default` or `continuous` | By default, the canvas is only updated when the drawing tree or animation values change. With `mode="continuous"`, the canvas will redraw on every frame | +| opaque? | `boolean` | By default, the canvas is transparent but on Android, you can make it opaque to improve performance. | | onSize? | `SharedValue` | Reanimated value to which the canvas size will be assigned (see [canvas size](/docs/animations/hooks#canvas-size)) | | onLayout? | `NativeEvent` | Invoked on mount and on layout changes (see [onLayout](https://reactnative.dev/docs/view#onlayout)) | diff --git a/apps/paper/ios/Podfile.lock b/apps/paper/ios/Podfile.lock index 5342d28e59..8a1df419a7 100644 --- a/apps/paper/ios/Podfile.lock +++ b/apps/paper/ios/Podfile.lock @@ -1283,7 +1283,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - react-native-wgpu (0.1.19): + - react-native-wgpu (0.1.20): - DoubleConversion - glog - hermes-engine @@ -1650,7 +1650,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNScreens (3.34.0): + - RNScreens (4.3.0): - DoubleConversion - glog - hermes-engine @@ -1937,7 +1937,7 @@ SPEC CHECKSUMS: react-native-safe-area-context: ab8f4a3d8180913bd78ae75dd599c94cce3d5e9a react-native-skia: 1549ee5068efc5a004b84b2e0ba109c6234e2fde react-native-slider: 97ce0bd921f40de79cead9754546d5e4e7ba44f8 - react-native-wgpu: 8d0437a304318e0e3d6ccbfed2a39880f8eae4dd + react-native-wgpu: efaa8c7c3ae15b346d887d13cca2fe72ed5ea105 React-nativeconfig: 57781b79e11d5af7573e6f77cbf1143b71802a6d React-NativeModulesApple: 7ff2e2cfb2e5fa5bdedcecf28ce37e696c6ef1e1 React-perflogger: 8a360ccf603de6ddbe9ff8f54383146d26e6c936 @@ -1966,10 +1966,10 @@ SPEC CHECKSUMS: ReactCommon: 6ef348087d250257c44c0204461c03f036650e9b RNGestureHandler: 939f21fabf5d45a725c0bf175eb819dd25cf2e70 RNReanimated: 190c12cb20dfa828353e99775beaa1bdf36e7ed9 - RNScreens: 19719a9c326e925498ac3b2d35c4e50fe87afc06 + RNScreens: b03d696c70cc5235ce4587fcc27ae1a93a48f98c RNSVG: 5da7a24f31968ec74f0b091e3440080f347e279b SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d - Yoga: 2a45d7e59592db061217551fd3bbe2dd993817ae + Yoga: a1d7895431387402a674fd0d1c04ec85e87909b8 PODFILE CHECKSUM: debc09f5cfcbea21f946ca0be3faa5351e907125 diff --git a/apps/paper/src/App.tsx b/apps/paper/src/App.tsx index 3698591015..b563270f6f 100644 --- a/apps/paper/src/App.tsx +++ b/apps/paper/src/App.tsx @@ -7,6 +7,7 @@ import type { HeaderBackButtonProps } from "@react-navigation/elements"; import { HeaderBackButton } from "@react-navigation/elements"; import { FiberProvider } from "its-fine"; import { GestureHandlerRootView } from "react-native-gesture-handler"; +import { enableScreens } from "react-native-screens"; import { ReanimatedExample, @@ -84,6 +85,8 @@ const HeaderLeft = (props: HeaderBackButtonProps) => { ); }; +enableScreens(true); + const App = () => { const Stack = createNativeStackNavigator(); const assets = useAssets(); diff --git a/apps/paper/src/Examples/Breathe/Breathe.tsx b/apps/paper/src/Examples/Breathe/Breathe.tsx index 4d00e1ac38..ddda1d3b99 100644 --- a/apps/paper/src/Examples/Breathe/Breathe.tsx +++ b/apps/paper/src/Examples/Breathe/Breathe.tsx @@ -68,7 +68,7 @@ export const Breathe = () => { return ( - + diff --git a/apps/paper/src/Examples/Glassmorphism/Glassmorphism.tsx b/apps/paper/src/Examples/Glassmorphism/Glassmorphism.tsx index 8b61686445..90121c76ca 100644 --- a/apps/paper/src/Examples/Glassmorphism/Glassmorphism.tsx +++ b/apps/paper/src/Examples/Glassmorphism/Glassmorphism.tsx @@ -40,7 +40,7 @@ export const Glassmorphism = () => { ); return ( - + { } const symbols = font.getGlyphIDs("abcdefghijklmnopqrstuvwxyz"); return ( - + diff --git a/apps/paper/src/Examples/Matrix/Symbol.tsx b/apps/paper/src/Examples/Matrix/Symbol.tsx index 3248753ed3..5c038311a0 100644 --- a/apps/paper/src/Examples/Matrix/Symbol.tsx +++ b/apps/paper/src/Examples/Matrix/Symbol.tsx @@ -4,8 +4,8 @@ import { interpolateColors, vec, Glyphs } from "@shopify/react-native-skia"; import type { SharedValue } from "react-native-reanimated"; import { useDerivedValue } from "react-native-reanimated"; -export const COLS = 15; -export const ROWS = 30; +export const COLS = 8; +export const ROWS = 15; const pos = vec(0, 0); interface SymbolProps { diff --git a/apps/paper/src/Examples/Severance/Severance.tsx b/apps/paper/src/Examples/Severance/Severance.tsx index 3f197322ae..a1356a18a3 100644 --- a/apps/paper/src/Examples/Severance/Severance.tsx +++ b/apps/paper/src/Examples/Severance/Severance.tsx @@ -29,7 +29,7 @@ export const Severance = () => { return ( - + diff --git a/apps/paper/src/Examples/Stickers/Stickers.tsx b/apps/paper/src/Examples/Stickers/Stickers.tsx index 9b5f39d89e..b0e72d9921 100644 --- a/apps/paper/src/Examples/Stickers/Stickers.tsx +++ b/apps/paper/src/Examples/Stickers/Stickers.tsx @@ -24,7 +24,7 @@ export const Stickers = () => { } return ( - + diff --git a/apps/paper/src/Examples/Video/Video.tsx b/apps/paper/src/Examples/Video/Video.tsx index e206e205c5..76c9b6ce55 100644 --- a/apps/paper/src/Examples/Video/Video.tsx +++ b/apps/paper/src/Examples/Video/Video.tsx @@ -37,7 +37,7 @@ export const Video = () => { style={{ flex: 1 }} onPress={() => (paused.value = !paused.value)} > - + { /> - + surfaceAvailable(surface, width, height); + virtual void surfaceAvailable(jobject surface, int width, int height, + bool opaque) { + _skiaAndroidView->surfaceAvailable(surface, width, height, opaque); } - virtual void surfaceSizeChanged(jobject surface, int width, int height) { - _skiaAndroidView->surfaceSizeChanged(surface, width, height); + virtual void surfaceSizeChanged(jobject surface, int width, int height, + bool opaque) { + _skiaAndroidView->surfaceSizeChanged(surface, width, height, opaque); } virtual void surfaceDestroyed() { _skiaAndroidView->surfaceDestroyed(); } diff --git a/packages/skia/android/cpp/jni/include/JniSkiaDomView.h b/packages/skia/android/cpp/jni/include/JniSkiaDomView.h index c675e098d8..954a076ff1 100644 --- a/packages/skia/android/cpp/jni/include/JniSkiaDomView.h +++ b/packages/skia/android/cpp/jni/include/JniSkiaDomView.h @@ -46,12 +46,14 @@ class JniSkiaDomView : public jni::HybridClass, } protected: - void surfaceAvailable(jobject surface, int width, int height) override { - JniSkiaBaseView::surfaceAvailable(surface, width, height); + void surfaceAvailable(jobject surface, int width, int height, + bool opaque) override { + JniSkiaBaseView::surfaceAvailable(surface, width, height, opaque); } - void surfaceSizeChanged(jobject surface, int width, int height) override { - JniSkiaBaseView::surfaceSizeChanged(surface, width, height); + void surfaceSizeChanged(jobject surface, int width, int height, + bool opaque) override { + JniSkiaBaseView::surfaceSizeChanged(surface, width, height, opaque); } void surfaceDestroyed() override { JniSkiaBaseView::surfaceDestroyed(); } diff --git a/packages/skia/android/cpp/jni/include/JniSkiaPictureView.h b/packages/skia/android/cpp/jni/include/JniSkiaPictureView.h index a55cd324a3..5171c81bac 100644 --- a/packages/skia/android/cpp/jni/include/JniSkiaPictureView.h +++ b/packages/skia/android/cpp/jni/include/JniSkiaPictureView.h @@ -48,12 +48,14 @@ class JniSkiaPictureView : public jni::HybridClass, } protected: - void surfaceAvailable(jobject surface, int width, int height) override { - JniSkiaBaseView::surfaceAvailable(surface, width, height); + void surfaceAvailable(jobject surface, int width, int height, + bool opaque) override { + JniSkiaBaseView::surfaceAvailable(surface, width, height, opaque); } - void surfaceSizeChanged(jobject surface, int width, int height) override { - JniSkiaBaseView::surfaceSizeChanged(surface, width, height); + void surfaceSizeChanged(jobject surface, int width, int height, + bool opaque) override { + JniSkiaBaseView::surfaceSizeChanged(surface, width, height, opaque); } void surfaceDestroyed() override { JniSkiaBaseView::surfaceDestroyed(); } diff --git a/packages/skia/android/cpp/rnskia-android/MainThreadDispatcher.h b/packages/skia/android/cpp/rnskia-android/MainThreadDispatcher.h index 0f82f47e4c..28f7feb8b1 100644 --- a/packages/skia/android/cpp/rnskia-android/MainThreadDispatcher.h +++ b/packages/skia/android/cpp/rnskia-android/MainThreadDispatcher.h @@ -2,6 +2,7 @@ #include #include +#include class MainThreadDispatcher { private: @@ -27,6 +28,10 @@ class MainThreadDispatcher { return instance; } + bool isOnMainThread() { + return ALooper_forThread() == mainLooper; + } + void post(std::function task) { // TODO: this is disabled for now but we can clean this up // if (ALooper_forThread() == mainLooper) { diff --git a/packages/skia/android/cpp/rnskia-android/OpenGLContext.h b/packages/skia/android/cpp/rnskia-android/OpenGLContext.h index 8f524c33ac..08206d0b9f 100644 --- a/packages/skia/android/cpp/rnskia-android/OpenGLContext.h +++ b/packages/skia/android/cpp/rnskia-android/OpenGLContext.h @@ -16,6 +16,30 @@ namespace RNSkia { +class OpenGLSharedContext { +public: + OpenGLSharedContext(const OpenGLSharedContext &) = delete; + OpenGLSharedContext &operator=(const OpenGLSharedContext &) = delete; + + static OpenGLSharedContext &getInstance() { + static OpenGLSharedContext instance; + return instance; + } + + gl::Display* getDisplay() { return _glDisplay.get(); } + gl::Context* getContext() { return _glContext.get(); } + +private: + std::unique_ptr _glDisplay; + std::unique_ptr _glContext; + + OpenGLSharedContext() { + _glDisplay = std::make_unique(); + auto glConfig = _glDisplay->chooseConfig(); + _glContext = _glDisplay->makeContext(glConfig, nullptr); + } +}; + class OpenGLContext { public: friend class OpenGLWindowContext; @@ -128,24 +152,24 @@ class OpenGLContext { // TODO: remove width, height std::unique_ptr MakeWindow(ANativeWindow *window, int width, int height) { + auto display = OpenGLSharedContext::getInstance().getDisplay(); return std::make_unique( - _directContext.get(), _glDisplay.get(), _glContext.get(), window); + _directContext.get(), display, _glContext.get(), window); } GrDirectContext *getDirectContext() { return _directContext.get(); } private: - EGLConfig _glConfig; - std::unique_ptr _glDisplay; std::unique_ptr _glContext; std::unique_ptr _glSurface; sk_sp _directContext; OpenGLContext() { - _glDisplay = std::make_unique(); - _glConfig = _glDisplay->chooseConfig(); - _glContext = _glDisplay->makeContext(_glConfig, nullptr); - _glSurface = _glDisplay->makePixelBufferSurface(_glConfig, 1, 1); + auto display = OpenGLSharedContext::getInstance().getDisplay(); + auto sharedContext = OpenGLSharedContext::getInstance().getContext(); + auto glConfig = display->chooseConfig(); + _glContext = display->makeContext(glConfig, sharedContext); + _glSurface = display->makePixelBufferSurface(glConfig, 1, 1); _glContext->makeCurrent(_glSurface.get()); auto backendInterface = GrGLMakeNativeInterface(); _directContext = GrDirectContexts::MakeGL(backendInterface); diff --git a/packages/skia/android/cpp/rnskia-android/OpenGLWindowContext.cpp b/packages/skia/android/cpp/rnskia-android/OpenGLWindowContext.cpp index ca390563dd..3916c0d67a 100644 --- a/packages/skia/android/cpp/rnskia-android/OpenGLWindowContext.cpp +++ b/packages/skia/android/cpp/rnskia-android/OpenGLWindowContext.cpp @@ -53,7 +53,6 @@ sk_sp OpenGLWindowContext::getSurface() { void OpenGLWindowContext::present() { _glContext->makeCurrent(_glSurface.get()); - // TODO: is flushAndSubmit needed here? _directContext->flushAndSubmit(); _glSurface->present(); } diff --git a/packages/skia/android/cpp/rnskia-android/RNSkAndroidView.h b/packages/skia/android/cpp/rnskia-android/RNSkAndroidView.h index e209a78bc9..78f716af69 100644 --- a/packages/skia/android/cpp/rnskia-android/RNSkAndroidView.h +++ b/packages/skia/android/cpp/rnskia-android/RNSkAndroidView.h @@ -11,11 +11,13 @@ namespace RNSkia { class RNSkBaseAndroidView { public: - virtual void surfaceAvailable(jobject surface, int width, int height) = 0; + virtual void surfaceAvailable(jobject surface, int width, int height, + bool opaque) = 0; virtual void surfaceDestroyed() = 0; - virtual void surfaceSizeChanged(jobject surface, int width, int height) = 0; + virtual void surfaceSizeChanged(jobject surface, int width, int height, + bool opaque) = 0; virtual float getPixelDensity() = 0; @@ -34,12 +36,10 @@ class RNSkAndroidView : public T, public RNSkBaseAndroidView { std::make_shared( std::bind(&RNSkia::RNSkView::requestRedraw, this), context)) {} - void surfaceAvailable(jobject surface, int width, int height) override { + void surfaceAvailable(jobject surface, int width, int height, + bool opaque) override { std::static_pointer_cast(T::getCanvasProvider()) - ->surfaceAvailable(surface, width, height); - - // Try to render directly when the surface has been set so that - // we don't have to wait until the draw loop returns. + ->surfaceAvailable(surface, width, height, opaque); RNSkView::redraw(); } @@ -48,9 +48,10 @@ class RNSkAndroidView : public T, public RNSkBaseAndroidView { ->surfaceDestroyed(); } - void surfaceSizeChanged(jobject surface, int width, int height) override { + void surfaceSizeChanged(jobject surface, int width, int height, + bool opaque) override { std::static_pointer_cast(T::getCanvasProvider()) - ->surfaceSizeChanged(surface, width, height); + ->surfaceSizeChanged(surface, width, height, opaque); // This is only need for the first time to frame, this renderImmediate call // will invoke updateTexImage for the previous frame RNSkView::redraw(); diff --git a/packages/skia/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp b/packages/skia/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp index c3c587f70f..361bdc2997 100644 --- a/packages/skia/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp +++ b/packages/skia/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp @@ -46,22 +46,24 @@ float RNSkOpenGLCanvasProvider::getScaledHeight() { bool RNSkOpenGLCanvasProvider::renderToCanvas( const std::function &cb) { - JNIEnv *env = facebook::jni::Environment::current(); if (_surfaceHolder != nullptr && cb != nullptr) { // Get the surface auto surface = _surfaceHolder->getSurface(); - env->CallVoidMethod(_jSurfaceTexture, _updateTexImageMethod); - - // Check for exceptions - if (env->ExceptionCheck()) { - RNSkLogger::logToConsole("updateAndRelease() failed. The exception above " - "can safely be ignored"); - env->ExceptionClear(); + if (_jSurfaceTexture) { + JNIEnv *env = facebook::jni::Environment::current(); + env->CallVoidMethod(_jSurfaceTexture, _updateTexImageMethod); + + // Check for exceptions + if (env->ExceptionCheck()) { + RNSkLogger::logToConsole( + "updateAndRelease() failed. The exception above " + "can safely be ignored"); + env->ExceptionClear(); + } } if (surface) { // Draw into canvas using callback cb(surface->getCanvas()); - // Swap buffers and show on screen _surfaceHolder->present(); return true; @@ -74,32 +76,36 @@ bool RNSkOpenGLCanvasProvider::renderToCanvas( } void RNSkOpenGLCanvasProvider::surfaceAvailable(jobject jSurfaceTexture, - int width, int height) { - // If the surface is 0, we can skip it - if (width == 0 && height == 0) { - return; - } + int width, int height, + bool opaque) { + // Release the old surface + _surfaceHolder = nullptr; + // Create renderer! + ANativeWindow *window = nullptr; JNIEnv *env = facebook::jni::Environment::current(); - - _jSurfaceTexture = env->NewGlobalRef(jSurfaceTexture); - jclass surfaceClass = env->FindClass("android/view/Surface"); - jmethodID surfaceConstructor = env->GetMethodID( - surfaceClass, "", "(Landroid/graphics/SurfaceTexture;)V"); - // Create a new Surface instance - jobject jSurface = - env->NewObject(surfaceClass, surfaceConstructor, jSurfaceTexture); - - jclass surfaceTextureClass = env->GetObjectClass(_jSurfaceTexture); - _updateTexImageMethod = - env->GetMethodID(surfaceTextureClass, "updateTexImage", "()V"); - - // Acquire the native window from the Surface - auto window = ANativeWindow_fromSurface(env, jSurface); - // Clean up local references - env->DeleteLocalRef(jSurface); - env->DeleteLocalRef(surfaceClass); - env->DeleteLocalRef(surfaceTextureClass); + if (!opaque) { + _jSurfaceTexture = env->NewGlobalRef(jSurfaceTexture); + jclass surfaceClass = env->FindClass("android/view/Surface"); + jmethodID surfaceConstructor = env->GetMethodID( + surfaceClass, "", "(Landroid/graphics/SurfaceTexture;)V"); + // Create a new Surface instance + auto jSurface = + env->NewObject(surfaceClass, surfaceConstructor, jSurfaceTexture); + window = ANativeWindow_fromSurface(env, jSurface); + + jclass surfaceTextureClass = env->GetObjectClass(_jSurfaceTexture); + _updateTexImageMethod = + env->GetMethodID(surfaceTextureClass, "updateTexImage", "()V"); + + // Acquire the native window from the Surface + // Clean up local references + env->DeleteLocalRef(jSurface); + env->DeleteLocalRef(surfaceClass); + env->DeleteLocalRef(surfaceTextureClass); + } else { + window = ANativeWindow_fromSurface(env, jSurfaceTexture); + } #if defined(SK_GRAPHITE) _surfaceHolder = DawnContext::getInstance().MakeWindow(window, width, height); #else @@ -121,8 +127,8 @@ void RNSkOpenGLCanvasProvider::surfaceDestroyed() { } } -void RNSkOpenGLCanvasProvider::surfaceSizeChanged(jobject jSurfaceTexture, - int width, int height) { +void RNSkOpenGLCanvasProvider::surfaceSizeChanged(jobject jSurface, int width, + int height, bool opaque) { if (width == 0 && height == 0) { // Setting width/height to zero is nothing we need to care about when // it comes to invalidating the surface. @@ -131,7 +137,7 @@ void RNSkOpenGLCanvasProvider::surfaceSizeChanged(jobject jSurfaceTexture, if (_surfaceHolder == nullptr) { _surfaceHolder = nullptr; - surfaceAvailable(jSurfaceTexture, width, height); + surfaceAvailable(jSurface, width, height, opaque); } else { _surfaceHolder->resize(width, height); } diff --git a/packages/skia/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.h b/packages/skia/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.h index fb73bcaa74..a6cac6580c 100644 --- a/packages/skia/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.h +++ b/packages/skia/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.h @@ -27,11 +27,11 @@ class RNSkOpenGLCanvasProvider bool renderToCanvas(const std::function &cb) override; - void surfaceAvailable(jobject surface, int width, int height); + void surfaceAvailable(jobject surface, int width, int height, bool opaque); void surfaceDestroyed(); - void surfaceSizeChanged(jobject jSurface, int width, int height); + void surfaceSizeChanged(jobject jSurface, int width, int height, bool opaque); private: std::unique_ptr _surfaceHolder = nullptr; diff --git a/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaAHBView.java b/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaAHBView.java new file mode 100644 index 0000000000..fb82bb0caa --- /dev/null +++ b/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaAHBView.java @@ -0,0 +1,113 @@ +package com.shopify.reactnative.skia; + + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.PixelFormat; +import android.hardware.HardwareBuffer; +import android.media.Image; +import android.media.ImageReader; +import android.os.Build; +import android.util.Log; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +@SuppressLint("ViewConstructor") +@RequiresApi(api = Build.VERSION_CODES.Q) +public class SkiaAHBView extends View implements ImageReader.OnImageAvailableListener { + + private ImageReader mReader; + + private Bitmap mBitmap = null; + + private final Matrix matrix = new Matrix(); + + SkiaViewAPI mApi; + boolean mDebug; + + public SkiaAHBView(Context context, SkiaViewAPI api, boolean debug) { + super(context); + mApi = api; + mDebug = debug; + } + + private ImageReader createReader() { + ImageReader reader = ImageReader.newInstance(getWidth(), getHeight(), PixelFormat.RGBA_8888, 2, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | + HardwareBuffer.USAGE_GPU_COLOR_OUTPUT); + reader.setOnImageAvailableListener(this, null); + return reader; + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + int width = getWidth(); + int height = getHeight(); + if (mReader == null) { + mReader = createReader(); + mApi.onSurfaceCreated(mReader.getSurface(), width, height); + } else { + mReader = createReader(); + mApi.onSurfaceChanged(mReader.getSurface(), width, height); + } + } + + @Override + public void onImageAvailable(ImageReader reader) { + try (Image image = reader.acquireLatestImage()) { + if (image != null) { + HardwareBuffer hb = image.getHardwareBuffer(); + if (mDebug) { + textureUpdated(image.getTimestamp()); + } + if (hb != null) { + Bitmap bitmap = Bitmap.wrapHardwareBuffer(hb, null); + if (bitmap != null) { + mBitmap = bitmap; + hb.close(); + invalidate(); + } + } + } + } + } + + @Override + protected void onDraw(@NonNull Canvas canvas) { + super.onDraw(canvas); + if (mBitmap != null) { + float viewWidth = getWidth(); + float viewHeight = getHeight(); + float bitmapWidth = mBitmap.getWidth(); + float bitmapHeight = mBitmap.getHeight(); + + // Calculate the scale factors + float scaleX = viewWidth / bitmapWidth; + float scaleY = viewHeight / bitmapHeight; + + // Reset the matrix and apply scaling + matrix.reset(); + matrix.setScale(scaleX, scaleY); + + canvas.drawBitmap(mBitmap, matrix, null); + } + } + + private long _prevTimestamp = 0; + public void textureUpdated(long ts) { + long frameDuration = (ts - _prevTimestamp)/1000000; + Log.i("SkiaAHBView", "onSurfaceTextureUpdated "+frameDuration+"ms"); + _prevTimestamp = ts; + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mApi.onSurfaceDestroyed(); + } +} \ No newline at end of file diff --git a/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseView.java b/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseView.java index 8f88d6b23b..8f465e0d2c 100644 --- a/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseView.java +++ b/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseView.java @@ -3,97 +3,88 @@ import android.content.Context; import android.graphics.SurfaceTexture; import android.util.Log; -import android.view.TextureView; +import android.view.Surface; +import android.view.View; import com.facebook.react.views.view.ReactViewGroup; -public abstract class SkiaBaseView extends ReactViewGroup implements TextureView.SurfaceTextureListener { - private TextureView mTexture; +public abstract class SkiaBaseView extends ReactViewGroup implements SkiaViewAPI { + private View mView; - private String tag = "SkiaView"; - - private boolean isDropped = false; + private final boolean debug = false; + private final String tag = "SkiaView"; public SkiaBaseView(Context context) { super(context); - mTexture = new TextureView(context); - mTexture.setSurfaceTextureListener(this); - mTexture.setOpaque(false); - addView(mTexture); + mView = new SkiaTextureView(context, this, debug); + addView(mView); } - private void createSurfaceTexture() { - // This API Level is >= 26, we created our own SurfaceTexture to have a faster time to first frame - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { - Log.i(tag, "Create SurfaceTexture"); - SurfaceTexture surface = new SurfaceTexture(false); - mTexture.setSurfaceTexture(surface); - this.onSurfaceTextureAvailable(surface, this.getMeasuredWidth(), this.getMeasuredHeight()); + public void setOpaque(boolean value) { + if (value && mView instanceof SkiaTextureView) { + removeView(mView); + mView = new SkiaSurfaceView(getContext(), this, debug); + addView(mView); + } else if (!value && mView instanceof SkiaSurfaceView) { + removeView(mView); + mView = new SkiaTextureView(getContext(), this, debug); + addView(mView); } } - void dropInstance() { - isDropped = true; - unregisterView(); - } - @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - if (this.getMeasuredWidth() == 0) { - createSurfaceTexture(); + if (getMeasuredWidth() == 0) { + if (mView instanceof SkiaTextureView) { + ((SkiaTextureView) mView).createSurfaceTexture(); + } + } + } + + void dropInstance() { + if (mView instanceof SkiaTextureView) { + ((SkiaTextureView)mView).isDropped = true; } + unregisterView(); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - Log.i(tag, "onLayout " + this.getMeasuredWidth() + "/" + this.getMeasuredHeight()); super.onLayout(changed, left, top, right, bottom); - mTexture.layout(0, 0, this.getMeasuredWidth(), this.getMeasuredHeight()); + mView.layout(0, 0, right - left, bottom - top); } @Override - public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { - Log.i(tag, "onSurfaceTextureAvailable " + width + "/" + height); - surfaceAvailable(surface, width, height); + public void onSurfaceCreated(Surface surface, int width, int height) { + surfaceAvailable(surface, width, height, true); } @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { - if (isDropped) { - return; - } + public void onSurfaceChanged(Surface surface, int width, int height) { Log.i(tag, "onSurfaceTextureSizeChanged " + width + "/" + height); - surfaceSizeChanged(surface, width, height); + surfaceSizeChanged(surface, width, height, true); } @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { - Log.i(tag, "onSurfaceTextureDestroyed"); - // https://developer.android.com/reference/android/view/TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed(android.graphics.SurfaceTexture) - surfaceDestroyed(); - // Because of React Native Screens (which dettach the view), we always keep the surface alive. - // If not, Texture view will recreate the texture surface by itself and - // we will lose the fast first time to frame. - // We only delete the surface when the view is dropped (destroySurface invoked by SkiaBaseViewManager); - if (!isDropped) { - createSurfaceTexture(); - } - return false; + public void onSurfaceTextureCreated(SurfaceTexture surface, int width, int height) { + surfaceAvailable(surface, width, height, false); } - private long _prevTimestamp = 0; @Override - public void onSurfaceTextureUpdated(SurfaceTexture surface) { - long timestamp = surface.getTimestamp(); - long frameDuration = (timestamp - _prevTimestamp)/1000000; - Log.i(tag, "onSurfaceTextureUpdated "+frameDuration+"ms"); - _prevTimestamp = timestamp; + public void onSurfaceTextureChanged(SurfaceTexture surface, int width, int height) { + Log.i(tag, "onSurfaceTextureSizeChanged " + width + "/" + height); + surfaceSizeChanged(surface, width, height, false); + } + + @Override + public void onSurfaceDestroyed() { + surfaceDestroyed(); } - protected abstract void surfaceAvailable(Object surface, int width, int height); + protected abstract void surfaceAvailable(Object surface, int width, int height, boolean opaque); - protected abstract void surfaceSizeChanged(Object surface, int width, int height); + protected abstract void surfaceSizeChanged(Object surface, int width, int height, boolean opaque); protected abstract void surfaceDestroyed(); diff --git a/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseViewManager.java b/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseViewManager.java index 4317de650a..38c8afc649 100644 --- a/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseViewManager.java +++ b/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseViewManager.java @@ -26,6 +26,11 @@ public void setDebug(T view, boolean show) { ((SkiaBaseView)view).setDebugMode(show); } + @ReactProp(name = "opaque") + public void setOpaque(T view, boolean value) { + ((SkiaBaseView)view).setOpaque(value); + } + @Override public void onDropViewInstance(@NonNull ReactViewGroup view) { super.onDropViewInstance(view); diff --git a/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaDomView.java b/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaDomView.java index eafe21a8f0..7a7ef539be 100644 --- a/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaDomView.java +++ b/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaDomView.java @@ -25,9 +25,9 @@ protected void finalize() throws Throwable { private native HybridData initHybrid(SkiaManager skiaManager); - protected native void surfaceAvailable(Object surface, int width, int height); + protected native void surfaceAvailable(Object surface, int width, int height, boolean opaque); - protected native void surfaceSizeChanged(Object surface, int width, int height); + protected native void surfaceSizeChanged(Object surface, int width, int height, boolean opaque); protected native void surfaceDestroyed(); diff --git a/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaPictureView.java b/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaPictureView.java index f4dc2a4695..e1ef8aa366 100644 --- a/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaPictureView.java +++ b/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaPictureView.java @@ -24,9 +24,9 @@ protected void finalize() throws Throwable { private native HybridData initHybrid(SkiaManager skiaManager); - protected native void surfaceAvailable(Object surface, int width, int height); + protected native void surfaceAvailable(Object surface, int width, int height, boolean opaque); - protected native void surfaceSizeChanged(Object surface, int width, int height); + protected native void surfaceSizeChanged(Object surface, int width, int height, boolean opaque); protected native void surfaceDestroyed(); diff --git a/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaSurfaceView.java b/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaSurfaceView.java new file mode 100644 index 0000000000..5e28a08c94 --- /dev/null +++ b/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaSurfaceView.java @@ -0,0 +1,42 @@ +package com.shopify.reactnative.skia; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import androidx.annotation.NonNull; + +@SuppressLint("ViewConstructor") +public class SkiaSurfaceView extends SurfaceView implements SurfaceHolder.Callback { + + SkiaViewAPI mApi; + boolean mDebug; + + public SkiaSurfaceView(Context context, SkiaViewAPI api, boolean debug) { + super(context); + mApi = api; + mDebug = debug; + getHolder().addCallback(this); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mApi.onSurfaceDestroyed(); + } + + @Override + public void surfaceCreated(@NonNull SurfaceHolder holder) { + mApi.onSurfaceCreated(holder.getSurface(), getWidth(), getHeight()); + } + + @Override + public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) { + mApi.onSurfaceChanged(holder.getSurface(), getWidth(), getHeight()); + } + + @Override + public void surfaceDestroyed(@NonNull SurfaceHolder holder) { + mApi.onSurfaceDestroyed(); + } +} \ No newline at end of file diff --git a/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaTextureView.java b/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaTextureView.java new file mode 100644 index 0000000000..e423f4666b --- /dev/null +++ b/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaTextureView.java @@ -0,0 +1,90 @@ +package com.shopify.reactnative.skia; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.SurfaceTexture; +import android.util.Log; +import android.view.Surface; +import android.view.TextureView; +import androidx.annotation.NonNull; + +@SuppressLint("ViewConstructor") +public class SkiaTextureView extends TextureView implements TextureView.SurfaceTextureListener { + + private String tag = "SkiaTextureView"; + + SkiaViewAPI mApi; + boolean mDebug; + boolean pristine = true; + + public boolean isDropped = false; + + public SkiaTextureView(Context context, SkiaViewAPI api, boolean debug) { + super(context); + mApi = api; + mDebug = debug; + setOpaque(false); + setSurfaceTextureListener(this); + } + + public void createSurfaceTexture() { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + Log.i(tag, "Create SurfaceTexture"); + SurfaceTexture surfaceTexture = new SurfaceTexture(false); + setSurfaceTexture(surfaceTexture); + onSurfaceTextureAvailable(surfaceTexture, getWidth(), getHeight()); + } + } + + private void reCreateSurfaceTexture() { + boolean surfaceIsAlreadyAvailable = getSurfaceTexture() != null; + if (surfaceIsAlreadyAvailable) { + createSurfaceTexture(); + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + } + + @Override + public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surfaceTexture, int width, int height) { + Log.i(tag, "onSurfaceTextureAvailable: " + width + "x" + height); + mApi.onSurfaceTextureCreated(surfaceTexture, width, height); + } + + @Override + public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surfaceTexture, int width, int height) { + Log.i(tag, "onSurfaceTextureSizeChanged: " + width + "x" + height); + if (isDropped) { + return; + } + mApi.onSurfaceTextureCreated(surfaceTexture, width, height); + } + + @Override + public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) { + mApi.onSurfaceDestroyed(); + // Because of React Native Screens (which dettach the view), we always keep the surface alive. + // If not, Texture view will recreate the texture surface by itself and + // we will lose the fast first time to frame. + // We only delete the surface when the view is dropped (destroySurface invoked by SkiaBaseViewManager); + if (!isDropped) { + reCreateSurfaceTexture(); + } + return false; + } + + private long _prevTimestamp = 0; + @Override + public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) { + if (!mDebug) { + return; + } + long timestamp = surface.getTimestamp(); + long frameDuration = (timestamp - _prevTimestamp)/1000000; + Log.i("SkiaTextureView", "onSurfaceTextureUpdated "+frameDuration+"ms"); + _prevTimestamp = timestamp; + } +} \ No newline at end of file diff --git a/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaViewAPI.java b/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaViewAPI.java new file mode 100644 index 0000000000..3bb79fe9a4 --- /dev/null +++ b/packages/skia/android/src/main/java/com/shopify/reactnative/skia/SkiaViewAPI.java @@ -0,0 +1,16 @@ +package com.shopify.reactnative.skia; + +import android.graphics.SurfaceTexture; +import android.view.Surface; + +public interface SkiaViewAPI { + void onSurfaceCreated(Surface surface, int width, int height); + + void onSurfaceChanged(Surface surface, int width, int height); + + void onSurfaceTextureCreated(SurfaceTexture surface, int width, int height); + + void onSurfaceTextureChanged(SurfaceTexture surface, int width, int height); + + void onSurfaceDestroyed(); +} diff --git a/packages/skia/android/src/paper/java/com/facebook/react/viewmanagers/SkiaDomViewManagerDelegate.java b/packages/skia/android/src/paper/java/com/facebook/react/viewmanagers/SkiaDomViewManagerDelegate.java index 929f675b29..13828a5c24 100644 --- a/packages/skia/android/src/paper/java/com/facebook/react/viewmanagers/SkiaDomViewManagerDelegate.java +++ b/packages/skia/android/src/paper/java/com/facebook/react/viewmanagers/SkiaDomViewManagerDelegate.java @@ -21,6 +21,9 @@ public SkiaDomViewManagerDelegate(U viewManager) { @Override public void setProperty(T view, String propName, @Nullable Object value) { switch (propName) { + case "opaque": + mViewManager.setOpaque(view, value != null && (boolean) value); + break; case "debug": mViewManager.setDebug(view, value != null && (boolean) value); break; diff --git a/packages/skia/android/src/paper/java/com/facebook/react/viewmanagers/SkiaDomViewManagerInterface.java b/packages/skia/android/src/paper/java/com/facebook/react/viewmanagers/SkiaDomViewManagerInterface.java index b3ceeab3ac..9423a93db7 100644 --- a/packages/skia/android/src/paper/java/com/facebook/react/viewmanagers/SkiaDomViewManagerInterface.java +++ b/packages/skia/android/src/paper/java/com/facebook/react/viewmanagers/SkiaDomViewManagerInterface.java @@ -14,4 +14,5 @@ public interface SkiaDomViewManagerInterface { void setDebug(T view, boolean value); + void setOpaque(T view, boolean value); } diff --git a/packages/skia/android/src/paper/java/com/facebook/react/viewmanagers/SkiaPictureViewManagerDelegate.java b/packages/skia/android/src/paper/java/com/facebook/react/viewmanagers/SkiaPictureViewManagerDelegate.java index 6690d32833..9d47510bfe 100644 --- a/packages/skia/android/src/paper/java/com/facebook/react/viewmanagers/SkiaPictureViewManagerDelegate.java +++ b/packages/skia/android/src/paper/java/com/facebook/react/viewmanagers/SkiaPictureViewManagerDelegate.java @@ -24,8 +24,10 @@ public void setProperty(T view, String propName, @Nullable Object value) { case "mode": mViewManager.setMode(view, value == null ? null : (String) value); break; + case "opaque": + mViewManager.setOpaque(view, value != null && (boolean) value); case "debug": - mViewManager.setDebug(view, value == null ? false : (boolean) value); + mViewManager.setDebug(view, value != null && (boolean) value); break; default: super.setProperty(view, propName, value); diff --git a/packages/skia/android/src/paper/java/com/facebook/react/viewmanagers/SkiaPictureViewManagerInterface.java b/packages/skia/android/src/paper/java/com/facebook/react/viewmanagers/SkiaPictureViewManagerInterface.java index 282296dd12..10c5f75ff2 100644 --- a/packages/skia/android/src/paper/java/com/facebook/react/viewmanagers/SkiaPictureViewManagerInterface.java +++ b/packages/skia/android/src/paper/java/com/facebook/react/viewmanagers/SkiaPictureViewManagerInterface.java @@ -15,4 +15,5 @@ public interface SkiaPictureViewManagerInterface { void setMode(T view, @Nullable String value); void setDebug(T view, boolean value); + void setOpaque(T view, boolean value); } diff --git a/packages/skia/cpp/rnskia/RNSkPictureView.h b/packages/skia/cpp/rnskia/RNSkPictureView.h index b68b68991b..39c5c38ffc 100644 --- a/packages/skia/cpp/rnskia/RNSkPictureView.h +++ b/packages/skia/cpp/rnskia/RNSkPictureView.h @@ -40,7 +40,7 @@ class RNSkPictureRenderer public: RNSkPictureRenderer(std::function requestRedraw, std::shared_ptr context) - : RNSkRenderer(requestRedraw), _platformContext(context) {} + : RNSkRenderer(std::move(requestRedraw)), _platformContext(std::move(context)) {} void renderImmediate(std::shared_ptr canvasProvider) override { @@ -51,31 +51,28 @@ class RNSkPictureRenderer if (picture == nullptr) { _picture = nullptr; } else { - _picture = std::dynamic_pointer_cast(picture); + _picture = std::dynamic_pointer_cast(picture)->getObject(); } _requestRedraw(); } private: bool performDraw(std::shared_ptr canvasProvider) { - canvasProvider->renderToCanvas([=](SkCanvas *canvas) { + return canvasProvider->renderToCanvas([=](SkCanvas *canvas) { // Make sure to scale correctly auto pd = _platformContext->getPixelDensity(); canvas->clear(SK_ColorTRANSPARENT); canvas->save(); canvas->scale(pd, pd); - if (_picture != nullptr) { - canvas->drawPicture(_picture->getObject()); + canvas->drawPicture(_picture); } - canvas->restore(); }); - return true; } std::shared_ptr _platformContext; - std::shared_ptr _picture; + sk_sp _picture; }; class RNSkPictureView : public RNSkView { diff --git a/packages/skia/cpp/rnskia/RNSkView.h b/packages/skia/cpp/rnskia/RNSkView.h index e450f568a1..d3ab09c091 100644 --- a/packages/skia/cpp/rnskia/RNSkView.h +++ b/packages/skia/cpp/rnskia/RNSkView.h @@ -52,7 +52,7 @@ class RNSkCanvasProvider { class RNSkRenderer { public: explicit RNSkRenderer(std::function requestRedraw) - : _requestRedraw(requestRedraw) {} + : _requestRedraw(std::move(requestRedraw)), _showDebugOverlays(false) {} virtual void renderImmediate(std::shared_ptr canvasProvider) = 0; @@ -60,7 +60,7 @@ class RNSkRenderer { void setShowDebugOverlays(bool showDebugOverlays) { _showDebugOverlays = showDebugOverlays; } - bool getShowDebugOverlays() { return _showDebugOverlays; } + bool getShowDebugOverlays() const { return _showDebugOverlays; } protected: std::function _requestRedraw; @@ -162,7 +162,7 @@ class RNSkView : public std::enable_shared_from_this { // Try to lock the weak pointer if (auto strongThis = weakThis.lock()) { // Only proceed if the object still exists - if (strongThis->_renderer) { + if (strongThis->_renderer && strongThis->_redrawRequested) { strongThis->_renderer->renderImmediate(strongThis->_canvasProvider); strongThis->_redrawRequested = false; } diff --git a/packages/skia/ios/RNSkia-iOS/SkiaCVPixelBufferUtils.mm b/packages/skia/ios/RNSkia-iOS/SkiaCVPixelBufferUtils.mm index 6e2b6fa064..67a4345518 100644 --- a/packages/skia/ios/RNSkia-iOS/SkiaCVPixelBufferUtils.mm +++ b/packages/skia/ios/RNSkia-iOS/SkiaCVPixelBufferUtils.mm @@ -23,16 +23,12 @@ #include #if TARGET_RT_BIG_ENDIAN #define FourCC2Str(fourcc) \ - (const char[]) { \ - *((char *)&fourcc), *(((char *)&fourcc) + 1), *(((char *)&fourcc) + 2), \ - *(((char *)&fourcc) + 3), 0 \ - } + (const char[]){*((char *)&fourcc), *(((char *)&fourcc) + 1), \ + *(((char *)&fourcc) + 2), *(((char *)&fourcc) + 3), 0} #else #define FourCC2Str(fourcc) \ - (const char[]) { \ - *(((char *)&fourcc) + 3), *(((char *)&fourcc) + 2), \ - *(((char *)&fourcc) + 1), *(((char *)&fourcc) + 0), 0 \ - } + (const char[]){*(((char *)&fourcc) + 3), *(((char *)&fourcc) + 2), \ + *(((char *)&fourcc) + 1), *(((char *)&fourcc) + 0), 0} #endif // pragma MARK: TextureHolder diff --git a/packages/skia/ios/RNSkia-iOS/SkiaDomViewManager.mm b/packages/skia/ios/RNSkia-iOS/SkiaDomViewManager.mm index 0710c0a38a..30f53eba76 100644 --- a/packages/skia/ios/RNSkia-iOS/SkiaDomViewManager.mm +++ b/packages/skia/ios/RNSkia-iOS/SkiaDomViewManager.mm @@ -31,6 +31,11 @@ - (SkiaManager *)skiaManager { [(SkiaUIView *)view setDebugMode:debug]; } +RCT_CUSTOM_VIEW_PROPERTY(opaque, BOOL, SkiaUIView) { + bool debug = json != NULL ? [RCTConvert BOOL:json] : false; + [(SkiaUIView *)view setOpaque:debug]; +} + - (UIView *)view { auto skManager = [[self skiaManager] skManager]; // Pass SkManager as a raw pointer to avoid circular dependenciesr diff --git a/packages/skia/ios/RNSkia-iOS/SkiaPictureView.mm b/packages/skia/ios/RNSkia-iOS/SkiaPictureView.mm index 022f409d91..9bf2f61eeb 100644 --- a/packages/skia/ios/RNSkia-iOS/SkiaPictureView.mm +++ b/packages/skia/ios/RNSkia-iOS/SkiaPictureView.mm @@ -54,6 +54,7 @@ - (void)updateProps:(const Props::Shared &)props [[RCTConvert NSString:RCTNSStringFromString(newProps.nativeId)] intValue]; [self setNativeId:nativeId]; [self setDebugMode:newProps.debug]; + [self setOpaque:newProps.opaque]; } @end diff --git a/packages/skia/ios/RNSkia-iOS/SkiaPictureViewManager.mm b/packages/skia/ios/RNSkia-iOS/SkiaPictureViewManager.mm index ce2eef8483..8ebccb1cce 100644 --- a/packages/skia/ios/RNSkia-iOS/SkiaPictureViewManager.mm +++ b/packages/skia/ios/RNSkia-iOS/SkiaPictureViewManager.mm @@ -31,6 +31,11 @@ - (SkiaManager *)skiaManager { [(SkiaUIView *)view setDebugMode:debug]; } +RCT_CUSTOM_VIEW_PROPERTY(opaque, BOOL, SkiaUIView) { + bool opaque = json != NULL ? [RCTConvert BOOL:json] : false; + [(SkiaUIView *)view setOpaque:opaque]; +} + - (UIView *)view { auto skManager = [[self skiaManager] skManager]; // Pass SkManager as a raw pointer to avoid circular dependenciesr diff --git a/packages/skia/ios/RNSkia-iOS/SkiaUIView.h b/packages/skia/ios/RNSkia-iOS/SkiaUIView.h index fb2bb6d278..8335d424fb 100644 --- a/packages/skia/ios/RNSkia-iOS/SkiaUIView.h +++ b/packages/skia/ios/RNSkia-iOS/SkiaUIView.h @@ -32,6 +32,7 @@ - (SkiaManager *)skiaManager; - (void)setDebugMode:(bool)debugMode; +- (void)setOpaque:(bool)opaque; - (void)setNativeId:(size_t)nativeId; @end diff --git a/packages/skia/ios/RNSkia-iOS/SkiaUIView.mm b/packages/skia/ios/RNSkia-iOS/SkiaUIView.mm index 7a2209aad5..8ec714e079 100644 --- a/packages/skia/ios/RNSkia-iOS/SkiaUIView.mm +++ b/packages/skia/ios/RNSkia-iOS/SkiaUIView.mm @@ -15,6 +15,7 @@ @implementation SkiaUIView { std::shared_ptr)> _factory; bool _debugMode; + bool _opaque; size_t _nativeId; } @@ -150,6 +151,10 @@ - (void)setDebugMode:(bool)debugMode { } } +- (void)setOpaque:(bool)opaque { + _opaque = opaque; +} + - (void)setNativeId:(size_t)nativeId { _nativeId = nativeId; diff --git a/packages/skia/src/specs/SkiaPictureViewNativeComponent.ts b/packages/skia/src/specs/SkiaPictureViewNativeComponent.ts index 79cfba1dc4..9ba262050e 100644 --- a/packages/skia/src/specs/SkiaPictureViewNativeComponent.ts +++ b/packages/skia/src/specs/SkiaPictureViewNativeComponent.ts @@ -3,6 +3,7 @@ import type { ViewProps } from "react-native"; export interface NativeProps extends ViewProps { debug?: boolean; + opaque?: boolean; } // eslint-disable-next-line import/no-default-export diff --git a/packages/skia/src/views/SkiaDomView.tsx b/packages/skia/src/views/SkiaDomView.tsx index 593ac66f4e..baad65a810 100644 --- a/packages/skia/src/views/SkiaDomView.tsx +++ b/packages/skia/src/views/SkiaDomView.tsx @@ -97,12 +97,13 @@ export class SkiaDomView extends React.Component { } render() { - const { debug = false, ...viewProps } = this.props; + const { debug = false, opaque = false, ...viewProps } = this.props; return ( ); diff --git a/packages/skia/src/views/SkiaPictureView.tsx b/packages/skia/src/views/SkiaPictureView.tsx index bc9ea2314c..864a8ae0fa 100644 --- a/packages/skia/src/views/SkiaPictureView.tsx +++ b/packages/skia/src/views/SkiaPictureView.tsx @@ -38,6 +38,7 @@ export class SkiaPictureView extends React.Component { } componentDidUpdate(prevProps: SkiaPictureViewProps) { + console.log("componentDidUpdate"); const { picture, onSize } = this.props; if (picture !== prevProps.picture) { assertSkiaViewApi(); @@ -78,16 +79,18 @@ export class SkiaPictureView extends React.Component { */ public redraw() { assertSkiaViewApi(); + console.log("Request redraw: ", this._nativeId); SkiaViewApi.requestRedraw(this._nativeId); } render() { - const { mode, debug = false, ...viewProps } = this.props; + const { mode, debug = false, opaque = false, ...viewProps } = this.props; return ( ); diff --git a/packages/skia/src/views/types.ts b/packages/skia/src/views/types.ts index 0d53e1f8bb..fb90840a79 100644 --- a/packages/skia/src/views/types.ts +++ b/packages/skia/src/views/types.ts @@ -6,6 +6,7 @@ import type { SharedValueType } from "../renderer/processors/Animations/Animatio export type NativeSkiaViewProps = ViewProps & { debug?: boolean; + opaque?: boolean; }; export interface DrawingInfo { @@ -32,6 +33,8 @@ export interface SkiaBaseViewProps extends ViewProps { * the Skia view is resized. */ onSize?: SharedValueType; + + opaque?: boolean; } export interface SkiaPictureViewNativeProps extends SkiaBaseViewProps {