From 203eb404a7cd6a80397535e63d22b3772939f03d Mon Sep 17 00:00:00 2001 From: Ruben Brunk Date: Thu, 18 Jul 2013 16:37:30 -0700 Subject: Refactoring Geometry handling. Bug: 9170644 Bug: 9366654 Bug: 9366263 - Consolidates all the geometry transforms in GeometryMathUtils and significantly reduces complexity. - Removes GeometryMetadata object and dependent code. - Removes ImageGeometry and geometry update callbacks. Change-Id: I59add51907459593244c9ebaadef585efc7486d5 --- .../gallery3d/filtershow/EditorPlaceHolder.java | 33 +- .../gallery3d/filtershow/FilterShowActivity.java | 24 +- .../gallery3d/filtershow/crop/BoundedRect.java | 8 +- .../gallery3d/filtershow/crop/CropMath.java | 10 +- .../gallery3d/filtershow/crop/CropObject.java | 7 +- .../gallery3d/filtershow/crop/CropView.java | 19 +- .../gallery3d/filtershow/editors/Editor.java | 45 +- .../gallery3d/filtershow/editors/EditorCrop.java | 106 ++- .../gallery3d/filtershow/editors/EditorFlip.java | 86 --- .../gallery3d/filtershow/editors/EditorMirror.java | 109 +++ .../gallery3d/filtershow/editors/EditorPanel.java | 17 +- .../gallery3d/filtershow/editors/EditorRotate.java | 35 +- .../filtershow/editors/EditorStraighten.java | 34 +- .../filtershow/filters/BaseFiltersManager.java | 24 +- .../filters/FilterCropRepresentation.java | 71 +- .../filters/FilterMirrorRepresentation.java | 33 +- .../filters/FilterRotateRepresentation.java | 27 +- .../filters/FilterStraightenRepresentation.java | 16 +- .../gallery3d/filtershow/filters/ImageFilter.java | 12 +- .../filtershow/filters/ImageFilterGeometry.java | 117 --- .../filtershow/filters/ImageFilterStraighten.java | 89 --- .../filtershow/imageshow/GeometryListener.java | 21 - .../filtershow/imageshow/GeometryMath.java | 143 ---- .../filtershow/imageshow/GeometryMathUtils.java | 416 ++++++++++ .../filtershow/imageshow/GeometryMetadata.java | 490 ------------ .../gallery3d/filtershow/imageshow/ImageCrop.java | 854 ++++++--------------- .../gallery3d/filtershow/imageshow/ImageDraw.java | 6 - .../gallery3d/filtershow/imageshow/ImageFlip.java | 160 ---- .../filtershow/imageshow/ImageGeometry.java | 527 ------------- .../filtershow/imageshow/ImageMirror.java | 78 ++ .../gallery3d/filtershow/imageshow/ImagePoint.java | 6 - .../filtershow/imageshow/ImageRotate.java | 66 +- .../gallery3d/filtershow/imageshow/ImageShow.java | 54 +- .../filtershow/imageshow/ImageStraighten.java | 235 ++++-- .../filtershow/imageshow/MasterImage.java | 84 +- .../filtershow/pipeline/CacheProcessing.java | 12 +- .../filtershow/pipeline/CachingPipeline.java | 29 +- .../filtershow/pipeline/FilterEnvironment.java | 4 + .../gallery3d/filtershow/pipeline/ImagePreset.java | 219 +++--- .../filtershow/editors/EditorManager.java | 2 +- 40 files changed, 1495 insertions(+), 2833 deletions(-) delete mode 100644 src/com/android/gallery3d/filtershow/editors/EditorFlip.java create mode 100644 src/com/android/gallery3d/filtershow/editors/EditorMirror.java delete mode 100644 src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java delete mode 100644 src/com/android/gallery3d/filtershow/filters/ImageFilterStraighten.java delete mode 100644 src/com/android/gallery3d/filtershow/imageshow/GeometryListener.java delete mode 100644 src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java create mode 100644 src/com/android/gallery3d/filtershow/imageshow/GeometryMathUtils.java delete mode 100644 src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java delete mode 100644 src/com/android/gallery3d/filtershow/imageshow/ImageFlip.java delete mode 100644 src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java create mode 100644 src/com/android/gallery3d/filtershow/imageshow/ImageMirror.java diff --git a/src/com/android/gallery3d/filtershow/EditorPlaceHolder.java b/src/com/android/gallery3d/filtershow/EditorPlaceHolder.java index 7faa3d3ad..95abce114 100644 --- a/src/com/android/gallery3d/filtershow/EditorPlaceHolder.java +++ b/src/com/android/gallery3d/filtershow/EditorPlaceHolder.java @@ -44,26 +44,21 @@ public class EditorPlaceHolder { return null; } - try { - editor.createEditor(mActivity, mContainer); - editor.getImageShow().bindAsImageLoadListener(); - mContainer.setVisibility(View.VISIBLE); - mContainer.removeAllViews(); - View eview = editor.getTopLevelView(); - ViewParent parent = eview.getParent(); - - if (parent != null && parent instanceof FrameLayout) { - ((FrameLayout) parent).removeAllViews(); - } - - mContainer.addView(eview); - hideOldViews(); - editor.setVisibility(View.VISIBLE); - return editor; - } catch (Exception e) { - e.printStackTrace(); + editor.createEditor(mActivity, mContainer); + editor.getImageShow().bindAsImageLoadListener(); + mContainer.setVisibility(View.VISIBLE); + mContainer.removeAllViews(); + View eview = editor.getTopLevelView(); + ViewParent parent = eview.getParent(); + + if (parent != null && parent instanceof FrameLayout) { + ((FrameLayout) parent).removeAllViews(); } - return null; + + mContainer.addView(eview); + hideOldViews(); + editor.setVisibility(View.VISIBLE); + return editor; } public void setOldViews(Vector views) { diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java index 0fca6520a..f0b1aa8c2 100644 --- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java +++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java @@ -72,7 +72,7 @@ import com.android.gallery3d.filtershow.editors.BasicEditor; import com.android.gallery3d.filtershow.editors.Editor; import com.android.gallery3d.filtershow.editors.EditorCrop; import com.android.gallery3d.filtershow.editors.EditorDraw; -import com.android.gallery3d.filtershow.editors.EditorFlip; +import com.android.gallery3d.filtershow.editors.EditorMirror; import com.android.gallery3d.filtershow.editors.EditorManager; import com.android.gallery3d.filtershow.editors.EditorPanel; import com.android.gallery3d.filtershow.editors.EditorRedEye; @@ -85,7 +85,6 @@ import com.android.gallery3d.filtershow.filters.FiltersManager; import com.android.gallery3d.filtershow.filters.ImageFilter; import com.android.gallery3d.filtershow.history.HistoryManager; import com.android.gallery3d.filtershow.history.HistoryItem; -import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; import com.android.gallery3d.filtershow.imageshow.ImageCrop; import com.android.gallery3d.filtershow.imageshow.ImageShow; import com.android.gallery3d.filtershow.imageshow.MasterImage; @@ -394,7 +393,7 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL mEditorPlaceHolder.addEditor(new EditorTinyPlanet()); mEditorPlaceHolder.addEditor(new EditorRedEye()); mEditorPlaceHolder.addEditor(new EditorCrop()); - mEditorPlaceHolder.addEditor(new EditorFlip()); + mEditorPlaceHolder.addEditor(new EditorMirror()); mEditorPlaceHolder.addEditor(new EditorRotate()); mEditorPlaceHolder.addEditor(new EditorStraighten()); } @@ -411,10 +410,6 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL int curveHandleSize = (int) res.getDimension(R.dimen.crop_indicator_size); Spline.setCurveHandle(curveHandle, curveHandleSize); Spline.setCurveWidth((int) getPixelsFromDip(3)); - - ImageCrop.setAspectTextSize((int) getPixelsFromDip(18)); - ImageCrop.setTouchTolerance((int) getPixelsFromDip(25)); - ImageCrop.setMinCropSize((int) getPixelsFromDip(55)); } private void startLoadBitmap(Uri uri) { @@ -494,6 +489,8 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL FilterRepresentation representation = copy.getRepresentation(filterRepresentation); if (representation == null) { copy.addFilter(filterRepresentation); + } else if (filterRepresentation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) { + filterRepresentation = representation; } else { if (filterRepresentation.allowsSingleInstanceOnly()) { // Don't just update the filter representation. Centralize the @@ -512,14 +509,7 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL return; } - // TODO: this check is needed because the GeometryMetadata doesn't quite - // follow the same pattern as the other filters to update/sync their values. - // We thus need to not call useFilterRepresentation() for now, as it - // would override the current Geometry. Once GeometryMetadata is fixed, - // let's remove the check and call useFilterRepresentation all the time. - if (!(representation instanceof GeometryMetadata)) { - useFilterRepresentation(representation); - } + useFilterRepresentation(representation); // show representation Editor mCurrentEditor = mEditorPlaceHolder.showEditor(representation.getEditorId()); @@ -633,7 +623,6 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL if (!mShowingTinyPlanet) { mCategoryFiltersAdapter.removeTinyPlanet(); } - MasterImage.getImage().setOriginalGeometry(largeBitmap); mCategoryLooksAdapter.imageLoaded(); mCategoryBordersAdapter.imageLoaded(); mCategoryGeometryAdapter.imageLoaded(); @@ -650,8 +639,6 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL if (mAction == TINY_PLANET_ACTION) { showRepresentation(mCategoryFiltersAdapter.getTinyPlanet()); } - - MasterImage.getImage().notifyGeometryChange(); LoadHighresBitmapTask highresLoad = new LoadHighresBitmapTask(); highresLoad.execute(); super.onPostExecute(result); @@ -937,7 +924,6 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL public void invalidateViews() { for (ImageShow views : mImageViews) { - views.invalidate(); views.updateImage(); } } diff --git a/src/com/android/gallery3d/filtershow/crop/BoundedRect.java b/src/com/android/gallery3d/filtershow/crop/BoundedRect.java index 74ce7cdd5..13b8d6de1 100644 --- a/src/com/android/gallery3d/filtershow/crop/BoundedRect.java +++ b/src/com/android/gallery3d/filtershow/crop/BoundedRect.java @@ -19,7 +19,7 @@ import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; -import com.android.gallery3d.filtershow.imageshow.GeometryMath; +import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils; import java.util.Arrays; @@ -134,7 +134,7 @@ public class BoundedRect { }; float[] nearestSide = CropMath.closestSide(badCorner, outerCorners); float[] correctionVec = - GeometryMath.shortestVectorFromPointToLine(badCorner, nearestSide); + GeometryMathUtils.shortestVectorFromPointToLine(badCorner, nearestSide); correction[0] += correctionVec[0]; correction[1] += correctionVec[1]; } @@ -195,7 +195,7 @@ public class BoundedRect { newInnerCorners[i], newInnerCorners[i + 1], oldInnerCorners[i], oldInnerCorners[i + 1] }; - float[] p = GeometryMath.lineIntersect(pathOfCorner, outerSide); + float[] p = GeometryMathUtils.lineIntersect(pathOfCorner, outerSide); if (p == null) { // lines are parallel or not well defined, so don't resize p = new float[2]; @@ -288,7 +288,7 @@ public class BoundedRect { newInnerCorners[i], newInnerCorners[i + 1], oldInnerCorners[i], oldInnerCorners[i + 1] }; - float[] p = GeometryMath.lineIntersect(l1, l2); + float[] p = GeometryMathUtils.lineIntersect(l1, l2); if (p == null) { // lines are parallel or not well defined, so set to old // corner diff --git a/src/com/android/gallery3d/filtershow/crop/CropMath.java b/src/com/android/gallery3d/filtershow/crop/CropMath.java index 671554f16..02c65310e 100644 --- a/src/com/android/gallery3d/filtershow/crop/CropMath.java +++ b/src/com/android/gallery3d/filtershow/crop/CropMath.java @@ -20,7 +20,7 @@ import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.RectF; -import com.android.gallery3d.filtershow.imageshow.GeometryMath; +import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils; import java.util.Arrays; @@ -97,8 +97,8 @@ public class CropMath { if (array.length < 2) return; for (int x = 0; x < array.length; x += 2) { - array[x] = GeometryMath.clamp(array[x], imageBound.left, imageBound.right); - array[x + 1] = GeometryMath.clamp(array[x + 1], imageBound.top, imageBound.bottom); + array[x] = GeometryMathUtils.clamp(array[x], imageBound.left, imageBound.right); + array[x + 1] = GeometryMathUtils.clamp(array[x + 1], imageBound.top, imageBound.bottom); } } @@ -119,8 +119,8 @@ public class CropMath { corners[i], corners[(i + 1) % len], corners[(i + 2) % len], corners[(i + 3) % len] }; - float mag = GeometryMath.vectorLength( - GeometryMath.shortestVectorFromPointToLine(point, line)); + float mag = GeometryMathUtils.vectorLength( + GeometryMathUtils.shortestVectorFromPointToLine(point, line)); if (mag < oldMag) { oldMag = mag; bestLine = line; diff --git a/src/com/android/gallery3d/filtershow/crop/CropObject.java b/src/com/android/gallery3d/filtershow/crop/CropObject.java index bea3ffabd..b98ed1bfd 100644 --- a/src/com/android/gallery3d/filtershow/crop/CropObject.java +++ b/src/com/android/gallery3d/filtershow/crop/CropObject.java @@ -19,10 +19,9 @@ package com.android.gallery3d.filtershow.crop; import android.graphics.Rect; import android.graphics.RectF; -import com.android.gallery3d.filtershow.imageshow.GeometryMath; +import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils; public class CropObject { - private BoundedRect mBoundedRect; private float mAspectWidth = 1; private float mAspectHeight = 1; @@ -234,8 +233,8 @@ public class CropObject { float[] disp = { dx, dy }; - float[] bUnit = GeometryMath.normalize(b); - float sp = GeometryMath.scalarProjection(disp, bUnit); + float[] bUnit = GeometryMathUtils.normalize(b); + float sp = GeometryMathUtils.scalarProjection(disp, bUnit); dx = sp * bUnit[0]; dy = sp * bUnit[1]; RectF newCrop = fixedCornerResize(crop, movingEdges, dx, dy); diff --git a/src/com/android/gallery3d/filtershow/crop/CropView.java b/src/com/android/gallery3d/filtershow/crop/CropView.java index 0a7950c20..bbb7cfd4c 100644 --- a/src/com/android/gallery3d/filtershow/crop/CropView.java +++ b/src/com/android/gallery3d/filtershow/crop/CropView.java @@ -20,7 +20,6 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.DashPathEffect; import android.graphics.Matrix; import android.graphics.Paint; @@ -50,8 +49,8 @@ public class CropView extends View { private NinePatchDrawable mShadow; private CropObject mCropObj = null; - private final Drawable mCropIndicator; - private final int mIndicatorSize; + private Drawable mCropIndicator; + private int mIndicatorSize; private int mRotation = 0; private boolean mMovingBlock = false; private Matrix mDisplayMatrix = null; @@ -80,8 +79,22 @@ public class CropView extends View { private Mode mState = Mode.NONE; + public CropView(Context context) { + super(context); + setup(context); + } + public CropView(Context context, AttributeSet attrs) { super(context, attrs); + setup(context); + } + + public CropView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setup(context); + } + + private void setup(Context context) { Resources rsc = context.getResources(); mShadow = (NinePatchDrawable) rsc.getDrawable(R.drawable.geometry_shadow); mCropIndicator = rsc.getDrawable(R.drawable.camera_crop); diff --git a/src/com/android/gallery3d/filtershow/editors/Editor.java b/src/com/android/gallery3d/filtershow/editors/Editor.java index 9539d651b..a9e56e0c1 100644 --- a/src/com/android/gallery3d/filtershow/editors/Editor.java +++ b/src/com/android/gallery3d/filtershow/editors/Editor.java @@ -31,13 +31,15 @@ import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import com.android.gallery3d.R; -import com.android.gallery3d.filtershow.cache.ImageLoader; import com.android.gallery3d.filtershow.controller.Control; import com.android.gallery3d.filtershow.filters.FilterRepresentation; import com.android.gallery3d.filtershow.imageshow.ImageShow; import com.android.gallery3d.filtershow.imageshow.MasterImage; import com.android.gallery3d.filtershow.pipeline.ImagePreset; +import java.util.ArrayList; +import java.util.Collection; + /** * Base class for Editors Must contain a mImageShow and a top level view */ @@ -51,6 +53,7 @@ public class Editor implements OnSeekBarChangeListener, SwapButton.SwapButtonLis protected Button mFilterTitle; protected int mID; private final String LOGTAG = "Editor"; + protected boolean mChangesGeometry = false; protected FilterRepresentation mLocalRepresentation = null; protected byte mShowParameter = SHOW_VALUE_UNDEFINED; private Button mButton; @@ -206,12 +209,50 @@ public class Editor implements OnSeekBarChangeListener, SwapButton.SwapButtonLis return mLocalRepresentation; } + /** + * Call this to update the preset in MasterImage with the current representation + * returned by getLocalRepresentation. This causes the preview bitmap to be + * regenerated. + */ public void commitLocalRepresentation() { + commitLocalRepresentation(getLocalRepresentation()); + } + + /** + * Call this to update the preset in MasterImage with a given representation. + * This causes the preview bitmap to be regenerated. + */ + public void commitLocalRepresentation(FilterRepresentation rep) { + ArrayList filter = new ArrayList(1); + filter.add(rep); + commitLocalRepresentation(filter); + } + + /** + * Call this to update the preset in MasterImage with a collection of FilterRepresnations. + * This causes the preview bitmap to be regenerated. + */ + public void commitLocalRepresentation(Collection reps) { ImagePreset preset = MasterImage.getImage().getPreset(); - preset.updateFilterRepresentation(getLocalRepresentation()); + preset.updateFilterRepresentations(reps); if (mButton != null) { updateText(); } + if (mChangesGeometry) { + // Regenerate both the filtered and the geometry-only bitmaps + MasterImage.getImage().updatePresets(true); + } else { + // Regenerate only the filtered bitmap. + MasterImage.getImage().invalidateFiltersOnly(); + } + preset.fillImageStateAdapter(MasterImage.getImage().getState()); + } + + /** + * This is called in response to a click to apply and leave the editor. + */ + public void finalApplyCalled() { + commitLocalRepresentation(); } protected void updateText() { diff --git a/src/com/android/gallery3d/filtershow/editors/EditorCrop.java b/src/com/android/gallery3d/filtershow/editors/EditorCrop.java index ec6e30b7e..511d4ff87 100644 --- a/src/com/android/gallery3d/filtershow/editors/EditorCrop.java +++ b/src/com/android/gallery3d/filtershow/editors/EditorCrop.java @@ -17,6 +17,8 @@ package com.android.gallery3d.filtershow.editors; import android.content.Context; +import android.util.Log; +import android.util.SparseArray; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; @@ -26,43 +28,77 @@ import android.widget.LinearLayout; import android.widget.PopupMenu; import com.android.gallery3d.R; -import com.android.gallery3d.filtershow.crop.CropExtras; +import com.android.gallery3d.filtershow.filters.FilterCropRepresentation; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; import com.android.gallery3d.filtershow.imageshow.ImageCrop; +import com.android.gallery3d.filtershow.imageshow.MasterImage; public class EditorCrop extends Editor implements EditorInfo { + public static final String TAG = EditorCrop.class.getSimpleName(); public static final int ID = R.id.editorCrop; - private static final String LOGTAG = "EditorCrop"; - ImageCrop mImageCrop; + // Holder for an aspect ratio it's string id + protected static final class AspectInfo { + int mAspectX; + int mAspectY; + int mStringId; + AspectInfo(int stringID, int x, int y) { + mStringId = stringID; + mAspectX = x; + mAspectY = y; + } + }; + + // Mapping from menu id to aspect ratio + protected static final SparseArray sAspects; + static { + sAspects = new SparseArray(); + sAspects.put(R.id.crop_menu_1to1, new AspectInfo(R.string.aspect1to1_effect, 1, 1)); + sAspects.put(R.id.crop_menu_4to3, new AspectInfo(R.string.aspect4to3_effect, 4, 3)); + sAspects.put(R.id.crop_menu_3to4, new AspectInfo(R.string.aspect3to4_effect, 3, 4)); + sAspects.put(R.id.crop_menu_5to7, new AspectInfo(R.string.aspect5to7_effect, 5, 7)); + sAspects.put(R.id.crop_menu_7to5, new AspectInfo(R.string.aspect7to5_effect, 7, 5)); + sAspects.put(R.id.crop_menu_none, new AspectInfo(R.string.aspectNone_effect, 0, 0)); + sAspects.put(R.id.crop_menu_original, new AspectInfo(R.string.aspectOriginal_effect, 0, 0)); + } + + protected ImageCrop mImageCrop; private String mAspectString = ""; - private boolean mCropActionFlag = false; - private CropExtras mCropExtras = null; public EditorCrop() { super(ID); + mChangesGeometry = true; } @Override public void createEditor(Context context, FrameLayout frameLayout) { super.createEditor(context, frameLayout); if (mImageCrop == null) { - // TODO: need this for now because there's extra state in ImageCrop. - // all the state instead should be in the representation. - // Same thing for the other geometry editors. mImageCrop = new ImageCrop(context); } mView = mImageShow = mImageCrop; - mImageCrop.bindAsImageLoadListener(); mImageCrop.setEditor(this); - mImageCrop.syncLocalToMasterGeometry(); - mImageCrop.setCropActionFlag(mCropActionFlag); - if (mCropActionFlag) { - mImageCrop.setExtras(mCropExtras); - mImageCrop.setAspectString(mAspectString); - mImageCrop.clear(); + } + + @Override + public void reflectCurrentFilter() { + MasterImage master = MasterImage.getImage(); + master.setCurrentFilterRepresentation(master.getPreset() + .getFilterWithSerializationName(FilterCropRepresentation.SERIALIZATION_NAME)); + super.reflectCurrentFilter(); + FilterRepresentation rep = getLocalRepresentation(); + if (rep == null || rep instanceof FilterCropRepresentation) { + mImageCrop.setFilterCropRepresentation((FilterCropRepresentation) rep); } else { - mImageCrop.setExtras(null); + Log.w(TAG, "Could not reflect current filter, not of type: " + + FilterCropRepresentation.class.getSimpleName()); } + mImageCrop.invalidate(); + } + + @Override + public void finalApplyCalled() { + commitLocalRepresentation(mImageCrop.getFinalRepresentation()); } @Override @@ -70,27 +106,36 @@ public class EditorCrop extends Editor implements EditorInfo { Button view = (Button) accessoryViewList.findViewById(R.id.applyEffect); view.setText(mContext.getString(R.string.crop)); view.setOnClickListener(new OnClickListener() { - - @Override + @Override public void onClick(View arg0) { showPopupMenu(accessoryViewList); } }); } - private void showPopupMenu(LinearLayout accessoryViewList) { - final Button button = (Button) accessoryViewList.findViewById( - R.id.applyEffect); - if (button == null) { - return; + private void changeCropAspect(int itemId) { + AspectInfo info = sAspects.get(itemId); + if (info == null) { + throw new IllegalArgumentException("Invalid resource ID: " + itemId); } + if (itemId == R.id.crop_menu_original) { + mImageCrop.applyOriginalAspect(); + } else if (itemId == R.id.crop_menu_none) { + mImageCrop.applyFreeAspect(); + } else { + mImageCrop.applyAspect(info.mAspectX, info.mAspectY); + } + setAspectString(mContext.getString(info.mStringId)); + } + + private void showPopupMenu(LinearLayout accessoryViewList) { + final Button button = (Button) accessoryViewList.findViewById(R.id.applyEffect); final PopupMenu popupMenu = new PopupMenu(mImageShow.getActivity(), button); popupMenu.getMenuInflater().inflate(R.menu.filtershow_menu_crop, popupMenu.getMenu()); popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { - @Override public boolean onMenuItemClick(MenuItem item) { - mImageCrop.setAspectButton(item.getItemId()); + changeCropAspect(item.getItemId()); return true; } }); @@ -117,16 +162,7 @@ public class EditorCrop extends Editor implements EditorInfo { return true; } - public void setExtras(CropExtras cropExtras) { - mCropExtras = cropExtras; - } - - public void setAspectString(String s) { + private void setAspectString(String s) { mAspectString = s; } - - public void setCropActionFlag(boolean b) { - mCropActionFlag = b; - } - } diff --git a/src/com/android/gallery3d/filtershow/editors/EditorFlip.java b/src/com/android/gallery3d/filtershow/editors/EditorFlip.java deleted file mode 100644 index 67070045b..000000000 --- a/src/com/android/gallery3d/filtershow/editors/EditorFlip.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * 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.android.gallery3d.filtershow.editors; - -import android.content.Context; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.Button; -import android.widget.FrameLayout; -import android.widget.LinearLayout; - -import com.android.gallery3d.R; -import com.android.gallery3d.filtershow.imageshow.ImageFlip; - -public class EditorFlip extends Editor implements EditorInfo { - public static final String LOGTAG = "EditorFlip"; - public static final int ID = R.id.editorFlip; - ImageFlip mImageFlip; - - public EditorFlip() { - super(ID); - } - - @Override - public void createEditor(Context context, FrameLayout frameLayout) { - super.createEditor(context, frameLayout); - if (mImageFlip == null) { - mImageFlip = new ImageFlip(context); - } - mView = mImageShow = mImageFlip; - mImageFlip.bindAsImageLoadListener(); - mImageFlip.setEditor(this); - mImageFlip.syncLocalToMasterGeometry(); - } - - @Override - public void openUtilityPanel(final LinearLayout accessoryViewList) { - final Button button = (Button) accessoryViewList.findViewById(R.id.applyEffect); - button.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View arg0) { - mImageFlip.flip(); - mImageFlip.saveAndSetPreset(); - } - }); - } - - @Override - public int getTextId() { - return R.string.mirror; - } - - @Override - public int getOverlayId() { - return R.drawable.filtershow_button_geometry_flip; - } - - @Override - public boolean getOverlayOnly() { - return true; - } - - @Override - public boolean showsSeekBar() { - return false; - } - - @Override - public boolean showsPopupIndicator() { - return false; - } -} diff --git a/src/com/android/gallery3d/filtershow/editors/EditorMirror.java b/src/com/android/gallery3d/filtershow/editors/EditorMirror.java new file mode 100644 index 000000000..d6d9ee75d --- /dev/null +++ b/src/com/android/gallery3d/filtershow/editors/EditorMirror.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * 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.android.gallery3d.filtershow.editors; + +import android.content.Context; +import android.util.Log; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.FrameLayout; +import android.widget.LinearLayout; + +import com.android.gallery3d.R; +import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; +import com.android.gallery3d.filtershow.imageshow.ImageMirror; +import com.android.gallery3d.filtershow.imageshow.MasterImage; + +public class EditorMirror extends Editor implements EditorInfo { + public static final String TAG = EditorMirror.class.getSimpleName(); + public static final int ID = R.id.editorFlip; + ImageMirror mImageMirror; + + public EditorMirror() { + super(ID); + mChangesGeometry = true; + } + + @Override + public void createEditor(Context context, FrameLayout frameLayout) { + super.createEditor(context, frameLayout); + if (mImageMirror == null) { + mImageMirror = new ImageMirror(context); + } + mView = mImageShow = mImageMirror; + mImageMirror.setEditor(this); + } + + @Override + public void reflectCurrentFilter() { + MasterImage master = MasterImage.getImage(); + master.setCurrentFilterRepresentation(master.getPreset() + .getFilterWithSerializationName(FilterMirrorRepresentation.SERIALIZATION_NAME)); + super.reflectCurrentFilter(); + FilterRepresentation rep = getLocalRepresentation(); + if (rep == null || rep instanceof FilterMirrorRepresentation) { + mImageMirror.setFilterMirrorRepresentation((FilterMirrorRepresentation) rep); + } else { + Log.w(TAG, "Could not reflect current filter, not of type: " + + FilterMirrorRepresentation.class.getSimpleName()); + } + mImageMirror.invalidate(); + } + + @Override + public void openUtilityPanel(final LinearLayout accessoryViewList) { + final Button button = (Button) accessoryViewList.findViewById(R.id.applyEffect); + button.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View arg0) { + mImageMirror.flip(); + } + }); + } + + @Override + public void finalApplyCalled() { + commitLocalRepresentation(mImageMirror.getFinalRepresentation()); + } + + @Override + public int getTextId() { + return R.string.mirror; + } + + @Override + public int getOverlayId() { + return R.drawable.filtershow_button_geometry_flip; + } + + @Override + public boolean getOverlayOnly() { + return true; + } + + @Override + public boolean showsSeekBar() { + return false; + } + + @Override + public boolean showsPopupIndicator() { + return false; + } +} diff --git a/src/com/android/gallery3d/filtershow/editors/EditorPanel.java b/src/com/android/gallery3d/filtershow/editors/EditorPanel.java index 40dfccd02..bc4ca6ab6 100644 --- a/src/com/android/gallery3d/filtershow/editors/EditorPanel.java +++ b/src/com/android/gallery3d/filtershow/editors/EditorPanel.java @@ -88,17 +88,8 @@ public class EditorPanel extends Fragment { activity.backToMain(); } }); - applyButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - MasterImage.getImage().invalidateFiltersOnly(); - FilterShowActivity activity = (FilterShowActivity) getActivity(); - activity.backToMain(); - } - }); Button toggleState = (Button) mMainView.findViewById(R.id.toggle_state); - mEditor = activity.getEditor(mEditorID); if (mEditor != null) { mEditor.setUpEditorUI(actionControl, editControl, editTitle, toggleState); @@ -107,6 +98,14 @@ public class EditorPanel extends Fragment { mEditor.openUtilityPanel((LinearLayout) actionControl); } } + applyButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + FilterShowActivity activity = (FilterShowActivity) getActivity(); + mEditor.finalApplyCalled(); + activity.backToMain(); + } + }); showImageStatePanel(activity.isShowingImageStatePanel()); return mMainView; diff --git a/src/com/android/gallery3d/filtershow/editors/EditorRotate.java b/src/com/android/gallery3d/filtershow/editors/EditorRotate.java index e66be2cc1..9452bf0c0 100644 --- a/src/com/android/gallery3d/filtershow/editors/EditorRotate.java +++ b/src/com/android/gallery3d/filtershow/editors/EditorRotate.java @@ -17,6 +17,7 @@ package com.android.gallery3d.filtershow.editors; import android.content.Context; +import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; @@ -24,15 +25,19 @@ import android.widget.FrameLayout; import android.widget.LinearLayout; import com.android.gallery3d.R; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; +import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation; import com.android.gallery3d.filtershow.imageshow.ImageRotate; +import com.android.gallery3d.filtershow.imageshow.MasterImage; public class EditorRotate extends Editor implements EditorInfo { - public static final String LOGTAG = "EditorRotate"; + public static final String TAG = EditorRotate.class.getSimpleName(); public static final int ID = R.id.editorRotate; ImageRotate mImageRotate; public EditorRotate() { super(ID); + mChangesGeometry = true; } @Override @@ -42,9 +47,23 @@ public class EditorRotate extends Editor implements EditorInfo { mImageRotate = new ImageRotate(context); } mView = mImageShow = mImageRotate; - mImageRotate.bindAsImageLoadListener(); mImageRotate.setEditor(this); - mImageRotate.syncLocalToMasterGeometry(); + } + + @Override + public void reflectCurrentFilter() { + MasterImage master = MasterImage.getImage(); + master.setCurrentFilterRepresentation(master.getPreset() + .getFilterWithSerializationName(FilterRotateRepresentation.SERIALIZATION_NAME)); + super.reflectCurrentFilter(); + FilterRepresentation rep = getLocalRepresentation(); + if (rep == null || rep instanceof FilterRotateRepresentation) { + mImageRotate.setFilterRotateRepresentation((FilterRotateRepresentation) rep); + } else { + Log.w(TAG, "Could not reflect current filter, not of type: " + + FilterRotateRepresentation.class.getSimpleName()); + } + mImageRotate.invalidate(); } @Override @@ -54,12 +73,18 @@ public class EditorRotate extends Editor implements EditorInfo { @Override public void onClick(View arg0) { mImageRotate.rotate(); - button.setText(mContext.getString(getTextId()) + " " + mImageRotate.getLocalValue()); - mImageRotate.saveAndSetPreset(); + String displayVal = mContext.getString(getTextId()) + " " + + mImageRotate.getLocalValue(); + button.setText(displayVal); } }); } + @Override + public void finalApplyCalled() { + commitLocalRepresentation(mImageRotate.getFinalRepresentation()); + } + @Override public int getTextId() { return R.string.rotate; diff --git a/src/com/android/gallery3d/filtershow/editors/EditorStraighten.java b/src/com/android/gallery3d/filtershow/editors/EditorStraighten.java index 40333aa00..ff84ba8f9 100644 --- a/src/com/android/gallery3d/filtershow/editors/EditorStraighten.java +++ b/src/com/android/gallery3d/filtershow/editors/EditorStraighten.java @@ -17,24 +17,26 @@ package com.android.gallery3d.filtershow.editors; import android.content.Context; -import android.view.View; +import android.util.Log; import android.widget.FrameLayout; import com.android.gallery3d.R; -import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; +import com.android.gallery3d.filtershow.filters.FilterStraightenRepresentation; import com.android.gallery3d.filtershow.imageshow.ImageStraighten; +import com.android.gallery3d.filtershow.imageshow.MasterImage; public class EditorStraighten extends Editor implements EditorInfo { + public static final String TAG = EditorStraighten.class.getSimpleName(); public static final int ID = R.id.editorStraighten; ImageStraighten mImageStraighten; - GeometryMetadata mGeometryMetadata; public EditorStraighten() { super(ID); mShowParameter = SHOW_VALUE_INT; + mChangesGeometry = true; } - // TODO use filter reflection like @Override public String calculateUserMessage(Context context, String effectName, Object parameterValue) { String apply = context.getString(R.string.apply_effect); @@ -49,9 +51,29 @@ public class EditorStraighten extends Editor implements EditorInfo { mImageStraighten = new ImageStraighten(context); } mView = mImageShow = mImageStraighten; - mImageStraighten.bindAsImageLoadListener(); mImageStraighten.setEditor(this); - mImageStraighten.syncLocalToMasterGeometry(); + } + + @Override + public void reflectCurrentFilter() { + MasterImage master = MasterImage.getImage(); + master.setCurrentFilterRepresentation(master.getPreset().getFilterWithSerializationName( + FilterStraightenRepresentation.SERIALIZATION_NAME)); + super.reflectCurrentFilter(); + FilterRepresentation rep = getLocalRepresentation(); + if (rep == null || rep instanceof FilterStraightenRepresentation) { + mImageStraighten + .setFilterStraightenRepresentation((FilterStraightenRepresentation) rep); + } else { + Log.w(TAG, "Could not reflect current filter, not of type: " + + FilterStraightenRepresentation.class.getSimpleName()); + } + mImageStraighten.invalidate(); + } + + @Override + public void finalApplyCalled() { + commitLocalRepresentation(mImageStraighten.getFinalRepresentation()); } @Override diff --git a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java index 2205b4dbb..4708abb24 100644 --- a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java +++ b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java @@ -20,7 +20,10 @@ import android.content.res.Resources; import android.util.Log; import com.android.gallery3d.R; -import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; +import com.android.gallery3d.filtershow.editors.EditorCrop; +import com.android.gallery3d.filtershow.editors.EditorMirror; +import com.android.gallery3d.filtershow.editors.EditorRotate; +import com.android.gallery3d.filtershow.editors.EditorStraighten; import com.android.gallery3d.filtershow.pipeline.ImagePreset; import java.util.ArrayList; @@ -138,7 +141,6 @@ public abstract class BaseFiltersManager implements FiltersManagerInterface { filters.add(ImageFilterFx.class); filters.add(ImageFilterBorder.class); filters.add(ImageFilterParametricBorder.class); - filters.add(ImageFilterGeometry.class); } public ArrayList getLooks() { @@ -238,8 +240,13 @@ public abstract class BaseFiltersManager implements FiltersManagerInterface { } public void addTools(Context context) { - GeometryMetadata geo = new GeometryMetadata(); - int[] editorsId = geo.getEditorIds(); + + int[] editorsId = { + EditorCrop.ID, + EditorStraighten.ID, + EditorRotate.ID, + EditorMirror.ID + }; int[] textId = { R.string.crop, @@ -255,9 +262,16 @@ public abstract class BaseFiltersManager implements FiltersManagerInterface { R.drawable.filtershow_button_geometry_flip }; + FilterRepresentation[] geometryFilters = { + new FilterCropRepresentation(), + new FilterStraightenRepresentation(), + new FilterRotateRepresentation(), + new FilterMirrorRepresentation() + }; + for (int i = 0; i < editorsId.length; i++) { int editorId = editorsId[i]; - GeometryMetadata geometry = new GeometryMetadata(geo); + FilterRepresentation geometry = geometryFilters[i]; geometry.setEditorId(editorId); geometry.setTextId(textId[i]); geometry.setOverlayId(overlayId[i]); diff --git a/src/com/android/gallery3d/filtershow/filters/FilterCropRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterCropRepresentation.java index fea8b2139..c1bd7b3bb 100644 --- a/src/com/android/gallery3d/filtershow/filters/FilterCropRepresentation.java +++ b/src/com/android/gallery3d/filtershow/filters/FilterCropRepresentation.java @@ -28,14 +28,13 @@ import java.io.IOException; public class FilterCropRepresentation extends FilterRepresentation { public static final String SERIALIZATION_NAME = "CROP"; public static final String[] BOUNDS = { - "C0", "C1", "C2", "C3", "I0", "I1", "I2", "I3" + "C0", "C1", "C2", "C3" }; private static final String TAG = FilterCropRepresentation.class.getSimpleName(); - RectF mCrop = new RectF(); - RectF mImage = new RectF(); + RectF mCrop = getNil(); - public FilterCropRepresentation(RectF crop, RectF image) { + public FilterCropRepresentation(RectF crop) { super(FilterCropRepresentation.class.getSimpleName()); setSerializationName(SERIALIZATION_NAME); setShowParameterValue(true); @@ -44,20 +43,18 @@ public class FilterCropRepresentation extends FilterRepresentation { setTextId(R.string.crop); setEditorId(EditorCrop.ID); setCrop(crop); - setImage(image); } public FilterCropRepresentation(FilterCropRepresentation m) { - this(m.getCrop(), m.getImage()); + this(m.mCrop); } public FilterCropRepresentation() { - this(new RectF(), new RectF()); + this(sNilRect); } public void set(FilterCropRepresentation r) { mCrop.set(r.mCrop); - mImage.set(r.mImage); } @Override @@ -69,11 +66,7 @@ public class FilterCropRepresentation extends FilterRepresentation { if (mCrop.bottom != crop.mCrop.bottom || mCrop.left != crop.mCrop.left || mCrop.right != crop.mCrop.right - || mCrop.top != crop.mCrop.top - || mImage.bottom != crop.mImage.bottom - || mImage.left != crop.mImage.left - || mImage.right != crop.mImage.right - || mImage.top != crop.mImage.top) { + || mCrop.top != crop.mCrop.top) { return false; } return true; @@ -94,19 +87,26 @@ public class FilterCropRepresentation extends FilterRepresentation { mCrop.set(crop); } - public RectF getImage() { - return new RectF(mImage); + /** + * Takes a crop rect contained by [0, 0, 1, 1] and scales it by the height + * and width of the image rect. + */ + public static void findScaledCrop(RectF crop, int bitmapWidth, int bitmapHeight) { + crop.left *= bitmapWidth; + crop.top *= bitmapHeight; + crop.right *= bitmapWidth; + crop.bottom *= bitmapHeight; } - public void getImage(RectF r) { - r.set(mImage); - } - - public void setImage(RectF image) { - if (image == null) { - throw new IllegalArgumentException("Argument to setImage is null"); - } - mImage.set(image); + /** + * Takes crop rect and normalizes it by scaling down by the height and width + * of the image rect. + */ + public static void findNormalizedCrop(RectF crop, int bitmapWidth, int bitmapHeight) { + crop.left /= bitmapWidth; + crop.top /= bitmapHeight; + crop.right /= bitmapWidth; + crop.bottom /= bitmapHeight; } @Override @@ -115,7 +115,7 @@ public class FilterCropRepresentation extends FilterRepresentation { } @Override - public FilterRepresentation copy(){ + public FilterRepresentation copy() { return new FilterCropRepresentation(this); } @@ -134,12 +134,17 @@ public class FilterCropRepresentation extends FilterRepresentation { throw new IllegalArgumentException("calling useParametersFrom with incompatible types!"); } setCrop(((FilterCropRepresentation) a).mCrop); - setImage(((FilterCropRepresentation) a).mImage); } + private static final RectF sNilRect = new RectF(0, 0, 1, 1); + @Override public boolean isNil() { - return mCrop.equals(mImage); + return mCrop.equals(sNilRect); + } + + public static RectF getNil() { + return new RectF(sNilRect); } @Override @@ -149,10 +154,6 @@ public class FilterCropRepresentation extends FilterRepresentation { writer.name(BOUNDS[1]).value(mCrop.top); writer.name(BOUNDS[2]).value(mCrop.right); writer.name(BOUNDS[3]).value(mCrop.bottom); - writer.name(BOUNDS[4]).value(mImage.left); - writer.name(BOUNDS[5]).value(mImage.top); - writer.name(BOUNDS[6]).value(mImage.right); - writer.name(BOUNDS[7]).value(mImage.bottom); writer.endObject(); } @@ -169,14 +170,6 @@ public class FilterCropRepresentation extends FilterRepresentation { mCrop.right = (float) reader.nextDouble(); } else if (BOUNDS[3].equals(name)) { mCrop.bottom = (float) reader.nextDouble(); - } else if (BOUNDS[4].equals(name)) { - mImage.left = (float) reader.nextDouble(); - } else if (BOUNDS[5].equals(name)) { - mImage.top = (float) reader.nextDouble(); - } else if (BOUNDS[6].equals(name)) { - mImage.right = (float) reader.nextDouble(); - } else if (BOUNDS[7].equals(name)) { - mImage.bottom = (float) reader.nextDouble(); } else { reader.skipValue(); } diff --git a/src/com/android/gallery3d/filtershow/filters/FilterMirrorRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterMirrorRepresentation.java index 22a15f27e..8dcff0d16 100644 --- a/src/com/android/gallery3d/filtershow/filters/FilterMirrorRepresentation.java +++ b/src/com/android/gallery3d/filtershow/filters/FilterMirrorRepresentation.java @@ -21,7 +21,7 @@ import android.util.JsonWriter; import android.util.Log; import com.android.gallery3d.R; -import com.android.gallery3d.filtershow.editors.EditorFlip; +import com.android.gallery3d.filtershow.editors.EditorMirror; import java.io.IOException; @@ -30,7 +30,7 @@ public class FilterMirrorRepresentation extends FilterRepresentation { private static final String SERIALIZATION_MIRROR_VALUE = "value"; private static final String TAG = FilterMirrorRepresentation.class.getSimpleName(); - Mirror mMirror = Mirror.NONE; + Mirror mMirror; public enum Mirror { NONE('N'), VERTICAL('V'), HORIZONTAL('H'), BOTH('B'); @@ -67,7 +67,7 @@ public class FilterMirrorRepresentation extends FilterRepresentation { setFilterClass(FilterMirrorRepresentation.class); setFilterType(FilterRepresentation.TYPE_GEOMETRY); setTextId(R.string.mirror); - setEditorId(EditorFlip.ID); + setEditorId(EditorMirror.ID); setMirror(mirror); } @@ -76,7 +76,7 @@ public class FilterMirrorRepresentation extends FilterRepresentation { } public FilterMirrorRepresentation() { - this(Mirror.NONE); + this(getNil()); } @Override @@ -85,7 +85,7 @@ public class FilterMirrorRepresentation extends FilterRepresentation { return false; } FilterMirrorRepresentation mirror = (FilterMirrorRepresentation) rep; - if (mirror.mMirror.value() != mirror.mMirror.value()) { + if (mMirror != mirror.mMirror) { return false; } return true; @@ -106,6 +106,23 @@ public class FilterMirrorRepresentation extends FilterRepresentation { mMirror = mirror; } + public void cycle() { + switch (mMirror) { + case NONE: + mMirror = Mirror.HORIZONTAL; + break; + case HORIZONTAL: + mMirror = Mirror.VERTICAL; + break; + case VERTICAL: + mMirror = Mirror.BOTH; + break; + case BOTH: + mMirror = Mirror.NONE; + break; + } + } + @Override public boolean allowsSingleInstanceOnly() { return true; @@ -135,7 +152,11 @@ public class FilterMirrorRepresentation extends FilterRepresentation { @Override public boolean isNil() { - return mMirror == Mirror.NONE; + return mMirror == getNil(); + } + + public static Mirror getNil() { + return Mirror.NONE; } @Override diff --git a/src/com/android/gallery3d/filtershow/filters/FilterRotateRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterRotateRepresentation.java index d5f3a5b40..eb89de036 100644 --- a/src/com/android/gallery3d/filtershow/filters/FilterRotateRepresentation.java +++ b/src/com/android/gallery3d/filtershow/filters/FilterRotateRepresentation.java @@ -30,7 +30,7 @@ public class FilterRotateRepresentation extends FilterRepresentation { public static final String SERIALIZATION_ROTATE_VALUE = "value"; private static final String TAG = FilterRotateRepresentation.class.getSimpleName(); - Rotation mRotation = Rotation.ZERO; + Rotation mRotation; public enum Rotation { ZERO(0), NINETY(90), ONE_EIGHTY(180), TWO_SEVENTY(270); @@ -76,13 +76,30 @@ public class FilterRotateRepresentation extends FilterRepresentation { } public FilterRotateRepresentation() { - this(Rotation.ZERO); + this(getNil()); } public Rotation getRotation() { return mRotation; } + public void rotateCW() { + switch(mRotation) { + case ZERO: + mRotation = Rotation.NINETY; + break; + case NINETY: + mRotation = Rotation.ONE_EIGHTY; + break; + case ONE_EIGHTY: + mRotation = Rotation.TWO_SEVENTY; + break; + case TWO_SEVENTY: + mRotation = Rotation.ZERO; + break; + } + } + public void set(FilterRotateRepresentation r) { mRotation = r.mRotation; } @@ -123,7 +140,11 @@ public class FilterRotateRepresentation extends FilterRepresentation { @Override public boolean isNil() { - return mRotation == Rotation.ZERO; + return mRotation == getNil(); + } + + public static Rotation getNil() { + return Rotation.ZERO; } @Override diff --git a/src/com/android/gallery3d/filtershow/filters/FilterStraightenRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterStraightenRepresentation.java index 890f1cfd6..94c9497fc 100644 --- a/src/com/android/gallery3d/filtershow/filters/FilterStraightenRepresentation.java +++ b/src/com/android/gallery3d/filtershow/filters/FilterStraightenRepresentation.java @@ -29,6 +29,8 @@ public class FilterStraightenRepresentation extends FilterRepresentation { public static final String SERIALIZATION_NAME = "STRAIGHTEN"; public static final String SERIALIZATION_STRAIGHTEN_VALUE = "value"; private static final String TAG = FilterStraightenRepresentation.class.getSimpleName(); + public static final int MAX_STRAIGHTEN_ANGLE = 45; + public static final int MIN_STRAIGHTEN_ANGLE = -45; float mStraighten; @@ -48,7 +50,7 @@ public class FilterStraightenRepresentation extends FilterRepresentation { } public FilterStraightenRepresentation() { - this(0); + this(getNil()); } public void set(FilterStraightenRepresentation r) { @@ -73,7 +75,7 @@ public class FilterStraightenRepresentation extends FilterRepresentation { public void setStraighten(float straighten) { if (!rangeCheck(straighten)) { - straighten = Math.min(Math.max(straighten, -45), 45); + straighten = Math.min(Math.max(straighten, MIN_STRAIGHTEN_ANGLE), MAX_STRAIGHTEN_ANGLE); } mStraighten = straighten; } @@ -107,7 +109,11 @@ public class FilterStraightenRepresentation extends FilterRepresentation { @Override public boolean isNil() { - return mStraighten == 0; + return mStraighten == getNil(); + } + + public static float getNil() { + return 0; } @Override @@ -124,7 +130,7 @@ public class FilterStraightenRepresentation extends FilterRepresentation { while (reader.hasNext()) { String name = reader.nextName(); if (SERIALIZATION_STRAIGHTEN_VALUE.equals(name)) { - int s = reader.nextInt(); + float s = (float) reader.nextDouble(); if (rangeCheck(s)) { setStraighten(s); unset = false; @@ -139,7 +145,7 @@ public class FilterStraightenRepresentation extends FilterRepresentation { reader.endObject(); } - private boolean rangeCheck(float s) { + private boolean rangeCheck(double s) { if (s < -45 || s > 45) { return false; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilter.java b/src/com/android/gallery3d/filtershow/filters/ImageFilter.java index 050dc436b..437137416 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilter.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilter.java @@ -22,10 +22,9 @@ import android.graphics.Matrix; import android.support.v8.renderscript.Allocation; import android.widget.Toast; -import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; +import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils; import com.android.gallery3d.filtershow.imageshow.MasterImage; import com.android.gallery3d.filtershow.pipeline.FilterEnvironment; -import com.android.gallery3d.filtershow.pipeline.ImagePreset; public abstract class ImageFilter implements Cloneable { private FilterEnvironment mEnvironment = null; @@ -89,13 +88,8 @@ public abstract class ImageFilter implements Cloneable { } protected Matrix getOriginalToScreenMatrix(int w, int h) { - ImagePreset preset = getEnvironment().getImagePreset(); - GeometryMetadata geo = getEnvironment().getImagePreset().getGeometry(); - Matrix originalToScreen = geo.getOriginalToScreen(true, - MasterImage.getImage().getOriginalBounds().width(), - MasterImage.getImage().getOriginalBounds().height(), - w, h); - return originalToScreen; + return GeometryMathUtils.getImageToScreenMatrix(getEnvironment().getImagePreset() + .getGeometryFilters(), true, MasterImage.getImage().getOriginalBounds(), w, h); } public void setEnvironment(FilterEnvironment environment) { diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java deleted file mode 100644 index 3c323e182..000000000 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * 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.android.gallery3d.filtershow.filters; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.RectF; -import android.util.Log; - -import com.android.gallery3d.filtershow.crop.CropExtras; -import com.android.gallery3d.filtershow.imageshow.GeometryMath; -import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; - -public class ImageFilterGeometry extends ImageFilter { - private final Bitmap.Config mConfig = Bitmap.Config.ARGB_8888; - private GeometryMetadata mGeometry = null; - private static final String LOGTAG = "ImageFilterGeometry"; - private static final boolean LOGV = false; - private static final int BOTH = 3; - private static final int VERTICAL = 2; - private static final int HORIZONTAL = 1; - private static final int NINETY = 1; - private static final int ONE_EIGHTY = 2; - private static final int TWO_SEVENTY = 3; - - public ImageFilterGeometry() { - mName = "Geometry"; - } - - @Override - public ImageFilter clone() throws CloneNotSupportedException { - // FIXME: clone() should not be needed. Remove when we fix geometry. - ImageFilterGeometry filter = (ImageFilterGeometry) super.clone(); - return filter; - } - - native protected void nativeApplyFilterFlip(Bitmap src, int srcWidth, int srcHeight, - Bitmap dst, int dstWidth, int dstHeight, int flip); - - native protected void nativeApplyFilterRotate(Bitmap src, int srcWidth, int srcHeight, - Bitmap dst, int dstWidth, int dstHeight, int rotate); - - native protected void nativeApplyFilterCrop(Bitmap src, int srcWidth, int srcHeight, - Bitmap dst, int dstWidth, int dstHeight, int offsetWidth, int offsetHeight); - - native protected void nativeApplyFilterStraighten(Bitmap src, int srcWidth, int srcHeight, - Bitmap dst, int dstWidth, int dstHeight, float straightenAngle); - - @Override - public void useRepresentation(FilterRepresentation representation) { - mGeometry = (GeometryMetadata) representation; - } - - @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { - // TODO: implement bilinear or bicubic here... for now, just use - // canvas to do a simple implementation... - // TODO: and be more memory efficient! (do it in native?) - RectF cb = mGeometry.getPreviewCropBounds(); - RectF pb = mGeometry.getPhotoBounds(); - if (cb.width() == 0 || cb.height() == 0 || pb.width() == 0 || pb.height() == 0) { - Log.w(LOGTAG, "Cannot apply geometry: geometry metadata has not been initialized"); - return bitmap; - } - - Rect cropBounds = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); - RectF crop = mGeometry.getCropBounds(bitmap); - if (crop.width() > 0 && crop.height() > 0) - cropBounds = GeometryMath.roundNearest(crop); - - int width = cropBounds.width(); - int height = cropBounds.height(); - - if (mGeometry.hasSwitchedWidthHeight()){ - int temp = width; - width = height; - height = temp; - } - - Bitmap temp = null; - temp = Bitmap.createBitmap(width, height, mConfig); - - float[] displayCenter = { - temp.getWidth() / 2f, temp.getHeight() / 2f - }; - - Matrix m1 = mGeometry.buildTotalXform(bitmap.getWidth(), bitmap.getHeight(), displayCenter); - - m1.postScale(1, 1, displayCenter[0], displayCenter[1]); - - Canvas canvas = new Canvas(temp); - Paint paint = new Paint(); - paint.setAntiAlias(true); - paint.setFilterBitmap(true); - paint.setDither(true); - canvas.drawBitmap(bitmap, m1, paint); - return temp; - } - -} diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterStraighten.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterStraighten.java deleted file mode 100644 index a3bb6f980..000000000 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterStraighten.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * 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.android.gallery3d.filtershow.filters; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; - -public class ImageFilterStraighten extends ImageFilter { - private final Bitmap.Config mConfig = Bitmap.Config.ARGB_8888; - private float mRotation; - private float mZoomFactor; - - public ImageFilterStraighten() { - mName = "Straighten"; - } - - @Override - public ImageFilter clone() throws CloneNotSupportedException { - // FIXME: clone() should not be needed. Remove when we fix geometry. - ImageFilterStraighten filter = (ImageFilterStraighten) super.clone(); - filter.mRotation = mRotation; - filter.mZoomFactor = mZoomFactor; - return filter; - } - - public ImageFilterStraighten(float rotation, float zoomFactor) { - mRotation = rotation; - mZoomFactor = zoomFactor; - } - - public void setRotation(float rotation) { - mRotation = rotation; - } - - public void setRotationZoomFactor(float zoomFactor) { - mZoomFactor = zoomFactor; - } - - @Override - public void useRepresentation(FilterRepresentation representation) { - - } - - @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { - // TODO: implement bilinear or bicubic here... for now, just use - // canvas to do a simple implementation... - // TODO: and be more memory efficient! (do it in native?) - - Bitmap temp = bitmap.copy(mConfig, true); - Canvas canvas = new Canvas(temp); - canvas.save(); - Rect bounds = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); - float w = temp.getWidth(); - float h = temp.getHeight(); - float mw = temp.getWidth() / 2.0f; - float mh = temp.getHeight() / 2.0f; - - canvas.scale(mZoomFactor, mZoomFactor, mw, mh); - canvas.rotate(mRotation, mw, mh); - canvas.drawBitmap(bitmap, bounds, bounds, new Paint()); - canvas.restore(); - - int[] pixels = new int[(int) (w * h)]; - temp.getPixels(pixels, 0, (int) w, 0, 0, (int) w, (int) h); - bitmap.setPixels(pixels, 0, (int) w, 0, 0, (int) w, (int) h); - temp.recycle(); - temp = null; - pixels = null; - return bitmap; - } - -} diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryListener.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryListener.java deleted file mode 100644 index 549c2e7a5..000000000 --- a/src/com/android/gallery3d/filtershow/imageshow/GeometryListener.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * 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.android.gallery3d.filtershow.imageshow; - -public interface GeometryListener { - public void geometryChanged(); -} diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java deleted file mode 100644 index 568dadfc3..000000000 --- a/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * 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.android.gallery3d.filtershow.imageshow; - -import android.graphics.Rect; -import android.graphics.RectF; - -public class GeometryMath { - - // Math operations for 2d vectors - public static float clamp(float i, float low, float high) { - return Math.max(Math.min(i, high), low); - } - - public static float[] lineIntersect(float[] line1, float[] line2) { - float a0 = line1[0]; - float a1 = line1[1]; - float b0 = line1[2]; - float b1 = line1[3]; - float c0 = line2[0]; - float c1 = line2[1]; - float d0 = line2[2]; - float d1 = line2[3]; - float t0 = a0 - b0; - float t1 = a1 - b1; - float t2 = b0 - d0; - float t3 = d1 - b1; - float t4 = c0 - d0; - float t5 = c1 - d1; - - float denom = t1 * t4 - t0 * t5; - if (denom == 0) - return null; - float u = (t3 * t4 + t5 * t2) / denom; - float[] intersect = { - b0 + u * t0, b1 + u * t1 - }; - return intersect; - } - - public static float[] shortestVectorFromPointToLine(float[] point, float[] line) { - float x1 = line[0]; - float x2 = line[2]; - float y1 = line[1]; - float y2 = line[3]; - float xdelt = x2 - x1; - float ydelt = y2 - y1; - if (xdelt == 0 && ydelt == 0) - return null; - float u = ((point[0] - x1) * xdelt + (point[1] - y1) * ydelt) - / (xdelt * xdelt + ydelt * ydelt); - float[] ret = { - (x1 + u * (x2 - x1)), (y1 + u * (y2 - y1)) - }; - float[] vec = { - ret[0] - point[0], ret[1] - point[1] - }; - return vec; - } - - // A . B - public static float dotProduct(float[] a, float[] b) { - return a[0] * b[0] + a[1] * b[1]; - } - - public static float[] normalize(float[] a) { - float length = (float) Math.sqrt(a[0] * a[0] + a[1] * a[1]); - float[] b = { - a[0] / length, a[1] / length - }; - return b; - } - - // A onto B - public static float scalarProjection(float[] a, float[] b) { - float length = (float) Math.sqrt(b[0] * b[0] + b[1] * b[1]); - return dotProduct(a, b) / length; - } - - public static float[] getVectorFromPoints(float[] point1, float[] point2) { - float[] p = { - point2[0] - point1[0], point2[1] - point1[1] - }; - return p; - } - - public static float[] getUnitVectorFromPoints(float[] point1, float[] point2) { - float[] p = { - point2[0] - point1[0], point2[1] - point1[1] - }; - float length = (float) Math.sqrt(p[0] * p[0] + p[1] * p[1]); - p[0] = p[0] / length; - p[1] = p[1] / length; - return p; - } - - public static RectF scaleRect(RectF r, float scale) { - return new RectF(r.left * scale, r.top * scale, r.right * scale, r.bottom * scale); - } - - // A - B - public static float[] vectorSubtract(float[] a, float[] b) { - int len = a.length; - if (len != b.length) - return null; - float[] ret = new float[len]; - for (int i = 0; i < len; i++) { - ret[i] = a[i] - b[i]; - } - return ret; - } - - public static float vectorLength(float[] a) { - return (float) Math.sqrt(a[0] * a[0] + a[1] * a[1]); - } - - public static float scale(float oldWidth, float oldHeight, float newWidth, float newHeight) { - if (oldHeight == 0 || oldWidth == 0) - return 1; - return Math.min(newWidth / oldWidth, newHeight / oldHeight); - } - - public static Rect roundNearest(RectF r) { - Rect q = new Rect(Math.round(r.left), Math.round(r.top), Math.round(r.right), - Math.round(r.bottom)); - return q; - } - -} diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryMathUtils.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMathUtils.java new file mode 100644 index 000000000..81394f142 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryMathUtils.java @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * 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.android.gallery3d.filtershow.imageshow; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; + +import com.android.gallery3d.filtershow.cache.ImageLoader; +import com.android.gallery3d.filtershow.filters.FilterCropRepresentation; +import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation; +import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation.Mirror; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; +import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation; +import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation.Rotation; +import com.android.gallery3d.filtershow.filters.FilterStraightenRepresentation; +import com.android.gallery3d.filtershow.pipeline.ImagePreset; + +import java.util.Collection; +import java.util.Iterator; + +public final class GeometryMathUtils { + private GeometryMathUtils() {}; + + // Holder class for Geometry data. + public static final class GeometryHolder { + public Rotation rotation = FilterRotateRepresentation.getNil(); + public float straighten = FilterStraightenRepresentation.getNil(); + public RectF crop = FilterCropRepresentation.getNil(); + public Mirror mirror = FilterMirrorRepresentation.getNil(); + + public void set(GeometryHolder h) { + rotation = h.rotation; + straighten = h.straighten; + crop.set(h.crop); + mirror = h.mirror; + } + + public void wipe() { + rotation = FilterRotateRepresentation.getNil(); + straighten = FilterStraightenRepresentation.getNil(); + crop = FilterCropRepresentation.getNil(); + mirror = FilterMirrorRepresentation.getNil(); + } + + public boolean isNil() { + return rotation == FilterRotateRepresentation.getNil() && + straighten == FilterStraightenRepresentation.getNil() && + crop.equals(FilterCropRepresentation.getNil()) && + mirror == FilterMirrorRepresentation.getNil(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof GeometryHolder)) { + return false; + } + GeometryHolder h = (GeometryHolder) o; + return rotation == h.rotation && straighten == h.straighten && + ((crop == null && h.crop == null) || (crop != null && crop.equals(h.crop))) && + mirror == h.mirror; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + "rotation:" + rotation.value() + + ",straighten:" + straighten + ",crop:" + crop.toString() + + ",mirror:" + mirror.value() + "]"; + } + } + + // Math operations for 2d vectors + public static float clamp(float i, float low, float high) { + return Math.max(Math.min(i, high), low); + } + + public static float[] lineIntersect(float[] line1, float[] line2) { + float a0 = line1[0]; + float a1 = line1[1]; + float b0 = line1[2]; + float b1 = line1[3]; + float c0 = line2[0]; + float c1 = line2[1]; + float d0 = line2[2]; + float d1 = line2[3]; + float t0 = a0 - b0; + float t1 = a1 - b1; + float t2 = b0 - d0; + float t3 = d1 - b1; + float t4 = c0 - d0; + float t5 = c1 - d1; + + float denom = t1 * t4 - t0 * t5; + if (denom == 0) + return null; + float u = (t3 * t4 + t5 * t2) / denom; + float[] intersect = { + b0 + u * t0, b1 + u * t1 + }; + return intersect; + } + + public static float[] shortestVectorFromPointToLine(float[] point, float[] line) { + float x1 = line[0]; + float x2 = line[2]; + float y1 = line[1]; + float y2 = line[3]; + float xdelt = x2 - x1; + float ydelt = y2 - y1; + if (xdelt == 0 && ydelt == 0) + return null; + float u = ((point[0] - x1) * xdelt + (point[1] - y1) * ydelt) + / (xdelt * xdelt + ydelt * ydelt); + float[] ret = { + (x1 + u * (x2 - x1)), (y1 + u * (y2 - y1)) + }; + float[] vec = { + ret[0] - point[0], ret[1] - point[1] + }; + return vec; + } + + // A . B + public static float dotProduct(float[] a, float[] b) { + return a[0] * b[0] + a[1] * b[1]; + } + + public static float[] normalize(float[] a) { + float length = (float) Math.sqrt(a[0] * a[0] + a[1] * a[1]); + float[] b = { + a[0] / length, a[1] / length + }; + return b; + } + + // A onto B + public static float scalarProjection(float[] a, float[] b) { + float length = (float) Math.sqrt(b[0] * b[0] + b[1] * b[1]); + return dotProduct(a, b) / length; + } + + public static float[] getVectorFromPoints(float[] point1, float[] point2) { + float[] p = { + point2[0] - point1[0], point2[1] - point1[1] + }; + return p; + } + + public static float[] getUnitVectorFromPoints(float[] point1, float[] point2) { + float[] p = { + point2[0] - point1[0], point2[1] - point1[1] + }; + float length = (float) Math.sqrt(p[0] * p[0] + p[1] * p[1]); + p[0] = p[0] / length; + p[1] = p[1] / length; + return p; + } + + public static void scaleRect(RectF r, float scale) { + r.set(r.left * scale, r.top * scale, r.right * scale, r.bottom * scale); + } + + // A - B + public static float[] vectorSubtract(float[] a, float[] b) { + int len = a.length; + if (len != b.length) + return null; + float[] ret = new float[len]; + for (int i = 0; i < len; i++) { + ret[i] = a[i] - b[i]; + } + return ret; + } + + public static float vectorLength(float[] a) { + return (float) Math.sqrt(a[0] * a[0] + a[1] * a[1]); + } + + public static float scale(float oldWidth, float oldHeight, float newWidth, float newHeight) { + if (oldHeight == 0 || oldWidth == 0 || (oldWidth == newWidth && oldHeight == newHeight)) { + return 1; + } + return Math.min(newWidth / oldWidth, newHeight / oldHeight); + } + + public static Rect roundNearest(RectF r) { + Rect q = new Rect(Math.round(r.left), Math.round(r.top), Math.round(r.right), + Math.round(r.bottom)); + return q; + } + + private static void concatMirrorMatrix(Matrix m, Mirror type) { + if (type == Mirror.HORIZONTAL) { + m.postScale(-1, 1); + } else if (type == Mirror.VERTICAL) { + m.postScale(1, -1); + } else if (type == Mirror.BOTH) { + m.postScale(1, -1); + m.postScale(-1, 1); + } + } + + private static int getRotationForOrientation(int orientation) { + switch (orientation) { + case ImageLoader.ORI_ROTATE_90: + return 90; + case ImageLoader.ORI_ROTATE_180: + return 180; + case ImageLoader.ORI_ROTATE_270: + return 270; + default: + return 0; + } + } + + public static GeometryHolder unpackGeometry(Collection geometry) { + GeometryHolder holder = new GeometryHolder(); + unpackGeometry(holder, geometry); + return holder; + } + + public static void unpackGeometry(GeometryHolder out, + Collection geometry) { + out.wipe(); + // Get geometry data from filters + for (FilterRepresentation r : geometry) { + if (r.isNil()) { + continue; + } + if (r.getSerializationName() == FilterRotateRepresentation.SERIALIZATION_NAME) { + out.rotation = ((FilterRotateRepresentation) r).getRotation(); + } else if (r.getSerializationName() == + FilterStraightenRepresentation.SERIALIZATION_NAME) { + out.straighten = ((FilterStraightenRepresentation) r).getStraighten(); + } else if (r.getSerializationName() == FilterCropRepresentation.SERIALIZATION_NAME) { + ((FilterCropRepresentation) r).getCrop(out.crop); + } else if (r.getSerializationName() == FilterMirrorRepresentation.SERIALIZATION_NAME) { + out.mirror = ((FilterMirrorRepresentation) r).getMirror(); + } + } + } + + public static void replaceInstances(Collection geometry, + FilterRepresentation rep) { + Iterator iter = geometry.iterator(); + while (iter.hasNext()) { + FilterRepresentation r = iter.next(); + if (ImagePreset.sameSerializationName(rep, r)) { + iter.remove(); + } + } + if (!rep.isNil()) { + geometry.add(rep); + } + } + + public static void initializeHolder(GeometryHolder outHolder, + FilterRepresentation currentLocal) { + Collection geometry = MasterImage.getImage().getPreset() + .getGeometryFilters(); + replaceInstances(geometry, currentLocal); + unpackGeometry(outHolder, geometry); + } + + private static Bitmap applyFullGeometryMatrix(Bitmap image, GeometryHolder holder) { + int width = image.getWidth(); + int height = image.getHeight(); + RectF crop = getTrueCropRect(holder, width, height); + Rect frame = new Rect(); + 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); + Canvas canvas = new Canvas(temp); + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setFilterBitmap(true); + paint.setDither(true); + canvas.drawBitmap(image, m, paint); + return temp; + } + + public static Matrix getImageToScreenMatrix(Collection geometry, + boolean reflectRotation, Rect bmapDimens, float viewWidth, float viewHeight) { + GeometryHolder h = unpackGeometry(geometry); + return GeometryMathUtils.getOriginalToScreen(h, reflectRotation, bmapDimens.width(), + bmapDimens.height(), viewWidth, viewHeight); + } + + public static Matrix getOriginalToScreen(GeometryHolder holder, boolean rotate, + float originalWidth, + float originalHeight, float viewWidth, float viewHeight) { + int orientation = MasterImage.getImage().getZoomOrientation(); + int rotation = getRotationForOrientation(orientation); + Rotation prev = holder.rotation; + rotation = (rotation + prev.value()) % 360; + holder.rotation = Rotation.fromValue(rotation); + Matrix m = getCropSelectionToScreenMatrix(null, holder, (int) originalWidth, + (int) originalHeight, (int) viewWidth, (int) viewHeight); + holder.rotation = prev; + return m; + } + + public static Bitmap applyGeometryRepresentations(Collection res, + Bitmap image) { + GeometryHolder holder = unpackGeometry(res); + Bitmap bmap = image; + // If there are geometry changes, apply them to the image + if (!holder.isNil()) { + bmap = applyFullGeometryMatrix(bmap, holder); + } + return bmap; + } + + public static RectF drawTransformedCropped(GeometryHolder holder, Canvas canvas, + Bitmap photo, int viewWidth, int viewHeight) { + if (photo == null) { + return null; + } + RectF crop = new RectF(); + Matrix m = getCropSelectionToScreenMatrix(crop, holder, photo.getWidth(), photo.getHeight(), + viewWidth, viewHeight); + canvas.save(); + canvas.clipRect(crop); + Paint p = new Paint(); + p.setAntiAlias(true); + canvas.drawBitmap(photo, m, p); + canvas.restore(); + return crop; + } + + public static boolean needsDimensionSwap(Rotation rotation) { + switch (rotation) { + case NINETY: + case TWO_SEVENTY: + return true; + default: + return false; + } + } + + // Gives matrix for rotated, straightened, mirrored bitmap centered at 0,0. + private static Matrix getFullGeometryMatrix(GeometryHolder holder, int bitmapWidth, + int bitmapHeight) { + float centerX = bitmapWidth / 2f; + float centerY = bitmapHeight / 2f; + Matrix m = new Matrix(); + m.setTranslate(-centerX, -centerY); + m.postRotate(holder.straighten + holder.rotation.value()); + concatMirrorMatrix(m, holder.mirror); + return m; + } + + public static Matrix getFullGeometryToScreenMatrix(GeometryHolder holder, int bitmapWidth, + int bitmapHeight, int viewWidth, int viewHeight) { + float scale = GeometryMathUtils.scale(bitmapWidth, bitmapHeight, viewWidth, viewHeight); + Matrix m = getFullGeometryMatrix(holder, bitmapWidth, bitmapHeight); + m.postScale(scale, scale); + m.postTranslate(viewWidth / 2f, viewHeight / 2f); + return m; + } + + public static RectF getTrueCropRect(GeometryHolder holder, int bitmapWidth, int bitmapHeight) { + RectF r = new RectF(holder.crop); + FilterCropRepresentation.findScaledCrop(r, bitmapWidth, bitmapHeight); + float s = holder.straighten; + holder.straighten = 0; + Matrix m1 = getFullGeometryMatrix(holder, bitmapWidth, bitmapHeight); + holder.straighten = s; + m1.mapRect(r); + return r; + } + + public static Matrix getCropSelectionToScreenMatrix(RectF outCrop, GeometryHolder holder, + int bitmapWidth, int bitmapHeight, int viewWidth, int viewHeight) { + Matrix m = getFullGeometryMatrix(holder, bitmapWidth, bitmapHeight); + RectF crop = getTrueCropRect(holder, bitmapWidth, bitmapHeight); + float scale = GeometryMathUtils.scale(crop.width(), crop.height(), viewWidth, viewHeight); + m.postScale(scale, scale); + GeometryMathUtils.scaleRect(crop, scale); + m.postTranslate(viewWidth / 2f - crop.centerX(), viewHeight / 2f - crop.centerY()); + if (outCrop != null) { + crop.offset(viewWidth / 2f - crop.centerX(), viewHeight / 2f - crop.centerY()); + outCrop.set(crop); + } + return m; + } + + public static Matrix getCropSelectionToScreenMatrix(RectF outCrop, + Collection res, int bitmapWidth, int bitmapHeight, int viewWidth, + int viewHeight) { + GeometryHolder holder = unpackGeometry(res); + return getCropSelectionToScreenMatrix(outCrop, holder, bitmapWidth, bitmapHeight, + viewWidth, viewHeight); + } +} diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java deleted file mode 100644 index adfce85cf..000000000 --- a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java +++ /dev/null @@ -1,490 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * 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.android.gallery3d.filtershow.imageshow; - -import android.graphics.Bitmap; -import android.graphics.Matrix; -import android.graphics.RectF; -import android.util.JsonReader; -import android.util.JsonWriter; - -import com.android.gallery3d.filtershow.cache.ImageLoader; -import com.android.gallery3d.filtershow.editors.EditorCrop; -import com.android.gallery3d.filtershow.editors.EditorFlip; -import com.android.gallery3d.filtershow.editors.EditorRotate; -import com.android.gallery3d.filtershow.editors.EditorStraighten; -import com.android.gallery3d.filtershow.filters.FilterCropRepresentation; -import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation; -import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation.Mirror; -import com.android.gallery3d.filtershow.filters.FilterRepresentation; -import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation; -import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation.Rotation; -import com.android.gallery3d.filtershow.filters.FilterStraightenRepresentation; -import com.android.gallery3d.filtershow.filters.ImageFilterGeometry; - -import java.util.HashMap; -import java.io.IOException; - -public class GeometryMetadata extends FilterRepresentation { - public static final String SERIALIZATION_NAME = "GEOM"; - public static final String SERIALIZATION_VALUE_SCALE = "scalevalue"; - private static final String LOGTAG = "GeometryMetadata"; - private float mScaleFactor = 1.0f; - - private FilterRotateRepresentation mRotationRep = new FilterRotateRepresentation(); - private FilterStraightenRepresentation mStraightenRep = new FilterStraightenRepresentation(); - private FilterCropRepresentation mCropRep = new FilterCropRepresentation(); - private FilterMirrorRepresentation mMirrorRep = new FilterMirrorRepresentation(); - - public GeometryMetadata() { - super("GeometryMetadata"); - setSerializationName(SERIALIZATION_NAME); - setFilterClass(ImageFilterGeometry.class); - setEditorId(EditorCrop.ID); - setTextId(0); - setShowParameterValue(true); - } - - @Override - public int[] getEditorIds() { - return new int[] { - EditorCrop.ID, - EditorStraighten.ID, - EditorRotate.ID, - EditorFlip.ID - }; - } - - public GeometryMetadata(GeometryMetadata g) { - super("GeometryMetadata"); - set(g); - } - - public boolean hasModifications() { - if (mScaleFactor != 1.0f) { - return true; - } - if (!mRotationRep.isNil()) { - return true; - } - if (!mStraightenRep.isNil()) { - return true; - } - if (!mCropRep.isNil()) { - return true; - } - if (!mMirrorRep.isNil()) { - return true; - } - return false; - } - - public void set(GeometryMetadata g) { - mScaleFactor = g.mScaleFactor; - mRotationRep.set(g.mRotationRep); - mStraightenRep.set(g.mStraightenRep); - mCropRep.set(g.mCropRep); - mMirrorRep.set(g.mMirrorRep); - } - - @Override - public boolean equals(FilterRepresentation rep) { - if (!(rep instanceof GeometryMetadata)) { - return false; - } - GeometryMetadata geo = (GeometryMetadata) rep; - if (geo.mScaleFactor != mScaleFactor - || !geo.mRotationRep.equals(mRotationRep) - || !geo.mStraightenRep.equals(mStraightenRep) - || !geo.mCropRep.equals(mCropRep) - || !geo.mMirrorRep.equals(mMirrorRep)) { - return false; - } - return true; - } - - public float getScaleFactor() { - return mScaleFactor; - } - - public int getRotation() { - return mRotationRep.getRotation().value(); - } - - public float getStraightenRotation() { - return mStraightenRep.getStraighten(); - } - - public RectF getPreviewCropBounds() { - return mCropRep.getCrop(); - } - - public RectF getCropBounds(Bitmap bitmap) { - float scale = 1.0f; - RectF photoBounds = mCropRep.getImage(); - RectF cropBounds = mCropRep.getCrop(); - scale = GeometryMath.scale(photoBounds.width(), photoBounds.height(), bitmap.getWidth(), - bitmap.getHeight()); - RectF croppedRegion = new RectF(cropBounds.left * scale, cropBounds.top * scale, - cropBounds.right * scale, cropBounds.bottom * scale); - - // If no crop has been applied, make sure to use the exact size values. - // Multiplying using scale will introduce rounding errors that modify - // even un-cropped images. - if (cropBounds.left == 0 && cropBounds.right == photoBounds.right) { - croppedRegion.left = 0; - croppedRegion.right = bitmap.getWidth(); - } - if (cropBounds.top == 0 && cropBounds.bottom == photoBounds.bottom) { - croppedRegion.top = 0; - croppedRegion.bottom = bitmap.getHeight(); - } - return croppedRegion; - } - - public Mirror getMirrorType() { - return mMirrorRep.getMirror(); - } - - public void setMirrorType(Mirror m) { - mMirrorRep.setMirror(m); - } - - public RectF getPhotoBounds() { - return mCropRep.getImage(); - } - - public void setScaleFactor(float scale) { - mScaleFactor = scale; - } - - public void setRotation(int rotation) { - Rotation r = Rotation.fromValue(rotation % 360); - mRotationRep.setRotation((r == null) ? Rotation.ZERO : r); - } - - public void setStraightenRotation(float straighten) { - mStraightenRep.setStraighten(straighten); - } - - public void setCropBounds(RectF newCropBounds) { - mCropRep.setCrop(newCropBounds); - } - - public void setPhotoBounds(RectF newPhotoBounds) { - mCropRep.setImage(newPhotoBounds); - } - - protected static void concatHorizontalMatrix(Matrix m, float width) { - m.postScale(-1, 1); - m.postTranslate(width, 0); - } - - protected static void concatVerticalMatrix(Matrix m, float height) { - m.postScale(1, -1); - m.postTranslate(0, height); - } - - public static void concatMirrorMatrix(Matrix m, float width, float height, Mirror type) { - if (type == Mirror.HORIZONTAL) { - concatHorizontalMatrix(m, width); - } else if (type == Mirror.VERTICAL) { - concatVerticalMatrix(m, height); - } else if (type == Mirror.BOTH) { - concatVerticalMatrix(m, height); - concatHorizontalMatrix(m, width); - } - } - - public static Matrix getMatrixOriginalOrientation(int orientation, float originalWidth, - float originalHeight) { - Matrix imageRotation = new Matrix(); - switch (orientation) { - case ImageLoader.ORI_ROTATE_90: { - imageRotation.setRotate(90, originalWidth / 2f, originalHeight / 2f); - imageRotation.postTranslate(-(originalWidth - originalHeight) / 2f, - -(originalHeight - originalWidth) / 2f); - break; - } - case ImageLoader.ORI_ROTATE_180: { - imageRotation.setRotate(180, originalWidth / 2f, originalHeight / 2f); - break; - } - case ImageLoader.ORI_ROTATE_270: { - imageRotation.setRotate(270, originalWidth / 2f, originalHeight / 2f); - imageRotation.postTranslate(-(originalWidth - originalHeight) / 2f, - -(originalHeight - originalWidth) / 2f); - break; - } - case ImageLoader.ORI_FLIP_HOR: { - imageRotation.preScale(-1, 1); - break; - } - case ImageLoader.ORI_FLIP_VERT: { - imageRotation.preScale(1, -1); - break; - } - case ImageLoader.ORI_TRANSPOSE: { - imageRotation.setRotate(90, originalWidth / 2f, originalHeight / 2f); - imageRotation.postTranslate(-(originalWidth - originalHeight) / 2f, - -(originalHeight - originalWidth) / 2f); - imageRotation.preScale(1, -1); - break; - } - case ImageLoader.ORI_TRANSVERSE: { - imageRotation.setRotate(270, originalWidth / 2f, originalHeight / 2f); - imageRotation.postTranslate(-(originalWidth - originalHeight) / 2f, - -(originalHeight - originalWidth) / 2f); - imageRotation.preScale(1, -1); - break; - } - } - return imageRotation; - } - - public Matrix getOriginalToScreen(boolean rotate, float originalWidth, float originalHeight, - float viewWidth, float viewHeight) { - RectF photoBounds = getPhotoBounds(); - RectF cropBounds = getPreviewCropBounds(); - float imageWidth = cropBounds.width(); - float imageHeight = cropBounds.height(); - - int orientation = MasterImage.getImage().getZoomOrientation(); - Matrix imageRotation = getMatrixOriginalOrientation(orientation, originalWidth, - originalHeight); - if (orientation == ImageLoader.ORI_ROTATE_90 || - orientation == ImageLoader.ORI_ROTATE_270 || - orientation == ImageLoader.ORI_TRANSPOSE || - orientation == ImageLoader.ORI_TRANSVERSE) { - float tmp = originalWidth; - originalWidth = originalHeight; - originalHeight = tmp; - } - - float preScale = GeometryMath.scale(originalWidth, originalHeight, - photoBounds.width(), photoBounds.height()); - float scale = GeometryMath.scale(imageWidth, imageHeight, viewWidth, viewHeight); - // checks if local rotation is an odd multiple of 90. - if (((int) (getRotation() / 90)) % 2 != 0) { - scale = GeometryMath.scale(imageWidth, imageHeight, viewHeight, viewWidth); - } - // put in screen coordinates - RectF scaledCrop = GeometryMath.scaleRect(cropBounds, scale); - RectF scaledPhoto = GeometryMath.scaleRect(photoBounds, scale); - float[] displayCenter = { - viewWidth / 2f, viewHeight / 2f - }; - Matrix m1 = GeometryMetadata.buildWanderingCropMatrix(scaledPhoto, scaledCrop, - getRotation(), getStraightenRotation(), getMirrorType(), displayCenter); - float[] cropCenter = { - scaledCrop.centerX(), scaledCrop.centerY() - }; - m1.mapPoints(cropCenter); - GeometryMetadata.concatRecenterMatrix(m1, cropCenter, displayCenter); - m1.preRotate(getStraightenRotation(), scaledPhoto.centerX(), scaledPhoto.centerY()); - m1.preScale(scale, scale); - m1.preScale(preScale, preScale); - m1.preConcat(imageRotation); - - return m1; - } - - public boolean hasSwitchedWidthHeight() { - return (((int) (mRotationRep.getRotation().value() / 90)) % 2) != 0; - } - - public static Matrix buildPhotoMatrix(RectF photo, RectF crop, float rotation, - float straighten, Mirror type) { - Matrix m = new Matrix(); - m.setRotate(straighten, photo.centerX(), photo.centerY()); - concatMirrorMatrix(m, photo.right, photo.bottom, type); - m.postRotate(rotation, crop.centerX(), crop.centerY()); - - return m; - } - - public static Matrix buildCropMatrix(RectF crop, float rotation) { - Matrix m = new Matrix(); - m.setRotate(rotation, crop.centerX(), crop.centerY()); - return m; - } - - public static void concatRecenterMatrix(Matrix m, float[] currentCenter, float[] newCenter) { - m.postTranslate(newCenter[0] - currentCenter[0], newCenter[1] - currentCenter[1]); - } - - /** - * Builds a matrix to transform a bitmap of width bmWidth and height - * bmHeight so that the region of the bitmap being cropped to is oriented - * and centered at displayCenter. - * - * @param bmWidth - * @param bmHeight - * @param displayCenter - * @return - */ - public Matrix buildTotalXform(float bmWidth, float bmHeight, float[] displayCenter) { - RectF rp = getPhotoBounds(); - RectF rc = getPreviewCropBounds(); - float scale = GeometryMath.scale(rp.width(), rp.height(), bmWidth, bmHeight); - RectF scaledCrop = GeometryMath.scaleRect(rc, scale); - RectF scaledPhoto = GeometryMath.scaleRect(rp, scale); - - // If no crop has been applied, make sure to use the exact size values. - // Multiplying using scale will introduce rounding errors that modify - // even un-cropped images. - if (rc.left == 0 && rc.right == rp.right) { - scaledCrop.left = scaledPhoto.left = 0; - scaledCrop.right = scaledPhoto.right = bmWidth; - } - if (rc.top == 0 && rc.bottom == rp.bottom) { - scaledCrop.top = scaledPhoto.top = 0; - scaledCrop.bottom = scaledPhoto.bottom = bmHeight; - } - - Matrix m1 = GeometryMetadata.buildWanderingCropMatrix(scaledPhoto, scaledCrop, - getRotation(), getStraightenRotation(), - getMirrorType(), displayCenter); - float[] cropCenter = { - scaledCrop.centerX(), scaledCrop.centerY() - }; - m1.mapPoints(cropCenter); - - GeometryMetadata.concatRecenterMatrix(m1, cropCenter, displayCenter); - m1.preRotate(getStraightenRotation(), scaledPhoto.centerX(), - scaledPhoto.centerY()); - return m1; - } - - /** - * Builds a matrix that rotates photo rect about it's center by the - * straighten angle, mirrors it about the crop center, and rotates it about - * the crop center by the rotation angle, and re-centers the photo rect. - * - * @param photo - * @param crop - * @param rotation - * @param straighten - * @param type - * @param newCenter - * @return - */ - public static Matrix buildCenteredPhotoMatrix(RectF photo, RectF crop, float rotation, - float straighten, Mirror type, float[] newCenter) { - Matrix m = buildPhotoMatrix(photo, crop, rotation, straighten, type); - float[] center = { - photo.centerX(), photo.centerY() - }; - m.mapPoints(center); - concatRecenterMatrix(m, center, newCenter); - return m; - } - - /** - * Builds a matrix that rotates a crop rect about it's center by rotation - * angle, then re-centers the crop rect. - * - * @param crop - * @param rotation - * @param newCenter - * @return - */ - public static Matrix buildCenteredCropMatrix(RectF crop, float rotation, float[] newCenter) { - Matrix m = buildCropMatrix(crop, rotation); - float[] center = { - crop.centerX(), crop.centerY() - }; - m.mapPoints(center); - concatRecenterMatrix(m, center, newCenter); - return m; - } - - /** - * Builds a matrix that transforms the crop rect to its view coordinates - * inside the photo rect. - * - * @param photo - * @param crop - * @param rotation - * @param straighten - * @param type - * @param newCenter - * @return - */ - public static Matrix buildWanderingCropMatrix(RectF photo, RectF crop, float rotation, - float straighten, Mirror type, float[] newCenter) { - Matrix m = buildCenteredPhotoMatrix(photo, crop, rotation, straighten, type, newCenter); - m.preRotate(-straighten, photo.centerX(), photo.centerY()); - return m; - } - - @Override - public void useParametersFrom(FilterRepresentation a) { - GeometryMetadata data = (GeometryMetadata) a; - set(data); - } - - @Override - public FilterRepresentation copy() { - GeometryMetadata representation = new GeometryMetadata(); - copyAllParameters(representation); - return representation; - } - - @Override - protected void copyAllParameters(FilterRepresentation representation) { - super.copyAllParameters(representation); - representation.useParametersFrom(this); - } - - @Override - public void serializeRepresentation(JsonWriter writer) throws IOException { - writer.beginObject(); - writer.name(FilterRotateRepresentation.SERIALIZATION_NAME); - mRotationRep.serializeRepresentation(writer); - writer.name(FilterMirrorRepresentation.SERIALIZATION_NAME); - mMirrorRep.serializeRepresentation(writer); - writer.name(FilterStraightenRepresentation.SERIALIZATION_NAME); - mStraightenRep.serializeRepresentation(writer); - writer.name(FilterCropRepresentation.SERIALIZATION_NAME); - mCropRep.serializeRepresentation(writer); - writer.name(SERIALIZATION_VALUE_SCALE).value(mScaleFactor); - writer.endObject(); - } - - @Override - public void deSerializeRepresentation(JsonReader reader) throws IOException { - reader.beginObject(); - while (reader.hasNext()) { - String name = reader.nextName(); - if (FilterRotateRepresentation.SERIALIZATION_NAME.equals(name)) { - mRotationRep.deSerializeRepresentation(reader); - } else if (FilterMirrorRepresentation.SERIALIZATION_NAME.equals(name)) { - mMirrorRep.deSerializeRepresentation(reader); - } else if (FilterStraightenRepresentation.SERIALIZATION_NAME.equals(name)) { - mStraightenRep.deSerializeRepresentation(reader); - } else if (FilterCropRepresentation.SERIALIZATION_NAME.equals(name)) { - mCropRep.deSerializeRepresentation(reader); - } else if (SERIALIZATION_VALUE_SCALE.equals(name)) { - mScaleFactor = (float) reader.nextDouble(); - } else { - reader.skipValue(); - } - } - reader.endObject(); - } -} diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java index 0c440654c..7fee03188 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,712 +20,288 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.widget.LinearLayout; -import android.widget.PopupMenu; +import android.util.Log; +import android.view.MotionEvent; import com.android.gallery3d.R; -import com.android.gallery3d.filtershow.crop.BoundedRect; -import com.android.gallery3d.filtershow.crop.CropExtras; +import com.android.gallery3d.filtershow.crop.CropDrawingUtils; import com.android.gallery3d.filtershow.crop.CropMath; +import com.android.gallery3d.filtershow.crop.CropObject; import com.android.gallery3d.filtershow.editors.EditorCrop; -import com.android.gallery3d.filtershow.ui.FramedTextButton; - -public class ImageCrop extends ImageGeometry { - private static final boolean LOGV = false; - - // Sides - private static final int MOVE_LEFT = 1; - private static final int MOVE_TOP = 2; - private static final int MOVE_RIGHT = 4; - private static final int MOVE_BOTTOM = 8; - private static final int MOVE_BLOCK = 16; - - // Corners - private static final int TOP_LEFT = MOVE_TOP | MOVE_LEFT; - private static final int TOP_RIGHT = MOVE_TOP | MOVE_RIGHT; - private static final int BOTTOM_RIGHT = MOVE_BOTTOM | MOVE_RIGHT; - private static final int BOTTOM_LEFT = MOVE_BOTTOM | MOVE_LEFT; - - private static int mMinSideSize = 100; - private static int mTouchTolerance = 45; - - private boolean mFirstDraw = true; - private float mAspectWidth = 1; - private float mAspectHeight = 1; - private boolean mFixAspectRatio = false; - - private float mLastRot = 0; - - private BoundedRect mBounded = null; - private int movingEdges; - private final Drawable cropIndicator; - private final int indicatorSize; - private final int mBorderColor = Color.argb(128, 255, 255, 255); - - // Offset between crop center and photo center - private float[] mOffset = { - 0, 0 - }; - private CropExtras mCropExtras = null; - private boolean mDoingCropIntentAction = false; - - private static final String LOGTAG = "ImageCrop"; - - private String mAspect = ""; - private static int mAspectTextSize = 24; - - private boolean mFixedAspect = false; - - private EditorCrop mEditorCrop; - - public static void setAspectTextSize(int textSize) { - mAspectTextSize = textSize; - } - - public void setAspectString(String a) { - mAspect = a; - } - - private static final Paint gPaint = new Paint(); +import com.android.gallery3d.filtershow.filters.FilterCropRepresentation; +import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils.GeometryHolder; + +public class ImageCrop extends ImageShow { + private static final String TAG = ImageCrop.class.getSimpleName(); + private RectF mImageBounds = new RectF(); + private RectF mScreenCropBounds = new RectF(); + private Paint mPaint = new Paint(); + private CropObject mCropObj = null; + private GeometryHolder mGeometry = new GeometryHolder(); + private GeometryHolder mUpdateHolder = new GeometryHolder(); + private Drawable mCropIndicator; + private int mIndicatorSize; + private boolean mMovingBlock = false; + private Matrix mDisplayMatrix = null; + private Matrix mDisplayCropMatrix = null; + private Matrix mDisplayMatrixInverse = null; + private float mPrevX = 0; + private float mPrevY = 0; + private int mMinSideSize = 90; + private int mTouchTolerance = 40; + private enum Mode { + NONE, MOVE + } + private Mode mState = Mode.NONE; + private boolean mValidDraw = false; + FilterCropRepresentation mLocalRep = new FilterCropRepresentation(); + EditorCrop mEditorCrop; public ImageCrop(Context context) { super(context); - Resources resources = context.getResources(); - cropIndicator = resources.getDrawable(R.drawable.camera_crop); - indicatorSize = (int) resources.getDimension(R.dimen.crop_indicator_size); + setup(context); } public ImageCrop(Context context, AttributeSet attrs) { super(context, attrs); - Resources resources = context.getResources(); - cropIndicator = resources.getDrawable(R.drawable.camera_crop); - indicatorSize = (int) resources.getDimension(R.dimen.crop_indicator_size); + setup(context); } - private void swapAspect() { - if (mDoingCropIntentAction) { - return; - } - float temp = mAspectWidth; - mAspectWidth = mAspectHeight; - mAspectHeight = temp; + public ImageCrop(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setup(context); } - /** - * Set tolerance for crop marker selection (in pixels) - */ - public static void setTouchTolerance(int tolerance) { - mTouchTolerance = tolerance; + private void setup(Context context) { + Resources rsc = context.getResources(); + mCropIndicator = rsc.getDrawable(R.drawable.camera_crop); + mIndicatorSize = (int) rsc.getDimension(R.dimen.crop_indicator_size); + mMinSideSize = (int) rsc.getDimension(R.dimen.crop_min_side); + mTouchTolerance = (int) rsc.getDimension(R.dimen.crop_touch_tolerance); } - /** - * Set minimum side length for crop box (in pixels) - */ - public static void setMinCropSize(int minHeightWidth) { - mMinSideSize = minHeightWidth; + public void setFilterCropRepresentation(FilterCropRepresentation crop) { + mLocalRep = (crop == null) ? new FilterCropRepresentation() : crop; + GeometryMathUtils.initializeHolder(mUpdateHolder, mLocalRep); + mValidDraw = true; } - public void setExtras(CropExtras e) { - mCropExtras = e; + public FilterCropRepresentation getFinalRepresentation() { + return mLocalRep; } - public void setCropActionFlag(boolean f) { - mDoingCropIntentAction = f; + private void internallyUpdateLocalRep(RectF crop, RectF image) { + FilterCropRepresentation + .findNormalizedCrop(crop, (int) image.width(), (int) image.height()); + mGeometry.crop.set(crop); + mUpdateHolder.set(mGeometry); + mLocalRep.setCrop(crop); } - public void apply(float w, float h) { - mFixAspectRatio = true; - mAspectWidth = w; - mAspectHeight = h; - setLocalCropBounds(getUntranslatedStraightenCropBounds(getLocalPhotoBounds(), - getLocalStraighten())); - cropSetup(); - saveAndSetPreset(); + @Override + public boolean onTouchEvent(MotionEvent event) { + float x = event.getX(); + float y = event.getY(); + if (mDisplayMatrix == null || mDisplayMatrixInverse == null) { + return true; + } + float[] touchPoint = { + x, y + }; + mDisplayMatrixInverse.mapPoints(touchPoint); + x = touchPoint[0]; + y = touchPoint[1]; + switch (event.getActionMasked()) { + case (MotionEvent.ACTION_DOWN): + if (mState == Mode.NONE) { + if (!mCropObj.selectEdge(x, y)) { + mMovingBlock = mCropObj.selectEdge(CropObject.MOVE_BLOCK); + } + mPrevX = x; + mPrevY = y; + mState = Mode.MOVE; + } + break; + case (MotionEvent.ACTION_UP): + if (mState == Mode.MOVE) { + mCropObj.selectEdge(CropObject.MOVE_NONE); + mMovingBlock = false; + mPrevX = x; + mPrevY = y; + mState = Mode.NONE; + internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds()); + } + break; + case (MotionEvent.ACTION_MOVE): + if (mState == Mode.MOVE) { + float dx = x - mPrevX; + float dy = y - mPrevY; + mCropObj.moveCurrentSelection(dx, dy); + mPrevX = x; + mPrevY = y; + } + break; + default: + break; + } invalidate(); + return true; } - public void applyOriginal() { - mFixAspectRatio = true; - RectF photobounds = getLocalPhotoBounds(); - float w = photobounds.width(); - float h = photobounds.height(); - float scale = Math.min(w, h); - mAspectWidth = w / scale; - mAspectHeight = h / scale; - setLocalCropBounds(getUntranslatedStraightenCropBounds(photobounds, - getLocalStraighten())); - cropSetup(); - saveAndSetPreset(); + private void clearDisplay() { + mDisplayMatrix = null; + mDisplayMatrixInverse = null; invalidate(); } - public void applyClear() { - mFixAspectRatio = false; - mAspectWidth = 1; - mAspectHeight = 1; - setLocalCropBounds(getUntranslatedStraightenCropBounds(getLocalPhotoBounds(), - getLocalStraighten())); - cropSetup(); - saveAndSetPreset(); + public void applyFreeAspect() { + mCropObj.unsetAspectRatio(); invalidate(); } - public void clear() { - if (mCropExtras != null) { - int x = mCropExtras.getAspectX(); - int y = mCropExtras.getAspectY(); - if (mDoingCropIntentAction && x > 0 && y > 0) { - apply(x, y); - } + public void applyOriginalAspect() { + RectF outer = mCropObj.getOuterBounds(); + float w = outer.width(); + float h = outer.height(); + if (w > 0 && h > 0) { + applyAspect(w, h); + mCropObj.resetBoundsTo(outer, outer); + internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds()); } else { - applyClear(); + Log.w(TAG, "failed to set aspect ratio original"); } - } - - private Matrix getPhotoBoundDisplayedMatrix() { - float[] displayCenter = new float[2]; - RectF scaledCrop = new RectF(); - RectF scaledPhoto = new RectF(); - float scale = getTransformState(scaledPhoto, scaledCrop, displayCenter); - Matrix m = GeometryMetadata.buildCenteredPhotoMatrix(scaledPhoto, scaledCrop, - getLocalRotation(), getLocalStraighten(), getLocalMirror(), displayCenter); - m.preScale(scale, scale); - return m; - } - - private Matrix getCropBoundDisplayedMatrix() { - float[] displayCenter = new float[2]; - RectF scaledCrop = new RectF(); - RectF scaledPhoto = new RectF(); - float scale = getTransformState(scaledPhoto, scaledCrop, displayCenter); - Matrix m1 = GeometryMetadata.buildWanderingCropMatrix(scaledPhoto, scaledCrop, - getLocalRotation(), getLocalStraighten(), getLocalMirror(), displayCenter); - m1.preScale(scale, scale); - return m1; - } - - /** - * Takes the rotated corners of a rectangle and returns the angle; sets - * unrotated to be the unrotated version of the rectangle. - */ - private static float getUnrotated(float[] rotatedRect, float[] center, RectF unrotated) { - float dy = rotatedRect[1] - rotatedRect[3]; - float dx = rotatedRect[0] - rotatedRect[2]; - float angle = (float) (Math.atan(dy / dx) * 180 / Math.PI); - Matrix m = new Matrix(); - m.setRotate(-angle, center[0], center[1]); - float[] unrotatedRect = new float[rotatedRect.length]; - m.mapPoints(unrotatedRect, rotatedRect); - unrotated.set(CropMath.trapToRect(unrotatedRect)); - return angle; - } - - /** - * Sets cropped bounds; modifies the bounds if it's smaller than the allowed - * dimensions. - */ - public boolean setCropBounds(RectF bounds) { - RectF cbounds = new RectF(bounds); - Matrix mc = getCropBoundDisplayedMatrix(); - Matrix mcInv = new Matrix(); - mc.invert(mcInv); - mcInv.mapRect(cbounds); - // Avoid cropping smaller than minimum - float newWidth = cbounds.width(); - float newHeight = cbounds.height(); - float scale = getTransformState(null, null, null); - float minWidthHeight = mMinSideSize / scale; - RectF pbounds = getLocalPhotoBounds(); - - // if photo is smaller than minimum, refuse to set crop bounds - if (pbounds.width() < minWidthHeight || pbounds.height() < minWidthHeight) { - return false; - } - - // if incoming crop is smaller than minimum, refuse to set crop bounds - if (newWidth < minWidthHeight || newHeight < minWidthHeight) { - return false; - } - - float newX = bounds.centerX() - (getWidth() / 2f); - float newY = bounds.centerY() - (getHeight() / 2f); - mOffset[0] = newX; - mOffset[1] = newY; - - setLocalCropBounds(cbounds); invalidate(); - return true; - } - - private BoundedRect getBoundedCrop(RectF crop) { - RectF photo = getLocalPhotoBounds(); - Matrix mp = getPhotoBoundDisplayedMatrix(); - float[] photoCorners = CropMath.getCornersFromRect(photo); - float[] photoCenter = { - photo.centerX(), photo.centerY() - }; - mp.mapPoints(photoCorners); - mp.mapPoints(photoCenter); - RectF scaledPhoto = new RectF(); - float angle = getUnrotated(photoCorners, photoCenter, scaledPhoto); - return new BoundedRect(angle, scaledPhoto, crop); } - private void detectMovingEdges(float x, float y) { - Matrix m = getCropBoundDisplayedMatrix(); - RectF cropped = getLocalCropBounds(); - m.mapRect(cropped); - mBounded = getBoundedCrop(cropped); - movingEdges = 0; - - float left = Math.abs(x - cropped.left); - float right = Math.abs(x - cropped.right); - float top = Math.abs(y - cropped.top); - float bottom = Math.abs(y - cropped.bottom); - - // Check left or right. - if ((left <= mTouchTolerance) && ((y + mTouchTolerance) >= cropped.top) - && ((y - mTouchTolerance) <= cropped.bottom) && (left < right)) { - movingEdges |= MOVE_LEFT; - } - else if ((right <= mTouchTolerance) && ((y + mTouchTolerance) >= cropped.top) - && ((y - mTouchTolerance) <= cropped.bottom)) { - movingEdges |= MOVE_RIGHT; - } - - // Check top or bottom. - if ((top <= mTouchTolerance) && ((x + mTouchTolerance) >= cropped.left) - && ((x - mTouchTolerance) <= cropped.right) && (top < bottom)) { - movingEdges |= MOVE_TOP; - } - else if ((bottom <= mTouchTolerance) && ((x + mTouchTolerance) >= cropped.left) - && ((x - mTouchTolerance) <= cropped.right)) { - movingEdges |= MOVE_BOTTOM; + public void applyAspect(float x, float y) { + if (x <= 0 || y <= 0) { + throw new IllegalArgumentException("Bad arguments to applyAspect"); } - if (movingEdges == 0) { - movingEdges = MOVE_BLOCK; + // If we are rotated by 90 degrees from horizontal, swap x and y + if (GeometryMathUtils.needsDimensionSwap(mGeometry.rotation)) { + float tmp = x; + x = y; + y = tmp; } - if (mFixAspectRatio && (movingEdges != MOVE_BLOCK)) { - movingEdges = fixEdgeToCorner(movingEdges); + if (!mCropObj.setInnerAspectRatio(x, y)) { + Log.w(TAG, "failed to set aspect ratio"); } + internallyUpdateLocalRep(mCropObj.getInnerBounds(), mCropObj.getOuterBounds()); invalidate(); } - private int fixEdgeToCorner(int moving_edges) { - if (moving_edges == MOVE_LEFT) { - moving_edges |= MOVE_TOP; - } - if (moving_edges == MOVE_TOP) { - moving_edges |= MOVE_LEFT; - } - if (moving_edges == MOVE_RIGHT) { - moving_edges |= MOVE_BOTTOM; - } - if (moving_edges == MOVE_BOTTOM) { - moving_edges |= MOVE_RIGHT; - } - return moving_edges; - } - - private RectF fixedCornerResize(RectF r, int moving_corner, float dx, float dy) { - RectF newCrop = null; - // Fix opposite corner in place and move sides - if (moving_corner == BOTTOM_RIGHT) { - newCrop = new RectF(r.left, r.top, r.left + r.width() + dx, r.top + r.height() - + dy); - } else if (moving_corner == BOTTOM_LEFT) { - newCrop = new RectF(r.right - r.width() + dx, r.top, r.right, r.top + r.height() - + dy); - } else if (moving_corner == TOP_LEFT) { - newCrop = new RectF(r.right - r.width() + dx, r.bottom - r.height() + dy, - r.right, r.bottom); - } else if (moving_corner == TOP_RIGHT) { - newCrop = new RectF(r.left, r.bottom - r.height() + dy, r.left - + r.width() + dx, r.bottom); - } - return newCrop; + /** + * Rotates first d bits in integer x to the left some number of times. + */ + private int bitCycleLeft(int x, int times, int d) { + int mask = (1 << d) - 1; + int mout = x & mask; + times %= d; + int hi = mout >> (d - times); + int low = (mout << times) & mask; + int ret = x & ~mask; + ret |= low; + ret |= hi; + return ret; } - private void moveEdges(float dX, float dY) { - RectF crop = mBounded.getInner(); - - Matrix mc = getCropBoundDisplayedMatrix(); - - RectF photo = getLocalPhotoBounds(); - Matrix mp = getPhotoBoundDisplayedMatrix(); - float[] photoCorners = CropMath.getCornersFromRect(photo); - float[] photoCenter = { - photo.centerX(), photo.centerY() - }; - mp.mapPoints(photoCorners); - mp.mapPoints(photoCenter); - - float minWidthHeight = mMinSideSize; - - if (movingEdges == MOVE_BLOCK) { - mBounded.moveInner(-dX, -dY); - RectF r = mBounded.getInner(); - setCropBounds(r); - return; - } else { - float dx = 0; - float dy = 0; - - if ((movingEdges & MOVE_LEFT) != 0) { - dx = Math.min(crop.left + dX, crop.right - minWidthHeight) - crop.left; - } - if ((movingEdges & MOVE_TOP) != 0) { - dy = Math.min(crop.top + dY, crop.bottom - minWidthHeight) - crop.top; - } - if ((movingEdges & MOVE_RIGHT) != 0) { - dx = Math.max(crop.right + dX, crop.left + minWidthHeight) - - crop.right; - } - if ((movingEdges & MOVE_BOTTOM) != 0) { - dy = Math.max(crop.bottom + dY, crop.top + minWidthHeight) - - crop.bottom; - } - - if (mFixAspectRatio) { - float[] l1 = { - crop.left, crop.bottom - }; - float[] l2 = { - crop.right, crop.top - }; - if (movingEdges == TOP_LEFT || movingEdges == BOTTOM_RIGHT) { - l1[1] = crop.top; - l2[1] = crop.bottom; - } - float[] b = { - l1[0] - l2[0], l1[1] - l2[1] - }; - float[] disp = { - dx, dy - }; - float[] bUnit = GeometryMath.normalize(b); - float sp = GeometryMath.scalarProjection(disp, bUnit); - dx = sp * bUnit[0]; - dy = sp * bUnit[1]; - RectF newCrop = fixedCornerResize(crop, movingEdges, dx, dy); - - mBounded.fixedAspectResizeInner(newCrop); - newCrop = mBounded.getInner(); - setCropBounds(newCrop); - return; - } else { - if ((movingEdges & MOVE_LEFT) != 0) { - crop.left += dx; - } - if ((movingEdges & MOVE_TOP) != 0) { - crop.top += dy; - } - if ((movingEdges & MOVE_RIGHT) != 0) { - crop.right += dx; - } - if ((movingEdges & MOVE_BOTTOM) != 0) { - crop.bottom += dy; - } - } + /** + * Find the selected edge or corner in screen coordinates. + */ + private int decode(int movingEdges, float rotation) { + int rot = CropMath.constrainedRotation(rotation); + switch (rot) { + case 90: + return bitCycleLeft(movingEdges, 1, 4); + case 180: + return bitCycleLeft(movingEdges, 2, 4); + case 270: + return bitCycleLeft(movingEdges, 3, 4); + default: + return movingEdges; } - mBounded.resizeInner(crop); - crop = mBounded.getInner(); - setCropBounds(crop); - } - - private void drawIndicator(Canvas canvas, Drawable indicator, float centerX, float centerY) { - int left = (int) centerX - indicatorSize / 2; - int top = (int) centerY - indicatorSize / 2; - indicator.setBounds(left, top, left + indicatorSize, top + indicatorSize); - indicator.draw(canvas); } - @Override - protected void setActionDown(float x, float y) { - super.setActionDown(x, y); - detectMovingEdges(x + mOffset[0], y + mOffset[1]); - - } - - @Override - protected void setActionUp() { - super.setActionUp(); - movingEdges = 0; - } - - @Override - protected void setActionMove(float x, float y) { - - if (movingEdges != 0) { - moveEdges(x - mCurrentX, y - mCurrentY); + private void forceStateConsistency() { + MasterImage master = MasterImage.getImage(); + Bitmap image = master.getFiltersOnlyImage(); + int width = image.getWidth(); + int height = image.getHeight(); + if (mCropObj == null || !mUpdateHolder.equals(mGeometry) + || mImageBounds.width() != width || mImageBounds.height() != height + || !mLocalRep.getCrop().equals(mUpdateHolder.crop)) { + mImageBounds.set(0, 0, width, height); + mGeometry.set(mUpdateHolder); + mLocalRep.setCrop(mUpdateHolder.crop); + RectF scaledCrop = new RectF(mUpdateHolder.crop); + FilterCropRepresentation.findScaledCrop(scaledCrop, width, height); + mCropObj = new CropObject(mImageBounds, scaledCrop, (int) mUpdateHolder.straighten); + mState = Mode.NONE; + clearDisplay(); } - super.setActionMove(x, y); - } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { - setActionUp(); - cropSetup(); - invalidate(); - } - - private void cropSetup() { - RectF crop = getLocalCropBounds(); - Matrix m = getCropBoundDisplayedMatrix(); - m.mapRect(crop); - if (mFixAspectRatio) { - CropMath.fixAspectRatio(crop, mAspectWidth, mAspectHeight); - } - float dCentX = getWidth() / 2; - float dCentY = getHeight() / 2; - - BoundedRect r = getBoundedCrop(crop); - crop = r.getInner(); - if (!setCropBounds(crop)) { - float h = mMinSideSize / 2; - float wScale = 1; - float hScale = mAspectHeight / mAspectWidth; - if (hScale < 1) { - wScale = mAspectWidth / mAspectHeight; - hScale = 1; - } - crop.set(dCentX - h * wScale, dCentY - h * hScale, dCentX + h * wScale, dCentY + h - * hScale); - if (mFixAspectRatio) { - CropMath.fixAspectRatio(crop, mAspectWidth, mAspectHeight); - } - r.setInner(crop); - crop = r.getInner(); - if (!setCropBounds(crop)) { - crop.set(dCentX - h, dCentY - h, dCentX + h, dCentY + h); - r.setInner(crop); - crop = r.getInner(); - setCropBounds(crop); - } - } - } - - @Override - public void imageLoaded() { - super.imageLoaded(); - syncLocalToMasterGeometry(); - clear(); - invalidate(); + super.onSizeChanged(w, h, oldw, oldh); + clearDisplay(); } @Override - protected void gainedVisibility() { - float rot = getLocalRotation(); - // if has changed orientation via rotate - if (((int) ((rot - mLastRot) / 90)) % 2 != 0) { - swapAspect(); - } - cropSetup(); - mFirstDraw = true; - } - - @Override - public void resetParameter() { - super.resetParameter(); - } - - @Override - protected void lostVisibility() { - mLastRot = getLocalRotation(); - } - - private void drawRuleOfThird(Canvas canvas, RectF bounds, Paint p) { - float stepX = bounds.width() / 3.0f; - float stepY = bounds.height() / 3.0f; - float x = bounds.left + stepX; - float y = bounds.top + stepY; - for (int i = 0; i < 2; i++) { - canvas.drawLine(x, bounds.top, x, bounds.bottom, p); - x += stepX; - } - for (int j = 0; j < 2; j++) { - canvas.drawLine(bounds.left, y, bounds.right, y, p); - y += stepY; - } - } - - @Override - protected void drawShape(Canvas canvas, Bitmap image) { - gPaint.setAntiAlias(true); - gPaint.setARGB(255, 255, 255, 255); - - if (mFirstDraw) { - cropSetup(); - mFirstDraw = false; - } - - RectF crop = drawTransformed(canvas, image, gPaint, mOffset); - gPaint.setColor(mBorderColor); - gPaint.setStrokeWidth(3); - gPaint.setStyle(Paint.Style.STROKE); - - boolean doThirds = true; - - if (mFixAspectRatio) { - float spotlightX = 0; - float spotlightY = 0; - if (mCropExtras != null) { - spotlightX = mCropExtras.getSpotlightX(); - spotlightY = mCropExtras.getSpotlightY(); - } - if (mDoingCropIntentAction && spotlightX > 0 && spotlightY > 0) { - float sx = crop.width() * spotlightX; - float sy = crop.height() * spotlightY; - float cx = crop.centerX(); - float cy = crop.centerY(); - RectF r1 = new RectF(cx - sx / 2, cy - sy / 2, cx + sx / 2, cy + sy / 2); - float temp = sx; - sx = sy; - sy = temp; - RectF r2 = new RectF(cx - sx / 2, cy - sy / 2, cx + sx / 2, cy + sy / 2); - canvas.drawRect(r1, gPaint); - canvas.drawRect(r2, gPaint); - doThirds = false; - } else { - float w = crop.width(); - float h = crop.height(); - float diag = (float) Math.sqrt(w * w + h * h); - - float dash_len = 20; - int num_intervals = (int) (diag / dash_len); - float[] tl = { - crop.left, crop.top - }; - float centX = tl[0] + w / 2; - float centY = tl[1] + h / 2 + 5; - float[] br = { - crop.right, crop.bottom - }; - float[] vec = GeometryMath.getUnitVectorFromPoints(tl, br); - - float[] counter = tl; - for (int x = 0; x < num_intervals; x++) { - float tempX = counter[0] + vec[0] * dash_len; - float tempY = counter[1] + vec[1] * dash_len; - if ((x % 2) == 0 && Math.abs(x - num_intervals / 2) > 2) { - canvas.drawLine(counter[0], counter[1], tempX, tempY, gPaint); - } - counter[0] = tempX; - counter[1] = tempY; - } - - gPaint.setTextAlign(Paint.Align.CENTER); - gPaint.setTextSize(mAspectTextSize); - canvas.drawText(mAspect, centX, centY, gPaint); - } - } - - if (doThirds) { - drawRuleOfThird(canvas, crop, gPaint); - + public void onDraw(Canvas canvas) { + Bitmap bitmap = MasterImage.getImage().getFiltersOnlyImage(); + if (!mValidDraw || bitmap == null) { + return; } - - RectF scaledCrop = crop; - boolean notMoving = (movingEdges == 0); - if (mFixAspectRatio) { - if ((movingEdges == TOP_LEFT) || notMoving) { - drawIndicator(canvas, cropIndicator, scaledCrop.left, scaledCrop.top); - } - if ((movingEdges == TOP_RIGHT) || notMoving) { - drawIndicator(canvas, cropIndicator, scaledCrop.right, scaledCrop.top); - } - if ((movingEdges == BOTTOM_LEFT) || notMoving) { - drawIndicator(canvas, cropIndicator, scaledCrop.left, scaledCrop.bottom); - } - if ((movingEdges == BOTTOM_RIGHT) || notMoving) { - drawIndicator(canvas, cropIndicator, scaledCrop.right, scaledCrop.bottom); - } - } else { - if (((movingEdges & MOVE_TOP) != 0) || notMoving) { - drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.top); - } - if (((movingEdges & MOVE_BOTTOM) != 0) || notMoving) { - drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.bottom); - } - if (((movingEdges & MOVE_LEFT) != 0) || notMoving) { - drawIndicator(canvas, cropIndicator, scaledCrop.left, scaledCrop.centerY()); - } - if (((movingEdges & MOVE_RIGHT) != 0) || notMoving) { - drawIndicator(canvas, cropIndicator, scaledCrop.right, scaledCrop.centerY()); + forceStateConsistency(); + mImageBounds.set(0, 0, bitmap.getWidth(), bitmap.getHeight()); + // If display matrix doesn't exist, create it and its dependencies + if (mDisplayCropMatrix == null || mDisplayMatrix == null || mDisplayMatrixInverse == null) { + mDisplayMatrix = GeometryMathUtils.getFullGeometryToScreenMatrix(mGeometry, + bitmap.getWidth(), bitmap.getHeight(), canvas.getWidth(), canvas.getHeight()); + float straighten = mGeometry.straighten; + mGeometry.straighten = 0; + mDisplayCropMatrix = GeometryMathUtils.getFullGeometryToScreenMatrix(mGeometry, + bitmap.getWidth(), bitmap.getHeight(), canvas.getWidth(), canvas.getHeight()); + mGeometry.straighten = straighten; + mDisplayMatrixInverse = new Matrix(); + mDisplayMatrixInverse.reset(); + if (!mDisplayCropMatrix.invert(mDisplayMatrixInverse)) { + Log.w(TAG, "could not invert display matrix"); + mDisplayMatrixInverse = null; + return; } + // Scale min side and tolerance by display matrix scale factor + mCropObj.setMinInnerSideSize(mDisplayMatrixInverse.mapRadius(mMinSideSize)); + mCropObj.setTouchTolerance(mDisplayMatrixInverse.mapRadius(mTouchTolerance)); } - } - - public void setAspectButton(int itemId) { - switch (itemId) { - case R.id.crop_menu_1to1: { - String t = getActivity().getString(R.string.aspect1to1_effect); - apply(1, 1); - setAspectString(t); - break; - } - case R.id.crop_menu_4to3: { - String t = getActivity().getString(R.string.aspect4to3_effect); - apply(4, 3); - setAspectString(t); - break; - } - case R.id.crop_menu_3to4: { - String t = getActivity().getString(R.string.aspect3to4_effect); - apply(3, 4); - setAspectString(t); - break; - } - case R.id.crop_menu_5to7: { - String t = getActivity().getString(R.string.aspect5to7_effect); - apply(5, 7); - setAspectString(t); - break; - } - case R.id.crop_menu_7to5: { - String t = getActivity().getString(R.string.aspect7to5_effect); - apply(7, 5); - setAspectString(t); - break; - } - case R.id.crop_menu_none: { - String t = getActivity().getString(R.string.aspectNone_effect); - applyClear(); - setAspectString(t); - break; - } - case R.id.crop_menu_original: { - String t = getActivity().getString(R.string.aspectOriginal_effect); - applyOriginal(); - setAspectString(t); - break; - } + // Draw actual bitmap + mPaint.reset(); + mPaint.setAntiAlias(true); + mPaint.setFilterBitmap(true); + canvas.drawBitmap(bitmap, mDisplayMatrix, mPaint); + mCropObj.getInnerBounds(mScreenCropBounds); + RectF outer = mCropObj.getOuterBounds(); + FilterCropRepresentation.findNormalizedCrop(mScreenCropBounds, (int) outer.width(), + (int) outer.height()); + FilterCropRepresentation.findScaledCrop(mScreenCropBounds, bitmap.getWidth(), + bitmap.getHeight()); + if (mDisplayCropMatrix.mapRect(mScreenCropBounds)) { + // Draw crop rect and markers + CropDrawingUtils.drawCropRect(canvas, mScreenCropBounds); + CropDrawingUtils.drawRuleOfThird(canvas, mScreenCropBounds); + CropDrawingUtils.drawIndicators(canvas, mCropIndicator, mIndicatorSize, + mScreenCropBounds, mCropObj.isFixedAspect(), + decode(mCropObj.getSelectState(), mGeometry.rotation.value())); } - invalidate(); - } - - public void setFixedAspect(boolean fixedAspect) { - mFixedAspect = fixedAspect; - } - - @Override - public boolean useUtilityPanel() { - // Only shows the aspect ratio popup if we are not fixed - return !mFixedAspect; } public void setEditor(EditorCrop editorCrop) { mEditorCrop = editorCrop; } - } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageDraw.java b/src/com/android/gallery3d/filtershow/imageshow/ImageDraw.java index 67a093bfb..9722034e0 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageDraw.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageDraw.java @@ -72,12 +72,6 @@ public class ImageDraw extends ImageShow { return (int) mCurrentSize; } - @Override - public void updateImage() { - super.updateImage(); - invalidate(); - } - float[] mTmpPoint = new float[2]; // so we do not malloc @Override public boolean onTouchEvent(MotionEvent event) { diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageFlip.java b/src/com/android/gallery3d/filtershow/imageshow/ImageFlip.java deleted file mode 100644 index fb39a9aec..000000000 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageFlip.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * 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.android.gallery3d.filtershow.imageshow; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.RectF; -import android.util.AttributeSet; - -import com.android.gallery3d.R; -import com.android.gallery3d.filtershow.editors.EditorFlip; -import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation.Mirror; - -public class ImageFlip extends ImageGeometry { - - private static final Paint gPaint = new Paint(); - private static final float MIN_FLICK_DIST_FOR_FLIP= 0.1f; - private static final String LOGTAG = "ImageFlip"; - private Mirror mNextFlip = Mirror.NONE; - private EditorFlip mEditorFlip; - - public ImageFlip(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public ImageFlip(Context context) { - super(context); - } - - @Override - protected void setActionDown(float x, float y) { - super.setActionDown(x, y); - } - - boolean hasRotated90(){ - int rot = constrainedRotation(getLocalRotation()); - return (rot / 90) % 2 != 0; - } - - public void flip() { - Mirror flip = getLocalMirror(); - boolean next = true; - // Picks next flip in order from enum Mirror (wrapping) - for (Mirror f : Mirror.values()) { - if (next) { - mNextFlip = f; - next = false; - } - if (f.equals(flip)) { - next = true; - } - } - setLocalMirror(mNextFlip); - } - - @Override - protected void setActionMove(float x, float y) { - super.setActionMove(x, y); - - float diffx = mTouchCenterX - x; - float diffy = mTouchCenterY - y; - float flick = getScaledMinFlick(); - if(hasRotated90()){ - float temp = diffx; - diffx = diffy; - diffy = temp; - } - if (Math.abs(diffx) >= flick) { - // flick moving left/right - Mirror flip = getLocalMirror(); - switch (flip) { - case NONE: - flip = Mirror.HORIZONTAL; - break; - case HORIZONTAL: - flip = Mirror.NONE; - break; - case VERTICAL: - flip = Mirror.BOTH; - break; - case BOTH: - flip = Mirror.VERTICAL; - break; - default: - flip = Mirror.NONE; - break; - } - mNextFlip = flip; - } - if (Math.abs(diffy) >= flick) { - // flick moving up/down - Mirror flip = getLocalMirror(); - switch (flip) { - case NONE: - flip = Mirror.VERTICAL; - break; - case VERTICAL: - flip = Mirror.NONE; - break; - case HORIZONTAL: - flip = Mirror.BOTH; - break; - case BOTH: - flip = Mirror.HORIZONTAL; - break; - default: - flip = Mirror.NONE; - break; - } - mNextFlip = flip; - } - } - - @Override - protected void setActionUp() { - super.setActionUp(); - setLocalMirror(mNextFlip); - } - - @Override - public void resetParameter() { - super.resetParameter(); - mNextFlip = Mirror.NONE; - } - - private float getScaledMinFlick() { - RectF disp = getLocalDisplayBounds(); - float scaled = Math.min(disp.width(), disp.height()) * MIN_FLICK_DIST_FOR_FLIP - / getLocalScale(); - return scaled; - } - - @Override - protected void drawShape(Canvas canvas, Bitmap image) { - gPaint.setAntiAlias(true); - gPaint.setARGB(255, 255, 255, 255); - drawTransformedCropped(canvas, image, gPaint); - } - - public void setEditor(EditorFlip editorFlip) { - mEditorFlip = editorFlip; - } - -} diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java deleted file mode 100644 index 7d6c787fe..000000000 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java +++ /dev/null @@ -1,527 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * 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.android.gallery3d.filtershow.imageshow; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Paint.Style; -import android.graphics.Path; -import android.graphics.RectF; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; - -import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation.Mirror; -import com.android.gallery3d.filtershow.filters.FilterRepresentation; -import com.android.gallery3d.filtershow.history.HistoryItem; -import com.android.gallery3d.filtershow.pipeline.ImagePreset; - -public abstract class ImageGeometry extends ImageShow { - protected boolean mVisibilityGained = false; - private boolean mHasDrawn = false; - - protected static final float MAX_STRAIGHTEN_ANGLE = 45; - protected static final float MIN_STRAIGHTEN_ANGLE = -45; - - protected float mCenterX; - protected float mCenterY; - - protected float mCurrentX; - protected float mCurrentY; - protected float mTouchCenterX; - protected float mTouchCenterY; - - // Local geometry data - private GeometryMetadata mLocalGeometry = null; - private RectF mLocalDisplayBounds = null; - protected float mXOffset = 0; - protected float mYOffset = 0; - - protected enum MODES { - NONE, DOWN, UP, MOVE - } - - protected MODES mMode = MODES.NONE; - - private static final String LOGTAG = "ImageGeometry"; - - public ImageGeometry(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public ImageGeometry(Context context) { - super(context); - } - - private void setupLocalDisplayBounds(RectF b) { - mLocalDisplayBounds = b; - calculateLocalScalingFactorAndOffset(); - } - - protected static float angleFor(float dx, float dy) { - return (float) (Math.atan2(dx, dy) * 180 / Math.PI); - } - - protected static int snappedAngle(float angle) { - float remainder = angle % 90; - int current = (int) (angle / 90); // truncates - if (remainder < -45) { - --current; - } else if (remainder > 45) { - ++current; - } - return current * 90; - } - - protected float getCurrentTouchAngle() { - if (mCurrentX == mTouchCenterX && mCurrentY == mTouchCenterY) { - return 0; - } - float dX1 = mTouchCenterX - mCenterX; - float dY1 = mTouchCenterY - mCenterY; - float dX2 = mCurrentX - mCenterX; - float dY2 = mCurrentY - mCenterY; - - float angleA = angleFor(dX1, dY1); - float angleB = angleFor(dX2, dY2); - return (angleB - angleA) % 360; - } - - protected float computeScale(float width, float height) { - float imageWidth = mLocalGeometry.getPhotoBounds().width(); - float imageHeight = mLocalGeometry.getPhotoBounds().height(); - return GeometryMath.scale(imageWidth, imageHeight, width, height); - } - - private void calculateLocalScalingFactorAndOffset() { - if (mLocalGeometry == null || mLocalDisplayBounds == null) - return; - RectF imageBounds = mLocalGeometry.getPhotoBounds(); - float imageWidth = imageBounds.width(); - float imageHeight = imageBounds.height(); - float displayWidth = mLocalDisplayBounds.width(); - float displayHeight = mLocalDisplayBounds.height(); - - mCenterX = displayWidth / 2; - mCenterY = displayHeight / 2; - mYOffset = (displayHeight - imageHeight) / 2.0f; - mXOffset = (displayWidth - imageWidth) / 2.0f; - updateScale(); - } - - @Override - public void resetParameter() { - super.resetParameter(); - setLocalRotation(0); - setLocalStraighten(0); - setLocalCropBounds(getLocalPhotoBounds()); - setLocalMirror(Mirror.NONE); - saveAndSetPreset(); - invalidate(); - } - - // Overwrites local with master - public void syncLocalToMasterGeometry() { - mLocalGeometry = getGeometry(); - calculateLocalScalingFactorAndOffset(); - } - - protected RectF getLocalPhotoBounds() { - return mLocalGeometry.getPhotoBounds(); - } - - protected RectF getLocalCropBounds() { - return mLocalGeometry.getPreviewCropBounds(); - } - - protected RectF getLocalDisplayBounds() { - return new RectF(mLocalDisplayBounds); - } - - protected float getLocalScale() { - return mLocalGeometry.getScaleFactor(); - } - - protected float getLocalRotation() { - return mLocalGeometry.getRotation(); - } - - protected float getLocalStraighten() { - return mLocalGeometry.getStraightenRotation(); - } - - protected void setLocalScale(float s) { - mLocalGeometry.setScaleFactor(s); - } - - protected void updateScale() { - RectF bounds = getUntranslatedStraightenCropBounds(mLocalGeometry.getPhotoBounds(), - getLocalStraighten()); - float zoom = computeScale(bounds.width(), bounds.height()); - setLocalScale(zoom); - } - - protected void setLocalRotation(int r) { - mLocalGeometry.setRotation(r); - updateScale(); - } - - /** - * Constrains rotation to be in [0, 90, 180, 270]. - */ - protected int constrainedRotation(float rotation) { - int r = (int) ((rotation % 360) / 90); - r = (r < 0) ? (r + 4) : r; - return r * 90; - } - - protected boolean isHeightWidthSwapped() { - return ((int) (getLocalRotation() / 90)) % 2 != 0; - } - - protected void setLocalStraighten(float r) { - mLocalGeometry.setStraightenRotation(r); - updateScale(); - } - - protected void setLocalCropBounds(RectF c) { - mLocalGeometry.setCropBounds(c); - updateScale(); - } - - protected Mirror getLocalMirror() { - return mLocalGeometry.getMirrorType(); - } - - protected void setLocalMirror(Mirror flip) { - mLocalGeometry.setMirrorType(flip); - } - - protected float getTotalLocalRotation() { - return getLocalRotation() + getLocalStraighten(); - } - - protected static Path drawClosedPath(Canvas canvas, Paint paint, float[] points) { - Path crop = new Path(); - crop.moveTo(points[0], points[1]); - crop.lineTo(points[2], points[3]); - crop.lineTo(points[4], points[5]); - crop.lineTo(points[6], points[7]); - crop.close(); - canvas.drawPath(crop, paint); - return crop; - } - - protected static float getNewHeightForWidthAspect(float width, float w, float h) { - return width * h / w; - } - - protected static float getNewWidthForHeightAspect(float height, float w, float h) { - return height * w / h; - } - - @Override - protected void onVisibilityChanged(View changedView, int visibility) { - super.onVisibilityChanged(changedView, visibility); - if (visibility == View.VISIBLE) { - mVisibilityGained = true; - MasterImage.getImage().invalidateFiltersOnly(); - syncLocalToMasterGeometry(); - updateScale(); - gainedVisibility(); - } else { - if (mVisibilityGained == true && mHasDrawn == true) { - lostVisibility(); - } - mVisibilityGained = false; - mHasDrawn = false; - } - } - - protected void gainedVisibility() { - // Override this stub. - } - - protected void lostVisibility() { - // Override this stub. - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - setupLocalDisplayBounds(new RectF(0, 0, w, h)); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - switch (event.getActionMasked()) { - case (MotionEvent.ACTION_DOWN): - setActionDown(event.getX(), event.getY()); - break; - case (MotionEvent.ACTION_UP): - setActionUp(); - saveAndSetPreset(); - break; - case (MotionEvent.ACTION_MOVE): - setActionMove(event.getX(), event.getY()); - break; - default: - setNoAction(); - } - invalidate(); - return true; - } - - protected int getLocalValue() { - return 0; // Override this - } - - protected void setActionDown(float x, float y) { - mTouchCenterX = x; - mTouchCenterY = y; - mCurrentX = x; - mCurrentY = y; - mMode = MODES.DOWN; - } - - protected void setActionMove(float x, float y) { - mCurrentX = x; - mCurrentY = y; - mMode = MODES.MOVE; - } - - protected void setActionUp() { - mMode = MODES.UP; - } - - protected void setNoAction() { - mMode = MODES.NONE; - } - - public void saveAndSetPreset() { - FilterRepresentation lastAction = null; - HistoryItem historyItem = MasterImage.getImage().getHistory().getLast(); - if (historyItem != null) { - lastAction = historyItem.getFilterRepresentation(); - } - - if (lastAction != null - && lastAction.getSerializationName().equalsIgnoreCase( - GeometryMetadata.SERIALIZATION_NAME)) { - getImagePreset().setGeometry(mLocalGeometry); - resetImageCaches(this); - } else { - if (mLocalGeometry.hasModifications()) { - ImagePreset copy = new ImagePreset(getImagePreset()); - copy.setGeometry(mLocalGeometry); - // TODO: we should have multiple filter representations for each - // of the different geometry operations we have, otherwise we cannot - // differentiate them in the history/state - MasterImage.getImage().setPreset(copy, copy.getGeometry(), true); - } - } - MasterImage.getImage().notifyGeometryChange(); - invalidate(); - } - - public static RectF getUntranslatedStraightenCropBounds(RectF imageRect, float straightenAngle) { - float deg = straightenAngle; - if (deg < 0) { - deg = -deg; - } - double a = Math.toRadians(deg); - double sina = Math.sin(a); - double cosa = Math.cos(a); - - double rw = imageRect.width(); - double rh = imageRect.height(); - double h1 = rh * rh / (rw * sina + rh * cosa); - double h2 = rh * rw / (rw * cosa + rh * sina); - double hh = Math.min(h1, h2); - double ww = hh * rw / rh; - - float left = (float) ((rw - ww) * 0.5f); - float top = (float) ((rh - hh) * 0.5f); - float right = (float) (left + ww); - float bottom = (float) (top + hh); - - return new RectF(left, top, right, bottom); - } - - protected RectF straightenBounds() { - RectF bounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(), - getLocalStraighten()); - float scale = computeScale(getWidth(), getHeight()); - bounds = GeometryMath.scaleRect(bounds, scale); - float dx = (getWidth() / 2) - bounds.centerX(); - float dy = (getHeight() / 2) - bounds.centerY(); - bounds.offset(dx, dy); - return bounds; - } - - protected static void drawRotatedShadows(Canvas canvas, Paint p, RectF innerBounds, - RectF outerBounds, - float rotation, float centerX, float centerY) { - canvas.save(); - canvas.rotate(rotation, centerX, centerY); - - float x = (outerBounds.left - outerBounds.right); - float y = (outerBounds.top - outerBounds.bottom); - float longest = (float) Math.sqrt(x * x + y * y) / 2; - float minX = centerX - longest; - float maxX = centerX + longest; - float minY = centerY - longest; - float maxY = centerY + longest; - canvas.drawRect(minX, minY, innerBounds.right, innerBounds.top, p); - canvas.drawRect(minX, innerBounds.top, innerBounds.left, maxY, p); - canvas.drawRect(innerBounds.left, innerBounds.bottom, maxX, maxY, - p); - canvas.drawRect(innerBounds.right, minY, maxX, - innerBounds.bottom, p); - canvas.rotate(-rotation, centerX, centerY); - canvas.restore(); - } - - protected void drawShadows(Canvas canvas, Paint p, RectF innerBounds) { - float w = getWidth(); - float h = getHeight(); - canvas.drawRect(0f, 0f, w, innerBounds.top, p); - canvas.drawRect(0f, innerBounds.top, innerBounds.left, innerBounds.bottom, p); - canvas.drawRect(innerBounds.right, innerBounds.top, w, innerBounds.bottom, p); - canvas.drawRect(0f, innerBounds.bottom, w, h, p); - } - - @Override - public void onDraw(Canvas canvas) { - Bitmap image = getFiltersOnlyImage(); - if (image == null) { - invalidate(); - return; - } - mHasDrawn = true; - - drawShape(canvas, image); - } - - protected void drawShape(Canvas canvas, Bitmap image) { - // TODO: Override this stub. - } - - /** - * Sets up inputs for buildCenteredPhotoMatrix and buildWanderingCropMatrix - * and returns the scale factor. - */ - protected float getTransformState(RectF photo, RectF crop, float[] displayCenter) { - RectF photoBounds = getLocalPhotoBounds(); - RectF cropBounds = getLocalCropBounds(); - float scale = computeScale(getWidth(), getHeight()); - // checks if local rotation is an odd multiple of 90. - if (isHeightWidthSwapped()) { - scale = computeScale(getHeight(), getWidth()); - } - // put in screen coordinates - if (crop != null) { - crop.set(GeometryMath.scaleRect(cropBounds, scale)); - } - if (photo != null) { - photo.set(GeometryMath.scaleRect(photoBounds, scale)); - } - if (displayCenter != null && displayCenter.length >= 2) { - displayCenter[0] = getWidth() / 2f; - displayCenter[1] = getHeight() / 2f; - } - return scale; - } - - protected RectF drawTransformed(Canvas canvas, Bitmap photo, Paint p, float[] offset) { - p.setARGB(255, 0, 0, 0); - float[] displayCenter = new float[2]; - RectF scaledCrop = new RectF(); - RectF scaledPhoto = new RectF(); - float scale = getTransformState(scaledPhoto, scaledCrop, displayCenter); - Matrix m = GeometryMetadata.buildCenteredPhotoMatrix(scaledPhoto, scaledCrop, - getLocalRotation(), getLocalStraighten(), getLocalMirror(), displayCenter); - - Matrix m1 = GeometryMetadata.buildWanderingCropMatrix(scaledPhoto, scaledCrop, - getLocalRotation(), getLocalStraighten(), getLocalMirror(), displayCenter); - m1.mapRect(scaledCrop); - Path path = new Path(); - scaledCrop.offset(-offset[0], -offset[1]); - path.addRect(scaledCrop, Path.Direction.CCW); - - m.preScale(scale, scale); - m.postTranslate(-offset[0], -offset[1]); - canvas.save(); - canvas.drawBitmap(photo, m, p); - canvas.restore(); - - p.setColor(Color.WHITE); - p.setStyle(Style.STROKE); - p.setStrokeWidth(2); - canvas.drawPath(path, p); - - p.setColor(mBackgroundColor); - p.setAlpha(128); - p.setStyle(Paint.Style.FILL); - drawShadows(canvas, p, scaledCrop); - return scaledCrop; - } - - protected void drawTransformedCropped(Canvas canvas, Bitmap photo, Paint p) { - RectF photoBounds = getLocalPhotoBounds(); - RectF cropBounds = getLocalCropBounds(); - float imageWidth = cropBounds.width(); - float imageHeight = cropBounds.height(); - float scale = GeometryMath.scale(imageWidth, imageHeight, getWidth(), getHeight()); - // checks if local rotation is an odd multiple of 90. - if (isHeightWidthSwapped()) { - scale = GeometryMath.scale(imageWidth, imageHeight, getHeight(), getWidth()); - } - // put in screen coordinates - RectF scaledCrop = GeometryMath.scaleRect(cropBounds, scale); - RectF scaledPhoto = GeometryMath.scaleRect(photoBounds, scale); - float[] displayCenter = { - getWidth() / 2f, getHeight() / 2f - }; - Matrix m1 = GeometryMetadata.buildWanderingCropMatrix(scaledPhoto, scaledCrop, - getLocalRotation(), getLocalStraighten(), getLocalMirror(), displayCenter); - float[] cropCenter = { - scaledCrop.centerX(), scaledCrop.centerY() - }; - m1.mapPoints(cropCenter); - GeometryMetadata.concatRecenterMatrix(m1, cropCenter, displayCenter); - m1.preRotate(getLocalStraighten(), scaledPhoto.centerX(), scaledPhoto.centerY()); - m1.preScale(scale, scale); - - p.setARGB(255, 0, 0, 0); - canvas.save(); - canvas.drawBitmap(photo, m1, p); - canvas.restore(); - - p.setColor(mBackgroundColor); - p.setStyle(Paint.Style.FILL); - scaledCrop.offset(displayCenter[0] - scaledCrop.centerX(), displayCenter[1] - - scaledCrop.centerY()); - RectF display = new RectF(0, 0, getWidth(), getHeight()); - drawRotatedShadows(canvas, p, scaledCrop, display, getLocalRotation(), getWidth() / 2, - getHeight() / 2); - } -} diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageMirror.java b/src/com/android/gallery3d/filtershow/imageshow/ImageMirror.java new file mode 100644 index 000000000..26c49b1a8 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageMirror.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * 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.android.gallery3d.filtershow.imageshow; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.util.AttributeSet; +import android.view.MotionEvent; + +import com.android.gallery3d.filtershow.editors.EditorMirror; +import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation; +import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils.GeometryHolder; + +public class ImageMirror extends ImageShow { + private static final String TAG = ImageMirror.class.getSimpleName(); + private EditorMirror mEditorMirror; + private FilterMirrorRepresentation mLocalRep = new FilterMirrorRepresentation(); + private GeometryHolder mDrawHolder = new GeometryHolder(); + + public ImageMirror(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ImageMirror(Context context) { + super(context); + } + + public void setFilterMirrorRepresentation(FilterMirrorRepresentation rep) { + mLocalRep = (rep == null) ? new FilterMirrorRepresentation() : rep; + } + + public void flip() { + mLocalRep.cycle(); + invalidate(); + } + + public FilterMirrorRepresentation getFinalRepresentation() { + return mLocalRep; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + // Treat event as handled. + return true; + } + + @Override + public void onDraw(Canvas canvas) { + MasterImage master = MasterImage.getImage(); + Bitmap image = master.getFiltersOnlyImage(); + if (image == null) { + return; + } + GeometryMathUtils.initializeHolder(mDrawHolder, mLocalRep); + GeometryMathUtils.drawTransformedCropped(mDrawHolder, canvas, image, getWidth(), + getHeight()); + } + + public void setEditor(EditorMirror editorFlip) { + mEditorMirror = editorFlip; + } + +} diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImagePoint.java b/src/com/android/gallery3d/filtershow/imageshow/ImagePoint.java index c58dd5f8e..fd5714139 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImagePoint.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImagePoint.java @@ -57,12 +57,6 @@ public abstract class ImagePoint extends ImageShow { invalidate(); } - @Override - public void updateImage() { - super.updateImage(); - invalidate(); - } - @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java b/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java index 87b5d3323..5186c09d7 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageRotate.java @@ -19,20 +19,18 @@ package com.android.gallery3d.filtershow.imageshow; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Paint; import android.util.AttributeSet; +import android.view.MotionEvent; -import com.android.gallery3d.R; import com.android.gallery3d.filtershow.editors.EditorRotate; +import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation; +import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils.GeometryHolder; -public class ImageRotate extends ImageGeometry { - - private float mBaseAngle = 0; - private float mAngle = 0; - - private final boolean mSnapToNinety = true; +public class ImageRotate extends ImageShow { private EditorRotate mEditorRotate; - private static final String LOGTAG = "ImageRotate"; + private static final String TAG = ImageRotate.class.getSimpleName(); + private FilterRotateRepresentation mLocalRep = new FilterRotateRepresentation(); + private GeometryHolder mDrawHolder = new GeometryHolder(); public ImageRotate(Context context, AttributeSet attrs) { super(context, attrs); @@ -42,51 +40,39 @@ public class ImageRotate extends ImageGeometry { super(context); } - private static final Paint gPaint = new Paint(); - - private void computeValue() { - float angle = getCurrentTouchAngle(); - mAngle = (mBaseAngle - angle) % 360; + public void setFilterRotateRepresentation(FilterRotateRepresentation rep) { + mLocalRep = (rep == null) ? new FilterRotateRepresentation() : rep; } public void rotate() { - mAngle += 90; - mAngle = snappedAngle(mAngle); - mAngle %= 360; - setLocalRotation((int) mAngle); - } - - @Override - protected void setActionDown(float x, float y) { - super.setActionDown(x, y); - mBaseAngle = mAngle = getLocalRotation(); + mLocalRep.rotateCW(); + invalidate(); } - @Override - protected void setActionMove(float x, float y) { - super.setActionMove(x, y); - computeValue(); - setLocalRotation((int) mAngle % 360); + public FilterRotateRepresentation getFinalRepresentation() { + return mLocalRep; } @Override - protected void setActionUp() { - super.setActionUp(); - if (mSnapToNinety) { - setLocalRotation(snappedAngle(mAngle % 360)); - } + public boolean onTouchEvent(MotionEvent event) { + // Treat event as handled. + return true; } - @Override public int getLocalValue() { - return constrainedRotation(snappedAngle(getLocalRotation())); + return mLocalRep.getRotation().value(); } @Override - protected void drawShape(Canvas canvas, Bitmap image) { - gPaint.setAntiAlias(true); - gPaint.setARGB(255, 255, 255, 255); - drawTransformedCropped(canvas, image, gPaint); + public void onDraw(Canvas canvas) { + MasterImage master = MasterImage.getImage(); + Bitmap image = master.getFiltersOnlyImage(); + if (image == null) { + return; + } + GeometryMathUtils.initializeHolder(mDrawHolder, mLocalRep); + GeometryMathUtils.drawTransformedCropped(mDrawHolder, canvas, image, canvas.getWidth(), + canvas.getHeight()); } public void setEditor(EditorRotate editorRotate) { diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java index 1ff4227a7..6278b2ad4 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java @@ -37,7 +37,6 @@ import android.widget.LinearLayout; import com.android.gallery3d.R; import com.android.gallery3d.filtershow.FilterShowActivity; -import com.android.gallery3d.filtershow.cache.ImageLoader; import com.android.gallery3d.filtershow.filters.ImageFilter; import com.android.gallery3d.filtershow.pipeline.ImagePreset; import com.android.gallery3d.filtershow.tools.SaveImage; @@ -87,10 +86,6 @@ public class ImageShow extends View implements OnGestureListener, } InteractionMode mInteractionMode = InteractionMode.NONE; - protected GeometryMetadata getGeometry() { - return new GeometryMetadata(getImagePreset().getGeometry()); - } - private FilterShowActivity mActivity = null; public FilterShowActivity getActivity() { @@ -154,16 +149,6 @@ public class ImageShow extends View implements OnGestureListener, return MasterImage.getImage().getCurrentFilter(); } - public Rect getImageBounds() { - Rect dst = new Rect(); - getImagePreset().getGeometry().getPhotoBounds().roundOut(dst); - return dst; - } - - public Rect getImageCropBounds() { - return GeometryMath.roundNearest(getImagePreset().getGeometry().getPreviewCropBounds()); - } - /* consider moving the following 2 methods into a subclass */ /** * This function calculates a Image to Screen Transformation matrix @@ -172,16 +157,14 @@ public class ImageShow extends View implements OnGestureListener, * @return Image to Screen transformation matrix */ protected Matrix getImageToScreenMatrix(boolean reflectRotation) { - GeometryMetadata geo = getImagePreset().getGeometry(); - if (geo == null - || MasterImage.getImage().getOriginalBounds() == null) { + MasterImage master = MasterImage.getImage(); + if (master.getOriginalBounds() == null) { return new Matrix(); } - Matrix m = geo.getOriginalToScreen(reflectRotation, - MasterImage.getImage().getOriginalBounds().width(), - MasterImage.getImage().getOriginalBounds().height(), getWidth(), getHeight()); - Point translate = MasterImage.getImage().getTranslation(); - float scaleFactor = MasterImage.getImage().getScaleFactor(); + Matrix m = GeometryMathUtils.getImageToScreenMatrix(master.getPreset().getGeometryFilters(), + reflectRotation, master.getOriginalBounds(), getWidth(), getHeight()); + Point translate = master.getTranslation(); + float scaleFactor = master.getScaleFactor(); m.postTranslate(translate.x, translate.y); m.postScale(scaleFactor, scaleFactor, getWidth() / 2.0f, getHeight() / 2.0f); return m; @@ -237,7 +220,7 @@ public class ImageShow extends View implements OnGestureListener, drawImage(canvas, getFilteredImage(), true); Bitmap highresPreview = MasterImage.getImage().getHighresImage(); if (highresPreview != null) { - drawImage(canvas, highresPreview, false); + drawImage(canvas, highresPreview, true); } canvas.restore(); @@ -278,7 +261,7 @@ public class ImageShow extends View implements OnGestureListener, Rect s = new Rect(0, 0, image.getWidth(), image.getHeight()); - float scale = GeometryMath.scale(image.getWidth(), image.getHeight(), getWidth(), + float scale = GeometryMathUtils.scale(image.getWidth(), image.getHeight(), getWidth(), getHeight()); float w = image.getWidth() * scale; @@ -360,33 +343,12 @@ public class ImageShow extends View implements OnGestureListener, MasterImage.getImage().addListener(this); } - private void imageSizeChanged(Bitmap image) { - if (image == null || getImagePreset() == null) - return; - float w = image.getWidth(); - float h = image.getHeight(); - GeometryMetadata geo = getImagePreset().getGeometry(); - RectF pb = geo.getPhotoBounds(); - if (w == pb.width() && h == pb.height()) { - return; - } - RectF r = new RectF(0, 0, w, h); - geo.setPhotoBounds(r); - geo.setCropBounds(r); - getImagePreset().setGeometry(geo); - } - public void updateImage() { invalidate(); - Bitmap bitmap = MasterImage.getImage().getOriginalBitmapLarge(); - if (bitmap != null) { - imageSizeChanged(bitmap); - } } public void imageLoaded() { updateImage(); - invalidate(); } public void saveImage(FilterShowActivity filterShowActivity, File file) { diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java index 17df2b703..ff75dcc09 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java @@ -19,21 +19,52 @@ package com.android.gallery3d.filtershow.imageshow; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Path; import android.graphics.RectF; import android.util.AttributeSet; +import android.view.MotionEvent; -import com.android.gallery3d.R; import com.android.gallery3d.filtershow.editors.EditorStraighten; +import com.android.gallery3d.filtershow.filters.FilterCropRepresentation; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; +import com.android.gallery3d.filtershow.filters.FilterStraightenRepresentation; +import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils.GeometryHolder; -public class ImageStraighten extends ImageGeometry { +import java.util.ArrayList; +import java.util.Collection; + +public class ImageStraighten extends ImageShow { + private static final String TAG = ImageStraighten.class.getSimpleName(); private float mBaseAngle = 0; private float mAngle = 0; + private float mInitialAngle = 0; + private boolean mFirstDrawSinceUp = false; private EditorStraighten mEditorStraighten; + private FilterStraightenRepresentation mLocalRep = new FilterStraightenRepresentation(); + private RectF mPriorCropAtUp = new RectF(); + private RectF mDrawRect = new RectF(); + private Path mDrawPath = new Path(); + private GeometryHolder mDrawHolder = new GeometryHolder(); + private enum MODES { + NONE, MOVE + } + private MODES mState = MODES.NONE; + private static final float MAX_STRAIGHTEN_ANGLE + = FilterStraightenRepresentation.MAX_STRAIGHTEN_ANGLE; + private static final float MIN_STRAIGHTEN_ANGLE + = FilterStraightenRepresentation.MIN_STRAIGHTEN_ANGLE; + private float mCurrentX; + private float mCurrentY; + private float mTouchCenterX; + private float mTouchCenterY; + private RectF mCrop = new RectF(); + private final Paint mPaint = new Paint(); - private static final String LOGTAG = "ImageStraighten"; - private static final Paint gPaint = new Paint(); public ImageStraighten(Context context) { super(context); } @@ -42,23 +73,76 @@ public class ImageStraighten extends ImageGeometry { super(context, attrs); } - @Override - protected void setActionDown(float x, float y) { - super.setActionDown(x, y); - mBaseAngle = mAngle = getLocalStraighten(); + public void setFilterStraightenRepresentation(FilterStraightenRepresentation rep) { + mLocalRep = (rep == null) ? new FilterStraightenRepresentation() : rep; + mInitialAngle = mBaseAngle = mAngle = mLocalRep.getStraighten(); } - private void setCropToStraighten(){ - setLocalCropBounds(getUntranslatedStraightenCropBounds(getLocalPhotoBounds(), - getLocalStraighten())); + public Collection getFinalRepresentation() { + ArrayList reps = new ArrayList(2); + reps.add(mLocalRep); + if (mInitialAngle != mLocalRep.getStraighten()) { + reps.add(new FilterCropRepresentation(mCrop)); + } + return reps; } @Override - protected void setActionMove(float x, float y) { - super.setActionMove(x, y); - computeValue(); - setLocalStraighten(mAngle); - setCropToStraighten(); + public boolean onTouchEvent(MotionEvent event) { + float x = event.getX(); + float y = event.getY(); + + switch (event.getActionMasked()) { + case (MotionEvent.ACTION_DOWN): + if (mState == MODES.NONE) { + mTouchCenterX = x; + mTouchCenterY = y; + mCurrentX = x; + mCurrentY = y; + mState = MODES.MOVE; + mBaseAngle = mAngle; + } + break; + case (MotionEvent.ACTION_UP): + if (mState == MODES.MOVE) { + mState = MODES.NONE; + mCurrentX = x; + mCurrentY = y; + computeValue(); + mFirstDrawSinceUp = true; + } + break; + case (MotionEvent.ACTION_MOVE): + if (mState == MODES.MOVE) { + mCurrentX = x; + mCurrentY = y; + computeValue(); + } + break; + default: + break; + } + invalidate(); + return true; + } + + private static float angleFor(float dx, float dy) { + return (float) (Math.atan2(dx, dy) * 180 / Math.PI); + } + + private float getCurrentTouchAngle() { + float centerX = getWidth() / 2f; + float centerY = getHeight() / 2f; + if (mCurrentX == mTouchCenterX && mCurrentY == mTouchCenterY) { + return 0; + } + float dX1 = mTouchCenterX - centerX; + float dY1 = mTouchCenterY - centerY; + float dX2 = mCurrentX - centerX; + float dY2 = mCurrentY - centerY; + float angleA = angleFor(dX1, dY1); + float angleB = angleFor(dX2, dY2); + return (angleB - angleA) % 360; } private void computeValue() { @@ -68,62 +152,105 @@ public class ImageStraighten extends ImageGeometry { mAngle = Math.min(MAX_STRAIGHTEN_ANGLE, mAngle); } - @Override - protected void lostVisibility() { - saveAndSetPreset(); + private static void getUntranslatedStraightenCropBounds(RectF outRect, float straightenAngle) { + float deg = straightenAngle; + if (deg < 0) { + deg = -deg; + } + double a = Math.toRadians(deg); + double sina = Math.sin(a); + double cosa = Math.cos(a); + double rw = outRect.width(); + double rh = outRect.height(); + double h1 = rh * rh / (rw * sina + rh * cosa); + double h2 = rh * rw / (rw * cosa + rh * sina); + double hh = Math.min(h1, h2); + double ww = hh * rw / rh; + float left = (float) ((rw - ww) * 0.5f); + float top = (float) ((rh - hh) * 0.5f); + float right = (float) (left + ww); + float bottom = (float) (top + hh); + outRect.set(left, top, right, bottom); } - @Override - protected void gainedVisibility(){ - setCropToStraighten(); + private void updateCurrentCrop(Matrix m, GeometryHolder h, RectF tmp, int imageWidth, + int imageHeight, int viewWidth, int viewHeight) { + if (GeometryMathUtils.needsDimensionSwap(h.rotation)) { + tmp.set(0, 0, imageHeight, imageWidth); + } else { + tmp.set(0, 0, imageWidth, imageHeight); + } + float scale = GeometryMathUtils.scale(imageWidth, imageHeight, viewWidth, viewHeight); + GeometryMathUtils.scaleRect(tmp, scale); + getUntranslatedStraightenCropBounds(tmp, mAngle); + tmp.offset(viewWidth / 2f - tmp.centerX(), viewHeight / 2f - tmp.centerY()); + h.straighten = 0; + Matrix m1 = GeometryMathUtils.getFullGeometryToScreenMatrix(h, imageWidth, + imageHeight, viewWidth, viewHeight); + m.reset(); + m1.invert(m); + mCrop.set(tmp); + m.mapRect(mCrop); + FilterCropRepresentation.findNormalizedCrop(mCrop, imageWidth, imageHeight); } - @Override - protected void setActionUp() { - super.setActionUp(); - setCropToStraighten(); - } @Override - public void onNewValue(int value) { - setLocalStraighten(GeometryMath.clamp(value, MIN_STRAIGHTEN_ANGLE, MAX_STRAIGHTEN_ANGLE)); - invalidate(); - } + public void onDraw(Canvas canvas) { + MasterImage master = MasterImage.getImage(); + Bitmap image = master.getFiltersOnlyImage(); + if (image == null) { + return; + } + GeometryMathUtils.initializeHolder(mDrawHolder, mLocalRep); + mDrawHolder.straighten = mAngle; + int imageWidth = image.getWidth(); + int imageHeight = image.getHeight(); + int viewWidth = canvas.getWidth(); + int viewHeight = canvas.getHeight(); - @Override - protected int getLocalValue() { - return (int) getLocalStraighten(); - } + // Get matrix for drawing bitmap + Matrix m = GeometryMathUtils.getFullGeometryToScreenMatrix(mDrawHolder, imageWidth, + imageHeight, viewWidth, viewHeight); + mPaint.reset(); + mPaint.setAntiAlias(true); + mPaint.setFilterBitmap(true); + canvas.drawBitmap(image, m, mPaint); - @Override - protected void drawShape(Canvas canvas, Bitmap image) { - float [] o = {0, 0}; - RectF bounds = drawTransformed(canvas, image, gPaint, o); + mPaint.setFilterBitmap(false); + mPaint.setColor(Color.WHITE); + mPaint.setStrokeWidth(2); + mPaint.setStyle(Paint.Style.FILL_AND_STROKE); + updateCurrentCrop(m, mDrawHolder, mDrawRect, imageWidth, + imageHeight, viewWidth, viewHeight); + if (mFirstDrawSinceUp) { + mPriorCropAtUp.set(mCrop); + mLocalRep.setStraighten(mAngle); + mFirstDrawSinceUp = false; + } // Draw the grid - gPaint.setARGB(255, 255, 255, 255); - gPaint.setStrokeWidth(3); - gPaint.setStyle(Paint.Style.FILL_AND_STROKE); - - RectF display = getLocalDisplayBounds(); - float dWidth = display.width(); - float dHeight = display.height(); - - if (mMode == MODES.MOVE) { + if (mState == MODES.MOVE) { canvas.save(); - canvas.clipRect(bounds); - + canvas.clipRect(mDrawRect); int n = 16; - float step = dWidth / n; + float step = viewWidth / n; float p = 0; for (int i = 1; i < n; i++) { p = i * step; - gPaint.setARGB(60, 255, 255, 255); - canvas.drawLine(p, 0, p, dHeight, gPaint); - canvas.drawLine(0, p, dWidth, p, gPaint); + mPaint.setAlpha(60); + canvas.drawLine(p, 0, p, viewHeight, mPaint); + canvas.drawLine(0, p, viewHeight, p, mPaint); } canvas.restore(); } + mPaint.reset(); + mPaint.setColor(Color.WHITE); + mPaint.setStyle(Style.STROKE); + mPaint.setStrokeWidth(3); + mDrawPath.reset(); + mDrawPath.addRect(mDrawRect, Path.Direction.CW); + canvas.drawPath(mDrawPath, mPaint); } public void setEditor(EditorStraighten editorStraighten) { diff --git a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java index 45ee5d7a4..92e57bfc1 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java +++ b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java @@ -82,9 +82,6 @@ public class MasterImage implements RenderingRequestCaller { private Vector mObservers = new Vector(); private FilterRepresentation mCurrentFilterRepresentation; - private Vector mGeometryListeners = new Vector(); - - private GeometryMetadata mPreviousGeometry = null; private float mScaleFactor = 1.0f; private float mMaxScaleFactor = 3.0f; // TODO: base this on the current view / image @@ -95,20 +92,6 @@ public class MasterImage implements RenderingRequestCaller { private boolean mShowsOriginal; - final private static int NEW_GEOMETRY = 1; - - private final Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case NEW_GEOMETRY: { - hasNewGeometry(); - break; - } - } - } - }; - private MasterImage() { } @@ -243,39 +226,15 @@ public class MasterImage implements RenderingRequestCaller { preset.showFilters(); } mPreset = preset; - setGeometry(); mPreset.fillImageStateAdapter(mState); if (addToHistory) { HistoryItem historyItem = new HistoryItem(mPreset, change); mHistory.addHistoryItem(historyItem); } updatePresets(true); - GeometryMetadata geo = mPreset.getGeometry(); - if (!geo.equals(mPreviousGeometry)) { - notifyGeometryChange(); - } - mPreviousGeometry = new GeometryMetadata(geo); mActivity.updateCategories(); } - private void setGeometry() { - Bitmap image = getOriginalBitmapLarge(); - if (image == null) { - return; - } - float w = image.getWidth(); - float h = image.getHeight(); - GeometryMetadata geo = mPreset.getGeometry(); - RectF pb = geo.getPhotoBounds(); - if (w == pb.width() && h == pb.height()) { - return; - } - RectF r = new RectF(0, 0, w, h); - geo.setPhotoBounds(r); - geo.setCropBounds(r); - mPreset.setGeometry(geo); - } - public void onHistoryItemClick(int position) { HistoryItem historyItem = mHistory.getItem(position); // We need a copy from the history @@ -337,16 +296,6 @@ public class MasterImage implements RenderingRequestCaller { return mPreviewPreset; } - public void setOriginalGeometry(Bitmap originalBitmapLarge) { - GeometryMetadata geo = getPreset().getGeometry(); - float w = originalBitmapLarge.getWidth(); - float h = originalBitmapLarge.getHeight(); - RectF r = new RectF(0, 0, w, h); - geo.setPhotoBounds(r); - geo.setCropBounds(r); - getPreset().setGeometry(geo); - } - public Bitmap getFilteredImage() { mPreviewBuffer.swapConsumerIfNeeded(); // get latest bitmap Buffer consumer = mPreviewBuffer.getConsumer(); @@ -450,14 +399,16 @@ public class MasterImage implements RenderingRequestCaller { } private Matrix getImageToScreenMatrix(boolean reflectRotation) { - GeometryMetadata geo = mPreset.getGeometry(); - if (geo == null || getOriginalBounds() == null - || mImageShowSize.x == 0) { + if (getOriginalBounds() == null || mImageShowSize.x == 0 || mImageShowSize.y == 0) { return new Matrix(); } - Matrix m = geo.getOriginalToScreen(reflectRotation, - getOriginalBounds().width(), - getOriginalBounds().height(), mImageShowSize.x, mImageShowSize.y); + Matrix m = GeometryMathUtils.getImageToScreenMatrix(mPreset.getGeometryFilters(), + reflectRotation, getOriginalBounds(), mImageShowSize.x, mImageShowSize.y); + if (m == null) { + m = new Matrix(); + m.reset(); + return m; + } Point translate = getTranslation(); float scaleFactor = getScaleFactor(); m.postTranslate(translate.x, translate.y); @@ -516,6 +467,7 @@ public class MasterImage implements RenderingRequestCaller { } if (request.getType() == RenderingRequest.FILTERS_RENDERING) { mFiltersOnlyBitmap = request.getBitmap(); + notifyObservers(); needsCheckModification = true; } if (request.getType() == RenderingRequest.PARTIAL_RENDERING @@ -538,24 +490,6 @@ public class MasterImage implements RenderingRequestCaller { sMasterImage = null; } - public void addGeometryListener(GeometryListener listener) { - mGeometryListeners.add(listener); - } - - public void notifyGeometryChange() { - if (mHandler.hasMessages(NEW_GEOMETRY)) { - return; - } - mHandler.sendEmptyMessage(NEW_GEOMETRY); - } - - public void hasNewGeometry() { - updatePresets(true); - for (GeometryListener listener : mGeometryListeners) { - listener.geometryChanged(); - } - } - public float getScaleFactor() { return mScaleFactor; } diff --git a/src/com/android/gallery3d/filtershow/pipeline/CacheProcessing.java b/src/com/android/gallery3d/filtershow/pipeline/CacheProcessing.java index 10b6c4923..e0269e9bb 100644 --- a/src/com/android/gallery3d/filtershow/pipeline/CacheProcessing.java +++ b/src/com/android/gallery3d/filtershow/pipeline/CacheProcessing.java @@ -90,7 +90,9 @@ public class CacheProcessing { cacheBitmap = originalBitmap.copy(Bitmap.Config.ARGB_8888, true); if (findBaseImageIndex > -1) { FilterRepresentation representation = filters.elementAt(findBaseImageIndex); - cacheBitmap = environment.applyRepresentation(representation, cacheBitmap); + if (representation.getFilterType() != FilterRepresentation.TYPE_GEOMETRY) { + cacheBitmap = environment.applyRepresentation(representation, cacheBitmap); + } mSteps.elementAt(findBaseImageIndex).representation = representation.copy(); mSteps.elementAt(findBaseImageIndex).cache = cacheBitmap; } @@ -115,7 +117,9 @@ public class CacheProcessing { } for (int i = findBaseImageIndex; i <= similarUpToIndex; i++) { FilterRepresentation representation = filters.elementAt(i); - cacheBitmap = environment.applyRepresentation(representation, cacheBitmap); + if (representation.getFilterType() != FilterRepresentation.TYPE_GEOMETRY) { + cacheBitmap = environment.applyRepresentation(representation, cacheBitmap); + } if (DEBUG) { Log.v(LOGTAG, " - " + i + " => apply " + representation.getName()); } @@ -134,7 +138,9 @@ public class CacheProcessing { FilterRepresentation representation = filters.elementAt(i); CacheStep currentStep = mSteps.elementAt(i); cacheBitmap = cacheBitmap.copy(Bitmap.Config.ARGB_8888, true); - cacheBitmap = environment.applyRepresentation(representation, cacheBitmap); + if (representation.getFilterType() != FilterRepresentation.TYPE_GEOMETRY) { + cacheBitmap = environment.applyRepresentation(representation, cacheBitmap); + } currentStep.representation = representation.copy(); currentStep.cache = cacheBitmap; if (DEBUG) { diff --git a/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java b/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java index beba993e5..155ddaa19 100644 --- a/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java +++ b/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java @@ -26,8 +26,7 @@ import android.util.Log; import com.android.gallery3d.filtershow.cache.ImageLoader; import com.android.gallery3d.filtershow.filters.FilterRepresentation; import com.android.gallery3d.filtershow.filters.FiltersManager; -import com.android.gallery3d.filtershow.filters.ImageFilterGeometry; -import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; +import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils; import com.android.gallery3d.filtershow.imageshow.MasterImage; import java.util.Vector; @@ -39,7 +38,6 @@ public class CachingPipeline implements PipelineInterface { private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888; private static volatile RenderScript sRS = null; - private static volatile Resources sResources = null; private FiltersManager mFiltersManager = null; private volatile Bitmap mOriginalBitmap = null; @@ -57,13 +55,10 @@ public class CachingPipeline implements PipelineInterface { private volatile int mWidth = 0; private volatile int mHeight = 0; - private volatile GeometryMetadata mPreviousGeometry = null; private volatile float mPreviewScaleFactor = 1.0f; private volatile float mHighResPreviewScaleFactor = 1.0f; private volatile String mName = ""; - private ImageFilterGeometry mGeometry = null; - public CachingPipeline(FiltersManager filtersManager, String name) { mFiltersManager = filtersManager; mName = name; @@ -79,7 +74,6 @@ public class CachingPipeline implements PipelineInterface { destroyRenderScriptContext(); } sRS = RenderScript.create(context); - sResources = context.getResources(); } public static synchronized void destroyRenderScriptContext() { @@ -87,7 +81,6 @@ public class CachingPipeline implements PipelineInterface { sRS.destroy(); } sRS = null; - sResources = null; } public void stop() { @@ -112,7 +105,6 @@ public class CachingPipeline implements PipelineInterface { mFiltersOnlyOriginalAllocation.destroy(); mFiltersOnlyOriginalAllocation = null; } - mPreviousGeometry = null; mPreviewScaleFactor = 1.0f; mHighResPreviewScaleFactor = 1.0f; @@ -190,15 +182,6 @@ public class CachingPipeline implements PipelineInterface { return false; } - GeometryMetadata geometry = preset.getGeometry(); - if (mPreviousGeometry != null && geometry.equals(mPreviousGeometry)) { - return false; - } - - if (DEBUG) { - Log.v(LOGTAG, "geometry has changed"); - } - RenderScript RS = getRenderScriptContext(); Allocation filtersOnlyOriginalAllocation = mFiltersOnlyOriginalAllocation; @@ -216,7 +199,6 @@ public class CachingPipeline implements PipelineInterface { originalAllocation.destroy(); } - mPreviousGeometry = new GeometryMetadata(geometry); return true; } @@ -333,14 +315,7 @@ public class CachingPipeline implements PipelineInterface { } public Bitmap renderGeometryIcon(Bitmap bitmap, ImagePreset preset) { - // Called by RenderRequest on the main thread - // TODO: change this -- we should reuse a pool of bitmaps instead... - if (mGeometry == null) { - mGeometry = new ImageFilterGeometry(); - } - mGeometry.useRepresentation(preset.getGeometry()); - return mGeometry.apply(bitmap, mPreviewScaleFactor, - FilterEnvironment.QUALITY_PREVIEW); + return GeometryMathUtils.applyGeometryRepresentations(preset.getGeometryFilters(), bitmap); } public void compute(SharedBuffer buffer, ImagePreset preset, int type) { diff --git a/src/com/android/gallery3d/filtershow/pipeline/FilterEnvironment.java b/src/com/android/gallery3d/filtershow/pipeline/FilterEnvironment.java index 29608bfbd..4fac956be 100644 --- a/src/com/android/gallery3d/filtershow/pipeline/FilterEnvironment.java +++ b/src/com/android/gallery3d/filtershow/pipeline/FilterEnvironment.java @@ -17,10 +17,14 @@ package com.android.gallery3d.filtershow.pipeline; import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; import android.support.v8.renderscript.Allocation; import com.android.gallery3d.filtershow.filters.FilterRepresentation; import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation; +import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation.Rotation; import com.android.gallery3d.filtershow.filters.FiltersManagerInterface; import com.android.gallery3d.filtershow.filters.ImageFilter; diff --git a/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java b/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java index 9bb3e00f2..64643e455 100644 --- a/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java +++ b/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java @@ -36,7 +36,7 @@ import com.android.gallery3d.filtershow.filters.FilterStraightenRepresentation; import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation; import com.android.gallery3d.filtershow.filters.FiltersManager; import com.android.gallery3d.filtershow.filters.ImageFilter; -import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; +import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils; import com.android.gallery3d.filtershow.imageshow.MasterImage; import com.android.gallery3d.filtershow.state.State; import com.android.gallery3d.filtershow.state.StateAdapter; @@ -45,6 +45,8 @@ import com.android.gallery3d.util.UsageStatistics; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collection; import java.util.Vector; public class ImagePreset { @@ -65,17 +67,8 @@ public class ImagePreset { public ImagePreset(ImagePreset source) { for (int i = 0; i < source.mFilters.size(); i++) { - FilterRepresentation representation = null; FilterRepresentation sourceRepresentation = source.mFilters.elementAt(i); - if (sourceRepresentation instanceof GeometryMetadata) { - GeometryMetadata geoData = new GeometryMetadata(); - GeometryMetadata srcGeo = (GeometryMetadata) sourceRepresentation; - geoData.set(srcGeo); - representation = geoData; - } else { - representation = sourceRepresentation.copy(); - } - mFilters.add(representation); + mFilters.add(sourceRepresentation.copy()); } } @@ -91,9 +84,24 @@ public class ImagePreset { return representation; } + private static boolean sameSerializationName(String a, String b) { + if (a != null && b != null) { + return a.equals(b); + } else { + return a == null && b == null; + } + } + + public static boolean sameSerializationName(FilterRepresentation a, FilterRepresentation b) { + if (a == null || b == null) { + return false; + } + return sameSerializationName(a.getSerializationName(), b.getSerializationName()); + } + public int getPositionForRepresentation(FilterRepresentation representation) { for (int i = 0; i < mFilters.size(); i++) { - if (mFilters.elementAt(i).getFilterClass() == representation.getFilterClass()) { + if (sameSerializationName(mFilters.elementAt(i), representation)) { return i; } } @@ -118,7 +126,8 @@ public class ImagePreset { return -1; } - public FilterRepresentation getFilterRepresentationCopyFrom(FilterRepresentation filterRepresentation) { + public FilterRepresentation getFilterRepresentationCopyFrom( + FilterRepresentation filterRepresentation) { // TODO: add concept of position in the filters (to allow multiple instances) if (filterRepresentation == null) { return null; @@ -134,22 +143,19 @@ public class ImagePreset { return representation; } - public void updateFilterRepresentation(FilterRepresentation representation) { - if (representation == null) { - return; + public void updateFilterRepresentations(Collection reps) { + for (FilterRepresentation r : reps) { + updateOrAddFilterRepresentation(r); } - if (representation instanceof GeometryMetadata) { - setGeometry((GeometryMetadata) representation); + } + + public void updateOrAddFilterRepresentation(FilterRepresentation rep) { + int pos = getPositionForRepresentation(rep); + if (pos != -1) { + mFilters.elementAt(pos).useParametersFrom(rep); } else { - int position = getPositionForRepresentation(representation); - if (position == -1) { - return; - } - FilterRepresentation old = mFilters.elementAt(position); - old.useParametersFrom(representation); + addFilter(rep.copy()); } - MasterImage.getImage().invalidatePreview(); - fillImageStateAdapter(MasterImage.getImage().getState()); } public void setDoApplyGeometry(boolean value) { @@ -164,25 +170,10 @@ public class ImagePreset { return mDoApplyFilters; } - public GeometryMetadata getGeometry() { - for (FilterRepresentation representation : mFilters) { - if (representation instanceof GeometryMetadata) { - return (GeometryMetadata) representation; - } - } - GeometryMetadata geo = new GeometryMetadata(); - mFilters.add(0, geo); // Hard Requirement for now -- Geometry ought to be first. - return geo; - } - public boolean hasModifications() { for (int i = 0; i < mFilters.size(); i++) { FilterRepresentation filter = mFilters.elementAt(i); - if (filter instanceof GeometryMetadata) { - if (((GeometryMetadata) filter).hasModifications()) { - return true; - } - } else if (!filter.isNil()) { + if (!filter.isNil()) { return true; } } @@ -191,10 +182,9 @@ public class ImagePreset { public boolean isPanoramaSafe() { for (FilterRepresentation representation : mFilters) { - if (representation instanceof GeometryMetadata) { - if (((GeometryMetadata) representation).hasModifications()) { - return false; - } + if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY + && !representation.isNil()) { + return false; } if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER && !representation.isNil()) { @@ -212,29 +202,6 @@ public class ImagePreset { return true; } - public void setGeometry(GeometryMetadata representation) { - GeometryMetadata geoData = getGeometry(); - if (geoData != representation) { - geoData.set(representation); - } - } - - public boolean equals(ImagePreset preset) { - if (!same(preset)) { - return false; - } - if (mDoApplyFilters && preset.mDoApplyFilters) { - for (int i = 0; i < preset.mFilters.size(); i++) { - FilterRepresentation a = preset.mFilters.elementAt(i); - FilterRepresentation b = mFilters.elementAt(i); - if (!a.equals(b)) { - return false; - } - } - } - return true; - } - public boolean same(ImagePreset preset) { if (preset == null) { return false; @@ -248,10 +215,6 @@ public class ImagePreset { return false; } - if (mDoApplyGeometry && !getGeometry().equals(preset.getGeometry())) { - return false; - } - if (mDoApplyFilters != preset.mDoApplyFilters) { if (mFilters.size() > 0 || preset.mFilters.size() > 0) { return false; @@ -262,10 +225,7 @@ public class ImagePreset { for (int i = 0; i < preset.mFilters.size(); i++) { FilterRepresentation a = preset.mFilters.elementAt(i); FilterRepresentation b = mFilters.elementAt(i); - if (a instanceof GeometryMetadata) { - // Note: Geometry will always be at the same place - continue; - } + if (!a.same(b)) { return false; } @@ -276,10 +236,6 @@ public class ImagePreset { } public int similarUpTo(ImagePreset preset) { - if (!getGeometry().equals(preset.getGeometry())) { - return -1; - } - for (int i = 0; i < preset.mFilters.size(); i++) { FilterRepresentation a = preset.mFilters.elementAt(i); if (i < mFilters.size()) { @@ -316,17 +272,16 @@ public class ImagePreset { public void removeFilter(FilterRepresentation filterRepresentation) { if (filterRepresentation.getFilterType() == FilterRepresentation.TYPE_BORDER) { - for (int i = 0; i < mFilters.size();i++) { + for (int i = 0; i < mFilters.size(); i++) { if (mFilters.elementAt(i).getFilterType() - == filterRepresentation.getFilterType()) { + == filterRepresentation.getFilterType()) { mFilters.remove(i); break; } } } else { for (int i = 0; i < mFilters.size(); i++) { - if (mFilters.elementAt(i).getFilterClass() - == filterRepresentation.getFilterClass()) { + if (sameSerializationName(mFilters.elementAt(i), filterRepresentation)) { mFilters.remove(i); break; } @@ -334,29 +289,27 @@ public class ImagePreset { } } - // If the filter is an "None" effect or border, then just don't add this - // filter. + // If the filter is an "None" effect or border, then just don't add this filter. public void addFilter(FilterRepresentation representation) { - if (representation instanceof GeometryMetadata) { - setGeometry((GeometryMetadata) representation); - return; - } if (representation instanceof FilterUserPresetRepresentation) { ImagePreset preset = ((FilterUserPresetRepresentation) representation).getImagePreset(); // user preset replace everything but geometry - GeometryMetadata geometry = getGeometry(); mFilters.clear(); - mFilters.add(geometry); for (int i = 0; i < preset.nbFilters(); i++) { - FilterRepresentation rep = preset.getFilterRepresentation(i); - if (!(representation instanceof GeometryMetadata)) { - addFilter(rep); - } + addFilter(preset.getFilterRepresentation(i)); } mFilters.add(representation); - return; - } - if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) { + } else if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) { + // Add geometry filter, removing duplicates and do-nothing operations. + for (int i = 0; i < mFilters.size(); i++) { + if (sameSerializationName(representation, mFilters.elementAt(i))) { + mFilters.remove(i); + } + } + if (!representation.isNil()) { + mFilters.add(representation); + } + } else if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) { removeFilter(representation); if (!isNoneBorderFilter(representation)) { mFilters.add(representation); @@ -421,13 +374,13 @@ public class ImagePreset { private boolean isNoneFxFilter(FilterRepresentation representation) { return representation instanceof FilterFxRepresentation && - ((FilterFxRepresentation)representation).getNameResource() == R.string.none; + ((FilterFxRepresentation) representation).getNameResource() == R.string.none; } public FilterRepresentation getRepresentation(FilterRepresentation filterRepresentation) { for (int i = 0; i < mFilters.size(); i++) { FilterRepresentation representation = mFilters.elementAt(i); - if (representation.getFilterClass() == filterRepresentation.getFilterClass()) { + if (sameSerializationName(representation, filterRepresentation)) { return representation; } } @@ -440,12 +393,32 @@ public class ImagePreset { return applyBorder(bitmap, environment); } + public Collection getGeometryFilters() { + ArrayList geometry = new ArrayList(); + for (FilterRepresentation r : mFilters) { + if (r.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) { + geometry.add(r); + } + } + return geometry; + } + + public FilterRepresentation getFilterWithSerializationName(String serializationName) { + for (FilterRepresentation r : mFilters) { + if (r != null) { + if (sameSerializationName(r.getSerializationName(), serializationName)) { + return r.copy(); + } + } + } + return null; + } + public Bitmap applyGeometry(Bitmap bitmap, FilterEnvironment environment) { // Apply any transform -- 90 rotate, flip, straighten, crop // Returns a new bitmap. if (mDoApplyGeometry) { - GeometryMetadata geoData = getGeometry(); - bitmap = environment.applyRepresentation(geoData, bitmap); + bitmap = GeometryMathUtils.applyGeometryRepresentations(getGeometryFilters(), bitmap); } return bitmap; } @@ -482,12 +455,13 @@ public class ImagePreset { } for (int i = from; i < to; i++) { FilterRepresentation representation = mFilters.elementAt(i); - if (representation instanceof GeometryMetadata) { + if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) { // skip the geometry as it's already applied. continue; } if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) { - // for now, let's skip the border as it will be applied in applyBorder() + // for now, let's skip the border as it will be applied in + // applyBorder() // TODO: might be worth getting rid of applyBorder. continue; } @@ -506,7 +480,7 @@ public class ImagePreset { } public void applyBorder(Allocation in, Allocation out, - boolean copyOut, FilterEnvironment environment) { + boolean copyOut, FilterEnvironment environment) { FilterRepresentation border = getFilterRepresentationForType( FilterRepresentation.TYPE_BORDER); if (border != null && mDoApplyGeometry) { @@ -522,7 +496,7 @@ public class ImagePreset { } public void applyFilters(int from, int to, Allocation in, Allocation out, - FilterEnvironment environment) { + FilterEnvironment environment) { if (mDoApplyFilters) { if (from < 0) { from = 0; @@ -532,12 +506,8 @@ public class ImagePreset { } for (int i = from; i < to; i++) { FilterRepresentation representation = mFilters.elementAt(i); - if (representation instanceof GeometryMetadata) { - // skip the geometry as it's already applied. - continue; - } - if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) { - // for now, let's skip the border as it will be applied in applyBorder() + if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY + || representation.getFilterType() == FilterRepresentation.TYPE_BORDER) { continue; } if (i > from) { @@ -554,8 +524,8 @@ public class ImagePreset { } for (int i = 0; i < mFilters.size(); i++) { FilterRepresentation representation = mFilters.elementAt(i); - if (representation instanceof GeometryMetadata - && ((GeometryMetadata) representation).hasModifications()) { + if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY + && !representation.isNil()) { return false; } if (!representation.supportsPartialRendering()) { @@ -571,7 +541,7 @@ public class ImagePreset { } Vector states = new Vector(); for (FilterRepresentation filter : mFilters) { - if (filter instanceof GeometryMetadata) { + if (filter.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) { // TODO: supports Geometry representations in the state panel. continue; } @@ -622,7 +592,7 @@ public class ImagePreset { } public void writeJson(JsonWriter writer, String name) { - int numFilters = mFilters.size(); + int numFilters = mFilters.size(); try { writer.beginObject(); for (int i = 0; i < numFilters; i++) { @@ -649,12 +619,13 @@ public class ImagePreset { /** * populates preset from JSON string + * * @param filterString a JSON string * @return true on success if false ImagePreset is undefined */ public boolean readJsonFromString(String filterString) { if (DEBUG) { - Log.v(LOGTAG,"reading preset: \""+filterString+"\""); + Log.v(LOGTAG, "reading preset: \"" + filterString + "\""); } StringReader sreader = new StringReader(filterString); try { @@ -666,7 +637,7 @@ public class ImagePreset { } reader.close(); } catch (Exception e) { - Log.e(LOGTAG,"parsing the filter parameters:",e); + Log.e(LOGTAG, "parsing the filter parameters:", e); return false; } return true; @@ -674,6 +645,7 @@ public class ImagePreset { /** * populates preset from JSON stream + * * @param sreader a JSON string * @return true on success if false ImagePreset is undefined */ @@ -684,7 +656,7 @@ public class ImagePreset { String name = sreader.nextName(); FilterRepresentation filter = creatFilterFromName(name); if (filter == null) { - Log.w(LOGTAG,"UNKNOWN FILTER! "+name); + Log.w(LOGTAG, "UNKNOWN FILTER! " + name); return false; } filter.deSerializeRepresentation(sreader); @@ -695,10 +667,7 @@ public class ImagePreset { } FilterRepresentation creatFilterFromName(String name) { - // TODO: move these to FiltersManager pattern. - if (GeometryMetadata.SERIALIZATION_NAME.equalsIgnoreCase(name)) { - return new GeometryMetadata(); - } else if (FilterRotateRepresentation.SERIALIZATION_NAME.equals(name)) { + if (FilterRotateRepresentation.SERIALIZATION_NAME.equals(name)) { return new FilterRotateRepresentation(); } else if (FilterMirrorRepresentation.SERIALIZATION_NAME.equals(name)) { return new FilterMirrorRepresentation(); diff --git a/src_pd/com/android/gallery3d/filtershow/editors/EditorManager.java b/src_pd/com/android/gallery3d/filtershow/editors/EditorManager.java index ab85110fe..8ea21ad26 100644 --- a/src_pd/com/android/gallery3d/filtershow/editors/EditorManager.java +++ b/src_pd/com/android/gallery3d/filtershow/editors/EditorManager.java @@ -30,7 +30,7 @@ public class EditorManager { editorPlaceHolder.addEditor(new EditorTinyPlanet()); editorPlaceHolder.addEditor(new EditorDraw()); editorPlaceHolder.addEditor(new EditorVignette()); - editorPlaceHolder.addEditor(new EditorFlip()); + editorPlaceHolder.addEditor(new EditorMirror()); editorPlaceHolder.addEditor(new EditorRotate()); editorPlaceHolder.addEditor(new EditorStraighten()); editorPlaceHolder.addEditor(new EditorCrop()); -- cgit v1.2.3