UNPKG

@zxing/library

Version:

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

490 lines (489 loc) 18.4 kB
"use strict"; /* * Copyright 2007 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); /*namespace com.google.zxing.common {*/ /*import java.util.Arrays;*/ var BitArray_1 = require("./BitArray"); var System_1 = require("../util/System"); var Arrays_1 = require("../util/Arrays"); var StringBuilder_1 = require("../util/StringBuilder"); var IllegalArgumentException_1 = require("../IllegalArgumentException"); /** * <p>Represents a 2D matrix of bits. In function arguments below, and throughout the common * module, x is the column position, and y is the row position. The ordering is always x, y. * The origin is at the top-left.</p> * * <p>Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins * with a new int. This is done intentionally so that we can copy out a row into a BitArray very * efficiently.</p> * * <p>The ordering of bits is row-major. Within each int, the least significant bits are used first, * meaning they represent lower x values. This is compatible with BitArray's implementation.</p> * * @author Sean Owen * @author dswitkin@google.com (Daniel Switkin) */ var BitMatrix /*implements Cloneable*/ = /** @class */ (function () { /** * Creates an empty square {@link BitMatrix}. * * @param dimension height and width */ // public constructor(dimension: number /*int*/) { // this(dimension, dimension) // } /** * Creates an empty {@link BitMatrix}. * * @param width bit matrix width * @param height bit matrix height */ // public constructor(width: number /*int*/, height: number /*int*/) { // if (width < 1 || height < 1) { // throw new IllegalArgumentException("Both dimensions must be greater than 0") // } // this.width = width // this.height = height // this.rowSize = (width + 31) / 32 // bits = new int[rowSize * height]; // } function BitMatrix(width /*int*/, height /*int*/, rowSize /*int*/, bits) { this.width = width; this.height = height; this.rowSize = rowSize; this.bits = bits; if (undefined === height || null === height) { height = width; } this.height = height; if (width < 1 || height < 1) { throw new IllegalArgumentException_1.default('Both dimensions must be greater than 0'); } if (undefined === rowSize || null === rowSize) { rowSize = Math.floor((width + 31) / 32); } this.rowSize = rowSize; if (undefined === bits || null === bits) { this.bits = new Int32Array(this.rowSize * this.height); } } /** * Interprets a 2D array of booleans as a {@link BitMatrix}, where "true" means an "on" bit. * * @function parse * @param image bits of the image, as a row-major 2D array. Elements are arrays representing rows * @return {@link BitMatrix} representation of image */ BitMatrix.parseFromBooleanArray = function (image) { var height = image.length; var width = image[0].length; var bits = new BitMatrix(width, height); for (var i = 0; i < height; i++) { var imageI = image[i]; for (var j = 0; j < width; j++) { if (imageI[j]) { bits.set(j, i); } } } return bits; }; /** * * @function parse * @param stringRepresentation * @param setString * @param unsetString */ BitMatrix.parseFromString = function (stringRepresentation, setString, unsetString) { if (stringRepresentation === null) { throw new IllegalArgumentException_1.default('stringRepresentation cannot be null'); } var bits = new Array(stringRepresentation.length); var bitsPos = 0; var rowStartPos = 0; var rowLength = -1; var nRows = 0; var pos = 0; while (pos < stringRepresentation.length) { if (stringRepresentation.charAt(pos) === '\n' || stringRepresentation.charAt(pos) === '\r') { if (bitsPos > rowStartPos) { if (rowLength === -1) { rowLength = bitsPos - rowStartPos; } else if (bitsPos - rowStartPos !== rowLength) { throw new IllegalArgumentException_1.default('row lengths do not match'); } rowStartPos = bitsPos; nRows++; } pos++; } else if (stringRepresentation.substring(pos, pos + setString.length) === setString) { pos += setString.length; bits[bitsPos] = true; bitsPos++; } else if (stringRepresentation.substring(pos, pos + unsetString.length) === unsetString) { pos += unsetString.length; bits[bitsPos] = false; bitsPos++; } else { throw new IllegalArgumentException_1.default('illegal character encountered: ' + stringRepresentation.substring(pos)); } } // no EOL at end? if (bitsPos > rowStartPos) { if (rowLength === -1) { rowLength = bitsPos - rowStartPos; } else if (bitsPos - rowStartPos !== rowLength) { throw new IllegalArgumentException_1.default('row lengths do not match'); } nRows++; } var matrix = new BitMatrix(rowLength, nRows); for (var i = 0; i < bitsPos; i++) { if (bits[i]) { matrix.set(Math.floor(i % rowLength), Math.floor(i / rowLength)); } } return matrix; }; /** * <p>Gets the requested bit, where true means black.</p> * * @param x The horizontal component (i.e. which column) * @param y The vertical component (i.e. which row) * @return value of given bit in matrix */ BitMatrix.prototype.get = function (x /*int*/, y /*int*/) { var offset = y * this.rowSize + Math.floor(x / 32); return ((this.bits[offset] >>> (x & 0x1f)) & 1) !== 0; }; /** * <p>Sets the given bit to true.</p> * * @param x The horizontal component (i.e. which column) * @param y The vertical component (i.e. which row) */ BitMatrix.prototype.set = function (x /*int*/, y /*int*/) { var offset = y * this.rowSize + Math.floor(x / 32); this.bits[offset] |= (1 << (x & 0x1f)) & 0xFFFFFFFF; }; BitMatrix.prototype.unset = function (x /*int*/, y /*int*/) { var offset = y * this.rowSize + Math.floor(x / 32); this.bits[offset] &= ~((1 << (x & 0x1f)) & 0xFFFFFFFF); }; /** * <p>Flips the given bit.</p> * * @param x The horizontal component (i.e. which column) * @param y The vertical component (i.e. which row) */ BitMatrix.prototype.flip = function (x /*int*/, y /*int*/) { var offset = y * this.rowSize + Math.floor(x / 32); this.bits[offset] ^= ((1 << (x & 0x1f)) & 0xFFFFFFFF); }; /** * Exclusive-or (XOR): Flip the bit in this {@code BitMatrix} if the corresponding * mask bit is set. * * @param mask XOR mask */ BitMatrix.prototype.xor = function (mask) { if (this.width !== mask.getWidth() || this.height !== mask.getHeight() || this.rowSize !== mask.getRowSize()) { throw new IllegalArgumentException_1.default('input matrix dimensions do not match'); } var rowArray = new BitArray_1.default(Math.floor(this.width / 32) + 1); var rowSize = this.rowSize; var bits = this.bits; for (var y = 0, height = this.height; y < height; y++) { var offset = y * rowSize; var row = mask.getRow(y, rowArray).getBitArray(); for (var x = 0; x < rowSize; x++) { bits[offset + x] ^= row[x]; } } }; /** * Clears all bits (sets to false). */ BitMatrix.prototype.clear = function () { var bits = this.bits; var max = bits.length; for (var i = 0; i < max; i++) { bits[i] = 0; } }; /** * <p>Sets a square region of the bit matrix to true.</p> * * @param left The horizontal position to begin at (inclusive) * @param top The vertical position to begin at (inclusive) * @param width The width of the region * @param height The height of the region */ BitMatrix.prototype.setRegion = function (left /*int*/, top /*int*/, width /*int*/, height /*int*/) { if (top < 0 || left < 0) { throw new IllegalArgumentException_1.default('Left and top must be nonnegative'); } if (height < 1 || width < 1) { throw new IllegalArgumentException_1.default('Height and width must be at least 1'); } var right = left + width; var bottom = top + height; if (bottom > this.height || right > this.width) { throw new IllegalArgumentException_1.default('The region must fit inside the matrix'); } var rowSize = this.rowSize; var bits = this.bits; for (var y = top; y < bottom; y++) { var offset = y * rowSize; for (var x = left; x < right; x++) { bits[offset + Math.floor(x / 32)] |= ((1 << (x & 0x1f)) & 0xFFFFFFFF); } } }; /** * A fast method to retrieve one row of data from the matrix as a BitArray. * * @param y The row to retrieve * @param row An optional caller-allocated BitArray, will be allocated if null or too small * @return The resulting BitArray - this reference should always be used even when passing * your own row */ BitMatrix.prototype.getRow = function (y /*int*/, row) { if (row === null || row === undefined || row.getSize() < this.width) { row = new BitArray_1.default(this.width); } else { row.clear(); } var rowSize = this.rowSize; var bits = this.bits; var offset = y * rowSize; for (var x = 0; x < rowSize; x++) { row.setBulk(x * 32, bits[offset + x]); } return row; }; /** * @param y row to set * @param row {@link BitArray} to copy from */ BitMatrix.prototype.setRow = function (y /*int*/, row) { System_1.default.arraycopy(row.getBitArray(), 0, this.bits, y * this.rowSize, this.rowSize); }; /** * Modifies this {@code BitMatrix} to represent the same but rotated 180 degrees */ BitMatrix.prototype.rotate180 = function () { var width = this.getWidth(); var height = this.getHeight(); var topRow = new BitArray_1.default(width); var bottomRow = new BitArray_1.default(width); for (var i = 0, length_1 = Math.floor((height + 1) / 2); i < length_1; i++) { topRow = this.getRow(i, topRow); bottomRow = this.getRow(height - 1 - i, bottomRow); topRow.reverse(); bottomRow.reverse(); this.setRow(i, bottomRow); this.setRow(height - 1 - i, topRow); } }; /** * This is useful in detecting the enclosing rectangle of a 'pure' barcode. * * @return {@code left,top,width,height} enclosing rectangle of all 1 bits, or null if it is all white */ BitMatrix.prototype.getEnclosingRectangle = function () { var width = this.width; var height = this.height; var rowSize = this.rowSize; var bits = this.bits; var left = width; var top = height; var right = -1; var bottom = -1; for (var y = 0; y < height; y++) { for (var x32 = 0; x32 < rowSize; x32++) { var theBits = bits[y * rowSize + x32]; if (theBits !== 0) { if (y < top) { top = y; } if (y > bottom) { bottom = y; } if (x32 * 32 < left) { var bit = 0; while (((theBits << (31 - bit)) & 0xFFFFFFFF) === 0) { bit++; } if ((x32 * 32 + bit) < left) { left = x32 * 32 + bit; } } if (x32 * 32 + 31 > right) { var bit = 31; while ((theBits >>> bit) === 0) { bit--; } if ((x32 * 32 + bit) > right) { right = x32 * 32 + bit; } } } } } if (right < left || bottom < top) { return null; } return Int32Array.from([left, top, right - left + 1, bottom - top + 1]); }; /** * This is useful in detecting a corner of a 'pure' barcode. * * @return {@code x,y} coordinate of top-left-most 1 bit, or null if it is all white */ BitMatrix.prototype.getTopLeftOnBit = function () { var rowSize = this.rowSize; var bits = this.bits; var bitsOffset = 0; while (bitsOffset < bits.length && bits[bitsOffset] === 0) { bitsOffset++; } if (bitsOffset === bits.length) { return null; } var y = bitsOffset / rowSize; var x = (bitsOffset % rowSize) * 32; var theBits = bits[bitsOffset]; var bit = 0; while (((theBits << (31 - bit)) & 0xFFFFFFFF) === 0) { bit++; } x += bit; return Int32Array.from([x, y]); }; BitMatrix.prototype.getBottomRightOnBit = function () { var rowSize = this.rowSize; var bits = this.bits; var bitsOffset = bits.length - 1; while (bitsOffset >= 0 && bits[bitsOffset] === 0) { bitsOffset--; } if (bitsOffset < 0) { return null; } var y = Math.floor(bitsOffset / rowSize); var x = Math.floor(bitsOffset % rowSize) * 32; var theBits = bits[bitsOffset]; var bit = 31; while ((theBits >>> bit) === 0) { bit--; } x += bit; return Int32Array.from([x, y]); }; /** * @return The width of the matrix */ BitMatrix.prototype.getWidth = function () { return this.width; }; /** * @return The height of the matrix */ BitMatrix.prototype.getHeight = function () { return this.height; }; /** * @return The row size of the matrix */ BitMatrix.prototype.getRowSize = function () { return this.rowSize; }; /*@Override*/ BitMatrix.prototype.equals = function (o) { if (!(o instanceof BitMatrix)) { return false; } var other = o; return this.width === other.width && this.height === other.height && this.rowSize === other.rowSize && Arrays_1.default.equals(this.bits, other.bits); }; /*@Override*/ BitMatrix.prototype.hashCode = function () { var hash = this.width; hash = 31 * hash + this.width; hash = 31 * hash + this.height; hash = 31 * hash + this.rowSize; hash = 31 * hash + Arrays_1.default.hashCode(this.bits); return hash; }; /** * @return string representation using "X" for set and " " for unset bits */ /*@Override*/ // public toString(): string { // return toString(": "X, " ") // } /** * @param setString representation of a set bit * @param unsetString representation of an unset bit * @return string representation of entire matrix utilizing given strings */ // public toString(setString: string = "X ", unsetString: string = " "): string { // return this.buildToString(setString, unsetString, "\n") // } /** * @param setString representation of a set bit * @param unsetString representation of an unset bit * @param lineSeparator newline character in string representation * @return string representation of entire matrix utilizing given strings and line separator * @deprecated call {@link #toString(String,String)} only, which uses \n line separator always */ // @Deprecated BitMatrix.prototype.toString = function (setString, unsetString, lineSeparator) { if (setString === void 0) { setString = 'X '; } if (unsetString === void 0) { unsetString = ' '; } if (lineSeparator === void 0) { lineSeparator = '\n'; } return this.buildToString(setString, unsetString, lineSeparator); }; BitMatrix.prototype.buildToString = function (setString, unsetString, lineSeparator) { var result = new StringBuilder_1.default(); // result.append(lineSeparator); for (var y = 0, height = this.height; y < height; y++) { for (var x = 0, width = this.width; x < width; x++) { result.append(this.get(x, y) ? setString : unsetString); } result.append(lineSeparator); } return result.toString(); }; /*@Override*/ BitMatrix.prototype.clone = function () { return new BitMatrix(this.width, this.height, this.rowSize, this.bits.slice()); }; return BitMatrix; }()); exports.default = BitMatrix;