/* * MIT License * * Copyright (c) 2017 Yuriy Budiyev [[email protected]] * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.budiyev.android.codescanner; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Collections; import java.util.Map; import java.util.Objects; import android.graphics.Bitmap; import android.graphics.Color; import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.google.zxing.BarcodeFormat; import com.google.zxing.DecodeHintType; import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatReader; import com.google.zxing.MultiFormatWriter; import com.google.zxing.PlanarYUVLuminanceSource; import com.google.zxing.RGBLuminanceSource; import com.google.zxing.ReaderException; import com.google.zxing.Result; import com.google.zxing.WriterException; import com.google.zxing.common.BitMatrix; /** * Utils for decoding and encoding bar codes */ public final class BarcodeUtils { public static final int ROTATION_0 = 0; public static final int ROTATION_90 = 90; public static final int ROTATION_180 = 180; public static final int ROTATION_270 = 270; private BarcodeUtils() { } /** * Decode barcode from bitmap * * @param bitmap Bitmap * @return Decode result, if barcode was decoded successfully, {@code null} otherwise */ @Nullable public static Result decodeBitmap(@NonNull final Bitmap bitmap) { return decodeBitmap(bitmap, null); } /** * Decode barcode from bitmap * * @param bitmap Bitmap * @param hints Decoder hints * @return Decode result, if barcode was decoded successfully, {@code null} otherwise * @see DecodeHintType */ @Nullable public static Result decodeBitmap(@NonNull final Bitmap bitmap, @Nullable final Map<DecodeHintType, ?> hints) { Objects.requireNonNull(bitmap); final int width = bitmap.getWidth(); final int height = bitmap.getHeight(); final int[] pixels = new int[width * height]; bitmap.getPixels(pixels, 0, width, 0, 0, width, height); return decodeRgb(pixels, width, height, hints); } /** * Decode barcode from RGB pixels array * * @param pixels Colors in standard Android ARGB format * @param width Image width * @param height Image height * @return Decode result, if barcode was decoded successfully, {@code null} otherwise * @see Color */ @Nullable public static Result decodeRgb(@NonNull final int[] pixels, final int width, final int height) { return decodeRgb(pixels, width, height, null); } /** * Decode barcode from RGB pixels array * * @param pixels Colors in standard Android ARGB format * @param width Image width * @param height Image height * @param hints Decoder hints * @return Decode result, if barcode was decoded successfully, {@code null} otherwise * @see DecodeHintType * @see Color */ @Nullable public static Result decodeRgb(@NonNull final int[] pixels, final int width, final int height, @Nullable final Map<DecodeHintType, ?> hints) { Objects.requireNonNull(pixels); final MultiFormatReader reader = createReader(hints); try { return Utils .decodeLuminanceSource(reader, new RGBLuminanceSource(width, height, pixels)); } catch (final ReaderException e) { return null; } } /** * Decode barcode from YUV pixels array * * @param pixels YUV image data * @param width Image width * @param height Image height * @return Decode result, if barcode was decoded successfully, {@code null} otherwise */ @Nullable public static Result decodeYuv(@NonNull final byte[] pixels, final int width, final int height) { return decodeYuv(pixels, width, height, ROTATION_0, false, null); } /** * Decode barcode from YUV pixels array * * @param pixels YUV image data * @param width Image width * @param height Image height * @param rotation Degrees to rotate image before decoding (only 0, 90, 180 or 270 are allowed) * @param reverseHorizontal Reverse image horizontally before decoding * @param hints Decoder hints * @return Decode result, if barcode was decoded successfully, {@code null} otherwise * @see DecodeHintType */ @Nullable @SuppressWarnings("SuspiciousNameCombination") public static Result decodeYuv(@NonNull final byte[] pixels, final int width, final int height, @Rotation final int rotation, final boolean reverseHorizontal, @Nullable final Map<DecodeHintType, ?> hints) { Objects.requireNonNull(pixels); final byte[] rotatedPixels = Utils.rotateYuv(pixels, width, height, rotation); final int rotatedWidth; final int rotatedHeight; if (rotation == ROTATION_90 || rotation == ROTATION_270) { rotatedWidth = height; rotatedHeight = width; } else { rotatedWidth = width; rotatedHeight = height; } final MultiFormatReader reader = createReader(hints); try { return Utils.decodeLuminanceSource(reader, new PlanarYUVLuminanceSource(rotatedPixels, rotatedWidth, rotatedHeight, 0, 0, rotatedWidth, rotatedHeight, reverseHorizontal)); } catch (final ReaderException e) { return null; } } /** * Encode text content * * @param content Text to be encoded * @param format Result barcode format * @param width Result image width * @param height Result image height * @return Barcode bit matrix, if it was encoded successfully, {@code null} otherwise * @see BitMatrix */ @Nullable public static BitMatrix encodeBitMatrix(@NonNull final String content, @NonNull final BarcodeFormat format, final int width, final int height) { return encodeBitMatrix(content, format, width, height, null); } /** * Encode text content * * @param content Text to be encoded * @param format Result barcode format * @param width Result image width * @param height Result image height * @param hints Encoder hints * @return Barcode bit matrix, if it was encoded successfully, {@code null} otherwise * @see EncodeHintType * @see BitMatrix */ @Nullable public static BitMatrix encodeBitMatrix(@NonNull final String content, @NonNull final BarcodeFormat format, final int width, final int height, @Nullable final Map<EncodeHintType, ?> hints) { Objects.requireNonNull(content); Objects.requireNonNull(format); final MultiFormatWriter writer = new MultiFormatWriter(); try { if (hints != null) { return writer.encode(content, format, width, height, hints); } else { return writer.encode(content, format, width, height); } } catch (final WriterException e) { return null; } } /** * Encode text content * * @param content Text to be encoded * @param format Result barcode format * @param width Result image width * @param height Result image height * @return Barcode bitmap, if it was encoded successfully, {@code null} otherwise */ @Nullable public static Bitmap encodeBitmap(@NonNull final String content, @NonNull final BarcodeFormat format, final int width, final int height) { return encodeBitmap(content, format, width, height, null); } /** * Encode text content * * @param content Text to be encoded * @param format Result barcode format * @param width Result image width * @param height Result image height * @param hints Encoder hints * @return Barcode bitmap, if it was encoded successfully, {@code null} otherwise * @see EncodeHintType */ @Nullable public static Bitmap encodeBitmap(@NonNull final String content, @NonNull final BarcodeFormat format, final int width, final int height, @Nullable final Map<EncodeHintType, ?> hints) { final BitMatrix matrix = encodeBitMatrix(content, format, width, height, hints); if (matrix != null) { return createBitmap(matrix); } else { return null; } } /** * Create bitmap from bit matrix * * @param matrix Bit matrix * @return Bitmap * @see BitMatrix * @see Bitmap */ @NonNull public static Bitmap createBitmap(@NonNull final BitMatrix matrix) { Objects.requireNonNull(matrix); final int width = matrix.getWidth(); final int height = matrix.getHeight(); final int length = width * height; final int[] pixels = new int[length]; for (int i = 0; i < length; i++) { pixels[i] = matrix.get(i % width, i / height) ? Color.BLACK : Color.WHITE; } return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888); } @NonNull private static MultiFormatReader createReader(@Nullable final Map<DecodeHintType, ?> hints) { final MultiFormatReader reader = new MultiFormatReader(); if (hints != null) { reader.setHints(hints); } else { reader.setHints(Collections .singletonMap(DecodeHintType.POSSIBLE_FORMATS, CodeScanner.ALL_FORMATS)); } return reader; } @Retention(RetentionPolicy.SOURCE) @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270}) public @interface Rotation { } }