Reformat Code
This commit is contained in:
1 parent
01daf4a63e
commit
12f857a936
73 files changed
+2712
-2844
No files matched your search
@@ -32,6 +32,7 @@ import com.hippo.glview.view.GLRoot;
|
||||
import com.hippo.yorozuya.thread.InfiniteThreadExecutor;
|
||||
import com.hippo.yorozuya.thread.PVLock;
|
||||
import com.hippo.yorozuya.thread.PriorityThreadFactory;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.ref.WeakReference;
|
||||
@@ -43,55 +44,23 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class ImageTexture implements Texture, Animatable {
|
||||
|
||||
@IntDef({TILE_SMALL, TILE_LARGE})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
private @interface TileType {}
|
||||
|
||||
private static final int TILE_SMALL = 0;
|
||||
private static final int TILE_LARGE = 1;
|
||||
|
||||
private static final int SMALL_CONTENT_SIZE = 254;
|
||||
private static final int SMALL_BORDER_SIZE = 1;
|
||||
private static final int SMALL_TILE_SIZE = SMALL_CONTENT_SIZE + 2 * SMALL_BORDER_SIZE;
|
||||
|
||||
private static final int LARGE_CONTENT_SIZE = SMALL_CONTENT_SIZE * 2;
|
||||
private static final int LARGE_BORDER_SIZE = SMALL_BORDER_SIZE * 2;
|
||||
private static final int LARGE_TILE_SIZE = LARGE_CONTENT_SIZE + 2 * LARGE_BORDER_SIZE;
|
||||
|
||||
private static final int INIT_CAPACITY = 8;
|
||||
|
||||
// We are targeting at 60fps, so we have 16ms for each frame.
|
||||
// In this 16ms, we use about 4~8 ms to upload tiles.
|
||||
private static final long UPLOAD_TILE_LIMIT = 4; // ms
|
||||
|
||||
private static final Executor sThreadExecutor;
|
||||
private static final PVLock sPVLock;
|
||||
|
||||
private static final Object sFreeTileLock = new Object();
|
||||
private static Tile sSmallFreeTileHead = null;
|
||||
private static Tile sLargeFreeTileHead = null;
|
||||
private static final Object sFreeTileLock = new Object();
|
||||
|
||||
private final ImageWrapper mImage;
|
||||
private int mUploadIndex = 0;
|
||||
private final Tile[] mTiles; // Can be modified in different threads.
|
||||
// Should be protected by "synchronized."
|
||||
|
||||
private final int mWidth;
|
||||
private final int mHeight;
|
||||
private final boolean mOpaque;
|
||||
private final RectF mSrcRect = new RectF();
|
||||
private final RectF mDestRect = new RectF();
|
||||
|
||||
private boolean mImageBusy = false;
|
||||
private final AtomicBoolean mRunning = new AtomicBoolean();
|
||||
private final AtomicBoolean mRequestAnimation = new AtomicBoolean();
|
||||
private final AtomicBoolean mFrameDirty = new AtomicBoolean();
|
||||
private final AtomicBoolean mNeedRelease = new AtomicBoolean();
|
||||
private final AtomicBoolean mReleased = new AtomicBoolean();
|
||||
|
||||
private Runnable mAnimateRunnable = null;
|
||||
|
||||
private WeakReference<Callback> mCallback;
|
||||
|
||||
static {
|
||||
sThreadExecutor = new InfiniteThreadExecutor(10 * 1000, new LinkedList<Runnable>(),
|
||||
@@ -99,275 +68,24 @@ public class ImageTexture implements Texture, Animatable {
|
||||
sPVLock = new PVLock(3);
|
||||
}
|
||||
|
||||
public static class Uploader implements GLRoot.OnGLIdleListener {
|
||||
private final ArrayDeque<ImageTexture> mTextures =
|
||||
new ArrayDeque<>(INIT_CAPACITY);
|
||||
private final ImageWrapper mImage;
|
||||
private final Tile[] mTiles; // Can be modified in different threads.
|
||||
private final int mWidth;
|
||||
// Should be protected by "synchronized."
|
||||
private final int mHeight;
|
||||
private final boolean mOpaque;
|
||||
private final RectF mSrcRect = new RectF();
|
||||
private final RectF mDestRect = new RectF();
|
||||
private final AtomicBoolean mRunning = new AtomicBoolean();
|
||||
private final AtomicBoolean mRequestAnimation = new AtomicBoolean();
|
||||
private final AtomicBoolean mFrameDirty = new AtomicBoolean();
|
||||
private final AtomicBoolean mNeedRelease = new AtomicBoolean();
|
||||
private final AtomicBoolean mReleased = new AtomicBoolean();
|
||||
private int mUploadIndex = 0;
|
||||
private boolean mImageBusy = false;
|
||||
private Runnable mAnimateRunnable = null;
|
||||
|
||||
private final GLRoot mGlRoot;
|
||||
private boolean mIsQueued = false;
|
||||
|
||||
public Uploader(GLRoot glRoot) {
|
||||
mGlRoot = glRoot;
|
||||
}
|
||||
|
||||
public synchronized void clear() {
|
||||
mTextures.clear();
|
||||
}
|
||||
|
||||
public synchronized void addTexture(ImageTexture t) {
|
||||
if (t.isReady()) return;
|
||||
mTextures.addLast(t);
|
||||
|
||||
if (mIsQueued) return;
|
||||
mIsQueued = true;
|
||||
mGlRoot.addOnGLIdleListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) {
|
||||
ArrayDeque<ImageTexture> deque = mTextures;
|
||||
synchronized (this) {
|
||||
long now = SystemClock.uptimeMillis();
|
||||
long dueTime = now + UPLOAD_TILE_LIMIT;
|
||||
while (now < dueTime && !deque.isEmpty()) {
|
||||
ImageTexture t = deque.peekFirst();
|
||||
if (t.uploadNextTile(canvas)) {
|
||||
deque.removeFirst();
|
||||
mGlRoot.requestRender();
|
||||
}
|
||||
now = SystemClock.uptimeMillis();
|
||||
}
|
||||
mIsQueued = !mTextures.isEmpty();
|
||||
|
||||
// return true to keep this listener in the queue
|
||||
return mIsQueued;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Tile extends NativeTexture {
|
||||
|
||||
@TileType
|
||||
private int mTileType;
|
||||
public int offsetX;
|
||||
public int offsetY;
|
||||
public ImageWrapper image;
|
||||
public Tile nextFreeTile;
|
||||
public int contentWidth;
|
||||
public int contentHeight;
|
||||
public int borderSize;
|
||||
|
||||
public void setSize(@TileType int tileType, int width, int height) {
|
||||
mTileType = tileType;
|
||||
int tileSize;
|
||||
if (tileType == TILE_SMALL) {
|
||||
borderSize = SMALL_BORDER_SIZE;
|
||||
tileSize = SMALL_TILE_SIZE;
|
||||
} else if (tileType == TILE_LARGE) {
|
||||
borderSize = LARGE_BORDER_SIZE;
|
||||
tileSize = LARGE_TILE_SIZE;
|
||||
} else {
|
||||
throw new IllegalStateException("Not support tile type: " + tileType);
|
||||
}
|
||||
contentWidth = width;
|
||||
contentHeight = height;
|
||||
|
||||
mWidth = width + 2 * borderSize;
|
||||
mHeight = height + 2 * borderSize;
|
||||
mTextureWidth = tileSize;
|
||||
mTextureHeight = tileSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void texImage(boolean init) {
|
||||
if (image != null && !image.isRecycled()) {
|
||||
int w, h;
|
||||
if (init) {
|
||||
w = mTextureWidth;
|
||||
h = mTextureHeight;
|
||||
} else {
|
||||
w = mWidth;
|
||||
h = mHeight;
|
||||
}
|
||||
image.texImage(init, offsetX - borderSize, offsetY - borderSize, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
private void invalidate() {
|
||||
invalidateContent();
|
||||
image = null;
|
||||
}
|
||||
|
||||
public void free() {
|
||||
switch (mTileType) {
|
||||
case TILE_SMALL:
|
||||
freeSmallTile(this);
|
||||
break;
|
||||
case TILE_LARGE:
|
||||
freeLargeTile(this);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Not support tile type: " + mTileType);
|
||||
}
|
||||
}
|
||||
|
||||
private static void freeSmallTile(Tile tile) {
|
||||
tile.invalidate();
|
||||
synchronized (sFreeTileLock) {
|
||||
tile.nextFreeTile = sSmallFreeTileHead;
|
||||
sSmallFreeTileHead = tile;
|
||||
}
|
||||
}
|
||||
|
||||
private static void freeLargeTile(Tile tile) {
|
||||
tile.invalidate();
|
||||
synchronized (sFreeTileLock) {
|
||||
tile.nextFreeTile = sLargeFreeTileHead;
|
||||
sLargeFreeTileHead = tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Tile obtainSmallTile() {
|
||||
synchronized (sFreeTileLock) {
|
||||
Tile result = sSmallFreeTileHead;
|
||||
if (result == null) {
|
||||
return new Tile();
|
||||
} else {
|
||||
sSmallFreeTileHead = result.nextFreeTile;
|
||||
result.nextFreeTile = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static Tile obtainLargeTile() {
|
||||
synchronized (sFreeTileLock) {
|
||||
Tile result = sLargeFreeTileHead;
|
||||
if (result == null) {
|
||||
return new Tile();
|
||||
} else {
|
||||
sLargeFreeTileHead = result.nextFreeTile;
|
||||
result.nextFreeTile = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private class AnimateRunnable implements Runnable {
|
||||
|
||||
public void doRun() {
|
||||
long lastTime = System.nanoTime();
|
||||
long lastDelay = -1L;
|
||||
|
||||
synchronized (mImage) {
|
||||
// Check released, image busy, Need release
|
||||
if (mReleased.get() || mImage.isImageRecycled() || mImageBusy || mNeedRelease.get()) {
|
||||
mAnimateRunnable = null;
|
||||
return;
|
||||
}
|
||||
// Obtain image
|
||||
mImageBusy = true;
|
||||
}
|
||||
|
||||
if (!mImage.isCompleted()) {
|
||||
try {
|
||||
sPVLock.p();
|
||||
} catch (InterruptedException e) {
|
||||
// Ignore
|
||||
}
|
||||
if (!mNeedRelease.get()) {
|
||||
mImage.complete();
|
||||
}
|
||||
sPVLock.v();
|
||||
}
|
||||
|
||||
int frameCount = mImage.getFrameCount();
|
||||
|
||||
synchronized (mImage) {
|
||||
// Release image
|
||||
mImageBusy = false;
|
||||
// Check need release, frameCount <= 1
|
||||
if (mNeedRelease.get() || frameCount <= 1) {
|
||||
mAnimateRunnable = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mRequestAnimation.get()) {
|
||||
mRunning.lazySet(true);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
// Obtain
|
||||
synchronized (mImage) {
|
||||
// Check released, image busy, Need release, not running
|
||||
if (mReleased.get() || mImage.isImageRecycled() || mImageBusy || mNeedRelease.get() || !mRunning.get()) {
|
||||
mAnimateRunnable = null;
|
||||
return;
|
||||
}
|
||||
// Obtain image
|
||||
mImageBusy = true;
|
||||
}
|
||||
|
||||
mImage.advance();
|
||||
long delay = mImage.getDelay();
|
||||
long time = System.nanoTime();
|
||||
if (-1L != lastDelay) {
|
||||
delay -= (time - lastTime) / 1000000 - lastDelay;
|
||||
}
|
||||
lastTime = time;
|
||||
lastDelay = delay;
|
||||
mFrameDirty.lazySet(true);
|
||||
invalidateSelf();
|
||||
|
||||
synchronized (mImage) {
|
||||
// Release image
|
||||
mImageBusy = false;
|
||||
}
|
||||
|
||||
if (delay > 0) {
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
} catch (InterruptedException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
doRun();
|
||||
|
||||
while (mNeedRelease.get()) {
|
||||
// Obtain
|
||||
synchronized (mImage) {
|
||||
// Check released
|
||||
if (mReleased.get() || mImage.isImageRecycled()) {
|
||||
break;
|
||||
}
|
||||
// Check image busy
|
||||
if (mImageBusy) {
|
||||
// Image is busy, means it is recycling
|
||||
break;
|
||||
}
|
||||
// Obtain image
|
||||
mImageBusy = true;
|
||||
}
|
||||
|
||||
if (!mReleased.get()) {
|
||||
mImage.release();
|
||||
mReleased.lazySet(true);
|
||||
}
|
||||
|
||||
synchronized (mImage) {
|
||||
// Release image
|
||||
mImageBusy = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private WeakReference<Callback> mCallback;
|
||||
|
||||
/**
|
||||
* Call {@link ImageWrapper#obtain()} first
|
||||
@@ -443,8 +161,52 @@ public class ImageTexture implements Texture, Animatable {
|
||||
}
|
||||
}
|
||||
|
||||
public final void setCallback(Callback cb) {
|
||||
mCallback = new WeakReference<>(cb);
|
||||
private static Tile obtainSmallTile() {
|
||||
synchronized (sFreeTileLock) {
|
||||
Tile result = sSmallFreeTileHead;
|
||||
if (result == null) {
|
||||
return new Tile();
|
||||
} else {
|
||||
sSmallFreeTileHead = result.nextFreeTile;
|
||||
result.nextFreeTile = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static Tile obtainLargeTile() {
|
||||
synchronized (sFreeTileLock) {
|
||||
Tile result = sLargeFreeTileHead;
|
||||
if (result == null) {
|
||||
return new Tile();
|
||||
} else {
|
||||
sLargeFreeTileHead = result.nextFreeTile;
|
||||
result.nextFreeTile = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// We want to draw the "source" on the "target".
|
||||
// This method is to find the "output" rectangle which is
|
||||
// the corresponding area of the "src".
|
||||
// (x,y) target
|
||||
// (x0,y0) source +---------------+
|
||||
// +----------+ | |
|
||||
// | src | | output |
|
||||
// | +--+ | linear map | +----+ |
|
||||
// | +--+ | ----------> | | | |
|
||||
// | | by (scaleX, scaleY) | +----+ |
|
||||
// +----------+ | |
|
||||
// Texture +---------------+
|
||||
// Canvas
|
||||
private static void mapRect(RectF output,
|
||||
RectF src, float x0, float y0, float x, float y, float scaleX,
|
||||
float scaleY) {
|
||||
output.set(x + (src.left - x0) * scaleX,
|
||||
y + (src.top - y0) * scaleY,
|
||||
x + (src.right - x0) * scaleX,
|
||||
y + (src.bottom - y0) * scaleY);
|
||||
}
|
||||
|
||||
public Callback getCallback() {
|
||||
@@ -454,6 +216,10 @@ public class ImageTexture implements Texture, Animatable {
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void setCallback(Callback cb) {
|
||||
mCallback = new WeakReference<>(cb);
|
||||
}
|
||||
|
||||
public void invalidateSelf() {
|
||||
final Callback callback = getCallback();
|
||||
if (callback != null) {
|
||||
@@ -537,28 +303,6 @@ public class ImageTexture implements Texture, Animatable {
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
// We want to draw the "source" on the "target".
|
||||
// This method is to find the "output" rectangle which is
|
||||
// the corresponding area of the "src".
|
||||
// (x,y) target
|
||||
// (x0,y0) source +---------------+
|
||||
// +----------+ | |
|
||||
// | src | | output |
|
||||
// | +--+ | linear map | +----+ |
|
||||
// | +--+ | ----------> | | | |
|
||||
// | | by (scaleX, scaleY) | +----+ |
|
||||
// +----------+ | |
|
||||
// Texture +---------------+
|
||||
// Canvas
|
||||
private static void mapRect(RectF output,
|
||||
RectF src, float x0, float y0, float x, float y, float scaleX,
|
||||
float scaleY) {
|
||||
output.set(x + (src.left - x0) * scaleX,
|
||||
y + (src.top - y0) * scaleY,
|
||||
x + (src.right - x0) * scaleX,
|
||||
y + (src.bottom - y0) * scaleY);
|
||||
}
|
||||
|
||||
@RenderThread
|
||||
private void syncFrame() {
|
||||
if (mFrameDirty.getAndSet(false)) {
|
||||
@@ -620,7 +364,7 @@ public class ImageTexture implements Texture, Animatable {
|
||||
// Draws a mixed color of this texture and a specified color onto the
|
||||
// a rectangle. The used color is: from * (1 - ratio) + to * ratio.
|
||||
public void drawMixed(GLCanvas canvas, int color, float ratio,
|
||||
int x, int y, int width, int height) {
|
||||
int x, int y, int width, int height) {
|
||||
RectF src = mSrcRect;
|
||||
RectF dest = mDestRect;
|
||||
float scaleX = (float) width / mWidth;
|
||||
@@ -637,7 +381,7 @@ public class ImageTexture implements Texture, Animatable {
|
||||
}
|
||||
|
||||
public void drawMixed(GLCanvas canvas, int color, float ratio,
|
||||
RectF source, RectF target) {
|
||||
RectF source, RectF target) {
|
||||
RectF src = mSrcRect;
|
||||
RectF dest = mDestRect;
|
||||
float x0 = source.left;
|
||||
@@ -699,7 +443,256 @@ public class ImageTexture implements Texture, Animatable {
|
||||
}
|
||||
}
|
||||
|
||||
@IntDef({TILE_SMALL, TILE_LARGE})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
private @interface TileType {
|
||||
}
|
||||
|
||||
public interface Callback {
|
||||
void invalidateImageTexture(ImageTexture who);
|
||||
}
|
||||
|
||||
public static class Uploader implements GLRoot.OnGLIdleListener {
|
||||
private final ArrayDeque<ImageTexture> mTextures =
|
||||
new ArrayDeque<>(INIT_CAPACITY);
|
||||
|
||||
private final GLRoot mGlRoot;
|
||||
private boolean mIsQueued = false;
|
||||
|
||||
public Uploader(GLRoot glRoot) {
|
||||
mGlRoot = glRoot;
|
||||
}
|
||||
|
||||
public synchronized void clear() {
|
||||
mTextures.clear();
|
||||
}
|
||||
|
||||
public synchronized void addTexture(ImageTexture t) {
|
||||
if (t.isReady()) return;
|
||||
mTextures.addLast(t);
|
||||
|
||||
if (mIsQueued) return;
|
||||
mIsQueued = true;
|
||||
mGlRoot.addOnGLIdleListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) {
|
||||
ArrayDeque<ImageTexture> deque = mTextures;
|
||||
synchronized (this) {
|
||||
long now = SystemClock.uptimeMillis();
|
||||
long dueTime = now + UPLOAD_TILE_LIMIT;
|
||||
while (now < dueTime && !deque.isEmpty()) {
|
||||
ImageTexture t = deque.peekFirst();
|
||||
if (t.uploadNextTile(canvas)) {
|
||||
deque.removeFirst();
|
||||
mGlRoot.requestRender();
|
||||
}
|
||||
now = SystemClock.uptimeMillis();
|
||||
}
|
||||
mIsQueued = !mTextures.isEmpty();
|
||||
|
||||
// return true to keep this listener in the queue
|
||||
return mIsQueued;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Tile extends NativeTexture {
|
||||
|
||||
public int offsetX;
|
||||
public int offsetY;
|
||||
public ImageWrapper image;
|
||||
public Tile nextFreeTile;
|
||||
public int contentWidth;
|
||||
public int contentHeight;
|
||||
public int borderSize;
|
||||
@TileType
|
||||
private int mTileType;
|
||||
|
||||
private static void freeSmallTile(Tile tile) {
|
||||
tile.invalidate();
|
||||
synchronized (sFreeTileLock) {
|
||||
tile.nextFreeTile = sSmallFreeTileHead;
|
||||
sSmallFreeTileHead = tile;
|
||||
}
|
||||
}
|
||||
|
||||
private static void freeLargeTile(Tile tile) {
|
||||
tile.invalidate();
|
||||
synchronized (sFreeTileLock) {
|
||||
tile.nextFreeTile = sLargeFreeTileHead;
|
||||
sLargeFreeTileHead = tile;
|
||||
}
|
||||
}
|
||||
|
||||
public void setSize(@TileType int tileType, int width, int height) {
|
||||
mTileType = tileType;
|
||||
int tileSize;
|
||||
if (tileType == TILE_SMALL) {
|
||||
borderSize = SMALL_BORDER_SIZE;
|
||||
tileSize = SMALL_TILE_SIZE;
|
||||
} else if (tileType == TILE_LARGE) {
|
||||
borderSize = LARGE_BORDER_SIZE;
|
||||
tileSize = LARGE_TILE_SIZE;
|
||||
} else {
|
||||
throw new IllegalStateException("Not support tile type: " + tileType);
|
||||
}
|
||||
contentWidth = width;
|
||||
contentHeight = height;
|
||||
|
||||
mWidth = width + 2 * borderSize;
|
||||
mHeight = height + 2 * borderSize;
|
||||
mTextureWidth = tileSize;
|
||||
mTextureHeight = tileSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void texImage(boolean init) {
|
||||
if (image != null && !image.isRecycled()) {
|
||||
int w, h;
|
||||
if (init) {
|
||||
w = mTextureWidth;
|
||||
h = mTextureHeight;
|
||||
} else {
|
||||
w = mWidth;
|
||||
h = mHeight;
|
||||
}
|
||||
image.texImage(init, offsetX - borderSize, offsetY - borderSize, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
private void invalidate() {
|
||||
invalidateContent();
|
||||
image = null;
|
||||
}
|
||||
|
||||
public void free() {
|
||||
switch (mTileType) {
|
||||
case TILE_SMALL:
|
||||
freeSmallTile(this);
|
||||
break;
|
||||
case TILE_LARGE:
|
||||
freeLargeTile(this);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Not support tile type: " + mTileType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class AnimateRunnable implements Runnable {
|
||||
|
||||
public void doRun() {
|
||||
long lastTime = System.nanoTime();
|
||||
long lastDelay = -1L;
|
||||
|
||||
synchronized (mImage) {
|
||||
// Check released, image busy, Need release
|
||||
if (mReleased.get() || mImage.isImageRecycled() || mImageBusy || mNeedRelease.get()) {
|
||||
mAnimateRunnable = null;
|
||||
return;
|
||||
}
|
||||
// Obtain image
|
||||
mImageBusy = true;
|
||||
}
|
||||
|
||||
if (!mImage.isCompleted()) {
|
||||
try {
|
||||
sPVLock.p();
|
||||
} catch (InterruptedException e) {
|
||||
// Ignore
|
||||
}
|
||||
if (!mNeedRelease.get()) {
|
||||
mImage.complete();
|
||||
}
|
||||
sPVLock.v();
|
||||
}
|
||||
|
||||
int frameCount = mImage.getFrameCount();
|
||||
|
||||
synchronized (mImage) {
|
||||
// Release image
|
||||
mImageBusy = false;
|
||||
// Check need release, frameCount <= 1
|
||||
if (mNeedRelease.get() || frameCount <= 1) {
|
||||
mAnimateRunnable = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mRequestAnimation.get()) {
|
||||
mRunning.lazySet(true);
|
||||
}
|
||||
|
||||
for (; ; ) {
|
||||
// Obtain
|
||||
synchronized (mImage) {
|
||||
// Check released, image busy, Need release, not running
|
||||
if (mReleased.get() || mImage.isImageRecycled() || mImageBusy || mNeedRelease.get() || !mRunning.get()) {
|
||||
mAnimateRunnable = null;
|
||||
return;
|
||||
}
|
||||
// Obtain image
|
||||
mImageBusy = true;
|
||||
}
|
||||
|
||||
mImage.advance();
|
||||
long delay = mImage.getDelay();
|
||||
long time = System.nanoTime();
|
||||
if (-1L != lastDelay) {
|
||||
delay -= (time - lastTime) / 1000000 - lastDelay;
|
||||
}
|
||||
lastTime = time;
|
||||
lastDelay = delay;
|
||||
mFrameDirty.lazySet(true);
|
||||
invalidateSelf();
|
||||
|
||||
synchronized (mImage) {
|
||||
// Release image
|
||||
mImageBusy = false;
|
||||
}
|
||||
|
||||
if (delay > 0) {
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
} catch (InterruptedException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
doRun();
|
||||
|
||||
while (mNeedRelease.get()) {
|
||||
// Obtain
|
||||
synchronized (mImage) {
|
||||
// Check released
|
||||
if (mReleased.get() || mImage.isImageRecycled()) {
|
||||
break;
|
||||
}
|
||||
// Check image busy
|
||||
if (mImageBusy) {
|
||||
// Image is busy, means it is recycling
|
||||
break;
|
||||
}
|
||||
// Obtain image
|
||||
mImageBusy = true;
|
||||
}
|
||||
|
||||
if (!mReleased.get()) {
|
||||
mImage.release();
|
||||
mReleased.lazySet(true);
|
||||
}
|
||||
|
||||
synchronized (mImage) {
|
||||
// Release image
|
||||
mImageBusy = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user