mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-05 18:25:09 +01:00
Include Subsampling Scale Image View as library to allow preloading tiles when a max bitmap size is provided.
This commit is contained in:
parent
80a59548a5
commit
322f54380d
@ -82,6 +82,7 @@ dependencies {
|
|||||||
final ICEPICK_VERSION = '3.1.0'
|
final ICEPICK_VERSION = '3.1.0'
|
||||||
|
|
||||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
compile project(":SubsamplingScaleImageView")
|
||||||
|
|
||||||
compile "com.android.support:support-v4:$SUPPORT_LIBRARY_VERSION"
|
compile "com.android.support:support-v4:$SUPPORT_LIBRARY_VERSION"
|
||||||
compile "com.android.support:appcompat-v7:$SUPPORT_LIBRARY_VERSION"
|
compile "com.android.support:appcompat-v7:$SUPPORT_LIBRARY_VERSION"
|
||||||
@ -107,7 +108,6 @@ dependencies {
|
|||||||
compile 'com.jakewharton.timber:timber:4.1.0'
|
compile 'com.jakewharton.timber:timber:4.1.0'
|
||||||
compile 'uk.co.ribot:easyadapter:1.5.0@aar'
|
compile 'uk.co.ribot:easyadapter:1.5.0@aar'
|
||||||
compile 'ch.acra:acra:4.7.0'
|
compile 'ch.acra:acra:4.7.0'
|
||||||
compile 'com.davemorrissey.labs:subsampling-scale-image-view:3.4.1'
|
|
||||||
compile "frankiesardo:icepick:$ICEPICK_VERSION"
|
compile "frankiesardo:icepick:$ICEPICK_VERSION"
|
||||||
provided "frankiesardo:icepick-processor:$ICEPICK_VERSION"
|
provided "frankiesardo:icepick-processor:$ICEPICK_VERSION"
|
||||||
compile 'com.github.dmytrodanylyk.android-process-button:library:1.0.4'
|
compile 'com.github.dmytrodanylyk.android-process-button:library:1.0.4'
|
||||||
|
@ -34,6 +34,7 @@ import eu.kanade.mangafeed.ui.reader.viewer.horizontal.LeftToRightReader;
|
|||||||
import eu.kanade.mangafeed.ui.reader.viewer.horizontal.RightToLeftReader;
|
import eu.kanade.mangafeed.ui.reader.viewer.horizontal.RightToLeftReader;
|
||||||
import eu.kanade.mangafeed.ui.reader.viewer.vertical.VerticalReader;
|
import eu.kanade.mangafeed.ui.reader.viewer.vertical.VerticalReader;
|
||||||
import eu.kanade.mangafeed.ui.reader.viewer.webtoon.WebtoonReader;
|
import eu.kanade.mangafeed.ui.reader.viewer.webtoon.WebtoonReader;
|
||||||
|
import eu.kanade.mangafeed.util.GLUtil;
|
||||||
import eu.kanade.mangafeed.util.ToastUtil;
|
import eu.kanade.mangafeed.util.ToastUtil;
|
||||||
import icepick.Icepick;
|
import icepick.Icepick;
|
||||||
import nucleus.factory.RequiresPresenter;
|
import nucleus.factory.RequiresPresenter;
|
||||||
@ -57,6 +58,8 @@ public class ReaderActivity extends BaseRxActivity<ReaderPresenter> {
|
|||||||
protected CompositeSubscription subscriptions;
|
protected CompositeSubscription subscriptions;
|
||||||
private Subscription customBrightnessSubscription;
|
private Subscription customBrightnessSubscription;
|
||||||
|
|
||||||
|
private int maxBitmapSize;
|
||||||
|
|
||||||
public static final int LEFT_TO_RIGHT = 1;
|
public static final int LEFT_TO_RIGHT = 1;
|
||||||
public static final int RIGHT_TO_LEFT = 2;
|
public static final int RIGHT_TO_LEFT = 2;
|
||||||
public static final int VERTICAL = 3;
|
public static final int VERTICAL = 3;
|
||||||
@ -88,6 +91,8 @@ public class ReaderActivity extends BaseRxActivity<ReaderPresenter> {
|
|||||||
setBlackTheme();
|
setBlackTheme();
|
||||||
|
|
||||||
initializeSettings();
|
initializeSettings();
|
||||||
|
|
||||||
|
maxBitmapSize = GLUtil.getMaxTextureSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -282,4 +287,8 @@ public class ReaderActivity extends BaseRxActivity<ReaderPresenter> {
|
|||||||
return viewer;
|
return viewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getMaxBitmapSize() {
|
||||||
|
return maxBitmapSize;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,8 @@ public class ViewPagerReaderFragment extends BaseFragment {
|
|||||||
progressText.setTextColor(ContextCompat.getColor(getContext(), R.color.light_grey));
|
progressText.setTextColor(ContextCompat.getColor(getContext(), R.color.light_grey));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
imageView.setParallelLoadingEnabled(true);
|
||||||
|
imageView.setMaxDimensions(activity.getMaxBitmapSize(), activity.getMaxBitmapSize());
|
||||||
imageView.setDoubleTapZoomStyle(SubsamplingScaleImageView.ZOOM_FOCUS_FIXED);
|
imageView.setDoubleTapZoomStyle(SubsamplingScaleImageView.ZOOM_FOCUS_FIXED);
|
||||||
imageView.setPanLimit(SubsamplingScaleImageView.PAN_LIMIT_INSIDE);
|
imageView.setPanLimit(SubsamplingScaleImageView.PAN_LIMIT_INSIDE);
|
||||||
imageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE);
|
imageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE);
|
||||||
@ -103,7 +105,7 @@ public class ViewPagerReaderFragment extends BaseFragment {
|
|||||||
if (page == null || page.getImagePath() == null)
|
if (page == null || page.getImagePath() == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
imageView.setImage(ImageSource.uri(page.getImagePath()).tilingDisabled());
|
imageView.setImage(ImageSource.uri(page.getImagePath()));
|
||||||
progressContainer.setVisibility(View.GONE);
|
progressContainer.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
50
app/src/main/java/eu/kanade/mangafeed/util/GLUtil.java
Normal file
50
app/src/main/java/eu/kanade/mangafeed/util/GLUtil.java
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package eu.kanade.mangafeed.util;
|
||||||
|
|
||||||
|
import javax.microedition.khronos.egl.EGL10;
|
||||||
|
import javax.microedition.khronos.egl.EGLConfig;
|
||||||
|
import javax.microedition.khronos.egl.EGLContext;
|
||||||
|
import javax.microedition.khronos.egl.EGLDisplay;
|
||||||
|
|
||||||
|
public class GLUtil {
|
||||||
|
|
||||||
|
public static int getMaxTextureSize() {
|
||||||
|
// Safe minimum default size
|
||||||
|
final int IMAGE_MAX_BITMAP_DIMENSION = 2048;
|
||||||
|
|
||||||
|
// Get EGL Display
|
||||||
|
EGL10 egl = (EGL10) EGLContext.getEGL();
|
||||||
|
EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
|
||||||
|
|
||||||
|
// Initialise
|
||||||
|
int[] version = new int[2];
|
||||||
|
egl.eglInitialize(display, version);
|
||||||
|
|
||||||
|
// Query total number of configurations
|
||||||
|
int[] totalConfigurations = new int[1];
|
||||||
|
egl.eglGetConfigs(display, null, 0, totalConfigurations);
|
||||||
|
|
||||||
|
// Query actual list configurations
|
||||||
|
EGLConfig[] configurationsList = new EGLConfig[totalConfigurations[0]];
|
||||||
|
egl.eglGetConfigs(display, configurationsList, totalConfigurations[0], totalConfigurations);
|
||||||
|
|
||||||
|
int[] textureSize = new int[1];
|
||||||
|
int maximumTextureSize = 0;
|
||||||
|
|
||||||
|
// Iterate through all the configurations to located the maximum texture size
|
||||||
|
for (int i = 0; i < totalConfigurations[0]; i++) {
|
||||||
|
// Only need to check for width since opengl textures are always squared
|
||||||
|
egl.eglGetConfigAttrib(display, configurationsList[i], EGL10.EGL_MAX_PBUFFER_WIDTH, textureSize);
|
||||||
|
|
||||||
|
// Keep track of the maximum texture size
|
||||||
|
if (maximumTextureSize < textureSize[0])
|
||||||
|
maximumTextureSize = textureSize[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release
|
||||||
|
egl.eglTerminate(display);
|
||||||
|
|
||||||
|
// Return largest texture size found, or default
|
||||||
|
return Math.max(maximumTextureSize, IMAGE_MAX_BITMAP_DIMENSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
1
libs/SubsamplingScaleImageView/.gitignore
vendored
Normal file
1
libs/SubsamplingScaleImageView/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
build/
|
7
libs/SubsamplingScaleImageView/AndroidManifest.xml
Normal file
7
libs/SubsamplingScaleImageView/AndroidManifest.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.davemorrissey.labs.subscaleview"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0">
|
||||||
|
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="14"/>
|
||||||
|
</manifest>
|
38
libs/SubsamplingScaleImageView/build.gradle
Normal file
38
libs/SubsamplingScaleImageView/build.gradle
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
group = 'com.davemorrissey.labs'
|
||||||
|
version = '3.4.1'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile fileTree(dir: 'libs', include: '*.jar')
|
||||||
|
compile "com.android.support:support-annotations:23.1.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 23
|
||||||
|
buildToolsVersion "23.0.2"
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
manifest.srcFile 'AndroidManifest.xml'
|
||||||
|
java.srcDirs = ['src']
|
||||||
|
resources.srcDirs = ['src']
|
||||||
|
aidl.srcDirs = ['src']
|
||||||
|
renderscript.srcDirs = ['src']
|
||||||
|
res.srcDirs = ['res']
|
||||||
|
assets.srcDirs = ['assets']
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the tests to tests/java, tests/res, etc...
|
||||||
|
instrumentTest.setRoot('tests')
|
||||||
|
|
||||||
|
// Move the build types to build-types/<type>
|
||||||
|
// For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
|
||||||
|
// This moves them out of them default location under src/<type>/... which would
|
||||||
|
// conflict with src/ being used by the main source set.
|
||||||
|
// Adding new build types or product flavors should be accompanied
|
||||||
|
// by a similar customization.
|
||||||
|
debug.setRoot('build-types/debug')
|
||||||
|
release.setRoot('build-types/release')
|
||||||
|
}
|
||||||
|
}
|
15
libs/SubsamplingScaleImageView/project.properties
Normal file
15
libs/SubsamplingScaleImageView/project.properties
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# This file is automatically generated by Android Tools.
|
||||||
|
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||||
|
#
|
||||||
|
# This file must be checked in Version Control Systems.
|
||||||
|
#
|
||||||
|
# To customize properties used by the Ant build system edit
|
||||||
|
# "ant.properties", and override values to adapt the script to your
|
||||||
|
# project structure.
|
||||||
|
#
|
||||||
|
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||||
|
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||||
|
|
||||||
|
# Project target.
|
||||||
|
target=android-19
|
||||||
|
android.library=true
|
28
libs/SubsamplingScaleImageView/res/values/attrs.xml
Normal file
28
libs/SubsamplingScaleImageView/res/values/attrs.xml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2014 David Morrissey
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<declare-styleable name="SubsamplingScaleImageView">
|
||||||
|
<attr name="src" format="reference"/>
|
||||||
|
<attr name="assetName" format="string"/>
|
||||||
|
<attr name="panEnabled" format="boolean"/>
|
||||||
|
<attr name="zoomEnabled" format="boolean"/>
|
||||||
|
<attr name="quickScaleEnabled" format="boolean"/>
|
||||||
|
<attr name="tileBackgroundColor" format="color"/>
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
|
</resources>
|
@ -0,0 +1,233 @@
|
|||||||
|
package com.davemorrissey.labs.subscaleview;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class used to set the source and additional attributes from a variety of sources. Supports
|
||||||
|
* use of a bitmap, asset, resource, external file or any other URI.
|
||||||
|
*
|
||||||
|
* When you are using a preview image, you must set the dimensions of the full size image on the
|
||||||
|
* ImageSource object for the full size image using the {@link #dimensions(int, int)} method.
|
||||||
|
*/
|
||||||
|
public final class ImageSource {
|
||||||
|
|
||||||
|
static final String FILE_SCHEME = "file:///";
|
||||||
|
static final String ASSET_SCHEME = "file:///android_asset/";
|
||||||
|
|
||||||
|
private final Uri uri;
|
||||||
|
private final Bitmap bitmap;
|
||||||
|
private final Integer resource;
|
||||||
|
private boolean tile;
|
||||||
|
private int sWidth;
|
||||||
|
private int sHeight;
|
||||||
|
private Rect sRegion;
|
||||||
|
private boolean cached;
|
||||||
|
|
||||||
|
private ImageSource(Bitmap bitmap, boolean cached) {
|
||||||
|
this.bitmap = bitmap;
|
||||||
|
this.uri = null;
|
||||||
|
this.resource = null;
|
||||||
|
this.tile = false;
|
||||||
|
this.sWidth = bitmap.getWidth();
|
||||||
|
this.sHeight = bitmap.getHeight();
|
||||||
|
this.cached = cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImageSource(Uri uri) {
|
||||||
|
// #114 If file doesn't exist, attempt to url decode the URI and try again
|
||||||
|
String uriString = uri.toString();
|
||||||
|
if (uriString.startsWith(FILE_SCHEME)) {
|
||||||
|
File uriFile = new File(uriString.substring(FILE_SCHEME.length() - 1));
|
||||||
|
if (!uriFile.exists()) {
|
||||||
|
try {
|
||||||
|
uri = Uri.parse(URLDecoder.decode(uriString, "UTF-8"));
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
// Fallback to encoded URI. This exception is not expected.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.bitmap = null;
|
||||||
|
this.uri = uri;
|
||||||
|
this.resource = null;
|
||||||
|
this.tile = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImageSource(int resource) {
|
||||||
|
this.bitmap = null;
|
||||||
|
this.uri = null;
|
||||||
|
this.resource = resource;
|
||||||
|
this.tile = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance from a resource. The correct resource for the device screen resolution will be used.
|
||||||
|
* @param resId resource ID.
|
||||||
|
*/
|
||||||
|
public static ImageSource resource(int resId) {
|
||||||
|
return new ImageSource(resId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance from an asset name.
|
||||||
|
* @param assetName asset name.
|
||||||
|
*/
|
||||||
|
public static ImageSource asset(String assetName) {
|
||||||
|
if (assetName == null) {
|
||||||
|
throw new NullPointerException("Asset name must not be null");
|
||||||
|
}
|
||||||
|
return uri(ASSET_SCHEME + assetName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance from a URI. If the URI does not start with a scheme, it's assumed to be the URI
|
||||||
|
* of a file.
|
||||||
|
* @param uri image URI.
|
||||||
|
*/
|
||||||
|
public static ImageSource uri(String uri) {
|
||||||
|
if (uri == null) {
|
||||||
|
throw new NullPointerException("Uri must not be null");
|
||||||
|
}
|
||||||
|
if (!uri.contains("://")) {
|
||||||
|
if (uri.startsWith("/")) {
|
||||||
|
uri = uri.substring(1);
|
||||||
|
}
|
||||||
|
uri = FILE_SCHEME + uri;
|
||||||
|
}
|
||||||
|
return new ImageSource(Uri.parse(uri));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance from a URI.
|
||||||
|
* @param uri image URI.
|
||||||
|
*/
|
||||||
|
public static ImageSource uri(Uri uri) {
|
||||||
|
if (uri == null) {
|
||||||
|
throw new NullPointerException("Uri must not be null");
|
||||||
|
}
|
||||||
|
return new ImageSource(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a loaded bitmap for display.
|
||||||
|
* @param bitmap bitmap to be displayed.
|
||||||
|
*/
|
||||||
|
public static ImageSource bitmap(Bitmap bitmap) {
|
||||||
|
if (bitmap == null) {
|
||||||
|
throw new NullPointerException("Bitmap must not be null");
|
||||||
|
}
|
||||||
|
return new ImageSource(bitmap, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a loaded and cached bitmap for display. This bitmap will not be recycled when it is no
|
||||||
|
* longer needed. Use this method if you loaded the bitmap with an image loader such as Picasso
|
||||||
|
* or Volley.
|
||||||
|
* @param bitmap bitmap to be displayed.
|
||||||
|
*/
|
||||||
|
public static ImageSource cachedBitmap(Bitmap bitmap) {
|
||||||
|
if (bitmap == null) {
|
||||||
|
throw new NullPointerException("Bitmap must not be null");
|
||||||
|
}
|
||||||
|
return new ImageSource(bitmap, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable tiling of the image. This does not apply to preview images which are always loaded as a single bitmap.,
|
||||||
|
* and tiling cannot be disabled when displaying a region of the source image.
|
||||||
|
* @return this instance for chaining.
|
||||||
|
*/
|
||||||
|
public ImageSource tilingEnabled() {
|
||||||
|
return tiling(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable tiling of the image. This does not apply to preview images which are always loaded as a single bitmap,
|
||||||
|
* and tiling cannot be disabled when displaying a region of the source image.
|
||||||
|
* @return this instance for chaining.
|
||||||
|
*/
|
||||||
|
public ImageSource tilingDisabled() {
|
||||||
|
return tiling(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable or disable tiling of the image. This does not apply to preview images which are always loaded as a single bitmap,
|
||||||
|
* and tiling cannot be disabled when displaying a region of the source image.
|
||||||
|
* @return this instance for chaining.
|
||||||
|
*/
|
||||||
|
public ImageSource tiling(boolean tile) {
|
||||||
|
this.tile = tile;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use a region of the source image. Region must be set independently for the full size image and the preview if
|
||||||
|
* you are using one.
|
||||||
|
* @return this instance for chaining.
|
||||||
|
*/
|
||||||
|
public ImageSource region(Rect sRegion) {
|
||||||
|
this.sRegion = sRegion;
|
||||||
|
setInvariants();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declare the dimensions of the image. This is only required for a full size image, when you are specifying a URI
|
||||||
|
* and also a preview image. When displaying a bitmap object, or not using a preview, you do not need to declare
|
||||||
|
* the image dimensions. Note if the declared dimensions are found to be incorrect, the view will reset.
|
||||||
|
* @return this instance for chaining.
|
||||||
|
*/
|
||||||
|
public ImageSource dimensions(int sWidth, int sHeight) {
|
||||||
|
if (bitmap == null) {
|
||||||
|
this.sWidth = sWidth;
|
||||||
|
this.sHeight = sHeight;
|
||||||
|
}
|
||||||
|
setInvariants();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setInvariants() {
|
||||||
|
if (this.sRegion != null) {
|
||||||
|
this.tile = true;
|
||||||
|
this.sWidth = this.sRegion.width();
|
||||||
|
this.sHeight = this.sRegion.height();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Uri getUri() {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Bitmap getBitmap() {
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Integer getResource() {
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final boolean getTile() {
|
||||||
|
return tile;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final int getSWidth() {
|
||||||
|
return sWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final int getSHeight() {
|
||||||
|
return sHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Rect getSRegion() {
|
||||||
|
return sRegion;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final boolean isCached() {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 David Morrissey
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.davemorrissey.labs.subscaleview;
|
||||||
|
|
||||||
|
import android.graphics.PointF;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps the scale, center and orientation of a displayed image for easy restoration on screen rotate.
|
||||||
|
*/
|
||||||
|
public class ImageViewState implements Serializable {
|
||||||
|
|
||||||
|
private float scale;
|
||||||
|
|
||||||
|
private float centerX;
|
||||||
|
|
||||||
|
private float centerY;
|
||||||
|
|
||||||
|
private int orientation;
|
||||||
|
|
||||||
|
public ImageViewState(float scale, PointF center, int orientation) {
|
||||||
|
this.scale = scale;
|
||||||
|
this.centerX = center.x;
|
||||||
|
this.centerY = center.y;
|
||||||
|
this.orientation = orientation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getScale() {
|
||||||
|
return scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PointF getCenter() {
|
||||||
|
return new PointF(centerX, centerY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOrientation() {
|
||||||
|
return orientation;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,20 @@
|
|||||||
|
package com.davemorrissey.labs.subscaleview.decoder;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compatibility factory to instantiate decoders with empty public constructors.
|
||||||
|
* @param <T> The base type of the decoder this factory will produce.
|
||||||
|
*/
|
||||||
|
public class CompatDecoderFactory <T> implements DecoderFactory<T> {
|
||||||
|
private Class<? extends T> clazz;
|
||||||
|
|
||||||
|
public CompatDecoderFactory(@NonNull Class<? extends T> clazz) {
|
||||||
|
this.clazz = clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T make() throws IllegalAccessException, InstantiationException {
|
||||||
|
return clazz.newInstance();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.davemorrissey.labs.subscaleview.decoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for decoder (and region decoder) factories.
|
||||||
|
* @param <T> the class of decoder that will be produced.
|
||||||
|
*/
|
||||||
|
public interface DecoderFactory<T> {
|
||||||
|
/**
|
||||||
|
* Produce a new instance of a decoder with type {@link T}.
|
||||||
|
* @return a new instance of your decoder.
|
||||||
|
*/
|
||||||
|
T make() throws IllegalAccessException, InstantiationException;
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.davemorrissey.labs.subscaleview.decoder;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for image decoding classes, allowing the default {@link android.graphics.BitmapRegionDecoder}
|
||||||
|
* based on the Skia library to be replaced with a custom class.
|
||||||
|
*/
|
||||||
|
public interface ImageDecoder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode an image. When possible, initial setup work once in this method. This method
|
||||||
|
* must return the dimensions of the image. The URI can be in one of the following formats:
|
||||||
|
* File: file:///scard/picture.jpg
|
||||||
|
* Asset: file:///android_asset/picture.png
|
||||||
|
* Resource: android.resource://com.example.app/drawable/picture
|
||||||
|
* @param context Application context. A reference may be held, but must be cleared on recycle.
|
||||||
|
* @param uri URI of the image.
|
||||||
|
* @return Dimensions of the image.
|
||||||
|
* @throws Exception if initialisation fails.
|
||||||
|
*/
|
||||||
|
Bitmap decode(Context context, Uri uri) throws Exception;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package com.davemorrissey.labs.subscaleview.decoder;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for image decoding classes, allowing the default {@link android.graphics.BitmapRegionDecoder}
|
||||||
|
* based on the Skia library to be replaced with a custom class.
|
||||||
|
*/
|
||||||
|
public interface ImageRegionDecoder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the decoder. When possible, initial setup work once in this method. This method
|
||||||
|
* must return the dimensions of the image. The URI can be in one of the following formats:
|
||||||
|
* File: file:///scard/picture.jpg
|
||||||
|
* Asset: file:///android_asset/picture.png
|
||||||
|
* Resource: android.resource://com.example.app/drawable/picture
|
||||||
|
* @param context Application context. A reference may be held, but must be cleared on recycle.
|
||||||
|
* @param uri URI of the image.
|
||||||
|
* @return Dimensions of the image.
|
||||||
|
* @throws Exception if initialisation fails.
|
||||||
|
*/
|
||||||
|
Point init(Context context, Uri uri) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a region of the image with the given sample size. This method is called off the UI thread so it can safely
|
||||||
|
* load the image on the current thread. It is called from an {@link android.os.AsyncTask} running in a single
|
||||||
|
* threaded executor, and while a synchronization lock is held on this object, so will never be called concurrently
|
||||||
|
* even if the decoder implementation supports it.
|
||||||
|
* @param sRect Source image rectangle to decode.
|
||||||
|
* @param sampleSize Sample size.
|
||||||
|
* @return The decoded region. It is safe to return null if decoding fails.
|
||||||
|
*/
|
||||||
|
Bitmap decodeRegion(Rect sRect, int sampleSize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Status check. Should return false before initialisation and after recycle.
|
||||||
|
* @return true if the decoder is ready to be used.
|
||||||
|
*/
|
||||||
|
boolean isReady();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will be called when the decoder is no longer required. It should clean up any resources still in use.
|
||||||
|
*/
|
||||||
|
void recycle();
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
package com.davemorrissey.labs.subscaleview.decoder;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of {@link com.davemorrissey.labs.subscaleview.decoder.ImageDecoder}
|
||||||
|
* using Android's {@link android.graphics.BitmapFactory}, based on the Skia library. This
|
||||||
|
* works well in most circumstances and has reasonable performance, however it has some problems
|
||||||
|
* with grayscale, indexed and CMYK images.
|
||||||
|
*/
|
||||||
|
public class SkiaImageDecoder implements ImageDecoder {
|
||||||
|
|
||||||
|
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 + "://";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bitmap decode(Context context, Uri uri) throws Exception {
|
||||||
|
String uriString = uri.toString();
|
||||||
|
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||||
|
Bitmap bitmap;
|
||||||
|
options.inPreferredConfig = Bitmap.Config.RGB_565;
|
||||||
|
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<String> segments = uri.getPathSegments();
|
||||||
|
int size = segments.size();
|
||||||
|
if (size == 2 && segments.get(0).equals("drawable")) {
|
||||||
|
String resName = segments.get(1);
|
||||||
|
id = res.getIdentifier(resName, "drawable", packageName);
|
||||||
|
} else if (size == 1 && TextUtils.isDigitsOnly(segments.get(0))) {
|
||||||
|
try {
|
||||||
|
id = Integer.parseInt(segments.get(0));
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmap = BitmapFactory.decodeResource(context.getResources(), id, options);
|
||||||
|
} else if (uriString.startsWith(ASSET_PREFIX)) {
|
||||||
|
String assetName = uriString.substring(ASSET_PREFIX.length());
|
||||||
|
bitmap = BitmapFactory.decodeStream(context.getAssets().open(assetName), null, options);
|
||||||
|
} else if (uriString.startsWith(FILE_PREFIX)) {
|
||||||
|
bitmap = BitmapFactory.decodeFile(uriString.substring(FILE_PREFIX.length()), options);
|
||||||
|
} else {
|
||||||
|
InputStream inputStream = null;
|
||||||
|
try {
|
||||||
|
ContentResolver contentResolver = context.getContentResolver();
|
||||||
|
inputStream = contentResolver.openInputStream(uri);
|
||||||
|
bitmap = BitmapFactory.decodeStream(inputStream, null, options);
|
||||||
|
} finally {
|
||||||
|
if (inputStream != null) {
|
||||||
|
try { inputStream.close(); } catch (Exception e) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bitmap == null) {
|
||||||
|
throw new RuntimeException("Skia image region decoder returned null bitmap - image format may not be supported");
|
||||||
|
}
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
package com.davemorrissey.labs.subscaleview.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.*;
|
||||||
|
import android.graphics.Bitmap.Config;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of {@link com.davemorrissey.labs.subscaleview.decoder.ImageRegionDecoder}
|
||||||
|
* using Android's {@link android.graphics.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.
|
||||||
|
*/
|
||||||
|
public class SkiaImageRegionDecoder implements ImageRegionDecoder {
|
||||||
|
|
||||||
|
private BitmapRegionDecoder decoder;
|
||||||
|
private final Object decoderLock = new Object();
|
||||||
|
|
||||||
|
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 + "://";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Point init(Context context, 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<String> segments = uri.getPathSegments();
|
||||||
|
int size = segments.size();
|
||||||
|
if (size == 2 && segments.get(0).equals("drawable")) {
|
||||||
|
String resName = segments.get(1);
|
||||||
|
id = res.getIdentifier(resName, "drawable", packageName);
|
||||||
|
} else if (size == 1 && TextUtils.isDigitsOnly(segments.get(0))) {
|
||||||
|
try {
|
||||||
|
id = Integer.parseInt(segments.get(0));
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder = BitmapRegionDecoder.newInstance(context.getResources().openRawResource(id), false);
|
||||||
|
} else if (uriString.startsWith(ASSET_PREFIX)) {
|
||||||
|
String assetName = uriString.substring(ASSET_PREFIX.length());
|
||||||
|
decoder = BitmapRegionDecoder.newInstance(context.getAssets().open(assetName, AssetManager.ACCESS_RANDOM), false);
|
||||||
|
} else if (uriString.startsWith(FILE_PREFIX)) {
|
||||||
|
decoder = BitmapRegionDecoder.newInstance(uriString.substring(FILE_PREFIX.length()), false);
|
||||||
|
} else {
|
||||||
|
InputStream inputStream = null;
|
||||||
|
try {
|
||||||
|
ContentResolver contentResolver = context.getContentResolver();
|
||||||
|
inputStream = contentResolver.openInputStream(uri);
|
||||||
|
decoder = BitmapRegionDecoder.newInstance(inputStream, false);
|
||||||
|
} finally {
|
||||||
|
if (inputStream != null) {
|
||||||
|
try { inputStream.close(); } catch (Exception e) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Point(decoder.getWidth(), decoder.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bitmap decodeRegion(Rect sRect, int sampleSize) {
|
||||||
|
synchronized (decoderLock) {
|
||||||
|
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||||
|
options.inSampleSize = sampleSize;
|
||||||
|
options.inPreferredConfig = Config.RGB_565;
|
||||||
|
Bitmap bitmap = decoder.decodeRegion(sRect, options);
|
||||||
|
if (bitmap == null) {
|
||||||
|
throw new RuntimeException("Skia image decoder returned null bitmap - image format may not be supported");
|
||||||
|
}
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReady() {
|
||||||
|
return decoder != null && !decoder.isRecycled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void recycle() {
|
||||||
|
decoder.recycle();
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1,2 @@
|
|||||||
include ':app'
|
include ':app', ':SubsamplingScaleImageView'
|
||||||
|
project(':SubsamplingScaleImageView').projectDir = new File('libs/SubsamplingScaleImageView')
|
Loading…
Reference in New Issue
Block a user