package com.ghnor.flora.core; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import com.ghnor.flora.common.BitmapOptionsCompat; import com.ghnor.flora.common.Degrees; import com.ghnor.flora.common.FileUtil; import com.ghnor.flora.common.Logger; import com.ghnor.flora.spec.CompressSpec; import com.ghnor.flora.spec.decoration.Decoration; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.concurrent.Callable; /** * Created by ghnor on 2017/7/8. * [email protected] */ public abstract class CompressCallable<T> implements Callable<String> { CompressSpec mCompressSpec; T mT; public CompressCallable(T t, CompressSpec compressSpec) { this.mT = t; this.mCompressSpec = compressSpec; } @Override public String call() throws Exception { BitmapFactory.Options decodeOptions = compressPre(mCompressSpec); Bitmap bm = call(mT, decodeOptions); bm = Degrees.handle(bm, mT); bm = compressOver(bm, mCompressSpec); return compress(bm, mCompressSpec); } protected BitmapFactory.Options compressPre(CompressSpec compressSpec) { BitmapFactory.Options decodeOptions = BitmapOptionsCompat.getDefaultDecodeOptions(); decodeOptions.inPreferredConfig = compressSpec.options.bitmapConfig; decodeOptions.inSampleSize = compressSpec.options.inSampleSize; return decodeOptions; } protected Bitmap compressOver(Bitmap bitmap, CompressSpec compressSpec) { for (Decoration decoration : compressSpec.decorations) { bitmap = decoration.onDraw(bitmap); } return bitmap; } protected Bitmap matrixCompress(Bitmap bitmap, int inSampleSize) { if (bitmap == null) return null; int bitmapWidth = bitmap.getWidth(); int bitmapHeight = bitmap.getHeight(); if (inSampleSize >= 1) { Matrix matrix = new Matrix(); matrix.postScale(1.0f / (float) inSampleSize, 1.0f / (float) inSampleSize); Bitmap result = Bitmap.createBitmap(bitmap, 0, 0, bitmapWidth, bitmapHeight, matrix, true); return result; } else { return bitmap; } } protected String compress(Bitmap bitmap, CompressSpec compressSpec) { int quality = compressSpec.options.quality; String outfile = null; float size = compressSpec.options.maxSize; if (quality < 0 || quality > 100) quality = CompressSpec.DEFAULT_QUALITY; if (bitmap.hasAlpha()) { outfile = FileUtil.generateCompressOutfileFormatPNG(compressSpec.dir, null).getAbsolutePath(); } else { outfile = FileUtil.generateCompressOutfileFormatJPEG(compressSpec.dir, null).getAbsolutePath(); } boolean isSuccess = compress(bitmap, outfile, quality); if (size > 0 && isSuccess) { float outfileSize = (float) FileUtil.getSizeInBytes(outfile) / (float) 1024; while (outfileSize > size) { if (quality <= 25) break; quality -= 5; isSuccess = compress(bitmap, outfile, quality); if (!isSuccess) break; outfileSize = (float) FileUtil.getSizeInBytes(outfile) / (float) 1024; } } Logger.i("final compress quality: " + quality); if (bitmap != mT) { bitmap.recycle(); bitmap = null; System.gc(); System.runFinalization(); } return outfile; } protected boolean compress(Bitmap bitmap, String outfile, int quality) { if (bitmap == null || bitmap.isRecycled()) return false; if (bitmap.hasAlpha()) { return compress(bitmap, outfile, quality, Bitmap.CompressFormat.PNG); } else { return compress(bitmap, outfile, quality, Bitmap.CompressFormat.JPEG); } } protected boolean compress(Bitmap bitmap, String outfile, int quality, Bitmap.CompressFormat format) { boolean isSuccess = false; FileOutputStream fos = null; try { fos = new FileOutputStream(outfile); isSuccess = bitmap.compress(format, quality, fos); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (Exception e) { //avoid v6.0+ occur crash without permission e.printStackTrace(); } finally { try { fos.close(); fos = null; } catch (IOException e) { e.printStackTrace(); } catch (NullPointerException e) { e.printStackTrace(); } } return isSuccess; } abstract Bitmap call(T t, BitmapFactory.Options options) throws Exception; }