Use Bitmap for thumb instead of Image

This commit is contained in:
seven332 committed 2016-03-11 14:09:01 +08:00
1 parent 722a51d243
commit d9f56830a4
9 files changed
+50 -482

No files matched your search

@@ -1,175 +0,0 @@
/*
* 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.drawable;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import com.hippo.yorozuya.MathUtils;
import java.util.ArrayList;
import java.util.List;
public class ImageDrawable extends Drawable implements ImageWrapper.Callback, Animatable {
private static final int TILE_SIZE = 512;
private ImageWrapper mImageWrapper;
private Paint mPaint;
private List<Tile> mTileList;
private static class Tile {
Bitmap bitmap;
int w;
int h;
int x;
int y;
}
private static final BitmapPool sBitmapPool = new BitmapPool();
public ImageDrawable(@NonNull ImageWrapper imageWrapper) {
mImageWrapper = imageWrapper;
mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
mTileList = createTileArray();
// Render first frame
render();
// Add callback
imageWrapper.addCallback(this);
}
private List<Tile> createTileArray() {
int width = mImageWrapper.getWidth();
int height = mImageWrapper.getHeight();
int capacity = MathUtils.clamp(MathUtils.ceilDivide(width, TILE_SIZE) *
MathUtils.ceilDivide(height, TILE_SIZE), 0, 100);
List<Tile> tiles = new ArrayList<>(capacity);
for (int x = 0; x < width; x += TILE_SIZE) {
int w = Math.min(TILE_SIZE, width - x);
for (int y = 0; y < height; y += TILE_SIZE) {
int h = Math.min(TILE_SIZE, height - y);
Tile tile = new Tile();
tile.x = x;
tile.y = y;
tile.w = w;
tile.h = h;
tile.bitmap = sBitmapPool.get(w, h);
tiles.add(tile);
}
}
return tiles;
}
private void render() {
ImageWrapper imageWrapper = mImageWrapper;
if (imageWrapper.isRecycled()) {
return;
}
List<Tile> tiles = mTileList;
for (int i = 0, length = tiles.size(); i < length; i++) {
Tile tile = tiles.get(i);
if (tile.bitmap != null) {
imageWrapper.render(tile.x, tile.y, tile.bitmap, 0, 0, tile.w, tile.h, false, 0);
}
}
}
@Override
public int getIntrinsicWidth() {
return mImageWrapper.getWidth();
}
@Override
public int getIntrinsicHeight() {
return mImageWrapper.getHeight();
}
@Override
public void draw(Canvas canvas) {
List<Tile> tiles = mTileList;
for (int i = 0, length = tiles.size(); i < length; i++) {
Tile tile = tiles.get(i);
if (tile.bitmap != null) {
canvas.drawBitmap(tile.bitmap, tile.x, tile.y, mPaint);
}
}
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
mPaint.setColorFilter(colorFilter);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
public void recycle() {
mImageWrapper.removeCallback(this);
// Free tile's bitmap
List<Tile> tiles = mTileList;
for (int i = 0, length = tiles.size(); i < length; i++) {
Tile tile = tiles.get(i);
sBitmapPool.put(tile.bitmap);
tile.bitmap = null;
}
}
@Override
public void renderImage(ImageWrapper who) {
render();
}
@Override
public void invalidateImage(ImageWrapper who) {
invalidateSelf();
}
@Override
public void start() {
mImageWrapper.start();
}
@Override
public void stop() {
mImageWrapper.stop();
}
@Override
public boolean isRunning() {
return mImageWrapper.isRunning();
}
}
@@ -1,180 +0,0 @@
/*
* 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.drawable;
import android.graphics.Bitmap;
import android.graphics.drawable.Animatable;
import android.support.annotation.NonNull;
import android.util.Log;
import com.hippo.image.Image;
import com.hippo.yorozuya.SimpleHandler;
import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
public class ImageWrapper implements Animatable, Runnable {
private static final String TAG = ImageWrapper.class.getSimpleName();
private final Image mImage;
private boolean mRunning = false;
private final Set<WeakReference<Callback>> mCallbackSet = new LinkedHashSet<>();
public ImageWrapper(@NonNull Image image) {
mImage = image;
}
public int getWidth() {
return mImage.getWidth();
}
public int getHeight() {
return mImage.getHeight();
}
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);
}
public int getFrameCount() {
return mImage.getFrameCount();
}
public void recycle() {
stop();
mImage.recycle();
}
public boolean isRecycled() {
return mImage.isRecycled();
}
public boolean isLarge() {
return mImage.getWidth() * mImage.getHeight() > 512 * 512;
}
public void addCallback(@NonNull Callback callback) {
final Iterator<WeakReference<Callback>> iterator = mCallbackSet.iterator();
Callback c;
while (iterator.hasNext()) {
c = iterator.next().get();
if (c == null) {
// Remove from the set if the reference has been cleared or
// it can't be used.
iterator.remove();
} else if (c == callback) {
return;
}
}
mCallbackSet.add(new WeakReference<>(callback));
}
public void removeCallback(@NonNull Callback callback) {
final Iterator<WeakReference<Callback>> iterator = mCallbackSet.iterator();
Callback c;
while (iterator.hasNext()) {
c = iterator.next().get();
if (c == null) {
// Remove from the set if the reference has been cleared or
// it can't be used.
iterator.remove();
} else if (c == callback) {
iterator.remove();
return;
}
}
}
@Override
public void start() {
if (mImage.isRecycled() || mImage.getFrameCount() <= 1 || mRunning) {
return;
}
mRunning = true;
SimpleHandler.getInstance().postDelayed(this, Math.max(0, mImage.getDelay()));
}
@Override
public void stop() {
mRunning = false;
SimpleHandler.getInstance().removeCallbacks(this);
}
@Override
public boolean isRunning() {
return mRunning;
}
private boolean notifyUpdate() {
boolean hasCallback = false;
final Iterator<WeakReference<Callback>> iterator = mCallbackSet.iterator();
Callback callback;
while (iterator.hasNext()) {
callback = iterator.next().get();
if (callback != null) {
hasCallback = true;
callback.renderImage(this);
callback.invalidateImage(this);
} else {
// Remove from the set if the reference has been cleared or
// it can't be used.
iterator.remove();
}
}
return hasCallback;
}
@Override
public void run() {
//Log.i(TAG, this + " run");
// Check recycled
if (mImage.isRecycled()) {
mRunning = false;
return;
}
mImage.advance();
if (notifyUpdate()) {
if (mRunning) {
SimpleHandler.getInstance().postDelayed(this, Math.max(0, mImage.getDelay()));
}
} else {
// No callback ? Stop now
Log.w(TAG, "No callback");
mRunning = false;
}
}
public interface Callback {
void renderImage(ImageWrapper who);
void invalidateImage(ImageWrapper who);
}
}
@@ -16,6 +16,8 @@
package com.hippo.drawable;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.widget.TextView;
@@ -24,13 +26,13 @@ import com.hippo.conaco.Conaco;
import com.hippo.conaco.Unikery;
import com.hippo.conaco.ValueHolder;
public class UnikeryDrawable extends WrapDrawable implements Unikery<ImageWrapper> {
public class UnikeryDrawable extends WrapDrawable implements Unikery<Bitmap> {
private int mTaskId = Unikery.INVALID_ID;
private TextView mTextView;
private final TextView mTextView;
private ValueHolder<ImageWrapper> mHolder;
private ValueHolder<Bitmap> mHolder;
public UnikeryDrawable(TextView textView) {
mTextView = textView;
@@ -82,40 +84,23 @@ public class UnikeryDrawable extends WrapDrawable implements Unikery<ImageWrappe
private void removeDrawableAndHolder() {
// Remove drawable
Drawable drawable = getDrawable();
if (drawable instanceof ImageDrawable) {
((ImageDrawable) drawable).recycle();
}
setDrawable(null);
// Remove holder
if (mHolder != null) {
mHolder.release(this);
ImageWrapper imageWrapper = mHolder.getValue();
if (mHolder.isFree()) {
// ImageWrapper is free, stop animate
imageWrapper.stop();
if (!mHolder.isInMemoryCache()) {
// ImageWrapper is not needed any more, recycle it
imageWrapper.recycle();
}
}
mHolder = null;
}
}
@Override
public boolean onGetObject(@NonNull ValueHolder<ImageWrapper> holder, Conaco.Source source) {
public boolean onGetObject(@NonNull ValueHolder<Bitmap> holder, Conaco.Source source) {
holder.obtain(this);
removeDrawableAndHolder();
mHolder = holder;
ImageWrapper imageWrapper = holder.getValue();
Drawable drawable = new ImageDrawable(imageWrapper);
imageWrapper.start();
Drawable drawable = new BitmapDrawable(holder.getValue());
setDrawable(drawable);
@@ -1,5 +1,5 @@
/*
* Copyright 2015 Hippo Seven
* 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.
@@ -16,32 +16,26 @@
package com.hippo.ehviewer;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.hippo.conaco.ValueHelper;
import com.hippo.conaco.ValueHolder;
import com.hippo.drawable.ImageWrapper;
import com.hippo.image.Image;
import com.hippo.yorozuya.io.InputStreamPipe;
public class ImageWrapperHelper implements ValueHelper<ImageWrapper> {
import java.io.IOException;
private static final String TAG = ImageWrapperHelper.class.getSimpleName();
public class BitmapHelper implements ValueHelper<Bitmap> {
@Nullable
@Override
public ImageWrapper decode(@NonNull InputStreamPipe isPipe) {
public Bitmap decode(@NonNull InputStreamPipe isPipe) {
try {
isPipe.obtain();
Image image = Image.decode(isPipe.open(), false);
if (image != null) {
return new ImageWrapper(image);
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return BitmapFactory.decodeStream(isPipe.open());
} catch (IOException e) {
return null;
} finally {
isPipe.close();
@@ -50,19 +44,15 @@ public class ImageWrapperHelper implements ValueHelper<ImageWrapper> {
}
@Override
public int sizeOf(@NonNull String key, @NonNull ImageWrapper value) {
return value.getWidth() * value.getHeight() * 4;
public int sizeOf(@NonNull String key, @NonNull Bitmap value) {
return value.getByteCount();
}
@Override
public void onRemove(@NonNull String key, @NonNull ValueHolder<ImageWrapper> oldValue) {
if (oldValue.isFree()) {
oldValue.getValue().recycle();
}
}
public void onRemove(@NonNull String key, @NonNull ValueHolder<Bitmap> oldValue) {}
@Override
public boolean useMemoryCache(@NonNull String key, ValueHolder<ImageWrapper> holder) {
return holder == null || !holder.getValue().isLarge();
public boolean useMemoryCache(@NonNull String key, ValueHolder<Bitmap> holder) {
return true;
}
}
@@ -18,6 +18,7 @@ package com.hippo.ehviewer;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Debug;
import android.support.annotation.NonNull;
import android.util.Log;
@@ -25,7 +26,6 @@ import android.util.SparseArray;
import com.hippo.beerbelly.LruCacheEx;
import com.hippo.conaco.Conaco;
import com.hippo.drawable.ImageWrapper;
import com.hippo.ehviewer.client.EhClient;
import com.hippo.ehviewer.client.EhCookieStore;
import com.hippo.ehviewer.client.data.GalleryDetail;
@@ -58,8 +58,8 @@ public class EhApplication extends SceneApplication {
private EhCookieStore mEhCookieStore;
private EhClient mEhClient;
private OkHttpClient mOkHttpClient;
private ImageWrapperHelper mImageWrapperHelper;
private Conaco<ImageWrapper> mConaco;
private BitmapHelper mBitmapHelper;
private Conaco<Bitmap> mConaco;
private LruCacheEx<Long, GalleryDetail> mGalleryDetailCache;
private LruCacheEx<String, LargePreviewSet> mLargePreviewSetCache;
private LruCacheEx<Long, Integer> mPreviewPagesCache;
@@ -170,12 +170,12 @@ public class EhApplication extends SceneApplication {
}
@NonNull
public static ImageWrapperHelper getImageWrapperHelper(@NonNull Context context) {
public static BitmapHelper getBitmapHelper(@NonNull Context context) {
EhApplication application = ((EhApplication) context.getApplicationContext());
if (application.mImageWrapperHelper == null) {
application.mImageWrapperHelper = new ImageWrapperHelper();
if (application.mBitmapHelper == null) {
application.mBitmapHelper = new BitmapHelper();
}
return application.mImageWrapperHelper;
return application.mBitmapHelper;
}
private static int getMemoryCacheMaxSize(Context context) {
@@ -186,17 +186,17 @@ public class EhApplication extends SceneApplication {
}
@NonNull
public static Conaco<ImageWrapper> getConaco(@NonNull Context context) {
public static Conaco<Bitmap> getConaco(@NonNull Context context) {
EhApplication application = ((EhApplication) context.getApplicationContext());
if (application.mConaco == null) {
Conaco.Builder<ImageWrapper> builder = new Conaco.Builder<>();
Conaco.Builder<Bitmap> builder = new Conaco.Builder<>();
builder.hasMemoryCache = true;
builder.memoryCacheMaxSize = getMemoryCacheMaxSize(context);
builder.hasDiskCache = true;
builder.diskCacheDir = new File(context.getCacheDir(), "thumb");
builder.diskCacheMaxSize = 80 * 1024 * 1024; // 80MB
builder.okHttpClient = getOkHttpClient(context);
builder.objectHelper = getImageWrapperHelper(context);
builder.objectHelper = getBitmapHelper(context);
builder.debug = DEBUG_CONACO;
application.mConaco = builder.build();
}
@@ -16,20 +16,20 @@
package com.hippo.text;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.widget.TextView;
import com.hippo.conaco.Conaco;
import com.hippo.conaco.ConacoTask;
import com.hippo.drawable.ImageWrapper;
import com.hippo.drawable.UnikeryDrawable;
public class URLImageGetter implements Html.ImageGetter {
private TextView mTextView;
private Conaco<ImageWrapper> mConaco;
private final TextView mTextView;
private final Conaco<Bitmap> mConaco;
public URLImageGetter(TextView textView, Conaco<ImageWrapper> conaco) {
public URLImageGetter(TextView textView, Conaco<Bitmap> conaco) {
mTextView = textView;
mConaco = conaco;
}
@@ -37,7 +37,7 @@ public class URLImageGetter implements Html.ImageGetter {
@Override
public Drawable getDrawable(String source) {
UnikeryDrawable drawable = new UnikeryDrawable(mTextView);
mConaco.load(new ConacoTask.Builder<ImageWrapper>().setUnikery(drawable).setUrl(source).setKey(source));
mConaco.load(new ConacoTask.Builder<Bitmap>().setUnikery(drawable).setUrl(source).setKey(source));
return drawable;
}
}
@@ -18,7 +18,9 @@ package com.hippo.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
@@ -32,24 +34,22 @@ import com.hippo.conaco.ConacoTask;
import com.hippo.conaco.DataContainer;
import com.hippo.conaco.Unikery;
import com.hippo.conaco.ValueHolder;
import com.hippo.drawable.ImageDrawable;
import com.hippo.drawable.ImageWrapper;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.R;
public class LoadImageView extends FixedAspectImageView implements Unikery<ImageWrapper>,
public class LoadImageView extends FixedAspectImageView implements Unikery<Bitmap>,
View.OnClickListener, View.OnLongClickListener {
private int mTaskId = Unikery.INVALID_ID;
private Conaco<ImageWrapper> mConaco;
private Conaco<Bitmap> mConaco;
private String mKey;
private String mUrl;
private DataContainer mContainer;
private boolean mUseNetwork;
private ValueHolder<ImageWrapper> mHolder;
private ValueHolder<Bitmap> mHolder;
private boolean mFailed;
@@ -173,7 +173,7 @@ public class LoadImageView extends FixedAspectImageView implements Unikery<Image
mContainer = container;
mUseNetwork = useNetwork;
ConacoTask.Builder<ImageWrapper> builder = new ConacoTask.Builder<ImageWrapper>()
ConacoTask.Builder<Bitmap> builder = new ConacoTask.Builder<Bitmap>()
.setUnikery(this)
.setKey(key)
.setUrl(url)
@@ -213,67 +213,15 @@ public class LoadImageView extends FixedAspectImageView implements Unikery<Image
private void removeDrawableAndHolder() {
// Remove drawable
Drawable drawable = getDrawable();
if (drawable instanceof TransitionDrawable) {
TransitionDrawable tDrawable = (TransitionDrawable) drawable;
int number = tDrawable.getNumberOfLayers();
if (number > 0) {
drawable = tDrawable.getDrawable(number - 1);
}
}
if (drawable instanceof ImageDrawable) {
((ImageDrawable) drawable).recycle();
}
setImageDrawable(null);
// Remove holder
if (mHolder != null) {
mHolder.release(this);
ImageWrapper imageWrapper = mHolder.getValue();
if (mHolder.isFree()) {
// ImageWrapper is free, stop animate
imageWrapper.stop();
if (!mHolder.isInMemoryCache()) {
// ImageWrapper is not needed any more, recycle it
imageWrapper.recycle();
}
}
mHolder = null;
}
}
public void start() {
Drawable drawable = getDrawable();
if (drawable instanceof TransitionDrawable) {
TransitionDrawable tDrawable = (TransitionDrawable) drawable;
int number = tDrawable.getNumberOfLayers();
if (number > 0) {
drawable = tDrawable.getDrawable(number - 1);
}
}
if (drawable instanceof ImageDrawable) {
((ImageDrawable) drawable).start();
}
}
public void stop() {
Drawable drawable = getDrawable();
if (drawable instanceof TransitionDrawable) {
TransitionDrawable tDrawable = (TransitionDrawable) drawable;
int number = tDrawable.getNumberOfLayers();
if (number > 0) {
drawable = tDrawable.getDrawable(number - 1);
}
}
if (drawable instanceof ImageDrawable) {
((ImageDrawable) drawable).stop();
}
}
@Override
public void onMiss(Conaco.Source source) {
if (source == Conaco.Source.MEMORY) {
@@ -290,7 +238,7 @@ public class LoadImageView extends FixedAspectImageView implements Unikery<Image
}
@Override
public boolean onGetObject(@NonNull ValueHolder<ImageWrapper> holder, Conaco.Source source) {
public boolean onGetObject(@NonNull ValueHolder<Bitmap> holder, Conaco.Source source) {
// Release
mKey = null;
mUrl = null;
@@ -301,12 +249,10 @@ public class LoadImageView extends FixedAspectImageView implements Unikery<Image
removeDrawableAndHolder();
mHolder = holder;
ImageWrapper imageWrapper = holder.getValue();
Drawable drawable = new ImageDrawable(imageWrapper);
imageWrapper.start();
Bitmap bitmap = holder.getValue();
Drawable drawable = new BitmapDrawable(getResources(), bitmap);
if ((source == Conaco.Source.DISK || source == Conaco.Source.NETWORK) &&
imageWrapper.getFrameCount() <= 1) {
if ((source == Conaco.Source.DISK || source == Conaco.Source.NETWORK)) {
Drawable[] layers = new Drawable[2];
layers[0] = new ColorDrawable(Color.TRANSPARENT);
layers[1] = drawable;
@@ -322,8 +268,10 @@ public class LoadImageView extends FixedAspectImageView implements Unikery<Image
@Override
public void onSetDrawable(Drawable drawable) {
mKey = null;
mUrl = null;
mContainer = null;
removeDrawableAndHolder();
setImageDrawable(drawable);
}