package droidkit.graphics;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.media.ExifInterface;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import droidkit.io.IOUtils;
import droidkit.log.Logger;
/**
* @author Daniel Serdyukov
*/
public final class Bitmaps {
private static final int BITMAP_HEAD = 1024;
private static final double LN_2 = Math.log(2);
private Bitmaps() {
}
@NonNull
public static Bitmap scale(@NonNull Bitmap bitmap, int maxSize) {
final float factor = (float) maxSize / Math.max(bitmap.getWidth(), bitmap.getHeight());
final Matrix matrix = new Matrix();
matrix.postScale(factor, factor);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
}
@NonNull
public static Bitmap scale(@NonNull Bitmap bitmap, int width, int height) {
return Bitmap.createScaledBitmap(bitmap, width, height, false);
}
@NonNull
public static Bitmap round(@NonNull Bitmap bitmap, float radius) {
final int width = bitmap.getWidth();
final int height = bitmap.getHeight();
final Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(shader);
final RectF rect = new RectF(0.0f, 0.0f, width, height);
canvas.drawRoundRect(rect, radius, radius, paint);
return output;
}
@NonNull
public static Bitmap circle(@NonNull Bitmap bitmap) {
final int size = Math.min(bitmap.getWidth(), bitmap.getHeight());
final Bitmap output = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, size, size);
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
final float bounds = (float) size / 2;
canvas.drawCircle(bounds, bounds, bounds, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
@Nullable
public static Bitmap decodeFile(@NonNull String filePath, int hwSize) {
return decodeFile(filePath, hwSize, true);
}
@Nullable
public static Bitmap decodeFile(@NonNull String filePath, int hwSize, boolean exif) {
final Bitmap bitmap = decodeFileInternal(filePath, hwSize);
if (bitmap != null && exif) {
return applyExif(bitmap, filePath);
}
return bitmap;
}
@Nullable
public static Bitmap decodeStream(@NonNull InputStream stream, int hwSize) {
return decodeStream(stream, null, hwSize);
}
@Nullable
public static Bitmap decodeStream(@NonNull InputStream stream, Rect outPadding, int hwSize) {
if (hwSize > 0) {
final InputStream localIn = new BufferedInputStream(stream);
try {
final BitmapFactory.Options ops = new BitmapFactory.Options();
ops.inJustDecodeBounds = true;
localIn.mark(BITMAP_HEAD);
BitmapFactory.decodeStream(localIn, outPadding, ops);
ops.inSampleSize = calculateInSampleSize(ops, hwSize);
ops.inJustDecodeBounds = false;
localIn.reset();
return BitmapFactory.decodeStream(localIn, outPadding, ops);
} catch (IOException e) {
Logger.error(e);
} finally {
IOUtils.closeQuietly(localIn);
}
return null;
}
return BitmapFactory.decodeStream(stream);
}
public static int calculateInSampleSize(BitmapFactory.Options ops, int hwSize) {
final int outHeight = ops.outHeight;
final int outWidth = ops.outWidth;
if (outWidth > hwSize || outHeight > hwSize) {
final double ratio = Math.max(
Math.round((double) outWidth / (double) hwSize),
Math.round((double) outHeight / (double) hwSize)
);
return ratio > 0 ? (int) Math.pow(2, Math.floor(Math.log(ratio) / LN_2)) : 1;
}
return 1;
}
@Nullable
private static Bitmap decodeFileInternal(String filePath, int hwSize) {
if (hwSize > 0) {
final BitmapFactory.Options ops = new BitmapFactory.Options();
ops.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, ops);
ops.inSampleSize = calculateInSampleSize(ops, hwSize);
ops.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(filePath, ops);
}
return BitmapFactory.decodeFile(filePath);
}
@Nullable
private static Bitmap applyExif(@NonNull Bitmap bitmap, String exifFilePath) {
final int orientation = getExifOrientation(exifFilePath);
if (orientation == ExifInterface.ORIENTATION_NORMAL
|| orientation == ExifInterface.ORIENTATION_UNDEFINED) {
return bitmap;
}
try {
return Bitmap.createBitmap(
bitmap, 0, 0,
bitmap.getWidth(), bitmap.getHeight(),
getExifMatrix(orientation), true
);
} finally {
bitmap.recycle();
}
}
private static int getExifOrientation(String filePath) {
try {
return new ExifInterface(filePath).getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL
);
} catch (IOException e) {
Logger.error(e);
}
return ExifInterface.ORIENTATION_UNDEFINED;
}
@NonNull
private static Matrix getExifMatrix(int orientation) {
final Matrix matrix = new Matrix();
if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
matrix.setRotate(180);
} else if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
matrix.setRotate(90);
} else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
matrix.setRotate(-90);
}
return matrix;
}
}