Browse Source

Merge pull request #13700 from Onkar755/improve-bitmaputils

Improve BitmapUtils: Bitmap Handling and Image Processing for Modern APIs and Performance Enhancements
Tobias Kaminsky 5 months ago
parent
commit
a7433351e5
1 changed files with 81 additions and 69 deletions
  1. 81 69
      app/src/main/java/com/owncloud/android/utils/BitmapUtils.java

+ 81 - 69
app/src/main/java/com/owncloud/android/utils/BitmapUtils.java

@@ -15,6 +15,7 @@ import android.graphics.Bitmap;
 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;
@@ -24,6 +25,7 @@ import android.graphics.Rect;
 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;
@@ -33,9 +35,9 @@ import com.owncloud.android.lib.resources.users.Status;
 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;
@@ -87,13 +89,20 @@ public final class BitmapUtils {
      * @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;
@@ -171,45 +180,53 @@ public final class BitmapUtils {
             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);
@@ -227,8 +244,8 @@ public final class BitmapUtils {
     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) {
@@ -249,22 +266,15 @@ public final class BitmapUtils {
 
     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) {
@@ -277,13 +287,9 @@ public final class BitmapUtils {
         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;
     }
@@ -346,15 +352,21 @@ public final class BitmapUtils {
 
         @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();
     }
 
     /**