鸿蒙采用Worker多线程实现图片滤镜处理

1、实现流程

在这里插入图片描述
在这里插入图片描述

2、伪代码实现

Step 1:创建项目与配置 Worker
新建 Stage 模型项目(DevEco Studio 4.0+),确保 module.json5 中声明 Worker 脚本路径(子线程脚本存放位置):

   // entry/src/main/module.json5
   {
     "module": {
       "name": "entry",
       "type": "entry",
       "workers": [ 
         "ets/workers/ImageFilterWorker.ts"  // 声明Worker脚本路径
       ],
       "abilities": [/* ... */]
     }
   }

Step 2:编写 Worker 子线程脚本(滤镜处理逻辑)
创建 entry/src/main/ets/workers/ImageFilterWorker.ts,实现图片像素处理(以 灰度化滤镜 为例,CPU 密集型任务):

// ImageFilterWorker.ts(Worker子线程脚本)
import worker, { ThreadWorkerGlobalScope, MessageEvents } from '@ohos.worker';

// 获取Worker全局作用域
const workerPort: ThreadWorkerGlobalScope = worker.workerPort;

// 监听主线程发送的图片处理任务
workerPort.onmessage = (e: MessageEvents) => {
  const { cmd, pixelData, width, height } = e.data; // 接收图片数据、宽高、命令

  try {
    let processedData: Uint8ClampedArray;
    switch (cmd) {
      case 'grayscale': // 灰度化滤镜
        processedData = grayscaleFilter(pixelData, width, height);
        break;
      case 'blur': // 模拟模糊滤镜(简化版)
        processedData = blurFilter(pixelData, width, height);
        break;
      default:
        throw new Error(`不支持的滤镜类型: ${cmd}`);
    }

    // 将处理后的像素数据发送回主线程(通过ArrayBuffer)
    workerPort.postMessage({
      status: 'success',
      data: processedData.buffer, // 传递ArrayBuffer
      width,
      height
    });
  } catch (error) {
    // 错误信息发送回主线程
    workerPort.postMessage({ status: 'error', message: error.message });
  }
};

/**
 * 灰度化滤镜算法:将RGB像素转为灰度值(Y = 0.299*R + 0.587*G + 0.114*B)
 * @param pixelData 原始像素数据(Uint8ClampedArray,格式:[R, G, B, A, R, G, B, A, ...])
 * @param width 图片宽度
 * @param height 图片高度
 */
function grayscaleFilter(
  pixelData: Uint8ClampedArray,
  width: number,
  height: number
): Uint8ClampedArray {
  const processedData = new Uint8ClampedArray(pixelData.length); // 新数组存储结果
  for (let i = 0; i < pixelData.length; i += 4) {
    const r = pixelData[i];     // 红色通道
    const g = pixelData[i + 1]; // 绿色通道
    const b = pixelData[i + 2]; // 蓝色通道
    const a = pixelData[i + 3]; // 透明度通道(保持不变)

    // 计算灰度值
    const gray = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
    // 写入新像素数据(R=G=B=gray,透明度不变)
    processedData[i] = gray;
    processedData[i + 1] = gray;
    processedData[i + 2] = gray;
    processedData[i + 3] = a;
  }
  return processedData;
}

/** 简化版模糊滤镜(示例,实际可替换为高斯模糊等复杂算法) */
function blurFilter(
  pixelData: Uint8ClampedArray,
  width: number,
  height: number
): Uint8ClampedArray {
  // 此处省略复杂算法,实际项目可引入第三方滤镜库(如通过NDK调用C++实现的高性能模糊算法)
  return pixelData; // 仅作示例,返回原始数据
}

Step 3:主线程实现(UI + 图片选择 + Worker 通信)
创建页面 entry/src/main/ets/pages/ImageFilterPage.ts,实现用户交互和 Worker 管理:

// ImageFilterPage.ts(主线程页面)
import worker, { ThreadWorker, MessageEvents } from '@ohos.worker';
import promptAction from '@ohos.promptAction';
import filePicker from '@ohos.file.picker';
import image from '@ohos.multimedia.image';
import fs from '@ohos.file.fs';

@Entry
@Component
struct ImageFilterPage {
  @State originalImage: PixelMap | null = null; // 原始图片PixelMap
  @State filteredImage: PixelMap | null = null; // 处理后图片PixelMap
  @State isProcessing: boolean = false; // 处理中状态(控制按钮禁用)
  private worker: ThreadWorker | null = null; // Worker实例

  // 页面加载时创建Worker
  aboutToAppear() {
    this.createWorker();
  }

  // 页面销毁时终止Worker
  aboutToDisappear() {
    this.terminateWorker();
  }

  // 创建Worker实例
  private createWorker() {
    try {
      this.worker = new worker.ThreadWorker('entry/ets/workers/ImageFilterWorker.ts');
      // 监听Worker返回的结果
      this.worker.onmessage = (e: MessageEvents) => this.handleWorkerResult(e);
      // 监听Worker错误
      this.worker.onerror = (e) => {
        promptAction.showToast({ message: `Worker错误: ${e.message}` });
        this.isProcessing = false;
      };
    } catch (error) {
      console.error('创建Worker失败:', error);
      promptAction.showToast({ message: 'Worker初始化失败' });
    }
  }

  // 终止Worker
  private terminateWorker() {
    if (this.worker) {
      this.worker.terminate();
      this.worker = null;
    }
  }

  // 处理Worker返回的结果(处理后的图片数据)
  private async handleWorkerResult(e: MessageEvents) {
    this.isProcessing = false; // 结束处理状态
    if (e.data.status === 'success') {
      const { data: buffer, width, height } = e.data;
      // 将Worker返回的ArrayBuffer转为PixelMap
      this.filteredImage = await this.bufferToPixelMap(new Uint8ClampedArray(buffer), width, height);
      promptAction.showToast({ message: '滤镜处理完成' });
    } else {
      promptAction.showToast({ message: `处理失败: ${e.data.message}` });
    }
  }

  // 选择本地图片(通过文件选择器)
  private async selectImage() {
    try {
      // 调用系统文件选择器,选择图片文件
      const result = await filePicker.selectFile({
        type: filePicker.Types.IMAGE,
        count: 1 // 仅选择1张图片
      });
      if (result.length === 0) return;

      // 读取选中的图片文件,转为PixelMap(用于UI显示)
      const uri = result[0].uri; // 图片文件URI
      const file = await fs.open(uri, fs.OpenMode.READ_ONLY);
      const pixelMap = await image.createPixelMap(file.fd);
      await fs.close(file.fd);

      this.originalImage = pixelMap; // 显示原始图片
      this.filteredImage = null; // 重置处理后图片
    } catch (error) {
      console.error('选择图片失败:', error);
      promptAction.showToast({ message: '选择图片失败' });
    }
  }

  // 启动滤镜处理(通过Worker子线程)
  private async startFilterProcessing(filterType: 'grayscale' | 'blur') {
    if (!this.originalImage || !this.worker || this.isProcessing) return;

    this.isProcessing = true; // 标记处理中
    promptAction.showToast({ message: `正在应用${filterType === 'grayscale' ? '灰度' : '模糊'}滤镜...` });

    try {
      // 1. 将原始图片PixelMap转为RGBA像素数据(Uint8ClampedArray)
      const pixelData = await this.pixelMapToPixelData(this.originalImage);
      const { width, height } = await this.originalImage.getImageInfo();

      // 2. 发送任务给Worker子线程(传递像素数据、宽高、滤镜类型)
      this.worker.postMessage({
        cmd: filterType, // 任务类型:'grayscale'或'blur'
        pixelData: pixelData.buffer, // 像素数据ArrayBuffer
        width,
        height
      });
    } catch (error) {
      console.error('处理图片失败:', error);
      this.isProcessing = false;
      promptAction.showToast({ message: '处理图片失败' });
    }
  }

  // PixelMap转为RGBA像素数据(Uint8ClampedArray:[R, G, B, A, R, G, B, A, ...])
  private async pixelMapToPixelData(pixelMap: PixelMap): Promise<Uint8ClampedArray> {
    const imageInfo = await pixelMap.getImageInfo();
    const bufferSize = imageInfo.size; // 像素数据总字节数(width * height * 4,4通道RGBA)
    const pixelBuffer = new ArrayBuffer(bufferSize);
    await pixelMap.readPixelsToBuffer(pixelBuffer); // 将PixelMap像素读取到ArrayBuffer
    return new Uint8ClampedArray(pixelBuffer); // 转为可操作的类型化数组
  }

  // 接收Worker返回的处理结果,转为PixelMap并更新UI
  private async handleWorkerResult(e: MessageEvents) {
    if (e.data.status !== 'success') return;

    const { data: buffer, width, height } = e.data;
    const pixelData = new Uint8ClampedArray(buffer); // 从ArrayBuffer恢复像素数据

    // 将像素数据转为PixelMap(用于UI显示)
    const pixelMap = await image.createPixelMap(
      pixelData,
      { width, height, pixelFormat: image.PixelFormat.RGBA_8888 }
    );
    this.filteredImage = pixelMap; // 更新处理后图片
  }

  build() {
    Column({ space: 16 }) {
      Text('图片滤镜处理(Worker多线程示例)')
        .fontSize(20)
        .fontWeight(FontWeight.Bold);

      // 原始图片区域
      Column() {
        Text('原始图片')
          .fontSize(16)
          .margin(4);
        if (this.originalImage) {
          Image(this.originalImage)
            .width(300)
            .height(300)
            .objectFit(ImageFit.Contain);
        } else {
          Text('未选择图片')
            .width(300)
            .height(300)
            .backgroundColor('#f5f5f5')
            .textAlign(TextAlign.Center)
            .fontColor('#999');
        }
      }

      // 操作按钮区域
      Row({ space: 12 }) {
        Button('选择图片')
          .onClick(() => this.selectImage())
          .backgroundColor('#007DFF');

        Button('灰度滤镜')
          .onClick(() => this.startFilterProcessing('grayscale'))
          .backgroundColor('#007DFF')
          .enabled(!this.isProcessing); // 处理中禁用按钮

        Button('模糊滤镜')
          .onClick(() => this.startFilterProcessing('blur'))
          .backgroundColor('#007DFF')
          .enabled(!this.isProcessing);
      }

      // 处理后图片区域
      Column() {
        Text('处理后图片')
          .fontSize(16)
          .margin(4);
        if (this.filteredImage) {
          Image(this.filteredImage)
            .width(300)
            .height(300)
            .objectFit(ImageFit.Contain);
        } else if (this.originalImage && !this.isProcessing) {
          Text('点击滤镜按钮开始处理')
            .width(300)
            .height(300)
            .backgroundColor('#f5f5f5')
            .textAlign(TextAlign.Center)
            .fontColor('#999');
        }
      }
    }
    .width('100%')
    .height('100%')
    .padding(16)
    .justifyContent(FlexAlign.Center);
  }
}

3、核心优势与注意事项

在这里插入图片描述

4、多Worker线程交互

在这里插入图片描述
在这里插入图片描述

采用主线程作为中介,接收a的worker线程的数据,然后b的worker线程从主线程中去获取数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bst@微胖子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值