@shopify/react-native-skia 0.1.220 → 0.1.222

Sign up to get free protection for your applications and to get access to all the features.
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