diff options
author | nicolasroard <nicolasroard@google.com> | 2013-08-23 00:10:01 -0700 |
---|---|---|
committer | nicolasroard <nicolasroard@google.com> | 2013-08-27 00:47:43 -0700 |
commit | 9f452e09889199a28a86d9bd8f8fdaa8508ca0c1 (patch) | |
tree | d2dc35313a9467fc23e7db308da67be07985aba9 /src/com/android/gallery3d/filtershow/imageshow | |
parent | 0594bd40959aaa55ec4f4204fbe3d12240f51480 (diff) | |
download | android_packages_apps_Gallery2-9f452e09889199a28a86d9bd8f8fdaa8508ca0c1.tar.gz android_packages_apps_Gallery2-9f452e09889199a28a86d9bd8f8fdaa8508ca0c1.tar.bz2 android_packages_apps_Gallery2-9f452e09889199a28a86d9bd8f8fdaa8508ca0c1.zip |
Improves UI / size
- improves overdraw
- filter change anim
- better borders
- remove unused images
bug:9470514
bug:8658176
Change-Id: I764879be415acbfba5a36f239d1905ec54e2d3cb
Diffstat (limited to 'src/com/android/gallery3d/filtershow/imageshow')
3 files changed, 310 insertions, 44 deletions
diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryMathUtils.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMathUtils.java index 1cefe085e..e3cd56db7 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/GeometryMathUtils.java +++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryMathUtils.java @@ -23,6 +23,7 @@ import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; +import com.android.gallery3d.filtershow.cache.BitmapCache; import com.android.gallery3d.filtershow.cache.ImageLoader; import com.android.gallery3d.filtershow.filters.FilterCropRepresentation; import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation; @@ -209,7 +210,19 @@ public final class GeometryMathUtils { return q; } - private static void concatMirrorMatrix(Matrix m, Mirror type) { + private static void concatMirrorMatrix(Matrix m, GeometryHolder holder) { + Mirror type = holder.mirror; + if (type == Mirror.HORIZONTAL) { + if (holder.rotation.value() == 90 + || holder.rotation.value() == 270) { + type = Mirror.VERTICAL; + } + } else if (type == Mirror.VERTICAL) { + if (holder.rotation.value() == 90 + || holder.rotation.value() == 270) { + type = Mirror.HORIZONTAL; + } + } if (type == Mirror.HORIZONTAL) { m.postScale(-1, 1); } else if (type == Mirror.VERTICAL) { @@ -299,7 +312,8 @@ public final class GeometryMathUtils { crop.roundOut(frame); Matrix m = getCropSelectionToScreenMatrix(null, holder, width, height, frame.width(), frame.height()); - Bitmap temp = Bitmap.createBitmap(frame.width(), frame.height(), Bitmap.Config.ARGB_8888); + BitmapCache bitmapCache = MasterImage.getImage().getBitmapCache(); + Bitmap temp = bitmapCache.getBitmap(frame.width(), frame.height()); Canvas canvas = new Canvas(temp); Paint paint = new Paint(); paint.setAntiAlias(true); @@ -345,7 +359,7 @@ public final class GeometryMathUtils { compensation.postRotate(angle, cx, cy); compensation.postRotate(rotation, cx, cy); compensation.postTranslate(-cx, -cy); - concatMirrorMatrix(compensation, holder.mirror); + concatMirrorMatrix(compensation, holder); compensation.postTranslate(cx, cy); return compensation; } @@ -371,6 +385,10 @@ public final class GeometryMathUtils { // If there are geometry changes, apply them to the image if (!holder.isNil()) { bmap = applyFullGeometryMatrix(bmap, holder); + if (bmap != image) { + BitmapCache cache = MasterImage.getImage().getBitmapCache(); + cache.cache(image); + } } return bmap; } @@ -410,7 +428,7 @@ public final class GeometryMathUtils { Matrix m = new Matrix(); m.setTranslate(-centerX, -centerY); m.postRotate(holder.straighten + holder.rotation.value()); - concatMirrorMatrix(m, holder.mirror); + concatMirrorMatrix(m, holder); return m; } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java index 2bb269e29..ad4568723 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java @@ -19,13 +19,15 @@ package com.android.gallery3d.filtershow.imageshow; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.RectF; +import android.graphics.Shader; import android.graphics.drawable.NinePatchDrawable; import android.util.AttributeSet; import android.view.GestureDetector; @@ -38,12 +40,14 @@ import android.widget.LinearLayout; import com.android.gallery3d.R; import com.android.gallery3d.filtershow.FilterShowActivity; +import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation; import com.android.gallery3d.filtershow.filters.FilterRepresentation; import com.android.gallery3d.filtershow.filters.ImageFilter; import com.android.gallery3d.filtershow.pipeline.ImagePreset; import com.android.gallery3d.filtershow.tools.SaveImage; import java.io.File; +import java.util.ArrayList; import java.util.Collection; public class ImageShow extends View implements OnGestureListener, @@ -74,6 +78,7 @@ public class ImageShow extends View implements OnGestureListener, private NinePatchDrawable mShadow = null; private Rect mShadowBounds = new Rect(); private int mShadowMargin = 15; // not scaled, fixed in the asset + private boolean mShadowDrawn = false; private Point mTouchDown = new Point(); private Point mTouch = new Point(); @@ -93,6 +98,21 @@ public class ImageShow extends View implements OnGestureListener, } InteractionMode mInteractionMode = InteractionMode.NONE; + private static Bitmap sMask; + private Paint mMaskPaint = new Paint(); + private Matrix mShaderMatrix = new Matrix(); + + private static Bitmap convertToAlphaMask(Bitmap b) { + Bitmap a = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Bitmap.Config.ALPHA_8); + Canvas c = new Canvas(a); + c.drawBitmap(b, 0.0f, 0.0f, null); + return a; + } + + private static Shader createShader(Bitmap b) { + return new BitmapShader(b, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + } + private FilterShowActivity mActivity = null; public FilterShowActivity getActivity() { @@ -138,7 +158,20 @@ public class ImageShow extends View implements OnGestureListener, mShadow = (NinePatchDrawable) res.getDrawable(R.drawable.geometry_shadow); setupGestureDetector(context); mActivity = (FilterShowActivity) context; + if (sMask == null) { + Bitmap mask = BitmapFactory.decodeResource(res, R.drawable.spot_mask); + sMask = convertToAlphaMask(mask); + } + } + + public void attach() { MasterImage.getImage().addObserver(this); + bindAsImageLoadListener(); + } + + public void detach() { + MasterImage.getImage().removeObserver(this); + mMaskPaint.reset(); } public void setupGestureDetector(Context context) { @@ -206,36 +239,27 @@ public class ImageShow extends View implements OnGestureListener, float scaleFactor = MasterImage.getImage().getScaleFactor(); Point translation = MasterImage.getImage().getTranslation(); - Matrix scalingMatrix = new Matrix(); - scalingMatrix.postScale(scaleFactor, scaleFactor, cx, cy); - scalingMatrix.preTranslate(translation.x, translation.y); - - RectF unscaledClipRect = new RectF(mImageBounds); - scalingMatrix.mapRect(unscaledClipRect, unscaledClipRect); - canvas.save(); - boolean enablePartialRendering = false; - - // For now, partial rendering is disabled for all filters, - // so no need to clip. - if (enablePartialRendering && !unscaledClipRect.isEmpty()) { - canvas.clipRect(unscaledClipRect); - } + mShadowDrawn = false; canvas.save(); // TODO: center scale on gesture canvas.scale(scaleFactor, scaleFactor, cx, cy); canvas.translate(translation.x, translation.y); - drawImage(canvas, getFilteredImage(), true); Bitmap highresPreview = MasterImage.getImage().getHighresImage(); - if (highresPreview != null) { + + boolean isDoingNewLookAnimation = MasterImage.getImage().onGoingNewLookAnimation(); + + if (!isDoingNewLookAnimation && highresPreview != null) { drawImage(canvas, highresPreview, true); + } else { + drawImage(canvas, getFilteredImage(), true); } canvas.restore(); Bitmap partialPreview = MasterImage.getImage().getPartialImage(); - if (partialPreview != null) { + if (!isDoingNewLookAnimation && partialPreview != null) { canvas.save(); Rect originalBounds = MasterImage.getImage().getOriginalBounds(); Collection<FilterRepresentation> geo = MasterImage.getImage().getPreset() @@ -274,30 +298,135 @@ public class ImageShow extends View implements OnGestureListener, } public void drawImage(Canvas canvas, Bitmap image, boolean updateBounds) { - if (image != null) { - Rect s = new Rect(0, 0, image.getWidth(), - image.getHeight()); - - float scale = GeometryMathUtils.scale(image.getWidth(), image.getHeight(), getWidth(), - getHeight()); - - float w = image.getWidth() * scale; - float h = image.getHeight() * scale; - float ty = (getHeight() - h) / 2.0f; - float tx = (getWidth() - w) / 2.0f; - - Rect d = new Rect((int) tx + mShadowMargin, - (int) ty + mShadowMargin, - (int) (w + tx) - mShadowMargin, - (int) (h + ty) - mShadowMargin); - if (updateBounds) { - mImageBounds = d; + if (image == null) { + return; + } + + Rect d = computeImageBounds(image); + + if (updateBounds) { + mImageBounds = d; + } + + float centerX = mShadowMargin + (getWidth() - 2 * mShadowMargin) / 2; + float centerY = mShadowMargin + (getHeight() - 2 * mShadowMargin) / 2; + + MasterImage master = MasterImage.getImage(); + canvas.save(); + if (master.onGoingNewLookAnimation()) { + if (master.getCurrentLookAnimation() + == MasterImage.CIRCLE_ANIMATION + && MasterImage.getImage().getPreviousImage() != null) { + float maskScale = MasterImage.getImage().getMaskScale(); + if (maskScale > 1.0f) { + float maskW = sMask.getWidth() / 2.0f; + float maskH = sMask.getHeight() / 2.0f; + float x = centerX - maskW * maskScale; + float y = centerY - maskH * maskScale; + + // Prepare the shader + mShaderMatrix.reset(); + mShaderMatrix.setScale(1.0f / maskScale, 1.0f / maskScale); + mShaderMatrix.preTranslate(-x + d.left, -y + d.top); + float scaleImageX = d.width() / (float) image.getWidth(); + float scaleImageY = d.height() / (float) image.getHeight(); + mShaderMatrix.preScale(scaleImageX, scaleImageY); + mMaskPaint.reset(); + mMaskPaint.setShader(createShader(image)); + mMaskPaint.getShader().setLocalMatrix(mShaderMatrix); + + drawImage(canvas, MasterImage.getImage().getPreviousImage()); + canvas.translate(x, y); + canvas.scale(maskScale, maskScale); + canvas.drawBitmap(sMask, 0, 0, mMaskPaint); + } else { + drawImage(canvas, image); + } + } else if (master.getCurrentLookAnimation() + == MasterImage.ROTATE_ANIMATION) { + Rect d2 = computeImageBounds(master.getPreviousImage()); + float finalScale = d.width() / (float) d2.height(); + finalScale = (1.0f * (1.0f - master.getAnimFraction())) + + (finalScale * master.getAnimFraction()); + canvas.rotate(master.getAnimRotationValue(), centerX, centerY); + canvas.scale(finalScale, finalScale, centerX, centerY); + drawImage(canvas, master.getPreviousImage()); + } else if (master.getCurrentLookAnimation() + == MasterImage.MIRROR_ANIMATION) { + if (master.getCurrentFilterRepresentation() + instanceof FilterMirrorRepresentation) { + FilterMirrorRepresentation rep = + (FilterMirrorRepresentation) master.getCurrentFilterRepresentation(); + + ImagePreset preset = master.getPreset(); + ArrayList<FilterRepresentation> geometry = + (ArrayList<FilterRepresentation>) preset.getGeometryFilters(); + GeometryMathUtils.GeometryHolder holder = null; + holder = GeometryMathUtils.unpackGeometry(geometry); + + if (holder.rotation.value() == 90 || holder.rotation.value() == 270) { + if (rep.isHorizontal() && !rep.isVertical()) { + canvas.scale(1, master.getAnimRotationValue(), centerX, centerY); + } else if (rep.isVertical() && !rep.isHorizontal()) { + canvas.scale(1, master.getAnimRotationValue(), centerX, centerY); + } else if (rep.isHorizontal() && rep.isVertical()) { + canvas.scale(master.getAnimRotationValue(), 1, centerX, centerY); + } else { + canvas.scale(master.getAnimRotationValue(), 1, centerX, centerY); + } + } else { + if (rep.isHorizontal() && !rep.isVertical()) { + canvas.scale(master.getAnimRotationValue(), 1, centerX, centerY); + } else if (rep.isVertical() && !rep.isHorizontal()) { + canvas.scale(master.getAnimRotationValue(), 1, centerX, centerY); + } else if (rep.isHorizontal() && rep.isVertical()) { + canvas.scale(1, master.getAnimRotationValue(), centerX, centerY); + } else { + canvas.scale(1, master.getAnimRotationValue(), centerX, centerY); + } + } + } + drawImage(canvas, master.getPreviousImage()); } + } else { + drawImage(canvas, image); + } + canvas.restore(); + } + + private void drawImage(Canvas canvas, Bitmap image) { + Rect d = computeImageBounds(image); + float scaleImageX = d.width() / (float) image.getWidth(); + float scaleImageY = d.height() / (float) image.getHeight(); + Matrix imageMatrix = new Matrix(); + imageMatrix.postScale(scaleImageX, scaleImageY); + imageMatrix.postTranslate(d.left, d.top); + drawShadow(canvas, d); + canvas.clipRect(d); + canvas.drawBitmap(image, imageMatrix, mPaint); + } + + private Rect computeImageBounds(Bitmap image) { + float scale = GeometryMathUtils.scale(image.getWidth(), image.getHeight(), + getWidth(), getHeight()); + + float w = image.getWidth() * scale; + float h = image.getHeight() * scale; + float ty = (getHeight() - h) / 2.0f; + float tx = (getWidth() - w) / 2.0f; + return new Rect((int) tx + mShadowMargin, + (int) ty + mShadowMargin, + (int) (w + tx) - mShadowMargin, + (int) (h + ty) - mShadowMargin); + } + + private void drawShadow(Canvas canvas, Rect d) { + if (!mShadowDrawn) { mShadowBounds.set(d.left - mShadowMargin, d.top - mShadowMargin, d.right + mShadowMargin, d.bottom + mShadowMargin); mShadow.setBounds(mShadowBounds); mShadow.draw(canvas); - canvas.drawBitmap(image, s, d, mPaint); + mShadowDrawn = true; } } diff --git a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java index fabc7b3dc..525b45801 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java +++ b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java @@ -16,6 +16,8 @@ package com.android.gallery3d.filtershow.imageshow; +import android.animation.Animator; +import android.animation.ValueAnimator; import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.Point; @@ -24,12 +26,16 @@ import android.graphics.RectF; import android.net.Uri; import android.os.Handler; import android.os.Message; +import android.util.Log; import com.android.gallery3d.exif.ExifTag; import com.android.gallery3d.filtershow.FilterShowActivity; import com.android.gallery3d.filtershow.cache.BitmapCache; import com.android.gallery3d.filtershow.cache.ImageLoader; +import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation; import com.android.gallery3d.filtershow.filters.FilterRepresentation; +import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation; +import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation; import com.android.gallery3d.filtershow.filters.ImageFilter; import com.android.gallery3d.filtershow.history.HistoryItem; import com.android.gallery3d.filtershow.history.HistoryManager; @@ -77,6 +83,16 @@ public class MasterImage implements RenderingRequestCaller { private Bitmap mFiltersOnlyBitmap = null; private Bitmap mPartialBitmap = null; private Bitmap mHighresBitmap = null; + private Bitmap mPreviousImage = null; + + private float mMaskScale = 1; + private boolean mOnGoingNewLookAnimation = false; + private float mAnimRotationValue = 0; + private float mAnimFraction = 0; + private int mCurrentLookAnimation = 0; + public static final int CIRCLE_ANIMATION = 1; + public static final int ROTATE_ANIMATION = 2; + public static final int MIRROR_ANIMATION = 3; private HistoryManager mHistory = null; private StateAdapter mState = null; @@ -205,6 +221,10 @@ public class MasterImage implements RenderingRequestCaller { mObservers.add(observer); } + public void removeObserver(ImageShow observer) { + mObservers.remove(observer); + } + public void setActivity(FilterShowActivity activity) { mActivity = activity; } @@ -328,6 +348,103 @@ public class MasterImage implements RenderingRequestCaller { return mHighresBitmap; } + public Bitmap getPreviousImage() { + return mPreviousImage; + } + + public float getMaskScale() { + return mMaskScale; + } + + public void setMaskScale(float scale) { + mMaskScale = scale; + notifyObservers(); + } + + public float getAnimRotationValue() { + return mAnimRotationValue; + } + + public void setAnimRotation(float rotation) { + mAnimRotationValue = rotation; + notifyObservers(); + } + + public void setAnimFraction(float fraction) { + mAnimFraction = fraction; + } + + public float getAnimFraction() { + return mAnimFraction; + } + + public boolean onGoingNewLookAnimation() { + return mOnGoingNewLookAnimation; + } + + public int getCurrentLookAnimation() { + return mCurrentLookAnimation; + } + + public void onNewLook(FilterRepresentation newRepresentation) { + getBitmapCache().cache(mPreviousImage); + mPreviousImage = getBitmapCache().getBitmapCopy(getFilteredImage()); + ValueAnimator animator = null; + if (newRepresentation instanceof FilterUserPresetRepresentation) { + mCurrentLookAnimation = CIRCLE_ANIMATION; + animator = ValueAnimator.ofFloat(1, 20); + } + if (newRepresentation instanceof FilterRotateRepresentation) { + mCurrentLookAnimation = ROTATE_ANIMATION; + animator = ValueAnimator.ofFloat(0, 90); + } + if (newRepresentation instanceof FilterMirrorRepresentation) { + mCurrentLookAnimation = MIRROR_ANIMATION; + animator = ValueAnimator.ofFloat(1, 0, -1); + } + animator.setDuration(400); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (mCurrentLookAnimation == CIRCLE_ANIMATION) { + Log.v(LOGTAG, "circle animation " + animation.getAnimatedValue()); + setMaskScale((Float) animation.getAnimatedValue()); + } else if (mCurrentLookAnimation == ROTATE_ANIMATION + || mCurrentLookAnimation == MIRROR_ANIMATION) { + setAnimRotation((Float) animation.getAnimatedValue()); + setAnimFraction(animation.getAnimatedFraction()); + } + } + }); + animator.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + mOnGoingNewLookAnimation = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + mBitmapCache.cache(mPreviousImage); + mPreviousImage = null; + mOnGoingNewLookAnimation = false; + setMaskScale(1); + setAnimRotation(0); + } + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + }); + animator.start(); + notifyObservers(); + } + public void notifyObservers() { for (ImageShow observer : mObservers) { observer.invalidate(); @@ -339,9 +456,9 @@ public class MasterImage implements RenderingRequestCaller { newPresetGeometryOnly.setDoApplyFilters(false); newPresetGeometryOnly.setDoApplyGeometry(true); if (force || mGeometryOnlyPreset == null - || !newPresetGeometryOnly.same(mGeometryOnlyPreset)) { + || !newPresetGeometryOnly.equals(mGeometryOnlyPreset)) { mGeometryOnlyPreset = newPresetGeometryOnly; - RenderingRequest.post(mActivity, getOriginalBitmapLarge(), + RenderingRequest.post(mActivity, null, mGeometryOnlyPreset, RenderingRequest.GEOMETRY_RENDERING, this); } ImagePreset newPresetFiltersOnly = new ImagePreset(mPreset); @@ -350,7 +467,7 @@ public class MasterImage implements RenderingRequestCaller { if (force || mFiltersOnlyPreset == null || !newPresetFiltersOnly.same(mFiltersOnlyPreset)) { mFiltersOnlyPreset = newPresetFiltersOnly; - RenderingRequest.post(mActivity, MasterImage.getImage().getOriginalBitmapLarge(), + RenderingRequest.post(mActivity, null, mFiltersOnlyPreset, RenderingRequest.FILTERS_RENDERING, this); } } @@ -474,10 +591,12 @@ public class MasterImage implements RenderingRequestCaller { boolean needsCheckModification = false; if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) { + mBitmapCache.cache(mGeometryOnlyBitmap); mGeometryOnlyBitmap = request.getBitmap(); needsCheckModification = true; } if (request.getType() == RenderingRequest.FILTERS_RENDERING) { + mBitmapCache.cache(mFiltersOnlyBitmap); mFiltersOnlyBitmap = request.getBitmap(); notifyObservers(); needsCheckModification = true; |