All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.theartofdev.edmodo.cropper.CropWindowHandler Maven / Gradle / Ivy

There is a newer version: 2.5.0
Show newest version
// "Therefore those skilled at the unorthodox
// are infinite as heaven and earth,
// inexhaustible as the great rivers.
// When they come to an end,
// they begin again,
// like the days and months;
// they die and are reborn,
// like the four seasons."
//
// - Sun Tsu,
// "The Art of War"

package com.theartofdev.edmodo.cropper;

import android.graphics.RectF;

/**
 * Handler from crop window stuff, moving and knowing possition.
 */
final class CropWindowHandler {

    //region: Fields and Consts

    /**
     * The 4 edges of the crop window defining its coordinates and size
     */
    private final RectF mEdges = new RectF();

    /**
     * Rectangle used to return the edges rectangle without ability to change it and without creating new all the time.
     */
    private final RectF mGetEdges = new RectF();

    /**
     * Minimum width in pixels that the crop window can get.
     */
    private float mMinCropWindowWidth;

    /**
     * Minimum height in pixels that the crop window can get.
     */
    private float mMinCropWindowHeight;

    /**
     * Maximum width in pixels that the crop window can CURRENTLY get.
     */
    private float mMaxCropWindowWidth;

    /**
     * Maximum height in pixels that the crop window can CURRENTLY get.
     */
    private float mMaxCropWindowHeight;

    /**
     * Minimum width in pixels that the result of cropping an image can get,
     * affects crop window width adjusted by width scale factor.
     */
    private float mMinCropResultWidth;

    /**
     * Minimum height in pixels that the result of cropping an image can get,
     * affects crop window height adjusted by height scale factor.
     */
    private float mMinCropResultHeight;

    /**
     * Maximum width in pixels that the result of cropping an image can get,
     * affects crop window width adjusted by width scale factor.
     */
    private float mMaxCropResultWidth;

    /**
     * Maximum height in pixels that the result of cropping an image can get,
     * affects crop window height adjusted by height scale factor.
     */
    private float mMaxCropResultHeight;

    /**
     * The width scale factor of shown image and actual image
     */
    private float mScaleFactorWidth = 1;

    /**
     * The height scale factor of shown image and actual image
     */
    private float mScaleFactorHeight = 1;
    //endregion

    /**
     * Get the left/top/right/bottom coordinates of the crop window.
     */
    public RectF getRect() {
        mGetEdges.set(mEdges);
        return mGetEdges;
    }

    /**
     * Minimum width in pixels that the crop window can get.
     */
    public float getMinCropWidth() {
        return Math.max(mMinCropWindowWidth, mMinCropResultWidth / mScaleFactorWidth);
    }

    /**
     * Minimum height in pixels that the crop window can get.
     */
    public float getMinCropHeight() {
        return Math.max(mMinCropWindowHeight, mMinCropResultHeight / mScaleFactorHeight);
    }

    /**
     * Maximum width in pixels that the crop window can get.
     */
    public float getMaxCropWidth() {
        return Math.min(mMaxCropWindowWidth, mMaxCropResultWidth / mScaleFactorWidth);
    }

    /**
     * Maximum height in pixels that the crop window can get.
     */
    public float getMaxCropHeight() {
        return Math.min(mMaxCropWindowHeight, mMaxCropResultHeight / mScaleFactorHeight);
    }

    /**
     * get the scale factor (on width) of the showen image to original image.
     */
    public float getScaleFactorWidth() {
        return mScaleFactorWidth;
    }

    /**
     * get the scale factor (on height) of the showen image to original image.
     */
    public float getScaleFactorHeight() {
        return mScaleFactorHeight;
    }

    /**
     * the min size the resulting cropping image is allowed to be, affects the cropping window limits
     * (in pixels).
*/ public void setMinCropResultSize(int minCropResultWidth, int minCropResultHeight) { mMinCropResultWidth = minCropResultWidth; mMinCropResultHeight = minCropResultHeight; } /** * the max size the resulting cropping image is allowed to be, affects the cropping window limits * (in pixels).
*/ public void setMaxCropResultSize(int maxCropResultWidth, int maxCropResultHeight) { mMaxCropResultWidth = maxCropResultWidth; mMaxCropResultHeight = maxCropResultHeight; } /** * set the max width/height and scale factor of the showen image to original image to scale the limits * appropriately. */ public void setCropWindowLimits(float maxWidth, float maxHeight, float scaleFactorWidth, float scaleFactorHeight) { mMaxCropWindowWidth = maxWidth; mMaxCropWindowHeight = maxHeight; mScaleFactorWidth = scaleFactorWidth; mScaleFactorHeight = scaleFactorHeight; } /** * Set the variables to be used during crop window handling. */ public void setInitialAttributeValues(CropImageOptions options) { mMinCropWindowWidth = options.minCropWindowWidth; mMinCropWindowHeight = options.minCropWindowHeight; mMinCropResultWidth = options.minCropResultWidth; mMinCropResultHeight = options.minCropResultHeight; mMaxCropResultWidth = options.maxCropResultWidth; mMaxCropResultHeight = options.maxCropResultHeight; } /** * Set the left/top/right/bottom coordinates of the crop window. */ public void setRect(RectF rect) { mEdges.set(rect); } /** * Indicates whether the crop window is small enough that the guidelines * should be shown. Public because this function is also used to determine * if the center handle should be focused. * * @return boolean Whether the guidelines should be shown or not */ public boolean showGuidelines() { return !(mEdges.width() < 100 || mEdges.height() < 100); } /** * Determines which, if any, of the handles are pressed given the touch * coordinates, the bounding box, and the touch radius. * * @param x the x-coordinate of the touch point * @param y the y-coordinate of the touch point * @param targetRadius the target radius in pixels * @return the Handle that was pressed; null if no Handle was pressed */ public CropWindowMoveHandler getMoveHandler(float x, float y, float targetRadius, CropImageView.CropShape cropShape) { CropWindowMoveHandler.Type type = cropShape == CropImageView.CropShape.OVAL ? getOvalPressedMoveType(x, y) : getRectanglePressedMoveType(x, y, targetRadius); return type != null ? new CropWindowMoveHandler(type, this, x, y) : null; } //region: Private methods /** * Determines which, if any, of the handles are pressed given the touch * coordinates, the bounding box, and the touch radius. * * @param x the x-coordinate of the touch point * @param y the y-coordinate of the touch point * @param targetRadius the target radius in pixels * @return the Handle that was pressed; null if no Handle was pressed */ private CropWindowMoveHandler.Type getRectanglePressedMoveType(float x, float y, float targetRadius) { CropWindowMoveHandler.Type moveType = null; // Note: corner-handles take precedence, then side-handles, then center. if (CropWindowHandler.isInCornerTargetZone(x, y, mEdges.left, mEdges.top, targetRadius)) { moveType = CropWindowMoveHandler.Type.TOP_LEFT; } else if (CropWindowHandler.isInCornerTargetZone(x, y, mEdges.right, mEdges.top, targetRadius)) { moveType = CropWindowMoveHandler.Type.TOP_RIGHT; } else if (CropWindowHandler.isInCornerTargetZone(x, y, mEdges.left, mEdges.bottom, targetRadius)) { moveType = CropWindowMoveHandler.Type.BOTTOM_LEFT; } else if (CropWindowHandler.isInCornerTargetZone(x, y, mEdges.right, mEdges.bottom, targetRadius)) { moveType = CropWindowMoveHandler.Type.BOTTOM_RIGHT; } else if (CropWindowHandler.isInCenterTargetZone(x, y, mEdges.left, mEdges.top, mEdges.right, mEdges.bottom) && focusCenter()) { moveType = CropWindowMoveHandler.Type.CENTER; } else if (CropWindowHandler.isInHorizontalTargetZone(x, y, mEdges.left, mEdges.right, mEdges.top, targetRadius)) { moveType = CropWindowMoveHandler.Type.TOP; } else if (CropWindowHandler.isInHorizontalTargetZone(x, y, mEdges.left, mEdges.right, mEdges.bottom, targetRadius)) { moveType = CropWindowMoveHandler.Type.BOTTOM; } else if (CropWindowHandler.isInVerticalTargetZone(x, y, mEdges.left, mEdges.top, mEdges.bottom, targetRadius)) { moveType = CropWindowMoveHandler.Type.LEFT; } else if (CropWindowHandler.isInVerticalTargetZone(x, y, mEdges.right, mEdges.top, mEdges.bottom, targetRadius)) { moveType = CropWindowMoveHandler.Type.RIGHT; } else if (CropWindowHandler.isInCenterTargetZone(x, y, mEdges.left, mEdges.top, mEdges.right, mEdges.bottom) && !focusCenter()) { moveType = CropWindowMoveHandler.Type.CENTER; } return moveType; } /** * Determines which, if any, of the handles are pressed given the touch * coordinates, the bounding box/oval, and the touch radius. * * @param x the x-coordinate of the touch point * @param y the y-coordinate of the touch point * @return the Handle that was pressed; null if no Handle was pressed */ private CropWindowMoveHandler.Type getOvalPressedMoveType(float x, float y) { /* Use a 6x6 grid system divided into 9 "handles", with the center the biggest region. While this is not perfect, it's a good quick-to-ship approach. TL T T T T TR L C C C C R L C C C C R L C C C C R L C C C C R BL B B B B BR */ float cellLength = mEdges.width() / 6; float leftCenter = mEdges.left + cellLength; float rightCenter = mEdges.left + (5 * cellLength); float cellHeight = mEdges.height() / 6; float topCenter = mEdges.top + cellHeight; float bottomCenter = mEdges.top + 5 * cellHeight; CropWindowMoveHandler.Type moveType; if (x < leftCenter) { if (y < topCenter) { moveType = CropWindowMoveHandler.Type.TOP_LEFT; } else if (y < bottomCenter) { moveType = CropWindowMoveHandler.Type.LEFT; } else { moveType = CropWindowMoveHandler.Type.BOTTOM_LEFT; } } else if (x < rightCenter) { if (y < topCenter) { moveType = CropWindowMoveHandler.Type.TOP; } else if (y < bottomCenter) { moveType = CropWindowMoveHandler.Type.CENTER; } else { moveType = CropWindowMoveHandler.Type.BOTTOM; } } else { if (y < topCenter) { moveType = CropWindowMoveHandler.Type.TOP_RIGHT; } else if (y < bottomCenter) { moveType = CropWindowMoveHandler.Type.RIGHT; } else { moveType = CropWindowMoveHandler.Type.BOTTOM_RIGHT; } } return moveType; } /** * Determines if the specified coordinate is in the target touch zone for a * corner handle. * * @param x the x-coordinate of the touch point * @param y the y-coordinate of the touch point * @param handleX the x-coordinate of the corner handle * @param handleY the y-coordinate of the corner handle * @param targetRadius the target radius in pixels * @return true if the touch point is in the target touch zone; false * otherwise */ private static boolean isInCornerTargetZone(float x, float y, float handleX, float handleY, float targetRadius) { return Math.abs(x - handleX) <= targetRadius && Math.abs(y - handleY) <= targetRadius; } /** * Determines if the specified coordinate is in the target touch zone for a * horizontal bar handle. * * @param x the x-coordinate of the touch point * @param y the y-coordinate of the touch point * @param handleXStart the left x-coordinate of the horizontal bar handle * @param handleXEnd the right x-coordinate of the horizontal bar handle * @param handleY the y-coordinate of the horizontal bar handle * @param targetRadius the target radius in pixels * @return true if the touch point is in the target touch zone; false * otherwise */ private static boolean isInHorizontalTargetZone(float x, float y, float handleXStart, float handleXEnd, float handleY, float targetRadius) { return x > handleXStart && x < handleXEnd && Math.abs(y - handleY) <= targetRadius; } /** * Determines if the specified coordinate is in the target touch zone for a * vertical bar handle. * * @param x the x-coordinate of the touch point * @param y the y-coordinate of the touch point * @param handleX the x-coordinate of the vertical bar handle * @param handleYStart the top y-coordinate of the vertical bar handle * @param handleYEnd the bottom y-coordinate of the vertical bar handle * @param targetRadius the target radius in pixels * @return true if the touch point is in the target touch zone; false * otherwise */ private static boolean isInVerticalTargetZone(float x, float y, float handleX, float handleYStart, float handleYEnd, float targetRadius) { return Math.abs(x - handleX) <= targetRadius && y > handleYStart && y < handleYEnd; } /** * Determines if the specified coordinate falls anywhere inside the given * bounds. * * @param x the x-coordinate of the touch point * @param y the y-coordinate of the touch point * @param left the x-coordinate of the left bound * @param top the y-coordinate of the top bound * @param right the x-coordinate of the right bound * @param bottom the y-coordinate of the bottom bound * @return true if the touch point is inside the bounding rectangle; false * otherwise */ private static boolean isInCenterTargetZone(float x, float y, float left, float top, float right, float bottom) { return x > left && x < right && y > top && y < bottom; } /** * Determines if the cropper should focus on the center handle or the side * handles. If it is a small image, focus on the center handle so the user * can move it. If it is a large image, focus on the side handles so user * can grab them. Corresponds to the appearance of the * RuleOfThirdsGuidelines. * * @return true if it is small enough such that it should focus on the * center; less than show_guidelines limit */ private boolean focusCenter() { return !showGuidelines(); } //endregion }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy