Home | History | Annotate | Download | only in crop
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.gallery3d.filtershow.crop;
     18 
     19 import android.graphics.Rect;
     20 import android.graphics.RectF;
     21 
     22 import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils;
     23 
     24 public class CropObject {
     25     private BoundedRect mBoundedRect;
     26     private float mAspectWidth = 1;
     27     private float mAspectHeight = 1;
     28     private boolean mFixAspectRatio = false;
     29     private float mRotation = 0;
     30     private float mTouchTolerance = 45;
     31     private float mMinSideSize = 20;
     32 
     33     public static final int MOVE_NONE = 0;
     34     // Sides
     35     public static final int MOVE_LEFT = 1;
     36     public static final int MOVE_TOP = 2;
     37     public static final int MOVE_RIGHT = 4;
     38     public static final int MOVE_BOTTOM = 8;
     39     public static final int MOVE_BLOCK = 16;
     40 
     41     // Corners
     42     public static final int TOP_LEFT = MOVE_TOP | MOVE_LEFT;
     43     public static final int TOP_RIGHT = MOVE_TOP | MOVE_RIGHT;
     44     public static final int BOTTOM_RIGHT = MOVE_BOTTOM | MOVE_RIGHT;
     45     public static final int BOTTOM_LEFT = MOVE_BOTTOM | MOVE_LEFT;
     46 
     47     private int mMovingEdges = MOVE_NONE;
     48 
     49     public CropObject(Rect outerBound, Rect innerBound, int outerAngle) {
     50         mBoundedRect = new BoundedRect(outerAngle % 360, outerBound, innerBound);
     51     }
     52 
     53     public CropObject(RectF outerBound, RectF innerBound, int outerAngle) {
     54         mBoundedRect = new BoundedRect(outerAngle % 360, outerBound, innerBound);
     55     }
     56 
     57     public void resetBoundsTo(RectF inner, RectF outer) {
     58         mBoundedRect.resetTo(0, outer, inner);
     59     }
     60 
     61     public void getInnerBounds(RectF r) {
     62         mBoundedRect.setToInner(r);
     63     }
     64 
     65     public void getOuterBounds(RectF r) {
     66         mBoundedRect.setToOuter(r);
     67     }
     68 
     69     public RectF getInnerBounds() {
     70         return mBoundedRect.getInner();
     71     }
     72 
     73     public RectF getOuterBounds() {
     74         return mBoundedRect.getOuter();
     75     }
     76 
     77     public int getSelectState() {
     78         return mMovingEdges;
     79     }
     80 
     81     public boolean isFixedAspect() {
     82         return mFixAspectRatio;
     83     }
     84 
     85     public void rotateOuter(int angle) {
     86         mRotation = angle % 360;
     87         mBoundedRect.setRotation(mRotation);
     88         clearSelectState();
     89     }
     90 
     91     public boolean setInnerAspectRatio(float width, float height) {
     92         if (width <= 0 || height <= 0) {
     93             throw new IllegalArgumentException("Width and Height must be greater than zero");
     94         }
     95         RectF inner = mBoundedRect.getInner();
     96         CropMath.fixAspectRatioContained(inner, width, height);
     97         if (inner.width() < mMinSideSize || inner.height() < mMinSideSize) {
     98             return false;
     99         }
    100         mAspectWidth = width;
    101         mAspectHeight = height;
    102         mFixAspectRatio = true;
    103         mBoundedRect.setInner(inner);
    104         clearSelectState();
    105         return true;
    106     }
    107 
    108     public void setTouchTolerance(float tolerance) {
    109         if (tolerance <= 0) {
    110             throw new IllegalArgumentException("Tolerance must be greater than zero");
    111         }
    112         mTouchTolerance = tolerance;
    113     }
    114 
    115     public void setMinInnerSideSize(float minSide) {
    116         if (minSide <= 0) {
    117             throw new IllegalArgumentException("Min dide must be greater than zero");
    118         }
    119         mMinSideSize = minSide;
    120     }
    121 
    122     public void unsetAspectRatio() {
    123         mFixAspectRatio = false;
    124         clearSelectState();
    125     }
    126 
    127     public boolean hasSelectedEdge() {
    128         return mMovingEdges != MOVE_NONE;
    129     }
    130 
    131     public static boolean checkCorner(int selected) {
    132         return selected == TOP_LEFT || selected == TOP_RIGHT || selected == BOTTOM_RIGHT
    133                 || selected == BOTTOM_LEFT;
    134     }
    135 
    136     public static boolean checkEdge(int selected) {
    137         return selected == MOVE_LEFT || selected == MOVE_TOP || selected == MOVE_RIGHT
    138                 || selected == MOVE_BOTTOM;
    139     }
    140 
    141     public static boolean checkBlock(int selected) {
    142         return selected == MOVE_BLOCK;
    143     }
    144 
    145     public static boolean checkValid(int selected) {
    146         return selected == MOVE_NONE || checkBlock(selected) || checkEdge(selected)
    147                 || checkCorner(selected);
    148     }
    149 
    150     public void clearSelectState() {
    151         mMovingEdges = MOVE_NONE;
    152     }
    153 
    154     public int wouldSelectEdge(float x, float y) {
    155         int edgeSelected = calculateSelectedEdge(x, y);
    156         if (edgeSelected != MOVE_NONE && edgeSelected != MOVE_BLOCK) {
    157             return edgeSelected;
    158         }
    159         return MOVE_NONE;
    160     }
    161 
    162     public boolean selectEdge(int edge) {
    163         if (!checkValid(edge)) {
    164             // temporary
    165             throw new IllegalArgumentException("bad edge selected");
    166             // return false;
    167         }
    168         if ((mFixAspectRatio && !checkCorner(edge)) && !checkBlock(edge) && edge != MOVE_NONE) {
    169             // temporary
    170             throw new IllegalArgumentException("bad corner selected");
    171             // return false;
    172         }
    173         mMovingEdges = edge;
    174         return true;
    175     }
    176 
    177     public boolean selectEdge(float x, float y) {
    178         int edgeSelected = calculateSelectedEdge(x, y);
    179         if (mFixAspectRatio) {
    180             edgeSelected = fixEdgeToCorner(edgeSelected);
    181         }
    182         if (edgeSelected == MOVE_NONE) {
    183             return false;
    184         }
    185         return selectEdge(edgeSelected);
    186     }
    187 
    188     public boolean moveCurrentSelection(float dX, float dY) {
    189         if (mMovingEdges == MOVE_NONE) {
    190             return false;
    191         }
    192         RectF crop = mBoundedRect.getInner();
    193 
    194         float minWidthHeight = mMinSideSize;
    195 
    196         int movingEdges = mMovingEdges;
    197         if (movingEdges == MOVE_BLOCK) {
    198             mBoundedRect.moveInner(dX, dY);
    199             return true;
    200         } else {
    201             float dx = 0;
    202             float dy = 0;
    203 
    204             if ((movingEdges & MOVE_LEFT) != 0) {
    205                 dx = Math.min(crop.left + dX, crop.right - minWidthHeight) - crop.left;
    206             }
    207             if ((movingEdges & MOVE_TOP) != 0) {
    208                 dy = Math.min(crop.top + dY, crop.bottom - minWidthHeight) - crop.top;
    209             }
    210             if ((movingEdges & MOVE_RIGHT) != 0) {
    211                 dx = Math.max(crop.right + dX, crop.left + minWidthHeight)
    212                         - crop.right;
    213             }
    214             if ((movingEdges & MOVE_BOTTOM) != 0) {
    215                 dy = Math.max(crop.bottom + dY, crop.top + minWidthHeight)
    216                         - crop.bottom;
    217             }
    218 
    219             if (mFixAspectRatio) {
    220                 float[] l1 = {
    221                         crop.left, crop.bottom
    222                 };
    223                 float[] l2 = {
    224                         crop.right, crop.top
    225                 };
    226                 if (movingEdges == TOP_LEFT || movingEdges == BOTTOM_RIGHT) {
    227                     l1[1] = crop.top;
    228                     l2[1] = crop.bottom;
    229                 }
    230                 float[] b = {
    231                         l1[0] - l2[0], l1[1] - l2[1]
    232                 };
    233                 float[] disp = {
    234                         dx, dy
    235                 };
    236                 float[] bUnit = GeometryMathUtils.normalize(b);
    237                 float sp = GeometryMathUtils.scalarProjection(disp, bUnit);
    238                 dx = sp * bUnit[0];
    239                 dy = sp * bUnit[1];
    240                 RectF newCrop = fixedCornerResize(crop, movingEdges, dx, dy);
    241 
    242                 mBoundedRect.fixedAspectResizeInner(newCrop);
    243             } else {
    244                 if ((movingEdges & MOVE_LEFT) != 0) {
    245                     crop.left += dx;
    246                 }
    247                 if ((movingEdges & MOVE_TOP) != 0) {
    248                     crop.top += dy;
    249                 }
    250                 if ((movingEdges & MOVE_RIGHT) != 0) {
    251                     crop.right += dx;
    252                 }
    253                 if ((movingEdges & MOVE_BOTTOM) != 0) {
    254                     crop.bottom += dy;
    255                 }
    256                 mBoundedRect.resizeInner(crop);
    257             }
    258         }
    259         return true;
    260     }
    261 
    262     // Helper methods
    263 
    264     private int calculateSelectedEdge(float x, float y) {
    265         RectF cropped = mBoundedRect.getInner();
    266 
    267         float left = Math.abs(x - cropped.left);
    268         float right = Math.abs(x - cropped.right);
    269         float top = Math.abs(y - cropped.top);
    270         float bottom = Math.abs(y - cropped.bottom);
    271 
    272         int edgeSelected = MOVE_NONE;
    273         // Check left or right.
    274         if ((left <= mTouchTolerance) && ((y + mTouchTolerance) >= cropped.top)
    275                 && ((y - mTouchTolerance) <= cropped.bottom) && (left < right)) {
    276             edgeSelected |= MOVE_LEFT;
    277         }
    278         else if ((right <= mTouchTolerance) && ((y + mTouchTolerance) >= cropped.top)
    279                 && ((y - mTouchTolerance) <= cropped.bottom)) {
    280             edgeSelected |= MOVE_RIGHT;
    281         }
    282 
    283         // Check top or bottom.
    284         if ((top <= mTouchTolerance) && ((x + mTouchTolerance) >= cropped.left)
    285                 && ((x - mTouchTolerance) <= cropped.right) && (top < bottom)) {
    286             edgeSelected |= MOVE_TOP;
    287         }
    288         else if ((bottom <= mTouchTolerance) && ((x + mTouchTolerance) >= cropped.left)
    289                 && ((x - mTouchTolerance) <= cropped.right)) {
    290             edgeSelected |= MOVE_BOTTOM;
    291         }
    292         return edgeSelected;
    293     }
    294 
    295     private static RectF fixedCornerResize(RectF r, int moving_corner, float dx, float dy) {
    296         RectF newCrop = null;
    297         // Fix opposite corner in place and move sides
    298         if (moving_corner == BOTTOM_RIGHT) {
    299             newCrop = new RectF(r.left, r.top, r.left + r.width() + dx, r.top + r.height()
    300                     + dy);
    301         } else if (moving_corner == BOTTOM_LEFT) {
    302             newCrop = new RectF(r.right - r.width() + dx, r.top, r.right, r.top + r.height()
    303                     + dy);
    304         } else if (moving_corner == TOP_LEFT) {
    305             newCrop = new RectF(r.right - r.width() + dx, r.bottom - r.height() + dy,
    306                     r.right, r.bottom);
    307         } else if (moving_corner == TOP_RIGHT) {
    308             newCrop = new RectF(r.left, r.bottom - r.height() + dy, r.left
    309                     + r.width() + dx, r.bottom);
    310         }
    311         return newCrop;
    312     }
    313 
    314     private static int fixEdgeToCorner(int moving_edges) {
    315         if (moving_edges == MOVE_LEFT) {
    316             moving_edges |= MOVE_TOP;
    317         }
    318         if (moving_edges == MOVE_TOP) {
    319             moving_edges |= MOVE_LEFT;
    320         }
    321         if (moving_edges == MOVE_RIGHT) {
    322             moving_edges |= MOVE_BOTTOM;
    323         }
    324         if (moving_edges == MOVE_BOTTOM) {
    325             moving_edges |= MOVE_RIGHT;
    326         }
    327         return moving_edges;
    328     }
    329 
    330 }
    331