
com.nostra13.universalimageloader.core.decode.BaseImageDecoder Maven / Gradle / Ivy
/*******************************************************************************
* Copyright 2011-2013 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.core.decode;
import java.io.IOException;
import java.io.InputStream;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.os.Build;
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
import com.nostra13.universalimageloader.core.assist.ImageSize;
import com.nostra13.universalimageloader.core.download.ImageDownloader.Scheme;
import com.nostra13.universalimageloader.utils.ImageSizeUtils;
import com.nostra13.universalimageloader.utils.IoUtils;
import com.nostra13.universalimageloader.utils.L;
/**
* Decodes images to {@link Bitmap}, scales them to needed size
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @since 1.8.3
* @see ImageDecodingInfo
*/
public class BaseImageDecoder implements ImageDecoder {
protected static final String LOG_SABSAMPLE_IMAGE = "Subsample original image (%1$s) to %2$s (scale = %3$d) [%4$s]";
protected static final String LOG_SCALE_IMAGE = "Scale subsampled image (%1$s) to %2$s (scale = %3$.5f) [%4$s]";
protected static final String LOG_ROTATE_IMAGE = "Rotate image on %1$d\u00B0 [%2$s]";
protected static final String LOG_FLIP_IMAGE = "Flip image horizontally [%s]";
protected static final String ERROR_CANT_DECODE_IMAGE = "Image can't be decoded [%s]";
protected boolean loggingEnabled;
public BaseImageDecoder() {
}
public BaseImageDecoder(boolean loggingEnabled) {
this.loggingEnabled = loggingEnabled;
}
/**
* Decodes image from URI into {@link Bitmap}. Image is scaled close to incoming {@linkplain ImageSize target size}
* during decoding (depend on incoming parameters).
*
* @param decodingInfo Needed data for decoding image
*
* @return Decoded bitmap
* @throws IOException if some I/O exception occurs during image reading
* @throws UnsupportedOperationException if image URI has unsupported scheme(protocol)
*/
public Bitmap decode(ImageDecodingInfo decodingInfo) throws IOException {
InputStream imageStream = getImageStream(decodingInfo);
ImageFileInfo imageInfo = defineImageSizeAndRotation(imageStream, decodingInfo.getImageUri());
Options decodingOptions = prepareDecodingOptions(imageInfo.imageSize, decodingInfo);
imageStream = getImageStream(decodingInfo);
Bitmap decodedBitmap = decodeStream(imageStream, decodingOptions);
if (decodedBitmap == null) {
L.e(ERROR_CANT_DECODE_IMAGE, decodingInfo.getImageKey());
} else {
decodedBitmap = considerExactScaleAndOrientaiton(decodedBitmap, decodingInfo, imageInfo.exif.rotation, imageInfo.exif.flipHorizontal);
}
return decodedBitmap;
}
protected InputStream getImageStream(ImageDecodingInfo decodingInfo) throws IOException {
return decodingInfo.getDownloader().getStream(decodingInfo.getImageUri(), decodingInfo.getExtraForDownloader());
}
protected ImageFileInfo defineImageSizeAndRotation(InputStream imageStream, String imageUri) throws IOException {
Options options = new Options();
options.inJustDecodeBounds = true;
try {
BitmapFactory.decodeStream(imageStream, null, options);
} finally {
IoUtils.closeSilently(imageStream);
}
ExifInfo exif;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) {
exif = defineExifOrientation(imageUri, options.outMimeType);
} else {
exif = new ExifInfo();
}
return new ImageFileInfo(new ImageSize(options.outWidth, options.outHeight, exif.rotation), exif);
}
protected ExifInfo defineExifOrientation(String imageUri, String mimeType) {
int rotation = 0;
boolean flip = false;
if ("image/jpeg".equalsIgnoreCase(mimeType) && Scheme.ofUri(imageUri) == Scheme.FILE) {
try {
ExifInterface exif = new ExifInterface(Scheme.FILE.crop(imageUri));
int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (exifOrientation) {
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
flip = true;
case ExifInterface.ORIENTATION_NORMAL:
rotation = 0;
break;
case ExifInterface.ORIENTATION_TRANSVERSE:
flip = true;
case ExifInterface.ORIENTATION_ROTATE_90:
rotation = 90;
break;
case ExifInterface.ORIENTATION_FLIP_VERTICAL:
flip = true;
case ExifInterface.ORIENTATION_ROTATE_180:
rotation = 180;
break;
case ExifInterface.ORIENTATION_TRANSPOSE:
flip = true;
case ExifInterface.ORIENTATION_ROTATE_270:
rotation = 270;
break;
}
} catch (IOException e) {
L.w("Can't read EXIF tags from file [%s]", imageUri);
}
}
return new ExifInfo(rotation, flip);
}
protected Options prepareDecodingOptions(ImageSize imageSize, ImageDecodingInfo decodingInfo) {
ImageScaleType scaleType = decodingInfo.getImageScaleType();
ImageSize targetSize = decodingInfo.getTargetSize();
int scale = 1;
if (scaleType != ImageScaleType.NONE) {
boolean powerOf2 = scaleType == ImageScaleType.IN_SAMPLE_POWER_OF_2;
scale = ImageSizeUtils.computeImageSampleSize(imageSize, targetSize, decodingInfo.getViewScaleType(), powerOf2);
if (loggingEnabled) L.i(LOG_SABSAMPLE_IMAGE, imageSize, imageSize.scaleDown(scale), scale, decodingInfo.getImageKey());
}
Options decodingOptions = decodingInfo.getDecodingOptions();
decodingOptions.inSampleSize = scale;
return decodingOptions;
}
protected Bitmap decodeStream(InputStream imageStream, Options decodingOptions) throws IOException {
try {
return BitmapFactory.decodeStream(imageStream, null, decodingOptions);
} finally {
IoUtils.closeSilently(imageStream);
}
}
protected Bitmap considerExactScaleAndOrientaiton(Bitmap subsampledBitmap, ImageDecodingInfo decodingInfo, int rotation, boolean flipHorizontal) {
Matrix m = new Matrix();
// Scale to exact size if need
ImageScaleType scaleType = decodingInfo.getImageScaleType();
if (scaleType == ImageScaleType.EXACTLY || scaleType == ImageScaleType.EXACTLY_STRETCHED) {
ImageSize srcSize = new ImageSize(subsampledBitmap.getWidth(), subsampledBitmap.getHeight(), rotation);
float scale = ImageSizeUtils.computeImageScale(srcSize, decodingInfo.getTargetSize(), decodingInfo.getViewScaleType(),
scaleType == ImageScaleType.EXACTLY_STRETCHED);
if (Float.compare(scale, 1f) != 0) {
m.setScale(scale, scale);
if (loggingEnabled) L.i(LOG_SCALE_IMAGE, srcSize, srcSize.scale(scale), scale, decodingInfo.getImageKey());
}
}
// Flip bitmap if need
if (flipHorizontal) {
m.postScale(-1, 1);
if (loggingEnabled) L.i(LOG_FLIP_IMAGE, decodingInfo.getImageKey());
}
// Rotate bitmap if need
if (rotation != 0) {
m.postRotate(rotation);
if (loggingEnabled) L.i(LOG_ROTATE_IMAGE, rotation, decodingInfo.getImageKey());
}
Bitmap finalBitmap = Bitmap.createBitmap(subsampledBitmap, 0, 0, subsampledBitmap.getWidth(), subsampledBitmap.getHeight(), m, true);
if (finalBitmap != subsampledBitmap) {
subsampledBitmap.recycle();
}
return finalBitmap;
}
public void setLoggingEnabled(boolean loggingEnabled) {
this.loggingEnabled = loggingEnabled;
}
protected static class ExifInfo {
final int rotation;
final boolean flipHorizontal;
ExifInfo() {
this.rotation = 0;
this.flipHorizontal = false;
}
ExifInfo(int rotation, boolean flipHorizontal) {
this.rotation = rotation;
this.flipHorizontal = flipHorizontal;
}
}
protected static class ImageFileInfo {
final ImageSize imageSize;
final ExifInfo exif;
ImageFileInfo(ImageSize imageSize, ExifInfo exif) {
this.imageSize = imageSize;
this.exif = exif;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy