GeometryMathUtils.java revision 55d6abe58c3a7574a5d76c68ec5c21848848e399
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 17package com.android.gallery3d.filtershow.imageshow; 18 19import android.graphics.Bitmap; 20import android.graphics.Canvas; 21import android.graphics.Matrix; 22import android.graphics.Paint; 23import android.graphics.Rect; 24import android.graphics.RectF; 25 26import com.android.gallery3d.filtershow.cache.ImageLoader; 27import com.android.gallery3d.filtershow.filters.FilterCropRepresentation; 28import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation; 29import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation.Mirror; 30import com.android.gallery3d.filtershow.filters.FilterRepresentation; 31import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation; 32import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation.Rotation; 33import com.android.gallery3d.filtershow.filters.FilterStraightenRepresentation; 34import com.android.gallery3d.filtershow.pipeline.ImagePreset; 35 36import java.util.Collection; 37import java.util.Iterator; 38 39public final class GeometryMathUtils { 40 private GeometryMathUtils() {}; 41 42 // Holder class for Geometry data. 43 public static final class GeometryHolder { 44 public Rotation rotation = FilterRotateRepresentation.getNil(); 45 public float straighten = FilterStraightenRepresentation.getNil(); 46 public RectF crop = FilterCropRepresentation.getNil(); 47 public Mirror mirror = FilterMirrorRepresentation.getNil(); 48 49 public void set(GeometryHolder h) { 50 rotation = h.rotation; 51 straighten = h.straighten; 52 crop.set(h.crop); 53 mirror = h.mirror; 54 } 55 56 public void wipe() { 57 rotation = FilterRotateRepresentation.getNil(); 58 straighten = FilterStraightenRepresentation.getNil(); 59 crop = FilterCropRepresentation.getNil(); 60 mirror = FilterMirrorRepresentation.getNil(); 61 } 62 63 public boolean isNil() { 64 return rotation == FilterRotateRepresentation.getNil() && 65 straighten == FilterStraightenRepresentation.getNil() && 66 crop.equals(FilterCropRepresentation.getNil()) && 67 mirror == FilterMirrorRepresentation.getNil(); 68 } 69 70 @Override 71 public boolean equals(Object o) { 72 if (this == o) { 73 return true; 74 } 75 if (!(o instanceof GeometryHolder)) { 76 return false; 77 } 78 GeometryHolder h = (GeometryHolder) o; 79 return rotation == h.rotation && straighten == h.straighten && 80 ((crop == null && h.crop == null) || (crop != null && crop.equals(h.crop))) && 81 mirror == h.mirror; 82 } 83 84 @Override 85 public String toString() { 86 return getClass().getSimpleName() + "[" + "rotation:" + rotation.value() 87 + ",straighten:" + straighten + ",crop:" + crop.toString() 88 + ",mirror:" + mirror.value() + "]"; 89 } 90 } 91 92 // Math operations for 2d vectors 93 public static float clamp(float i, float low, float high) { 94 return Math.max(Math.min(i, high), low); 95 } 96 97 public static float[] lineIntersect(float[] line1, float[] line2) { 98 float a0 = line1[0]; 99 float a1 = line1[1]; 100 float b0 = line1[2]; 101 float b1 = line1[3]; 102 float c0 = line2[0]; 103 float c1 = line2[1]; 104 float d0 = line2[2]; 105 float d1 = line2[3]; 106 float t0 = a0 - b0; 107 float t1 = a1 - b1; 108 float t2 = b0 - d0; 109 float t3 = d1 - b1; 110 float t4 = c0 - d0; 111 float t5 = c1 - d1; 112 113 float denom = t1 * t4 - t0 * t5; 114 if (denom == 0) 115 return null; 116 float u = (t3 * t4 + t5 * t2) / denom; 117 float[] intersect = { 118 b0 + u * t0, b1 + u * t1 119 }; 120 return intersect; 121 } 122 123 public static float[] shortestVectorFromPointToLine(float[] point, float[] line) { 124 float x1 = line[0]; 125 float x2 = line[2]; 126 float y1 = line[1]; 127 float y2 = line[3]; 128 float xdelt = x2 - x1; 129 float ydelt = y2 - y1; 130 if (xdelt == 0 && ydelt == 0) 131 return null; 132 float u = ((point[0] - x1) * xdelt + (point[1] - y1) * ydelt) 133 / (xdelt * xdelt + ydelt * ydelt); 134 float[] ret = { 135 (x1 + u * (x2 - x1)), (y1 + u * (y2 - y1)) 136 }; 137 float[] vec = { 138 ret[0] - point[0], ret[1] - point[1] 139 }; 140 return vec; 141 } 142 143 // A . B 144 public static float dotProduct(float[] a, float[] b) { 145 return a[0] * b[0] + a[1] * b[1]; 146 } 147 148 public static float[] normalize(float[] a) { 149 float length = (float) Math.sqrt(a[0] * a[0] + a[1] * a[1]); 150 float[] b = { 151 a[0] / length, a[1] / length 152 }; 153 return b; 154 } 155 156 // A onto B 157 public static float scalarProjection(float[] a, float[] b) { 158 float length = (float) Math.sqrt(b[0] * b[0] + b[1] * b[1]); 159 return dotProduct(a, b) / length; 160 } 161 162 public static float[] getVectorFromPoints(float[] point1, float[] point2) { 163 float[] p = { 164 point2[0] - point1[0], point2[1] - point1[1] 165 }; 166 return p; 167 } 168 169 public static float[] getUnitVectorFromPoints(float[] point1, float[] point2) { 170 float[] p = { 171 point2[0] - point1[0], point2[1] - point1[1] 172 }; 173 float length = (float) Math.sqrt(p[0] * p[0] + p[1] * p[1]); 174 p[0] = p[0] / length; 175 p[1] = p[1] / length; 176 return p; 177 } 178 179 public static void scaleRect(RectF r, float scale) { 180 r.set(r.left * scale, r.top * scale, r.right * scale, r.bottom * scale); 181 } 182 183 // A - B 184 public static float[] vectorSubtract(float[] a, float[] b) { 185 int len = a.length; 186 if (len != b.length) 187 return null; 188 float[] ret = new float[len]; 189 for (int i = 0; i < len; i++) { 190 ret[i] = a[i] - b[i]; 191 } 192 return ret; 193 } 194 195 public static float vectorLength(float[] a) { 196 return (float) Math.sqrt(a[0] * a[0] + a[1] * a[1]); 197 } 198 199 public static float scale(float oldWidth, float oldHeight, float newWidth, float newHeight) { 200 if (oldHeight == 0 || oldWidth == 0 || (oldWidth == newWidth && oldHeight == newHeight)) { 201 return 1; 202 } 203 return Math.min(newWidth / oldWidth, newHeight / oldHeight); 204 } 205 206 public static Rect roundNearest(RectF r) { 207 Rect q = new Rect(Math.round(r.left), Math.round(r.top), Math.round(r.right), 208 Math.round(r.bottom)); 209 return q; 210 } 211 212 private static void concatMirrorMatrix(Matrix m, Mirror type) { 213 if (type == Mirror.HORIZONTAL) { 214 m.postScale(-1, 1); 215 } else if (type == Mirror.VERTICAL) { 216 m.postScale(1, -1); 217 } else if (type == Mirror.BOTH) { 218 m.postScale(1, -1); 219 m.postScale(-1, 1); 220 } 221 } 222 223 private static int getRotationForOrientation(int orientation) { 224 switch (orientation) { 225 case ImageLoader.ORI_ROTATE_90: 226 return 90; 227 case ImageLoader.ORI_ROTATE_180: 228 return 180; 229 case ImageLoader.ORI_ROTATE_270: 230 return 270; 231 default: 232 return 0; 233 } 234 } 235 236 public static GeometryHolder unpackGeometry(Collection<FilterRepresentation> geometry) { 237 GeometryHolder holder = new GeometryHolder(); 238 unpackGeometry(holder, geometry); 239 return holder; 240 } 241 242 public static void unpackGeometry(GeometryHolder out, 243 Collection<FilterRepresentation> geometry) { 244 out.wipe(); 245 // Get geometry data from filters 246 for (FilterRepresentation r : geometry) { 247 if (r.isNil()) { 248 continue; 249 } 250 if (r.getSerializationName() == FilterRotateRepresentation.SERIALIZATION_NAME) { 251 out.rotation = ((FilterRotateRepresentation) r).getRotation(); 252 } else if (r.getSerializationName() == 253 FilterStraightenRepresentation.SERIALIZATION_NAME) { 254 out.straighten = ((FilterStraightenRepresentation) r).getStraighten(); 255 } else if (r.getSerializationName() == FilterCropRepresentation.SERIALIZATION_NAME) { 256 ((FilterCropRepresentation) r).getCrop(out.crop); 257 } else if (r.getSerializationName() == FilterMirrorRepresentation.SERIALIZATION_NAME) { 258 out.mirror = ((FilterMirrorRepresentation) r).getMirror(); 259 } 260 } 261 } 262 263 public static void replaceInstances(Collection<FilterRepresentation> geometry, 264 FilterRepresentation rep) { 265 Iterator<FilterRepresentation> iter = geometry.iterator(); 266 while (iter.hasNext()) { 267 FilterRepresentation r = iter.next(); 268 if (ImagePreset.sameSerializationName(rep, r)) { 269 iter.remove(); 270 } 271 } 272 if (!rep.isNil()) { 273 geometry.add(rep); 274 } 275 } 276 277 public static void initializeHolder(GeometryHolder outHolder, 278 FilterRepresentation currentLocal) { 279 Collection<FilterRepresentation> geometry = MasterImage.getImage().getPreset() 280 .getGeometryFilters(); 281 replaceInstances(geometry, currentLocal); 282 unpackGeometry(outHolder, geometry); 283 } 284 285 public static Rect finalGeometryRect(int width, int height, 286 Collection<FilterRepresentation> geometry) { 287 GeometryHolder holder = unpackGeometry(geometry); 288 RectF crop = getTrueCropRect(holder, width, height); 289 Rect frame = new Rect(); 290 crop.roundOut(frame); 291 return frame; 292 } 293 294 private static Bitmap applyFullGeometryMatrix(Bitmap image, GeometryHolder holder) { 295 int width = image.getWidth(); 296 int height = image.getHeight(); 297 RectF crop = getTrueCropRect(holder, width, height); 298 Rect frame = new Rect(); 299 crop.roundOut(frame); 300 Matrix m = getCropSelectionToScreenMatrix(null, holder, width, height, frame.width(), 301 frame.height()); 302 Bitmap temp = Bitmap.createBitmap(frame.width(), frame.height(), Bitmap.Config.ARGB_8888); 303 Canvas canvas = new Canvas(temp); 304 Paint paint = new Paint(); 305 paint.setAntiAlias(true); 306 paint.setFilterBitmap(true); 307 paint.setDither(true); 308 canvas.drawBitmap(image, m, paint); 309 return temp; 310 } 311 312 public static Matrix getImageToScreenMatrix(Collection<FilterRepresentation> geometry, 313 boolean reflectRotation, Rect bmapDimens, float viewWidth, float viewHeight) { 314 GeometryHolder h = unpackGeometry(geometry); 315 return GeometryMathUtils.getOriginalToScreen(h, reflectRotation, bmapDimens.width(), 316 bmapDimens.height(), viewWidth, viewHeight); 317 } 318 319 public static Matrix getPartialToScreenMatrix(Collection<FilterRepresentation> geometry, 320 Rect originalBounds, float w, float h, 321 float pw, float ph) { 322 GeometryHolder holder = unpackGeometry(geometry); 323 RectF rCrop = new RectF(0, 0, originalBounds.width(), originalBounds.height()); 324 float angle = holder.straighten; 325 int rotation = holder.rotation.value(); 326 327 ImageStraighten.getUntranslatedStraightenCropBounds(rCrop, angle); 328 float dx = (w - pw) / 2f; 329 float dy = (h - ph) / 2f; 330 Matrix compensation = new Matrix(); 331 compensation.postTranslate(dx, dy); 332 float cScale = originalBounds.width() / rCrop.width(); 333 if (rCrop.width() < rCrop.height()) { 334 cScale = originalBounds.height() / rCrop.height(); 335 } 336 float scale = w / pw; 337 if (w < h) { 338 scale = h / ph; 339 } 340 scale = scale * cScale; 341 float cx = w / 2f; 342 float cy = h / 2f; 343 344 compensation.postScale(scale, scale, cx, cy); 345 compensation.postRotate(angle, cx, cy); 346 compensation.postRotate(rotation, cx, cy); 347 compensation.postTranslate(-cx, -cy); 348 concatMirrorMatrix(compensation, holder.mirror); 349 compensation.postTranslate(cx, cy); 350 return compensation; 351 } 352 353 public static Matrix getOriginalToScreen(GeometryHolder holder, boolean rotate, 354 float originalWidth, 355 float originalHeight, float viewWidth, float viewHeight) { 356 int orientation = MasterImage.getImage().getZoomOrientation(); 357 int rotation = getRotationForOrientation(orientation); 358 Rotation prev = holder.rotation; 359 rotation = (rotation + prev.value()) % 360; 360 holder.rotation = Rotation.fromValue(rotation); 361 Matrix m = getCropSelectionToScreenMatrix(null, holder, (int) originalWidth, 362 (int) originalHeight, (int) viewWidth, (int) viewHeight); 363 holder.rotation = prev; 364 return m; 365 } 366 367 public static Bitmap applyGeometryRepresentations(Collection<FilterRepresentation> res, 368 Bitmap image) { 369 GeometryHolder holder = unpackGeometry(res); 370 Bitmap bmap = image; 371 // If there are geometry changes, apply them to the image 372 if (!holder.isNil()) { 373 bmap = applyFullGeometryMatrix(bmap, holder); 374 } 375 return bmap; 376 } 377 378 public static RectF drawTransformedCropped(GeometryHolder holder, Canvas canvas, 379 Bitmap photo, int viewWidth, int viewHeight) { 380 if (photo == null) { 381 return null; 382 } 383 RectF crop = new RectF(); 384 Matrix m = getCropSelectionToScreenMatrix(crop, holder, photo.getWidth(), photo.getHeight(), 385 viewWidth, viewHeight); 386 canvas.save(); 387 canvas.clipRect(crop); 388 Paint p = new Paint(); 389 p.setAntiAlias(true); 390 canvas.drawBitmap(photo, m, p); 391 canvas.restore(); 392 return crop; 393 } 394 395 public static boolean needsDimensionSwap(Rotation rotation) { 396 switch (rotation) { 397 case NINETY: 398 case TWO_SEVENTY: 399 return true; 400 default: 401 return false; 402 } 403 } 404 405 // Gives matrix for rotated, straightened, mirrored bitmap centered at 0,0. 406 private static Matrix getFullGeometryMatrix(GeometryHolder holder, int bitmapWidth, 407 int bitmapHeight) { 408 float centerX = bitmapWidth / 2f; 409 float centerY = bitmapHeight / 2f; 410 Matrix m = new Matrix(); 411 m.setTranslate(-centerX, -centerY); 412 m.postRotate(holder.straighten + holder.rotation.value()); 413 concatMirrorMatrix(m, holder.mirror); 414 return m; 415 } 416 417 public static Matrix getFullGeometryToScreenMatrix(GeometryHolder holder, int bitmapWidth, 418 int bitmapHeight, int viewWidth, int viewHeight) { 419 float scale = GeometryMathUtils.scale(bitmapWidth, bitmapHeight, viewWidth, viewHeight); 420 Matrix m = getFullGeometryMatrix(holder, bitmapWidth, bitmapHeight); 421 m.postScale(scale, scale); 422 m.postTranslate(viewWidth / 2f, viewHeight / 2f); 423 return m; 424 } 425 426 public static RectF getTrueCropRect(GeometryHolder holder, int bitmapWidth, int bitmapHeight) { 427 RectF r = new RectF(holder.crop); 428 FilterCropRepresentation.findScaledCrop(r, bitmapWidth, bitmapHeight); 429 float s = holder.straighten; 430 holder.straighten = 0; 431 Matrix m1 = getFullGeometryMatrix(holder, bitmapWidth, bitmapHeight); 432 holder.straighten = s; 433 m1.mapRect(r); 434 return r; 435 } 436 437 public static Matrix getCropSelectionToScreenMatrix(RectF outCrop, GeometryHolder holder, 438 int bitmapWidth, int bitmapHeight, int viewWidth, int viewHeight) { 439 Matrix m = getFullGeometryMatrix(holder, bitmapWidth, bitmapHeight); 440 RectF crop = getTrueCropRect(holder, bitmapWidth, bitmapHeight); 441 float scale = GeometryMathUtils.scale(crop.width(), crop.height(), viewWidth, viewHeight); 442 m.postScale(scale, scale); 443 GeometryMathUtils.scaleRect(crop, scale); 444 m.postTranslate(viewWidth / 2f - crop.centerX(), viewHeight / 2f - crop.centerY()); 445 if (outCrop != null) { 446 crop.offset(viewWidth / 2f - crop.centerX(), viewHeight / 2f - crop.centerY()); 447 outCrop.set(crop); 448 } 449 return m; 450 } 451 452 public static Matrix getCropSelectionToScreenMatrix(RectF outCrop, 453 Collection<FilterRepresentation> res, int bitmapWidth, int bitmapHeight, int viewWidth, 454 int viewHeight) { 455 GeometryHolder holder = unpackGeometry(res); 456 return getCropSelectionToScreenMatrix(outCrop, holder, bitmapWidth, bitmapHeight, 457 viewWidth, viewHeight); 458 } 459} 460