@shopify/react-native-skia 2.0.1 → 2.0.3

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 (92) hide show
  1. package/android/CMakeLists.txt +47 -21
  2. package/android/cpp/rnskia-android/RNSkAndroidPlatformContext.h +8 -5
  3. package/apple/MetalWindowContext.mm +4 -2
  4. package/apple/RNSkApplePlatformContext.h +7 -5
  5. package/apple/RNSkApplePlatformContext.mm +30 -29
  6. package/apple/SkiaCVPixelBufferUtils.mm +4 -8
  7. package/apple/SkiaManager.mm +0 -3
  8. package/cpp/api/JsiSkImageFactory.h +14 -1
  9. package/cpp/api/JsiSkSurface.h +7 -1
  10. package/cpp/api/recorder/DrawingCtx.h +19 -2
  11. package/cpp/api/recorder/Paint.h +1 -3
  12. package/cpp/api/recorder/RNRecorder.h +5 -13
  13. package/cpp/rnskia/DawnContext.h +11 -2
  14. package/cpp/rnskia/DawnUtils.h +97 -6
  15. package/cpp/rnskia/DawnWindowContext.h +18 -11
  16. package/cpp/rnskia/RNSkJsiViewApi.h +91 -71
  17. package/cpp/rnskia/RNSkManager.cpp +3 -18
  18. package/cpp/rnskia/RNSkManager.h +0 -6
  19. package/cpp/rnskia/RNSkPlatformContext.h +22 -5
  20. package/lib/commonjs/skia/types/Image/ImageFactory.d.ts +2 -1
  21. package/lib/commonjs/skia/types/Image/ImageFactory.js.map +1 -1
  22. package/lib/commonjs/skia/types/Surface/Surface.d.ts +1 -1
  23. package/lib/commonjs/skia/types/Surface/Surface.js.map +1 -1
  24. package/lib/commonjs/skia/web/JsiSkImageFactory.d.ts +1 -0
  25. package/lib/commonjs/skia/web/JsiSkImageFactory.js +3 -0
  26. package/lib/commonjs/skia/web/JsiSkImageFactory.js.map +1 -1
  27. package/lib/commonjs/skia/web/JsiSkSurface.d.ts +2 -1
  28. package/lib/commonjs/skia/web/JsiSkSurface.js +4 -1
  29. package/lib/commonjs/skia/web/JsiSkSurface.js.map +1 -1
  30. package/lib/commonjs/sksg/HostConfig.js +1 -1
  31. package/lib/commonjs/sksg/HostConfig.js.map +1 -1
  32. package/lib/commonjs/sksg/Recorder/DrawingContext.d.ts +2 -0
  33. package/lib/commonjs/sksg/Recorder/DrawingContext.js +14 -2
  34. package/lib/commonjs/sksg/Recorder/DrawingContext.js.map +1 -1
  35. package/lib/commonjs/sksg/Recorder/Player.js +6 -2
  36. package/lib/commonjs/sksg/Recorder/Player.js.map +1 -1
  37. package/lib/commonjs/sksg/Recorder/commands/Paint.d.ts +3 -2
  38. package/lib/commonjs/sksg/Recorder/commands/Paint.js +5 -4
  39. package/lib/commonjs/sksg/Recorder/commands/Paint.js.map +1 -1
  40. package/lib/commonjs/views/SkiaBaseWebView.js +4 -0
  41. package/lib/commonjs/views/SkiaBaseWebView.js.map +1 -1
  42. package/lib/module/skia/types/Image/ImageFactory.d.ts +2 -1
  43. package/lib/module/skia/types/Image/ImageFactory.js.map +1 -1
  44. package/lib/module/skia/types/Surface/Surface.d.ts +1 -1
  45. package/lib/module/skia/types/Surface/Surface.js.map +1 -1
  46. package/lib/module/skia/web/JsiSkImageFactory.d.ts +1 -0
  47. package/lib/module/skia/web/JsiSkImageFactory.js +3 -0
  48. package/lib/module/skia/web/JsiSkImageFactory.js.map +1 -1
  49. package/lib/module/skia/web/JsiSkSurface.d.ts +2 -1
  50. package/lib/module/skia/web/JsiSkSurface.js +4 -1
  51. package/lib/module/skia/web/JsiSkSurface.js.map +1 -1
  52. package/lib/module/sksg/HostConfig.js +1 -1
  53. package/lib/module/sksg/HostConfig.js.map +1 -1
  54. package/lib/module/sksg/Recorder/DrawingContext.d.ts +2 -0
  55. package/lib/module/sksg/Recorder/DrawingContext.js +14 -2
  56. package/lib/module/sksg/Recorder/DrawingContext.js.map +1 -1
  57. package/lib/module/sksg/Recorder/Player.js +6 -2
  58. package/lib/module/sksg/Recorder/Player.js.map +1 -1
  59. package/lib/module/sksg/Recorder/commands/Paint.d.ts +3 -2
  60. package/lib/module/sksg/Recorder/commands/Paint.js +5 -4
  61. package/lib/module/sksg/Recorder/commands/Paint.js.map +1 -1
  62. package/lib/module/views/SkiaBaseWebView.js +4 -0
  63. package/lib/module/views/SkiaBaseWebView.js.map +1 -1
  64. package/lib/typescript/lib/commonjs/skia/web/JsiSkImageFactory.d.ts +1 -0
  65. package/lib/typescript/lib/commonjs/skia/web/JsiSkSurface.d.ts +1 -1
  66. package/lib/typescript/lib/commonjs/sksg/HostConfig.d.ts +1 -1
  67. package/lib/typescript/lib/commonjs/sksg/Recorder/DrawingContext.d.ts +2 -0
  68. package/lib/typescript/lib/commonjs/sksg/Recorder/commands/Paint.d.ts +1 -1
  69. package/lib/typescript/lib/module/skia/web/JsiSkImageFactory.d.ts +1 -0
  70. package/lib/typescript/lib/module/skia/web/JsiSkSurface.d.ts +1 -1
  71. package/lib/typescript/lib/module/sksg/HostConfig.d.ts +1 -1
  72. package/lib/typescript/lib/module/sksg/Recorder/DrawingContext.d.ts +2 -0
  73. package/lib/typescript/lib/module/sksg/Recorder/commands/Paint.d.ts +1 -1
  74. package/lib/typescript/src/skia/types/Image/ImageFactory.d.ts +2 -1
  75. package/lib/typescript/src/skia/types/Surface/Surface.d.ts +1 -1
  76. package/lib/typescript/src/skia/web/JsiSkImageFactory.d.ts +1 -0
  77. package/lib/typescript/src/skia/web/JsiSkSurface.d.ts +2 -1
  78. package/lib/typescript/src/sksg/Recorder/DrawingContext.d.ts +2 -0
  79. package/lib/typescript/src/sksg/Recorder/commands/Paint.d.ts +3 -2
  80. package/package.json +1 -1
  81. package/react-native-skia.podspec +43 -12
  82. package/src/renderer/__tests__/e2e/DataEncoding.spec.tsx +6 -2
  83. package/src/renderer/__tests__/e2e/Paint.spec.tsx +44 -1
  84. package/src/skia/types/Image/ImageFactory.ts +3 -1
  85. package/src/skia/types/Surface/Surface.ts +1 -1
  86. package/src/skia/web/JsiSkImageFactory.ts +4 -0
  87. package/src/skia/web/JsiSkSurface.ts +4 -1
  88. package/src/sksg/HostConfig.ts +2 -4
  89. package/src/sksg/Recorder/DrawingContext.ts +15 -1
  90. package/src/sksg/Recorder/Player.ts +6 -2
  91. package/src/sksg/Recorder/commands/Paint.ts +6 -5
  92. package/src/views/SkiaBaseWebView.tsx +4 -0
@@ -4,8 +4,48 @@ require "json"
4
4
 
5
5
  package = JSON.parse(File.read(File.join(__dir__, "package.json")))
6
6
 
7
- # Check for GRAPHITE env var
8
- use_graphite = ENV['SK_GRAPHITE'] == '1'
7
+ # Check if Graphite symbols are available in libskia
8
+ libskia_path = File.join(__dir__, "libs/apple/libskia.xcframework")
9
+ use_graphite = false
10
+
11
+ if File.exist?(libskia_path)
12
+ # Look for any arm64 or x86_64 framework inside the xcframework
13
+ framework_paths = Dir.glob(File.join(libskia_path, "**/libskia.framework/libskia"))
14
+
15
+ # Also try looking for static libraries if frameworks aren't found
16
+ if framework_paths.empty?
17
+ framework_paths = Dir.glob(File.join(libskia_path, "**/libskia.a"))
18
+ end
19
+
20
+ framework_paths.each do |framework_path|
21
+ if File.exist?(framework_path)
22
+ # Look for specific Dawn function symbols that indicate Graphite support
23
+ dawn_symbols = [
24
+ 'dawn::',
25
+ 'wgpu',
26
+ '_ZN4dawn',
27
+ 'DawnDevice',
28
+ 'dawn_native'
29
+ ]
30
+
31
+ dawn_symbols.each do |symbol|
32
+ nm_output = `nm "#{framework_path}" 2>/dev/null | grep "#{symbol}"`
33
+ if $?.success? && !nm_output.empty?
34
+ use_graphite = true
35
+ break
36
+ end
37
+ end
38
+
39
+ break if use_graphite
40
+ end
41
+ end
42
+ end
43
+
44
+ if use_graphite
45
+ puts "SK_GRAPHITE: ON (Graphite symbols found in libskia)"
46
+ else
47
+ puts "SK_GRAPHITE: OFF (Graphite symbols not found in libskia)"
48
+ end
9
49
 
10
50
  # Set preprocessor definitions based on GRAPHITE flag
11
51
  preprocessor_defs = use_graphite ?
@@ -20,13 +60,6 @@ base_frameworks = ['libs/apple/libskia.xcframework',
20
60
  'libs/apple/libskunicode_core.xcframework',
21
61
  'libs/apple/libskunicode_libgrapheme.xcframework',]
22
62
 
23
- # Add Graphite frameworks if enabled
24
- graphite_frameworks = [
25
- 'libs/apple/libdawn_native_static.xcframework',
26
- 'libs/apple/libdawn_platform_static.xcframework',
27
- 'libs/apple/libdawn_proc_static.xcframework'
28
- ]
29
-
30
63
  Pod::Spec.new do |s|
31
64
  s.name = "react-native-skia"
32
65
  s.version = package["version"]
@@ -54,9 +87,7 @@ Pod::Spec.new do |s|
54
87
 
55
88
  s.frameworks = ['MetalKit', 'AVFoundation', 'AVKit', 'CoreMedia']
56
89
 
57
- s.vendored_frameworks = use_graphite ?
58
- base_frameworks + graphite_frameworks :
59
- base_frameworks
90
+ s.vendored_frameworks = base_frameworks
60
91
 
61
92
  # All iOS cpp/h files
62
93
  s.source_files = [
@@ -52,7 +52,9 @@ describe("Data Encoding", () => {
52
52
  const offscreen = Skia.Surface.MakeOffscreen(1, 1)!;
53
53
  const canvas = offscreen.getCanvas();
54
54
  canvas.drawImage(img, 0, 0);
55
- return Array.from(offscreen.makeImageSnapshot().encodeToBytes());
55
+ const snapshotImage = Skia.Image.MakeNull();
56
+ offscreen.makeImageSnapshot(undefined, snapshotImage);
57
+ return Array.from(snapshotImage.encodeToBytes());
56
58
  });
57
59
  expect(result.length).toBeGreaterThan(0);
58
60
  });
@@ -68,7 +70,9 @@ describe("Data Encoding", () => {
68
70
  const offscreen = Skia.Surface.MakeOffscreen(1, 1)!;
69
71
  const canvas = offscreen.getCanvas();
70
72
  canvas.drawImage(img, 0, 0);
71
- return offscreen.makeImageSnapshot().encodeToBase64();
73
+ const snapshotImage = Skia.Image.MakeNull();
74
+ offscreen.makeImageSnapshot(undefined, snapshotImage);
75
+ return snapshotImage.encodeToBase64();
72
76
  });
73
77
  expect(result.length).toBeGreaterThan(0);
74
78
  });
@@ -10,7 +10,7 @@ import {
10
10
  Paint,
11
11
  Path,
12
12
  } from "../../components";
13
- import { checkImage } from "../../../__tests__/setup";
13
+ import { checkImage, docPath } from "../../../__tests__/setup";
14
14
  import { fitbox } from "../../components/shapes/FitBox";
15
15
 
16
16
  const blendModes = [
@@ -167,4 +167,47 @@ describe("Paint", () => {
167
167
  threshold: 0,
168
168
  });
169
169
  });
170
+ it("should override colors", async () => {
171
+ const { vec } = importSkia();
172
+ const strokeWidth = 10;
173
+ const { width, height } = surface;
174
+ const c = vec(width / 2, height / 2);
175
+ const r = (width - strokeWidth) / 2;
176
+ const result = await surface.draw(
177
+ <>
178
+ <Circle c={c} r={r} color="transparent">
179
+ <Paint color="lightblue" />
180
+ <Paint color="#adbce6" style="stroke" strokeWidth={strokeWidth} />
181
+ <Paint color="#ade6d8" style="stroke" strokeWidth={strokeWidth / 2} />
182
+ </Circle>
183
+ </>
184
+ );
185
+ checkImage(result, docPath("paint/stroke.png"));
186
+ });
187
+ it("colors don't influence opacity (1)", async () => {
188
+ const { vec } = importSkia();
189
+ const strokeWidth = 10;
190
+ const { width, height } = surface;
191
+ const c = vec(width / 2, height / 2);
192
+ const r = (width - strokeWidth) / 2;
193
+ const result = await surface.draw(
194
+ <Group color="rgba(0,0,0,0.5)">
195
+ <Circle c={c} r={r} color="lightblue" />
196
+ </Group>
197
+ );
198
+ checkImage(result, docPath("paint/opaque-circle.png"));
199
+ });
200
+ it("colors don't influence opacity (2)", async () => {
201
+ const { vec } = importSkia();
202
+ const strokeWidth = 10;
203
+ const { width, height } = surface;
204
+ const c = vec(width / 2, height / 2);
205
+ const r = (width - strokeWidth) / 2;
206
+ const result = await surface.draw(
207
+ <Group opacity={0.5}>
208
+ <Circle c={c} r={r} color="lightblue" />
209
+ </Group>
210
+ );
211
+ checkImage(result, docPath("paint/semi-transparent-circle.png"));
212
+ });
170
213
  });
@@ -21,6 +21,7 @@ export interface ImageInfo {
21
21
  }
22
22
 
23
23
  export interface ImageFactory {
24
+ MakeNull: () => SkImage;
24
25
  /**
25
26
  * Return an Image backed by the encoded data, but attempt to defer decoding until the image
26
27
  * is actually used/drawn. This deferral allows the system to cache the result, either on the
@@ -70,7 +71,8 @@ export interface ImageFactory {
70
71
  texture: unknown,
71
72
  width: number,
72
73
  height: number,
73
- mipmapped?: boolean
74
+ mipmapped?: boolean,
75
+ outputImage?: SkImage
74
76
  ) => SkImage;
75
77
 
76
78
  /**
@@ -35,7 +35,7 @@ export interface SkSurface extends SkJSIInstance<"Surface"> {
35
35
 
36
36
  example: https://fiddle.skia.org/c/@Surface_makeImageSnapshot
37
37
  */
38
- makeImageSnapshot(bounds?: SkRect): SkImage;
38
+ makeImageSnapshot(bounds?: SkRect, outputImage?: SkImage): SkImage;
39
39
 
40
40
  /**
41
41
  * Make sure any queued draws are sent to the screen or the GPU.
@@ -20,6 +20,10 @@ export class JsiSkImageFactory extends Host implements ImageFactory {
20
20
  super(CanvasKit);
21
21
  }
22
22
 
23
+ MakeNull() {
24
+ return new JsiSkImage(this.CanvasKit, null as unknown as Image);
25
+ }
26
+
23
27
  MakeImageFromViewTag(viewTag: number): Promise<SkImage | null> {
24
28
  const view = viewTag as unknown as HTMLElement;
25
29
  // TODO: Implement screenshot from view in React JS
@@ -35,12 +35,15 @@ export class JsiSkSurface
35
35
  return new JsiSkCanvas(this.CanvasKit, this.ref.getCanvas());
36
36
  }
37
37
 
38
- makeImageSnapshot(bounds?: SkRect): SkImage {
38
+ makeImageSnapshot(bounds?: SkRect, outputImage?: JsiSkImage): SkImage {
39
39
  const image = this.ref.makeImageSnapshot(
40
40
  bounds
41
41
  ? Array.from(JsiSkRect.fromValue(this.CanvasKit, bounds))
42
42
  : undefined
43
43
  );
44
+ if (outputImage) {
45
+ outputImage.ref = image;
46
+ }
44
47
  return new JsiSkImage(this.CanvasKit, image);
45
48
  }
46
49
 
@@ -177,10 +177,8 @@ export const sksgHostConfig: SkiaHostConfig = {
177
177
  _type,
178
178
  _oldProps,
179
179
  newProps,
180
- _updatePayload,
181
- _internalInstanceHandle,
182
- keepChildren: boolean,
183
- _recyclableInstance: null | Instance
180
+ keepChildren,
181
+ _newChildSet
184
182
  ) {
185
183
  debug("cloneInstance");
186
184
  return {
@@ -22,12 +22,14 @@ export const createDrawingContext = (
22
22
  const imageFilters: SkImageFilter[] = [];
23
23
  const pathEffects: SkPathEffect[] = [];
24
24
  const paintDeclarations: SkPaint[] = [];
25
+ const opacities: number[] = [];
25
26
 
26
27
  let nextPaintIndex = 1;
27
28
 
28
- // Initialize first paint
29
+ // Initialize first paint and opacity
29
30
  paintPool[0] = Skia.Paint();
30
31
  paints.push(paintPool[0]);
32
+ opacities.push(1);
31
33
 
32
34
  // Methods (formerly class methods)
33
35
  const savePaint = () => {
@@ -39,9 +41,18 @@ export const createDrawingContext = (
39
41
  const nextPaint = paintPool[nextPaintIndex];
40
42
  nextPaint.assign(getCurrentPaint()); // Reuse allocation by copying properties
41
43
  paints.push(nextPaint);
44
+ opacities.push(opacities[opacities.length - 1]);
42
45
  nextPaintIndex++;
43
46
  };
44
47
 
48
+ const getOpacity = () => {
49
+ return opacities[opacities.length - 1];
50
+ };
51
+
52
+ const setOpacity = (newOpacity: number) => {
53
+ opacities[opacities.length - 1] = Math.max(0, Math.min(1, newOpacity));
54
+ };
55
+
45
56
  const saveBackdropFilter = () => {
46
57
  let imageFilter: SkImageFilter | null = null;
47
58
  const imgf = imageFilters.pop();
@@ -63,6 +74,7 @@ export const createDrawingContext = (
63
74
  };
64
75
 
65
76
  const restorePaint = () => {
77
+ opacities.pop();
66
78
  return paints.pop();
67
79
  };
68
80
 
@@ -125,6 +137,8 @@ export const createDrawingContext = (
125
137
  }, // the "getter" for the current paint
126
138
  restorePaint,
127
139
  materializePaint,
140
+ getOpacity,
141
+ setOpacity,
128
142
  };
129
143
  };
130
144
 
@@ -67,7 +67,7 @@ function play(ctx: DrawingContext, _command: Command) {
67
67
  ctx.paints.push(command.props.paint);
68
68
  } else {
69
69
  ctx.savePaint();
70
- setPaintProperties(ctx.Skia, ctx.paint, command.props);
70
+ setPaintProperties(ctx.Skia, ctx, command.props);
71
71
  }
72
72
  } else if (isCommand(command, CommandType.RestorePaint)) {
73
73
  ctx.restorePaint();
@@ -101,7 +101,11 @@ function play(ctx: DrawingContext, _command: Command) {
101
101
  } else if (isCommand(command, CommandType.RestoreCTM)) {
102
102
  ctx.canvas.restore();
103
103
  } else {
104
- const paints = [ctx.paint, ...ctx.paintDeclarations];
104
+ // TODO: is a copy needed here?
105
+ // apply opacity to the current paint.
106
+ const paint = ctx.paint.copy();
107
+ paint.setAlphaf(paint.getAlphaf() * ctx.getOpacity());
108
+ const paints = [paint, ...ctx.paintDeclarations];
105
109
  ctx.paintDeclarations = [];
106
110
  paints.forEach((p) => {
107
111
  ctx.paints.push(p);
@@ -6,11 +6,12 @@ import {
6
6
  StrokeCap,
7
7
  StrokeJoin,
8
8
  } from "../../../skia/types";
9
- import type { SkPaint, Skia } from "../../../skia/types";
9
+ import type { Skia } from "../../../skia/types";
10
+ import type { DrawingContext } from "../DrawingContext";
10
11
 
11
12
  export const setPaintProperties = (
12
13
  Skia: Skia,
13
- paint: SkPaint,
14
+ ctx: DrawingContext,
14
15
  {
15
16
  opacity,
16
17
  color,
@@ -25,14 +26,14 @@ export const setPaintProperties = (
25
26
  }: PaintProps
26
27
  ) => {
27
28
  "worklet";
29
+ const { paint } = ctx;
30
+
28
31
  if (opacity !== undefined) {
29
- paint.setAlphaf(paint.getAlphaf() * opacity);
32
+ ctx.setOpacity(ctx.getOpacity() * opacity);
30
33
  }
31
34
  if (color !== undefined) {
32
- const currentOpacity = paint.getAlphaf();
33
35
  paint.setShader(null);
34
36
  paint.setColor(processColor(Skia, color));
35
- paint.setAlphaf(currentOpacity * paint.getAlphaf());
36
37
  }
37
38
  if (blendMode !== undefined) {
38
39
  paint.setBlendMode(BlendMode[enumKey(blendMode)]);
@@ -42,6 +42,10 @@ export abstract class SkiaBaseWebView<
42
42
  canvas.width = this.width * pd;
43
43
  canvas.height = this.height * pd;
44
44
  const surface = CanvasKit.MakeWebGLCanvasSurface(canvas);
45
+ const ctx = canvas.getContext("webgl2");
46
+ if (ctx) {
47
+ ctx.drawingBufferColorSpace = "display-p3";
48
+ }
45
49
  if (!surface) {
46
50
  throw new Error("Could not create surface");
47
51
  }