BVB Source Codes

Luban Show Luban.java Source code

Return Download Luban: download Luban.java Source code - Download Luban Source code - Type:.java
  1. package top.zibin.luban;
  2.  
  3. import android.content.Context;
  4. import android.graphics.Bitmap;
  5. import android.graphics.BitmapFactory;
  6. import android.graphics.Matrix;
  7. import android.media.ExifInterface;
  8. import android.support.annotation.NonNull;
  9. import android.text.TextUtils;
  10. import android.util.Log;
  11.  
  12. import java.io.ByteArrayOutputStream;
  13. import java.io.File;
  14. import java.io.FileOutputStream;
  15. import java.io.IOException;
  16.  
  17. import rx.Observable;
  18. import rx.android.schedulers.AndroidSchedulers;
  19. import rx.functions.Action1;
  20. import rx.functions.Func1;
  21. import rx.schedulers.Schedulers;
  22.  
  23. import static top.zibin.luban.Preconditions.checkNotNull;
  24.  
  25. public class Luban {
  26.  
  27.     private static final int FIRST_GEAR = 1;
  28.     public static final int THIRD_GEAR = 3;
  29.  
  30.     private static final String TAG = "Luban";
  31.     private static String DEFAULT_DISK_CACHE_DIR = "luban_disk_cache";
  32.  
  33.     private static volatile Luban INSTANCE;
  34.  
  35.     private final File mCacheDir;
  36.  
  37.     private OnCompressListener compressListener;
  38.     private File mFile;
  39.     private int gear = THIRD_GEAR;
  40.     private String filename;
  41.  
  42.     private Luban(File cacheDir) {
  43.         mCacheDir = cacheDir;
  44.     }
  45.  
  46.     /**
  47.      * Returns a directory with a default name in the private cache directory of the application to use to store
  48.      * retrieved media and thumbnails.
  49.      *
  50.      * @param context A context.
  51.      * @see #getPhotoCacheDir(android.content.Context, String)
  52.      */
  53.     private static synchronized File getPhotoCacheDir(Context context) {
  54.         return getPhotoCacheDir(context, Luban.DEFAULT_DISK_CACHE_DIR);
  55.     }
  56.  
  57.     /**
  58.      * Returns a directory with the given name in the private cache directory of the application to use to store
  59.      * retrieved media and thumbnails.
  60.      *
  61.      * @param context   A context.
  62.      * @param cacheName The name of the subdirectory in which to store the cache.
  63.      * @see #getPhotoCacheDir(android.content.Context)
  64.      */
  65.     private static File getPhotoCacheDir(Context context, String cacheName) {
  66.         File cacheDir = context.getCacheDir();
  67.         if (cacheDir != null) {
  68.             File result = new File(cacheDir, cacheName);
  69.             if (!result.mkdirs() && (!result.exists() || !result.isDirectory())) {
  70.                 // File wasn't able to create a directory, or the result exists but not a directory
  71.                 return null;
  72.             }
  73.  
  74.             File noMedia = new File(cacheDir + "/.nomedia");
  75.             if (!noMedia.mkdirs() && (!noMedia.exists() || !noMedia.isDirectory())) {
  76.                 return null;
  77.             }
  78.  
  79.             return result;
  80.         }
  81.         if (Log.isLoggable(TAG, Log.ERROR)) {
  82.             Log.e(TAG, "default disk cache dir is null");
  83.         }
  84.         return null;
  85.     }
  86.  
  87.     public static Luban get(Context context) {
  88.         if (INSTANCE == null) INSTANCE = new Luban(Luban.getPhotoCacheDir(context));
  89.         return INSTANCE;
  90.     }
  91.  
  92.     public Luban launch() {
  93.         checkNotNull(mFile, "the image file cannot be null, please call .load() before this method!");
  94.  
  95.         if (compressListener != null) compressListener.onStart();
  96.  
  97.         if (gear == Luban.FIRST_GEAR)
  98.             Observable.just(mFile)
  99.                     .map(new Func1<File, File>() {
  100.                         @Override
  101.                         public File call(File file) {
  102.                             return firstCompress(file);
  103.                         }
  104.                     })
  105.                     .subscribeOn(Schedulers.io())
  106.                     .observeOn(AndroidSchedulers.mainThread())
  107.                     .doOnError(new Action1<Throwable>() {
  108.                         @Override
  109.                         public void call(Throwable throwable) {
  110.                             if (compressListener != null) compressListener.onError(throwable);
  111.                         }
  112.                     })
  113.                     .onErrorResumeNext(Observable.<File>empty())
  114.                     .filter(new Func1<File, Boolean>() {
  115.                         @Override
  116.                         public Boolean call(File file) {
  117.                             return file != null;
  118.                         }
  119.                     })
  120.                     .subscribe(new Action1<File>() {
  121.                         @Override
  122.                         public void call(File file) {
  123.                             if (compressListener != null) compressListener.onSuccess(file);
  124.                         }
  125.                     });
  126.         else if (gear == Luban.THIRD_GEAR)
  127.             Observable.just(mFile)
  128.                     .map(new Func1<File, File>() {
  129.                         @Override
  130.                         public File call(File file) {
  131.                             return thirdCompress(file);
  132.                         }
  133.                     })
  134.                     .subscribeOn(Schedulers.io())
  135.                     .observeOn(AndroidSchedulers.mainThread())
  136.                     .doOnError(new Action1<Throwable>() {
  137.                         @Override
  138.                         public void call(Throwable throwable) {
  139.                             if (compressListener != null) compressListener.onError(throwable);
  140.                         }
  141.                     })
  142.                     .onErrorResumeNext(Observable.<File>empty())
  143.                     .filter(new Func1<File, Boolean>() {
  144.                         @Override
  145.                         public Boolean call(File file) {
  146.                             return file != null;
  147.                         }
  148.                     })
  149.                     .subscribe(new Action1<File>() {
  150.                         @Override
  151.                         public void call(File file) {
  152.                             if (compressListener != null) compressListener.onSuccess(file);
  153.                         }
  154.                     });
  155.  
  156.         return this;
  157.     }
  158.  
  159.     public Luban load(File file) {
  160.         mFile = file;
  161.         return this;
  162.     }
  163.  
  164.     public Luban setCompressListener(OnCompressListener listener) {
  165.         compressListener = listener;
  166.         return this;
  167.     }
  168.  
  169.     public Luban putGear(int gear) {
  170.         this.gear = gear;
  171.         return this;
  172.     }
  173.  
  174.     /**
  175.      * @deprecated
  176.      */
  177.     public Luban setFilename(String filename) {
  178.         this.filename = filename;
  179.         return this;
  180.     }
  181.  
  182.     public Observable<File> asObservable() {
  183.         if (gear == FIRST_GEAR)
  184.             return Observable.just(mFile).map(new Func1<File, File>() {
  185.                 @Override
  186.                 public File call(File file) {
  187.                     return firstCompress(file);
  188.                 }
  189.             });
  190.         else if (gear == THIRD_GEAR)
  191.             return Observable.just(mFile).map(new Func1<File, File>() {
  192.                 @Override
  193.                 public File call(File file) {
  194.                     return thirdCompress(file);
  195.                 }
  196.             });
  197.         else return Observable.empty();
  198.     }
  199.  
  200.     private File thirdCompress(@NonNull File file) {
  201.         String thumb = mCacheDir.getAbsolutePath() + File.separator +
  202.                 (TextUtils.isEmpty(filename) ? System.currentTimeMillis() : filename) + ".jpg";
  203.  
  204.         double size;
  205.         String filePath = file.getAbsolutePath();
  206.  
  207.         int angle = getImageSpinAngle(filePath);
  208.         int width = getImageSize(filePath)[0];
  209.         int height = getImageSize(filePath)[1];
  210.         int thumbW = width % 2 == 1 ? width + 1 : width;
  211.         int thumbH = height % 2 == 1 ? height + 1 : height;
  212.  
  213.         width = thumbW > thumbH ? thumbH : thumbW;
  214.         height = thumbW > thumbH ? thumbW : thumbH;
  215.  
  216.         double scale = ((double) width / height);
  217.  
  218.         if (scale <= 1 && scale > 0.5625) {
  219.             if (height < 1664) {
  220.                 if (file.length() / 1024 < 150) return file;
  221.  
  222.                 size = (width * height) / Math.pow(1664, 2) * 150;
  223.                 size = size < 60 ? 60 : size;
  224.             } else if (height >= 1664 && height < 4990) {
  225.                 thumbW = width / 2;
  226.                 thumbH = height / 2;
  227.                 size = (thumbW * thumbH) / Math.pow(2495, 2) * 300;
  228.                 size = size < 60 ? 60 : size;
  229.             } else if (height >= 4990 && height < 10240) {
  230.                 thumbW = width / 4;
  231.                 thumbH = height / 4;
  232.                 size = (thumbW * thumbH) / Math.pow(2560, 2) * 300;
  233.                 size = size < 100 ? 100 : size;
  234.             } else {
  235.                 int multiple = height / 1280 == 0 ? 1 : height / 1280;
  236.                 thumbW = width / multiple;
  237.                 thumbH = height / multiple;
  238.                 size = (thumbW * thumbH) / Math.pow(2560, 2) * 300;
  239.                 size = size < 100 ? 100 : size;
  240.             }
  241.         } else if (scale <= 0.5625 && scale > 0.5) {
  242.             if (height < 1280 && file.length() / 1024 < 200) return file;
  243.  
  244.             int multiple = height / 1280 == 0 ? 1 : height / 1280;
  245.             thumbW = width / multiple;
  246.             thumbH = height / multiple;
  247.             size = (thumbW * thumbH) / (1440.0 * 2560.0) * 400;
  248.             size = size < 100 ? 100 : size;
  249.         } else {
  250.             int multiple = (int) Math.ceil(height / (1280.0 / scale));
  251.             thumbW = width / multiple;
  252.             thumbH = height / multiple;
  253.             size = ((thumbW * thumbH) / (1280.0 * (1280 / scale))) * 500;
  254.             size = size < 100 ? 100 : size;
  255.         }
  256.  
  257.         return compress(filePath, thumb, thumbW, thumbH, angle, (long) size);
  258.     }
  259.  
  260.     private File firstCompress(@NonNull File file) {
  261.         int minSize = 60;
  262.         int longSide = 720;
  263.         int shortSide = 1280;
  264.  
  265.         String filePath = file.getAbsolutePath();
  266.         String thumbFilePath = mCacheDir.getAbsolutePath() + File.separator +
  267.                 (TextUtils.isEmpty(filename) ? System.currentTimeMillis() : filename) + ".jpg";
  268.  
  269.         long size = 0;
  270.         long maxSize = file.length() / 5;
  271.  
  272.         int angle = getImageSpinAngle(filePath);
  273.         int[] imgSize = getImageSize(filePath);
  274.         int width = 0, height = 0;
  275.         if (imgSize[0] <= imgSize[1]) {
  276.             double scale = (double) imgSize[0] / (double) imgSize[1];
  277.             if (scale <= 1.0 && scale > 0.5625) {
  278.                 width = imgSize[0] > shortSide ? shortSide : imgSize[0];
  279.                 height = width * imgSize[1] / imgSize[0];
  280.                 size = minSize;
  281.             } else if (scale <= 0.5625) {
  282.                 height = imgSize[1] > longSide ? longSide : imgSize[1];
  283.                 width = height * imgSize[0] / imgSize[1];
  284.                 size = maxSize;
  285.             }
  286.         } else {
  287.             double scale = (double) imgSize[1] / (double) imgSize[0];
  288.             if (scale <= 1.0 && scale > 0.5625) {
  289.                 height = imgSize[1] > shortSide ? shortSide : imgSize[1];
  290.                 width = height * imgSize[0] / imgSize[1];
  291.                 size = minSize;
  292.             } else if (scale <= 0.5625) {
  293.                 width = imgSize[0] > longSide ? longSide : imgSize[0];
  294.                 height = width * imgSize[1] / imgSize[0];
  295.                 size = maxSize;
  296.             }
  297.         }
  298.  
  299.         return compress(filePath, thumbFilePath, width, height, angle, size);
  300.     }
  301.  
  302.     /**
  303.      * obtain the image's width and height
  304.      *
  305.      * @param imagePath the path of image
  306.      */
  307.     public int[] getImageSize(String imagePath) {
  308.         int[] res = new int[2];
  309.  
  310.         BitmapFactory.Options options = new BitmapFactory.Options();
  311.         options.inJustDecodeBounds = true;
  312.         options.inSampleSize = 1;
  313.         BitmapFactory.decodeFile(imagePath, options);
  314.  
  315.         res[0] = options.outWidth;
  316.         res[1] = options.outHeight;
  317.  
  318.         return res;
  319.     }
  320.  
  321.     /**
  322.      * obtain the thumbnail that specify the size
  323.      *
  324.      * @param imagePath the target image path
  325.      * @param width     the width of thumbnail
  326.      * @param height    the height of thumbnail
  327.      * @return {@link Bitmap}
  328.      */
  329.     private Bitmap compress(String imagePath, int width, int height) {
  330.         BitmapFactory.Options options = new BitmapFactory.Options();
  331.         options.inJustDecodeBounds = true;
  332.         BitmapFactory.decodeFile(imagePath, options);
  333.  
  334.         int outH = options.outHeight;
  335.         int outW = options.outWidth;
  336.         int inSampleSize = 1;
  337.  
  338.         if (outH > height || outW > width) {
  339.             int halfH = outH / 2;
  340.             int halfW = outW / 2;
  341.  
  342.             while ((halfH / inSampleSize) > height && (halfW / inSampleSize) > width) {
  343.                 inSampleSize *= 2;
  344.             }
  345.         }
  346.  
  347.         options.inSampleSize = inSampleSize;
  348.  
  349.         options.inJustDecodeBounds = false;
  350.  
  351.         int heightRatio = (int) Math.ceil(options.outHeight / (float) height);
  352.         int widthRatio = (int) Math.ceil(options.outWidth / (float) width);
  353.  
  354.         if (heightRatio > 1 || widthRatio > 1) {
  355.             if (heightRatio > widthRatio) {
  356.                 options.inSampleSize = heightRatio;
  357.             } else {
  358.                 options.inSampleSize = widthRatio;
  359.             }
  360.         }
  361.         options.inJustDecodeBounds = false;
  362.  
  363.         return BitmapFactory.decodeFile(imagePath, options);
  364.     }
  365.  
  366.     /**
  367.      * obtain the image rotation angle
  368.      *
  369.      * @param path path of target image
  370.      */
  371.     private int getImageSpinAngle(String path) {
  372.         int degree = 0;
  373.         try {
  374.             ExifInterface exifInterface = new ExifInterface(path);
  375.             int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
  376.             switch (orientation) {
  377.                 case ExifInterface.ORIENTATION_ROTATE_90:
  378.                     degree = 90;
  379.                     break;
  380.                 case ExifInterface.ORIENTATION_ROTATE_180:
  381.                     degree = 180;
  382.                     break;
  383.                 case ExifInterface.ORIENTATION_ROTATE_270:
  384.                     degree = 270;
  385.                     break;
  386.             }
  387.         } catch (IOException e) {
  388.             e.printStackTrace();
  389.         }
  390.         return degree;
  391.     }
  392.  
  393.     /**
  394.      * 指定参数压缩图片
  395.      * create the thumbnail with the true rotate angle
  396.      *
  397.      * @param largeImagePath the big image path
  398.      * @param thumbFilePath  the thumbnail path
  399.      * @param width          width of thumbnail
  400.      * @param height         height of thumbnail
  401.      * @param angle          rotation angle of thumbnail
  402.      * @param size           the file size of image
  403.      */
  404.     private File compress(String largeImagePath, String thumbFilePath, int width, int height, int angle, long size) {
  405.         Bitmap thbBitmap = compress(largeImagePath, width, height);
  406.  
  407.         thbBitmap = rotatingImage(angle, thbBitmap);
  408.  
  409.         return saveImage(thumbFilePath, thbBitmap, size);
  410.     }
  411.  
  412.     /**
  413.      * 旋转图片
  414.      * rotate the image with specified angle
  415.      *
  416.      * @param angle  the angle will be rotating 旋转的角度
  417.      * @param bitmap target image               目标图片
  418.      */
  419.     private static Bitmap rotatingImage(int angle, Bitmap bitmap) {
  420.         //rotate image
  421.         Matrix matrix = new Matrix();
  422.         matrix.postRotate(angle);
  423.  
  424.         //create a new image
  425.         return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
  426.     }
  427.  
  428.     /**
  429.      * 保存图片到指定路径
  430.      * Save image with specified size
  431.      *
  432.      * @param filePath the image file save path 储存路径
  433.      * @param bitmap   the image what be save   目标图片
  434.      * @param size     the file size of image   期望大小
  435.      */
  436.     private File saveImage(String filePath, Bitmap bitmap, long size) {
  437.         checkNotNull(bitmap, TAG + "bitmap cannot be null");
  438.  
  439.         File result = new File(filePath.substring(0, filePath.lastIndexOf("/")));
  440.  
  441.         if (!result.exists() && !result.mkdirs()) return null;
  442.  
  443.         ByteArrayOutputStream stream = new ByteArrayOutputStream();
  444.         int options = 100;
  445.         bitmap.compress(Bitmap.CompressFormat.JPEG, options, stream);
  446.  
  447.         while (stream.toByteArray().length / 1024 > size && options > 6) {
  448.             stream.reset();
  449.             options -= 6;
  450.             bitmap.compress(Bitmap.CompressFormat.JPEG, options, stream);
  451.         }
  452.         bitmap.recycle();
  453.  
  454.         try {
  455.             FileOutputStream fos = new FileOutputStream(filePath);
  456.             fos.write(stream.toByteArray());
  457.             fos.flush();
  458.             fos.close();
  459.             stream.close();
  460.         } catch (IOException e) {
  461.             e.printStackTrace();
  462.         }
  463.  
  464.         return new File(filePath);
  465.     }
  466. }
downloadLuban.java Source code - Download Luban Source code
Related Source Codes/Software:
vue-router - The official router for Vue.js. ... 2017-01-11
socketcluster - Highly scalable realtime framework ... 2017-01-11
forum - Blue light (the Lantern) the official BBS 2017-01-11
tensorflow-zh - Google new open source ai system TensorFlow Chines... 2017-01-11
react-native-elements - React Native Elements UI Toolkit 2017-01-11
show-me-the-code - Python's book and a small program every day 2017-01-11
places - 2017-01-11
buck - A fast build system that encourages the creation o... 2017-01-11
react-native-web - React Native for We 2017-01-11
awesome-python-cn - Python resources of Chinese version, include: Web ... 2017-01-11
mongo-express - Web-based MongoDB admin interface, written with No... 2017-05-17
XCL-Charts - Android charting libraries (XCL-Charts is a free c... 2017-05-17
scrollMonitor - A simple and fast API to monitor elements as you s... 2017-05-16
XCDYouTubeKit - YouTube video player for iOS, tvOS and macOS 2017-05-16
rails-erd - Generate Entity-Relationship Diagrams for Rails ap... 2017-05-16
np - A better `npm publish` 2017-05-16
android - Smartisan open source code for full build.(repo ma... 2017-05-16
word_cloud - A little word cloud generator in Pytho 2017-05-16
mobileplayer-ios - 2017-05-16
Destroy-Windows-10-Spying - Destroy Windows Spying tool ... 2017-05-15

 Back to top