TypeScript port of ZXing multi-format 1D/2D barcode image processing library
Core utility classes for bit manipulation, character encoding, mathematical operations, and image transformations used throughout the ZXing library.
core/common/The common utilities module provides fundamental building blocks for barcode encoding and decoding operations:
BitArray, BitMatrix) for efficient bit storage and operationsCharacterSetECI, StringUtils) and string encoding utilitiesPerspectiveTransform) for image processingGridSampler, DefaultGridSampler) from images with perspective correctionWhiteRectangleDetector)MathUtils) for barcode processingimport {
// Bit structures
BitArray,
BitMatrix,
BitSource,
// Character encoding
CharacterSetECI,
StringUtils,
StringEncoding,
Charset,
StringBuilder,
StandardCharsets,
// Geometric transformations
PerspectiveTransform,
GridSampler,
DefaultGridSampler,
GridSamplerInstance,
// Detection utilities
WhiteRectangleDetector,
// Math utilities
MathUtils,
Integer,
// Array utilities
Arrays,
System,
// Result types
DecoderResult,
DetectorResult
} from "@zxing/library";Compact bit array representation using Int32Array internally for efficient storage and manipulation of binary data.
/**
* A simple, fast array of bits
* Represented compactly by an array of 32-bit integers internally
* Optimized for sequential access and bulk operations
*/
class BitArray {
/**
* Create a bit array
* @param size - number: optional initial size in bits
* @param bits - Int32Array: optional pre-existing int array
*/
constructor(size?: number, bits?: Int32Array);
/**
* Get total size in bits
* @returns number: number of bits in array
*/
getSize(): number;
/**
* Get size in bytes (rounded up)
* @returns number: number of bytes needed to store bits
*/
getSizeInBytes(): number;
/**
* Get bit at index i
* @param i - number: bit index in [0, size)
* @returns boolean: true if bit is set (1), false if unset (0)
*/
get(i: number): boolean;
/**
* Set bit at index i to 1
* @param i - number: bit index
*/
set(i: number): void;
/**
* Flip bit at index i (0→1, 1→0)
* @param i - number: bit index
*/
flip(i: number): void;
/**
* Find index of next set bit from given position
* @param from - number: starting position
* @returns number: index of next set bit, or size if none found
*/
getNextSet(from: number): number;
/**
* Find index of next unset bit from given position
* @param from - number: starting position
* @returns number: index of next unset bit, or size if none found
*/
getNextUnset(from: number): number;
/**
* Set multiple bits starting at index i
* @param i - number: starting bit index
* @param newBits - number: 32-bit value to set
*/
setBulk(i: number, newBits: number): void;
/**
* Set range of bits from start to end (exclusive)
* @param start - number: start index (inclusive)
* @param end - number: end index (exclusive)
*/
setRange(start: number, end: number): void;
/**
* Clear all bits to 0
*/
clear(): void;
/**
* Check if range of bits all match expected value
* @param start - number: start index (inclusive)
* @param end - number: end index (exclusive)
* @param value - boolean: expected value (true or false)
* @returns boolean: true if all bits in range match value
*/
isRange(start: number, end: number, value: boolean): boolean;
/**
* Append single bit to end of array
* @param bit - boolean: bit value to append
*/
appendBit(bit: boolean): void;
/**
* Append multiple bits from value (least-significant bits first)
* @param value - number: value containing bits to append
* @param numBits - number: number of bits to take from value
*/
appendBits(value: number, numBits: number): void;
/**
* Append another BitArray to end of this array
* @param other - BitArray: array to append
*/
appendBitArray(other: BitArray): void;
/**
* XOR this array with another (arrays must be same size)
* @param other - BitArray: array to XOR with
* @throws IllegalArgumentException if sizes don't match
*/
xor(other: BitArray): void;
/**
* Convert bits to byte array
* @param bitOffset - number: bit offset in source
* @param array - Uint8Array: destination byte array
* @param offset - number: offset in destination array
* @param numBytes - number: number of bytes to convert
*/
toBytes(bitOffset: number, array: Uint8Array, offset: number, numBytes: number): void;
/**
* Get underlying Int32Array
* @returns Int32Array: internal bit storage
*/
getBitArray(): Int32Array;
/**
* Reverse all bits in the array
*/
reverse(): void;
/**
* Convert to boolean array
* @returns boolean[]: array where each element is true (1) or false (0)
*/
toArray(): boolean[];
/**
* Clone the BitArray
* @returns BitArray: deep copy of this array
*/
clone(): BitArray;
/**
* String representation using 'X' for set bits and '.' for unset bits
* @returns string: visualization of bit array
*/
toString(): string;
}Usage Examples:
import { BitArray } from "@zxing/library";
// Create a BitArray
const bits = new BitArray(32);
// Set individual bits
bits.set(5);
bits.set(10);
bits.set(15);
// Check bits
console.log("Bit 5:", bits.get(5)); // true
console.log("Bit 6:", bits.get(6)); // false
// Get size
console.log("Size:", bits.getSize()); // 32
console.log("Size in bytes:", bits.getSizeInBytes()); // 4
// Append bits
bits.appendBits(0b1010, 4); // Append 4 bits: 1, 0, 1, 0
console.log("After append, size:", bits.getSize()); // 36
// Set range of bits
bits.setRange(20, 25); // Set bits 20-24 to 1
// Find next set bit
const next: number = bits.getNextSet(0);
console.log("First set bit at index:", next); // 5
// Find next unset bit
const nextUnset: number = bits.getNextUnset(5);
console.log("Next unset bit after 5:", nextUnset); // 6
// XOR operation
const other = new BitArray(bits.getSize());
other.set(5);
other.set(25);
bits.xor(other); // XOR with other array
// Bit 5: was 1, XOR with 1 = 0
// Bit 25: was 0, XOR with 1 = 1
// Reverse bits
const original = bits.clone();
bits.reverse();
console.log("Reversed");
// Convert to boolean array
const boolArray: boolean[] = bits.toArray();
console.log("Boolean array length:", boolArray.length);
// String visualization
console.log("Bit pattern:", bits.toString());
// Output like: "....X....X....X................."
// Bulk operations
bits.setBulk(0, 0xFFFFFFFF); // Set first 32 bits
// Clear all bits
bits.clear();
console.log("After clear:", bits.getSize()); // Still 36, but all bits are 0
// Check range
bits.setRange(10, 20);
console.log("Bits 10-19 are set:", bits.isRange(10, 20, true)); // true
console.log("Bits 0-10 are unset:", bits.isRange(0, 10, false)); // true2D matrix of bits representing images or barcode patterns. Internally optimized with each row beginning on a 32-bit boundary.
/**
* Represents a 2D matrix of bits
* Coordinates are (x, y) where x is column and y is row
* Origin is at top-left (0, 0)
* true = black/set bit, false = white/unset bit
*/
class BitMatrix {
/**
* Create a bit matrix
* @param width - number: matrix width in bits
* @param height - number: optional matrix height (defaults to width for square)
* @param rowSize - number: optional internal row size (auto-calculated if not provided)
* @param bits - Int32Array: optional pre-existing data array
*/
constructor(
width: number,
height?: number,
rowSize?: number,
bits?: Int32Array
);
/**
* Get bit at position (x, y)
* @param x - number: column index in [0, width)
* @param y - number: row index in [0, height)
* @returns boolean: true means black/set, false means white/unset
*/
get(x: number, y: number): boolean;
/**
* Set bit at position (x, y) to 1 (black)
* @param x - number: column index
* @param y - number: row index
*/
set(x: number, y: number): void;
/**
* Unset bit at position (x, y) to 0 (white)
* @param x - number: column index
* @param y - number: row index
*/
unset(x: number, y: number): void;
/**
* Flip bit at position (x, y)
* @param x - number: column index
* @param y - number: row index
*/
flip(x: number, y: number): void;
/**
* XOR with another BitMatrix (must be same dimensions)
* @param mask - BitMatrix: matrix to XOR with
* @throws IllegalArgumentException if dimensions don't match
*/
xor(mask: BitMatrix): void;
/**
* Clear all bits to 0
*/
clear(): void;
/**
* Set rectangular region of bits to 1
* @param left - number: left coordinate
* @param top - number: top coordinate
* @param width - number: region width
* @param height - number: region height
*/
setRegion(left: number, top: number, width: number, height: number): void;
/**
* Get row as BitArray (reuses provided row if possible for performance)
* @param y - number: row index in [0, height)
* @param row - BitArray: optional pre-allocated BitArray to reuse
* @returns BitArray: binary row data
*/
getRow(y: number, row?: BitArray): BitArray;
/**
* Set entire row from BitArray
* @param y - number: row index
* @param row - BitArray: binary row data to set
*/
setRow(y: number, row: BitArray): void;
/**
* Rotate matrix 180 degrees in place
*/
rotate180(): void;
/**
* Get enclosing rectangle of all set bits
* @returns Int32Array|null: [left, top, width, height] or null if no set bits
*/
getEnclosingRectangle(): Int32Array | null;
/**
* Get coordinates of top-left set bit
* @returns Int32Array|null: [x, y] or null if no set bits
*/
getTopLeftOnBit(): Int32Array | null;
/**
* Get coordinates of bottom-right set bit
* @returns Int32Array|null: [x, y] or null if no set bits
*/
getBottomRightOnBit(): Int32Array | null;
/**
* Get matrix width
* @returns number: width in bits
*/
getWidth(): number;
/**
* Get matrix height
* @returns number: height in bits
*/
getHeight(): number;
/**
* Get internal row size (for memory layout)
* @returns number: row size in Int32Array elements
*/
getRowSize(): number;
/**
* Clone the BitMatrix
* @returns BitMatrix: deep copy
*/
clone(): BitMatrix;
/**
* String representation with custom characters for set/unset bits
* @param setString - string: character for set bits (default: "X")
* @param unsetString - string: character for unset bits (default: " ")
* @param lineSeparator - string: line separator (default: "\n")
* @returns string: formatted matrix visualization
*/
toString(setString?: string, unsetString?: string, lineSeparator?: string): string;
/**
* Parse BitMatrix from boolean 2D array
* @param image - boolean[][]: 2D array where true = set bit
* @returns BitMatrix: parsed matrix
*/
static parseFromBooleanArray(image: boolean[][]): BitMatrix;
/**
* Parse BitMatrix from string representation
* @param stringRepresentation - string: matrix as string
* @param setString - string: character representing set bits
* @param unsetString - string: character representing unset bits
* @returns BitMatrix: parsed matrix
*/
static parseFromString(
stringRepresentation: string,
setString: string,
unsetString: string
): BitMatrix;
}Usage Examples:
import { BitMatrix, BitArray } from "@zxing/library";
// Create a 21×21 BitMatrix (typical QR Code Version 1 size)
const matrix = new BitMatrix(21, 21);
// Set individual bits (drawing a finder pattern)
for (let i = 0; i < 7; i++) {
matrix.set(i, 0); // Top border
matrix.set(i, 6); // Bottom border
matrix.set(0, i); // Left border
matrix.set(6, i); // Right border
}
// Set a 3×3 region (finder pattern center)
matrix.setRegion(2, 2, 3, 3);
// Check bits
const isSet: boolean = matrix.get(2, 2); // true
const isUnset: boolean = matrix.get(10, 10); // false
// Get dimensions
console.log("Width:", matrix.getWidth()); // 21
console.log("Height:", matrix.getHeight()); // 21
// Get a row as BitArray
const row5: BitArray = matrix.getRow(5);
console.log("Row 5 size:", row5.getSize()); // 21
// Reuse BitArray for performance (in loops)
let reuseRow: BitArray | undefined;
for (let y = 0; y < matrix.getHeight(); y++) {
reuseRow = matrix.getRow(y, reuseRow);
// Process row...
}
// Find enclosing rectangle of all set bits
const rect: Int32Array | null = matrix.getEnclosingRectangle();
if (rect) {
console.log(`Enclosing rect: [${rect[0]}, ${rect[1]}, ${rect[2]}, ${rect[3]}]`);
// [left, top, width, height]
}
// Get top-left and bottom-right corners
const topLeft: Int32Array | null = matrix.getTopLeftOnBit();
const bottomRight: Int32Array | null = matrix.getBottomRightOnBit();
if (topLeft && bottomRight) {
console.log(`Top-left: (${topLeft[0]}, ${topLeft[1]})`);
console.log(`Bottom-right: (${bottomRight[0]}, ${bottomRight[1]})`);
}
// Rotate 180 degrees in place
matrix.rotate180();
console.log("Matrix rotated 180°");
// Clear all bits
matrix.clear();
// Clone matrix
const copy: BitMatrix = matrix.clone();
// Create from boolean array
const boolMatrix: boolean[][] = [
[true, false, true],
[false, true, false],
[true, false, true]
];
const parsedFromBool: BitMatrix = BitMatrix.parseFromBooleanArray(boolMatrix);
console.log("Parsed from boolean array:", parsedFromBool.getWidth(), "×", parsedFromBool.getHeight());
// Parse from string
const strRepresentation = "X X\n X \nX X\n";
const parsedFromString: BitMatrix = BitMatrix.parseFromString(strRepresentation, "X", " ");
console.log("Parsed from string:", parsedFromString.getWidth(), "×", parsedFromString.getHeight());
// Print to console with custom characters
console.log("Matrix visualization:");
console.log(matrix.toString("■", "□"));
// Output uses ■ for set bits and □ for unset bits
// XOR with another matrix (for mask application)
const mask = new BitMatrix(21, 21);
// ... populate mask ...
matrix.xor(mask);
console.log("Applied mask using XOR");Performance Tips:
BitArray instances when calling getRow() repeatedlysetRegion() for bulk operations instead of individual set() callsget() is very fast - optimized for inner loopsgetRow() returns a view when possible - avoid modifying returned arraysBit stream reader for sequentially reading bits from a byte array. Essential for decoding barcode data streams.
/**
* Provides easy abstraction to read bits at a time from a sequence of bytes
* Not reentrant - caller must not modify the bytes array during use
* Reads bits from most-significant to least-significant
*/
class BitSource {
/**
* Create bit source from byte array
* @param bytes - Uint8Array: bytes from which to read bits
*/
constructor(bytes: Uint8Array);
/**
* Get current bit offset within current byte
* @returns number: bit offset (0-7)
*/
getBitOffset(): number;
/**
* Get current byte offset in the array
* @returns number: byte offset
*/
getByteOffset(): number;
/**
* Read specified number of bits and return as integer
* @param numBits - number: number of bits to read (1-32)
* @returns number: value read from bits
* @throws IllegalArgumentException if not enough bits available
*/
readBits(numBits: number): number;
/**
* Get number of bits available to read
* @returns number: remaining bits
*/
available(): number;
}Usage Examples:
import { BitSource } from "@zxing/library";
// Create from byte array
const bytes = new Uint8Array([0b11010010, 0b10101111, 0b00110011]);
const source = new BitSource(bytes);
console.log("Total available bits:", source.available()); // 24
// Read bits sequentially
const first4: number = source.readBits(4); // 0b1101 = 13
console.log("First 4 bits:", first4);
const next3: number = source.readBits(3); // 0b001 = 1
console.log("Next 3 bits:", next3);
const next5: number = source.readBits(5); // 0b01010 = 10
console.log("Next 5 bits:", next5);
// Check position
console.log("Bit offset:", source.getBitOffset()); // 4 (bits into current byte)
console.log("Byte offset:", source.getByteOffset()); // 1 (current byte index)
// Check available bits
console.log("Available:", source.available()); // 12 bits remaining
// Read remaining bits in chunks
while (source.available() >= 8) {
const byte: number = source.readBits(8);
console.log("Read byte:", byte.toString(16).padStart(2, '0'));
}
// Read final partial byte
if (source.available() > 0) {
const remaining: number = source.available();
const finalBits: number = source.readBits(remaining);
console.log(`Final ${remaining} bits:`, finalBits.toString(2));
}
// Common pattern: reading mode indicators and counts
function readModeAndCount(source: BitSource): { mode: number; count: number } {
const mode: number = source.readBits(4); // 4-bit mode indicator
const count: number = source.readBits(8); // 8-bit character count
return { mode, count };
}
const bytes2 = new Uint8Array([0b00010100, 0b11001100]); // Mode 1, count 76
const source2 = new BitSource(bytes2);
const modeCount = readModeAndCount(source2);
console.log("Mode:", modeCount.mode); // 1
console.log("Count:", modeCount.count); // 76
// Error handling - reading too many bits
try {
const source3 = new BitSource(new Uint8Array([0xFF]));
const tooMany: number = source3.readBits(16); // Only 8 bits available
} catch (error) {
console.error("Not enough bits available:", error);
}Extended Channel Interpretation for character sets. Maps ECI values to character encodings per ISO 18004.
/**
* Encapsulates a Character Set ECI according to ISO 18004
* Provides mapping between ECI values and character set names
* Used for QR Code and other formats supporting multiple character encodings
*/
class CharacterSetECI {
/**
* Get ECI value identifier enum
* @returns CharacterSetValueIdentifiers: enum value
*/
getValueIdentifier(): CharacterSetValueIdentifiers;
/**
* Get character set name
* @returns string: character set name (e.g., "UTF8", "ISO8859_1")
*/
getName(): string;
/**
* Get numeric ECI value
* @returns number: ECI value (0-899)
*/
getValue(): number;
/**
* Get CharacterSetECI by numeric ECI value
* @param value - number: ECI value (0-899)
* @returns CharacterSetECI: corresponding character set
* @throws FormatException if value is unknown
*/
static getCharacterSetECIByValue(value: number): CharacterSetECI;
/**
* Get CharacterSetECI by encoding name
* @param name - string: character set name (case-insensitive, accepts aliases)
* @returns CharacterSetECI: corresponding character set
* @throws FormatException if name is unknown
*/
static getCharacterSetECIByName(name: string): CharacterSetECI;
// Predefined character sets (commonly used)
static readonly Cp437: CharacterSetECI; // ECI 2
static readonly ISO8859_1: CharacterSetECI; // ECI 3
static readonly ISO8859_2: CharacterSetECI; // ECI 4
static readonly ISO8859_3: CharacterSetECI; // ECI 5
static readonly ISO8859_4: CharacterSetECI; // ECI 6
static readonly ISO8859_5: CharacterSetECI; // ECI 7
static readonly ISO8859_6: CharacterSetECI; // ECI 8
static readonly ISO8859_7: CharacterSetECI; // ECI 9
static readonly ISO8859_8: CharacterSetECI; // ECI 10
static readonly ISO8859_9: CharacterSetECI; // ECI 11
static readonly ISO8859_10: CharacterSetECI; // ECI 12
static readonly ISO8859_11: CharacterSetECI; // ECI 13
static readonly ISO8859_13: CharacterSetECI; // ECI 15
static readonly ISO8859_14: CharacterSetECI; // ECI 16
static readonly ISO8859_15: CharacterSetECI; // ECI 17
static readonly ISO8859_16: CharacterSetECI; // ECI 18
static readonly SJIS: CharacterSetECI; // ECI 20 (Shift_JIS)
static readonly Cp1250: CharacterSetECI; // ECI 21
static readonly Cp1251: CharacterSetECI; // ECI 22
static readonly Cp1252: CharacterSetECI; // ECI 23
static readonly Cp1256: CharacterSetECI; // ECI 24
static readonly UnicodeBigUnmarked: CharacterSetECI; // ECI 25
static readonly UTF8: CharacterSetECI; // ECI 26
static readonly ASCII: CharacterSetECI; // ECI 27
static readonly Big5: CharacterSetECI; // ECI 28
static readonly GB18030: CharacterSetECI; // ECI 29
static readonly EUC_KR: CharacterSetECI; // ECI 30
}Usage Examples:
import { CharacterSetECI } from "@zxing/library";
// Get character set by name (case-insensitive, accepts aliases)
const utf8: CharacterSetECI = CharacterSetECI.getCharacterSetECIByName("UTF-8");
console.log("Name:", utf8.getName()); // "UTF8"
console.log("Value:", utf8.getValue()); // 26
// Alternative names work
const iso1: CharacterSetECI = CharacterSetECI.getCharacterSetECIByName("ISO-8859-1");
const iso2: CharacterSetECI = CharacterSetECI.getCharacterSetECIByName("ISO8859_1");
const iso3: CharacterSetECI = CharacterSetECI.getCharacterSetECIByName("ISO88591");
console.log("All ISO-8859-1:", iso1.equals(iso2) && iso2.equals(iso3)); // true
// Get character set by ECI value
const charset26: CharacterSetECI = CharacterSetECI.getCharacterSetECIByValue(26);
console.log("ECI 26:", charset26.getName()); // "UTF8"
const charset3: CharacterSetECI = CharacterSetECI.getCharacterSetECIByValue(3);
console.log("ECI 3:", charset3.getName()); // "ISO8859_1"
// Use predefined constants
const utf8Const: CharacterSetECI = CharacterSetECI.UTF8;
const iso8859_1: CharacterSetECI = CharacterSetECI.ISO8859_1;
const shiftJis: CharacterSetECI = CharacterSetECI.SJIS;
const gb18030: CharacterSetECI = CharacterSetECI.GB18030;
console.log("UTF-8 ECI:", utf8Const.getValue()); // 26
console.log("ISO-8859-1 ECI:", iso8859_1.getValue()); // 3
console.log("Shift_JIS ECI:", shiftJis.getValue()); // 20
console.log("GB18030 ECI:", gb18030.getValue()); // 29
// Common encodings with ECI values
const encodings = [
{ eci: CharacterSetECI.UTF8, value: 26, name: "UTF-8" },
{ eci: CharacterSetECI.ISO8859_1, value: 3, name: "ISO-8859-1" },
{ eci: CharacterSetECI.SJIS, value: 20, name: "Shift_JIS" },
{ eci: CharacterSetECI.GB18030, value: 29, name: "GB18030" },
{ eci: CharacterSetECI.Big5, value: 28, name: "Big5" },
{ eci: CharacterSetECI.EUC_KR, value: 30, name: "EUC-KR" },
];
console.log("Common character set ECIs:");
encodings.forEach(enc => {
console.log(` ${enc.name}: ECI ${enc.value}`);
});
// Handle QR Code ECI segment
function processECISegment(eciValue: number): string {
try {
const charset: CharacterSetECI = CharacterSetECI.getCharacterSetECIByValue(eciValue);
return charset.getName();
} catch (e) {
console.error(`Unsupported ECI value: ${eciValue}`);
return "UTF8"; // Fallback to UTF-8
}
}
console.log("ECI 26 →", processECISegment(26)); // "UTF8"
console.log("ECI 20 →", processECISegment(20)); // "SJIS"
console.log("ECI 999 →", processECISegment(999)); // "UTF8" (fallback)Perspective transformation matrix for correcting image distortion. Based on "Digital Image Warping" by George Wolberg. Essential for extracting skewed or rotated barcodes.
/**
* Implements perspective transform in 2D
* Computes transformation between four source and four destination points
* Used for correcting perspective distortion in barcode images
*/
class PerspectiveTransform {
/**
* Create transform from arbitrary quadrilateral to arbitrary quadrilateral
* @param x0, y0, x1, y1, x2, y2, x3, y3 - number: source quadrilateral corners
* @param x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p - number: destination quadrilateral corners
* @returns PerspectiveTransform: transformation from source to destination
*/
static quadrilateralToQuadrilateral(
x0: number, y0: number,
x1: number, y1: number,
x2: number, y2: number,
x3: number, y3: number,
x0p: number, y0p: number,
x1p: number, y1p: number,
x2p: number, y2p: number,
x3p: number, y3p: number
): PerspectiveTransform;
/**
* Create transform from unit square to arbitrary quadrilateral
* @param x0, y0, x1, y1, x2, y2, x3, y3 - number: destination quadrilateral corners
* @returns PerspectiveTransform: transformation from unit square
*/
static squareToQuadrilateral(
x0: number, y0: number,
x1: number, y1: number,
x2: number, y2: number,
x3: number, y3: number
): PerspectiveTransform;
/**
* Create transform from arbitrary quadrilateral to unit square
* @param x0, y0, x1, y1, x2, y2, x3, y3 - number: source quadrilateral corners
* @returns PerspectiveTransform: transformation to unit square
*/
static quadrilateralToSquare(
x0: number, y0: number,
x1: number, y1: number,
x2: number, y2: number,
x3: number, y3: number
): PerspectiveTransform;
/**
* Transform points in place (x0, y0, x1, y1, ..., xN, yN)
* Points array is modified in place with transformed coordinates
* @param points - Float32Array: interleaved x,y coordinates
*/
transformPoints(points: Float32Array): void;
/**
* Transform separate x and y coordinate arrays
* Arrays are modified in place with transformed coordinates
* @param xValues - Float32Array: array of x coordinates
* @param yValues - Float32Array: array of y coordinates
*/
transformPointsWithValues(xValues: Float32Array, yValues: Float32Array): void;
/**
* Build adjoint matrix (used for inverse transform)
* @returns PerspectiveTransform: adjoint transformation
*/
buildAdjoint(): PerspectiveTransform;
/**
* Multiply with another transform (compose transformations)
* @param other - PerspectiveTransform: transform to compose with
* @returns PerspectiveTransform: composed transformation
*/
times(other: PerspectiveTransform): PerspectiveTransform;
}Usage Examples:
import { PerspectiveTransform, ResultPoint } from "@zxing/library";
// Define source quadrilateral (skewed barcode corners in image)
const srcCorners = {
topLeft: { x: 10, y: 20 },
topRight: { x: 110, y: 25 },
bottomRight: { x: 115, y: 125 },
bottomLeft: { x: 15, y: 120 }
};
// Define destination quadrilateral (corrected rectangle)
const dstCorners = {
topLeft: { x: 0, y: 0 },
topRight: { x: 100, y: 0 },
bottomRight: { x: 100, y: 100 },
bottomLeft: { x: 0, y: 100 }
};
// Create transform
const transform: PerspectiveTransform = PerspectiveTransform.quadrilateralToQuadrilateral(
srcCorners.topLeft.x, srcCorners.topLeft.y,
srcCorners.topRight.x, srcCorners.topRight.y,
srcCorners.bottomRight.x, srcCorners.bottomRight.y,
srcCorners.bottomLeft.x, srcCorners.bottomLeft.y,
dstCorners.topLeft.x, dstCorners.topLeft.y,
dstCorners.topRight.x, dstCorners.topRight.y,
dstCorners.bottomRight.x, dstCorners.bottomRight.y,
dstCorners.bottomLeft.x, dstCorners.bottomLeft.y
);
// Transform multiple points
const points = new Float32Array([
10, 20, // Point 1
50, 60, // Point 2
90, 100 // Point 3
]);
transform.transformPoints(points);
console.log("Transformed points:", points);
// Points are now in destination coordinate system
// Transform separate arrays
const xCoords = new Float32Array([10, 50, 90]);
const yCoords = new Float32Array([20, 60, 100]);
transform.transformPointsWithValues(xCoords, yCoords);
console.log("Transformed X:", xCoords);
console.log("Transformed Y:", yCoords);
// Map from unit square to skewed quadrilateral
const squareToQuad: PerspectiveTransform = PerspectiveTransform.squareToQuadrilateral(
10, 20, 110, 25, 115, 125, 15, 120
);
// Sample points from unit square [0,1] × [0,1]
const samplePoints = new Float32Array([
0.5, 0.5, // Center of unit square
0, 0, // Top-left
1, 1 // Bottom-right
]);
squareToQuad.transformPoints(samplePoints);
console.log("Sampled points in image:", samplePoints);
// Inverse transform (quadrilateral to square)
const quadToSquare: PerspectiveTransform = PerspectiveTransform.quadrilateralToSquare(
10, 20, 110, 25, 115, 125, 15, 120
);
// Transform image points to normalized [0,1] space
const imagePoints = new Float32Array([
60, 70, // Point in image
110, 25 // Another point
]);
quadToSquare.transformPoints(imagePoints);
console.log("Normalized points:", imagePoints);
// Compose transforms
const composed: PerspectiveTransform = transform.times(quadToSquare);
console.log("Composed transformation created");
// Build adjoint (for inverse operations)
const adjoint: PerspectiveTransform = transform.buildAdjoint();
console.log("Adjoint transformation created");Mathematical utility functions for barcode processing.
/**
* General math-related and numeric utility functions
* Optimized for barcode processing operations
*/
class MathUtils {
/**
* Round to nearest integer (faster than Math.round for positive numbers)
* Note: For negative numbers, rounds toward zero (different from Math.round)
* Example: -2.5 rounds to -3 (not -2 like Math.round)
* @param d - number: real value to round
* @returns number: nearest integer
*/
static round(d: number): number;
/**
* Calculate Euclidean distance between two points
* @param aX - number: point A x coordinate
* @param aY - number: point A y coordinate
* @param bX - number: point B x coordinate
* @param bY - number: point B y coordinate
* @returns number: distance between points in pixels
*/
static distance(
aX: number,
aY: number,
bX: number,
bY: number
): number;
/**
* Sum all values in array
* @param array - Int32Array: values to sum
* @returns number: sum of all values
*/
static sum(array: Int32Array): number;
}Usage Examples:
import { MathUtils, ResultPoint } from "@zxing/library";
// Round values
console.log(MathUtils.round(3.7)); // 4
console.log(MathUtils.round(3.2)); // 3
console.log(MathUtils.round(3.5)); // 4
console.log(MathUtils.round(-2.5)); // -3 (note: different from Math.round which gives -2)
console.log(Math.round(-2.5)); // -2
// For positive values, MathUtils.round is faster than Math.round in tight loops
// Calculate distances between points
const dist1: number = MathUtils.distance(0, 0, 3, 4);
console.log("Distance (0,0) to (3,4):", dist1); // 5.0
const dist2: number = MathUtils.distance(10, 20, 13, 24);
console.log("Distance (10,20) to (13,24):", dist2); // 5.0
// Distance between finder pattern corners
const p1 = { x: 10, y: 10 };
const p2 = { x: 110, y: 15 };
const patternDist: number = MathUtils.distance(p1.x, p1.y, p2.x, p2.y);
console.log(`Finder pattern distance: ${patternDist.toFixed(2)} pixels`);
// Use with ResultPoint
const point1 = new ResultPoint(10, 20);
const point2 = new ResultPoint(40, 50);
const resultPointDist: number = ResultPoint.distance(point1, point2);
console.log("ResultPoint distance:", resultPointDist.toFixed(2));
// Sum histogram values
const histogram = new Int32Array([5, 10, 15, 20, 25, 30]);
const total: number = MathUtils.sum(histogram);
console.log(`Histogram total: ${total}`); // 105
// Calculate average
const average: number = total / histogram.length;
console.log(`Average: ${average.toFixed(2)}`); // 17.50
// Use in barcode detection - calculate module size
function calculateModuleSize(
point1X: number, point1Y: number,
point2X: number, point2Y: number,
modules: number
): number {
const distance: number = MathUtils.distance(point1X, point1Y, point2X, point2Y);
return distance / modules;
}
const moduleSize: number = calculateModuleSize(10, 10, 110, 10, 21);
console.log(`Module size: ${moduleSize.toFixed(2)} pixels/module`); // ~4.76
// Fast rounding in tight loops (performance critical code)
const coordinates = new Float32Array([10.7, 20.3, 30.8, 40.2]);
const rounded = new Int32Array(coordinates.length);
for (let i = 0; i < coordinates.length; i++) {
rounded[i] = MathUtils.round(coordinates[i]);
}
console.log("Rounded coordinates:", Array.from(rounded)); // [11, 20, 31, 40]Install with Tessl CLI
npx tessl i tessl/npm-zxing--library@0.21.14