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