Glide4内存优化-尺寸优化

722 阅读2分钟

使用inSampleSize对Bitmap进行尺寸缩放,如果inSampleSize大于1,解码器会对原始的bitmap进行子采样图像,减少图像内存占用,例如inSampleSize为4,则采样后的图像宽高为原图像的1/4,而像素值为原图的1/16,内存占用为原图的1/16。而当inSampleSize的值小于等于1,会当作1处理,使用原图像。

调用Glide.into()会执行Downsampler.calculateScaling()对图片的尺寸进行缩放,首先计算出图片与View的宽高比,再根据缩放策略是省内存还是高品质,决定取宽高比的最大值还是最小值,但如果设备的Sdk小于等于23将不支持对wbmp文件进行下采样,不然会通过Integer.highestOneBit()将选取到的比例值四舍五入到最接近的2的幂值得到inSampleSize的值。若此时的缩放策略为省内存,并且inSampleSize值小于由裁剪策略得出的exactScaleFactor值,则会将inSampleSize值再增加一倍。

glide/library/src/main/java/com/bumptech/glide/load/resource/bitmap/Downsampler.java

private static void calculateScaling(
        ImageType imageType,
        ImageReader imageReader,
        DecodeCallbacks decodeCallbacks,
        BitmapPool bitmapPool,
        DownsampleStrategy downsampleStrategy,
        int degreesToRotate,
        int sourceWidth,
        int sourceHeight,
        int targetWidth,
        int targetHeight,
        BitmapFactory.Options options)
        throws IOException {
    // We can't downsample source content if we can't determine its dimensions.
    if (sourceWidth <= 0 || sourceHeight <= 0) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(
                    TAG,
                    "Unable to determine dimensions for: "
                            + imageType
                            + " with target ["
                            + targetWidth
                            + "x"
                            + targetHeight
                            + "]");
        }
        return;
    }

    int orientedSourceWidth = sourceWidth;
    int orientedSourceHeight = sourceHeight;
    
    //noinspection SuspiciousNameCombination

    //  如果我们将图像旋转 +-90 度,我们需要相应地进行下采样,使图像宽度减小到接近目标高度,图像高度减小到接近目标宽度。

    if (isRotationRequired(degreesToRotate)) {
        orientedSourceWidth = sourceHeight;
        orientedSourceHeight = sourceWidth;
    }

    final float exactScaleFactor =
            downsampleStrategy.getScaleFactor(
                    orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight);

    if (exactScaleFactor <= 0f) {
        throw new IllegalArgumentException(
                "Cannot scale with factor: "
                        + exactScaleFactor
                        + " from: "
                        + downsampleStrategy
                        + ", source: ["
                        + sourceWidth
                        + "x"
                        + sourceHeight
                        + "]"
                        + ", target: ["
                        + targetWidth
                        + "x"
                        + targetHeight
                        + "]");
    }

    SampleSizeRounding rounding =
            downsampleStrategy.getSampleSizeRounding(
                    orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight);
    if (rounding == null) {
        throw new IllegalArgumentException("Cannot round with null rounding");
    }

    int outWidth = round(exactScaleFactor * orientedSourceWidth);
    int outHeight = round(exactScaleFactor * orientedSourceHeight);

    //  1、首先计算出图片与View的宽高比
    int widthScaleFactor = orientedSourceWidth / outWidth;
    int heightScaleFactor = orientedSourceHeight / outHeight;

    //  这对于 CenterOutside 和 CenterInside 来说都不太合适。
    //  考虑允许 DownsampleStrategy 选择,或者尝试做一些更复杂的事情,比如选择导致精确匹配的比例因子。

    //  2、根据缩放策略是省内存还是高品质,决定取宽高比的最大值还是最小值
    int scaleFactor =
            rounding == SampleSizeRounding.MEMORY
                    ? Math.max(widthScaleFactor, heightScaleFactor)
                    : Math.min(widthScaleFactor, heightScaleFactor);

    int powerOfTwoSampleSize;
    // BitmapFactory does not support downsampling wbmp files on platforms <= M. See b/27305903.

    //  BitmapFactory 不支持在平台 <= M 上对 wbmp 文件进行下采样。
    if (Build.VERSION.SDK_INT <= 23
            && NO_DOWNSAMPLE_PRE_N_MIME_TYPES.contains(options.outMimeType)) {
        powerOfTwoSampleSize = 1;
    } else {
        //  4、通过highestOneBit()把计算的比例四舍五入到最接近2的幂
        powerOfTwoSampleSize = Math.max(1, Integer.highestOneBit(scaleFactor));
        if (rounding == SampleSizeRounding.MEMORY
                //  exactScaleFactor 由各个裁剪策略如CenterCrop重写得到
                && powerOfTwoSampleSize < (1.f / exactScaleFactor)) {
            //  5、如果缩放策略为省内存,并且计算得出的sampleSize < exactScaleFactor,则sampleSize再增加一倍
            powerOfTwoSampleSize = powerOfTwoSampleSize << 1;
        }
    }

    // Here we mimic framework logic for determining how inSampleSize division is rounded on various
    // versions of Android. The logic here has been tested on emulators for Android versions 15-26.
    // PNG - Always uses floor
    // JPEG - Always uses ceiling
    // Webp - Prior to N, always uses floor. At and after N, always uses round.
    options.inSampleSize = powerOfTwoSampleSize;
    
    ..........

参照:www.jianshu.com/p/7005dbc17…