Advance Blender 5.2 web parity through M12-03D
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

This commit is contained in:
mes123456
2026-08-17 17:30:27 -04:00
parent 0fe8d2bb56
commit 5a11045ca5
148 changed files with 11683 additions and 66 deletions

View File

@@ -0,0 +1,87 @@
import { planAssetPreviewDecode } from "../../../protocol/asset-preview-decode";
import type { AssetPreviewIdentityIR } from "../../../protocol/asset-preview";
export type AssetPreviewDisplayBackend = "MAIN_THREAD_CANVAS_2D" | "OFFSCREEN_CANVAS_2D";
export interface AssetPreviewDisplayReceiptIR {
schemaVersion: 1;
status: "READY";
backend: AssetPreviewDisplayBackend;
identitySha256: string;
contentSha256: string;
width: number;
height: number;
pixelByteLength: number;
pixelSha256: string;
nonTransparentPixels: number;
bitmapClosed: true;
}
export interface AssetPreviewDisplayResultIR {
receipt: AssetPreviewDisplayReceiptIR;
pixels: Uint8Array;
}
type PreviewCanvas = HTMLCanvasElement | OffscreenCanvas;
async function sha256Bytes(value: Uint8Array): Promise<string> {
const digest = await crypto.subtle.digest("SHA-256", Uint8Array.from(value).buffer);
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
}
export async function displayAssetPreview(
canvas: PreviewCanvas,
backend: AssetPreviewDisplayBackend,
identityValue: unknown,
encodedBytes: ArrayBuffer,
): Promise<AssetPreviewDisplayResultIR> {
const plan = await planAssetPreviewDecode(identityValue, encodedBytes);
if (typeof createImageBitmap !== "function") throw new Error("CAPABILITY_MISSING: createImageBitmap is unavailable");
const identity = identityValue as AssetPreviewIdentityIR;
const bitmap = await createImageBitmap(new Blob([encodedBytes], { type: plan.mimeType }), {
colorSpaceConversion: "none",
premultiplyAlpha: "none",
imageOrientation: "none",
});
try {
if (bitmap.width !== plan.width || bitmap.height !== plan.height) {
throw new Error("ASSET_PREVIEW_IDENTITY_MISMATCH: decoded preview dimensions changed");
}
canvas.width = plan.width;
canvas.height = plan.height;
const context = canvas.getContext("2d", { alpha: true, willReadFrequently: true }) as
| CanvasRenderingContext2D
| OffscreenCanvasRenderingContext2D
| null;
if (!context) throw new Error("CAPABILITY_MISSING: preview Canvas2D context is unavailable");
context.clearRect(0, 0, plan.width, plan.height);
context.imageSmoothingEnabled = false;
context.globalCompositeOperation = "copy";
context.drawImage(bitmap, 0, 0, plan.width, plan.height);
const pixels = new Uint8Array(context.getImageData(0, 0, plan.width, plan.height).data);
let nonTransparentPixels = 0;
for (let offset = 3; offset < pixels.byteLength; offset += 4) if (pixels[offset] !== 0) nonTransparentPixels++;
const receipt: AssetPreviewDisplayReceiptIR = {
schemaVersion: 1,
status: "READY",
backend,
identitySha256: identity.identitySha256,
contentSha256: identity.content.sha256,
width: plan.width,
height: plan.height,
pixelByteLength: pixels.byteLength,
pixelSha256: await sha256Bytes(pixels),
nonTransparentPixels,
bitmapClosed: true,
};
if (typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement) {
canvas.dataset.assetPreviewStatus = "ready";
canvas.dataset.assetPreviewBackend = backend;
canvas.dataset.assetPreviewPixelSha256 = receipt.pixelSha256;
}
return { receipt, pixels };
}
finally {
bitmap.close();
}
}