918 lines
26 KiB
JavaScript
918 lines
26 KiB
JavaScript
/**
|
||
* 增强版液化管理器
|
||
* 整合WebGL和CPU实现,智能选择最佳渲染方式
|
||
*/
|
||
import { LiquifyWebGLManager } from "./LiquifyWebGLManager";
|
||
import { LiquifyCPUManager } from "./LiquifyCPUManager";
|
||
import { findLayerRecursively, LayerType } from "../../utils/layerHelper";
|
||
import i18n from "@/lang/index.ts";
|
||
const {t} = i18n.global;
|
||
|
||
export class EnhancedLiquifyManager {
|
||
/**
|
||
* 创建增强版液化管理器
|
||
* @param {Object} options 配置选项
|
||
*/
|
||
constructor(options = {}) {
|
||
this.config = {
|
||
// 性能阈值:图像超过此尺寸会尝试使用WebGL
|
||
webglSizeThreshold: options.webglSizeThreshold || 1000 * 1000, // 默认100万像素
|
||
// 是否强制使用CPU模式
|
||
forceCPU: options.forceCPU || false,
|
||
// 是否强制使用WebGL模式
|
||
forceWebGL: options.forceWebGL || false,
|
||
// 网格大小
|
||
gridSize: options.gridSize || 15,
|
||
// 最大变形强度
|
||
maxStrength: options.maxStrength || 100,
|
||
// 平滑迭代次数
|
||
smoothingIterations: options.smoothingIterations || 2,
|
||
// 网格弹性因子
|
||
relaxFactor: options.relaxFactor || 0.25,
|
||
// WebGL网格精度
|
||
meshResolution: options.meshResolution || 64,
|
||
};
|
||
|
||
// 性能监控
|
||
this.performance = {
|
||
lastOperationTime: 0,
|
||
renderTimes: [], // 最近的渲染时间记录
|
||
isPerformanceIssue: false, // 是否存在性能问题
|
||
operationCount: 0, // 操作计数
|
||
};
|
||
|
||
// 初始化标志
|
||
this.initialized = false;
|
||
|
||
// 当前参数
|
||
this.params = {
|
||
size: 50, // 工具尺寸
|
||
pressure: 0.5, // 压力大小 (0-1)
|
||
distortion: 0, // 失真程度 (0-1)
|
||
power: 0.5, // 动力/强度 (0-1)
|
||
};
|
||
|
||
// 液化工具模式
|
||
this.modes = {
|
||
PUSH: "push",
|
||
CLOCKWISE: "clockwise",
|
||
COUNTERCLOCKWISE: "counterclockwise",
|
||
PINCH: "pinch",
|
||
EXPAND: "expand",
|
||
CRYSTAL: "crystal",
|
||
EDGE: "edge",
|
||
RECONSTRUCT: "reconstruct",
|
||
};
|
||
|
||
// 当前模式
|
||
this.currentMode = this.modes.PUSH;
|
||
|
||
// 图像数据和目标对象
|
||
this.originalImageData = null;
|
||
this.currentImageData = null;
|
||
this.targetObject = null;
|
||
this.targetLayerId = null;
|
||
|
||
// 创建渲染器实例
|
||
this.webglRenderer = null;
|
||
this.cpuRenderer = null;
|
||
|
||
// 当前激活的渲染器
|
||
this.activeRenderer = null;
|
||
this.renderMode = "unknown"; // 'webgl', 'cpu', 'unknown'
|
||
|
||
// 画布和管理器引用
|
||
this.canvas = options.canvas || null;
|
||
this.layerManager = options.layerManager || null;
|
||
|
||
// 渲染器状态
|
||
this.isWebGLAvailable = LiquifyWebGLManager.isSupported();
|
||
}
|
||
|
||
/**
|
||
* 初始化液化管理器
|
||
* @param {Object} options 配置选项
|
||
* @returns {Boolean} 是否初始化成功
|
||
*/
|
||
initialize(options = {}) {
|
||
if (options.canvas) this.canvas = options.canvas;
|
||
if (options.layerManager) this.layerManager = options.layerManager;
|
||
|
||
if (!this.canvas || !this.layerManager) {
|
||
console.error("液化管理器初始化失败:缺少canvas或layerManager");
|
||
return false;
|
||
}
|
||
|
||
// 记录初始化时间,用于性能监控
|
||
this.performance.lastInitTime = Date.now();
|
||
|
||
// 创建CPU渲染器 (始终创建作为备选)
|
||
this.cpuRenderer = new LiquifyCPUManager({
|
||
gridSize: this.config.gridSize,
|
||
maxStrength: this.config.maxStrength,
|
||
smoothingIterations: this.config.smoothingIterations,
|
||
relaxFactor: this.config.relaxFactor,
|
||
});
|
||
|
||
// 检查是否应创建WebGL渲染器
|
||
if (this.isWebGLAvailable && !this.config.forceCPU) {
|
||
this.webglRenderer = new LiquifyWebGLManager({
|
||
gridSize: this.config.gridSize,
|
||
maxStrength: this.config.maxStrength,
|
||
meshResolution: this.config.meshResolution,
|
||
});
|
||
}
|
||
|
||
this.initialized = true;
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 为液化操作准备图像
|
||
* @param {Object|String} target 目标对象或图层ID
|
||
* @returns {Promise<Object>} 准备结果
|
||
*/
|
||
async prepareForLiquify(target) {
|
||
if (!this.initialized) {
|
||
throw new Error("液化管理器未初始化");
|
||
}
|
||
|
||
let targetObject, targetLayerId;
|
||
|
||
// 处理传入的是图层ID的情况
|
||
if (typeof target === "string") {
|
||
targetLayerId = target;
|
||
const { layer } = findLayerRecursively(
|
||
this.layerManager.layers?.value ?? this.layerManager.layers,
|
||
targetLayerId
|
||
);
|
||
|
||
// 检查图层是否存在和是否有对象
|
||
let hasObjects = false;
|
||
if (layer) {
|
||
if (layer.type === "background" && layer.fabricObject) {
|
||
hasObjects = true;
|
||
targetObject = layer.fabricObject;
|
||
} else if (layer.fabricObjects && layer.fabricObjects.length > 0) {
|
||
hasObjects = true;
|
||
targetObject = layer.fabricObjects[0];
|
||
}
|
||
}
|
||
|
||
if (!hasObjects) {
|
||
throw new Error("目标图层为空或不存在");
|
||
}
|
||
} else if (typeof target === "object") {
|
||
// 传入的是对象
|
||
targetObject = target;
|
||
const { layer } = findLayerRecursively(
|
||
this.layerManager.layers?.value ?? this.layerManager.layers,
|
||
targetObject.layerId
|
||
);
|
||
if (layer) {
|
||
targetLayerId = layer.id;
|
||
} else {
|
||
throw new Error("无法找到目标对象所属图层");
|
||
}
|
||
} else {
|
||
throw new Error("无效的目标参数");
|
||
}
|
||
|
||
// 检查是否为图像对象
|
||
if (!targetObject || targetObject.type !== "image") {
|
||
throw new Error("目标对象不是图像,无法进行液化操作");
|
||
}
|
||
|
||
// 保存目标对象引用
|
||
this.targetObject = targetObject;
|
||
this.targetLayerId = targetLayerId;
|
||
|
||
// 获取图像数据
|
||
const imageData = await this._getImageData(targetObject);
|
||
if (!imageData) {
|
||
throw new Error("无法获取图像数据");
|
||
}
|
||
|
||
// 保存原始图像数据
|
||
this.originalImageData = imageData;
|
||
this.currentImageData = this._cloneImageData(imageData);
|
||
|
||
// 检查图像大小,选择适合的渲染器
|
||
await this._selectRenderer(imageData);
|
||
|
||
// 预热选定的渲染器
|
||
await this._warmupRenderer(imageData);
|
||
|
||
return {
|
||
targetObject: this.targetObject,
|
||
targetLayerId: this.targetLayerId,
|
||
imageData: this.currentImageData,
|
||
originalImageData: this.originalImageData,
|
||
renderMode: this.renderMode,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 根据图像大小和设备性能选择渲染器
|
||
* @param {ImageData} imageData 图像数据
|
||
* @private
|
||
*/
|
||
async _selectRenderer(imageData) {
|
||
// 计算图像大小
|
||
const pixelCount = imageData.width * imageData.height;
|
||
|
||
console.log(
|
||
`液化选择渲染器: 图像大小=${pixelCount}像素, WebGL可用=${this.isWebGLAvailable}`
|
||
);
|
||
|
||
// 默认使用CPU渲染器
|
||
this.activeRenderer = this.cpuRenderer;
|
||
this.renderMode = "cpu";
|
||
|
||
// 如果配置强制使用WebGL
|
||
if (this.config.forceWebGL && this.isWebGLAvailable && this.webglRenderer) {
|
||
console.log("液化功能: 强制使用WebGL渲染模式");
|
||
this.activeRenderer = this.webglRenderer;
|
||
this.renderMode = "webgl";
|
||
return;
|
||
}
|
||
|
||
// 如果配置强制使用CPU
|
||
if (this.config.forceCPU) {
|
||
console.log("液化功能: 强制使用CPU渲染模式");
|
||
return;
|
||
}
|
||
|
||
// 根据图像大小和WebGL可用性决定
|
||
if (
|
||
pixelCount > this.config.webglSizeThreshold / 2 && // 降低阈值,让更多尺寸的图像使用WebGL
|
||
this.isWebGLAvailable &&
|
||
this.webglRenderer
|
||
) {
|
||
// 切换到WebGL渲染器
|
||
console.log("液化功能: 自动选择WebGL渲染模式(基于图像尺寸)");
|
||
this.activeRenderer = this.webglRenderer;
|
||
this.renderMode = "webgl";
|
||
} else {
|
||
console.log(
|
||
`液化功能: 使用CPU渲染模式${
|
||
!this.isWebGLAvailable ? " (WebGL不可用)" : ""
|
||
}`
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 预热渲染器
|
||
* @param {ImageData} imageData 图像数据
|
||
* @private
|
||
*/
|
||
async _warmupRenderer(imageData) {
|
||
// 创建图像元素
|
||
const img = document.createElement("img");
|
||
|
||
// 将ImageData转换为URL
|
||
const canvas = document.createElement("canvas");
|
||
canvas.width = imageData.width;
|
||
canvas.height = imageData.height;
|
||
const ctx = canvas.getContext("2d");
|
||
ctx.putImageData(imageData, 0, 0);
|
||
|
||
// 使用Promise等待图像加载
|
||
await new Promise((resolve, reject) => {
|
||
img.onload = resolve;
|
||
img.onerror = reject;
|
||
img.src = canvas.toDataURL();
|
||
});
|
||
|
||
// 初始化当前渲染器
|
||
if (this.activeRenderer) {
|
||
if (this.renderMode === "webgl") {
|
||
this.activeRenderer.initialize(img);
|
||
} else {
|
||
this.activeRenderer.initialize(imageData);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设置液化模式
|
||
* @param {String} mode 模式名称
|
||
*/
|
||
setMode(mode) {
|
||
if (Object.values(this.modes).includes(mode)) {
|
||
this.currentMode = mode;
|
||
|
||
// 同步更新当前渲染器
|
||
if (this.activeRenderer) {
|
||
this.activeRenderer.setMode(mode);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 设置液化参数
|
||
* @param {String} param 参数名称
|
||
* @param {Number} value 参数值
|
||
*/
|
||
setParam(param, value) {
|
||
if (param in this.params) {
|
||
this.params[param] = value;
|
||
|
||
// 同步更新当前渲染器 - 关键修复:确保参数正确传递
|
||
if (
|
||
this.activeRenderer &&
|
||
typeof this.activeRenderer.setParam === "function"
|
||
) {
|
||
console.log(`EnhancedLiquifyManager 设置参数: ${param}=${value}`);
|
||
this.activeRenderer.setParam(param, value);
|
||
} else {
|
||
console.warn(
|
||
`EnhancedLiquifyManager: 无法设置参数 ${param},渲染器未就绪`
|
||
);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
console.warn(`EnhancedLiquifyManager: 无效参数 ${param}`);
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 批量设置参数
|
||
* @param {Object} params 参数对象
|
||
*/
|
||
setParams(params) {
|
||
console.log("EnhancedLiquifyManager 批量设置参数:", params);
|
||
|
||
if (params && typeof params === "object") {
|
||
Object.entries(params).forEach(([key, value]) => {
|
||
this.setParam(key, value);
|
||
});
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取当前参数
|
||
* @returns {Object} 当前参数对象
|
||
*/
|
||
getParams() {
|
||
return { ...this.params };
|
||
}
|
||
|
||
/**
|
||
* 重置参数为默认值
|
||
*/
|
||
resetParams() {
|
||
this.params = {
|
||
size: 50,
|
||
pressure: 0.5,
|
||
distortion: 0,
|
||
power: 0.5,
|
||
};
|
||
|
||
// 同步更新当前渲染器
|
||
if (this.activeRenderer) {
|
||
this.activeRenderer.resetParams();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 开始液化操作(记录初始点)
|
||
* @param {Number} x 初始X坐标
|
||
* @param {Number} y 初始Y坐标
|
||
*/
|
||
startLiquifyOperation(x, y) {
|
||
if (
|
||
this.activeRenderer &&
|
||
typeof this.activeRenderer.startDeformation === "function"
|
||
) {
|
||
this.activeRenderer.startDeformation(x, y);
|
||
}
|
||
console.log(
|
||
`开始液化操作,渲染模式=${this.renderMode}, 初始点: (${x}, ${y})`
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 结束液化操作
|
||
*/
|
||
endLiquifyOperation() {
|
||
if (
|
||
this.activeRenderer &&
|
||
typeof this.activeRenderer.endDeformation === "function"
|
||
) {
|
||
this.activeRenderer.endDeformation();
|
||
}
|
||
console.log(`结束液化操作,渲染模式=${this.renderMode}`);
|
||
}
|
||
|
||
/**
|
||
* 应用液化变形
|
||
* @param {Object} target 目标对象
|
||
* @param {String} mode 液化模式
|
||
* @param {Object} params 液化参数
|
||
* @param {Number} x 操作中心点X坐标 (图像像素坐标)
|
||
* @param {Number} y 操作中心点Y坐标 (图像像素坐标)
|
||
* @returns {Promise<ImageData>} 处理后的图像数据
|
||
*/
|
||
async applyLiquify(target, mode, params, x, y) {
|
||
// 性能追踪开始
|
||
const startTime = performance.now();
|
||
|
||
// 如果首次调用,先准备环境
|
||
if (!this.targetObject || this.targetObject !== target) {
|
||
await this.prepareForLiquify(target);
|
||
}
|
||
|
||
// 更新模式和参数
|
||
if (mode) this.setMode(mode);
|
||
if (params) {
|
||
for (const [key, value] of Object.entries(params)) {
|
||
this.setParam(key, value);
|
||
}
|
||
}
|
||
|
||
// 验证坐标是否在图像范围内
|
||
if (!this.originalImageData) {
|
||
console.error("缺少原始图像数据");
|
||
return null;
|
||
}
|
||
|
||
const imageWidth = this.originalImageData.width;
|
||
const imageHeight = this.originalImageData.height;
|
||
|
||
// 坐标边界检查
|
||
if (x < 0 || x >= imageWidth || y < 0 || y >= imageHeight) {
|
||
console.warn(
|
||
`液化坐标超出图像范围: (${x}, ${y}), 图像尺寸: ${imageWidth}x${imageHeight}`
|
||
);
|
||
return null;
|
||
}
|
||
|
||
console.log(
|
||
`应用液化变形: 模式=${mode}, 图像坐标=(${x}, ${y}), 图像尺寸=${imageWidth}x${imageHeight}`
|
||
);
|
||
|
||
// 检查并应用变形
|
||
if (this.activeRenderer && typeof x === "number" && typeof y === "number") {
|
||
// 应用变形
|
||
let result;
|
||
|
||
if (this.renderMode === "webgl") {
|
||
// WebGL渲染器:传入图像像素坐标
|
||
result = this.activeRenderer.applyDeformation(x, y);
|
||
} else {
|
||
// CPU渲染器:传入图像像素坐标
|
||
result = this.activeRenderer.applyDeformation(x, y);
|
||
}
|
||
|
||
// 更新当前图像数据
|
||
if (result) {
|
||
this.currentImageData = result;
|
||
}
|
||
|
||
// 性能追踪结束
|
||
const endTime = performance.now();
|
||
this._trackPerformance(endTime - startTime);
|
||
|
||
return result;
|
||
}
|
||
|
||
console.error("无法应用液化变形:渲染器未初始化或坐标无效");
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 追踪性能数据
|
||
* @param {Number} time 操作耗时(毫秒)
|
||
* @private
|
||
*/
|
||
_trackPerformance(time) {
|
||
this.performance.lastOperationTime = time;
|
||
this.performance.operationCount++;
|
||
|
||
// 维护最近10次操作的耗时记录
|
||
this.performance.renderTimes.push(time);
|
||
if (this.performance.renderTimes.length > 10) {
|
||
this.performance.renderTimes.shift();
|
||
}
|
||
|
||
// 计算平均耗时
|
||
const avgTime =
|
||
this.performance.renderTimes.reduce((sum, t) => sum + t, 0) /
|
||
this.performance.renderTimes.length;
|
||
|
||
// 检测性能问题
|
||
this.performance.isPerformanceIssue = avgTime > 100; // 如果平均耗时超过100毫秒
|
||
|
||
// 输出性能信息(调试用)
|
||
if (this.performance.operationCount % 10 === 0) {
|
||
console.log(
|
||
`液化性能数据: 模式=${this.renderMode}, 平均耗时=${avgTime.toFixed(
|
||
2
|
||
)}ms, 图像尺寸=${this.originalImageData?.width}x${
|
||
this.originalImageData?.height
|
||
}`
|
||
);
|
||
}
|
||
|
||
// 如果使用WebGL但性能差,可以考虑切换到优化的CPU实现
|
||
if (
|
||
this.renderMode === "webgl" &&
|
||
this.performance.isPerformanceIssue &&
|
||
this.performance.operationCount > 5
|
||
) {
|
||
console.warn("WebGL液化性能不佳,考虑切换到CPU模式");
|
||
// 注意:这里不自动切换,因为可能会导致中途渲染结果不一致
|
||
}
|
||
}
|
||
setRealtimeUpdater(realtimeUpdater) {
|
||
this.realtimeUpdater = realtimeUpdater;
|
||
}
|
||
/**
|
||
* 重置液化操作
|
||
* @returns {ImageData} 重置后的图像数据
|
||
*/
|
||
reset() {
|
||
if (!this.activeRenderer) return null;
|
||
|
||
// 使用当前渲染器重置
|
||
const result = this.activeRenderer.reset();
|
||
|
||
// 更新当前图像数据
|
||
if (result) {
|
||
this.currentImageData = result;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 检查图层是否可以液化
|
||
* @param {String} layerId 图层ID
|
||
* @returns {Object} 检查结果
|
||
*/
|
||
checkLayerForLiquify(layerId) {
|
||
if (!this.layerManager) {
|
||
return {
|
||
valid: false,
|
||
message: "图层管理器未初始化",
|
||
needsRasterization: false,
|
||
isImage: false,
|
||
isEmpty: true,
|
||
isGroup: false,
|
||
};
|
||
}
|
||
|
||
// 获取图层
|
||
// const layer = this.layerManager.getLayerById(layerId);
|
||
const { layer } = findLayerRecursively(
|
||
this.layerManager.layers?.value ?? this.layerManager.layers,
|
||
layerId
|
||
);
|
||
if (!layer) {
|
||
return {
|
||
valid: false,
|
||
message: "图层不存在",
|
||
needsRasterization: false,
|
||
isImage: false,
|
||
isEmpty: true,
|
||
isGroup: false,
|
||
};
|
||
}
|
||
|
||
// 检查图层是否为空
|
||
let objectsToCheck = [];
|
||
if (layer.isBackground || layer.type === "background" || layer.isFixed) {
|
||
// 背景图层使用 fabricObject (单数)
|
||
if (layer.fabricObject) {
|
||
objectsToCheck = [layer.fabricObject];
|
||
}
|
||
} else {
|
||
// 普通图层使用 fabricObjects (复数)
|
||
objectsToCheck = layer.fabricObjects || [];
|
||
}
|
||
|
||
if (objectsToCheck.length === 0) {
|
||
return {
|
||
valid: false,
|
||
message: t('Canvas.layerEmptyNoLiquidation'),
|
||
needsRasterization: false,
|
||
isImage: false,
|
||
isEmpty: true,
|
||
isGroup: false,
|
||
};
|
||
}
|
||
|
||
// 检查是否为单一图像
|
||
const singleObject = objectsToCheck.length === 1;
|
||
const isImage =
|
||
singleObject &&
|
||
(objectsToCheck[0].type === "image" ||
|
||
objectsToCheck[0].type === "rasterized-layer");
|
||
|
||
// 检查是否为组
|
||
const isGroup =
|
||
objectsToCheck.some((obj) => obj.type === "group") ||
|
||
layer.type === LayerType.GROUP ||
|
||
layer.children?.length > 0;
|
||
|
||
// 如果不是单一图像,需要栅格化
|
||
const needsRasterization = !isImage || isGroup;
|
||
|
||
return {
|
||
valid: isImage && !isGroup,
|
||
message: isImage ? "图层可以进行液化操作" : "需要先将图层栅格化",
|
||
needsRasterization: needsRasterization,
|
||
isImage: isImage,
|
||
isEmpty: false,
|
||
isGroup: isGroup,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 获取图像数据
|
||
* @param {Object} fabricObject Fabric图像对象
|
||
* @returns {Promise<ImageData>} 图像数据
|
||
* @private
|
||
*/
|
||
async _getImageData(fabricObject) {
|
||
return new Promise((resolve, reject) => {
|
||
try {
|
||
console.log("开始获取图像数据,对象类型:", fabricObject.type);
|
||
console.log("对象属性:", {
|
||
width: fabricObject.width,
|
||
height: fabricObject.height,
|
||
hasElement: !!fabricObject._element,
|
||
hasSrc: !!fabricObject.getSrc,
|
||
});
|
||
|
||
// 检查基本属性
|
||
if (!fabricObject.width || !fabricObject.height) {
|
||
reject(new Error("图像对象缺少有效的宽度或高度"));
|
||
return;
|
||
}
|
||
|
||
// 创建临时canvas - 使用原始图像尺寸,不考虑fabric对象的缩放
|
||
const tempCanvas = document.createElement("canvas");
|
||
tempCanvas.width = fabricObject.width;
|
||
tempCanvas.height = fabricObject.height;
|
||
const tempCtx = tempCanvas.getContext("2d");
|
||
|
||
console.log(
|
||
`创建临时Canvas,尺寸: ${tempCanvas.width}x${tempCanvas.height}`
|
||
);
|
||
|
||
// 处理不同的图像源
|
||
if (fabricObject._element) {
|
||
console.log("使用 _element 绘制图像");
|
||
|
||
// 检查_element是否有效
|
||
if (
|
||
!fabricObject._element.complete &&
|
||
fabricObject._element.tagName === "IMG"
|
||
) {
|
||
console.log("图像未加载完成,等待加载...");
|
||
fabricObject._element.onload = () => {
|
||
try {
|
||
tempCtx.drawImage(
|
||
fabricObject._element,
|
||
0,
|
||
0,
|
||
fabricObject.width,
|
||
fabricObject.height
|
||
);
|
||
const imageData = tempCtx.getImageData(
|
||
0,
|
||
0,
|
||
tempCanvas.width,
|
||
tempCanvas.height
|
||
);
|
||
console.log("✅ 图像加载完成后获取数据成功");
|
||
resolve(imageData);
|
||
} catch (error) {
|
||
console.error("图像加载后绘制失败:", error);
|
||
reject(error);
|
||
}
|
||
};
|
||
fabricObject._element.onerror = () => {
|
||
reject(new Error("图像加载失败"));
|
||
};
|
||
return;
|
||
}
|
||
|
||
// 直接绘制已加载的图像
|
||
tempCtx.drawImage(
|
||
fabricObject._element,
|
||
0,
|
||
0,
|
||
fabricObject.width,
|
||
fabricObject.height
|
||
);
|
||
} else if (
|
||
fabricObject.getSrc &&
|
||
typeof fabricObject.getSrc === "function"
|
||
) {
|
||
console.log("使用 getSrc() 方法获取图像源");
|
||
|
||
// 通过URL创建图像
|
||
const img = new Image();
|
||
img.crossOrigin = "anonymous"; // 避免跨域问题
|
||
|
||
img.onload = () => {
|
||
try {
|
||
console.log(
|
||
`图像加载成功,原始尺寸: ${img.naturalWidth}x${img.naturalHeight}`
|
||
);
|
||
|
||
tempCtx.drawImage(
|
||
img,
|
||
0,
|
||
0,
|
||
fabricObject.width,
|
||
fabricObject.height
|
||
);
|
||
|
||
const imageData = tempCtx.getImageData(
|
||
0,
|
||
0,
|
||
tempCanvas.width,
|
||
tempCanvas.height
|
||
);
|
||
|
||
console.log("✅ 通过URL获取图像数据成功");
|
||
resolve(imageData);
|
||
} catch (error) {
|
||
console.error("绘制图像失败:", error);
|
||
reject(error);
|
||
}
|
||
};
|
||
|
||
img.onerror = (error) => {
|
||
console.error("图像加载失败:", error);
|
||
reject(new Error("无法加载图像URL: " + fabricObject.getSrc()));
|
||
};
|
||
|
||
const srcUrl = fabricObject.getSrc();
|
||
console.log("加载图像URL:", srcUrl);
|
||
img.src = srcUrl;
|
||
return;
|
||
} else if (fabricObject.src) {
|
||
console.log("使用 src 属性获取图像源");
|
||
|
||
// 通过src属性创建图像
|
||
const img = new Image();
|
||
img.crossOrigin = "anonymous";
|
||
|
||
img.onload = () => {
|
||
try {
|
||
tempCtx.drawImage(
|
||
img,
|
||
0,
|
||
0,
|
||
fabricObject.width,
|
||
fabricObject.height
|
||
);
|
||
|
||
const imageData = tempCtx.getImageData(
|
||
0,
|
||
0,
|
||
tempCanvas.width,
|
||
tempCanvas.height
|
||
);
|
||
|
||
console.log("✅ 通过src属性获取图像数据成功");
|
||
resolve(imageData);
|
||
} catch (error) {
|
||
console.error("通过src绘制图像失败:", error);
|
||
reject(error);
|
||
}
|
||
};
|
||
|
||
img.onerror = (error) => {
|
||
console.error("通过src加载图像失败:", error);
|
||
reject(new Error("无法加载图像src: " + fabricObject.src));
|
||
};
|
||
|
||
console.log("加载图像src:", fabricObject.src);
|
||
img.src = fabricObject.src;
|
||
return;
|
||
} else {
|
||
console.error("无法找到有效的图像源");
|
||
reject(
|
||
new Error("图像对象缺少有效的图像源(_element, getSrc, 或 src)")
|
||
);
|
||
return;
|
||
}
|
||
|
||
// 如果走到这里,说明使用了_element直接绘制
|
||
try {
|
||
const imageData = tempCtx.getImageData(
|
||
0,
|
||
0,
|
||
tempCanvas.width,
|
||
tempCanvas.height
|
||
);
|
||
|
||
console.log(
|
||
`✅ 获取图像数据成功: 对象尺寸=${fabricObject.width}x${fabricObject.height}, ` +
|
||
`对象缩放=(${fabricObject.scaleX}, ${fabricObject.scaleY}), ` +
|
||
`图像数据尺寸=${imageData.width}x${imageData.height}`
|
||
);
|
||
|
||
resolve(imageData);
|
||
} catch (error) {
|
||
console.error("获取ImageData失败:", error);
|
||
reject(new Error("无法从Canvas获取图像数据: " + error.message));
|
||
}
|
||
} catch (error) {
|
||
console.error("_getImageData 执行失败:", error);
|
||
reject(error);
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 克隆图像数据
|
||
* @param {ImageData} imageData 原始图像数据
|
||
* @returns {ImageData} 克隆的图像数据
|
||
* @private
|
||
*/
|
||
_cloneImageData(imageData) {
|
||
if (!imageData) return null;
|
||
|
||
// 使用新的浏览器API直接复制
|
||
if (typeof ImageData.prototype.constructor === "function") {
|
||
try {
|
||
return new ImageData(
|
||
new Uint8ClampedArray(imageData.data),
|
||
imageData.width,
|
||
imageData.height
|
||
);
|
||
} catch (e) {
|
||
console.warn("使用备选方法克隆ImageData");
|
||
}
|
||
}
|
||
|
||
// 备选方法
|
||
const canvas = document.createElement("canvas");
|
||
const ctx = canvas.getContext("2d");
|
||
|
||
canvas.width = imageData.width;
|
||
canvas.height = imageData.height;
|
||
|
||
ctx.putImageData(imageData, 0, 0);
|
||
|
||
return ctx.getImageData(0, 0, imageData.width, imageData.height);
|
||
}
|
||
|
||
/**
|
||
* 释放资源
|
||
*/
|
||
dispose() {
|
||
// 释放渲染器资源
|
||
if (this.webglRenderer) {
|
||
this.webglRenderer.dispose();
|
||
this.webglRenderer = null;
|
||
}
|
||
|
||
if (this.cpuRenderer) {
|
||
this.cpuRenderer.dispose();
|
||
this.cpuRenderer = null;
|
||
}
|
||
|
||
// 清除引用
|
||
this.activeRenderer = null;
|
||
this.canvas = null;
|
||
this.layerManager = null;
|
||
this.targetObject = null;
|
||
this.originalImageData = null;
|
||
this.currentImageData = null;
|
||
this.initialized = false;
|
||
this.renderMode = "unknown";
|
||
}
|
||
|
||
/**
|
||
* 获取当前状态信息
|
||
* @returns {Object} 状态信息
|
||
*/
|
||
getStatus() {
|
||
return {
|
||
initialized: this.initialized,
|
||
renderMode: this.renderMode,
|
||
isWebGLAvailable: this.isWebGLAvailable,
|
||
currentMode: this.currentMode,
|
||
params: { ...this.params },
|
||
performance: { ...this.performance },
|
||
imageSize: this.originalImageData
|
||
? `${this.originalImageData.width}x${this.originalImageData.height}`
|
||
: "N/A",
|
||
};
|
||
}
|
||
}
|