package im.actor.sdk.util.images.ops; import android.content.Context; import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; import im.actor.sdk.util.images.common.*; import im.actor.sdk.util.images.sources.FileSource; import im.actor.sdk.util.images.sources.ImageSource; import im.actor.sdk.util.images.sources.MemorySource; import im.actor.sdk.util.images.sources.UriSource; import im.actor.sdk.util.images.BitmapUtil; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.IOException; /** * Image loading */ public class ImageLoading { private static final int MAX_PIXELS = 1200 * 1200; private static final int MAX_PIXELS_HQ = 1500 * 1500; public static final int JPEG_QUALITY = 80; public static final int JPEG_QUALITY_HQ = 90; public static final int JPEG_QUALITY_LOW = 55; /** * Loading bitmap without any modifications * * @param fileName Image file name * @return loaded bitmap (always not null) * @throws ImageLoadException if it is unable to load file */ public static Bitmap loadBitmap(String fileName) throws ImageLoadException { return loadBitmap(new FileSource(fileName)); } /** * Loading bitmap without any modifications * * @param uri content uri for bitmap * @param context Application Context * @return loaded bitmap (always not null) * @throws ImageLoadException if it is unable to load file */ public static Bitmap loadBitmap(Uri uri, Context context) throws ImageLoadException { return loadBitmap(new UriSource(uri, context)); } /** * Loading bitmap without any modifications * * @param data image file contents * @return loaded bitmap (always not null) * @throws ImageLoadException if it is unable to load file */ public static Bitmap loadBitmap(byte[] data) throws ImageLoadException { return loadBitmap(new MemorySource(data)); } /** * Loading bitmap with scaling * * @param fileName Image file name * @param scale divider of size, might be factor of two * @return loaded bitmap (always not null) * @throws ImageLoadException if it is unable to load file */ public static Bitmap loadBitmap(String fileName, int scale) throws ImageLoadException { return loadBitmap(new FileSource(fileName), scale); } /** * Loading bitmap with scaling * * @param fileName Image file name * @param minW minimal width * @param minH minimal height * @return loaded bitmap (always not null) * @throws ImageLoadException if it is unable to load file */ public static Bitmap loadBitmap(String fileName, int minW, int minH) throws ImageLoadException { return loadBitmapOptimized(new FileSource(fileName), minW, minH); } /** * Loading bitmap with optimized loaded size less than 1.4 MPX * * @param uri content uri for bitmap * @param context Application Context * @return loaded bitmap (always not null) * @throws ImageLoadException if it is unable to load file */ public static Bitmap loadBitmapOptimized(Uri uri, Context context) throws ImageLoadException { return loadBitmapOptimized(uri, context, MAX_PIXELS); } /** * Loading bitmap with optimized loaded size less than 1.4 MPX * * @param fileName Image file name * @return loaded bitmap (always not null) * @throws ImageLoadException if it is unable to load file */ public static Bitmap loadBitmapOptimized(String fileName) throws ImageLoadException { return loadBitmapOptimized(fileName, MAX_PIXELS); } /** * Loading bitmap with optimized loaded size less than 2.2 MPX * * @param fileName Image file name * @return loaded bitmap (always not null) * @throws ImageLoadException if it is unable to load file */ public static Bitmap loadBitmapOptimizedHQ(String fileName) throws ImageLoadException { return loadBitmapOptimized(fileName, MAX_PIXELS_HQ); } /** * Loading bitmap with optimized loaded size less than specific pixels count * * @param fileName Image file name * @param limit maximum pixels size * @return loaded bitmap (always not null) * @throws ImageLoadException if it is unable to load file */ public static Bitmap loadBitmapOptimized(String fileName, int limit) throws ImageLoadException { return loadBitmapOptimized(new FileSource(fileName), limit); } /** * Loading bitmap with optimized loaded size less than specific pixels count * * @param uri content uri for bitmap * @param context Application Context * @param limit maximum pixels size * @return loaded bitmap (always not null) * @throws ImageLoadException if it is unable to load file */ public static Bitmap loadBitmapOptimized(Uri uri, Context context, int limit) throws ImageLoadException { return loadBitmapOptimized(new UriSource(uri, context) { }, limit); } /** * Loading bitmap with using reuse bitmap with the same size of source image. * If it is unable to load with reuse method tries to load without it. * Reuse works only in 3.0+ * * @param fileName Image file name * @param dest reuse bitmap * @return result of loading * @throws ImageLoadException if it is unable to load file */ public static ReuseResult loadReuseExact(String fileName, Bitmap dest) throws ImageLoadException { return loadBitmapReuseExact(new FileSource(fileName), dest); } /** * Loading bitmap with using reuse bitmap with the same size of source image. * If it is unable to load with reuse method tries to load without it. * Reuse works only in 3.0+ * * @param data image file contents * @param dest reuse bitmap * @return result of loading * @throws ImageLoadException if it is unable to load file */ public static ReuseResult loadReuseExact(byte[] data, Bitmap dest) throws ImageLoadException { return loadBitmapReuseExact(new MemorySource(data), dest); } /** * Loading bitmap with using reuse bitmap with the same size of source image. * If it is unable to load with reuse method tries to load without it. * Reuse works only in 3.0+ * * @param uri content uri for bitmap * @param context Application Context * @param dest reuse bitmap * @return result of loading * @throws ImageLoadException if it is unable to load file */ public static ReuseResult loadReuseExact(Uri uri, Context context, Bitmap dest) throws ImageLoadException { return loadBitmapReuseExact(new UriSource(uri, context), dest); } /** * Loading bitmap with using reuse bitmap with the different size of source image. * If it is unable to load with reuse method tries to load without it. * Reuse works only for Android 4.4+ * * @param fileName Image file name * @param dest reuse bitmap * @return result of loading * @throws ImageLoadException if it is unable to load file */ public static ReuseResult loadReuse(String fileName, Bitmap dest) throws ImageLoadException { return loadBitmapReuse(new FileSource(fileName), dest); } /** * Loading bitmap with using reuse bitmap with the different size of source image. * If it is unable to load with reuse method tries to load without it. * Reuse works only for Android 4.4+ * * @param data image file contents * @param dest reuse bitmap * @return result of loading * @throws ImageLoadException if it is unable to load file */ public static ReuseResult loadReuse(byte[] data, Bitmap dest) throws ImageLoadException { return loadBitmapReuse(new MemorySource(data), dest); } /** * Loading bitmap with using reuse bitmap with the different size of source image. * If it is unable to load with reuse method tries to load without it. * Reuse works only for Android 4.4+ * * @param uri content uri for bitmap * @param dest destination bitmap * @param context Application Context * @return result of loading * @throws ImageLoadException if it is unable to load file */ public static ReuseResult loadReuse(Uri uri, Context context, Bitmap dest) throws ImageLoadException { return loadBitmapReuse(new UriSource(uri, context), dest); } /** * Saving image in jpeg to byte array with quality 80 * * @param src source image * @return saved data * @throws ImageSaveException if it is unable to save image */ public static byte[] save(Bitmap src) throws ImageSaveException { return save(src, Bitmap.CompressFormat.JPEG, JPEG_QUALITY); } /** * Saving image in jpeg to byte array with better quality 90 * * @param src source image * @return saved data * @throws ImageSaveException if it is unable to save image */ public static byte[] saveHq(Bitmap src) throws ImageSaveException { return save(src, Bitmap.CompressFormat.JPEG, JPEG_QUALITY_HQ); } /** * Saving image in jpeg to byte array with specific quality * * @param src source image * @param quality jpeg quality * @return saved data * @throws ImageSaveException if it is unable to save image */ public static byte[] saveJpeg(Bitmap src, int quality) throws ImageSaveException { return save(src, Bitmap.CompressFormat.JPEG, quality); } /** * Saving image in png to byte array with specific quality * * @param src source image * @return saved data * @throws ImageSaveException if it is unable to save image */ public static byte[] savePng(Bitmap src) throws ImageSaveException { return save(src, Bitmap.CompressFormat.PNG, 100); } /** * Saving image in jpeg to file with quality 80 * * @param src source image * @param fileName destination file name * @throws ImageSaveException if it is unable to save image */ public static void save(Bitmap src, String fileName) throws ImageSaveException { saveJpeg(src, fileName, JPEG_QUALITY); } /** * Saving image in jpeg to file with quality 55 * * @param src source image * @param fileName destination file name * @throws ImageSaveException if it is unable to save image */ public static void saveLq(Bitmap src, String fileName) throws ImageSaveException { saveJpeg(src, fileName, JPEG_QUALITY_LOW); } /** * Saving image in jpeg to file with better quality 90 * * @param src source image * @param fileName destination file name * @throws ImageSaveException if it is unable to save image */ public static void saveHq(Bitmap src, String fileName) throws ImageSaveException { saveJpeg(src, fileName, JPEG_QUALITY_HQ); } /** * Saving image in jpeg to file with better quality 90 * * @param src source image * @param fileName destination file name * @throws ImageSaveException if it is unable to save image */ public static void saveJpeg(Bitmap src, String fileName, int quality) throws ImageSaveException { save(src, fileName, Bitmap.CompressFormat.JPEG, quality); } /** * Saving image in png to file * * @param src source image * @param fileName destination file name * @throws ImageSaveException if it is unable to save image */ public static void savePng(Bitmap src, String fileName) throws ImageSaveException { save(src, fileName, Bitmap.CompressFormat.PNG, 100); } /** * Saving image in bmp to file * * @param src source image * @param fileName destination file name * @throws ImageSaveException if it is unable to save image */ public static void saveBmp(Bitmap src, String fileName) throws ImageSaveException { try { BitmapUtil.save(src, fileName); } catch (IOException e) { throw new ImageSaveException(e); } } /** * Calculating allocated memory for bitmap * * @param bitmap source bitmap * @return size in bytes */ public static int bitmapSize(Bitmap bitmap) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { return bitmap.getByteCount(); } else { return bitmap.getRowBytes() * bitmap.getHeight(); } } /** * Loading bitmap from ImageSource * * @param source image source * @return loaded bitmap * @throws ImageLoadException if it is unable to load image */ private static Bitmap loadBitmap(ImageSource source) throws ImageLoadException { return source.loadBitmap(); } /** * Loading bitmap from ImageSource with limit of amout of pixels * * @param source image source * @param limit maximum pixels size * @return loaded bitmap * @throws ImageLoadException if it is unable to load image */ private static Bitmap loadBitmapOptimized(ImageSource source, int limit) throws ImageLoadException { int scale = getScaleFactor(source.getImageMetadata(), limit); return loadBitmap(source, scale); } /** * Loading bitmap from ImageSource with limit of amout of pixels * * @param source image source * @param w min width * @param h min height * @return loaded bitmap * @throws ImageLoadException if it is unable to load image */ private static Bitmap loadBitmapOptimized(ImageSource source, int w, int h) throws ImageLoadException { int scale = getScaleFactor(source.getImageMetadata(), w, h); return loadBitmap(source, scale); } /** * Loading bitmap from ImageSource with specific scale * * @param source image source * @param scale divider of size, might be factor of two * @return loaded bitmap * @throws ImageLoadException if it is unable to load image */ private static Bitmap loadBitmap(ImageSource source, int scale) throws ImageLoadException { return source.loadBitmap(scale); } /** * Loading image with reuse bitmap of same size as source * * @param source image source * @param dest destination bitmap * @return loaded bitmap result * @throws ImageLoadException if it is unable to load image */ private static ReuseResult loadBitmapReuseExact(ImageSource source, Bitmap dest) throws ImageLoadException { ImageMetadata metadata = source.getImageMetadata(); boolean tryReuse = false; if (dest.isMutable() && dest.getWidth() == metadata.getW() && dest.getHeight() == metadata.getH()) { if (Build.VERSION.SDK_INT >= 19) { tryReuse = true; } else if (Build.VERSION.SDK_INT >= 11) { if (metadata.getFormat() == ImageFormat.JPEG || metadata.getFormat() == ImageFormat.PNG) { tryReuse = true; } } } if (tryReuse) { return source.loadBitmap(dest); } else { return new ReuseResult(loadBitmap(source), false); } } /** * Loading image with reuse bitmap * * @param source image source * @param dest destination bitmap * @return loaded bitmap result * @throws ImageLoadException if it is unable to load image */ private static ReuseResult loadBitmapReuse(ImageSource source, Bitmap dest) throws ImageLoadException { ImageMetadata metadata = source.getImageMetadata(); boolean tryReuse = false; if (dest.isMutable()) { if (Build.VERSION.SDK_INT >= 19) { tryReuse = dest.getAllocationByteCount() >= metadata.getW() * metadata.getH() * 4; } else if (Build.VERSION.SDK_INT >= 11) { if (metadata.getFormat() == ImageFormat.JPEG || metadata.getFormat() == ImageFormat.PNG) { tryReuse = dest.getWidth() == metadata.getW() && dest.getHeight() == metadata.getH(); } } } if (tryReuse) { return source.loadBitmap(dest); } else { return new ReuseResult(loadBitmap(source), false); } } /** * Saving image to file * * @param src source image * @param fileName destination file name * @param format image format * @param quality jpeg quality * @throws ImageSaveException if it is unable to save image */ private static void save(Bitmap src, String fileName, Bitmap.CompressFormat format, int quality) throws ImageSaveException { FileOutputStream outputStream = null; try { outputStream = new FileOutputStream(fileName); src.compress(format, quality, outputStream); outputStream.close(); } catch (IOException e) { throw new ImageSaveException(e); } finally { if (outputStream != null) { try { outputStream.close(); } catch (IOException e1) { e1.printStackTrace(); } } } } /** * Saving image to byte array * * @param src source image * @param format image format * @param quality jpeg quality * @return saved image */ private static byte[] save(Bitmap src, Bitmap.CompressFormat format, int quality) { ByteArrayOutputStream outputStream = null; try { outputStream = new ByteArrayOutputStream(); src.compress(format, quality, outputStream); return outputStream.toByteArray(); } finally { if (outputStream != null) { try { outputStream.close(); } catch (IOException e1) { e1.printStackTrace(); } } } } /** * Calculating scale factor with limit of pixel amount * * @param metadata image metadata * @param maxPixels limit for pixels * @return scale factor */ private static int getScaleFactor(ImageMetadata metadata, int maxPixels) { int scale = 1; int scaledW = metadata.getW(); int scaledH = metadata.getH(); while (scaledW * scaledH > maxPixels) { scale *= 2; scaledH /= 2; scaledW /= 2; } return scale; } /** * Calculating scale factor with limit of pixel amount * * @param metadata image metadata * @param minH minimal height * @param minW minimal width * @return scale factor */ private static int getScaleFactor(ImageMetadata metadata, int minW, int minH) { int scale = 1; int scaledW = metadata.getW(); int scaledH = metadata.getH(); while (scaledW / 2 > minW && scaledH / 2 > minH) { scale *= 2; scaledH /= 2; scaledW /= 2; } return scale; } protected ImageLoading() { } }