/* * Copyright (C) 2013 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.feature; import android.content.Context; import android.util.DisplayMetrics; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import java.lang.reflect.Field; import me.xiaopan.sketch.Identifier; import me.xiaopan.sketch.request.FixedSize; import me.xiaopan.sketch.request.ImageViewInterface; import me.xiaopan.sketch.request.MaxSize; import me.xiaopan.sketch.request.Resize; import me.xiaopan.sketch.util.SketchUtils; /** * 图片最大尺寸和修正尺寸计算器 */ public class ImageSizeCalculator implements Identifier { protected String logName = "ImageSizeCalculator"; private int openGLMaxTextureSize = -1; private float targetSizeScale = 1.1f; public static int getWidth(View imageView, boolean checkMaxWidth, boolean acceptWrapContent, boolean subtractPadding) { if (imageView == null) { return 0; } int width = 0; final ViewGroup.LayoutParams params = imageView.getLayoutParams(); if (params != null) { width = params.width; if (subtractPadding && width > 0 && (width - imageView.getPaddingLeft() - imageView.getPaddingRight()) > 0) { width -= imageView.getPaddingLeft() + imageView.getPaddingRight(); return width; } } if (width <= 0 && checkMaxWidth) { width = getViewFieldValue(imageView, "mMaxWidth"); } if (width <= 0 && acceptWrapContent && params != null && params.width == ViewGroup.LayoutParams.WRAP_CONTENT) { width = -1; } return width; } public static int getHeight(View imageView, boolean checkMaxHeight, boolean acceptWrapContent, boolean subtractPadding) { if (imageView == null) { return 0; } int height = 0; final ViewGroup.LayoutParams params = imageView.getLayoutParams(); if (params != null) { height = params.height; if (subtractPadding && height > 0 && (height - imageView.getPaddingTop() - imageView.getPaddingBottom()) > 0) { height -= imageView.getPaddingTop() + imageView.getPaddingBottom(); return height; } } if (height <= 0 && checkMaxHeight) { height = getViewFieldValue(imageView, "mMaxHeight"); } if (height <= 0 && acceptWrapContent && params != null && params.height == ViewGroup.LayoutParams.WRAP_CONTENT) { height = -1; } return height; } private static int getViewFieldValue(Object object, String fieldName) { int value = 0; try { Field field = ImageView.class.getDeclaredField(fieldName); field.setAccessible(true); int fieldValue = (Integer) field.get(object); if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) { value = fieldValue; } } catch (Exception e) { e.printStackTrace(); } return value; } /** * 获取OpenGL所允许的最大尺寸 */ @SuppressWarnings("WeakerAccess") public int getOpenGLMaxTextureSize() { if (openGLMaxTextureSize == -1) { openGLMaxTextureSize = SketchUtils.getOpenGLMaxTextureSize(); } return openGLMaxTextureSize; } /** * 设置OpenGL所允许的最大尺寸,用来计算inSampleSize */ @SuppressWarnings("unused") public void setOpenGLMaxTextureSize(int openGLMaxTextureSize) { this.openGLMaxTextureSize = openGLMaxTextureSize; } /** * 计算MaxSize * * @param imageViewInterface 你需要根据ImageView的宽高来计算 * @return MaxSize */ public MaxSize calculateImageMaxSize(ImageViewInterface imageViewInterface) { View imageView = imageViewInterface.getSelf(); if (imageView == null) { return null; } int width = getWidth(imageView, true, true, false); int height = getHeight(imageView, true, true, false); if (width > 0 || height > 0) { // 因为OpenGL对图片的宽高有上限,因此要限制一下,这里就严格一点不能大于屏幕宽高的1.5倍 DisplayMetrics displayMetrics = imageViewInterface.getSelf().getResources().getDisplayMetrics(); int maxWidth = (int) (displayMetrics.widthPixels * 1.5f); int maxHeight = (int) (displayMetrics.heightPixels * 1.5f); if (width > maxWidth || height > maxHeight) { float widthScale = (float) width / maxWidth; float heightScale = (float) height / maxHeight; float finalScale = widthScale > heightScale ? widthScale : heightScale; width /= finalScale; height /= finalScale; } return new MaxSize(width, height); } else { return null; } } /** * 获取默认的maxSize,默认maxSize是屏幕宽高的70% * * @param context 上下文 * @return maxSize */ public MaxSize getDefaultImageMaxSize(Context context) { DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); return new MaxSize(displayMetrics.widthPixels, displayMetrics.heightPixels); } /** * 计算Resize * * @param imageViewInterface 你需要根据ImageView的宽高来计算 * @return Resize */ @Deprecated public Resize calculateImageResize(ImageViewInterface imageViewInterface) { View imageView = imageViewInterface.getSelf(); if (imageView == null) { return null; } int width = getWidth(imageView, false, false, true); int height = getHeight(imageView, false, false, true); if (width > 0 && height > 0) { return new Resize(width, height, imageViewInterface.getScaleType()); } else { return null; } } /** * 计算FixedSize * * @param imageViewInterface 你需要根据ImageView的宽高来计算 * @return FixedSize */ public FixedSize calculateImageFixedSize(ImageViewInterface imageViewInterface) { View imageView = imageViewInterface.getSelf(); if (imageView == null) { return null; } ViewGroup.LayoutParams layoutParams = imageView.getLayoutParams(); if (layoutParams != null && layoutParams.width > 0 && layoutParams.height > 0) { int fixedWidth = layoutParams.width - (imageView.getPaddingLeft() + imageView.getPaddingRight()); int fixedHeight = layoutParams.height - (imageView.getPaddingTop() + imageView.getPaddingBottom()); // 限制不能超过OpenGL所允许的最大尺寸 int maxSize = getOpenGLMaxTextureSize(); if (fixedWidth > maxSize || fixedHeight > maxSize) { float finalScale = Math.max((float) fixedWidth / maxSize, (float) fixedHeight / maxSize); fixedWidth /= finalScale; fixedHeight /= finalScale; } return new FixedSize(fixedWidth, fixedHeight); } return null; } /** * 比较两个maxSize的大小,在使用options()方法批量设置属性的时候会使用此方法比较Options的maxSize和已有的maxSize,如果前者小于后者就会使用前者代替后者 * * @param maxSize1 maxSize1 * @param maxSize2 maxSize2 * @return 等于0:两者相等;小于0:maxSize1小于maxSize2;大于0:maxSize1大于maxSize2 */ @Deprecated public int compareMaxSize(MaxSize maxSize1, MaxSize maxSize2) { if (maxSize1 == null || maxSize2 == null) { return 0; } return (maxSize1.getWidth() * maxSize1.getHeight()) - (maxSize2.getWidth() - maxSize2.getHeight()); } /** * 计算InSampleSize * * @param outWidth 原始宽 * @param outHeight 原始高 * @param targetWidth 目标宽 * @param targetHeight 目标高 * @param supportLargeImage 是否支持大图,大图时会有特殊处理 * @return 合适的InSampleSize */ public int calculateInSampleSize(int outWidth, int outHeight, int targetWidth, int targetHeight, boolean supportLargeImage) { targetWidth *= targetSizeScale; targetHeight *= targetSizeScale; // 限制target宽高不能大于OpenGL所允许的最大尺寸 int maxSize = getOpenGLMaxTextureSize(); if (targetWidth > maxSize) { targetWidth = maxSize; } if (targetHeight > maxSize) { targetHeight = maxSize; } int inSampleSize = 1; // 如果目标宽高都小于等于0,就别计算了 if (targetWidth <= 0 && targetHeight <= 0) { return inSampleSize; } // 如果目标宽高都大于等于原始尺寸,也别计算了 if (targetWidth >= outWidth && targetHeight >= outHeight) { return inSampleSize; } if (targetWidth <= 0) { // 目标宽小于等于0时,只要高度满足要求即可 while (SketchUtils.ceil(outHeight, inSampleSize) > targetHeight) { inSampleSize *= 2; } } else if (targetHeight <= 0) { // 目标高小于等于0时,只要宽度满足要求即可 while (SketchUtils.ceil(outWidth, inSampleSize) > targetWidth) { inSampleSize *= 2; } } else { // 首先限制像素数不能超过目标宽高的像素数 final long maxPixels = targetWidth * targetHeight; while ((SketchUtils.ceil(outWidth, inSampleSize)) * (SketchUtils.ceil(outHeight, inSampleSize)) > maxPixels) { inSampleSize *= 2; } // 然后限制宽高不能大于OpenGL所允许的最大尺寸 while (SketchUtils.ceil(outWidth, inSampleSize) > maxSize || SketchUtils.ceil(outHeight, inSampleSize) > maxSize) { inSampleSize *= 2; } // 最后如果是为大图功能加载预览图的话,当缩小2倍的话为了节省内存考虑还不如缩小4倍(缩小1倍时不会启用大图功能,因此无需处理) if (supportLargeImage && inSampleSize == 2) { inSampleSize = 4; } } return inSampleSize; } /** * 根据高度计算是否可以使用阅读模式 */ public boolean canUseReadModeByHeight(int imageWidth, int imageHeight){ return imageHeight > imageWidth * 2; } /** * 根据宽度度计算是否可以使用阅读模式 */ public boolean canUseReadModeByWidth(int imageWidth, int imageHeight){ return imageWidth > imageHeight * 3; } /** * 是否可以使用缩略图模式 */ public boolean canUseThumbnailMode(int outWidth, int outHeight, int resizeWidth, int resizeHeight){ if (resizeWidth > outWidth && resizeHeight > outHeight) { return false; } float resizeScale = (float) resizeWidth / resizeHeight; float imageScale = (float) outWidth / outHeight; return Math.max(resizeScale, imageScale) > Math.min(resizeScale, imageScale) * 1.5f; } @SuppressWarnings("unused") public float getTargetSizeScale() { return targetSizeScale; } /** * 计算inSampleSize的时候将targetSize稍微放大一点儿,就是乘以这个倍数,默认值是1.25f * * @param targetSizeScale 将targetSize稍微放大一点儿 */ @SuppressWarnings("unused") public void setTargetSizeScale(float targetSizeScale) { this.targetSizeScale = targetSizeScale; } @Override public String getKey() { return logName; } }