下面列出了android.graphics.BitmapRegionDecoder# isRecycled ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
/**
* Acquire a read lock to prevent decoding overlapping with recycling, then check the pool still
* exists and acquire a decoder to load the requested region. There is no check whether the pool
* currently has decoders, because it's guaranteed to have one decoder after {@link #init(Context, Uri)}
* is called and be null once {@link #recycle()} is called. In practice the view can't call this
* method until after {@link #init(Context, Uri)}, so there will be no blocking on an empty pool.
*/
@Override
@NonNull
public Bitmap decodeRegion(@NonNull Rect sRect, int sampleSize) {
debug("Decode region " + sRect + " on thread " + Thread.currentThread().getName());
if (sRect.width() < imageDimensions.x || sRect.height() < imageDimensions.y) {
lazyInit();
}
decoderLock.readLock().lock();
try {
if (decoderPool != null) {
BitmapRegionDecoder decoder = decoderPool.acquire();
try {
// Decoder can't be null or recycled in practice
if (decoder != null && !decoder.isRecycled()) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
options.inPreferredConfig = bitmapConfig;
Bitmap bitmap = decoder.decodeRegion(sRect, options);
if (bitmap == null) {
throw new RuntimeException("Skia image decoder returned null bitmap - image format may not be supported");
}
return bitmap;
}
} finally {
if (decoder != null) {
decoderPool.release(decoder);
}
}
}
throw new IllegalStateException("Cannot decode region after decoder has been recycled");
} finally {
decoderLock.readLock().unlock();
}
}
/**
* Acquire a read lock to prevent decoding overlapping with recycling, then check the pool still
* exists and acquire a decoder to load the requested region. There is no check whether the pool
* currently has decoders, because it's guaranteed to have one decoder after {@link #init(Context, Uri)}
* is called and be null once {@link #recycle()} is called. In practice the view can't call this
* method until after {@link #init(Context, Uri)}, so there will be no blocking on an empty pool.
*/
@Override
@NonNull
public Bitmap decodeRegion(@NonNull Rect sRect, int sampleSize) {
debug("Decode region " + sRect + " on thread " + Thread.currentThread().getName());
if (sRect.width() < imageDimensions.x || sRect.height() < imageDimensions.y) {
lazyInit();
}
decoderLock.readLock().lock();
try {
if (decoderPool != null) {
BitmapRegionDecoder decoder = decoderPool.acquire();
try {
// Decoder can't be null or recycled in practice
if (decoder != null && !decoder.isRecycled()) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
options.inPreferredConfig = bitmapConfig;
Bitmap bitmap = decoder.decodeRegion(sRect, options);
if (bitmap == null) {
throw new RuntimeException("Skia image decoder returned null bitmap - image format may not be supported");
}
return bitmap;
}
} finally {
if (decoder != null) {
decoderPool.release(decoder);
}
}
}
throw new IllegalStateException("Cannot decode region after decoder has been recycled");
} finally {
decoderLock.readLock().unlock();
}
}
/**
* Acquire a read lock to prevent decoding overlapping with recycling, then check the pool still
* exists and acquire a decoder to load the requested region. There is no check whether the pool
* currently has decoders, because it's guaranteed to have one decoder after {@link #init(Context, Uri)}
* is called and be null once {@link #recycle()} is called. In practice the view can't call this
* method until after {@link #init(Context, Uri)}, so there will be no blocking on an empty pool.
*/
@Override
@NonNull
public Bitmap decodeRegion(@NonNull Rect sRect, int sampleSize) {
debug("Decode region " + sRect + " on thread " + Thread.currentThread().getName());
if (sRect.width() < imageDimensions.x || sRect.height() < imageDimensions.y) {
lazyInit();
}
decoderLock.readLock().lock();
try {
if (decoderPool != null) {
BitmapRegionDecoder decoder = decoderPool.acquire();
try {
// Decoder can't be null or recycled in practice
if (decoder != null && !decoder.isRecycled()) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
options.inPreferredConfig = bitmapConfig;
Bitmap bitmap = decoder.decodeRegion(sRect, options);
if (bitmap == null) {
throw new RuntimeException("Skia image decoder returned null bitmap - image format may not be supported");
}
return bitmap;
}
} finally {
if (decoder != null) {
decoderPool.release(decoder);
}
}
}
throw new IllegalStateException("Cannot decode region after decoder has been recycled");
} finally {
decoderLock.readLock().unlock();
}
}
/**
* Acquire a read lock to prevent decoding overlapping with recycling, then check the pool still
* exists and acquire a decoder to load the requested region. There is no check whether the pool
* currently has decoders, because it's guaranteed to have one decoder after {@link #init(Context, Uri)}
* is called and be null once {@link #recycle()} is called. In practice the view can't call this
* method until after {@link #init(Context, Uri)}, so there will be no blocking on an empty pool.
*/
@Override
public Bitmap decodeRegion(Rect sRect, int sampleSize) {
debug("Decode region " + sRect + " on thread " + Thread.currentThread().getName());
if (sRect.width() < imageDimensions.x || sRect.height() < imageDimensions.y) {
lazyInit();
}
decoderLock.readLock().lock();
try {
if (decoderPool != null) {
BitmapRegionDecoder decoder = decoderPool.acquire();
try {
// Decoder can't be null or recycled in practice
if (decoder != null && !decoder.isRecycled()) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
options.inPreferredConfig = bitmapConfig;
Bitmap bitmap = decoder.decodeRegion(sRect, options);
if (bitmap == null) {
throw new RuntimeException("Skia image decoder returned null bitmap - image format may not be supported");
}
return bitmap;
}
} finally {
if (decoder != null) {
decoderPool.release(decoder);
}
}
}
throw new IllegalStateException("Cannot decode region after decoder has been recycled");
} finally {
decoderLock.readLock().unlock();
}
}
@Override
@NonNull
public Bitmap decodeRegion(@NonNull Rect sRect, int sampleSize) {
debug("Decode region " + sRect + " on thread " + Thread.currentThread().getName());
if (sRect.width() < imageDimensions.x || sRect.height() < imageDimensions.y) {
lazyInit();
}
decoderLock.readLock().lock();
try {
if (decoderPool != null) {
BitmapRegionDecoder decoder = decoderPool.acquire();
try {
// Decoder can't be null or recycled in practice
if (decoder != null && !decoder.isRecycled()) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
options.inPreferredConfig = bitmapConfig;
Bitmap bitmap = decoder.decodeRegion(sRect, options);
if (bitmap == null) {
throw new RuntimeException(
"null bitmap - image format may not be supported");
}
return bitmap;
}
} finally {
if (decoder != null) {
decoderPool.release(decoder);
}
}
}
throw new IllegalStateException("Cannot decode region after decoder has been recycled");
} finally {
decoderLock.readLock().unlock();
}
}
/**
* Acquire a read lock to prevent decoding overlapping with recycling, then check the pool still
* exists and acquire a decoder to load the requested region. There is no check whether the pool
* currently has decoders, because it's guaranteed to have one decoder after {@link #init(Context, Uri)}
* is called and be null once {@link #recycle()} is called. In practice the view can't call this
* method until after {@link #init(Context, Uri)}, so there will be no blocking on an empty pool.
*/
@Override
@NonNull
public Bitmap decodeRegion(@NonNull Rect sRect, int sampleSize) {
debug("Decode region " + sRect + " on thread " + Thread.currentThread().getName());
if (sRect.width() < imageDimensions.x || sRect.height() < imageDimensions.y) {
lazyInit();
}
decoderLock.readLock().lock();
try {
if (decoderPool != null) {
BitmapRegionDecoder decoder = decoderPool.acquire();
try {
// Decoder can't be null or recycled in practice
if (decoder != null && !decoder.isRecycled()) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
options.inPreferredConfig = bitmapConfig;
// If that is not set, some PNGs are read with a ColorSpace of code "Unknown" (-1),
// which makes resizing buggy (generates a black picture)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
options.inPreferredColorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
Bitmap bitmap = decoder.decodeRegion(sRect, options);
if (bitmap == null) {
throw new RuntimeException("Skia image decoder returned null bitmap - image format may not be supported");
}
return bitmap;
}
} finally {
if (decoder != null) {
decoderPool.release(decoder);
}
}
}
throw new IllegalStateException("Cannot decode region after decoder has been recycled");
} finally {
decoderLock.readLock().unlock();
}
}
/**
* Acquire a read lock to prevent decoding overlapping with recycling, then check the pool still
* exists and acquire a decoder to load the requested region. There is no check whether the pool
* currently has decoders, because it's guaranteed to have one decoder after {@link #init(Context, Uri)}
* is called and be null once {@link #recycle()} is called. In practice the view can't call this
* method until after {@link #init(Context, Uri)}, so there will be no blocking on an empty pool.
*/
@Override
@NonNull
public Bitmap decodeRegion(@NonNull Rect sRect, int sampleSize) {
debug("Decode region " + sRect + " on thread " + Thread.currentThread().getName());
if (sRect.width() < imageDimensions.x || sRect.height() < imageDimensions.y) {
lazyInit();
}
decoderLock.readLock().lock();
try {
if (decoderPool != null) {
BitmapRegionDecoder decoder = decoderPool.acquire();
try {
// Decoder can't be null or recycled in practice
if (decoder != null && !decoder.isRecycled()) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
options.inPreferredConfig = bitmapConfig;
Bitmap bitmap = decoder.decodeRegion(sRect, options);
if (bitmap == null) {
throw new RuntimeException("Skia image decoder returned null bitmap - image format may not be supported");
}
return bitmap;
}
} finally {
if (decoder != null) {
decoderPool.release(decoder);
}
}
}
throw new IllegalStateException("Cannot decode region after decoder has been recycled");
} finally {
decoderLock.readLock().unlock();
}
}
@Override
protected Bitmap doInBackground(Void... params) {
try {
if (decoderRef != null && tileRef != null && viewRef != null) {
final BitmapRegionDecoder decoder = decoderRef.get();
final Object decoderLock = decoderLockRef.get();
final Tile tile = tileRef.get();
final SubsamplingScaleImageView view = viewRef.get();
if (decoder != null && decoderLock != null && tile != null && view != null && !decoder.isRecycled()) {
synchronized (decoderLock) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = tile.sampleSize;
options.inPreferredConfig = Config.RGB_565;
options.inDither = true;
Bitmap bitmap = decoder.decodeRegion(view.fileSRect(tile.sRect), options);
int rotation = view.getRequiredRotation();
if (rotation != 0) {
Matrix matrix = new Matrix();
matrix.postRotate(rotation);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
return bitmap;
}
} else if (tile != null) {
tile.loading = false;
}
}
} catch (Exception e) {
Log.e(TAG, "Failed to decode tile", e);
}
return null;
}
/**
* Acquire a read lock to prevent decoding overlapping with recycling, then check the pool still
* exists and acquire a decoder to load the requested region. There is no check whether the pool
* currently has decoders, because it's guaranteed to have one decoder after {@link #init(Context, Uri)}
* is called and be null once {@link #recycle()} is called. In practice the view can't call this
* method until after {@link #init(Context, Uri)}, so there will be no blocking on an empty pool.
*/
@Override
@NonNull
public Bitmap decodeRegion(@NonNull Rect sRect, int sampleSize) {
debug("Decode region " + sRect + " on thread " + Thread.currentThread().getName());
if (sRect.width() < imageDimensions.x || sRect.height() < imageDimensions.y) {
lazyInit();
}
decoderLock.readLock().lock();
try {
if (decoderPool != null) {
BitmapRegionDecoder decoder = decoderPool.acquire();
try {
// Decoder can't be null or recycled in practice
if (decoder != null && !decoder.isRecycled()) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;
options.inPreferredConfig = bitmapConfig;
Bitmap bitmap = decoder.decodeRegion(sRect, options);
if (bitmap == null) {
throw new RuntimeException("Skia image decoder returned null bitmap - image format may not be supported");
}
return bitmap;
}
} finally {
if (decoder != null) {
decoderPool.release(decoder);
}
}
}
throw new IllegalStateException("Cannot decode region after decoder has been recycled");
} finally {
decoderLock.readLock().unlock();
}
}
private Bitmap createClippedBitmap() {
//if (mSampleSize <= 1) {
// TODO has problem, this method is not useful on some picture
// return mClipImageView.clip();
//}
final float[] matrixValues = mClipImageView.getClipMatrixValues();
final float scale = matrixValues[Matrix.MSCALE_X];
final float transX = matrixValues[Matrix.MTRANS_X];
final float transY = matrixValues[Matrix.MTRANS_Y];
final Rect border = mClipImageView.getClipBorder();
final float cropX = ((-transX + border.left) / scale) * mSampleSize;
final float cropY = ((-transY + border.top) / scale) * mSampleSize;
final float cropWidth = (border.width() / scale) * mSampleSize;
final float cropHeight = (border.height() / scale) * mSampleSize;
final RectF srcRect = new RectF(cropX, cropY, cropX + cropWidth, cropY + cropHeight);
final Rect clipRect = getRealRect(srcRect);
final BitmapFactory.Options ops = new BitmapFactory.Options();
final Matrix outputMatrix = new Matrix();
outputMatrix.setRotate(mDegree);
if (mMaxWidth > 0 && cropWidth > mMaxWidth) {
ops.inSampleSize = findBestSample((int) cropWidth, mMaxWidth);
final float outputScale = mMaxWidth / (cropWidth / ops.inSampleSize);
outputMatrix.postScale(outputScale, outputScale);
}
BitmapRegionDecoder decoder = null;
try {
decoder = BitmapRegionDecoder.newInstance(mInput, false);
final Bitmap source = decoder.decodeRegion(clipRect, ops);
recycleImageViewBitmap();
return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(),
outputMatrix, false);
} catch (Exception e) {
return mClipImageView.clip();
} finally {
if (decoder != null && !decoder.isRecycled()) {
decoder.recycle();
}
}
}
private Bitmap createClippedBitmap() {
if (mSampleSize <= 1) {
return mClipImageView.clip();
}
// 获取缩放位移后的矩阵值
final float[] matrixValues = mClipImageView.getClipMatrixValues();
final float scale = matrixValues[Matrix.MSCALE_X];
final float transX = matrixValues[Matrix.MTRANS_X];
final float transY = matrixValues[Matrix.MTRANS_Y];
// 获取在显示的图片中裁剪的位置
final Rect border = mClipImageView.getClipBorder();
final float cropX = ((-transX + border.left) / scale) * mSampleSize;
final float cropY = ((-transY + border.top) / scale) * mSampleSize;
final float cropWidth = (border.width() / scale) * mSampleSize;
final float cropHeight = (border.height() / scale) * mSampleSize;
// 获取在旋转之前的裁剪位置
final RectF srcRect = new RectF(cropX, cropY, cropX + cropWidth, cropY + cropHeight);
final Rect clipRect = getRealRect(srcRect);
final BitmapFactory.Options ops = new BitmapFactory.Options();
final Matrix outputMatrix = new Matrix();
outputMatrix.setRotate(mDegree);
// 如果裁剪之后的图片宽高仍然太大,则进行缩小
if (mMaxWidth > 0 && cropWidth > mMaxWidth) {
ops.inSampleSize = findBestSample((int) cropWidth, mMaxWidth);
final float outputScale = mMaxWidth / (cropWidth / ops.inSampleSize);
outputMatrix.postScale(outputScale, outputScale);
}
// 裁剪
BitmapRegionDecoder decoder = null;
try {
decoder = BitmapRegionDecoder.newInstance(mInput, false);
final Bitmap source = decoder.decodeRegion(clipRect, ops);
recycleImageViewBitmap();
return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), outputMatrix, false);
} catch (Exception e) {
return mClipImageView.clip();
} finally {
if (decoder != null && !decoder.isRecycled()) {
decoder.recycle();
}
}
}