[LIB-9] Add chesshog-qrcode module
[chesshog.git] / chesshog-qrcode / src / main / java / org / hedgecode / chess / qrcode / ChessQRCodeReader.java
diff --git a/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeReader.java b/chesshog-qrcode/src/main/java/org/hedgecode/chess/qrcode/ChessQRCodeReader.java
new file mode 100644 (file)
index 0000000..a0bbbdf
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2018-2019. Developed by Hedgecode.
+ *
+ * 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.
+ */
+
+package org.hedgecode.chess.qrcode;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.EnumMap;
+import java.util.Map;
+
+import javax.imageio.ImageIO;
+
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.ReaderException;
+import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
+import com.google.zxing.common.DecoderResult;
+import com.google.zxing.common.DetectorResult;
+import com.google.zxing.common.HybridBinarizer;
+import com.google.zxing.qrcode.decoder.Decoder;
+import com.google.zxing.qrcode.detector.Detector;
+
+import org.hedgecode.chess.ChessQRCodeConstants;
+
+/**
+ * Chess-specific QR Codes reader (image decoder).
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class ChessQRCodeReader {
+
+    private final Decoder decoder;
+
+    private static ChessQRCodeReader _instance = new ChessQRCodeReader();
+
+    private ChessQRCodeReader() {
+        decoder = new Decoder();
+    }
+
+    public ChessQRResult read(File qrCodeImageFile) throws ChessQRCodeException {
+        BufferedImage qrCodeImage;
+        try {
+            qrCodeImage = ImageIO.read(qrCodeImageFile);
+        } catch (IOException e) {
+            throw new ChessQRCodeException(
+                    ChessQRCodeException.Type.READ, "read.input.qrcode.file", qrCodeImageFile.getName()
+            );
+        }
+        return read(qrCodeImage);
+    }
+
+    public ChessQRResult read(InputStream qrCodeInputStream) throws ChessQRCodeException {
+        BufferedImage qrCodeImage;
+        try {
+            qrCodeImage = ImageIO.read(qrCodeInputStream);
+        } catch (IOException e) {
+            throw new ChessQRCodeException(
+                    ChessQRCodeException.Type.READ, "read.input.qrcode.stream", e.getMessage()
+            );
+        }
+        return read(qrCodeImage);
+    }
+
+    public ChessQRResult read(URI qrCodeUri) throws ChessQRCodeException {
+        BufferedImage qrCodeImage;
+        try {
+            qrCodeImage = ImageIO.read(qrCodeUri.toURL());
+        } catch (IOException e) {
+            throw new ChessQRCodeException(
+                    ChessQRCodeException.Type.READ, "read.input.qrcode.url", qrCodeUri.toASCIIString()
+            );
+        }
+        return read(qrCodeImage);
+    }
+
+    public ChessQRResult read(BufferedImage qrCodeImage) throws ChessQRCodeException {
+        BinaryBitmap bitmap = new BinaryBitmap(
+                new HybridBinarizer(
+                        new BufferedImageLuminanceSource(qrCodeImage)
+                )
+        );
+        return decode(bitmap, null);
+    }
+
+    private ChessQRResult decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
+            throws ChessQRCodeException
+    {
+        Map<DecodeHintType,Object> newHints = new EnumMap<>(DecodeHintType.class);
+        if (hints != null) {
+            newHints.putAll(hints);
+            if (!hints.containsKey(DecodeHintType.CHARACTER_SET))
+                newHints.put(DecodeHintType.CHARACTER_SET, ChessQRCodeConstants.CHARSET.name());
+        } else {
+            newHints.put(DecodeHintType.CHARACTER_SET, ChessQRCodeConstants.CHARSET.name());
+        }
+        hints = newHints;
+
+        DecoderResult decoderResult;
+        try {
+            DetectorResult detectorResult = new Detector(
+                    image.getBlackMatrix()
+            ).detect(hints);
+
+            decoderResult = decoder.decode(
+                    detectorResult.getBits(), hints
+            );
+        } catch (ReaderException e) {
+            throw new ChessQRCodeException(
+                    ChessQRCodeException.Type.READ, "read.zxing.qrcode.error", e.getMessage()
+            );
+        }
+
+        ChessQRCodeMode mode = ChessQRCodeMode.byCode(
+                decoderResult.getText().substring(0, ChessQRCodeMode.CODE_LENGTH)
+        );
+
+        if (mode == null) {
+            throw new ChessQRCodeException(
+                    ChessQRCodeException.Type.READ, "read.unknown.chess.mode"
+            );
+        }
+
+        return new ChessQRResult(
+                mode,
+                decoderResult.getText().substring(ChessQRCodeMode.CODE_LENGTH)
+        );
+/*
+        ResultPoint[] points = detectorResult.getPoints();
+
+        if (decoderResult.getOther() instanceof QRCodeDecoderMetaData) {
+            ((QRCodeDecoderMetaData) decoderResult.getOther()).applyMirroredCorrection(points);
+        }
+
+        Result result = new Result(
+                decoderResult.getText(),
+                decoderResult.getRawBytes(),
+                points,
+                BarcodeFormat.QR_CODE
+        );
+
+        List<byte[]> byteSegments = decoderResult.getByteSegments();
+        if (byteSegments != null) {
+            result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);
+        }
+        String ecLevel = decoderResult.getECLevel();
+        if (ecLevel != null) {
+            result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
+        }
+        if (decoderResult.hasStructuredAppend()) {
+            result.putMetadata(
+                    ResultMetadataType.STRUCTURED_APPEND_SEQUENCE,
+                    decoderResult.getStructuredAppendSequenceNumber()
+            );
+            result.putMetadata(
+                    ResultMetadataType.STRUCTURED_APPEND_PARITY,
+                    decoderResult.getStructuredAppendParity()
+            );
+        }
+        return result;
+*/
+    }
+
+    public static ChessQRCodeReader getInstance() {
+        return _instance;
+    }
+
+
+    public static void main(String[] args) {
+        try {
+            ChessQRResult qrCodeResult = ChessQRCodeReader.getInstance().read(
+                    new File("MyQRCode.png")
+            );
+            System.out.println("Decoded format = " + qrCodeResult.getMode());
+            System.out.println("Decoded text = " + qrCodeResult.getContents());
+        } catch (ChessQRCodeException e) {
+            System.out.println("Could not decode QR Code. Exception: " + e.getMessage());
+        }
+    }
+
+}