You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Emscripten应用中从主线程调用工作线程处理字体数据?

解决方案与实现示例

问题1:从JavaScript调用C++创建的WASM Worker

要实现主线程JS拿到字体数据后传递给C创建的Worker,需通过Emscripten的绑定机制暴露C函数给JS,同时在C++中处理Worker的消息接收逻辑:

步骤1:C++端代码实现

#include <emscripten.h>
#include <emscripten/threading.h>
#include <vector>
#include <string>

// 全局Worker线程ID
emscripten_thread_t font_worker = 0;

// Worker线程的入口函数
void font_processing_worker(void* arg) {
  // 循环接收主线程消息
  while (true) {
    emscripten_message_t msg;
    if (emscripten_receive_message(&msg, -1) == EMSCRIPTEN_RESULT_SUCCESS) {
      // 解析字体数据(示例传递字体名称,可扩展为二进制数据)
      std::string font_name(static_cast<char*>(msg.data), msg.numBytes);
      // 这里添加字体处理逻辑,比如解析字体结构、传递给渲染模块等
      printf("Processing font: %s\n", font_name.c_str());

      // 释放消息内存
      emscripten_free(msg.data);
    }
  }
}

// 暴露给JS的函数:初始化Worker并传递字体数据
extern "C" EMSCRIPTEN_KEEPALIVE void init_font_worker_and_send_data(const char* font_name, const uint8_t* font_data, size_t data_len) {
  if (!font_worker) {
    // 创建WASM Worker
    font_worker = emscripten_malloc_wasm_worker(font_processing_worker, nullptr);
  }

  // 打包字体名称(实际场景可传递二进制数据)
  size_t total_size = strlen(font_name) + 1;
  void* msg_data = emscripten_malloc(total_size);
  memcpy(msg_data, font_name, total_size);

  // 发送消息到Worker
  emscripten_send_message(font_worker, msg_data, total_size);
}

EMSCRIPTEN_BINDINGS(font_worker_module) {
  emscripten_bind_void_func("initFontWorkerAndSendData", &init_font_worker_and_send_data);
}

步骤2:JavaScript端调用

主线程获取queryLocalFonts结果后,调用暴露的C++函数:

async function fetchAndSendFonts() {
  const fonts = await window.queryLocalFonts();
  for (const font of fonts) {
    // 获取字体二进制数据
    const fontBlob = await font.blob();
    const fontArrayBuffer = await fontBlob.arrayBuffer();
    const fontUint8 = new Uint8Array(fontArrayBuffer);
    
    // 调用C++暴露的函数,传递字体名称和数据
    Module.initFontWorkerAndSendData(font.fullName, fontUint8, fontUint8.length);
  }
}

window.onload = () => {
  fetchAndSendFonts();
};

编译注意事项

编译时需添加线程相关选项:

emcc your_code.cpp -o output.js -pthread -s PROXY_TO_PTHREAD=1 -s EXPORTED_FUNCTIONS='["_init_font_worker_and_send_data"]' -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]'

问题2:自定义Web Worker脚本解决self is not defined错误

Emscripten的Worker必须加载其运行时环境,直接写空脚本会导致运行时变量未定义。正确的ProcessLocalFonts.js需先导入Emscripten的Worker运行时,再处理消息:

步骤1:ProcessLocalFonts.js 内容

// 加载Emscripten的Worker运行时(路径需与你的输出文件对应)
importScripts('./output.js');

// 等待WASM运行时初始化完成
Module.onRuntimeInitialized = () => {
  console.log("Font processing worker ready");
  
  // 接收主线程消息
  self.onmessage = async (e) => {
    const { fontName, fontData } = e.data;
    // 调用WASM中的字体处理函数(需C++提前暴露)
    Module.ccall('processFont', null, ['string', 'array', 'number'], [fontName, fontData, fontData.length]);
  };
};

步骤2:主线程JS创建Worker并传递数据

// 初始化Worker
const fontWorker = new Worker('./ProcessLocalFonts.js');

async function fetchAndSendFonts() {
  const fonts = await window.queryLocalFonts();
  for (const font of fonts) {
    const fontBlob = await font.blob();
    const fontArrayBuffer = await fontBlob.arrayBuffer();
    const fontUint8 = new Uint8Array(fontArrayBuffer);
    
    // 发送字体数据到Worker,转移ArrayBuffer所有权避免拷贝
    fontWorker.postMessage({
      fontName: font.fullName,
      fontData: fontUint8
    }, [fontUint8.buffer]);
  }
}

window.onload = () => {
  fetchAndSendFonts();
};

步骤3:C++端暴露处理函数

#include <emscripten.h>
#include <vector>
#include <string>

extern "C" EMSCRIPTEN_KEEPALIVE void processFont(const char* font_name, const uint8_t* font_data, size_t data_len) {
  // 字体处理逻辑:比如解析字体文件、提取字形数据等
  printf("Processing font in custom worker: %s, data length: %zu\n", font_name, data_len);
}

EMSCRIPTEN_BINDINGS(font_process_module) {
  emscripten_bind_void_func("processFont", &processFont);
}

编译注意事项

编译时需启用Worker支持:

emcc your_code.cpp -o output.js -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=1 -s EXPORTED_FUNCTIONS='["_processFont"]' -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]'

内容的提问来源于stack exchange,提问作者Ashot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 03:05:39