Skip to content

Commit

Permalink
Faster ViewPager swiping; Improved logging; Fix: loading huge images;…
Browse files Browse the repository at this point in the history
… fix memory leak

This is a Backport of PhotoView improvements from  https://github.com/k3b/APhotoManager

* Faster initial loading so swiping through ViewPager is more fluent:
  * initially the view is loaded with low res image. (defined via PhotoView.setImageXxx)
  * on first zoom it is reloaded (defined via PhotoView.setImageReloadFile())
  * see k3b/APhotoManager#10 Fast swiping through images does not work
* Fix: No crash when loading images bigger than 4096x4096 or out of memory
  * HugeImageLoader.java
  * see k3b/APhotoManager#15
* Improved logging
  * possibility to reduce logging: either logg all or info+warn+err or warn+err
    * via LogManager.setLogger() and LogManager.enableDebug()
  * many internal functions get a string parameter `why` so logging shows why a function was called
* fix memory leak
  • Loading branch information
k3b committed Jun 12, 2016
1 parent cc328a3 commit be40f6c
Show file tree
Hide file tree
Showing 12 changed files with 406 additions and 48 deletions.
5 changes: 3 additions & 2 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ android {
defaultConfig {
minSdkVersion 4
targetSdkVersion 23
versionCode 1
versionName "1.0"
// use values from gradle.properties
versionCode=Long.valueOf(VERSION_CODE)
versionName=VERSION_NAME
}
}

Expand Down
101 changes: 101 additions & 0 deletions library/src/main/java/uk/co/senab/photoview/HugeImageLoader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package uk.co.senab.photoview;

import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.opengl.GLES20;
import android.os.Build;

import java.io.File;

import javax.microedition.khronos.opengles.GL10;

import uk.co.senab.photoview.log.LogManager;

/**
* If image is bigger that available memory
* load a scaled down version.
* see http://developer.android.com/training/displaying-bitmaps/index.html.
*
* Created by k3b on 14.09.2015.
*/
public class HugeImageLoader {
public static final String LOG_TAG = "HugeImageLoader";

// let debug flag be dynamic, but still Proguard can be used to remove from
// release builds
public static boolean DEBUG = true; //!!! Log.isLoggable(LOG_TAG, Log.DEBUG);

@TargetApi(Build.VERSION_CODES.FROYO)
public static int getMaxTextureSize() {
try {
int[] max = new int[1];
new GLES20().glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, max, 0);

if (max[0] > 0) {
return max[0];
}
} catch (Exception ex) {

}
return 4096;
}

public static Bitmap loadImage(File file, int maxWidth, int maxHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
// int imageHeight = options.outHeight;
// int imageWidth = options.outWidth;
// String imageType = options.outMimeType;

int downscale = calculateInSampleSize(options, maxWidth, maxHeight);
options.inSampleSize = downscale;

if (DEBUG) {
Runtime r = Runtime.getRuntime();
int width = options.outWidth;
int height = options.outHeight;
LogManager.getLogger().d(
LOG_TAG,
"loadImage(" +
"\n\t'" + file +
"', " + width +
"x" + height +
", max=" + maxWidth +
"x" + maxHeight +
", size=" + (width*height*4/1024) +
"k, " +
"\n\tmemory(total/free/avail)=(" + r.totalMemory()/1024 + "k,"+ r.freeMemory()/1024+ "k,"+ r.maxMemory()/1024 +
"k) ) " +
"\n\t==> " + width/downscale +
"x" + height/downscale +
", size=" + (width*height*4/1024/downscale/downscale) +
"k, scale=" + downscale);
}

// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(file.getAbsolutePath(), options);
}

private static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
int height = options.outHeight;
int width = options.outWidth;
int inSampleSize = 1;

// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((height > reqHeight)
|| (width > reqWidth)) {
inSampleSize *= 2;
height /= 2;
width /= 2;
}

return inSampleSize;
}
}
27 changes: 24 additions & 3 deletions library/src/main/java/uk/co/senab/photoview/PhotoView.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,18 @@
import android.view.GestureDetector;
import android.widget.ImageView;

import java.io.File;

import uk.co.senab.photoview.PhotoViewAttacher.OnMatrixChangedListener;
import uk.co.senab.photoview.PhotoViewAttacher.OnPhotoTapListener;
import uk.co.senab.photoview.PhotoViewAttacher.OnViewTapListener;

public class PhotoView extends ImageView implements IPhotoView {

// for debug purposes
private static int lastDebugId = 1;
private final String mDebugId;

private PhotoViewAttacher mAttacher;

private ScaleType mPendingScaleType;
Expand All @@ -45,10 +51,19 @@ public PhotoView(Context context, AttributeSet attr) {

public PhotoView(Context context, AttributeSet attr, int defStyle) {
super(context, attr, defStyle);
// for debug purposes
this.mDebugId = "PhotoView#" + (++lastDebugId);

super.setScaleType(ScaleType.MATRIX);
init();
}

// for debug purposes
@Override
public String toString() {
return mDebugId;
}

protected void init() {
if (null == mAttacher || null == mAttacher.getImageView()) {
mAttacher = new PhotoViewAttacher(this);
Expand Down Expand Up @@ -153,23 +168,23 @@ public void setScaleLevels(float minimumScale, float mediumScale, float maximumS
public void setImageDrawable(Drawable drawable) {
super.setImageDrawable(drawable);
if (null != mAttacher) {
mAttacher.update();
mAttacher.update("setImageDrawable");
}
}

@Override
public void setImageResource(int resId) {
super.setImageResource(resId);
if (null != mAttacher) {
mAttacher.update();
mAttacher.update("setImageResource");
}
}

@Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
if (null != mAttacher) {
mAttacher.update();
mAttacher.update("setImageURI");
}
}

Expand Down Expand Up @@ -255,6 +270,7 @@ public void setOnSingleFlingListener(PhotoViewAttacher.OnSingleFlingListener onS
@Override
protected void onDetachedFromWindow() {
mAttacher.cleanup();
mAttacher = null; // else memory leak
super.onDetachedFromWindow();
}

Expand All @@ -263,4 +279,9 @@ protected void onAttachedToWindow() {
init();
super.onAttachedToWindow();
}

/** k3b 20150913 #10: Faster initial loading: initially the view is loaded with low res image. on first zoom it is reloaded with this uri */
public void setImageReloadFile(File file) {
mAttacher.setImageReloadFile(file);
}
}
Loading

0 comments on commit be40f6c

Please sign in to comment.