summaryrefslogtreecommitdiffstats
path: root/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapUtils.java
diff options
context:
space:
mode:
Diffstat (limited to 'cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapUtils.java')
-rw-r--r--cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapUtils.java923
1 files changed, 0 insertions, 923 deletions
diff --git a/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapUtils.java b/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapUtils.java
deleted file mode 100644
index 7190bd429..000000000
--- a/cropper/src/main/java/com/theartofdev/edmodo/cropper/BitmapUtils.java
+++ /dev/null
@@ -1,923 +0,0 @@
-// "Therefore those skilled at the unorthodox
-// are infinite as heaven and earth,
-// inexhaustible as the great rivers.
-// When they come to an end,
-// they begin again,
-// like the days and months;
-// they die and are reborn,
-// like the four seasons."
-//
-// - Sun Tsu,
-// "The Art of War"
-
-package com.theartofdev.edmodo.cropper;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.BitmapRegionDecoder;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.net.Uri;
-import android.util.Log;
-import android.util.Pair;
-
-import androidx.exifinterface.media.ExifInterface;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.ref.WeakReference;
-
-import javax.microedition.khronos.egl.EGL10;
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.egl.EGLContext;
-import javax.microedition.khronos.egl.EGLDisplay;
-
-/**
- * Utility class that deals with operations with an ImageView.
- */
-final class BitmapUtils {
-
- static final Rect EMPTY_RECT = new Rect();
-
- static final RectF EMPTY_RECT_F = new RectF();
-
- /**
- * Reusable rectangle for general internal usage
- */
- static final RectF RECT = new RectF();
-
- /**
- * Reusable point for general internal usage
- */
- static final float[] POINTS = new float[6];
-
- /**
- * Reusable point for general internal usage
- */
- static final float[] POINTS2 = new float[6];
- /**
- * used to save bitmaps during state save and restore so not to reload them.
- */
- static Pair<String, WeakReference<Bitmap>> mStateBitmap;
- /**
- * Used to know the max texture size allowed to be rendered
- */
- private static int mMaxTextureSize;
-
- /**
- * Rotate the given image by reading the Exif value of the image (uri).<br>
- * If no rotation is required the image will not be rotated.<br>
- * New bitmap is created and the old one is recycled.
- */
- static RotateBitmapResult rotateBitmapByExif(Bitmap bitmap, Context context, Uri uri) {
- ExifInterface ei = null;
- try {
- InputStream is = context.getContentResolver().openInputStream(uri);
- if (is != null) {
- ei = new ExifInterface(is);
- is.close();
- }
- } catch (Exception ignored) {
- }
- return ei != null ? rotateBitmapByExif(bitmap, ei) : new RotateBitmapResult(bitmap, 0);
- }
-
- /**
- * Rotate the given image by given Exif value.<br>
- * If no rotation is required the image will not be rotated.<br>
- * New bitmap is created and the old one is recycled.
- */
- static RotateBitmapResult rotateBitmapByExif(Bitmap bitmap, ExifInterface exif) {
- int degrees;
- int orientation =
- exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
- switch (orientation) {
- case ExifInterface.ORIENTATION_ROTATE_90:
- degrees = 90;
- break;
- case ExifInterface.ORIENTATION_ROTATE_180:
- degrees = 180;
- break;
- case ExifInterface.ORIENTATION_ROTATE_270:
- degrees = 270;
- break;
- default:
- degrees = 0;
- break;
- }
- return new RotateBitmapResult(bitmap, degrees);
- }
-
- /**
- * Decode bitmap from stream using sampling to get bitmap with the requested limit.
- */
- static BitmapSampled decodeSampledBitmap(Context context, Uri uri, int reqWidth, int reqHeight) {
-
- try {
- ContentResolver resolver = context.getContentResolver();
-
- // First decode with inJustDecodeBounds=true to check dimensions
- BitmapFactory.Options options = decodeImageForOption(resolver, uri);
-
- if (options.outWidth == -1 && options.outHeight == -1)
- throw new RuntimeException("File is not a picture");
-
- // Calculate inSampleSize
- options.inSampleSize =
- Math.max(
- calculateInSampleSizeByReqestedSize(
- options.outWidth, options.outHeight, reqWidth, reqHeight),
- calculateInSampleSizeByMaxTextureSize(options.outWidth, options.outHeight));
-
- // Decode bitmap with inSampleSize set
- Bitmap bitmap = decodeImage(resolver, uri, options);
-
- return new BitmapSampled(bitmap, options.inSampleSize);
-
- } catch (Exception e) {
- throw new RuntimeException(
- "Failed to load sampled bitmap: " + uri + "\r\n" + e.getMessage(), e);
- }
- }
-
- /**
- * Crop image bitmap from given bitmap using the given points in the original bitmap and the given
- * rotation.<br>
- * if the rotation is not 0,90,180 or 270 degrees then we must first crop a larger area of the
- * image that contains the requires rectangle, rotate and then crop again a sub rectangle.<br>
- * If crop fails due to OOM we scale the cropping image by 0.5 every time it fails until it is
- * small enough.
- */
- static BitmapSampled cropBitmapObjectHandleOOM(
- Bitmap bitmap,
- float[] points,
- int degreesRotated,
- boolean fixAspectRatio,
- int aspectRatioX,
- int aspectRatioY,
- boolean flipHorizontally,
- boolean flipVertically) {
- int scale = 1;
- while (true) {
- try {
- Bitmap cropBitmap =
- cropBitmapObjectWithScale(
- bitmap,
- points,
- degreesRotated,
- fixAspectRatio,
- aspectRatioX,
- aspectRatioY,
- 1 / (float) scale,
- flipHorizontally,
- flipVertically);
- return new BitmapSampled(cropBitmap, scale);
- } catch (OutOfMemoryError e) {
- scale *= 2;
- if (scale > 8) {
- throw e;
- }
- }
- }
- }
-
- /**
- * Crop image bitmap from given bitmap using the given points in the original bitmap and the given
- * rotation.<br>
- * if the rotation is not 0,90,180 or 270 degrees then we must first crop a larger area of the
- * image that contains the requires rectangle, rotate and then crop again a sub rectangle.
- *
- * @param scale how much to scale the cropped image part, use 0.5 to lower the image by half (OOM
- * handling)
- */
- private static Bitmap cropBitmapObjectWithScale(
- Bitmap bitmap,
- float[] points,
- int degreesRotated,
- boolean fixAspectRatio,
- int aspectRatioX,
- int aspectRatioY,
- float scale,
- boolean flipHorizontally,
- boolean flipVertically) {
-
- // get the rectangle in original image that contains the required cropped area (larger for non
- // rectangular crop)
- Rect rect =
- getRectFromPoints(
- points,
- bitmap.getWidth(),
- bitmap.getHeight(),
- fixAspectRatio,
- aspectRatioX,
- aspectRatioY);
-
- // crop and rotate the cropped image in one operation
- Matrix matrix = new Matrix();
- matrix.setRotate(degreesRotated, bitmap.getWidth() / 2, bitmap.getHeight() / 2);
- matrix.postScale(flipHorizontally ? -scale : scale, flipVertically ? -scale : scale);
- Bitmap result =
- Bitmap.createBitmap(bitmap, rect.left, rect.top, rect.width(), rect.height(), matrix, true);
-
- if (result == bitmap) {
- // corner case when all bitmap is selected, no worth optimizing for it
- result = bitmap.copy(bitmap.getConfig(), false);
- }
-
- // rotating by 0, 90, 180 or 270 degrees doesn't require extra cropping
- if (degreesRotated % 90 != 0) {
-
- // extra crop because non rectangular crop cannot be done directly on the image without
- // rotating first
- result =
- cropForRotatedImage(
- result, points, rect, degreesRotated, fixAspectRatio, aspectRatioX, aspectRatioY);
- }
-
- return result;
- }
-
- /**
- * Crop image bitmap from URI by decoding it with specific width and height to down-sample if
- * required.<br>
- * Additionally if OOM is thrown try to increase the sampling (2,4,8).
- */
- static BitmapSampled cropBitmap(
- Context context,
- Uri loadedImageUri,
- float[] points,
- int degreesRotated,
- int orgWidth,
- int orgHeight,
- boolean fixAspectRatio,
- int aspectRatioX,
- int aspectRatioY,
- int reqWidth,
- int reqHeight,
- boolean flipHorizontally,
- boolean flipVertically) {
- int sampleMulti = 1;
- while (true) {
- try {
- // if successful, just return the resulting bitmap
- return cropBitmap(
- context,
- loadedImageUri,
- points,
- degreesRotated,
- orgWidth,
- orgHeight,
- fixAspectRatio,
- aspectRatioX,
- aspectRatioY,
- reqWidth,
- reqHeight,
- flipHorizontally,
- flipVertically,
- sampleMulti);
- } catch (OutOfMemoryError e) {
- // if OOM try to increase the sampling to lower the memory usage
- sampleMulti *= 2;
- if (sampleMulti > 16) {
- throw new RuntimeException(
- "Failed to handle OOM by sampling ("
- + sampleMulti
- + "): "
- + loadedImageUri
- + "\r\n"
- + e.getMessage(),
- e);
- }
- }
- }
- }
-
- /**
- * Get left value of the bounding rectangle of the given points.
- */
- static float getRectLeft(float[] points) {
- return Math.min(Math.min(Math.min(points[0], points[2]), points[4]), points[6]);
- }
-
- /**
- * Get top value of the bounding rectangle of the given points.
- */
- static float getRectTop(float[] points) {
- return Math.min(Math.min(Math.min(points[1], points[3]), points[5]), points[7]);
- }
-
- /**
- * Get right value of the bounding rectangle of the given points.
- */
- static float getRectRight(float[] points) {
- return Math.max(Math.max(Math.max(points[0], points[2]), points[4]), points[6]);
- }
-
- /**
- * Get bottom value of the bounding rectangle of the given points.
- */
- static float getRectBottom(float[] points) {
- return Math.max(Math.max(Math.max(points[1], points[3]), points[5]), points[7]);
- }
-
- /**
- * Get width of the bounding rectangle of the given points.
- */
- static float getRectWidth(float[] points) {
- return getRectRight(points) - getRectLeft(points);
- }
-
- /**
- * Get height of the bounding rectangle of the given points.
- */
- static float getRectHeight(float[] points) {
- return getRectBottom(points) - getRectTop(points);
- }
-
- /**
- * Get horizontal center value of the bounding rectangle of the given points.
- */
- static float getRectCenterX(float[] points) {
- return (getRectRight(points) + getRectLeft(points)) / 2f;
- }
-
- /**
- * Get vertical center value of the bounding rectangle of the given points.
- */
- static float getRectCenterY(float[] points) {
- return (getRectBottom(points) + getRectTop(points)) / 2f;
- }
-
- /**
- * Get a rectangle for the given 4 points (x0,y0,x1,y1,x2,y2,x3,y3) by finding the min/max 2
- * points that contains the given 4 points and is a straight rectangle.
- */
- static Rect getRectFromPoints(
- float[] points,
- int imageWidth,
- int imageHeight,
- boolean fixAspectRatio,
- int aspectRatioX,
- int aspectRatioY) {
- int left = Math.round(Math.max(0, getRectLeft(points)));
- int top = Math.round(Math.max(0, getRectTop(points)));
- int right = Math.round(Math.min(imageWidth, getRectRight(points)));
- int bottom = Math.round(Math.min(imageHeight, getRectBottom(points)));
-
- Rect rect = new Rect(left, top, right, bottom);
- if (fixAspectRatio) {
- fixRectForAspectRatio(rect, aspectRatioX, aspectRatioY);
- }
-
- return rect;
- }
-
- /**
- * Fix the given rectangle if it doesn't confirm to aspect ration rule.<br>
- * Make sure that width and height are equal if 1:1 fixed aspect ratio is requested.
- */
- private static void fixRectForAspectRatio(Rect rect, int aspectRatioX, int aspectRatioY) {
- if (aspectRatioX == aspectRatioY && rect.width() != rect.height()) {
- if (rect.height() > rect.width()) {
- rect.bottom -= rect.height() - rect.width();
- } else {
- rect.right -= rect.width() - rect.height();
- }
- }
- }
-
- /**
- * Write given bitmap to a temp file. If file already exists no-op as we already saved the file in
- * this session. Uses JPEG 95% compression.
- *
- * @param uri the uri to write the bitmap to, if null
- * @return the uri where the image was saved in, either the given uri or new pointing to temp
- * file.
- */
- static Uri writeTempStateStoreBitmap(Context context, Bitmap bitmap, Uri uri) {
- try {
- boolean needSave = true;
- if (uri == null) {
- uri =
- Uri.fromFile(
- File.createTempFile("aic_state_store_temp", ".jpg", context.getCacheDir()));
- } else if (new File(uri.getPath()).exists()) {
- needSave = false;
- }
- if (needSave) {
- writeBitmapToUri(context, bitmap, uri, Bitmap.CompressFormat.JPEG, 95);
- }
- return uri;
- } catch (Exception e) {
- Log.w("AIC", "Failed to write bitmap to temp file for image-cropper save instance state", e);
- return null;
- }
- }
-
- /**
- * Write the given bitmap to the given uri using the given compression.
- */
- static void writeBitmapToUri(
- Context context,
- Bitmap bitmap,
- Uri uri,
- Bitmap.CompressFormat compressFormat,
- int compressQuality)
- throws FileNotFoundException {
- OutputStream outputStream = null;
- try {
- outputStream = context.getContentResolver().openOutputStream(uri);
- bitmap.compress(compressFormat, compressQuality, outputStream);
- } finally {
- closeSafe(outputStream);
- }
- }
-
- /**
- * Resize the given bitmap to the given width/height by the given option.<br>
- */
- static Bitmap resizeBitmap(
- Bitmap bitmap, int reqWidth, int reqHeight, CropImageView.RequestSizeOptions options) {
- try {
- if (reqWidth > 0
- && reqHeight > 0
- && (options == CropImageView.RequestSizeOptions.RESIZE_FIT
- || options == CropImageView.RequestSizeOptions.RESIZE_INSIDE
- || options == CropImageView.RequestSizeOptions.RESIZE_EXACT)) {
-
- Bitmap resized = null;
- if (options == CropImageView.RequestSizeOptions.RESIZE_EXACT) {
- resized = Bitmap.createScaledBitmap(bitmap, reqWidth, reqHeight, false);
- } else {
- int width = bitmap.getWidth();
- int height = bitmap.getHeight();
- float scale = Math.max(width / (float) reqWidth, height / (float) reqHeight);
- if (scale > 1 || options == CropImageView.RequestSizeOptions.RESIZE_FIT) {
- resized =
- Bitmap.createScaledBitmap(
- bitmap, (int) (width / scale), (int) (height / scale), false);
- }
- }
- if (resized != null) {
- if (resized != bitmap) {
- bitmap.recycle();
- }
- return resized;
- }
- }
- } catch (Exception e) {
- Log.w("AIC", "Failed to resize cropped image, return bitmap before resize", e);
- }
- return bitmap;
- }
-
- // region: Private methods
-
- /**
- * Crop image bitmap from URI by decoding it with specific width and height to down-sample if
- * required.
- *
- * @param orgWidth used to get rectangle from points (handle edge cases to limit rectangle)
- * @param orgHeight used to get rectangle from points (handle edge cases to limit rectangle)
- * @param sampleMulti used to increase the sampling of the image to handle memory issues.
- */
- private static BitmapSampled cropBitmap(
- Context context,
- Uri loadedImageUri,
- float[] points,
- int degreesRotated,
- int orgWidth,
- int orgHeight,
- boolean fixAspectRatio,
- int aspectRatioX,
- int aspectRatioY,
- int reqWidth,
- int reqHeight,
- boolean flipHorizontally,
- boolean flipVertically,
- int sampleMulti) {
-
- // get the rectangle in original image that contains the required cropped area (larger for non
- // rectangular crop)
- Rect rect =
- getRectFromPoints(points, orgWidth, orgHeight, fixAspectRatio, aspectRatioX, aspectRatioY);
-
- int width = reqWidth > 0 ? reqWidth : rect.width();
- int height = reqHeight > 0 ? reqHeight : rect.height();
-
- Bitmap result = null;
- int sampleSize = 1;
- try {
- // decode only the required image from URI, optionally sub-sampling if reqWidth/reqHeight is
- // given.
- BitmapSampled bitmapSampled =
- decodeSampledBitmapRegion(context, loadedImageUri, rect, width, height, sampleMulti);
- result = bitmapSampled.bitmap;
- sampleSize = bitmapSampled.sampleSize;
- } catch (Exception ignored) {
- }
-
- if (result != null) {
- try {
- // rotate the decoded region by the required amount
- result = rotateAndFlipBitmapInt(result, degreesRotated, flipHorizontally, flipVertically);
-
- // rotating by 0, 90, 180 or 270 degrees doesn't require extra cropping
- if (degreesRotated % 90 != 0) {
-
- // extra crop because non rectangular crop cannot be done directly on the image without
- // rotating first
- result =
- cropForRotatedImage(
- result, points, rect, degreesRotated, fixAspectRatio, aspectRatioX, aspectRatioY);
- }
- } catch (OutOfMemoryError e) {
- if (result != null) {
- result.recycle();
- }
- throw e;
- }
- return new BitmapSampled(result, sampleSize);
- } else {
- // failed to decode region, may be skia issue, try full decode and then crop
- return cropBitmap(
- context,
- loadedImageUri,
- points,
- degreesRotated,
- fixAspectRatio,
- aspectRatioX,
- aspectRatioY,
- sampleMulti,
- rect,
- width,
- height,
- flipHorizontally,
- flipVertically);
- }
- }
-
- /**
- * Crop bitmap by fully loading the original and then cropping it, fallback in case cropping
- * region failed.
- */
- private static BitmapSampled cropBitmap(
- Context context,
- Uri loadedImageUri,
- float[] points,
- int degreesRotated,
- boolean fixAspectRatio,
- int aspectRatioX,
- int aspectRatioY,
- int sampleMulti,
- Rect rect,
- int width,
- int height,
- boolean flipHorizontally,
- boolean flipVertically) {
- Bitmap result = null;
- int sampleSize;
- try {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inSampleSize =
- sampleSize =
- sampleMulti
- * calculateInSampleSizeByReqestedSize(rect.width(), rect.height(), width, height);
-
- Bitmap fullBitmap = decodeImage(context.getContentResolver(), loadedImageUri, options);
- if (fullBitmap != null) {
- try {
- // adjust crop points by the sampling because the image is smaller
- float[] points2 = new float[points.length];
- System.arraycopy(points, 0, points2, 0, points.length);
- for (int i = 0; i < points2.length; i++) {
- points2[i] = points2[i] / options.inSampleSize;
- }
-
- result =
- cropBitmapObjectWithScale(
- fullBitmap,
- points2,
- degreesRotated,
- fixAspectRatio,
- aspectRatioX,
- aspectRatioY,
- 1,
- flipHorizontally,
- flipVertically);
- } finally {
- if (result != fullBitmap) {
- fullBitmap.recycle();
- }
- }
- }
- } catch (OutOfMemoryError e) {
- if (result != null) {
- result.recycle();
- }
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(
- "Failed to load sampled bitmap: " + loadedImageUri + "\r\n" + e.getMessage(), e);
- }
- return new BitmapSampled(result, sampleSize);
- }
-
- /**
- * Decode image from uri using "inJustDecodeBounds" to get the image dimensions.
- */
- private static BitmapFactory.Options decodeImageForOption(ContentResolver resolver, Uri uri)
- throws FileNotFoundException {
- InputStream stream = null;
- try {
- stream = resolver.openInputStream(uri);
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeStream(stream, EMPTY_RECT, options);
- options.inJustDecodeBounds = false;
- return options;
- } finally {
- closeSafe(stream);
- }
- }
-
- /**
- * Decode image from uri using given "inSampleSize", but if failed due to out-of-memory then raise
- * the inSampleSize until success.
- */
- private static Bitmap decodeImage(
- ContentResolver resolver, Uri uri, BitmapFactory.Options options)
- throws FileNotFoundException {
- do {
- InputStream stream = null;
- try {
- stream = resolver.openInputStream(uri);
- return BitmapFactory.decodeStream(stream, EMPTY_RECT, options);
- } catch (OutOfMemoryError e) {
- options.inSampleSize *= 2;
- } finally {
- closeSafe(stream);
- }
- } while (options.inSampleSize <= 512);
- throw new RuntimeException("Failed to decode image: " + uri);
- }
-
- /**
- * Decode specific rectangle bitmap from stream using sampling to get bitmap with the requested
- * limit.
- *
- * @param sampleMulti used to increase the sampling of the image to handle memory issues.
- */
- private static BitmapSampled decodeSampledBitmapRegion(
- Context context, Uri uri, Rect rect, int reqWidth, int reqHeight, int sampleMulti) {
- InputStream stream = null;
- BitmapRegionDecoder decoder = null;
- try {
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inSampleSize =
- sampleMulti
- * calculateInSampleSizeByReqestedSize(
- rect.width(), rect.height(), reqWidth, reqHeight);
-
- stream = context.getContentResolver().openInputStream(uri);
- decoder = BitmapRegionDecoder.newInstance(stream, false);
- do {
- try {
- return new BitmapSampled(decoder.decodeRegion(rect, options), options.inSampleSize);
- } catch (OutOfMemoryError e) {
- options.inSampleSize *= 2;
- }
- } while (options.inSampleSize <= 512);
- } catch (Exception e) {
- throw new RuntimeException(
- "Failed to load sampled bitmap: " + uri + "\r\n" + e.getMessage(), e);
- } finally {
- closeSafe(stream);
- if (decoder != null) {
- decoder.recycle();
- }
- }
- return new BitmapSampled(null, 1);
- }
-
- /**
- * Special crop of bitmap rotated by not stright angle, in this case the original crop bitmap
- * contains parts beyond the required crop area, this method crops the already cropped and rotated
- * bitmap to the final rectangle.<br>
- * Note: rotating by 0, 90, 180 or 270 degrees doesn't require extra cropping.
- */
- private static Bitmap cropForRotatedImage(
- Bitmap bitmap,
- float[] points,
- Rect rect,
- int degreesRotated,
- boolean fixAspectRatio,
- int aspectRatioX,
- int aspectRatioY) {
- if (degreesRotated % 90 != 0) {
-
- int adjLeft = 0, adjTop = 0, width = 0, height = 0;
- double rads = Math.toRadians(degreesRotated);
- int compareTo =
- degreesRotated < 90 || (degreesRotated > 180 && degreesRotated < 270)
- ? rect.left
- : rect.right;
- for (int i = 0; i < points.length; i += 2) {
- if (points[i] >= compareTo - 1 && points[i] <= compareTo + 1) {
- adjLeft = (int) Math.abs(Math.sin(rads) * (rect.bottom - points[i + 1]));
- adjTop = (int) Math.abs(Math.cos(rads) * (points[i + 1] - rect.top));
- width = (int) Math.abs((points[i + 1] - rect.top) / Math.sin(rads));
- height = (int) Math.abs((rect.bottom - points[i + 1]) / Math.cos(rads));
- break;
- }
- }
-
- rect.set(adjLeft, adjTop, adjLeft + width, adjTop + height);
- if (fixAspectRatio) {
- fixRectForAspectRatio(rect, aspectRatioX, aspectRatioY);
- }
-
- Bitmap bitmapTmp = bitmap;
- bitmap = Bitmap.createBitmap(bitmap, rect.left, rect.top, rect.width(), rect.height());
- if (bitmapTmp != bitmap) {
- bitmapTmp.recycle();
- }
- }
- return bitmap;
- }
-
- /**
- * Calculate the largest inSampleSize value that is a power of 2 and keeps both height and width
- * larger than the requested height and width.
- */
- private static int calculateInSampleSizeByReqestedSize(
- int width, int height, int reqWidth, int reqHeight) {
- int inSampleSize = 1;
- if (height > reqHeight || width > reqWidth) {
- while ((height / 2 / inSampleSize) > reqHeight && (width / 2 / inSampleSize) > reqWidth) {
- inSampleSize *= 2;
- }
- }
- return inSampleSize;
- }
-
- /**
- * Calculate the largest inSampleSize value that is a power of 2 and keeps both height and width
- * smaller than max texture size allowed for the device.
- */
- private static int calculateInSampleSizeByMaxTextureSize(int width, int height) {
- int inSampleSize = 1;
- if (mMaxTextureSize == 0) {
- mMaxTextureSize = getMaxTextureSize();
- }
- if (mMaxTextureSize > 0) {
- while ((height / inSampleSize) > mMaxTextureSize
- || (width / inSampleSize) > mMaxTextureSize) {
- inSampleSize *= 2;
- }
- }
- return inSampleSize;
- }
-
- /**
- * Rotate the given bitmap by the given degrees.<br>
- * New bitmap is created and the old one is recycled.
- */
- private static Bitmap rotateAndFlipBitmapInt(
- Bitmap bitmap, int degrees, boolean flipHorizontally, boolean flipVertically) {
- if (degrees > 0 || flipHorizontally || flipVertically) {
- Matrix matrix = new Matrix();
- matrix.setRotate(degrees);
- matrix.postScale(flipHorizontally ? -1 : 1, flipVertically ? -1 : 1);
- Bitmap newBitmap =
- Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
- if (newBitmap != bitmap) {
- bitmap.recycle();
- }
- return newBitmap;
- } else {
- return bitmap;
- }
- }
-
- /**
- * Get the max size of bitmap allowed to be rendered on the device.<br>
- * http://stackoverflow.com/questions/7428996/hw-accelerated-activity-how-to-get-opengl-texture-size-limit.
- */
- private static int getMaxTextureSize() {
- // Safe minimum default size
- final int IMAGE_MAX_BITMAP_DIMENSION = 2048;
-
- try {
- // Get EGL Display
- EGL10 egl = (EGL10) EGLContext.getEGL();
- EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
-
- // Initialise
- int[] version = new int[2];
- egl.eglInitialize(display, version);
-
- // Query total number of configurations
- int[] totalConfigurations = new int[1];
- egl.eglGetConfigs(display, null, 0, totalConfigurations);
-
- // Query actual list configurations
- EGLConfig[] configurationsList = new EGLConfig[totalConfigurations[0]];
- egl.eglGetConfigs(display, configurationsList, totalConfigurations[0], totalConfigurations);
-
- int[] textureSize = new int[1];
- int maximumTextureSize = 0;
-
- // Iterate through all the configurations to located the maximum texture size
- for (int i = 0; i < totalConfigurations[0]; i++) {
- // Only need to check for width since opengl textures are always squared
- egl.eglGetConfigAttrib(
- display, configurationsList[i], EGL10.EGL_MAX_PBUFFER_WIDTH, textureSize);
-
- // Keep track of the maximum texture size
- if (maximumTextureSize < textureSize[0]) {
- maximumTextureSize = textureSize[0];
- }
- }
-
- // Release
- egl.eglTerminate(display);
-
- // Return largest texture size found, or default
- return Math.max(maximumTextureSize, IMAGE_MAX_BITMAP_DIMENSION);
- } catch (Exception e) {
- return IMAGE_MAX_BITMAP_DIMENSION;
- }
- }
-
- /**
- * Close the given closeable object (Stream) in a safe way: check if it is null and catch-log
- * exception thrown.
- *
- * @param closeable the closable object to close
- */
- private static void closeSafe(Closeable closeable) {
- if (closeable != null) {
- try {
- closeable.close();
- } catch (IOException ignored) {
- }
- }
- }
- // endregion
-
- // region: Inner class: BitmapSampled
-
- /**
- * Holds bitmap instance and the sample size that the bitmap was loaded/cropped with.
- */
- static final class BitmapSampled {
-
- /**
- * The bitmap instance
- */
- public final Bitmap bitmap;
-
- /**
- * The sample size used to lower the size of the bitmap (1,2,4,8,...)
- */
- final int sampleSize;
-
- BitmapSampled(Bitmap bitmap, int sampleSize) {
- this.bitmap = bitmap;
- this.sampleSize = sampleSize;
- }
- }
- // endregion
-
- // region: Inner class: RotateBitmapResult
-
- /**
- * The result of {@link #rotateBitmapByExif(android.graphics.Bitmap, ExifInterface)}.
- */
- static final class RotateBitmapResult {
-
- /**
- * The loaded bitmap
- */
- public final Bitmap bitmap;
-
- /**
- * The degrees the image was rotated
- */
- final int degrees;
-
- RotateBitmapResult(Bitmap bitmap, int degrees) {
- this.bitmap = bitmap;
- this.degrees = degrees;
- }
- }
- // endregion
-}