/* * Copyright (C) The Android Open Source Project * * 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 com.fuse.qreader; import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.View; import com.google.android.gms.vision.CameraSource; import java.util.HashSet; import java.util.Set; /** * A view which renders a series of custom graphics to be overlayed on top of an associated preview * (i.e., the camera preview). The creator can add graphics objects, update the objects, and remove * them, triggering the appropriate drawing and invalidation within the view.<p> * * Supports scaling and mirroring of the graphics relative the camera's preview properties. The * idea is that detection items are expressed in terms of a preview size, but need to be scaled up * to the full view size, and also mirrored in the case of the front-facing camera.<p> * * Associated {@link Graphic} items should use the following methods to convert to view coordinates * for the graphics that are drawn: * <ol> * <li>{@link Graphic#scaleX(float)} and {@link Graphic#scaleY(float)} adjust the size of the * supplied value from the preview scale to the view scale.</li> * <li>{@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the coordinate * from the preview's coordinate system to the view coordinate system.</li> * </ol> */ public class GraphicOverlay<T extends GraphicOverlay.Graphic> extends View { private final Object mLock = new Object(); private int mPreviewWidth; private float mWidthScaleFactor = 1.0f; private int mPreviewHeight; private float mHeightScaleFactor = 1.0f; private int mFacing = CameraSource.CAMERA_FACING_BACK; private Set<T> mGraphics = new HashSet<T>(); private T mFirstGraphic; /** * Base class for a custom graphics object to be rendered within the graphic overlay. Subclass * this and implement the {@link Graphic#draw(Canvas)} method to define the * graphics element. Add instances to the overlay using {@link GraphicOverlay#add(Graphic)}. */ public static abstract class Graphic { private GraphicOverlay mOverlay; public Graphic(GraphicOverlay overlay) { mOverlay = overlay; } /** * Draw the graphic on the supplied canvas. Drawing should use the following methods to * convert to view coordinates for the graphics that are drawn: * <ol> * <li>{@link Graphic#scaleX(float)} and {@link Graphic#scaleY(float)} adjust the size of * the supplied value from the preview scale to the view scale.</li> * <li>{@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the * coordinate from the preview's coordinate system to the view coordinate system.</li> * </ol> * * @param canvas drawing canvas */ public abstract void draw(Canvas canvas); /** * Adjusts a horizontal value of the supplied value from the preview scale to the view * scale. */ public float scaleX(float horizontal) { return horizontal * mOverlay.mWidthScaleFactor; } /** * Adjusts a vertical value of the supplied value from the preview scale to the view scale. */ public float scaleY(float vertical) { return vertical * mOverlay.mHeightScaleFactor; } /** * Adjusts the x coordinate from the preview's coordinate system to the view coordinate * system. */ public float translateX(float x) { if (mOverlay.mFacing == CameraSource.CAMERA_FACING_FRONT) { return mOverlay.getWidth() - scaleX(x); } else { return scaleX(x); } } /** * Adjusts the y coordinate from the preview's coordinate system to the view coordinate * system. */ public float translateY(float y) { return scaleY(y); } public void postInvalidate() { mOverlay.postInvalidate(); } } public GraphicOverlay(Context context) { super(context); } public GraphicOverlay(Context context, AttributeSet attrs) { super(context, attrs); } /** * Removes all graphics from the overlay. */ public void clear() { synchronized (mLock) { mGraphics.clear(); mFirstGraphic = null; } postInvalidate(); } /** * Adds a graphic to the overlay. */ public void add(T graphic) { synchronized (mLock) { mGraphics.add(graphic); if (mFirstGraphic == null) { mFirstGraphic = graphic; } } postInvalidate(); } /** * Removes a graphic from the overlay. */ public void remove(T graphic) { synchronized (mLock) { mGraphics.remove(graphic); if (mFirstGraphic != null && mFirstGraphic.equals(graphic)) { mFirstGraphic = null; } } postInvalidate(); } /** * Returns the first (oldest) graphic added. This is used * to get the barcode that was detected first. * @return graphic containing the barcode, or null if no barcodes are detected. */ public T getFirstGraphic() { synchronized (mLock) { return mFirstGraphic; } } /** * Sets the camera attributes for size and facing direction, which informs how to transform * image coordinates later. */ public void setCameraInfo(int previewWidth, int previewHeight, int facing) { synchronized (mLock) { mPreviewWidth = previewWidth; mPreviewHeight = previewHeight; mFacing = facing; } postInvalidate(); } /** * Draws the overlay with its associated graphic objects. */ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); synchronized (mLock) { if ((mPreviewWidth != 0) && (mPreviewHeight != 0)) { mWidthScaleFactor = (float) canvas.getWidth() / (float) mPreviewWidth; mHeightScaleFactor = (float) canvas.getHeight() / (float) mPreviewHeight; } for (Graphic graphic : mGraphics) { graphic.draw(canvas); } } } }