/*******************************************************************************
|
* Copyright 2013-2014 Sergey Tarasevich
|
*
|
* 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 com.nostra13.universalimageloader.utils;
|
|
import android.graphics.BitmapFactory;
|
import android.opengl.GLES10;
|
import com.nostra13.universalimageloader.core.assist.ImageSize;
|
import com.nostra13.universalimageloader.core.assist.ViewScaleType;
|
import com.nostra13.universalimageloader.core.imageaware.ImageAware;
|
|
import javax.microedition.khronos.opengles.GL10;
|
|
/**
|
* Provides calculations with image sizes, scales
|
*
|
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
|
* @since 1.8.3
|
*/
|
public final class ImageSizeUtils {
|
|
private static final int DEFAULT_MAX_BITMAP_DIMENSION = 2048;
|
|
private static ImageSize maxBitmapSize;
|
|
static {
|
int[] maxTextureSize = new int[1];
|
GLES10.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0);
|
int maxBitmapDimension = Math.max(maxTextureSize[0], DEFAULT_MAX_BITMAP_DIMENSION);
|
maxBitmapSize = new ImageSize(maxBitmapDimension, maxBitmapDimension);
|
}
|
|
private ImageSizeUtils() {
|
}
|
|
/**
|
* Defines target size for image aware view. Size is defined by target
|
* {@link com.nostra13.universalimageloader.core.imageaware.ImageAware view} parameters, configuration
|
* parameters or device display dimensions.<br />
|
*/
|
public static ImageSize defineTargetSizeForView(ImageAware imageAware, ImageSize maxImageSize) {
|
int width = imageAware.getWidth();
|
if (width <= 0) width = maxImageSize.getWidth();
|
|
int height = imageAware.getHeight();
|
if (height <= 0) height = maxImageSize.getHeight();
|
|
return new ImageSize(width, height);
|
}
|
|
/**
|
* Computes sample size for downscaling image size (<b>srcSize</b>) to view size (<b>targetSize</b>). This sample
|
* size is used during
|
* {@linkplain BitmapFactory#decodeStream(java.io.InputStream, android.graphics.Rect, android.graphics.BitmapFactory.Options)
|
* decoding image} to bitmap.<br />
|
* <br />
|
* <b>Examples:</b><br />
|
* <p/>
|
* <pre>
|
* srcSize(100x100), targetSize(10x10), powerOf2Scale = true -> sampleSize = 8
|
* srcSize(100x100), targetSize(10x10), powerOf2Scale = false -> sampleSize = 10
|
*
|
* srcSize(100x100), targetSize(20x40), viewScaleType = FIT_INSIDE -> sampleSize = 5
|
* srcSize(100x100), targetSize(20x40), viewScaleType = CROP -> sampleSize = 2
|
* </pre>
|
* <p/>
|
* <br />
|
* The sample size is the number of pixels in either dimension that correspond to a single pixel in the decoded
|
* bitmap. For example, inSampleSize == 4 returns an image that is 1/4 the width/height of the original, and 1/16
|
* the number of pixels. Any value <= 1 is treated the same as 1.
|
*
|
* @param srcSize Original (image) size
|
* @param targetSize Target (view) size
|
* @param viewScaleType {@linkplain ViewScaleType Scale type} for placing image in view
|
* @param powerOf2Scale <i>true</i> - if sample size be a power of 2 (1, 2, 4, 8, ...)
|
* @return Computed sample size
|
*/
|
public static int computeImageSampleSize(ImageSize srcSize, ImageSize targetSize, ViewScaleType viewScaleType,
|
boolean powerOf2Scale) {
|
final int srcWidth = srcSize.getWidth();
|
final int srcHeight = srcSize.getHeight();
|
final int targetWidth = targetSize.getWidth();
|
final int targetHeight = targetSize.getHeight();
|
|
int scale = 1;
|
|
switch (viewScaleType) {
|
case FIT_INSIDE:
|
if (powerOf2Scale) {
|
final int halfWidth = srcWidth / 2;
|
final int halfHeight = srcHeight / 2;
|
while ((halfWidth / scale) > targetWidth || (halfHeight / scale) > targetHeight) { // ||
|
scale *= 2;
|
}
|
} else {
|
scale = Math.max(srcWidth / targetWidth, srcHeight / targetHeight); // max
|
}
|
break;
|
case CROP:
|
if (powerOf2Scale) {
|
final int halfWidth = srcWidth / 2;
|
final int halfHeight = srcHeight / 2;
|
while ((halfWidth / scale) > targetWidth && (halfHeight / scale) > targetHeight) { // &&
|
scale *= 2;
|
}
|
} else {
|
scale = Math.min(srcWidth / targetWidth, srcHeight / targetHeight); // min
|
}
|
break;
|
}
|
|
if (scale < 1) {
|
scale = 1;
|
}
|
scale = considerMaxTextureSize(srcWidth, srcHeight, scale, powerOf2Scale);
|
|
return scale;
|
}
|
|
private static int considerMaxTextureSize(int srcWidth, int srcHeight, int scale, boolean powerOf2) {
|
final int maxWidth = maxBitmapSize.getWidth();
|
final int maxHeight = maxBitmapSize.getHeight();
|
while ((srcWidth / scale) > maxWidth || (srcHeight / scale) > maxHeight) {
|
if (powerOf2) {
|
scale *= 2;
|
} else {
|
scale++;
|
}
|
}
|
return scale;
|
}
|
|
/**
|
* Computes minimal sample size for downscaling image so result image size won't exceed max acceptable OpenGL
|
* texture size.<br />
|
* We can't create Bitmap in memory with size exceed max texture size (usually this is 2048x2048) so this method
|
* calculate minimal sample size which should be applied to image to fit into these limits.
|
*
|
* @param srcSize Original image size
|
* @return Minimal sample size
|
*/
|
public static int computeMinImageSampleSize(ImageSize srcSize) {
|
final int srcWidth = srcSize.getWidth();
|
final int srcHeight = srcSize.getHeight();
|
final int targetWidth = maxBitmapSize.getWidth();
|
final int targetHeight = maxBitmapSize.getHeight();
|
|
final int widthScale = (int) Math.ceil((float) srcWidth / targetWidth);
|
final int heightScale = (int) Math.ceil((float) srcHeight / targetHeight);
|
|
return Math.max(widthScale, heightScale); // max
|
}
|
|
/**
|
* Computes scale of target size (<b>targetSize</b>) to source size (<b>srcSize</b>).<br />
|
* <br />
|
* <b>Examples:</b><br />
|
* <p/>
|
* <pre>
|
* srcSize(40x40), targetSize(10x10) -> scale = 0.25
|
*
|
* srcSize(10x10), targetSize(20x20), stretch = false -> scale = 1
|
* srcSize(10x10), targetSize(20x20), stretch = true -> scale = 2
|
*
|
* srcSize(100x100), targetSize(20x40), viewScaleType = FIT_INSIDE -> scale = 0.2
|
* srcSize(100x100), targetSize(20x40), viewScaleType = CROP -> scale = 0.4
|
* </pre>
|
*
|
* @param srcSize Source (image) size
|
* @param targetSize Target (view) size
|
* @param viewScaleType {@linkplain ViewScaleType Scale type} for placing image in view
|
* @param stretch Whether source size should be stretched if target size is larger than source size. If <b>false</b>
|
* then result scale value can't be greater than 1.
|
* @return Computed scale
|
*/
|
public static float computeImageScale(ImageSize srcSize, ImageSize targetSize, ViewScaleType viewScaleType,
|
boolean stretch) {
|
final int srcWidth = srcSize.getWidth();
|
final int srcHeight = srcSize.getHeight();
|
final int targetWidth = targetSize.getWidth();
|
final int targetHeight = targetSize.getHeight();
|
|
final float widthScale = (float) srcWidth / targetWidth;
|
final float heightScale = (float) srcHeight / targetHeight;
|
|
final int destWidth;
|
final int destHeight;
|
if ((viewScaleType == ViewScaleType.FIT_INSIDE && widthScale >= heightScale) || (viewScaleType == ViewScaleType.CROP && widthScale < heightScale)) {
|
destWidth = targetWidth;
|
destHeight = (int) (srcHeight / widthScale);
|
} else {
|
destWidth = (int) (srcWidth / heightScale);
|
destHeight = targetHeight;
|
}
|
|
float scale = 1;
|
if ((!stretch && destWidth < srcWidth && destHeight < srcHeight) || (stretch && destWidth != srcWidth && destHeight != srcHeight)) {
|
scale = (float) destWidth / srcWidth;
|
}
|
|
return scale;
|
}
|
}
|