2025-10-29 21:27:29 +00:00

171 lines
4.1 KiB
JavaScript

// QR Processing Module for EmblemScanner
// Self-contained WASM-based QR code processing
var qrtool = null;
var qrtool_ready = false;
/**
* Load qrtool WASM module
*/
function load_qrtool(wasmFilePath) {
// Set global WASM file path before loading the module
global.wasmFilePath = wasmFilePath;
// Load the WASM module
var m = require('./qrtool.wx.js');
m.onRuntimeInitialized = () => {
qrtool_ready = true;
qrtool = m;
}
}
/**
* Check if qrtool is ready for processing
*/
function is_qrtool_ready() {
return qrtool_ready;
}
/**
* Process camera frame for QR code detection
*/
function process_frame(width, height, image_data, camera_sensitivity, enable_debug = false) {
if (!qrtool_ready) {
return null;
}
try {
// Copy frame data to avoid TOCTOU
var uca1 = new Uint8ClampedArray(image_data);
var uca = new Uint8ClampedArray(uca1);
var buf = qrtool._malloc(uca.length * uca.BYTES_PER_ELEMENT);
qrtool.HEAPU8.set(uca, buf);
var dot_area_buf = 0;
var debug_data_url = null;
if (enable_debug) {
const dot_area_size = 32;
const da_len = dot_area_size * dot_area_size * uca.BYTES_PER_ELEMENT * 4;
dot_area_buf = qrtool._malloc(da_len);
}
var result_str = qrtool.ccall('qrtool_angle', 'string',
['number', 'number', 'number', 'number', 'number'],
[buf, width, height, dot_area_buf, camera_sensitivity || 1]
);
if (enable_debug && dot_area_buf) {
const dot_area_size = 32;
const da_len = dot_area_size * dot_area_size * uca.BYTES_PER_ELEMENT * 4;
var debug_view = qrtool.HEAPU8.subarray(dot_area_buf, dot_area_buf + da_len);
debug_data_url = data_url_from_frame(dot_area_size, dot_area_size, debug_view);
}
qrtool._free(buf);
if (dot_area_buf) {
qrtool._free(dot_area_buf);
}
// Parse result
var result = JSON.parse(result_str);
var returnValue = {
qrcode: result.qrcode || '',
angle: result.angle || 0,
ok: result.ok || false,
err: result.err || ''
};
if (enable_debug && debug_data_url) {
returnValue.debug_data_url = debug_data_url;
}
return returnValue;
} catch (error) {
console.error('QR processing error:', error);
return null;
}
}
/**
* Create offscreen canvas for debug image generation
*/
let offscreenCanvas = null;
function getOffscreenCanvas() {
if (!offscreenCanvas) {
offscreenCanvas = wx.createOffscreenCanvas({
type: '2d',
width: 100,
height: 100,
});
}
return offscreenCanvas;
}
/**
* Convert raw frame data to data URL for image visualization
*/
function data_url_from_frame(width, height, image_data) {
const canvas = getOffscreenCanvas();
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
var imgd = ctx.createImageData(width, height);
imgd.data.set(image_data);
ctx.putImageData(imgd, 0, 0);
return canvas.toDataURL("image/jpeg", 1.0);
}
/**
* Check if QR code matches Emblem pattern
*/
function is_emblem_qr_pattern(p) {
if (!p) return false;
if (p.search(/code=[0-9a-zA-Z]+/) >= 0) return true;
if (p.search(/id=[0-9a-zA-Z]+/) >= 0) return true;
if (p.search(/c=[0-9a-zA-Z]+/) >= 0) return true;
if (p.search(/https:\/\/xy\.ltd\/v\/[0-9a-zA-Z]+/) >= 0) return true;
return false;
}
/**
* Generate hint text based on QR processing result
*/
function make_hint_text(result) {
if (!result) {
return "查找二维码";
}
if (result.qrcode && result.qrcode.length > 0) {
if (!is_emblem_qr_pattern(result.qrcode)) {
return "无效编码";
}
if (result.ok) {
return "识别成功";
}
// Check specific error conditions
var err = result.err || "";
if (err.includes("margin too small")) {
return "对齐定位点";
} else if (err.includes("energy check failed") || err.includes("cannot detect angle")) {
return "移近一点";
}
}
return "对齐定位点";
}
module.exports = {
load_qrtool,
is_qrtool_ready,
process_frame,
data_url_from_frame,
is_emblem_qr_pattern,
make_hint_text
};