mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-20 17:26:01 +08:00
[ts][canvaskit] Properly fix vertices info misalignment that led to crash when clipping (0b426772).
This commit is contained in:
parent
a0c9d6a6aa
commit
532fdb87eb
@ -37,27 +37,20 @@ import {
|
|||||||
ClippingAttachment,
|
ClippingAttachment,
|
||||||
Color,
|
Color,
|
||||||
MeshAttachment,
|
MeshAttachment,
|
||||||
NumberArrayLike,
|
type NumberArrayLike,
|
||||||
Physics,
|
Physics,
|
||||||
RegionAttachment,
|
RegionAttachment,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
SkeletonBinary,
|
SkeletonBinary,
|
||||||
SkeletonClipping,
|
SkeletonClipping,
|
||||||
SkeletonData,
|
type SkeletonData,
|
||||||
SkeletonJson,
|
SkeletonJson,
|
||||||
Texture,
|
Texture,
|
||||||
TextureAtlas,
|
TextureAtlas,
|
||||||
TextureFilter,
|
|
||||||
TextureWrap,
|
|
||||||
Utils,
|
Utils,
|
||||||
} from "@esotericsoftware/spine-core";
|
} from "@esotericsoftware/spine-core";
|
||||||
import {
|
|
||||||
Canvas,
|
import type { Canvas, CanvasKit, Image, Paint, Shader } from "canvaskit-wasm";
|
||||||
CanvasKit,
|
|
||||||
Image,
|
|
||||||
Paint,
|
|
||||||
Shader,
|
|
||||||
} from "canvaskit-wasm";
|
|
||||||
|
|
||||||
Skeleton.yDown = true;
|
Skeleton.yDown = true;
|
||||||
|
|
||||||
@ -83,7 +76,7 @@ function toCkBlendMode (ck: CanvasKit, blendMode: BlendMode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bufferToUtf8String (buffer: any) {
|
function bufferToUtf8String (buffer: ArrayBuffer | Buffer) {
|
||||||
if (typeof Buffer !== "undefined") {
|
if (typeof Buffer !== "undefined") {
|
||||||
return buffer.toString("utf-8");
|
return buffer.toString("utf-8");
|
||||||
} else if (typeof TextDecoder !== "undefined") {
|
} else if (typeof TextDecoder !== "undefined") {
|
||||||
@ -98,9 +91,9 @@ class CanvasKitTexture extends Texture {
|
|||||||
return this._image;
|
return this._image;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFilters (minFilter: TextureFilter, magFilter: TextureFilter): void { }
|
setFilters (): void { }
|
||||||
|
|
||||||
setWraps (uWrap: TextureWrap, vWrap: TextureWrap): void { }
|
setWraps (): void { }
|
||||||
|
|
||||||
dispose (): void {
|
dispose (): void {
|
||||||
const data: CanvasKitImage = this._image;
|
const data: CanvasKitImage = this._image;
|
||||||
@ -117,7 +110,7 @@ class CanvasKitTexture extends Texture {
|
|||||||
static async fromFile (
|
static async fromFile (
|
||||||
ck: CanvasKit,
|
ck: CanvasKit,
|
||||||
path: string,
|
path: string,
|
||||||
readFile: (path: string) => Promise<any>
|
readFile: (path: string) => Promise<ArrayBuffer | Buffer>
|
||||||
): Promise<CanvasKitTexture> {
|
): Promise<CanvasKitTexture> {
|
||||||
const imgData = await readFile(path);
|
const imgData = await readFile(path);
|
||||||
if (!imgData) throw new Error(`Could not load image ${path}`);
|
if (!imgData) throw new Error(`Could not load image ${path}`);
|
||||||
@ -154,7 +147,7 @@ class CanvasKitTexture extends Texture {
|
|||||||
export async function loadTextureAtlas (
|
export async function loadTextureAtlas (
|
||||||
ck: CanvasKit,
|
ck: CanvasKit,
|
||||||
atlasFile: string,
|
atlasFile: string,
|
||||||
readFile: (path: string) => Promise<Buffer>
|
readFile: (path: string) => Promise<ArrayBuffer | Buffer>
|
||||||
): Promise<TextureAtlas> {
|
): Promise<TextureAtlas> {
|
||||||
const atlas = new TextureAtlas(bufferToUtf8String(await readFile(atlasFile)));
|
const atlas = new TextureAtlas(bufferToUtf8String(await readFile(atlasFile)));
|
||||||
const slashIndex = atlasFile.lastIndexOf("/");
|
const slashIndex = atlasFile.lastIndexOf("/");
|
||||||
@ -178,7 +171,7 @@ export async function loadTextureAtlas (
|
|||||||
export async function loadSkeletonData (
|
export async function loadSkeletonData (
|
||||||
skeletonFile: string,
|
skeletonFile: string,
|
||||||
atlas: TextureAtlas,
|
atlas: TextureAtlas,
|
||||||
readFile: (path: string) => Promise<Buffer>,
|
readFile: (path: string) => Promise<ArrayBuffer | Buffer>,
|
||||||
scale = 1
|
scale = 1
|
||||||
): Promise<SkeletonData> {
|
): Promise<SkeletonData> {
|
||||||
const attachmentLoader = new AtlasAttachmentLoader(atlas);
|
const attachmentLoader = new AtlasAttachmentLoader(atlas);
|
||||||
@ -186,12 +179,11 @@ export async function loadSkeletonData (
|
|||||||
? new SkeletonJson(attachmentLoader)
|
? new SkeletonJson(attachmentLoader)
|
||||||
: new SkeletonBinary(attachmentLoader);
|
: new SkeletonBinary(attachmentLoader);
|
||||||
loader.scale = scale;
|
loader.scale = scale;
|
||||||
let data = await readFile(skeletonFile);
|
const data = await readFile(skeletonFile);
|
||||||
if (skeletonFile.endsWith(".json")) {
|
if (loader instanceof SkeletonJson) {
|
||||||
data = bufferToUtf8String(data);
|
return loader.readSkeletonData(bufferToUtf8String(data))
|
||||||
}
|
}
|
||||||
const skeletonData = loader.readSkeletonData(data);
|
return loader.readSkeletonData(data);
|
||||||
return skeletonData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -252,18 +244,18 @@ export class SkeletonRenderer {
|
|||||||
*/
|
*/
|
||||||
render (canvas: Canvas, skeleton: Skeleton | SkeletonDrawable) {
|
render (canvas: Canvas, skeleton: Skeleton | SkeletonDrawable) {
|
||||||
if (skeleton instanceof SkeletonDrawable) skeleton = skeleton.skeleton;
|
if (skeleton instanceof SkeletonDrawable) skeleton = skeleton.skeleton;
|
||||||
let clipper = this.clipper;
|
const clipper = this.clipper;
|
||||||
let drawOrder = skeleton.drawOrder;
|
const drawOrder = skeleton.drawOrder;
|
||||||
let skeletonColor = skeleton.color;
|
const skeletonColor = skeleton.color;
|
||||||
|
|
||||||
for (let i = 0, n = drawOrder.length; i < n; i++) {
|
for (let i = 0, n = drawOrder.length; i < n; i++) {
|
||||||
let slot = drawOrder[i];
|
const slot = drawOrder[i];
|
||||||
if (!slot.bone.active) {
|
if (!slot.bone.active) {
|
||||||
clipper.clipEndWithSlot(slot);
|
clipper.clipEndWithSlot(slot);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let attachment = slot.getAttachment();
|
const attachment = slot.getAttachment();
|
||||||
let positions = this.scratchPositions;
|
let positions = this.scratchPositions;
|
||||||
let colors = this.scratchColors;
|
let colors = this.scratchColors;
|
||||||
let uvs: NumberArrayLike;
|
let uvs: NumberArrayLike;
|
||||||
@ -272,18 +264,19 @@ export class SkeletonRenderer {
|
|||||||
let attachmentColor: Color;
|
let attachmentColor: Color;
|
||||||
let numVertices = 0;
|
let numVertices = 0;
|
||||||
if (attachment instanceof RegionAttachment) {
|
if (attachment instanceof RegionAttachment) {
|
||||||
let region = attachment as RegionAttachment;
|
const region = attachment;
|
||||||
if (positions.length < 8) this.scratchPositions = positions = Utils.newFloatArray(8);
|
|
||||||
numVertices = 4;
|
numVertices = 4;
|
||||||
region.computeWorldVertices(slot, positions, 0, 2);
|
region.computeWorldVertices(slot, positions, 0, 2);
|
||||||
triangles = SkeletonRenderer.QUAD_TRIANGLES;
|
triangles = SkeletonRenderer.QUAD_TRIANGLES;
|
||||||
uvs = region.uvs as Float32Array;
|
uvs = region.uvs as Float32Array;
|
||||||
texture = region.region?.texture as CanvasKitTexture;
|
texture = region.region ?.texture as CanvasKitTexture;
|
||||||
attachmentColor = region.color;
|
attachmentColor = region.color;
|
||||||
} else if (attachment instanceof MeshAttachment) {
|
} else if (attachment instanceof MeshAttachment) {
|
||||||
let mesh = attachment as MeshAttachment;
|
const mesh = attachment as MeshAttachment;
|
||||||
if (positions.length < mesh.worldVerticesLength)
|
if (positions.length < mesh.worldVerticesLength) {
|
||||||
this.scratchPositions = positions = Utils.newFloatArray(mesh.worldVerticesLength);
|
this.scratchPositions = Utils.newFloatArray(mesh.worldVerticesLength);
|
||||||
|
positions = this.scratchPositions;
|
||||||
|
}
|
||||||
numVertices = mesh.worldVerticesLength >> 1;
|
numVertices = mesh.worldVerticesLength >> 1;
|
||||||
mesh.computeWorldVertices(
|
mesh.computeWorldVertices(
|
||||||
slot,
|
slot,
|
||||||
@ -294,11 +287,11 @@ export class SkeletonRenderer {
|
|||||||
2
|
2
|
||||||
);
|
);
|
||||||
triangles = mesh.triangles;
|
triangles = mesh.triangles;
|
||||||
texture = mesh.region?.texture as CanvasKitTexture;
|
texture = mesh.region ?.texture as CanvasKitTexture;
|
||||||
uvs = mesh.uvs as Float32Array;
|
uvs = mesh.uvs as Float32Array;
|
||||||
attachmentColor = mesh.color;
|
attachmentColor = mesh.color;
|
||||||
} else if (attachment instanceof ClippingAttachment) {
|
} else if (attachment instanceof ClippingAttachment) {
|
||||||
let clip = attachment as ClippingAttachment;
|
const clip = attachment as ClippingAttachment;
|
||||||
clipper.clipStart(slot, clip);
|
clipper.clipStart(slot, clip);
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
@ -307,41 +300,47 @@ export class SkeletonRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (texture) {
|
if (texture) {
|
||||||
|
let scaledUvs: NumberArrayLike;
|
||||||
if (clipper.isClipping()) {
|
if (clipper.isClipping()) {
|
||||||
clipper.clipTrianglesUnpacked(
|
clipper.clipTrianglesUnpacked(positions, triangles, triangles.length, uvs);
|
||||||
positions,
|
if (clipper.clippedVertices.length <= 0) {
|
||||||
triangles,
|
|
||||||
triangles.length,
|
|
||||||
uvs
|
|
||||||
);
|
|
||||||
|
|
||||||
if (clipper.clippedVertices.length > 0) {
|
|
||||||
// clipping eventually "erases" clippedVertices array (set length to 0 at next clip)
|
|
||||||
// it's necessary to keep the array alive until canvaskit paints,
|
|
||||||
// otherwise a memory access occurs
|
|
||||||
if (positions.length < clipper.clippedVertices.length)
|
|
||||||
this.scratchPositions = positions = Utils.newFloatArray(clipper.clippedVertices.length);
|
|
||||||
numVertices = clipper.clippedVertices.length / 2;
|
|
||||||
for (let i = 0; i < clipper.clippedVertices.length; i++)
|
|
||||||
positions[i] = clipper.clippedVertices[i];
|
|
||||||
|
|
||||||
uvs = clipper.clippedUVs;
|
|
||||||
triangles = clipper.clippedTriangles;
|
|
||||||
} else {
|
|
||||||
clipper.clipEndWithSlot(slot);
|
clipper.clipEndWithSlot(slot);
|
||||||
continue;
|
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 = slot.color;
|
const ckImage = texture.getImage();
|
||||||
let finalColor = this.tempColor;
|
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 = slot.color;
|
||||||
|
const finalColor = this.tempColor;
|
||||||
finalColor.r = skeletonColor.r * slotColor.r * attachmentColor.r;
|
finalColor.r = skeletonColor.r * slotColor.r * attachmentColor.r;
|
||||||
finalColor.g = skeletonColor.g * slotColor.g * attachmentColor.g;
|
finalColor.g = skeletonColor.g * slotColor.g * attachmentColor.g;
|
||||||
finalColor.b = skeletonColor.b * slotColor.b * attachmentColor.b;
|
finalColor.b = skeletonColor.b * slotColor.b * attachmentColor.b;
|
||||||
finalColor.a = skeletonColor.a * slotColor.a * attachmentColor.a;
|
finalColor.a = skeletonColor.a * slotColor.a * attachmentColor.a;
|
||||||
|
|
||||||
if (colors.length / 4 < numVertices)
|
|
||||||
this.scratchColors = colors = Utils.newFloatArray(numVertices * 4);
|
|
||||||
for (let i = 0, n = numVertices * 4; i < n; i += 4) {
|
for (let i = 0, n = numVertices * 4; i < n; i += 4) {
|
||||||
colors[i] = finalColor.r;
|
colors[i] = finalColor.r;
|
||||||
colors[i + 1] = finalColor.g;
|
colors[i + 1] = finalColor.g;
|
||||||
@ -349,17 +348,6 @@ export class SkeletonRenderer {
|
|||||||
colors[i + 3] = finalColor.a;
|
colors[i + 3] = finalColor.a;
|
||||||
}
|
}
|
||||||
|
|
||||||
const scaledUvs = this.scratchUVs.length < uvs.length
|
|
||||||
? (this.scratchUVs = 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(
|
const vertices = this.ck.MakeVertices(
|
||||||
this.ck.VertexMode.Triangles,
|
this.ck.VertexMode.Triangles,
|
||||||
positions,
|
positions,
|
||||||
@ -368,11 +356,8 @@ export class SkeletonRenderer {
|
|||||||
triangles,
|
triangles,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
canvas.drawVertices(
|
const ckPaint = ckImage.paintPerBlendMode.get(slot.data.blendMode);
|
||||||
vertices,
|
if (ckPaint) canvas.drawVertices(vertices, this.ck.BlendMode.Modulate, ckPaint);
|
||||||
this.ck.BlendMode.Modulate,
|
|
||||||
texture.getImage().paintPerBlendMode.get(blendMode)!
|
|
||||||
);
|
|
||||||
vertices.delete();
|
vertices.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user