Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New scene graph model #2862

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/paper/src/Examples/Breathe/Breathe.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
Group,
polar2Canvas,
mix,
Canvas2,
} from "@shopify/react-native-skia";
import type { SharedValue } from "react-native-reanimated";
import { useDerivedValue } from "react-native-reanimated";
Expand Down Expand Up @@ -71,7 +72,7 @@ export const Breathe = () => {
<Canvas style={styles.container} opaque>
<Fill color="rgb(36,43,56)" />
<Group origin={center} transform={transform} blendMode="screen">
<BlurMask style="solid" blur={40} />
<BlurMask style="solid" blur={30} />
{new Array(6).fill(0).map((_, index) => {
return (
<Ring key={index} index={index} progress={progress} total={6} />
Expand Down
2 changes: 1 addition & 1 deletion apps/paper/src/Examples/Matrix/Matrix.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const Matrix = () => {
<Canvas style={{ flex: 1 }} opaque>
<Fill color="black" />
<Group>
<BlurMask blur={8} style="solid" />
<BlurMask blur={4} style="solid" />
{cols.map((_i, i) =>
rows.map((_j, j) => (
<Symbol
Expand Down
6 changes: 3 additions & 3 deletions apps/paper/src/Examples/Matrix/Symbol.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { interpolateColors, vec, Glyphs } from "@shopify/react-native-skia";
import type { SharedValue } from "react-native-reanimated";
import { useDerivedValue } from "react-native-reanimated";

export const COLS = 8;
export const ROWS = 15;
export const COLS = 32;
export const ROWS = 64;
const pos = vec(0, 0);

interface SymbolProps {
Expand Down Expand Up @@ -38,7 +38,7 @@ export const Symbol = ({
}, [timestamp]);

const opacity = useDerivedValue(() => {
const idx = Math.round(timestamp.value / 100);
const idx = Math.round(timestamp.value / 75);
return stream[(stream.length - j + idx) % stream.length];
}, [timestamp]);

Expand Down
9 changes: 8 additions & 1 deletion packages/skia/cpp/api/JsiSkPaint.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ class JsiSkPaint : public JsiSkWrappingSharedPtrHostObject<SkPaint> {
public:
EXPORT_JSI_API_TYPENAME(JsiSkPaint, Paint)

JSI_HOST_FUNCTION(assign) {
SkPaint* paint = JsiSkPaint::fromValue(runtime, arguments[0]).get();
*getObject() = *paint;
return jsi::Value::undefined();
}

JSI_HOST_FUNCTION(copy) {
const auto *paint = getObject().get();
return jsi::Object::createFromHostObject(
Expand Down Expand Up @@ -163,7 +169,8 @@ class JsiSkPaint : public JsiSkWrappingSharedPtrHostObject<SkPaint> {
return jsi::Value::undefined();
}

JSI_EXPORT_FUNCTIONS(JSI_EXPORT_FUNC(JsiSkPaint, copy),
JSI_EXPORT_FUNCTIONS(JSI_EXPORT_FUNC(JsiSkPaint, assign),
JSI_EXPORT_FUNC(JsiSkPaint, copy),
JSI_EXPORT_FUNC(JsiSkPaint, reset),
JSI_EXPORT_FUNC(JsiSkPaint, getAlphaf),
JSI_EXPORT_FUNC(JsiSkPaint, getColor),
Expand Down
12 changes: 7 additions & 5 deletions packages/skia/src/dom/types/Common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ export interface TransformProps {
matrix?: InputMatrix;
}

export interface CTMProps extends TransformProps {
clip?: ClipDef;
invertClip?: boolean;
layer?: SkPaint | boolean;
}

export interface PaintProps extends ChildrenProps {
color?: Color;
strokeWidth?: number;
Expand All @@ -83,8 +89,4 @@ export interface PaintProps extends ChildrenProps {
dither?: boolean;
}

export interface GroupProps extends PaintProps, TransformProps {
clip?: ClipDef;
invertClip?: boolean;
layer?: SkPaint | boolean;
}
export interface GroupProps extends PaintProps, CTMProps {}
158 changes: 80 additions & 78 deletions packages/skia/src/renderer/Canvas.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,24 @@
import React, {
useEffect,
import {
forwardRef,
useCallback,
useEffect,
useImperativeHandle,
useMemo,
forwardRef,
useRef,
} from "react";
import type {
RefObject,
ReactNode,
MutableRefObject,
ForwardedRef,
FunctionComponent,
} from "react";
import type { LayoutChangeEvent } from "react-native";
import type { LayoutChangeEvent, ViewProps } from "react-native";
import type { SharedValue } from "react-native-reanimated";

import { SkiaDomView } from "../views";
import { SkiaViewNativeId } from "../views/SkiaViewNativeId";
import SkiaPictureViewNativeComponent from "../specs/SkiaPictureViewNativeComponent";
import type { SkRect, SkSize } from "../skia/types";
import { SkiaSGRoot } from "../sksg/Reconciler";
import { Skia } from "../skia";
import type { SkiaBaseViewProps } from "../views";

import { SkiaRoot } from "./Reconciler";

export const useCanvasRef = () => useRef<SkiaDomView>(null);

export interface CanvasProps extends SkiaBaseViewProps {
ref?: RefObject<SkiaDomView>;
children: ReactNode;
mode?: "default" | "continuous";
}
const NativeSkiaPictureView = SkiaPictureViewNativeComponent;

// TODO: no need to go through the JS thread for this
const useOnSizeEvent = (
resultValue: SkiaBaseViewProps["onSize"],
onLayout?: (event: LayoutChangeEvent) => void
Expand All @@ -46,81 +38,91 @@ const useOnSizeEvent = (
);
};

export const Canvas = forwardRef<SkiaDomView, CanvasProps>(
export interface CanvasProps extends ViewProps {
debug?: boolean;
opaque?: boolean;
onSize?: SharedValue<SkSize>;
mode?: "continuous" | "default";
}

export const Canvas = forwardRef(
(
{
children,
style,
mode,
debug,
mode = "default",
onSize: _onSize,
opaque,
children,
onSize,
onLayout: _onLayout,
...props
},
forwardedRef
...viewProps
}: CanvasProps,
ref
) => {
const onLayout = useOnSizeEvent(_onSize, _onLayout);
const innerRef = useCanvasRef();
const ref = useCombinedRefs(forwardedRef, innerRef);
const redraw = useCallback(() => {
innerRef.current?.redraw();
}, [innerRef]);
const getNativeId = useCallback(() => {
const id = innerRef.current?.nativeId ?? -1;
return id;
}, [innerRef]);
const rafId = useRef<number | null>(null);
const onLayout = useOnSizeEvent(onSize, _onLayout);
// Native ID
const nativeId = useMemo(() => {
return SkiaViewNativeId.current++;
}, []);

const root = useMemo(
() => new SkiaRoot(redraw, getNativeId),
[redraw, getNativeId]
);
// Root
const root = useMemo(() => new SkiaSGRoot(Skia, nativeId), [nativeId]);

// Render effect
// Render effects
useEffect(() => {
root.render(children);
}, [children, root, redraw]);
}, [children, root]);

useEffect(() => {
return () => {
root.unmount();
};
}, [root]);

const requestRedraw = useCallback(() => {
rafId.current = requestAnimationFrame(() => {
root.render(children);
if (mode === "continuous") {
requestRedraw();
}
});
}, [children, mode, root]);

useEffect(() => {
if (mode === "continuous") {
requestRedraw();
}
return () => {
if (rafId.current !== null) {
cancelAnimationFrame(rafId.current);
}
};
}, [mode, requestRedraw]);

// Component methods
useImperativeHandle(ref, () => ({
makeImageSnapshot: (rect?: SkRect) => {
return SkiaViewApi.makeImageSnapshot(nativeId, rect);
},
makeImageSnapshotAsync: (rect?: SkRect) => {
return SkiaViewApi.makeImageSnapshotAsync(nativeId, rect);
},
redraw: () => {
SkiaViewApi.requestRedraw(nativeId);
},
getNativeId: () => {
return nativeId;
},
}));
return (
<SkiaDomView
ref={ref}
style={style}
root={root.dom}
onLayout={onLayout}
<NativeSkiaPictureView
collapsable={false}
nativeID={`${nativeId}`}
debug={debug}
mode={mode}
{...props}
opaque={opaque}
onLayout={onLayout}
{...viewProps}
/>
);
}
) as FunctionComponent<CanvasProps & React.RefAttributes<SkiaDomView>>;

/**
* Combines a list of refs into a single ref. This can be used to provide
* both a forwarded ref and an internal ref keeping the same functionality
* on both of the refs.
* @param refs Array of refs to combine
* @returns A single ref that can be used in a ref prop.
*/
const useCombinedRefs = <T,>(
...refs: Array<MutableRefObject<T> | ForwardedRef<T>>
) => {
const targetRef = React.useRef<T>(null);
React.useEffect(() => {
refs.forEach((ref) => {
if (ref) {
if (typeof ref === "function") {
ref(targetRef.current);
} else {
ref.current = targetRef.current;
}
}
});
}, [refs]);
return targetRef;
};
);
2 changes: 2 additions & 0 deletions packages/skia/src/skia/types/Paint/Paint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export interface SkPaint extends SkJSIInstance<"Paint"> {
*/
reset(): void;

assign(paint: SkPaint): void;

/**
* Retrieves the alpha and RGB unpremultiplied. RGB are extended sRGB values
* (sRGB gamut, and encoded with the sRGB transfer function).
Expand Down
4 changes: 4 additions & 0 deletions packages/skia/src/skia/web/JsiSkPaint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ export class JsiSkPaint extends HostObject<Paint, "Paint"> implements SkPaint {
return new JsiSkPaint(this.CanvasKit, this.ref.copy());
}

assign(paint: JsiSkPaint) {
this.ref = paint.ref.copy();
}

reset() {
this.ref = new this.CanvasKit.Paint();
}
Expand Down
Loading
Loading