[LIB-9] Add chesshog-qrcode module
[chesshog.git] / chesshog-qrcode / src / main / java / org / hedgecode / chess / qrcode / ChessQRCodeReader.java
1 /*
2  * Copyright (c) 2018-2019. Developed by Hedgecode.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.hedgecode.chess.qrcode;
18
19 import java.awt.image.BufferedImage;
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.net.URI;
24 import java.util.EnumMap;
25 import java.util.Map;
26
27 import javax.imageio.ImageIO;
28
29 import com.google.zxing.BinaryBitmap;
30 import com.google.zxing.DecodeHintType;
31 import com.google.zxing.ReaderException;
32 import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
33 import com.google.zxing.common.DecoderResult;
34 import com.google.zxing.common.DetectorResult;
35 import com.google.zxing.common.HybridBinarizer;
36 import com.google.zxing.qrcode.decoder.Decoder;
37 import com.google.zxing.qrcode.detector.Detector;
38
39 import org.hedgecode.chess.ChessQRCodeConstants;
40
41 /**
42  * Chess-specific QR Codes reader (image decoder).
43  *
44  * @author Dmitry Samoshin aka gotty
45  */
46 public class ChessQRCodeReader {
47
48     private final Decoder decoder;
49
50     private static ChessQRCodeReader _instance = new ChessQRCodeReader();
51
52     private ChessQRCodeReader() {
53         decoder = new Decoder();
54     }
55
56     public ChessQRResult read(File qrCodeImageFile) throws ChessQRCodeException {
57         BufferedImage qrCodeImage;
58         try {
59             qrCodeImage = ImageIO.read(qrCodeImageFile);
60         } catch (IOException e) {
61             throw new ChessQRCodeException(
62                     ChessQRCodeException.Type.READ, "read.input.qrcode.file", qrCodeImageFile.getName()
63             );
64         }
65         return read(qrCodeImage);
66     }
67
68     public ChessQRResult read(InputStream qrCodeInputStream) throws ChessQRCodeException {
69         BufferedImage qrCodeImage;
70         try {
71             qrCodeImage = ImageIO.read(qrCodeInputStream);
72         } catch (IOException e) {
73             throw new ChessQRCodeException(
74                     ChessQRCodeException.Type.READ, "read.input.qrcode.stream", e.getMessage()
75             );
76         }
77         return read(qrCodeImage);
78     }
79
80     public ChessQRResult read(URI qrCodeUri) throws ChessQRCodeException {
81         BufferedImage qrCodeImage;
82         try {
83             qrCodeImage = ImageIO.read(qrCodeUri.toURL());
84         } catch (IOException e) {
85             throw new ChessQRCodeException(
86                     ChessQRCodeException.Type.READ, "read.input.qrcode.url", qrCodeUri.toASCIIString()
87             );
88         }
89         return read(qrCodeImage);
90     }
91
92     public ChessQRResult read(BufferedImage qrCodeImage) throws ChessQRCodeException {
93         BinaryBitmap bitmap = new BinaryBitmap(
94                 new HybridBinarizer(
95                         new BufferedImageLuminanceSource(qrCodeImage)
96                 )
97         );
98         return decode(bitmap, null);
99     }
100
101     private ChessQRResult decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
102             throws ChessQRCodeException
103     {
104         Map<DecodeHintType,Object> newHints = new EnumMap<>(DecodeHintType.class);
105         if (hints != null) {
106             newHints.putAll(hints);
107             if (!hints.containsKey(DecodeHintType.CHARACTER_SET))
108                 newHints.put(DecodeHintType.CHARACTER_SET, ChessQRCodeConstants.CHARSET.name());
109         } else {
110             newHints.put(DecodeHintType.CHARACTER_SET, ChessQRCodeConstants.CHARSET.name());
111         }
112         hints = newHints;
113
114         DecoderResult decoderResult;
115         try {
116             DetectorResult detectorResult = new Detector(
117                     image.getBlackMatrix()
118             ).detect(hints);
119
120             decoderResult = decoder.decode(
121                     detectorResult.getBits(), hints
122             );
123         } catch (ReaderException e) {
124             throw new ChessQRCodeException(
125                     ChessQRCodeException.Type.READ, "read.zxing.qrcode.error", e.getMessage()
126             );
127         }
128
129         ChessQRCodeMode mode = ChessQRCodeMode.byCode(
130                 decoderResult.getText().substring(0, ChessQRCodeMode.CODE_LENGTH)
131         );
132
133         if (mode == null) {
134             throw new ChessQRCodeException(
135                     ChessQRCodeException.Type.READ, "read.unknown.chess.mode"
136             );
137         }
138
139         return new ChessQRResult(
140                 mode,
141                 decoderResult.getText().substring(ChessQRCodeMode.CODE_LENGTH)
142         );
143 /*
144         ResultPoint[] points = detectorResult.getPoints();
145
146         if (decoderResult.getOther() instanceof QRCodeDecoderMetaData) {
147             ((QRCodeDecoderMetaData) decoderResult.getOther()).applyMirroredCorrection(points);
148         }
149
150         Result result = new Result(
151                 decoderResult.getText(),
152                 decoderResult.getRawBytes(),
153                 points,
154                 BarcodeFormat.QR_CODE
155         );
156
157         List<byte[]> byteSegments = decoderResult.getByteSegments();
158         if (byteSegments != null) {
159             result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);
160         }
161         String ecLevel = decoderResult.getECLevel();
162         if (ecLevel != null) {
163             result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
164         }
165         if (decoderResult.hasStructuredAppend()) {
166             result.putMetadata(
167                     ResultMetadataType.STRUCTURED_APPEND_SEQUENCE,
168                     decoderResult.getStructuredAppendSequenceNumber()
169             );
170             result.putMetadata(
171                     ResultMetadataType.STRUCTURED_APPEND_PARITY,
172                     decoderResult.getStructuredAppendParity()
173             );
174         }
175         return result;
176 */
177     }
178
179     public static ChessQRCodeReader getInstance() {
180         return _instance;
181     }
182
183
184     public static void main(String[] args) {
185         try {
186             ChessQRResult qrCodeResult = ChessQRCodeReader.getInstance().read(
187                     new File("MyQRCode.png")
188             );
189             System.out.println("Decoded format = " + qrCodeResult.getMode());
190             System.out.println("Decoded text = " + qrCodeResult.getContents());
191         } catch (ChessQRCodeException e) {
192             System.out.println("Could not decode QR Code. Exception: " + e.getMessage());
193         }
194     }
195
196 }