@shopify/react-native-skia 0.1.116 → 0.1.118

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