|
|
|
@ -18,11 +18,14 @@ package com.keylesspalace.tusky; |
|
|
|
|
import android.content.ContentResolver; |
|
|
|
|
import android.graphics.Bitmap; |
|
|
|
|
import android.graphics.BitmapFactory; |
|
|
|
|
import android.graphics.Matrix; |
|
|
|
|
import android.net.Uri; |
|
|
|
|
import android.os.AsyncTask; |
|
|
|
|
import android.support.media.ExifInterface; |
|
|
|
|
|
|
|
|
|
import java.io.ByteArrayOutputStream; |
|
|
|
|
import java.io.FileNotFoundException; |
|
|
|
|
import java.io.IOException; |
|
|
|
|
import java.io.InputStream; |
|
|
|
|
import java.util.ArrayList; |
|
|
|
|
import java.util.List; |
|
|
|
@ -39,13 +42,85 @@ class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> { |
|
|
|
|
this.listener = listener; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static Bitmap reorientBitmap(Bitmap bitmap, int orientation) { |
|
|
|
|
Matrix matrix = new Matrix(); |
|
|
|
|
switch (orientation) { |
|
|
|
|
default: |
|
|
|
|
case ExifInterface.ORIENTATION_NORMAL: { |
|
|
|
|
return bitmap; |
|
|
|
|
} |
|
|
|
|
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL: { |
|
|
|
|
matrix.setScale(-1, 1); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
case ExifInterface.ORIENTATION_ROTATE_180: { |
|
|
|
|
matrix.setRotate(180); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
case ExifInterface.ORIENTATION_FLIP_VERTICAL: { |
|
|
|
|
matrix.setRotate(180); |
|
|
|
|
matrix.postScale(-1, 1); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
case ExifInterface.ORIENTATION_TRANSPOSE: { |
|
|
|
|
matrix.setRotate(90); |
|
|
|
|
matrix.postScale(-1, 1); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
case ExifInterface.ORIENTATION_ROTATE_90: { |
|
|
|
|
matrix.setRotate(90); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
case ExifInterface.ORIENTATION_TRANSVERSE: { |
|
|
|
|
matrix.setRotate(-90); |
|
|
|
|
matrix.postScale(-1, 1); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
case ExifInterface.ORIENTATION_ROTATE_270: { |
|
|
|
|
matrix.setRotate(-90); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
try { |
|
|
|
|
Bitmap result = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), |
|
|
|
|
bitmap.getHeight(), matrix, true); |
|
|
|
|
bitmap.recycle(); |
|
|
|
|
return result; |
|
|
|
|
} catch (OutOfMemoryError e) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static int getOrientation(Uri uri, ContentResolver contentResolver) { |
|
|
|
|
InputStream inputStream; |
|
|
|
|
try { |
|
|
|
|
inputStream = contentResolver.openInputStream(uri); |
|
|
|
|
} catch (FileNotFoundException e) { |
|
|
|
|
return ExifInterface.ORIENTATION_UNDEFINED; |
|
|
|
|
} |
|
|
|
|
if (inputStream == null) { |
|
|
|
|
return ExifInterface.ORIENTATION_UNDEFINED; |
|
|
|
|
} |
|
|
|
|
ExifInterface exifInterface; |
|
|
|
|
try { |
|
|
|
|
exifInterface = new ExifInterface(inputStream); |
|
|
|
|
} catch (IOException e) { |
|
|
|
|
IOUtils.closeQuietly(inputStream); |
|
|
|
|
return ExifInterface.ORIENTATION_UNDEFINED; |
|
|
|
|
} |
|
|
|
|
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, |
|
|
|
|
ExifInterface.ORIENTATION_NORMAL); |
|
|
|
|
IOUtils.closeQuietly(inputStream); |
|
|
|
|
return orientation; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static int calculateInSampleSize(int width, int height, int requiredScale) { |
|
|
|
|
int inSampleSize = 1; |
|
|
|
|
if (height > requiredScale || width > requiredScale) { |
|
|
|
|
final int halfHeight = height / 2; |
|
|
|
|
final int halfWidth = width / 2; |
|
|
|
|
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
|
|
|
|
|
// height and width larger than the requested height and width.
|
|
|
|
|
/* Calculate the largest inSampleSize value that is a power of 2 and keeps both height |
|
|
|
|
* and width larger than the requested height and width. */ |
|
|
|
|
while (halfHeight / inSampleSize >= requiredScale |
|
|
|
|
&& halfWidth / inSampleSize >= requiredScale) { |
|
|
|
|
inSampleSize *= 2; |
|
|
|
@ -71,6 +146,8 @@ class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> { |
|
|
|
|
int beforeWidth = options.outWidth; |
|
|
|
|
int beforeHeight = options.outHeight; |
|
|
|
|
IOUtils.closeQuietly(inputStream); |
|
|
|
|
// Get EXIF data, for orientation info.
|
|
|
|
|
int orientation = getOrientation(uri, contentResolver); |
|
|
|
|
// Then use that information to determine how much to compress.
|
|
|
|
|
ByteArrayOutputStream stream = new ByteArrayOutputStream(); |
|
|
|
|
/* Unfortunately, there isn't a determined worst case compression ratio for image |
|
|
|
@ -79,7 +156,7 @@ class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> { |
|
|
|
|
* many cases, so it should only iterate once, but the loop is used to be absolutely |
|
|
|
|
* sure it gets downsized to below the limit. */ |
|
|
|
|
int iterations = 0; |
|
|
|
|
int scaledImageSize = 4096; |
|
|
|
|
int scaledImageSize = 1024; |
|
|
|
|
do { |
|
|
|
|
stream.reset(); |
|
|
|
|
try { |
|
|
|
@ -101,6 +178,7 @@ class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> { |
|
|
|
|
if (scaledBitmap == null) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
reorientBitmap(scaledBitmap, orientation); |
|
|
|
|
Bitmap.CompressFormat format; |
|
|
|
|
/* It's not likely the user will give transparent images over the upload limit, but |
|
|
|
|
* if they do, make sure the transparency is retained. */ |
|
|
|
|