Js案例-Web Worker + OffscreenCanvas 高性能图像处理实现

06-01 1071阅读

Web Worker + OffscreenCanvas 高性能图像处理实现详解

文章目录

  • Web Worker + OffscreenCanvas 高性能图像处理实现详解
    • 引言
    • 技术背景
    • 实现架构
    • 代码实现
      • 主线程代码
      • Worker 线程代码
      • 常见问题及解决方案
        • 1. Worker 中使用 Image 对象
        • 2. OffscreenCanvas 保存图像问题
        • 性能优化
        • 兼容性考虑
        • 总结
        • 效果
        • 完整代码
          • HTML
          • main.js
          • worker.js
          • css

            引言

            随着网页应用的不断发展,在浏览器中进行图像处理的需求日益增长。然而,传统的图像处理方式常常导致主线程阻塞,造成用户界面卡顿。本文将详细介绍如何使用 Web Worker 和 OffscreenCanvas 这两项强大的浏览器技术,实现流畅高效的图像处理功能。

            技术背景

            Web Worker 提供了一种在浏览器中运行后台线程的方法,可以执行耗时操作而不影响用户界面响应。OffscreenCanvas 则允许将画布元素"转移"到 Web Worker 中进行操作,进一步提升性能。这两项技术相结合,为浏览器中的图像处理提供了理想的解决方案。

            实现架构

            整个图像处理应用采用了以下架构:

            1. 主线程负责用户界面交互和状态管理
            2. Web Worker 线程负责图像处理算法执行
            3. OffscreenCanvas 在 Worker 线程中进行绘制操作
            4. 主线程与 Worker 之间通过消息传递进行通信

            代码实现

            主线程代码

            首先,开发者需要在主线程中初始化 Web Worker 并创建 OffscreenCanvas:

            // 创建 Worker
            const worker = new Worker('worker.js');
            // 获取画布元素
            const displayCanvas = document.getElementById('display-canvas');
            // 创建并转移 OffscreenCanvas
            const offscreenCanvas = displayCanvas.transferControlToOffscreen();
            worker.postMessage({
                type: 'init',
                canvas: offscreenCanvas
            }, [offscreenCanvas]);  // 使用转移所有权模式
            

            这里的关键是 transferControlToOffscreen() 方法,它创建了一个 OffscreenCanvas 对象并将控制权从原始的 canvas 元素转移出去。然后通过 postMessage 将这个对象传递给 Worker,注意第二个参数 [offscreenCanvas] 表示转移所有权而非复制。

            主线程还需要设置消息处理器来接收 Worker 的处理结果:

            // 设置 Worker 消息处理
            worker.onmessage = function(e) {
                const data = e.data;
                switch (data.type) {
                    case 'imageLoaded':
                        // 图像加载完成
                        enableControls(true);
                        processTime.textContent = `${data.processingTime}ms`;
                        break;
                        
                    case 'processingComplete':
                        // 处理完成
                        showProcessingIndicator(false);
                        processTime.textContent = `${data.processingTime}ms`;
                        break;
                        
                    case 'imageBlob':
                        // 处理图像保存请求
                        const url = URL.createObjectURL(data.blob);
                        const link = document.createElement('a');
                        link.download = 'processed-image.png';
                        link.href = url;
                        link.click();
                        URL.revokeObjectURL(url);
                        break;
                }
            };
            

            主线程通过发送消息来请求 Worker 执行图像处理操作:

            // 应用滤镜
            function applyFilters() {
                if (workerBusy || !originalImageData) return;
                
                showProcessingIndicator(true);
                
                // 获取调整参数
                const adjustments = {
                    brightness: parseInt(document.getElementById('brightness').value),
                    contrast: parseInt(document.getElementById('contrast').value),
                    saturation: parseInt(document.getElementById('saturation').value)
                };
                
                // 发送消息到 Worker
                worker.postMessage({
                    type: 'applyFilters',
                    filters: currentFilters,
                    adjustments,
                    originalImageData
                });
            }
            

            Worker 线程代码

            Worker 线程负责实际执行图像处理操作。首先,它需要处理来自主线程的消息:

            // Worker 线程代码
            let canvas = null;
            let ctx = null;
            // 接收主线程消息
            self.onmessage = function(e) {
                const data = e.data;
                
                switch (data.type) {
                    case 'init':
                        initCanvas(data.canvas);
                        break;
                        
                    case 'loadImage':
                        loadImage(data.image);
                        break;
                        
                    case 'applyFilters':
                        applyFilters(data.filters, data.adjustments, data.originalImageData);
                        break;
                        
                    case 'resetImage':
                        resetImage(data.originalImageData);
                        break;
                        
                    case 'getImageData':
                        getImageDataUrl();
                        break;
                }
            };
            

            初始化 OffscreenCanvas:

            // 初始化 Canvas
            function initCanvas(offscreenCanvas) {
                canvas = offscreenCanvas;
                ctx = canvas.getContext('2d');
            }
            

            在 Worker 中加载图像需要注意,Worker 不能直接使用 DOM API,包括 Image 对象。正确的做法是使用 fetch 和 createImageBitmap:

            // 加载图像
            function loadImage(imageSrc) {
                const startTime = performance.now();
                
                // 使用 fetch 和 createImageBitmap 代替 new Image()
                fetch(imageSrc)
                    .then(response => response.blob())
                    .then(blob => createImageBitmap(blob))
                    .then(bitmap => {
                        // 调整 canvas 大小以适应图像
                        canvas.width = bitmap.width;
                        canvas.height = bitmap.height;
                        
                        // 绘制图像
                        ctx.clearRect(0, 0, canvas.width, canvas.height);
                        ctx.drawImage(bitmap, 0, 0);
                        
                        // 获取原始图像数据
                        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
                        
                        // 发送加载完成消息
                        self.postMessage({
                            type: 'imageLoaded',
                            imageData: imageData,
                            processingTime: Math.round(performance.now() - startTime)
                        });
                    })
                    .catch(error => {
                        self.postMessage({
                            type: 'error',
                            message: '加载图像失败: ' + error.message
                        });
                    });
            }
            

            实现各种图像处理算法,如灰度、棕褐色等滤镜效果:

            // 应用滤镜
            function applyFilters(filters, adjustments, originalImageData) {
                const startTime = performance.now();
                
                // 创建图像数据副本
                let imageData = new ImageData(
                    new Uint8ClampedArray(originalImageData.data),
                    originalImageData.width,
                    originalImageData.height
                );
                
                // 应用调整参数
                if (adjustments.brightness !== 0 || adjustments.contrast !== 0 || adjustments.saturation !== 0) {
                    imageData = applyAdjustments(imageData, adjustments);
                }
                
                // 应用滤镜
                filters.forEach(filter => {
                    switch (filter) {
                        case 'grayscale':
                            imageData = applyGrayscale(imageData);
                            break;
                        case 'sepia':
                            imageData = applySepia(imageData);
                            break;
                        // 其他滤镜...
                    }
                });
                
                // 将处理后的图像数据绘制到 canvas
                ctx.putImageData(imageData, 0, 0);
                
                // 发送处理完成消息
                self.postMessage({
                    type: 'processingComplete',
                    processingTime: Math.round(performance.now() - startTime)
                });
            }
            

            保存图像时,由于 OffscreenCanvas 不支持 toDataURL(),需要使用 convertToBlob():

            // 获取图像数据
            async function getImageDataUrl() {
                try {
                    // OffscreenCanvas 使用 convertToBlob 而非 toDataURL
                    const blob = await canvas.convertToBlob({type: 'image/png'});
                    
                    // 直接发送 blob 到主线程,更高效
                    self.postMessage({
                        type: 'imageBlob',
                        blob: blob
                    }, [blob]);  // 使用转移所有权模式避免复制
                } catch (error) {
                    self.postMessage({
                        type: 'error',
                        message: '保存图像失败: ' + error.message
                    });
                }
            }
            

            常见问题及解决方案

            在实现过程中,开发者可能会遇到以下常见问题:

            1. Worker 中使用 Image 对象

            错误信息:Uncaught ReferenceError: Image is not defined

            解决方案:Web Worker 中没有 DOM API,因此不能使用 new Image()。应使用 fetch() 加载图像,然后使用 createImageBitmap() 创建位图对象。

            Js案例-Web Worker + OffscreenCanvas 高性能图像处理实现
            (图片来源网络,侵删)

            2. OffscreenCanvas 保存图像问题

            错误信息:Uncaught TypeError: canvas.toDataURL is not a function

            解决方案:OffscreenCanvas 不支持 toDataURL(),应使用 convertToBlob() 方法,然后通过消息传递将 Blob 对象发送回主线程。

            Js案例-Web Worker + OffscreenCanvas 高性能图像处理实现
            (图片来源网络,侵删)

            性能优化

            为了获得最佳性能,开发者可以考虑以下几点:

            1. 使用 transferable objects(可转移对象)进行消息传递,避免大数据的复制开销
            2. 在处理大图像时,考虑分块处理以避免长时间阻塞 Worker 线程
            3. 对于复杂的图像处理操作,可以实现进度报告机制
            4. 使用 requestAnimationFrame 在主线程上平滑地显示处理进度

            兼容性考虑

            截至2025年,主流浏览器(Chrome、Firefox、Edge、Safari)都已支持 Web Worker 和 OffscreenCanvas。但在实际应用中,开发者应当添加特性检测:

            Js案例-Web Worker + OffscreenCanvas 高性能图像处理实现
            (图片来源网络,侵删)
            if (!window.OffscreenCanvas) {
                alert('您的浏览器不支持 OffscreenCanvas API。请使用较新版本的浏览器。');
                return;
            }
            if (!window.Worker) {
                alert('您的浏览器不支持 Web Worker API。请使用较新版本的浏览器。');
                return;
            }
            

            总结

            Web Worker 和 OffscreenCanvas 的结合为浏览器中的图像处理提供了强大的性能优势。通过将计算密集型的图像处理任务转移到后台线程,可以保持用户界面的响应性,提供流畅的用户体验。尽管这种方法的实现相对复杂,需要处理线程间通信和特定的 API 限制,但其带来的性能提升使得这些额外的工作变得值得。

            随着 Web 应用复杂度的不断提高,这种多线程的编程模型将在前端开发中扮演越来越重要的角色。掌握这些技术,将使开发者能够构建更加高效和响应迅速的 Web 应用。

            效果

            完整代码

            Web Worker + OffscreenCanvas 高

            HTML

            
            
              
              
              图像处理工作室
              
              
              
            
            
              

            高性能图像处理工作室

            基于Web Worker和OffscreenCanvas技术

            拖放图片到此处或点击上传

            图像处理中...

            上传图片 保存结果 重置图像

            滤镜效果

            灰度 棕褐色 反色 模糊 锐化 浮雕

            图像调整

            亮度 0
            对比度 0
            饱和度 0

            性能指标

            处理时间 -
            图像尺寸 -
            已应用滤镜

            main.js

            /**
             * main.js - 图像处理应用主线程代码
             * 
             * 负责用户界面交互和与Worker线程的通信
             * 使用Web Worker和OffscreenCanvas技术实现高性能图像处理
             */
            (function () {
                // DOM元素引用 - 缓存所有需要操作的DOM元素,提高性能
                const displayCanvas = document.getElementById('display-canvas');        // 显示图像的画布
                const placeholder = document.getElementById('placeholder');             // 上传占位区域
                const processingIndicator = document.getElementById('processing-indicator'); // 处理指示器
                const imageInput = document.getElementById('image-input');              // 文件输入元素
                const uploadBtn = document.getElementById('upload-btn');                // 上传按钮
                const saveBtn = document.getElementById('save-btn');                    // 保存按钮
                const resetBtn = document.getElementById('reset-btn');                  // 重置按钮
                const filterButtons = document.querySelectorAll('.filter-btn');         // 所有滤镜按钮
                const sliders = document.querySelectorAll('input[type="range"]');       // 所有滑块控件
                const processTime = document.getElementById('process-time');            // 处理时间显示元素
                const imageSize = document.getElementById('image-size');                // 图像尺寸显示元素
                const appliedFilters = document.getElementById('applied-filters');      // 已应用滤镜显示元素
                // 应用状态变量
                let originalImageData = null;  // 原始图像数据,用于重置和应用新滤镜
                let currentFilters = [];       // 当前应用的滤镜列表
                let workerBusy = false;        // Worker线程忙碌状态标志
                let worker = null;             // Web Worker实例
                let offscreenCanvas = null;    // OffscreenCanvas实例
                /**
                 * 初始化应用
                 * 检查浏览器兼容性,创建Worker,设置事件处理
                 */
                function init() {
                    // 检查OffscreenCanvas支持
                    if (!window.OffscreenCanvas) {
                        alert('您的浏览器不支持OffscreenCanvas API。请使用较新版本的Chrome或Edge浏览器。');
                        // 降级处理:可以在这里添加使用普通Canvas的备选方案
                        return;
                    }
                    // 检查Web Worker支持
                    if (!window.Worker) {
                        alert('您的浏览器不支持Web Worker API。请使用较新版本的浏览器。');
                        return;
                    }
                    // 创建Worker实例
                    worker = new Worker('worker.js');
                    // 设置Worker消息处理函数
                    worker.onmessage = handleWorkerMessage;
                    // 创建OffscreenCanvas并将控制权转移给Worker
                    offscreenCanvas = displayCanvas.transferControlToOffscreen();
                    worker.postMessage({
                        type: 'init',
                        canvas: offscreenCanvas
                    }, [offscreenCanvas]); // 使用transferable objects提高性能
                    // 绑定所有UI事件处理
                    bindEvents();
                }
                /**
                 * 绑定所有用户界面事件
                 * 包括按钮点击、拖放操作、滑块控制等
                 */
                function bindEvents() {
                    // 上传按钮点击事件 - 触发文件选择器
                    uploadBtn.addEventListener('click', () => {
                        imageInput.click();
                    });
                    // 文件选择变化事件 - 处理用户选择的文件
                    imageInput.addEventListener('change', handleImageSelect);
                    // 拖放区域点击事件 - 同样触发文件选择器
                    placeholder.addEventListener('click', () => {
                        imageInput.click();
                    });
                    // 拖拽悬停效果
                    displayCanvas.parentElement.addEventListener('dragover', (e) => {
                        e.preventDefault(); // 阻止默认行为以允许放置
                        placeholder.style.backgroundColor = 'rgba(0, 0, 0, 0.1)'; // 视觉反馈
                    });
                    // 拖拽离开效果
                    displayCanvas.parentElement.addEventListener('dragleave', () => {
                        placeholder.style.backgroundColor = 'rgba(0, 0, 0, 0.05)';
                    });
                    // 拖放文件处理
                    displayCanvas.parentElement.addEventListener('drop', (e) => {
                        e.preventDefault(); // 阻止默认行为
                        placeholder.style.backgroundColor = 'rgba(0, 0, 0, 0.05)';
                        // 如果有文件,处理第一个文件
                        if (e.dataTransfer.files.length > 0) {
                            handleImageFile(e.dataTransfer.files[0]);
                        }
                    });
                    // 保存按钮点击事件
                    saveBtn.addEventListener('click', saveImage);
                    // 重置按钮点击事件
                    resetBtn.addEventListener('click', resetImage);
                    // 滤镜按钮点击事件
                    filterButtons.forEach(btn => {
                        btn.addEventListener('click', () => {
                            // 如果Worker忙或没有图像,忽略点击
                            if (workerBusy || !originalImageData) return;
                            const filterName = btn.dataset.filter;
                            if (btn.classList.contains('active')) {
                                // 取消已应用的滤镜
                                btn.classList.remove('active');
                                // 从滤镜列表中移除
                                currentFilters = currentFilters.filter(f => f !== filterName);
                            } else {
                                // 应用新滤镜
                                btn.classList.add('active');
                                currentFilters.push(filterName);
                            }
                            // 应用更新后的滤镜列表
                            applyFilters();
                            // 更新滤镜显示
                            updateAppliedFilters();
                        });
                    });
                    // 调整滑块事件处理
                    sliders.forEach(slider => {
                        const valueDisplay = slider.nextElementSibling;
                        // 滑动时实时更新显示值
                        slider.addEventListener('input', () => {
                            valueDisplay.textContent = slider.value;
                        });
                        // 滑动结束后应用效果
                        slider.addEventListener('change', () => {
                            if (workerBusy || !originalImageData) return;
                            applyFilters();
                        });
                    });
                }
                /**
                 * 处理图像选择事件
                 * @param {Event} e - 事件对象
                 */
                function handleImageSelect(e) {
                    if (e.target.files.length > 0) {
                        handleImageFile(e.target.files[0]);
                    }
                }
                /**
                 * 处理图像文件
                 * 读取文件并发送到Worker进行处理
                 * 
                 * @param {File} file - 用户选择的图像文件
                 */
                function handleImageFile(file) {
                    // 验证文件类型是否为图像
                    if (!file.type.startsWith('image/')) {
                        alert('请选择有效的图像文件。');
                        return;
                    }
                    // 使用FileReader读取文件内容
                    const reader = new FileReader();
                    reader.onload = (e) => {
                        const img = new Image();
                        img.onload = () => {
                            // 显示处理指示器
                            showProcessingIndicator(true);
                            // 发送图像数据到Worker进行处理
                            worker.postMessage({
                                type: 'loadImage',
                                image: img.src // 图像的Data URL
                            });
                            // 更新图像尺寸信息显示
                            imageSize.textContent = `${img.width} x ${img.height}像素`;
                        };
                        img.src = e.target.result; // 设置图像源为FileReader的结果
                    };
                    reader.readAsDataURL(file); // 以Data URL形式读取文件
                }
                /**
                 * 处理来自Worker的消息
                 * @param {MessageEvent} e - Worker发送的消息事件
                 */
                function handleWorkerMessage(e) {
                    const data = e.data;
                    switch (data.type) {
                        case 'imageLoaded':
                            // 图像加载完成
                            originalImageData = data.imageData;
                            enableControls(true); // 启用控件
                            showProcessingIndicator(false); // 隐藏处理指示器
                            placeholder.style.display = 'none'; // 隐藏占位符
                            appliedFilters.textContent = '无'; // 重置滤镜显示
                            break;
                        case 'processingComplete':
                            // 图像处理完成
                            showProcessingIndicator(false);
                            workerBusy = false;
                            processTime.textContent = `${data.processingTime}ms`; // 更新处理时间
                            break;
                        case 'error':
                            // 错误处理
                            alert(data.message);
                            showProcessingIndicator(false);
                            workerBusy = false;
                            break;
                        case 'imageData':
                            // 接收图像数据URL并触发下载
                            downloadImage(data.dataUrl);
                            break;
                    }
                }
                /**
                 * 显示或隐藏处理指示器
                 * @param {boolean} show - 是否显示处理指示器
                 */
                function showProcessingIndicator(show) {
                    processingIndicator.style.display = show ? 'flex' : 'none';
                    workerBusy = show;
                }
                /**
                 * 启用或禁用控件
                 * @param {boolean} enable - 是否启用控件
                 */
                function enableControls(enable) {
                    saveBtn.disabled = !enable;
                    resetBtn.disabled = !enable;
                    sliders.forEach(slider => {
                        slider.disabled = !enable;
                    });
                }
                /**
                 * 应用当前滤镜和调整到图像
                 */
                function applyFilters() {
                    if (workerBusy || !originalImageData) return;
                    showProcessingIndicator(true);
                    // 获取滑块的当前值
                    const adjustments = {
                        brightness: parseInt(document.getElementById('brightness').value),
                        contrast: parseInt(document.getElementById('contrast').value),
                        saturation: parseInt(document.getElementById('saturation').value)
                    };
                    // 发送滤镜请求到Worker
                    worker.postMessage({
                        type: 'applyFilters',
                        filters: currentFilters,
                        adjustments,
                        originalImageData
                    });
                }
                /**
                 * 更新已应用滤镜的显示
                 */
                function updateAppliedFilters() {
                    if (currentFilters.length === 0) {
                        appliedFilters.textContent = '无';
                    } else {
                        // 滤镜英文名到中文名的映射
                        const filterMap = {
                            'grayscale': '灰度',
                            'sepia': '棕褐色',
                            'invert': '反色',
                            'blur': '模糊',
                            'sharpen': '锐化',
                            'emboss': '浮雕'
                        };
                        // 将英文滤镜名转换为中文后连接
                        appliedFilters.textContent = currentFilters.map(f => filterMap[f]).join(', ');
                    }
                }
                /**
                 * 下载处理后的图像
                 * @param {string} dataUrl - 图像的Data URL
                 */
                function downloadImage(dataUrl) {
                    // 创建下载链接
                    const link = document.createElement('a');
                    link.download = 'processed-image.png'; // 设置下载文件名
                    link.href = dataUrl; // 设置链接地址为图像数据
                    document.body.appendChild(link); // 添加到文档
                    link.click(); // 触发点击,开始下载
                    document.body.removeChild(link); // 移除链接
                }
                /**
                 * 保存图像 - 请求Worker提供图像数据
                 */
                function saveImage() {
                    if (workerBusy || !originalImageData) return;
                    // 请求Worker生成图像数据
                    worker.postMessage({
                        type: 'getImageData'
                    });
                }
                /**
                 * 重置图像到原始状态
                 */
                function resetImage() {
                    if (workerBusy || !originalImageData) return;
                    // 重置滤镜状态
                    currentFilters = [];
                    filterButtons.forEach(btn => {
                        btn.classList.remove('active');
                    });
                    // 重置滑块
                    sliders.forEach(slider => {
                        slider.value = 0;
                        slider.nextElementSibling.textContent = '0';
                    });
                    // 应用原始图像
                    showProcessingIndicator(true);
                    worker.postMessage({
                        type: 'resetImage',
                        originalImageData
                    });
                    // 更新已应用滤镜显示
                    updateAppliedFilters();
                }
                // 启动应用
                init();
            })();
            

            worker.js

            /**
             * worker.js - Web Worker线程代码
             * 
             * 这个Worker负责处理所有的图像处理任务,以避免阻塞主UI线程。
             * 使用OffscreenCanvas进行高性能图像渲染和处理。
             */
            // Worker全局变量
            let canvas = null;  // OffscreenCanvas实例
            let ctx = null;     // 绘图上下文
            /**
             * 处理来自主线程的消息
             * 根据消息类型执行不同的操作
             */
            self.onmessage = function (e) {
                const data = e.data;
                switch (data.type) {
                    case 'init':
                        // 初始化OffscreenCanvas
                        initCanvas(data.canvas);
                        break;
                    case 'loadImage':
                        // 加载并处理新图像
                        loadImage(data.image);
                        break;
                    case 'applyFilters':
                        // 应用滤镜和调整
                        applyFilters(data.filters, data.adjustments, data.originalImageData);
                        break;
                    case 'resetImage':
                        // 重置图像到原始状态
                        resetImage(data.originalImageData);
                        break;
                    case 'getImageData':
                        // 获取处理后的图像数据以便保存
                        getImageDataUrl();
                        break;
                }
            };
            /**
             * 初始化OffscreenCanvas
             * @param {OffscreenCanvas} offscreenCanvas - 从主线程转移来的OffscreenCanvas
             */
            function initCanvas(offscreenCanvas) {
                canvas = offscreenCanvas;
                ctx = canvas.getContext('2d');
            }
            /**
             * 加载图像到OffscreenCanvas
             * 使用fetch和createImageBitmap以获得最佳性能
             * 
             * @param {string} imageSrc - 图像的DataURL
             */
            function loadImage(imageSrc) {
                const startTime = performance.now();  // 记录开始时间,用于性能测量
                // 使用fetch和createImageBitmap加载图像,这是最高效的方法
                fetch(imageSrc)
                    .then(response => response.blob())
                    .then(blob => createImageBitmap(blob))
                    .then(bitmap => {
                        // 调整canvas大小以适应图像
                        canvas.width = bitmap.width;
                        canvas.height = bitmap.height;
                        // 绘制图像
                        ctx.clearRect(0, 0, canvas.width, canvas.height);
                        ctx.drawImage(bitmap, 0, 0);
                        // 获取原始图像数据,用于后续处理和重置
                        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
                        // 向主线程发送加载完成消息,包含图像数据和处理时间
                        self.postMessage({
                            type: 'imageLoaded',
                            imageData: imageData,
                            processingTime: Math.round(performance.now() - startTime)
                        });
                    })
                    .catch(error => {
                        // 向主线程报告错误
                        self.postMessage({
                            type: 'error',
                            message: '加载图像失败: ' + error.message
                        });
                    });
            }
            /**
             * 应用滤镜和调整参数到图像
             * 
             * @param {Array} filters - 要应用的滤镜名称数组
             * @param {Object} adjustments - 包含亮度、对比度、饱和度的调整参数
             * @param {ImageData} originalImageData - 原始图像数据
             */
            function applyFilters(filters, adjustments, originalImageData) {
                const startTime = performance.now();  // 记录处理开始时间
                // 创建图像数据副本,避免直接修改原始数据
                let imageData = new ImageData(
                    new Uint8ClampedArray(originalImageData.data),
                    originalImageData.width,
                    originalImageData.height
                );
                // 首先应用调整参数(亮度、对比度、饱和度)
                if (adjustments.brightness !== 0 || adjustments.contrast !== 0 || adjustments.saturation !== 0) {
                    imageData = applyAdjustments(imageData, adjustments);
                }
                // 然后应用各种滤镜效果
                filters.forEach(filter => {
                    switch (filter) {
                        case 'grayscale':
                            imageData = applyGrayscale(imageData);
                            break;
                        case 'sepia':
                            imageData = applySepia(imageData);
                            break;
                        case 'invert':
                            imageData = applyInvert(imageData);
                            break;
                        case 'blur':
                            imageData = applyBlur(imageData);
                            break;
                        case 'sharpen':
                            imageData = applySharpen(imageData);
                            break;
                        case 'emboss':
                            imageData = applyEmboss(imageData);
                            break;
                    }
                });
                // 将处理后的图像数据绘制到canvas
                ctx.putImageData(imageData, 0, 0);
                // 向主线程发送处理完成消息,包含处理时间
                self.postMessage({
                    type: 'processingComplete',
                    processingTime: Math.round(performance.now() - startTime)
                });
            }
            /**
             * 重置图像到原始状态
             * 
             * @param {ImageData} originalImageData - 原始图像数据
             */
            function resetImage(originalImageData) {
                const startTime = performance.now();
                // 将原始图像数据绘制到canvas
                ctx.putImageData(originalImageData, 0, 0);
                // 向主线程发送处理完成消息
                self.postMessage({
                    type: 'processingComplete',
                    processingTime: Math.round(performance.now() - startTime)
                });
            }
            /**
             * 获取当前图像的数据URL,用于保存图像
             */
            async function getImageDataUrl() {
                try {
                    // 将canvas内容转换为Blob对象
                    const blob = await canvas.convertToBlob({ type: 'image/png' });
                    // 使用FileReader将Blob转换为DataURL
                    const reader = new FileReader();
                    reader.onload = function () {
                        // 将数据URL发送给主线程
                        self.postMessage({
                            type: 'imageData',
                            dataUrl: reader.result
                        });
                    };
                    reader.readAsDataURL(blob);
                } catch (error) {
                    // 向主线程报告错误
                    self.postMessage({
                        type: 'error',
                        message: '保存图像失败: ' + error.message
                    });
                }
            }
            /**
             * 应用灰度滤镜 - 将彩色图像转换为黑白图像
             * 基于RGB通道的加权平均值计算灰度
             * 
             * @param {ImageData} imageData - 图像数据
             * @returns {ImageData} - 处理后的图像数据
             */
            function applyGrayscale(imageData) {
                const data = imageData.data;
                for (let i = 0; i 
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

相关阅读

目录[+]

取消
微信二维码
微信二维码
支付宝二维码