Forked from gokhanbarisaker/PicassoDecoder.java
Last active
November 26, 2021 14:17
-
-
Save davemorrissey/e2781ba5b966c9e95539 to your computer and use it in GitHub Desktop.
Picasso decoder for subsampling-scale-image-view
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Created by gokhanbarisaker on 8/30/15. | |
*/ | |
public class PicassoDecoder implements ImageDecoder | |
{ | |
private String tag; | |
private String picasso; | |
public PicassoDecoder(String tag, Picasso picasso) { | |
this.tag = tag; | |
this.picasso = picasso; | |
} | |
@Override | |
public Bitmap decode(Context context, Uri uri) throws Exception { | |
return picasso | |
.load(uri) | |
.tag(tag) | |
.config(Bitmap.Config.RGB_565) | |
.memoryPolicy(MemoryPolicy.NO_CACHE) | |
.get(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Created by gokhanbarisaker on 8/30/15. | |
*/ | |
public class PicassoRegionDecoder implements ImageRegionDecoder { | |
private OkHttpClient client; | |
private BitmapRegionDecoder decoder; | |
private final Object decoderLock = new Object(); | |
public PicassoRegionDecoder (OkHttpClient client) { | |
this.client = client; | |
} | |
@Override | |
public Point init(Context context, Uri uri) throws Exception { | |
OkHttpDownloader downloader = new OkHttpDownloader(client); | |
InputStream inputStream = downloader.load(uri, 0).getInputStream(); | |
this.decoder = BitmapRegionDecoder.newInstance(inputStream, false); | |
return new Point(this.decoder.getWidth(), this.decoder.getHeight()); | |
} | |
@Override | |
public Bitmap decodeRegion(Rect rect, int sampleSize) { | |
synchronized(this.decoderLock) { | |
BitmapFactory.Options options = new BitmapFactory.Options(); | |
options.inSampleSize = sampleSize; | |
options.inPreferredConfig = Bitmap.Config.RGB_565; | |
Bitmap bitmap = this.decoder.decodeRegion(rect, options); | |
if(bitmap == null) { | |
throw new RuntimeException("Region decoder returned null bitmap - image format may not be supported"); | |
} else { | |
return bitmap; | |
} | |
} | |
} | |
@Override | |
public boolean isReady() { | |
return this.decoder != null && !this.decoder.isRecycled(); | |
} | |
@Override | |
public void recycle() { | |
this.decoder.recycle(); | |
} | |
} |
Jesus! Combine libraries can be a nightmare sometimes. You need to line up the proper versions of each library.
These are the versions that work for me:
implementation 'com.davemorrissey.labs:subsampling-scale-image-view:3.10.0'
implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'com.squareup.okhttp:okhttp:2.1.0'
Anyone got a code snippet that uses the isImageLoaded() override so I can have a "downloading...." display until the image comes up when using this with Picasso/Okhttp?
(Nevermind -- figured it out ;-) I like this a lot....)
@tickerguy how did you do that?
use the SSIV listener
image.setOnImageEventListener(new SubsamplingScaleImageView.OnImageEventListener() {
@Override
public void onReady() {
//You could start loading your image here, so the ssiv is ready.
}
@Override
public void onImageLoaded() {
//Do your stuff here.
}
@Override
public void onPreviewLoadError(Exception e) {}
@Override
public void onImageLoadError(Exception e) {}
@Override
public void onTileLoadError(Exception e) {}
@Override
public void onPreviewReleased() {}
});
** How to intercept the bitmap before the SSIV** : using Glide : (Working on 8k images with latest implementation 4.11.0)
Glide.with(ssiv.getContext())
.asBitmap()
.load("https://vah.dy.fi/testcard/7680x4320.png") //Your own link
.into(new CustomTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
//Do whatever you want with the bitmap (like saving it to storage)
//ex : ssiv.setImage(ImageSource.bitmap(resource);
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
}
});
Provide a decoder based on HttpUrlConnection
, not OkHttp
or Picasso
dependency needed. Use only if you don't need image cache.
class URLImageDecoder : ImageDecoder {
private var bitmapConfig: Bitmap.Config =
SubsamplingScaleImageView.getPreferredBitmapConfig() ?: Bitmap.Config.RGB_565
@Throws(java.lang.Exception::class)
override fun decode(context: Context, uri: Uri): Bitmap {
val options = BitmapFactory.Options().apply { inPreferredConfig = bitmapConfig }
val bitmap: Bitmap?
var inputStream: InputStream? = null
try {
inputStream = URL(uri.toString()).openStream()
bitmap = BitmapFactory.decodeStream(inputStream, null, options)
} finally {
try {
inputStream?.close()
} catch (ignored: Exception) {
}
}
return bitmap
?: throw RuntimeException("Region decoder returned null bitmap - image format may not be supported")
}
}
class URLImageRegionDecoder : ImageRegionDecoder {
private lateinit var decoder: BitmapRegionDecoder
private val decoderLock: ReadWriteLock = ReentrantReadWriteLock(true)
private val bitmapConfig: Bitmap.Config =
SubsamplingScaleImageView.getPreferredBitmapConfig() ?: Bitmap.Config.RGB_565
@Throws(Exception::class)
override fun init(context: Context, uri: Uri): Point {
var inputStream: InputStream? = null
try {
inputStream = URL(uri.toString()).openStream()
decoder = BitmapRegionDecoder.newInstance(inputStream, false)
} finally {
try {
inputStream?.close()
} catch (ignored: Exception) {
}
}
return Point(decoder.width, decoder.height)
}
override fun decodeRegion(rect: Rect, sampleSize: Int): Bitmap {
getDecodeLock().lock()
return try {
if (!decoder.isRecycled) {
val options = BitmapFactory.Options()
options.inSampleSize = sampleSize
options.inPreferredConfig = bitmapConfig
decoder.decodeRegion(rect, options) ?: throw java.lang.RuntimeException(
"Skia image decoder returned null bitmap - image format may not be supported")
} else {
throw IllegalStateException("Cannot decode region after decoder has been recycled")
}
} finally {
getDecodeLock().unlock()
}
}
override fun isReady(): Boolean = !decoder.isRecycled
override fun recycle() = decoder.recycle()
/**
* Before SDK 21, BitmapRegionDecoder was not synchronized internally. Any attempt to decode
* regions from multiple threads with one decoder instance causes a segfault. For old versions
* use the write lock to enforce single threaded decoding.
*/
private fun getDecodeLock(): Lock {
return if (Build.VERSION.SDK_INT < 21) {
decoderLock.writeLock()
} else {
decoderLock.readLock()
}
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
With the new Picasso snapshot the
init
function of thePicassoRegionDecoder
looks like this: