From 293ada12c67a7a631b58ac0c2b713fbf97a1387e Mon Sep 17 00:00:00 2001 From: seven332 Date: Thu, 11 Feb 2016 22:14:21 +0800 Subject: [PATCH] Improve GalleryView --- .../hippo/ehviewer/ui/GalleryActivity.java | 49 +- .../java/com/hippo/ehviewer/ui/gl/Fling.java | 61 +-- .../hippo/ehviewer/ui/gl/GalleryPageView.java | 26 +- .../com/hippo/ehviewer/ui/gl/GalleryView.java | 30 +- .../com/hippo/ehviewer/ui/gl/ImageView.java | 83 +++- .../ehviewer/ui/gl/PagerLayoutManager.java | 218 +++++++-- .../ehviewer/ui/gl/ScrollLayoutManager.java | 5 + .../java/com/hippo/gl/anim/Animation.java | 8 +- .../com/hippo/gl/annotation/RenderThread.java | 27 ++ .../com/hippo/gl/glrenderer/ImageTexture.java | 418 ++++++------------ .../com/hippo/gl/image/AnimatedImage.java | 122 ----- .../main/java/com/hippo/gl/image/Image.java | 87 ---- .../main/java/com/hippo/gl/view/GLView.java | 68 +-- .../java/com/hippo/gl/widget/GLEdgeView.java | 3 +- .../com/hippo/gl/widget/GLProgressView.java | 4 +- .../java/com/hippo/gl/widget/GLTextView.java | 4 + 16 files changed, 557 insertions(+), 656 deletions(-) create mode 100644 app/src/main/java/com/hippo/gl/annotation/RenderThread.java delete mode 100644 app/src/main/java/com/hippo/gl/image/AnimatedImage.java delete mode 100644 app/src/main/java/com/hippo/gl/image/Image.java diff --git a/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java index d13ed5a99..e28eb2f0d 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java @@ -25,13 +25,20 @@ import android.view.MotionEvent; import com.hippo.ehviewer.R; import com.hippo.ehviewer.ui.gl.GalleryPageView; import com.hippo.ehviewer.ui.gl.GalleryView; +import com.hippo.gl.glrenderer.ImageTexture; import com.hippo.gl.view.GLRootView; +import com.hippo.image.Image; import com.hippo.util.Dpad; +import java.io.FileInputStream; +import java.io.FileNotFoundException; + public class GalleryActivity extends AppCompatActivity { private GalleryView mGalleryView; + private ImageTexture.Uploader mUploader; + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -40,12 +47,14 @@ public class GalleryActivity extends AppCompatActivity { GLRootView glRootView = (GLRootView) findViewById(R.id.gl_root_view); mGalleryView = new GalleryView(this, new InvalidPageIterator(), null); glRootView.setContentPane(mGalleryView); + mUploader = new ImageTexture.Uploader(glRootView); } @Override protected void onDestroy() { super.onDestroy(); mGalleryView = null; + mUploader = null; } @Override @@ -145,51 +154,73 @@ public class GalleryActivity extends AppCompatActivity { } + private static final int MAX_SIZE = 10; + private class InvalidPageIterator implements GalleryView.PageIterator { + private int mIndex = 0; + private int mBackup = mIndex; + + public ImageTexture mImageTexture; + + public InvalidPageIterator() { + try { + mImageTexture = new ImageTexture(Image.decode(new FileInputStream("/sdcard/1.jpg"), false)); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } + @Override public void mark() { - + mBackup = mIndex; } @Override public void reset() { - + mIndex = mBackup; } @Override public boolean hasNext() { - return false; + return mIndex < MAX_SIZE - 1; } @Override public boolean hasPrevious() { - return false; + return mIndex > 0; } @Override public boolean isValid() { - return false; + return mIndex >= 0 && mIndex < MAX_SIZE; } @Override public void next() { - + if (mIndex >= MAX_SIZE - 1) { + throw new IndexOutOfBoundsException(); + } + mIndex++; } @Override public void previous() { - + if (mIndex <= 0) { + throw new IndexOutOfBoundsException(); + } + mIndex--; } @Override public void bind(GalleryPageView view) { - + //view.showImage(); + view.getImageView().setTexture(mImageTexture); } @Override public void unbind(GalleryPageView view) { - + view.getImageView().setTexture(null); } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/Fling.java b/app/src/main/java/com/hippo/ehviewer/ui/gl/Fling.java index ac9eb3512..6cd82e021 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/gl/Fling.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/gl/Fling.java @@ -23,7 +23,7 @@ import android.view.animation.Interpolator; import com.hippo.gl.anim.Animation; -class Fling extends Animation { +public abstract class Fling extends Animation { private static final float DECELERATION_RATE = (float) (Math.log(0.78) / Math.log(0.9)); private static final float INFLEXION = 0.35f; // Tension lines cross at (INFLEXION, 1) @@ -39,13 +39,6 @@ class Fling extends Animation { private float mPhysicalCoeff; - //private GalleryPageView mPage; - private int mDx; - private int mDy; - private int mLastX; - private int mLastY; - private int[] mTemp = new int[2]; - static { float x_min = 0.0f; float y_min = 0.0f; @@ -103,6 +96,7 @@ class Fling extends Animation { * 39.37f // inch/meter * ppi * 0.84f; // look and feel tuning + setInterpolator(FLING_INTERPOLATOR); } private double getSplineDeceleration(int velocity) { @@ -110,13 +104,13 @@ class Fling extends Animation { } /* Returns the duration, expressed in milliseconds */ - private int getSplineFlingDuration(int velocity) { + protected int getSplineFlingDuration(int velocity) { final double l = getSplineDeceleration(velocity); final double decelMinusOne = DECELERATION_RATE - 1.0; return (int) (1000.0 * Math.exp(l / decelMinusOne)); } - private double getSplineFlingDistance(int velocity) { + protected double getSplineFlingDistance(int velocity) { final double l = getSplineDeceleration(velocity); final double decelMinusOne = DECELERATION_RATE - 1.0; return FLING_FRICTION * mPhysicalCoeff * Math.exp(DECELERATION_RATE / decelMinusOne * l); @@ -126,7 +120,7 @@ class Fling extends Animation { * Modifies mDuration to the duration it takes to get from start to newFinal using the * spline interpolation. The previous duration was needed to get to oldFinal. **/ - private int adjustDuration(int start, int oldFinal, int newFinal, int duration) { + protected int adjustDuration(int start, int oldFinal, int newFinal, int duration) { final int oldDistance = oldFinal - start; final int newDistance = newFinal - start; final float x = Math.abs((float) newDistance / oldDistance); @@ -141,49 +135,4 @@ class Fling extends Animation { } return duration; } - - public void startFling(GalleryPageView page, int velocityX, int minX, int maxX, - int velocityY, int minY, int maxY) { - //mPage = page; - mDx = (int) (getSplineFlingDistance(velocityX) * Math.signum(velocityX)); - mDy = (int) (getSplineFlingDistance(velocityY) * Math.signum(velocityY)); - mLastX = 0; - mLastY = 0; - int durationX = getSplineFlingDuration(velocityX); - int durationY = getSplineFlingDuration(velocityY); - - if (mDx < minX) { - durationX = adjustDuration(0, mDx, minX, durationX); - mDx = minX; - } - if (mDx > maxX) { - durationX = adjustDuration(0, mDx, maxX, durationX); - mDx = maxX; - } - if (mDy < minY) { - durationY = adjustDuration(0, mDy, minY, durationY); - mDy = minY; - } - if (mDy > maxY) { - durationY = adjustDuration(0, mDy, maxY, durationY); - mDy = maxY; - } - - setDuration(Math.max(durationX, durationY)); - start(); - // TODO invalidate(); - } - - @Override - protected void onCalculate(float progress) { - int x = (int) (mDx * progress); - int y = (int) (mDy * progress); - int offsetX = x - mLastX; - int offsetY = y - mLastY; - if (offsetX != 0 || offsetY != 0) { - // TODO mPage.scroll(-offsetX, -offsetY, mTemp); - } - mLastX = x; - mLastY = y; - } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryPageView.java b/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryPageView.java index 59f58e8cd..f8fb38cb6 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryPageView.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryPageView.java @@ -29,10 +29,11 @@ import com.hippo.yorozuya.LayoutUtils; public class GalleryPageView extends GLFrameLayout { - public ImageView mImage; - public GLTextView mPage; - public GLTextureView mError; - public GLProgressView mProgress; + private ImageView mImage; + private GLLinearLayout mInfo; + private GLTextView mPage; + private GLTextureView mError; + private GLProgressView mProgress; // TODO private static TextTexture sPageTextTexture; @@ -45,12 +46,12 @@ public class GalleryPageView extends GLFrameLayout { addComponent(mImage, glp); // Add other panel - GLLinearLayout ll = new GLLinearLayout(); - ll.setOrientation(GLLinearLayout.VERTICAL); + mInfo = new GLLinearLayout(); + mInfo.setOrientation(GLLinearLayout.VERTICAL); glp = new GravityLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); glp.gravity = Gravity.CENTER; - addComponent(ll, glp); + addComponent(mInfo, glp); // Add page mPage = new GLTextView(); @@ -58,14 +59,14 @@ public class GalleryPageView extends GLFrameLayout { GLLinearLayout.LayoutParams lp = new GLLinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); lp.gravity = Gravity.CENTER_HORIZONTAL; - ll.addComponent(mPage, lp); + mInfo.addComponent(mPage, lp); // Add error mError = new GLTextureView(); lp = new GLLinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); lp.gravity = Gravity.CENTER_HORIZONTAL; - ll.addComponent(mError, lp); + mInfo.addComponent(mError, lp); // Add progress mProgress = new GLProgressView(); @@ -74,7 +75,12 @@ public class GalleryPageView extends GLFrameLayout { lp = new GLLinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); lp.gravity = Gravity.CENTER_HORIZONTAL; - ll.addComponent(mProgress, lp); + mInfo.addComponent(mProgress, lp); + } + + public void showImage() { + //mImage.setVisibility(VISIBLE); + //mInfo.setVisibility(INVISIBLE); } public ImageView getImageView() { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryView.java b/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryView.java index 5aaea4c74..4220ed59e 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryView.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryView.java @@ -26,6 +26,7 @@ import android.view.animation.Interpolator; import android.view.animation.OvershootInterpolator; import com.hippo.ehviewer.R; +import com.hippo.gl.annotation.RenderThread; import com.hippo.gl.glrenderer.GLCanvas; import com.hippo.gl.util.GalleryUtils; import com.hippo.gl.view.AnimationTime; @@ -139,7 +140,7 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { int interval = LayoutUtils.dp2pix(context, INTERVAL_IN_DP); int progressSize = LayoutUtils.dp2pix(context, PROGRESS_SIZE_IN_DP); - mPagerLayoutManager = new PagerLayoutManager(this, interval, progressSize); + mPagerLayoutManager = new PagerLayoutManager(context, this, interval, progressSize); mPagerLayoutManager.onAttach(iterator); mLayoutManager = mPagerLayoutManager; @@ -182,12 +183,8 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { } public void requestFill() { - if (GalleryUtils.isRenderThread()) { - fill(); - } else { - mRequestFill = true; - invalidate(); - } + mRequestFill = true; + invalidate(); } @Override @@ -254,16 +251,9 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { return onScale(focusX, focusY, 1.0f); } - private boolean canScale() { - if (mLastLayoutMode != LAYOUT_MODE_LEFT_TO_RIGHT && mLastLayoutMode != LAYOUT_MODE_RIGHT_TO_LEFT) { - return false; - } - return !mScroll; - } - @Override public boolean onScale(float focusX, float focusY, float scale) { - if (!canScale()) { + if (mScroll || !mLayoutManager.canScale()) { return false; } mScale = true; @@ -294,7 +284,7 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { @Override public void onPointerDown(float x, float y) { - if (canScale()) { + if (!mScroll && mLayoutManager.canScale()) { mScale = true; } } @@ -330,6 +320,7 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { } } + @RenderThread private void fill() { // Must be in render thread GalleryUtils.assertInRenderThread(); @@ -341,7 +332,7 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { } @Override - public void onRender(GLCanvas canvas) { + public void render(GLCanvas canvas) { long time = AnimationTime.get(); boolean invalidate = mLayoutManager.onUpdateAnimation(time); if (invalidate) { @@ -354,7 +345,8 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { fill(); } - mEdgeView.onRender(canvas); + super.render(canvas); + mEdgeView.render(canvas); } public GalleryPageView obtainPage() { @@ -436,6 +428,8 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { public abstract void onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY); + public abstract boolean canScale(); + public abstract void onScale(float focusX, float focusY, float scale); public abstract boolean onUpdateAnimation(long time); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/ImageView.java b/app/src/main/java/com/hippo/ehviewer/ui/gl/ImageView.java index 312560b72..6f99f8290 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/gl/ImageView.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/gl/ImageView.java @@ -39,7 +39,7 @@ public class ImageView extends GLView { @Retention(RetentionPolicy.SOURCE) public @interface StartPosition {} - // TODO adjust scale max and min accroding to image size and screen size + // TODO adjust scale max and min according to image size and screen size private static final float SCALE_MIN = 1 / 10.0f; private static final float SCALE_MAX = 10.0f; @@ -100,6 +100,83 @@ public class ImageView extends GLView { mPositionInRootDirty = true; } + public boolean isLoaded() { + return mTexture != null; + } + + public boolean canFlingVertically() { + if (mScaleOffsetDirty) { + setScaleOffset(mScaleMode, mStartPosition, mScaleValue); + if (mScaleOffsetDirty) { + return false; + } + } + + return mDst.top < 0.0f || mDst.bottom > getHeight(); + } + + public boolean canFlingHorizontally() { + if (mScaleOffsetDirty) { + setScaleOffset(mScaleMode, mStartPosition, mScaleValue); + if (mScaleOffsetDirty) { + return false; + } + } + + return mDst.left < 0.0f || mDst.right > getWidth(); + } + + public boolean canFling() { + if (mScaleOffsetDirty) { + setScaleOffset(mScaleMode, mStartPosition, mScaleValue); + if (mScaleOffsetDirty) { + return false; + } + } + + return mDst.left < 0.0f || mDst.top < 0.0f || mDst.right > getWidth() || mDst.bottom > getHeight(); + } + + public int getMaxDx() { + if (mScaleOffsetDirty) { + setScaleOffset(mScaleMode, mStartPosition, mScaleValue); + if (mScaleOffsetDirty) { + return 0; + } + } + return Math.max(0, -(int) mDst.left); + } + + public int getMinDx() { + if (mScaleOffsetDirty) { + setScaleOffset(mScaleMode, mStartPosition, mScaleValue); + if (mScaleOffsetDirty) { + return 0; + } + } + return Math.min(0, getWidth() - (int) mDst.right); + } + + public int getMaxDy() { + if (mScaleOffsetDirty) { + setScaleOffset(mScaleMode, mStartPosition, mScaleValue); + if (mScaleOffsetDirty) { + return 0; + } + } + return Math.max(0, -(int) mDst.top); + } + + public int getMinDy() { + if (mScaleOffsetDirty) { + setScaleOffset(mScaleMode, mStartPosition, mScaleValue); + if (mScaleOffsetDirty) { + return 0; + } + } + return Math.min(0, getHeight() - (int) mDst.bottom); + } + /** * If target is shorter then screen, make it in screen center. If target is * longer then parent, make sure target fill parent over @@ -251,7 +328,6 @@ public class ImageView extends GLView { mPositionInRootDirty = true; } - public void scroll(int dx, int dy, int[] remain) { // Only work after layout if (mScaleOffsetDirty) { @@ -367,6 +443,8 @@ public class ImageView extends GLView { srcActual.setEmpty(); dstActual.setEmpty(); } + + mPositionInRootDirty = false; } @Override @@ -381,7 +459,6 @@ public class ImageView extends GLView { } if (mPositionInRootDirty) { - mPositionInRootDirty = false; applyPositionInRoot(); } else { mSrcActual.set(0, 0, texture.getWidth(), texture.getHeight()); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/PagerLayoutManager.java b/app/src/main/java/com/hippo/ehviewer/ui/gl/PagerLayoutManager.java index 00d79b2e8..47d23f752 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/gl/PagerLayoutManager.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/gl/PagerLayoutManager.java @@ -16,9 +16,9 @@ package com.hippo.ehviewer.ui.gl; +import android.content.Context; import android.support.annotation.IntDef; import android.support.annotation.NonNull; -import android.util.Log; import android.view.MotionEvent; import android.view.animation.Interpolator; @@ -70,10 +70,17 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { private int mDeltaX; private int mDeltaY; - public PagerLayoutManager(@NonNull GalleryView galleryView, int interval, int progressSize) { + private PageFling mPageFling; + private SmoothScroller mSmoothScroller; + + public PagerLayoutManager(Context context, @NonNull GalleryView galleryView, + int interval, int progressSize) { super(galleryView); mInterval = interval; mProgressSpec = GLView.MeasureSpec.makeMeasureSpec(progressSize, GLView.MeasureSpec.EXACTLY); + + mPageFling = new PageFling(context); + mSmoothScroller = new SmoothScroller(); } public void setMode(@Mode int mode) { @@ -255,11 +262,30 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { public void onDown() { mDeltaX = 0; mDeltaY = 0; + + mSmoothScroller.cancel(); + mPageFling.cancel(); } @Override public void onUp() { mGalleryView.getEdgeView().onRelease(); + + // Scroll + if (mOffset != 0) { + int width = mGalleryView.getWidth(); + int dx; + if (mOffset >= mInterval && getLeftPage() != null) { + dx = mOffset - width - mInterval; + } else if (mOffset <= -mInterval && getRightPage() != null) { + dx = mOffset + width + mInterval; + } else { + dx = mOffset; + } + final float pageDelta = 7 * (float) Math.abs(mOffset) / (width + mInterval); + int duration = (int) ((pageDelta + 1) * 100); + mSmoothScroller.startSmoothScroll(dx, 0, duration); + } } private void pagePrevious() { @@ -303,9 +329,9 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { if (iterator.hasNext()) { iterator.mark(); iterator.next(); - mPrevious = mGalleryView.obtainPage(); - iterator.bind(mPrevious); - mGalleryView.addComponent(mPrevious); + mNext = mGalleryView.obtainPage(); + iterator.bind(mNext); + mGalleryView.addComponent(mNext); iterator.reset(); } } @@ -341,31 +367,41 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { int remain; if (dx < 0) { // Try to show left + int limit; if (leftPage == null) { - return dx; + limit = 0; + } else { + limit = width + mInterval; } - if (dx > mOffset - width - mInterval) { + if (dx > mOffset - limit) { remain = 0; mOffset -= dx; } else { - // Go to left page - pageLeft(); - remain = dx + width + mInterval - mOffset; + // Go to left page if left page not null + if (leftPage != null) { + pageLeft(); + } + remain = dx + limit - mOffset; mOffset = 0; } } else { // Try to show right + int limit; if (rightPage == null) { - return dx; + limit = 0; + } else { + limit = - width - mInterval; } - if (dx < width + mInterval + mOffset) { + if (dx < mOffset - limit) { remain = 0; mOffset -= dx; } else { - // Go to right page - pageRight(); - remain = dx - width - mInterval - mOffset; + // Go to right page if right page not null + if (rightPage != null) { + pageRight(); + } + remain = dx + limit - mOffset; mOffset = 0; } } @@ -373,7 +409,7 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { return remain; } - public void overScrollEdge(float x, float y, int dx, int dy) { + public void overScrollEdge(int dx, int dy, float x, float y) { GLEdgeView edgeView = mGalleryView.getEdgeView(); mDeltaX += dx; @@ -391,11 +427,22 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { } } - // TODO What about top and bottom + if (mCurrent.getImageView().canFlingVertically()) { + if (mDeltaY < 0) { + edgeView.onPull(-mDeltaY, x, GLEdgeView.TOP); + if (!edgeView.isFinished(GLEdgeView.BOTTOM)) { + edgeView.onRelease(GLEdgeView.BOTTOM); + } + } else if (mDeltaY > 0) { + edgeView.onPull(mDeltaY, y, GLEdgeView.BOTTOM); + if (!edgeView.isFinished(GLEdgeView.TOP)) { + edgeView.onRelease(GLEdgeView.TOP); + } + } + } } - @Override - public void onScroll(float dx, float dy, float totalX, float totalY, float x, float y) { + public void scrollInternal(float dx, float dy, float x, float y) { if (mCurrent == null) { return; } @@ -422,7 +469,7 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { (getLeftPage() == null && mOffset == 0 && remainX < 0) || (getRightPage() == null && mOffset == 0 && remainX > 0)) { // On edge - overScrollEdge(x, y, remainX, remainY); + overScrollEdge(remainX, remainY, x, y); remainX = 0; remainY = 0; } else if (mCanScrollBetweenPages) { @@ -432,7 +479,6 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { mDeltaX = 0; mDeltaY = 0; } else { - Log.w(TAG, "Something wrong?"); remainX = 0; remainY = 0; mDeltaX = 0; @@ -445,14 +491,26 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { } } + @Override + public void onScroll(float dx, float dy, float totalX, float totalY, float x, float y) { + scrollInternal(dx, dy, x, y); + } + @Override public void onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - if (mCurrent == null) { + if (mCurrent == null || mOffset != 0 || !mCurrent.getImageView().isLoaded() || + !mCurrent.getImageView().canFling()) { return; } + ImageView image = mCurrent.getImageView(); + mPageFling.startFling((int) velocityX, image.getMinDx(), image.getMaxDx(), + (int) velocityY, image.getMinDy(), image.getMaxDy()); + } - + @Override + public boolean canScale() { + return mCurrent != null && mOffset == 0 && mCurrent.getImageView().isLoaded(); } @Override @@ -461,22 +519,17 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { return; } - - + mCurrent.getImageView().scale(focusX, focusY, scale); + // TODO Save scale } @Override public boolean onUpdateAnimation(long time) { - return false; + boolean invalidate = mSmoothScroller.calculate(time); + invalidate |= mPageFling.calculate(time); + return invalidate; } - - - - - - - class SmoothScroller extends Animation { private int mDx; @@ -484,6 +537,10 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { private int mLastX; private int mLastY; + public SmoothScroller() { + setInterpolator(SMOOTH_SCROLLER_INTERPOLATOR); + } + public void startSmoothScroll(int dx, int dy, int duration) { mDx = dx; mDy = dy; @@ -491,7 +548,7 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { mLastY = 0; setDuration(duration); start(); - // TODO invalidate(); + mGalleryView.invalidate(); } @Override @@ -501,16 +558,105 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { int offsetX = x - mLastX; while (offsetX != 0) { int oldOffsetX = offsetX; - // TODO offsetX = scrollBetweenPagesHMode(offsetX); + offsetX = scrollBetweenPages(offsetX); // Avoid loop infinitely if (offsetX == oldOffsetX) { break; } else { - // TODO mRequestFill = true; + mGalleryView.requestFill(); } } mLastX = x; mLastY = y; } } + + class PageFling extends Fling { + + private int mVelocityX; + private int mVelocityY; + private int mDx; + private int mDy; + private int mLastX; + private int mLastY; + private int[] mTemp = new int[2]; + + public PageFling(Context context) { + super(context); + } + + public void startFling(int velocityX, int minX, int maxX, + int velocityY, int minY, int maxY) { + mVelocityX = velocityX; + mVelocityY = velocityY; + mDx = (int) (getSplineFlingDistance(velocityX) * Math.signum(velocityX)); + mDy = (int) (getSplineFlingDistance(velocityY) * Math.signum(velocityY)); + mLastX = 0; + mLastY = 0; + int durationX = getSplineFlingDuration(velocityX); + int durationY = getSplineFlingDuration(velocityY); + + if (mDx < minX) { + durationX = adjustDuration(0, mDx, minX, durationX); + mDx = minX; + } + if (mDx > maxX) { + durationX = adjustDuration(0, mDx, maxX, durationX); + mDx = maxX; + } + if (mDy < minY) { + durationY = adjustDuration(0, mDy, minY, durationY); + mDy = minY; + } + if (mDy > maxY) { + durationY = adjustDuration(0, mDy, maxY, durationY); + mDy = maxY; + } + + setDuration(Math.max(durationX, durationY)); + start(); + mGalleryView.invalidate(); + } + + @Override + protected void onCalculate(float progress) { + int x = (int) (mDx * progress); + int y = (int) (mDy * progress); + int offsetX = x - mLastX; + int offsetY = y - mLastY; + if (mCurrent != null && (offsetX != 0 || offsetY != 0)) { + mCurrent.getImageView().scroll(-offsetX, -offsetY, mTemp); + } + mLastX = x; + mLastY = y; + } + + @Override + protected void onFinish() { + if (mCurrent == null) { + return; + } + + GLEdgeView edgeView = mGalleryView.getEdgeView(); + ImageView imageView = mCurrent.getImageView(); + if (imageView.canFlingHorizontally()) { + if (mVelocityX > 0 && getLeftPage() == null && imageView.getMaxDx() == 0 && + edgeView.isFinished(GLEdgeView.LEFT)) { + edgeView.onAbsorb(mVelocityX, GLEdgeView.LEFT); + } else if (mVelocityX < 0 && getRightPage() == null && imageView.getMinDx() == 0 && + edgeView.isFinished(GLEdgeView.RIGHT)) { + edgeView.onAbsorb(-mVelocityX, GLEdgeView.RIGHT); + } + } + if (imageView.canFlingVertically()) { + if (mVelocityY > 0 && imageView.getMaxDy() == 0 && + edgeView.isFinished(GLEdgeView.TOP)) { + edgeView.onAbsorb(mVelocityY, GLEdgeView.TOP); + } else if (mVelocityY < 0 && imageView.getMinDy() == 0 && + edgeView.isFinished(GLEdgeView.BOTTOM)) { + edgeView.onAbsorb(-mVelocityY, GLEdgeView.BOTTOM); + } + } + } + } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/ScrollLayoutManager.java b/app/src/main/java/com/hippo/ehviewer/ui/gl/ScrollLayoutManager.java index 47ec331c8..441083edc 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/gl/ScrollLayoutManager.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/gl/ScrollLayoutManager.java @@ -60,6 +60,11 @@ public class ScrollLayoutManager extends GalleryView.LayoutManager { } + @Override + public boolean canScale() { + return false; + } + @Override public void onScale(float focusX, float focusY, float scale) { diff --git a/app/src/main/java/com/hippo/gl/anim/Animation.java b/app/src/main/java/com/hippo/gl/anim/Animation.java index eeb9a648e..db7149222 100644 --- a/app/src/main/java/com/hippo/gl/anim/Animation.java +++ b/app/src/main/java/com/hippo/gl/anim/Animation.java @@ -125,11 +125,11 @@ abstract public class Animation { return mStartTime != NO_ANIMATION; } - public void forceStop() { + public void cancel() { mStartTime = NO_ANIMATION; } - public void forceReset() { + public void reset() { mStartTime = ANIMATION_START; mRunnedCount = 0; mLastFrameTime = 0; @@ -153,6 +153,7 @@ abstract public class Animation { if (elapse >= mDuration) { mRunnedCount++; if (mRunnedCount >= mRepeatCount && mRepeatCount != INFINITE) { + onFinish(); mStartTime = NO_ANIMATION; } else { mStartTime += elapse; @@ -163,4 +164,7 @@ abstract public class Animation { } abstract protected void onCalculate(float progress); + + protected void onFinish() { + } } diff --git a/app/src/main/java/com/hippo/gl/annotation/RenderThread.java b/app/src/main/java/com/hippo/gl/annotation/RenderThread.java new file mode 100644 index 000000000..344d7a67e --- /dev/null +++ b/app/src/main/java/com/hippo/gl/annotation/RenderThread.java @@ -0,0 +1,27 @@ +/* + * 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.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE}) +public @interface RenderThread { +} diff --git a/app/src/main/java/com/hippo/gl/glrenderer/ImageTexture.java b/app/src/main/java/com/hippo/gl/glrenderer/ImageTexture.java index 441f00b6d..9320cf7f1 100644 --- a/app/src/main/java/com/hippo/gl/glrenderer/ImageTexture.java +++ b/app/src/main/java/com/hippo/gl/glrenderer/ImageTexture.java @@ -19,14 +19,13 @@ package com.hippo.gl.glrenderer; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.RectF; -import android.os.Process; import android.os.SystemClock; import android.support.annotation.IntDef; import android.support.annotation.NonNull; -import com.hippo.gl.image.AnimatedImage; -import com.hippo.gl.image.Image; +import com.hippo.gl.annotation.RenderThread; import com.hippo.gl.view.GLRoot; +import com.hippo.image.Image; import com.hippo.yorozuya.InfiniteThreadExecutor; import com.hippo.yorozuya.PVLock; import com.hippo.yorozuya.PriorityThreadFactory; @@ -39,12 +38,12 @@ import java.util.concurrent.LinkedBlockingQueue; public class ImageTexture implements Texture { - @IntDef({SMALL, LARGE}) + @IntDef({TILE_SMALL, TILE_LARGE}) @Retention(RetentionPolicy.SOURCE) - private @interface Size {} + private @interface TileType {} - private static final int SMALL = 0; - private static final int LARGE = 1; + 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; @@ -62,7 +61,6 @@ public class ImageTexture implements Texture { private static Bitmap sSmallUploadBitmap; private static Bitmap sLargeUploadBitmap; - private static InfiniteThreadExecutor sThreadExecutor; private static PVLock sPVLock; @@ -70,21 +68,6 @@ public class ImageTexture implements Texture { private static Tile sLargeFreeTileHead = null; private static final Object sFreeTileLock = new Object(); - public static void prepareResources() { - sSmallUploadBitmap = Bitmap.createBitmap(SMALL_TILE_SIZE, SMALL_TILE_SIZE, Bitmap.Config.ARGB_8888); - sLargeUploadBitmap = Bitmap.createBitmap(LARGE_TILE_SIZE, LARGE_TILE_SIZE, Bitmap.Config.ARGB_8888); - sThreadExecutor = new InfiniteThreadExecutor(3000, new LinkedBlockingQueue(), - new PriorityThreadFactory(ImageTexture.class.getSimpleName(), Process.THREAD_PRIORITY_BACKGROUND)); - sPVLock = new PVLock(3); - } - - public static void freeResources() { - sSmallUploadBitmap = null; - sLargeUploadBitmap = null; - sThreadExecutor = null; - sPVLock = null; - } - private final Image mImage; private int mUploadIndex = 0; private final Tile[] mTiles; // Can be modified in different threads. @@ -95,12 +78,17 @@ public class ImageTexture implements Texture { private final RectF mSrcRect = new RectF(); private final RectF mDestRect = new RectF(); - private boolean mRecycled = false; - private boolean mRecycleLock = false; - private boolean mPause = false; - private volatile boolean mConfirmFrame = false; - private int mTargetFrame = -1; - private Callback mCallback; + private volatile boolean mFrameDirty = false; + + + static { + sSmallUploadBitmap = Bitmap.createBitmap(SMALL_TILE_SIZE, SMALL_TILE_SIZE, Bitmap.Config.ARGB_8888); + sLargeUploadBitmap = Bitmap.createBitmap(LARGE_TILE_SIZE, LARGE_TILE_SIZE, Bitmap.Config.ARGB_8888); + sThreadExecutor = new InfiniteThreadExecutor(3000, new LinkedBlockingQueue(), + new PriorityThreadFactory(ImageTexture.class.getSimpleName(), + android.os.Process.THREAD_PRIORITY_BACKGROUND)); + sPVLock = new PVLock(3); + } public static class Uploader implements GLRoot.OnGLIdleListener { private final ArrayDeque mTextures = @@ -150,7 +138,8 @@ public class ImageTexture implements Texture { private static class Tile extends UploadedTexture { - private int mSize; + @TileType + private int mTileType; public int offsetX; public int offsetY; public Image image; @@ -161,18 +150,18 @@ public class ImageTexture implements Texture { public int tileSize; public Bitmap uploadBitmap; - public void setSize(@Size int size, int width, int height) { - mSize = size; - if (size == SMALL) { + public void setSize(@TileType int tileType, int width, int height) { + mTileType = tileType; + if (tileType == TILE_SMALL) { borderSize = SMALL_BORDER_SIZE; tileSize = SMALL_TILE_SIZE; uploadBitmap = sSmallUploadBitmap; - } else if (size == LARGE) { + } else if (tileType == TILE_LARGE) { borderSize = LARGE_BORDER_SIZE; tileSize = LARGE_TILE_SIZE; uploadBitmap = sLargeUploadBitmap; } else { - throw new IllegalStateException("Not support size " + size); + throw new IllegalStateException("Not support tile type: " + tileType); } contentWidth = width; contentHeight = height; @@ -188,8 +177,29 @@ public class ImageTexture implements Texture { final Image image = this.image; if (image != null) { synchronized (image) { - image.copyPixels(offsetX - borderSize, offsetY - borderSize, uploadBitmap, - 0, 0, tileSize, tileSize, Color.TRANSPARENT); + int srcX; + int dstX; + if (offsetX - borderSize < 0) { + srcX = 0; + dstX = borderSize - offsetX; + } else { + srcX = offsetX - borderSize; + dstX = 0; + } + int srcY; + int dstY; + if (offsetY - borderSize < 0) { + srcY = 0; + dstY = borderSize - offsetY; + } else { + srcY = offsetY - borderSize; + dstY = 0; + } + + image.render(srcX, srcY, uploadBitmap, dstX, dstY, + Math.min(image.getWidth() - srcX, tileSize - dstX), + Math.min(image.getHeight() - srcY, tileSize - dstY), + true, Color.TRANSPARENT); } } return uploadBitmap; @@ -224,15 +234,31 @@ public class ImageTexture implements Texture { } public void free() { - switch (mSize) { - case SMALL: + switch (mTileType) { + case TILE_SMALL: freeSmallTile(this); break; - case LARGE: + case TILE_LARGE: freeLargeTile(this); break; default: - throw new IllegalStateException("Not support size " + mSize); + 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; } } } @@ -263,20 +289,69 @@ public class ImageTexture implements Texture { } } - private static void freeSmallTile(Tile tile) { - tile.invalidate(); - synchronized (sFreeTileLock) { - tile.nextFreeTile = sSmallFreeTileHead; - sSmallFreeTileHead = tile; - } - } + public ImageTexture(@NonNull Image image) { + mImage = image; + int width = mWidth = image.getWidth(); + int height = mHeight = image.getHeight(); + ArrayList list = new ArrayList<>(); - private static void freeLargeTile(Tile tile) { - tile.invalidate(); - synchronized (sFreeTileLock) { - tile.nextFreeTile = sLargeFreeTileHead; - sLargeFreeTileHead = tile; + for (int x = 0; x < width; x += LARGE_CONTENT_SIZE) { + for (int y = 0; y < height; y += LARGE_CONTENT_SIZE) { + int w = Math.min(LARGE_CONTENT_SIZE, width - x); + int h = Math.min(LARGE_CONTENT_SIZE, height - y); + + if (w <= SMALL_CONTENT_SIZE) { + Tile tile = obtainSmallTile(); + tile.offsetX = x; + tile.offsetY = y; + tile.image = image; + tile.setSize(TILE_SMALL, w, Math.min(SMALL_CONTENT_SIZE, h)); + list.add(tile); + + int nextHeight = h - SMALL_CONTENT_SIZE; + if (nextHeight > 0) { + Tile nextTile = obtainSmallTile(); + nextTile.offsetX = x; + nextTile.offsetY = y + SMALL_CONTENT_SIZE; + nextTile.image = image; + nextTile.setSize(TILE_SMALL, w, nextHeight); + list.add(nextTile); + } + } else if (h <= SMALL_CONTENT_SIZE) { + Tile tile = obtainSmallTile(); + tile.offsetX = x; + tile.offsetY = y; + tile.image = image; + tile.setSize(TILE_SMALL, Math.min(SMALL_CONTENT_SIZE, w), h); + list.add(tile); + + int nextWidth = w - SMALL_CONTENT_SIZE; + if (nextWidth > 0) { + Tile nextTile = obtainSmallTile(); + nextTile.offsetX = x + SMALL_CONTENT_SIZE; + nextTile.offsetY = y; + nextTile.image = image; + nextTile.setSize(TILE_SMALL, nextWidth, h); + list.add(nextTile); + } + } else { + Tile tile = obtainLargeTile(); + tile.offsetX = x; + tile.offsetY = y; + tile.image = image; + tile.setSize(TILE_LARGE, w, h); + list.add(tile); + } + } } + + mTiles = list.toArray(new Tile[list.size()]); + + /* + if (image instanceof AnimatedImage) { + sThreadExecutor.execute(new DecodeTask()); + } + */ } private boolean uploadNextTile(GLCanvas canvas) { @@ -301,195 +376,6 @@ public class ImageTexture implements Texture { return mUploadIndex == mTiles.length; } - private class DecodeTask implements Runnable { - - private boolean checkRecycle() { - if (mRecycled) { - if (!mImage.isRecycled()) { - mImage.recycle(); - } - return true; - } else { - return false; - } - } - - @Override - public void run() { - synchronized (mImage) { - if (checkRecycle()) { - return; - } - mRecycleLock = true; - } - - AnimatedImage aImage = (AnimatedImage) mImage; - - // Avoid a lot of decode thread at same time - sPVLock.p(); - boolean decode = aImage.decode(); - sPVLock.v(); - - synchronized (mImage) { - mRecycleLock = false; - if (checkRecycle()) { - return; - } - } - - // No need to do animate if decode failed - if (!decode) { - return; - } - - long delay; - long time; - int frame = aImage.getCurrentFrame(); - int frameCount = aImage.getFrameCount(); - long forecast = System.currentTimeMillis(); - - // No need to do animate if only on frame - if (frameCount <= 1) { - return; - } - - while (true) { - synchronized (mImage) { - // Check recycle and unlock recycle - mRecycleLock = false; - if (checkRecycle()) { - return; - } - - // Check pause - while (mPause && mTargetFrame == -1) { - try { - mImage.wait(); - } catch (InterruptedException e) { - // Ignore - } - mPause = false; - - // Check recycle - if (checkRecycle()) { - return; - } - - // Update forecast - forecast = System.currentTimeMillis(); - } - - // Get next frame and delay - time = System.currentTimeMillis(); - if (mTargetFrame == -1) { - frame = (frame + 1) % frameCount; - delay = aImage.getDelay(frame) - (time - forecast); - } else { - frame = mTargetFrame; - delay = 0; - mTargetFrame = -1; - } - forecast = time + delay; - } - - // Do sleep - if (delay > 0) { - try { - Thread.sleep(delay); - } catch (InterruptedException e) { - // Ignore - } - } - - // Check recycle and lock recycle - synchronized (mImage) { - if (checkRecycle()) { - return; - } - mRecycleLock = true; - } - - aImage.setCurrentFrame(frame); - mConfirmFrame = true; - - // Notify - Callback callback = mCallback; - if (callback != null) { - callback.invalidateImage(mImage); - } - } - } - } - - public ImageTexture(@NonNull Image image) { - mImage = image; - int width = mWidth = image.getWidth(); - int height = mHeight = image.getHeight(); - ArrayList list = new ArrayList<>(); - - for (int x = 0; x < width; x += LARGE_CONTENT_SIZE) { - for (int y = 0; y < height; y += LARGE_CONTENT_SIZE) { - int w = Math.min(LARGE_CONTENT_SIZE, width - x); - int h = Math.min(LARGE_CONTENT_SIZE, height - y); - - if (w <= SMALL_TILE_SIZE) { - Tile tile = obtainSmallTile(); - tile.offsetX = x; - tile.offsetY = y; - tile.image = image; - tile.setSize(SMALL, w, Math.min(SMALL_TILE_SIZE, h)); - list.add(tile); - - int nextHeight = h - SMALL_TILE_SIZE; - if (nextHeight > 0) { - Tile nextTile = obtainSmallTile(); - nextTile.offsetX = x; - nextTile.offsetY = y + SMALL_TILE_SIZE; - nextTile.image = image; - nextTile.setSize(SMALL, w, nextHeight); - list.add(nextTile); - } - - } else if (h <= SMALL_TILE_SIZE) { - Tile tile = obtainSmallTile(); - tile.offsetX = x; - tile.offsetY = y; - tile.image = image; - tile.setSize(SMALL, Math.min(SMALL_TILE_SIZE, w), h); - list.add(tile); - - int nextWidth = w - SMALL_TILE_SIZE; - if (nextWidth > 0) { - Tile nextTile = obtainSmallTile(); - nextTile.offsetX = x + SMALL_TILE_SIZE; - nextTile.offsetY = y; - nextTile.image = image; - nextTile.setSize(SMALL, nextWidth, h); - list.add(nextTile); - } - - } else { - Tile tile = obtainLargeTile(); - tile.offsetX = x; - tile.offsetY = y; - tile.image = image; - tile.setSize(LARGE, w, h); - list.add(tile); - } - } - } - - mTiles = list.toArray(new Tile[list.size()]); - - if (image instanceof AnimatedImage) { - sThreadExecutor.execute(new DecodeTask()); - } - } - - public void setCallback(Callback callback) { - mCallback = callback; - } - @Override public int getWidth() { return mWidth; @@ -522,9 +408,10 @@ public class ImageTexture implements Texture { y + (src.bottom - y0) * scaleY); } + @RenderThread private void syncFrame() { - if (mConfirmFrame && mImage instanceof AnimatedImage) { - mConfirmFrame = false; + if (mFrameDirty) { + mFrameDirty = false; // invalid tiles for (int i = 0, n = mTiles.length; i < n; i++) { @@ -582,8 +469,9 @@ public class ImageTexture implements Texture { Tile t = mTiles[i]; src.set(0, 0, t.contentWidth, t.contentHeight); src.offset(t.offsetX, t.offsetY); - if (!src.intersect(source)) + if (!src.intersect(source)) { continue; + } mapRect(dest, src, x0, y0, x, y, scaleX, scaleY); src.offset(t.borderSize - t.offsetX, t.borderSize - t.offsetY); canvas.drawTexture(t, src, dest); @@ -630,8 +518,9 @@ public class ImageTexture implements Texture { Tile t = mTiles[i]; src.set(0, 0, t.contentWidth, t.contentHeight); src.offset(t.offsetX, t.offsetY); - if (!src.intersect(source)) + if (!src.intersect(source)) { continue; + } mapRect(dest, src, x0, y0, x, y, scaleX, scaleY); src.offset(t.borderSize - t.offsetX, t.borderSize - t.offsetY); canvas.drawMixed(t, color, ratio, src, dest); @@ -647,41 +536,4 @@ public class ImageTexture implements Texture { public boolean isReady() { return mUploadIndex == mTiles.length; } - - public void setFrame(int frame) { - synchronized (mImage) { - mTargetFrame = frame; - mImage.notify(); - } - } - - public void setPause(boolean pause) { - synchronized (mImage) { - mPause = pause; - if (!pause) { - mImage.notify(); - } - } - } - - // Can be called in UI thread. - public void recycle() { - synchronized (mImage) { - mRecycled = true; - - for (int i = 0, n = mTiles.length; i < n; ++i) { - mTiles[i].free(); - } - - mImage.notify(); - - if (!mRecycleLock) { - mImage.recycle(); - } - } - } - - public interface Callback { - void invalidateImage(Image image); - } } diff --git a/app/src/main/java/com/hippo/gl/image/AnimatedImage.java b/app/src/main/java/com/hippo/gl/image/AnimatedImage.java deleted file mode 100644 index f25d13812..000000000 --- a/app/src/main/java/com/hippo/gl/image/AnimatedImage.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2015 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.image; - -public class AnimatedImage extends Image { - - private boolean mDecoded; - private int mCurrentFrame; - - private boolean mDirty = false; - - protected AnimatedImage(long nativeImage, int format, int width, int height) { - super(nativeImage, format, width, height); - mCurrentFrame = 0; - } - - protected void checkDecoded() { - if (!mDecoded) { - throw new IllegalStateException("Call decode() first!"); - } - } - - public boolean isDecoded() { - checkRecycled(); - - return mDecoded; - } - - public boolean decode() { - checkRecycled(); - - if (mDecoded) { - return true; - } - if (mNativeImage != 0) { - boolean result = nativeDecode(mNativeImage, mFormat); - mDecoded = true; - return result; - } else { - return false; - } - } - - public boolean isAnimated() { - checkRecycled(); - checkDecoded(); - - return getFrameCount() != 1; - } - - public int getFrameCount() { - checkRecycled(); - checkDecoded(); - - return nativeGetFrameCount(mNativeImage, mFormat); - } - - public int getCurrentFrame() { - checkRecycled(); - checkDecoded(); - - return mCurrentFrame; - } - - public void setCurrentFrame(int frame) { - checkRecycled(); - checkDecoded(); - - int frameCount = getFrameCount(); - if (frame < 0 || frame >= frameCount) { - throw new IndexOutOfBoundsException("Invalid frame " + frame + ", frame count is " + frameCount); - } - mCurrentFrame = frame; - if (mNativeImage != 0) { - nativeSetCurrentFrame(mNativeImage, mFormat, frame); - mDirty = true; - } - } - - public void advance() { - setCurrentFrame((mCurrentFrame + 1) % getFrameCount()); - } - - public int getCurrentDelay() { - return getDelay(mCurrentFrame); - } - - public int getDelay(int frame) { - checkRecycled(); - checkDecoded(); - - int frameCount = getFrameCount(); - if (frame < 0 || frame >= frameCount) { - throw new IndexOutOfBoundsException("Invalid frame " + frame + ", frame count is " + frameCount); - } - return nativeGetDelay(mNativeImage, mFormat, frame); - } - - @Override - public void recycle() { - super.recycle(); - } - - private static native boolean nativeDecode(long nativeImage, int format); - private static native int nativeGetFrameCount(long nativeImage, int format); - private static native void nativeSetCurrentFrame(long nativeImage, int format, int frame); - private static native int nativeGetDelay(long nativeImage, int format, int frame); -} diff --git a/app/src/main/java/com/hippo/gl/image/Image.java b/app/src/main/java/com/hippo/gl/image/Image.java deleted file mode 100644 index 0aaa9e847..000000000 --- a/app/src/main/java/com/hippo/gl/image/Image.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2015 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.image; - -import android.graphics.Bitmap; - -import com.hippo.yorozuya.io.InputStreamPipe; - -public class Image { - - protected long mNativeImage; - - protected final int mFormat; - protected final int mWidth; - protected final int mHeight; - - protected Image(long nativeImage, int format, int width, int height) { - mNativeImage = nativeImage; - mFormat = format; - mWidth = width; - mHeight = height; - } - - protected void checkRecycled() { - if (mNativeImage == 0) { - throw new IllegalStateException("Image is recycled"); - } - } - - public int getFormat() { - return mFormat; - } - - public int getWidth() { - return mWidth; - } - - public int getHeight() { - return mHeight; - } - - public boolean copyPixels(int srcX, int srcY, Bitmap bitmap, int dstX, int dstY, - int width, int height, int defaultColor) { - checkRecycled(); - - return nativeCopyPixels(mNativeImage, mFormat, srcX, srcY, bitmap, - dstX, dstY, width, height, defaultColor); - } - - public void recycle() { - if (mNativeImage != 0) { - nativeRecycle(mNativeImage, mFormat); - mNativeImage = 0; - } - } - - public boolean isRecycled() { - return mNativeImage == 0; - } - - public static Image decodeStreamPipe(InputStreamPipe isPipe) { - return nativeDecodeStreamPipe(isPipe); - } - - static { - System.loadLibrary("image"); - } - - private static native Image nativeDecodeStreamPipe(InputStreamPipe isPipe); - private static native boolean nativeCopyPixels(long nativeImage, int format, int srcX, int srcY, - Bitmap bitmap, int dstX, int dstY, int width, int height, int otherColor); - private static native void nativeRecycle(long nativeImage, int format); -} diff --git a/app/src/main/java/com/hippo/gl/view/GLView.java b/app/src/main/java/com/hippo/gl/view/GLView.java index 58f427499..f5673bc13 100644 --- a/app/src/main/java/com/hippo/gl/view/GLView.java +++ b/app/src/main/java/com/hippo/gl/view/GLView.java @@ -49,7 +49,7 @@ import java.util.ArrayList; public class GLView implements TouchOwner { private static final String TAG = "GLView"; - private static final boolean DEBUG_DRAW_BOUNDS = false; + private static final boolean DEBUG_DRAW_BOUNDS = true; public static final int VISIBLE = 0; public static final int INVISIBLE = 1; @@ -86,7 +86,7 @@ public class GLView implements TouchOwner { /** * Position in root */ - private final int[] mPosition = new int[2]; + private final int[] mPositionInRoot = new int[2]; protected final Rect mPaddings = new Rect(); private GLRoot mRoot; @@ -429,7 +429,7 @@ public class GLView implements TouchOwner { } } - protected void render(GLCanvas canvas) { + public void render(GLCanvas canvas) { // render background color if (Color.alpha(mBackgroundColor) != 0) { canvas.fillRect(0, 0, getWidth(), getHeight(), mBackgroundColor); @@ -626,10 +626,45 @@ public class GLView implements TouchOwner { final boolean forceLayout = (mViewFlags & FLAG_LAYOUT_REQUESTED) == FLAG_LAYOUT_REQUESTED; if (sizeChanged || forceLayout) { onLayout(sizeChanged, left, top, right, bottom); + notifyPositionInRoot(); + } else { + dispatchNotifyPositionInRoot(); } mViewFlags &= ~FLAG_LAYOUT_REQUESTED; } + public void dispatchNotifyPositionInRoot() { + if (notifyPositionInRoot()) { + for (int i = 0, size = getComponentCount(); i < size; i++) { + getComponent(i).dispatchNotifyPositionInRoot(); + } + } + } + + public boolean notifyPositionInRoot() { + // Update position in root + int[] position = mPositionInRoot; + int oldX = position[0]; + int oldY = position[1]; + if (mParent == null) { + position[0] = 0; + position[1] = 0; + } else { + mParent.getPositionInRoot(position); + // Apply offset in parent + position[0] += mBounds.left; + position[1] += mBounds.top; + } + int x = position[0]; + int y = position[1]; + if (x != oldX || y != oldY) { + onPositionInRootChanged(x, y, oldX, oldY); + return true; + } else { + return false; + } + } + private boolean setBounds(int left, int top, int right, int bottom) { Rect bounds = mBounds; int oldW = bounds.right - bounds.left; @@ -644,25 +679,6 @@ public class GLView implements TouchOwner { onSizeChanged(newW, newH, oldW, oldH); } - // Update position in root - int[] position = mPosition; - int oldX = position[0]; - int oldY = position[1]; - if (mParent == null) { - position[0] = 0; - position[1] = 0; - } else { - mParent.getPositionInRoot(position); - // Apply offset in parent - position[0] += left; - position[1] += top; - } - int x = position[0]; - int y = position[1]; - if (x != oldX || y != oldY) { - onPositionInRootChanged(x, y, oldX, oldY); - } - return sizeChanged; } @@ -674,8 +690,8 @@ public class GLView implements TouchOwner { public void getPositionInRoot(int[] position) { AssertUtils.assertEquals("position should be 2 length int array", position.length, 2); - position[0] = mPosition[0]; - position[1] = mPosition[1]; + position[0] = mPositionInRoot[0]; + position[1] = mPositionInRoot[1]; } /** @@ -689,8 +705,8 @@ public class GLView implements TouchOwner { return; } - int x = mPosition[0]; - int y = mPosition[1]; + int x = mPositionInRoot[0]; + int y = mPositionInRoot[1]; rect.set(x, y, x + getWidth(), y + getHeight()); if (!rect.intersect(0, 0, root.getWidth(), root.getHeight())) { rect.setEmpty(); diff --git a/app/src/main/java/com/hippo/gl/widget/GLEdgeView.java b/app/src/main/java/com/hippo/gl/widget/GLEdgeView.java index d56dcf27e..d09210bbf 100644 --- a/app/src/main/java/com/hippo/gl/widget/GLEdgeView.java +++ b/app/src/main/java/com/hippo/gl/widget/GLEdgeView.java @@ -96,8 +96,7 @@ public final class GLEdgeView extends GLView { } @Override - public void onRender(GLCanvas canvas) { - super.onRender(canvas); + public void render(GLCanvas canvas) { boolean more = false; for (int i = 0; i < 4; i++) { canvas.save(GLCanvas.SAVE_FLAG_MATRIX); diff --git a/app/src/main/java/com/hippo/gl/widget/GLProgressView.java b/app/src/main/java/com/hippo/gl/widget/GLProgressView.java index ca74997b4..7f785e8ec 100644 --- a/app/src/main/java/com/hippo/gl/widget/GLProgressView.java +++ b/app/src/main/java/com/hippo/gl/widget/GLProgressView.java @@ -135,14 +135,14 @@ public class GLProgressView extends GLView { private void startAnimations() { List animations = mAnimations; for (int i = 0, n = animations.size(); i < n; i++) { - animations.get(i).forceReset(); + animations.get(i).reset(); } } private void stopAnimations() { List animations = mAnimations; for (int i = 0, n = animations.size(); i < n; i++) { - animations.get(i).forceStop(); + animations.get(i).cancel(); } } diff --git a/app/src/main/java/com/hippo/gl/widget/GLTextView.java b/app/src/main/java/com/hippo/gl/widget/GLTextView.java index e1ca0c6c7..70addee1f 100644 --- a/app/src/main/java/com/hippo/gl/widget/GLTextView.java +++ b/app/src/main/java/com/hippo/gl/widget/GLTextView.java @@ -94,6 +94,10 @@ public class GLTextView extends GLView { @Override public void onRender(GLCanvas canvas) { + if (mTextTexture == null || mIndexes == null) { + return; + } + Rect paddings = getPaddings(); int x = getDefaultBegin(getWidth(), (int) mTextTexture.getTextWidth(mIndexes), paddings.left, paddings.right, Gravity.getPosition(mGravity, Gravity.HORIZONTAL));