/* * 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 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); } }