/** * 增强版液化管理器 * 整合WebGL和CPU实现,智能选择最佳渲染方式 */ import { LiquifyWebGLManager } from "./LiquifyWebGLManager"; import { LiquifyCPUManager } from "./LiquifyCPUManager"; 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} 准备结果 */ async prepareForLiquify(target) { if (!this.initialized) { throw new Error("液化管理器未初始化"); } let targetObject, targetLayerId; // 处理传入的是图层ID的情况 if (typeof target === "string") { targetLayerId = target; const layer = this.layerManager.getLayerById(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 = this.layerManager.findLayerByObject(targetObject); 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) { this.activeRenderer.setParam(param, value); } return true; } return false; } /** * 获取当前参数 * @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 {Object} target 目标对象 * @param {String} mode 液化模式 * @param {Object} params 液化参数 * @param {Number} x 操作中心点X坐标 (图像像素坐标) * @param {Number} y 操作中心点Y坐标 (图像像素坐标) * @returns {Promise} 处理后的图像数据 */ 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模式"); // 注意:这里不自动切换,因为可能会导致中途渲染结果不一致 } } /** * 重置液化操作 * @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); if (!layer) { return { valid: false, message: "图层不存在", needsRasterization: false, isImage: false, isEmpty: true, isGroup: false, }; } // 检查图层是否为空 let objectsToCheck = []; if (layer.isBackground || layer.type === "background") { // 背景图层使用 fabricObject (单数) if (layer.fabricObject) { objectsToCheck = [layer.fabricObject]; } } else { // 普通图层使用 fabricObjects (复数) objectsToCheck = layer.fabricObjects || []; } if (objectsToCheck.length === 0) { return { valid: false, message: "图层为空,无法进行液化操作", 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"); // 如果不是单一图像,需要栅格化 const needsRasterization = !isImage || isGroup; return { valid: isImage && !isGroup, message: isImage ? "图层可以进行液化操作" : "需要先将图层栅格化", needsRasterization: needsRasterization, isImage: isImage, isEmpty: false, isGroup: isGroup, }; } /** * 获取图像数据 * @param {Object} fabricObject Fabric图像对象 * @returns {Promise} 图像数据 * @private */ async _getImageData(fabricObject) { return new Promise((resolve, reject) => { try { // 创建临时canvas const tempCanvas = document.createElement("canvas"); tempCanvas.width = fabricObject.width * fabricObject.scaleX; tempCanvas.height = fabricObject.height * fabricObject.scaleY; const tempCtx = tempCanvas.getContext("2d"); // 如果对象有图像元素 if (fabricObject._element) { tempCtx.drawImage( fabricObject._element, 0, 0, tempCanvas.width, tempCanvas.height ); } else if (fabricObject.getSrc) { // 通过URL创建图像 const img = new Image(); img.onload = () => { tempCtx.drawImage(img, 0, 0, tempCanvas.width, tempCanvas.height); const imageData = tempCtx.getImageData( 0, 0, tempCanvas.width, tempCanvas.height ); resolve(imageData); }; img.onerror = reject; img.src = fabricObject.getSrc(); return; } else { reject(new Error("无法获取图像数据")); return; } // 获取图像数据 const imageData = tempCtx.getImageData( 0, 0, tempCanvas.width, tempCanvas.height ); resolve(imageData); } catch (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", }; } }