/* * Copyright (C) 2017 Peng fei Pan <sky@xiaopan.me> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package me.xiaopan.sketch.decode; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Build; import me.xiaopan.sketch.SLog; import me.xiaopan.sketch.SLogType; import me.xiaopan.sketch.SketchMonitor; import me.xiaopan.sketch.cache.BitmapPool; import me.xiaopan.sketch.cache.BitmapPoolUtils; import me.xiaopan.sketch.drawable.ImageAttrs; import me.xiaopan.sketch.feature.ImageSizeCalculator; import me.xiaopan.sketch.feature.ResizeCalculator; import me.xiaopan.sketch.request.LoadOptions; import me.xiaopan.sketch.request.LoadRequest; import me.xiaopan.sketch.request.Resize; import me.xiaopan.sketch.util.SketchUtils; public class ThumbnailModeDecodeHelper implements DecodeHelper { private static final String LOG_NAME = "ThumbnailModeDecodeHelper"; /** * 要想使用缩略图功能需要配置开启缩略图功能、配置resize并且图片格式和系统版本支持BitmapRegionDecoder才行 */ @Override public boolean match(LoadRequest request, DataSource dataSource, ImageType imageType, BitmapFactory.Options boundOptions) { LoadOptions loadOptions = request.getOptions(); if (!loadOptions.isThumbnailMode() || !SketchUtils.sdkSupportBitmapRegionDecoder() || !SketchUtils.formatSupportBitmapRegionDecoder(imageType)) { return false; } Resize resize = loadOptions.getResize(); if (resize == null) { SLog.e(LOG_NAME, "thumbnailMode need resize "); return false; } // 只有原始图片的宽高比和resize的宽高比相差3倍的时候才能使用略略图方式读取图片 ImageSizeCalculator sizeCalculator = request.getConfiguration().getImageSizeCalculator(); return sizeCalculator.canUseThumbnailMode(boundOptions.outWidth, boundOptions.outHeight, resize.getWidth(), resize.getHeight()); } @Override public DecodeResult decode(LoadRequest request, DataSource dataSource, ImageType imageType, BitmapFactory.Options boundOptions, BitmapFactory.Options decodeOptions, int orientation) { decodeOptions.outWidth = boundOptions.outWidth; decodeOptions.outHeight = boundOptions.outHeight; decodeOptions.outMimeType = boundOptions.outMimeType; // 缩略图模式强制质量优先 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1 && !decodeOptions.inPreferQualityOverSpeed) { decodeOptions.inPreferQualityOverSpeed = true; } LoadOptions loadOptions = request.getOptions(); Resize resize = loadOptions.getResize(); ImageSizeCalculator sizeCalculator = request.getConfiguration().getImageSizeCalculator(); // 计算resize区域在原图中的对应区域 ResizeCalculator resizeCalculator = request.getConfiguration().getResizeCalculator(); ResizeCalculator.Result result = resizeCalculator.calculator(boundOptions.outWidth, boundOptions.outHeight, resize.getWidth(), resize.getHeight(), resize.getScaleType(), false); boolean supportLargeImage = SketchUtils.supportLargeImage(request, imageType); // 根据resize的大小和原图中对应区域的大小计算缩小倍数,这样会得到一个较为清晰的缩略图 decodeOptions.inSampleSize = sizeCalculator.calculateInSampleSize(result.srcRect.width(), result.srcRect.height(), resize.getWidth(), resize.getHeight(), supportLargeImage); if (BitmapPoolUtils.sdkSupportInBitmapForRegionDecoder() && !loadOptions.isBitmapPoolDisabled()) { BitmapPool bitmapPool = request.getConfiguration().getBitmapPool(); BitmapPoolUtils.setInBitmapFromPoolForRegionDecoder(decodeOptions, result.srcRect, bitmapPool); } Bitmap bitmap = null; try { bitmap = DefaultImageDecoder.decodeRegionBitmap(dataSource, result.srcRect, decodeOptions); } catch (IllegalArgumentException e) { e.printStackTrace(); // TODO: 2017/5/9 过滤异常message,准确的识别出inBitmap异常 // 要是因为inBitmap而解码失败就记录日志并再此尝试 if (BitmapPoolUtils.sdkSupportInBitmapForRegionDecoder()) { if (!loadOptions.isBitmapPoolDisabled() && decodeOptions.inBitmap != null) { BitmapPool bitmapPool = request.getConfiguration().getBitmapPool(); SketchMonitor sketchMonitor = request.getConfiguration().getMonitor(); BitmapPoolUtils.inBitmapThrowForRegionDecoder(e, decodeOptions, sketchMonitor, bitmapPool, request.getUri(), boundOptions.outWidth, boundOptions.outHeight, result.srcRect); decodeOptions.inBitmap = null; try { bitmap = DefaultImageDecoder.decodeRegionBitmap(dataSource, result.srcRect, decodeOptions); } catch (Throwable error) { error.printStackTrace(); sketchMonitor.onDecodeNormalImageError(error, request, boundOptions.outWidth, boundOptions.outHeight, boundOptions.outMimeType); } } } } catch (Throwable error) { error.printStackTrace(); SketchMonitor sketchMonitor = request.getConfiguration().getMonitor(); sketchMonitor.onDecodeNormalImageError(error, request, boundOptions.outWidth, boundOptions.outHeight, boundOptions.outMimeType); } // 过滤掉无效的图片 if (bitmap == null || bitmap.isRecycled()) { DefaultImageDecoder.decodeError(request, dataSource, LOG_NAME); return null; } // 过滤宽高小于等于1的图片 if (bitmap.getWidth() <= 1 || bitmap.getHeight() <= 1) { if (SLogType.REQUEST.isEnabled()) { SLog.w(SLogType.REQUEST, LOG_NAME, "image width or height less than or equal to 1px. imageSize: %dx%d. bitmapSize: %dx%d. %s", boundOptions.outWidth, boundOptions.outHeight, bitmap.getWidth(), bitmap.getHeight(), request.getKey()); } bitmap.recycle(); DefaultImageDecoder.decodeError(request, dataSource, LOG_NAME); return null; } // 成功 DefaultImageDecoder.decodeSuccess(bitmap, boundOptions.outWidth, boundOptions.outHeight, decodeOptions.inSampleSize, request, LOG_NAME); ImageAttrs imageAttrs = new ImageAttrs(boundOptions.outMimeType, boundOptions.outWidth, boundOptions.outHeight, orientation); return new BitmapDecodeResult(imageAttrs, bitmap).setProcessed(true); } }