Quellcode durchsuchen

添加了图片扫描,修改了对焦方式

Kevin vor 6 Jahren
Ursprung
Commit
ebe5a33654

+ 1 - 1
android/gradle.properties

@@ -1 +1 @@
-org.gradle.jvmargs=-Xmx1536M
+org.gradle.jvmargs=-Xmx1536M

+ 75 - 0
android/src/main/java/info/geteasy/fqreader/BitmapLuminanceSource.java

@@ -0,0 +1,75 @@
+package info.geteasy.fqreader;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+import com.google.zxing.LuminanceSource;
+
+public class BitmapLuminanceSource extends LuminanceSource {
+
+
+
+    private byte luminances[];
+
+    public BitmapLuminanceSource(Bitmap bitmap) {
+        super(bitmap.getWidth(), bitmap.getHeight());
+
+        int width = bitmap.getWidth();
+        int height = bitmap.getHeight();
+
+        int[] pixels = new int[width * height];
+        bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
+
+        // 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.
+        luminances = new byte[width * height];
+        for (int y = 0; y < height; y++) {
+            int offset = y * width;
+            for (int x = 0; x < width; x++) {
+                int pixel = pixels[offset + x];
+                int r = (pixel >> 16) & 0xff;
+                int g = (pixel >> 8) & 0xff;
+                int b = pixel & 0xff;
+                if (r == g && g == b) {
+                    // Image is already greyscale, so pick any channel.
+                    luminances[offset + x] = (byte) r;
+                } else {
+                    // Calculate luminance cheaply, favoring green.
+                    luminances[offset + x] = (byte) ((r + g + g + b) >> 2);
+                }
+            }
+        }
+    }
+
+    public BitmapLuminanceSource(byte[] bytes){
+        this(BitmapFactory.decodeByteArray(bytes, 0, bytes.length));
+    }
+
+
+    @Override
+    public byte[] getRow(int y, byte[] row) {
+        if (y < 0 || y >= getHeight()) {
+            throw new IllegalArgumentException("Requested row is outside the image: " + y);
+        }
+        int width = getWidth();
+        if (row == null || row.length < width) {
+            row = new byte[width];
+        }
+
+        System.arraycopy(luminances, y * width, row, 0, width);
+        return row;
+    }
+
+    // Since this class does not support cropping, the underlying byte array
+    // already contains
+    // exactly what the caller is asking for, so give it to them without a copy.
+    @Override
+    public byte[] getMatrix() {
+        return luminances;
+    }
+
+
+}
+

+ 39 - 99
android/src/main/java/info/geteasy/fqreader/DecodeThread.java

@@ -8,6 +8,7 @@ import android.os.Message;
 import com.google.zxing.BarcodeFormat;
 import com.google.zxing.BinaryBitmap;
 import com.google.zxing.DecodeHintType;
+import com.google.zxing.LuminanceSource;
 import com.google.zxing.MultiFormatReader;
 import com.google.zxing.NotFoundException;
 import com.google.zxing.PlanarYUVLuminanceSource;
@@ -35,32 +36,44 @@ import java.util.List;
 import java.util.Map;
 
 public class DecodeThread extends  Thread {
-    public boolean exit = false;
-    private Map<DecodeHintType, Object> mHints = new Hashtable<>();
+    private boolean mExit = false;
     private byte[] mImageBytes;;
     private Rect mScanRect;
     private Camera.Size mCameraSize;
     private Handler mDecodeHandler;
-    private Reader[] mReaders;
+    private MultipleDecode mDecode;
 
-    public DecodeThread(
+    DecodeThread(
                         Handler decodeHandler,
                         Camera.Size cameraSize,
                         Rect scanRect){
         this.mScanRect = scanRect;
         this.mCameraSize = cameraSize;
         this.mDecodeHandler = decodeHandler;
+        mDecode = new MultipleDecode();
 
         //设置字符集为UTF-8
-        mHints.put(DecodeHintType.CHARACTER_SET, "utf-8");
+        Map<DecodeHintType, Object> hints = new Hashtable<>();
+        hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
+        mDecode.setHints(hints);
     }
-
-    public void decode(byte[] bytes){
+    void decode(byte[] bytes){
         synchronized (this){
             this.notify();
         }
         this.mImageBytes = bytes;
     }
+    void setFormats(List<String> formats){
+        mDecode.setFormats(formats);
+    }
+
+    void release(){
+        synchronized (this) {
+            mExit = true;
+            mImageBytes = null;
+            this.notify();//等待下一次解码
+        }
+    }
 
     @Override
     public void run() {
@@ -71,22 +84,22 @@ public class DecodeThread extends  Thread {
                 e.printStackTrace();
             }
         }
-        while (!exit) {
+        while (!mExit) {
             try {
                 int width = mCameraSize.width;
                 int height = mCameraSize.height;
 
                 com.google.zxing.Result rawResult = null;
-                PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(mImageBytes,
+                LuminanceSource source = new PlanarYUVLuminanceSource(mImageBytes,
                         width, height,
-                        mScanRect.top,mScanRect.left,
-                        mScanRect.height(),mScanRect.width(),
+                        mScanRect.left,mScanRect.top,
+                        mScanRect.width(),mScanRect.height(),
                         true);
 
                 BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
                 try {
 
-                    rawResult = decodeInternal(bitmap);
+                    rawResult = mDecode.decode(bitmap);
                 } catch (ReaderException re) {
                     // continue
                 } catch (NullPointerException npe) {
@@ -94,21 +107,21 @@ public class DecodeThread extends  Thread {
                 } catch (ArrayIndexOutOfBoundsException aoe) {
                     //
                 } finally {
-                    reset();
+                    mDecode.reset();
                 }
-//
-//                    if (rawResult == null) {
-//                        LuminanceSource invertedSource = source.invert();
-//                        bitmap = new BinaryBitmap(new HybridBinarizer(invertedSource));
-//                        try {
-//                            rawResult = multiFormatReader.decodeWithState(bitmap);
-//
-//                        } catch (NotFoundException e) {
-//                            // continue
-//                        } finally {
-//                            multiFormatReader.reset();
-//                        }
-//                    }
+
+                    if (rawResult == null) {
+                        LuminanceSource invertedSource = source.rotateCounterClockwise();
+                        bitmap = new BinaryBitmap(new HybridBinarizer(invertedSource));
+                        try {
+                            rawResult = mDecode.decode(bitmap);
+
+                        } catch (NotFoundException e) {
+                            // continue
+                        } finally {
+                            mDecode.reset();
+                        }
+                    }
 
                 if (rawResult != null) {
                     Message msg = new Message();
@@ -130,77 +143,4 @@ public class DecodeThread extends  Thread {
         }
     }
 
-    public void setFormats(List<String> formats){
-        ArrayList<Reader> readers = new ArrayList<>();
-        for(int i = 0;i< formats.size();i++){
-            String item = formats.get(i);
-            switch (item){
-                case "ScanType.CODABAR":
-                    readers.add(new CodaBarReader());
-                    break;
-                case "ScanType.QR_CODE":
-                    readers.add(new QRCodeReader());
-                    break;
-                case "ScanType.AZTEC":
-                    readers.add(new AztecReader());
-                    break;
-                case "ScanType.CODE_39":
-                    readers.add(new Code39Reader());
-                    break;
-                case "ScanType.CODE_93":
-                    readers.add(new Code93Reader());
-                    break;
-                case "ScanType.CODE_128":
-                    readers.add(new Code128Reader());
-                    break;
-                case "ScanType.EAN8":
-                    readers.add(new EAN8Reader());
-                    break;
-                case "ScanType.EAN13":
-                    readers.add(new EAN13Reader());
-                    break;
-                case "ScanType.ITF":
-                    readers.add(new ITFReader());
-                    break;
-                case "ScanType.DATA_MATRIX":
-                    readers.add(new DataMatrixReader());
-                    break;
-                case "ScanType.PDF_417":
-                    readers.add(new PDF417Reader());
-                    break;
-            }
-        }
-        this.mReaders = readers.toArray(new Reader[readers.size()]);
-
-    }
-
-    public  void release(){
-        synchronized (this) {
-            exit = true;
-            mImageBytes = null;
-            mReaders = null;
-            this.notify();//等待下一次解码
-        }
-    }
-
-    private void reset() {
-        if (mReaders != null) {
-            for (Reader reader : mReaders) {
-                reader.reset();
-            }
-        }
-    }
-
-    private Result decodeInternal(BinaryBitmap image) throws NotFoundException {
-        if (mReaders != null) {
-            for (Reader reader : mReaders) {
-                try {
-                    return reader.decode(image, mHints);
-                } catch (ReaderException re) {
-                    // continue
-                }
-            }
-        }
-        throw NotFoundException.getNotFoundInstance();
-    }
 }

+ 37 - 0
android/src/main/java/info/geteasy/fqreader/FqreaderPlugin.java

@@ -10,8 +10,18 @@ import android.hardware.Camera;
 import android.hardware.camera2.CameraManager;
 import android.os.Build;
 
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.ChecksumException;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.FormatException;
+import com.google.zxing.NotFoundException;
+import com.google.zxing.PlanarYUVLuminanceSource;
+import com.google.zxing.common.HybridBinarizer;
+
 import java.util.HashMap;
+import java.util.Hashtable;
 import java.util.List;
+import java.util.Map;
 
 import io.flutter.plugin.common.MethodCall;
 import io.flutter.plugin.common.MethodChannel;
@@ -103,6 +113,33 @@ public class FqreaderPlugin implements MethodCallHandler {
                 scanView.release();
                 scanView = null;
                 break;
+            case "decodeImg":
+                MultipleDecode decode = new MultipleDecode();
+                List<String> decodeImgScanType = call.argument("scanType");
+                byte[] imgData = call.argument("image");
+
+                Map<DecodeHintType, Object> hints = new Hashtable<>();
+                hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
+                decode.setHints(hints);
+                decode.setFormats(decodeImgScanType);
+                try {
+                    com.google.zxing.Result scanResult  = decode.decode(new BinaryBitmap(
+                            new HybridBinarizer(
+                                new BitmapLuminanceSource(imgData)
+                            )));
+                    result.success(scanResult.getText());
+                } catch (NotFoundException e) {
+                    result.success(null);
+                } catch (ChecksumException e) {
+                    result.error("ChecksumException ",e.getLocalizedMessage(),e);
+//                    e.printStackTrace();
+                } catch (FormatException e) {
+                    result.error("FormatException ",e.getLocalizedMessage(),e);
+//                    e.printStackTrace();
+                }catch (Exception e){
+                    result.error("Exception ",e.getLocalizedMessage(),e);
+                }
+                break;
         }
     }
 

+ 126 - 0
android/src/main/java/info/geteasy/fqreader/MultipleDecode.java

@@ -0,0 +1,126 @@
+package info.geteasy.fqreader;
+
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.ChecksumException;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.FormatException;
+import com.google.zxing.NotFoundException;
+import com.google.zxing.Reader;
+import com.google.zxing.ReaderException;
+import com.google.zxing.Result;
+import com.google.zxing.aztec.AztecReader;
+import com.google.zxing.datamatrix.DataMatrixReader;
+import com.google.zxing.oned.CodaBarReader;
+import com.google.zxing.oned.Code128Reader;
+import com.google.zxing.oned.Code39Reader;
+import com.google.zxing.oned.Code93Reader;
+import com.google.zxing.oned.EAN13Reader;
+import com.google.zxing.oned.EAN8Reader;
+import com.google.zxing.oned.ITFReader;
+import com.google.zxing.pdf417.PDF417Reader;
+import com.google.zxing.qrcode.QRCodeReader;
+
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+public class MultipleDecode implements Reader {
+    private Reader[] mReaders;
+    private Map<DecodeHintType, ?> mHints;
+
+    void setFormats(List<String> formats){
+        ArrayList<Reader> readers = new ArrayList<>();
+        if(formats.contains("ScanType.ALL")){
+            readers.add(new CodaBarReader());
+            readers.add(new QRCodeReader());
+            readers.add(new AztecReader());
+            readers.add(new Code39Reader());
+            readers.add(new Code93Reader());
+            readers.add(new Code128Reader());
+            readers.add(new EAN8Reader());
+            readers.add(new EAN13Reader());
+            readers.add(new ITFReader());
+            readers.add(new DataMatrixReader());
+            readers.add(new PDF417Reader());
+        }else{
+            for(int i = 0;i< formats.size();i++){
+                String item = formats.get(i);
+                switch (item){
+                    case "ScanType.CODABAR":
+                        readers.add(new CodaBarReader());
+                        break;
+                    case "ScanType.QR_CODE":
+                        readers.add(new QRCodeReader());
+                        break;
+                    case "ScanType.AZTEC":
+                        readers.add(new AztecReader());
+                        break;
+                    case "ScanType.CODE_39":
+                        readers.add(new Code39Reader());
+                        break;
+                    case "ScanType.CODE_93":
+                        readers.add(new Code93Reader());
+                        break;
+                    case "ScanType.CODE_128":
+                        readers.add(new Code128Reader());
+                        break;
+                    case "ScanType.EAN8":
+                        readers.add(new EAN8Reader());
+                        break;
+                    case "ScanType.EAN13":
+                        readers.add(new EAN13Reader());
+                        break;
+                    case "ScanType.ITF":
+                        readers.add(new ITFReader());
+                        break;
+                    case "ScanType.DATA_MATRIX":
+                        readers.add(new DataMatrixReader());
+                        break;
+                    case "ScanType.PDF_417":
+                        readers.add(new PDF417Reader());
+                        break;
+                }
+            }
+        }
+        this.mReaders = readers.toArray(new Reader[readers.size()]);
+
+    }
+
+    void setHints(Map<DecodeHintType, ?> hints){
+        this.mHints = hints;
+    }
+
+    @Override
+    public Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException {
+        return decodeInternal(image);
+    }
+
+    @Override
+    public Result decode(BinaryBitmap image, Map<DecodeHintType, ?> hints) throws NotFoundException, ChecksumException, FormatException {
+        setHints(hints);
+        return decodeInternal(image);
+    }
+
+    @Override
+    public void reset() {
+        if (mReaders != null) {
+            for (Reader reader : mReaders) {
+                reader.reset();
+            }
+        }
+    }
+
+    private Result decodeInternal(BinaryBitmap image) throws NotFoundException {
+        if (mReaders != null) {
+            for (Reader reader : mReaders) {
+                try {
+                    return reader.decode(image, mHints);
+                } catch (ReaderException re) {
+                    // continue
+                }
+            }
+        }
+        throw NotFoundException.getNotFoundInstance();
+    }
+}

+ 1 - 1
android/src/main/java/info/geteasy/fqreader/ScanView.java

@@ -51,7 +51,7 @@ public class ScanView {
         try {
             mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
             Camera.Parameters param = mCamera.getParameters();
-            param.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
+            param.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
             param.setRotation(90);
 
             // 选择最合适的预览图像大小

+ 2 - 0
example/android/gradle.properties

@@ -1 +1,3 @@
 org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true

+ 20 - 9
example/lib/main.dart

@@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
 import 'package:fqreader/fqreader.dart';
 import 'package:flustars/flustars.dart';
 import 'package:cool_ui/cool_ui.dart';
+import 'package:image_picker/image_picker.dart';
 
 void main() => runApp(new MyApp());
 
@@ -45,15 +46,6 @@ class _MyAppState extends State<MyApp> {
               ScanView(
                   key: scanView,
                   continuityScan: true,
-                  scanType: [
-                    ScanType.QR_CODE,
-                    ScanType.CODABAR,
-                    ScanType.CODE_39,
-                    ScanType.CODE_93,
-                    ScanType.CODE_128,
-                    ScanType.EAN8,
-                    ScanType.EAN13
-                  ],
                   onScan: (value) async {
                     showWeuiSuccessToast(
                         context: context, message: Text("扫描成功:" + value),closeDuration:Duration(milliseconds: 500));
@@ -97,6 +89,25 @@ class _MyAppState extends State<MyApp> {
                   onPressed: () => scanView.currentState.turnOff(),
                 ),
               ),
+              Positioned(
+                top: 60.0,
+                left: 0.0,
+                child: FlatButton(
+                  child: Text("扫描图片"),
+                  color: Colors.red,
+                  onPressed: () async {
+                    var image = await ImagePicker.pickImage(source: ImageSource.camera);
+                    var value = await Fqreader.decodeImg(image, [ScanType.ALL]);
+                    if(value == null){
+                      showWeuiSuccessToast(
+                          context: context, message: Text("未扫描到数据"),closeDuration:Duration(milliseconds: 3000));
+                    }else{
+                      showWeuiSuccessToast(
+                          context: context, message: Text("扫描成功:" + value),closeDuration:Duration(milliseconds: 500));
+                    }
+                  },
+                ),
+              ),
               Positioned(
                 top: scanRect.top - 60,
                 left: scanRect.left,

+ 1 - 0
example/pubspec.yaml

@@ -21,6 +21,7 @@ dependencies:
   cupertino_icons: ^0.1.2
   flustars: 0.1.3
   cool_ui: 0.1.3
+  image_picker: ^0.4.10+1
   fqreader:
     path: ../
 

+ 39 - 18
lib/fqreader.dart

@@ -1,6 +1,8 @@
 library fqreader;
 
 import 'dart:async';
+import 'dart:io';
+import 'dart:typed_data';
 
 import 'dart:ui' as ui;
 
@@ -10,11 +12,26 @@ import 'package:flutter/services.dart';
 
 typedef ScanEvent = Future<bool> Function(String value);
 
-class _Fqreader {
+class Fqreader {
   static const MethodChannel _channel =
   const MethodChannel('fqreader');
 
-  static Future<int> initView({
+  static Future<String> decodeImg(File file,List<ScanType> scanType) async{
+    var scanStr = new List<String>();
+    scanType.forEach((item){
+      scanStr.add(item.toString());
+    });
+
+    List<int> data = file.readAsBytesSync();
+    Uint8List uData = new Uint8List.fromList(data);
+
+    return await  _channel.invokeMethod('decodeImg',{
+      "image": uData,
+      "scanType": scanStr
+    });
+  }
+
+  static Future<int> _initView({
       @required Rect viewRect,
       @required Rect scanRect,
       @required List<ScanType> scanType,
@@ -42,20 +59,20 @@ class _Fqreader {
     });
     return textureId;
   }
-  static Future startScan() async{
+  static Future _startScan() async{
     await _channel.invokeMethod('startScan');
   }
 
-  static Future stopScan() async{
+  static Future _stopScan() async{
     await _channel.invokeMethod('stopScan');
   }
-  static Future turnOn() async{
+  static Future _turnOn() async{
     await _channel.invokeMethod("turnOn");
   }
-  static Future turnOff() async{
+  static Future _turnOff() async{
     await _channel.invokeMethod("turnOff");
   }
-  static Future release() async{
+  static Future _release() async{
     await _channel.invokeMethod("release");
   }
 }
@@ -102,7 +119,7 @@ class ScanView extends StatefulWidget{
       this.onScan,
       @required this.viewRect,
       @required this.scanRect,
-      this.scanType = const [ScanType.QR_CODE],
+      this.scanType = const [ScanType.ALL],
       this.autoScan = true,
       this.continuityScan = false,
       this.scanInterval = const Duration(milliseconds:500)})
@@ -120,7 +137,7 @@ class ScanViewState extends State<ScanView>{
     // TODO: implement initState
     super.initState();
     MediaQueryData mediaQuery = MediaQueryData.fromWindow(ui.window);
-    _Fqreader.initView(
+    Fqreader._initView(
         viewRect: widget.viewRect,
         scanRect:widget.scanRect,
         devicePixelRatio:mediaQuery.devicePixelRatio,
@@ -130,7 +147,7 @@ class ScanViewState extends State<ScanView>{
         _textureId = textureId;
       });
       if(widget.autoScan){
-        _Fqreader.startScan();
+        Fqreader._startScan();
       }
       _readySubscription = new EventChannel('fqreader/scanEvents$_textureId')
           .receiveBroadcastStream()
@@ -149,33 +166,33 @@ class ScanViewState extends State<ScanView>{
   void dispose(){
     super.dispose();
     _readySubscription.cancel();
-    _Fqreader.release();
+    Fqreader._release();
   }
 
   /**
    * 开始扫描
    */
   Future startScan() async{
-    await _Fqreader.startScan();
+    await Fqreader._startScan();
   }
 
   /**
    * 暂停扫描
    */
   Future stopScan() async{
-    await _Fqreader.stopScan();
+    await Fqreader._stopScan();
   }
   /**
    * 开灯
    */
   Future turnOn() async{
-    await _Fqreader.turnOn();
+    await Fqreader._turnOn();
   }
   /**
    * 关灯
    */
   Future turnOff() async{
-    await _Fqreader.turnOff();
+    await Fqreader._turnOff();
   }
 
   void _listener(dynamic value) {
@@ -183,15 +200,15 @@ class ScanViewState extends State<ScanView>{
       {
         if(!widget.continuityScan) //是否连续扫描
           {
-            _Fqreader.stopScan();
+            Fqreader._stopScan();
           }
         widget.onScan(value).then((result){
           if(widget.continuityScan && result){
             Future.delayed(widget.scanInterval,(){
-              _Fqreader.startScan();
+              Fqreader._startScan();
             });
           }else{
-            _Fqreader.stopScan();
+            Fqreader._stopScan();
           }
         });
       }
@@ -199,6 +216,10 @@ class ScanViewState extends State<ScanView>{
 }
 
 enum ScanType{
+  /**
+   * 所有条形码
+   */
+  ALL,
   /**
    *  普通二维码
    */