171 lines
4.1 KiB
JavaScript
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) {
|
|
// Load the WASM module
|
|
var m = require('./qrtool.wx.js');
|
|
|
|
// Set the WASM file path
|
|
m.wasmFilePath = wasmFilePath;
|
|
|
|
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
|
|
}; |