@shopify/react-native-skia 0.1.116 → 0.1.118

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 (188) hide show
  1. package/cpp/api/JsiSkApi.h +13 -9
  2. package/cpp/api/JsiSkCanvas.h +9 -1
  3. package/cpp/api/JsiSkPicture.h +71 -0
  4. package/cpp/api/JsiSkPictureFactory.h +50 -0
  5. package/cpp/api/JsiSkPictureRecorder.h +53 -0
  6. package/lib/commonjs/renderer/Canvas.js +22 -36
  7. package/lib/commonjs/renderer/Canvas.js.map +1 -1
  8. package/lib/commonjs/renderer/HostConfig.js +2 -24
  9. package/lib/commonjs/renderer/HostConfig.js.map +1 -1
  10. package/lib/commonjs/renderer/components/Drawing.js +31 -0
  11. package/lib/commonjs/renderer/components/Drawing.js.map +1 -0
  12. package/lib/commonjs/renderer/components/Group.js +1 -1
  13. package/lib/commonjs/renderer/components/Group.js.map +1 -1
  14. package/lib/commonjs/renderer/components/Picture.js +35 -0
  15. package/lib/commonjs/renderer/components/Picture.js.map +1 -0
  16. package/lib/commonjs/renderer/components/imageFilters/InnerShadow.js +5 -34
  17. package/lib/commonjs/renderer/components/imageFilters/InnerShadow.js.map +1 -1
  18. package/lib/commonjs/renderer/components/imageFilters/{DropShadow.js → Shadow.js} +19 -8
  19. package/lib/commonjs/renderer/components/imageFilters/Shadow.js.map +1 -0
  20. package/lib/commonjs/renderer/components/imageFilters/index.js +4 -17
  21. package/lib/commonjs/renderer/components/imageFilters/index.js.map +1 -1
  22. package/lib/commonjs/renderer/components/index.js +26 -0
  23. package/lib/commonjs/renderer/components/index.js.map +1 -1
  24. package/lib/commonjs/renderer/components/maskFilters/Blur.js +1 -1
  25. package/lib/commonjs/renderer/components/maskFilters/Blur.js.map +1 -1
  26. package/lib/commonjs/renderer/components/shapes/Box.js +126 -0
  27. package/lib/commonjs/renderer/components/shapes/Box.js.map +1 -0
  28. package/lib/commonjs/renderer/components/shapes/index.js +13 -0
  29. package/lib/commonjs/renderer/components/shapes/index.js.map +1 -1
  30. package/lib/commonjs/renderer/index.js +13 -0
  31. package/lib/commonjs/renderer/index.js.map +1 -1
  32. package/lib/commonjs/renderer/nodes/Declaration.js +8 -1
  33. package/lib/commonjs/renderer/nodes/Declaration.js.map +1 -1
  34. package/lib/commonjs/renderer/nodes/Drawing.js +5 -8
  35. package/lib/commonjs/renderer/nodes/Drawing.js.map +1 -1
  36. package/lib/commonjs/renderer/processors/math/Math.js +19 -1
  37. package/lib/commonjs/renderer/processors/math/Math.js.map +1 -1
  38. package/lib/commonjs/renderer/processors/math/Matrix3.js +11 -5
  39. package/lib/commonjs/renderer/processors/math/Matrix3.js.map +1 -1
  40. package/lib/commonjs/renderer/typeddash.js +26 -2
  41. package/lib/commonjs/renderer/typeddash.js.map +1 -1
  42. package/lib/commonjs/renderer/useContextBridge.js +35 -0
  43. package/lib/commonjs/renderer/useContextBridge.js.map +1 -0
  44. package/lib/commonjs/skia/Canvas.js.map +1 -1
  45. package/lib/commonjs/skia/Picture/Picture.js +6 -0
  46. package/lib/commonjs/skia/Picture/Picture.js.map +1 -0
  47. package/lib/commonjs/skia/Picture/PictureFactory.js +6 -0
  48. package/lib/commonjs/skia/Picture/PictureFactory.js.map +1 -0
  49. package/lib/commonjs/skia/Picture/PictureRecorder.js +6 -0
  50. package/lib/commonjs/skia/Picture/PictureRecorder.js.map +1 -0
  51. package/lib/commonjs/skia/Picture/index.js +58 -0
  52. package/lib/commonjs/skia/Picture/index.js.map +1 -0
  53. package/lib/commonjs/skia/Picture/usePicture.js +30 -0
  54. package/lib/commonjs/skia/Picture/usePicture.js.map +1 -0
  55. package/lib/commonjs/skia/Skia.js +2 -0
  56. package/lib/commonjs/skia/Skia.js.map +1 -1
  57. package/lib/commonjs/skia/index.js +14 -0
  58. package/lib/commonjs/skia/index.js.map +1 -1
  59. package/lib/commonjs/values/hooks/useDerivedValue.js +2 -2
  60. package/lib/commonjs/values/hooks/useDerivedValue.js.map +1 -1
  61. package/lib/commonjs/views/SkiaView.js +6 -5
  62. package/lib/commonjs/views/SkiaView.js.map +1 -1
  63. package/lib/commonjs/views/types.js.map +1 -1
  64. package/lib/module/renderer/Canvas.js +20 -32
  65. package/lib/module/renderer/Canvas.js.map +1 -1
  66. package/lib/module/renderer/HostConfig.js +1 -23
  67. package/lib/module/renderer/HostConfig.js.map +1 -1
  68. package/lib/module/renderer/components/Drawing.js +17 -0
  69. package/lib/module/renderer/components/Drawing.js.map +1 -0
  70. package/lib/module/renderer/components/Group.js +1 -1
  71. package/lib/module/renderer/components/Group.js.map +1 -1
  72. package/lib/module/renderer/components/Picture.js +21 -0
  73. package/lib/module/renderer/components/Picture.js.map +1 -0
  74. package/lib/module/renderer/components/imageFilters/InnerShadow.js +4 -26
  75. package/lib/module/renderer/components/imageFilters/InnerShadow.js.map +1 -1
  76. package/lib/module/renderer/components/imageFilters/{DropShadow.js → Shadow.js} +16 -6
  77. package/lib/module/renderer/components/imageFilters/Shadow.js.map +1 -0
  78. package/lib/module/renderer/components/imageFilters/index.js +1 -2
  79. package/lib/module/renderer/components/imageFilters/index.js.map +1 -1
  80. package/lib/module/renderer/components/index.js +2 -0
  81. package/lib/module/renderer/components/index.js.map +1 -1
  82. package/lib/module/renderer/components/maskFilters/Blur.js +1 -1
  83. package/lib/module/renderer/components/maskFilters/Blur.js.map +1 -1
  84. package/lib/module/renderer/components/shapes/Box.js +102 -0
  85. package/lib/module/renderer/components/shapes/Box.js.map +1 -0
  86. package/lib/module/renderer/components/shapes/index.js +1 -0
  87. package/lib/module/renderer/components/shapes/index.js.map +1 -1
  88. package/lib/module/renderer/index.js +1 -0
  89. package/lib/module/renderer/index.js.map +1 -1
  90. package/lib/module/renderer/nodes/Declaration.js +3 -0
  91. package/lib/module/renderer/nodes/Declaration.js.map +1 -1
  92. package/lib/module/renderer/nodes/Drawing.js +3 -4
  93. package/lib/module/renderer/nodes/Drawing.js.map +1 -1
  94. package/lib/module/renderer/processors/math/Math.js +15 -0
  95. package/lib/module/renderer/processors/math/Math.js.map +1 -1
  96. package/lib/module/renderer/processors/math/Matrix3.js +6 -2
  97. package/lib/module/renderer/processors/math/Matrix3.js.map +1 -1
  98. package/lib/module/renderer/typeddash.js +21 -0
  99. package/lib/module/renderer/typeddash.js.map +1 -1
  100. package/lib/module/renderer/useContextBridge.js +21 -0
  101. package/lib/module/renderer/useContextBridge.js.map +1 -0
  102. package/lib/module/skia/Canvas.js.map +1 -1
  103. package/lib/module/skia/Picture/Picture.js +2 -0
  104. package/lib/module/skia/Picture/Picture.js.map +1 -0
  105. package/lib/module/skia/Picture/PictureFactory.js +2 -0
  106. package/lib/module/skia/Picture/PictureFactory.js.map +1 -0
  107. package/lib/module/skia/Picture/PictureRecorder.js +2 -0
  108. package/lib/module/skia/Picture/PictureRecorder.js.map +1 -0
  109. package/lib/module/skia/Picture/index.js +5 -0
  110. package/lib/module/skia/Picture/index.js.map +1 -0
  111. package/lib/module/skia/Picture/usePicture.js +19 -0
  112. package/lib/module/skia/Picture/usePicture.js.map +1 -0
  113. package/lib/module/skia/Skia.js +2 -0
  114. package/lib/module/skia/Skia.js.map +1 -1
  115. package/lib/module/skia/index.js +1 -0
  116. package/lib/module/skia/index.js.map +1 -1
  117. package/lib/module/values/hooks/useDerivedValue.js +1 -1
  118. package/lib/module/values/hooks/useDerivedValue.js.map +1 -1
  119. package/lib/module/views/SkiaView.js +6 -5
  120. package/lib/module/views/SkiaView.js.map +1 -1
  121. package/lib/module/views/types.js.map +1 -1
  122. package/lib/typescript/src/renderer/Canvas.d.ts +5 -10
  123. package/lib/typescript/src/renderer/components/Drawing.d.ts +7 -0
  124. package/lib/typescript/src/renderer/components/Picture.d.ts +6 -0
  125. package/lib/typescript/src/renderer/components/imageFilters/InnerShadow.d.ts +3 -12
  126. package/lib/typescript/src/renderer/components/imageFilters/{DropShadow.d.ts → Shadow.d.ts} +3 -2
  127. package/lib/typescript/src/renderer/components/imageFilters/index.d.ts +1 -2
  128. package/lib/typescript/src/renderer/components/index.d.ts +2 -0
  129. package/lib/typescript/src/renderer/components/shapes/Box.d.ts +23 -0
  130. package/lib/typescript/src/renderer/components/shapes/index.d.ts +1 -0
  131. package/lib/typescript/src/renderer/index.d.ts +1 -0
  132. package/lib/typescript/src/renderer/nodes/Declaration.d.ts +2 -0
  133. package/lib/typescript/src/renderer/nodes/Drawing.d.ts +2 -2
  134. package/lib/typescript/src/renderer/processors/math/Math.d.ts +14 -0
  135. package/lib/typescript/src/renderer/processors/math/Matrix3.d.ts +1 -0
  136. package/lib/typescript/src/renderer/typeddash.d.ts +1 -0
  137. package/lib/typescript/src/renderer/useContextBridge.d.ts +5 -0
  138. package/lib/typescript/src/skia/Canvas.d.ts +6 -0
  139. package/lib/typescript/src/skia/Picture/Picture.d.ts +26 -0
  140. package/lib/typescript/src/skia/Picture/PictureFactory.d.ts +8 -0
  141. package/lib/typescript/src/skia/Picture/PictureRecorder.d.ts +15 -0
  142. package/lib/typescript/src/skia/Picture/index.d.ts +4 -0
  143. package/lib/typescript/src/skia/Picture/usePicture.d.ts +11 -0
  144. package/lib/typescript/src/skia/Skia.d.ts +5 -0
  145. package/lib/typescript/src/skia/index.d.ts +1 -0
  146. package/lib/typescript/src/views/SkiaView.d.ts +25 -1
  147. package/lib/typescript/src/views/types.d.ts +0 -24
  148. package/libs/ios/libskia.xcframework/ios-arm64_arm64e/libskia.a +0 -0
  149. package/libs/ios/libskia.xcframework/ios-arm64_arm64e_x86_64-simulator/libskia.a +0 -0
  150. package/libs/ios/libskshaper.xcframework/ios-arm64_arm64e/libskshaper.a +0 -0
  151. package/libs/ios/libskshaper.xcframework/ios-arm64_arm64e_x86_64-simulator/libskshaper.a +0 -0
  152. package/libs/ios/libsvg.xcframework/Info.plist +5 -5
  153. package/libs/ios/libsvg.xcframework/ios-arm64_arm64e/libsvg.a +0 -0
  154. package/libs/ios/libsvg.xcframework/ios-arm64_arm64e_x86_64-simulator/libsvg.a +0 -0
  155. package/package.json +1 -1
  156. package/src/renderer/Canvas.tsx +14 -30
  157. package/src/renderer/HostConfig.ts +1 -19
  158. package/src/renderer/components/Drawing.tsx +16 -0
  159. package/src/renderer/components/Group.tsx +1 -1
  160. package/src/renderer/components/Picture.tsx +17 -0
  161. package/src/renderer/components/imageFilters/InnerShadow.tsx +33 -47
  162. package/src/renderer/components/imageFilters/Shadow.tsx +39 -0
  163. package/src/renderer/components/imageFilters/index.ts +1 -2
  164. package/src/renderer/components/index.ts +2 -0
  165. package/src/renderer/components/maskFilters/Blur.tsx +1 -1
  166. package/src/renderer/components/shapes/Box.tsx +98 -0
  167. package/src/renderer/components/shapes/index.ts +1 -0
  168. package/src/renderer/index.ts +1 -0
  169. package/src/renderer/nodes/Declaration.tsx +10 -0
  170. package/src/renderer/nodes/Drawing.tsx +6 -6
  171. package/src/renderer/processors/math/Math.ts +16 -0
  172. package/src/renderer/processors/math/Matrix3.ts +35 -31
  173. package/src/renderer/typeddash.ts +18 -0
  174. package/src/renderer/useContextBridge.tsx +21 -0
  175. package/src/skia/Canvas.ts +7 -0
  176. package/src/skia/Picture/Picture.ts +34 -0
  177. package/src/skia/Picture/PictureFactory.ts +9 -0
  178. package/src/skia/Picture/PictureRecorder.ts +18 -0
  179. package/src/skia/Picture/index.ts +4 -0
  180. package/src/skia/Picture/usePicture.ts +28 -0
  181. package/src/skia/Skia.ts +5 -0
  182. package/src/skia/index.ts +1 -0
  183. package/src/values/hooks/useDerivedValue.ts +1 -1
  184. package/src/views/SkiaView.tsx +27 -3
  185. package/src/views/types.ts +0 -25
  186. package/lib/commonjs/renderer/components/imageFilters/DropShadow.js.map +0 -1
  187. package/lib/module/renderer/components/imageFilters/DropShadow.js.map +0 -1
  188. package/src/renderer/components/imageFilters/DropShadow.tsx +0 -31
@@ -11,8 +11,6 @@ import type {
11
11
  RefObject,
12
12
  ReactNode,
13
13
  ComponentProps,
14
- Context,
15
- ReactElement,
16
14
  MutableRefObject,
17
15
  ForwardedRef,
18
16
  } from "react";
@@ -23,6 +21,8 @@ import { SkiaView, useDrawCallback } from "../views";
23
21
  import type { TouchHandler } from "../views";
24
22
  import { Skia } from "../skia";
25
23
  import type { FontMgr } from "../skia/FontMgr/FontMgr";
24
+ import { useValue } from "../values/hooks/useValue";
25
+ import type { SkiaReadonlyValue } from "../values/types";
26
26
 
27
27
  import { debug as hostDebug, skHostConfig } from "./HostConfig";
28
28
  // import { debugTree } from "./nodes";
@@ -30,33 +30,12 @@ import { vec } from "./processors";
30
30
  import { Container } from "./nodes";
31
31
  import { DependencyManager } from "./DependencyManager";
32
32
 
33
- // useContextBridge() is taken from https://github.com/pmndrs/drei#usecontextbridge
34
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
- export const useContextBridge = (...contexts: Context<any>[]) => {
36
- const values =
37
- // eslint-disable-next-line react-hooks/rules-of-hooks
38
- contexts.map((context) => useContext(context));
39
- return useMemo(
40
- () =>
41
- ({ children }: { children: ReactNode }) =>
42
- contexts.reduceRight(
43
- (acc, Context, i) => (
44
- <Context.Provider value={values[i]} children={acc} />
45
- ),
46
- children
47
- ) as ReactElement,
48
- [contexts, values]
49
- );
50
- };
51
-
52
- interface CanvasContext {
33
+ const CanvasContext = React.createContext<SkiaReadonlyValue<{
53
34
  width: number;
54
35
  height: number;
55
- }
36
+ }> | null>(null);
56
37
 
57
- const CanvasContext = React.createContext<CanvasContext | null>(null);
58
-
59
- export const useCanvas = () => {
38
+ export const useCanvasSize = () => {
60
39
  const canvas = useContext(CanvasContext);
61
40
  if (!canvas) {
62
41
  throw new Error("Canvas context is not available");
@@ -93,6 +72,7 @@ const defaultFontMgr = Skia.FontMgr.RefDefault();
93
72
 
94
73
  export const Canvas = forwardRef<SkiaView, CanvasProps>(
95
74
  ({ children, style, debug, mode, onTouch, fontMgr }, forwardedRef) => {
75
+ const canvasCtx = useValue({ width: 0, height: 0 });
96
76
  const innerRef = useCanvasRef();
97
77
  const ref = useCombinedRefs(forwardedRef, innerRef);
98
78
  const [tick, setTick] = useState(0);
@@ -103,7 +83,6 @@ export const Canvas = forwardRef<SkiaView, CanvasProps>(
103
83
  [redraw, ref]
104
84
  );
105
85
 
106
- const canvasCtx = useRef({ width: 0, height: 0 });
107
86
  const root = useMemo(
108
87
  () => skiaReconciler.createContainer(container, 0, false, null),
109
88
  [container]
@@ -111,13 +90,13 @@ export const Canvas = forwardRef<SkiaView, CanvasProps>(
111
90
  // Render effect
112
91
  useEffect(() => {
113
92
  render(
114
- <CanvasContext.Provider value={canvasCtx.current}>
93
+ <CanvasContext.Provider value={canvasCtx}>
115
94
  {children}
116
95
  </CanvasContext.Provider>,
117
96
  root,
118
97
  container
119
98
  );
120
- }, [children, root, redraw, container]);
99
+ }, [children, root, redraw, container, canvasCtx]);
121
100
 
122
101
  // Draw callback
123
102
  const onDraw = useDrawCallback(
@@ -127,6 +106,12 @@ export const Canvas = forwardRef<SkiaView, CanvasProps>(
127
106
  if (onTouch) {
128
107
  onTouch(info.touches);
129
108
  }
109
+ if (
110
+ width !== canvasCtx.current.width ||
111
+ height !== canvasCtx.current.height
112
+ ) {
113
+ canvasCtx.current = { width, height };
114
+ }
130
115
  const paint = Skia.Paint();
131
116
  paint.setAntiAlias(true);
132
117
  const ctx = {
@@ -140,7 +125,6 @@ export const Canvas = forwardRef<SkiaView, CanvasProps>(
140
125
  center: vec(width / 2, height / 2),
141
126
  fontMgr: fontMgr ?? defaultFontMgr,
142
127
  };
143
- canvasCtx.current = ctx;
144
128
  container.draw(ctx);
145
129
  },
146
130
  [tick, onTouch]
@@ -3,7 +3,7 @@ import type { HostConfig } from "react-reconciler";
3
3
 
4
4
  import type { Node, Container, DeclarationProps, DrawingProps } from "./nodes";
5
5
  import { DeclarationNode, DrawingNode, NodeType } from "./nodes";
6
- import { exhaustiveCheck, mapKeys } from "./typeddash";
6
+ import { exhaustiveCheck, shallowEq } from "./typeddash";
7
7
 
8
8
  const DEBUG = false;
9
9
  export const debug = (...args: Parameters<typeof console.log>) => {
@@ -53,24 +53,6 @@ type SkiaHostConfig = HostConfig<
53
53
  NoTimeout
54
54
  >;
55
55
 
56
- // Shallow eq on props (without children)
57
- const shallowEq = <P extends Props>(p1: P, p2: P): boolean => {
58
- const keys1 = mapKeys(p1);
59
- const keys2 = mapKeys(p2);
60
- if (keys1.length !== keys2.length) {
61
- return false;
62
- }
63
- for (const key of keys1) {
64
- if (key === "children") {
65
- continue;
66
- }
67
- if (p1[key] !== p2[key]) {
68
- return false;
69
- }
70
- }
71
- return true;
72
- };
73
-
74
56
  const allChildrenAreMemoized = (node: Instance) => {
75
57
  if (!node.memoizable) {
76
58
  return false;
@@ -0,0 +1,16 @@
1
+ import React from "react";
2
+
3
+ import type { DrawingContext } from "../DrawingContext";
4
+ import { createDrawing } from "../nodes/Drawing";
5
+
6
+ interface DrawingProps {
7
+ drawing: (ctx: DrawingContext) => void;
8
+ }
9
+
10
+ const onDraw = createDrawing<DrawingProps>((ctx, { drawing }) => {
11
+ drawing(ctx);
12
+ });
13
+
14
+ export const Drawing = (props: DrawingProps) => {
15
+ return <skDrawing onDraw={onDraw} skipProcessing {...props} />;
16
+ };
@@ -33,7 +33,7 @@ const onDraw = createDrawing<GroupProps>(
33
33
  processPaint(paint, opacity, groupProps);
34
34
  const hasTransform = !!groupProps.transform || !!groupProps.matrix;
35
35
  const hasClip = !!clip;
36
- const shouldSave = hasTransform || hasClip;
36
+ const shouldSave = hasTransform || hasClip || !!layer;
37
37
  if (shouldSave) {
38
38
  if (layer) {
39
39
  if (typeof layer === "boolean") {
@@ -0,0 +1,17 @@
1
+ import React from "react";
2
+
3
+ import type { SkPicture } from "../../skia";
4
+ import { createDrawing } from "../nodes/Drawing";
5
+
6
+ export interface PictureProps {
7
+ picture: SkPicture;
8
+ }
9
+
10
+ const onDraw = createDrawing<PictureProps>((ctx, { picture }) => {
11
+ const { canvas } = ctx;
12
+ canvas.drawPicture(picture);
13
+ });
14
+
15
+ export const Picture = (props: PictureProps) => {
16
+ return <skDrawing onDraw={onDraw} {...props} skipProcessing />;
17
+ };
@@ -1,50 +1,36 @@
1
- import React from "react";
1
+ import type { SkColor } from "../../../skia";
2
+ import { BlendMode, Skia, TileMode } from "../../../skia";
3
+ import type { SkImageFilter } from "../../../skia/ImageFilter/ImageFilter";
2
4
 
3
- import { BlendMode, Skia, TileMode, processColor } from "../../../skia";
4
- import { createDeclaration } from "../../nodes/Declaration";
5
- import type { AnimatedProps } from "../../processors";
6
- import type { Color } from "../../../skia";
7
-
8
- import { getInput } from "./getInput";
9
-
10
- export interface InnerShadowProps {
11
- dx: number;
12
- dy: number;
13
- blur: number;
14
- color: Color;
15
- shadowOnly?: boolean;
16
- inner?: boolean;
17
- }
18
-
19
- const onDeclare = createDeclaration<InnerShadowProps>(
20
- ({ dx, dy, blur, color, shadowOnly }, children, { opacity }) => {
21
- const input = getInput(children);
22
- const cl = processColor(color, opacity);
23
- const sourceGraphic = Skia.ImageFilter.MakeColorFilter(
24
- Skia.ColorFilter.MakeBlend(0xff000000, BlendMode.Dst),
25
- null
26
- );
27
- const sourceAlpha = Skia.ImageFilter.MakeColorFilter(
28
- Skia.ColorFilter.MakeBlend(0xff000000, BlendMode.SrcIn),
29
- null
30
- );
31
- const f1 = Skia.ImageFilter.MakeColorFilter(
32
- Skia.ColorFilter.MakeBlend(cl, BlendMode.SrcOut),
33
- null
34
- );
35
- const f2 = Skia.ImageFilter.MakeOffset(dx, dy, f1);
36
- const f3 = Skia.ImageFilter.MakeBlur(blur, blur, TileMode.Decal, f2);
37
- const f4 = Skia.ImageFilter.MakeBlend(BlendMode.SrcIn, sourceAlpha, f3);
38
- if (shadowOnly) {
39
- return f4;
40
- }
41
- return Skia.ImageFilter.MakeCompose(
42
- input,
43
- Skia.ImageFilter.MakeBlend(BlendMode.SrcOver, sourceGraphic, f4)
44
- );
5
+ export const MakeInnerShadow = (
6
+ shadowOnly: boolean | undefined,
7
+ dx: number,
8
+ dy: number,
9
+ sigmaX: number,
10
+ sigmaY: number,
11
+ color: SkColor,
12
+ input: SkImageFilter | null
13
+ ) => {
14
+ const sourceGraphic = Skia.ImageFilter.MakeColorFilter(
15
+ Skia.ColorFilter.MakeBlend(0xff000000, BlendMode.Dst),
16
+ null
17
+ );
18
+ const sourceAlpha = Skia.ImageFilter.MakeColorFilter(
19
+ Skia.ColorFilter.MakeBlend(0xff000000, BlendMode.SrcIn),
20
+ null
21
+ );
22
+ const f1 = Skia.ImageFilter.MakeColorFilter(
23
+ Skia.ColorFilter.MakeBlend(color, BlendMode.SrcOut),
24
+ null
25
+ );
26
+ const f2 = Skia.ImageFilter.MakeOffset(dx, dy, f1);
27
+ const f3 = Skia.ImageFilter.MakeBlur(sigmaX, sigmaY, TileMode.Decal, f2);
28
+ const f4 = Skia.ImageFilter.MakeBlend(BlendMode.SrcIn, sourceAlpha, f3);
29
+ if (shadowOnly) {
30
+ return f4;
45
31
  }
46
- );
47
-
48
- export const InnerShadow = (props: AnimatedProps<InnerShadowProps>) => {
49
- return <skDeclaration onDeclare={onDeclare} {...props} />;
32
+ return Skia.ImageFilter.MakeCompose(
33
+ input,
34
+ Skia.ImageFilter.MakeBlend(BlendMode.SrcOver, sourceGraphic, f4)
35
+ );
50
36
  };
@@ -0,0 +1,39 @@
1
+ import React from "react";
2
+
3
+ import { Skia } from "../../../skia";
4
+ import { createDeclaration } from "../../nodes/Declaration";
5
+ import type { AnimatedProps } from "../../processors/Animations/Animations";
6
+ import type { Color } from "../../../skia/Color";
7
+ import { processColor } from "../../../skia/Color";
8
+
9
+ import { getInput } from "./getInput";
10
+ import { MakeInnerShadow } from "./InnerShadow";
11
+
12
+ export interface ShadowProps {
13
+ dx: number;
14
+ dy: number;
15
+ blur: number;
16
+ color: Color;
17
+ inner?: boolean;
18
+ shadowOnly?: boolean;
19
+ }
20
+
21
+ const onDeclare = createDeclaration<ShadowProps>(
22
+ ({ dx, dy, blur, color: cl, shadowOnly, inner }, children, { opacity }) => {
23
+ const input = getInput(children);
24
+ const color = processColor(cl, opacity);
25
+ let factory;
26
+ if (inner) {
27
+ factory = MakeInnerShadow.bind(null, shadowOnly);
28
+ } else {
29
+ factory = shadowOnly
30
+ ? Skia.ImageFilter.MakeDropShadowOnly
31
+ : Skia.ImageFilter.MakeDropShadow;
32
+ }
33
+ return factory(dx, dy, blur, blur, color, input);
34
+ }
35
+ );
36
+
37
+ export const Shadow = (props: AnimatedProps<ShadowProps>) => {
38
+ return <skDeclaration onDeclare={onDeclare} {...props} />;
39
+ };
@@ -1,6 +1,5 @@
1
1
  export * from "./Blur";
2
2
  export * from "./Offset";
3
3
  export * from "./DisplacementMap";
4
- export * from "./DropShadow";
5
- export * from "./InnerShadow";
4
+ export * from "./Shadow";
6
5
  export * from "./Morphology";
@@ -8,6 +8,7 @@ export * from "./maskFilters";
8
8
  export * from "./imageFilters";
9
9
  export * from "./pathEffects";
10
10
  export * from "../processors";
11
+ export * from "./Picture";
11
12
 
12
13
  export * from "./Group";
13
14
  export * from "./Mask";
@@ -15,3 +16,4 @@ export * from "./Paint";
15
16
  export * from "./Compose";
16
17
  export * from "./Blend";
17
18
  export * from "./Defs";
19
+ export * from "./Drawing";
@@ -29,5 +29,5 @@ export const BlurMask = (props: AnimatedProps<BlurMaskProps>) => {
29
29
 
30
30
  BlurMask.defaultProps = {
31
31
  style: "normal",
32
- respectCTM: false,
32
+ respectCTM: true,
33
33
  };
@@ -0,0 +1,98 @@
1
+ import React from "react";
2
+
3
+ import type { Color, SkRRect } from "../../../skia";
4
+ import { ClipOp, BlurStyle, Skia, processColor } from "../../../skia";
5
+ import { createDrawing } from "../../nodes";
6
+ import type { AnimatedProps, CustomPaintProps } from "../../processors";
7
+ import { add, vec, rrect } from "../../processors";
8
+ import { rect, isRRect } from "../../processors/Rects";
9
+ import { createDeclaration } from "../../nodes/Declaration";
10
+ import type { SkJSIInstance } from "../../../skia/JsiInstance";
11
+ import type { SkRect } from "../../../skia/Rect";
12
+
13
+ const inflate = (box: SkRRect, dx: number, dy: number, tx = 0, ty = 0) =>
14
+ rrect(
15
+ rect(
16
+ box.rect.x - dx + tx,
17
+ box.rect.y - dy + ty,
18
+ box.rect.width + 2 * dx,
19
+ box.rect.height + 2 * dy
20
+ ),
21
+ box.rx + dx,
22
+ box.ry + dy
23
+ );
24
+
25
+ const deflate = (box: SkRRect, dx: number, dy: number, tx = 0, ty = 0) =>
26
+ inflate(box, -dx, -dy, tx, ty);
27
+
28
+ interface BoxShadowProps {
29
+ dx?: number;
30
+ dy?: number;
31
+ spread?: number;
32
+ blur: number;
33
+ color?: Color;
34
+ inner?: boolean;
35
+ }
36
+
37
+ interface BoxShadowDecl extends BoxShadowProps, SkJSIInstance<"BoxShadow"> {}
38
+
39
+ const onDeclare = createDeclaration<BoxShadowProps>(
40
+ ({ dx, dy, spread, blur, color, inner }) => {
41
+ return { dx, dy, spread, blur, color, inner, __typename__: "BoxShadow" };
42
+ }
43
+ );
44
+
45
+ export const BoxShadow = (props: AnimatedProps<BoxShadowProps>) => {
46
+ return <skDeclaration onDeclare={onDeclare} {...props} />;
47
+ };
48
+
49
+ const isBoxShadow = (s: SkJSIInstance<string>): s is BoxShadowDecl =>
50
+ s.__typename__ === "BoxShadow";
51
+
52
+ interface BoxProps extends CustomPaintProps {
53
+ box: SkRRect | SkRect;
54
+ }
55
+
56
+ const onDraw = createDrawing<BoxProps>((ctx, { box: defaultBox }, node) => {
57
+ const box = isRRect(defaultBox) ? defaultBox : rrect(defaultBox, 0, 0);
58
+ const { canvas, paint, opacity } = ctx;
59
+ const shadows = node.visit(ctx).filter<BoxShadowDecl>(isBoxShadow);
60
+ shadows
61
+ .filter((shadow) => !shadow.inner)
62
+ .map((shadow) => {
63
+ const { color = "black", blur, spread = 0, dx = 0, dy = 0 } = shadow;
64
+ const lPaint = Skia.Paint();
65
+ lPaint.setColor(processColor(color, opacity));
66
+ lPaint.setMaskFilter(
67
+ Skia.MaskFilter.MakeBlur(BlurStyle.Normal, blur, true)
68
+ );
69
+ canvas.drawRRect(inflate(box, spread, spread, dx, dy), lPaint);
70
+ });
71
+ canvas.drawRRect(box, paint);
72
+
73
+ shadows
74
+ .filter((shadow) => shadow.inner)
75
+ .map((shadow) => {
76
+ const { color = "black", blur, spread = 0, dx = 0, dy = 0 } = shadow;
77
+ const delta = add(vec(10, 10), vec(Math.abs(dx), Math.abs(dy)));
78
+ canvas.save();
79
+ canvas.clipRRect(box, ClipOp.Intersect, false);
80
+ const lPaint = Skia.Paint();
81
+ lPaint.setColor(processColor(color, opacity));
82
+ lPaint.setMaskFilter(
83
+ Skia.MaskFilter.MakeBlur(BlurStyle.Normal, blur, true)
84
+ );
85
+ const inner = deflate(box, spread, spread, dx, dy);
86
+ const outer = inflate(box, delta.x, delta.y);
87
+ canvas.drawDRRect(outer, inner, lPaint);
88
+ canvas.restore();
89
+ });
90
+ });
91
+
92
+ export const Box = (props: AnimatedProps<BoxProps>) => {
93
+ return <skDrawing onDraw={onDraw} {...props} />;
94
+ };
95
+
96
+ Box.defaultProps = {
97
+ shadows: [],
98
+ };
@@ -10,3 +10,4 @@ export * from "./Patch";
10
10
  export * from "./Vertices";
11
11
  export * from "./Fill";
12
12
  export * from "./FitBox";
13
+ export * from "./Box";
@@ -1,3 +1,4 @@
1
1
  export * from "./Canvas";
2
2
  export * from "./components";
3
3
  export * from "./nodes";
4
+ export * from "./useContextBridge";
@@ -1,3 +1,6 @@
1
+ import type { DependencyList } from "react";
2
+ import { useCallback } from "react";
3
+
1
4
  import type { DrawingContext } from "../DrawingContext";
2
5
  import type { SkJSIInstance } from "../../skia/JsiInstance";
3
6
  import type { AnimatedProps } from "../processors";
@@ -18,6 +21,13 @@ export const createDeclaration = <T,>(
18
21
  cb: DeclarationCallback<T>
19
22
  ): DeclarationCallback<T> => cb;
20
23
 
24
+ export const useDeclaration = <P,>(
25
+ cb: DeclarationCallback<P>,
26
+ deps?: DependencyList
27
+ ) =>
28
+ // eslint-disable-next-line react-hooks/exhaustive-deps
29
+ useCallback(cb, deps ?? []);
30
+
21
31
  export interface DeclarationProps<P> {
22
32
  onDeclare: DeclarationCallback<P>;
23
33
  }
@@ -1,5 +1,5 @@
1
- import type { ReactNode } from "react";
2
- import React from "react";
1
+ import type { DependencyList, ReactNode } from "react";
2
+ import { useCallback } from "react";
3
3
 
4
4
  import type { DrawingContext } from "../DrawingContext";
5
5
  import { processPaint, selectPaint } from "../processors";
@@ -21,16 +21,16 @@ type OnDrawCallback<P> = (ctx: DrawingContext, props: P, node: Node<P>) => void;
21
21
  export const createDrawing = <P,>(cb: OnDrawCallback<P>): DrawingCallback<P> =>
22
22
  cb;
23
23
 
24
+ export const useDrawing = <P,>(cb: OnDrawCallback<P>, deps?: DependencyList) =>
25
+ // eslint-disable-next-line react-hooks/exhaustive-deps
26
+ useCallback(cb, deps ?? []);
27
+
24
28
  export type DrawingProps<T> = {
25
29
  onDraw: DrawingCallback<T>;
26
30
  skipProcessing?: boolean;
27
31
  children?: ReactNode | ReactNode[];
28
32
  };
29
33
 
30
- export const Drawing = <P,>(props: DrawingProps<P>) => {
31
- return <skDrawing {...props} />;
32
- };
33
-
34
34
  export class DrawingNode<P> extends Node<P> {
35
35
  onDraw: DrawingCallback<P>;
36
36
  skipProcessing: boolean;
@@ -1,2 +1,18 @@
1
+ /**
2
+ * Linear interpolation
3
+ * @param value
4
+ * @param x
5
+ * @param y
6
+ */
1
7
  export const mix = (value: number, x: number, y: number) =>
2
8
  x * (1 - value) + y * value;
9
+
10
+ /**
11
+ * @summary Clamps a node with a lower and upper bound.
12
+ * @example
13
+ clamp(-1, 0, 100); // 0
14
+ clamp(1, 0, 100); // 1
15
+ clamp(101, 0, 100); // 100
16
+ */
17
+ export const clamp = (value: number, lowerBound: number, upperBound: number) =>
18
+ Math.min(Math.max(lowerBound, value), upperBound);
@@ -102,6 +102,9 @@ const multiply3 = (m1: Matrix3, m2: Matrix3) => {
102
102
  ] as const;
103
103
  };
104
104
 
105
+ export const matrixVecMul3 = (m: Matrix3, v: Vec3) =>
106
+ [dot3(m[0], v), dot3(m[1], v), dot3(m[2], v)] as const;
107
+
105
108
  const skiaMatrix3 = (m: Matrix3): SkMatrix => {
106
109
  return [
107
110
  m[0][0],
@@ -117,34 +120,35 @@ const skiaMatrix3 = (m: Matrix3): SkMatrix => {
117
120
  };
118
121
 
119
122
  export const processTransform2d = (transforms: Transforms2d) =>
120
- skiaMatrix3(
121
- transforms.reduce((acc, transform) => {
122
- const key = Object.keys(transform)[0] as Transform2dName;
123
- const value = (transform as Pick<Transformations, typeof key>)[key];
124
- if (key === "translateX") {
125
- return multiply3(acc, translateXMatrix(value));
126
- }
127
- if (key === "translateY") {
128
- return multiply3(acc, translateYMatrix(value));
129
- }
130
- if (key === "scale") {
131
- return multiply3(acc, scaleMatrix(value));
132
- }
133
- if (key === "scaleX") {
134
- return multiply3(acc, scaleXMatrix(value));
135
- }
136
- if (key === "scaleY") {
137
- return multiply3(acc, scaleYMatrix(value));
138
- }
139
- if (key === "skewX") {
140
- return multiply3(acc, skewXMatrix(value));
141
- }
142
- if (key === "skewY") {
143
- return multiply3(acc, skewYMatrix(value));
144
- }
145
- if (key === "rotate" || key === "rotateZ") {
146
- return multiply3(acc, rotateZMatrix(value));
147
- }
148
- return exhaustiveCheck(key);
149
- }, identityMatrix)
150
- );
123
+ skiaMatrix3(processTransform(transforms));
124
+
125
+ const processTransform = (transforms: Transforms2d) =>
126
+ transforms.reduce((acc, transform) => {
127
+ const key = Object.keys(transform)[0] as Transform2dName;
128
+ const value = (transform as Pick<Transformations, typeof key>)[key];
129
+ if (key === "translateX") {
130
+ return multiply3(acc, translateXMatrix(value));
131
+ }
132
+ if (key === "translateY") {
133
+ return multiply3(acc, translateYMatrix(value));
134
+ }
135
+ if (key === "scale") {
136
+ return multiply3(acc, scaleMatrix(value));
137
+ }
138
+ if (key === "scaleX") {
139
+ return multiply3(acc, scaleXMatrix(value));
140
+ }
141
+ if (key === "scaleY") {
142
+ return multiply3(acc, scaleYMatrix(value));
143
+ }
144
+ if (key === "skewX") {
145
+ return multiply3(acc, skewXMatrix(value));
146
+ }
147
+ if (key === "skewY") {
148
+ return multiply3(acc, skewYMatrix(value));
149
+ }
150
+ if (key === "rotate" || key === "rotateZ") {
151
+ return multiply3(acc, rotateZMatrix(value));
152
+ }
153
+ return exhaustiveCheck(key);
154
+ }, identityMatrix);
@@ -3,3 +3,21 @@ export const mapKeys = <T>(obj: T) => Object.keys(obj) as (keyof T)[];
3
3
  export const exhaustiveCheck = (a: never): never => {
4
4
  throw new Error(`Unexhaustive handling for ${a}`);
5
5
  };
6
+
7
+ // Shallow eq on props (without children)
8
+ export const shallowEq = <P>(p1: P, p2: P): boolean => {
9
+ const keys1 = mapKeys(p1);
10
+ const keys2 = mapKeys(p2);
11
+ if (keys1.length !== keys2.length) {
12
+ return false;
13
+ }
14
+ for (const key of keys1) {
15
+ if (key === "children") {
16
+ continue;
17
+ }
18
+ if (p1[key] !== p2[key]) {
19
+ return false;
20
+ }
21
+ }
22
+ return true;
23
+ };
@@ -0,0 +1,21 @@
1
+ import React, { useMemo, useContext } from "react";
2
+ import type { ReactNode, Context, ReactElement } from "react";
3
+
4
+ // useContextBridge() is taken from https://github.com/pmndrs/drei#usecontextbridge
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ export const useContextBridge = (...contexts: Context<any>[]) => {
7
+ const values =
8
+ // eslint-disable-next-line react-hooks/rules-of-hooks
9
+ contexts.map((context) => useContext(context));
10
+ return useMemo(
11
+ () =>
12
+ ({ children }: { children: ReactNode }) =>
13
+ contexts.reduceRight(
14
+ (acc, Context, i) => (
15
+ <Context.Provider value={values[i]} children={acc} />
16
+ ),
17
+ children
18
+ ) as ReactElement,
19
+ [contexts, values]
20
+ );
21
+ };
@@ -12,6 +12,7 @@ import type { SkMatrix } from "./Matrix";
12
12
  import type { SkImageFilter } from "./ImageFilter";
13
13
  import type { SkVertices } from "./Vertices";
14
14
  import type { SkTextBlob } from "./TextBlob";
15
+ import type { SkPicture } from "./Picture";
15
16
 
16
17
  export enum ClipOp {
17
18
  Difference,
@@ -485,4 +486,10 @@ export interface SkCanvas {
485
486
  * @param m
486
487
  */
487
488
  concat(m: SkMatrix): void;
489
+
490
+ /**
491
+ * Draws the given picture using the current clip, current matrix, and the provided paint.
492
+ * @param skp
493
+ */
494
+ drawPicture(skp: SkPicture): void;
488
495
  }