Skip to content

Commit

Permalink
Merge pull request #13700 from Onkar755/improve-bitmaputils
Browse files Browse the repository at this point in the history
Improve BitmapUtils: Bitmap Handling and Image Processing for Modern APIs and Performance Enhancements
  • Loading branch information
tobiasKaminsky authored Nov 12, 2024
2 parents 6cdae0d + dd2104a commit a743335
Showing 1 changed file with 81 additions and 69 deletions.
150 changes: 81 additions & 69 deletions app/src/main/java/com/owncloud/android/utils/BitmapUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.Canvas;
import android.graphics.ImageDecoder;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
Expand All @@ -24,6 +25,7 @@
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.widget.ImageView;

import com.owncloud.android.MainApp;
Expand All @@ -33,9 +35,9 @@
import com.owncloud.android.lib.resources.users.StatusType;
import com.owncloud.android.ui.StatusDrawable;

import org.apache.commons.codec.binary.Hex;

import java.nio.charset.Charset;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Locale;
Expand Down Expand Up @@ -87,13 +89,20 @@ public static Bitmap addColorFilter(Bitmap originalBitmap, int filterColor, int
* @return decoded bitmap
*/
public static Bitmap decodeSampledBitmapFromFile(String srcPath, int reqWidth, int reqHeight) {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// For API 28 and above, use ImageDecoder
try {
return ImageDecoder.decodeBitmap(ImageDecoder.createSource(new File(srcPath)),
(decoder, info, source) -> {
// Set the target size
decoder.setTargetSize(reqWidth, reqHeight);
});
} catch (Exception exception) {
Log_OC.e("BitmapUtil", "Error decoding the bitmap from file: " + srcPath + ", exception: " + exception.getMessage());
}
}
// set desired options that will affect the size of the bitmap
final Options options = new Options();
options.inScaled = true;
options.inPurgeable = true;
options.inPreferQualityOverSpeed = false;
options.inMutable = false;

// make a false load of the bitmap to get its dimensions
options.inJustDecodeBounds = true;
Expand Down Expand Up @@ -171,45 +180,53 @@ public static Bitmap rotateImage(Bitmap bitmap, String storagePath) {
ExifInterface exifInterface = new ExifInterface(storagePath);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);

Matrix matrix = new Matrix();

// 1: nothing to do

// 2
if (orientation == ExifInterface.ORIENTATION_FLIP_HORIZONTAL) {
matrix.postScale(-1.0f, 1.0f);
}
// 3
else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
matrix.postRotate(180);
}
// 4
else if (orientation == ExifInterface.ORIENTATION_FLIP_VERTICAL) {
matrix.postScale(1.0f, -1.0f);
}
// 5
else if (orientation == ExifInterface.ORIENTATION_TRANSPOSE) {
matrix.postRotate(-90);
matrix.postScale(1.0f, -1.0f);
}
// 6
else if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
matrix.postRotate(90);
}
// 7
else if (orientation == ExifInterface.ORIENTATION_TRANSVERSE) {
matrix.postRotate(90);
matrix.postScale(1.0f, -1.0f);
}
// 8
else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
matrix.postRotate(270);
}
if (orientation != ExifInterface.ORIENTATION_NORMAL) {
Matrix matrix = new Matrix();
switch (orientation) {
// 2
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL: {
matrix.postScale(-1.0f, 1.0f);
break;
}
// 3
case ExifInterface.ORIENTATION_ROTATE_180: {
matrix.postRotate(180);
break;
}
// 4
case ExifInterface.ORIENTATION_FLIP_VERTICAL: {
matrix.postScale(1.0f, -1.0f);
break;
}
// 5
case ExifInterface.ORIENTATION_TRANSPOSE: {
matrix.postRotate(-90);
matrix.postScale(1.0f, -1.0f);
break;
}
// 6
case ExifInterface.ORIENTATION_ROTATE_90: {
matrix.postRotate(90);
break;
}
// 7
case ExifInterface.ORIENTATION_TRANSVERSE: {
matrix.postRotate(90);
matrix.postScale(1.0f, -1.0f);
break;
}
// 8
case ExifInterface.ORIENTATION_ROTATE_270: {
matrix.postRotate(270);
break;
}
}

// Rotate the bitmap
resultBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
if (!resultBitmap.equals(bitmap)) {
bitmap.recycle();
// Rotate the bitmap
resultBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
if (!resultBitmap.equals(bitmap)) {
bitmap.recycle();
}
}
} catch (Exception exception) {
Log_OC.e("BitmapUtil", "Could not rotate the image: " + storagePath);
Expand All @@ -227,8 +244,8 @@ public static int[] getImageResolution(String srcPath) {
public static Color usernameToColor(String name) {
String hash = name.toLowerCase(Locale.ROOT);

// already a md5 hash?
if (!hash.matches("([0-9a-f]{4}-?){8}$")) {
// Check if the input is already a valid MD5 hash (32 hex characters)
if (hash.length() != 32 || !hash.matches("[0-9a-f]+")) {
try {
hash = md5(hash);
} catch (NoSuchAlgorithmException e) {
Expand All @@ -249,22 +266,15 @@ public static Color usernameToColor(String name) {

private static int hashToInt(String hash, int maximum) {
int finalInt = 0;
int[] result = new int[hash.length()];

// splitting evenly the string
// Sum the values of the hexadecimal digits
for (int i = 0; i < hash.length(); i++) {
// chars in md5 goes up to f, hex: 16
result[i] = Integer.parseInt(String.valueOf(hash.charAt(i)), 16) % 16;
}

// adds up all results
for (int value : result) {
finalInt += value;
// Efficient hex char-to-int conversion
finalInt += Character.digit(hash.charAt(i), 16);
}

// chars in md5 goes up to f, hex:16
// make sure we're always using int in our operation
return Integer.parseInt(String.valueOf(Integer.parseInt(String.valueOf(finalInt), 10) % maximum), 10);
// Return the sum modulo maximum
return finalInt % maximum;
}

private static Color[] generateColors(int steps) {
Expand All @@ -277,13 +287,9 @@ private static Color[] generateColors(int steps) {
Color[] palette3 = mixPalette(steps, blue, red);

Color[] resultPalette = new Color[palette1.length + palette2.length + palette3.length];
System.arraycopy(palette1, 0, resultPalette, 0, palette1.length);
System.arraycopy(palette2, 0, resultPalette, palette1.length, palette2.length);
System.arraycopy(palette3,
0,
resultPalette,
palette1.length + palette2.length,
palette1.length);
System.arraycopy(palette1, 0, resultPalette, 0, steps);
System.arraycopy(palette2, 0, resultPalette, steps, steps);
System.arraycopy(palette3, 0, resultPalette, steps * 2, steps);

return resultPalette;
}
Expand Down Expand Up @@ -346,15 +352,21 @@ public boolean equals(@Nullable Object obj) {

@Override
public int hashCode() {
return r * 10000 + g * 1000 + b;
return (r << 16) + (g << 8) + b;
}
}

public static String md5(String string) throws NoSuchAlgorithmException {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(string.getBytes(Charset.defaultCharset()));
// Use UTF-8 for consistency
byte[] hashBytes = md5.digest(string.getBytes(StandardCharsets.UTF_8));

return new String(Hex.encodeHex(md5.digest()));
StringBuilder hexString = new StringBuilder(32);
for (byte b : hashBytes) {
// Convert each byte to a 2-digit hex string
hexString.append(String.format("%02x", b));
}
return hexString.toString();
}

/**
Expand Down

0 comments on commit a743335

Please sign in to comment.