blob: be1cd32be845d9049a297f432a30ce5e3da9c109 [file] [log] [blame]
David Morrisseyf06b6c02015-01-12 21:04:09 +00001package com.davemorrissey.labs.subscaleview.decoder;
2
3import android.content.ContentResolver;
4import android.content.Context;
5import android.content.pm.PackageManager;
6import android.content.res.Resources;
7import android.graphics.Bitmap;
8import android.graphics.BitmapFactory;
9import android.net.Uri;
David Morrisseyda5fff72017-12-03 09:38:16 +000010import android.support.annotation.Keep;
David Morrisseyf06b6c02015-01-12 21:04:09 +000011import android.text.TextUtils;
12
David Morrisseybd263d82017-11-26 08:37:31 +000013import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
14
David Morrisseyfc7ce782015-10-13 09:08:50 +010015import java.io.InputStream;
David Morrisseyf06b6c02015-01-12 21:04:09 +000016import java.util.List;
17
18/**
19 * Default implementation of {@link com.davemorrissey.labs.subscaleview.decoder.ImageDecoder}
20 * using Android's {@link android.graphics.BitmapFactory}, based on the Skia library. This
21 * works well in most circumstances and has reasonable performance, however it has some problems
22 * with grayscale, indexed and CMYK images.
23 */
24public class SkiaImageDecoder implements ImageDecoder {
25
26 private static final String FILE_PREFIX = "file://";
27 private static final String ASSET_PREFIX = FILE_PREFIX + "/android_asset/";
28 private static final String RESOURCE_PREFIX = ContentResolver.SCHEME_ANDROID_RESOURCE + "://";
29
Khabensky Denisc95f6e22017-10-10 12:57:13 +030030 private final Bitmap.Config bitmapConfig;
31
David Morrisseyda5fff72017-12-03 09:38:16 +000032 @Keep
33 @SuppressWarnings("unused")
Khabensky Denisc95f6e22017-10-10 12:57:13 +030034 public SkiaImageDecoder() {
35 this(null);
36 }
37
David Morrissey4856a412017-12-12 07:54:58 +000038 @SuppressWarnings({"WeakerAccess", "SameParameterValue"})
Khabensky Denisc95f6e22017-10-10 12:57:13 +030039 public SkiaImageDecoder(Bitmap.Config bitmapConfig) {
David Morrisseybd263d82017-11-26 08:37:31 +000040 Bitmap.Config globalBitmapConfig = SubsamplingScaleImageView.getPreferredBitmapConfig();
41 if (bitmapConfig != null) {
Khabensky Denisc95f6e22017-10-10 12:57:13 +030042 this.bitmapConfig = bitmapConfig;
David Morrisseybd263d82017-11-26 08:37:31 +000043 } else if (globalBitmapConfig != null) {
44 this.bitmapConfig = globalBitmapConfig;
45 } else {
46 this.bitmapConfig = Bitmap.Config.RGB_565;
David Morrisseyaca42712017-11-22 19:47:20 +000047 }
Khabensky Denisc95f6e22017-10-10 12:57:13 +030048 }
49
David Morrisseyf06b6c02015-01-12 21:04:09 +000050 @Override
51 public Bitmap decode(Context context, Uri uri) throws Exception {
52 String uriString = uri.toString();
53 BitmapFactory.Options options = new BitmapFactory.Options();
David Morrisseyc431b2a2015-06-14 17:55:11 +010054 Bitmap bitmap;
Khabensky Denisc95f6e22017-10-10 12:57:13 +030055 options.inPreferredConfig = bitmapConfig;
David Morrisseyf06b6c02015-01-12 21:04:09 +000056 if (uriString.startsWith(RESOURCE_PREFIX)) {
57 Resources res;
58 String packageName = uri.getAuthority();
59 if (context.getPackageName().equals(packageName)) {
60 res = context.getResources();
61 } else {
62 PackageManager pm = context.getPackageManager();
63 res = pm.getResourcesForApplication(packageName);
64 }
65
66 int id = 0;
67 List<String> segments = uri.getPathSegments();
68 int size = segments.size();
69 if (size == 2 && segments.get(0).equals("drawable")) {
70 String resName = segments.get(1);
71 id = res.getIdentifier(resName, "drawable", packageName);
72 } else if (size == 1 && TextUtils.isDigitsOnly(segments.get(0))) {
73 try {
74 id = Integer.parseInt(segments.get(0));
75 } catch (NumberFormatException ignored) {
76 }
77 }
78
David Morrisseyc431b2a2015-06-14 17:55:11 +010079 bitmap = BitmapFactory.decodeResource(context.getResources(), id, options);
David Morrisseyf06b6c02015-01-12 21:04:09 +000080 } else if (uriString.startsWith(ASSET_PREFIX)) {
81 String assetName = uriString.substring(ASSET_PREFIX.length());
David Morrisseyc431b2a2015-06-14 17:55:11 +010082 bitmap = BitmapFactory.decodeStream(context.getAssets().open(assetName), null, options);
David Morrisseyf06b6c02015-01-12 21:04:09 +000083 } else if (uriString.startsWith(FILE_PREFIX)) {
David Morrisseyc431b2a2015-06-14 17:55:11 +010084 bitmap = BitmapFactory.decodeFile(uriString.substring(FILE_PREFIX.length()), options);
David Morrisseyf06b6c02015-01-12 21:04:09 +000085 } else {
David Morrisseyfc7ce782015-10-13 09:08:50 +010086 InputStream inputStream = null;
87 try {
88 ContentResolver contentResolver = context.getContentResolver();
89 inputStream = contentResolver.openInputStream(uri);
90 bitmap = BitmapFactory.decodeStream(inputStream, null, options);
91 } finally {
92 if (inputStream != null) {
David Morrisseyda5fff72017-12-03 09:38:16 +000093 try { inputStream.close(); } catch (Exception e) { /* Ignore */ }
David Morrisseyfc7ce782015-10-13 09:08:50 +010094 }
95 }
David Morrisseyf06b6c02015-01-12 21:04:09 +000096 }
David Morrisseyc431b2a2015-06-14 17:55:11 +010097 if (bitmap == null) {
98 throw new RuntimeException("Skia image region decoder returned null bitmap - image format may not be supported");
99 }
100 return bitmap;
David Morrisseyf06b6c02015-01-12 21:04:09 +0000101 }
102}