Decode image partly
This commit is contained in:
1 parent
0a472d28b5
commit
b8189d76d6
11 files changed
+262
-104
No files matched your search
@@ -20,7 +20,6 @@ import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.hippo.conaco.ValueHelper;
|
||||
import com.hippo.image.Image;
|
||||
import com.hippo.image.ImageBitmap;
|
||||
import com.hippo.streampipe.InputStreamPipe;
|
||||
|
||||
@@ -35,12 +34,7 @@ public class ImageBitmapHelper implements ValueHelper<ImageBitmap> {
|
||||
public ImageBitmap decode(@NonNull InputStreamPipe isPipe) {
|
||||
try {
|
||||
isPipe.obtain();
|
||||
Image image = Image.decode(isPipe.open(), false);
|
||||
if (image != null) {
|
||||
return new ImageBitmap(image);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return ImageBitmap.decode(isPipe.open());
|
||||
} catch (OutOfMemoryError e) {
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -23,9 +23,9 @@ import android.support.annotation.UiThread;
|
||||
|
||||
import com.hippo.beerbelly.LruCache;
|
||||
import com.hippo.gl.glrenderer.GLCanvas;
|
||||
import com.hippo.gl.glrenderer.ImageWrapper;
|
||||
import com.hippo.gl.view.GLRoot;
|
||||
import com.hippo.image.Image;
|
||||
import com.hippo.image.ImageWrapper;
|
||||
import com.hippo.unifile.UniFile;
|
||||
import com.hippo.yorozuya.ConcurrentPool;
|
||||
import com.hippo.yorozuya.IOUtils;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package com.hippo.ehviewer.gallery;
|
||||
|
||||
import com.hippo.image.ImageWrapper;
|
||||
import com.hippo.gl.glrenderer.ImageWrapper;
|
||||
|
||||
public interface GalleryProviderListener {
|
||||
|
||||
|
||||
@@ -206,6 +206,7 @@ public class ImageView extends GLView implements ImageTexture.Callback {
|
||||
// Remove callback
|
||||
if (mImageTexture != null) {
|
||||
mImageTexture.setCallback(null);
|
||||
mImageTexture.stop();
|
||||
}
|
||||
|
||||
int oldTextureWidth = mTextureWidth;
|
||||
@@ -229,6 +230,7 @@ public class ImageView extends GLView implements ImageTexture.Callback {
|
||||
getValidRect(mValidRect);
|
||||
if (!mValidRect.isEmpty()) {
|
||||
startAnimation(mAlphaAnimation, true);
|
||||
mImageTexture.start();
|
||||
}
|
||||
} else {
|
||||
mTextureWidth = 1;
|
||||
|
||||
@@ -1347,27 +1347,34 @@ public final class SpiderQueen implements Runnable {
|
||||
|
||||
Image image = null;
|
||||
String error = null;
|
||||
InputStream is;
|
||||
|
||||
pipe.obtain();
|
||||
try {
|
||||
pipe.obtain();
|
||||
// TODO how to keep stream open
|
||||
image = Image.decode(pipe.open(), false);
|
||||
is = new AutoCloseInputStream(pipe, pipe.open());
|
||||
} catch (IOException e) {
|
||||
// Can't open pipe
|
||||
error = GetText.getString(R.string.error_reading_failed);
|
||||
is = null;
|
||||
pipe.close();
|
||||
pipe.release();
|
||||
}
|
||||
|
||||
if (is != null) {
|
||||
image = Image.decode(is, true);
|
||||
if (image == null) {
|
||||
error = GetText.getString(R.string.error_decoding_failed);
|
||||
}
|
||||
resetDecodeIndex();
|
||||
} catch (IOException e) {
|
||||
resetDecodeIndex();
|
||||
error = GetText.getString(R.string.error_reading_failed);
|
||||
} finally {
|
||||
pipe.close();
|
||||
pipe.release();
|
||||
// Notify
|
||||
if (image != null) {
|
||||
notifyGetImageSuccess(index, image);
|
||||
} else {
|
||||
notifyGetImageFailure(index, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Notify
|
||||
if (image != null) {
|
||||
notifyGetImageSuccess(index, image);
|
||||
} else {
|
||||
notifyGetImageFailure(index, error);
|
||||
}
|
||||
|
||||
resetDecodeIndex();
|
||||
}
|
||||
|
||||
if (DEBUG_LOG) {
|
||||
@@ -1376,6 +1383,33 @@ public final class SpiderQueen implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private class AutoCloseInputStream extends InputStream {
|
||||
|
||||
private final InputStreamPipe mPipe;
|
||||
private final InputStream mIs;
|
||||
|
||||
public AutoCloseInputStream(InputStreamPipe pipe, InputStream is) {
|
||||
mPipe = pipe;
|
||||
mIs = is;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return mIs.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(@NonNull byte[] buffer, int byteOffset, int byteCount) throws IOException {
|
||||
return mIs.read(buffer, byteOffset, byteCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
mPipe.close();
|
||||
mPipe.release();
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnSpiderListener {
|
||||
|
||||
void onGetPages(int pages);
|
||||
|
||||
@@ -61,8 +61,8 @@ import com.hippo.ehviewer.gallery.gl.ImageView;
|
||||
import com.hippo.ehviewer.widget.GalleryGuideView;
|
||||
import com.hippo.ehviewer.widget.ReversibleSeekBar;
|
||||
import com.hippo.gl.glrenderer.ImageTexture;
|
||||
import com.hippo.gl.glrenderer.ImageWrapper;
|
||||
import com.hippo.gl.view.GLRootView;
|
||||
import com.hippo.image.ImageWrapper;
|
||||
import com.hippo.unifile.UniFile;
|
||||
import com.hippo.util.SystemUiHelper;
|
||||
import com.hippo.widget.ColorView;
|
||||
|
||||
@@ -24,7 +24,6 @@ import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.hippo.image.Image;
|
||||
import com.hippo.image.ImageWrapper;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ package com.hippo.gl.glrenderer;
|
||||
import android.graphics.RectF;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.hippo.image.ImageWrapper;
|
||||
import com.hippo.yorozuya.AssertUtils;
|
||||
|
||||
public class ImageSpriteTexture extends ImageTexture {
|
||||
|
||||
@@ -18,13 +18,14 @@ package com.hippo.gl.glrenderer;
|
||||
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.os.Process;
|
||||
import android.os.SystemClock;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.hippo.gl.annotation.RenderThread;
|
||||
import com.hippo.gl.view.GLRoot;
|
||||
import com.hippo.image.ImageWrapper;
|
||||
import com.hippo.yorozuya.thread.InfiniteThreadExecutor;
|
||||
import com.hippo.yorozuya.thread.PVLock;
|
||||
import com.hippo.yorozuya.thread.PriorityThreadFactory;
|
||||
|
||||
@@ -33,8 +34,9 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class ImageTexture implements Texture, Animatable {
|
||||
|
||||
@@ -59,7 +61,7 @@ public class ImageTexture implements Texture, Animatable {
|
||||
// In this 16ms, we use about 4~8 ms to upload tiles.
|
||||
private static final long UPLOAD_TILE_LIMIT = 4; // ms
|
||||
|
||||
private static final PriorityThreadFactory sThreadFactory;
|
||||
private static final Executor sThreadExecutor;
|
||||
private static final PVLock sPVLock;
|
||||
|
||||
private static Tile sSmallFreeTileHead = null;
|
||||
@@ -77,17 +79,19 @@ public class ImageTexture implements Texture, Animatable {
|
||||
private final RectF mSrcRect = new RectF();
|
||||
private final RectF mDestRect = new RectF();
|
||||
|
||||
private final AtomicBoolean mFrameDirty = new AtomicBoolean();
|
||||
private boolean mImageBusy = false;
|
||||
private final AtomicBoolean mRunning = new AtomicBoolean();
|
||||
private final AtomicBoolean mFrameDirty = new AtomicBoolean();
|
||||
private final AtomicBoolean mNeedRelease = new AtomicBoolean();
|
||||
private final AtomicBoolean mReleased = new AtomicBoolean();
|
||||
|
||||
private final AtomicReference<Thread> mAnimateThread = new AtomicReference<>();
|
||||
private Runnable mAnimateRunnable = null;
|
||||
|
||||
private WeakReference<Callback> mCallback;
|
||||
|
||||
static {
|
||||
sThreadFactory = new PriorityThreadFactory("ImageTexture$AnimateTask", android.os.Process.THREAD_PRIORITY_BACKGROUND);
|
||||
sThreadExecutor = new InfiniteThreadExecutor(10 * 1000, new LinkedList<>(),
|
||||
new PriorityThreadFactory("ImageTexture$AnimateTask", Process.THREAD_PRIORITY_BACKGROUND));
|
||||
sPVLock = new PVLock(3);
|
||||
}
|
||||
|
||||
@@ -253,37 +257,25 @@ public class ImageTexture implements Texture, Animatable {
|
||||
long lastDelay = -1L;
|
||||
|
||||
synchronized (mImage) {
|
||||
// Check released
|
||||
if (mReleased.get()) {
|
||||
return;
|
||||
}
|
||||
// Check image busy
|
||||
if (mImageBusy) {
|
||||
// Image is busy, means it is recycling
|
||||
return;
|
||||
}
|
||||
if (mNeedRelease.get()) {
|
||||
// Need release
|
||||
// Check released, image busy, Need release
|
||||
if (mReleased.get() || mImageBusy || mNeedRelease.get()) {
|
||||
mAnimateRunnable = null;
|
||||
return;
|
||||
}
|
||||
// Obtain image
|
||||
mImageBusy = true;
|
||||
}
|
||||
|
||||
boolean interrupted = Thread.currentThread().isInterrupted();
|
||||
|
||||
if (!mImage.isCompleted()) {
|
||||
if (!interrupted) {
|
||||
try {
|
||||
sPVLock.p();
|
||||
} catch (InterruptedException e) {
|
||||
interrupted = true;
|
||||
}
|
||||
if (!interrupted && !mNeedRelease.get()) {
|
||||
mImage.complete();
|
||||
}
|
||||
sPVLock.v();
|
||||
try {
|
||||
sPVLock.p();
|
||||
} catch (InterruptedException e) {
|
||||
// Ignore
|
||||
}
|
||||
if (!mNeedRelease.get()) {
|
||||
mImage.complete();
|
||||
}
|
||||
sPVLock.v();
|
||||
}
|
||||
|
||||
int frameCount = mImage.getFrameCount();
|
||||
@@ -291,26 +283,19 @@ public class ImageTexture implements Texture, Animatable {
|
||||
synchronized (mImage) {
|
||||
// Release image
|
||||
mImageBusy = false;
|
||||
// Check need release, frameCount <= 1
|
||||
if (mNeedRelease.get() || frameCount <= 1) {
|
||||
mAnimateRunnable = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (interrupted || mNeedRelease.get() || frameCount <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
for (;;) {
|
||||
// Obtain
|
||||
synchronized (mImage) {
|
||||
// Check released
|
||||
if (mReleased.get()) {
|
||||
return;
|
||||
}
|
||||
// Check image busy
|
||||
if (mImageBusy) {
|
||||
// Image is busy, means it is recycling
|
||||
return;
|
||||
}
|
||||
if (mNeedRelease.get()) {
|
||||
// Need release
|
||||
// Check released, image busy, Need release, not running
|
||||
if (mReleased.get() || mImageBusy || mNeedRelease.get() || !mRunning.get()) {
|
||||
mAnimateRunnable = null;
|
||||
return;
|
||||
}
|
||||
// Obtain image
|
||||
@@ -337,7 +322,7 @@ public class ImageTexture implements Texture, Animatable {
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -371,8 +356,6 @@ public class ImageTexture implements Texture, Animatable {
|
||||
mImageBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
mAnimateThread.lazySet(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,10 +426,10 @@ public class ImageTexture implements Texture, Animatable {
|
||||
|
||||
mTiles = list.toArray(new Tile[list.size()]);
|
||||
|
||||
if (!mImage.isCompleted() || mImage.getFrameCount() > 1) {
|
||||
Thread thread = sThreadFactory.newThread(new AnimateRunnable());
|
||||
mAnimateThread.lazySet(thread);
|
||||
thread.start();
|
||||
if (!mImage.isCompleted()) {
|
||||
Runnable runnable = new AnimateRunnable();
|
||||
mAnimateRunnable = runnable;
|
||||
sThreadExecutor.execute(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,33 +453,31 @@ public class ImageTexture implements Texture, Animatable {
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (mReleased.get() || (mImage.isCompleted() && mImage.getFrameCount() <= 1)) {
|
||||
if (mReleased.get() || mNeedRelease.get() || (mImage.isCompleted() && mImage.getFrameCount() <= 1) ||
|
||||
mRunning.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Thread thread = mAnimateThread.get();
|
||||
if (thread == null) {
|
||||
thread = sThreadFactory.newThread(new AnimateRunnable());
|
||||
mAnimateThread.set(thread);
|
||||
thread.start();
|
||||
}
|
||||
}
|
||||
mRunning.lazySet(true);
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (!mReleased.get() && (mImage.isCompleted() && mImage.getFrameCount() > 1)) {
|
||||
Thread thread = mAnimateThread.getAndSet(null);
|
||||
if (null != thread) {
|
||||
thread.interrupt();
|
||||
synchronized (mImage) {
|
||||
if (mAnimateRunnable == null) {
|
||||
Runnable runnable = new AnimateRunnable();
|
||||
mAnimateRunnable = runnable;
|
||||
sThreadExecutor.execute(runnable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return mAnimateThread.get() != null;
|
||||
public void stop() {
|
||||
mRunning.lazySet(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return mRunning.get();
|
||||
}
|
||||
|
||||
private boolean uploadNextTile(GLCanvas canvas) {
|
||||
if (mUploadIndex == mTiles.length) return true;
|
||||
@@ -663,6 +644,8 @@ public class ImageTexture implements Texture, Animatable {
|
||||
}
|
||||
|
||||
public void recycle() {
|
||||
mRunning.lazySet(false);
|
||||
|
||||
for (Tile mTile : mTiles) {
|
||||
mTile.free();
|
||||
}
|
||||
@@ -675,6 +658,7 @@ public class ImageTexture implements Texture, Animatable {
|
||||
mImageBusy = true;
|
||||
} else {
|
||||
releaseNow = false;
|
||||
mNeedRelease.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -684,13 +668,6 @@ public class ImageTexture implements Texture, Animatable {
|
||||
synchronized (mImage) {
|
||||
mImageBusy = false;
|
||||
}
|
||||
} else {
|
||||
mNeedRelease.set(true);
|
||||
}
|
||||
|
||||
Thread thread = mAnimateThread.getAndSet(null);
|
||||
if (null != thread) {
|
||||
thread.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* 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.hippo.gl.glrenderer;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.hippo.image.Image;
|
||||
|
||||
/**
|
||||
* A wrapper for {@link Image}. It is useful for multi-usage.
|
||||
* It handles image recycle automatically.
|
||||
*/
|
||||
public class ImageWrapper {
|
||||
|
||||
private final Image mImage;
|
||||
private int mReferences;
|
||||
|
||||
/**
|
||||
* Create ImageWrapper
|
||||
*
|
||||
* @param image the image should not be obtained or recycled.
|
||||
*/
|
||||
public ImageWrapper(@NonNull Image image) {
|
||||
mImage = image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the image
|
||||
*
|
||||
* @return false for the image is recycled and obtain failed
|
||||
*/
|
||||
public synchronized boolean obtain() {
|
||||
if (mImage.isRecycled()) {
|
||||
return false;
|
||||
} else {
|
||||
++mReferences;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the image
|
||||
*/
|
||||
public synchronized void release() {
|
||||
--mReferences;
|
||||
if (mReferences <= 0 && !mImage.isRecycled()) {
|
||||
mImage.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Image#getFormat()
|
||||
*/
|
||||
public int getFormat() {
|
||||
return mImage.getFormat();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Image#getFormat()
|
||||
*/
|
||||
public int getWidth() {
|
||||
return mImage.getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Image#getHeight()
|
||||
*/
|
||||
public int getHeight() {
|
||||
return mImage.getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Image#complete()
|
||||
*/
|
||||
public boolean complete() {
|
||||
synchronized (mImage) {
|
||||
return mImage.complete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Image#isCompleted()
|
||||
*/
|
||||
public boolean isCompleted() {
|
||||
return mImage.isCompleted();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Image#render(int, int, Bitmap, int, int, int, int, boolean, int)
|
||||
*/
|
||||
public void render(int srcX, int srcY, Bitmap dst, int dstX, int dstY,
|
||||
int width, int height, boolean fillBlank, int defaultColor) {
|
||||
mImage.render(srcX, srcY, dst, dstX, dstY,
|
||||
width, height, fillBlank, defaultColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Image#texImage(boolean, int, int, int, int)
|
||||
*/
|
||||
public void texImage(boolean init, int offsetX, int offsetY, int width, int height) {
|
||||
mImage.texImage(init, offsetX, offsetY, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Image#advance()
|
||||
*/
|
||||
public void advance() {
|
||||
mImage.advance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Image#getDelay()
|
||||
*/
|
||||
public int getDelay() {
|
||||
return mImage.getDelay();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Image#getFrameCount()
|
||||
*/
|
||||
public int getFrameCount() {
|
||||
return mImage.getFrameCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Image#isOpaque()
|
||||
*/
|
||||
public boolean isOpaque() {
|
||||
return mImage.isOpaque();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Image#isRecycled()
|
||||
*/
|
||||
public boolean isRecycled() {
|
||||
return mImage.isRecycled();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user