UNPKG

@shopify/react-native-skia

Version:

High-performance React Native Graphics using Skia

294 lines (257 loc) 6.35 kB
import type { CanvasKit, PathBuilder as CKPathBuilder } from "canvaskit-wasm"; import type { FillType, InputMatrix, InputRRect, SkMatrix, SkPath, SkPathBuilder, SkPoint, SkRect, } from "../types"; import { getEnum, HostObject } from "./Host"; import { JsiSkPath, toMatrix3x3 } from "./JsiSkPath"; import { JsiSkMatrix } from "./JsiSkMatrix"; import { JsiSkPoint } from "./JsiSkPoint"; import { JsiSkRect } from "./JsiSkRect"; import { JsiSkRRect } from "./JsiSkRRect"; /** * Web implementation of SkPathBuilder using CanvasKit's native PathBuilder. */ export class JsiSkPathBuilder extends HostObject<CKPathBuilder, "PathBuilder"> implements SkPathBuilder { constructor(CanvasKit: CanvasKit, ref: CKPathBuilder) { super(CanvasKit, ref, "PathBuilder"); } // Movement methods moveTo(x: number, y: number) { this.ref.moveTo(x, y); return this; } rMoveTo(x: number, y: number) { this.ref.rMoveTo(x, y); return this; } lineTo(x: number, y: number) { this.ref.lineTo(x, y); return this; } rLineTo(x: number, y: number) { this.ref.rLineTo(x, y); return this; } // Curve methods quadTo(x1: number, y1: number, x2: number, y2: number) { this.ref.quadTo(x1, y1, x2, y2); return this; } rQuadTo(x1: number, y1: number, x2: number, y2: number) { this.ref.rQuadTo(x1, y1, x2, y2); return this; } conicTo(x1: number, y1: number, x2: number, y2: number, w: number) { this.ref.conicTo(x1, y1, x2, y2, w); return this; } rConicTo(x1: number, y1: number, x2: number, y2: number, w: number) { this.ref.rConicTo(x1, y1, x2, y2, w); return this; } cubicTo( x1: number, y1: number, x2: number, y2: number, x3: number, y3: number ) { this.ref.cubicTo(x1, y1, x2, y2, x3, y3); return this; } rCubicTo( x1: number, y1: number, x2: number, y2: number, x3: number, y3: number ) { this.ref.rCubicTo(x1, y1, x2, y2, x3, y3); return this; } close() { this.ref.close(); return this; } // Arc methods arcToOval( oval: SkRect, startAngleInDegrees: number, sweepAngleInDegrees: number, forceMoveTo: boolean ) { this.ref.arcToOval( JsiSkRect.fromValue(this.CanvasKit, oval), startAngleInDegrees, sweepAngleInDegrees, forceMoveTo ); return this; } arcToRotated( rx: number, ry: number, xAxisRotateInDegrees: number, useSmallArc: boolean, isCCW: boolean, x: number, y: number ) { this.ref.arcToRotated( rx, ry, xAxisRotateInDegrees, useSmallArc, isCCW, x, y ); return this; } rArcTo( rx: number, ry: number, xAxisRotateInDegrees: number, useSmallArc: boolean, isCCW: boolean, dx: number, dy: number ) { this.ref.rArcTo(rx, ry, xAxisRotateInDegrees, useSmallArc, isCCW, dx, dy); return this; } arcToTangent(x1: number, y1: number, x2: number, y2: number, radius: number) { this.ref.arcToTangent(x1, y1, x2, y2, radius); return this; } // Shape methods addRect(rect: SkRect, isCCW?: boolean) { this.ref.addRect(JsiSkRect.fromValue(this.CanvasKit, rect), isCCW); return this; } addOval(oval: SkRect, isCCW?: boolean, startIndex?: number) { this.ref.addOval( JsiSkRect.fromValue(this.CanvasKit, oval), isCCW, startIndex ); return this; } addArc( oval: SkRect, startAngleInDegrees: number, sweepAngleInDegrees: number ) { this.ref.addArc( JsiSkRect.fromValue(this.CanvasKit, oval), startAngleInDegrees, sweepAngleInDegrees ); return this; } addRRect(rrect: InputRRect, isCCW?: boolean) { this.ref.addRRect(JsiSkRRect.fromValue(this.CanvasKit, rrect), isCCW); return this; } addCircle(x: number, y: number, r: number, _isCCW?: boolean) { this.ref.addCircle(x, y, r); return this; } addPoly(points: SkPoint[], close: boolean) { this.ref.addPolygon( points.map((p) => Array.from(JsiSkPoint.fromValue(p))).flat(), close ); return this; } addPath(src: SkPath, matrix?: SkMatrix, extend = false) { const srcPath = JsiSkPath.pathFromValue(src); const args = [ srcPath, ...(matrix ? JsiSkMatrix.fromValue<Float32Array>(matrix) : []), extend, ]; this.ref.addPath(...args); srcPath.delete(); return this; } // Configuration methods setFillType(fill: FillType) { this.ref.setFillType(getEnum(this.CanvasKit, "FillType", fill)); return this; } setIsVolatile(_isVolatile: boolean) { // Not supported in CanvasKit PathBuilder - no-op return this; } reset() { // CanvasKit PathBuilder doesn't have reset - recreate const newBuilder = new this.CanvasKit.PathBuilder(); // Swap the ref - delete old one if ( this.ref !== null && typeof this.ref === "object" && "delete" in this.ref && typeof this.ref.delete === "function" ) { this.ref.delete(); } this.ref = newBuilder; return this; } offset(dx: number, dy: number) { this.ref.offset(dx, dy); return this; } transform(m: InputMatrix) { const matrix = toMatrix3x3(m); this.ref.transform(matrix); return this; } // Query methods computeBounds(): SkRect { return new JsiSkRect(this.CanvasKit, this.ref.getBounds()); } isEmpty(): boolean { return this.ref.isEmpty(); } getLastPt(): { x: number; y: number } { const count = this.ref.countPoints(); if (count === 0) { return { x: 0, y: 0 }; } // PathBuilder doesn't have getPoint - snapshot to get it const path = this.ref.snapshot(); const pt = path.getPoint(count - 1); path.delete(); return { x: pt[0], y: pt[1] }; } countPoints(): number { return this.ref.countPoints(); } // Build methods build(): SkPath { const path = this.ref.snapshot(); const builder = new this.CanvasKit.PathBuilder(path); path.delete(); return new JsiSkPath(this.CanvasKit, builder); } detach(): SkPath { const path = this.ref.detach(); const builder = new this.CanvasKit.PathBuilder(path); path.delete(); return new JsiSkPath(this.CanvasKit, builder); } }