/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.imagepipeline.decoder;
import android.graphics.Bitmap;
import com.facebook.common.internal.Closeables;
import com.facebook.common.references.CloseableReference;
import com.facebook.imageformat.GifFormatChecker;
import com.facebook.imageformat.ImageFormat;
import com.facebook.imageformat.ImageFormatChecker;
import com.facebook.imagepipeline.animated.factory.AnimatedImageFactory;
import com.facebook.imagepipeline.common.ImageDecodeOptions;
import com.facebook.imagepipeline.image.CloseableImage;
import com.facebook.imagepipeline.image.CloseableStaticBitmap;
import com.facebook.imagepipeline.image.EncodedImage;
import com.facebook.imagepipeline.image.ImmutableQualityInfo;
import com.facebook.imagepipeline.image.QualityInfo;
import com.facebook.imagepipeline.platform.PlatformDecoder;
import java.io.InputStream;
/**
* Decodes images.
*
* <p> ImageDecoder implements image type recognition and passes decode requests to
* specialized methods implemented by subclasses.
*
* On dalvik, it produces 'pinned' purgeable bitmaps.
*
* <p> Pinned purgeables behave as specified in
* {@link android.graphics.BitmapFactory.Options#inPurgeable} with one modification. The bitmap is
* 'pinned' so is never purged.
*
* <p> For API 21 and higher, this class produces standard Bitmaps, as purgeability is not supported
* on the most recent versions of Android.
*/
public class ImageDecoder {
private final AnimatedImageFactory mAnimatedImageFactory;
private final Bitmap.Config mBitmapConfig;
private final PlatformDecoder mPlatformDecoder;
public ImageDecoder(
final AnimatedImageFactory animatedImageFactory,
final PlatformDecoder platformDecoder,
final Bitmap.Config bitmapConfig) {
mAnimatedImageFactory = animatedImageFactory;
mBitmapConfig = bitmapConfig;
mPlatformDecoder = platformDecoder;
}
/**
* Decodes image.
*
* @param encodedImage input image (encoded bytes plus meta data)
* @param length if image type supports decoding incomplete image then determines where
* the image data should be cut for decoding.
* @param qualityInfo quality information for the image
* @param options options that cange decode behavior
*/
public CloseableImage decodeImage(
final EncodedImage encodedImage,
final int length,
final QualityInfo qualityInfo,
final ImageDecodeOptions options) {
ImageFormat imageFormat = encodedImage.getImageFormat();
if (imageFormat == null || imageFormat == ImageFormat.UNKNOWN) {
imageFormat = ImageFormatChecker.getImageFormat_WrapIOException(
encodedImage.getInputStream());
}
switch (imageFormat) {
case UNKNOWN:
throw new IllegalArgumentException("unknown image format");
case JPEG:
return decodeJpeg(encodedImage, length, qualityInfo);
case GIF:
return decodeGif(encodedImage, options);
case WEBP_ANIMATED:
return decodeAnimatedWebp(encodedImage, options);
default:
return decodeStaticImage(encodedImage);
}
}
/**
* Decodes gif into CloseableImage.
*
* @param encodedImage input image (encoded bytes plus meta data)
* @return a CloseableImage
*/
public CloseableImage decodeGif(
EncodedImage encodedImage,
ImageDecodeOptions options) {
InputStream is = encodedImage.getInputStream();
if (is == null) {
return null;
}
try {
if (GifFormatChecker.isAnimated(is)) {
return mAnimatedImageFactory.decodeGif(encodedImage, options, mBitmapConfig);
}
return decodeStaticImage(encodedImage);
} finally {
Closeables.closeQuietly(is);
}
}
/**
* @param encodedImage input image (encoded bytes plus meta data)
* @return a CloseableStaticBitmap
*/
public CloseableStaticBitmap decodeStaticImage(
final EncodedImage encodedImage) {
CloseableReference<Bitmap> bitmapReference =
mPlatformDecoder.decodeFromEncodedImage(encodedImage, mBitmapConfig);
try {
return new CloseableStaticBitmap(
bitmapReference,
ImmutableQualityInfo.FULL_QUALITY,
encodedImage.getRotationAngle());
} finally {
bitmapReference.close();
}
}
/**
* Decodes a partial jpeg.
*
* @param encodedImage input image (encoded bytes plus meta data)
* @param length amount of currently available data in bytes
* @param qualityInfo quality info for the image
* @return a CloseableStaticBitmap
*/
public CloseableStaticBitmap decodeJpeg(
final EncodedImage encodedImage,
int length,
QualityInfo qualityInfo) {
CloseableReference<Bitmap> bitmapReference =
mPlatformDecoder.decodeJPEGFromEncodedImage(encodedImage, mBitmapConfig, length);
try {
return new CloseableStaticBitmap(
bitmapReference,
qualityInfo,
encodedImage.getRotationAngle());
} finally {
bitmapReference.close();
}
}
/**
* Decode a webp animated image into a CloseableImage.
*
* <p> The image is decoded into a 'pinned' purgeable bitmap.
*
* @param encodedImage input image (encoded bytes plus meta data)
* @param options
* @return a {@link CloseableImage}
*/
public CloseableImage decodeAnimatedWebp(
final EncodedImage encodedImage,
final ImageDecodeOptions options) {
return mAnimatedImageFactory.decodeWebP(encodedImage, options, mBitmapConfig);
}
}