content: package com.yalantis.ucrop.view;
  
  import android.content.Context;
  import android.content.res.TypedArray;
  import android.graphics.Canvas;
  import android.graphics.Paint;
  import android.graphics.Path;
  import android.graphics.RectF;
  import android.graphics.Region;
  import android.os.Build;
  import android.support.annotation.ColorInt;
  import android.support.annotation.IntRange;
  import android.support.annotation.NonNull;
  import android.util.AttributeSet;
  import android.view.View;
  
  import com.yalantis.ucrop.R;
  
  /**
   * Created by Oleksii Shliama (https://github.com/shliama).
   * <p/>
   * This view is used for drawing the overlay on top of the image. It may have frame, crop guidelines and dimmed area.
   * This must have LAYER_TYPE_SOFTWARE to draw itself properly.
   */
  public class OverlayView extends View {
  
      public static final boolean DEFAULT_SHOW_CROP_FRAME = true;
      public static final boolean DEFAULT_SHOW_CROP_GRID = true;
      public static final boolean DEFAULT_OVAL_DIMMED_LAYER = false;
      public static final int DEFAULT_CROP_GRID_ROW_COUNT = 2;
      public static final int DEFAULT_CROP_GRID_COLUMN_COUNT = 2;
  
      private final RectF mCropViewRect = new RectF();
  
      private int mCropGridRowCount, mCropGridColumnCount;
      private float mTargetAspectRatio;
      private float[] mGridPoints = null;
      private boolean mShowCropFrame, mShowCropGrid;
      private boolean mOvalDimmedLayer;
      private int mDimmedColor;
      private Path mCircularPath = new Path();
      private Paint mDimmedStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
      private Paint mCropGridPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
      private Paint mCropFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  
      protected int mThisWidth, mThisHeight;
  
      public OverlayView(Context context) {
          this(context, null);
      }
  
      public OverlayView(Context context, AttributeSet attrs) {
          this(context, attrs, 0);
      }
  
      public OverlayView(Context context, AttributeSet attrs, int defStyle) {
          super(context, attrs, defStyle);
          init();
      }
  
      /**
       * Setter for {@link #mOvalDimmedLayer} variable.
       *
       * @param ovalDimmedLayer - set it to true if you want dimmed layer to be an oval
       */
      public void setOvalDimmedLayer(boolean ovalDimmedLayer) {
          mOvalDimmedLayer = ovalDimmedLayer;
      }
  
      /**
       * Setter for crop grid rows count.
       * Resets {@link #mGridPoints} variable because it is not valid anymore.
       */
      public void setCropGridRowCount(@IntRange(from = 0) int cropGridRowCount) {
          mCropGridRowCount = cropGridRowCount;
          mGridPoints = null;
      }
  
      /**
       * Setter for crop grid columns count.
       * Resets {@link #mGridPoints} variable because it is not valid anymore.
       */
      public void setCropGridColumnCount(@IntRange(from = 0) int cropGridColumnCount) {
          mCropGridColumnCount = cropGridColumnCount;
          mGridPoints = null;
      }
  
      /**
       * Setter for {@link #mShowCropFrame} variable.
       *
       * @param showCropFrame - set to true if you want to see a crop frame rectangle on top of an image
       */
      public void setShowCropFrame(boolean showCropFrame) {
          mShowCropFrame = showCropFrame;
      }
  
      /**
       * Setter for {@link #mShowCropGrid} variable.
       *
       * @param showCropGrid - set to true if you want to see a crop grid on top of an image
       */
      public void setShowCropGrid(boolean showCropGrid) {
          mShowCropGrid = showCropGrid;
      }
  
      /**
       * Setter for {@link #mDimmedColor} variable.
       *
       * @param dimmedColor - desired color of dimmed area around the crop bounds
       */
      public void setDimmedColor(@ColorInt int dimmedColor) {
          mDimmedColor = dimmedColor;
      }
  
      /**
       * Setter for crop frame stroke width
       */
      public void setCropFrameStrokeWidth(@IntRange(from = 0) int width) {
          mCropFramePaint.setStrokeWidth(width);
      }
  
      /**
       * Setter for crop grid stroke width
       */
      public void setCropGridStrokeWidth(@IntRange(from = 0) int width) {
          mCropGridPaint.setStrokeWidth(width);
      }
  
      /**
       * Setter for crop frame color
       */
      public void setCropFrameColor(@ColorInt int color) {
          mCropFramePaint.setColor(color);
      }
  
      /**
       * Setter for crop grid color
       */
      public void setCropGridColor(@ColorInt int color) {
          mCropGridPaint.setColor(color);
      }
  
      /**
       * This method sets aspect ratio for crop bounds.
       *
       * @param targetAspectRatio - aspect ratio for image crop (e.g. 1.77(7) for 16:9)
       */
      public void setTargetAspectRatio(float targetAspectRatio) {
          mTargetAspectRatio = targetAspectRatio;
          setupCropBounds();
      }
  
      /**
       * This method setups crop bounds rectangles for given aspect ratio and view size.
       * {@link #mCropViewRect} is used to draw crop bounds - uses padding.
       */
      public void setupCropBounds() {
          int height = (int) (mThisWidth / mTargetAspectRatio);
          if (height > mThisHeight) {
              int width = (int) (mThisHeight * mTargetAspectRatio);
              int halfDiff = (mThisWidth - width) / 2;
              mCropViewRect.set(getPaddingLeft() + halfDiff, getPaddingTop(),
                      getPaddingLeft() + width + halfDiff, getPaddingTop() + mThisHeight);
          } else {
              int halfDiff = (mThisHeight - height) / 2;
              mCropViewRect.set(getPaddingLeft(), getPaddingTop() + halfDiff,
                      getPaddingLeft() + mThisWidth, getPaddingTop() + height + halfDiff);
          }
  
          mGridPoints = null;
          mCircularPath.reset();
          mCircularPath.addOval(mCropViewRect, Path.Direction.CW);
      }
  
      protected void init() {
          if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2 &&
                  Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
              setLayerType(LAYER_TYPE_SOFTWARE, null);
          }
      }
  
      @Override
      protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
          super.onLayout(changed, left, top, right, bottom);
          if (changed) {
              left = getPaddingLeft();
              top = getPaddingTop();
              right = getWidth() - getPaddingRight();
              bottom = getHeight() - getPaddingBottom();
              mThisWidth = right - left;
              mThisHeight = bottom - top;
              setupCropBounds();
          }
      }
  
      /**
       * Along with image there are dimmed layer, crop bounds and crop guidelines that must be drawn.
       */
      @Override
      protected void onDraw(Canvas canvas) {
          super.onDraw(canvas);
          drawDimmedLayer(canvas);
          drawCropGrid(canvas);
      }
  
      /**
       * This method draws dimmed area around the crop bounds.
       *
       * @param canvas - valid canvas object
       */
      protected void drawDimmedLayer(@NonNull Canvas canvas) {
          canvas.save();
          if (mOvalDimmedLayer) {
              canvas.clipPath(mCircularPath, Region.Op.DIFFERENCE);
          } else {
              canvas.clipRect(mCropViewRect, Region.Op.DIFFERENCE);
          }
          canvas.drawColor(mDimmedColor);
          canvas.restore();
  
          if (mOvalDimmedLayer) { // Draw 1px stroke to fix antialias
              canvas.drawOval(mCropViewRect, mDimmedStrokePaint);
          }
      }
  
      /**
       * This method draws crop bounds (empty rectangle)
       * and crop guidelines (vertical and horizontal lines inside the crop bounds) if needed.
       *
       * @param canvas - valid canvas object
       */
      protected void drawCropGrid(@NonNull Canvas canvas) {
          if (mShowCropGrid) {
              if (mGridPoints == null && !mCropViewRect.isEmpty()) {
  
                  mGridPoints = new float[(mCropGridRowCount) * 4 + (mCropGridColumnCount) * 4];
  
                  int index = 0;
                  for (int i = 0; i < mCropGridRowCount; i++) {
                      mGridPoints[index++] = mCropViewRect.left;
                      mGridPoints[index++] = (mCropViewRect.height() * (((float) i + 1.0f) / (float) (mCropGridRowCount + 1))) + mCropViewRect.top;
                      mGridPoints[index++] = mCropViewRect.right;
                      mGridPoints[index++] = (mCropViewRect.height() * (((float) i + 1.0f) / (float) (mCropGridRowCount + 1))) + mCropViewRect.top;
                  }
  
                  for (int i = 0; i < mCropGridColumnCount; i++) {
                      mGridPoints[index++] = (mCropViewRect.width() * (((float) i + 1.0f) / (float) (mCropGridColumnCount + 1))) + mCropViewRect.left;
                      mGridPoints[index++] = mCropViewRect.top;
                      mGridPoints[index++] = (mCropViewRect.width() * (((float) i + 1.0f) / (float) (mCropGridColumnCount + 1))) + mCropViewRect.left;
                      mGridPoints[index++] = mCropViewRect.bottom;
                  }
              }
  
              if (mGridPoints != null) {
                  canvas.drawLines(mGridPoints, mCropGridPaint);
              }
          }
  
          if (mShowCropFrame) {
              canvas.drawRect(mCropViewRect, mCropFramePaint);
          }
      }
  
      /**
       * This method extracts all needed values from the styled attributes.
       * Those are used to configure the view.
       */
      @SuppressWarnings("deprecation")
      protected void processStyledAttributes(@NonNull TypedArray a) {
          mOvalDimmedLayer = a.getBoolean(R.styleable.ucrop_UCropView_ucrop_oval_dimmed_layer, DEFAULT_OVAL_DIMMED_LAYER);
          mDimmedColor = a.getColor(R.styleable.ucrop_UCropView_ucrop_dimmed_color,
                  getResources().getColor(R.color.ucrop_color_default_dimmed));
          mDimmedStrokePaint.setColor(mDimmedColor);
          mDimmedStrokePaint.setStyle(Paint.Style.STROKE);
          mDimmedStrokePaint.setStrokeWidth(1);
  
          initCropFrameStyle(a);
          mShowCropFrame = a.getBoolean(R.styleable.ucrop_UCropView_ucrop_show_frame, DEFAULT_SHOW_CROP_FRAME);
  
          initCropGridStyle(a);
          mShowCropGrid = a.getBoolean(R.styleable.ucrop_UCropView_ucrop_show_grid, DEFAULT_SHOW_CROP_GRID);
      }
  
      /**
       * This method setups Paint object for the crop bounds.
       */
      @SuppressWarnings("deprecation")
      private void initCropFrameStyle(@NonNull TypedArray a) {
          int cropFrameStrokeSize = a.getDimensionPixelSize(R.styleable.ucrop_UCropView_ucrop_frame_stroke_size,
                  getResources().getDimensionPixelSize(R.dimen.ucrop_default_crop_frame_stoke_width));
          int cropFrameColor = a.getColor(R.styleable.ucrop_UCropView_ucrop_frame_color,
                  getResources().getColor(R.color.ucrop_color_default_crop_frame));
          mCropFramePaint.setStrokeWidth(cropFrameStrokeSize);
          mCropFramePaint.setColor(cropFrameColor);
          mCropFramePaint.setStyle(Paint.Style.STROKE);
      }
  
      /**
       * This method setups Paint object for the crop guidelines.
       */
      @SuppressWarnings("deprecation")
      private void initCropGridStyle(@NonNull TypedArray a) {
          int cropGridStrokeSize = a.getDimensionPixelSize(R.styleable.ucrop_UCropView_ucrop_grid_stroke_size,
                  getResources().getDimensionPixelSize(R.dimen.ucrop_default_crop_grid_stoke_width));
          int cropGridColor = a.getColor(R.styleable.ucrop_UCropView_ucrop_grid_color,
                  getResources().getColor(R.color.ucrop_color_default_crop_grid));
          mCropGridPaint.setStrokeWidth(cropGridStrokeSize);
          mCropGridPaint.setColor(cropGridColor);
  
          mCropGridRowCount = a.getInt(R.styleable.ucrop_UCropView_ucrop_grid_row_count, DEFAULT_CROP_GRID_ROW_COUNT);
          mCropGridColumnCount = a.getInt(R.styleable.ucrop_UCropView_ucrop_grid_column_count, DEFAULT_CROP_GRID_COLUMN_COUNT);
      }
  
  }
  