vision-camera-simple-scanner
Version:
High performance barcode scanner for React Native using VisionCamera, forked from vision-camera-barcode-scanner
577 lines (563 loc) • 20.9 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
AndroidAddressType: () => AndroidAddressType,
AndroidBarcodeFormat: () => AndroidBarcodeFormat,
AndroidBarcodeValueType: () => AndroidBarcodeValueType,
AndroidEmailType: () => AndroidEmailType,
AndroidEncryptionType: () => AndroidEncryptionType,
AndroidPhoneType: () => AndroidPhoneType,
BARCODE_FORMATS: () => BARCODE_FORMATS,
BARCODE_TYPES: () => BARCODE_TYPES,
Templates: () => Templates,
VisionCameraSimpleScanner: () => VisionCameraSimpleScanner,
applyScaleFactor: () => applyScaleFactor,
applyTransformation: () => applyTransformation,
computeBoundingBoxFromCornerPoints: () => computeBoundingBoxFromCornerPoints,
computeHighlights: () => computeHighlights,
convertVisionCameraCodeToBarcode: () => convertVisionCameraCodeToBarcode,
isAndroidBarcode: () => isAndroidBarcode,
isIOSBarcode: () => isIOSBarcode,
normalizeAndroidCodeType: () => normalizeAndroidCodeType,
normalizeNativeBarcode: () => normalizeNativeBarcode,
normalizePrecision: () => normalizePrecision,
normalizeiOSCodeType: () => normalizeiOSCodeType,
onBarcodeDetected: () => onBarcodeDetected,
scanCodes: () => scanCodes,
useBarcodeScanner: () => useBarcodeScanner,
useLatestSharedValue: () => useLatestSharedValue
});
module.exports = __toCommonJS(src_exports);
// src/constants/android.ts
var AndroidBarcodeFormat = /* @__PURE__ */ ((AndroidBarcodeFormat2) => {
AndroidBarcodeFormat2[AndroidBarcodeFormat2["UNKNOWN"] = -1] = "UNKNOWN";
AndroidBarcodeFormat2[AndroidBarcodeFormat2["ALL_FORMATS"] = 0] = "ALL_FORMATS";
AndroidBarcodeFormat2[AndroidBarcodeFormat2["CODE_128"] = 1] = "CODE_128";
AndroidBarcodeFormat2[AndroidBarcodeFormat2["CODE_39"] = 2] = "CODE_39";
AndroidBarcodeFormat2[AndroidBarcodeFormat2["CODE_93"] = 4] = "CODE_93";
AndroidBarcodeFormat2[AndroidBarcodeFormat2["CODABAR"] = 8] = "CODABAR";
AndroidBarcodeFormat2[AndroidBarcodeFormat2["DATA_MATRIX"] = 16] = "DATA_MATRIX";
AndroidBarcodeFormat2[AndroidBarcodeFormat2["EAN_13"] = 32] = "EAN_13";
AndroidBarcodeFormat2[AndroidBarcodeFormat2["EAN_8"] = 64] = "EAN_8";
AndroidBarcodeFormat2[AndroidBarcodeFormat2["ITF"] = 128] = "ITF";
AndroidBarcodeFormat2[AndroidBarcodeFormat2["QR_CODE"] = 256] = "QR_CODE";
AndroidBarcodeFormat2[AndroidBarcodeFormat2["UPC_A"] = 512] = "UPC_A";
AndroidBarcodeFormat2[AndroidBarcodeFormat2["UPC_E"] = 1024] = "UPC_E";
AndroidBarcodeFormat2[AndroidBarcodeFormat2["PDF417"] = 2048] = "PDF417";
AndroidBarcodeFormat2[AndroidBarcodeFormat2["AZTEC"] = 4096] = "AZTEC";
return AndroidBarcodeFormat2;
})(AndroidBarcodeFormat || {});
var AndroidBarcodeValueType = /* @__PURE__ */ ((AndroidBarcodeValueType2) => {
AndroidBarcodeValueType2[AndroidBarcodeValueType2["UNKNOWN"] = 0] = "UNKNOWN";
AndroidBarcodeValueType2[AndroidBarcodeValueType2["CONTACT_INFO"] = 1] = "CONTACT_INFO";
AndroidBarcodeValueType2[AndroidBarcodeValueType2["EMAIL"] = 2] = "EMAIL";
AndroidBarcodeValueType2[AndroidBarcodeValueType2["ISBN"] = 3] = "ISBN";
AndroidBarcodeValueType2[AndroidBarcodeValueType2["PHONE"] = 4] = "PHONE";
AndroidBarcodeValueType2[AndroidBarcodeValueType2["PRODUCT"] = 5] = "PRODUCT";
AndroidBarcodeValueType2[AndroidBarcodeValueType2["SMS"] = 6] = "SMS";
AndroidBarcodeValueType2[AndroidBarcodeValueType2["TEXT"] = 7] = "TEXT";
AndroidBarcodeValueType2[AndroidBarcodeValueType2["URL"] = 8] = "URL";
AndroidBarcodeValueType2[AndroidBarcodeValueType2["WIFI"] = 9] = "WIFI";
AndroidBarcodeValueType2[AndroidBarcodeValueType2["GEO"] = 10] = "GEO";
AndroidBarcodeValueType2[AndroidBarcodeValueType2["CALENDAR_EVENT"] = 11] = "CALENDAR_EVENT";
AndroidBarcodeValueType2[AndroidBarcodeValueType2["DRIVER_LICENSE"] = 12] = "DRIVER_LICENSE";
return AndroidBarcodeValueType2;
})(AndroidBarcodeValueType || {});
var AndroidAddressType = /* @__PURE__ */ ((AndroidAddressType2) => {
AndroidAddressType2[AndroidAddressType2["UNKNOWN"] = 0] = "UNKNOWN";
AndroidAddressType2[AndroidAddressType2["WORK"] = 1] = "WORK";
AndroidAddressType2[AndroidAddressType2["HOME"] = 2] = "HOME";
return AndroidAddressType2;
})(AndroidAddressType || {});
var AndroidEncryptionType = /* @__PURE__ */ ((AndroidEncryptionType2) => {
AndroidEncryptionType2[AndroidEncryptionType2["OPEN"] = 1] = "OPEN";
AndroidEncryptionType2[AndroidEncryptionType2["WPA"] = 2] = "WPA";
AndroidEncryptionType2[AndroidEncryptionType2["WEP"] = 3] = "WEP";
return AndroidEncryptionType2;
})(AndroidEncryptionType || {});
var AndroidEmailType = /* @__PURE__ */ ((AndroidEmailType2) => {
AndroidEmailType2[AndroidEmailType2["UNKNOWN"] = 0] = "UNKNOWN";
AndroidEmailType2[AndroidEmailType2["WORK"] = 1] = "WORK";
AndroidEmailType2[AndroidEmailType2["HOME"] = 2] = "HOME";
return AndroidEmailType2;
})(AndroidEmailType || {});
var AndroidPhoneType = /* @__PURE__ */ ((AndroidPhoneType2) => {
AndroidPhoneType2[AndroidPhoneType2["UNKNOWN"] = 0] = "UNKNOWN";
AndroidPhoneType2[AndroidPhoneType2["WORK"] = 1] = "WORK";
AndroidPhoneType2[AndroidPhoneType2["HOME"] = 2] = "HOME";
AndroidPhoneType2[AndroidPhoneType2["FAX"] = 3] = "FAX";
AndroidPhoneType2[AndroidPhoneType2["MOBILE"] = 4] = "MOBILE";
return AndroidPhoneType2;
})(AndroidPhoneType || {});
// src/constants/templates.ts
var Templates = {
/**
* HD-quality for faster Frame Processing in YUV pixelFormat (e.g. 720p)
*/
FrameProcessingYUV: [{ videoResolution: { width: 1080, height: 720 } }],
/**
* XGA-quality for faster Frame Processing in YUV pixelFormat
*/
FrameProcessingBarcodeXGA: [
{ videoResolution: { width: 1024, height: 768 } }
]
};
// src/hooks/useBarcodeScanner.ts
var import_react2 = require("react");
var import_react_native_vision_camera2 = require("react-native-vision-camera");
var import_react_native_worklets_core2 = require("react-native-worklets-core");
// src/module.ts
var import_react_native4 = require("react-native");
var import_react_native_vision_camera = require("react-native-vision-camera");
// src/utils/convert.ts
var import_react_native = require("react-native");
// src/utils/types.ts
var normalizeiOSCodeType = (symbology) => {
"worklet";
switch (symbology) {
case "VNBarcodeSymbologyAztec":
return "aztec";
case "VNBarcodeSymbologyCode39":
case "VNBarcodeSymbologyCode39Checksum":
case "VNBarcodeSymbologyCode39FullASCII":
case "VNBarcodeSymbologyCode39FullASCIIChecksum":
return "code-39";
case "VNBarcodeSymbologyCode93":
case "VNBarcodeSymbologyCode93i":
return "code-93";
case "VNBarcodeSymbologyCode128":
return "code-128";
case "VNBarcodeSymbologyDataMatrix":
return "data-matrix";
case "VNBarcodeSymbologyEAN8":
return "ean-8";
case "VNBarcodeSymbologyEAN13":
return "ean-13";
case "VNBarcodeSymbologyGS1DataBar":
case "VNBarcodeSymbologyGS1DataBarExpanded":
case "VNBarcodeSymbologyGS1DataBarLimited":
return "gs1-databar";
case "VNBarcodeSymbologyI2of5":
case "VNBarcodeSymbologyI2of5Checksum":
case "VNBarcodeSymbologyITF14":
return "itf";
case "VNBarcodeSymbologyMicroPDF417":
case "VNBarcodeSymbologyPDF417":
return "pdf-417";
case "VNBarcodeSymbologyMicroQR":
case "VNBarcodeSymbologyQR":
return "qr";
case "VNBarcodeSymbologyMSIPlessey":
return "msi-plessey";
case "VNBarcodeSymbologyUPCE":
return "upc-e";
default:
return "unknown";
}
};
var normalizeAndroidCodeType = (format) => {
"worklet";
switch (format) {
case 4096 /* AZTEC */:
return "aztec";
case 8 /* CODABAR */:
return "codabar";
case 2 /* CODE_39 */:
return "code-39";
case 4 /* CODE_93 */:
return "code-93";
case 1 /* CODE_128 */:
return "code-128";
case 16 /* DATA_MATRIX */:
return "data-matrix";
case 64 /* EAN_8 */:
return "ean-8";
case 32 /* EAN_13 */:
return "ean-13";
case 128 /* ITF */:
return "itf";
case 2048 /* PDF417 */:
return "pdf-417";
case 256 /* QR_CODE */:
return "qr";
case 512 /* UPC_A */:
return "upc-a";
case 1024 /* UPC_E */:
return "upc-e";
case -1 /* UNKNOWN */:
default:
return "unknown";
}
};
// src/utils/convert.ts
var isIOSBarcode = (barcode) => {
"worklet";
return import_react_native.Platform.OS === "ios";
};
var isAndroidBarcode = (barcode) => {
"worklet";
return import_react_native.Platform.OS === "android";
};
var computeBoundingBoxFromCornerPoints = (cornerPoints) => {
"worklet";
const xArray = cornerPoints.map((corner) => corner.x);
const yArray = cornerPoints.map((corner) => corner.y);
const x = Math.min.apply(null, xArray);
const y = Math.min.apply(null, yArray);
const width = Math.max.apply(null, xArray) - x;
const height = Math.max.apply(null, yArray) - y;
return {
origin: { x, y },
size: {
width,
height
}
};
};
var normalizePrecision = (number) => {
"worklet";
return Math.round(number);
};
var normalizeNativeBarcode = (barcode, frame) => {
"worklet";
if (isIOSBarcode(barcode)) {
const { payload, symbology, boundingBox, corners } = barcode;
return {
value: payload,
type: normalizeiOSCodeType(symbology),
boundingBox: {
origin: {
x: normalizePrecision(boundingBox.origin.x * frame.width),
y: normalizePrecision(boundingBox.origin.y * frame.height)
},
size: {
width: normalizePrecision(boundingBox.size.width * frame.width),
height: normalizePrecision(boundingBox.size.height * frame.height)
}
},
// This explicitly defined to match android's implementation which is to start top left and go clockwise.
// https://developers.google.com/android/reference/com/google/mlkit/vision/barcode/common/Barcode#public-point[]-getcornerpoints
cornerPoints: [
corners.topLeft,
corners.topRight,
corners.bottomRight,
corners.bottomLeft
].map(({ x, y }) => ({
x: normalizePrecision(x * frame.width),
y: normalizePrecision(y * frame.height)
})),
native: barcode
};
} else if (isAndroidBarcode(barcode)) {
const { rawValue, format, cornerPoints } = barcode;
return {
value: rawValue,
type: normalizeAndroidCodeType(format),
boundingBox: computeBoundingBoxFromCornerPoints(cornerPoints),
cornerPoints,
native: barcode
};
} else {
throw new Error(`Unsupported platform: ${import_react_native.Platform.OS}`);
}
};
var convertVisionCameraCodeToBarcode = (code) => {
return {
value: code.value ?? null,
type: code.type,
boundingBox: {
origin: {
x: code.frame?.x ?? 0,
y: code.frame?.y ?? 0
},
size: {
width: code.frame?.width ?? 0,
height: code.frame?.height ?? 0
}
},
cornerPoints: code.corners ?? []
};
};
// src/utils/geometry.ts
var import_react_native2 = require("react-native");
var applyScaleFactor = ({ x, y }, source, target, resizeMode = "cover") => {
"worklet";
const ratio = {
width: target.width / source.width,
height: target.height / source.height
};
let scaleFactor;
if (resizeMode === "contain") {
scaleFactor = Math.min(ratio.width, ratio.height);
} else if (resizeMode === "cover") {
scaleFactor = Math.max(ratio.width, ratio.height);
} else {
throw new Error(`Invalid resize mode: ${resizeMode}`);
}
let newX = x * scaleFactor;
let newY = y * scaleFactor;
if (ratio.width < ratio.height && resizeMode === "contain" || ratio.width > ratio.height && resizeMode === "cover") {
newY += (target.height - source.height * scaleFactor) / 2;
} else {
newX += (target.width - source.width * scaleFactor) / 2;
}
return { x: normalizePrecision(newX), y: normalizePrecision(newY) };
};
var applyTransformation = ({ x, y }, { width, height }, orientation) => {
"worklet";
if (import_react_native2.Platform.OS === "android") {
switch (orientation) {
case "portrait":
return { x, y };
default:
console.warn(`Unsupported orientation: ${orientation}`);
return { x, y };
}
} else if (import_react_native2.Platform.OS === "ios") {
switch (orientation) {
case "portrait":
return { x: height - y, y: width - x };
case "landscape-left":
return { x: width - x, y };
case "landscape-right":
return { x, y: height - y };
case "portrait-upside-down":
return { x: y, y: x };
default:
console.warn(`Unsupported orientation: ${orientation}`);
return { x, y };
}
} else {
throw new Error(`Unsupported platform: ${import_react_native2.Platform.OS}`);
}
};
// src/utils/highlights.ts
var import_react_native3 = require("react-native");
var computeHighlights = (barcodes, frame, layout, resizeMode = "cover", pointMapper) => {
"worklet";
if (layout.width === 0 || layout.height === 0) {
return [];
}
let adjustedLayout = {
width: layout.height,
height: layout.width
};
if (import_react_native3.Platform.OS === "ios") {
adjustedLayout = ["landscape-left"].includes(frame.orientation) ? {
width: layout.height,
height: layout.width
} : {
width: layout.width,
height: layout.height
};
}
const highlights = barcodes.map((barcode, index) => {
const { value, cornerPoints } = barcode;
let translatedCornerPoints = cornerPoints;
translatedCornerPoints = translatedCornerPoints?.map(
(point) => applyScaleFactor(point, frame, adjustedLayout, resizeMode)
);
if (pointMapper) {
translatedCornerPoints = translatedCornerPoints?.map(
(point) => pointMapper(point, layout, frame.orientation)
);
} else {
translatedCornerPoints = translatedCornerPoints?.map(
(point) => applyTransformation(point, adjustedLayout, frame.orientation)
);
}
const valueFromCornerPoints = computeBoundingBoxFromCornerPoints(
translatedCornerPoints
);
return {
...barcode,
key: `${value}.${index}`,
cornerPoints: translatedCornerPoints,
boundingBox: valueFromCornerPoints
};
});
return highlights;
};
// src/module.ts
var LINKING_ERROR = `The package 'vision-camera-code-scanner' doesn't seem to be linked. Make sure:
` + import_react_native4.Platform.select({ ios: "- You have run 'pod install'\n", default: "" }) + "- You rebuilt the app after installing the package\n- You are not using Expo Go\n";
var VisionCameraSimpleScanner = import_react_native4.NativeModules.VisionCameraSimpleScanner ? import_react_native4.NativeModules.VisionCameraSimpleScanner : new Proxy(
{},
{
get() {
throw new Error(LINKING_ERROR);
}
}
);
var { MODULE_NAME, BARCODE_TYPES, BARCODE_FORMATS } = VisionCameraSimpleScanner.getConstants();
var visionCameraEventEmitter = new import_react_native4.NativeEventEmitter(
VisionCameraSimpleScanner
);
var onBarcodeDetected = (callback) => {
visionCameraEventEmitter.addListener("onBarcodeDetected", (nativeBarcode) => {
callback(nativeBarcode);
});
};
var visionCameraProcessorPlugin = import_react_native_vision_camera.VisionCameraProxy.initFrameProcessorPlugin(
MODULE_NAME
);
var scanCodes = (frame, options) => {
"worklet";
if (visionCameraProcessorPlugin == null) {
throw new Error(`Failed to load Frame Processor Plugin "${MODULE_NAME}"!`);
}
frame.incrementRefCount && frame.incrementRefCount();
const nativeCodes = visionCameraProcessorPlugin.call(
frame,
options
);
frame.decrementRefCount && frame.decrementRefCount();
if (!Array.isArray(nativeCodes)) {
console.log(
`Native frame processor ${MODULE_NAME} failed to return a proper array!`,
nativeCodes
);
return [];
}
return nativeCodes.slice().map((nativeBarcode) => normalizeNativeBarcode(nativeBarcode, frame));
};
// src/hooks/useLatestSharedValue.ts
var import_react = require("react");
var import_react_native_worklets_core = require("react-native-worklets-core");
function useLatestSharedValue(value, dependencies = [value]) {
const sharedValue = (0, import_react_native_worklets_core.useSharedValue)(value);
(0, import_react.useEffect)(() => {
sharedValue.value = value;
}, dependencies);
return sharedValue;
}
// src/hooks/useBarcodeScanner.ts
var useBarcodeScanner = ({
barcodeTypes,
regionOfInterest,
onBarcodeScanned,
pointMapper,
disableHighlighting,
resizeMode = "cover",
scanMode = "continuous",
isMountedRef,
fps = 30
}) => {
const layoutRef = (0, import_react_native_worklets_core2.useSharedValue)({ width: 0, height: 0 });
const onLayout = (event) => {
const { width, height } = event.nativeEvent.layout;
layoutRef.value = { width, height };
};
const resizeModeRef = useLatestSharedValue(resizeMode);
const barcodesRef = (0, import_react_native_worklets_core2.useSharedValue)([]);
const [highlights, setHighlights] = (0, import_react2.useState)([]);
const lastHighlightsCount = (0, import_react_native_worklets_core2.useSharedValue)(0);
const setHighlightsJS = import_react_native_worklets_core2.Worklets.createRunOnJS(setHighlights);
const pixelFormat = "yuv";
const frameProcessor = (0, import_react_native_vision_camera2.useFrameProcessor)(
(frame) => {
"worklet";
if (isMountedRef && isMountedRef.value === false) {
return;
}
(0, import_react_native_vision_camera2.runAtTargetFps)(fps, () => {
"worklet";
const { value: layout } = layoutRef;
const { value: prevBarcodes } = barcodesRef;
const { value: resizeMode2 } = resizeModeRef;
const { width, height, orientation } = frame;
const options = {};
if (barcodeTypes !== void 0) {
options.barcodeTypes = barcodeTypes;
}
if (regionOfInterest !== void 0) {
const { x, y, width: width2, height: height2 } = regionOfInterest;
options.regionOfInterest = [x, y, width2, height2];
}
const barcodes = scanCodes(frame, options);
if (barcodes.length > 0) {
if (scanMode === "continuous") {
onBarcodeScanned && onBarcodeScanned(barcodes, frame);
} else if (scanMode === "once") {
const hasChanged = prevBarcodes.length !== barcodes.length || JSON.stringify(prevBarcodes.map(({ value }) => value)) !== JSON.stringify(barcodes.map(({ value }) => value));
if (hasChanged) {
onBarcodeScanned && onBarcodeScanned(barcodes, frame);
}
}
barcodesRef.value = barcodes;
}
if (disableHighlighting !== true && resizeMode2 !== void 0) {
const highlights2 = computeHighlights(
barcodes,
{ width, height, orientation },
// "serialized" frame
layout,
resizeMode2,
pointMapper
);
if (lastHighlightsCount.value === 0 && highlights2.length === 0) {
return;
}
lastHighlightsCount.value = highlights2.length;
setHighlightsJS(highlights2);
}
});
},
[layoutRef, resizeModeRef, disableHighlighting]
);
return {
props: {
pixelFormat,
frameProcessor,
onLayout
},
highlights
};
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
AndroidAddressType,
AndroidBarcodeFormat,
AndroidBarcodeValueType,
AndroidEmailType,
AndroidEncryptionType,
AndroidPhoneType,
BARCODE_FORMATS,
BARCODE_TYPES,
Templates,
VisionCameraSimpleScanner,
applyScaleFactor,
applyTransformation,
computeBoundingBoxFromCornerPoints,
computeHighlights,
convertVisionCameraCodeToBarcode,
isAndroidBarcode,
isIOSBarcode,
normalizeAndroidCodeType,
normalizeNativeBarcode,
normalizePrecision,
normalizeiOSCodeType,
onBarcodeDetected,
scanCodes,
useBarcodeScanner,
useLatestSharedValue
});
//# sourceMappingURL=index.cjs.map