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.GeometryMath; 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 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 */ 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 */ 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 */ 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] = GeometryMath.clamp(array[x], imageBound.left, imageBound.right); 101 array[x + 1] = GeometryMath.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 */ 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 = GeometryMath.vectorLength( 123 GeometryMath.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 */ 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 */ 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 */ 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 */ 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 } else { 200 finalW = origH * a; 201 } 202 float centX = r.centerX(); 203 float centY = r.centerY(); 204 float hw = finalW / 2; 205 float hh = finalH / 2; 206 r.set(centX - hw, centY - hh, centX + hw, centY + hh); 207 } 208 209 /** 210 * Stretches/Scales/Translates photoBounds to match displayBounds, and 211 * and returns an equivalent stretched/scaled/translated cropBounds or null 212 * if the mapping is invalid. 213 * @param cropBounds cropBounds to transform 214 * @param photoBounds original bounds containing crop bounds 215 * @param displayBounds final bounds for crop 216 * @return the stretched/scaled/translated crop bounds that fit within displayBounds 217 */ 218 public static RectF getScaledCropBounds(RectF cropBounds, RectF photoBounds, 219 RectF displayBounds) { 220 Matrix m = new Matrix(); 221 m.setRectToRect(photoBounds, displayBounds, Matrix.ScaleToFit.FILL); 222 RectF trueCrop = new RectF(cropBounds); 223 if (!m.mapRect(trueCrop)) { 224 return null; 225 } 226 return trueCrop; 227 } 228 229 /** 230 * Returns the size of a bitmap in bytes. 231 * @param bmap bitmap whose size to check 232 * @return bitmap size in bytes 233 */ 234 public static int getBitmapSize(Bitmap bmap) { 235 return bmap.getRowBytes() * bmap.getHeight(); 236 } 237 238 /** 239 * Constrains rotation to be in [0, 90, 180, 270] rounding down. 240 * @param rotation any rotation value, in degrees 241 * @return integer rotation in [0, 90, 180, 270] 242 */ 243 public static int constrainedRotation(float rotation) { 244 int r = (int) ((rotation % 360) / 90); 245 r = (r < 0) ? (r + 4) : r; 246 return r * 90; 247 } 248 249 private static float getUnrotated(float[] rotatedRect, float[] center, RectF unrotated) { 250 float dy = rotatedRect[1] - rotatedRect[3]; 251 float dx = rotatedRect[0] - rotatedRect[2]; 252 float angle = (float) (Math.atan(dy / dx) * 180 / Math.PI); 253 Matrix m = new Matrix(); 254 m.setRotate(-angle, center[0], center[1]); 255 float[] unrotatedRect = new float[rotatedRect.length]; 256 m.mapPoints(unrotatedRect, rotatedRect); 257 unrotated.set(trapToRect(unrotatedRect)); 258 return angle; 259 } 260 261 } 262