@shopify/react-native-skia 0.1.220 → 0.1.222

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/android/cpp/rnskia-android/RNSkAndroidView.h +3 -0
  2. package/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp +1 -0
  3. package/android/cpp/rnskia-android/SkiaOpenGLHelper.h +0 -1
  4. package/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.h +46 -6
  5. package/android/src/main/java/com/shopify/reactnative/skia/PlatformContext.java +3 -3
  6. package/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseView.java +8 -14
  7. package/android/src/main/java/com/shopify/reactnative/skia/ViewScreenshotService.java +93 -105
  8. package/cpp/api/JsiSkCanvas.h +38 -1
  9. package/cpp/api/JsiSkImage.h +66 -1
  10. package/cpp/api/JsiSkImageInfo.h +19 -0
  11. package/cpp/utils/RNSkLog.h +3 -3
  12. package/cpp/utils/RNSkTypedArray.h +41 -0
  13. package/lib/commonjs/skia/types/Canvas.d.ts +9 -1
  14. package/lib/commonjs/skia/types/Canvas.js.map +1 -1
  15. package/lib/commonjs/skia/types/Image/Image.d.ts +13 -0
  16. package/lib/commonjs/skia/types/Image/Image.js.map +1 -1
  17. package/lib/commonjs/skia/types/Image/ImageFactory.d.ts +14 -11
  18. package/lib/commonjs/skia/types/Image/ImageFactory.js +14 -11
  19. package/lib/commonjs/skia/types/Image/ImageFactory.js.map +1 -1
  20. package/lib/commonjs/skia/web/Host.d.ts +2 -1
  21. package/lib/commonjs/skia/web/Host.js +10 -1
  22. package/lib/commonjs/skia/web/Host.js.map +1 -1
  23. package/lib/commonjs/skia/web/JsiSkCanvas.d.ts +2 -1
  24. package/lib/commonjs/skia/web/JsiSkCanvas.js +11 -0
  25. package/lib/commonjs/skia/web/JsiSkCanvas.js.map +1 -1
  26. package/lib/commonjs/skia/web/JsiSkFontMgrFactory.js +1 -3
  27. package/lib/commonjs/skia/web/JsiSkFontMgrFactory.js.map +1 -1
  28. package/lib/commonjs/skia/web/JsiSkImage.d.ts +3 -1
  29. package/lib/commonjs/skia/web/JsiSkImage.js +22 -0
  30. package/lib/commonjs/skia/web/JsiSkImage.js.map +1 -1
  31. package/lib/commonjs/skia/web/JsiSkTypefaceFontProvider.d.ts +2 -2
  32. package/lib/commonjs/skia/web/JsiSkTypefaceFontProvider.js +0 -4
  33. package/lib/commonjs/skia/web/JsiSkTypefaceFontProvider.js.map +1 -1
  34. package/lib/module/skia/types/Canvas.d.ts +9 -1
  35. package/lib/module/skia/types/Canvas.js.map +1 -1
  36. package/lib/module/skia/types/Image/Image.d.ts +13 -0
  37. package/lib/module/skia/types/Image/Image.js.map +1 -1
  38. package/lib/module/skia/types/Image/ImageFactory.d.ts +14 -11
  39. package/lib/module/skia/types/Image/ImageFactory.js +14 -11
  40. package/lib/module/skia/types/Image/ImageFactory.js.map +1 -1
  41. package/lib/module/skia/web/Host.d.ts +2 -1
  42. package/lib/module/skia/web/Host.js +6 -0
  43. package/lib/module/skia/web/Host.js.map +1 -1
  44. package/lib/module/skia/web/JsiSkCanvas.d.ts +2 -1
  45. package/lib/module/skia/web/JsiSkCanvas.js +12 -1
  46. package/lib/module/skia/web/JsiSkCanvas.js.map +1 -1
  47. package/lib/module/skia/web/JsiSkFontMgrFactory.js +1 -3
  48. package/lib/module/skia/web/JsiSkFontMgrFactory.js.map +1 -1
  49. package/lib/module/skia/web/JsiSkImage.d.ts +3 -1
  50. package/lib/module/skia/web/JsiSkImage.js +25 -1
  51. package/lib/module/skia/web/JsiSkImage.js.map +1 -1
  52. package/lib/module/skia/web/JsiSkTypefaceFontProvider.d.ts +2 -2
  53. package/lib/module/skia/web/JsiSkTypefaceFontProvider.js +0 -4
  54. package/lib/module/skia/web/JsiSkTypefaceFontProvider.js.map +1 -1
  55. package/lib/typescript/src/skia/types/Canvas.d.ts +9 -1
  56. package/lib/typescript/src/skia/types/Image/Image.d.ts +13 -0
  57. package/lib/typescript/src/skia/types/Image/ImageFactory.d.ts +14 -11
  58. package/lib/typescript/src/skia/web/Host.d.ts +2 -1
  59. package/lib/typescript/src/skia/web/JsiSkCanvas.d.ts +2 -1
  60. package/lib/typescript/src/skia/web/JsiSkImage.d.ts +3 -1
  61. package/lib/typescript/src/skia/web/JsiSkTypefaceFontProvider.d.ts +2 -2
  62. package/package.json +2 -2
  63. package/src/skia/types/Canvas.ts +14 -1
  64. package/src/skia/types/Image/Image.ts +20 -0
  65. package/src/skia/types/Image/ImageFactory.ts +30 -25
  66. package/src/skia/web/Host.ts +3 -1
  67. package/src/skia/web/JsiSkCanvas.ts +13 -1
  68. package/src/skia/web/JsiSkFontMgrFactory.ts +0 -2
  69. package/src/skia/web/JsiSkImage.ts +36 -3
  70. package/src/skia/web/JsiSkTypefaceFontProvider.ts +0 -4
@@ -56,6 +56,9 @@ public:
56
56
  void surfaceSizeChanged(int width, int height) override {
57
57
  std::static_pointer_cast<RNSkOpenGLCanvasProvider>(T::getCanvasProvider())
58
58
  ->surfaceSizeChanged(width, height);
59
+ // This is only need for the first time to frame, this renderImmediate call
60
+ // will invoke updateTexImage for the previous frame
61
+ RNSkView::renderImmediate();
59
62
  }
60
63
 
61
64
  float getPixelDensity() override {
@@ -39,6 +39,7 @@ bool RNSkOpenGLCanvasProvider::renderToCanvas(
39
39
  if (!_surfaceHolder->makeCurrent()) {
40
40
  return false;
41
41
  }
42
+ _surfaceHolder->updateTexImage();
42
43
 
43
44
  // Draw into canvas using callback
44
45
  cb(surface->getCanvas());
@@ -187,7 +187,6 @@ public:
187
187
  RNSkLogger::logToConsole("eglMakeCurrent failed: %d\n", eglGetError());
188
188
  return false;
189
189
  }
190
- return true;
191
190
  }
192
191
  return true;
193
192
  }
@@ -6,6 +6,8 @@
6
6
  #include <jni.h>
7
7
 
8
8
  #include <android/native_window_jni.h>
9
+ #include <android/surface_texture.h>
10
+ #include <android/surface_texture_jni.h>
9
11
  #include <condition_variable>
10
12
  #include <memory>
11
13
  #include <thread>
@@ -42,12 +44,34 @@ public:
42
44
  */
43
45
  class WindowSurfaceHolder {
44
46
  public:
45
- WindowSurfaceHolder(jobject surface, int width, int height)
46
- : _width(width), _height(height),
47
- _window(ANativeWindow_fromSurface(facebook::jni::Environment::current(),
48
- surface)) {}
47
+ WindowSurfaceHolder(jobject jSurfaceTexture, int width, int height)
48
+ : _width(width), _height(height) {
49
+ JNIEnv *env = facebook::jni::Environment::current();
50
+ _jSurfaceTexture = env->NewGlobalRef(jSurfaceTexture);
51
+ jclass surfaceClass = env->FindClass("android/view/Surface");
52
+ jmethodID surfaceConstructor = env->GetMethodID(
53
+ surfaceClass, "<init>", "(Landroid/graphics/SurfaceTexture;)V");
54
+ // Create a new Surface instance
55
+ jobject jSurface =
56
+ env->NewObject(surfaceClass, surfaceConstructor, jSurfaceTexture);
57
+
58
+ jclass surfaceTextureClass = env->GetObjectClass(_jSurfaceTexture);
59
+ _updateTexImageMethod =
60
+ env->GetMethodID(surfaceTextureClass, "updateTexImage", "()V");
61
+
62
+ // Acquire the native window from the Surface
63
+ _window = ANativeWindow_fromSurface(env, jSurface);
64
+ // Clean up local references
65
+ env->DeleteLocalRef(jSurface);
66
+ env->DeleteLocalRef(surfaceClass);
67
+ env->DeleteLocalRef(surfaceTextureClass);
68
+ }
49
69
 
50
- ~WindowSurfaceHolder() { ANativeWindow_release(_window); }
70
+ ~WindowSurfaceHolder() {
71
+ JNIEnv *env = facebook::jni::Environment::current();
72
+ env->DeleteGlobalRef(_jSurfaceTexture);
73
+ ANativeWindow_release(_window);
74
+ }
51
75
 
52
76
  int getWidth() { return _width; }
53
77
  int getHeight() { return _height; }
@@ -57,6 +81,20 @@ public:
57
81
  */
58
82
  sk_sp<SkSurface> getSurface();
59
83
 
84
+ void updateTexImage() {
85
+ JNIEnv *env = facebook::jni::Environment::current();
86
+
87
+ // Call updateTexImage on the SurfaceTexture object
88
+ env->CallVoidMethod(_jSurfaceTexture, _updateTexImageMethod);
89
+
90
+ // Check for exceptions
91
+ if (env->ExceptionCheck()) {
92
+ RNSkLogger::logToConsole(
93
+ "updateTexImage() failed. The exception above can safely be ignored");
94
+ env->ExceptionClear();
95
+ }
96
+ }
97
+
60
98
  /**
61
99
  * Resizes the surface
62
100
  * @param width
@@ -92,9 +130,11 @@ public:
92
130
  }
93
131
 
94
132
  private:
95
- ANativeWindow *_window = nullptr;
133
+ ANativeWindow *_window;
96
134
  sk_sp<SkSurface> _skSurface = nullptr;
135
+ jobject _jSurfaceTexture = nullptr;
97
136
  EGLSurface _glSurface = EGL_NO_SURFACE;
137
+ jmethodID _updateTexImageMethod = nullptr;
98
138
  int _width = 0;
99
139
  int _height = 0;
100
140
  };
@@ -52,13 +52,13 @@ public class PlatformContext {
52
52
  Choreographer.FrameCallback frameCallback = new Choreographer.FrameCallback() {
53
53
  @Override
54
54
  public void doFrame(long frameTimeNanos) {
55
+ if (_drawLoopActive) {
56
+ Choreographer.getInstance().postFrameCallback(this);
57
+ }
55
58
  if (_isPaused) {
56
59
  return;
57
60
  }
58
61
  notifyDrawLoop();
59
- if (_drawLoopActive) {
60
- postFrameLoop();
61
- }
62
62
  }
63
63
  };
64
64
  Choreographer.getInstance().postFrameCallback(frameCallback);
@@ -4,16 +4,11 @@ import android.content.Context;
4
4
  import android.graphics.SurfaceTexture;
5
5
  import android.util.Log;
6
6
  import android.view.MotionEvent;
7
- import android.view.Surface;
8
7
  import android.view.TextureView;
9
8
 
10
- import com.facebook.jni.annotations.DoNotStrip;
11
9
  import com.facebook.react.views.view.ReactViewGroup;
12
10
 
13
11
  public abstract class SkiaBaseView extends ReactViewGroup implements TextureView.SurfaceTextureListener {
14
-
15
- @DoNotStrip
16
- private Surface mSurface;
17
12
  private TextureView mTexture;
18
13
 
19
14
  private String tag = "SkiaView";
@@ -30,12 +25,8 @@ public abstract class SkiaBaseView extends ReactViewGroup implements TextureView
30
25
  }
31
26
 
32
27
  public void destroySurface() {
33
- if (mSurface != null) {
34
- Log.i(tag, "destroySurface");
35
- surfaceDestroyed();
36
- mSurface.release();
37
- mSurface = null;
38
- }
28
+ Log.i(tag, "destroySurface");
29
+ surfaceDestroyed();
39
30
  }
40
31
 
41
32
  private void createSurfaceTexture() {
@@ -138,8 +129,7 @@ public abstract class SkiaBaseView extends ReactViewGroup implements TextureView
138
129
  @Override
139
130
  public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
140
131
  Log.i(tag, "onSurfaceTextureAvailable " + width + "/" + height);
141
- mSurface = new Surface(surface);
142
- surfaceAvailable(mSurface, width, height);
132
+ surfaceAvailable(surface, width, height);
143
133
  }
144
134
 
145
135
  @Override
@@ -153,6 +143,10 @@ public abstract class SkiaBaseView extends ReactViewGroup implements TextureView
153
143
  Log.i(tag, "onSurfaceTextureDestroyed");
154
144
  // https://developer.android.com/reference/android/view/TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed(android.graphics.SurfaceTexture)
155
145
  destroySurface();
146
+ // Because of React Native Screens (which dettach the view), we always keep the surface alive.
147
+ // If not, Texture view will recreate the texture surface by itself and
148
+ // we will lose the fast first time to frame.
149
+ // We only delete the surface when the view is dropped (destroySurface invoked by SkiaBaseViewManager);
156
150
  createSurfaceTexture();
157
151
  return false;
158
152
  }
@@ -181,4 +175,4 @@ public abstract class SkiaBaseView extends ReactViewGroup implements TextureView
181
175
  protected abstract void registerView(int nativeId);
182
176
 
183
177
  protected abstract void unregisterView();
184
- }
178
+ }
@@ -1,11 +1,10 @@
1
1
  package com.shopify.reactnative.skia;
2
2
 
3
- import static android.view.View.VISIBLE;
4
-
5
3
  import android.graphics.Bitmap;
6
4
  import android.graphics.Canvas;
7
5
  import android.graphics.Matrix;
8
6
  import android.graphics.Paint;
7
+ import android.graphics.drawable.Drawable;
9
8
  import android.os.Build;
10
9
  import android.os.Handler;
11
10
  import android.os.Looper;
@@ -15,14 +14,12 @@ import android.view.SurfaceView;
15
14
  import android.view.TextureView;
16
15
  import android.view.View;
17
16
  import android.view.ViewGroup;
18
-
19
17
  import androidx.annotation.NonNull;
20
-
21
18
  import com.facebook.react.bridge.ReactContext;
22
19
  import com.facebook.react.uimanager.UIManagerModule;
20
+ import com.facebook.react.views.view.ReactViewGroup;
23
21
 
24
- import java.util.ArrayList;
25
- import java.util.List;
22
+ import java.lang.reflect.Method;
26
23
  import java.util.concurrent.CountDownLatch;
27
24
  import java.util.concurrent.TimeUnit;
28
25
 
@@ -42,143 +39,134 @@ public class ViewScreenshotService {
42
39
  return null;
43
40
  }
44
41
 
45
- // Measure and get size of view
46
42
  int width = view.getWidth();
47
43
  int height = view.getHeight();
48
-
49
44
  if (width <= 0 || height <= 0) {
50
45
  return null;
51
46
  }
52
47
 
53
- // The following code is taken from react-native-view-shot to be able to handle and
54
- // correctly render all kinds of views, also including TextureViews and SurfaceViews
55
48
  Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
49
+ final Canvas canvas = new Canvas(bitmap);
50
+ final Paint paint = createPaint();
51
+
52
+ canvas.save();
53
+ canvas.translate(-view.getLeft(), -view.getTop());
54
+ renderViewToCanvas(canvas, view, paint, 1.0f); // Initial opacity
55
+ canvas.restore();
56
+
57
+ return bitmap;
58
+ }
56
59
 
60
+ private static Paint createPaint() {
57
61
  final Paint paint = new Paint();
58
62
  paint.setAntiAlias(true);
59
63
  paint.setFilterBitmap(true);
60
64
  paint.setDither(true);
61
-
62
- // Render the main view and its children
63
- final Canvas canvas = new Canvas(bitmap);
64
-
65
- // Renders view with child views to canvas
66
- renderViewToCanvas(canvas, view, paint);
67
-
68
- return bitmap;
65
+ return paint;
69
66
  }
70
67
 
71
- private static void renderViewToCanvas(Canvas canvas, View view, Paint paint) {
72
- // Apply transformations for the current view
68
+ private static void renderViewToCanvas(Canvas canvas, View view, Paint paint, float parentOpacity) {
69
+ float combinedOpacity = parentOpacity * view.getAlpha();
73
70
  canvas.save();
74
71
  applyTransformations(canvas, view);
75
72
 
76
- // Draw children if the view has children
77
- if ((view instanceof ViewGroup)) {
78
- // Draw children
73
+ if (view instanceof ViewGroup) {
79
74
  ViewGroup group = (ViewGroup) view;
75
+ drawBackgroundIfPresent(canvas, view, combinedOpacity);
76
+ drawChildren(canvas, group, paint, combinedOpacity);
77
+ } else {
78
+ drawView(canvas, view, paint, combinedOpacity);
79
+ }
80
80
 
81
- // Hide visible children - this needs to be done because view.draw(canvas)
82
- // will render all visible non-texture/surface views directly - causing
83
- // views to be rendered twice - once by view.draw() and once when we
84
- // enumerate children. We therefore need to turn off rendering of visible
85
- // children before we call view.draw:
86
- List<View> visibleChildren = new ArrayList<>();
87
- for (int i = 0; i < group.getChildCount(); i++) {
88
- View child = group.getChildAt(i);
89
- if (child.getVisibility() == VISIBLE) {
90
- visibleChildren.add(child);
91
- child.setVisibility(View.INVISIBLE);
92
- }
93
- }
81
+ canvas.restore();
82
+ }
94
83
 
95
- // Draw ourselves
96
- canvas.saveLayerAlpha(null, Math.round(view.getAlpha() * 255));
97
- view.draw(canvas);
84
+ private static void drawBackgroundIfPresent(Canvas canvas, View view, float opacity) {
85
+ Drawable bg = view.getBackground();
86
+ if (bg != null) {
87
+ canvas.saveLayerAlpha(null, Math.round(opacity * 255));
88
+ bg.draw(canvas);
98
89
  canvas.restore();
90
+ }
91
+ }
99
92
 
100
- // Enable children again
101
- for (int i = 0; i < visibleChildren.size(); i++) {
102
- View child = visibleChildren.get(i);
103
- child.setVisibility(VISIBLE);
93
+ private static void drawChildren(Canvas canvas, ViewGroup group, Paint paint, float parentOpacity) {
94
+ // Handle clipping for ReactViewGroup
95
+ if (group instanceof ReactViewGroup) {
96
+ try {
97
+ Class[] cArg = new Class[1];
98
+ cArg[0] = Canvas.class;
99
+ Method method = ReactViewGroup.class.getDeclaredMethod("dispatchOverflowDraw", cArg);
100
+ method.setAccessible(true);
101
+ method.invoke(group, canvas);
102
+ } catch (Exception e) {
103
+ Log.e(TAG, "couldn't invoke dispatchOverflowDraw() on ReactViewGroup", e);
104
104
  }
105
+ }
106
+ for (int i = 0; i < group.getChildCount(); i++) {
107
+ View child = group.getChildAt(i);
108
+ if (child.getVisibility() != View.VISIBLE) continue;
109
+
110
+ if (child instanceof TextureView) {
111
+ drawTextureView(canvas, (TextureView) child, paint, parentOpacity);
112
+ } else if (child instanceof SurfaceView) {
113
+ drawSurfaceView(canvas, (SurfaceView) child, paint, parentOpacity);
114
+ } else {
115
+ renderViewToCanvas(canvas, child, paint, parentOpacity);
116
+ }
117
+ }
118
+ }
105
119
 
106
- // Draw children
107
- for (int i = 0; i < group.getChildCount(); i++) {
108
- View child = group.getChildAt(i);
109
-
110
- // skip all invisible to user child views
111
- if (child.getVisibility() != VISIBLE) continue;
120
+ private static void drawView(Canvas canvas, View view, Paint paint, float opacity) {
121
+ canvas.saveLayerAlpha(null, Math.round(opacity * 255));
122
+ view.draw(canvas);
123
+ canvas.restore();
124
+ }
112
125
 
113
- // skip any child that we don't know how to process
114
- if (child instanceof TextureView) {
115
- final TextureView tvChild = (TextureView) child;
116
- tvChild.setOpaque(false); // <-- switch off background fill
126
+ private static void drawTextureView(Canvas canvas, TextureView tv, Paint paint, float opacity) {
127
+ tv.setOpaque(false);
128
+ Bitmap childBitmapBuffer = tv.getBitmap(Bitmap.createBitmap(tv.getWidth(), tv.getHeight(), Bitmap.Config.ARGB_8888));
129
+ canvas.save();
130
+ applyTransformations(canvas, tv);
131
+ paint.setAlpha(Math.round(opacity * 255)); // Set paint alpha based on opacity
132
+ canvas.drawBitmap(childBitmapBuffer, 0, 0, paint);
133
+ canvas.restore();
134
+ }
117
135
 
136
+ private static void drawSurfaceView(Canvas canvas, SurfaceView sv, Paint paint, float opacity) {
137
+ final CountDownLatch latch = new CountDownLatch(1);
138
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
139
+ Bitmap childBitmapBuffer = Bitmap.createBitmap(sv.getWidth(), sv.getHeight(), Bitmap.Config.ARGB_8888);
140
+ try {
141
+ PixelCopy.request(sv, childBitmapBuffer, copyResult -> {
118
142
  canvas.save();
119
- applyTransformations(canvas, child);
120
-
121
- // TextureView should use bitmaps with matching size,
122
- // otherwise content of the TextureView will be scaled to provided bitmap dimensions
123
- final Bitmap childBitmapBuffer = tvChild.getBitmap(Bitmap.createBitmap(child.getWidth(), child.getHeight(), Bitmap.Config.ARGB_8888));
143
+ applyTransformations(canvas, sv);
144
+ paint.setAlpha(Math.round(opacity * 255)); // Set paint alpha based on opacity
124
145
  canvas.drawBitmap(childBitmapBuffer, 0, 0, paint);
125
-
126
146
  canvas.restore();
127
-
128
- } else if (child instanceof SurfaceView) {
129
- final SurfaceView svChild = (SurfaceView) child;
130
- final CountDownLatch latch = new CountDownLatch(1);
131
-
132
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
133
- final Bitmap childBitmapBuffer = Bitmap.createBitmap(child.getWidth(), child.getHeight(), Bitmap.Config.ARGB_8888);
134
- try {
135
- PixelCopy.request(svChild, childBitmapBuffer, copyResult -> {
136
- canvas.save();
137
- applyTransformations(canvas, child);
138
- canvas.drawBitmap(childBitmapBuffer, 0, 0, paint);
139
- canvas.restore();
140
- latch.countDown();
141
- }, new Handler(Looper.getMainLooper()));
142
- latch.await(SURFACE_VIEW_READ_PIXELS_TIMEOUT, TimeUnit.SECONDS);
143
- } catch (Exception e) {
144
- Log.e(TAG, "Cannot PixelCopy for " + svChild, e);
145
- }
146
- } else {
147
- Bitmap cache = svChild.getDrawingCache();
148
- if (cache != null) {
149
- canvas.save();
150
- applyTransformations(canvas, child);
151
- canvas.drawBitmap(svChild.getDrawingCache(), 0, 0, paint);
152
- canvas.restore();
153
- }
154
- }
155
- } else {
156
- // Regular views needs to be rendered again to ensure correct z-index
157
- // order with texture views and surface views.
158
- renderViewToCanvas(canvas, child, paint);
159
- }
147
+ latch.countDown();
148
+ }, new Handler(Looper.getMainLooper()));
149
+ latch.await(SURFACE_VIEW_READ_PIXELS_TIMEOUT, TimeUnit.SECONDS);
150
+ } catch (Exception e) {
151
+ Log.e(TAG, "Cannot PixelCopy for " + sv, e);
160
152
  }
161
153
  } else {
162
- // Draw ourselves
163
- view.draw(canvas);
154
+ Bitmap cache = sv.getDrawingCache();
155
+ if (cache != null) {
156
+ canvas.save();
157
+ applyTransformations(canvas, sv);
158
+ paint.setAlpha(Math.round(opacity * 255)); // Set paint alpha based on opacity
159
+ canvas.drawBitmap(cache, 0, 0, paint);
160
+ canvas.restore();
161
+ }
164
162
  }
165
-
166
- // Restore canvas
167
- canvas.restore();
168
163
  }
169
164
 
170
165
  @NonNull
171
166
  private static void applyTransformations(final Canvas c, @NonNull final View view) {
172
- // Get the transformation matrix of the view
173
167
  final Matrix matrix = view.getMatrix();
174
-
175
- // Create a new matrix for translation
176
168
  final Matrix translateMatrix = new Matrix();
177
- final float dx = view.getLeft() + view.getPaddingLeft();
178
- final float dy = view.getTop() + view.getPaddingTop();
179
- translateMatrix.setTranslate(dx, dy);
180
-
181
- // Pre-concatenate the current matrix of the canvas with the translation and transformation matrices of the view
169
+ translateMatrix.setTranslate(view.getLeft() + view.getPaddingLeft(), view.getTop() + view.getPaddingTop());
182
170
  c.concat(translateMatrix);
183
171
  c.concat(matrix);
184
172
  }
@@ -7,6 +7,7 @@
7
7
  #include "JsiSkFont.h"
8
8
  #include "JsiSkHostObjects.h"
9
9
  #include "JsiSkImage.h"
10
+ #include "JsiSkImageInfo.h"
10
11
  #include "JsiSkMatrix.h"
11
12
  #include "JsiSkPaint.h"
12
13
  #include "JsiSkPath.h"
@@ -17,6 +18,8 @@
17
18
  #include "JsiSkTextBlob.h"
18
19
  #include "JsiSkVertices.h"
19
20
 
21
+ #include "RNSkTypedArray.h"
22
+
20
23
  #include <jsi/jsi.h>
21
24
 
22
25
  #pragma clang diagnostic push
@@ -491,6 +494,39 @@ public:
491
494
  return jsi::Value::undefined();
492
495
  }
493
496
 
497
+ JSI_HOST_FUNCTION(readPixels) {
498
+ auto srcX = static_cast<int>(arguments[0].asNumber());
499
+ auto srcY = static_cast<int>(arguments[1].asNumber());
500
+ auto info = JsiSkImageInfo::fromValue(runtime, arguments[2]);
501
+ if (!info) {
502
+ return jsi::Value::null();
503
+ }
504
+ size_t bytesPerRow = 0;
505
+ if (count > 4 && !arguments[4].isUndefined()) {
506
+ bytesPerRow = static_cast<size_t>(arguments[4].asNumber());
507
+ } else {
508
+ bytesPerRow = info->minRowBytes();
509
+ }
510
+ auto dest =
511
+ count > 3
512
+ ? RNSkTypedArray::getTypedArray(runtime, arguments[3], *info)
513
+ : RNSkTypedArray::getTypedArray(runtime, jsi::Value::null(), *info);
514
+ if (!dest.isObject()) {
515
+ return jsi::Value::null();
516
+ }
517
+ jsi::ArrayBuffer buffer =
518
+ dest.asObject(runtime)
519
+ .getProperty(runtime, jsi::PropNameID::forAscii(runtime, "buffer"))
520
+ .asObject(runtime)
521
+ .getArrayBuffer(runtime);
522
+ auto bfrPtr = reinterpret_cast<void *>(buffer.data(runtime));
523
+
524
+ if (!_canvas->readPixels(*info, bfrPtr, bytesPerRow, srcX, srcY)) {
525
+ return jsi::Value::null();
526
+ }
527
+ return std::move(dest);
528
+ }
529
+
494
530
  JSI_EXPORT_FUNCTIONS(JSI_EXPORT_FUNC(JsiSkCanvas, drawPaint),
495
531
  JSI_EXPORT_FUNC(JsiSkCanvas, drawLine),
496
532
  JSI_EXPORT_FUNC(JsiSkCanvas, drawRect),
@@ -529,7 +565,8 @@ public:
529
565
  JSI_EXPORT_FUNC(JsiSkCanvas, drawColor),
530
566
  JSI_EXPORT_FUNC(JsiSkCanvas, clear),
531
567
  JSI_EXPORT_FUNC(JsiSkCanvas, concat),
532
- JSI_EXPORT_FUNC(JsiSkCanvas, drawPicture))
568
+ JSI_EXPORT_FUNC(JsiSkCanvas, drawPicture),
569
+ JSI_EXPORT_FUNC(JsiSkCanvas, readPixels))
533
570
 
534
571
  explicit JsiSkCanvas(std::shared_ptr<RNSkPlatformContext> context)
535
572
  : JsiSkHostObject(std::move(context)) {}
@@ -5,9 +5,12 @@
5
5
  #include <utility>
6
6
 
7
7
  #include "JsiSkHostObjects.h"
8
+ #include "JsiSkImageInfo.h"
8
9
  #include "JsiSkMatrix.h"
9
10
  #include "JsiSkShader.h"
10
11
 
12
+ #include "RNSkTypedArray.h"
13
+
11
14
  #pragma clang diagnostic push
12
15
  #pragma clang diagnostic ignored "-Wdocumentation"
13
16
 
@@ -17,6 +20,7 @@
17
20
  #include "include/codec/SkEncodedImageFormat.h"
18
21
  #include "include/encode/SkJpegEncoder.h"
19
22
  #include "include/encode/SkPngEncoder.h"
23
+ #include "include/encode/SkWebpEncoder.h"
20
24
 
21
25
  #pragma clang diagnostic pop
22
26
 
@@ -34,6 +38,11 @@ public:
34
38
  return static_cast<double>(getObject()->height());
35
39
  }
36
40
 
41
+ JSI_HOST_FUNCTION(getImageInfo) {
42
+ return JsiSkImageInfo::toValue(runtime, getContext(),
43
+ getObject()->imageInfo());
44
+ }
45
+
37
46
  JSI_HOST_FUNCTION(makeShaderOptions) {
38
47
  auto tmx = (SkTileMode)arguments[0].asNumber();
39
48
  auto tmy = (SkTileMode)arguments[1].asNumber();
@@ -70,20 +79,34 @@ public:
70
79
  count >= 1 ? static_cast<SkEncodedImageFormat>(arguments[0].asNumber())
71
80
  : SkEncodedImageFormat::kPNG;
72
81
 
73
- auto quality = count == 2 ? arguments[1].asNumber() : 100.0;
82
+ auto quality = (count >= 2 && arguments[1].isNumber())
83
+ ? arguments[1].asNumber()
84
+ : 100.0;
74
85
  auto image = getObject();
75
86
  if (image->isTextureBacked()) {
76
87
  image = image->makeNonTextureImage();
77
88
  }
78
89
  sk_sp<SkData> data;
90
+
79
91
  if (format == SkEncodedImageFormat::kJPEG) {
80
92
  SkJpegEncoder::Options options;
81
93
  options.fQuality = quality;
82
94
  data = SkJpegEncoder::Encode(nullptr, image.get(), options);
95
+ } else if (format == SkEncodedImageFormat::kWEBP) {
96
+ SkWebpEncoder::Options options;
97
+ if (quality >= 100) {
98
+ options.fCompression = SkWebpEncoder::Compression::kLossless;
99
+ options.fQuality = 75; // This is effort to compress
100
+ } else {
101
+ options.fCompression = SkWebpEncoder::Compression::kLossy;
102
+ options.fQuality = quality;
103
+ }
104
+ data = SkWebpEncoder::Encode(nullptr, image.get(), options);
83
105
  } else {
84
106
  SkPngEncoder::Options options;
85
107
  data = SkPngEncoder::Encode(nullptr, image.get(), options);
86
108
  }
109
+
87
110
  return data;
88
111
  }
89
112
 
@@ -117,6 +140,46 @@ public:
117
140
  return jsi::String::createFromAscii(runtime, buffer);
118
141
  }
119
142
 
143
+ JSI_HOST_FUNCTION(readPixels) {
144
+ int srcX = 0;
145
+ int srcY = 0;
146
+ if (count > 0 && !arguments[0].isUndefined()) {
147
+ srcX = static_cast<int>(arguments[0].asNumber());
148
+ }
149
+ if (count > 1 && !arguments[1].isUndefined()) {
150
+ srcY = static_cast<int>(arguments[1].asNumber());
151
+ }
152
+ SkImageInfo info =
153
+ (count > 2 && !arguments[2].isUndefined())
154
+ ? *JsiSkImageInfo::fromValue(runtime, arguments[2])
155
+ : SkImageInfo::MakeN32(getObject()->width(), getObject()->height(),
156
+ getObject()->imageInfo().alphaType());
157
+ size_t bytesPerRow = 0;
158
+ if (count > 4 && !arguments[4].isUndefined()) {
159
+ bytesPerRow = static_cast<size_t>(arguments[4].asNumber());
160
+ } else {
161
+ bytesPerRow = info.minRowBytes();
162
+ }
163
+ auto dest =
164
+ count > 3
165
+ ? RNSkTypedArray::getTypedArray(runtime, arguments[3], info)
166
+ : RNSkTypedArray::getTypedArray(runtime, jsi::Value::null(), info);
167
+ if (!dest.isObject()) {
168
+ return jsi::Value::null();
169
+ }
170
+ jsi::ArrayBuffer buffer =
171
+ dest.asObject(runtime)
172
+ .getProperty(runtime, jsi::PropNameID::forAscii(runtime, "buffer"))
173
+ .asObject(runtime)
174
+ .getArrayBuffer(runtime);
175
+ auto bfrPtr = reinterpret_cast<void *>(buffer.data(runtime));
176
+
177
+ if (!getObject()->readPixels(info, bfrPtr, bytesPerRow, srcX, srcY)) {
178
+ return jsi::Value::null();
179
+ }
180
+ return std::move(dest);
181
+ }
182
+
120
183
  JSI_HOST_FUNCTION(makeNonTextureImage) {
121
184
  auto image = getObject()->makeNonTextureImage();
122
185
  return jsi::Object::createFromHostObject(
@@ -127,10 +190,12 @@ public:
127
190
 
128
191
  JSI_EXPORT_FUNCTIONS(JSI_EXPORT_FUNC(JsiSkImage, width),
129
192
  JSI_EXPORT_FUNC(JsiSkImage, height),
193
+ JSI_EXPORT_FUNC(JsiSkImage, getImageInfo),
130
194
  JSI_EXPORT_FUNC(JsiSkImage, makeShaderOptions),
131
195
  JSI_EXPORT_FUNC(JsiSkImage, makeShaderCubic),
132
196
  JSI_EXPORT_FUNC(JsiSkImage, encodeToBytes),
133
197
  JSI_EXPORT_FUNC(JsiSkImage, encodeToBase64),
198
+ JSI_EXPORT_FUNC(JsiSkImage, readPixels),
134
199
  JSI_EXPORT_FUNC(JsiSkImage, makeNonTextureImage),
135
200
  JSI_EXPORT_FUNC(JsiSkImage, dispose))
136
201
 
@@ -56,5 +56,24 @@ public:
56
56
  runtime,
57
57
  std::make_shared<JsiSkImageInfo>(std::move(context), imageInfo));
58
58
  }
59
+
60
+ JSI_PROPERTY_GET(width) { return static_cast<double>(getObject()->width()); }
61
+ JSI_PROPERTY_GET(height) {
62
+ return static_cast<double>(getObject()->height());
63
+ }
64
+ JSI_PROPERTY_GET(colorType) {
65
+ return static_cast<double>(getObject()->colorType());
66
+ }
67
+ JSI_PROPERTY_GET(alphaType) {
68
+ return static_cast<double>(getObject()->alphaType());
69
+ }
70
+
71
+ JSI_API_TYPENAME(ImageInfo);
72
+
73
+ JSI_EXPORT_PROPERTY_GETTERS(JSI_EXPORT_PROP_GET(JsiSkImageInfo, width),
74
+ JSI_EXPORT_PROP_GET(JsiSkImageInfo, height),
75
+ JSI_EXPORT_PROP_GET(JsiSkImageInfo, colorType),
76
+ JSI_EXPORT_PROP_GET(JsiSkImageInfo, alphaType),
77
+ JSI_EXPORT_PROP_GET(JsiSkImageInfo, __typename__))
59
78
  };
60
79
  } // namespace RNSkia