ImageDecoder.java

/*
 * Decompiled with CFR 0_132.
 * 
 * Could not load the following classes:
 *  android.content.res.Resources
 *  android.graphics.Bitmap
 *  android.graphics.Bitmap$CompressFormat
 *  android.graphics.Bitmap$Config
 *  android.graphics.BitmapFactory
 *  android.graphics.BitmapFactory$Options
 *  android.graphics.Canvas
 *  android.graphics.Matrix
 *  android.graphics.Movie
 *  android.graphics.Paint
 *  android.graphics.PorterDuff
 *  android.graphics.PorterDuff$Mode
 *  android.graphics.PorterDuffXfermode
 *  android.graphics.RectF
 *  android.graphics.Xfermode
 *  android.graphics.drawable.Drawable
 *  android.media.ExifInterface
 *  android.os.Build
 *  android.os.Build$VERSION
 *  android.widget.ImageView
 */
package org.xutils.image;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Movie;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Xfermode;
import android.graphics.drawable.Drawable;
import android.media.ExifInterface;
import android.os.Build;
import android.widget.ImageView;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import org.xutils.cache.DiskCacheEntity;
import org.xutils.cache.DiskCacheFile;
import org.xutils.cache.LruDiskCache;
import org.xutils.common.Callback;
import org.xutils.common.task.PriorityExecutor;
import org.xutils.common.util.IOUtil;
import org.xutils.common.util.LogUtil;
import org.xutils.image.GifDrawable;
import org.xutils.image.ImageOptions;
import org.xutils.image.ReusableBitmapDrawable;
import org.xutils.x;

public final class ImageDecoder {
    private static final int BITMAP_DECODE_MAX_WORKER;
    private static final AtomicInteger bitmapDecodeWorker;
    private static final Object bitmapDecodeLock;
    private static final Object gifDecodeLock;
    private static final byte[] GIF_HEADER;
    private static final Executor THUMB_CACHE_EXECUTOR;
    private static final LruDiskCache THUMB_CACHE;
    private static final boolean supportWebP;

    private ImageDecoder() {
    }

    static void clearCacheFiles() {
        THUMB_CACHE.clearCacheFiles();
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    static Drawable decodeFileWithLock(final File file, final ImageOptions options, Callback.Cancelable cancelable) throws IOException {
        if (file == null || !file.exists() || file.length() < 1L) {
            return null;
        }
        if (cancelable != null && cancelable.isCancelled()) {
            throw new Callback.CancelledException("cancelled during decode image");
        }
        Object result = null;
        if (!options.isIgnoreGif() && ImageDecoder.isGif(file)) {
            Movie movie = null;
            Object object = gifDecodeLock;
            synchronized (object) {
                movie = ImageDecoder.decodeGif(file, options, cancelable);
            }
            if (movie != null) {
                result = new GifDrawable(movie, (int)file.length());
            }
        } else {
            Bitmap bitmap;
            Object finalBitmap;
            bitmap = null;
            try {
                Object object = bitmapDecodeLock;
                synchronized (object) {
                    while (!(bitmapDecodeWorker.get() < BITMAP_DECODE_MAX_WORKER || cancelable != null && cancelable.isCancelled())) {
                        try {
                            bitmapDecodeLock.wait();
                        }
                        catch (InterruptedException iex2) {
                            throw new Callback.CancelledException("cancelled during decode image");
                        }
                        catch (Throwable iex2) {
                        }
                    }
                }
                if (cancelable != null && cancelable.isCancelled()) {
                    throw new Callback.CancelledException("cancelled during decode image");
                }
                bitmapDecodeWorker.incrementAndGet();
                if (options.isCompress()) {
                    bitmap = ImageDecoder.getThumbCache(file, options);
                }
                if (bitmap == null && (bitmap = ImageDecoder.decodeBitmap(file, options, cancelable)) != null && options.isCompress()) {
                    finalBitmap = bitmap;
                    THUMB_CACHE_EXECUTOR.execute(new Runnable((Bitmap)finalBitmap){
                        final /* synthetic */ Bitmap val$finalBitmap;
                        {
                            this.val$finalBitmap = bitmap;
                        }

                        @Override
                        public void run() {
                            ImageDecoder.saveThumbCache(file, options, this.val$finalBitmap);
                        }
                    });
                }
            }
            finally {
                bitmapDecodeWorker.decrementAndGet();
                finalBitmap = bitmapDecodeLock;
                synchronized (finalBitmap) {
                    bitmapDecodeLock.notifyAll();
                }
            }
            if (bitmap != null) {
                result = new ReusableBitmapDrawable(x.app().getResources(), bitmap);
            }
        }
        return result;
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    public static boolean isGif(File file) {
        boolean bl;
        FileInputStream in = null;
        try {
            in = new FileInputStream(file);
            byte[] header = IOUtil.readBytes(in, 0L, 3);
            bl = Arrays.equals(GIF_HEADER, header);
        }
        catch (Throwable ex) {
            try {
                LogUtil.e(ex.getMessage(), ex);
            }
            catch (Throwable throwable) {
                IOUtil.closeQuietly(in);
                throw throwable;
            }
            IOUtil.closeQuietly(in);
        }
        IOUtil.closeQuietly(in);
        return bl;
        return false;
    }

    public static Bitmap decodeBitmap(File file, ImageOptions options, Callback.Cancelable cancelable) throws IOException {
        if (file == null || !file.exists() || file.length() < 1L) {
            return null;
        }
        if (options == null) {
            options = ImageOptions.DEFAULT;
        }
        if (options.getMaxWidth() <= 0 || options.getMaxHeight() <= 0) {
            options.optimizeMaxSize(null);
        }
        Bitmap result = null;
        try {
            if (cancelable != null && cancelable.isCancelled()) {
                throw new Callback.CancelledException("cancelled during decode image");
            }
            BitmapFactory.Options bitmapOps = new BitmapFactory.Options();
            bitmapOps.inJustDecodeBounds = true;
            bitmapOps.inPurgeable = true;
            bitmapOps.inInputShareable = true;
            BitmapFactory.decodeFile((String)file.getAbsolutePath(), (BitmapFactory.Options)bitmapOps);
            bitmapOps.inJustDecodeBounds = false;
            bitmapOps.inPreferredConfig = options.getConfig();
            int rotateAngle = 0;
            int rawWidth = bitmapOps.outWidth;
            int rawHeight = bitmapOps.outHeight;
            int optionWith = options.getWidth();
            int optionHeight = options.getHeight();
            if (options.isAutoRotate() && (rotateAngle = ImageDecoder.getRotateAngle(file.getAbsolutePath())) / 90 % 2 == 1) {
                rawWidth = bitmapOps.outHeight;
                rawHeight = bitmapOps.outWidth;
            }
            if (!options.isCrop() && optionWith > 0 && optionHeight > 0) {
                if (rotateAngle / 90 % 2 == 1) {
                    bitmapOps.outWidth = optionHeight;
                    bitmapOps.outHeight = optionWith;
                } else {
                    bitmapOps.outWidth = optionWith;
                    bitmapOps.outHeight = optionHeight;
                }
            }
            bitmapOps.inSampleSize = ImageDecoder.calculateSampleSize(rawWidth, rawHeight, options.getMaxWidth(), options.getMaxHeight());
            if (cancelable != null && cancelable.isCancelled()) {
                throw new Callback.CancelledException("cancelled during decode image");
            }
            Bitmap bitmap = null;
            bitmap = BitmapFactory.decodeFile((String)file.getAbsolutePath(), (BitmapFactory.Options)bitmapOps);
            if (bitmap == null) {
                throw new IOException("decode image error");
            }
            if (cancelable != null && cancelable.isCancelled()) {
                throw new Callback.CancelledException("cancelled during decode image");
            }
            if (rotateAngle != 0) {
                bitmap = ImageDecoder.rotate(bitmap, rotateAngle, true);
            }
            if (cancelable != null && cancelable.isCancelled()) {
                throw new Callback.CancelledException("cancelled during decode image");
            }
            if (options.isCrop() && optionWith > 0 && optionHeight > 0) {
                bitmap = ImageDecoder.cut2ScaleSize(bitmap, optionWith, optionHeight, true);
            }
            if (bitmap == null) {
                throw new IOException("decode image error");
            }
            if (cancelable != null && cancelable.isCancelled()) {
                throw new Callback.CancelledException("cancelled during decode image");
            }
            if (options.isCircular()) {
                bitmap = ImageDecoder.cut2Circular(bitmap, true);
            } else if (options.getRadius() > 0) {
                bitmap = ImageDecoder.cut2RoundCorner(bitmap, options.getRadius(), options.isSquare(), true);
            } else if (options.isSquare()) {
                bitmap = ImageDecoder.cut2Square(bitmap, true);
            }
            if (bitmap == null) {
                throw new IOException("decode image error");
            }
            result = bitmap;
        }
        catch (IOException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            LogUtil.e(ex.getMessage(), ex);
            result = null;
        }
        return result;
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     * Loose catch block
     * Enabled aggressive block sorting
     * Enabled unnecessary exception pruning
     * Enabled aggressive exception aggregation
     * Lifted jumps to return sites
     */
    public static Movie decodeGif(File file, ImageOptions options, Callback.Cancelable cancelable) throws IOException {
        if (file == null) return null;
        if (!file.exists()) return null;
        if (file.length() < 1L) {
            return null;
        }
        BufferedInputStream in = null;
        try {
            if (cancelable != null && cancelable.isCancelled()) {
                throw new Callback.CancelledException("cancelled during decode image");
            }
            int buffSize = 16384;
            in = new BufferedInputStream(new FileInputStream(file), buffSize);
            in.mark(buffSize);
            Movie movie = Movie.decodeStream((InputStream)in);
            if (movie == null) {
                throw new IOException("decode image error");
            }
            Movie movie2 = movie;
            IOUtil.closeQuietly(in);
            return movie2;
        }
        catch (IOException ex) {
            try {
                throw ex;
                catch (Throwable ex2) {
                    LogUtil.e(ex2.getMessage(), ex2);
                    Movie movie = null;
                    return movie;
                }
            }
            catch (Throwable throwable) {}
            throw throwable;
        }
        finally {
            IOUtil.closeQuietly(in);
        }
    }

    public static int calculateSampleSize(int rawWidth, int rawHeight, int maxWidth, int maxHeight) {
        int sampleSize = 1;
        if (rawWidth > maxWidth || rawHeight > maxHeight) {
            sampleSize = rawWidth > rawHeight ? Math.round((float)rawHeight / (float)maxHeight) : Math.round((float)rawWidth / (float)maxWidth);
            if (sampleSize < 1) {
                sampleSize = 1;
            }
            float totalPixels = rawWidth * rawHeight;
            float maxTotalPixels = maxWidth * maxHeight * 2;
            while (totalPixels / (float)(sampleSize * sampleSize) > maxTotalPixels) {
                ++sampleSize;
            }
        }
        return sampleSize;
    }

    public static Bitmap cut2Square(Bitmap source, boolean recycleSource) {
        int height;
        int width = source.getWidth();
        if (width == (height = source.getHeight())) {
            return source;
        }
        int squareWith = Math.min(width, height);
        Bitmap result = Bitmap.createBitmap((Bitmap)source, (int)((width - squareWith) / 2), (int)((height - squareWith) / 2), (int)squareWith, (int)squareWith);
        if (result != null) {
            if (recycleSource && result != source) {
                source.recycle();
                source = null;
            }
        } else {
            result = source;
        }
        return result;
    }

    public static Bitmap cut2Circular(Bitmap source, boolean recycleSource) {
        int width = source.getWidth();
        int height = source.getHeight();
        int diameter = Math.min(width, height);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        Bitmap result = Bitmap.createBitmap((int)diameter, (int)diameter, (Bitmap.Config)Bitmap.Config.ARGB_8888);
        if (result != null) {
            Canvas canvas = new Canvas(result);
            canvas.drawCircle((float)(diameter / 2), (float)(diameter / 2), (float)(diameter / 2), paint);
            paint.setXfermode((Xfermode)new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
            canvas.drawBitmap(source, (float)((diameter - width) / 2), (float)((diameter - height) / 2), paint);
            if (recycleSource) {
                source.recycle();
                source = null;
            }
        } else {
            result = source;
        }
        return result;
    }

    public static Bitmap cut2RoundCorner(Bitmap source, int radius, boolean isSquare, boolean recycleSource) {
        if (radius <= 0) {
            return source;
        }
        int sourceWidth = source.getWidth();
        int sourceHeight = source.getHeight();
        int targetWidth = sourceWidth;
        int targetHeight = sourceHeight;
        if (isSquare) {
            targetWidth = targetHeight = Math.min(sourceWidth, sourceHeight);
        }
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        Bitmap result = Bitmap.createBitmap((int)targetWidth, (int)targetHeight, (Bitmap.Config)Bitmap.Config.ARGB_8888);
        if (result != null) {
            Canvas canvas = new Canvas(result);
            RectF rect = new RectF(0.0f, 0.0f, (float)targetWidth, (float)targetHeight);
            canvas.drawRoundRect(rect, (float)radius, (float)radius, paint);
            paint.setXfermode((Xfermode)new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
            canvas.drawBitmap(source, (float)((targetWidth - sourceWidth) / 2), (float)((targetHeight - sourceHeight) / 2), paint);
            if (recycleSource) {
                source.recycle();
                source = null;
            }
        } else {
            result = source;
        }
        return result;
    }

    public static Bitmap cut2ScaleSize(Bitmap source, int dstWidth, int dstHeight, boolean recycleSource) {
        int width = source.getWidth();
        int height = source.getHeight();
        if (width == dstWidth && height == dstHeight) {
            return source;
        }
        Matrix m = new Matrix();
        int l = 0;
        int t = 0;
        int r = width;
        int b = height;
        float sx = (float)dstWidth / (float)width;
        float sy = (float)dstHeight / (float)height;
        if (sx > sy) {
            sy = sx;
            l = 0;
            r = width;
            t = (int)(((float)height - (float)dstHeight / sx) / 2.0f);
            b = (int)(((float)height + (float)dstHeight / sx) / 2.0f);
        } else {
            sx = sy;
            l = (int)(((float)width - (float)dstWidth / sx) / 2.0f);
            r = (int)(((float)width + (float)dstWidth / sx) / 2.0f);
            t = 0;
            b = height;
        }
        m.setScale(sx, sy);
        Bitmap result = Bitmap.createBitmap((Bitmap)source, (int)l, (int)t, (int)(r - l), (int)(b - t), (Matrix)m, (boolean)true);
        if (result != null) {
            if (recycleSource && result != source) {
                source.recycle();
                source = null;
            }
        } else {
            result = source;
        }
        return result;
    }

    public static Bitmap rotate(Bitmap source, int angle, boolean recycleSource) {
        Bitmap result = null;
        if (angle != 0) {
            Matrix m = new Matrix();
            m.setRotate((float)angle);
            try {
                result = Bitmap.createBitmap((Bitmap)source, (int)0, (int)0, (int)source.getWidth(), (int)source.getHeight(), (Matrix)m, (boolean)true);
            }
            catch (Throwable ex) {
                LogUtil.e(ex.getMessage(), ex);
            }
        }
        if (result != null) {
            if (recycleSource && result != source) {
                source.recycle();
                source = null;
            }
        } else {
            result = source;
        }
        return result;
    }

    public static int getRotateAngle(String filePath) {
        int angle = 0;
        try {
            ExifInterface exif = new ExifInterface(filePath);
            int orientation = exif.getAttributeInt("Orientation", 0);
            switch (orientation) {
                case 6: {
                    angle = 90;
                    break;
                }
                case 3: {
                    angle = 180;
                    break;
                }
                case 8: {
                    angle = 270;
                    break;
                }
                default: {
                    angle = 0;
                    break;
                }
            }
        }
        catch (Throwable ex) {
            LogUtil.e(ex.getMessage(), ex);
        }
        return angle;
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private static void saveThumbCache(File file, ImageOptions options, Bitmap thumbBitmap) {
        DiskCacheFile cacheFile;
        FileOutputStream out;
        block4 : {
            DiskCacheEntity entity = new DiskCacheEntity();
            entity.setKey(file.getAbsolutePath() + "@" + file.lastModified() + options.toString());
            cacheFile = null;
            out = null;
            try {
                cacheFile = THUMB_CACHE.createDiskCacheFile(entity);
                if (cacheFile == null) break block4;
                out = new FileOutputStream(cacheFile);
                thumbBitmap.compress(supportWebP ? Bitmap.CompressFormat.WEBP : Bitmap.CompressFormat.PNG, 80, (OutputStream)out);
                out.flush();
                cacheFile = cacheFile.commit();
            }
            catch (Throwable ex) {
                try {
                    IOUtil.deleteFileOrDir(cacheFile);
                    LogUtil.w(ex.getMessage(), ex);
                }
                catch (Throwable throwable) {
                    IOUtil.closeQuietly(cacheFile);
                    IOUtil.closeQuietly(out);
                    throw throwable;
                }
                IOUtil.closeQuietly(cacheFile);
                IOUtil.closeQuietly(out);
            }
        }
        IOUtil.closeQuietly(cacheFile);
        IOUtil.closeQuietly(out);
    }

    /*
     * WARNING - Removed try catching itself - possible behaviour change.
     */
    private static Bitmap getThumbCache(File file, ImageOptions options) {
        DiskCacheFile cacheFile;
        block5 : {
            Bitmap bitmap;
            cacheFile = null;
            try {
                cacheFile = THUMB_CACHE.getDiskCacheFile(file.getAbsolutePath() + "@" + file.lastModified() + options.toString());
                if (cacheFile == null || !cacheFile.exists()) break block5;
                BitmapFactory.Options bitmapOps = new BitmapFactory.Options();
                bitmapOps.inJustDecodeBounds = false;
                bitmapOps.inPurgeable = true;
                bitmapOps.inInputShareable = true;
                bitmapOps.inPreferredConfig = Bitmap.Config.ARGB_8888;
                bitmap = BitmapFactory.decodeFile((String)cacheFile.getAbsolutePath(), (BitmapFactory.Options)bitmapOps);
            }
            catch (Throwable ex) {
                try {
                    LogUtil.w(ex.getMessage(), ex);
                }
                catch (Throwable throwable) {
                    throw throwable;
                }
                finally {
                    IOUtil.closeQuietly(cacheFile);
                }
            }
            IOUtil.closeQuietly(cacheFile);
            return bitmap;
        }
        IOUtil.closeQuietly(cacheFile);
        return null;
    }

    static {
        bitmapDecodeWorker = new AtomicInteger(0);
        bitmapDecodeLock = new Object();
        gifDecodeLock = new Object();
        GIF_HEADER = new byte[]{71, 73, 70};
        THUMB_CACHE_EXECUTOR = new PriorityExecutor(1, true);
        THUMB_CACHE = LruDiskCache.getDiskCache("xUtils_img_thumb");
        supportWebP = Build.VERSION.SDK_INT > 16;
        int cpuCount = Runtime.getRuntime().availableProcessors();
        BITMAP_DECODE_MAX_WORKER = cpuCount > 4 ? 2 : 1;
    }

}