-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
/
ImageUtils.java
278 lines (231 loc) · 9.78 KB
/
ImageUtils.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
package com.termux.shared.image;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.BlendMode;
import android.graphics.BlendModeColorFilter;
import android.graphics.ImageDecoder;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import com.termux.shared.errors.Error;
import com.termux.shared.file.FileUtils;
import com.termux.shared.file.FileUtilsErrno;
import com.termux.shared.logger.Logger;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public final class ImageUtils {
/**
* Request code that can be used to distinguish Activity result.
*/
public static final int REQUEST_CODE_IMAGE = 100;
/**
* Compression quality used to compress image. The value is interpreted differently depending on the {@link CompressFormat CompressFormat}.
*/
public static final int COMPRESS_QUALITY = 80;
/**
* Tolerance for diffrence in original image required optimized image.
*/
public static final int OPTIMALITY_TOLERANCE = 50;
public static final String IMAGE_TYPE = "image";
public static final String ANY_IMAGE_TYPE = IMAGE_TYPE + "/*";
private static final String LOG_TAG = "ImageUtils";
/**
* Don't let anyone instantiate this class.
*/
private ImageUtils() {
}
/**
* Get an {@link Bitmap} image from the {@link Uri}.
*
* @param context The context for the operations.
* @param uri The uri from where image content will be loaded.
* @return Bitmap containing the image, or return {@code null} if failed to
* load bitmap content.
*/
public static Bitmap getBitmap(final Context context, Uri uri) {
Bitmap bitmap = null;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(context.getContentResolver(), uri));
} else {
bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri);
}
bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri);
} catch (IOException e) {
Logger.logStackTraceWithMessage(LOG_TAG, "Failed to load bitmap from " + uri, e);
}
return bitmap;
}
/**
* Generate image bitmap from the path.
*
* @param path The path for image file
* @return Bitmap generated from image path, if fails to
* to generate returns {@code null}
*/
public static Bitmap getBitmap(String path) {
return BitmapFactory.decodeFile(path);
}
/**
* Creates an centered and resized {@link Bitmap} according to given size.
*
* @param bitmap Original bitmap source to resize.
* @param point Target size containing width and height to resize.
* @return Returns the resized bitmap for given size.
*/
public static Bitmap resizeBitmap(Bitmap bitmap, Point point) {
return ThumbnailUtils.extractThumbnail(bitmap, point.x, point.y);
}
/**
* Wrapper for {@link #compressAndSaveBitmap(Bitmap, int, String)} with `{@link #COMPRESS_QUALITY} `quality`.
*/
public static Error compressAndSaveBitmap(Bitmap bitmap, String path) {
return compressAndSaveBitmap(bitmap, COMPRESS_QUALITY, path);
}
/**
* Wrapper for {@link #compressAndSaveBitmap(Bitmap, CompressFormat, int, String)} with `{@link Bitmap.CompressFormat#JPEG}` `format`.
*/
public static Error compressAndSaveBitmap(Bitmap bitmap, int quality, String path) {
return compressAndSaveBitmap(bitmap, Bitmap.CompressFormat.JPEG, quality, path);
}
/**
* Compress the given bitmap image file for given format and quality.
*
* @param bitmap Original source bitmap.
* @param foramt The format for image compression.
* @param quality Hint to the compressor, 0-100. The value is interpreted differently
* depending on the {@link Bitmap.CompressFormat}.
* @param path The path to store compressed bitmap.
* @return Returns the {@code error} if compression and save operation was not successful,
* otherwise {@code null}.
*/
public static Error compressAndSaveBitmap(Bitmap bitmap, Bitmap.CompressFormat foramt, int quality, String path) {
FileUtils.deleteRegularFile(null, path, true);
Error error = FileUtils.createRegularFile(path);
if (error != null)
return error;
try (FileOutputStream out = new FileOutputStream(path)) {
bitmap.compress(foramt, quality, out);
} catch (Exception e) {
FileUtils.deleteRegularFile(null, path, true);
error = FileUtilsErrno.ERRNO_CREATING_FILE_FAILED_WITH_EXCEPTION.getError(e, e.getMessage());
}
return error;
}
/**
* Wrapper for {@link #getDrawable(String)} with `file.getAbsolutePath()` `path` of file.
*/
public static Drawable getDrawable(File file) {
String path = file.getAbsolutePath();
return getDrawable(path);
}
/**
* Create {@link BitmapDrawable} from specified file path.
*
* @param path The path file to load image bitmap drawable.
* @return Drawable created from image file path.
*/
public static Drawable getDrawable(String path) {
return BitmapDrawable.createFromPath(path);
}
/**
* Add an overlay color/tint on image with {@link BlendMode#MULTIPLY}.
*
* @param drawable The source image bitmap drawable.
* @param color Overlay color for image.
*/
public static void addOverlay(Drawable drawable, int color) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
drawable.setColorFilter(new BlendModeColorFilter(color, BlendMode.DARKEN));
} else {
drawable.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.DARKEN));
}
}
/**
* Wrapper for {@link #isImageOptimized(String, Point, int)} with `{@link #OPTIMALITY_TOLERANCE}` `tolerance`.
*/
public static boolean isImageOptimized(String path, Point point) {
return isImageOptimized(path, point, OPTIMALITY_TOLERANCE);
}
/**
* Wrapper for {@link #isImageOptimized(String, int, int, int)} with `width` and `height` obtained from {@link Point}.
*/
public static boolean isImageOptimized(String path, Point point, int tolerance) {
return isImageOptimized(path, point.x, point.y, tolerance);
}
/**
* Check whether the image file present at file location is optimized corresponding
* to given width and height. It can tolorent error upto given value.
*
* @param path The file path of image.
* @param width The required width of image file.
* @param height The required width of image file.
* @param tolerance The tolerance value upto which diffrence is ignored.
* @return Returns whether the given image is optimized or not.
*/
public static boolean isImageOptimized(String path, int width, int height, int tolerance) {
if (!FileUtils.regularFileExists(path, false)) {
Logger.logInfo(LOG_TAG, "Image file " + path + " does not exist.");
return false;
}
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, opt);
int imgWidth = opt.outWidth;
int imgHeight = opt.outHeight;
return Math.abs(imgWidth - width) <= tolerance && Math.abs(imgHeight - height) <= tolerance;
}
public static boolean isImage(String path) {
if (!FileUtils.regularFileExists(path, false)) {
Logger.logInfo(LOG_TAG, "Image file " + path + " does not exist.");
return false;
}
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, opt);
return opt.outWidth != -1 && opt.outHeight != -1;
}
/**
* Resize bitmap for {@link Configuration#ORIENTATION_PORTRAIT Portrait} and {@link
* Configuration#ORIENTATION_LANDSCAPE Landscape} display view.
* Also Compress the image bitmap before saving it to given path.
*
* @param bitmap The original bitmap image to resize and store.
* @param point Display resolution containing width and height.
* @param path1 The path for storing image with width point.x and height point.y
* @param path2 The path for storing image with width point.y and height point.x
* @return Returns the {@code error} if save operation was not successful,
* otherwise {@code null}.
*/
public static Error saveForDisplayResolution(Bitmap bitmap, Point point, String path1, String path2) {
Error error;
Bitmap bitmap1 = resizeBitmap(bitmap, point);
error = compressAndSaveBitmap(bitmap1, path1);
if (error != null) {
return error;
}
Bitmap bitmap2 = resizeBitmap(bitmap, new Point(point.y, point.x));
error = compressAndSaveBitmap(bitmap2, path2);
return error;
}
/**
* Check for the given {@link Drawable} whether it is instance of {@link
* BitmapDrawable} or not.
*
* @param drawable The drawable to check.
* @return Retruns whether drawable is bitmap drawable.
*/
public static boolean isBitmapDrawable(Drawable drawable) {
return drawable instanceof BitmapDrawable;
}
}