Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Image Scaling/Rotating #14501

Merged
merged 2 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ import com.owncloud.android.ui.fragment.FileFragment
import com.owncloud.android.ui.preview.PreviewMediaFragment.Companion.newInstance
import com.owncloud.android.utils.BitmapUtils
import com.owncloud.android.utils.DisplayUtils
import com.owncloud.android.utils.MimeType
import com.owncloud.android.utils.MimeTypeUtil
import com.owncloud.android.utils.theme.ViewThemeUtils
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
Expand Down Expand Up @@ -529,11 +528,7 @@ class PreviewImageFragment : FileFragment(), Injectable {
}

try {
bitmapResult = BitmapUtils.decodeSampledBitmapFromFile(
storagePath,
minWidth,
minHeight
)
bitmapResult = BitmapUtils.retrieveBitmapFromFile(storagePath, minWidth, minHeight)

if (isCancelled) {
return LoadImage(bitmapResult, null, ocFile)
Expand All @@ -543,11 +538,6 @@ class PreviewImageFragment : FileFragment(), Injectable {
mErrorMessageId = R.string.preview_image_error_unknown_format
Log_OC.e(TAG, "File could not be loaded as a bitmap: $storagePath")
break
} else {
if (MimeType.JPEG.equals(ocFile.mimeType, ignoreCase = true)) {
// Rotate image, obeying exif tag.
bitmapResult = BitmapUtils.rotateImage(bitmapResult, storagePath)
}
}
} catch (e: OutOfMemoryError) {
mErrorMessageId = R.string.common_error_out_memory
Expand Down
87 changes: 87 additions & 0 deletions app/src/main/java/com/owncloud/android/utils/BitmapUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,44 @@ public static Bitmap decodeSampledBitmapFromFile(String srcPath, int reqWidth, i
return BitmapFactory.decodeFile(srcPath, options);
}

/**
* Decodes a bitmap from a file containing it minimizing the memory use. Scales image to screen size.
*
* @param storagePath Absolute path to the file containing the image.
*/
public static Bitmap retrieveBitmapFromFile(String storagePath, int minWidth, int minHeight){
// Get the original dimensions of the bitmap
var bitmapResolution = getImageResolution(storagePath);
var originalWidth = bitmapResolution[0];
var originalHeight = bitmapResolution[1];

// Detect Orientation and swap height/width if the image is to be rotated
var shouldRotate = detectRotateImage(storagePath);
if (shouldRotate) {
// Swap the width and height
var tempWidth = originalWidth;
originalWidth = originalHeight;
originalHeight = tempWidth;
}

var bitmapResult = decodeSampledBitmapFromFile(
storagePath, originalWidth, originalHeight);

// Calculate the scaling factors based on screen dimensions
var widthScaleFactor = (float) minWidth/ bitmapResult.getWidth();
var heightScaleFactor = (float) minHeight / bitmapResult.getHeight();

// Use the smaller scaling factor to maintain aspect ratio
var scaleFactor = Math.min(widthScaleFactor, heightScaleFactor);

// Calculate the new scaled width and height
var scaledWidth = (int) (bitmapResult.getWidth() * scaleFactor);
var scaledHeight = (int) (bitmapResult.getHeight() * scaleFactor);

bitmapResult = scaleBitmap(bitmapResult,scaledWidth,scaledHeight);

return bitmapResult;
}
/**
* Calculates a proper value for options.inSampleSize in order to decode a Bitmap minimizing the memory overload and
* covering a target surface of reqWidth x reqHeight if the original image is big enough.
Expand Down Expand Up @@ -162,6 +199,18 @@ public static Bitmap scaleBitmap(Bitmap bitmap, float px, int width, int height,
return Bitmap.createScaledBitmap(bitmap, w, h, true);
}

/**
* scales a given bitmap depending on the given size parameters.
*
* @param bitmap the bitmap to be scaled
* @param width the width
* @param height the height
* @return the scaled bitmap
*/
public static Bitmap scaleBitmap(Bitmap bitmap, int width, int height) {
return Bitmap.createScaledBitmap(bitmap, width, height, true);
}

/**
* Rotate bitmap according to EXIF orientation. Cf. http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/
*
Expand Down Expand Up @@ -230,6 +279,44 @@ public static Bitmap rotateImage(Bitmap bitmap, String storagePath) {
return resultBitmap;
}

/**
* Detect if Image will be rotated according to EXIF orientation. Cf. http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/
*
* @param storagePath Path to source file of bitmap. Needed for EXIF information.
* @return true if image's orientation determines it will be rotated to where height and width change
*/
public static boolean detectRotateImage(String storagePath) {
try {
ExifInterface exifInterface = new ExifInterface(storagePath);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);

if (orientation != ExifInterface.ORIENTATION_NORMAL) {
switch (orientation) {
// 5
case ExifInterface.ORIENTATION_TRANSPOSE: {
return true;
}
// 6
case ExifInterface.ORIENTATION_ROTATE_90: {
return true;
}
// 7
case ExifInterface.ORIENTATION_TRANSVERSE: {
return true;
}
// 8
case ExifInterface.ORIENTATION_ROTATE_270: {
return true;
}
}
}
}
catch (Exception exception) {
Log_OC.e("BitmapUtil", "Could not read orientation at: " + storagePath);
}
return false;
}

public static int[] getImageResolution(String srcPath) {
Options options = new Options();
options.inJustDecodeBounds = true;
Expand Down
Loading