1 /* 2 * Copyright (C) 2012 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.Bitmap; 20 import android.graphics.Matrix; 21 import android.graphics.RectF; 22 23 import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils; 24 25 import java.util.Arrays; 26 27 public class CropMath { 28 29 /** 30 * Gets a float array of the 2D coordinates representing a rectangles 31 * corners. 32 * The order of the corners in the float array is: 33 * 0------->1 34 * ^ | 35 * | v 36 * 3<-------2 37 * 38 * @param r the rectangle to get the corners of 39 * @return the float array of corners (8 floats) 40 */ 41 getCornersFromRect(RectF r)42 public static float[] getCornersFromRect(RectF r) { 43 float[] corners = { 44 r.left, r.top, 45 r.right, r.top, 46 r.right, r.bottom, 47 r.left, r.bottom 48 }; 49 return corners; 50 } 51 52 /** 53 * Returns true iff point (x, y) is within or on the rectangle's bounds. 54 * RectF's "contains" function treats points on the bottom and right bound 55 * as not being contained. 56 * 57 * @param r the rectangle 58 * @param x the x value of the point 59 * @param y the y value of the point 60 * @return 61 */ inclusiveContains(RectF r, float x, float y)62 public static boolean inclusiveContains(RectF r, float x, float y) { 63 return !(x > r.right || x < r.left || y > r.bottom || y < r.top); 64 } 65 66 /** 67 * Takes an array of 2D coordinates representing corners and returns the 68 * smallest rectangle containing those coordinates. 69 * 70 * @param array array of 2D coordinates 71 * @return smallest rectangle containing coordinates 72 */ trapToRect(float[] array)73 public static RectF trapToRect(float[] array) { 74 RectF r = new RectF(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, 75 Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY); 76 for (int i = 1; i < array.length; i += 2) { 77 float x = array[i - 1]; 78 float y = array[i]; 79 r.left = (x < r.left) ? x : r.left; 80 r.top = (y < r.top) ? y : r.top; 81 r.right = (x > r.right) ? x : r.right; 82 r.bottom = (y > r.bottom) ? y : r.bottom; 83 } 84 r.sort(); 85 return r; 86 } 87 88 /** 89 * If edge point [x, y] in array [x0, y0, x1, y1, ...] is outside of the 90 * image bound rectangle, clamps it to the edge of the rectangle. 91 * 92 * @param imageBound the rectangle to clamp edge points to. 93 * @param array an array of points to clamp to the rectangle, gets set to 94 * the clamped values. 95 */ getEdgePoints(RectF imageBound, float[] array)96 public static void getEdgePoints(RectF imageBound, float[] array) { 97 if (array.length < 2) 98 return; 99 for (int x = 0; x < array.length; x += 2) { 100 array[x] = GeometryMathUtils.clamp(array[x], imageBound.left, imageBound.right); 101 array[x + 1] = GeometryMathUtils.clamp(array[x + 1], imageBound.top, imageBound.bottom); 102 } 103 } 104 105 /** 106 * Takes a point and the corners of a rectangle and returns the two corners 107 * representing the side of the rectangle closest to the point. 108 * 109 * @param point the point which is being checked 110 * @param corners the corners of the rectangle 111 * @return two corners representing the side of the rectangle 112 */ closestSide(float[] point, float[] corners)113 public static float[] closestSide(float[] point, float[] corners) { 114 int len = corners.length; 115 float oldMag = Float.POSITIVE_INFINITY; 116 float[] bestLine = null; 117 for (int i = 0; i < len; i += 2) { 118 float[] line = { 119 corners[i], corners[(i + 1) % len], 120 corners[(i + 2) % len], corners[(i + 3) % len] 121 }; 122 float mag = GeometryMathUtils.vectorLength( 123 GeometryMathUtils.shortestVectorFromPointToLine(point, line)); 124 if (mag < oldMag) { 125 oldMag = mag; 126 bestLine = line; 127 } 128 } 129 return bestLine; 130 } 131 132 /** 133 * Checks if a given point is within a rotated rectangle. 134 * 135 * @param point 2D point to check 136 * @param bound rectangle to rotate 137 * @param rot angle of rotation about rectangle center 138 * @return true if point is within rotated rectangle 139 */ pointInRotatedRect(float[] point, RectF bound, float rot)140 public static boolean pointInRotatedRect(float[] point, RectF bound, float rot) { 141 Matrix m = new Matrix(); 142 float[] p = Arrays.copyOf(point, 2); 143 m.setRotate(rot, bound.centerX(), bound.centerY()); 144 Matrix m0 = new Matrix(); 145 if (!m.invert(m0)) 146 return false; 147 m0.mapPoints(p); 148 return inclusiveContains(bound, p[0], p[1]); 149 } 150 151 /** 152 * Checks if a given point is within a rotated rectangle. 153 * 154 * @param point 2D point to check 155 * @param rotatedRect corners of a rotated rectangle 156 * @param center center of the rotated rectangle 157 * @return true if point is within rotated rectangle 158 */ pointInRotatedRect(float[] point, float[] rotatedRect, float[] center)159 public static boolean pointInRotatedRect(float[] point, float[] rotatedRect, float[] center) { 160 RectF unrotated = new RectF(); 161 float angle = getUnrotated(rotatedRect, center, unrotated); 162 return pointInRotatedRect(point, unrotated, angle); 163 } 164 165 /** 166 * Resizes rectangle to have a certain aspect ratio (center remains 167 * stationary). 168 * 169 * @param r rectangle to resize 170 * @param w new width aspect 171 * @param h new height aspect 172 */ fixAspectRatio(RectF r, float w, float h)173 public static void fixAspectRatio(RectF r, float w, float h) { 174 float scale = Math.min(r.width() / w, r.height() / h); 175 float centX = r.centerX(); 176 float centY = r.centerY(); 177 float hw = scale * w / 2; 178 float hh = scale * h / 2; 179 r.set(centX - hw, centY - hh, centX + hw, centY + hh); 180 } 181 182 /** 183 * Resizes rectangle to have a certain aspect ratio (center remains 184 * stationary) while constraining it to remain within the original rect. 185 * 186 * @param r rectangle to resize 187 * @param w new width aspect 188 * @param h new height aspect 189 */ fixAspectRatioContained(RectF r, float w, float h)190 public static void fixAspectRatioContained(RectF r, float w, float h) { 191 float origW = r.width(); 192 float origH = r.height(); 193 float origA = origW / origH; 194 float a = w / h; 195 float finalW = origW; 196 float finalH = origH; 197 if (origA < a) { 198 finalH = origW / a; 199 r.top = r.centerY() - finalH / 2; 200 r.bottom = r.top + finalH; 201 } else { 202 finalW = origH * a; 203 r.left = r.centerX() - finalW / 2; 204 r.right = r.left + finalW; 205 } 206 } 207 208 /** 209 * Stretches/Scales/Translates photoBounds to match displayBounds, and 210 * and returns an equivalent stretched/scaled/translated cropBounds or null 211 * if the mapping is invalid. 212 * @param cropBounds cropBounds to transform 213 * @param photoBounds original bounds containing crop bounds 214 * @param displayBounds final bounds for crop 215 * @return the stretched/scaled/translated crop bounds that fit within displayBounds 216 */ getScaledCropBounds(RectF cropBounds, RectF photoBounds, RectF displayBounds)217 public static RectF getScaledCropBounds(RectF cropBounds, RectF photoBounds, 218 RectF displayBounds) { 219 Matrix m = new Matrix(); 220 m.setRectToRect(photoBounds, displayBounds, Matrix.ScaleToFit.FILL); 221 RectF trueCrop = new RectF(cropBounds); 222 if (!m.mapRect(trueCrop)) { 223 return null; 224 } 225 return trueCrop; 226 } 227 228 /** 229 * Returns the size of a bitmap in bytes. 230 * @param bmap bitmap whose size to check 231 * @return bitmap size in bytes 232 */ getBitmapSize(Bitmap bmap)233 public static int getBitmapSize(Bitmap bmap) { 234 return bmap.getRowBytes() * bmap.getHeight(); 235 } 236 237 /** 238 * Constrains rotation to be in [0, 90, 180, 270] rounding down. 239 * @param rotation any rotation value, in degrees 240 * @return integer rotation in [0, 90, 180, 270] 241 */ constrainedRotation(float rotation)242 public static int constrainedRotation(float rotation) { 243 int r = (int) ((rotation % 360) / 90); 244 r = (r < 0) ? (r + 4) : r; 245 return r * 90; 246 } 247 getUnrotated(float[] rotatedRect, float[] center, RectF unrotated)248 private static float getUnrotated(float[] rotatedRect, float[] center, RectF unrotated) { 249 float dy = rotatedRect[1] - rotatedRect[3]; 250 float dx = rotatedRect[0] - rotatedRect[2]; 251 float angle = (float) (Math.atan(dy / dx) * 180 / Math.PI); 252 Matrix m = new Matrix(); 253 m.setRotate(-angle, center[0], center[1]); 254 float[] unrotatedRect = new float[rotatedRect.length]; 255 m.mapPoints(unrotatedRect, rotatedRect); 256 unrotated.set(trapToRect(unrotatedRect)); 257 return angle; 258 } 259 260 } 261