Skip to content

Commit

Permalink
📋 CanvasKit Compatibility Layer (#495)
Browse files Browse the repository at this point in the history
  • Loading branch information
wcandillon authored May 30, 2022
1 parent 7fae154 commit d55f839
Show file tree
Hide file tree
Showing 22 changed files with 1,033 additions and 29 deletions.
6 changes: 4 additions & 2 deletions package/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"tsc": "tsc --noEmit",
"lint": "eslint . --ext .ts,.tsx --max-warnings 0",
"test": "jest",
"postinstall": "node scripts/install-npm.js",
"postinstall": "node scripts/install-npm.js && patch-package",
"build": "bob build"
},
"repository": {
Expand Down Expand Up @@ -69,14 +69,16 @@
"@types/jest": "^27.0.3",
"@types/react-native": "^0.65.0",
"@types/react-reconciler": "^0.26.4",
"canvaskit-wasm": "^0.34.0",
"eslint": "7.32.0",
"eslint-config-react-native-wcandillon": "^3.7.2",
"jest": "^27.4.3",
"react": "17.0.2",
"react-native": "0.66.2",
"react-native-builder-bob": "^0.18.2",
"ts-jest": "^27.0.7",
"typescript": "^4.6.4"
"typescript": "^4.6.4",
"patch-package": "^6.4.7"
},
"dependencies": {
"react-reconciler": "^0.26.2"
Expand Down
50 changes: 50 additions & 0 deletions package/patches/canvaskit-wasm+0.34.0.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
diff --git a/node_modules/canvaskit-wasm/types/index.d.ts b/node_modules/canvaskit-wasm/types/index.d.ts
index 3abbcc3..c578760 100644
--- a/node_modules/canvaskit-wasm/types/index.d.ts
+++ b/node_modules/canvaskit-wasm/types/index.d.ts
@@ -1,5 +1,5 @@
// Minimum TypeScript Version: 3.7
-export function CanvasKitInit(opts: CanvasKitInitOptions): Promise<CanvasKit>;
+export default function CanvasKitInit(opts?: CanvasKitInitOptions): Promise<CanvasKit>;

export interface CanvasKitInitOptions {
/**
@@ -1983,7 +1983,7 @@ export interface Paint extends EmbindObject<Paint> {
* Sets the current color filter, replacing the existing one if there was one.
* @param filter
*/
- setColorFilter(filter: ColorFilter): void;
+ setColorFilter(filter: ColorFilter | null): void;

/**
* Sets the color used when stroking and filling. The color values are interpreted as being in
@@ -1997,25 +1997,25 @@ export interface Paint extends EmbindObject<Paint> {
* Sets the current image filter, replacing the existing one if there was one.
* @param filter
*/
- setImageFilter(filter: ImageFilter): void;
+ setImageFilter(filter: ImageFilter | null): void;

/**
* Sets the current mask filter, replacing the existing one if there was one.
* @param filter
*/
- setMaskFilter(filter: MaskFilter): void;
+ setMaskFilter(filter: MaskFilter | null): void;

/**
* Sets the current path effect, replacing the existing one if there was one.
* @param effect
*/
- setPathEffect(effect: PathEffect): void;
+ setPathEffect(effect: PathEffect | null): void;

/**
* Sets the current shader, replacing the existing one if there was one.
* @param shader
*/
- setShader(shader: Shader): void;
+ setShader(shader: Shader | null): void;

/**
* Sets the geometry drawn at the beginning and end of strokes.
40 changes: 40 additions & 0 deletions package/src/skia/__tests__/Rects.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import fs from "fs";
import path from "path";

import CanvasKitInit from "canvaskit-wasm";

import { JsiSkApi } from "../web";

//import type { SkiaApi } from "../SkiaApi";

let Skia: ReturnType<typeof JsiSkApi>;

beforeAll(async () => {
const CanvasKit = await CanvasKitInit();
Skia = JsiSkApi(CanvasKit);
});

describe("Draw a rectangle", () => {
it("Check that CanvasKit and CanvasKit are loaded", async () => {
expect(Skia).toBeDefined();
});
it("Draws a lightblue rectange", () => {
const paint = Skia.Paint();
paint.setColor(Skia.Color("lightblue"));
const rct = Skia.XYWHRect(64, 64, 128, 128);
const surface = Skia.Surface.Make(256, 256);
expect(surface).toBeDefined();
if (!surface) {
return;
}
const canvas = surface.getCanvas();
canvas.drawRect(rct, paint);
surface.ref.flush();
const image = surface.makeImageSnapshot();
const png = image.encodeToBytes();
const ref = fs.readFileSync(
path.resolve(__dirname, "snapshots/lightblue-rect.png")
);
expect(ref.equals(png)).toBe(true);
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions package/src/skia/types/MaskFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ export enum BlurStyle {

export const isMaskFilter = (
obj: SkJSIInstance<string> | null
): obj is IMaskFilter => obj !== null && obj.__typename__ === "MaskFilter";
): obj is SkMaskFilter => obj !== null && obj.__typename__ === "MaskFilter";

export type IMaskFilter = SkJSIInstance<"MaskFilter">;
export type SkMaskFilter = SkJSIInstance<"MaskFilter">;

/**
* See SkMaskFilter.h for more details.
Expand All @@ -23,5 +23,5 @@ export interface MaskFilterFactory {
* @param sigma - Standard deviation of the Gaussian blur to apply. Must be > 0.
* @param respectCTM - if true the blur's sigma is modified by the CTM.
*/
MakeBlur(style: BlurStyle, sigma: number, respectCTM: boolean): IMaskFilter;
MakeBlur(style: BlurStyle, sigma: number, respectCTM: boolean): SkMaskFilter;
}
8 changes: 4 additions & 4 deletions package/src/skia/types/Paint/Paint.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { SkImageFilter } from "../ImageFilter";
import type { IMaskFilter } from "../MaskFilter";
import type { SkMaskFilter } from "../MaskFilter";
import type { SkColorFilter } from "../ColorFilter";
import type { SkColor } from "../Color";
import type { IPathEffect } from "../PathEffect";
import type { SkPathEffect } from "../PathEffect";
import type { SkJSIInstance } from "../JsiInstance";
import type { SkShader } from "../Shader";

Expand Down Expand Up @@ -107,13 +107,13 @@ export interface SkPaint extends SkJSIInstance<"Paint"> {
* Sets the current mask filter, replacing the existing one if there was one.
* @param filter
*/
setMaskFilter(filter: IMaskFilter | null): void;
setMaskFilter(filter: SkMaskFilter | null): void;

/**
* Sets the current path effect, replacing the existing one if there was one.
* @param effect
*/
setPathEffect(effect: IPathEffect | null): void;
setPathEffect(effect: SkPathEffect | null): void;

/**
* Sets the current shader, replacing the existing one if there was one.
Expand Down
24 changes: 14 additions & 10 deletions package/src/skia/types/PathEffect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import type { SkJSIInstance } from "./JsiInstance";
import type { SkPath } from "./Path/Path";
import type { SkMatrix } from "./Matrix";

export type IPathEffect = SkJSIInstance<"PathEffect">;
export type SkPathEffect = SkJSIInstance<"PathEffect">;

export const isPathEffect = (
obj: SkJSIInstance<string> | null
): obj is IPathEffect => obj !== null && obj.__typename__ === "PathEffect";
): obj is SkPathEffect => obj !== null && obj.__typename__ === "PathEffect";

export enum Path1DEffectStyle {
Translate,
Expand All @@ -19,7 +19,7 @@ export interface PathEffectFactory {
* Returns a PathEffect that can turn sharp corners into rounded corners.
* @param radius - if <=0, returns null
*/
MakeCorner(radius: number): IPathEffect | null;
MakeCorner(radius: number): SkPathEffect | null;

/**
* Returns a PathEffect that add dashes to the path.
Expand All @@ -30,7 +30,7 @@ export interface PathEffectFactory {
* the "on" intervals, and the odd indices specifying the length of "off".
* @param phase - offset length into the intervals array. Defaults to 0.
*/
MakeDash(intervals: number[], phase?: number): IPathEffect;
MakeDash(intervals: number[], phase?: number): SkPathEffect;

/**
* Returns a PathEffect that breaks path into segments of segLength length, and randomly move
Expand All @@ -39,23 +39,27 @@ export interface PathEffectFactory {
* @param dev - limit of the movement of the endpoints.
* @param seedAssist - modifies the randomness. See SkDiscretePathEffect.h for more.
*/
MakeDiscrete(segLength: number, dev: number, seedAssist: number): IPathEffect;
MakeDiscrete(
segLength: number,
dev: number,
seedAssist: number
): SkPathEffect;

/**
*
* A pathEffect whose effect is to apply first the inner pathEffect and the the
* outer pathEffect (i.e. outer(inner(path))).
*
*/
MakeCompose(outer: IPathEffect, inner: IPathEffect): IPathEffect;
MakeCompose(outer: SkPathEffect, inner: SkPathEffect): SkPathEffect;

/**
*
* A pathEffect pathEffect whose effect is to apply two effects,
* in sequence (i.e. first(path) + second(path)).
*
*/
MakeSum(outer: IPathEffect, inner: IPathEffect): IPathEffect;
MakeSum(outer: SkPathEffect, inner: SkPathEffect): SkPathEffect;

/**
* Returns a PathEffect that will fill the drawing path with a pattern made by applying
Expand All @@ -65,7 +69,7 @@ export interface PathEffectFactory {
* @param width - must be >= 0
* @param matrix
*/
MakeLine2D(width: number, matrix: SkMatrix): IPathEffect | null;
MakeLine2D(width: number, matrix: SkMatrix): SkPathEffect | null;

/**
* Returns a PathEffect which implements dashing by replicating the specified path.
Expand All @@ -80,7 +84,7 @@ export interface PathEffectFactory {
advance: number,
phase: number,
style: Path1DEffectStyle
): IPathEffect | null;
): SkPathEffect | null;

/**
* Returns a PathEffect that will fill the drawing path with a pattern by repeating the
Expand All @@ -89,5 +93,5 @@ export interface PathEffectFactory {
* @param matrix
* @param path
*/
MakePath2D(matrix: SkMatrix, path: SkPath): IPathEffect | null;
MakePath2D(matrix: SkMatrix, path: SkPath): SkPathEffect | null;
}
2 changes: 1 addition & 1 deletion package/src/skia/types/SVG/SVGFactory.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Data } from "../Data/Data";
import type { Data } from "../Data";

import type { SkSVG } from "./SVG";

Expand Down
10 changes: 5 additions & 5 deletions package/src/skia/types/Skia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import type { MaskFilterFactory } from "./MaskFilter";
import type { SkPaint } from "./Paint";
import type { SkRect } from "./Rect";
import type { SkRRect } from "./RRect";
import type { RuntimeEffectFactory } from "./RuntimeEffect";
import type {
RuntimeEffectFactory,
SkRuntimeEffect,
SkRuntimeShaderBuilder,
} from "./RuntimeEffect";
import type { ShaderFactory } from "./Shader";
import type { SkMatrix } from "./Matrix";
import type { PathEffectFactory } from "./PathEffect";
Expand All @@ -24,10 +28,6 @@ import type { SkPath } from "./Path/Path";
import type { SkContourMeasureIter } from "./ContourMeasure";
import type { PictureFactory, SkPictureRecorder } from "./Picture";
import type { Color, SkColor } from "./Color";
import type {
SkRuntimeShaderBuilder,
SkRuntimeEffect,
} from "./RuntimeEffect/RuntimeEffect";

/**
* Declares the interface for the native Skia API
Expand Down
2 changes: 1 addition & 1 deletion package/src/skia/types/Typeface/TypefaceFactory.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Data } from "../Data/Data";
import type { Data } from "../Data";

import type { SkTypeface } from "./Typeface";

Expand Down
50 changes: 50 additions & 0 deletions package/src/skia/web/api/Host.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* eslint-disable no-nested-ternary */
import type { CanvasKit, EmbindEnumEntity } from "canvaskit-wasm";

import type { SkJSIInstance } from "../../types";

export abstract class Host {
readonly CanvasKit: CanvasKit;

constructor(CanvasKit: CanvasKit) {
this.CanvasKit = CanvasKit;
}
}

export abstract class HostObject<T, N extends string>
extends Host
implements SkJSIInstance<N>
{
readonly __typename__: N;
readonly ref: T;

constructor(CanvasKit: CanvasKit, ref: T, typename: N) {
super(CanvasKit);
this.ref = ref;
this.__typename__ = typename;
}
}

// eslint-disable-next-line @typescript-eslint/ban-types
export type NonNullish = {};

export const toOptionalValue = <T>(
value: NonNullish | undefined | null
): T | undefined | null =>
value === undefined ? undefined : value === null ? null : toValue(value);

export const toUndefinedableValue = <T>(
value: NonNullish | undefined
): T | undefined => (value === undefined ? undefined : toValue(value));

export const toNullableValue = <T>(value: NonNullish | null): T | null =>
value === null ? null : toValue(value);

export const toValue = <T>(value: NonNullish): T =>
(value as HostObject<T, string>).ref;

export const ckEnum = (value: number): EmbindEnumEntity => ({ value });
export const optEnum = (
value: number | undefined
): EmbindEnumEntity | undefined =>
value === undefined ? undefined : { value };
Loading

0 comments on commit d55f839

Please sign in to comment.