7
7
import android .graphics .Bitmap ;
8
8
import android .graphics .BitmapFactory ;
9
9
import android .util .Log ;
10
+ import androidx .annotation .NonNull ;
10
11
import androidx .annotation .Nullable ;
12
+ import androidx .core .util .SizeFCompat ;
11
13
import java .io .ByteArrayOutputStream ;
12
14
import java .io .File ;
13
15
import java .io .FileOutputStream ;
@@ -30,8 +32,8 @@ class ImageResizer {
30
32
*/
31
33
String resizeImageIfNeeded (
32
34
String imagePath , @ Nullable Double maxWidth , @ Nullable Double maxHeight , int imageQuality ) {
33
- Bitmap bmp = decodeFile (imagePath );
34
- if (bmp == null ) {
35
+ SizeFCompat originalSize = readFileDimensions (imagePath );
36
+ if (originalSize . getWidth () == - 1 || originalSize . getHeight () == - 1 ) {
35
37
return imagePath ;
36
38
}
37
39
boolean shouldScale = maxWidth != null || maxHeight != null || imageQuality < 100 ;
@@ -41,7 +43,26 @@ String resizeImageIfNeeded(
41
43
try {
42
44
String [] pathParts = imagePath .split ("/" );
43
45
String imageName = pathParts [pathParts .length - 1 ];
44
- File file = resizedImage (bmp , maxWidth , maxHeight , imageQuality , imageName );
46
+ SizeFCompat targetSize =
47
+ calculateTargetSize (
48
+ (double ) originalSize .getWidth (),
49
+ (double ) originalSize .getHeight (),
50
+ maxWidth ,
51
+ maxHeight );
52
+ BitmapFactory .Options options = new BitmapFactory .Options ();
53
+ options .inSampleSize =
54
+ calculateSampleSize (options , (int ) targetSize .getWidth (), (int ) targetSize .getHeight ());
55
+ Bitmap bmp = decodeFile (imagePath , options );
56
+ if (bmp == null ) {
57
+ return imagePath ;
58
+ }
59
+ File file =
60
+ resizedImage (
61
+ bmp ,
62
+ (double ) targetSize .getWidth (),
63
+ (double ) targetSize .getHeight (),
64
+ imageQuality ,
65
+ imageName );
45
66
copyExif (imagePath , file .getPath ());
46
67
return file .getPath ();
47
68
} catch (IOException e ) {
@@ -50,10 +71,19 @@ String resizeImageIfNeeded(
50
71
}
51
72
52
73
private File resizedImage (
53
- Bitmap bmp , Double maxWidth , Double maxHeight , int imageQuality , String outputImageName )
74
+ Bitmap bmp , Double width , Double height , int imageQuality , String outputImageName )
54
75
throws IOException {
55
- double originalWidth = bmp .getWidth () * 1.0 ;
56
- double originalHeight = bmp .getHeight () * 1.0 ;
76
+ Bitmap scaledBmp = createScaledBitmap (bmp , width .intValue (), height .intValue (), false );
77
+ File file =
78
+ createImageOnExternalDirectory ("/scaled_" + outputImageName , scaledBmp , imageQuality );
79
+ return file ;
80
+ }
81
+
82
+ private SizeFCompat calculateTargetSize (
83
+ @ NonNull Double originalWidth ,
84
+ @ NonNull Double originalHeight ,
85
+ @ Nullable Double maxWidth ,
86
+ @ Nullable Double maxHeight ) {
57
87
58
88
boolean hasMaxWidth = maxWidth != null ;
59
89
boolean hasMaxHeight = maxHeight != null ;
@@ -90,10 +120,7 @@ private File resizedImage(
90
120
}
91
121
}
92
122
93
- Bitmap scaledBmp = createScaledBitmap (bmp , width .intValue (), height .intValue (), false );
94
- File file =
95
- createImageOnExternalDirectory ("/scaled_" + outputImageName , scaledBmp , imageQuality );
96
- return file ;
123
+ return new SizeFCompat (width .floatValue (), height .floatValue ());
97
124
}
98
125
99
126
private File createFile (File externalFilesDirectory , String child ) {
@@ -112,14 +139,47 @@ private void copyExif(String filePathOri, String filePathDest) {
112
139
exifDataCopier .copyExif (filePathOri , filePathDest );
113
140
}
114
141
115
- private Bitmap decodeFile (String path ) {
116
- return BitmapFactory .decodeFile (path );
142
+ private SizeFCompat readFileDimensions (String path ) {
143
+ BitmapFactory .Options options = new BitmapFactory .Options ();
144
+ options .inJustDecodeBounds = true ;
145
+ decodeFile (path , options );
146
+ return new SizeFCompat (options .outWidth , options .outHeight );
147
+ }
148
+
149
+ private Bitmap decodeFile (String path , @ Nullable BitmapFactory .Options opts ) {
150
+ return BitmapFactory .decodeFile (path , opts );
117
151
}
118
152
119
153
private Bitmap createScaledBitmap (Bitmap bmp , int width , int height , boolean filter ) {
120
154
return Bitmap .createScaledBitmap (bmp , width , height , filter );
121
155
}
122
156
157
+ /**
158
+ * Calculates the largest sample size value that is a power of two based on a target width and
159
+ * height.
160
+ *
161
+ * <p>This value is necessary to tell the Bitmap decoder to subsample the original image,
162
+ * returning a smaller image to save memory.
163
+ *
164
+ * @see <a
165
+ * href="https://developer.android.com/topic/performance/graphics/load-bitmap#load-bitmap">
166
+ * Loading Large Bitmaps Efficiently</a>
167
+ */
168
+ private int calculateSampleSize (
169
+ BitmapFactory .Options options , int targetWidth , int targetHeight ) {
170
+ final int height = options .outHeight ;
171
+ final int width = options .outWidth ;
172
+ int sampleSize = 1 ;
173
+ if (height > targetHeight || width > targetWidth ) {
174
+ final int halfHeight = height / 2 ;
175
+ final int halfWidth = width / 2 ;
176
+ while ((halfHeight / sampleSize ) >= targetHeight && (halfWidth / sampleSize ) >= targetWidth ) {
177
+ sampleSize *= 2 ;
178
+ }
179
+ }
180
+ return sampleSize ;
181
+ }
182
+
123
183
private File createImageOnExternalDirectory (String name , Bitmap bitmap , int imageQuality )
124
184
throws IOException {
125
185
ByteArrayOutputStream outputStream = new ByteArrayOutputStream ();
0 commit comments