package cc.shinichi.library.view.subsampling.decoder; import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapRegionDecoder; import android.graphics.Point; import android.graphics.Rect; import android.net.Uri; import android.text.TextUtils; import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import java.io.InputStream; import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import cc.shinichi.library.view.subsampling.SubsamplingScaleImageView; /** * Default implementation of {@link cc.shinichi.library.view.subsampling.decoder.ImageRegionDecoder} * using Android's {@link BitmapRegionDecoder}, based on the Skia library. This * works well in most circumstances and has reasonable performance due to the cached decoder instance, * however it has some problems with grayscale, indexed and CMYK images. *
* A {@link ReadWriteLock} is used to delegate responsibility for multi threading behaviour to the
* {@link BitmapRegionDecoder} instance on SDK >= 21, whilst allowing this class to block until no
* tiles are being loaded before recycling the decoder. In practice, {@link BitmapRegionDecoder} is
* synchronized internally so this has no real impact on performance.
*/
public class SkiaImageRegionDecoder implements ImageRegionDecoder {
private static final String FILE_PREFIX = "file://";
private static final String ASSET_PREFIX = FILE_PREFIX + "/android_asset/";
private static final String RESOURCE_PREFIX = ContentResolver.SCHEME_ANDROID_RESOURCE + "://";
private final ReadWriteLock decoderLock = new ReentrantReadWriteLock(true);
private final Bitmap.Config bitmapConfig;
private BitmapRegionDecoder decoder;
@Keep
@SuppressWarnings("unused")
public SkiaImageRegionDecoder() {
this(null);
}
@SuppressWarnings({"WeakerAccess", "SameParameterValue"})
public SkiaImageRegionDecoder(@Nullable Bitmap.Config bitmapConfig) {
Bitmap.Config globalBitmapConfig = SubsamplingScaleImageView.getPreferredBitmapConfig();
if (bitmapConfig != null) {
this.bitmapConfig = bitmapConfig;
} else if (globalBitmapConfig != null) {
this.bitmapConfig = globalBitmapConfig;
} else {
this.bitmapConfig = Bitmap.Config.RGB_565;
}
}
@Override
@NonNull
public Point init(Context context, @NonNull Uri uri) throws Exception {
String uriString = uri.toString();
if (uriString.startsWith(RESOURCE_PREFIX)) {
Resources res;
String packageName = uri.getAuthority();
if (context.getPackageName().equals(packageName)) {
res = context.getResources();
} else {
PackageManager pm = context.getPackageManager();
res = pm.getResourcesForApplication(packageName);
}
int id = 0;
List