添加网站文件
This commit is contained in:
2
vendor/khanamiryan/qrcode-detector-decoder/.gitignore
vendored
Normal file
2
vendor/khanamiryan/qrcode-detector-decoder/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/composer.lock
|
||||
/vendor
|
||||
37
vendor/khanamiryan/qrcode-detector-decoder/README.md
vendored
Normal file
37
vendor/khanamiryan/qrcode-detector-decoder/README.md
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
# QR code decoder / reader for PHP
|
||||
This is a PHP library to detect and decode QR-codes.<br />This is first and only QR code reader that works without extensions.<br />
|
||||
Ported from [ZXing library](https://github.com/zxing/zxing)
|
||||
|
||||
|
||||
## Installation
|
||||
The recommended method of installing this library is via [Composer](https://getcomposer.org/).
|
||||
|
||||
Run the following command from your project root:
|
||||
|
||||
```bash
|
||||
$ composer require khanamiryan/qrcode-detector-decoder
|
||||
```
|
||||
|
||||
|
||||
## Usage
|
||||
```php
|
||||
require __DIR__ . "/vendor/autoload.php";
|
||||
$qrcode = new QrReader('path/to_image');
|
||||
$text = $qrcode->text(); //return decoded text from QR Code
|
||||
```
|
||||
|
||||
## Requirements
|
||||
* PHP >= 5.6
|
||||
* GD Library
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
You can help the project by adding features, cleaning the code, adding composer and other.
|
||||
|
||||
|
||||
1. Fork it
|
||||
2. Create your feature branch: `git checkout -b my-new-feature`
|
||||
3. Commit your changes: `git commit -am 'Add some feature'`
|
||||
4. Push to the branch: `git push origin my-new-feature`
|
||||
5. Submit a pull request
|
||||
24
vendor/khanamiryan/qrcode-detector-decoder/composer.json
vendored
Normal file
24
vendor/khanamiryan/qrcode-detector-decoder/composer.json
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "khanamiryan/qrcode-detector-decoder",
|
||||
"type": "library",
|
||||
"description": "QR code decoder / reader",
|
||||
"keywords": ["qr", "zxing", "barcode"],
|
||||
"homepage": "https://github.com/khanamiryan/php-qrcode-detector-decoder",
|
||||
"license": "MIT",
|
||||
"authors": [{
|
||||
"name": "Ashot Khanamiryan",
|
||||
"email": "a.khanamiryan@gmail.com",
|
||||
"homepage": "https://github.com/khanamiryan",
|
||||
"role": "Developer"
|
||||
}],
|
||||
"require": {
|
||||
"php": "^5.6|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.7"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["lib/"],
|
||||
"files": ["lib/common/customFunctions.php"]
|
||||
}
|
||||
}
|
||||
89
vendor/khanamiryan/qrcode-detector-decoder/lib/Binarizer.php
vendored
Normal file
89
vendor/khanamiryan/qrcode-detector-decoder/lib/Binarizer.php
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2009 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.
|
||||
*/
|
||||
|
||||
|
||||
namespace Zxing;
|
||||
|
||||
use Zxing\Common\BitArray;
|
||||
use Zxing\Common\BitMatrix;
|
||||
|
||||
/**
|
||||
* This class hierarchy provides a set of methods to convert luminance data to 1 bit data.
|
||||
* It allows the algorithm to vary polymorphically, for example allowing a very expensive
|
||||
* thresholding technique for servers and a fast one for mobile. It also permits the implementation
|
||||
* to vary, e.g. a JNI version for Android and a Java fallback version for other platforms.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
abstract class Binarizer {
|
||||
|
||||
private $source;
|
||||
|
||||
protected function __construct($source) {
|
||||
$this->source = $source;
|
||||
}
|
||||
|
||||
public final function getLuminanceSource() {
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
|
||||
* cached data. Callers should assume this method is expensive and call it as seldom as possible.
|
||||
* This method is intended for decoding 1D barcodes and may choose to apply sharpening.
|
||||
* For callers which only examine one row of pixels at a time, the same BitArray should be reused
|
||||
* and passed in with each call for performance. However it is legal to keep more than one row
|
||||
* at a time if needed.
|
||||
*
|
||||
* @param y The row to fetch, which must be in [0, bitmap height)
|
||||
* @param row An optional preallocated array. If null or too small, it will be ignored.
|
||||
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
|
||||
* @return The array of bits for this row (true means black).
|
||||
* @throws NotFoundException if row can't be binarized
|
||||
*/
|
||||
public abstract function getBlackRow($y, $row);
|
||||
|
||||
/**
|
||||
* Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive
|
||||
* and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
|
||||
* may not apply sharpening. Therefore, a row from this matrix may not be identical to one
|
||||
* fetched using getBlackRow(), so don't mix and match between them.
|
||||
*
|
||||
* @return The 2D array of bits for the image (true means black).
|
||||
* @throws NotFoundException if image can't be binarized to make a matrix
|
||||
*/
|
||||
public abstract function getBlackMatrix();
|
||||
|
||||
/**
|
||||
* Creates a new object with the same type as this Binarizer implementation, but with pristine
|
||||
* state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache
|
||||
* of 1 bit data. See Effective Java for why we can't use Java's clone() method.
|
||||
*
|
||||
* @param source The LuminanceSource this Binarizer will operate on.
|
||||
* @return A new concrete Binarizer implementation object.
|
||||
*/
|
||||
public abstract function createBinarizer($source);
|
||||
|
||||
public final function getWidth() {
|
||||
return $this->source->getWidth();
|
||||
}
|
||||
|
||||
public final function getHeight() {
|
||||
return $this->source->getHeight();
|
||||
}
|
||||
|
||||
}
|
||||
152
vendor/khanamiryan/qrcode-detector-decoder/lib/BinaryBitmap.php
vendored
Normal file
152
vendor/khanamiryan/qrcode-detector-decoder/lib/BinaryBitmap.php
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2009 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.
|
||||
*/
|
||||
|
||||
namespace Zxing;
|
||||
|
||||
use Zxing\Common\BitArray;
|
||||
use Zxing\Common\BitMatrix;
|
||||
|
||||
|
||||
/**
|
||||
* This class is the core bitmap class used by ZXing to represent 1 bit data. Reader objects
|
||||
* accept a BinaryBitmap and attempt to decode it.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
final class BinaryBitmap {
|
||||
|
||||
private $binarizer;
|
||||
private $matrix;
|
||||
|
||||
public function __construct($binarizer) {
|
||||
if ($binarizer == null) {
|
||||
throw new \InvalidArgumentException("Binarizer must be non-null.");
|
||||
}
|
||||
$this->binarizer = $binarizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The width of the bitmap.
|
||||
*/
|
||||
public function getWidth() {
|
||||
return $this->binarizer->getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The height of the bitmap.
|
||||
*/
|
||||
public function getHeight() {
|
||||
return $this->binarizer->getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
|
||||
* cached data. Callers should assume this method is expensive and call it as seldom as possible.
|
||||
* This method is intended for decoding 1D barcodes and may choose to apply sharpening.
|
||||
*
|
||||
* @param y The row to fetch, which must be in [0, bitmap height)
|
||||
* @param row An optional preallocated array. If null or too small, it will be ignored.
|
||||
* If used, the Binarizer will call BitArray.clear(). Always use the returned object.
|
||||
* @return The array of bits for this row (true means black).
|
||||
* @throws NotFoundException if row can't be binarized
|
||||
*/
|
||||
public function getBlackRow($y, $row) {
|
||||
return $this->binarizer->getBlackRow($y, $row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive
|
||||
* and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
|
||||
* may not apply sharpening. Therefore, a row from this matrix may not be identical to one
|
||||
* fetched using getBlackRow(), so don't mix and match between them.
|
||||
*
|
||||
* @return The 2D array of bits for the image (true means black).
|
||||
* @throws NotFoundException if image can't be binarized to make a matrix
|
||||
*/
|
||||
public function getBlackMatrix(){
|
||||
// The matrix is created on demand the first time it is requested, then cached. There are two
|
||||
// reasons for this:
|
||||
// 1. This work will never be done if the caller only installs 1D Reader objects, or if a
|
||||
// 1D Reader finds a barcode before the 2D Readers run.
|
||||
// 2. This work will only be done once even if the caller installs multiple 2D Readers.
|
||||
if ($this->matrix == null) {
|
||||
$this->matrix = $this->binarizer->getBlackMatrix();
|
||||
}
|
||||
return $this->matrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether this bitmap can be cropped.
|
||||
*/
|
||||
public function isCropSupported() {
|
||||
return $this->binarizer->getLuminanceSource()->isCropSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with cropped image data. Implementations may keep a reference to the
|
||||
* original data rather than a copy. Only callable if isCropSupported() is true.
|
||||
*
|
||||
* @param left The left coordinate, which must be in [0,getWidth())
|
||||
* @param top The top coordinate, which must be in [0,getHeight())
|
||||
* @param width The width of the rectangle to crop.
|
||||
* @param height The height of the rectangle to crop.
|
||||
* @return A cropped version of this object.
|
||||
*/
|
||||
public function crop($left, $top, $width, $height) {
|
||||
$newSource = $this->binarizer->getLuminanceSource()->crop($left, $top, $width, $height);
|
||||
return new BinaryBitmap($this->binarizer->createBinarizer($newSource));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether this bitmap supports counter-clockwise rotation.
|
||||
*/
|
||||
public function isRotateSupported() {
|
||||
return $this->binarizer->getLuminanceSource()->isRotateSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 90 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
*/
|
||||
public function rotateCounterClockwise() {
|
||||
$newSource = $this->binarizer->getLuminanceSource()->rotateCounterClockwise();
|
||||
return new BinaryBitmap($this->binarizer->createBinarizer($newSource));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 45 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
*/
|
||||
public function rotateCounterClockwise45() {
|
||||
$newSource = $this->binarizer->getLuminanceSource()->rotateCounterClockwise45();
|
||||
return new BinaryBitmap($this->binarizer->createBinarizer($newSource));
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function toString() {
|
||||
try {
|
||||
return $this->getBlackMatrix()->toString();
|
||||
} catch (NotFoundException $e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
44
vendor/khanamiryan/qrcode-detector-decoder/lib/ChecksumException.php
vendored
Normal file
44
vendor/khanamiryan/qrcode-detector-decoder/lib/ChecksumException.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing;
|
||||
|
||||
/**
|
||||
* Thrown when a barcode was successfully detected and decoded, but
|
||||
* was not returned because its checksum feature failed.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class ChecksumException extends ReaderException {
|
||||
|
||||
private static $instance;
|
||||
|
||||
|
||||
|
||||
public static function getChecksumInstance($cause=null) {
|
||||
if (self::$isStackTrace) {
|
||||
return new ChecksumException($cause);
|
||||
} else {
|
||||
if(!self::$instance){
|
||||
self::$instance = new ChecksumException($cause);
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
52
vendor/khanamiryan/qrcode-detector-decoder/lib/FormatException.php
vendored
Normal file
52
vendor/khanamiryan/qrcode-detector-decoder/lib/FormatException.php
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing;
|
||||
|
||||
/**
|
||||
* Thrown when a barcode was successfully detected, but some aspect of
|
||||
* the content did not conform to the barcode's format rules. This could have
|
||||
* been due to a mis-detection.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class FormatException extends ReaderException {
|
||||
|
||||
private static $instance;
|
||||
|
||||
|
||||
public function __construct($cause=null) {
|
||||
|
||||
if($cause){
|
||||
parent::__construct($cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static function getFormatInstance($cause=null) {
|
||||
if(!self::$instance){
|
||||
self::$instance = new FormatException();
|
||||
}
|
||||
if (self::$isStackTrace) {
|
||||
return new FormatException($cause);
|
||||
} else {
|
||||
return self::$instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
173
vendor/khanamiryan/qrcode-detector-decoder/lib/GDLuminanceSource.php
vendored
Normal file
173
vendor/khanamiryan/qrcode-detector-decoder/lib/GDLuminanceSource.php
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Zxing;
|
||||
|
||||
/**
|
||||
* This class is used to help decode images from files which arrive as GD Resource
|
||||
* It does not support rotation.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
final class GDLuminanceSource extends LuminanceSource {
|
||||
|
||||
public $luminances;
|
||||
private $dataWidth;
|
||||
private $dataHeight;
|
||||
private $left;
|
||||
private $top;
|
||||
private $gdImage;
|
||||
|
||||
|
||||
|
||||
public function __construct($gdImage,
|
||||
$dataWidth,
|
||||
$dataHeight,
|
||||
$left=null,
|
||||
$top=null,
|
||||
$width=null,
|
||||
$height=null) {
|
||||
if(!$left&&!$top&&!$width&&!$height){
|
||||
$this->GDLuminanceSource($gdImage,$dataWidth,$dataHeight);
|
||||
return;
|
||||
}
|
||||
parent::__construct($width, $height);
|
||||
if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
|
||||
throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
$this->luminances = $gdImage;
|
||||
$this->dataWidth = $dataWidth;
|
||||
$this->dataHeight = $dataHeight;
|
||||
$this->left = $left;
|
||||
$this->top = $top;
|
||||
}
|
||||
|
||||
public function GDLuminanceSource($gdImage, $width, $height)
|
||||
{
|
||||
parent::__construct($width, $height);
|
||||
|
||||
$this->dataWidth = $width;
|
||||
$this->dataHeight = $height;
|
||||
$this->left = 0;
|
||||
$this->top = 0;
|
||||
$this->$gdImage = $gdImage;
|
||||
|
||||
|
||||
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
|
||||
// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
|
||||
$this->luminances = array();
|
||||
//$this->luminances = $this->grayScaleToBitmap($this->grayscale());
|
||||
|
||||
$array = array();
|
||||
$rgb = array();
|
||||
|
||||
for($j=0;$j<$height;$j++){
|
||||
for($i=0;$i<$width;$i++){
|
||||
$argb = imagecolorat($this->$gdImage, $i, $j);
|
||||
$pixel = imagecolorsforindex($this->$gdImage, $argb);
|
||||
$r = $pixel['red'];
|
||||
$g = $pixel['green'];
|
||||
$b = $pixel['blue'];
|
||||
if ($r == $g && $g == $b) {
|
||||
// Image is already greyscale, so pick any channel.
|
||||
|
||||
$this->luminances[] = $r;//(($r + 128) % 256) - 128;
|
||||
} else {
|
||||
// Calculate luminance cheaply, favoring green.
|
||||
$this->luminances[] = ($r+2*$g+$b)/4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$offset = $y * $width;
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
$pixel = $pixels[$offset + $x];
|
||||
$r = ($pixel >> 16) & 0xff;
|
||||
$g = ($pixel >> 8) & 0xff;
|
||||
$b = $pixel & 0xff;
|
||||
if ($r == $g && $g == $b) {
|
||||
// Image is already greyscale, so pick any channel.
|
||||
|
||||
$this->luminances[intval($offset + $x)] = (($r+128) % 256) - 128;
|
||||
} else {
|
||||
// Calculate luminance cheaply, favoring green.
|
||||
$this->luminances[intval($offset + $x)] = (((($r + 2 * $g + $b) / 4)+128)%256) - 128;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
//}
|
||||
// $this->luminances = $this->grayScaleToBitmap($this->luminances);
|
||||
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function getRow($y, $row=null) {
|
||||
if ($y < 0 || $y >= $this->getHeight()) {
|
||||
throw new \InvalidArgumentException("Requested row is outside the image: " + y);
|
||||
}
|
||||
$width = $this->getWidth();
|
||||
if ($row == null || count($row) < $width) {
|
||||
$row = array();
|
||||
}
|
||||
$offset = ($y + $this->top) * $this->dataWidth + $this->left;
|
||||
$row = arraycopy($this->luminances,$offset, $row, 0, $width);
|
||||
return $row;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function getMatrix() {
|
||||
$width = $this->getWidth();
|
||||
$height = $this->getHeight();
|
||||
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if ($width == $this->dataWidth && $height == $this->dataHeight) {
|
||||
return $this->luminances;
|
||||
}
|
||||
|
||||
$area = $width * $height;
|
||||
$matrix = array();
|
||||
$inputOffset = $this->top * $this->dataWidth + $this->left;
|
||||
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if ($width == $this->dataWidth) {
|
||||
$matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area);
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
// Otherwise copy one cropped row at a time.
|
||||
$rgb = $this->luminances;
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$outputOffset = $y * $width;
|
||||
$matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width);
|
||||
$inputOffset += $this->dataWidth;
|
||||
}
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function isCropSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function crop($left, $top, $width, $height) {
|
||||
return new GDLuminanceSource($this->luminances,
|
||||
$this->dataWidth,
|
||||
$this->dataHeight,
|
||||
$this->left + $left,
|
||||
$this->top + $top,
|
||||
$width,
|
||||
$height);
|
||||
}
|
||||
|
||||
}
|
||||
149
vendor/khanamiryan/qrcode-detector-decoder/lib/IMagickLuminanceSource.php
vendored
Normal file
149
vendor/khanamiryan/qrcode-detector-decoder/lib/IMagickLuminanceSource.php
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Zxing;
|
||||
|
||||
/**
|
||||
* This class is used to help decode images from files which arrive as GD Resource
|
||||
* It does not support rotation.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
final class IMagickLuminanceSource extends LuminanceSource {
|
||||
|
||||
public $luminances;
|
||||
private $dataWidth;
|
||||
private $dataHeight;
|
||||
private $left;
|
||||
private $top;
|
||||
private $image;
|
||||
|
||||
|
||||
|
||||
public function __construct($image,
|
||||
$dataWidth,
|
||||
$dataHeight,
|
||||
$left=null,
|
||||
$top=null,
|
||||
$width=null,
|
||||
$height=null) {
|
||||
if(!$left&&!$top&&!$width&&!$height){
|
||||
$this->_IMagickLuminanceSource($image,$dataWidth,$dataHeight);
|
||||
return;
|
||||
}
|
||||
parent::__construct($width, $height);
|
||||
if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
|
||||
throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
$this->luminances = $image;
|
||||
$this->dataWidth = $dataWidth;
|
||||
$this->dataHeight = $dataHeight;
|
||||
$this->left = $left;
|
||||
$this->top = $top;
|
||||
}
|
||||
|
||||
public function _IMagickLuminanceSource($image, $width, $height)
|
||||
{
|
||||
parent::__construct($width, $height);
|
||||
|
||||
$this->dataWidth = $width;
|
||||
$this->dataHeight = $height;
|
||||
$this->left = 0;
|
||||
$this->top = 0;
|
||||
$this->image = $image;
|
||||
|
||||
|
||||
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
|
||||
// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
|
||||
$this->luminances = array();
|
||||
|
||||
$image->setImageColorspace (\Imagick::COLORSPACE_GRAY);
|
||||
// $image->newPseudoImage(0, 0, "magick:rose");
|
||||
$pixels = $image->exportImagePixels(1, 1, $width, $height, "RGB", \Imagick::COLORSPACE_RGB);
|
||||
|
||||
$array = array();
|
||||
$rgb = array();
|
||||
|
||||
|
||||
for($i=0;$i<count($pixels);$i+=3){
|
||||
|
||||
$r = $pixels[$i]& 0xff;
|
||||
$g = $pixels[$i+1]& 0xff;
|
||||
$b = $pixels[$i+2]& 0xff;
|
||||
if ($r == $g && $g == $b) {
|
||||
// Image is already greyscale, so pick any channel.
|
||||
|
||||
$this->luminances[] = $r;//(($r + 128) % 256) - 128;
|
||||
} else {
|
||||
// Calculate luminance cheaply, favoring green.
|
||||
$this->luminances[] = ($r+2*$g+$b)/4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function getRow($y, $row=null) {
|
||||
if ($y < 0 || $y >= $this->getHeight()) {
|
||||
throw new \InvalidArgumentException("Requested row is outside the image: " + y);
|
||||
}
|
||||
$width = $this->getWidth();
|
||||
if ($row == null || count($row) < $width) {
|
||||
$row = array();
|
||||
}
|
||||
$offset = ($y + $this->top) * $this->dataWidth + $this->left;
|
||||
$row = arraycopy($this->luminances,$offset, $row, 0, $width);
|
||||
return $row;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function getMatrix() {
|
||||
$width = $this->getWidth();
|
||||
$height = $this->getHeight();
|
||||
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if ($width == $this->dataWidth && $height == $this->dataHeight) {
|
||||
return $this->luminances;
|
||||
}
|
||||
|
||||
$area = $width * $height;
|
||||
$matrix = array();
|
||||
$inputOffset = $this->top * $this->dataWidth + $this->left;
|
||||
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if ($width == $this->dataWidth) {
|
||||
$matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area);
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
// Otherwise copy one cropped row at a time.
|
||||
$rgb = $this->luminances;
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$outputOffset = $y * $width;
|
||||
$matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width);
|
||||
$inputOffset += $this->dataWidth;
|
||||
}
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function isCropSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function crop($left, $top, $width, $height) {
|
||||
return new GDLuminanceSource($this->luminances,
|
||||
$this->dataWidth,
|
||||
$this->dataHeight,
|
||||
$this->left + $left,
|
||||
$this->top + $top,
|
||||
$width,
|
||||
$height);
|
||||
}
|
||||
|
||||
}
|
||||
159
vendor/khanamiryan/qrcode-detector-decoder/lib/LuminanceSource.php
vendored
Normal file
159
vendor/khanamiryan/qrcode-detector-decoder/lib/LuminanceSource.php
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2009 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.
|
||||
*/
|
||||
|
||||
namespace Zxing;
|
||||
|
||||
/**
|
||||
* The purpose of this class hierarchy is to abstract different bitmap implementations across
|
||||
* platforms into a standard interface for requesting greyscale luminance values. The interface
|
||||
* only provides immutable methods; therefore crop and rotation create copies. This is to ensure
|
||||
* that one Reader does not modify the original luminance source and leave it in an unknown state
|
||||
* for other Readers in the chain.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
abstract class LuminanceSource {
|
||||
|
||||
private $width;
|
||||
private $height;
|
||||
|
||||
function __construct($width, $height) {
|
||||
$this->width = $width;
|
||||
$this->height = $height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches one row of luminance data from the underlying platform's bitmap. Values range from
|
||||
* 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have
|
||||
* to bitwise and with 0xff for each value. It is preferable for implementations of this method
|
||||
* to only fetch this row rather than the whole image, since no 2D Readers may be installed and
|
||||
* getMatrix() may never be called.
|
||||
*
|
||||
* @param $y; The row to fetch, which must be in [0,getHeight())
|
||||
* @param $row; An optional preallocated array. If null or too small, it will be ignored.
|
||||
* Always use the returned object, and ignore the .length of the array.
|
||||
* @return array
|
||||
* An array containing the luminance data.
|
||||
*/
|
||||
public abstract function getRow($y, $row);
|
||||
|
||||
/**
|
||||
* Fetches luminance data for the underlying bitmap. Values should be fetched using:
|
||||
* {@code int luminance = array[y * width + x] & 0xff}
|
||||
*
|
||||
* @return A row-major 2D array of luminance values. Do not use result.length as it may be
|
||||
* larger than width * height bytes on some platforms. Do not modify the contents
|
||||
* of the result.
|
||||
*/
|
||||
public abstract function getMatrix();
|
||||
|
||||
/**
|
||||
* @return The width of the bitmap.
|
||||
*/
|
||||
public final function getWidth() {
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The height of the bitmap.
|
||||
*/
|
||||
public final function getHeight() {
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether this subclass supports cropping.
|
||||
*/
|
||||
public function isCropSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with cropped image data. Implementations may keep a reference to the
|
||||
* original data rather than a copy. Only callable if isCropSupported() is true.
|
||||
*
|
||||
* @param left The left coordinate, which must be in [0,getWidth())
|
||||
* @param top The top coordinate, which must be in [0,getHeight())
|
||||
* @param width The width of the rectangle to crop.
|
||||
* @param height The height of the rectangle to crop.
|
||||
* @return A cropped version of this object.
|
||||
*/
|
||||
public function crop($left, $top, $width, $height) {
|
||||
throw new \Exception("This luminance source does not support cropping.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether this subclass supports counter-clockwise rotation.
|
||||
*/
|
||||
public function isRotateSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a wrapper of this {@code LuminanceSource} which inverts the luminances it returns -- black becomes
|
||||
* white and vice versa, and each value becomes (255-value).
|
||||
*/
|
||||
public function invert() {
|
||||
return new InvertedLuminanceSource($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 90 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
*/
|
||||
public function rotateCounterClockwise() {
|
||||
throw new \Exception("This luminance source does not support rotation by 90 degrees.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new object with rotated image data by 45 degrees counterclockwise.
|
||||
* Only callable if {@link #isRotateSupported()} is true.
|
||||
*
|
||||
* @return A rotated version of this object.
|
||||
*/
|
||||
public function rotateCounterClockwise45() {
|
||||
throw new \Exception("This luminance source does not support rotation by 45 degrees.");
|
||||
}
|
||||
|
||||
//@Override
|
||||
public final function toString() {
|
||||
$row = array();
|
||||
$result = '';
|
||||
for ($y = 0;$y < $this->height; $y++) {
|
||||
$row = $this->getRow($y, $row);
|
||||
for ($x = 0; $x < $this->width; $x++) {
|
||||
$luminance = $row[$x] & 0xFF;
|
||||
$c='';
|
||||
if ($luminance < 0x40) {
|
||||
$c = '#';
|
||||
} else if ($luminance < 0x80) {
|
||||
$c = '+';
|
||||
} else if ($luminance < 0xC0) {
|
||||
$c = '.';
|
||||
} else {
|
||||
$c = ' ';
|
||||
}
|
||||
$result.=($c);
|
||||
}
|
||||
$result.=('\n');
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
38
vendor/khanamiryan/qrcode-detector-decoder/lib/NotFoundException.php
vendored
Normal file
38
vendor/khanamiryan/qrcode-detector-decoder/lib/NotFoundException.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing;
|
||||
|
||||
/**
|
||||
* Thrown when a barcode was not found in the image. It might have been
|
||||
* partially detected but could not be confirmed.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class NotFoundException extends ReaderException {
|
||||
|
||||
private static $instance;
|
||||
|
||||
|
||||
public static function getNotFoundInstance() {
|
||||
if(!self::$instance ){
|
||||
self::$instance = new NotFoundException();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
}
|
||||
172
vendor/khanamiryan/qrcode-detector-decoder/lib/PlanarYUVLuminanceSource.php
vendored
Normal file
172
vendor/khanamiryan/qrcode-detector-decoder/lib/PlanarYUVLuminanceSource.php
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2009 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.
|
||||
*/
|
||||
|
||||
namespace Zxing;
|
||||
|
||||
/**
|
||||
* This object extends LuminanceSource around an array of YUV data returned from the camera driver,
|
||||
* with the option to crop to a rectangle within the full data. This can be used to exclude
|
||||
* superfluous pixels around the perimeter and speed up decoding.
|
||||
*
|
||||
* It works for any pixel format where the Y channel is planar and appears first, including
|
||||
* YCbCr_420_SP and YCbCr_422_SP.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
final class PlanarYUVLuminanceSource extends LuminanceSource {
|
||||
|
||||
private static $THUMBNAIL_SCALE_FACTOR = 2;
|
||||
|
||||
private $yuvData;
|
||||
private $dataWidth;
|
||||
private $dataHeight;
|
||||
private $left;
|
||||
private $top;
|
||||
|
||||
public function __construct($yuvData,
|
||||
$dataWidth,
|
||||
$dataHeight,
|
||||
$left,
|
||||
$top,
|
||||
$width,
|
||||
$height,
|
||||
$reverseHorizontal) {
|
||||
parent::__construct($width, $height);
|
||||
|
||||
if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
|
||||
throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
|
||||
$this->yuvData = $yuvData;
|
||||
$this->dataWidth = $dataWidth;
|
||||
$this->dataHeight = $dataHeight;
|
||||
$this->left = $left;
|
||||
$this->top = $top;
|
||||
if ($reverseHorizontal) {
|
||||
$this->reverseHorizontal($width, $height);
|
||||
}
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function getRow($y, $row=null) {
|
||||
if ($y < 0 || $y >= getHeight()) {
|
||||
throw new IllegalArgumentException("Requested row is outside the image: " + y);
|
||||
}
|
||||
$width = $this->getWidth();
|
||||
if ($row == null || count($row) < $width) {
|
||||
$row = array();//new byte[width];
|
||||
}
|
||||
$offset = ($y + $this->top) * $this->dataWidth + $this->left;
|
||||
$row = arraycopy($this->yuvData, $offset, $row, 0, $width);
|
||||
return $row;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function getMatrix() {
|
||||
$width = $this->getWidth();
|
||||
$height = $this->getHeight();
|
||||
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if ($width == $this->dataWidth && $height == $this->dataHeight) {
|
||||
return $this->yuvData;
|
||||
}
|
||||
|
||||
$area = $width * $height;
|
||||
$matrix = array();//new byte[area];
|
||||
$inputOffset = $this->top * $this->dataWidth + $this->left;
|
||||
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if ($width == $this->dataWidth) {
|
||||
$matrix = arraycopy($this->yuvData, $inputOffset, $matrix, 0, $area);
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
// Otherwise copy one cropped row at a time.
|
||||
$yuv = $this->yuvData;
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$outputOffset = $y * $width;
|
||||
$matrix = arraycopy($this->yuvData, $inputOffset, $matrix, $outputOffset, $width);
|
||||
$inputOffset += $this->dataWidth;
|
||||
}
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public function isCropSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public function crop($left, $top, $width, $height) {
|
||||
return new PlanarYUVLuminanceSource($this->yuvData,
|
||||
$this->dataWidth,
|
||||
$this->dataHeight,
|
||||
$this->left + $left,
|
||||
$this->top + $top,
|
||||
$width,
|
||||
$height,
|
||||
false);
|
||||
}
|
||||
|
||||
public function renderThumbnail() {
|
||||
$width = intval($this->getWidth() / self::$THUMBNAIL_SCALE_FACTOR);
|
||||
$height = intval($this->getHeight() / self::$THUMBNAIL_SCALE_FACTOR);
|
||||
$pixels = array();//new int[width * height];
|
||||
$yuv = $this->yuvData;
|
||||
$inputOffset = $this->top * $this->dataWidth + $this->left;
|
||||
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$outputOffset = $y * $width;
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
$grey = intval32bits($yuv[$inputOffset + $x * self::$THUMBNAIL_SCALE_FACTOR] & 0xff);
|
||||
$pixels[$outputOffset + $x] = intval32bits(0xFF000000 | ($grey * 0x00010101));
|
||||
}
|
||||
$inputOffset += $this->dataWidth * self::$THUMBNAIL_SCALE_FACTOR;
|
||||
}
|
||||
return $pixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return width of image from {@link #renderThumbnail()}
|
||||
*/
|
||||
/*
|
||||
public int getThumbnailWidth() {
|
||||
return getWidth() / THUMBNAIL_SCALE_FACTOR;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* @return height of image from {@link #renderThumbnail()}
|
||||
*/
|
||||
/*
|
||||
public int getThumbnailHeight() {
|
||||
return getHeight() / THUMBNAIL_SCALE_FACTOR;
|
||||
}
|
||||
|
||||
private void reverseHorizontal(int width, int height) {
|
||||
byte[] yuvData = this.yuvData;
|
||||
for (int y = 0, rowStart = top * dataWidth + left; y < height; y++, rowStart += dataWidth) {
|
||||
int middle = rowStart + width / 2;
|
||||
for (int x1 = rowStart, x2 = rowStart + width - 1; x1 < middle; x1++, x2--) {
|
||||
byte temp = yuvData[x1];
|
||||
yuvData[x1] = yuvData[x2];
|
||||
yuvData[x2] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
84
vendor/khanamiryan/qrcode-detector-decoder/lib/QrReader.php
vendored
Normal file
84
vendor/khanamiryan/qrcode-detector-decoder/lib/QrReader.php
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
final class QrReader
|
||||
{
|
||||
const SOURCE_TYPE_FILE = 'file';
|
||||
const SOURCE_TYPE_BLOB = 'blob';
|
||||
const SOURCE_TYPE_RESOURCE = 'resource';
|
||||
public $result;
|
||||
|
||||
function __construct($imgsource, $sourcetype = QrReader::SOURCE_TYPE_FILE, $isUseImagickIfAvailable = true)
|
||||
{
|
||||
|
||||
try {
|
||||
switch($sourcetype) {
|
||||
case QrReader::SOURCE_TYPE_FILE:
|
||||
if($isUseImagickIfAvailable && extension_loaded('imagick')) {
|
||||
$im = new Imagick();
|
||||
$im->readImage($imgsource);
|
||||
}else {
|
||||
$image = file_get_contents($imgsource);
|
||||
$im = imagecreatefromstring($image);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case QrReader::SOURCE_TYPE_BLOB:
|
||||
if($isUseImagickIfAvailable && extension_loaded('imagick')) {
|
||||
$im = new Imagick();
|
||||
$im->readimageblob($imgsource);
|
||||
}else {
|
||||
$im = imagecreatefromstring($imgsource);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case QrReader::SOURCE_TYPE_RESOURCE:
|
||||
$im = $imgsource;
|
||||
if($isUseImagickIfAvailable && extension_loaded('imagick')) {
|
||||
$isUseImagickIfAvailable = true;
|
||||
}else {
|
||||
$isUseImagickIfAvailable = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if($isUseImagickIfAvailable && extension_loaded('imagick')) {
|
||||
$width = $im->getImageWidth();
|
||||
$height = $im->getImageHeight();
|
||||
$source = new \Zxing\IMagickLuminanceSource($im, $width, $height);
|
||||
}else {
|
||||
$width = imagesx($im);
|
||||
$height = imagesy($im);
|
||||
$source = new \Zxing\GDLuminanceSource($im, $width, $height);
|
||||
}
|
||||
$histo = new \Zxing\Common\HybridBinarizer($source);
|
||||
$bitmap = new \Zxing\BinaryBitmap($histo);
|
||||
$reader = new \Zxing\Qrcode\QRCodeReader();
|
||||
|
||||
$this->result = $reader->decode($bitmap);
|
||||
}catch (\Zxing\NotFoundException $er){
|
||||
$this->result = false;
|
||||
}catch( \Zxing\FormatException $er){
|
||||
$this->result = false;
|
||||
}catch( \Zxing\ChecksumException $er){
|
||||
$this->result = false;
|
||||
}
|
||||
}
|
||||
|
||||
public function text()
|
||||
{
|
||||
if(method_exists($this->result,'toString')) {
|
||||
return ($this->result->toString());
|
||||
}else{
|
||||
return $this->result;
|
||||
}
|
||||
}
|
||||
|
||||
public function decode()
|
||||
{
|
||||
return $this->text();
|
||||
}
|
||||
}
|
||||
|
||||
310
vendor/khanamiryan/qrcode-detector-decoder/lib/RGBLuminanceSource.php
vendored
Normal file
310
vendor/khanamiryan/qrcode-detector-decoder/lib/RGBLuminanceSource.php
vendored
Normal file
@@ -0,0 +1,310 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2009 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.
|
||||
*/
|
||||
|
||||
namespace Zxing;
|
||||
|
||||
/**
|
||||
* This class is used to help decode images from files which arrive as RGB data from
|
||||
* an ARGB pixel array. It does not support rotation.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
* @author Betaminos
|
||||
*/
|
||||
final class RGBLuminanceSource extends LuminanceSource {
|
||||
|
||||
public $luminances;
|
||||
private $dataWidth;
|
||||
private $dataHeight;
|
||||
private $left;
|
||||
private $top;
|
||||
private $pixels;
|
||||
|
||||
|
||||
|
||||
public function __construct($pixels,
|
||||
$dataWidth,
|
||||
$dataHeight,
|
||||
$left=null,
|
||||
$top=null,
|
||||
$width=null,
|
||||
$height=null) {
|
||||
if(!$left&&!$top&&!$width&&!$height){
|
||||
$this->RGBLuminanceSource_($pixels,$dataWidth,$dataHeight);
|
||||
return;
|
||||
}
|
||||
parent::__construct($width, $height);
|
||||
if ($left + $width > $dataWidth || $top + $height > $dataHeight) {
|
||||
throw new \InvalidArgumentException("Crop rectangle does not fit within image data.");
|
||||
}
|
||||
$this->luminances = $pixels;
|
||||
$this->dataWidth = $dataWidth;
|
||||
$this->dataHeight = $dataHeight;
|
||||
$this->left = $left;
|
||||
$this->top = $top;
|
||||
}
|
||||
|
||||
public function RGBLuminanceSource_($width, $height, $pixels)
|
||||
{
|
||||
parent::__construct($width, $height);
|
||||
|
||||
$this->dataWidth = $width;
|
||||
$this->dataHeight = $height;
|
||||
$this->left = 0;
|
||||
$this->top = 0;
|
||||
$this->pixels = $pixels;
|
||||
|
||||
|
||||
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
|
||||
// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
|
||||
$this->luminances = array();
|
||||
//$this->luminances = $this->grayScaleToBitmap($this->grayscale());
|
||||
|
||||
foreach ($pixels as $key => $pixel) {
|
||||
$r = $pixel['red'];
|
||||
$g = $pixel['green'];
|
||||
$b = $pixel['blue'];
|
||||
|
||||
/* if (($pixel & 0xFF000000) == 0) {
|
||||
$pixel = 0xFFFFFFFF; // = white
|
||||
}
|
||||
|
||||
// .229R + 0.587G + 0.114B (YUV/YIQ for PAL and NTSC)
|
||||
|
||||
$this->luminances[$key] =
|
||||
(306 * (($pixel >> 16) & 0xFF) +
|
||||
601 * (($pixel >> 8) & 0xFF) +
|
||||
117 * ($pixel & 0xFF) +
|
||||
0x200) >> 10;
|
||||
|
||||
*/
|
||||
//$r = ($pixel >> 16) & 0xff;
|
||||
//$g = ($pixel >> 8) & 0xff;
|
||||
//$b = $pixel & 0xff;
|
||||
if ($r == $g && $g == $b) {
|
||||
// Image is already greyscale, so pick any channel.
|
||||
|
||||
$this->luminances[$key] = $r;//(($r + 128) % 256) - 128;
|
||||
} else {
|
||||
// Calculate luminance cheaply, favoring green.
|
||||
$this->luminances[$key] = ($r+2*$g+$b)/4;//(((($r + 2 * $g + $b) / 4) + 128) % 256) - 128;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$offset = $y * $width;
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
$pixel = $pixels[$offset + $x];
|
||||
$r = ($pixel >> 16) & 0xff;
|
||||
$g = ($pixel >> 8) & 0xff;
|
||||
$b = $pixel & 0xff;
|
||||
if ($r == $g && $g == $b) {
|
||||
// Image is already greyscale, so pick any channel.
|
||||
|
||||
$this->luminances[intval($offset + $x)] = (($r+128) % 256) - 128;
|
||||
} else {
|
||||
// Calculate luminance cheaply, favoring green.
|
||||
$this->luminances[intval($offset + $x)] = (((($r + 2 * $g + $b) / 4)+128)%256) - 128;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
//}
|
||||
// $this->luminances = $this->grayScaleToBitmap($this->luminances);
|
||||
|
||||
}
|
||||
function grayscale(){
|
||||
$width = $this->dataWidth;
|
||||
$height = $this->dataHeight;
|
||||
|
||||
$ret = fill_array(0, $width*$height,0);
|
||||
for ($y = 0; $y < $height; $y++)
|
||||
{
|
||||
for ($x = 0; $x < $width; $x++)
|
||||
{
|
||||
$gray = $this->getPixel($x, $y,$width,$height);
|
||||
|
||||
$ret[$x+$y*$width] = $gray;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
function getPixel($x,$y,$width,$height){
|
||||
$image = $this->pixels;
|
||||
if ($width < $x) {
|
||||
die('error');
|
||||
}
|
||||
if ($height < $y) {
|
||||
die('error');
|
||||
}
|
||||
$point = ($x) + ($y * $width);
|
||||
|
||||
$r = $image[$point]['red'];//($image[$point] >> 16) & 0xff;
|
||||
$g = $image[$point]['green'];//($image[$point] >> 8) & 0xff;
|
||||
$b = $image[$point]['blue'];//$image[$point] & 0xff;
|
||||
|
||||
$p = intval(($r*33 +$g*34 + $b*33)/100);
|
||||
|
||||
|
||||
return $p;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function getMiddleBrightnessPerArea($image)
|
||||
{
|
||||
$numSqrtArea = 4;
|
||||
//obtain middle brightness((min + max) / 2) per area
|
||||
$areaWidth = floor($this->dataWidth / $numSqrtArea);
|
||||
$areaHeight = floor($this->dataHeight / $numSqrtArea);
|
||||
$minmax = fill_array(0,$numSqrtArea,0);
|
||||
for ($i = 0; $i < $numSqrtArea; $i++)
|
||||
{
|
||||
$minmax[$i] = fill_array(0,$numSqrtArea,0);
|
||||
for ($i2 = 0; $i2 < $numSqrtArea; $i2++)
|
||||
{
|
||||
$minmax[$i][$i2] = array(0,0);
|
||||
}
|
||||
}
|
||||
for ($ay = 0; $ay < $numSqrtArea; $ay++)
|
||||
{
|
||||
for ($ax = 0; $ax < $numSqrtArea; $ax++)
|
||||
{
|
||||
$minmax[$ax][$ay][0] = 0xFF;
|
||||
for ($dy = 0; $dy < $areaHeight; $dy++)
|
||||
{
|
||||
for ($dx = 0; $dx < $areaWidth; $dx++)
|
||||
{
|
||||
$target = $image[intval($areaWidth * $ax + $dx+($areaHeight * $ay + $dy)*$this->dataWidth)];
|
||||
if ($target < $minmax[$ax][$ay][0])
|
||||
$minmax[$ax][$ay][0] = $target;
|
||||
if ($target > $minmax[$ax][$ay][1])
|
||||
$minmax[$ax][$ay][1] = $target;
|
||||
}
|
||||
}
|
||||
//minmax[ax][ay][0] = (minmax[ax][ay][0] + minmax[ax][ay][1]) / 2;
|
||||
}
|
||||
}
|
||||
$middle = array();
|
||||
for ($i3 = 0; $i3 < $numSqrtArea; $i3++)
|
||||
{
|
||||
$middle[$i3] = array();
|
||||
}
|
||||
for ($ay = 0; $ay < $numSqrtArea; $ay++)
|
||||
{
|
||||
for ($ax = 0; $ax < $numSqrtArea; $ax++)
|
||||
{
|
||||
$middle[$ax][$ay] = floor(($minmax[$ax][$ay][0] + $minmax[$ax][$ay][1]) / 2);
|
||||
//Console.out.print(middle[ax][ay] + ",");
|
||||
}
|
||||
//Console.out.println("");
|
||||
}
|
||||
//Console.out.println("");
|
||||
|
||||
return $middle;
|
||||
}
|
||||
|
||||
function grayScaleToBitmap ($grayScale)
|
||||
{
|
||||
$middle = $this->getMiddleBrightnessPerArea($grayScale);
|
||||
$sqrtNumArea = count($middle);
|
||||
$areaWidth = floor($this->dataWidth/ $sqrtNumArea);
|
||||
$areaHeight = floor($this->dataHeight / $sqrtNumArea);
|
||||
$bitmap = fill_array(0,$this->dataWidth*$this->dataHeight,0);
|
||||
|
||||
for ($ay = 0; $ay < $sqrtNumArea; $ay++)
|
||||
{
|
||||
for ($ax = 0; $ax < $sqrtNumArea; $ax++)
|
||||
{
|
||||
for ($dy = 0; $dy < $areaHeight; $dy++)
|
||||
{
|
||||
for ($dx = 0; $dx < $areaWidth; $dx++)
|
||||
{
|
||||
$bitmap[intval($areaWidth * $ax + $dx+ ($areaHeight * $ay + $dy)*$this->dataWidth)] = ($grayScale[intval($areaWidth * $ax + $dx+ ($areaHeight * $ay + $dy)*$this->dataWidth)] < $middle[$ax][$ay])?0:255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $bitmap;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function getRow($y, $row=null) {
|
||||
if ($y < 0 || $y >= $this->getHeight()) {
|
||||
throw new \InvalidArgumentException("Requested row is outside the image: " + y);
|
||||
}
|
||||
$width = $this->getWidth();
|
||||
if ($row == null || count($row) < $width) {
|
||||
$row = array();
|
||||
}
|
||||
$offset = ($y + $this->top) * $this->dataWidth + $this->left;
|
||||
$row = arraycopy($this->luminances,$offset, $row, 0, $width);
|
||||
return $row;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function getMatrix() {
|
||||
$width = $this->getWidth();
|
||||
$height = $this->getHeight();
|
||||
|
||||
// If the caller asks for the entire underlying image, save the copy and give them the
|
||||
// original data. The docs specifically warn that result.length must be ignored.
|
||||
if ($width == $this->dataWidth && $height == $this->dataHeight) {
|
||||
return $this->luminances;
|
||||
}
|
||||
|
||||
$area = $width * $height;
|
||||
$matrix = array();
|
||||
$inputOffset = $this->top * $this->dataWidth + $this->left;
|
||||
|
||||
// If the width matches the full width of the underlying data, perform a single copy.
|
||||
if ($width == $this->dataWidth) {
|
||||
$matrix = arraycopy($this->luminances, $inputOffset, $matrix, 0, $area);
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
// Otherwise copy one cropped row at a time.
|
||||
$rgb = $this->luminances;
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$outputOffset = $y * $width;
|
||||
$matrix = arraycopy($rgb, $inputOffset, $matrix, $outputOffset, $width);
|
||||
$inputOffset += $this->dataWidth;
|
||||
}
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function isCropSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function crop($left, $top, $width, $height) {
|
||||
return new RGBLuminanceSource($this->luminances,
|
||||
$this->dataWidth,
|
||||
$this->dataHeight,
|
||||
$this->left + $left,
|
||||
$this->top + $top,
|
||||
$width,
|
||||
$height);
|
||||
}
|
||||
|
||||
}
|
||||
13
vendor/khanamiryan/qrcode-detector-decoder/lib/Reader.php
vendored
Normal file
13
vendor/khanamiryan/qrcode-detector-decoder/lib/Reader.php
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Zxing;
|
||||
|
||||
interface Reader {
|
||||
|
||||
public function decode($image);
|
||||
|
||||
|
||||
public function reset();
|
||||
|
||||
|
||||
}
|
||||
48
vendor/khanamiryan/qrcode-detector-decoder/lib/ReaderException.php
vendored
Normal file
48
vendor/khanamiryan/qrcode-detector-decoder/lib/ReaderException.php
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing;
|
||||
|
||||
/**
|
||||
* The general exception class throw when something goes wrong during decoding of a barcode.
|
||||
* This includes, but is not limited to, failing checksums / error correction algorithms, being
|
||||
* unable to locate finder timing patterns, and so on.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
abstract class ReaderException extends \Exception {
|
||||
|
||||
// disable stack traces when not running inside test units
|
||||
//protected static $isStackTrace = System.getProperty("surefire.test.class.path") != null;
|
||||
protected static $isStackTrace = false;
|
||||
|
||||
function ReaderException($cause=null) {
|
||||
if($cause){
|
||||
parent::__construct($cause);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Prevent stack traces from being taken
|
||||
// srowen says: huh, my IDE is saying this is not an override. native methods can't be overridden?
|
||||
// This, at least, does not hurt. Because we use a singleton pattern here, it doesn't matter anyhow.
|
||||
//@Override
|
||||
public final function fillInStackTrace() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
128
vendor/khanamiryan/qrcode-detector-decoder/lib/Result.php
vendored
Normal file
128
vendor/khanamiryan/qrcode-detector-decoder/lib/Result.php
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Encapsulates the result of decoding a barcode within an image.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class Result {
|
||||
|
||||
private $text;
|
||||
private $rawBytes;
|
||||
private $resultPoints;
|
||||
private $format;
|
||||
private $resultMetadata;
|
||||
private $timestamp;
|
||||
|
||||
|
||||
|
||||
public function __construct($text,
|
||||
$rawBytes,
|
||||
$resultPoints,
|
||||
$format,
|
||||
$timestamp = '') {
|
||||
|
||||
$this->text = $text;
|
||||
$this->rawBytes = $rawBytes;
|
||||
$this->resultPoints = $resultPoints;
|
||||
$this->format = $format;
|
||||
$this->resultMetadata = null;
|
||||
$this->timestamp = $timestamp?$timestamp:time();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return raw text encoded by the barcode
|
||||
*/
|
||||
public function getText() {
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return raw bytes encoded by the barcode, if applicable, otherwise {@code null}
|
||||
*/
|
||||
public function getRawBytes() {
|
||||
return $this->rawBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return points related to the barcode in the image. These are typically points
|
||||
* identifying finder patterns or the corners of the barcode. The exact meaning is
|
||||
* specific to the type of barcode that was decoded.
|
||||
*/
|
||||
public function getResultPoints() {
|
||||
return $this->resultPoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link BarcodeFormat} representing the format of the barcode that was decoded
|
||||
*/
|
||||
public function getBarcodeFormat() {
|
||||
return $this->format;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link Map} mapping {@link ResultMetadataType} keys to values. May be
|
||||
* {@code null}. This contains optional metadata about what was detected about the barcode,
|
||||
* like orientation.
|
||||
*/
|
||||
public function getResultMetadata() {
|
||||
return $this->resultMetadata;
|
||||
}
|
||||
|
||||
public function putMetadata($type, $value) {
|
||||
if ($this->resultMetadata == null) {
|
||||
$this->resultMetadata = array();
|
||||
}
|
||||
$resultMetadata[$type] = $value;
|
||||
}
|
||||
|
||||
public function putAllMetadata($metadata) {
|
||||
if ($metadata != null) {
|
||||
if ($this->resultMetadata == null) {
|
||||
$this->resultMetadata = $metadata;
|
||||
} else {
|
||||
$this->resultMetadata = array_merge($this->resultMetadata, $metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function addResultPoints($newPoints) {
|
||||
$oldPoints = $this->resultPoints;
|
||||
if ($oldPoints == null) {
|
||||
$this->resultPoints = $newPoints;
|
||||
} else if ($newPoints != null && count($newPoints) > 0) {
|
||||
$allPoints = fill_array(0,count($oldPoints)+count($newPoints),0);
|
||||
$allPoints = arraycopy($oldPoints, 0, $allPoints, 0, count($oldPoints));
|
||||
$allPoints = arraycopy($newPoints, 0, $allPoints, count($oldPoints), count($newPoints));
|
||||
$this->resultPoints = $allPoints;
|
||||
}
|
||||
}
|
||||
|
||||
public function getTimestamp() {
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function toString() {
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
}
|
||||
140
vendor/khanamiryan/qrcode-detector-decoder/lib/ResultPoint.php
vendored
Normal file
140
vendor/khanamiryan/qrcode-detector-decoder/lib/ResultPoint.php
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing;
|
||||
|
||||
use Zxing\Common\Detector\MathUtils;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates a point of interest in an image containing a barcode. Typically, this
|
||||
* would be the location of a finder pattern or the corner of the barcode, for example.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
class ResultPoint {
|
||||
|
||||
private $x;
|
||||
private $y;
|
||||
|
||||
public function __construct($x, $y) {
|
||||
$this->x = (float)($x);
|
||||
$this->y = (float)($y);
|
||||
}
|
||||
|
||||
public final function getX() {
|
||||
return (float)($this->x);
|
||||
}
|
||||
|
||||
public final function getY() {
|
||||
return (float)($this->y);
|
||||
}
|
||||
|
||||
//@Override
|
||||
public final function equals($other) {
|
||||
if ($other instanceof ResultPoint) {
|
||||
$otherPoint = $other;
|
||||
return $this->x == $otherPoint->x && $this->y == $otherPoint->y;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public final function hashCode() {
|
||||
return 31 * floatToIntBits($this->x) + floatToIntBits($this->y);
|
||||
}
|
||||
|
||||
//@Override
|
||||
public final function toString() {
|
||||
$result = '';
|
||||
$result.= ('(');
|
||||
$result.=($this->x);
|
||||
$result.=(',');
|
||||
$result.=($this->y);
|
||||
$result.=(')');
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Orders an array of three ResultPoints in an order [A,B,C] such that AB is less than AC
|
||||
* and BC is less than AC, and the angle between BC and BA is less than 180 degrees.
|
||||
*
|
||||
* @param patterns array of three {@code ResultPoint} to order
|
||||
*/
|
||||
public static function orderBestPatterns($patterns) {
|
||||
|
||||
// Find distances between pattern centers
|
||||
$zeroOneDistance = self::distance($patterns[0], $patterns[1]);
|
||||
$oneTwoDistance = self::distance($patterns[1], $patterns[2]);
|
||||
$zeroTwoDistance = self::distance($patterns[0], $patterns[2]);
|
||||
|
||||
$pointA='';
|
||||
$pointB='';
|
||||
$pointC='';
|
||||
// Assume one closest to other two is B; A and C will just be guesses at first
|
||||
if ($oneTwoDistance >= $zeroOneDistance && $oneTwoDistance >= $zeroTwoDistance) {
|
||||
$pointB = $patterns[0];
|
||||
$pointA = $patterns[1];
|
||||
$pointC = $patterns[2];
|
||||
} else if ($zeroTwoDistance >= $oneTwoDistance && $zeroTwoDistance >= $zeroOneDistance) {
|
||||
$pointB = $patterns[1];
|
||||
$pointA = $patterns[0];
|
||||
$pointC = $patterns[2];
|
||||
} else {
|
||||
$pointB = $patterns[2];
|
||||
$pointA = $patterns[0];
|
||||
$pointC = $patterns[1];
|
||||
}
|
||||
|
||||
// Use cross product to figure out whether A and C are correct or flipped.
|
||||
// This asks whether BC x BA has a positive z component, which is the arrangement
|
||||
// we want for A, B, C. If it's negative, then we've got it flipped around and
|
||||
// should swap A and C.
|
||||
if (self::crossProductZ($pointA, $pointB, $pointC) < 0.0) {
|
||||
$temp = $pointA;
|
||||
$pointA = $pointC;
|
||||
$pointC = $temp;
|
||||
}
|
||||
|
||||
$patterns[0] = $pointA;
|
||||
$patterns[1] = $pointB;
|
||||
$patterns[2] = $pointC;
|
||||
return $patterns;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param pattern1 first pattern
|
||||
* @param pattern2 second pattern
|
||||
* @return distance between two points
|
||||
*/
|
||||
public static function distance($pattern1, $pattern2) {
|
||||
return MathUtils::distance($pattern1->x, $pattern1->y, $pattern2->x, $pattern2->y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the z component of the cross product between vectors BC and BA.
|
||||
*/
|
||||
private static function crossProductZ($pointA,
|
||||
$pointB,
|
||||
$pointC) {
|
||||
$bX = $pointB->x;
|
||||
$bY = $pointB->y;
|
||||
return (($pointC->x - $bX) * ($pointA->y - $bY)) - (($pointC->y - $bY) * ($pointA->x - $bX));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
95
vendor/khanamiryan/qrcode-detector-decoder/lib/common/AbstractEnum.php
vendored
Normal file
95
vendor/khanamiryan/qrcode-detector-decoder/lib/common/AbstractEnum.php
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace Zxing\Common\CharacterSetEci\AbstractEnum;
|
||||
|
||||
use \Zxing\NotFoundException;
|
||||
use ReflectionClass;
|
||||
/**
|
||||
* A general enum implementation until we got SplEnum.
|
||||
*/
|
||||
final class AbstractEnum
|
||||
{
|
||||
/**
|
||||
* Default value.
|
||||
*/
|
||||
const __default = null;
|
||||
/**
|
||||
* Current value.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $value;
|
||||
/**
|
||||
* Cache of constants.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $constants;
|
||||
/**
|
||||
* Whether to handle values strict or not.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $strict;
|
||||
/**
|
||||
* Creates a new enum.
|
||||
*
|
||||
* @param mixed $initialValue
|
||||
* @param boolean $strict
|
||||
*/
|
||||
public function __construct($initialValue = null, $strict = false)
|
||||
{
|
||||
$this->strict = $strict;
|
||||
$this->change($initialValue);
|
||||
}
|
||||
/**
|
||||
* Changes the value of the enum.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function change($value)
|
||||
{
|
||||
if (!in_array($value, $this->getConstList(), $this->strict)) {
|
||||
throw new Exception\UnexpectedValueException('Value not a const in enum ' . get_class($this));
|
||||
}
|
||||
$this->value = $value;
|
||||
}
|
||||
/**
|
||||
* Gets current value.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
/**
|
||||
* Gets all constants (possible values) as an array.
|
||||
*
|
||||
* @param boolean $includeDefault
|
||||
* @return array
|
||||
*/
|
||||
public function getConstList($includeDefault = true)
|
||||
{
|
||||
if ($this->constants === null) {
|
||||
$reflection = new ReflectionClass($this);
|
||||
$this->constants = $reflection->getConstants();
|
||||
}
|
||||
if ($includeDefault) {
|
||||
return $this->constants;
|
||||
}
|
||||
$constants = $this->constants;
|
||||
unset($constants['__default']);
|
||||
return $constants;
|
||||
}
|
||||
/**
|
||||
* Gets the name of the enum.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return array_search($this->value, $this->getConstList());
|
||||
}
|
||||
}
|
||||
394
vendor/khanamiryan/qrcode-detector-decoder/lib/common/BitArray.php
vendored
Normal file
394
vendor/khanamiryan/qrcode-detector-decoder/lib/common/BitArray.php
vendored
Normal file
@@ -0,0 +1,394 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Ashot
|
||||
* Date: 3/25/15
|
||||
* Time: 11:51
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Common;
|
||||
|
||||
/**
|
||||
* <p>A simple, fast array of bits, represented compactly by an array of ints internally.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
|
||||
final class BitArray {
|
||||
|
||||
private $bits;
|
||||
private $size;
|
||||
|
||||
|
||||
|
||||
public function __construct($bits=array(),$size=0) {
|
||||
|
||||
if(!$bits&&!$size){
|
||||
$this->$size = 0;
|
||||
$this->bits = array();
|
||||
}elseif($bits&&!$size){
|
||||
$this->size = $bits;
|
||||
$this->bits = $this->makeArray($bits);
|
||||
}else{
|
||||
$this->bits = $bits;
|
||||
$this->size = $size;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public function getSize() {
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
public function getSizeInBytes() {
|
||||
return ($this->size + 7) / 8;
|
||||
}
|
||||
|
||||
private function ensureCapacity($size) {
|
||||
if ($size > count($this->bits) * 32) {
|
||||
$newBits = $this->makeArray($size);
|
||||
$newBits = arraycopy($this->bits, 0, $newBits, 0, count($this->bits));
|
||||
$this->bits = $newBits;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $i; bit to get
|
||||
* @return true iff bit i is set
|
||||
*/
|
||||
public function get($i) {
|
||||
$key = intval($i / 32);
|
||||
return intval32bits($this->bits[$key] & (1 << ($i & 0x1F))) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets bit i.
|
||||
*
|
||||
* @param i bit to set
|
||||
*/
|
||||
public function set($i) {
|
||||
$this->bits[intval($i / 32)] |= 1 << ($i & 0x1F);
|
||||
$this->bits[intval($i / 32)] = overflow($this->bits[intval($i / 32)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flips bit i.
|
||||
*
|
||||
* @param i bit to set
|
||||
*/
|
||||
public function flip($i) {
|
||||
$this->bits[intval($i / 32)] ^= 1 << ($i & 0x1F);
|
||||
$this->bits[intval($i / 32)] = overflow32($this->bits[intval($i / 32)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from first bit to check
|
||||
* @return index of first bit that is set, starting from the given index, or size if none are set
|
||||
* at or beyond this given index
|
||||
* @see #getNextUnset(int)
|
||||
*/
|
||||
public function getNextSet($from) {
|
||||
if ($from >= $this->size) {
|
||||
return $this->size;
|
||||
}
|
||||
$bitsOffset = intval($from / 32);
|
||||
$currentBits = (int)$this->bits[$bitsOffset];
|
||||
// mask off lesser bits first
|
||||
$currentBits &= ~((1 << ($from & 0x1F)) - 1);
|
||||
while ($currentBits == 0) {
|
||||
if (++$bitsOffset == count($this->bits)) {
|
||||
return $this->size;
|
||||
}
|
||||
$currentBits = $this->bits[$bitsOffset];
|
||||
}
|
||||
$result = ($bitsOffset * 32) + numberOfTrailingZeros($currentBits); //numberOfTrailingZeros
|
||||
return $result > $this->size ? $this->size : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from index to start looking for unset bit
|
||||
* @return index of next unset bit, or {@code size} if none are unset until the end
|
||||
* @see #getNextSet(int)
|
||||
*/
|
||||
public function getNextUnset($from) {
|
||||
if ($from >= $this->size) {
|
||||
return $this->size;
|
||||
}
|
||||
$bitsOffset = intval($from / 32);
|
||||
$currentBits = ~$this->bits[$bitsOffset];
|
||||
// mask off lesser bits first
|
||||
$currentBits &= ~((1 << ($from & 0x1F)) - 1);
|
||||
while ($currentBits == 0) {
|
||||
if (++$bitsOffset == count($this->bits)) {
|
||||
return $this->size;
|
||||
}
|
||||
$currentBits = overflow32(~$this->bits[$bitsOffset]);
|
||||
}
|
||||
$result = ($bitsOffset * 32) + numberOfTrailingZeros($currentBits);
|
||||
return $result > $this->size ? $this->size : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a block of 32 bits, starting at bit i.
|
||||
*
|
||||
* @param i first bit to set
|
||||
* @param newBits the new value of the next 32 bits. Note again that the least-significant bit
|
||||
* corresponds to bit i, the next-least-significant to i+1, and so on.
|
||||
*/
|
||||
public function setBulk($i, $newBits) {
|
||||
$this->bits[intval($i / 32)] = $newBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a range of bits.
|
||||
*
|
||||
* @param start start of range, inclusive.
|
||||
* @param end end of range, exclusive
|
||||
*/
|
||||
public function setRange($start, $end) {
|
||||
if ($end < $start) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
if ($end == $start) {
|
||||
return;
|
||||
}
|
||||
$end--; // will be easier to treat this as the last actually set bit -- inclusive
|
||||
$firstInt = intval($start / 32);
|
||||
$lastInt = intval($end / 32);
|
||||
for ($i = $firstInt; $i <= $lastInt; $i++) {
|
||||
$firstBit = $i > $firstInt ? 0 : $start & 0x1F;
|
||||
$lastBit = $i < $lastInt ? 31 : $end & 0x1F;
|
||||
$mask = 0;
|
||||
if ($firstBit == 0 && $lastBit == 31) {
|
||||
$mask = -1;
|
||||
} else {
|
||||
$mask = 0;
|
||||
for ($j = $firstBit; $j <= $lastBit; $j++) {
|
||||
$mask |= 1 << $j;
|
||||
}
|
||||
}
|
||||
$this->bits[$i] = overflow32($this->bits[$i]|$mask);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all bits (sets to false).
|
||||
*/
|
||||
public function clear() {
|
||||
$max = count($this->bits);
|
||||
for ($i = 0; $i < $max; $i++) {
|
||||
$this->bits[$i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Efficient method to check if a range of bits is set, or not set.
|
||||
*
|
||||
* @param start start of range, inclusive.
|
||||
* @param end end of range, exclusive
|
||||
* @param value if true, checks that bits in range are set, otherwise checks that they are not set
|
||||
* @return true iff all bits are set or not set in range, according to value argument
|
||||
* @throws InvalidArgumentException if end is less than or equal to start
|
||||
*/
|
||||
public function isRange($start, $end, $value) {
|
||||
if ($end < $start) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
if ($end == $start) {
|
||||
return true; // empty range matches
|
||||
}
|
||||
$end--; // will be easier to treat this as the last actually set bit -- inclusive
|
||||
$firstInt = intval($start / 32);
|
||||
$lastInt = intval($end / 32);
|
||||
for ($i = $firstInt; $i <= $lastInt; $i++) {
|
||||
$firstBit = $i > $firstInt ? 0 : $start & 0x1F;
|
||||
$lastBit = $i < $lastInt ? 31 :$end & 0x1F;
|
||||
$mask = 0;
|
||||
if ($firstBit == 0 && $lastBit == 31) {
|
||||
$mask = -1;
|
||||
} else {
|
||||
$mask = 0;
|
||||
for ($j = $firstBit; $j <= $lastBit; $j++) {
|
||||
$mask = overflow32($mask|(1 << $j));
|
||||
}
|
||||
}
|
||||
|
||||
// Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is,
|
||||
// equals the mask, or we're looking for 0s and the masked portion is not all 0s
|
||||
if (($this->bits[$i] & $mask) != ($value ? $mask : 0)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function appendBit($bit) {
|
||||
$this->ensureCapacity($this->size + 1);
|
||||
if ($bit) {
|
||||
$this->bits[intval($this->size / 32)] |= 1 << ($this->size & 0x1F);
|
||||
}
|
||||
$this->size++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the least-significant bits, from value, in order from most-significant to
|
||||
* least-significant. For example, appending 6 bits from 0x000001E will append the bits
|
||||
* 0, 1, 1, 1, 1, 0 in that order.
|
||||
*
|
||||
* @param value {@code int} containing bits to append
|
||||
* @param numBits bits from value to append
|
||||
*/
|
||||
public function appendBits($value, $numBits) {
|
||||
if ($numBits < 0 || $numBits > 32) {
|
||||
throw new \InvalidArgumentException("Num bits must be between 0 and 32");
|
||||
}
|
||||
$this->ensureCapacity($this->size + $numBits);
|
||||
for ($numBitsLeft = $numBits; $numBitsLeft > 0; $numBitsLeft--) {
|
||||
$this->appendBit((($value >> ($numBitsLeft - 1)) & 0x01) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
public function appendBitArray($other) {
|
||||
$otherSize = $other->size;
|
||||
$this->ensureCapacity($this->size + $otherSize);
|
||||
for ($i = 0; $i < $otherSize; $i++) {
|
||||
$this->appendBit($other->get($i));
|
||||
}
|
||||
}
|
||||
|
||||
public function _xor($other) {
|
||||
if (count($this->bits) != count($other->bits)) {
|
||||
throw new \InvalidArgumentException("Sizes don't match");
|
||||
}
|
||||
for ($i = 0; $i < count($this->bits); $i++) {
|
||||
// The last byte could be incomplete (i.e. not have 8 bits in
|
||||
// it) but there is no problem since 0 XOR 0 == 0.
|
||||
$this->bits[$i] ^= $other->bits[$i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param bitOffset first bit to start writing
|
||||
* @param array array to write into. Bytes are written most-significant byte first. This is the opposite
|
||||
* of the internal representation, which is exposed by {@link #getBitArray()}
|
||||
* @param offset position in array to start writing
|
||||
* @param numBytes how many bytes to write
|
||||
*/
|
||||
public function toBytes($bitOffset, &$array, $offset, $numBytes) {
|
||||
for ($i = 0; $i < $numBytes; $i++) {
|
||||
$theByte = 0;
|
||||
for ($j = 0; $j < 8; $j++) {
|
||||
if ($this->get($bitOffset)) {
|
||||
$theByte |= 1 << (7 - $j);
|
||||
}
|
||||
$bitOffset++;
|
||||
}
|
||||
$array[(int)($offset + $i)] = $theByte;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return underlying array of ints. The first element holds the first 32 bits, and the least
|
||||
* significant bit is bit 0.
|
||||
*/
|
||||
public function getBitArray() {
|
||||
return $this->bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverses all bits in the array.
|
||||
*/
|
||||
public function reverse() {
|
||||
$newBits = array();
|
||||
// reverse all int's first
|
||||
$len = (($this->size-1) / 32);
|
||||
$oldBitsLen = $len + 1;
|
||||
for ($i = 0; $i < $oldBitsLen; $i++) {
|
||||
$x = $this->bits[$i];/*
|
||||
$x = (($x >> 1) & 0x55555555L) | (($x & 0x55555555L) << 1);
|
||||
$x = (($x >> 2) & 0x33333333L) | (($x & 0x33333333L) << 2);
|
||||
$x = (($x >> 4) & 0x0f0f0f0fL) | (($x & 0x0f0f0f0fL) << 4);
|
||||
$x = (($x >> 8) & 0x00ff00ffL) | (($x & 0x00ff00ffL) << 8);
|
||||
$x = (($x >> 16) & 0x0000ffffL) | (($x & 0x0000ffffL) << 16);*/
|
||||
$x = (($x >> 1) & 0x55555555) | (($x & 0x55555555) << 1);
|
||||
$x = (($x >> 2) & 0x33333333) | (($x & 0x33333333) << 2);
|
||||
$x = (($x >> 4) & 0x0f0f0f0f) | (($x & 0x0f0f0f0f) << 4);
|
||||
$x = (($x >> 8) & 0x00ff00ff) | (($x & 0x00ff00ff) << 8);
|
||||
$x = (($x >> 16) & 0x0000ffff) | (($x & 0x0000ffff) << 16);
|
||||
$newBits[(int)$len - $i] = (int) $x;
|
||||
}
|
||||
// now correct the int's if the bit size isn't a multiple of 32
|
||||
if ($this->size != $oldBitsLen * 32) {
|
||||
$leftOffset = $oldBitsLen * 32 - $this->size;
|
||||
$mask = 1;
|
||||
for ($i = 0; $i < 31 - $leftOffset; $i++) {
|
||||
$mask = ($mask << 1) | 1;
|
||||
}
|
||||
$currentInt = ($newBits[0] >> $leftOffset) & $mask;
|
||||
for ($i = 1; $i < $oldBitsLen; $i++) {
|
||||
$nextInt = $newBits[$i];
|
||||
$currentInt |= $nextInt << (32 - $leftOffset);
|
||||
$newBits[intval($i) - 1] = $currentInt;
|
||||
$currentInt = ($nextInt >> $leftOffset) & $mask;
|
||||
}
|
||||
$newBits[intval($oldBitsLen) - 1] = $currentInt;
|
||||
}
|
||||
$bits = $newBits;
|
||||
}
|
||||
|
||||
private static function makeArray($size) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// @Override
|
||||
public function equals($o) {
|
||||
if (!($o instanceof BitArray)) {
|
||||
return false;
|
||||
}
|
||||
$other = $o;
|
||||
return $this->size == $other->size && $this->bits===$other->bits;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function hashCode() {
|
||||
return 31 * $this->size +hashCode($this->bits);
|
||||
}
|
||||
|
||||
// @Override
|
||||
public function toString() {
|
||||
$result = '';
|
||||
for ($i = 0; $i < $this->size; $i++) {
|
||||
if (($i & 0x07) == 0) {
|
||||
$result.=' ';
|
||||
}
|
||||
$result.= ($this->get($i) ? 'X' : '.');
|
||||
}
|
||||
return (string) $result;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public function _clone() {
|
||||
return new BitArray($this->bits, $this->size);
|
||||
}
|
||||
|
||||
}
|
||||
424
vendor/khanamiryan/qrcode-detector-decoder/lib/common/BitMatrix.php
vendored
Normal file
424
vendor/khanamiryan/qrcode-detector-decoder/lib/common/BitMatrix.php
vendored
Normal file
File diff suppressed because one or more lines are too long
112
vendor/khanamiryan/qrcode-detector-decoder/lib/common/BitSource.php
vendored
Normal file
112
vendor/khanamiryan/qrcode-detector-decoder/lib/common/BitSource.php
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Common;
|
||||
|
||||
/**
|
||||
* <p>This provides an easy abstraction to read bits at a time from a sequence of bytes, where the
|
||||
* number of bits read is not often a multiple of 8.</p>
|
||||
*
|
||||
* <p>This class is thread-safe but not reentrant -- unless the caller modifies the bytes array
|
||||
* it passed in, in which case all bets are off.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class BitSource {
|
||||
|
||||
private $bytes;
|
||||
private $byteOffset = 0;
|
||||
private $bitOffset = 0;
|
||||
|
||||
/**
|
||||
* @param bytes bytes from which this will read bits. Bits will be read from the first byte first.
|
||||
* Bits are read within a byte from most-significant to least-significant bit.
|
||||
*/
|
||||
public function __construct($bytes) {
|
||||
$this->bytes = $bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return index of next bit in current byte which would be read by the next call to {@link #readBits(int)}.
|
||||
*/
|
||||
public function getBitOffset() {
|
||||
return $this->bitOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}.
|
||||
*/
|
||||
public function getByteOffset() {
|
||||
return $this->byteOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numBits number of bits to read
|
||||
* @return int representing the bits read. The bits will appear as the least-significant
|
||||
* bits of the int
|
||||
* @throws IllegalArgumentException if numBits isn't in [1,32] or more than is available
|
||||
*/
|
||||
public function readBits($numBits) {
|
||||
if ($numBits < 1 || $numBits > 32 || $numBits > $this->available()) {
|
||||
throw new \InvalidArgumentException(strval($numBits));
|
||||
}
|
||||
|
||||
$result = 0;
|
||||
|
||||
// First, read remainder from current byte
|
||||
if ($this->bitOffset > 0) {
|
||||
$bitsLeft = 8 - $this->bitOffset;
|
||||
$toRead = $numBits < $bitsLeft ? $numBits : $bitsLeft;
|
||||
$bitsToNotRead = $bitsLeft - $toRead;
|
||||
$mask = (0xFF >> (8 - $toRead)) << $bitsToNotRead;
|
||||
$result = ($this->bytes[$this->byteOffset] & $mask) >> $bitsToNotRead;
|
||||
$numBits -= $toRead;
|
||||
$this->bitOffset += $toRead;
|
||||
if ($this->bitOffset == 8) {
|
||||
$this->bitOffset = 0;
|
||||
$this->byteOffset++;
|
||||
}
|
||||
}
|
||||
|
||||
// Next read whole bytes
|
||||
if ($numBits > 0) {
|
||||
while ($numBits >= 8) {
|
||||
$result = ($result << 8) | ($this->bytes[$this->byteOffset] & 0xFF);
|
||||
$this->byteOffset++;
|
||||
$numBits -= 8;
|
||||
}
|
||||
|
||||
// Finally read a partial byte
|
||||
if ($numBits > 0) {
|
||||
$bitsToNotRead = 8 - $numBits;
|
||||
$mask = (0xFF >> $bitsToNotRead) << $bitsToNotRead;
|
||||
$result = ($result << $numBits) | (($this->bytes[$this->byteOffset] & $mask) >> $bitsToNotRead);
|
||||
$this->bitOffset += $numBits;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number of bits that can be read successfully
|
||||
*/
|
||||
public function available() {
|
||||
return 8 * (count($this->bytes) - $this->byteOffset) - $this->bitOffset;
|
||||
}
|
||||
|
||||
}
|
||||
154
vendor/khanamiryan/qrcode-detector-decoder/lib/common/CharacterSetEci.php
vendored
Normal file
154
vendor/khanamiryan/qrcode-detector-decoder/lib/common/CharacterSetEci.php
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Zxing\Common;
|
||||
use Zxing\Common\AbstractEnum;
|
||||
/**
|
||||
* Encapsulates a Character Set ECI, according to "Extended Channel
|
||||
* Interpretations" 5.3.1.1 of ISO 18004.
|
||||
*/
|
||||
final class CharacterSetECI
|
||||
{
|
||||
private static $name = null;
|
||||
/**#@+
|
||||
* Character set constants.
|
||||
*/
|
||||
const CP437 = 0;
|
||||
const ISO8859_1 = 1;
|
||||
const ISO8859_2 = 4;
|
||||
const ISO8859_3 = 5;
|
||||
const ISO8859_4 = 6;
|
||||
const ISO8859_5 = 7;
|
||||
const ISO8859_6 = 8;
|
||||
const ISO8859_7 = 9;
|
||||
const ISO8859_8 = 10;
|
||||
const ISO8859_9 = 11;
|
||||
const ISO8859_10 = 12;
|
||||
const ISO8859_11 = 13;
|
||||
const ISO8859_12 = 14;
|
||||
const ISO8859_13 = 15;
|
||||
const ISO8859_14 = 16;
|
||||
const ISO8859_15 = 17;
|
||||
const ISO8859_16 = 18;
|
||||
const SJIS = 20;
|
||||
const CP1250 = 21;
|
||||
const CP1251 = 22;
|
||||
const CP1252 = 23;
|
||||
const CP1256 = 24;
|
||||
const UNICODE_BIG_UNMARKED = 25;
|
||||
const UTF8 = 26;
|
||||
const ASCII = 27;
|
||||
const BIG5 = 28;
|
||||
const GB18030 = 29;
|
||||
const EUC_KR = 30;
|
||||
/**#@-*/
|
||||
/**
|
||||
* Map between character names and their ECI values.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $nameToEci = array(
|
||||
'ISO-8859-1' => self::ISO8859_1,
|
||||
'ISO-8859-2' => self::ISO8859_2,
|
||||
'ISO-8859-3' => self::ISO8859_3,
|
||||
'ISO-8859-4' => self::ISO8859_4,
|
||||
'ISO-8859-5' => self::ISO8859_5,
|
||||
'ISO-8859-6' => self::ISO8859_6,
|
||||
'ISO-8859-7' => self::ISO8859_7,
|
||||
'ISO-8859-8' => self::ISO8859_8,
|
||||
'ISO-8859-9' => self::ISO8859_9,
|
||||
'ISO-8859-10' => self::ISO8859_10,
|
||||
'ISO-8859-11' => self::ISO8859_11,
|
||||
'ISO-8859-12' => self::ISO8859_12,
|
||||
'ISO-8859-13' => self::ISO8859_13,
|
||||
'ISO-8859-14' => self::ISO8859_14,
|
||||
'ISO-8859-15' => self::ISO8859_15,
|
||||
'ISO-8859-16' => self::ISO8859_16,
|
||||
'SHIFT-JIS' => self::SJIS,
|
||||
'WINDOWS-1250' => self::CP1250,
|
||||
'WINDOWS-1251' => self::CP1251,
|
||||
'WINDOWS-1252' => self::CP1252,
|
||||
'WINDOWS-1256' => self::CP1256,
|
||||
'UTF-16BE' => self::UNICODE_BIG_UNMARKED,
|
||||
'UTF-8' => self::UTF8,
|
||||
'ASCII' => self::ASCII,
|
||||
'GBK' => self::GB18030,
|
||||
'EUC-KR' => self::EUC_KR,
|
||||
);
|
||||
/**
|
||||
* Additional possible values for character sets.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $additionalValues = array(
|
||||
self::CP437 => 2,
|
||||
self::ASCII => 170,
|
||||
);
|
||||
/**
|
||||
* Gets character set ECI by value.
|
||||
*
|
||||
* @param string $name
|
||||
* @return CharacterSetEci|null
|
||||
*/
|
||||
public static function getCharacterSetECIByValue($value)
|
||||
{
|
||||
if ($value < 0 || $value >= 900) {
|
||||
throw new Exception\InvalidArgumentException('Value must be between 0 and 900');
|
||||
}
|
||||
if (false !== ($key = array_search($value, self::$additionalValues))) {
|
||||
$value = $key;
|
||||
}
|
||||
array_search($value, self::$nameToEci);
|
||||
try
|
||||
{
|
||||
self::setName($value);
|
||||
return new self($value);
|
||||
} catch (Exception\UnexpectedValueException $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static function setName($value)
|
||||
{
|
||||
foreach (self::$nameToEci as $name => $key) {
|
||||
if($key == $value)
|
||||
{
|
||||
self::$name = $name;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(self::$name == null)
|
||||
{
|
||||
foreach (self::$additionalValues as $name => $key) {
|
||||
if($key == $value)
|
||||
{
|
||||
self::$name = $name;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Gets character set ECI name.
|
||||
*
|
||||
* @return character set ECI name|null
|
||||
*/
|
||||
public static function name()
|
||||
{
|
||||
return self::$name;
|
||||
}
|
||||
/**
|
||||
* Gets character set ECI by name.
|
||||
*
|
||||
* @param string $name
|
||||
* @return CharacterSetEci|null
|
||||
*/
|
||||
public static function getCharacterSetECIByName($name)
|
||||
{
|
||||
$name = strtoupper($name);
|
||||
if (isset(self::$nameToEci[$name])) {
|
||||
return new self(self::$nameToEci[$name]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
109
vendor/khanamiryan/qrcode-detector-decoder/lib/common/DecoderResult.php
vendored
Normal file
109
vendor/khanamiryan/qrcode-detector-decoder/lib/common/DecoderResult.php
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Common;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Encapsulates the result of decoding a matrix of bits. This typically
|
||||
* applies to 2D barcode formats. For now it contains the raw bytes obtained,
|
||||
* as well as a String interpretation of those bytes, if applicable.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class DecoderResult {
|
||||
|
||||
private $rawBytes;
|
||||
private $text;
|
||||
private $byteSegments;
|
||||
private $ecLevel;
|
||||
private $errorsCorrected;
|
||||
private $erasures;
|
||||
private $other;
|
||||
private $structuredAppendParity;
|
||||
private $structuredAppendSequenceNumber;
|
||||
|
||||
|
||||
|
||||
public function __construct($rawBytes,
|
||||
$text,
|
||||
$byteSegments,
|
||||
$ecLevel,
|
||||
$saSequence = -1,
|
||||
$saParity = -1) {
|
||||
$this->rawBytes = $rawBytes;
|
||||
$this->text = $text;
|
||||
$this->byteSegments = $byteSegments;
|
||||
$this->ecLevel = $ecLevel;
|
||||
$this->structuredAppendParity = $saParity;
|
||||
$this->structuredAppendSequenceNumber = $saSequence;
|
||||
}
|
||||
|
||||
public function getRawBytes() {
|
||||
return $this->rawBytes;
|
||||
}
|
||||
|
||||
public function getText() {
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
public function getByteSegments() {
|
||||
return $this->byteSegments;
|
||||
}
|
||||
|
||||
public function getECLevel() {
|
||||
return $this->ecLevel;
|
||||
}
|
||||
|
||||
public function getErrorsCorrected() {
|
||||
return $this->errorsCorrected;
|
||||
}
|
||||
|
||||
public function setErrorsCorrected($errorsCorrected) {
|
||||
$this->errorsCorrected = $errorsCorrected;
|
||||
}
|
||||
|
||||
public function getErasures() {
|
||||
return $this->erasures;
|
||||
}
|
||||
|
||||
public function setErasures($erasures) {
|
||||
$this->erasures = $erasures;
|
||||
}
|
||||
|
||||
public function getOther() {
|
||||
return $this->other;
|
||||
}
|
||||
|
||||
public function setOther($other) {
|
||||
$this->other = $other;
|
||||
}
|
||||
|
||||
public function hasStructuredAppend() {
|
||||
return $this->structuredAppendParity >= 0 && $this->structuredAppendSequenceNumber >= 0;
|
||||
}
|
||||
|
||||
public function getStructuredAppendParity() {
|
||||
return $this->structuredAppendParity;
|
||||
}
|
||||
|
||||
public function getStructuredAppendSequenceNumber() {
|
||||
return $this->structuredAppendSequenceNumber;
|
||||
}
|
||||
|
||||
}
|
||||
89
vendor/khanamiryan/qrcode-detector-decoder/lib/common/DefaultGridSampler.php
vendored
Normal file
89
vendor/khanamiryan/qrcode-detector-decoder/lib/common/DefaultGridSampler.php
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Common;
|
||||
|
||||
use Zxing\NotFoundException;
|
||||
|
||||
/**
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class DefaultGridSampler extends GridSampler {
|
||||
|
||||
//@Override
|
||||
public function sampleGrid($image,
|
||||
$dimensionX,
|
||||
$dimensionY,
|
||||
$p1ToX, $p1ToY,
|
||||
$p2ToX, $p2ToY,
|
||||
$p3ToX, $p3ToY,
|
||||
$p4ToX, $p4ToY,
|
||||
$p1FromX, $p1FromY,
|
||||
$p2FromX, $p2FromY,
|
||||
$p3FromX, $p3FromY,
|
||||
$p4FromX, $p4FromY) {
|
||||
|
||||
$transform = PerspectiveTransform::quadrilateralToQuadrilateral(
|
||||
$p1ToX, $p1ToY, $p2ToX, $p2ToY, $p3ToX, $p3ToY, $p4ToX, $p4ToY,
|
||||
$p1FromX, $p1FromY, $p2FromX, $p2FromY, $p3FromX, $p3FromY, $p4FromX, $p4FromY);
|
||||
|
||||
return $this->sampleGrid_($image, $dimensionX, $dimensionY, $transform);
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function sampleGrid_($image,
|
||||
$dimensionX,
|
||||
$dimensionY,
|
||||
$transform) {
|
||||
if ($dimensionX <= 0 || $dimensionY <= 0) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$bits = new BitMatrix($dimensionX, $dimensionY);
|
||||
$points = fill_array(0,2 * $dimensionX,0.0);
|
||||
for ($y = 0; $y < $dimensionY; $y++) {
|
||||
$max = count($points);
|
||||
$iValue = (float) $y + 0.5;
|
||||
for ($x = 0; $x < $max; $x += 2) {
|
||||
$points[$x] = (float) ($x / 2) + 0.5;
|
||||
$points[$x + 1] = $iValue;
|
||||
}
|
||||
$transform->transformPoints($points);
|
||||
// Quick check to see if points transformed to something inside the image;
|
||||
// sufficient to check the endpoints
|
||||
$this->checkAndNudgePoints($image, $points);
|
||||
try {
|
||||
for ($x = 0; $x < $max; $x += 2) {
|
||||
if ($image->get((int) $points[$x], (int) $points[$x + 1])) {
|
||||
// Black(-ish) pixel
|
||||
$bits->set($x / 2, $y);
|
||||
}
|
||||
}
|
||||
} catch (\Exception $aioobe) {//ArrayIndexOutOfBoundsException
|
||||
// This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
|
||||
// transform gets "twisted" such that it maps a straight line of points to a set of points
|
||||
// whose endpoints are in bounds, but others are not. There is probably some mathematical
|
||||
// way to detect this about the transformation that I don't know yet.
|
||||
// This results in an ugly runtime exception despite our clever checks above -- can't have
|
||||
// that. We could check each point's coordinates but that feels duplicative. We settle for
|
||||
// catching and wrapping ArrayIndexOutOfBoundsException.
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
}
|
||||
return $bits;
|
||||
}
|
||||
|
||||
}
|
||||
47
vendor/khanamiryan/qrcode-detector-decoder/lib/common/DetectorResult.php
vendored
Normal file
47
vendor/khanamiryan/qrcode-detector-decoder/lib/common/DetectorResult.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Common;
|
||||
|
||||
use Zxing\ResultPoint;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates the result of detecting a barcode in an image. This includes the raw
|
||||
* matrix of black/white pixels corresponding to the barcode, and possibly points of interest
|
||||
* in the image, like the location of finder patterns or corners of the barcode in the image.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
class DetectorResult {
|
||||
|
||||
private $bits;
|
||||
private $points;
|
||||
|
||||
public function __construct($bits, $points) {
|
||||
$this->bits = $bits;
|
||||
$this->points = $points;
|
||||
}
|
||||
|
||||
public final function getBits() {
|
||||
return $this->bits;
|
||||
}
|
||||
|
||||
public final function getPoints() {
|
||||
return $this->points;
|
||||
}
|
||||
|
||||
}
|
||||
206
vendor/khanamiryan/qrcode-detector-decoder/lib/common/GlobalHistogramBinarizer.php
vendored
Normal file
206
vendor/khanamiryan/qrcode-detector-decoder/lib/common/GlobalHistogramBinarizer.php
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2009 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Common;
|
||||
|
||||
use Zxing\Binarizer;
|
||||
use Zxing\LuminanceSource;
|
||||
use Zxing\NotFoundException;
|
||||
|
||||
/**
|
||||
* This Binarizer implementation uses the old ZXing global histogram approach. It is suitable
|
||||
* for low-end mobile devices which don't have enough CPU or memory to use a local thresholding
|
||||
* algorithm. However, because it picks a global black point, it cannot handle difficult shadows
|
||||
* and gradients.
|
||||
*
|
||||
* Faster mobile devices and all desktop applications should probably use HybridBinarizer instead.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
* @author Sean Owen
|
||||
*/
|
||||
|
||||
class GlobalHistogramBinarizer extends Binarizer {
|
||||
|
||||
private static $LUMINANCE_BITS = 5;
|
||||
private static $LUMINANCE_SHIFT=3;
|
||||
private static $LUMINANCE_BUCKETS = 32;
|
||||
|
||||
private static $EMPTY = array();
|
||||
|
||||
private $luminances=array();
|
||||
private $buckets = array();
|
||||
private $source = array();
|
||||
|
||||
public function __construct($source) {
|
||||
|
||||
self::$LUMINANCE_SHIFT = 8 - self::$LUMINANCE_BITS;
|
||||
self::$LUMINANCE_BUCKETS = 1 << self::$LUMINANCE_BITS;
|
||||
|
||||
parent::__construct($source);
|
||||
|
||||
$this->luminances = self::$EMPTY;
|
||||
$this->buckets = fill_array(0, self::$LUMINANCE_BUCKETS,0);
|
||||
$this->source = $source;
|
||||
}
|
||||
|
||||
// Applies simple sharpening to the row data to improve performance of the 1D Readers.
|
||||
//@Override
|
||||
public function getBlackRow($y, $row=null) {
|
||||
$this->source = $this->getLuminanceSource();
|
||||
$width = $this->source->getWidth();
|
||||
if ($row == null || $row->getSize() < $width) {
|
||||
$row = new BitArray($width);
|
||||
} else {
|
||||
$row->clear();
|
||||
}
|
||||
|
||||
$this->initArrays($width);
|
||||
$localLuminances = $this->source->getRow($y, $this->luminances);
|
||||
$localBuckets = $this->buckets;
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
$pixel = $localLuminances[$x] & 0xff;
|
||||
$localBuckets[$pixel >> self::$LUMINANCE_SHIFT]++;
|
||||
}
|
||||
$blackPoint = $this->estimateBlackPoint($localBuckets);
|
||||
|
||||
$left = $localLuminances[0] & 0xff;
|
||||
$center = $localLuminances[1] & 0xff;
|
||||
for ($x = 1; $x < $width - 1; $x++) {
|
||||
$right = $localLuminances[$x + 1] & 0xff;
|
||||
// A simple -1 4 -1 box filter with a weight of 2.
|
||||
$luminance = (($center * 4) - $left - $right) / 2;
|
||||
if ($luminance < $blackPoint) {
|
||||
$row->set($x);
|
||||
}
|
||||
$left = $center;
|
||||
$center = $right;
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
// Does not sharpen the data, as this call is intended to only be used by 2D Readers.
|
||||
//@Override
|
||||
public function getBlackMatrix(){
|
||||
$source = $this->getLuminanceSource();
|
||||
$width = $source->getWidth();
|
||||
$height = $source->getHeight();
|
||||
$matrix = new BitMatrix($width, $height);
|
||||
|
||||
// Quickly calculates the histogram by sampling four rows from the image. This proved to be
|
||||
// more robust on the blackbox tests than sampling a diagonal as we used to do.
|
||||
$this->initArrays($width);
|
||||
$localBuckets = $this->buckets;
|
||||
for ($y = 1; $y < 5; $y++) {
|
||||
$row = intval($height * $y / 5);
|
||||
$localLuminances = $source->getRow($row, $this->luminances);
|
||||
$right = intval(($width * 4) / 5);
|
||||
for ($x = intval($width / 5); $x < $right; $x++) {
|
||||
$pixel = intval32bits($localLuminances[intval($x)] & 0xff);
|
||||
$localBuckets[intval32bits($pixel >> self::$LUMINANCE_SHIFT)]++;
|
||||
}
|
||||
}
|
||||
$blackPoint = $this->estimateBlackPoint($localBuckets);
|
||||
|
||||
// We delay reading the entire image luminance until the black point estimation succeeds.
|
||||
// Although we end up reading four rows twice, it is consistent with our motto of
|
||||
// "fail quickly" which is necessary for continuous scanning.
|
||||
$localLuminances = $source->getMatrix();
|
||||
for ($y = 0; $y < $height; $y++) {
|
||||
$offset = $y * $width;
|
||||
for ($x = 0; $x< $width; $x++) {
|
||||
$pixel = intval($localLuminances[$offset + $x] & 0xff);
|
||||
if ($pixel < $blackPoint) {
|
||||
$matrix->set($x, $y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $matrix;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function createBinarizer($source) {
|
||||
return new GlobalHistogramBinarizer($source);
|
||||
}
|
||||
|
||||
private function initArrays($luminanceSize) {
|
||||
if (count($this->luminances) < $luminanceSize) {
|
||||
$this->luminances = array();
|
||||
}
|
||||
for ($x = 0; $x < self::$LUMINANCE_BUCKETS; $x++) {
|
||||
$this->buckets[$x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static function estimateBlackPoint($buckets){
|
||||
// Find the tallest peak in the histogram.
|
||||
$numBuckets = count($buckets);
|
||||
$maxBucketCount = 0;
|
||||
$firstPeak = 0;
|
||||
$firstPeakSize = 0;
|
||||
for ($x = 0; $x < $numBuckets; $x++) {
|
||||
if ($buckets[$x] > $firstPeakSize) {
|
||||
$firstPeak = $x;
|
||||
$firstPeakSize = $buckets[$x];
|
||||
}
|
||||
if ($buckets[$x] > $maxBucketCount) {
|
||||
$maxBucketCount = $buckets[$x];
|
||||
}
|
||||
}
|
||||
|
||||
// Find the second-tallest peak which is somewhat far from the tallest peak.
|
||||
$secondPeak = 0;
|
||||
$secondPeakScore = 0;
|
||||
for ($x = 0; $x < $numBuckets; $x++) {
|
||||
$distanceToBiggest = $x - $firstPeak;
|
||||
// Encourage more distant second peaks by multiplying by square of distance.
|
||||
$score = $buckets[$x] * $distanceToBiggest * $distanceToBiggest;
|
||||
if ($score > $secondPeakScore) {
|
||||
$secondPeak = $x;
|
||||
$secondPeakScore = $score;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure firstPeak corresponds to the black peak.
|
||||
if ($firstPeak > $secondPeak) {
|
||||
$temp = $firstPeak;
|
||||
$firstPeak = $secondPeak;
|
||||
$secondPeak = $temp;
|
||||
}
|
||||
|
||||
// If there is too little contrast in the image to pick a meaningful black point, throw rather
|
||||
// than waste time trying to decode the image, and risk false positives.
|
||||
if ($secondPeak - $firstPeak <= $numBuckets / 16) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
// Find a valley between them that is low and closer to the white peak.
|
||||
$bestValley = $secondPeak - 1;
|
||||
$bestValleyScore = -1;
|
||||
for ($x = $secondPeak - 1; $x > $firstPeak; $x--) {
|
||||
$fromFirst = $x - $firstPeak;
|
||||
$score = $fromFirst * $fromFirst * ($secondPeak - $x) * ($maxBucketCount - $buckets[$x]);
|
||||
if ($score > $bestValleyScore) {
|
||||
$bestValley = $x;
|
||||
$bestValleyScore = $score;
|
||||
}
|
||||
}
|
||||
|
||||
return intval32bits($bestValley << self::$LUMINANCE_SHIFT);
|
||||
}
|
||||
|
||||
}
|
||||
177
vendor/khanamiryan/qrcode-detector-decoder/lib/common/GridSampler.php
vendored
Normal file
177
vendor/khanamiryan/qrcode-detector-decoder/lib/common/GridSampler.php
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Common;
|
||||
|
||||
use Zxing\NotFoundException;
|
||||
|
||||
/**
|
||||
* Implementations of this class can, given locations of finder patterns for a QR code in an
|
||||
* image, sample the right points in the image to reconstruct the QR code, accounting for
|
||||
* perspective distortion. It is abstracted since it is relatively expensive and should be allowed
|
||||
* to take advantage of platform-specific optimized implementations, like Sun's Java Advanced
|
||||
* Imaging library, but which may not be available in other environments such as J2ME, and vice
|
||||
* versa.
|
||||
*
|
||||
* The implementation used can be controlled by calling {@link #setGridSampler(GridSampler)}
|
||||
* with an instance of a class which implements this interface.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
abstract class GridSampler {
|
||||
|
||||
private static $gridSampler;
|
||||
|
||||
/**
|
||||
* Sets the implementation of GridSampler used by the library. One global
|
||||
* instance is stored, which may sound problematic. But, the implementation provided
|
||||
* ought to be appropriate for the entire platform, and all uses of this library
|
||||
* in the whole lifetime of the JVM. For instance, an Android activity can swap in
|
||||
* an implementation that takes advantage of native platform libraries.
|
||||
*
|
||||
* @param newGridSampler The platform-specific object to install.
|
||||
*/
|
||||
public static function setGridSampler($newGridSampler) {
|
||||
self::$gridSampler = $newGridSampler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current implementation of GridSampler
|
||||
*/
|
||||
public static function getInstance() {
|
||||
if(!self::$gridSampler){
|
||||
self::$gridSampler = new DefaultGridSampler();
|
||||
}
|
||||
return self::$gridSampler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Samples an image for a rectangular matrix of bits of the given dimension. The sampling
|
||||
* transformation is determined by the coordinates of 4 points, in the original and transformed
|
||||
* image space.
|
||||
*
|
||||
* @param image image to sample
|
||||
* @param dimensionX width of {@link BitMatrix} to sample from image
|
||||
* @param dimensionY height of {@link BitMatrix} to sample from image
|
||||
* @param p1ToX point 1 preimage X
|
||||
* @param p1ToY point 1 preimage Y
|
||||
* @param p2ToX point 2 preimage X
|
||||
* @param p2ToY point 2 preimage Y
|
||||
* @param p3ToX point 3 preimage X
|
||||
* @param p3ToY point 3 preimage Y
|
||||
* @param p4ToX point 4 preimage X
|
||||
* @param p4ToY point 4 preimage Y
|
||||
* @param p1FromX point 1 image X
|
||||
* @param p1FromY point 1 image Y
|
||||
* @param p2FromX point 2 image X
|
||||
* @param p2FromY point 2 image Y
|
||||
* @param p3FromX point 3 image X
|
||||
* @param p3FromY point 3 image Y
|
||||
* @param p4FromX point 4 image X
|
||||
* @param p4FromY point 4 image Y
|
||||
* @return {@link BitMatrix} representing a grid of points sampled from the image within a region
|
||||
* defined by the "from" parameters
|
||||
* @throws NotFoundException if image can't be sampled, for example, if the transformation defined
|
||||
* by the given points is invalid or results in sampling outside the image boundaries
|
||||
*/
|
||||
public abstract function sampleGrid($image,
|
||||
$dimensionX,
|
||||
$dimensionY,
|
||||
$p1ToX, $p1ToY,
|
||||
$p2ToX, $p2ToY,
|
||||
$p3ToX, $p3ToY,
|
||||
$p4ToX, $p4ToY,
|
||||
$p1FromX, $p1FromY,
|
||||
$p2FromX, $p2FromY,
|
||||
$p3FromX, $p3FromY,
|
||||
$p4FromX, $p4FromY);
|
||||
|
||||
public abstract function sampleGrid_($image,
|
||||
$dimensionX,
|
||||
$dimensionY,
|
||||
$transform);
|
||||
|
||||
/**
|
||||
* <p>Checks a set of points that have been transformed to sample points on an image against
|
||||
* the image's dimensions to see if the point are even within the image.</p>
|
||||
*
|
||||
* <p>This method will actually "nudge" the endpoints back onto the image if they are found to be
|
||||
* barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder
|
||||
* patterns in an image where the QR Code runs all the way to the image border.</p>
|
||||
*
|
||||
* <p>For efficiency, the method will check points from either end of the line until one is found
|
||||
* to be within the image. Because the set of points are assumed to be linear, this is valid.</p>
|
||||
*
|
||||
* @param image image into which the points should map
|
||||
* @param points actual points in x1,y1,...,xn,yn form
|
||||
* @throws NotFoundException if an endpoint is lies outside the image boundaries
|
||||
*/
|
||||
protected static function checkAndNudgePoints($image,
|
||||
$points) {
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
// Check and nudge points from start until we see some that are OK:
|
||||
$nudged = true;
|
||||
for ($offset = 0; $offset < count($points) && $nudged; $offset += 2) {
|
||||
$x = (int) $points[$offset];
|
||||
$y = (int) $points[$offset + 1];
|
||||
if ($x < -1 || $x > $width || $y < -1 || $y > $height) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$nudged = false;
|
||||
if ($x == -1) {
|
||||
$points[$offset] = 0.0;
|
||||
$nudged = true;
|
||||
} else if ($x == $width) {
|
||||
$points[$offset] = $width - 1;
|
||||
$nudged = true;
|
||||
}
|
||||
if ($y == -1) {
|
||||
$points[$offset + 1] = 0.0;
|
||||
$nudged = true;
|
||||
} else if ($y == $height) {
|
||||
$points[$offset + 1] = $height - 1;
|
||||
$nudged = true;
|
||||
}
|
||||
}
|
||||
// Check and nudge points from end:
|
||||
$nudged = true;
|
||||
for ($offset = count($points) - 2; $offset >= 0 && $nudged; $offset -= 2) {
|
||||
$x = (int) $points[$offset];
|
||||
$y = (int) $points[$offset + 1];
|
||||
if ($x < -1 || $x > $width || $y < -1 || $y > $height) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$nudged = false;
|
||||
if ($x == -1) {
|
||||
$points[$offset] = 0.0;
|
||||
$nudged = true;
|
||||
} else if ($x == $width) {
|
||||
$points[$offset] = $width - 1;
|
||||
$nudged = true;
|
||||
}
|
||||
if ($y == -1) {
|
||||
$points[$offset + 1] = 0.0;
|
||||
$nudged = true;
|
||||
} else if ($y == $height) {
|
||||
$points[$offset + 1] = $height - 1;
|
||||
$nudged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
259
vendor/khanamiryan/qrcode-detector-decoder/lib/common/HybridBinarizer.php
vendored
Normal file
259
vendor/khanamiryan/qrcode-detector-decoder/lib/common/HybridBinarizer.php
vendored
Normal file
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2009 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Common;
|
||||
|
||||
use Zxing\Binarizer;
|
||||
use Zxing\LuminanceSource;
|
||||
use Zxing\NotFoundException;
|
||||
|
||||
/**
|
||||
* This class implements a local thresholding algorithm, which while slower than the
|
||||
* GlobalHistogramBinarizer, is fairly efficient for what it does. It is designed for
|
||||
* high frequency images of barcodes with black data on white backgrounds. For this application,
|
||||
* it does a much better job than a global blackpoint with severe shadows and gradients.
|
||||
* However it tends to produce artifacts on lower frequency images and is therefore not
|
||||
* a good general purpose binarizer for uses outside ZXing.
|
||||
*
|
||||
* This class extends GlobalHistogramBinarizer, using the older histogram approach for 1D readers,
|
||||
* and the newer local approach for 2D readers. 1D decoding using a per-row histogram is already
|
||||
* inherently local, and only fails for horizontal gradients. We can revisit that problem later,
|
||||
* but for now it was not a win to use local blocks for 1D.
|
||||
*
|
||||
* This Binarizer is the default for the unit tests and the recommended class for library users.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
final class HybridBinarizer extends GlobalHistogramBinarizer {
|
||||
|
||||
// This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels.
|
||||
// So this is the smallest dimension in each axis we can accept.
|
||||
private static $BLOCK_SIZE_POWER = 3;
|
||||
private static $BLOCK_SIZE = 8; // ...0100...00
|
||||
private static $BLOCK_SIZE_MASK = 7; // ...0011...11
|
||||
private static $MINIMUM_DIMENSION = 40;
|
||||
private static $MIN_DYNAMIC_RANGE=24;
|
||||
|
||||
private $matrix;
|
||||
|
||||
public function __construct($source) {
|
||||
|
||||
parent::__construct($source);
|
||||
self::$BLOCK_SIZE_POWER = 3;
|
||||
self::$BLOCK_SIZE = 1 << self::$BLOCK_SIZE_POWER; // ...0100...00
|
||||
self::$BLOCK_SIZE_MASK = self::$BLOCK_SIZE - 1; // ...0011...11
|
||||
self::$MINIMUM_DIMENSION = self::$BLOCK_SIZE * 5;
|
||||
self::$MIN_DYNAMIC_RANGE = 24;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the final BitMatrix once for all requests. This could be called once from the
|
||||
* constructor instead, but there are some advantages to doing it lazily, such as making
|
||||
* profiling easier, and not doing heavy lifting when callers don't expect it.
|
||||
*/
|
||||
//@Override
|
||||
public function getBlackMatrix(){
|
||||
if ($this->matrix != null) {
|
||||
return $this->matrix;
|
||||
}
|
||||
$source = $this->getLuminanceSource();
|
||||
$width = $source->getWidth();
|
||||
$height = $source->getHeight();
|
||||
if ($width >= self::$MINIMUM_DIMENSION && $height >= self::$MINIMUM_DIMENSION) {
|
||||
$luminances = $source->getMatrix();
|
||||
$subWidth = $width >> self::$BLOCK_SIZE_POWER;
|
||||
if (($width & self::$BLOCK_SIZE_MASK) != 0) {
|
||||
$subWidth++;
|
||||
}
|
||||
$subHeight = $height >> self::$BLOCK_SIZE_POWER;
|
||||
if (($height & self::$BLOCK_SIZE_MASK) != 0) {
|
||||
$subHeight++;
|
||||
}
|
||||
$blackPoints = $this->calculateBlackPoints($luminances, $subWidth, $subHeight, $width, $height);
|
||||
|
||||
$newMatrix = new BitMatrix($width, $height);
|
||||
$this->calculateThresholdForBlock($luminances, $subWidth, $subHeight, $width, $height, $blackPoints, $newMatrix);
|
||||
$this->matrix = $newMatrix;
|
||||
} else {
|
||||
// If the image is too small, fall back to the global histogram approach.
|
||||
$this->matrix = parent::getBlackMatrix();
|
||||
}
|
||||
return $this->matrix;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function createBinarizer($source) {
|
||||
return new HybridBinarizer($source);
|
||||
}
|
||||
|
||||
/**
|
||||
* For each block in the image, calculate the average black point using a 5x5 grid
|
||||
* of the blocks around it. Also handles the corner cases (fractional blocks are computed based
|
||||
* on the last pixels in the row/column which are also used in the previous block).
|
||||
*/
|
||||
private static function calculateThresholdForBlock($luminances,
|
||||
$subWidth,
|
||||
$subHeight,
|
||||
$width,
|
||||
$height,
|
||||
$blackPoints,
|
||||
$matrix) {
|
||||
for ($y = 0; $y < $subHeight; $y++) {
|
||||
$yoffset = intval32bits($y << self::$BLOCK_SIZE_POWER);
|
||||
$maxYOffset = $height - self::$BLOCK_SIZE;
|
||||
if ($yoffset > $maxYOffset) {
|
||||
$yoffset = $maxYOffset;
|
||||
}
|
||||
for ($x = 0; $x < $subWidth; $x++) {
|
||||
$xoffset = intval32bits($x << self::$BLOCK_SIZE_POWER);
|
||||
$maxXOffset = $width - self::$BLOCK_SIZE;
|
||||
if ($xoffset > $maxXOffset) {
|
||||
$xoffset = $maxXOffset;
|
||||
}
|
||||
$left = self::cap($x, 2, $subWidth - 3);
|
||||
$top = self::cap($y, 2, $subHeight - 3);
|
||||
$sum = 0;
|
||||
for ($z = -2; $z <= 2; $z++) {
|
||||
$blackRow = $blackPoints[$top + $z];
|
||||
$sum += $blackRow[$left - 2] + $blackRow[$left - 1] + $blackRow[$left] + $blackRow[$left + 1] + $blackRow[$left + 2];
|
||||
}
|
||||
$average = intval($sum / 25);
|
||||
|
||||
self::thresholdBlock($luminances, $xoffset, $yoffset, $average, $width, $matrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function cap($value, $min, $max) {
|
||||
if($value<$min){
|
||||
return $min;
|
||||
}elseif($value>$max){
|
||||
return $max;
|
||||
}else{
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a single threshold to a block of pixels.
|
||||
*/
|
||||
private static function thresholdBlock($luminances,
|
||||
$xoffset,
|
||||
$yoffset,
|
||||
$threshold,
|
||||
$stride,
|
||||
$matrix) {
|
||||
|
||||
for ($y = 0, $offset = $yoffset * $stride + $xoffset; $y < self::$BLOCK_SIZE; $y++, $offset += $stride) {
|
||||
for ($x = 0; $x < self::$BLOCK_SIZE; $x++) {
|
||||
// Comparison needs to be <= so that black == 0 pixels are black even if the threshold is 0.
|
||||
if (($luminances[$offset + $x] & 0xFF) <= $threshold) {
|
||||
$matrix->set($xoffset + $x, $yoffset + $y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a single black point for each block of pixels and saves it away.
|
||||
* See the following thread for a discussion of this algorithm:
|
||||
* http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0
|
||||
*/
|
||||
private static function calculateBlackPoints($luminances,
|
||||
$subWidth,
|
||||
$subHeight,
|
||||
$width,
|
||||
$height) {
|
||||
$blackPoints = fill_array(0,$subHeight,0);
|
||||
foreach($blackPoints as $key=>$point){
|
||||
$blackPoints[$key] = fill_array(0,$subWidth,0);
|
||||
}
|
||||
for ($y = 0; $y < $subHeight; $y++) {
|
||||
$yoffset = intval32bits($y << self::$BLOCK_SIZE_POWER);
|
||||
$maxYOffset = $height - self::$BLOCK_SIZE;
|
||||
if ($yoffset > $maxYOffset) {
|
||||
$yoffset = $maxYOffset;
|
||||
}
|
||||
for ($x = 0; $x < $subWidth; $x++) {
|
||||
$xoffset = intval32bits($x << self::$BLOCK_SIZE_POWER);
|
||||
$maxXOffset = $width - self::$BLOCK_SIZE;
|
||||
if ($xoffset > $maxXOffset) {
|
||||
$xoffset = $maxXOffset;
|
||||
}
|
||||
$sum = 0;
|
||||
$min = 0xFF;
|
||||
$max = 0;
|
||||
for ($yy = 0, $offset = $yoffset * $width + $xoffset; $yy < self::$BLOCK_SIZE; $yy++, $offset += $width) {
|
||||
for ($xx = 0; $xx < self::$BLOCK_SIZE; $xx++) {
|
||||
$pixel = intval32bits(intval($luminances[intval($offset +$xx)]) & 0xFF);
|
||||
$sum += $pixel;
|
||||
// still looking for good contrast
|
||||
if ($pixel < $min) {
|
||||
$min = $pixel;
|
||||
}
|
||||
if ($pixel > $max) {
|
||||
$max = $pixel;
|
||||
}
|
||||
}
|
||||
// short-circuit min/max tests once dynamic range is met
|
||||
if ($max - $min > self::$MIN_DYNAMIC_RANGE) {
|
||||
// finish the rest of the rows quickly
|
||||
for ($yy++, $offset += $width; $yy < self::$BLOCK_SIZE; $yy++, $offset += $width) {
|
||||
for ($xx = 0; $xx < self::$BLOCK_SIZE; $xx++) {
|
||||
$sum += intval32bits($luminances[$offset +$xx] & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The default estimate is the average of the values in the block.
|
||||
$average = intval32bits($sum >> (self::$BLOCK_SIZE_POWER * 2));
|
||||
if ($max - $min <= self::$MIN_DYNAMIC_RANGE) {
|
||||
// If variation within the block is low, assume this is a block with only light or only
|
||||
// dark pixels. In that case we do not want to use the average, as it would divide this
|
||||
// low contrast area into black and white pixels, essentially creating data out of noise.
|
||||
//
|
||||
// The default assumption is that the block is light/background. Since no estimate for
|
||||
// the level of dark pixels exists locally, use half the min for the block.
|
||||
$average = intval($min / 2);
|
||||
|
||||
if ($y > 0 && $x > 0) {
|
||||
// Correct the "white background" assumption for blocks that have neighbors by comparing
|
||||
// the pixels in this block to the previously calculated black points. This is based on
|
||||
// the fact that dark barcode symbology is always surrounded by some amount of light
|
||||
// background for which reasonable black point estimates were made. The bp estimated at
|
||||
// the boundaries is used for the interior.
|
||||
|
||||
// The (min < bp) is arbitrary but works better than other heuristics that were tried.
|
||||
$averageNeighborBlackPoint =
|
||||
intval(($blackPoints[$y - 1][$x] + (2 * $blackPoints[$y][$x - 1]) + $blackPoints[$y - 1][$x - 1]) / 4);
|
||||
if ($min < $averageNeighborBlackPoint) {
|
||||
$average = $averageNeighborBlackPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
$blackPoints[$y][$x] = intval($average);
|
||||
}
|
||||
}
|
||||
return $blackPoints;
|
||||
}
|
||||
|
||||
}
|
||||
161
vendor/khanamiryan/qrcode-detector-decoder/lib/common/PerspectiveTransform.php
vendored
Normal file
161
vendor/khanamiryan/qrcode-detector-decoder/lib/common/PerspectiveTransform.php
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Common;
|
||||
|
||||
/**
|
||||
* <p>This class implements a perspective transform in two dimensions. Given four source and four
|
||||
* destination points, it will compute the transformation implied between them. The code is based
|
||||
* directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class PerspectiveTransform {
|
||||
|
||||
private $a11;
|
||||
private $a12;
|
||||
private $a13;
|
||||
private $a21;
|
||||
private $a22;
|
||||
private $a23;
|
||||
private $a31;
|
||||
private $a32;
|
||||
private $a33;
|
||||
|
||||
private function __construct($a11, $a21, $a31,
|
||||
$a12, $a22, $a32,
|
||||
$a13, $a23, $a33) {
|
||||
$this->a11 = $a11;
|
||||
$this->a12 = $a12;
|
||||
$this->a13 = $a13;
|
||||
$this->a21 = $a21;
|
||||
$this->a22 = $a22;
|
||||
$this->a23 = $a23;
|
||||
$this->a31 = $a31;
|
||||
$this->a32 = $a32;
|
||||
$this->a33 = $a33;
|
||||
}
|
||||
|
||||
public static function quadrilateralToQuadrilateral($x0, $y0,
|
||||
$x1, $y1,
|
||||
$x2, $y2,
|
||||
$x3, $y3,
|
||||
$x0p, $y0p,
|
||||
$x1p, $y1p,
|
||||
$x2p, $y2p,
|
||||
$x3p, $y3p) {
|
||||
|
||||
$qToS = self::quadrilateralToSquare($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3);
|
||||
$sToQ = self::squareToQuadrilateral($x0p, $y0p, $x1p, $y1p, $x2p, $y2p, $x3p, $y3p);
|
||||
return $sToQ->times($qToS);
|
||||
}
|
||||
|
||||
public function transformPoints(&$points,&$yValues=0) {
|
||||
if($yValues) {
|
||||
$this->transformPoints_($points,$yValues);
|
||||
return;
|
||||
}
|
||||
$max =count($points);
|
||||
$a11 = $this->a11;
|
||||
$a12 = $this->a12;
|
||||
$a13 = $this->a13;
|
||||
$a21 = $this->a21;
|
||||
$a22 = $this->a22;
|
||||
$a23 = $this->a23;
|
||||
$a31 = $this->a31;
|
||||
$a32 = $this->a32;
|
||||
$a33 = $this->a33;
|
||||
for ($i = 0; $i < $max; $i += 2) {
|
||||
$x = $points[$i];
|
||||
$y = $points[$i + 1];
|
||||
$denominator = $a13 * $x + $a23 * $y + $a33;
|
||||
$points[$i] = ($a11 * $x + $a21 * $y + $a31) / $denominator;
|
||||
$points[$i + 1] = ($a12 * $x + $a22 * $y +$a32) / $denominator;
|
||||
}
|
||||
}
|
||||
|
||||
public function transformPoints_(&$xValues, &$yValues) {
|
||||
$n = count($xValues);
|
||||
for ($i = 0; $i < $n; $i ++) {
|
||||
$x = $xValues[$i];
|
||||
$y = $yValues[$i];
|
||||
$denominator = $this->a13 * $x + $this->a23 * $y + $this->a33;
|
||||
$xValues[$i] = ($this->a11 * $x + $this->a21 * $y + $this->a31) / $denominator;
|
||||
$yValues[$i] = ($this->a12 * $x + $this->a22 *$y + $this->a32) / $denominator;
|
||||
}
|
||||
}
|
||||
|
||||
public static function squareToQuadrilateral($x0, $y0,
|
||||
$x1, $y1,
|
||||
$x2, $y2,
|
||||
$x3, $y3) {
|
||||
$dx3 = $x0 - $x1 + $x2 - $x3;
|
||||
$dy3 = $y0 - $y1 + $y2 - $y3;
|
||||
if ($dx3 == 0.0 && $dy3 == 0.0) {
|
||||
// Affine
|
||||
return new PerspectiveTransform($x1 - $x0, $x2 - $x1, $x0,
|
||||
$y1 - $y0, $y2 - $y1, $y0,
|
||||
0.0, 0.0, 1.0);
|
||||
} else {
|
||||
$dx1 = $x1 - $x2;
|
||||
$dx2 = $x3 - $x2;
|
||||
$dy1 = $y1 - $y2;
|
||||
$dy2 = $y3 - $y2;
|
||||
$denominator = $dx1 * $dy2 - $dx2 * $dy1;
|
||||
$a13 = ($dx3 * $dy2 - $dx2 * $dy3) / $denominator;
|
||||
$a23 = ($dx1 * $dy3 - $dx3 * $dy1) / $denominator;
|
||||
return new PerspectiveTransform($x1 - $x0 + $a13 * $x1, $x3 - $x0 + $a23 * $x3, $x0,
|
||||
$y1 - $y0 + $a13 * $y1, $y3 - $y0 + $a23 * $y3, $y0,
|
||||
$a13, $a23, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
public static function quadrilateralToSquare($x0, $y0,
|
||||
$x1, $y1,
|
||||
$x2, $y2,
|
||||
$x3, $y3) {
|
||||
// Here, the adjoint serves as the inverse:
|
||||
return self::squareToQuadrilateral($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)->buildAdjoint();
|
||||
}
|
||||
|
||||
function buildAdjoint() {
|
||||
// Adjoint is the transpose of the cofactor matrix:
|
||||
return new PerspectiveTransform($this->a22 * $this->a33 - $this->a23 * $this->a32,
|
||||
$this->a23 * $this->a31 - $this->a21 * $this->a33,
|
||||
$this->a21 * $this->a32 - $this->a22 * $this->a31,
|
||||
$this->a13 * $this->a32 - $this->a12 * $this->a33,
|
||||
$this->a11 * $this->a33 - $this->a13 * $this->a31,
|
||||
$this->a12 * $this->a31 - $this->a11 * $this->a32,
|
||||
$this->a12 * $this->a23 - $this->a13 * $this->a22,
|
||||
$this->a13 * $this->a21 - $this->a11 * $this->a23,
|
||||
$this->a11 * $this->a22 - $this->a12 * $this->a21);
|
||||
}
|
||||
|
||||
function times($other) {
|
||||
return new PerspectiveTransform($this->a11 * $other->a11 + $this->a21 * $other->a12 + $this->a31 * $other->a13,
|
||||
$this->a11 * $other->a21 + $this->a21 * $other->a22 + $this->a31 * $other->a23,
|
||||
$this->a11 * $other->a31 + $this->a21 * $other->a32 + $this->a31 * $other->a33,
|
||||
$this->a12 * $other->a11 + $this->a22 * $other->a12 + $this->a32 * $other->a13,
|
||||
$this->a12 * $other->a21 + $this->a22 * $other->a22 + $this->a32 * $other->a23,
|
||||
$this->a12 * $other->a31 + $this->a22 * $other->a32 + $this->a32 * $other->a33,
|
||||
$this->a13 * $other->a11 + $this->a23 * $other->a12 + $this->a33 * $other->a13,
|
||||
$this->a13 * $other->a21 + $this->a23 * $other->a22 + $this->a33 * $other->a23,
|
||||
$this->a13 * $other->a31 + $this->a23 * $other->a32 + $this->a33 * $other->a33);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
93
vendor/khanamiryan/qrcode-detector-decoder/lib/common/customFunctions.php
vendored
Normal file
93
vendor/khanamiryan/qrcode-detector-decoder/lib/common/customFunctions.php
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
function arraycopy($srcArray,$srcPos,$destArray, $destPos, $length){//System.arraycopy
|
||||
|
||||
$srcArrayToCopy = array_slice($srcArray,$srcPos,$length);
|
||||
array_splice($destArray,$destPos,$length,$srcArrayToCopy);
|
||||
return $destArray;
|
||||
}
|
||||
|
||||
|
||||
function overflow32($value) {//There is no need to overflow 64 bits to 32 bit
|
||||
return $value;
|
||||
}
|
||||
|
||||
function hashCode( $s )
|
||||
{
|
||||
$h = 0;
|
||||
$len = strlen($s);
|
||||
for($i = 0; $i < $len; $i++)
|
||||
{
|
||||
$h = overflow32(31 * $h + ord($s[$i]));
|
||||
}
|
||||
|
||||
return $h;
|
||||
}
|
||||
|
||||
|
||||
function numberOfTrailingZeros($i) {
|
||||
if ($i == 0) return 32;
|
||||
$num = 0;
|
||||
while (($i & 1) == 0) {
|
||||
$i >>= 1;
|
||||
$num++;
|
||||
}
|
||||
return $num;
|
||||
}
|
||||
function intval32bits($value)
|
||||
{
|
||||
$value = ($value & 0xFFFFFFFF);
|
||||
|
||||
if ($value & 0x80000000)
|
||||
$value = -((~$value & 0xFFFFFFFF) + 1);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
function uRShift($a, $b)
|
||||
{
|
||||
|
||||
if($b == 0) return $a;
|
||||
return ($a >> $b) & ~(1<<(8*PHP_INT_SIZE-1)>>($b-1));
|
||||
}
|
||||
/*
|
||||
function sdvig3($num,$count=1){//>>> 32 bit
|
||||
$s = decbin($num);
|
||||
|
||||
$sarray = str_split($s,1);
|
||||
$sarray = array_slice($sarray,-32);//32bit
|
||||
|
||||
for($i=0;$i<=1;$i++) {
|
||||
array_pop($sarray);
|
||||
array_unshift($sarray, '0');
|
||||
}
|
||||
return bindec(implode($sarray));
|
||||
}
|
||||
*/
|
||||
|
||||
function sdvig3($a,$b) {
|
||||
|
||||
if ($a >= 0) {
|
||||
return bindec(decbin($a>>$b)); //simply right shift for positive number
|
||||
}
|
||||
|
||||
$bin = decbin($a>>$b);
|
||||
|
||||
$bin = substr($bin, $b); // zero fill on the left side
|
||||
|
||||
$o = bindec($bin);
|
||||
return $o;
|
||||
}
|
||||
|
||||
function floatToIntBits($float_val)
|
||||
{
|
||||
$int = unpack('i', pack('f', $float_val));
|
||||
return $int[1];
|
||||
}
|
||||
|
||||
function fill_array($index,$count,$value){
|
||||
if($count<=0){
|
||||
return array(0);
|
||||
}else {
|
||||
return array_fill($index, $count, $value);
|
||||
}
|
||||
}
|
||||
46
vendor/khanamiryan/qrcode-detector-decoder/lib/common/detector/MathUtils.php
vendored
Normal file
46
vendor/khanamiryan/qrcode-detector-decoder/lib/common/detector/MathUtils.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2012 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Common\Detector;
|
||||
|
||||
final class MathUtils {
|
||||
|
||||
private function __construct() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends up being a bit faster than {@link Math#round(float)}. This merely rounds its
|
||||
* argument to the nearest int, where x.5 rounds up to x+1. Semantics of this shortcut
|
||||
* differ slightly from {@link Math#round(float)} in that half rounds down for negative
|
||||
* values. -2.5 rounds to -3, not -2. For purposes here it makes no difference.
|
||||
*
|
||||
* @param d real value to round
|
||||
* @return nearest {@code int}
|
||||
*/
|
||||
public static function round($d) {
|
||||
return (int) ($d + ($d < 0.0 ? -0.5 : 0.5));
|
||||
}
|
||||
|
||||
public static function distance($aX, $aY, $bX, $bY) {
|
||||
$xDiff = $aX - $bX;
|
||||
$yDiff = $aY - $bY;
|
||||
return (float) sqrt($xDiff * $xDiff + $yDiff * $yDiff);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
225
vendor/khanamiryan/qrcode-detector-decoder/lib/common/detector/MonochromeRectangleDetector.php
vendored
Normal file
225
vendor/khanamiryan/qrcode-detector-decoder/lib/common/detector/MonochromeRectangleDetector.php
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Ashot
|
||||
* Date: 3/24/15
|
||||
* Time: 21:23
|
||||
*/
|
||||
namespace Zxing\Common\Detector;
|
||||
|
||||
use \Zxing\NotFoundException;
|
||||
use \Zxing\ResultPoint;
|
||||
use \Zxing\BitMatrix;
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
*/
|
||||
//require_once('./lib/NotFoundException.php');
|
||||
//require_once('./lib/ResultPoint.php');
|
||||
//require_once('./lib/common/BitMatrix.php');
|
||||
|
||||
|
||||
/**
|
||||
* <p>A somewhat generic detector that looks for a barcode-like rectangular region within an image.
|
||||
* It looks within a mostly white region of an image for a region of black and white, but mostly
|
||||
* black. It returns the four corners of the region, as best it can determine.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @port Ashot Khanamiryan
|
||||
*/
|
||||
class MonochromeRectangleDetector {
|
||||
private static $MAX_MODULES = 32;
|
||||
private $image;
|
||||
function __construct($image){
|
||||
$this->image = $image;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Detects a rectangular region of black and white -- mostly black -- with a region of mostly
|
||||
* white, in an image.</p>
|
||||
*
|
||||
* @return {@link ResultPoint}[] describing the corners of the rectangular region. The first and
|
||||
* last points are opposed on the diagonal, as are the second and third. The first point will be
|
||||
* the topmost point and the last, the bottommost. The second point will be leftmost and the
|
||||
* third, the rightmost
|
||||
* @throws NotFoundException if no Data Matrix Code can be found
|
||||
*/
|
||||
public function detect(){
|
||||
|
||||
$height = $this->image->getHeight();
|
||||
$width = $this->image->getWidth();
|
||||
$halfHeight = $height / 2;
|
||||
$halfWidth = $width / 2;
|
||||
|
||||
$deltaY = max(1, $height / (self::$MAX_MODULES * 8));
|
||||
$deltaX = max(1, $width / (self::$MAX_MODULES * 8));
|
||||
|
||||
|
||||
$top = 0;
|
||||
$bottom = $height;
|
||||
$left = 0;
|
||||
$right = $width;
|
||||
$pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right,
|
||||
$halfHeight, -$deltaY, $top, $bottom, $halfWidth / 2);
|
||||
$top = (int) $pointA->getY() - 1;
|
||||
$pointB = $this->findCornerFromCenter($halfWidth, -$deltaX, $left,$right,
|
||||
$halfHeight, 0, $top, $bottom, $halfHeight / 2);
|
||||
$left = (int) $pointB->getX() - 1;
|
||||
$pointC = $this->findCornerFromCenter($halfWidth, $deltaX, $left, $right,
|
||||
$halfHeight, 0, $top, $bottom, $halfHeight / 2);
|
||||
$right = (int) $pointC->getX() + 1;
|
||||
$pointD = $this->findCornerFromCenter($halfWidth, 0, $left, $right,
|
||||
$halfHeight, $deltaY, $top, $bottom, $halfWidth / 2);
|
||||
$bottom = (int) $pointD->getY() + 1;
|
||||
|
||||
// Go try to find po$A again with better information -- might have been off at first.
|
||||
$pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right,
|
||||
$halfHeight, -$deltaY, $top, $bottom, $halfWidth / 4);
|
||||
|
||||
return new ResultPoint( $pointA, $pointB, $pointC, $pointD );
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
|
||||
* point which should be within the barcode.
|
||||
*
|
||||
* @param centerX center's x component (horizontal)
|
||||
* @param deltaX same as deltaY but change in x per step instead
|
||||
* @param left minimum value of x
|
||||
* @param right maximum value of x
|
||||
* @param centerY center's y component (vertical)
|
||||
* @param deltaY change in y per step. If scanning up this is negative; down, positive;
|
||||
* left or right, 0
|
||||
* @param top minimum value of y to search through (meaningless when di == 0)
|
||||
* @param bottom maximum value of y
|
||||
* @param maxWhiteRun maximum run of white pixels that can still be considered to be within
|
||||
* the barcode
|
||||
* @return a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found
|
||||
* @throws NotFoundException if such a point cannot be found
|
||||
*/
|
||||
private function findCornerFromCenter($centerX,
|
||||
$deltaX,
|
||||
$left,
|
||||
$right,
|
||||
$centerY,
|
||||
$deltaY,
|
||||
$top,
|
||||
$bottom,
|
||||
$maxWhiteRun){
|
||||
$lastRange = null;
|
||||
for ($y = $centerY, $x = $centerX;
|
||||
$y < $bottom && $y >= $top && $x < $right && $x >= $left;
|
||||
$y += $deltaY, $x += $deltaX) {
|
||||
$range = 0;
|
||||
if ($deltaX == 0) {
|
||||
// horizontal slices, up and down
|
||||
$range = $this->blackWhiteRange($y, $maxWhiteRun, $left, $right, true);
|
||||
} else {
|
||||
// vertical slices, left and right
|
||||
$range = $this->blackWhiteRange($x, $maxWhiteRun, $top, $bottom, false);
|
||||
}
|
||||
if ($range == null) {
|
||||
if ($lastRange == null) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
// lastRange was found
|
||||
if ($deltaX == 0) {
|
||||
$lastY = $y - $deltaY;
|
||||
if ($lastRange[0] < $centerX) {
|
||||
if ($lastRange[1] > $centerX) {
|
||||
// straddle, choose one or the other based on direction
|
||||
return new ResultPoint($deltaY > 0 ? $lastRange[0] : $lastRange[1], $lastY);
|
||||
}
|
||||
return new ResultPoint($lastRange[0], $lastY);
|
||||
} else {
|
||||
return new ResultPoint($lastRange[1], $lastY);
|
||||
}
|
||||
} else {
|
||||
$lastX = $x - $deltaX;
|
||||
if ($lastRange[0] < $centerY) {
|
||||
if ($lastRange[1] > $centerY) {
|
||||
return new ResultPoint($lastX, $deltaX < 0 ? $lastRange[0] : $lastRange[1]);
|
||||
}
|
||||
return new ResultPoint($lastX, $lastRange[0]);
|
||||
} else {
|
||||
return new ResultPoint($lastX, $lastRange[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$lastRange = $range;
|
||||
}
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Computes the start and end of a region of pixels, either horizontally or vertically, that could
|
||||
* be part of a Data Matrix barcode.
|
||||
*
|
||||
* @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location)
|
||||
* where we are scanning. If scanning vertically it's the column, the fixed horizontal location
|
||||
* @param maxWhiteRun largest run of white pixels that can still be considered part of the
|
||||
* barcode region
|
||||
* @param minDim minimum pixel location, horizontally or vertically, to consider
|
||||
* @param maxDim maximum pixel location, horizontally or vertically, to consider
|
||||
* @param horizontal if true, we're scanning left-right, instead of up-down
|
||||
* @return int[] with start and end of found range, or null if no such range is found
|
||||
* (e.g. only white was found)
|
||||
*/
|
||||
|
||||
private function blackWhiteRange($fixedDimension, $maxWhiteRun, $minDim, $maxDim, $horizontal){
|
||||
$center = ($minDim + $maxDim) / 2;
|
||||
|
||||
// Scan left/up first
|
||||
$start = $center;
|
||||
while ($start >= $minDim) {
|
||||
if ($horizontal ? $this->image->get($start, $fixedDimension) : $this->image->get($fixedDimension, $start)) {
|
||||
$start--;
|
||||
} else {
|
||||
$whiteRunStart = $start;
|
||||
do {
|
||||
$start--;
|
||||
} while ($start >= $minDim && !($horizontal ? $this->image->get($start, $fixedDimension) :
|
||||
$this->image->get($fixedDimension, $start)));
|
||||
$whiteRunSize = $whiteRunStart - $start;
|
||||
if ($start < $minDim || $whiteRunSize > $maxWhiteRun) {
|
||||
$start = $whiteRunStart;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$start++;
|
||||
|
||||
// Then try right/down
|
||||
$end = $center;
|
||||
while ($end < $maxDim) {
|
||||
if ($horizontal ? $this->image->get($end, $fixedDimension) : $this->image->get($fixedDimension, $end)) {
|
||||
$end++;
|
||||
} else {
|
||||
$whiteRunStart = $end;
|
||||
do {
|
||||
$end++;
|
||||
} while ($end < $maxDim && !($horizontal ? $this->image->get($end, $fixedDimension) :
|
||||
$this->image->get($fixedDimension, $end)));
|
||||
$whiteRunSize = $end - $whiteRunStart;
|
||||
if ($end >= $maxDim || $whiteRunSize > $maxWhiteRun) {
|
||||
$end = $whiteRunStart;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$end--;
|
||||
|
||||
return $end > $start ? array($start, $end) : null;
|
||||
}
|
||||
}
|
||||
181
vendor/khanamiryan/qrcode-detector-decoder/lib/common/reedsolomon/GenericGF.php
vendored
Normal file
181
vendor/khanamiryan/qrcode-detector-decoder/lib/common/reedsolomon/GenericGF.php
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Common\Reedsolomon;
|
||||
|
||||
/**
|
||||
* <p>This class contains utility methods for performing mathematical operations over
|
||||
* the Galois Fields. Operations use a given primitive polynomial in calculations.</p>
|
||||
*
|
||||
* <p>Throughout this package, elements of the GF are represented as an {@code int}
|
||||
* for convenience and speed (but at the cost of memory).
|
||||
* </p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author David Olivier
|
||||
*/
|
||||
final class GenericGF {
|
||||
|
||||
public static $AZTEC_DATA_12;
|
||||
public static $AZTEC_DATA_10;
|
||||
public static $AZTEC_DATA_6;
|
||||
public static $AZTEC_PARAM;
|
||||
public static $QR_CODE_FIELD_256;
|
||||
public static $DATA_MATRIX_FIELD_256;
|
||||
public static $AZTEC_DATA_8;
|
||||
public static $MAXICODE_FIELD_64;
|
||||
|
||||
private $expTable;
|
||||
private $logTable;
|
||||
private $zero;
|
||||
private $one;
|
||||
private $size;
|
||||
private $primitive;
|
||||
private $generatorBase;
|
||||
|
||||
|
||||
public static function Init(){
|
||||
self::$AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1
|
||||
self::$AZTEC_DATA_10 = new GenericGF(0x409, 1024, 1); // x^10 + x^3 + 1
|
||||
self::$AZTEC_DATA_6 = new GenericGF(0x43, 64, 1); // x^6 + x + 1
|
||||
self::$AZTEC_PARAM = new GenericGF(0x13, 16, 1); // x^4 + x + 1
|
||||
self::$QR_CODE_FIELD_256 = new GenericGF(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1
|
||||
self::$DATA_MATRIX_FIELD_256 = new GenericGF(0x012D, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1
|
||||
self::$AZTEC_DATA_8 = self::$DATA_MATRIX_FIELD_256;
|
||||
self::$MAXICODE_FIELD_64 = self::$AZTEC_DATA_6;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a representation of GF(size) using the given primitive polynomial.
|
||||
*
|
||||
* @param primitive irreducible polynomial whose coefficients are represented by
|
||||
* the bits of an int, where the least-significant bit represents the constant
|
||||
* coefficient
|
||||
* @param size the size of the field
|
||||
* @param b the factor b in the generator polynomial can be 0- or 1-based
|
||||
* (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))).
|
||||
* In most cases it should be 1, but for QR code it is 0.
|
||||
*/
|
||||
public function __construct($primitive, $size, $b) {
|
||||
$this->primitive = $primitive;
|
||||
$this->size = $size;
|
||||
$this->generatorBase = $b;
|
||||
|
||||
$this->expTable = array();
|
||||
$this->logTable =array();
|
||||
$x = 1;
|
||||
for ($i = 0; $i < $size; $i++) {
|
||||
$this->expTable[$i] = $x;
|
||||
$x *= 2; // we're assuming the generator alpha is 2
|
||||
if ($x >= $size) {
|
||||
$x ^= $primitive;
|
||||
$x &= $size-1;
|
||||
}
|
||||
}
|
||||
for ($i = 0; $i < $size-1; $i++) {
|
||||
$this->logTable[$this->expTable[$i]] = $i;
|
||||
}
|
||||
// logTable[0] == 0 but this should never be used
|
||||
$this->zero = new GenericGFPoly($this, array(0));
|
||||
$this->one = new GenericGFPoly($this, array(1));
|
||||
}
|
||||
|
||||
function getZero() {
|
||||
return $this->zero;
|
||||
}
|
||||
|
||||
function getOne() {
|
||||
return $this->one;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the monomial representing coefficient * x^degree
|
||||
*/
|
||||
function buildMonomial($degree, $coefficient) {
|
||||
if ($degree < 0) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
if ($coefficient == 0) {
|
||||
return $this->zero;
|
||||
}
|
||||
$coefficients = fill_array(0,$degree+1,0);//new int[degree + 1];
|
||||
$coefficients[0] = $coefficient;
|
||||
return new GenericGFPoly($this, $coefficients);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements both addition and subtraction -- they are the same in GF(size).
|
||||
*
|
||||
* @return sum/difference of a and b
|
||||
*/
|
||||
static function addOrSubtract($a, $b) {
|
||||
return $a ^ $b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 2 to the power of a in GF(size)
|
||||
*/
|
||||
function exp($a) {
|
||||
return $this->expTable[$a];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return base 2 log of a in GF(size)
|
||||
*/
|
||||
function log($a) {
|
||||
if ($a == 0) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
return $this->logTable[$a];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return multiplicative inverse of a
|
||||
*/
|
||||
function inverse($a) {
|
||||
if ($a == 0) {
|
||||
throw new Exception();
|
||||
}
|
||||
return $this->expTable[$this->size - $this->logTable[$a] - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return product of a and b in GF(size)
|
||||
*/
|
||||
function multiply($a, $b) {
|
||||
if ($a == 0 || $b == 0) {
|
||||
return 0;
|
||||
}
|
||||
return $this->expTable[($this->logTable[$a] + $this->logTable[$b]) % ($this->size - 1)];
|
||||
}
|
||||
|
||||
public function getSize() {
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
public function getGeneratorBase() {
|
||||
return $this->generatorBase;
|
||||
}
|
||||
|
||||
// @Override
|
||||
public function toString() {
|
||||
return "GF(0x" . dechex(intval($this->primitive)) . ',' . $this->size . ')';
|
||||
}
|
||||
|
||||
}
|
||||
GenericGF::Init();
|
||||
268
vendor/khanamiryan/qrcode-detector-decoder/lib/common/reedsolomon/GenericGFPoly.php
vendored
Normal file
268
vendor/khanamiryan/qrcode-detector-decoder/lib/common/reedsolomon/GenericGFPoly.php
vendored
Normal file
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Common\Reedsolomon;
|
||||
|
||||
/**
|
||||
* <p>Represents a polynomial whose coefficients are elements of a GF.
|
||||
* Instances of this class are immutable.</p>
|
||||
*
|
||||
* <p>Much credit is due to William Rucklidge since portions of this code are an indirect
|
||||
* port of his C++ Reed-Solomon implementation.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class GenericGFPoly {
|
||||
|
||||
private $field;
|
||||
private $coefficients;
|
||||
|
||||
/**
|
||||
* @param field the {@link GenericGF} instance representing the field to use
|
||||
* to perform computations
|
||||
* @param coefficients array coefficients as ints representing elements of GF(size), arranged
|
||||
* from most significant (highest-power term) coefficient to least significant
|
||||
* @throws IllegalArgumentException if argument is null or empty,
|
||||
* or if leading coefficient is 0 and this is not a
|
||||
* constant polynomial (that is, it is not the monomial "0")
|
||||
*/
|
||||
function __construct($field, $coefficients) {
|
||||
if (count($coefficients) == 0) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
$this->field = $field;
|
||||
$coefficientsLength = count($coefficients);
|
||||
if ($coefficientsLength > 1 && $coefficients[0] == 0) {
|
||||
// Leading term must be non-zero for anything except the constant polynomial "0"
|
||||
$firstNonZero = 1;
|
||||
while ($firstNonZero < $coefficientsLength && $coefficients[$firstNonZero] == 0) {
|
||||
$firstNonZero++;
|
||||
}
|
||||
if ($firstNonZero == $coefficientsLength) {
|
||||
$this->coefficients = array(0);
|
||||
} else {
|
||||
$this->coefficients = fill_array(0,$coefficientsLength - $firstNonZero,0);
|
||||
$this->coefficients = arraycopy($coefficients,
|
||||
$firstNonZero,
|
||||
$this->coefficients,
|
||||
0,
|
||||
count($this->coefficients));
|
||||
}
|
||||
} else {
|
||||
$this->coefficients = $coefficients;
|
||||
}
|
||||
}
|
||||
|
||||
function getCoefficients() {
|
||||
return $this->coefficients;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return degree of this polynomial
|
||||
*/
|
||||
function getDegree() {
|
||||
return count($this->coefficients) - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff this polynomial is the monomial "0"
|
||||
*/
|
||||
function isZero() {
|
||||
return $this->coefficients[0] == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return coefficient of x^degree term in this polynomial
|
||||
*/
|
||||
function getCoefficient($degree) {
|
||||
return $this->coefficients[count($this->coefficients) - 1 - $degree];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return evaluation of this polynomial at a given point
|
||||
*/
|
||||
function evaluateAt($a) {
|
||||
if ($a == 0) {
|
||||
// Just return the x^0 coefficient
|
||||
return $this->getCoefficient(0);
|
||||
}
|
||||
$size = count($this->coefficients);
|
||||
if ($a == 1) {
|
||||
// Just the sum of the coefficients
|
||||
$result = 0;
|
||||
foreach ($this->coefficients as $coefficient ) {
|
||||
$result = GenericGF::addOrSubtract($result, $coefficient);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
$result = $this->coefficients[0];
|
||||
for ($i = 1; $i < $size; $i++) {
|
||||
$result = GenericGF::addOrSubtract($this->field->multiply($a, $result), $this->coefficients[$i]);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
function addOrSubtract($other) {
|
||||
if ($this->field !== $other->field) {
|
||||
throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
|
||||
}
|
||||
if ($this->isZero()) {
|
||||
return $other;
|
||||
}
|
||||
if ($other->isZero()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$smallerCoefficients = $this->coefficients;
|
||||
$largerCoefficients = $other->coefficients;
|
||||
if (count($smallerCoefficients) > count($largerCoefficients)) {
|
||||
$temp = $smallerCoefficients;
|
||||
$smallerCoefficients = $largerCoefficients;
|
||||
$largerCoefficients = $temp;
|
||||
}
|
||||
$sumDiff = fill_array(0,count($largerCoefficients),0);
|
||||
$lengthDiff = count($largerCoefficients) - count($smallerCoefficients);
|
||||
// Copy high-order terms only found in higher-degree polynomial's coefficients
|
||||
$sumDiff = arraycopy($largerCoefficients, 0, $sumDiff, 0, $lengthDiff);
|
||||
|
||||
for ($i = $lengthDiff; $i < count($largerCoefficients); $i++) {
|
||||
$sumDiff[$i] = GenericGF::addOrSubtract($smallerCoefficients[$i - $lengthDiff], $largerCoefficients[$i]);
|
||||
}
|
||||
|
||||
return new GenericGFPoly($this->field, $sumDiff);
|
||||
}
|
||||
|
||||
function multiply($other) {
|
||||
if(is_int($other)){
|
||||
return $this->multiply_($other);
|
||||
}
|
||||
if ($this->field !== $other->field) {
|
||||
throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
|
||||
}
|
||||
if ($this->isZero() || $other->isZero()) {
|
||||
return $this->field->getZero();
|
||||
}
|
||||
$aCoefficients = $this->coefficients;
|
||||
$aLength = count($aCoefficients);
|
||||
$bCoefficients = $other->coefficients;
|
||||
$bLength = count($bCoefficients);
|
||||
$product = fill_array(0,$aLength + $bLength - 1,0);
|
||||
for ($i = 0; $i < $aLength; $i++) {
|
||||
$aCoeff = $aCoefficients[$i];
|
||||
for ($j = 0; $j < $bLength; $j++) {
|
||||
$product[$i + $j] = GenericGF::addOrSubtract($product[$i + $j],
|
||||
$this->field->multiply($aCoeff, $bCoefficients[$j]));
|
||||
}
|
||||
}
|
||||
return new GenericGFPoly($this->field, $product);
|
||||
}
|
||||
|
||||
function multiply_($scalar) {
|
||||
if ($scalar == 0) {
|
||||
return $this->field->getZero();
|
||||
}
|
||||
if ($scalar == 1) {
|
||||
return $this;
|
||||
}
|
||||
$size = count($this->coefficients);
|
||||
$product = fill_array(0,$size,0);
|
||||
for ($i = 0; $i < $size; $i++) {
|
||||
$product[$i] = $this->field->multiply($this->coefficients[$i], $scalar);
|
||||
}
|
||||
return new GenericGFPoly($this->field, $product);
|
||||
}
|
||||
|
||||
function multiplyByMonomial($degree, $coefficient) {
|
||||
if ($degree < 0) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
if ($coefficient == 0) {
|
||||
return $this->field->getZero();
|
||||
}
|
||||
$size = count($this->coefficients);
|
||||
$product = fill_array(0,$size + $degree,0);
|
||||
for ($i = 0; $i < $size; $i++) {
|
||||
$product[$i] = $this->field->multiply($this->coefficients[$i], $coefficient);
|
||||
}
|
||||
return new GenericGFPoly($this->field, $product);
|
||||
}
|
||||
|
||||
function divide($other) {
|
||||
if ($this->field !==$other->field) {
|
||||
throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
|
||||
}
|
||||
if ($other->isZero()) {
|
||||
throw new \InvalidArgumentException("Divide by 0");
|
||||
}
|
||||
|
||||
$quotient = $this->field->getZero();
|
||||
$remainder = $this;
|
||||
|
||||
$denominatorLeadingTerm = $other->getCoefficient($other->getDegree());
|
||||
$inverseDenominatorLeadingTerm = $this->field->inverse($denominatorLeadingTerm);
|
||||
|
||||
while ($remainder->getDegree() >= $other->getDegree() && !$remainder->isZero()) {
|
||||
$degreeDifference = $remainder->getDegree() - $other->getDegree();
|
||||
$scale = $this->field->multiply($remainder->getCoefficient($remainder->getDegree()), $inverseDenominatorLeadingTerm);
|
||||
$term = $other->multiplyByMonomial($degreeDifference, $scale);
|
||||
$iterationQuotient = $this->field->buildMonomial($degreeDifference, $scale);
|
||||
$quotient = $quotient->addOrSubtract($iterationQuotient);
|
||||
$remainder = $remainder->addOrSubtract($term);
|
||||
}
|
||||
|
||||
return array($quotient, $remainder );
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function toString() {
|
||||
$result = '';
|
||||
for ($degree = $this->getDegree(); $degree >= 0; $degree--) {
|
||||
$coefficient = $this->getCoefficient($degree);
|
||||
if ($coefficient != 0) {
|
||||
if ($coefficient < 0) {
|
||||
$result.=" - ";
|
||||
$coefficient = -$coefficient;
|
||||
} else {
|
||||
if (strlen($result) > 0) {
|
||||
$result .= " + ";
|
||||
}
|
||||
}
|
||||
if ($degree == 0 || $coefficient != 1) {
|
||||
$alphaPower = $this->field->log($coefficient);
|
||||
if ($alphaPower == 0) {
|
||||
$result.='1';
|
||||
} else if ($alphaPower == 1) {
|
||||
$result.='a';
|
||||
} else {
|
||||
$result.="a^";
|
||||
$result.=($alphaPower);
|
||||
}
|
||||
}
|
||||
if ($degree != 0) {
|
||||
if ($degree == 1) {
|
||||
$result.='x';
|
||||
} else {
|
||||
$result.="x^";
|
||||
$result.= $degree;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
192
vendor/khanamiryan/qrcode-detector-decoder/lib/common/reedsolomon/ReedSolomonDecoder.php
vendored
Normal file
192
vendor/khanamiryan/qrcode-detector-decoder/lib/common/reedsolomon/ReedSolomonDecoder.php
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Common\Reedsolomon;
|
||||
|
||||
/**
|
||||
* <p>Implements Reed-Solomon decoding, as the name implies.</p>
|
||||
*
|
||||
* <p>The algorithm will not be explained here, but the following references were helpful
|
||||
* in creating this implementation:</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>Bruce Maggs.
|
||||
* <a href="http://www.cs.cmu.edu/afs/cs.cmu.edu/project/pscico-guyb/realworld/www/rs_decode.ps">
|
||||
* "Decoding Reed-Solomon Codes"</a> (see discussion of Forney's Formula)</li>
|
||||
* <li>J.I. Hall. <a href="www.mth.msu.edu/~jhall/classes/codenotes/GRS.pdf">
|
||||
* "Chapter 5. Generalized Reed-Solomon Codes"</a>
|
||||
* (see discussion of Euclidean algorithm)</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Much credit is due to William Rucklidge since portions of this code are an indirect
|
||||
* port of his C++ Reed-Solomon implementation.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author William Rucklidge
|
||||
* @author sanfordsquires
|
||||
*/
|
||||
final class ReedSolomonDecoder {
|
||||
|
||||
private $field;
|
||||
|
||||
public function __construct($field) {
|
||||
$this->field = $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Decodes given set of received codewords, which include both data and error-correction
|
||||
* codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place,
|
||||
* in the input.</p>
|
||||
*
|
||||
* @param received data and error-correction codewords
|
||||
* @param twoS number of error-correction codewords available
|
||||
* @throws ReedSolomonException if decoding fails for any reason
|
||||
*/
|
||||
public function decode(&$received, $twoS) {
|
||||
$poly = new GenericGFPoly($this->field, $received);
|
||||
$syndromeCoefficients = fill_array(0,$twoS,0);
|
||||
$noError = true;
|
||||
for ($i = 0; $i < $twoS; $i++) {
|
||||
$eval = $poly->evaluateAt($this->field->exp($i + $this->field->getGeneratorBase()));
|
||||
$syndromeCoefficients[count($syndromeCoefficients) - 1 - $i] = $eval;
|
||||
if ($eval != 0) {
|
||||
$noError = false;
|
||||
}
|
||||
}
|
||||
if ($noError) {
|
||||
return;
|
||||
}
|
||||
$syndrome = new GenericGFPoly($this->field, $syndromeCoefficients);
|
||||
$sigmaOmega =
|
||||
$this->runEuclideanAlgorithm($this->field->buildMonomial($twoS, 1), $syndrome, $twoS);
|
||||
$sigma = $sigmaOmega[0];
|
||||
$omega = $sigmaOmega[1];
|
||||
$errorLocations = $this->findErrorLocations($sigma);
|
||||
$errorMagnitudes = $this->findErrorMagnitudes($omega, $errorLocations);
|
||||
for ($i = 0; $i < count($errorLocations); $i++) {
|
||||
$position = count($received) - 1 - $this->field->log($errorLocations[$i]);
|
||||
if ($position < 0) {
|
||||
throw new ReedSolomonException("Bad error location");
|
||||
}
|
||||
$received[$position] = GenericGF::addOrSubtract($received[$position], $errorMagnitudes[$i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function runEuclideanAlgorithm($a, $b, $R)
|
||||
{
|
||||
// Assume a's degree is >= b's
|
||||
if ($a->getDegree() < $b->getDegree()) {
|
||||
$temp = $a;
|
||||
$a = $b;
|
||||
$b = $temp;
|
||||
}
|
||||
|
||||
$rLast = $a;
|
||||
$r = $b;
|
||||
$tLast = $this->field->getZero();
|
||||
$t = $this->field->getOne();
|
||||
|
||||
// Run Euclidean algorithm until r's degree is less than R/2
|
||||
while ($r->getDegree() >= $R / 2) {
|
||||
$rLastLast = $rLast;
|
||||
$tLastLast = $tLast;
|
||||
$rLast = $r;
|
||||
$tLast = $t;
|
||||
|
||||
// Divide rLastLast by rLast, with quotient in q and remainder in r
|
||||
if ($rLast->isZero()) {
|
||||
// Oops, Euclidean algorithm already terminated?
|
||||
throw new ReedSolomonException("r_{i-1} was zero");
|
||||
}
|
||||
$r = $rLastLast;
|
||||
$q = $this->field->getZero();
|
||||
$denominatorLeadingTerm = $rLast->getCoefficient($rLast->getDegree());
|
||||
$dltInverse = $this->field->inverse($denominatorLeadingTerm);
|
||||
while ($r->getDegree() >= $rLast->getDegree() && !$r->isZero()) {
|
||||
$degreeDiff = $r->getDegree() - $rLast->getDegree();
|
||||
$scale = $this->field->multiply($r->getCoefficient($r->getDegree()), $dltInverse);
|
||||
$q = $q->addOrSubtract($this->field->buildMonomial($degreeDiff, $scale));
|
||||
$r = $r->addOrSubtract($rLast->multiplyByMonomial($degreeDiff, $scale));
|
||||
}
|
||||
|
||||
$t = $q->multiply($tLast)->addOrSubtract($tLastLast);
|
||||
|
||||
if ($r->getDegree() >= $rLast->getDegree()) {
|
||||
throw new IllegalStateException("Division algorithm failed to reduce polynomial?");
|
||||
}
|
||||
}
|
||||
|
||||
$sigmaTildeAtZero = $t->getCoefficient(0);
|
||||
if ($sigmaTildeAtZero == 0) {
|
||||
throw new ReedSolomonException("sigmaTilde(0) was zero");
|
||||
}
|
||||
|
||||
$inverse = $this->field->inverse($sigmaTildeAtZero);
|
||||
$sigma = $t->multiply($inverse);
|
||||
$omega = $r->multiply($inverse);
|
||||
return array($sigma, $omega);
|
||||
}
|
||||
|
||||
private function findErrorLocations($errorLocator) {
|
||||
// This is a direct application of Chien's search
|
||||
$numErrors = $errorLocator->getDegree();
|
||||
if ($numErrors == 1) { // shortcut
|
||||
return array($errorLocator->getCoefficient(1) );
|
||||
}
|
||||
$result = fill_array(0,$numErrors,0);
|
||||
$e = 0;
|
||||
for ($i = 1; $i < $this->field->getSize() && $e < $numErrors; $i++) {
|
||||
if ($errorLocator->evaluateAt($i) == 0) {
|
||||
$result[$e] = $this->field->inverse($i);
|
||||
$e++;
|
||||
}
|
||||
}
|
||||
if ($e != $numErrors) {
|
||||
throw new ReedSolomonException("Error locator degree does not match number of roots");
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function findErrorMagnitudes($errorEvaluator, $errorLocations) {
|
||||
// This is directly applying Forney's Formula
|
||||
$s = count($errorLocations);
|
||||
$result = fill_array(0,$s,0);
|
||||
for ($i = 0; $i < $s; $i++) {
|
||||
$xiInverse = $this->field->inverse($errorLocations[$i]);
|
||||
$denominator = 1;
|
||||
for ($j = 0; $j < $s; $j++) {
|
||||
if ($i != $j) {
|
||||
//denominator = field.multiply(denominator,
|
||||
// GenericGF.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse)));
|
||||
// Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug.
|
||||
// Below is a funny-looking workaround from Steven Parkes
|
||||
$term = $this->field->multiply($errorLocations[$j], $xiInverse);
|
||||
$termPlus1 = ($term & 0x1) == 0 ? $term | 1 : $term & ~1;
|
||||
$denominator = $this->field->multiply($denominator, $termPlus1);
|
||||
}
|
||||
}
|
||||
$result[$i] = $this->field->multiply($errorEvaluator->evaluateAt($xiInverse),
|
||||
$this->field->inverse($denominator));
|
||||
if ($this->field->getGeneratorBase() != 0) {
|
||||
$result[$i] = $this->field->multiply($result[$i], $xiInverse);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
31
vendor/khanamiryan/qrcode-detector-decoder/lib/common/reedsolomon/ReedSolomonException.php
vendored
Normal file
31
vendor/khanamiryan/qrcode-detector-decoder/lib/common/reedsolomon/ReedSolomonException.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Common\Reedsolomon;
|
||||
|
||||
/**
|
||||
* <p>Thrown when an exception occurs during Reed-Solomon decoding, such as when
|
||||
* there are too many errors to correct.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class ReedSolomonException extends \Exception {
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
222
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/QRCodeReader.php
vendored
Normal file
222
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/QRCodeReader.php
vendored
Normal file
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode;
|
||||
|
||||
use Zxing\BarcodeFormat;
|
||||
use Zxing\BinaryBitmap;
|
||||
use Zxing\ChecksumException;
|
||||
use Zxing\DecodeHintType;
|
||||
use Zxing\FormatException;
|
||||
use Zxing\NotFoundException;
|
||||
use Zxing\Reader;
|
||||
use Zxing\Result;
|
||||
use Zxing\ResultMetadataType;
|
||||
use Zxing\ResultPoint;
|
||||
use Zxing\Common\BitMatrix;
|
||||
use Zxing\Common\DecoderResult;
|
||||
use Zxing\Common\DetectorResult;
|
||||
use Zxing\Qrcode\Decoder\Decoder;
|
||||
use Zxing\Qrcode\Decoder\QRCodeDecoderMetaData;
|
||||
use Zxing\Qrcode\Detector\Detector;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This implementation can detect and decode QR Codes in an image.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
class QRCodeReader implements Reader {
|
||||
|
||||
|
||||
private static $NO_POINTS = array();
|
||||
private $decoder;
|
||||
|
||||
function __construct(){
|
||||
$this->decoder = new Decoder();
|
||||
|
||||
}
|
||||
|
||||
protected final function getDecoder() {
|
||||
return $this->decoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates and decodes a QR code in an image.
|
||||
*
|
||||
* @return a String representing the content encoded by the QR code
|
||||
* @throws NotFoundException if a QR code cannot be found
|
||||
* @throws FormatException if a QR code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
//@Override
|
||||
|
||||
|
||||
// @Override
|
||||
public function decode($image, $hints=null){/* Map<DecodeHintType,?> hints*/
|
||||
$decoderResult = null;
|
||||
$points = array();
|
||||
if ($hints != null && $hints['PURE_BARCODE']) {//hints.containsKey(DecodeHintType.PURE_BARCODE)) {
|
||||
$bits = $this->extractPureBits($image->getBlackMatrix());
|
||||
$decoderResult = $this->decoder->decode($bits, $hints);
|
||||
$points = self::$NO_POINTS;
|
||||
} else {
|
||||
$detector = new Detector($image->getBlackMatrix());
|
||||
$detectorResult = $detector->detect($hints);
|
||||
|
||||
$decoderResult = $this->decoder->decode($detectorResult->getBits(), $hints);
|
||||
$points = $detectorResult->getPoints();
|
||||
}
|
||||
|
||||
// If the code was mirrored: swap the bottom-left and the top-right points.
|
||||
if ($decoderResult->getOther() instanceof QRCodeDecoderMetaData) {
|
||||
$decoderResult->getOther()->applyMirroredCorrection($points);
|
||||
}
|
||||
|
||||
$result = new Result($decoderResult->getText(), $decoderResult->getRawBytes(), $points, 'QR_CODE');//BarcodeFormat.QR_CODE
|
||||
$byteSegments = $decoderResult->getByteSegments();
|
||||
if ($byteSegments != null) {
|
||||
$result->putMetadata('BYTE_SEGMENTS', $byteSegments);//ResultMetadataType.BYTE_SEGMENTS
|
||||
}
|
||||
$ecLevel = $decoderResult->getECLevel();
|
||||
if ($ecLevel != null) {
|
||||
$result->putMetadata('ERROR_CORRECTION_LEVEL', $ecLevel);//ResultMetadataType.ERROR_CORRECTION_LEVEL
|
||||
}
|
||||
if ($decoderResult->hasStructuredAppend()) {
|
||||
$result->putMetadata('STRUCTURED_APPEND_SEQUENCE',//ResultMetadataType.STRUCTURED_APPEND_SEQUENCE
|
||||
$decoderResult->getStructuredAppendSequenceNumber());
|
||||
$result->putMetadata('STRUCTURED_APPEND_PARITY',//ResultMetadataType.STRUCTURED_APPEND_PARITY
|
||||
$decoderResult->getStructuredAppendParity());
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function reset() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* This method detects a code in a "pure" image -- that is, pure monochrome image
|
||||
* which contains only an unrotated, unskewed, image of a code, with some white border
|
||||
* around it. This is a specialized method that works exceptionally fast in this special
|
||||
* case.
|
||||
*
|
||||
* @see com.google.zxing.datamatrix.DataMatrixReader#extractPureBits(BitMatrix)
|
||||
*/
|
||||
private static function extractPureBits($image) {
|
||||
|
||||
$leftTopBlack = $image->getTopLeftOnBit();
|
||||
$rightBottomBlack = $image->getBottomRightOnBit();
|
||||
if ($leftTopBlack == null || $rightBottomBlack == null) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
$moduleSize = self::moduleSize($leftTopBlack, $image);
|
||||
|
||||
$top = $leftTopBlack[1];
|
||||
$bottom = $rightBottomBlack[1];
|
||||
$left = $leftTopBlack[0];
|
||||
$right = $rightBottomBlack[0];
|
||||
|
||||
// Sanity check!
|
||||
if ($left >= $right || $top >= $bottom) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
if ($bottom - $top != $right - $left) {
|
||||
// Special case, where bottom-right module wasn't black so we found something else in the last row
|
||||
// Assume it's a square, so use height as the width
|
||||
$right = $left + ($bottom - $top);
|
||||
}
|
||||
|
||||
$matrixWidth = round(($right - $left + 1) / $moduleSize);
|
||||
$matrixHeight = round(($bottom - $top + 1) / $moduleSize);
|
||||
if ($matrixWidth <= 0 || $matrixHeight <= 0) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
if ($matrixHeight != $matrixWidth) {
|
||||
// Only possibly decode square regions
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
// Push in the "border" by half the module width so that we start
|
||||
// sampling in the middle of the module. Just in case the image is a
|
||||
// little off, this will help recover.
|
||||
$nudge = (int) ($moduleSize / 2.0);// $nudge = (int) ($moduleSize / 2.0f);
|
||||
$top += $nudge;
|
||||
$left += $nudge;
|
||||
|
||||
// But careful that this does not sample off the edge
|
||||
// "right" is the farthest-right valid pixel location -- right+1 is not necessarily
|
||||
// This is positive by how much the inner x loop below would be too large
|
||||
$nudgedTooFarRight = $left + (int) (($matrixWidth - 1) * $moduleSize) - $right;
|
||||
if ($nudgedTooFarRight > 0) {
|
||||
if ($nudgedTooFarRight > $nudge) {
|
||||
// Neither way fits; abort
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$left -= $nudgedTooFarRight;
|
||||
}
|
||||
// See logic above
|
||||
$nudgedTooFarDown = $top + (int) (($matrixHeight - 1) * $moduleSize) - $bottom;
|
||||
if ($nudgedTooFarDown > 0) {
|
||||
if ($nudgedTooFarDown > $nudge) {
|
||||
// Neither way fits; abort
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$top -= $nudgedTooFarDown;
|
||||
}
|
||||
|
||||
// Now just read off the bits
|
||||
$bits = new BitMatrix($matrixWidth, $matrixHeight);
|
||||
for ($y = 0; $y < $matrixHeight; $y++) {
|
||||
$iOffset = $top + (int) ($y * $moduleSize);
|
||||
for ($x = 0; $x < $matrixWidth; $x++) {
|
||||
if ($image->get($left + (int) ($x * $moduleSize), $iOffset)) {
|
||||
$bits->set($x, $y);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $bits;
|
||||
}
|
||||
|
||||
private static function moduleSize($leftTopBlack, $image) {
|
||||
$height = $image->getHeight();
|
||||
$width = $image->getWidth();
|
||||
$x = $leftTopBlack[0];
|
||||
$y = $leftTopBlack[1];
|
||||
$inBlack = true;
|
||||
$transitions = 0;
|
||||
while ($x < $width && $y < $height) {
|
||||
if ($inBlack != $image->get($x, $y)) {
|
||||
if (++$transitions == 5) {
|
||||
break;
|
||||
}
|
||||
$inBlack = !$inBlack;
|
||||
}
|
||||
$x++;
|
||||
$y++;
|
||||
}
|
||||
if ($x == $width || $y == $height) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
return ($x - $leftTopBlack[0]) / 7.0; //return ($x - $leftTopBlack[0]) / 7.0f;
|
||||
}
|
||||
|
||||
}
|
||||
250
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/BitMatrixParser.php
vendored
Normal file
250
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/BitMatrixParser.php
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
use Zxing\FormatException;
|
||||
use Zxing\Common\BitMatrix;
|
||||
|
||||
/**
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class BitMatrixParser {
|
||||
|
||||
private $bitMatrix;
|
||||
private $parsedVersion;
|
||||
private $parsedFormatInfo;
|
||||
private $mirror;
|
||||
|
||||
/**
|
||||
* @param bitMatrix {@link BitMatrix} to parse
|
||||
* @throws FormatException if dimension is not >= 21 and 1 mod 4
|
||||
*/
|
||||
function __construct($bitMatrix) {
|
||||
$dimension = $bitMatrix->getHeight();
|
||||
if ($dimension < 21 || ($dimension & 0x03) != 1) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$this->bitMatrix = $bitMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads format information from one of its two locations within the QR Code.</p>
|
||||
*
|
||||
* @return {@link FormatInformation} encapsulating the QR Code's format info
|
||||
* @throws FormatException if both format information locations cannot be parsed as
|
||||
* the valid encoding of format information
|
||||
*/
|
||||
function readFormatInformation() {
|
||||
|
||||
if ($this->parsedFormatInfo != null) {
|
||||
return $this->parsedFormatInfo;
|
||||
}
|
||||
|
||||
// Read top-left format info bits
|
||||
$formatInfoBits1 = 0;
|
||||
for ($i = 0; $i < 6; $i++) {
|
||||
$formatInfoBits1 = $this->copyBit($i, 8, $formatInfoBits1);
|
||||
}
|
||||
// .. and skip a bit in the timing pattern ...
|
||||
$formatInfoBits1 = $this->copyBit(7, 8, $formatInfoBits1);
|
||||
$formatInfoBits1 = $this->copyBit(8, 8, $formatInfoBits1);
|
||||
$formatInfoBits1 = $this->copyBit(8, 7, $formatInfoBits1);
|
||||
// .. and skip a bit in the timing pattern ...
|
||||
for ($j = 5; $j >= 0; $j--) {
|
||||
$formatInfoBits1 = $this->copyBit(8, $j, $formatInfoBits1);
|
||||
}
|
||||
|
||||
// Read the top-right/bottom-left pattern too
|
||||
$dimension = $this->bitMatrix->getHeight();
|
||||
$formatInfoBits2 = 0;
|
||||
$jMin = $dimension - 7;
|
||||
for ($j = $dimension - 1; $j >= $jMin; $j--) {
|
||||
$formatInfoBits2 = $this->copyBit(8, $j, $formatInfoBits2);
|
||||
}
|
||||
for ($i = $dimension - 8; $i < $dimension; $i++) {
|
||||
$formatInfoBits2 = $this->copyBit($i, 8, $formatInfoBits2);
|
||||
}
|
||||
|
||||
$parsedFormatInfo = FormatInformation::decodeFormatInformation($formatInfoBits1, $formatInfoBits2);
|
||||
if ($parsedFormatInfo != null) {
|
||||
return $parsedFormatInfo;
|
||||
}
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads version information from one of its two locations within the QR Code.</p>
|
||||
*
|
||||
* @return {@link Version} encapsulating the QR Code's version
|
||||
* @throws FormatException if both version information locations cannot be parsed as
|
||||
* the valid encoding of version information
|
||||
*/
|
||||
function readVersion(){
|
||||
|
||||
if ($this->parsedVersion != null) {
|
||||
return $this->parsedVersion;
|
||||
}
|
||||
|
||||
$dimension = $this->bitMatrix->getHeight();
|
||||
|
||||
$provisionalVersion = ($dimension - 17) / 4;
|
||||
if ($provisionalVersion <= 6) {
|
||||
return Version::getVersionForNumber($provisionalVersion);
|
||||
}
|
||||
|
||||
// Read top-right version info: 3 wide by 6 tall
|
||||
$versionBits = 0;
|
||||
$ijMin = $dimension - 11;
|
||||
for ($j = 5; $j >= 0; $j--) {
|
||||
for ($i = $dimension - 9; $i >= $ijMin; $i--) {
|
||||
$versionBits = $this->copyBit($i, $j, $versionBits);
|
||||
}
|
||||
}
|
||||
|
||||
$theParsedVersion = Version::decodeVersionInformation($versionBits);
|
||||
if ($theParsedVersion != null && $theParsedVersion->getDimensionForVersion() == $dimension) {
|
||||
$this->parsedVersion = $theParsedVersion;
|
||||
return $theParsedVersion;
|
||||
}
|
||||
|
||||
// Hmm, failed. Try bottom left: 6 wide by 3 tall
|
||||
$versionBits = 0;
|
||||
for ($i = 5; $i >= 0; $i--) {
|
||||
for ($j = $dimension - 9; $j >=$ijMin; $j--) {
|
||||
$versionBits = $this->copyBit($i, $j, $versionBits);
|
||||
}
|
||||
}
|
||||
|
||||
$theParsedVersion = Version::decodeVersionInformation($versionBits);
|
||||
if ($theParsedVersion != null && $theParsedVersion->getDimensionForVersion() == $dimension) {
|
||||
$this->parsedVersion = $theParsedVersion;
|
||||
return $theParsedVersion;
|
||||
}
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
private function copyBit($i, $j, $versionBits) {
|
||||
$bit = $this->mirror ? $this->bitMatrix->get($j, $i) : $this->bitMatrix->get($i, $j);
|
||||
return $bit ? ($versionBits << 1) | 0x1 : $versionBits << 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the
|
||||
* correct order in order to reconstruct the codewords bytes contained within the
|
||||
* QR Code.</p>
|
||||
*
|
||||
* @return bytes encoded within the QR Code
|
||||
* @throws FormatException if the exact number of bytes expected is not read
|
||||
*/
|
||||
function readCodewords(){
|
||||
|
||||
$formatInfo = $this->readFormatInformation();
|
||||
$version = $this->readVersion();
|
||||
|
||||
// Get the data mask for the format used in this QR Code. This will exclude
|
||||
// some bits from reading as we wind through the bit matrix.
|
||||
$dataMask = DataMask::forReference($formatInfo->getDataMask());
|
||||
$dimension = $this->bitMatrix->getHeight();
|
||||
$dataMask->unmaskBitMatrix($this->bitMatrix, $dimension);
|
||||
|
||||
$functionPattern = $version->buildFunctionPattern();
|
||||
|
||||
$readingUp = true;
|
||||
if($version->getTotalCodewords()) {
|
||||
$result = fill_array(0, $version->getTotalCodewords(), 0);
|
||||
}else{
|
||||
$result = array();
|
||||
}
|
||||
$resultOffset = 0;
|
||||
$currentByte = 0;
|
||||
$bitsRead = 0;
|
||||
// Read columns in pairs, from right to left
|
||||
for ($j = $dimension - 1; $j > 0; $j -= 2) {
|
||||
if ($j == 6) {
|
||||
// Skip whole column with vertical alignment pattern;
|
||||
// saves time and makes the other code proceed more cleanly
|
||||
$j--;
|
||||
}
|
||||
// Read alternatingly from bottom to top then top to bottom
|
||||
for ($count = 0; $count < $dimension; $count++) {
|
||||
$i = $readingUp ? $dimension - 1 - $count : $count;
|
||||
for ($col = 0; $col < 2; $col++) {
|
||||
// Ignore bits covered by the function pattern
|
||||
if (!$functionPattern->get($j - $col, $i)) {
|
||||
// Read a bit
|
||||
$bitsRead++;
|
||||
$currentByte <<= 1;
|
||||
if ($this->bitMatrix->get($j - $col, $i)) {
|
||||
$currentByte |= 1;
|
||||
}
|
||||
// If we've made a whole byte, save it off
|
||||
if ($bitsRead == 8) {
|
||||
$result[$resultOffset++] = $currentByte; //(byte)
|
||||
$bitsRead = 0;
|
||||
$currentByte = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$readingUp ^= true; // readingUp = !readingUp; // switch directions
|
||||
}
|
||||
if ($resultOffset != $version->getTotalCodewords()) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert the mask removal done while reading the code words. The bit matrix should revert to its original state.
|
||||
*/
|
||||
function remask() {
|
||||
if ($this->parsedFormatInfo == null) {
|
||||
return; // We have no format information, and have no data mask
|
||||
}
|
||||
$dataMask = DataMask::forReference($this->parsedFormatInfo->getDataMask());
|
||||
$dimension = $this->bitMatrix->getHeight();
|
||||
$dataMask->unmaskBitMatrix($this->bitMatrix, $dimension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the parser for a mirrored operation.
|
||||
* This flag has effect only on the {@link #readFormatInformation()} and the
|
||||
* {@link #readVersion()}. Before proceeding with {@link #readCodewords()} the
|
||||
* {@link #mirror()} method should be called.
|
||||
*
|
||||
* @param mirror Whether to read version and format information mirrored.
|
||||
*/
|
||||
function setMirror($mirror) {
|
||||
$parsedVersion = null;
|
||||
$parsedFormatInfo = null;
|
||||
$this->mirror = $mirror;
|
||||
}
|
||||
|
||||
/** Mirror the bit matrix in order to attempt a second reading. */
|
||||
function mirror() {
|
||||
for ($x = 0; $x < $this->bitMatrix->getWidth(); $x++) {
|
||||
for ($y = $x + 1; $y < $this->bitMatrix->getHeight(); $y++) {
|
||||
if ($this->bitMatrix->get($x, $y) != $this->bitMatrix->get($y, $x)) {
|
||||
$this->bitMatrix->flip($y, $x);
|
||||
$this->bitMatrix->flip($x, $y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
123
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataBlock.php
vendored
Normal file
123
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataBlock.php
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates a block of data within a QR Code. QR Codes may split their data into
|
||||
* multiple blocks, each of which is a unit of data and error-correction codewords. Each
|
||||
* is represented by an instance of this class.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class DataBlock {
|
||||
|
||||
private $numDataCodewords;
|
||||
private $codewords; //byte[]
|
||||
|
||||
private function __construct($numDataCodewords, $codewords) {
|
||||
$this->numDataCodewords = $numDataCodewords;
|
||||
$this->codewords = $codewords;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>When QR Codes use multiple data blocks, they are actually interleaved.
|
||||
* That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
|
||||
* method will separate the data into original blocks.</p>
|
||||
*
|
||||
* @param rawCodewords bytes as read directly from the QR Code
|
||||
* @param version version of the QR Code
|
||||
* @param ecLevel error-correction level of the QR Code
|
||||
* @return DataBlocks containing original bytes, "de-interleaved" from representation in the
|
||||
* QR Code
|
||||
*/
|
||||
static function getDataBlocks($rawCodewords,
|
||||
$version,
|
||||
$ecLevel) {
|
||||
|
||||
if (count($rawCodewords) != $version->getTotalCodewords()) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
|
||||
// Figure out the number and size of data blocks used by this version and
|
||||
// error correction level
|
||||
$ecBlocks = $version->getECBlocksForLevel($ecLevel);
|
||||
|
||||
// First count the total number of data blocks
|
||||
$totalBlocks = 0;
|
||||
$ecBlockArray = $ecBlocks->getECBlocks();
|
||||
foreach ($ecBlockArray as $ecBlock) {
|
||||
$totalBlocks += $ecBlock->getCount();
|
||||
}
|
||||
|
||||
// Now establish DataBlocks of the appropriate size and number of data codewords
|
||||
$result = array();//new DataBlock[$totalBlocks];
|
||||
$numResultBlocks = 0;
|
||||
foreach ($ecBlockArray as $ecBlock) {
|
||||
for ($i = 0; $i < $ecBlock->getCount(); $i++) {
|
||||
$numDataCodewords = $ecBlock->getDataCodewords();
|
||||
$numBlockCodewords = $ecBlocks->getECCodewordsPerBlock() + $numDataCodewords;
|
||||
$result[$numResultBlocks++] = new DataBlock($numDataCodewords, fill_array(0,$numBlockCodewords,0));
|
||||
}
|
||||
}
|
||||
|
||||
// All blocks have the same amount of data, except that the last n
|
||||
// (where n may be 0) have 1 more byte. Figure out where these start.
|
||||
$shorterBlocksTotalCodewords = count($result[0]->codewords);
|
||||
$longerBlocksStartAt = count($result) - 1;
|
||||
while ($longerBlocksStartAt >= 0) {
|
||||
$numCodewords = count($result[$longerBlocksStartAt]->codewords);
|
||||
if ($numCodewords == $shorterBlocksTotalCodewords) {
|
||||
break;
|
||||
}
|
||||
$longerBlocksStartAt--;
|
||||
}
|
||||
$longerBlocksStartAt++;
|
||||
|
||||
$shorterBlocksNumDataCodewords = $shorterBlocksTotalCodewords - $ecBlocks->getECCodewordsPerBlock();
|
||||
// The last elements of result may be 1 element longer;
|
||||
// first fill out as many elements as all of them have
|
||||
$rawCodewordsOffset = 0;
|
||||
for ($i = 0; $i < $shorterBlocksNumDataCodewords; $i++) {
|
||||
for ($j = 0; $j < $numResultBlocks; $j++) {
|
||||
$result[$j]->codewords[$i] = $rawCodewords[$rawCodewordsOffset++];
|
||||
}
|
||||
}
|
||||
// Fill out the last data block in the longer ones
|
||||
for ($j = $longerBlocksStartAt; $j < $numResultBlocks; $j++) {
|
||||
$result[$j]->codewords[$shorterBlocksNumDataCodewords] = $rawCodewords[$rawCodewordsOffset++];
|
||||
}
|
||||
// Now add in error correction blocks
|
||||
$max = count($result[0]->codewords);
|
||||
for ($i = $shorterBlocksNumDataCodewords; $i < $max; $i++) {
|
||||
for ($j = 0; $j < $numResultBlocks; $j++) {
|
||||
$iOffset = $j < $longerBlocksStartAt ? $i : $i + 1;
|
||||
$result[$j]->codewords[$iOffset] = $rawCodewords[$rawCodewordsOffset++];
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
function getNumDataCodewords() {
|
||||
return $this->numDataCodewords;
|
||||
}
|
||||
|
||||
function getCodewords() {
|
||||
return $this->codewords;
|
||||
}
|
||||
|
||||
}
|
||||
175
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php
vendored
Normal file
175
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DataMask.php
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
use Zxing\Common\BitMatrix;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations
|
||||
* of this class can un-mask a raw BitMatrix. For simplicity, they will unmask the entire BitMatrix,
|
||||
* including areas used for finder patterns, timing patterns, etc. These areas should be unused
|
||||
* after the point they are unmasked anyway.</p>
|
||||
*
|
||||
* <p>Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position
|
||||
* and j is row position. In fact, as the text says, i is row position and j is column position.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
abstract class DataMask
|
||||
{
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006 6.8.1
|
||||
*/
|
||||
private static $DATA_MASKS = array();
|
||||
|
||||
static function Init()
|
||||
{
|
||||
self::$DATA_MASKS = array(
|
||||
new DataMask000(),
|
||||
new DataMask001(),
|
||||
new DataMask010(),
|
||||
new DataMask011(),
|
||||
new DataMask100(),
|
||||
new DataMask101(),
|
||||
new DataMask110(),
|
||||
new DataMask111(),
|
||||
);
|
||||
}
|
||||
|
||||
function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Implementations of this method reverse the data masking process applied to a QR Code and
|
||||
* make its bits ready to read.</p>
|
||||
*
|
||||
* @param bits representation of QR Code bits
|
||||
* @param dimension dimension of QR Code, represented by bits, being unmasked
|
||||
*/
|
||||
final function unmaskBitMatrix($bits, $dimension)
|
||||
{
|
||||
for ($i = 0; $i < $dimension; $i++) {
|
||||
for ($j = 0; $j < $dimension; $j++) {
|
||||
if ($this->isMasked($i, $j)) {
|
||||
$bits->flip($j, $i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract function isMasked($i, $j);
|
||||
|
||||
/**
|
||||
* @param reference a value between 0 and 7 indicating one of the eight possible
|
||||
* data mask patterns a QR Code may use
|
||||
* @return DataMask encapsulating the data mask pattern
|
||||
*/
|
||||
static function forReference($reference)
|
||||
{
|
||||
if ($reference < 0 || $reference > 7) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
return self::$DATA_MASKS[$reference];
|
||||
}
|
||||
}
|
||||
DataMask::Init();
|
||||
/**
|
||||
* 000: mask bits for which (x + y) mod 2 == 0
|
||||
*/
|
||||
final class DataMask000 extends DataMask {
|
||||
// @Override
|
||||
function isMasked($i, $j) {
|
||||
return (($i + $j) & 0x01) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 001: mask bits for which x mod 2 == 0
|
||||
*/
|
||||
final class DataMask001 extends DataMask {
|
||||
//@Override
|
||||
function isMasked($i, $j) {
|
||||
return ($i & 0x01) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 010: mask bits for which y mod 3 == 0
|
||||
*/
|
||||
final class DataMask010 extends DataMask {
|
||||
//@Override
|
||||
function isMasked($i, $j) {
|
||||
return $j % 3 == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 011: mask bits for which (x + y) mod 3 == 0
|
||||
*/
|
||||
final class DataMask011 extends DataMask {
|
||||
//@Override
|
||||
function isMasked($i, $j) {
|
||||
return ($i + $j) % 3 == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 100: mask bits for which (x/2 + y/3) mod 2 == 0
|
||||
*/
|
||||
final class DataMask100 extends DataMask {
|
||||
//@Override
|
||||
function isMasked($i, $j) {
|
||||
return intval((intval($i / 2) + intval($j /3)) & 0x01) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 101: mask bits for which xy mod 2 + xy mod 3 == 0
|
||||
*/
|
||||
final class DataMask101 extends DataMask {
|
||||
//@Override
|
||||
function isMasked($i, $j) {
|
||||
$temp = $i * $j;
|
||||
return ($temp & 0x01) + ($temp % 3) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0
|
||||
*/
|
||||
final class DataMask110 extends DataMask {
|
||||
//@Override
|
||||
function isMasked($i, $j) {
|
||||
$temp = $i * $j;
|
||||
return ((($temp & 0x01) + ($temp % 3)) & 0x01) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0
|
||||
*/
|
||||
final class DataMask111 extends DataMask {
|
||||
//@Override
|
||||
function isMasked($i, $j) {
|
||||
return (((($i + $j) & 0x01) + (($i * $j) % 3)) & 0x01) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
359
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DecodedBitStreamParser.php
vendored
Normal file
359
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/DecodedBitStreamParser.php
vendored
Normal file
@@ -0,0 +1,359 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
use Zxing\DecodeHintType;
|
||||
use Zxing\FormatException;
|
||||
use Zxing\Common\BitSource;
|
||||
use Zxing\Common\CharacterSetECI;
|
||||
use Zxing\Common\DecoderResult;
|
||||
use Zxing\Common\StringUtils;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>QR Codes can encode text as bits in one of several modes, and can use multiple modes
|
||||
* in one QR Code. This class decodes the bits back into text.</p>
|
||||
*
|
||||
* <p>See ISO 18004:2006, 6.4.3 - 6.4.7</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class DecodedBitStreamParser {
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006, 6.4.4 Table 5
|
||||
*/
|
||||
private static $ALPHANUMERIC_CHARS = array(
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
|
||||
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
||||
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
' ', '$', '%', '*', '+', '-', '.', '/', ':'
|
||||
);
|
||||
private static $GB2312_SUBSET = 1;
|
||||
|
||||
|
||||
private function DecodedBitStreamParser() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
static function decode($bytes,
|
||||
$version,
|
||||
$ecLevel,
|
||||
$hints) {
|
||||
$bits = new BitSource($bytes);
|
||||
$result = '';//new StringBuilder(50);
|
||||
$byteSegments = array();
|
||||
$symbolSequence = -1;
|
||||
$parityData = -1;
|
||||
|
||||
try {
|
||||
$currentCharacterSetECI = null;
|
||||
$fc1InEffect = false;
|
||||
$mode='';
|
||||
do {
|
||||
// While still another segment to read...
|
||||
if ($bits->available() < 4) {
|
||||
// OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
|
||||
$mode = Mode::$TERMINATOR;
|
||||
} else {
|
||||
$mode = Mode::forBits($bits->readBits(4)); // mode is encoded by 4 bits
|
||||
}
|
||||
if ($mode != Mode::$TERMINATOR) {
|
||||
if ($mode == Mode::$FNC1_FIRST_POSITION || $mode == Mode::$FNC1_SECOND_POSITION) {
|
||||
// We do little with FNC1 except alter the parsed result a bit according to the spec
|
||||
$fc1InEffect = true;
|
||||
} else if ($mode == Mode::$STRUCTURED_APPEND) {
|
||||
if ($bits->available() < 16) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
// sequence number and parity is added later to the result metadata
|
||||
// Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
|
||||
$symbolSequence = $bits->readBits(8);
|
||||
$parityData = $bits->readBits(8);
|
||||
} else if ($mode == Mode::$ECI) {
|
||||
// Count doesn't apply to ECI
|
||||
$value = self::parseECIValue($bits);
|
||||
$currentCharacterSetECI = CharacterSetECI::getCharacterSetECIByValue($value);
|
||||
if ($currentCharacterSetECI == null) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
} else {
|
||||
// First handle Hanzi mode which does not start with character count
|
||||
if ($mode == Mode::$HANZI) {
|
||||
//chinese mode contains a sub set indicator right after mode indicator
|
||||
$subset = $bits->readBits(4);
|
||||
$countHanzi = $bits->readBits($mode->getCharacterCountBits($version));
|
||||
if ($subset == self::$GB2312_SUBSET) {
|
||||
self::decodeHanziSegment($bits, $result, $countHanzi);
|
||||
}
|
||||
} else {
|
||||
// "Normal" QR code modes:
|
||||
// How many characters will follow, encoded in this mode?
|
||||
$count = $bits->readBits($mode->getCharacterCountBits($version));
|
||||
if ($mode == Mode::$NUMERIC) {
|
||||
self::decodeNumericSegment($bits, $result, $count);
|
||||
} else if ($mode == Mode::$ALPHANUMERIC) {
|
||||
self::decodeAlphanumericSegment($bits, $result, $count, $fc1InEffect);
|
||||
} else if ($mode == Mode::$BYTE) {
|
||||
self::decodeByteSegment($bits, $result, $count, $currentCharacterSetECI, $byteSegments, $hints);
|
||||
} else if ($mode == Mode::$KANJI) {
|
||||
self::decodeKanjiSegment($bits, $result, $count);
|
||||
} else {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while ($mode != Mode::$TERMINATOR);
|
||||
} catch (IllegalArgumentException $iae) {
|
||||
// from readBits() calls
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
return new DecoderResult($bytes,
|
||||
$result,
|
||||
empty($byteSegments) ? null : $byteSegments,
|
||||
$ecLevel == null ? null : 'L',//ErrorCorrectionLevel::toString($ecLevel),
|
||||
$symbolSequence,
|
||||
$parityData);
|
||||
}
|
||||
|
||||
/**
|
||||
* See specification GBT 18284-2000
|
||||
*/
|
||||
private static function decodeHanziSegment($bits,
|
||||
&$result,
|
||||
$count) {
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if ($count * 13 > $bits->available()) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||
// and decode as GB2312 afterwards
|
||||
$buffer = fill_array(0,2 * $count,0);
|
||||
$offset = 0;
|
||||
while ($count > 0) {
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
$twoBytes = $bits->readBits(13);
|
||||
$assembledTwoBytes = (($twoBytes / 0x060) << 8) | ($twoBytes % 0x060);
|
||||
if ($assembledTwoBytes < 0x003BF) {
|
||||
// In the 0xA1A1 to 0xAAFE range
|
||||
$assembledTwoBytes += 0x0A1A1;
|
||||
} else {
|
||||
// In the 0xB0A1 to 0xFAFE range
|
||||
$assembledTwoBytes += 0x0A6A1;
|
||||
}
|
||||
$buffer[$offset] = (($assembledTwoBytes >> 8) & 0xFF);//(byte)
|
||||
$buffer[$offset + 1] = ($assembledTwoBytes & 0xFF);//(byte)
|
||||
$offset += 2;
|
||||
$count--;
|
||||
}
|
||||
|
||||
try {
|
||||
$result .= iconv('GB2312', 'UTF-8', implode($buffer));
|
||||
} catch (UnsupportedEncodingException $ignored) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
}
|
||||
|
||||
private static function decodeKanjiSegment($bits,
|
||||
&$result,
|
||||
$count) {
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if ($count * 13 > $bits->available()) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||
// and decode as Shift_JIS afterwards
|
||||
$buffer = array(0,2 * $count,0);
|
||||
$offset = 0;
|
||||
while ($count > 0) {
|
||||
// Each 13 bits encodes a 2-byte character
|
||||
$twoBytes = $bits->readBits(13);
|
||||
$assembledTwoBytes = (($twoBytes / 0x0C0) << 8) | ($twoBytes % 0x0C0);
|
||||
if ($assembledTwoBytes < 0x01F00) {
|
||||
// In the 0x8140 to 0x9FFC range
|
||||
$assembledTwoBytes += 0x08140;
|
||||
} else {
|
||||
// In the 0xE040 to 0xEBBF range
|
||||
$assembledTwoBytes += 0x0C140;
|
||||
}
|
||||
$buffer[$offset] = ($assembledTwoBytes >> 8);//(byte)
|
||||
$buffer[$offset + 1] = $assembledTwoBytes; //(byte)
|
||||
$offset += 2;
|
||||
$count--;
|
||||
}
|
||||
// Shift_JIS may not be supported in some environments:
|
||||
try {
|
||||
$result .= iconv('shift-jis','utf-8',implode($buffer));
|
||||
|
||||
|
||||
} catch (UnsupportedEncodingException $ignored) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
}
|
||||
|
||||
private static function decodeByteSegment($bits,
|
||||
&$result,
|
||||
$count,
|
||||
$currentCharacterSetECI,
|
||||
&$byteSegments,
|
||||
$hints) {
|
||||
// Don't crash trying to read more bits than we have available.
|
||||
if (8 * $count > $bits->available()) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
$readBytes = fill_array(0,$count,0);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$readBytes[$i] = $bits->readBits(8);//(byte)
|
||||
}
|
||||
$text = implode(array_map('chr',$readBytes));
|
||||
$encoding = '';
|
||||
if ($currentCharacterSetECI == null) {
|
||||
// The spec isn't clear on this mode; see
|
||||
// section 6.4.5: t does not say which encoding to assuming
|
||||
// upon decoding. I have seen ISO-8859-1 used as well as
|
||||
// Shift_JIS -- without anything like an ECI designator to
|
||||
// give a hint.
|
||||
|
||||
$encoding = mb_detect_encoding($text, $hints);
|
||||
} else {
|
||||
$encoding = $currentCharacterSetECI->name();
|
||||
}
|
||||
try {
|
||||
// $result.= mb_convert_encoding($text ,$encoding);//(new String(readBytes, encoding));
|
||||
$result.= $text;//(new String(readBytes, encoding));
|
||||
} catch (UnsupportedEncodingException $ignored) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$byteSegments = array_merge($byteSegments, $readBytes);
|
||||
}
|
||||
|
||||
private static function toAlphaNumericChar($value) {
|
||||
if ($value >= count(self::$ALPHANUMERIC_CHARS)) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
return self::$ALPHANUMERIC_CHARS[$value];
|
||||
}
|
||||
|
||||
private static function decodeAlphanumericSegment($bits,
|
||||
&$result,
|
||||
$count,
|
||||
$fc1InEffect) {
|
||||
// Read two characters at a time
|
||||
$start = strlen($result);
|
||||
while ($count > 1) {
|
||||
if ($bits->available() < 11) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$nextTwoCharsBits = $bits->readBits(11);
|
||||
$result.=(self::toAlphaNumericChar($nextTwoCharsBits / 45));
|
||||
$result.=(self::toAlphaNumericChar($nextTwoCharsBits % 45));
|
||||
$count -= 2;
|
||||
}
|
||||
if ($count == 1) {
|
||||
// special case: one character left
|
||||
if ($bits->available() < 6) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$result.=self::toAlphaNumericChar($bits->readBits(6));
|
||||
}
|
||||
// See section 6.4.8.1, 6.4.8.2
|
||||
if ($fc1InEffect) {
|
||||
// We need to massage the result a bit if in an FNC1 mode:
|
||||
for ($i = $start; $i < strlen($result); $i++) {
|
||||
if ($result{$i} == '%') {
|
||||
if ($i < strlen($result) - 1 && $result{$i + 1} == '%') {
|
||||
// %% is rendered as %
|
||||
$result = substr_replace($result,'',$i + 1,1);//deleteCharAt(i + 1);
|
||||
} else {
|
||||
// In alpha mode, % should be converted to FNC1 separator 0x1D
|
||||
$result.setCharAt($i, chr(0x1D));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function decodeNumericSegment($bits,
|
||||
&$result,
|
||||
$count) {
|
||||
// Read three digits at a time
|
||||
while ($count >= 3) {
|
||||
// Each 10 bits encodes three digits
|
||||
if ($bits->available() < 10) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$threeDigitsBits = $bits->readBits(10);
|
||||
if ($threeDigitsBits >= 1000) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$result.=(self::toAlphaNumericChar($threeDigitsBits / 100));
|
||||
$result.=(self::toAlphaNumericChar(($threeDigitsBits / 10) % 10));
|
||||
$result.=(self::toAlphaNumericChar($threeDigitsBits % 10));
|
||||
$count -= 3;
|
||||
}
|
||||
if ($count == 2) {
|
||||
// Two digits left over to read, encoded in 7 bits
|
||||
if ($bits->available() < 7) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$twoDigitsBits = $bits->readBits(7);
|
||||
if ($twoDigitsBits >= 100) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$result.=(self::toAlphaNumericChar($twoDigitsBits / 10));
|
||||
$result.=(self::toAlphaNumericChar($twoDigitsBits % 10));
|
||||
} else if ($count == 1) {
|
||||
// One digit left over to read
|
||||
if ($bits->available() < 4) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$digitBits = $bits->readBits(4);
|
||||
if ($digitBits >= 10) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
$result.=(self::toAlphaNumericChar($digitBits));
|
||||
}
|
||||
}
|
||||
|
||||
private static function parseECIValue($bits) {
|
||||
$firstByte = $bits->readBits(8);
|
||||
if (($firstByte & 0x80) == 0) {
|
||||
// just one byte
|
||||
return $firstByte & 0x7F;
|
||||
}
|
||||
if (($firstByte & 0xC0) == 0x80) {
|
||||
// two bytes
|
||||
$secondByte = $bits->readBits(8);
|
||||
return (($firstByte & 0x3F) << 8) | $secondByte;
|
||||
}
|
||||
if (($firstByte & 0xE0) == 0xC0) {
|
||||
// three bytes
|
||||
$secondThirdBytes = $bits->readBits(16);
|
||||
return (($firstByte & 0x1F) << 16) | $secondThirdBytes;
|
||||
}
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
|
||||
}
|
||||
214
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/Decoder.php
vendored
Normal file
214
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/Decoder.php
vendored
Normal file
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
use Zxing\ChecksumException;
|
||||
use Zxing\DecodeHintType;
|
||||
use Zxing\FormatException;
|
||||
use Zxing\Common\BitMatrix;
|
||||
use Zxing\Common\DecoderResult;
|
||||
use Zxing\Common\Reedsolomon\GenericGF;
|
||||
use Zxing\Common\Reedsolomon\ReedSolomonDecoder;
|
||||
use Zxing\Common\Reedsolomon\ReedSolomonException;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>The main class which implements QR Code decoding -- as opposed to locating and extracting
|
||||
* the QR Code from an image.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class Decoder {
|
||||
|
||||
private $rsDecoder;
|
||||
|
||||
public function __construct() {
|
||||
$this->rsDecoder = new ReedSolomonDecoder(GenericGF::$QR_CODE_FIELD_256);
|
||||
}
|
||||
|
||||
|
||||
function decode($variable, $hints=null){
|
||||
if(is_array($variable)){
|
||||
return $this->decodeImage($variable,$hints);
|
||||
}elseif(is_object($variable)&&$variable instanceof BitMatrix){
|
||||
return $this->decodeBits($variable,$hints);
|
||||
}elseif(is_object($variable)&&$variable instanceof BitMatrixParser){
|
||||
return $this->decodeParser($variable,$hints);
|
||||
}else{
|
||||
die('decode error Decoder.php');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.
|
||||
* "true" is taken to mean a black module.</p>
|
||||
*
|
||||
* @param image booleans representing white/black QR Code modules
|
||||
* @param hints decoding hints that should be used to influence decoding
|
||||
* @return text and bytes encoded within the QR Code
|
||||
* @throws FormatException if the QR Code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
public function decodeImage($image, $hints=null)
|
||||
{
|
||||
$dimension = count($image);
|
||||
$bits = new BitMatrix($dimension);
|
||||
for ($i = 0; $i < $dimension; $i++) {
|
||||
for ($j = 0; $j < $dimension; $j++) {
|
||||
if ($image[$i][$j]) {
|
||||
$bits->set($j, $i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->decode($bits, $hints);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>
|
||||
*
|
||||
* @param bits booleans representing white/black QR Code modules
|
||||
* @param hints decoding hints that should be used to influence decoding
|
||||
* @return text and bytes encoded within the QR Code
|
||||
* @throws FormatException if the QR Code cannot be decoded
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
public function decodeBits($bits, $hints=null)
|
||||
{
|
||||
|
||||
// Construct a parser and read version, error-correction level
|
||||
$parser = new BitMatrixParser($bits);
|
||||
$fe = null;
|
||||
$ce = null;
|
||||
try {
|
||||
return $this->decode($parser, $hints);
|
||||
} catch (FormatException $e) {
|
||||
$fe = $e;
|
||||
} catch (ChecksumException $e) {
|
||||
$ce = $e;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
// Revert the bit matrix
|
||||
$parser->remask();
|
||||
|
||||
// Will be attempting a mirrored reading of the version and format info.
|
||||
$parser->setMirror(true);
|
||||
|
||||
// Preemptively read the version.
|
||||
$parser->readVersion();
|
||||
|
||||
// Preemptively read the format information.
|
||||
$parser->readFormatInformation();
|
||||
|
||||
/*
|
||||
* Since we're here, this means we have successfully detected some kind
|
||||
* of version and format information when mirrored. This is a good sign,
|
||||
* that the QR code may be mirrored, and we should try once more with a
|
||||
* mirrored content.
|
||||
*/
|
||||
// Prepare for a mirrored reading.
|
||||
$parser->mirror();
|
||||
|
||||
$result = $this->decode($parser, $hints);
|
||||
|
||||
// Success! Notify the caller that the code was mirrored.
|
||||
$result->setOther(new QRCodeDecoderMetaData(true));
|
||||
|
||||
return $result;
|
||||
|
||||
} catch (FormatException $e ) {// catch (FormatException | ChecksumException e) {
|
||||
// Throw the exception from the original reading
|
||||
if ($fe != null) {
|
||||
throw $fe;
|
||||
}
|
||||
if ($ce != null) {
|
||||
throw $ce;
|
||||
}
|
||||
throw $e;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private function decodeParser($parser,$hints=null)
|
||||
{
|
||||
$version = $parser->readVersion();
|
||||
$ecLevel = $parser->readFormatInformation()->getErrorCorrectionLevel();
|
||||
|
||||
// Read codewords
|
||||
$codewords = $parser->readCodewords();
|
||||
// Separate into data blocks
|
||||
$dataBlocks = DataBlock::getDataBlocks($codewords, $version, $ecLevel);
|
||||
|
||||
// Count total number of data bytes
|
||||
$totalBytes = 0;
|
||||
foreach ($dataBlocks as $dataBlock) {
|
||||
$totalBytes += $dataBlock->getNumDataCodewords();
|
||||
}
|
||||
$resultBytes = fill_array(0,$totalBytes,0);
|
||||
$resultOffset = 0;
|
||||
|
||||
// Error-correct and copy data blocks together into a stream of bytes
|
||||
foreach ($dataBlocks as $dataBlock) {
|
||||
$codewordBytes = $dataBlock->getCodewords();
|
||||
$numDataCodewords = $dataBlock->getNumDataCodewords();
|
||||
$this->correctErrors($codewordBytes, $numDataCodewords);
|
||||
for ($i = 0; $i < $numDataCodewords; $i++) {
|
||||
$resultBytes[$resultOffset++] = $codewordBytes[$i];
|
||||
}
|
||||
}
|
||||
|
||||
// Decode the contents of that stream of bytes
|
||||
return DecodedBitStreamParser::decode($resultBytes, $version, $ecLevel, $hints);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
|
||||
* correct the errors in-place using Reed-Solomon error correction.</p>
|
||||
*
|
||||
* @param codewordBytes data and error correction codewords
|
||||
* @param numDataCodewords number of codewords that are data bytes
|
||||
* @throws ChecksumException if error correction fails
|
||||
*/
|
||||
private function correctErrors(&$codewordBytes, $numDataCodewords){
|
||||
$numCodewords = count($codewordBytes);
|
||||
// First read into an array of ints
|
||||
$codewordsInts =fill_array(0,$numCodewords,0);
|
||||
for ($i = 0; $i < $numCodewords; $i++) {
|
||||
$codewordsInts[$i] = $codewordBytes[$i] & 0xFF;
|
||||
}
|
||||
$numECCodewords = count($codewordBytes)- $numDataCodewords;
|
||||
try {
|
||||
$this->rsDecoder->decode($codewordsInts, $numECCodewords);
|
||||
} catch (ReedSolomonException $ignored) {
|
||||
throw ChecksumException::getChecksumInstance();
|
||||
}
|
||||
// Copy back into array of bytes -- only need to worry about the bytes that were data
|
||||
// We don't care about errors in the error-correction codewords
|
||||
for ($i = 0; $i < $numDataCodewords; $i++) {
|
||||
$codewordBytes[$i] = $codewordsInts[$i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
86
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/ErrorCorrectionLevel.php
vendored
Normal file
86
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/ErrorCorrectionLevel.php
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
/**
|
||||
* <p>See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels
|
||||
* defined by the QR code standard.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
class ErrorCorrectionLevel {
|
||||
|
||||
|
||||
private static $FOR_BITS;
|
||||
|
||||
|
||||
private $bits;
|
||||
private $ordinal;
|
||||
|
||||
function __construct($bits,$ordinal=0) {
|
||||
$this->bits = $bits;
|
||||
$this->ordinal = $ordinal;
|
||||
}
|
||||
|
||||
public static function Init(){
|
||||
self::$FOR_BITS = array(
|
||||
|
||||
|
||||
new ErrorCorrectionLevel(0x00,1), //M
|
||||
new ErrorCorrectionLevel(0x01,0), //L
|
||||
new ErrorCorrectionLevel(0x02,3), //H
|
||||
new ErrorCorrectionLevel(0x03,2), //Q
|
||||
|
||||
);
|
||||
}
|
||||
/** L = ~7% correction */
|
||||
// self::$L = new ErrorCorrectionLevel(0x01);
|
||||
/** M = ~15% correction */
|
||||
//self::$M = new ErrorCorrectionLevel(0x00);
|
||||
/** Q = ~25% correction */
|
||||
//self::$Q = new ErrorCorrectionLevel(0x03);
|
||||
/** H = ~30% correction */
|
||||
//self::$H = new ErrorCorrectionLevel(0x02);
|
||||
|
||||
|
||||
public function getBits() {
|
||||
return $this->bits;
|
||||
}
|
||||
public function toString() {
|
||||
return $this->bits;
|
||||
}
|
||||
public function getOrdinal() {
|
||||
return $this->ordinal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bits int containing the two bits encoding a QR Code's error correction level
|
||||
* @return ErrorCorrectionLevel representing the encoded error correction level
|
||||
*/
|
||||
public static function forBits($bits) {
|
||||
if ($bits < 0 || $bits >= count(self::$FOR_BITS)) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
$level = self::$FOR_BITS[$bits];
|
||||
// $lev = self::$$bit;
|
||||
return $level;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
ErrorCorrectionLevel::Init();
|
||||
179
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/FormatInformation.php
vendored
Normal file
179
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/FormatInformation.php
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates a QR Code's format information, including the data mask used and
|
||||
* error correction level.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @see DataMask
|
||||
* @see ErrorCorrectionLevel
|
||||
*/
|
||||
final class FormatInformation {
|
||||
|
||||
public static $FORMAT_INFO_MASK_QR;
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006, Annex C, Table C.1
|
||||
*/
|
||||
public static $FORMAT_INFO_DECODE_LOOKUP;
|
||||
/**
|
||||
* Offset i holds the number of 1 bits in the binary representation of i
|
||||
*/
|
||||
private static $BITS_SET_IN_HALF_BYTE;
|
||||
|
||||
private $errorCorrectionLevel;
|
||||
private $dataMask;
|
||||
|
||||
public static function Init(){
|
||||
|
||||
self::$FORMAT_INFO_MASK_QR= 0x5412;
|
||||
self::$BITS_SET_IN_HALF_BYTE = array(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);
|
||||
self::$FORMAT_INFO_DECODE_LOOKUP = array(
|
||||
array(0x5412, 0x00),
|
||||
array (0x5125, 0x01),
|
||||
array(0x5E7C, 0x02),
|
||||
array(0x5B4B, 0x03),
|
||||
array(0x45F9, 0x04),
|
||||
array(0x40CE, 0x05),
|
||||
array(0x4F97, 0x06),
|
||||
array(0x4AA0, 0x07),
|
||||
array(0x77C4, 0x08),
|
||||
array(0x72F3, 0x09),
|
||||
array(0x7DAA, 0x0A),
|
||||
array(0x789D, 0x0B),
|
||||
array(0x662F, 0x0C),
|
||||
array(0x6318, 0x0D),
|
||||
array(0x6C41, 0x0E),
|
||||
array(0x6976, 0x0F),
|
||||
array(0x1689, 0x10),
|
||||
array(0x13BE, 0x11),
|
||||
array(0x1CE7, 0x12),
|
||||
array(0x19D0, 0x13),
|
||||
array(0x0762, 0x14),
|
||||
array(0x0255, 0x15),
|
||||
array(0x0D0C, 0x16),
|
||||
array(0x083B, 0x17),
|
||||
array(0x355F, 0x18),
|
||||
array(0x3068, 0x19),
|
||||
array(0x3F31, 0x1A),
|
||||
array(0x3A06, 0x1B),
|
||||
array(0x24B4, 0x1C),
|
||||
array(0x2183, 0x1D),
|
||||
array(0x2EDA, 0x1E),
|
||||
array(0x2BED, 0x1F),
|
||||
);
|
||||
|
||||
}
|
||||
private function __construct($formatInfo) {
|
||||
// Bits 3,4
|
||||
$this->errorCorrectionLevel = ErrorCorrectionLevel::forBits(($formatInfo >> 3) & 0x03);
|
||||
// Bottom 3 bits
|
||||
$this->dataMask = ($formatInfo & 0x07);//(byte)
|
||||
}
|
||||
|
||||
static function numBitsDiffering($a, $b) {
|
||||
$a ^= $b; // a now has a 1 bit exactly where its bit differs with b's
|
||||
// Count bits set quickly with a series of lookups:
|
||||
return self::$BITS_SET_IN_HALF_BYTE[$a & 0x0F] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[intval(uRShift($a, 4) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a ,8) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a , 12) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 16) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a , 20) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 24) & 0x0F)] +
|
||||
self::$BITS_SET_IN_HALF_BYTE[(uRShift($a ,28) & 0x0F)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maskedFormatInfo1; format info indicator, with mask still applied
|
||||
* @param maskedFormatInfo2; second copy of same info; both are checked at the same time
|
||||
* to establish best match
|
||||
* @return information about the format it specifies, or {@code null}
|
||||
* if doesn't seem to match any known pattern
|
||||
*/
|
||||
static function decodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2) {
|
||||
$formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2);
|
||||
if ($formatInfo != null) {
|
||||
return $formatInfo;
|
||||
}
|
||||
// Should return null, but, some QR codes apparently
|
||||
// do not mask this info. Try again by actually masking the pattern
|
||||
// first
|
||||
return self::doDecodeFormatInformation($maskedFormatInfo1 ^ self::$FORMAT_INFO_MASK_QR,
|
||||
$maskedFormatInfo2 ^ self::$FORMAT_INFO_MASK_QR);
|
||||
}
|
||||
|
||||
private static function doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2) {
|
||||
// Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
|
||||
$bestDifference = PHP_INT_MAX;
|
||||
$bestFormatInfo = 0;
|
||||
foreach (self::$FORMAT_INFO_DECODE_LOOKUP as $decodeInfo ) {
|
||||
$targetInfo = $decodeInfo[0];
|
||||
if ($targetInfo == $maskedFormatInfo1 || $targetInfo == $maskedFormatInfo2) {
|
||||
// Found an exact match
|
||||
return new FormatInformation($decodeInfo[1]);
|
||||
}
|
||||
$bitsDifference = self::numBitsDiffering($maskedFormatInfo1, $targetInfo);
|
||||
if ($bitsDifference < $bestDifference) {
|
||||
$bestFormatInfo = $decodeInfo[1];
|
||||
$bestDifference = $bitsDifference;
|
||||
}
|
||||
if ($maskedFormatInfo1 != $maskedFormatInfo2) {
|
||||
// also try the other option
|
||||
$bitsDifference = self::numBitsDiffering($maskedFormatInfo2, $targetInfo);
|
||||
if ($bitsDifference < $bestDifference) {
|
||||
$bestFormatInfo = $decodeInfo[1];
|
||||
$bestDifference = $bitsDifference;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits
|
||||
// differing means we found a match
|
||||
if ($bestDifference <= 3) {
|
||||
return new FormatInformation($bestFormatInfo);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getErrorCorrectionLevel() {
|
||||
return $this->errorCorrectionLevel;
|
||||
}
|
||||
|
||||
function getDataMask() {
|
||||
return $this->dataMask;
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function hashCode() {
|
||||
return ($this->errorCorrectionLevel->ordinal() << 3) | intval($this->dataMask);
|
||||
}
|
||||
|
||||
//@Override
|
||||
public function equals($o) {
|
||||
if (!($o instanceof FormatInformation)) {
|
||||
return false;
|
||||
}
|
||||
$other =$o;
|
||||
return $this->errorCorrectionLevel == $other->errorCorrectionLevel &&
|
||||
$this->dataMask == $other->dataMask;
|
||||
}
|
||||
|
||||
}
|
||||
FormatInformation::Init();
|
||||
118
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/Mode.php
vendored
Normal file
118
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/Mode.php
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
/**
|
||||
* <p>See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which
|
||||
* data can be encoded to bits in the QR code standard.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
class Mode {
|
||||
static $TERMINATOR;
|
||||
static $NUMERIC;
|
||||
static $ALPHANUMERIC;
|
||||
static $STRUCTURED_APPEND;
|
||||
static $BYTE;
|
||||
static $ECI;
|
||||
static $KANJI;
|
||||
static $FNC1_FIRST_POSITION;
|
||||
static $FNC1_SECOND_POSITION;
|
||||
static $HANZI;
|
||||
|
||||
|
||||
private $characterCountBitsForVersions;
|
||||
private $bits;
|
||||
|
||||
function __construct($characterCountBitsForVersions, $bits) {
|
||||
$this->characterCountBitsForVersions = $characterCountBitsForVersions;
|
||||
$this->bits = $bits;
|
||||
}
|
||||
static function Init()
|
||||
{
|
||||
|
||||
|
||||
self::$TERMINATOR = new Mode(array(0, 0, 0), 0x00); // Not really a mode...
|
||||
self::$NUMERIC = new Mode(array(10, 12, 14), 0x01);
|
||||
self::$ALPHANUMERIC = new Mode(array(9, 11, 13), 0x02);
|
||||
self::$STRUCTURED_APPEND = new Mode(array(0, 0, 0), 0x03); // Not supported
|
||||
self::$BYTE = new Mode(array(8, 16, 16), 0x04);
|
||||
self::$ECI = new Mode(array(0, 0, 0), 0x07); // character counts don't apply
|
||||
self::$KANJI = new Mode(array(8, 10, 12), 0x08);
|
||||
self::$FNC1_FIRST_POSITION = new Mode(array(0, 0, 0), 0x05);
|
||||
self::$FNC1_SECOND_POSITION =new Mode(array(0, 0, 0), 0x09);
|
||||
/** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */
|
||||
self::$HANZI = new Mode(array(8, 10, 12), 0x0D);
|
||||
}
|
||||
/**
|
||||
* @param bits four bits encoding a QR Code data mode
|
||||
* @return Mode encoded by these bits
|
||||
* @throws IllegalArgumentException if bits do not correspond to a known mode
|
||||
*/
|
||||
public static function forBits($bits) {
|
||||
switch ($bits) {
|
||||
case 0x0:
|
||||
return self::$TERMINATOR;
|
||||
case 0x1:
|
||||
return self::$NUMERIC;
|
||||
case 0x2:
|
||||
return self::$ALPHANUMERIC;
|
||||
case 0x3:
|
||||
return self::$STRUCTURED_APPEND;
|
||||
case 0x4:
|
||||
return self::$BYTE;
|
||||
case 0x5:
|
||||
return self::$FNC1_FIRST_POSITION;
|
||||
case 0x7:
|
||||
return self::$ECI;
|
||||
case 0x8:
|
||||
return self::$KANJI;
|
||||
case 0x9:
|
||||
return self::$FNC1_SECOND_POSITION;
|
||||
case 0xD:
|
||||
// 0xD is defined in GBT 18284-2000, may not be supported in foreign country
|
||||
return self::$HANZI;
|
||||
default:
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param version version in question
|
||||
* @return number of bits used, in this QR Code symbol {@link Version}, to encode the
|
||||
* count of characters that will follow encoded in this Mode
|
||||
*/
|
||||
public function getCharacterCountBits($version) {
|
||||
$number = $version->getVersionNumber();
|
||||
$offset = 0;
|
||||
if ($number <= 9) {
|
||||
$offset = 0;
|
||||
} else if ($number <= 26) {
|
||||
$offset = 1;
|
||||
} else {
|
||||
$offset = 2;
|
||||
}
|
||||
return $this->characterCountBitsForVersions[$offset];
|
||||
}
|
||||
|
||||
public function getBits() {
|
||||
return $this->bits;
|
||||
}
|
||||
|
||||
}
|
||||
Mode::Init();
|
||||
619
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/Version.php
vendored
Normal file
619
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/decoder/Version.php
vendored
Normal file
@@ -0,0 +1,619 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Decoder;
|
||||
|
||||
use Zxing\FormatException;
|
||||
use Zxing\Common\BitMatrix;
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006 Annex D
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
class Version
|
||||
{
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006 Annex D.
|
||||
* Element i represents the raw version bits that specify version i + 7
|
||||
*/
|
||||
private static $VERSION_DECODE_INFO = array(
|
||||
0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6,
|
||||
0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78,
|
||||
0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683,
|
||||
0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB,
|
||||
0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250,
|
||||
0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B,
|
||||
0x2542E, 0x26A64, 0x27541, 0x28C69
|
||||
);
|
||||
|
||||
private static $VERSIONS;
|
||||
private $versionNumber;
|
||||
private $alignmentPatternCenters;
|
||||
private $ecBlocks;
|
||||
private $totalCodewords;
|
||||
|
||||
public function __construct($versionNumber,
|
||||
$alignmentPatternCenters,
|
||||
$ecBlocks)
|
||||
{//ECBlocks... ecBlocks
|
||||
|
||||
|
||||
|
||||
$this->versionNumber = $versionNumber;
|
||||
$this->alignmentPatternCenters = $alignmentPatternCenters;
|
||||
$this->ecBlocks = $ecBlocks;
|
||||
$total = 0;
|
||||
if(is_array($ecBlocks)) {
|
||||
$ecCodewords = $ecBlocks[0]->getECCodewordsPerBlock();
|
||||
$ecbArray = $ecBlocks[0]->getECBlocks();
|
||||
}else{
|
||||
$ecCodewords = $ecBlocks->getECCodewordsPerBlock();
|
||||
$ecbArray = $ecBlocks->getECBlocks();
|
||||
}
|
||||
foreach ($ecbArray as $ecBlock) {
|
||||
$total += $ecBlock->getCount() * ($ecBlock->getDataCodewords() + $ecCodewords);
|
||||
}
|
||||
$this->totalCodewords = $total;
|
||||
}
|
||||
public function getVersionNumber()
|
||||
{
|
||||
return $this->versionNumber;
|
||||
}
|
||||
|
||||
public function getAlignmentPatternCenters()
|
||||
{
|
||||
return $this->alignmentPatternCenters;
|
||||
}
|
||||
|
||||
public function getTotalCodewords()
|
||||
{
|
||||
return $this->totalCodewords;
|
||||
}
|
||||
|
||||
public function getDimensionForVersion()
|
||||
{
|
||||
return 17 + 4 * $this->versionNumber;
|
||||
}
|
||||
|
||||
public function getECBlocksForLevel($ecLevel)
|
||||
{
|
||||
return $this->ecBlocks[$ecLevel->getOrdinal()];
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Deduces version information purely from QR Code dimensions.</p>
|
||||
*
|
||||
* @param dimension dimension in modules
|
||||
* @return Version for a QR Code of that dimension
|
||||
* @throws FormatException if dimension is not 1 mod 4
|
||||
*/
|
||||
public static function getProvisionalVersionForDimension($dimension)
|
||||
{
|
||||
if ($dimension % 4 != 1) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
try {
|
||||
return self::getVersionForNumber(($dimension - 17) / 4);
|
||||
} catch (InvalidArgumentException $ignored) {
|
||||
throw FormatException::getFormatInstance();
|
||||
}
|
||||
}
|
||||
|
||||
public static function getVersionForNumber($versionNumber)
|
||||
{
|
||||
if ($versionNumber < 1 || $versionNumber > 40) {
|
||||
throw new \InvalidArgumentException();
|
||||
}
|
||||
if(!self::$VERSIONS){
|
||||
|
||||
self::$VERSIONS = self::buildVersions();
|
||||
|
||||
}
|
||||
return self::$VERSIONS[$versionNumber - 1];
|
||||
}
|
||||
|
||||
static function decodeVersionInformation($versionBits)
|
||||
{
|
||||
$bestDifference = PHP_INT_MAX;
|
||||
$bestVersion = 0;
|
||||
for ($i = 0; $i < count(self::$VERSION_DECODE_INFO); $i++) {
|
||||
$targetVersion = self::$VERSION_DECODE_INFO[$i];
|
||||
// Do the version info bits match exactly? done.
|
||||
if ($targetVersion == $versionBits) {
|
||||
return self::getVersionForNumber($i + 7);
|
||||
}
|
||||
// Otherwise see if this is the closest to a real version info bit string
|
||||
// we have seen so far
|
||||
$bitsDifference = FormatInformation::numBitsDiffering($versionBits, $targetVersion);
|
||||
if ($bitsDifference < $bestDifference) {
|
||||
$bestVersion = $i + 7;
|
||||
$bestDifference = $bitsDifference;
|
||||
}
|
||||
}
|
||||
// We can tolerate up to 3 bits of error since no two version info codewords will
|
||||
// differ in less than 8 bits.
|
||||
if ($bestDifference <= 3) {
|
||||
return self::getVersionForNumber($bestVersion);
|
||||
}
|
||||
// If we didn't find a close enough match, fail
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* See ISO 18004:2006 Annex E
|
||||
*/
|
||||
function buildFunctionPattern()
|
||||
{
|
||||
$dimension = self::getDimensionForVersion();
|
||||
$bitMatrix = new BitMatrix($dimension);
|
||||
|
||||
// Top left finder pattern + separator + format
|
||||
$bitMatrix->setRegion(0, 0, 9, 9);
|
||||
// Top right finder pattern + separator + format
|
||||
$bitMatrix->setRegion($dimension - 8, 0, 8, 9);
|
||||
// Bottom left finder pattern + separator + format
|
||||
$bitMatrix->setRegion(0, $dimension - 8, 9, 8);
|
||||
|
||||
// Alignment patterns
|
||||
$max = count($this->alignmentPatternCenters);
|
||||
for ($x = 0; $x < $max; $x++) {
|
||||
$i = $this->alignmentPatternCenters[$x] - 2;
|
||||
for ($y = 0; $y < $max; $y++) {
|
||||
if (($x == 0 && ($y == 0 || $y == $max - 1)) || ($x == $max - 1 && $y == 0)) {
|
||||
// No alignment patterns near the three finder paterns
|
||||
continue;
|
||||
}
|
||||
$bitMatrix->setRegion($this->alignmentPatternCenters[$y] - 2, $i, 5, 5);
|
||||
}
|
||||
}
|
||||
|
||||
// Vertical timing pattern
|
||||
$bitMatrix->setRegion(6, 9, 1, $dimension - 17);
|
||||
// Horizontal timing pattern
|
||||
$bitMatrix->setRegion(9, 6, $dimension - 17, 1);
|
||||
|
||||
if ($this->versionNumber > 6) {
|
||||
// Version info, top right
|
||||
$bitMatrix->setRegion($dimension - 11, 0, 3, 6);
|
||||
// Version info, bottom left
|
||||
$bitMatrix->setRegion(0, $dimension - 11, 6, 3);
|
||||
}
|
||||
|
||||
return $bitMatrix;
|
||||
}
|
||||
/**
|
||||
* See ISO 18004:2006 6.5.1 Table 9
|
||||
*/
|
||||
private static function buildVersions()
|
||||
{
|
||||
|
||||
|
||||
return array(
|
||||
new Version(1, array(),
|
||||
array(new ECBlocks(7, array(new ECB(1, 19))),
|
||||
new ECBlocks(10, array(new ECB(1, 16))),
|
||||
new ECBlocks(13, array(new ECB(1, 13))),
|
||||
new ECBlocks(17, array(new ECB(1, 9))))),
|
||||
new Version(2, array(6, 18),
|
||||
array(new ECBlocks(10, array(new ECB(1, 34))),
|
||||
new ECBlocks(16, array(new ECB(1, 28))),
|
||||
new ECBlocks(22, array(new ECB(1, 22))),
|
||||
new ECBlocks(28, array(new ECB(1, 16))))),
|
||||
new Version(3, array(6, 22),
|
||||
array( new ECBlocks(15, array(new ECB(1, 55))),
|
||||
new ECBlocks(26, array(new ECB(1, 44))),
|
||||
new ECBlocks(18, array(new ECB(2, 17))),
|
||||
new ECBlocks(22, array(new ECB(2, 13))))),
|
||||
new Version(4, array(6, 26),
|
||||
array(new ECBlocks(20, array(new ECB(1, 80))),
|
||||
new ECBlocks(18, array(new ECB(2, 32))),
|
||||
new ECBlocks(26, array(new ECB(2, 24))),
|
||||
new ECBlocks(16, array(new ECB(4, 9))))),
|
||||
new Version(5, array(6, 30),
|
||||
array(new ECBlocks(26, array(new ECB(1, 108))),
|
||||
new ECBlocks(24, array(new ECB(2, 43))),
|
||||
new ECBlocks(18, array(new ECB(2, 15),
|
||||
new ECB(2, 16))),
|
||||
new ECBlocks(22, array(new ECB(2, 11),
|
||||
new ECB(2, 12))))),
|
||||
new Version(6, array(6, 34),
|
||||
array(new ECBlocks(18, array(new ECB(2, 68))),
|
||||
new ECBlocks(16, array(new ECB(4, 27))),
|
||||
new ECBlocks(24, array(new ECB(4, 19))),
|
||||
new ECBlocks(28, array(new ECB(4, 15))))),
|
||||
new Version(7, array(6, 22, 38),
|
||||
array(new ECBlocks(20, array(new ECB(2, 78))),
|
||||
new ECBlocks(18, array(new ECB(4, 31))),
|
||||
new ECBlocks(18, array(new ECB(2, 14),
|
||||
new ECB(4, 15))),
|
||||
new ECBlocks(26, array(new ECB(4, 13),
|
||||
new ECB(1, 14))))),
|
||||
new Version(8, array(6, 24, 42),
|
||||
array(new ECBlocks(24, array(new ECB(2, 97))),
|
||||
new ECBlocks(22, array(new ECB(2, 38),
|
||||
new ECB(2, 39))),
|
||||
new ECBlocks(22, array(new ECB(4, 18),
|
||||
new ECB(2, 19))),
|
||||
new ECBlocks(26, array(new ECB(4, 14),
|
||||
new ECB(2, 15))))),
|
||||
new Version(9, array(6, 26, 46),
|
||||
array(new ECBlocks(30, array(new ECB(2, 116))),
|
||||
new ECBlocks(22, array(new ECB(3, 36),
|
||||
new ECB(2, 37))),
|
||||
new ECBlocks(20, array(new ECB(4, 16),
|
||||
new ECB(4, 17))),
|
||||
new ECBlocks(24, array(new ECB(4, 12),
|
||||
new ECB(4, 13))))),
|
||||
new Version(10, array(6, 28, 50),
|
||||
array(new ECBlocks(18, array(new ECB(2, 68),
|
||||
new ECB(2, 69))),
|
||||
new ECBlocks(26, array(new ECB(4, 43),
|
||||
new ECB(1, 44))),
|
||||
new ECBlocks(24, array(new ECB(6, 19),
|
||||
new ECB(2, 20))),
|
||||
new ECBlocks(28, array(new ECB(6, 15),
|
||||
new ECB(2, 16))))),
|
||||
new Version(11, array(6, 30, 54),
|
||||
array(new ECBlocks(20, array(new ECB(4, 81))),
|
||||
new ECBlocks(30, array(new ECB(1, 50),
|
||||
new ECB(4, 51))),
|
||||
new ECBlocks(28, array(new ECB(4, 22),
|
||||
new ECB(4, 23))),
|
||||
new ECBlocks(24, array(new ECB(3, 12),
|
||||
new ECB(8, 13))))),
|
||||
new Version(12, array(6, 32, 58),
|
||||
array(new ECBlocks(24, array(new ECB(2, 92),
|
||||
new ECB(2, 93))),
|
||||
new ECBlocks(22, array(new ECB(6, 36),
|
||||
new ECB(2, 37))),
|
||||
new ECBlocks(26, array(new ECB(4, 20),
|
||||
new ECB(6, 21))),
|
||||
new ECBlocks(28, array(new ECB(7, 14),
|
||||
new ECB(4, 15))))),
|
||||
new Version(13, array(6, 34, 62),
|
||||
array(new ECBlocks(26, array(new ECB(4, 107))),
|
||||
new ECBlocks(22, array(new ECB(8, 37),
|
||||
new ECB(1, 38))),
|
||||
new ECBlocks(24, array(new ECB(8, 20),
|
||||
new ECB(4, 21))),
|
||||
new ECBlocks(22, array(new ECB(12, 11),
|
||||
new ECB(4, 12))))),
|
||||
new Version(14, array(6, 26, 46, 66),
|
||||
array(new ECBlocks(30, array(new ECB(3, 115),
|
||||
new ECB(1, 116))),
|
||||
new ECBlocks(24, array(new ECB(4, 40),
|
||||
new ECB(5, 41))),
|
||||
new ECBlocks(20, array(new ECB(11, 16),
|
||||
new ECB(5, 17))),
|
||||
new ECBlocks(24, array(new ECB(11, 12),
|
||||
new ECB(5, 13))))),
|
||||
new Version(15, array(6, 26, 48, 70),
|
||||
array(new ECBlocks(22, array(new ECB(5, 87),
|
||||
new ECB(1, 88))),
|
||||
new ECBlocks(24, array(new ECB(5, 41),
|
||||
new ECB(5, 42))),
|
||||
new ECBlocks(30, array(new ECB(5, 24),
|
||||
new ECB(7, 25))),
|
||||
new ECBlocks(24, array(new ECB(11, 12),
|
||||
new ECB(7, 13))))),
|
||||
new Version(16, array(6, 26, 50, 74),
|
||||
array(new ECBlocks(24, array(new ECB(5, 98),
|
||||
new ECB(1, 99))),
|
||||
new ECBlocks(28, array(new ECB(7, 45),
|
||||
new ECB(3, 46))),
|
||||
new ECBlocks(24, array(new ECB(15, 19),
|
||||
new ECB(2, 20))),
|
||||
new ECBlocks(30, array(new ECB(3, 15),
|
||||
new ECB(13, 16))))),
|
||||
new Version(17, array(6, 30, 54, 78),
|
||||
array(new ECBlocks(28, array(new ECB(1, 107),
|
||||
new ECB(5, 108))),
|
||||
new ECBlocks(28, array(new ECB(10, 46),
|
||||
new ECB(1, 47))),
|
||||
new ECBlocks(28, array(new ECB(1, 22),
|
||||
new ECB(15, 23))),
|
||||
new ECBlocks(28, array(new ECB(2, 14),
|
||||
new ECB(17, 15))))),
|
||||
new Version(18, array(6, 30, 56, 82),
|
||||
array(new ECBlocks(30, array(new ECB(5, 120),
|
||||
new ECB(1, 121))),
|
||||
new ECBlocks(26, array(new ECB(9, 43),
|
||||
new ECB(4, 44))),
|
||||
new ECBlocks(28, array(new ECB(17, 22),
|
||||
new ECB(1, 23))),
|
||||
new ECBlocks(28, array(new ECB(2, 14),
|
||||
new ECB(19, 15))))),
|
||||
new Version(19, array(6, 30, 58, 86),
|
||||
array(new ECBlocks(28, array(new ECB(3, 113),
|
||||
new ECB(4, 114))),
|
||||
new ECBlocks(26, array(new ECB(3, 44),
|
||||
new ECB(11, 45))),
|
||||
new ECBlocks(26, array(new ECB(17, 21),
|
||||
new ECB(4, 22))),
|
||||
new ECBlocks(26, array(new ECB(9, 13),
|
||||
new ECB(16, 14))))),
|
||||
new Version(20, array(6, 34, 62, 90),
|
||||
array(new ECBlocks(28, array(new ECB(3, 107),
|
||||
new ECB(5, 108))),
|
||||
new ECBlocks(26, array(new ECB(3, 41),
|
||||
new ECB(13, 42))),
|
||||
new ECBlocks(30, array(new ECB(15, 24),
|
||||
new ECB(5, 25))),
|
||||
new ECBlocks(28, array(new ECB(15, 15),
|
||||
new ECB(10, 16))))),
|
||||
new Version(21, array(6, 28, 50, 72, 94),
|
||||
array( new ECBlocks(28, array(new ECB(4, 116),
|
||||
new ECB(4, 117))),
|
||||
new ECBlocks(26, array(new ECB(17, 42))),
|
||||
new ECBlocks(28, array(new ECB(17, 22),
|
||||
new ECB(6, 23))),
|
||||
new ECBlocks(30, array(new ECB(19, 16),
|
||||
new ECB(6, 17))))),
|
||||
new Version(22, array(6, 26, 50, 74, 98),
|
||||
array(new ECBlocks(28, array(new ECB(2, 111),
|
||||
new ECB(7, 112))),
|
||||
new ECBlocks(28, array(new ECB(17, 46))),
|
||||
new ECBlocks(30, array(new ECB(7, 24),
|
||||
new ECB(16, 25))),
|
||||
new ECBlocks(24, array(new ECB(34, 13))))),
|
||||
new Version(23, array(6, 30, 54, 78, 102),
|
||||
new ECBlocks(30, array(new ECB(4, 121),
|
||||
new ECB(5, 122))),
|
||||
new ECBlocks(28, array(new ECB(4, 47),
|
||||
new ECB(14, 48))),
|
||||
new ECBlocks(30, array(new ECB(11, 24),
|
||||
new ECB(14, 25))),
|
||||
new ECBlocks(30, array(new ECB(16, 15),
|
||||
new ECB(14, 16)))),
|
||||
new Version(24, array(6, 28, 54, 80, 106),
|
||||
array(new ECBlocks(30, array(new ECB(6, 117),
|
||||
new ECB(4, 118))),
|
||||
new ECBlocks(28, array(new ECB(6, 45),
|
||||
new ECB(14, 46))),
|
||||
new ECBlocks(30, array(new ECB(11, 24),
|
||||
new ECB(16, 25))),
|
||||
new ECBlocks(30, array(new ECB(30, 16),
|
||||
new ECB(2, 17))))),
|
||||
new Version(25, array(6, 32, 58, 84, 110),
|
||||
array(new ECBlocks(26, array(new ECB(8, 106),
|
||||
new ECB(4, 107))),
|
||||
new ECBlocks(28, array(new ECB(8, 47),
|
||||
new ECB(13, 48))),
|
||||
new ECBlocks(30, array(new ECB(7, 24),
|
||||
new ECB(22, 25))),
|
||||
new ECBlocks(30, array(new ECB(22, 15),
|
||||
new ECB(13, 16))))),
|
||||
new Version(26, array(6, 30, 58, 86, 114),
|
||||
array(new ECBlocks(28, array(new ECB(10, 114),
|
||||
new ECB(2, 115))),
|
||||
new ECBlocks(28, array(new ECB(19, 46),
|
||||
new ECB(4, 47))),
|
||||
new ECBlocks(28, array(new ECB(28, 22),
|
||||
new ECB(6, 23))),
|
||||
new ECBlocks(30, array(new ECB(33, 16),
|
||||
new ECB(4, 17))))),
|
||||
new Version(27, array(6, 34, 62, 90, 118),
|
||||
array(new ECBlocks(30, array(new ECB(8, 122),
|
||||
new ECB(4, 123))),
|
||||
new ECBlocks(28, array(new ECB(22, 45),
|
||||
new ECB(3, 46))),
|
||||
new ECBlocks(30, array(new ECB(8, 23),
|
||||
new ECB(26, 24))),
|
||||
new ECBlocks(30, array(new ECB(12, 15),
|
||||
new ECB(28, 16))))),
|
||||
new Version(28, array(6, 26, 50, 74, 98, 122),
|
||||
array(new ECBlocks(30, array(new ECB(3, 117),
|
||||
new ECB(10, 118))),
|
||||
new ECBlocks(28, array(new ECB(3, 45),
|
||||
new ECB(23, 46))),
|
||||
new ECBlocks(30, array(new ECB(4, 24),
|
||||
new ECB(31, 25))),
|
||||
new ECBlocks(30, array(new ECB(11, 15),
|
||||
new ECB(31, 16))))),
|
||||
new Version(29, array(6, 30, 54, 78, 102, 126),
|
||||
array(new ECBlocks(30, array(new ECB(7, 116),
|
||||
new ECB(7, 117))),
|
||||
new ECBlocks(28, array(new ECB(21, 45),
|
||||
new ECB(7, 46))),
|
||||
new ECBlocks(30, array(new ECB(1, 23),
|
||||
new ECB(37, 24))),
|
||||
new ECBlocks(30, array(new ECB(19, 15),
|
||||
new ECB(26, 16))))),
|
||||
new Version(30, array(6, 26, 52, 78, 104, 130),
|
||||
array(new ECBlocks(30, array(new ECB(5, 115),
|
||||
new ECB(10, 116))),
|
||||
new ECBlocks(28, array(new ECB(19, 47),
|
||||
new ECB(10, 48))),
|
||||
new ECBlocks(30, array(new ECB(15, 24),
|
||||
new ECB(25, 25))),
|
||||
new ECBlocks(30, array(new ECB(23, 15),
|
||||
new ECB(25, 16))))),
|
||||
new Version(31, array(6, 30, 56, 82, 108, 134),
|
||||
array(new ECBlocks(30, array(new ECB(13, 115),
|
||||
new ECB(3, 116))),
|
||||
new ECBlocks(28, array(new ECB(2, 46),
|
||||
new ECB(29, 47))),
|
||||
new ECBlocks(30, array(new ECB(42, 24),
|
||||
new ECB(1, 25))),
|
||||
new ECBlocks(30, array(new ECB(23, 15),
|
||||
new ECB(28, 16))))),
|
||||
new Version(32, array(6, 34, 60, 86, 112, 138),
|
||||
array(new ECBlocks(30, array(new ECB(17, 115))),
|
||||
new ECBlocks(28, array(new ECB(10, 46),
|
||||
new ECB(23, 47))),
|
||||
new ECBlocks(30, array(new ECB(10, 24),
|
||||
new ECB(35, 25))),
|
||||
new ECBlocks(30, array(new ECB(19, 15),
|
||||
new ECB(35, 16))))),
|
||||
new Version(33, array(6, 30, 58, 86, 114, 142),
|
||||
array(new ECBlocks(30, array(new ECB(17, 115),
|
||||
new ECB(1, 116))),
|
||||
new ECBlocks(28, array(new ECB(14, 46),
|
||||
new ECB(21, 47))),
|
||||
new ECBlocks(30, array(new ECB(29, 24),
|
||||
new ECB(19, 25))),
|
||||
new ECBlocks(30, array(new ECB(11, 15),
|
||||
new ECB(46, 16))))),
|
||||
new Version(34, array(6, 34, 62, 90, 118, 146),
|
||||
array(new ECBlocks(30, array(new ECB(13, 115),
|
||||
new ECB(6, 116))),
|
||||
new ECBlocks(28, array(new ECB(14, 46),
|
||||
new ECB(23, 47))),
|
||||
new ECBlocks(30, array(new ECB(44, 24),
|
||||
new ECB(7, 25))),
|
||||
new ECBlocks(30, array(new ECB(59, 16),
|
||||
new ECB(1, 17))))),
|
||||
new Version(35, array(6, 30, 54, 78, 102, 126, 150),
|
||||
array(new ECBlocks(30, array(new ECB(12, 121),
|
||||
new ECB(7, 122))),
|
||||
new ECBlocks(28, array(new ECB(12, 47),
|
||||
new ECB(26, 48))),
|
||||
new ECBlocks(30, array(new ECB(39, 24),
|
||||
new ECB(14, 25))),
|
||||
new ECBlocks(30, array(new ECB(22, 15),
|
||||
new ECB(41, 16))))),
|
||||
new Version(36, array(6, 24, 50, 76, 102, 128, 154),
|
||||
array(new ECBlocks(30, array(new ECB(6, 121),
|
||||
new ECB(14, 122))),
|
||||
new ECBlocks(28, array(new ECB(6, 47),
|
||||
new ECB(34, 48))),
|
||||
new ECBlocks(30, array(new ECB(46, 24),
|
||||
new ECB(10, 25))),
|
||||
new ECBlocks(30, array(new ECB(2, 15),
|
||||
new ECB(64, 16))))),
|
||||
new Version(37, array(6, 28, 54, 80, 106, 132, 158),
|
||||
array(new ECBlocks(30, array(new ECB(17, 122),
|
||||
new ECB(4, 123))),
|
||||
new ECBlocks(28, array(new ECB(29, 46),
|
||||
new ECB(14, 47))),
|
||||
new ECBlocks(30, array(new ECB(49, 24),
|
||||
new ECB(10, 25))),
|
||||
new ECBlocks(30, array(new ECB(24, 15),
|
||||
new ECB(46, 16))))),
|
||||
new Version(38, array(6, 32, 58, 84, 110, 136, 162),
|
||||
array(new ECBlocks(30, array(new ECB(4, 122),
|
||||
new ECB(18, 123))),
|
||||
new ECBlocks(28, array(new ECB(13, 46),
|
||||
new ECB(32, 47))),
|
||||
new ECBlocks(30, array(new ECB(48, 24),
|
||||
new ECB(14, 25))),
|
||||
new ECBlocks(30, array(new ECB(42, 15),
|
||||
new ECB(32, 16))))),
|
||||
new Version(39, array(6, 26, 54, 82, 110, 138, 166),
|
||||
array(new ECBlocks(30, array(new ECB(20, 117),
|
||||
new ECB(4, 118))),
|
||||
new ECBlocks(28, array(new ECB(40, 47),
|
||||
new ECB(7, 48))),
|
||||
new ECBlocks(30, array(new ECB(43, 24),
|
||||
new ECB(22, 25))),
|
||||
new ECBlocks(30, array(new ECB(10, 15),
|
||||
new ECB(67, 16))))),
|
||||
new Version(40, array(6, 30, 58, 86, 114, 142, 170),
|
||||
array(new ECBlocks(30, array(new ECB(19, 118),
|
||||
new ECB(6, 119))),
|
||||
new ECBlocks(28, array(new ECB(18, 47),
|
||||
new ECB(31, 48))),
|
||||
new ECBlocks(30, array(new ECB(34, 24),
|
||||
new ECB(34, 25))),
|
||||
new ECBlocks(30, array(new ECB(20, 15),
|
||||
new ECB(61, 16)))))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Encapsulates a set of error-correction blocks in one symbol version. Most versions will
|
||||
* use blocks of differing sizes within one version, so, this encapsulates the parameters for
|
||||
* each set of blocks. It also holds the number of error-correction codewords per block since it
|
||||
* will be the same across all blocks within one version.</p>
|
||||
*/
|
||||
final class ECBlocks
|
||||
{
|
||||
private $ecCodewordsPerBlock;
|
||||
private $ecBlocks;
|
||||
|
||||
function __construct($ecCodewordsPerBlock, $ecBlocks)
|
||||
{
|
||||
$this->ecCodewordsPerBlock = $ecCodewordsPerBlock;
|
||||
$this->ecBlocks = $ecBlocks;
|
||||
}
|
||||
|
||||
public function getECCodewordsPerBlock()
|
||||
{
|
||||
return $this->ecCodewordsPerBlock;
|
||||
}
|
||||
|
||||
public function getNumBlocks()
|
||||
{
|
||||
$total = 0;
|
||||
foreach ($this->ecBlocks as $ecBlock) {
|
||||
$total += $ecBlock->getCount();
|
||||
}
|
||||
return $total;
|
||||
}
|
||||
|
||||
public function getTotalECCodewords()
|
||||
{
|
||||
return $this->ecCodewordsPerBlock * $this->getNumBlocks();
|
||||
}
|
||||
|
||||
public function getECBlocks()
|
||||
{
|
||||
return $this->ecBlocks;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Encapsualtes the parameters for one error-correction block in one symbol version.
|
||||
* This includes the number of data codewords, and the number of times a block with these
|
||||
* parameters is used consecutively in the QR code version's format.</p>
|
||||
*/
|
||||
final class ECB
|
||||
{
|
||||
private $count;
|
||||
private $dataCodewords;
|
||||
|
||||
function __construct($count, $dataCodewords)
|
||||
{
|
||||
$this->count = $count;
|
||||
$this->dataCodewords = $dataCodewords;
|
||||
}
|
||||
|
||||
public function getCount()
|
||||
{
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
public function getDataCodewords()
|
||||
{
|
||||
return $this->dataCodewords;
|
||||
}
|
||||
|
||||
|
||||
//@Override
|
||||
public function toString()
|
||||
{
|
||||
die('Version ECB toString()');
|
||||
// return parent::$versionNumber;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
60
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/AlignmentPattern.php
vendored
Normal file
60
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/AlignmentPattern.php
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Detector;
|
||||
|
||||
use Zxing\ResultPoint;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates an alignment pattern, which are the smaller square patterns found in
|
||||
* all but the simplest QR Codes.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class AlignmentPattern extends ResultPoint {
|
||||
|
||||
private $estimatedModuleSize;
|
||||
|
||||
function __construct($posX, $posY, $estimatedModuleSize) {
|
||||
parent::__construct($posX, $posY);
|
||||
$this->estimatedModuleSize = $estimatedModuleSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Determines if this alignment pattern "about equals" an alignment pattern at the stated
|
||||
* position and size -- meaning, it is at nearly the same center with nearly the same size.</p>
|
||||
*/
|
||||
function aboutEquals($moduleSize, $i, $j) {
|
||||
if (abs($i - $this->getY()) <= $moduleSize && abs($j - $this->getX()) <= $moduleSize) {
|
||||
$moduleSizeDiff = abs($moduleSize - $this->estimatedModuleSize);
|
||||
return $moduleSizeDiff <= 1.0 || $moduleSizeDiff <= $this->estimatedModuleSize;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines this object's current estimate of a finder pattern position and module size
|
||||
* with a new estimate. It returns a new {@code FinderPattern} containing an average of the two.
|
||||
*/
|
||||
function combineEstimate($i, $j, $newModuleSize) {
|
||||
$combinedX = ($this->getX() + $j) / 2.0;
|
||||
$combinedY = ($this->getY() + $i) / 2.0;
|
||||
$combinedModuleSize = ($this->estimatedModuleSize + $newModuleSize) / 2.0;
|
||||
return new AlignmentPattern($combinedX, $combinedY, $combinedModuleSize);
|
||||
}
|
||||
|
||||
}
|
||||
277
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/AlignmentPatternFinder.php
vendored
Normal file
277
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/AlignmentPatternFinder.php
vendored
Normal file
@@ -0,0 +1,277 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Detector;
|
||||
|
||||
use Zxing\NotFoundException;
|
||||
use Zxing\ResultPointCallback;
|
||||
use Zxing\Common\BitMatrix;
|
||||
|
||||
|
||||
/**
|
||||
* <p>This class attempts to find alignment patterns in a QR Code. Alignment patterns look like finder
|
||||
* patterns but are smaller and appear at regular intervals throughout the image.</p>
|
||||
*
|
||||
* <p>At the moment this only looks for the bottom-right alignment pattern.</p>
|
||||
*
|
||||
* <p>This is mostly a simplified copy of {@link FinderPatternFinder}. It is copied,
|
||||
* pasted and stripped down here for maximum performance but does unfortunately duplicate
|
||||
* some code.</p>
|
||||
*
|
||||
* <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class AlignmentPatternFinder {
|
||||
|
||||
private $image;
|
||||
private $possibleCenters;
|
||||
private $startX;
|
||||
private $startY;
|
||||
private $width;
|
||||
private $height;
|
||||
private $moduleSize;
|
||||
private $crossCheckStateCount;
|
||||
private $resultPointCallback;
|
||||
|
||||
/**
|
||||
* <p>Creates a finder that will look in a portion of the whole image.</p>
|
||||
*
|
||||
* @param image image to search
|
||||
* @param startX left column from which to start searching
|
||||
* @param startY top row from which to start searching
|
||||
* @param width width of region to search
|
||||
* @param height height of region to search
|
||||
* @param moduleSize estimated module size so far
|
||||
*/
|
||||
function __construct($image,
|
||||
$startX,
|
||||
$startY,
|
||||
$width,
|
||||
$height,
|
||||
$moduleSize,
|
||||
$resultPointCallback) {
|
||||
$this->image = $image;
|
||||
$this->possibleCenters = array();
|
||||
$this->startX = $startX;
|
||||
$this->startY = $startY;
|
||||
$this->width = $width;
|
||||
$this->height = $height;
|
||||
$this->moduleSize = $moduleSize;
|
||||
$this->crossCheckStateCount = array();
|
||||
$this->resultPointCallback = $resultPointCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since
|
||||
* it's pretty performance-critical and so is written to be fast foremost.</p>
|
||||
*
|
||||
* @return {@link AlignmentPattern} if found
|
||||
* @throws NotFoundException if not found
|
||||
*/
|
||||
function find() {
|
||||
$startX = $this->startX;
|
||||
$height = $this->height;
|
||||
$maxJ = $startX + $this->width;
|
||||
$middleI = $this->startY + ($height / 2);
|
||||
// We are looking for black/white/black modules in 1:1:1 ratio;
|
||||
// this tracks the number of black/white/black modules seen so far
|
||||
$stateCount = array();
|
||||
for ($iGen = 0; $iGen < $height; $iGen++) {
|
||||
// Search from middle outwards
|
||||
$i = $middleI + (($iGen & 0x01) == 0 ? ($iGen + 1) / 2 : -(($iGen + 1) / 2));
|
||||
$i = intval($i);
|
||||
$stateCount[0] = 0;
|
||||
$stateCount[1] = 0;
|
||||
$stateCount[2] = 0;
|
||||
$j = $startX;
|
||||
// Burn off leading white pixels before anything else; if we start in the middle of
|
||||
// a white run, it doesn't make sense to count its length, since we don't know if the
|
||||
// white run continued to the left of the start point
|
||||
while ($j < $maxJ && !$this->image->get($j, $i)) {
|
||||
$j++;
|
||||
}
|
||||
$currentState = 0;
|
||||
while ($j < $maxJ) {
|
||||
if ($this->image->get($j, $i)) {
|
||||
// Black pixel
|
||||
if ($currentState == 1) { // Counting black pixels
|
||||
$stateCount[$currentState]++;
|
||||
} else { // Counting white pixels
|
||||
if ($currentState == 2) { // A winner?
|
||||
if ($this->foundPatternCross($stateCount)) { // Yes
|
||||
$confirmed = $this->handlePossibleCenter($stateCount, $i, $j);
|
||||
if ($confirmed != null) {
|
||||
return $confirmed;
|
||||
}
|
||||
}
|
||||
$stateCount[0] = $stateCount[2];
|
||||
$stateCount[1] = 1;
|
||||
$stateCount[2] = 0;
|
||||
$currentState = 1;
|
||||
} else {
|
||||
$stateCount[++$currentState]++;
|
||||
}
|
||||
}
|
||||
} else { // White pixel
|
||||
if ($currentState == 1) { // Counting black pixels
|
||||
$currentState++;
|
||||
}
|
||||
$stateCount[$currentState]++;
|
||||
}
|
||||
$j++;
|
||||
}
|
||||
if ($this->foundPatternCross($stateCount)) {
|
||||
$confirmed = $this->handlePossibleCenter($stateCount, $i, $maxJ);
|
||||
if ($confirmed != null) {
|
||||
return $confirmed;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Hmm, nothing we saw was observed and confirmed twice. If we had
|
||||
// any guess at all, return it.
|
||||
if (count($this->possibleCenters)) {
|
||||
return $this->possibleCenters[0];
|
||||
}
|
||||
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a count of black/white/black pixels just seen and an end position,
|
||||
* figures the location of the center of this black/white/black run.
|
||||
*/
|
||||
private static function centerFromEnd($stateCount, $end) {
|
||||
return (float) ($end - $stateCount[2]) - $stateCount[1] / 2.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stateCount count of black/white/black pixels just read
|
||||
* @return true iff the proportions of the counts is close enough to the 1/1/1 ratios
|
||||
* used by alignment patterns to be considered a match
|
||||
*/
|
||||
private function foundPatternCross($stateCount) {
|
||||
$moduleSize = $this->moduleSize;
|
||||
$maxVariance = $moduleSize / 2.0;
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
if (abs($moduleSize - $stateCount[$i]) >= $maxVariance) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>After a horizontal scan finds a potential alignment pattern, this method
|
||||
* "cross-checks" by scanning down vertically through the center of the possible
|
||||
* alignment pattern to see if the same proportion is detected.</p>
|
||||
*
|
||||
* @param startI row where an alignment pattern was detected
|
||||
* @param centerJ center of the section that appears to cross an alignment pattern
|
||||
* @param maxCount maximum reasonable number of modules that should be
|
||||
* observed in any reading state, based on the results of the horizontal scan
|
||||
* @return vertical center of alignment pattern, or {@link Float#NaN} if not found
|
||||
*/
|
||||
private function crossCheckVertical($startI, $centerJ, $maxCount,
|
||||
$originalStateCountTotal) {
|
||||
$image = $this->image;
|
||||
|
||||
$maxI = $image->getHeight();
|
||||
$stateCount = $this->crossCheckStateCount;
|
||||
$stateCount[0] = 0;
|
||||
$stateCount[1] = 0;
|
||||
$stateCount[2] = 0;
|
||||
|
||||
// Start counting up from center
|
||||
$i = $startI;
|
||||
while ($i >= 0 && $image->get($centerJ, $i) && $stateCount[1] <= $maxCount) {
|
||||
$stateCount[1]++;
|
||||
$i--;
|
||||
}
|
||||
// If already too many modules in this state or ran off the edge:
|
||||
if ($i < 0 || $stateCount[1] > $maxCount) {
|
||||
return NAN;
|
||||
}
|
||||
while ($i >= 0 && !$image->get($centerJ, $i) && $stateCount[0] <= $maxCount) {
|
||||
$stateCount[0]++;
|
||||
$i--;
|
||||
}
|
||||
if ($stateCount[0] > $maxCount) {
|
||||
return NAN;
|
||||
}
|
||||
|
||||
// Now also count down from center
|
||||
$i = $startI + 1;
|
||||
while ($i < $maxI && $image->get($centerJ, $i) && $stateCount[1] <= $maxCount) {
|
||||
$stateCount[1]++;
|
||||
$i++;
|
||||
}
|
||||
if ($i == $maxI || $stateCount[1] > $maxCount) {
|
||||
return NAN;
|
||||
}
|
||||
while ($i < $maxI && !$image->get($centerJ, $i) && $stateCount[2] <= $maxCount) {
|
||||
$stateCount[2]++;
|
||||
$i++;
|
||||
}
|
||||
if ($stateCount[2] > $maxCount) {
|
||||
return NAN;
|
||||
}
|
||||
|
||||
$stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2];
|
||||
if (5 * abs($stateCountTotal - $originalStateCountTotal) >= 2 * $originalStateCountTotal) {
|
||||
return NAN;
|
||||
}
|
||||
|
||||
return $this->foundPatternCross($stateCount) ? $this->centerFromEnd($stateCount, $i) : NAN;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>This is called when a horizontal scan finds a possible alignment pattern. It will
|
||||
* cross check with a vertical scan, and if successful, will see if this pattern had been
|
||||
* found on a previous horizontal scan. If so, we consider it confirmed and conclude we have
|
||||
* found the alignment pattern.</p>
|
||||
*
|
||||
* @param stateCount reading state module counts from horizontal scan
|
||||
* @param i row where alignment pattern may be found
|
||||
* @param j end of possible alignment pattern in row
|
||||
* @return {@link AlignmentPattern} if we have found the same pattern twice, or null if not
|
||||
*/
|
||||
private function handlePossibleCenter($stateCount, $i, $j) {
|
||||
$stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2];
|
||||
$centerJ = $this->centerFromEnd($stateCount, $j);
|
||||
$centerI = $this->crossCheckVertical($i, (int) $centerJ, 2 * $stateCount[1], $stateCountTotal);
|
||||
if (!is_nan($centerI)) {
|
||||
$estimatedModuleSize = (float) ($stateCount[0] + $stateCount[1] + $stateCount[2]) / 3.0;
|
||||
foreach ($this->possibleCenters as $center) {
|
||||
// Look for about the same center and module size:
|
||||
if ($center->aboutEquals($estimatedModuleSize, $centerI, $centerJ)) {
|
||||
return $center->combineEstimate($centerI, $centerJ, $estimatedModuleSize);
|
||||
}
|
||||
}
|
||||
// Hadn't found this before; save it
|
||||
$point = new AlignmentPattern($centerJ, $centerI, $estimatedModuleSize);
|
||||
$this->possibleCenters[] = $point;
|
||||
if ($this->resultPointCallback != null) {
|
||||
$this->resultPointCallback->foundPossibleResultPoint($point);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
405
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/Detector.php
vendored
Normal file
405
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/Detector.php
vendored
Normal file
@@ -0,0 +1,405 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Detector;
|
||||
|
||||
use Zxing\DecodeHintType;
|
||||
use Zxing\FormatException;
|
||||
use Zxing\NotFoundException;
|
||||
use Zxing\ResultPoint;
|
||||
use Zxing\ResultPointCallback;
|
||||
use Zxing\Common\BitMatrix;
|
||||
use Zxing\Common\DetectorResult;
|
||||
use Zxing\Common\GridSampler;
|
||||
use Zxing\Common\PerspectiveTransform;
|
||||
use Zxing\Common\Detector\MathUtils;
|
||||
use Zxing\Qrcode\Decoder\Version;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Encapsulates logic that can detect a QR Code in an image, even if the QR Code
|
||||
* is rotated or skewed, or partially obscured.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
?>
|
||||
<?php
|
||||
class Detector {
|
||||
|
||||
private $image;
|
||||
private $resultPointCallback;
|
||||
|
||||
public function __construct($image) {
|
||||
$this->image = $image;
|
||||
}
|
||||
|
||||
protected final function getImage() {
|
||||
return $this->image;
|
||||
}
|
||||
|
||||
protected final function getResultPointCallback() {
|
||||
return $this->resultPointCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Detects a QR Code in an image.</p>
|
||||
*
|
||||
* @return {@link DetectorResult} encapsulating results of detecting a QR Code
|
||||
* @throws NotFoundException if QR Code cannot be found
|
||||
* @throws FormatException if a QR Code cannot be decoded
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* <p>Detects a QR Code in an image.</p>
|
||||
*
|
||||
* @param hints optional hints to detector
|
||||
* @return {@link DetectorResult} encapsulating results of detecting a QR Code
|
||||
* @throws NotFoundException if QR Code cannot be found
|
||||
* @throws FormatException if a QR Code cannot be decoded
|
||||
*/
|
||||
public final function detect($hints=null){/*Map<DecodeHintType,?>*/
|
||||
|
||||
$resultPointCallback = $hints == null ? null :
|
||||
$hints->get('NEED_RESULT_POINT_CALLBACK');
|
||||
/* resultPointCallback = hints == null ? null :
|
||||
(ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);*/
|
||||
$finder = new FinderPatternFinder($this->image, $resultPointCallback);
|
||||
$info = $finder->find($hints);
|
||||
|
||||
return $this->processFinderPatternInfo($info);
|
||||
}
|
||||
|
||||
protected final function processFinderPatternInfo($info){
|
||||
|
||||
$topLeft = $info->getTopLeft();
|
||||
$topRight = $info->getTopRight();
|
||||
$bottomLeft = $info->getBottomLeft();
|
||||
|
||||
$moduleSize = (float) $this->calculateModuleSize($topLeft, $topRight, $bottomLeft);
|
||||
if ($moduleSize < 1.0) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
$dimension =(int) $this->computeDimension($topLeft, $topRight, $bottomLeft, $moduleSize);
|
||||
$provisionalVersion = Version::getProvisionalVersionForDimension($dimension);
|
||||
$modulesBetweenFPCenters = $provisionalVersion->getDimensionForVersion() - 7;
|
||||
|
||||
$alignmentPattern = null;
|
||||
// Anything above version 1 has an alignment pattern
|
||||
if (count($provisionalVersion->getAlignmentPatternCenters())> 0) {
|
||||
|
||||
// Guess where a "bottom right" finder pattern would have been
|
||||
$bottomRightX = $topRight->getX() - $topLeft->getX() + $bottomLeft->getX();
|
||||
$bottomRightY = $topRight->getY() - $topLeft->getY() + $bottomLeft->getY();
|
||||
|
||||
// Estimate that alignment pattern is closer by 3 modules
|
||||
// from "bottom right" to known top left location
|
||||
$correctionToTopLeft = 1.0 - 3.0 / (float) $modulesBetweenFPCenters;
|
||||
$estAlignmentX = (int) ($topLeft->getX() + $correctionToTopLeft * ($bottomRightX - $topLeft->getX()));
|
||||
$estAlignmentY = (int) ($topLeft->getY() + $correctionToTopLeft * ($bottomRightY - $topLeft->getY()));
|
||||
|
||||
// Kind of arbitrary -- expand search radius before giving up
|
||||
for ($i = 4; $i <= 16; $i <<= 1) {//??????????
|
||||
try {
|
||||
$alignmentPattern = $this->findAlignmentInRegion($moduleSize,
|
||||
$estAlignmentX,
|
||||
$estAlignmentY,
|
||||
(float) $i);
|
||||
break;
|
||||
} catch (NotFoundException $re) {
|
||||
// try next round
|
||||
}
|
||||
}
|
||||
// If we didn't find alignment pattern... well try anyway without it
|
||||
}
|
||||
|
||||
$transform =
|
||||
$this->createTransform($topLeft, $topRight, $bottomLeft, $alignmentPattern, $dimension);
|
||||
|
||||
$bits = $this->sampleGrid($this->image, $transform, $dimension);
|
||||
|
||||
$points = array();
|
||||
if ($alignmentPattern == null) {
|
||||
$points = array($bottomLeft, $topLeft, $topRight);
|
||||
} else {
|
||||
// die('$points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern};');
|
||||
$points = array($bottomLeft, $topLeft, $topRight, $alignmentPattern);
|
||||
}
|
||||
return new DetectorResult($bits, $points);
|
||||
}
|
||||
|
||||
private static function createTransform($topLeft,
|
||||
$topRight,
|
||||
$bottomLeft,
|
||||
$alignmentPattern,
|
||||
$dimension) {
|
||||
$dimMinusThree = (float) $dimension - 3.5;
|
||||
$bottomRightX = 0.0;
|
||||
$bottomRightY = 0.0;
|
||||
$sourceBottomRightX = 0.0;
|
||||
$sourceBottomRightY = 0.0;
|
||||
if ($alignmentPattern != null) {
|
||||
$bottomRightX = $alignmentPattern->getX();
|
||||
$bottomRightY = $alignmentPattern->getY();
|
||||
$sourceBottomRightX = $dimMinusThree - 3.0;
|
||||
$sourceBottomRightY = $sourceBottomRightX;
|
||||
} else {
|
||||
// Don't have an alignment pattern, just make up the bottom-right point
|
||||
$bottomRightX = ($topRight->getX() - $topLeft->getX()) + $bottomLeft->getX();
|
||||
$bottomRightY = ($topRight->getY() - $topLeft->getY()) + $bottomLeft->getY();
|
||||
$sourceBottomRightX = $dimMinusThree;
|
||||
$sourceBottomRightY = $dimMinusThree;
|
||||
}
|
||||
|
||||
return PerspectiveTransform::quadrilateralToQuadrilateral(
|
||||
3.5,
|
||||
3.5,
|
||||
$dimMinusThree,
|
||||
3.5,
|
||||
$sourceBottomRightX,
|
||||
$sourceBottomRightY,
|
||||
3.5,
|
||||
$dimMinusThree,
|
||||
$topLeft->getX(),
|
||||
$topLeft->getY(),
|
||||
$topRight->getX(),
|
||||
$topRight->getY(),
|
||||
$bottomRightX,
|
||||
$bottomRightY,
|
||||
$bottomLeft->getX(),
|
||||
$bottomLeft->getY());
|
||||
}
|
||||
|
||||
private static function sampleGrid($image, $transform,
|
||||
$dimension) {
|
||||
|
||||
$sampler = GridSampler::getInstance();
|
||||
return $sampler->sampleGrid_($image, $dimension, $dimension, $transform);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Computes the dimension (number of modules on a size) of the QR Code based on the position
|
||||
* of the finder patterns and estimated module size.</p>
|
||||
*/
|
||||
private static function computeDimension($topLeft,
|
||||
$topRight,
|
||||
$bottomLeft,
|
||||
$moduleSize) {
|
||||
$tltrCentersDimension = MathUtils::round(ResultPoint::distance($topLeft, $topRight) / $moduleSize);
|
||||
$tlblCentersDimension = MathUtils::round(ResultPoint::distance($topLeft, $bottomLeft) / $moduleSize);
|
||||
$dimension = (($tltrCentersDimension + $tlblCentersDimension) / 2) + 7;
|
||||
switch ($dimension & 0x03) { // mod 4
|
||||
case 0:
|
||||
$dimension++;
|
||||
break;
|
||||
// 1? do nothing
|
||||
case 2:
|
||||
$dimension--;
|
||||
break;
|
||||
case 3:
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
return $dimension;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Computes an average estimated module size based on estimated derived from the positions
|
||||
* of the three finder patterns.</p>
|
||||
*
|
||||
* @param topLeft detected top-left finder pattern center
|
||||
* @param topRight detected top-right finder pattern center
|
||||
* @param bottomLeft detected bottom-left finder pattern center
|
||||
* @return estimated module size
|
||||
*/
|
||||
protected final function calculateModuleSize($topLeft,
|
||||
$topRight,
|
||||
$bottomLeft) {
|
||||
// Take the average
|
||||
return ($this->calculateModuleSizeOneWay($topLeft, $topRight) +
|
||||
$this->calculateModuleSizeOneWay($topLeft, $bottomLeft)) / 2.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Estimates module size based on two finder patterns -- it uses
|
||||
* {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the
|
||||
* width of each, measuring along the axis between their centers.</p>
|
||||
*/
|
||||
private function calculateModuleSizeOneWay($pattern, $otherPattern) {
|
||||
$moduleSizeEst1 = $this->sizeOfBlackWhiteBlackRunBothWays($pattern->getX(),
|
||||
(int) $pattern->getY(),
|
||||
(int) $otherPattern->getX(),
|
||||
(int) $otherPattern->getY());
|
||||
$moduleSizeEst2 = $this->sizeOfBlackWhiteBlackRunBothWays((int) $otherPattern->getX(),
|
||||
(int) $otherPattern->getY(),
|
||||
(int) $pattern->getX(),
|
||||
(int) $pattern->getY());
|
||||
if (is_nan($moduleSizeEst1)) {
|
||||
return $moduleSizeEst2 / 7.0;
|
||||
}
|
||||
if (is_nan($moduleSizeEst2)) {
|
||||
return $moduleSizeEst1 / 7.0;
|
||||
}
|
||||
// Average them, and divide by 7 since we've counted the width of 3 black modules,
|
||||
// and 1 white and 1 black module on either side. Ergo, divide sum by 14.
|
||||
return ($moduleSizeEst1 + $moduleSizeEst2) / 14.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #sizeOfBlackWhiteBlackRun(int, int, int, int)}; computes the total width of
|
||||
* a finder pattern by looking for a black-white-black run from the center in the direction
|
||||
* of another po$(another finder pattern center), and in the opposite direction too.</p>
|
||||
*/
|
||||
private function sizeOfBlackWhiteBlackRunBothWays($fromX, $fromY, $toX, $toY) {
|
||||
|
||||
$result = $this->sizeOfBlackWhiteBlackRun($fromX, $fromY, $toX, $toY);
|
||||
|
||||
// Now count other way -- don't run off image though of course
|
||||
$scale = 1.0;
|
||||
$otherToX = $fromX - ($toX - $fromX);
|
||||
if ($otherToX < 0) {
|
||||
$scale = (float) $fromX / (float) ($fromX - $otherToX);
|
||||
$otherToX = 0;
|
||||
} else if ($otherToX >= $this->image->getWidth()) {
|
||||
$scale = (float) ($this->image->getWidth() - 1 - $fromX) / (float) ($otherToX - $fromX);
|
||||
$otherToX = $this->image->getWidth() - 1;
|
||||
}
|
||||
$otherToY = (int) ($fromY - ($toY - $fromY) * $scale);
|
||||
|
||||
$scale = 1.0;
|
||||
if ($otherToY < 0) {
|
||||
$scale = (float) $fromY / (float) ($fromY - $otherToY);
|
||||
$otherToY = 0;
|
||||
} else if ($otherToY >= $this->image->getHeight()) {
|
||||
$scale = (float) ($this->image->getHeight() - 1 - $fromY) / (float) ($otherToY - $fromY);
|
||||
$otherToY = $this->image->getHeight() - 1;
|
||||
}
|
||||
$otherToX = (int) ($fromX + ($otherToX - $fromX) * $scale);
|
||||
|
||||
$result += $this->sizeOfBlackWhiteBlackRun($fromX, $fromY, $otherToX, $otherToY);
|
||||
|
||||
// Middle pixel is double-counted this way; subtract 1
|
||||
return $result - 1.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>This method traces a line from a po$in the image, in the direction towards another point.
|
||||
* It begins in a black region, and keeps going until it finds white, then black, then white again.
|
||||
* It reports the distance from the start to this point.</p>
|
||||
*
|
||||
* <p>This is used when figuring out how wide a finder pattern is, when the finder pattern
|
||||
* may be skewed or rotated.</p>
|
||||
*/
|
||||
private function sizeOfBlackWhiteBlackRun($fromX, $fromY, $toX, $toY) {
|
||||
// Mild variant of Bresenham's algorithm;
|
||||
// see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
|
||||
$steep = abs($toY - $fromY) > abs($toX - $fromX);
|
||||
if ($steep) {
|
||||
$temp = $fromX;
|
||||
$fromX = $fromY;
|
||||
$fromY = $temp;
|
||||
$temp = $toX;
|
||||
$toX = $toY;
|
||||
$toY = $temp;
|
||||
}
|
||||
|
||||
$dx = abs($toX - $fromX);
|
||||
$dy = abs($toY - $fromY);
|
||||
$error = -$dx / 2;
|
||||
$xstep = $fromX < $toX ? 1 : -1;
|
||||
$ystep = $fromY < $toY ? 1 : -1;
|
||||
|
||||
// In black pixels, looking for white, first or second time.
|
||||
$state = 0;
|
||||
// Loop up until x == toX, but not beyond
|
||||
$xLimit = $toX + $xstep;
|
||||
for ($x = $fromX, $y = $fromY; $x != $xLimit; $x += $xstep) {
|
||||
$realX = $steep ? $y : $x;
|
||||
$realY = $steep ? $x : $y;
|
||||
|
||||
// Does current pixel mean we have moved white to black or vice versa?
|
||||
// Scanning black in state 0,2 and white in state 1, so if we find the wrong
|
||||
// color, advance to next state or end if we are in state 2 already
|
||||
if (($state == 1) == $this->image->get($realX, $realY)) {
|
||||
if ($state == 2) {
|
||||
return MathUtils::distance($x, $y, $fromX, $fromY);
|
||||
}
|
||||
$state++;
|
||||
}
|
||||
|
||||
$error += $dy;
|
||||
if ($error > 0) {
|
||||
if ($y == $toY) {
|
||||
break;
|
||||
}
|
||||
$y += $ystep;
|
||||
$error -= $dx;
|
||||
}
|
||||
}
|
||||
// Found black-white-black; give the benefit of the doubt that the next pixel outside the image
|
||||
// is "white" so this last po$at (toX+xStep,toY) is the right ending. This is really a
|
||||
// small approximation; (toX+xStep,toY+yStep) might be really correct. Ignore this.
|
||||
if ($state == 2) {
|
||||
return MathUtils::distance($toX + $xstep, $toY, $fromX, $fromY);
|
||||
}
|
||||
// else we didn't find even black-white-black; no estimate is really possible
|
||||
return NAN;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Attempts to locate an alignment pattern in a limited region of the image, which is
|
||||
* guessed to contain it. This method uses {@link AlignmentPattern}.</p>
|
||||
*
|
||||
* @param overallEstModuleSize estimated module size so far
|
||||
* @param estAlignmentX x coordinate of center of area probably containing alignment pattern
|
||||
* @param estAlignmentY y coordinate of above
|
||||
* @param allowanceFactor number of pixels in all directions to search from the center
|
||||
* @return {@link AlignmentPattern} if found, or null otherwise
|
||||
* @throws NotFoundException if an unexpected error occurs during detection
|
||||
*/
|
||||
protected final function findAlignmentInRegion($overallEstModuleSize,
|
||||
$estAlignmentX,
|
||||
$estAlignmentY,
|
||||
$allowanceFactor)
|
||||
{
|
||||
// Look for an alignment pattern (3 modules in size) around where it
|
||||
// should be
|
||||
$allowance = (int) ($allowanceFactor * $overallEstModuleSize);
|
||||
$alignmentAreaLeftX = max(0, $estAlignmentX - $allowance);
|
||||
$alignmentAreaRightX = min($this->image->getWidth() - 1, $estAlignmentX + $allowance);
|
||||
if ($alignmentAreaRightX - $alignmentAreaLeftX < $overallEstModuleSize * 3) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
$alignmentAreaTopY = max(0, $estAlignmentY - $allowance);
|
||||
$alignmentAreaBottomY = min($this->image->getHeight() - 1, $estAlignmentY + $allowance);
|
||||
if ($alignmentAreaBottomY - $alignmentAreaTopY < $overallEstModuleSize * 3) {
|
||||
throw NotFoundException::getNotFoundInstance();
|
||||
}
|
||||
|
||||
$alignmentFinder =
|
||||
new AlignmentPatternFinder(
|
||||
$this->image,
|
||||
$alignmentAreaLeftX,
|
||||
$alignmentAreaTopY,
|
||||
$alignmentAreaRightX - $alignmentAreaLeftX,
|
||||
$alignmentAreaBottomY - $alignmentAreaTopY,
|
||||
$overallEstModuleSize,
|
||||
$this->resultPointCallback);
|
||||
return $alignmentFinder->find();
|
||||
}
|
||||
|
||||
}
|
||||
81
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/FinderPattern.php
vendored
Normal file
81
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/FinderPattern.php
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Detector;
|
||||
|
||||
use Zxing\ResultPoint;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates a finder pattern, which are the three square patterns found in
|
||||
* the corners of QR Codes. It also encapsulates a count of similar finder patterns,
|
||||
* as a convenience to the finder's bookkeeping.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class FinderPattern extends ResultPoint {
|
||||
|
||||
private $estimatedModuleSize;
|
||||
private $count;
|
||||
|
||||
|
||||
|
||||
function __construct($posX, $posY, $estimatedModuleSize, $count=1) {
|
||||
parent::__construct($posX, $posY);
|
||||
$this->estimatedModuleSize = $estimatedModuleSize;
|
||||
$this->count = $count;
|
||||
}
|
||||
|
||||
public function getEstimatedModuleSize() {
|
||||
return $this->estimatedModuleSize;
|
||||
}
|
||||
|
||||
function getCount() {
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
/*
|
||||
void incrementCount() {
|
||||
this.count++;
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* <p>Determines if this finder pattern "about equals" a finder pattern at the stated
|
||||
* position and size -- meaning, it is at nearly the same center with nearly the same size.</p>
|
||||
*/
|
||||
function aboutEquals($moduleSize, $i, $j) {
|
||||
if (abs($i - $this->getY()) <= $moduleSize && abs($j - $this->getX()) <= $moduleSize) {
|
||||
$moduleSizeDiff = abs($moduleSize - $this->estimatedModuleSize);
|
||||
return $moduleSizeDiff <= 1.0 || $moduleSizeDiff <= $this->estimatedModuleSize;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines this object's current estimate of a finder pattern position and module size
|
||||
* with a new estimate. It returns a new {@code FinderPattern} containing a weighted average
|
||||
* based on count.
|
||||
*/
|
||||
function combineEstimate($i, $j, $newModuleSize) {
|
||||
$combinedCount = $this->count + 1;
|
||||
$combinedX = ($this->count * $this->getX() + $j) / $combinedCount;
|
||||
$combinedY = ($this->count * $this->getY() + $i) / $combinedCount;
|
||||
$combinedModuleSize = ($this->count * $this->estimatedModuleSize + $newModuleSize) / $combinedCount;
|
||||
return new FinderPattern($combinedX, $combinedY, $combinedModuleSize, $combinedCount);
|
||||
}
|
||||
|
||||
}
|
||||
705
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/FinderPatternFinder.php
vendored
Normal file
705
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/FinderPatternFinder.php
vendored
Normal file
@@ -0,0 +1,705 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Detector;
|
||||
|
||||
use Zxing\DecodeHintType;
|
||||
use Zxing\NotFoundException;
|
||||
use Zxing\ResultPoint;
|
||||
use Zxing\ResultPointCallback;
|
||||
use Zxing\Common\BitMatrix;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>This class attempts to find finder patterns in a QR Code. Finder patterns are the square
|
||||
* markers at three corners of a QR Code.</p>
|
||||
*
|
||||
* <p>This class is thread-safe but not reentrant. Each thread must allocate its own object.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
class FinderPatternFinder
|
||||
{
|
||||
|
||||
private static $CENTER_QUORUM = 2;
|
||||
protected static $MIN_SKIP = 3; // 1 pixel/module times 3 modules/center
|
||||
protected static $MAX_MODULES = 57; // support up to version 10 for mobile clients
|
||||
|
||||
private $image;
|
||||
private $average;
|
||||
private $possibleCenters; //private final List<FinderPattern> possibleCenters;
|
||||
private $hasSkipped = false;
|
||||
private $crossCheckStateCount;
|
||||
private $resultPointCallback;
|
||||
|
||||
/**
|
||||
* <p>Creates a finder that will search the image for three finder patterns.</p>
|
||||
*
|
||||
* @param image image to search
|
||||
*/
|
||||
public function __construct($image, $resultPointCallback = null)
|
||||
{
|
||||
$this->image = $image;
|
||||
|
||||
|
||||
$this->possibleCenters = array();//new ArrayList<>();
|
||||
$this->crossCheckStateCount = fill_array(0,5,0);
|
||||
$this->resultPointCallback = $resultPointCallback;
|
||||
}
|
||||
|
||||
protected final function getImage()
|
||||
{
|
||||
return $this->image;
|
||||
}
|
||||
|
||||
protected final function getPossibleCenters()
|
||||
{ //List<FinderPattern> getPossibleCenters()
|
||||
return $this->possibleCenters;
|
||||
}
|
||||
|
||||
final function find($hints)
|
||||
{/*final FinderPatternInfo find(Map<DecodeHintType,?> hints) throws NotFoundException {*/
|
||||
$tryHarder = $hints != null && $hints['TRY_HARDER'];
|
||||
$pureBarcode = $hints != null && $hints['PURE_BARCODE'];
|
||||
$maxI = $this->image->getHeight();
|
||||
$maxJ = $this->image->getWidth();
|
||||
// We are looking for black/white/black/white/black modules in
|
||||
// 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
|
||||
|
||||
// Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
|
||||
// image, and then account for the center being 3 modules in size. This gives the smallest
|
||||
// number of pixels the center could be, so skip this often. When trying harder, look for all
|
||||
// QR versions regardless of how dense they are.
|
||||
$iSkip = intval((3 * $maxI) / (4 * self::$MAX_MODULES));
|
||||
if ($iSkip < self::$MIN_SKIP || $tryHarder) {
|
||||
$iSkip = self::$MIN_SKIP;
|
||||
}
|
||||
|
||||
$done = false;
|
||||
$stateCount = array();
|
||||
for ($i = $iSkip - 1; $i < $maxI && !$done; $i += $iSkip) {
|
||||
// Get a row of black/white values
|
||||
$stateCount[0] = 0;
|
||||
$stateCount[1] = 0;
|
||||
$stateCount[2] = 0;
|
||||
$stateCount[3] = 0;
|
||||
$stateCount[4] = 0;
|
||||
$currentState = 0;
|
||||
for ($j = 0; $j < $maxJ; $j++) {
|
||||
if ($this->image->get($j, $i)) {
|
||||
// Black pixel
|
||||
if (($currentState & 1) == 1) { // Counting white pixels
|
||||
$currentState++;
|
||||
}
|
||||
$stateCount[$currentState]++;
|
||||
} else { // White pixel
|
||||
if (($currentState & 1) == 0) { // Counting black pixels
|
||||
if ($currentState == 4) { // A winner?
|
||||
if ($this->foundPatternCross($stateCount)) { // Yes
|
||||
$confirmed = $this->handlePossibleCenter($stateCount, $i, $j, $pureBarcode);
|
||||
if ($confirmed) {
|
||||
// Start examining every other line. Checking each line turned out to be too
|
||||
// expensive and didn't improve performance.
|
||||
$iSkip = 2;
|
||||
if ($this->hasSkipped) {
|
||||
$done = $this->haveMultiplyConfirmedCenters();
|
||||
} else {
|
||||
$rowSkip = $this->findRowSkip();
|
||||
if ($rowSkip > $stateCount[2]) {
|
||||
// Skip rows between row of lower confirmed center
|
||||
// and top of presumed third confirmed center
|
||||
// but back up a bit to get a full chance of detecting
|
||||
// it, entire width of center of finder pattern
|
||||
|
||||
// Skip by rowSkip, but back off by $stateCount[2] (size of last center
|
||||
// of pattern we saw) to be conservative, and also back off by iSkip which
|
||||
// is about to be re-added
|
||||
$i += $rowSkip - $stateCount[2] - $iSkip;
|
||||
$j = $maxJ - 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
|
||||
|
||||
$stateCount[0] = $stateCount[2];
|
||||
$stateCount[1] = $stateCount[3];
|
||||
$stateCount[2] = $stateCount[4];
|
||||
$stateCount[3] = 1;
|
||||
$stateCount[4] = 0;
|
||||
$currentState = 3;
|
||||
continue;
|
||||
}
|
||||
// Clear state to start looking again
|
||||
$currentState = 0;
|
||||
$stateCount[0] = 0;
|
||||
$stateCount[1] = 0;
|
||||
$stateCount[2] = 0;
|
||||
$stateCount[3] = 0;
|
||||
$stateCount[4] = 0;
|
||||
} else { // No, shift counts back by two
|
||||
$stateCount[0] = $stateCount[2];
|
||||
$stateCount[1] = $stateCount[3];
|
||||
$stateCount[2] = $stateCount[4];
|
||||
$stateCount[3] = 1;
|
||||
$stateCount[4] = 0;
|
||||
$currentState = 3;
|
||||
}
|
||||
} else {
|
||||
$stateCount[++$currentState]++;
|
||||
}
|
||||
} else { // Counting white pixels
|
||||
$stateCount[$currentState]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->foundPatternCross($stateCount)) {
|
||||
$confirmed = $this->handlePossibleCenter($stateCount, $i, $maxJ, $pureBarcode);
|
||||
if ($confirmed) {
|
||||
$iSkip = $stateCount[0];
|
||||
if ($this->hasSkipped) {
|
||||
// Found a third one
|
||||
$done = $this->haveMultiplyConfirmedCenters();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$patternInfo = $this->selectBestPatterns();
|
||||
$patternInfo = ResultPoint::orderBestPatterns($patternInfo);
|
||||
|
||||
return new FinderPatternInfo($patternInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a count of black/white/black/white/black pixels just seen and an end position,
|
||||
* figures the location of the center of this run.
|
||||
*/
|
||||
private static function centerFromEnd($stateCount, $end)
|
||||
{
|
||||
return (float)($end - $stateCount[4] - $stateCount[3]) - $stateCount[2] / 2.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $stateCount ; count of black/white/black/white/black pixels just read
|
||||
* @return true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios
|
||||
* used by finder patterns to be considered a match
|
||||
*/
|
||||
protected static function foundPatternCross($stateCount)
|
||||
{
|
||||
$totalModuleSize = 0;
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$count = $stateCount[$i];
|
||||
if ($count == 0) {
|
||||
return false;
|
||||
}
|
||||
$totalModuleSize += $count;
|
||||
}
|
||||
if ($totalModuleSize < 7) {
|
||||
return false;
|
||||
}
|
||||
$moduleSize = $totalModuleSize / 7.0;
|
||||
$maxVariance = $moduleSize / 2.0;
|
||||
// Allow less than 50% variance from 1-1-3-1-1 proportions
|
||||
return
|
||||
abs($moduleSize - $stateCount[0]) < $maxVariance &&
|
||||
abs($moduleSize - $stateCount[1]) < $maxVariance &&
|
||||
abs(3.0 * $moduleSize - $stateCount[2]) < 3 * $maxVariance &&
|
||||
abs($moduleSize - $stateCount[3]) < $maxVariance &&
|
||||
abs($moduleSize - $stateCount[4]) < $maxVariance;
|
||||
}
|
||||
|
||||
private function getCrossCheckStateCount()
|
||||
{
|
||||
$this->crossCheckStateCount[0] = 0;
|
||||
$this->crossCheckStateCount[1] = 0;
|
||||
$this->crossCheckStateCount[2] = 0;
|
||||
$this->crossCheckStateCount[3] = 0;
|
||||
$this->crossCheckStateCount[4] = 0;
|
||||
return $this->crossCheckStateCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* After a vertical and horizontal scan finds a potential finder pattern, this method
|
||||
* "cross-cross-cross-checks" by scanning down diagonally through the center of the possible
|
||||
* finder pattern to see if the same proportion is detected.
|
||||
*
|
||||
* @param $startI ; row where a finder pattern was detected
|
||||
* @param centerJ ; center of the section that appears to cross a finder pattern
|
||||
* @param $maxCount ; maximum reasonable number of modules that should be
|
||||
* observed in any reading state, based on the results of the horizontal scan
|
||||
* @param originalStateCountTotal ; The original state count total.
|
||||
* @return true if proportions are withing expected limits
|
||||
*/
|
||||
private function crossCheckDiagonal($startI, $centerJ, $maxCount, $originalStateCountTotal)
|
||||
{
|
||||
$stateCount = $this->getCrossCheckStateCount();
|
||||
|
||||
// Start counting up, left from center finding black center mass
|
||||
$i = 0;
|
||||
$startI = intval($startI);
|
||||
$centerJ = intval($centerJ);
|
||||
while ($startI >= $i && $centerJ >= $i && $this->image->get($centerJ - $i, $startI - $i)) {
|
||||
$stateCount[2]++;
|
||||
$i++;
|
||||
}
|
||||
|
||||
if ($startI < $i || $centerJ < $i) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Continue up, left finding white space
|
||||
while ($startI >= $i && $centerJ >= $i && !$this->image->get($centerJ - $i, $startI - $i) &&
|
||||
$stateCount[1] <= $maxCount) {
|
||||
$stateCount[1]++;
|
||||
$i++;
|
||||
}
|
||||
|
||||
// If already too many modules in this state or ran off the edge:
|
||||
if ($startI < $i || $centerJ < $i || $stateCount[1] > $maxCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Continue up, left finding black border
|
||||
while ($startI >= $i && $centerJ >= $i && $this->image->get($centerJ - $i, $startI - $i) &&
|
||||
$stateCount[0] <= $maxCount) {
|
||||
$stateCount[0]++;
|
||||
$i++;
|
||||
}
|
||||
if ($stateCount[0] > $maxCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$maxI = $this->image->getHeight();
|
||||
$maxJ = $this->image->getWidth();
|
||||
|
||||
// Now also count down, right from center
|
||||
$i = 1;
|
||||
while ($startI + $i < $maxI && $centerJ + $i < $maxJ && $this->image->get($centerJ + $i, $startI + $i)) {
|
||||
$stateCount[2]++;
|
||||
$i++;
|
||||
}
|
||||
|
||||
// Ran off the edge?
|
||||
if ($startI + $i >= $maxI || $centerJ + $i >= $maxJ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while ($startI + $i < $maxI && $centerJ + $i < $maxJ && !$this->image->get($centerJ + $i, $startI + $i) &&
|
||||
$stateCount[3] < $maxCount) {
|
||||
$stateCount[3]++;
|
||||
$i++;
|
||||
}
|
||||
|
||||
if ($startI + $i >= $maxI || $centerJ + $i >= $maxJ || $stateCount[3] >= $maxCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while ($startI + $i < $maxI && $centerJ + $i < $maxJ && $this->image->get($centerJ + $i, $startI + $i) &&
|
||||
$stateCount[4] < $maxCount) {
|
||||
$stateCount[4]++;
|
||||
$i++;
|
||||
}
|
||||
|
||||
if ($stateCount[4] >= $maxCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we found a finder-pattern-like section, but its size is more than 100% different than
|
||||
// the original, assume it's a false positive
|
||||
$stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] + $stateCount[4];
|
||||
return
|
||||
abs($stateCountTotal - $originalStateCountTotal) < 2 * $originalStateCountTotal &&
|
||||
$this->foundPatternCross($stateCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>After a horizontal scan finds a potential finder pattern, this method
|
||||
* "cross-checks" by scanning down vertically through the center of the possible
|
||||
* finder pattern to see if the same proportion is detected.</p>
|
||||
*
|
||||
* @param $startI ; row where a finder pattern was detected
|
||||
* @param centerJ ; center of the section that appears to cross a finder pattern
|
||||
* @param $maxCount ; maximum reasonable number of modules that should be
|
||||
* observed in any reading state, based on the results of the horizontal scan
|
||||
* @return vertical center of finder pattern, or {@link Float#NaN} if not found
|
||||
*/
|
||||
private function crossCheckVertical($startI, $centerJ, $maxCount,
|
||||
$originalStateCountTotal)
|
||||
{
|
||||
$image = $this->image;
|
||||
|
||||
$maxI = $image->getHeight();
|
||||
$stateCount = $this->getCrossCheckStateCount();
|
||||
|
||||
// Start counting up from center
|
||||
$i = $startI;
|
||||
while ($i >= 0 && $image->get($centerJ, $i)) {
|
||||
$stateCount[2]++;
|
||||
$i--;
|
||||
}
|
||||
if ($i < 0) {
|
||||
return NAN;
|
||||
}
|
||||
while ($i >= 0 && !$image->get($centerJ, $i) && $stateCount[1] <= $maxCount) {
|
||||
$stateCount[1]++;
|
||||
$i--;
|
||||
}
|
||||
// If already too many modules in this state or ran off the edge:
|
||||
if ($i < 0 || $stateCount[1] > $maxCount) {
|
||||
return NAN;
|
||||
}
|
||||
while ($i >= 0 && $image->get($centerJ, $i) && $stateCount[0] <= $maxCount) {
|
||||
$stateCount[0]++;
|
||||
$i--;
|
||||
}
|
||||
if ($stateCount[0] > $maxCount) {
|
||||
return NAN;
|
||||
}
|
||||
|
||||
// Now also count down from center
|
||||
$i = $startI + 1;
|
||||
while ($i < $maxI && $image->get($centerJ, $i)) {
|
||||
$stateCount[2]++;
|
||||
$i++;
|
||||
}
|
||||
if ($i == $maxI) {
|
||||
return NAN;
|
||||
}
|
||||
while ($i < $maxI && !$image->get($centerJ, $i) && $stateCount[3] < $maxCount) {
|
||||
$stateCount[3]++;
|
||||
$i++;
|
||||
}
|
||||
if ($i == $maxI || $stateCount[3] >= $maxCount) {
|
||||
return NAN;
|
||||
}
|
||||
while ($i < $maxI && $image->get($centerJ, $i) && $stateCount[4] < $maxCount) {
|
||||
$stateCount[4]++;
|
||||
$i++;
|
||||
}
|
||||
if ($stateCount[4] >= $maxCount) {
|
||||
return NAN;
|
||||
}
|
||||
|
||||
// If we found a finder-pattern-like section, but its size is more than 40% different than
|
||||
// the original, assume it's a false positive
|
||||
$stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] +
|
||||
$stateCount[4];
|
||||
if (5 * abs($stateCountTotal - $originalStateCountTotal) >= 2 * $originalStateCountTotal) {
|
||||
return NAN;
|
||||
}
|
||||
|
||||
return $this->foundPatternCross($stateCount) ? $this->centerFromEnd($stateCount, $i) : NAN;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Like {@link #crossCheckVertical(int, int, int, int)}, and in fact is basically identical,
|
||||
* except it reads horizontally instead of vertically. This is used to cross-cross
|
||||
* check a vertical cross check and locate the real center of the alignment pattern.</p>
|
||||
*/
|
||||
private function crossCheckHorizontal($startJ, $centerI, $maxCount,
|
||||
$originalStateCountTotal)
|
||||
{
|
||||
$image = $this->image;
|
||||
|
||||
$maxJ = $this->image->getWidth();
|
||||
$stateCount = $this->getCrossCheckStateCount();
|
||||
|
||||
$j = $startJ;
|
||||
while ($j >= 0 && $image->get($j, $centerI)) {
|
||||
$stateCount[2]++;
|
||||
$j--;
|
||||
}
|
||||
if ($j < 0) {
|
||||
return NAN;
|
||||
}
|
||||
while ($j >= 0 && !$image->get($j, $centerI) && $stateCount[1] <= $maxCount) {
|
||||
$stateCount[1]++;
|
||||
$j--;
|
||||
}
|
||||
if ($j < 0 || $stateCount[1] > $maxCount) {
|
||||
return NAN;
|
||||
}
|
||||
while ($j >= 0 && $image->get($j, $centerI) && $stateCount[0] <= $maxCount) {
|
||||
$stateCount[0]++;
|
||||
$j--;
|
||||
}
|
||||
if ($stateCount[0] > $maxCount) {
|
||||
return NAN;
|
||||
}
|
||||
|
||||
$j = $startJ + 1;
|
||||
while ($j < $maxJ && $image->get($j, $centerI)) {
|
||||
$stateCount[2]++;
|
||||
$j++;
|
||||
}
|
||||
if ($j == $maxJ) {
|
||||
return NAN;
|
||||
}
|
||||
while ($j < $maxJ && !$image->get($j, $centerI) && $stateCount[3] < $maxCount) {
|
||||
$stateCount[3]++;
|
||||
$j++;
|
||||
}
|
||||
if ($j == $maxJ || $stateCount[3] >= $maxCount) {
|
||||
return NAN;
|
||||
}
|
||||
while ($j < $maxJ && $this->image->get($j, $centerI) && $stateCount[4] < $maxCount) {
|
||||
$stateCount[4]++;
|
||||
$j++;
|
||||
}
|
||||
if ($stateCount[4] >= $maxCount) {
|
||||
return NAN;
|
||||
}
|
||||
|
||||
// If we found a finder-pattern-like section, but its size is significantly different than
|
||||
// the original, assume it's a false positive
|
||||
$stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] +
|
||||
$stateCount[4];
|
||||
if (5 * abs($stateCountTotal - $originalStateCountTotal) >= $originalStateCountTotal) {
|
||||
return NAN;
|
||||
}
|
||||
|
||||
return $this->foundPatternCross($stateCount) ? $this->centerFromEnd($stateCount, $j) : NAN;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>This is called when a horizontal scan finds a possible alignment pattern. It will
|
||||
* cross check with a vertical scan, and if successful, will, ah, cross-cross-check
|
||||
* with another horizontal scan. This is needed primarily to locate the real horizontal
|
||||
* center of the pattern in cases of extreme skew.
|
||||
* And then we cross-cross-cross check with another diagonal scan.</p>
|
||||
*
|
||||
* <p>If that succeeds the finder pattern location is added to a list that tracks
|
||||
* the number of times each location has been nearly-matched as a finder pattern.
|
||||
* Each additional find is more evidence that the location is in fact a finder
|
||||
* pattern center
|
||||
*
|
||||
* @param $stateCount reading state module counts from horizontal scan
|
||||
* @param i row where finder pattern may be found
|
||||
* @param j end of possible finder pattern in row
|
||||
* @param pureBarcode true if in "pure barcode" mode
|
||||
* @return true if a finder pattern candidate was found this time
|
||||
*/
|
||||
protected final function handlePossibleCenter($stateCount, $i, $j, $pureBarcode)
|
||||
{
|
||||
$stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] +
|
||||
$stateCount[4];
|
||||
$centerJ = $this->centerFromEnd($stateCount, $j);
|
||||
$centerI = $this->crossCheckVertical($i, intval($centerJ), $stateCount[2], $stateCountTotal);
|
||||
if (!is_nan($centerI)) {
|
||||
// Re-cross check
|
||||
$centerJ = $this->crossCheckHorizontal(intval($centerJ), intval($centerI), $stateCount[2], $stateCountTotal);
|
||||
if (!is_nan($centerJ) &&
|
||||
(!$pureBarcode || $this->crossCheckDiagonal(intval($centerI), intval($centerJ), $stateCount[2], $stateCountTotal))
|
||||
) {
|
||||
$estimatedModuleSize = (float)$stateCountTotal / 7.0;
|
||||
$found = false;
|
||||
for ($index = 0; $index < count($this->possibleCenters); $index++) {
|
||||
$center = $this->possibleCenters[$index];
|
||||
// Look for about the same center and module size:
|
||||
if ($center->aboutEquals($estimatedModuleSize, $centerI, $centerJ)) {
|
||||
$this->possibleCenters[$index] = $center->combineEstimate($centerI, $centerJ, $estimatedModuleSize);
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$found) {
|
||||
$point = new FinderPattern($centerJ, $centerI, $estimatedModuleSize);
|
||||
$this->possibleCenters[] = $point;
|
||||
if ($this->resultPointCallback != null) {
|
||||
$this->resultPointCallback->foundPossibleResultPoint($point);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number of rows we could safely skip during scanning, based on the first
|
||||
* two finder patterns that have been located. In some cases their position will
|
||||
* allow us to infer that the third pattern must lie below a certain point farther
|
||||
* down in the image.
|
||||
*/
|
||||
private function findRowSkip()
|
||||
{
|
||||
$max = count($this->possibleCenters);
|
||||
if ($max <= 1) {
|
||||
return 0;
|
||||
}
|
||||
$firstConfirmedCenter = null;
|
||||
foreach ($this->possibleCenters as $center) {
|
||||
|
||||
|
||||
if ($center->getCount() >= self::$CENTER_QUORUM) {
|
||||
if ($firstConfirmedCenter == null) {
|
||||
$firstConfirmedCenter = $center;
|
||||
} else {
|
||||
// We have two confirmed centers
|
||||
// How far down can we skip before resuming looking for the next
|
||||
// pattern? In the worst case, only the difference between the
|
||||
// difference in the x / y coordinates of the two centers.
|
||||
// This is the case where you find top left last.
|
||||
$this->hasSkipped = true;
|
||||
return intval((abs($firstConfirmedCenter->getX() - $center->getX()) -
|
||||
abs($firstConfirmedCenter->getY() - $center->getY())) / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true iff we have found at least 3 finder patterns that have been detected
|
||||
* at least {@link #CENTER_QUORUM} times each, and, the estimated module size of the
|
||||
* candidates is "pretty similar"
|
||||
*/
|
||||
private function haveMultiplyConfirmedCenters()
|
||||
{
|
||||
$confirmedCount = 0;
|
||||
$totalModuleSize = 0.0;
|
||||
$max = count($this->possibleCenters);
|
||||
foreach ($this->possibleCenters as $pattern) {
|
||||
if ($pattern->getCount() >= self::$CENTER_QUORUM) {
|
||||
$confirmedCount++;
|
||||
$totalModuleSize += $pattern->getEstimatedModuleSize();
|
||||
}
|
||||
}
|
||||
if ($confirmedCount < 3) {
|
||||
return false;
|
||||
}
|
||||
// OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive"
|
||||
// and that we need to keep looking. We detect this by asking if the estimated module sizes
|
||||
// vary too much. We arbitrarily say that when the total deviation from average exceeds
|
||||
// 5% of the total module size estimates, it's too much.
|
||||
$average = $totalModuleSize / (float)$max;
|
||||
$totalDeviation = 0.0;
|
||||
foreach ($this->possibleCenters as $pattern) {
|
||||
$totalDeviation += abs($pattern->getEstimatedModuleSize() - $average);
|
||||
}
|
||||
return $totalDeviation <= 0.05 * $totalModuleSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are
|
||||
* those that have been detected at least {@link #CENTER_QUORUM} times, and whose module
|
||||
* size differs from the average among those patterns the least
|
||||
* @throws NotFoundException if 3 such finder patterns do not exist
|
||||
*/
|
||||
private function selectBestPatterns()
|
||||
{
|
||||
$startSize = count($this->possibleCenters);
|
||||
if ($startSize < 3) {
|
||||
// Couldn't find enough finder patterns
|
||||
throw new NotFoundException;
|
||||
}
|
||||
|
||||
// Filter outlier possibilities whose module size is too different
|
||||
if ($startSize > 3) {
|
||||
// But we can only afford to do so if we have at least 4 possibilities to choose from
|
||||
$totalModuleSize = 0.0;
|
||||
$square = 0.0;
|
||||
foreach ($this->possibleCenters as $center) {
|
||||
$size = $center->getEstimatedModuleSize();
|
||||
$totalModuleSize += $size;
|
||||
$square += $size * $size;
|
||||
}
|
||||
$this->average = $totalModuleSize / (float)$startSize;
|
||||
$stdDev = (float)sqrt($square / $startSize - $this->average * $this->average);
|
||||
|
||||
usort($this->possibleCenters, array($this,'FurthestFromAverageComparator'));
|
||||
|
||||
$limit = max(0.2 * $this->average, $stdDev);
|
||||
|
||||
for ($i = 0; $i < count($this->possibleCenters) && count($this->possibleCenters) > 3; $i++) {
|
||||
$pattern = $this->possibleCenters[$i];
|
||||
if (abs($pattern->getEstimatedModuleSize() - $this->average) > $limit) {
|
||||
unset($this->possibleCenters[$i]);//возможно что ключи меняются в java при вызове .remove(i) ???
|
||||
$this->possibleCenters = array_values($this->possibleCenters);
|
||||
$i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($this->possibleCenters) > 3) {
|
||||
// Throw away all but those first size candidate points we found.
|
||||
|
||||
$totalModuleSize = 0.0;
|
||||
foreach ($this->possibleCenters as $possibleCenter) {
|
||||
$totalModuleSize += $possibleCenter->getEstimatedModuleSize();
|
||||
}
|
||||
|
||||
$this->average = $totalModuleSize / (float)count($this->possibleCenters);
|
||||
|
||||
usort($this->possibleCenters, array($this,'CenterComparator'));
|
||||
|
||||
array_slice($this->possibleCenters, 3, count($this->possibleCenters) - 3);
|
||||
|
||||
|
||||
}
|
||||
|
||||
return array($this->possibleCenters[0], $this->possibleCenters[1], $this->possibleCenters[2]);
|
||||
|
||||
|
||||
}
|
||||
/**
|
||||
* <p>Orders by furthest from average</p>
|
||||
*/
|
||||
public function FurthestFromAverageComparator($center1, $center2)
|
||||
{
|
||||
|
||||
$dA = abs($center2->getEstimatedModuleSize() - $this->average);
|
||||
$dB = abs($center1->getEstimatedModuleSize() - $this->average);
|
||||
if ($dA < $dB) {
|
||||
return -1;
|
||||
} elseif ($dA == $dB) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <p>Orders by {@link FinderPattern#getCount()}, descending.</p>
|
||||
*/
|
||||
|
||||
//@Override
|
||||
public function CenterComparator($center1, $center2) {
|
||||
if ($center2->getCount() == $center1->getCount()) {
|
||||
$dA = abs($center2->getEstimatedModuleSize() - $this->average);
|
||||
$dB = abs($center1->getEstimatedModuleSize() - $this->average);
|
||||
if($dA < $dB){
|
||||
return 1;
|
||||
}elseif( $dA == $dB){
|
||||
return 0;
|
||||
}else{
|
||||
return -1;
|
||||
}
|
||||
|
||||
} else {
|
||||
return $center2->getCount() - $center1->getCount();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
50
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/FinderPatternInfo.php
vendored
Normal file
50
vendor/khanamiryan/qrcode-detector-decoder/lib/qrcode/detector/FinderPatternInfo.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Zxing\Qrcode\Detector;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates information about finder patterns in an image, including the location of
|
||||
* the three finder patterns, and their estimated module size.</p>
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
final class FinderPatternInfo {
|
||||
|
||||
private $bottomLeft;
|
||||
private $topLeft;
|
||||
private $topRight;
|
||||
|
||||
public function __construct($patternCenters) {
|
||||
$this->bottomLeft = $patternCenters[0];
|
||||
$this->topLeft = $patternCenters[1];
|
||||
$this->topRight = $patternCenters[2];
|
||||
}
|
||||
|
||||
public function getBottomLeft() {
|
||||
return $this->bottomLeft;
|
||||
}
|
||||
|
||||
public function getTopLeft() {
|
||||
return $this->topLeft;
|
||||
}
|
||||
|
||||
public function getTopRight() {
|
||||
return $this->topRight;
|
||||
}
|
||||
|
||||
}
|
||||
10
vendor/khanamiryan/qrcode-detector-decoder/phpunit.xml.dist
vendored
Normal file
10
vendor/khanamiryan/qrcode-detector-decoder/phpunit.xml.dist
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<phpunit bootstrap="tests/bootstrap.php">
|
||||
<testsuite>
|
||||
<directory suffix="Test.php">tests</directory>
|
||||
</testsuite>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
15
vendor/khanamiryan/qrcode-detector-decoder/tests/QrReaderTest.php
vendored
Normal file
15
vendor/khanamiryan/qrcode-detector-decoder/tests/QrReaderTest.php
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Khanamiryan\QrCodeTests;
|
||||
|
||||
class QrReaderTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function testText1()
|
||||
{
|
||||
$image = __DIR__ . "/qrcodes/hello_world.png";
|
||||
|
||||
$qrcode = new \QrReader($image);
|
||||
$this->assertSame("Hello world!", $qrcode->text());
|
||||
}
|
||||
}
|
||||
3
vendor/khanamiryan/qrcode-detector-decoder/tests/bootstrap.php
vendored
Normal file
3
vendor/khanamiryan/qrcode-detector-decoder/tests/bootstrap.php
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
require __DIR__ . "/../vendor/autoload.php";
|
||||
BIN
vendor/khanamiryan/qrcode-detector-decoder/tests/qrcodes/hello_world.png
vendored
Normal file
BIN
vendor/khanamiryan/qrcode-detector-decoder/tests/qrcodes/hello_world.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
Reference in New Issue
Block a user