1 package com.davemorrissey.labs.subscaleview; 2 3 import android.graphics.Bitmap; 4 import android.graphics.Rect; 5 import android.net.Uri; 6 7 import java.io.File; 8 import java.io.UnsupportedEncodingException; 9 import java.net.URLDecoder; 10 11 /** 12 * Helper class used to set the source and additional attributes from a variety of sources. Supports 13 * use of a bitmap, asset, resource, external file or any other URI. 14 * 15 * When you are using a preview image, you must set the dimensions of the full size image on the 16 * ImageSource object for the full size image using the {@link #dimensions(int, int)} method. 17 */ 18 @SuppressWarnings({"unused", "WeakerAccess"}) 19 public final class ImageSource { 20 21 static final String FILE_SCHEME = "file:///"; 22 static final String ASSET_SCHEME = "file:///android_asset/"; 23 24 private final Uri uri; 25 private final Bitmap bitmap; 26 private final Integer resource; 27 private boolean tile; 28 private int sWidth; 29 private int sHeight; 30 private Rect sRegion; 31 private boolean cached; 32 ImageSource(Bitmap bitmap, boolean cached)33 private ImageSource(Bitmap bitmap, boolean cached) { 34 this.bitmap = bitmap; 35 this.uri = null; 36 this.resource = null; 37 this.tile = false; 38 this.sWidth = bitmap.getWidth(); 39 this.sHeight = bitmap.getHeight(); 40 this.cached = cached; 41 } 42 ImageSource(Uri uri)43 private ImageSource(Uri uri) { 44 // #114 If file doesn't exist, attempt to url decode the URI and try again 45 String uriString = uri.toString(); 46 if (uriString.startsWith(FILE_SCHEME)) { 47 File uriFile = new File(uriString.substring(FILE_SCHEME.length() - 1)); 48 if (!uriFile.exists()) { 49 try { 50 uri = Uri.parse(URLDecoder.decode(uriString, "UTF-8")); 51 } catch (UnsupportedEncodingException e) { 52 // Fallback to encoded URI. This exception is not expected. 53 } 54 } 55 } 56 this.bitmap = null; 57 this.uri = uri; 58 this.resource = null; 59 this.tile = true; 60 } 61 ImageSource(int resource)62 private ImageSource(int resource) { 63 this.bitmap = null; 64 this.uri = null; 65 this.resource = resource; 66 this.tile = true; 67 } 68 69 /** 70 * Create an instance from a resource. The correct resource for the device screen resolution will be used. 71 * @param resId resource ID. 72 * @return an {@link ImageSource} instance. 73 */ resource(int resId)74 public static ImageSource resource(int resId) { 75 return new ImageSource(resId); 76 } 77 78 /** 79 * Create an instance from an asset name. 80 * @param assetName asset name. 81 * @return an {@link ImageSource} instance. 82 */ asset(String assetName)83 public static ImageSource asset(String assetName) { 84 if (assetName == null) { 85 throw new NullPointerException("Asset name must not be null"); 86 } 87 return uri(ASSET_SCHEME + assetName); 88 } 89 90 /** 91 * Create an instance from a URI. If the URI does not start with a scheme, it's assumed to be the URI 92 * of a file. 93 * @param uri image URI. 94 * @return an {@link ImageSource} instance. 95 */ uri(String uri)96 public static ImageSource uri(String uri) { 97 if (uri == null) { 98 throw new NullPointerException("Uri must not be null"); 99 } 100 if (!uri.contains("://")) { 101 if (uri.startsWith("/")) { 102 uri = uri.substring(1); 103 } 104 uri = FILE_SCHEME + uri; 105 } 106 return new ImageSource(Uri.parse(uri)); 107 } 108 109 /** 110 * Create an instance from a URI. 111 * @param uri image URI. 112 * @return an {@link ImageSource} instance. 113 */ uri(Uri uri)114 public static ImageSource uri(Uri uri) { 115 if (uri == null) { 116 throw new NullPointerException("Uri must not be null"); 117 } 118 return new ImageSource(uri); 119 } 120 121 /** 122 * Provide a loaded bitmap for display. 123 * @param bitmap bitmap to be displayed. 124 * @return an {@link ImageSource} instance. 125 */ bitmap(Bitmap bitmap)126 public static ImageSource bitmap(Bitmap bitmap) { 127 if (bitmap == null) { 128 throw new NullPointerException("Bitmap must not be null"); 129 } 130 return new ImageSource(bitmap, false); 131 } 132 133 /** 134 * Provide a loaded and cached bitmap for display. This bitmap will not be recycled when it is no 135 * longer needed. Use this method if you loaded the bitmap with an image loader such as Picasso 136 * or Volley. 137 * @param bitmap bitmap to be displayed. 138 * @return an {@link ImageSource} instance. 139 */ cachedBitmap(Bitmap bitmap)140 public static ImageSource cachedBitmap(Bitmap bitmap) { 141 if (bitmap == null) { 142 throw new NullPointerException("Bitmap must not be null"); 143 } 144 return new ImageSource(bitmap, true); 145 } 146 147 /** 148 * Enable tiling of the image. This does not apply to preview images which are always loaded as a single bitmap., 149 * and tiling cannot be disabled when displaying a region of the source image. 150 * @return this instance for chaining. 151 */ tilingEnabled()152 public ImageSource tilingEnabled() { 153 return tiling(true); 154 } 155 156 /** 157 * Disable tiling of the image. This does not apply to preview images which are always loaded as a single bitmap, 158 * and tiling cannot be disabled when displaying a region of the source image. 159 * @return this instance for chaining. 160 */ tilingDisabled()161 public ImageSource tilingDisabled() { 162 return tiling(false); 163 } 164 165 /** 166 * Enable or disable tiling of the image. This does not apply to preview images which are always loaded as a single bitmap, 167 * and tiling cannot be disabled when displaying a region of the source image. 168 * @param tile whether tiling should be enabled. 169 * @return this instance for chaining. 170 */ tiling(boolean tile)171 public ImageSource tiling(boolean tile) { 172 this.tile = tile; 173 return this; 174 } 175 176 /** 177 * Use a region of the source image. Region must be set independently for the full size image and the preview if 178 * you are using one. 179 * @param sRegion the region of the source image to be displayed. 180 * @return this instance for chaining. 181 */ region(Rect sRegion)182 public ImageSource region(Rect sRegion) { 183 this.sRegion = sRegion; 184 setInvariants(); 185 return this; 186 } 187 188 /** 189 * Declare the dimensions of the image. This is only required for a full size image, when you are specifying a URI 190 * and also a preview image. When displaying a bitmap object, or not using a preview, you do not need to declare 191 * the image dimensions. Note if the declared dimensions are found to be incorrect, the view will reset. 192 * @param sWidth width of the source image. 193 * @param sHeight height of the source image. 194 * @return this instance for chaining. 195 */ dimensions(int sWidth, int sHeight)196 public ImageSource dimensions(int sWidth, int sHeight) { 197 if (bitmap == null) { 198 this.sWidth = sWidth; 199 this.sHeight = sHeight; 200 } 201 setInvariants(); 202 return this; 203 } 204 setInvariants()205 private void setInvariants() { 206 if (this.sRegion != null) { 207 this.tile = true; 208 this.sWidth = this.sRegion.width(); 209 this.sHeight = this.sRegion.height(); 210 } 211 } 212 getUri()213 protected final Uri getUri() { 214 return uri; 215 } 216 getBitmap()217 protected final Bitmap getBitmap() { 218 return bitmap; 219 } 220 getResource()221 protected final Integer getResource() { 222 return resource; 223 } 224 getTile()225 protected final boolean getTile() { 226 return tile; 227 } 228 getSWidth()229 protected final int getSWidth() { 230 return sWidth; 231 } 232 getSHeight()233 protected final int getSHeight() { 234 return sHeight; 235 } 236 getSRegion()237 protected final Rect getSRegion() { 238 return sRegion; 239 } 240 isCached()241 protected final boolean isCached() { 242 return cached; 243 } 244 } 245