Parcourir la source

[ts][canvaskit] Properly fix vertices info misalignment that led to crash when clipping (0b426772).

# Conflicts:
#	spine-ts/spine-canvaskit/src/index.ts
Davide Tantillo il y a 2 mois
Parent
commit
5826c4b1cf
1 fichiers modifiés avec 87 ajouts et 63 suppressions
  1. 87 63
      spine-ts/spine-canvaskit/src/index.ts

+ 87 - 63
spine-ts/spine-canvaskit/src/index.ts

@@ -1,3 +1,32 @@
+/******************************************************************************
+ * Spine Runtimes License Agreement
+ * Last updated July 28, 2023. Replaces all prior versions.
+ *
+ * Copyright (c) 2013-2023, Esoteric Software LLC
+ *
+ * Integration of the Spine Runtimes into software or otherwise creating
+ * derivative works of the Spine Runtimes is permitted under the terms and
+ * conditions of Section 2 of the Spine Editor License Agreement:
+ * http://esotericsoftware.com/spine-editor-license
+ *
+ * Otherwise, it is permitted to integrate the Spine Runtimes into software or
+ * otherwise create derivative works of the Spine Runtimes (collectively,
+ * "Products"), provided that each user of the Products must obtain their own
+ * Spine Editor license and redistribution of the Products in any form must
+ * include this license and copyright notice.
+ *
+ * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
+ * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE
+ * SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
 export * from "@esotericsoftware/spine-core";
 
 import {
@@ -8,28 +37,20 @@ import {
 	ClippingAttachment,
 	Color,
 	MeshAttachment,
-	NumberArrayLike,
+	type NumberArrayLike,
 	Physics,
 	RegionAttachment,
 	Skeleton,
 	SkeletonBinary,
 	SkeletonClipping,
-	SkeletonData,
+	type SkeletonData,
 	SkeletonJson,
 	Texture,
 	TextureAtlas,
-	TextureFilter,
-	TextureWrap,
 	Utils,
 } from "@esotericsoftware/spine-core";
-import {
-	Canvas,
-	CanvasKit,
-	Image,
-	Paint,
-	Shader,
-	BlendMode as CanvasKitBlendMode,
-} from "canvaskit-wasm";
+
+import type { Canvas, CanvasKit, Image, Paint, Shader } from "canvaskit-wasm";
 
 Skeleton.yDown = true;
 
@@ -55,7 +76,7 @@ function toCkBlendMode (ck: CanvasKit, blendMode: BlendMode) {
 	}
 }
 
-function bufferToUtf8String (buffer: any) {
+function bufferToUtf8String (buffer: ArrayBuffer | Buffer) {
 	if (typeof Buffer !== "undefined") {
 		return buffer.toString("utf-8");
 	} else if (typeof TextDecoder !== "undefined") {
@@ -70,9 +91,9 @@ class CanvasKitTexture extends Texture {
 		return this._image;
 	}
 
-	setFilters (minFilter: TextureFilter, magFilter: TextureFilter): void { }
+	setFilters (): void { }
 
-	setWraps (uWrap: TextureWrap, vWrap: TextureWrap): void { }
+	setWraps (): void { }
 
 	dispose (): void {
 		const data: CanvasKitImage = this._image;
@@ -89,7 +110,7 @@ class CanvasKitTexture extends Texture {
 	static async fromFile (
 		ck: CanvasKit,
 		path: string,
-		readFile: (path: string) => Promise<any>
+		readFile: (path: string) => Promise<ArrayBuffer | Buffer>
 	): Promise<CanvasKitTexture> {
 		const imgData = await readFile(path);
 		if (!imgData) throw new Error(`Could not load image ${path}`);
@@ -126,7 +147,7 @@ class CanvasKitTexture extends Texture {
 export async function loadTextureAtlas (
 	ck: CanvasKit,
 	atlasFile: string,
-	readFile: (path: string) => Promise<Buffer>
+	readFile: (path: string) => Promise<ArrayBuffer | Buffer>
 ): Promise<TextureAtlas> {
 	const atlas = new TextureAtlas(bufferToUtf8String(await readFile(atlasFile)));
 	const slashIndex = atlasFile.lastIndexOf("/");
@@ -150,7 +171,7 @@ export async function loadTextureAtlas (
 export async function loadSkeletonData (
 	skeletonFile: string,
 	atlas: TextureAtlas,
-	readFile: (path: string) => Promise<Buffer>,
+	readFile: (path: string) => Promise<ArrayBuffer | Buffer>,
 	scale = 1
 ): Promise<SkeletonData> {
 	const attachmentLoader = new AtlasAttachmentLoader(atlas);
@@ -158,12 +179,11 @@ export async function loadSkeletonData (
 		? new SkeletonJson(attachmentLoader)
 		: new SkeletonBinary(attachmentLoader);
 	loader.scale = scale;
-	let data = await readFile(skeletonFile);
-	if (skeletonFile.endsWith(".json")) {
-		data = bufferToUtf8String(data);
+	const data = await readFile(skeletonFile);
+	if (loader instanceof SkeletonJson) {
+		return loader.readSkeletonData(bufferToUtf8String(data))
 	}
-	const skeletonData = loader.readSkeletonData(data);
-	return skeletonData;
+	return loader.readSkeletonData(data);
 }
 
 /**
@@ -224,12 +244,12 @@ export class SkeletonRenderer {
 	 */
 	render (canvas: Canvas, skeleton: Skeleton | SkeletonDrawable) {
 		if (skeleton instanceof SkeletonDrawable) skeleton = skeleton.skeleton;
-		let clipper = this.clipper;
-		let drawOrder = skeleton.drawOrder;
-		let skeletonColor = skeleton.color;
+		const clipper = this.clipper;
+		const drawOrder = skeleton.drawOrder;
+		const skeletonColor = skeleton.color;
 
 		for (let i = 0, n = drawOrder.length; i < n; i++) {
-			let slot = drawOrder[i];
+			const slot = drawOrder[i];
 			if (!slot.bone.active) {
 				clipper.clipEnd(slot);
 				continue;
@@ -245,20 +265,19 @@ export class SkeletonRenderer {
 			let attachmentColor: Color;
 			let numVertices = 0;
 			if (attachment instanceof RegionAttachment) {
-				let region = attachment as RegionAttachment;
-				positions = positions.length < 8 ? Utils.newFloatArray(8) : positions;
+				const region = attachment;
 				numVertices = 4;
 				region.computeWorldVertices(slot, positions, 0, 2);
 				triangles = SkeletonRenderer.QUAD_TRIANGLES;
 				uvs = region.uvs as Float32Array;
-				texture = region.region?.texture as CanvasKitTexture;
+				texture = region.region ?.texture as CanvasKitTexture;
 				attachmentColor = region.color;
 			} else if (attachment instanceof MeshAttachment) {
-				let mesh = attachment as MeshAttachment;
-				positions =
-					positions.length < mesh.worldVerticesLength
-						? Utils.newFloatArray(mesh.worldVerticesLength)
-						: positions;
+				const mesh = attachment as MeshAttachment;
+				if (positions.length < mesh.worldVerticesLength) {
+					this.scratchPositions = Utils.newFloatArray(mesh.worldVerticesLength);
+					positions = this.scratchPositions;
+				}
 				numVertices = mesh.worldVerticesLength >> 1;
 				mesh.computeWorldVertices(
 					skeleton,
@@ -270,7 +289,7 @@ export class SkeletonRenderer {
 					2
 				);
 				triangles = mesh.triangles;
-				texture = mesh.region?.texture as CanvasKitTexture;
+				texture = mesh.region ?.texture as CanvasKitTexture;
 				uvs = mesh.uvs as Float32Array;
 				attachmentColor = mesh.color;
 			} else if (attachment instanceof ClippingAttachment) {
@@ -283,27 +302,47 @@ export class SkeletonRenderer {
 			}
 
 			if (texture) {
+				let scaledUvs: NumberArrayLike;
 				if (clipper.isClipping()) {
-					clipper.clipTrianglesUnpacked(
-						positions,
-						triangles,
-						triangles.length,
-						uvs
-					);
+					clipper.clipTrianglesUnpacked(positions, triangles, triangles.length, uvs);
+					if (clipper.clippedVertices.length <= 0) {
+						clipper.clipEnd(slot);
+						continue;
+					}
 					positions = clipper.clippedVertices;
 					uvs = clipper.clippedUVs;
+					scaledUvs = clipper.clippedUVs;
 					triangles = clipper.clippedTriangles;
+					numVertices = clipper.clippedVertices.length / 2;
+					colors = Utils.newFloatArray(numVertices * 4);
+				} else {
+					scaledUvs = this.scratchUVs;
+					if (this.scratchUVs.length < uvs.length) {
+						this.scratchUVs = Utils.newFloatArray(uvs.length);
+						scaledUvs = this.scratchUVs;
+					}
+					if (colors.length / 4 < numVertices) {
+						this.scratchColors = Utils.newFloatArray(numVertices * 4);
+						colors = this.scratchColors;
+					}
 				}
 
-				let slotColor = pose.color;
-				let finalColor = this.tempColor;
+				const ckImage = texture.getImage();
+				const image = ckImage.image;
+				const width = image.width();
+				const height = image.height();
+				for (let i = 0; i < uvs.length; i += 2) {
+					scaledUvs[i] = uvs[i] * width;
+					scaledUvs[i + 1] = uvs[i + 1] * height;
+				}
+
+				const slotColor = pose.color;
+				const finalColor = this.tempColor;
 				finalColor.r = skeletonColor.r * slotColor.r * attachmentColor.r;
 				finalColor.g = skeletonColor.g * slotColor.g * attachmentColor.g;
 				finalColor.b = skeletonColor.b * slotColor.b * attachmentColor.b;
 				finalColor.a = skeletonColor.a * slotColor.a * attachmentColor.a;
 
-				if (colors.length / 4 < numVertices)
-					colors = Utils.newFloatArray(numVertices * 4);
 				for (let i = 0, n = numVertices * 4; i < n; i += 4) {
 					colors[i] = finalColor.r;
 					colors[i + 1] = finalColor.g;
@@ -311,18 +350,6 @@ export class SkeletonRenderer {
 					colors[i + 3] = finalColor.a;
 				}
 
-				const scaledUvs =
-					this.scratchUVs.length < uvs.length
-						? Utils.newFloatArray(uvs.length)
-						: this.scratchUVs;
-				const width = texture.getImage().image.width();
-				const height = texture.getImage().image.height();
-				for (let i = 0; i < uvs.length; i += 2) {
-					scaledUvs[i] = uvs[i] * width;
-					scaledUvs[i + 1] = uvs[i + 1] * height;
-				}
-
-				const blendMode = slot.data.blendMode;
 				const vertices = this.ck.MakeVertices(
 					this.ck.VertexMode.Triangles,
 					positions,
@@ -331,11 +358,8 @@ export class SkeletonRenderer {
 					triangles,
 					false
 				);
-				canvas.drawVertices(
-					vertices,
-					this.ck.BlendMode.Modulate,
-					texture.getImage().paintPerBlendMode.get(blendMode)!
-				);
+				const ckPaint = ckImage.paintPerBlendMode.get(slot.data.blendMode);
+				if (ckPaint) canvas.drawVertices(vertices, this.ck.BlendMode.Modulate, ckPaint);
 				vertices.delete();
 			}