CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-zxing--library

TypeScript port of ZXing multi-format 1D/2D barcode image processing library

Overview
Eval results
Files

common-utilities.mddocs/reference/

Common Utilities

Core utility classes for bit manipulation, character encoding, mathematical operations, and image transformations used throughout the ZXing library.

Package Information

  • Module: @zxing/library
  • Package Type: npm
  • Language: TypeScript
  • Location: core/common/
  • Purpose: Foundation classes for barcode encoding and decoding

Overview

The common utilities module provides fundamental building blocks for barcode encoding and decoding operations:

  • Bit Manipulation: Compact data structures (BitArray, BitMatrix) for efficient bit storage and operations
  • Character Encoding: Character set interpretation (CharacterSetECI, StringUtils) and string encoding utilities
  • Transformations: Perspective transformations (PerspectiveTransform) for image processing
  • Grid Sampling: Extract grid samples (GridSampler, DefaultGridSampler) from images with perspective correction
  • Detection Utilities: Helper functions for pattern and shape detection (WhiteRectangleDetector)
  • Math Utilities: Common mathematical operations (MathUtils) for barcode processing

Core Imports

import {
  // 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";

Capabilities

BitArray

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)); // true

BitMatrix

2D 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:

  • Reuse BitArray instances when calling getRow() repeatedly
  • Use setRegion() for bulk operations instead of individual set() calls
  • get() is very fast - optimized for inner loops
  • getRow() returns a view when possible - avoid modifying returned arrays

BitSource

Bit 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);
}

CharacterSetECI

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)

PerspectiveTransform

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");

MathUtils

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]

Related Documentation

  • Core API - Main reader/writer classes and Result
  • Error Correction - Reed-Solomon implementation
  • Image Processing - LuminanceSource, Binarizer
  • QR Code - QR Code specific usage
  • Data Matrix - Data Matrix specific usage

Install with Tessl CLI

npx tessl i tessl/npm-zxing--library

docs

index.md

tile.json