Zhu Jinhui 7 سال پیش
والد
کامیت
cf2bf19d41
55فایلهای تغییر یافته به همراه8983 افزوده شده و 6 حذف شده
  1. 2 2
      src/Application.php
  2. 2 2
      src/Autoloader/Import.php
  3. 1 1
      src/Autoloader/Psr4.php
  4. 1 1
      src/Language/Loader.php
  5. 96 0
      src/Library/QrReader/Binarizer.php
  6. 166 0
      src/Library/QrReader/BinaryBitmap.php
  7. 42 0
      src/Library/QrReader/ChecksumException.php
  8. 104 0
      src/Library/QrReader/Common/AbstractEnum.php
  9. 422 0
      src/Library/QrReader/Common/BitArray.php
  10. 102 0
      src/Library/QrReader/Common/BitMatrix.php
  11. 118 0
      src/Library/QrReader/Common/BitSource.php
  12. 158 0
      src/Library/QrReader/Common/CharacterSetECI.php
  13. 122 0
      src/Library/QrReader/Common/DecoderResult.php
  14. 93 0
      src/Library/QrReader/Common/DefaultGridSampler.php
  15. 49 0
      src/Library/QrReader/Common/Detector/MathUtils.php
  16. 234 0
      src/Library/QrReader/Common/Detector/MonochromeRectangleDetector.php
  17. 47 0
      src/Library/QrReader/Common/DetectorResult.php
  18. 207 0
      src/Library/QrReader/Common/GlobalHistogramBinarizer.php
  19. 188 0
      src/Library/QrReader/Common/GridSampler.php
  20. 264 0
      src/Library/QrReader/Common/HybridBinarizer.php
  21. 175 0
      src/Library/QrReader/Common/PerspectiveTransform.php
  22. 198 0
      src/Library/QrReader/Common/Reedsolomon/GenericGF.php
  23. 289 0
      src/Library/QrReader/Common/Reedsolomon/GenericGFPoly.php
  24. 201 0
      src/Library/QrReader/Common/Reedsolomon/ReedSolomonDecoder.php
  25. 28 0
      src/Library/QrReader/Common/Reedsolomon/ReedSolomonException.php
  26. 101 0
      src/Library/QrReader/Common/customFunctions.php
  27. 49 0
      src/Library/QrReader/FormatException.php
  28. 176 0
      src/Library/QrReader/GDLuminanceSource.php
  29. 150 0
      src/Library/QrReader/IMagickLuminanceSource.php
  30. 171 0
      src/Library/QrReader/LuminanceSource.php
  31. 38 0
      src/Library/QrReader/NotFoundException.php
  32. 182 0
      src/Library/QrReader/PlanarYUVLuminanceSource.php
  33. 105 0
      src/Library/QrReader/QrReader.php
  34. 263 0
      src/Library/QrReader/Qrcode/Decoder/BitMatrixParser.php
  35. 129 0
      src/Library/QrReader/Qrcode/Decoder/DataBlock.php
  36. 196 0
      src/Library/QrReader/Qrcode/Decoder/DataMask.php
  37. 355 0
      src/Library/QrReader/Qrcode/Decoder/DecodedBitStreamParser.php
  38. 213 0
      src/Library/QrReader/Qrcode/Decoder/Decoder.php
  39. 91 0
      src/Library/QrReader/Qrcode/Decoder/ErrorCorrectionLevel.php
  40. 190 0
      src/Library/QrReader/Qrcode/Decoder/FormatInformation.php
  41. 128 0
      src/Library/QrReader/Qrcode/Decoder/Mode.php
  42. 619 0
      src/Library/QrReader/Qrcode/Decoder/Version.php
  43. 65 0
      src/Library/QrReader/Qrcode/Detector/AlignmentPattern.php
  44. 286 0
      src/Library/QrReader/Qrcode/Detector/AlignmentPatternFinder.php
  45. 420 0
      src/Library/QrReader/Qrcode/Detector/Detector.php
  46. 86 0
      src/Library/QrReader/Qrcode/Detector/FinderPattern.php
  47. 699 0
      src/Library/QrReader/Qrcode/Detector/FinderPatternFinder.php
  48. 53 0
      src/Library/QrReader/Qrcode/Detector/FinderPatternInfo.php
  49. 222 0
      src/Library/QrReader/Qrcode/QRCodeReader.php
  50. 309 0
      src/Library/QrReader/RGBLuminanceSource.php
  51. 10 0
      src/Library/QrReader/Reader.php
  52. 50 0
      src/Library/QrReader/ReaderException.php
  53. 135 0
      src/Library/QrReader/Result.php
  54. 155 0
      src/Library/QrReader/ResultPoint.php
  55. 28 0
      src/Library/Qrcode.php

+ 2 - 2
src/Application.php

@@ -175,7 +175,7 @@ class Application
             $ini,
             $env
         )
-        ) throw new \Qii\Exceptions\FileNotFound(\Qii::i(1405, $ini), __LINE__);
+        ) throw new \Qii\Exceptions\FileNotFound(\Qii::i(1405, $ini), 404);
         //载入request方法
         $this->request = Psr4::getInstance()->loadClass('\Qii\Request\Http');
         Setting::getInstance()->setDefaultTimeZone();
@@ -312,7 +312,7 @@ class Application
             $env
         )
         ) {
-            throw new \Qii\Exceptions\FileNotFound(\Qii::i(1405, $ini), __LINE__);
+            throw new \Qii\Exceptions\FileNotFound(\Qii::i(1405, $ini), 404);
         }
         return $this;
     }

+ 2 - 2
src/Autoloader/Import.php

@@ -50,7 +50,7 @@ class Import
             return $configure;
         }else{
             //print_r(debug_backtrace());
-            throw new \Qii\Exceptions\FileNotFound(\Qii::i(1405, $file), __LINE__);
+            throw new \Qii\Exceptions\FileNotFound(\Qii::i(1405, $file), 404);
         }
         return false;
     }
@@ -74,7 +74,7 @@ class Import
      */
     public static function requireByDir($dir)
     {
-        if (!is_dir($dir)) throw new \Qii\Exceptions\FileNotFound(\Qii::i(1405, $dir), __LINE__);
+        if (!is_dir($dir)) throw new \Qii\Exceptions\FileNotFound(\Qii::i(1405, $dir), 404);
         $files = self::findFiles($dir, array('php'));
         if (isset($files['php'])) self::requires($files['php']);
     }

+ 1 - 1
src/Autoloader/Psr4.php

@@ -277,7 +277,7 @@ class Psr4
         if(!$this->searchMappedFile($class))
         {
             $notLoaded = isset(self::$lastErrorLoadedFile[$class]) ? self::$lastErrorLoadedFile[$class] : self::getClassName($class);
-            throw new \Qii\Exceptions\FileNotFound(\Qii::i(1405, $notLoaded), __LINE__);
+            throw new \Qii\Exceptions\FileNotFound(\Qii::i(1405, $notLoaded), 404);
         }
     }
 

+ 1 - 1
src/Language/Loader.php

@@ -70,7 +70,7 @@ class Loader
 		if (is_file($fileName)) {
 			return $this->merge($fileName);
 		}
-		throw new \Qii\Exceptions\FileNotFound(\Qii::i(1405, $fileName), __LINE__);
+		throw new \Qii\Exceptions\FileNotFound(\Qii::i(1405, $fileName), 404);
 	}
 
 	/**

+ 96 - 0
src/Library/QrReader/Binarizer.php

@@ -0,0 +1,96 @@
+<?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;
+    }
+
+    /**
+     * @return LuminanceSource
+     */
+    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 array 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 BitMatrix 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 Binarizer 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();
+    }
+}

+ 166 - 0
src/Library/QrReader/BinaryBitmap.php

@@ -0,0 +1,166 @@
+<?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\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 $binarizer)
+    {
+        if ($binarizer === null) {
+            throw new \InvalidArgumentException("Binarizer must be non-null.");
+        }
+        $this->binarizer = $binarizer;
+    }
+
+    /**
+     * @return int The width of the bitmap.
+     */
+    public function getWidth()
+    {
+        return $this->binarizer->getWidth();
+    }
+
+    /**
+     * @return int 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 array 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);
+    }
+
+    /**
+     * @return bool 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 BinaryBitmap 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 BinaryBitmap 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 BinaryBitmap A rotated version of this object.
+     */
+    public function rotateCounterClockwise45()
+    {
+        $newSource = $this->binarizer->getLuminanceSource()->rotateCounterClockwise45();
+
+        return new BinaryBitmap($this->binarizer->createBinarizer($newSource));
+    }
+
+    public function toString()
+    {
+        try {
+            return $this->getBlackMatrix()->toString();
+        } catch (NotFoundException $e) {
+        }
+
+        return '';
+    }
+
+    /**
+     * 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 BitMatrix 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;
+    }
+}

+ 42 - 0
src/Library/QrReader/ChecksumException.php

@@ -0,0 +1,42 @@
+<?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;
+        }
+    }
+}

+ 104 - 0
src/Library/QrReader/Common/AbstractEnum.php

@@ -0,0 +1,104 @@
+<?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 \UnexpectedValueException('Value not a const in enum ' . get_class($this));
+        }
+        $this->value = $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 current value.
+     *
+     * @return mixed
+     */
+    public function get()
+    {
+        return $this->value;
+    }
+
+    /**
+     * Gets the name of the enum.
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return (string)array_search($this->value, $this->getConstList());
+    }
+}

+ 422 - 0
src/Library/QrReader/Common/BitArray.php

@@ -0,0 +1,422 @@
+<?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 = [], $size = 0)
+    {
+
+        if (!$bits && !$size) {
+            $this->$size = 0;
+            $this->bits  = [];
+        } elseif ($bits && !$size) {
+            $this->size = $bits;
+            $this->bits = $this->makeArray($bits);
+        } else {
+            $this->bits = $bits;
+            $this->size = $size;
+        }
+
+    }
+
+    private static function makeArray($size)
+    {
+        return [];
+    }
+
+    public function getSize()
+    {
+        return $this->size;
+    }
+
+    public function getSizeInBytes()
+    {
+        return ($this->size + 7) / 8;
+    }
+
+    /**
+     * Sets bit i.
+     *
+     * @param i bit to set
+     */
+    public function set($i)
+    {
+        $this->bits[(int)($i / 32)] |= 1 << ($i & 0x1F);
+        $this->bits[(int)($i / 32)] = ($this->bits[(int)($i / 32)]);
+    }
+
+    /**
+     * Flips bit i.
+     *
+     * @param i bit to set
+     */
+    public function flip($i)
+    {
+        $this->bits[(int)($i / 32)] ^= 1 << ($i & 0x1F);
+        $this->bits[(int)($i / 32)] = ($this->bits[(int)($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  = (int)($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  = (int)($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 = (~$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[(int)($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 = (int)($start / 32);
+        $lastInt  = (int)($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] = ($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 = (int)($start / 32);
+        $lastInt  = (int)($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 = ($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;
+    }
+
+    /**
+     * 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);
+        }
+    }
+
+    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;
+        }
+    }
+
+    public function appendBit($bit)
+    {
+        $this->ensureCapacity($this->size + 1);
+        if ($bit) {
+            $this->bits[(int)($this->size / 32)] |= 1 << ($this->size & 0x1F);
+        }
+        $this->size++;
+    }
+
+    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");
+        }
+        $count = count($this->bits);
+        for ($i = 0; $i < $count; $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;
+        }
+    }
+
+    /**
+     * @param $i ; bit to get
+     *
+     * @return true iff bit i is set
+     */
+    public function get($i)
+    {
+        $key = (int)($i / 32);
+
+        return ($this->bits[$key] & (1 << ($i & 0x1F))) != 0;
+    }
+
+    /**
+     * @return array 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 = [];
+        // 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[(int)($i) - 1] = $currentInt;
+                $currentInt             = ($nextInt >> $leftOffset) & $mask;
+            }
+            $newBits[(int)($oldBitsLen) - 1] = $currentInt;
+        }
+//        $bits = $newBits;
+    }
+
+    public function equals($o)
+    {
+        if (!($o instanceof BitArray)) {
+            return false;
+        }
+        $other = $o;
+
+        return $this->size == $other->size && $this->bits === $other->bits;
+    }
+
+    public function hashCode()
+    {
+        return 31 * $this->size + hashCode($this->bits);
+    }
+
+    public function toString()
+    {
+        $result = '';
+        for ($i = 0; $i < $this->size; $i++) {
+            if (($i & 0x07) == 0) {
+                $result .= ' ';
+            }
+            $result .= ($this->get($i) ? 'X' : '.');
+        }
+
+        return (string)$result;
+    }
+
+    public function _clone()
+    {
+        return new BitArray($this->bits, $this->size);
+    }
+}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 102 - 0
src/Library/QrReader/Common/BitMatrix.php


+ 118 - 0
src/Library/QrReader/Common/BitSource.php

@@ -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\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;
+    }
+}

+ 158 - 0
src/Library/QrReader/Common/CharacterSetECI.php

@@ -0,0 +1,158 @@
+<?php
+
+namespace Zxing\Common;
+
+/**
+ * Encapsulates a Character Set ECI, according to "Extended Channel
+ * Interpretations" 5.3.1.1 of ISO 18004.
+ */
+final class CharacterSetECI
+{
+    /**#@+
+     * 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 = [
+        '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 = [
+        self::CP437 => 2,
+        self::ASCII => 170,
+    ];
+    private static $name = null;
+
+    /**
+     * Gets character set ECI by value.
+     *
+     * @param  string $name
+     *
+     * @return CharacterSetEci|null
+     */
+    public static function getCharacterSetECIByValue($value)
+    {
+        if ($value < 0 || $value >= 900) {
+            throw new \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 (\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;
+    }
+}

+ 122 - 0
src/Library/QrReader/Common/DecoderResult.php

@@ -0,0 +1,122 @@
+<?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;
+    }
+
+}

+ 93 - 0
src/Library/QrReader/Common/DefaultGridSampler.php

@@ -0,0 +1,93 @@
+<?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;
+    }
+}

+ 49 - 0
src/Library/QrReader/Common/Detector/MathUtils.php

@@ -0,0 +1,49 @@
+<?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 float $d real value to round
+     *
+     * @return int $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);
+    }
+}

+ 234 - 0
src/Library/QrReader/Common/Detector/MonochromeRectangleDetector.php

@@ -0,0 +1,234 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: Ashot
+ * Date: 3/24/15
+ * Time: 21:23
+ */
+
+namespace Zxing\Common\Detector;
+
+use Zxing\BinaryBitmap;
+use \Zxing\NotFoundException;
+use \Zxing\ResultPoint;
+
+/*
+ *
+ *
+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;
+
+    public function __construct(BinaryBitmap $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 float $centerX     center's x component (horizontal)
+     * @param float $deltaX      same as deltaY but change in x per step instead
+     * @param float $left        minimum value of x
+     * @param float $right       maximum value of x
+     * @param float $centerY     center's y component (vertical)
+     * @param float $deltaY      change in y per step. If scanning up this is negative; down, positive;
+     *                    left or right, 0
+     * @param float $top         minimum value of y to search through (meaningless when di == 0)
+     * @param float $bottom      maximum value of y
+     * @param float $maxWhiteRun maximum run of white pixels that can still be considered to be within
+     *                    the barcode
+     *
+     * @return ResultPoint $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 ? [$start, $end] : null;
+    }
+}

+ 47 - 0
src/Library/QrReader/Common/DetectorResult.php

@@ -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;
+
+/**
+ * <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;
+    }
+}

+ 207 - 0
src/Library/QrReader/Common/GlobalHistogramBinarizer.php

@@ -0,0 +1,207 @@
+<?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 = [];
+
+    private $luminances = [];
+    private $buckets = [];
+    private $source = [];
+
+    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.
+    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 = self::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.
+    private function initArrays($luminanceSize)
+    {
+        if (count($this->luminances) < $luminanceSize) {
+            $this->luminances = [];
+        }
+        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 ($bestValley << self::$LUMINANCE_SHIFT);
+    }
+
+    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             = (int)($height * $y / 5);
+            $localLuminances = $source->getRow($row, $this->luminances);
+            $right           = (int)(($width * 4) / 5);
+            for ($x = (int)($width / 5); $x < $right; $x++) {
+                $pixel = ($localLuminances[(int)($x)] & 0xff);
+                $localBuckets[($pixel >> self::$LUMINANCE_SHIFT)]++;
+            }
+        }
+        $blackPoint = self::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 = (int)($localLuminances[$offset + $x] & 0xff);
+                if ($pixel < $blackPoint) {
+                    $matrix->set($x, $y);
+                }
+            }
+        }
+
+        return $matrix;
+    }
+
+    public function createBinarizer($source)
+    {
+        return new GlobalHistogramBinarizer($source);
+    }
+}

+ 188 - 0
src/Library/QrReader/Common/GridSampler.php

@@ -0,0 +1,188 @@
+<?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;
+    }
+
+    /**
+     * <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;
+            }
+        }
+    }
+
+    /**
+     * 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
+    );
+
+}

+ 264 - 0
src/Library/QrReader/Common/HybridBinarizer.php

@@ -0,0 +1,264 @@
+<?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.
+     */
+    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 = self::calculateBlackPoints($luminances, $subWidth, $subHeight, $width, $height);
+
+            $newMatrix = new BitMatrix($width, $height);
+            self::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;
+    }
+
+    /**
+     * 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    = ($y << self::$BLOCK_SIZE_POWER);
+            $maxYOffset = $height - self::$BLOCK_SIZE;
+            if ($yoffset > $maxYOffset) {
+                $yoffset = $maxYOffset;
+            }
+            for ($x = 0; $x < $subWidth; $x++) {
+                $xoffset    = ($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 = ((int)($luminances[(int)($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 += ($luminances[$offset + $xx] & 0xFF);
+                            }
+                        }
+                    }
+                }
+
+                // The default estimate is the average of the values in the block.
+                $average = ($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 = (int)($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 =
+                            (int)(($blackPoints[$y - 1][$x] + (2 * $blackPoints[$y][$x - 1]) + $blackPoints[$y - 1][$x - 1]) / 4);
+                        if ($min < $averageNeighborBlackPoint) {
+                            $average = $averageNeighborBlackPoint;
+                        }
+                    }
+                }
+                $blackPoints[$y][$x] = (int)($average);
+            }
+        }
+
+        return $blackPoints;
+    }
+
+    /**
+     * 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    = ($y << self::$BLOCK_SIZE_POWER);
+            $maxYOffset = $height - self::$BLOCK_SIZE;
+            if ($yoffset > $maxYOffset) {
+                $yoffset = $maxYOffset;
+            }
+            for ($x = 0; $x < $subWidth; $x++) {
+                $xoffset    = ($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 = (int)($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);
+                }
+            }
+        }
+    }
+
+    public function createBinarizer($source)
+    {
+        return new HybridBinarizer($source);
+    }
+}

+ 175 - 0
src/Library/QrReader/Common/PerspectiveTransform.php

@@ -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\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 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();
+    }
+
+    public 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);
+    }
+
+    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 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);
+
+    }
+
+    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;
+        }
+    }
+}

+ 198 - 0
src/Library/QrReader/Common/Reedsolomon/GenericGF.php

@@ -0,0 +1,198 @@
+<?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;
+
+    /**
+     * 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 = [];
+        $this->logTable = [];
+        $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, [0]);
+        $this->one  = new GenericGFPoly($this, [1]);
+    }
+
+    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;
+    }
+
+    /**
+     * Implements both addition and subtraction -- they are the same in GF(size).
+     *
+     * @return sum/difference of a and b
+     */
+    public static function addOrSubtract($a, $b)
+    {
+        return $a ^ $b;
+    }
+
+    public function getZero()
+    {
+        return $this->zero;
+    }
+
+    public function getOne()
+    {
+        return $this->one;
+    }
+
+    /**
+     * @return the monomial representing coefficient * x^degree
+     */
+    public 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);
+    }
+
+    /**
+     * @return 2 to the power of a in GF(size)
+     */
+    public function exp($a)
+    {
+        return $this->expTable[$a];
+    }
+
+    /**
+     * @return base 2 log of a in GF(size)
+     */
+    public function log($a)
+    {
+        if ($a == 0) {
+            throw new \InvalidArgumentException();
+        }
+
+        return $this->logTable[$a];
+    }
+
+    /**
+     * @return multiplicative inverse of a
+     */
+    public function inverse($a)
+    {
+        if ($a == 0) {
+            throw new \Exception();
+        }
+
+        return $this->expTable[$this->size - $this->logTable[$a] - 1];
+    }
+
+    /**
+     * @return int product of a and b in GF(size)
+     */
+    public 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((int)($this->primitive)) . ',' . $this->size . ')';
+    }
+
+}
+
+GenericGF::Init();

+ 289 - 0
src/Library/QrReader/Common/Reedsolomon/GenericGFPoly.php

@@ -0,0 +1,289 @@
+<?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")
+     */
+    public 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 = [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;
+        }
+    }
+
+    public function getCoefficients()
+    {
+        return $this->coefficients;
+    }
+
+    /**
+     * @return evaluation of this polynomial at a given point
+     */
+    public 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;
+    }
+
+    /**
+     * @return coefficient of x^degree term in this polynomial
+     */
+    public function getCoefficient($degree)
+    {
+        return $this->coefficients[count($this->coefficients) - 1 - $degree];
+    }
+
+    public 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);
+    }
+
+    public 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);
+    }
+
+    /**
+     * @return true iff this polynomial is the monomial "0"
+     */
+    public function isZero()
+    {
+        return $this->coefficients[0] == 0;
+    }
+
+    public 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);
+    }
+
+    public 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 [$quotient, $remainder];
+    }
+
+    /**
+     * @return degree of this polynomial
+     */
+    public function getDegree()
+    {
+        return count($this->coefficients) - 1;
+    }
+
+    public 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);
+
+        $countLargerCoefficients = count($largerCoefficients);
+        for ($i = $lengthDiff; $i < $countLargerCoefficients; $i++) {
+            $sumDiff[$i] = GenericGF::addOrSubtract($smallerCoefficients[$i - $lengthDiff], $largerCoefficients[$i]);
+        }
+
+        return new GenericGFPoly($this->field, $sumDiff);
+    }
+
+    //@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;
+    }
+}

+ 201 - 0
src/Library/QrReader/Common/Reedsolomon/ReedSolomonDecoder.php

@@ -0,0 +1,201 @@
+<?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);
+        $errorLocationsCount = count($errorLocations);
+        for ($i = 0; $i < $errorLocationsCount; $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 [$sigma, $omega];
+    }
+
+    private function findErrorLocations($errorLocator)
+    {
+        // This is a direct application of Chien's search
+        $numErrors = $errorLocator->getDegree();
+        if ($numErrors == 1) { // shortcut
+            return [$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;
+    }
+}

+ 28 - 0
src/Library/QrReader/Common/Reedsolomon/ReedSolomonException.php

@@ -0,0 +1,28 @@
+<?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
+{
+}

+ 101 - 0
src/Library/QrReader/Common/customFunctions.php

@@ -0,0 +1,101 @@
+<?php
+
+if (!function_exists('arraycopy')) {
+    function arraycopy($srcArray, $srcPos, $destArray, $destPos, $length)
+    {
+        $srcArrayToCopy = array_slice($srcArray, $srcPos, $length);
+        array_splice($destArray, $destPos, $length, $srcArrayToCopy);
+
+        return $destArray;
+    }
+}
+
+if (!function_exists('hashCode')) {
+    function hashCode($s)
+    {
+        $h   = 0;
+        $len = strlen($s);
+        for ($i = 0; $i < $len; $i++) {
+            $h = (31 * $h + ord($s[$i]));
+        }
+
+        return $h;
+    }
+}
+
+if (!function_exists('numberOfTrailingZeros')) {
+    function numberOfTrailingZeros($i)
+    {
+        if ($i == 0) return 32;
+        $num = 0;
+        while (($i & 1) == 0) {
+            $i >>= 1;
+            $num++;
+        }
+
+        return $num;
+    }
+}
+
+if (!function_exists('uRShift')) {
+    function uRShift($a, $b)
+    {
+        static $mask = (8 * PHP_INT_SIZE - 1);
+        if ($b === 0) {
+            return $a;
+        }
+
+        return ($a >> $b) & ~(1 << $mask >> ($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));
+}
+*/
+
+if (!function_exists('sdvig3')) {
+    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
+
+        return bindec($bin);
+    }
+}
+
+if (!function_exists('floatToIntBits')) {
+    function floatToIntBits($float_val)
+    {
+        $int = unpack('i', pack('f', $float_val));
+
+        return $int[1];
+    }
+}
+
+
+if (!function_exists('fill_array')) {
+    function fill_array($index, $count, $value)
+    {
+        if ($count <= 0) {
+            return [0];
+        }
+
+        return array_fill($index, $count, $value);
+    }
+}

+ 49 - 0
src/Library/QrReader/FormatException.php

@@ -0,0 +1,49 @@
+<?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;
+        }
+    }
+}

+ 176 - 0
src/Library/QrReader/GDLuminanceSource.php

@@ -0,0 +1,176 @@
+<?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 = [];
+        //$this->luminances = $this->grayScaleToBitmap($this->grayscale());
+
+        $array = [];
+        $rgb   = [];
+
+        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[(int)($offset + $x)] = (($r+128) % 256) - 128;
+                } else {
+// Calculate luminance cheaply, favoring green.
+                    $this->luminances[(int)($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 = [];
+        }
+        $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      = [];
+        $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);
+    }
+}

+ 150 - 0
src/Library/QrReader/IMagickLuminanceSource.php

@@ -0,0 +1,150 @@
+<?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(
+        \Imagick $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(\Imagick $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 = [];
+
+        $image->setImageColorspace(\Imagick::COLORSPACE_GRAY);
+        // $image->newPseudoImage(0, 0, "magick:rose");
+        $pixels = $image->exportImagePixels(1, 1, $width, $height, "RGB", \Imagick::PIXEL_CHAR);
+
+        $array = [];
+        $rgb   = [];
+
+        $countPixels = count($pixels);
+        for ($i = 0; $i < $countPixels; $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 = [];
+        }
+        $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      = [];
+        $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 $this->luminances->cropImage($width, $height, $left, $top);
+
+        return new GDLuminanceSource($this->luminances,
+            $this->dataWidth,
+            $this->dataHeight,
+            $this->left + $left,
+            $this->top + $top,
+            $width,
+            $height);
+    }
+}

+ 171 - 0
src/Library/QrReader/LuminanceSource.php

@@ -0,0 +1,171 @@
+<?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;
+
+    public function __construct($width, $height)
+    {
+        $this->width  = $width;
+        $this->height = $height;
+    }
+
+    /**
+     * 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 bool 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.");
+    }
+
+    public final function toString()
+    {
+        $row    = [];
+        $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;
+    }
+
+    /**
+     * 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);
+}

+ 38 - 0
src/Library/QrReader/NotFoundException.php

@@ -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;
+    }
+}

+ 182 - 0
src/Library/QrReader/PlanarYUVLuminanceSource.php

@@ -0,0 +1,182 @@
+<?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 \InvalidArgumentException("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 \InvalidArgumentException("Requested row is outside the image: " + y);
+        }
+        $width = $this->getWidth();
+        if ($row == null || count($row) < $width) {
+            $row = [];//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      = [];//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       = (int)($this->getWidth() / self::$THUMBNAIL_SCALE_FACTOR);
+        $height      = (int)($this->getHeight() / self::$THUMBNAIL_SCALE_FACTOR);
+        $pixels      = [];//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                       = ($yuv[$inputOffset + $x * self::$THUMBNAIL_SCALE_FACTOR] & 0xff);
+                $pixels[$outputOffset + $x] = (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;
+      }
+    }
+  }
+*/
+}

+ 105 - 0
src/Library/QrReader/QrReader.php

@@ -0,0 +1,105 @@
+<?php
+
+namespace Zxing;
+
+use Zxing\Common\HybridBinarizer;
+use Zxing\Qrcode\QRCodeReader;
+
+final class QrReader
+{
+    const SOURCE_TYPE_FILE     = 'file';
+    const SOURCE_TYPE_BLOB     = 'blob';
+    const SOURCE_TYPE_RESOURCE = 'resource';
+
+    private $bitmap;
+    private $reader;
+    private $result;
+
+    public function __construct($imgSource, $sourceType = QrReader::SOURCE_TYPE_FILE, $useImagickIfAvailable = true)
+    {
+        if (!in_array($sourceType, [
+            self::SOURCE_TYPE_FILE,
+            self::SOURCE_TYPE_BLOB,
+            self::SOURCE_TYPE_RESOURCE,
+        ], true)) {
+            throw new \InvalidArgumentException('Invalid image source.');
+        }
+        $im = null;
+        switch ($sourceType) {
+            case QrReader::SOURCE_TYPE_FILE:
+                if ($useImagickIfAvailable && 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 ($useImagickIfAvailable && extension_loaded('imagick')) {
+                    $im = new \Imagick();
+                    $im->readImageBlob($imgSource);
+                } else {
+                    $im = imagecreatefromstring($imgSource);
+                }
+                break;
+
+            case QrReader::SOURCE_TYPE_RESOURCE:
+                $im = $imgSource;
+                if ($useImagickIfAvailable && extension_loaded('imagick')) {
+                    $useImagickIfAvailable = true;
+                } else {
+                    $useImagickIfAvailable = false;
+                }
+                break;
+        }
+        if ($useImagickIfAvailable && extension_loaded('imagick')) {
+            if (!$im instanceof \Imagick) {
+                throw new \InvalidArgumentException('Invalid image source.');
+            }
+            $width  = $im->getImageWidth();
+            $height = $im->getImageHeight();
+            $source = new IMagickLuminanceSource($im, $width, $height);
+        } else {
+            if (!is_resource($im)) {
+                throw new \InvalidArgumentException('Invalid image source.');
+            }
+            $width  = imagesx($im);
+            $height = imagesy($im);
+            $source = new GDLuminanceSource($im, $width, $height);
+        }
+        $histo        = new HybridBinarizer($source);
+        $this->bitmap = new BinaryBitmap($histo);
+        $this->reader = new QRCodeReader();
+    }
+
+    public function decode()
+    {
+        try {
+            $this->result = $this->reader->decode($this->bitmap);
+        } catch (NotFoundException $er) {
+            $this->result = false;
+        } catch (FormatException $er) {
+            $this->result = false;
+        } catch (ChecksumException $er) {
+            $this->result = false;
+        }
+    }
+
+    public function text()
+    {
+        $this->decode();
+
+        if (method_exists($this->result, 'toString')) {
+            return $this->result->toString();
+        }
+
+        return $this->result;
+    }
+
+    public function getResult()
+    {
+        return $this->result;
+    }
+}

+ 263 - 0
src/Library/QrReader/Qrcode/Decoder/BitMatrixParser.php

@@ -0,0 +1,263 @@
+<?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
+     */
+    public function __construct($bitMatrix)
+    {
+        $dimension = $bitMatrix->getHeight();
+        if ($dimension < 21 || ($dimension & 0x03) != 1) {
+            throw FormatException::getFormatInstance();
+        }
+        $this->bitMatrix = $bitMatrix;
+    }
+
+    /**
+     * <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
+     */
+    public 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 = [];
+        }
+        $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;
+    }
+
+    /**
+     * <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
+     */
+    public 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();
+    }
+
+    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 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
+     */
+    public 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();
+    }
+
+    /**
+     * Revert the mask removal done while reading the code words. The bit matrix should revert to its original state.
+     */
+    public 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.
+     */
+    public function setMirror($mirror)
+    {
+        $parsedVersion    = null;
+        $parsedFormatInfo = null;
+        $this->mirror     = $mirror;
+    }
+
+    /** Mirror the bit matrix in order to attempt a second reading. */
+    public 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);
+                }
+            }
+        }
+    }
+}

+ 129 - 0
src/Library/QrReader/Qrcode/Decoder/DataBlock.php

@@ -0,0 +1,129 @@
+<?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 array DataBlocks containing original bytes, "de-interleaved" from representation in the
+     *         QR Code
+     */
+    public 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          = [];//new DataBlock[$totalBlocks];
+        $numResultBlocks = 0;
+        foreach ($ecBlockArray as $ecBlock) {
+            $ecBlockCount = $ecBlock->getCount();
+            for ($i = 0; $i < $ecBlockCount; $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;
+    }
+
+    public function getNumDataCodewords()
+    {
+        return $this->numDataCodewords;
+    }
+
+    public function getCodewords()
+    {
+        return $this->codewords;
+    }
+}

+ 196 - 0
src/Library/QrReader/Qrcode/Decoder/DataMask.php

@@ -0,0 +1,196 @@
+<?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 = [];
+
+    public function __construct()
+    {
+
+    }
+
+    public static function Init()
+    {
+        self::$DATA_MASKS = [
+            new DataMask000(),
+            new DataMask001(),
+            new DataMask010(),
+            new DataMask011(),
+            new DataMask100(),
+            new DataMask101(),
+            new DataMask110(),
+            new DataMask111(),
+        ];
+    }
+
+    /**
+     * @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
+     */
+    public static function forReference($reference)
+    {
+        if ($reference < 0 || $reference > 7) {
+            throw new \InvalidArgumentException();
+        }
+
+        return self::$DATA_MASKS[$reference];
+    }
+
+    /**
+     * <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 public 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 public function isMasked($i, $j);
+}
+
+DataMask::Init();
+
+/**
+ * 000: mask bits for which (x + y) mod 2 == 0
+ */
+final class DataMask000 extends DataMask
+{
+    // @Override
+    public function isMasked($i, $j)
+    {
+        return (($i + $j) & 0x01) == 0;
+    }
+}
+
+/**
+ * 001: mask bits for which x mod 2 == 0
+ */
+final class DataMask001 extends DataMask
+{
+    //@Override
+    public function isMasked($i, $j)
+    {
+        return ($i & 0x01) == 0;
+    }
+}
+
+/**
+ * 010: mask bits for which y mod 3 == 0
+ */
+final class DataMask010 extends DataMask
+{
+    //@Override
+    public function isMasked($i, $j)
+    {
+        return $j % 3 == 0;
+    }
+}
+
+/**
+ * 011: mask bits for which (x + y) mod 3 == 0
+ */
+final class DataMask011 extends DataMask
+{
+    //@Override
+    public 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
+    public function isMasked($i, $j)
+    {
+        return (int)(((int)($i / 2) + (int)($j / 3)) & 0x01) == 0;
+    }
+}
+
+/**
+ * 101: mask bits for which xy mod 2 + xy mod 3 == 0
+ */
+final class DataMask101 extends DataMask
+{
+    //@Override
+    public 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
+    public 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
+    public function isMasked($i, $j)
+    {
+        return (((($i + $j) & 0x01) + (($i * $j) % 3)) & 0x01) == 0;
+    }
+}

+ 355 - 0
src/Library/QrReader/Qrcode/Decoder/DecodedBitStreamParser.php

@@ -0,0 +1,355 @@
+<?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 = [
+        '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;
+
+    public static function decode($bytes,
+                                  $version,
+                                  $ecLevel,
+                                  $hints)
+    {
+        $bits           = new BitSource($bytes);
+        $result         = '';//new StringBuilder(50);
+        $byteSegments   = [];
+        $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 (\InvalidArgumentException $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);
+    }
+
+    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();
+    }
+
+    /**
+     * 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--;
+        }
+        $result .= iconv('GB2312', 'UTF-8', implode($buffer));
+    }
+
+    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 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 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();
+        }
+        //  $result.= mb_convert_encoding($text ,$encoding);//(new String(readBytes, encoding));
+        $result .= $text;//(new String(readBytes, encoding));
+
+        $byteSegments = array_merge($byteSegments, $readBytes);
+    }
+
+    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 = [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:
+
+        $result .= iconv('shift-jis', 'utf-8', implode($buffer));
+    }
+
+    private function DecodedBitStreamParser()
+    {
+
+    }
+}

+ 213 - 0
src/Library/QrReader/Qrcode/Decoder/Decoder.php

@@ -0,0 +1,213 @@
+<?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);
+    }
+
+    public function decode($variable, $hints = null)
+    {
+        if (is_array($variable)) {
+            return $this->decodeImage($variable, $hints);
+        } elseif ($variable instanceof BitMatrix) {
+            return $this->decodeBits($variable, $hints);
+        } elseif ($variable instanceof BitMatrixParser) {
+            return $this->decodeParser($variable, $hints);
+        }
+        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 array $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 BitMatrix $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];
+        }
+    }
+}

+ 91 - 0
src/Library/QrReader/Qrcode/Decoder/ErrorCorrectionLevel.php

@@ -0,0 +1,91 @@
+<?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;
+
+    public function __construct($bits, $ordinal = 0)
+    {
+        $this->bits    = $bits;
+        $this->ordinal = $ordinal;
+    }
+
+    public static function Init()
+    {
+        self::$FOR_BITS = [
+
+
+            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);
+    /**
+     * @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;
+    }
+
+
+    public function getBits()
+    {
+        return $this->bits;
+    }
+
+    public function toString()
+    {
+        return $this->bits;
+    }
+
+    public function getOrdinal()
+    {
+        return $this->ordinal;
+    }
+}
+
+ErrorCorrectionLevel::Init();

+ 190 - 0
src/Library/QrReader/Qrcode/Decoder/FormatInformation.php

@@ -0,0 +1,190 @@
+<?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;
+
+    private function __construct($formatInfo)
+    {
+        // Bits 3,4
+        $this->errorCorrectionLevel = ErrorCorrectionLevel::forBits(($formatInfo >> 3) & 0x03);
+        // Bottom 3 bits
+        $this->dataMask = ($formatInfo & 0x07);//(byte)
+    }
+
+    public static function Init()
+    {
+        self::$FORMAT_INFO_MASK_QR       = 0x5412;
+        self::$BITS_SET_IN_HALF_BYTE     = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];
+        self::$FORMAT_INFO_DECODE_LOOKUP = [
+            [0x5412, 0x00],
+            [0x5125, 0x01],
+            [0x5E7C, 0x02],
+            [0x5B4B, 0x03],
+            [0x45F9, 0x04],
+            [0x40CE, 0x05],
+            [0x4F97, 0x06],
+            [0x4AA0, 0x07],
+            [0x77C4, 0x08],
+            [0x72F3, 0x09],
+            [0x7DAA, 0x0A],
+            [0x789D, 0x0B],
+            [0x662F, 0x0C],
+            [0x6318, 0x0D],
+            [0x6C41, 0x0E],
+            [0x6976, 0x0F],
+            [0x1689, 0x10],
+            [0x13BE, 0x11],
+            [0x1CE7, 0x12],
+            [0x19D0, 0x13],
+            [0x0762, 0x14],
+            [0x0255, 0x15],
+            [0x0D0C, 0x16],
+            [0x083B, 0x17],
+            [0x355F, 0x18],
+            [0x3068, 0x19],
+            [0x3F31, 0x1A],
+            [0x3A06, 0x1B],
+            [0x24B4, 0x1C],
+            [0x2183, 0x1D],
+            [0x2EDA, 0x1E],
+            [0x2BED, 0x1F],
+        ];
+    }
+
+    /**
+     * @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
+     */
+    public 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;
+    }
+
+    public 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[(int)(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)];
+    }
+
+    public function getErrorCorrectionLevel()
+    {
+        return $this->errorCorrectionLevel;
+    }
+
+    public function getDataMask()
+    {
+        return $this->dataMask;
+    }
+
+    //@Override
+    public function hashCode()
+    {
+        return ($this->errorCorrectionLevel->ordinal() << 3) | (int)($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();

+ 128 - 0
src/Library/QrReader/Qrcode/Decoder/Mode.php

@@ -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\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
+{
+    public static $TERMINATOR;
+    public static $NUMERIC;
+    public static $ALPHANUMERIC;
+    public static $STRUCTURED_APPEND;
+    public static $BYTE;
+    public static $ECI;
+    public static $KANJI;
+    public static $FNC1_FIRST_POSITION;
+    public static $FNC1_SECOND_POSITION;
+    public static $HANZI;
+
+    private $characterCountBitsForVersions;
+    private $bits;
+
+    public function __construct($characterCountBitsForVersions, $bits)
+    {
+        $this->characterCountBitsForVersions = $characterCountBitsForVersions;
+        $this->bits                          = $bits;
+    }
+
+    public static function Init()
+    {
+
+
+        self::$TERMINATOR           = new Mode([0, 0, 0], 0x00); // Not really a mode...
+        self::$NUMERIC              = new Mode([10, 12, 14], 0x01);
+        self::$ALPHANUMERIC         = new Mode([9, 11, 13], 0x02);
+        self::$STRUCTURED_APPEND    = new Mode([0, 0, 0], 0x03); // Not supported
+        self::$BYTE                 = new Mode([8, 16, 16], 0x04);
+        self::$ECI                  = new Mode([0, 0, 0], 0x07); // character counts don't apply
+        self::$KANJI                = new Mode([8, 10, 12], 0x08);
+        self::$FNC1_FIRST_POSITION  = new Mode([0, 0, 0], 0x05);
+        self::$FNC1_SECOND_POSITION = new Mode([0, 0, 0], 0x09);
+        /** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */
+        self::$HANZI = new Mode([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 - 0
src/Library/QrReader/Qrcode/Decoder/Version.php

@@ -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;
+    }
+
+
+}
+
+

+ 65 - 0
src/Library/QrReader/Qrcode/Detector/AlignmentPattern.php

@@ -0,0 +1,65 @@
+<?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;
+
+    public 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>
+     */
+    public 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.
+     */
+    public 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);
+    }
+}

+ 286 - 0
src/Library/QrReader/Qrcode/Detector/AlignmentPatternFinder.php

@@ -0,0 +1,286 @@
+<?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
+     */
+    public function __construct($image,
+                                $startX,
+                                $startY,
+                                $width,
+                                $height,
+                                $moduleSize,
+                                $resultPointCallback)
+    {
+        $this->image                = $image;
+        $this->possibleCenters      = [];
+        $this->startX               = $startX;
+        $this->startY               = $startY;
+        $this->width                = $width;
+        $this->height               = $height;
+        $this->moduleSize           = $moduleSize;
+        $this->crossCheckStateCount = [];
+        $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
+     */
+    public 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 = [];
+        for ($iGen = 0; $iGen < $height; $iGen++) {
+            // Search from middle outwards
+            $i             = $middleI + (($iGen & 0x01) == 0 ? ($iGen + 1) / 2 : -(($iGen + 1) / 2));
+            $i             = (int)($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();
+    }
+
+    /**
+     * @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>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;
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * <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;
+    }
+}

+ 420 - 0
src/Library/QrReader/Qrcode/Detector/Detector.php

@@ -0,0 +1,420 @@
+<?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
+ */
+class Detector
+{
+
+    private $image;
+    private $resultPointCallback;
+
+    public function __construct($image)
+    {
+        $this->image = $image;
+    }
+
+    /**
+     * <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)self::computeDimension($topLeft, $topRight, $bottomLeft, $moduleSize);
+        $provisionalVersion      = \Zxing\Qrcode\Decoder\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 = self::createTransform($topLeft, $topRight, $bottomLeft, $alignmentPattern, $dimension);
+
+        $bits = self::sampleGrid($this->image, $transform, $dimension);
+
+        $points = [];
+        if ($alignmentPattern == null) {
+            $points = [$bottomLeft, $topLeft, $topRight];
+        } else {
+            // die('$points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern};');
+            $points = [$bottomLeft, $topLeft, $topRight, $alignmentPattern];
+        }
+
+        return new DetectorResult($bits, $points);
+    }
+
+    /**
+     * <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>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>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>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();
+    }
+
+    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);
+    }
+
+    protected final function getImage()
+    {
+        return $this->image;
+    }
+
+    protected final function getResultPointCallback()
+    {
+        return $this->resultPointCallback;
+    }
+}

+ 86 - 0
src/Library/QrReader/Qrcode/Detector/FinderPattern.php

@@ -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\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;
+
+    public function __construct($posX, $posY, $estimatedModuleSize, $count = 1)
+    {
+        parent::__construct($posX, $posY);
+        $this->estimatedModuleSize = $estimatedModuleSize;
+        $this->count               = $count;
+    }
+
+    public function getEstimatedModuleSize()
+    {
+        return $this->estimatedModuleSize;
+    }
+
+    public 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>
+     */
+    public 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.
+     */
+    public 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);
+    }
+}

+ 699 - 0
src/Library/QrReader/Qrcode/Detector/FinderPatternFinder.php

@@ -0,0 +1,699 @@
+<?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\BinaryBitmap;
+use Zxing\Common\BitMatrix;
+use Zxing\NotFoundException;
+use Zxing\ResultPoint;
+
+/**
+ * <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
+{
+    protected static $MIN_SKIP = 3;
+    protected static $MAX_MODULES = 57; // 1 pixel/module times 3 modules/center
+    private static $CENTER_QUORUM = 2; // 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 BitMatrix $image image to search
+     */
+    public function __construct($image, $resultPointCallback = null)
+    {
+        $this->image = $image;
+
+
+        $this->possibleCenters      = [];//new ArrayList<>();
+        $this->crossCheckStateCount = fill_array(0, 5, 0);
+        $this->resultPointCallback  = $resultPointCallback;
+    }
+
+    final public function find($hints)
+    {/*final FinderPatternInfo find(Map<DecodeHintType,?> hints) throws NotFoundException {*/
+        $tryHarder = true;//$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 = (int)((3 * $maxI) / (4 * self::$MAX_MODULES));
+        if ($iSkip < self::$MIN_SKIP || $tryHarder) {
+            $iSkip = self::$MIN_SKIP;
+        }
+
+        $done       = false;
+        $stateCount = [];
+        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 (self::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 = 3;
+                                    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 (self::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);
+    }
+
+    /**
+     * @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;
+    }
+
+    /**
+     * <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, (int)($centerJ), $stateCount[2], $stateCountTotal);
+        if (!is_nan($centerI)) {
+            // Re-cross check
+            $centerJ = $this->crossCheckHorizontal((int)($centerJ), (int)($centerI), $stateCount[2], $stateCountTotal);
+            if (!is_nan($centerJ) &&
+                (!$pureBarcode || $this->crossCheckDiagonal((int)($centerI), (int)($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;
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * <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 self::foundPatternCross($stateCount) ? $this->centerFromEnd($stateCount, $i) : NAN;
+    }
+
+    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;
+    }
+
+    /**
+     * <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;
+    }
+
+    /**
+     * 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  = (int)($startI);
+        $centerJ = (int)($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 &&
+            self::foundPatternCross($stateCount);
+    }
+
+    /**
+     * @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 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 (int)((abs($firstConfirmedCenter->getX() - $center->getX()) -
+                            abs($firstConfirmedCenter->getY() - $center->getY())) / 2);
+                }
+            }
+        }
+
+        return 0;
+    }
+
+    /**
+     * @return array 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, [$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, [$this, 'CenterComparator']);
+
+            array_slice($this->possibleCenters, 3, count($this->possibleCenters) - 3);
+        }
+
+        return [$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;
+        }
+    }
+
+    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();
+        }
+    }
+
+    protected final function getImage()
+    {
+        return $this->image;
+    }
+    /**
+     * <p>Orders by {@link FinderPattern#getCount()}, descending.</p>
+     */
+
+    //@Override
+    protected final function getPossibleCenters()
+    { //List<FinderPattern> getPossibleCenters()
+        return $this->possibleCenters;
+    }
+}

+ 53 - 0
src/Library/QrReader/Qrcode/Detector/FinderPatternInfo.php

@@ -0,0 +1,53 @@
+<?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;
+    }
+}

+ 222 - 0
src/Library/QrReader/Qrcode/QRCodeReader.php

@@ -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\BinaryBitmap;
+use Zxing\ChecksumException;
+use Zxing\FormatException;
+use Zxing\NotFoundException;
+use Zxing\Reader;
+use Zxing\Result;
+use Zxing\Common\BitMatrix;
+use Zxing\Qrcode\Decoder\Decoder;
+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 = [];
+    private $decoder;
+
+    public function __construct()
+    {
+        $this->decoder = new Decoder();
+    }
+
+    /**
+     * @param BinaryBitmap $image
+     * @param null         $hints
+     *
+     * @return Result
+     * @throws \Zxing\FormatException
+     * @throws \Zxing\NotFoundException
+     */
+    public function decode(BinaryBitmap $image, $hints = null)
+    {
+        $decoderResult = null;
+        if ($hints !== null && $hints['PURE_BARCODE']) {
+            $bits          = self::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();
+        }
+        $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;
+    }
+
+    /**
+     * 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
+     */
+
+    /**
+     * 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(BitMatrix $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, BitMatrix $image)
+    {
+        $height = $image->getHeight();
+        $width  = $image->getWidth();
+        $x           = $leftTopBlack[0];
+        $y           = $leftTopBlack[1];
+        /*$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;
+    }
+
+    public function reset()
+    {
+        // do nothing
+    }
+
+    protected final function getDecoder()
+    {
+        return $this->decoder;
+    }
+}

+ 309 - 0
src/Library/QrReader/RGBLuminanceSource.php

@@ -0,0 +1,309 @@
+<?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 = [];
+        //$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[(int)($offset + $x)] = (($r+128) % 256) - 128;
+                } else {
+// Calculate luminance cheaply, favoring green.
+                    $this->luminances[(int)($offset + $x)] =  (((($r + 2 * $g + $b) / 4)+128)%256) - 128;
+                }
+
+
+
+            }
+        */
+        //}
+        //   $this->luminances = $this->grayScaleToBitmap($this->luminances);
+
+    }
+
+    public 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;
+    }
+
+    public 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 = (int)(($r * 33 + $g * 34 + $b * 33) / 100);
+
+
+        return $p;
+
+    }
+
+    public 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[(int)($areaWidth * $ax + $dx + ($areaHeight * $ay + $dy) * $this->dataWidth)] = ($grayScale[(int)($areaWidth * $ax + $dx + ($areaHeight * $ay + $dy) * $this->dataWidth)] < $middle[$ax][$ay]) ? 0 : 255;
+                    }
+                }
+            }
+        }
+
+        return $bitmap;
+    }
+
+    public 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] = [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[(int)($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 = [];
+        for ($i3 = 0; $i3 < $numSqrtArea; $i3++) {
+            $middle[$i3] = [];
+        }
+        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;
+    }
+
+//@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 = [];
+        }
+        $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      = [];
+        $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);
+    }
+}

+ 10 - 0
src/Library/QrReader/Reader.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace Zxing;
+
+interface Reader
+{
+    public function decode(BinaryBitmap $image);
+
+    public function reset();
+}

+ 50 - 0
src/Library/QrReader/ReaderException.php

@@ -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;
+
+/**
+ * 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;
+    }
+}

+ 135 - 0
src/Library/QrReader/Result.php

@@ -0,0 +1,135 @@
+<?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 ?: 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 = [];
+        }
+        $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;
+    }
+
+    public function toString()
+    {
+        return $this->text;
+    }
+}

+ 155 - 0
src/Library/QrReader/ResultPoint.php

@@ -0,0 +1,155 @@
+<?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);
+    }
+
+    /**
+     * 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);
+    }
+
+//@Override
+
+    /**
+     * 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));
+    }
+
+//@Override
+
+    public final function getX()
+    {
+        return (float)($this->x);
+    }
+
+//@Override
+
+    public final function getY()
+    {
+        return (float)($this->y);
+    }
+
+    public final function equals($other)
+    {
+        if ($other instanceof ResultPoint) {
+            $otherPoint = $other;
+
+            return $this->x == $otherPoint->x && $this->y == $otherPoint->y;
+        }
+
+        return false;
+    }
+
+    public final function hashCode()
+    {
+        return 31 * floatToIntBits($this->x) + floatToIntBits($this->y);
+    }
+
+    public final function toString()
+    {
+        $result = '';
+        $result .= ('(');
+        $result .= ($this->x);
+        $result .= (',');
+        $result .= ($this->y);
+        $result .= (')');
+
+        return $result;
+    }
+}

+ 28 - 0
src/Library/Qrcode.php

@@ -0,0 +1,28 @@
+<?php
+namespace Qii\Library;
+
+\Qii\Autoloader\Psr4::getInstance()
+	->setUseNamespaces([['Zxing', true]])
+	->addNamespaces([
+		['Zxing', Qii_DIR . DS . 'Library'. DS .'QrReader']
+	]);
+
+_require(__DIR__ . DS . 'QrReader'. DS . 'QrReader.php');
+
+class Qrcode
+{
+	public function __construct()
+	{
+		
+	}
+	public function reader($image)
+	{
+		if(!file_exists($image))
+		{
+			throw new \Exception('Unknow image file', __LINE__);
+		}
+		$qrcode = new QrReader($image);
+		$text = $qrcode->text();
+		return $text;
+	}
+}

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است