summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/filtershow
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/gallery3d/filtershow')
-rw-r--r--src/com/android/gallery3d/filtershow/CropExtras.java121
-rw-r--r--src/com/android/gallery3d/filtershow/FilterShowActivity.java257
-rw-r--r--src/com/android/gallery3d/filtershow/PanelController.java118
-rw-r--r--src/com/android/gallery3d/filtershow/cache/ImageLoader.java279
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilter.java37
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java12
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java12
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java21
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java12
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java45
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java12
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterRedEye.java157
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java12
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java12
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java20
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java11
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java12
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java12
-rw-r--r--src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java23
-rw-r--r--src/com/android/gallery3d/filtershow/filters/RedEyeCandidate.java50
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/BoundedRect.java340
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/CropMath.java191
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java76
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java98
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java731
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java206
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageRedEyes.java148
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageShow.java20
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java18
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java5
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageWithIcon.java50
-rw-r--r--src/com/android/gallery3d/filtershow/tools/BitmapTask.java68
-rw-r--r--src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java22
33 files changed, 2232 insertions, 976 deletions
diff --git a/src/com/android/gallery3d/filtershow/CropExtras.java b/src/com/android/gallery3d/filtershow/CropExtras.java
new file mode 100644
index 000000000..7ed8f1eb5
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/CropExtras.java
@@ -0,0 +1,121 @@
+/*
+ * 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;
+
+import android.net.Uri;
+
+public class CropExtras {
+
+ public static final String KEY_CROPPED_RECT = "cropped-rect";
+ public static final String KEY_OUTPUT_X = "outputX";
+ public static final String KEY_OUTPUT_Y = "outputY";
+ public static final String KEY_SCALE = "scale";
+ public static final String KEY_SCALE_UP_IF_NEEDED = "scaleUpIfNeeded";
+ public static final String KEY_ASPECT_X = "aspectX";
+ public static final String KEY_ASPECT_Y = "aspectY";
+ public static final String KEY_SET_AS_WALLPAPER = "set-as-wallpaper";
+ public static final String KEY_RETURN_DATA = "return-data";
+ public static final String KEY_DATA = "data";
+ public static final String KEY_SPOTLIGHT_X = "spotlightX";
+ public static final String KEY_SPOTLIGHT_Y = "spotlightY";
+ public static final String KEY_SHOW_WHEN_LOCKED = "showWhenLocked";
+ public static final String KEY_OUTPUT_FORMAT = "outputFormat";
+
+ private int mOutputX = 0;
+ private int mOutputY = 0;
+ private boolean mScaleUp = true;
+ private int mAspectX = 0;
+ private int mAspectY = 0;
+ private boolean mSetAsWallpaper = false;
+ private boolean mReturnData = false;
+ private Uri mExtraOutput = null;
+ private String mOutputFormat = null;
+ private boolean mShowWhenLocked = false;
+ private float mSpotlightX = 0;
+ private float mSpotlightY = 0;
+
+ public CropExtras(int outputX, int outputY, boolean scaleUp, int aspectX, int aspectY,
+ boolean setAsWallpaper, boolean returnData, Uri extraOutput, String outputFormat,
+ boolean showWhenLocked, float spotlightX, float spotlightY) {
+ mOutputX = outputX;
+ mOutputY = outputY;
+ mScaleUp = scaleUp;
+ mAspectX = aspectX;
+ mAspectY = aspectY;
+ mSetAsWallpaper = setAsWallpaper;
+ mReturnData = returnData;
+ mExtraOutput = extraOutput;
+ mOutputFormat = outputFormat;
+ mShowWhenLocked = showWhenLocked;
+ mSpotlightX = spotlightX;
+ mSpotlightY = spotlightY;
+ }
+
+ public CropExtras(CropExtras c) {
+ this(c.mOutputX, c.mOutputY, c.mScaleUp, c.mAspectX, c.mAspectY, c.mSetAsWallpaper,
+ c.mReturnData, c.mExtraOutput, c.mOutputFormat, c.mShowWhenLocked,
+ c.mSpotlightX, c.mSpotlightY);
+ }
+
+ public int getOutputX() {
+ return mOutputX;
+ }
+
+ public int getOutputY() {
+ return mOutputY;
+ }
+
+ public boolean getScaleUp() {
+ return mScaleUp;
+ }
+
+ public int getAspectX() {
+ return mAspectX;
+ }
+
+ public int getAspectY() {
+ return mAspectY;
+ }
+
+ public boolean getSetAsWallpaper() {
+ return mSetAsWallpaper;
+ }
+
+ public boolean getReturnData() {
+ return mReturnData;
+ }
+
+ public Uri getExtraOutput() {
+ return mExtraOutput;
+ }
+
+ public String getOutputFormat() {
+ return mOutputFormat;
+ }
+
+ public boolean getShowWhenLocked() {
+ return mShowWhenLocked;
+ }
+
+ public float getSpotlightX() {
+ return mSpotlightX;
+ }
+
+ public float getSpotlightY() {
+ return mSpotlightY;
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
index c1e4f6add..356a0a074 100644
--- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java
+++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
@@ -20,6 +20,7 @@ import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
import android.app.ProgressDialog;
+import android.app.WallpaperManager;
import android.content.ContentValues;
import android.content.Intent;
import android.content.res.Configuration;
@@ -32,6 +33,7 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.provider.MediaStore;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
@@ -58,6 +60,7 @@ import com.android.gallery3d.filtershow.filters.ImageFilter;
import com.android.gallery3d.filtershow.filters.ImageFilterBorder;
import com.android.gallery3d.filtershow.filters.ImageFilterBwFilter;
import com.android.gallery3d.filtershow.filters.ImageFilterContrast;
+import com.android.gallery3d.filtershow.filters.ImageFilterCurves;
import com.android.gallery3d.filtershow.filters.ImageFilterExposure;
import com.android.gallery3d.filtershow.filters.ImageFilterFx;
import com.android.gallery3d.filtershow.filters.ImageFilterHue;
@@ -65,6 +68,7 @@ import com.android.gallery3d.filtershow.filters.ImageFilterParametricBorder;
import com.android.gallery3d.filtershow.filters.ImageFilterRS;
import com.android.gallery3d.filtershow.filters.ImageFilterSaturated;
import com.android.gallery3d.filtershow.filters.ImageFilterShadows;
+import com.android.gallery3d.filtershow.filters.ImageFilterSharpen;
import com.android.gallery3d.filtershow.filters.ImageFilterTinyPlanet;
import com.android.gallery3d.filtershow.filters.ImageFilterVibrance;
import com.android.gallery3d.filtershow.filters.ImageFilterVignette;
@@ -72,13 +76,13 @@ import com.android.gallery3d.filtershow.filters.ImageFilterWBalance;
import com.android.gallery3d.filtershow.imageshow.ImageBorder;
import com.android.gallery3d.filtershow.imageshow.ImageCrop;
import com.android.gallery3d.filtershow.imageshow.ImageFlip;
+import com.android.gallery3d.filtershow.imageshow.ImageRedEyes;
import com.android.gallery3d.filtershow.imageshow.ImageRotate;
import com.android.gallery3d.filtershow.imageshow.ImageShow;
import com.android.gallery3d.filtershow.imageshow.ImageSmallBorder;
import com.android.gallery3d.filtershow.imageshow.ImageSmallFilter;
import com.android.gallery3d.filtershow.imageshow.ImageStraighten;
import com.android.gallery3d.filtershow.imageshow.ImageTinyPlanet;
-import com.android.gallery3d.filtershow.imageshow.ImageWithIcon;
import com.android.gallery3d.filtershow.imageshow.ImageZoom;
import com.android.gallery3d.filtershow.presets.ImagePreset;
import com.android.gallery3d.filtershow.provider.SharedImageProvider;
@@ -90,6 +94,7 @@ import com.android.gallery3d.filtershow.ui.Spline;
import com.android.gallery3d.util.GalleryUtils;
import java.io.File;
+import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Vector;
@@ -97,14 +102,19 @@ import java.util.Vector;
public class FilterShowActivity extends Activity implements OnItemClickListener,
OnShareTargetSelectedListener {
- public static final String CROP_ACTION = "com.android.camera.action.EDITOR_CROP";
+ // fields for supporting crop action
+ public static final String CROP_ACTION = "com.android.camera.action.CROP";
+ private CropExtras mCropExtras = null;
+
public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET";
public static final String LAUNCH_FULLSCREEN = "launch-fullscreen";
+ public static final int MAX_BMAP_IN_INTENT = 990000;
private final PanelController mPanelController = new PanelController();
private ImageLoader mImageLoader = null;
private ImageShow mImageShow = null;
private ImageCurves mImageCurves = null;
private ImageBorder mImageBorders = null;
+ private ImageRedEyes mImageRedEyes = null;
private ImageStraighten mImageStraighten = null;
private ImageZoom mImageZoom = null;
private ImageCrop mImageCrop = null;
@@ -117,6 +127,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
private View mListGeometry = null;
private View mListColors = null;
private View mListFilterButtons = null;
+ private View mSaveButton = null;
private ImageButton mFxButton = null;
private ImageButton mBorderButton = null;
@@ -155,7 +166,8 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
ImageFilterRS.setRenderScriptContext(this);
ImageShow.setDefaultBackgroundColor(getResources().getColor(R.color.background_screen));
- ImageSmallFilter.setDefaultBackgroundColor(getResources().getColor(R.color.background_main_toolbar));
+ ImageSmallFilter.setDefaultBackgroundColor(getResources().getColor(
+ R.color.background_main_toolbar));
// TODO: get those values from XML.
ImageZoom.setZoomedSize(getPixelsFromDip(256));
FramedTextButton.setTextSize((int) getPixelsFromDip(14));
@@ -180,7 +192,8 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
actionBar.setCustomView(R.layout.filtershow_actionbar);
- actionBar.getCustomView().setOnClickListener(new OnClickListener() {
+ mSaveButton = actionBar.getCustomView();
+ mSaveButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
saveImage();
@@ -202,9 +215,11 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
mImageRotate = (ImageRotate) findViewById(R.id.imageRotate);
mImageFlip = (ImageFlip) findViewById(R.id.imageFlip);
mImageTinyPlanet = (ImageTinyPlanet) findViewById(R.id.imageTinyPlanet);
+ mImageRedEyes = (ImageRedEyes) findViewById(R.id.imageRedEyes);
mImageCrop.setAspectTextSize((int) getPixelsFromDip(18));
ImageCrop.setTouchTolerance((int) getPixelsFromDip(25));
+ ImageCrop.setMinCropSize((int) getPixelsFromDip(55));
mImageViews.add(mImageShow);
mImageViews.add(mImageCurves);
mImageViews.add(mImageBorders);
@@ -214,6 +229,10 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
mImageViews.add(mImageRotate);
mImageViews.add(mImageFlip);
mImageViews.add(mImageTinyPlanet);
+ mImageViews.add(mImageRedEyes);
+ for (ImageShow imageShow : mImageViews) {
+ mImageLoader.addCacheListener(imageShow);
+ }
mListFx = findViewById(R.id.fxList);
mListBorders = findViewById(R.id.bordersList);
@@ -248,6 +267,8 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
mImageFlip.setMaster(mImageShow);
mImageTinyPlanet.setImageLoader(mImageLoader);
mImageTinyPlanet.setMaster(mImageShow);
+ mImageRedEyes.setImageLoader(mImageLoader);
+ mImageRedEyes.setMaster(mImageShow);
mPanelController.setActivity(this);
@@ -260,6 +281,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
mPanelController.addImageView(findViewById(R.id.imageFlip));
mPanelController.addImageView(findViewById(R.id.imageZoom));
mPanelController.addImageView(findViewById(R.id.imageTinyPlanet));
+ mPanelController.addImageView(findViewById(R.id.imageRedEyes));
mPanelController.addPanel(mFxButton, mListFx, 0);
mPanelController.addPanel(mBorderButton, mListBorders, 1);
@@ -269,86 +291,40 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
mPanelController.addComponent(mGeometryButton, findViewById(R.id.cropButton));
mPanelController.addComponent(mGeometryButton, findViewById(R.id.rotateButton));
mPanelController.addComponent(mGeometryButton, findViewById(R.id.flipButton));
+ mPanelController.addComponent(mGeometryButton, findViewById(R.id.redEyeButton));
mPanelController.addPanel(mColorsButton, mListColors, 3);
- int[] recastIDs = {
- R.id.tinyplanetButton,
- R.id.vignetteButton,
- R.id.vibranceButton,
- R.id.contrastButton,
- R.id.saturationButton,
- R.id.bwfilterButton,
- R.id.wbalanceButton,
- R.id.hueButton,
- R.id.exposureButton,
- R.id.shadowRecoveryButton
- };
ImageFilter[] filters = {
new ImageFilterTinyPlanet(),
+ new ImageFilterWBalance(),
+ new ImageFilterExposure(),
new ImageFilterVignette(),
- new ImageFilterVibrance(),
new ImageFilterContrast(),
- new ImageFilterSaturated(),
- new ImageFilterBwFilter(),
- new ImageFilterWBalance(),
+ new ImageFilterShadows(),
+ new ImageFilterVibrance(),
+ new ImageFilterSharpen(),
+ new ImageFilterCurves(),
new ImageFilterHue(),
- new ImageFilterExposure(),
- new ImageFilterShadows()
+ new ImageFilterSaturated(),
+ new ImageFilterBwFilter()
};
for (int i = 0; i < filters.length; i++) {
ImageSmallFilter fView = new ImageSmallFilter(this);
- View v = listColors.findViewById(recastIDs[i]);
- int pos = listColors.indexOfChild(v);
- listColors.removeView(v);
-
filters[i].setParameter(filters[i].getPreviewParameter());
- if (v instanceof ImageButtonTitle)
- filters[i].setName(((ImageButtonTitle) v).getText());
+ filters[i].setName(getString(filters[i].getTextId()));
fView.setImageFilter(filters[i]);
fView.setController(this);
fView.setImageLoader(mImageLoader);
- fView.setId(recastIDs[i]);
- mPanelController.addComponent(mColorsButton, fView);
- listColors.addView(fView, pos);
- }
-
- int[] overlayIDs = {
- R.id.sharpenButton,
- R.id.curvesButtonRGB
- };
- int[] overlayBitmaps = {
- R.drawable.filtershow_button_colors_sharpen,
- R.drawable.filtershow_button_colors_curve
- };
- int[] overlayNames = {
- R.string.sharpness,
- R.string.curvesRGB
- };
-
- for (int i = 0; i < overlayIDs.length; i++) {
- ImageWithIcon fView = new ImageWithIcon(this);
- View v = listColors.findViewById(overlayIDs[i]);
- int pos = listColors.indexOfChild(v);
- listColors.removeView(v);
- final int sid = overlayNames[i];
- ImageFilterExposure efilter = new ImageFilterExposure() {
- {
- mName = getString(sid);
- }
- };
- efilter.setParameter(-300);
- Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
- overlayBitmaps[i]);
-
- fView.setIcon(bitmap);
- fView.setImageFilter(efilter);
- fView.setController(this);
- fView.setImageLoader(mImageLoader);
- fView.setId(overlayIDs[i]);
+ fView.setId(filters[i].getButtonId());
+ if (filters[i].getOverlayBitmaps() != 0) {
+ Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
+ filters[i].getOverlayBitmaps());
+ fView.setOverlayBitmap(bitmap);
+ }
mPanelController.addComponent(mColorsButton, fView);
- listColors.addView(fView, pos);
+ listColors.addView(fView);
}
mPanelController.addView(findViewById(R.id.applyEffect));
@@ -390,8 +366,37 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
pickImage();
}
+ // Handle behavior for various actions
String action = intent.getAction();
if (action.equalsIgnoreCase(CROP_ACTION)) {
+ Bundle extras = intent.getExtras();
+ if (extras != null) {
+ mCropExtras = new CropExtras(extras.getInt(CropExtras.KEY_OUTPUT_X, 0),
+ extras.getInt(CropExtras.KEY_OUTPUT_Y, 0),
+ extras.getBoolean(CropExtras.KEY_SCALE, true) &&
+ extras.getBoolean(CropExtras.KEY_SCALE_UP_IF_NEEDED, false),
+ extras.getInt(CropExtras.KEY_ASPECT_X, 0),
+ extras.getInt(CropExtras.KEY_ASPECT_Y, 0),
+ extras.getBoolean(CropExtras.KEY_SET_AS_WALLPAPER, false),
+ extras.getBoolean(CropExtras.KEY_RETURN_DATA, false),
+ (Uri) extras.getParcelable(MediaStore.EXTRA_OUTPUT),
+ extras.getString(CropExtras.KEY_OUTPUT_FORMAT),
+ extras.getBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, false),
+ extras.getFloat(CropExtras.KEY_SPOTLIGHT_X),
+ extras.getFloat(CropExtras.KEY_SPOTLIGHT_Y));
+
+ if (mCropExtras.getShowWhenLocked()) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ }
+ mImageShow.getImagePreset().mGeoData.setCropExtras(mCropExtras);
+
+ mImageCrop.setExtras(mCropExtras);
+ String s = getString(R.string.Fixed);
+ mImageCrop.setAspectString(s);
+ mImageCrop.setCropActionFlag(true);
+ mPanelController.setFixedAspect(mCropExtras.getAspectX() > 0
+ && mCropExtras.getAspectY() > 0);
+ }
mPanelController.showComponent(findViewById(R.id.cropButton));
} else if (action.equalsIgnoreCase(TINY_PLANET_ACTION)) {
mPanelController.showComponent(findViewById(R.id.tinyplanetButton));
@@ -411,7 +416,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
mLoadBitmapTask.execute(uri);
}
- private class LoadBitmapTask extends AsyncTask<Uri, Void, Boolean> {
+ private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> {
View mTinyPlanetButton;
int mBitmapSize;
@@ -422,19 +427,26 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
@Override
protected Boolean doInBackground(Uri... params) {
- mImageLoader.loadBitmap(params[0], mBitmapSize);
- publishProgress();
- return mImageLoader.queryLightCycle360();
+ if (!mImageLoader.loadBitmap(params[0], mBitmapSize)) {
+ return false;
+ }
+ publishProgress(mImageLoader.queryLightCycle360());
+ return true;
}
@Override
- protected void onProgressUpdate(Void... values) {
+ protected void onProgressUpdate(Boolean... values) {
super.onProgressUpdate(values);
- if (isCancelled()) return;
+ if (isCancelled()) {
+ return;
+ }
final View filters = findViewById(R.id.filtersPanel);
final View loading = findViewById(R.id.loading);
loading.setVisibility(View.GONE);
filters.setVisibility(View.VISIBLE);
+ if (values[0]) {
+ mTinyPlanetButton.setVisibility(View.VISIBLE);
+ }
}
@Override
@@ -442,9 +454,10 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
if (isCancelled()) {
return;
}
- if (result) {
- mTinyPlanetButton.setVisibility(View.VISIBLE);
+ if (!result) {
+ cannotLoadImage();
}
+
mLoadBitmapTask = null;
super.onPostExecute(result);
}
@@ -640,6 +653,11 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
return false;
}
+ public void enableSave(boolean enable) {
+ if (mSaveButton != null)
+ mSaveButton.setEnabled(enable);
+ }
+
private void fillListImages(LinearLayout listFilters) {
// TODO: use listview
// TODO: load the filters straight from the filesystem
@@ -990,17 +1008,88 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
}
}
+ private boolean mSaveToExtraUri = false;
+ private boolean mSaveAsWallpaper = false;
+ private boolean mReturnAsExtra = false;
+ private boolean outputted = false;
+
public void saveImage() {
- if (mImageShow.hasModifications()) {
- // Get the name of the album, to which the image will be saved
- File saveDir = SaveCopyTask.getFinalSaveDirectory(this, mImageLoader.getUri());
- int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
- String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
- showSavingProgress(albumName);
- mImageShow.saveImage(this, null);
- } else {
- finish();
+ // boolean outputted = false;
+ if (mCropExtras != null) {
+ if (mCropExtras.getExtraOutput() != null) {
+ mSaveToExtraUri = true;
+ outputted = true;
+ }
+ if (mCropExtras.getSetAsWallpaper()) {
+ mSaveAsWallpaper = true;
+ outputted = true;
+ }
+ if (mCropExtras.getReturnData()) {
+
+ mReturnAsExtra = true;
+ outputted = true;
+ }
+
+ if (outputted) {
+ mImageShow.getImagePreset().mGeoData.setUseCropExtrasFlag(true);
+ showSavingProgress(null);
+ mImageShow.returnFilteredResult(this);
+ }
+ }
+ if (!outputted) {
+ if (mImageShow.hasModifications()) {
+ // Get the name of the album, to which the image will be saved
+ File saveDir = SaveCopyTask.getFinalSaveDirectory(this, mImageLoader.getUri());
+ int bucketId = GalleryUtils.getBucketId(saveDir.getPath());
+ String albumName = LocalAlbum.getLocalizedName(getResources(), bucketId, null);
+ showSavingProgress(albumName);
+ mImageShow.saveImage(this, null);
+ } else {
+ done();
+ }
+ }
+ }
+
+ public void onFilteredResult(Bitmap filtered) {
+ Intent intent = new Intent();
+ intent.putExtra(CropExtras.KEY_CROPPED_RECT, mImageShow.getImageCropBounds());
+ if (mSaveToExtraUri) {
+ mImageShow.saveToUri(filtered, mCropExtras.getExtraOutput(),
+ mCropExtras.getOutputFormat(), this);
+ }
+ if (mSaveAsWallpaper) {
+ try {
+ WallpaperManager.getInstance(this).setBitmap(filtered);
+ } catch (IOException e) {
+ Log.w(LOGTAG, "fail to set wall paper", e);
+ }
}
+ if (mReturnAsExtra) {
+ if (filtered != null) {
+ int bmapSize = filtered.getRowBytes() * filtered.getHeight();
+ /*
+ * Max size of Binder transaction buffer is 1Mb, so constrain
+ * Bitmap to be somewhat less than this, otherwise we get
+ * TransactionTooLargeExceptions.
+ */
+ if (bmapSize > MAX_BMAP_IN_INTENT) {
+ Log.w(LOGTAG, "Bitmap too large to be returned via intent");
+ } else {
+ intent.putExtra(CropExtras.KEY_DATA, filtered);
+ }
+ }
+ }
+ setResult(RESULT_OK, intent);
+ if (!mSaveToExtraUri) {
+ done();
+ }
+ }
+
+ public void done() {
+ if (outputted) {
+ hideSavingProgress();
+ }
+ finish();
}
static {
diff --git a/src/com/android/gallery3d/filtershow/PanelController.java b/src/com/android/gallery3d/filtershow/PanelController.java
index 52bf98aa7..1d9cc22b5 100644
--- a/src/com/android/gallery3d/filtershow/PanelController.java
+++ b/src/com/android/gallery3d/filtershow/PanelController.java
@@ -42,6 +42,7 @@ import com.android.gallery3d.filtershow.filters.ImageFilterVignette;
import com.android.gallery3d.filtershow.filters.ImageFilterWBalance;
import com.android.gallery3d.filtershow.imageshow.ImageCrop;
import com.android.gallery3d.filtershow.imageshow.ImageShow;
+import com.android.gallery3d.filtershow.imageshow.ImageSmallFilter;
import com.android.gallery3d.filtershow.presets.ImagePreset;
import com.android.gallery3d.filtershow.ui.FramedTextButton;
import com.android.gallery3d.filtershow.ui.ImageCurves;
@@ -56,6 +57,11 @@ public class PanelController implements OnClickListener {
private static int HORIZONTAL_MOVE = 1;
private static final int ANIM_DURATION = 200;
private static final String LOGTAG = "PanelController";
+ private boolean mFixedAspect = false;
+
+ public void setFixedAspect(boolean t) {
+ mFixedAspect = t;
+ }
class Panel {
private final View mView;
@@ -587,6 +593,27 @@ public class PanelController implements OnClickListener {
}
mUtilityPanel.hideAspectButtons();
mUtilityPanel.hideCurvesButtons();
+
+ if (view instanceof ImageSmallFilter) {
+ ImageSmallFilter component = (ImageSmallFilter) view;
+ ImageFilter filter = component.getImageFilter();
+ if (filter.getEditingViewId() != 0) {
+ mCurrentImage = showImageView(filter.getEditingViewId());
+ mCurrentImage.setShowControls(filter.showEditingControls());
+ String ename = mCurrentImage.getContext().getString(filter.getTextId());
+ mUtilityPanel.setEffectName(ename);
+ if (view.getId() == R.id.curvesButtonRGB) {
+ // TODO: delegate to the filter / editing view the management of the
+ // panel accessory view
+ mUtilityPanel.setShowParameter(false);
+ mUtilityPanel.showCurvesButtons();
+ }
+ ensureFilter(ename);
+ mCurrentImage.select();
+ }
+ return;
+ }
+
switch (view.getId()) {
case R.id.tinyplanetButton: {
mCurrentImage = showImageView(R.id.imageTinyPlanet).setShowControls(true);
@@ -606,11 +633,13 @@ public class PanelController implements OnClickListener {
String ename = mCurrentImage.getContext().getString(R.string.crop);
mUtilityPanel.setEffectName(ename);
mUtilityPanel.setShowParameter(false);
- if (mCurrentImage instanceof ImageCrop && mUtilityPanel.firstTimeCropDisplayed){
- ((ImageCrop) mCurrentImage).applyClear();
+ if (mCurrentImage instanceof ImageCrop && mUtilityPanel.firstTimeCropDisplayed) {
+ ((ImageCrop) mCurrentImage).clear();
mUtilityPanel.firstTimeCropDisplayed = false;
}
- mUtilityPanel.showAspectButtons();
+ if (!mFixedAspect) {
+ mUtilityPanel.showAspectButtons();
+ }
break;
}
case R.id.rotateButton: {
@@ -626,89 +655,8 @@ public class PanelController implements OnClickListener {
mUtilityPanel.setShowParameter(false);
break;
}
- case R.id.vignetteButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.vignette);
- mUtilityPanel.setEffectName(ename);
- ensureFilter(ename);
- break;
- }
- case R.id.curvesButtonRGB: {
- ImageCurves curves = (ImageCurves) showImageView(R.id.imageCurves);
- String ename = curves.getContext().getString(R.string.curvesRGB);
- mUtilityPanel.setEffectName(ename);
- mUtilityPanel.setShowParameter(false);
- mUtilityPanel.showCurvesButtons();
- mCurrentImage = curves;
- ensureFilter(ename);
- break;
- }
- case R.id.sharpenButton: {
- mCurrentImage = showImageView(R.id.imageZoom).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.sharpness);
- mUtilityPanel.setEffectName(ename);
- ensureFilter(ename);
- break;
- }
- case R.id.contrastButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.contrast);
- mUtilityPanel.setEffectName(ename);
- ensureFilter(ename);
- break;
- }
- case R.id.saturationButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.saturation);
- mUtilityPanel.setEffectName(ename);
- ensureFilter(ename);
- break;
- }
- case R.id.bwfilterButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.bwfilter);
- mUtilityPanel.setEffectName(ename);
- ensureFilter(ename);
- break;
- }
- case R.id.wbalanceButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(false);
- String ename = mCurrentImage.getContext().getString(R.string.wbalance);
- mUtilityPanel.setEffectName(ename);
- mUtilityPanel.setShowParameter(false);
- ensureFilter(ename);
- break;
- }
- case R.id.hueButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.hue);
- mUtilityPanel.setEffectName(ename);
- ensureFilter(ename);
- break;
- }
- case R.id.exposureButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.exposure);
- mUtilityPanel.setEffectName(ename);
- ensureFilter(ename);
- break;
- }
- case R.id.vibranceButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.vibrance);
- mUtilityPanel.setEffectName(ename);
- ensureFilter(ename);
- break;
- }
- case R.id.shadowRecoveryButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
- String ename = mCurrentImage.getContext().getString(R.string.shadow_recovery);
- mUtilityPanel.setEffectName(ename);
- ensureFilter(ename);
- break;
- }
case R.id.redEyeButton: {
- mCurrentImage = showImageView(R.id.imageShow).setShowControls(true);
+ mCurrentImage = showImageView(R.id.imageRedEyes).setShowControls(true);
String ename = mCurrentImage.getContext().getString(R.string.redeye);
mUtilityPanel.setEffectName(ename);
ensureFilter(ename);
diff --git a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
index afef58aad..a1a1ba186 100644
--- a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
+++ b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
@@ -18,6 +18,7 @@ package com.android.gallery3d.filtershow.cache;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
@@ -26,6 +27,7 @@ import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Matrix;
import android.graphics.Rect;
+import android.graphics.Bitmap.CompressFormat;
import android.media.ExifInterface;
import android.net.Uri;
import android.provider.MediaStore;
@@ -36,19 +38,27 @@ import com.adobe.xmp.XMPMeta;
import com.android.gallery3d.R;
import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.exif.ExifInvalidFormatException;
+import com.android.gallery3d.exif.ExifParser;
+import com.android.gallery3d.exif.ExifTag;
+import com.android.gallery3d.filtershow.CropExtras;
import com.android.gallery3d.filtershow.FilterShowActivity;
import com.android.gallery3d.filtershow.HistoryAdapter;
import com.android.gallery3d.filtershow.imageshow.ImageCrop;
import com.android.gallery3d.filtershow.imageshow.ImageShow;
import com.android.gallery3d.filtershow.presets.ImagePreset;
+import com.android.gallery3d.filtershow.tools.BitmapTask;
import com.android.gallery3d.filtershow.tools.SaveCopyTask;
+import com.android.gallery3d.util.InterruptableOutputStream;
import com.android.gallery3d.util.XmpUtilHelper;
import java.io.Closeable;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.util.Vector;
import java.util.concurrent.locks.ReentrantLock;
@@ -69,13 +79,16 @@ public class ImageLoader {
private FilterShowActivity mActivity = null;
- public static final int ORI_NORMAL = ExifInterface.ORIENTATION_NORMAL;
- public static final int ORI_ROTATE_90 = ExifInterface.ORIENTATION_ROTATE_90;
+ public static final String DEFAULT_SAVE_DIRECTORY = "EditedOnlinePhotos";
+ public static final int DEFAULT_COMPRESS_QUALITY = 95;
+
+ public static final int ORI_NORMAL = ExifInterface.ORIENTATION_NORMAL;
+ public static final int ORI_ROTATE_90 = ExifInterface.ORIENTATION_ROTATE_90;
public static final int ORI_ROTATE_180 = ExifInterface.ORIENTATION_ROTATE_180;
public static final int ORI_ROTATE_270 = ExifInterface.ORIENTATION_ROTATE_270;
- public static final int ORI_FLIP_HOR = ExifInterface.ORIENTATION_FLIP_HORIZONTAL;
- public static final int ORI_FLIP_VERT = ExifInterface.ORIENTATION_FLIP_VERTICAL;
- public static final int ORI_TRANSPOSE = ExifInterface.ORIENTATION_TRANSPOSE;
+ public static final int ORI_FLIP_HOR = ExifInterface.ORIENTATION_FLIP_HORIZONTAL;
+ public static final int ORI_FLIP_VERT = ExifInterface.ORIENTATION_FLIP_VERTICAL;
+ public static final int ORI_TRANSPOSE = ExifInterface.ORIENTATION_TRANSPOSE;
public static final int ORI_TRANSVERSE = ExifInterface.ORIENTATION_TRANSVERSE;
private Context mContext = null;
@@ -101,18 +114,24 @@ public class ImageLoader {
return mActivity;
}
- public void loadBitmap(Uri uri,int size) {
+ public boolean loadBitmap(Uri uri, int size) {
mLoadingLock.lock();
mUri = uri;
mOrientation = getOrientation(mContext, uri);
mOriginalBitmapSmall = loadScaledBitmap(uri, 160);
if (mOriginalBitmapSmall == null) {
// Couldn't read the bitmap, let's exit
- mActivity.cannotLoadImage();
+ mLoadingLock.unlock();
+ return false;
}
mOriginalBitmapLarge = loadScaledBitmap(uri, size);
+ if (mOriginalBitmapLarge == null) {
+ mLoadingLock.unlock();
+ return false;
+ }
updateBitmaps();
mLoadingLock.unlock();
+ return true;
}
public Uri getUri() {
@@ -135,21 +154,25 @@ public class ImageLoader {
MediaStore.Images.ImageColumns.ORIENTATION
},
null, null, null);
- if (cursor.moveToNext()){
- int ori = cursor.getInt(0);
-
- switch (ori){
- case 0: return ORI_NORMAL;
- case 90: return ORI_ROTATE_90;
- case 270: return ORI_ROTATE_270;
- case 180: return ORI_ROTATE_180;
- default:
- return -1;
- }
- } else{
+ if (cursor.moveToNext()) {
+ int ori = cursor.getInt(0);
+
+ switch (ori) {
+ case 0:
+ return ORI_NORMAL;
+ case 90:
+ return ORI_ROTATE_90;
+ case 270:
+ return ORI_ROTATE_270;
+ case 180:
+ return ORI_ROTATE_180;
+ default:
+ return -1;
+ }
+ } else {
return -1;
}
- } catch (SQLiteException e){
+ } catch (SQLiteException e) {
return ExifInterface.ORIENTATION_UNDEFINED;
} catch (IllegalArgumentException e) {
return ExifInterface.ORIENTATION_UNDEFINED;
@@ -160,12 +183,27 @@ public class ImageLoader {
static int getOrientationFromPath(String path) {
int orientation = -1;
+ InputStream is = null;
try {
- ExifInterface EXIF = new ExifInterface(path);
- orientation = EXIF.getAttributeInt(ExifInterface.TAG_ORIENTATION,
- 1);
+ is = new FileInputStream(path);
+ ExifParser parser = ExifParser.parse(is, ExifParser.OPTION_IFD_0);
+ int event = parser.next();
+ while (event != ExifParser.EVENT_END) {
+ if (event == ExifParser.EVENT_NEW_TAG) {
+ ExifTag tag = parser.getTag();
+ if (tag.getTagId() == ExifTag.TAG_ORIENTATION) {
+ orientation = (int) tag.getValueAt(0);
+ break;
+ }
+ }
+ event = parser.next();
+ }
} catch (IOException e) {
e.printStackTrace();
+ } catch (ExifInvalidFormatException e) {
+ e.printStackTrace();
+ } finally {
+ Utils.closeSilently(is);
}
return orientation;
}
@@ -181,46 +219,46 @@ public class ImageLoader {
warnListeners();
}
- public static Bitmap rotateToPortrait(Bitmap bitmap,int ori) {
- Matrix matrix = new Matrix();
- int w = bitmap.getWidth();
- int h = bitmap.getHeight();
- if (ori == ORI_ROTATE_90 ||
- ori == ORI_ROTATE_270 ||
- ori == ORI_TRANSPOSE||
- ori == ORI_TRANSVERSE) {
- int tmp = w;
- w = h;
- h = tmp;
- }
- switch(ori){
- case ORI_ROTATE_90:
- matrix.setRotate(90,w/2f,h/2f);
- break;
- case ORI_ROTATE_180:
- matrix.setRotate(180,w/2f,h/2f);
- break;
- case ORI_ROTATE_270:
- matrix.setRotate(270,w/2f,h/2f);
- break;
- case ORI_FLIP_HOR:
- matrix.preScale(-1, 1);
- break;
- case ORI_FLIP_VERT:
- matrix.preScale(1, -1);
- break;
- case ORI_TRANSPOSE:
- matrix.setRotate(90,w/2f,h/2f);
- matrix.preScale(1, -1);
- break;
- case ORI_TRANSVERSE:
- matrix.setRotate(270,w/2f,h/2f);
- matrix.preScale(1, -1);
- break;
- case ORI_NORMAL:
- default:
- return bitmap;
- }
+ public static Bitmap rotateToPortrait(Bitmap bitmap, int ori) {
+ Matrix matrix = new Matrix();
+ int w = bitmap.getWidth();
+ int h = bitmap.getHeight();
+ if (ori == ORI_ROTATE_90 ||
+ ori == ORI_ROTATE_270 ||
+ ori == ORI_TRANSPOSE ||
+ ori == ORI_TRANSVERSE) {
+ int tmp = w;
+ w = h;
+ h = tmp;
+ }
+ switch (ori) {
+ case ORI_ROTATE_90:
+ matrix.setRotate(90, w / 2f, h / 2f);
+ break;
+ case ORI_ROTATE_180:
+ matrix.setRotate(180, w / 2f, h / 2f);
+ break;
+ case ORI_ROTATE_270:
+ matrix.setRotate(270, w / 2f, h / 2f);
+ break;
+ case ORI_FLIP_HOR:
+ matrix.preScale(-1, 1);
+ break;
+ case ORI_FLIP_VERT:
+ matrix.preScale(1, -1);
+ break;
+ case ORI_TRANSPOSE:
+ matrix.setRotate(90, w / 2f, h / 2f);
+ matrix.preScale(1, -1);
+ break;
+ case ORI_TRANSVERSE:
+ matrix.setRotate(270, w / 2f, h / 2f);
+ matrix.preScale(1, -1);
+ break;
+ case ORI_NORMAL:
+ default:
+ return bitmap;
+ }
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
@@ -253,6 +291,7 @@ public class ImageLoader {
}
static final int MAX_BITMAP_DIM = 2048;
+
private Bitmap loadScaledBitmap(Uri uri, int size) {
InputStream is = null;
try {
@@ -338,7 +377,8 @@ public class ImageLoader {
}
};
- // TODO: this currently does the loading + filtering on the UI thread -- need to
+ // TODO: this currently does the loading + filtering on the UI thread --
+ // need to
// move this to a background thread.
public Bitmap getScaleOneImageForPreset(ImageShow caller, ImagePreset imagePreset, Rect bounds,
boolean force) {
@@ -354,6 +394,7 @@ public class ImageLoader {
bmp2 = imagePreset.apply(bmp2);
imagePreset.setScaleFactor(scaleFactor);
mZoomCache.setImage(imagePreset, bounds, bmp2);
+ mLoadingLock.unlock();
return bmp2;
}
}
@@ -366,9 +407,11 @@ public class ImageLoader {
boolean hiRes) {
mLoadingLock.lock();
if (mOriginalBitmapSmall == null) {
+ mLoadingLock.unlock();
return null;
}
if (mOriginalBitmapLarge == null) {
+ mLoadingLock.unlock();
return null;
}
@@ -415,6 +458,109 @@ public class ImageLoader {
}).execute(preset);
}
+ public static Bitmap loadMutableBitmap(Context context, Uri sourceUri)
+ throws FileNotFoundException {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ // TODO: on <3.x we need a copy of the bitmap (inMutable doesn't
+ // exist)
+ options.inMutable = true;
+
+ InputStream is = context.getContentResolver().openInputStream(sourceUri);
+ Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
+ int orientation = ImageLoader.getOrientation(context, sourceUri);
+ bitmap = ImageLoader.rotateToPortrait(bitmap, orientation);
+ return bitmap;
+ }
+
+ public void returnFilteredResult(ImagePreset preset,
+ final FilterShowActivity filterShowActivity) {
+ preset.setIsHighQuality(true);
+ preset.setScaleFactor(1.0f);
+
+ BitmapTask.Callbacks<ImagePreset> cb = new BitmapTask.Callbacks<ImagePreset>() {
+
+ @Override
+ public void onComplete(Bitmap result) {
+ filterShowActivity.onFilteredResult(result);
+ }
+
+ @Override
+ public void onCancel() {
+ }
+
+ @Override
+ public Bitmap onExecute(ImagePreset param) {
+ if (param == null) {
+ return null;
+ }
+ try {
+ Bitmap bitmap = param.apply(loadMutableBitmap(mContext, mUri));
+ return bitmap;
+ } catch (FileNotFoundException ex) {
+ Log.w(LOGTAG, "Failed to save image!", ex);
+ return null;
+ }
+ }
+ };
+
+ (new BitmapTask<ImagePreset>(cb)).execute(preset);
+ }
+
+ private String getFileExtension(String requestFormat) {
+ String outputFormat = (requestFormat == null)
+ ? "jpg"
+ : requestFormat;
+ outputFormat = outputFormat.toLowerCase();
+ return (outputFormat.equals("png") || outputFormat.equals("gif"))
+ ? "png" // We don't support gif compression.
+ : "jpg";
+ }
+
+ private CompressFormat convertExtensionToCompressFormat(String extension) {
+ return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG;
+ }
+
+ public void saveToUri(Bitmap bmap, Uri uri, final String outputFormat,
+ final FilterShowActivity filterShowActivity) {
+
+ OutputStream out = null;
+ try {
+ out = filterShowActivity.getContentResolver().openOutputStream(uri);
+ } catch (FileNotFoundException e) {
+ Log.w(LOGTAG, "cannot write output", e);
+ out = null;
+ } finally {
+ if (bmap == null || out == null) {
+ return;
+ }
+ }
+
+ final InterruptableOutputStream ios = new InterruptableOutputStream(out);
+
+ BitmapTask.Callbacks<Bitmap> cb = new BitmapTask.Callbacks<Bitmap>() {
+
+ @Override
+ public void onComplete(Bitmap result) {
+ filterShowActivity.done();
+ }
+
+ @Override
+ public void onCancel() {
+ ios.interrupt();
+ }
+
+ @Override
+ public Bitmap onExecute(Bitmap param) {
+ CompressFormat cf = convertExtensionToCompressFormat(getFileExtension(outputFormat));
+ param.compress(cf, DEFAULT_COMPRESS_QUALITY, ios);
+ Utils.closeSilently(ios);
+ return null;
+ }
+ };
+
+ (new BitmapTask<Bitmap>(cb)).execute(bmap);
+ }
+
public void setAdapter(HistoryAdapter adapter) {
mAdapter = adapter;
}
@@ -475,4 +621,9 @@ public class ImageLoader {
}
}
+ public void addCacheListener(ImageShow imageShow) {
+ mHiresCache.addObserver(imageShow);
+ mCache.addObserver(imageShow);
+ }
+
}
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilter.java b/src/com/android/gallery3d/filtershow/filters/ImageFilter.java
index 7f4d5ed2a..5c24fbf65 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilter.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilter.java
@@ -18,6 +18,7 @@ package com.android.gallery3d.filtershow.filters;
import android.graphics.Bitmap;
+import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.presets.ImagePreset;
public class ImageFilter implements Cloneable {
@@ -31,22 +32,42 @@ public class ImageFilter implements Cloneable {
protected String mName = "Original";
private final String LOGTAG = "ImageFilter";
- public static final byte TYPE_BORDER =1;
- public static final byte TYPE_FX = 2;
+ public static final byte TYPE_BORDER = 1;
+ public static final byte TYPE_FX = 2;
public static final byte TYPE_WBALANCE = 3;
public static final byte TYPE_VIGNETTE = 4;
public static final byte TYPE_NORMAL = 5;
public static final byte TYPE_TINYPLANET = 6;
private byte filterType = TYPE_NORMAL;
- public byte getFilterType(){
+ public byte getFilterType() {
return filterType;
}
- protected void setFilterType(byte type){
+ protected void setFilterType(byte type) {
filterType = type;
}
+ public int getButtonId() {
+ return 0;
+ }
+
+ public int getTextId() {
+ return 0;
+ }
+
+ public int getOverlayBitmaps() {
+ return 0;
+ }
+
+ public int getEditingViewId() {
+ return R.id.imageShow;
+ }
+
+ public boolean showEditingControls() {
+ return true;
+ }
+
@Override
public ImageFilter clone() throws CloneNotSupportedException {
ImageFilter filter = (ImageFilter) super.clone();
@@ -93,7 +114,7 @@ public class ImageFilter implements Cloneable {
* The maximum allowed value (inclusive)
* @return maximum value allowed as input to this filter
*/
- public int getMaxParameter(){
+ public int getMaxParameter() {
return mMaxParameter;
}
@@ -101,7 +122,7 @@ public class ImageFilter implements Cloneable {
* The parameter value to be used in previews.
* @return parameter value to be used to preview the filter
*/
- public int getPreviewParameter(){
+ public int getPreviewParameter() {
return mPreviewParameter;
}
@@ -109,7 +130,7 @@ public class ImageFilter implements Cloneable {
* The minimum allowed value (inclusive)
* @return minimum value allowed as input to this filter
*/
- public int getMinParameter(){
+ public int getMinParameter() {
return mMinParameter;
}
@@ -117,7 +138,7 @@ public class ImageFilter implements Cloneable {
* Returns the default value returned by this filter.
* @return default value
*/
- public int getDefaultParameter(){
+ public int getDefaultParameter() {
return mDefaultParameter;
}
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java
index 558abe3c3..1bb5c76ac 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.filters;
+import com.android.gallery3d.R;
+
import android.graphics.Bitmap;
import android.graphics.Color;
@@ -29,6 +31,16 @@ public class ImageFilterBwFilter extends ImageFilter {
}
@Override
+ public int getButtonId() {
+ return R.id.bwfilterButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.bwfilter;
+ }
+
+ @Override
public ImageFilter clone() throws CloneNotSupportedException {
ImageFilterBwFilter filter = (ImageFilterBwFilter) super.clone();
return filter;
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java
index 0c3bb37ca..70e3d8589 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.filters;
+import com.android.gallery3d.R;
+
import android.graphics.Bitmap;
public class ImageFilterContrast extends ImageFilter {
@@ -24,6 +26,16 @@ public class ImageFilterContrast extends ImageFilter {
mName = "Contrast";
}
+ @Override
+ public int getButtonId() {
+ return R.id.contrastButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.contrast;
+ }
+
native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float strength);
@Override
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java
index 89641d103..46a00f47a 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java
@@ -18,6 +18,7 @@ package com.android.gallery3d.filtershow.filters;
import android.graphics.Bitmap;
+import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.ui.Spline;
public class ImageFilterCurves extends ImageFilter {
@@ -31,6 +32,26 @@ public class ImageFilterCurves extends ImageFilter {
}
@Override
+ public int getButtonId() {
+ return R.id.curvesButtonRGB;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.curvesRGB;
+ }
+
+ @Override
+ public int getOverlayBitmaps() {
+ return R.drawable.filtershow_button_colors_curve;
+ }
+
+ @Override
+ public int getEditingViewId() {
+ return R.id.imageCurves;
+ }
+
+ @Override
public ImageFilter clone() throws CloneNotSupportedException {
ImageFilterCurves filter = (ImageFilterCurves) super.clone();
for (int i = 0; i < 4; i++) {
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java
index e38dc8eb5..63f860171 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.filters;
+import com.android.gallery3d.R;
+
import android.graphics.Bitmap;
public class ImageFilterExposure extends ImageFilter {
@@ -24,6 +26,16 @@ public class ImageFilterExposure extends ImageFilter {
mName = "Exposure";
}
+ @Override
+ public int getButtonId() {
+ return R.id.exposureButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.exposure;
+ }
+
native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float bright);
@Override
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java
index d74a6faab..33ecc8ab9 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java
@@ -23,6 +23,7 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
+import com.android.gallery3d.filtershow.CropExtras;
import com.android.gallery3d.filtershow.imageshow.GeometryMath;
import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
@@ -69,22 +70,56 @@ public class ImageFilterGeometry extends ImageFilter {
// 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?)
+
+ CropExtras extras = mGeometry.getCropExtras();
+ boolean useExtras = mGeometry.getUseCropExtrasFlag();
+ int outputX = 0;
+ int outputY = 0;
+ boolean s = false;
+ if (extras != null && useExtras){
+ outputX = extras.getOutputX();
+ outputY = extras.getOutputY();
+ s = extras.getScaleUp();
+ }
+
+
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);
- Bitmap temp = null;
- if (mGeometry.hasSwitchedWidthHeight()) {
- temp = Bitmap.createBitmap(cropBounds.height(), cropBounds.width(), mConfig);
- } else {
- temp = Bitmap.createBitmap(cropBounds.width(), cropBounds.height(), mConfig);
+
+ int width = cropBounds.width();
+ int height = cropBounds.height();
+
+ if (mGeometry.hasSwitchedWidthHeight()){
+ int temp = width;
+ width = height;
+ height = temp;
}
+
+ if(outputX <= 0 || outputY <= 0){
+ outputX = width;
+ outputY = height;
+ }
+
+ float scaleX = 1;
+ float scaleY = 1;
+ if (s){
+ scaleX = (float) outputX / width;
+ scaleY = (float) outputY / height;
+ }
+
+ Bitmap temp = null;
+ temp = Bitmap.createBitmap(outputX, outputY, mConfig);
+
float[] displayCenter = {
temp.getWidth() / 2f, temp.getHeight() / 2f
};
Matrix m1 = mGeometry.buildTotalXform(bitmap.getWidth(), bitmap.getHeight(), displayCenter);
+ m1.postScale(scaleX, scaleY, displayCenter[0], displayCenter[1]);
+
Canvas canvas = new Canvas(temp);
Paint paint = new Paint();
paint.setAntiAlias(true);
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java
index 279718edb..e2ea388dc 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.filters;
+import com.android.gallery3d.R;
+
import android.graphics.Bitmap;
public class ImageFilterHue extends ImageFilter {
@@ -29,6 +31,16 @@ public class ImageFilterHue extends ImageFilter {
}
@Override
+ public int getButtonId() {
+ return R.id.hueButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.hue;
+ }
+
+ @Override
public ImageFilter clone() throws CloneNotSupportedException {
ImageFilterHue filter = (ImageFilterHue) super.clone();
filter.cmatrix = new ColorSpaceMatrix(cmatrix);
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterRedEye.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterRedEye.java
index c77de330f..9ae6f511e 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterRedEye.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterRedEye.java
@@ -17,40 +17,167 @@
package com.android.gallery3d.filtershow.filters;
import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.graphics.RectF;
-public class ImageFilterRedEye extends ImageFilter {
- private static final String TAG = "ImageFilterRedEye";
+import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
+
+import java.util.Vector;
+public class ImageFilterRedEye extends ImageFilter {
+ private static final String LOGTAG = "ImageFilterRedEye";
+ private Vector<RedEyeCandidate> mCandidates = null;
public ImageFilterRedEye() {
- mName = "Redeye";
+ mName = "Red Eye";
+ }
+ @Override
+ public int getButtonId() {
+ return R.id.redEyeButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.redeye;
+ }
+
+ @Override
+ public int getEditingViewId() {
+ return R.id.imageRedEyes;
}
@Override
public ImageFilter clone() throws CloneNotSupportedException {
ImageFilterRedEye filter = (ImageFilterRedEye) super.clone();
-
+ if (mCandidates != null) {
+ int size = mCandidates.size();
+ filter.mCandidates = new Vector<RedEyeCandidate>();
+ for (int i = 0; i < size; i++) {
+ filter.mCandidates.add(new RedEyeCandidate(mCandidates.elementAt(i)));
+ }
+ }
return filter;
}
- native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, short []matrix);
+ @Override
+ public boolean isNil() {
+ if (mCandidates != null && mCandidates.size() > 0) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean same(ImageFilter filter) {
+ boolean isRedEyeFilter = super.same(filter);
+ if (!isRedEyeFilter) {
+ return false;
+ }
+ ImageFilterRedEye redEyeFilter = (ImageFilterRedEye) filter;
+ if (redEyeFilter.mCandidates == null && mCandidates == null) {
+ return true;
+ }
+ if (redEyeFilter.mCandidates == null || mCandidates == null) {
+ return false;
+ }
+ if (redEyeFilter.mCandidates.size() != mCandidates.size()) {
+ return false;
+ }
+ int size = mCandidates.size();
+ for (int i = 0; i < size; i++) {
+ RedEyeCandidate c1 = mCandidates.elementAt(i);
+ RedEyeCandidate c2 = redEyeFilter.mCandidates.elementAt(i);
+ if (!c1.equals(c2)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public Vector<RedEyeCandidate> getCandidates() {
+ if (mCandidates == null) {
+ mCandidates = new Vector<RedEyeCandidate>();
+ }
+ return mCandidates;
+ }
+
+ public void addRect(RectF rect, RectF bounds) {
+ if (mCandidates == null) {
+ mCandidates = new Vector<RedEyeCandidate>();
+ }
+ Vector<RedEyeCandidate> intersects = new Vector<RedEyeCandidate>();
+ for (int i = 0; i < mCandidates.size(); i++) {
+ RedEyeCandidate r = mCandidates.elementAt(i);
+ if (r.intersect(rect)) {
+ intersects.add(r);
+ }
+ }
+ for (int i = 0; i < intersects.size(); i++) {
+ RedEyeCandidate r = intersects.elementAt(i);
+ rect.union(r.mRect);
+ bounds.union(r.mBounds);
+ mCandidates.remove(r);
+ }
+ mCandidates.add(new RedEyeCandidate(rect, bounds));
+ }
+
+ public void clear() {
+ if (mCandidates == null) {
+ mCandidates = new Vector<RedEyeCandidate>();
+ }
+ mCandidates.clear();
+ }
+
+ native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, short[] matrix);
@Override
public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
- float p = mParameter;
- float value = p;
- int box = Math.min(w, h);
- int sizex = Math.min((int)((p+100)*box/400),w/2);
- int sizey = Math.min((int)((p+100)*box/800),h/2);
-
- short [] rect = new short[]{
- (short) (w/2-sizex),(short) (w/2-sizey),
- (short) (2*sizex),(short) (2*sizey)};
+ short[] rect = new short[4];
- nativeApplyFilter(bitmap, w, h, rect);
+ if (mCandidates != null && mCandidates.size() > 0) {
+ for (int i = 0; i < mCandidates.size(); i++) {
+ RectF r = new RectF(mCandidates.elementAt(i).mRect);
+ GeometryMetadata geo = getImagePreset().mGeoData;
+ Matrix originalToScreen = geo.getOriginalToScreen(true,
+ getImagePreset().getImageLoader().getOriginalBounds().width(),
+ getImagePreset().getImageLoader().getOriginalBounds().height(),
+ w, h);
+ originalToScreen.mapRect(r);
+ if (r.left < 0) {
+ r.left = 0;
+ }
+ if (r.left > w) {
+ r.left = w;
+ }
+ if (r.top < 0) {
+ r.top = 0;
+ }
+ if (r.top > h) {
+ r.top = h;
+ }
+ if (r.right < 0) {
+ r.right = 0;
+ }
+ if (r.right > w) {
+ r.right = w;
+ }
+ if (r.bottom < 0) {
+ r.bottom = 0;
+ }
+ if (r.bottom > h) {
+ r.bottom = h;
+ }
+ rect[0] = (short) r.left;
+ rect[1] = (short) r.top;
+ rect[2] = (short) r.width();
+ rect[3] = (short) r.height();
+ nativeApplyFilter(bitmap, w, h, rect);
+ }
+ }
return bitmap;
}
}
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java
index 1d3459195..129165b3e 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.filters;
+import com.android.gallery3d.R;
+
import android.graphics.Bitmap;
public class ImageFilterSaturated extends ImageFilter {
@@ -24,6 +26,16 @@ public class ImageFilterSaturated extends ImageFilter {
mName = "Saturated";
}
+ @Override
+ public int getButtonId() {
+ return R.id.saturationButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.saturation;
+ }
+
native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float saturation);
@Override
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java
index 4e6b848ae..de8fcd5ea 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.filters;
+import com.android.gallery3d.R;
+
import android.graphics.Bitmap;
public class ImageFilterShadows extends ImageFilter {
@@ -26,6 +28,16 @@ public class ImageFilterShadows extends ImageFilter {
}
@Override
+ public int getButtonId() {
+ return R.id.shadowRecoveryButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.shadow_recovery;
+ }
+
+ @Override
public ImageFilter clone() throws CloneNotSupportedException {
ImageFilterShadows filter = (ImageFilterShadows) super.clone();
return filter;
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java
index a355539c2..1951b9b9e 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java
@@ -28,6 +28,26 @@ public class ImageFilterSharpen extends ImageFilterRS {
}
@Override
+ public int getButtonId() {
+ return R.id.sharpenButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.sharpness;
+ }
+
+ @Override
+ public int getOverlayBitmaps() {
+ return R.drawable.filtershow_button_colors_sharpen;
+ }
+
+ @Override
+ public int getEditingViewId() {
+ return R.id.imageZoom;
+ }
+
+ @Override
public void createFilter(android.content.res.Resources res, float scaleFactor,
boolean highQuality) {
int w = mInPixelsAllocation.getType().getX();
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java
index effd89ebe..36bd62630 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java
@@ -22,6 +22,7 @@ import android.graphics.RectF;
import com.adobe.xmp.XMPException;
import com.adobe.xmp.XMPMeta;
+import com.android.gallery3d.R;
import com.android.gallery3d.app.Log;
import com.android.gallery3d.filtershow.presets.ImagePreset;
@@ -59,6 +60,16 @@ public class ImageFilterTinyPlanet extends ImageFilter {
mAngle = 0;
}
+ @Override
+ public int getButtonId() {
+ return R.id.tinyplanetButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.tinyplanet;
+ }
+
public void setAngle(float angle) {
mAngle = angle;
}
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java
index 34f8b245e..7720d0490 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.filters;
+import com.android.gallery3d.R;
+
import android.graphics.Bitmap;
public class ImageFilterVibrance extends ImageFilter {
@@ -24,6 +26,16 @@ public class ImageFilterVibrance extends ImageFilter {
mName = "Vibrance";
}
+ @Override
+ public int getButtonId() {
+ return R.id.vibranceButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.vibrance;
+ }
+
native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float bright);
@Override
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java
index 7a471e5b9..3c904fa6c 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.filters;
+import com.android.gallery3d.R;
+
import android.graphics.Bitmap;
public class ImageFilterVignette extends ImageFilter {
@@ -25,6 +27,16 @@ public class ImageFilterVignette extends ImageFilter {
mName = "Vignette";
}
+ @Override
+ public int getButtonId() {
+ return R.id.vignetteButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.vignette;
+ }
+
native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float strength);
@Override
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
index b00b867b3..8665dc54c 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
@@ -16,6 +16,8 @@
package com.android.gallery3d.filtershow.filters;
+import com.android.gallery3d.R;
+
import android.graphics.Bitmap;
public class ImageFilterWBalance extends ImageFilter {
@@ -26,13 +28,32 @@ public class ImageFilterWBalance extends ImageFilter {
mName = "WBalance";
}
+ @Override
+ public int getButtonId() {
+ return R.id.wbalanceButton;
+ }
+
+ @Override
+ public int getTextId() {
+ return R.string.wbalance;
+ }
+
+ public boolean showEditingControls() {
+ return false;
+ }
+
native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, int locX, int locY);
@Override
public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
- nativeApplyFilter(bitmap, w, h, -1,-1);
+ nativeApplyFilter(bitmap, w, h, -1, -1);
return bitmap;
}
+
+ @Override
+ public boolean isNil() {
+ return false;
+ }
}
diff --git a/src/com/android/gallery3d/filtershow/filters/RedEyeCandidate.java b/src/com/android/gallery3d/filtershow/filters/RedEyeCandidate.java
new file mode 100644
index 000000000..58d3afa3b
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/filters/RedEyeCandidate.java
@@ -0,0 +1,50 @@
+/*
+ * 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.RectF;
+
+public class RedEyeCandidate {
+ RectF mRect = new RectF();
+ RectF mBounds = new RectF();
+
+ public RedEyeCandidate(RedEyeCandidate candidate) {
+ mRect.set(candidate.mRect);
+ mBounds.set(candidate.mBounds);
+ }
+
+ public RedEyeCandidate(RectF rect, RectF bounds) {
+ mRect.set(rect);
+ mBounds.set(bounds);
+ }
+
+ public boolean equals(RedEyeCandidate candidate) {
+ if (candidate.mRect.equals(mRect)
+ && candidate.mBounds.equals(mBounds)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean intersect(RectF rect) {
+ return mRect.intersect(rect);
+ }
+
+ public RectF getRect() {
+ return mRect;
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/BoundedRect.java b/src/com/android/gallery3d/filtershow/imageshow/BoundedRect.java
new file mode 100644
index 000000000..e94d1ed9e
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/imageshow/BoundedRect.java
@@ -0,0 +1,340 @@
+/*
+ * 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.Matrix;
+import android.graphics.RectF;
+
+import java.util.Arrays;
+
+/**
+ * Maintains invariant that inner rectangle is constrained to be within the
+ * outer, rotated rectangle.
+ */
+public class BoundedRect {
+ private float rot;
+ private RectF outer;
+ private RectF inner;
+ private float[] innerRotated;
+
+ public BoundedRect() {
+ rot = 0;
+ outer = new RectF();
+ inner = new RectF();
+ innerRotated = new float[8];
+ }
+
+ public BoundedRect(float rotation, RectF outerRect, RectF innerRect) {
+ rot = rotation;
+ outer = new RectF(outerRect);
+ inner = new RectF(innerRect);
+ innerRotated = CropMath.getCornersFromRect(inner);
+ rotateInner();
+ if (!isConstrained())
+ reconstrain();
+ }
+
+ /**
+ * Sets inner, and re-constrains it to fit within the rotated bounding rect.
+ */
+ public void setInner(RectF newInner) {
+ if (inner.equals(newInner))
+ return;
+ inner = newInner;
+ innerRotated = CropMath.getCornersFromRect(inner);
+ rotateInner();
+ if (!isConstrained())
+ reconstrain();
+ }
+
+ /**
+ * Sets rotation, and re-constrains inner to fit within the rotated bounding rect.
+ */
+ public void setRotation(float rotation) {
+ if (rotation == rot)
+ return;
+ rot = rotation;
+ innerRotated = CropMath.getCornersFromRect(inner);
+ rotateInner();
+ if (!isConstrained())
+ reconstrain();
+ }
+
+ public RectF getInner() {
+ return new RectF(inner);
+ }
+
+ /**
+ * Tries to move the inner rectangle by (dx, dy). If this would cause it to leave
+ * the bounding rectangle, snaps the inner rectangle to the edge of the bounding
+ * rectangle.
+ */
+ public void moveInner(float dx, float dy) {
+ Matrix m0 = getInverseRotMatrix();
+
+ RectF translatedInner = new RectF(inner);
+ translatedInner.offset(dx, dy);
+
+ float[] translatedInnerCorners = CropMath.getCornersFromRect(translatedInner);
+ float[] outerCorners = CropMath.getCornersFromRect(outer);
+
+ m0.mapPoints(translatedInnerCorners);
+ float[] correction = {
+ 0, 0
+ };
+
+ // find correction vectors for corners that have moved out of bounds
+ for (int i = 0; i < translatedInnerCorners.length; i += 2) {
+ float correctedInnerX = translatedInnerCorners[i] + correction[0];
+ float correctedInnerY = translatedInnerCorners[i + 1] + correction[1];
+ if (!CropMath.inclusiveContains(outer, correctedInnerX, correctedInnerY)) {
+ float[] badCorner = {
+ correctedInnerX, correctedInnerY
+ };
+ float[] nearestSide = CropMath.closestSide(badCorner, outerCorners);
+ float[] correctionVec =
+ GeometryMath.shortestVectorFromPointToLine(badCorner, nearestSide);
+ correction[0] += correctionVec[0];
+ correction[1] += correctionVec[1];
+ }
+ }
+
+ for (int i = 0; i < translatedInnerCorners.length; i += 2) {
+ float correctedInnerX = translatedInnerCorners[i] + correction[0];
+ float correctedInnerY = translatedInnerCorners[i + 1] + correction[1];
+ if (!CropMath.inclusiveContains(outer, correctedInnerX, correctedInnerY)) {
+ float[] correctionVec = {
+ correctedInnerX, correctedInnerY
+ };
+ CropMath.getEdgePoints(outer, correctionVec);
+ correctionVec[0] -= correctedInnerX;
+ correctionVec[1] -= correctedInnerY;
+ correction[0] += correctionVec[0];
+ correction[1] += correctionVec[1];
+ }
+ }
+
+ // Set correction
+ for (int i = 0; i < translatedInnerCorners.length; i += 2) {
+ float correctedInnerX = translatedInnerCorners[i] + correction[0];
+ float correctedInnerY = translatedInnerCorners[i + 1] + correction[1];
+ // update translated corners with correction vectors
+ translatedInnerCorners[i] = correctedInnerX;
+ translatedInnerCorners[i + 1] = correctedInnerY;
+ }
+
+ innerRotated = translatedInnerCorners;
+ // reconstrain to update inner
+ reconstrain();
+ }
+
+ /**
+ * Attempts to resize the inner rectangle. If this would cause it to leave
+ * the bounding rect, clips the inner rectangle to fit.
+ */
+ public void resizeInner(RectF newInner) {
+ Matrix m = getRotMatrix();
+ Matrix m0 = getInverseRotMatrix();
+
+ float[] outerCorners = CropMath.getCornersFromRect(outer);
+ m.mapPoints(outerCorners);
+ float[] oldInnerCorners = CropMath.getCornersFromRect(inner);
+ float[] newInnerCorners = CropMath.getCornersFromRect(newInner);
+ RectF ret = new RectF(newInner);
+
+ for (int i = 0; i < newInnerCorners.length; i += 2) {
+ float[] c = {
+ newInnerCorners[i], newInnerCorners[i + 1]
+ };
+ float[] c0 = Arrays.copyOf(c, 2);
+ m0.mapPoints(c0);
+ if (!CropMath.inclusiveContains(outer, c0[0], c0[1])) {
+ float[] outerSide = CropMath.closestSide(c, outerCorners);
+ float[] pathOfCorner = {
+ newInnerCorners[i], newInnerCorners[i + 1],
+ oldInnerCorners[i], oldInnerCorners[i + 1]
+ };
+ float[] p = GeometryMath.lineIntersect(pathOfCorner, outerSide);
+ if (p == null) {
+ // lines are parallel or not well defined, so don't resize
+ p = new float[2];
+ p[0] = oldInnerCorners[i];
+ p[1] = oldInnerCorners[i + 1];
+ }
+ // relies on corners being in same order as method
+ // getCornersFromRect
+ switch (i) {
+ case 0:
+ case 1:
+ ret.left = (p[0] > ret.left) ? p[0] : ret.left;
+ ret.top = (p[1] > ret.top) ? p[1] : ret.top;
+ break;
+ case 2:
+ case 3:
+ ret.right = (p[0] < ret.right) ? p[0] : ret.right;
+ ret.top = (p[1] > ret.top) ? p[1] : ret.top;
+ break;
+ case 4:
+ case 5:
+ ret.right = (p[0] < ret.right) ? p[0] : ret.right;
+ ret.bottom = (p[1] < ret.bottom) ? p[1] : ret.bottom;
+ break;
+ case 6:
+ case 7:
+ ret.left = (p[0] > ret.left) ? p[0] : ret.left;
+ ret.bottom = (p[1] < ret.bottom) ? p[1] : ret.bottom;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ float[] retCorners = CropMath.getCornersFromRect(ret);
+ m0.mapPoints(retCorners);
+ innerRotated = retCorners;
+ // reconstrain to update inner
+ reconstrain();
+ }
+
+ /**
+ * Attempts to resize the inner rectangle. If this would cause it to leave
+ * the bounding rect, clips the inner rectangle to fit while maintaining
+ * aspect ratio.
+ */
+ public void fixedAspectResizeInner(RectF newInner) {
+ Matrix m = getRotMatrix();
+ Matrix m0 = getInverseRotMatrix();
+
+ float aspectW = inner.width();
+ float aspectH = inner.height();
+ float aspRatio = aspectW / aspectH;
+ float[] corners = CropMath.getCornersFromRect(outer);
+
+ m.mapPoints(corners);
+ float[] oldInnerCorners = CropMath.getCornersFromRect(inner);
+ float[] newInnerCorners = CropMath.getCornersFromRect(newInner);
+
+ // find fixed corner
+ int fixed = -1;
+ if (inner.top == newInner.top) {
+ if (inner.left == newInner.left)
+ fixed = 0; // top left
+ else if (inner.right == newInner.right)
+ fixed = 2; // top right
+ } else if (inner.bottom == newInner.bottom) {
+ if (inner.right == newInner.right)
+ fixed = 4; // bottom right
+ else if (inner.left == newInner.left)
+ fixed = 6; // bottom left
+ }
+ // no fixed corner, return without update
+ if (fixed == -1)
+ return;
+ float widthSoFar = newInner.width();
+ int moved = -1;
+ for (int i = 0; i < newInnerCorners.length; i += 2) {
+ float[] c = {
+ newInnerCorners[i], newInnerCorners[i + 1]
+ };
+ float[] c0 = Arrays.copyOf(c, 2);
+ m0.mapPoints(c0);
+ if (!CropMath.inclusiveContains(outer, c0[0], c0[1])) {
+ moved = i;
+ if (moved == fixed)
+ continue;
+ float[] l2 = CropMath.closestSide(c, corners);
+ float[] l1 = {
+ newInnerCorners[i], newInnerCorners[i + 1],
+ oldInnerCorners[i], oldInnerCorners[i + 1]
+ };
+ float[] p = GeometryMath.lineIntersect(l1, l2);
+ if (p == null) {
+ // lines are parallel or not well defined, so set to old
+ // corner
+ p = new float[2];
+ p[0] = oldInnerCorners[i];
+ p[1] = oldInnerCorners[i + 1];
+ }
+ // relies on corners being in same order as method
+ // getCornersFromRect
+ float fixed_x = oldInnerCorners[fixed];
+ float fixed_y = oldInnerCorners[fixed + 1];
+ float newWidth = Math.abs(fixed_x - p[0]);
+ float newHeight = Math.abs(fixed_y - p[1]);
+ newWidth = Math.max(newWidth, aspRatio * newHeight);
+ if (newWidth < widthSoFar)
+ widthSoFar = newWidth;
+ }
+ }
+
+ float heightSoFar = widthSoFar / aspRatio;
+ RectF ret = new RectF(inner);
+ if (fixed == 0) {
+ ret.right = ret.left + widthSoFar;
+ ret.bottom = ret.top + heightSoFar;
+ } else if (fixed == 2) {
+ ret.left = ret.right - widthSoFar;
+ ret.bottom = ret.top + heightSoFar;
+ } else if (fixed == 4) {
+ ret.left = ret.right - widthSoFar;
+ ret.top = ret.bottom - heightSoFar;
+ } else if (fixed == 6) {
+ ret.right = ret.left + widthSoFar;
+ ret.top = ret.bottom - heightSoFar;
+ }
+ float[] retCorners = CropMath.getCornersFromRect(ret);
+ m0.mapPoints(retCorners);
+ innerRotated = retCorners;
+ // reconstrain to update inner
+ reconstrain();
+ }
+
+ // internal methods
+
+ private boolean isConstrained() {
+ for (int i = 0; i < 8; i += 2) {
+ if (!CropMath.inclusiveContains(outer, innerRotated[i], innerRotated[i + 1]))
+ return false;
+ }
+ return true;
+ }
+
+ private void reconstrain() {
+ // innerRotated has been changed to have incorrect values
+ CropMath.getEdgePoints(outer, innerRotated);
+ Matrix m = getRotMatrix();
+ float[] unrotated = Arrays.copyOf(innerRotated, 8);
+ m.mapPoints(unrotated);
+ inner = CropMath.trapToRect(unrotated);
+ }
+
+ private void rotateInner() {
+ Matrix m = getInverseRotMatrix();
+ m.mapPoints(innerRotated);
+ }
+
+ private Matrix getRotMatrix() {
+ Matrix m = new Matrix();
+ m.setRotate(rot, outer.centerX(), outer.centerY());
+ return m;
+ }
+
+ private Matrix getInverseRotMatrix() {
+ Matrix m = new Matrix();
+ m.setRotate(-rot, outer.centerX(), outer.centerY());
+ return m;
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/CropMath.java b/src/com/android/gallery3d/filtershow/imageshow/CropMath.java
new file mode 100644
index 000000000..9037ca043
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/imageshow/CropMath.java
@@ -0,0 +1,191 @@
+/*
+ * 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.Matrix;
+import android.graphics.RectF;
+
+import java.util.Arrays;
+
+public class CropMath {
+
+ /**
+ * Gets a float array of the 2D coordinates representing a rectangles
+ * corners.
+ * The order of the corners in the float array is:
+ * 0------->1
+ * ^ |
+ * | v
+ * 3<-------2
+ *
+ * @param r the rectangle to get the corners of
+ * @return the float array of corners (8 floats)
+ */
+
+ public static float[] getCornersFromRect(RectF r) {
+ float[] corners = {
+ r.left, r.top,
+ r.right, r.top,
+ r.right, r.bottom,
+ r.left, r.bottom
+ };
+ return corners;
+ }
+
+ /**
+ * Returns true iff point (x, y) is within or on the rectangle's bounds.
+ * RectF's "contains" function treats points on the bottom and right bound
+ * as not being contained.
+ *
+ * @param r the rectangle
+ * @param x the x value of the point
+ * @param y the y value of the point
+ * @return
+ */
+ public static boolean inclusiveContains(RectF r, float x, float y) {
+ return !(x > r.right || x < r.left || y > r.bottom || y < r.top);
+ }
+
+ /**
+ * Takes an array of 2D coordinates representing corners and returns the
+ * smallest rectangle containing those coordinates.
+ *
+ * @param array array of 2D coordinates
+ * @return smallest rectangle containing coordinates
+ */
+ public static RectF trapToRect(float[] array) {
+ RectF r = new RectF(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY,
+ Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
+ for (int i = 1; i < array.length; i += 2) {
+ float x = array[i - 1];
+ float y = array[i];
+ r.left = (x < r.left) ? x : r.left;
+ r.top = (y < r.top) ? y : r.top;
+ r.right = (x > r.right) ? x : r.right;
+ r.bottom = (y > r.bottom) ? y : r.bottom;
+ }
+ r.sort();
+ return r;
+ }
+
+ /**
+ * If edge point [x, y] in array [x0, y0, x1, y1, ...] is outside of the
+ * image bound rectangle, clamps it to the edge of the rectangle.
+ *
+ * @param imageBound the rectangle to clamp edge points to.
+ * @param array an array of points to clamp to the rectangle, gets set to
+ * the clamped values.
+ */
+ public static void getEdgePoints(RectF imageBound, float[] array) {
+ 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);
+ }
+ }
+
+ /**
+ * Takes a point and the corners of a rectangle and returns the two corners
+ * representing the side of the rectangle closest to the point.
+ *
+ * @param point the point which is being checked
+ * @param corners the corners of the rectangle
+ * @return two corners representing the side of the rectangle
+ */
+ public static float[] closestSide(float[] point, float[] corners) {
+ int len = corners.length;
+ float oldMag = Float.POSITIVE_INFINITY;
+ float[] bestLine = null;
+ for (int i = 0; i < len; i += 2) {
+ float[] line = {
+ corners[i], corners[(i + 1) % len],
+ corners[(i + 2) % len], corners[(i + 3) % len]
+ };
+ float mag = GeometryMath.vectorLength(
+ GeometryMath.shortestVectorFromPointToLine(point, line));
+ if (mag < oldMag) {
+ oldMag = mag;
+ bestLine = line;
+ }
+ }
+ return bestLine;
+ }
+
+ /**
+ * Checks if a given point is within a rotated rectangle.
+ *
+ * @param point 2D point to check
+ * @param bound rectangle to rotate
+ * @param rot angle of rotation about rectangle center
+ * @return true if point is within rotated rectangle
+ */
+ public static boolean pointInRotatedRect(float[] point, RectF bound, float rot) {
+ Matrix m = new Matrix();
+ float[] p = Arrays.copyOf(point, 2);
+ m.setRotate(rot, bound.centerX(), bound.centerY());
+ Matrix m0 = new Matrix();
+ if (!m.invert(m0))
+ return false;
+ m0.mapPoints(p);
+ return inclusiveContains(bound, p[0], p[1]);
+ }
+
+ /**
+ * Checks if a given point is within a rotated rectangle.
+ *
+ * @param point 2D point to check
+ * @param rotatedRect corners of a rotated rectangle
+ * @param center center of the rotated rectangle
+ * @return true if point is within rotated rectangle
+ */
+ public static boolean pointInRotatedRect(float[] point, float[] rotatedRect, float[] center) {
+ RectF unrotated = new RectF();
+ float angle = getUnrotated(rotatedRect, center, unrotated);
+ return pointInRotatedRect(point, unrotated, angle);
+ }
+
+ /**
+ * Resizes rectangle to have a certain aspect ratio (center remains
+ * stationary).
+ *
+ * @param r rectangle to resize
+ * @param w new width aspect
+ * @param h new height aspect
+ */
+ public static void fixAspectRatio(RectF r, float w, float h) {
+ float scale = Math.min(r.width() / w, r.height() / h);
+ float centX = r.centerX();
+ float centY = r.centerY();
+ float hw = scale * w / 2;
+ float hh = scale * h / 2;
+ r.set(centX - hw, centY - hh, centX + hw, centY + hh);
+ }
+
+ 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(trapToRect(unrotatedRect));
+ return angle;
+ }
+
+}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java
index 55f791820..568dadfc3 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryMath.java
@@ -26,11 +26,37 @@ public class GeometryMath {
return Math.max(Math.min(i, high), low);
}
- protected static float[] shortestVectorFromPointToLine(float[] point, float[] l1, float[] l2) {
- float x1 = l1[0];
- float x2 = l2[0];
- float y1 = l1[1];
- float y2 = l2[1];
+ 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)
@@ -40,67 +66,75 @@ public class GeometryMath {
float[] ret = {
(x1 + u * (x2 - x1)), (y1 + u * (y2 - y1))
};
- float [] vec = {ret[0] - point[0], ret[1] - point[1] };
+ float[] vec = {
+ ret[0] - point[0], ret[1] - point[1]
+ };
return vec;
}
// A . B
- public static float dotProduct(float[] a, float[] b){
+ public static float dotProduct(float[] a, float[] b) {
return a[0] * b[0] + a[1] * b[1];
}
- public static float[] normalize(float[] a){
+ 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 };
+ float[] b = {
+ a[0] / length, a[1] / length
+ };
return b;
}
// A onto B
- public static float scalarProjection(float[] a, float[] 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] };
+ 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] };
+ 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){
+ 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){
+ 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++){
+ 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){
+ 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);
+ return Math.min(newWidth / oldWidth, newHeight / oldHeight);
}
- public static Rect roundNearest(RectF r){
+ 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/GeometryMetadata.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
index dffdc2449..b53284061 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java
@@ -21,12 +21,11 @@ import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
+import com.android.gallery3d.filtershow.CropExtras;
import com.android.gallery3d.filtershow.cache.ImageLoader;
import com.android.gallery3d.filtershow.filters.ImageFilterGeometry;
public class GeometryMetadata {
- // Applied in order: rotate, crop, scale.
- // Do not scale saved image (presumably?).
private static final ImageFilterGeometry mImageFilter = new ImageFilterGeometry();
private static final String LOGTAG = "GeometryMetadata";
private float mScaleFactor = 1.0f;
@@ -36,12 +35,29 @@ public class GeometryMetadata {
private final RectF mPhotoBounds = new RectF();
private FLIP mFlip = FLIP.NONE;
- private RectF mBounds = new RectF();
-
public enum FLIP {
NONE, VERTICAL, HORIZONTAL, BOTH
}
+ // Output format data from intent extras
+ private boolean mUseCropExtras = false;
+ private CropExtras mCropExtras = null;
+ public void setUseCropExtrasFlag(boolean f){
+ mUseCropExtras = f;
+ }
+
+ public boolean getUseCropExtrasFlag(){
+ return mUseCropExtras;
+ }
+
+ public void setCropExtras(CropExtras e){
+ mCropExtras = e;
+ }
+
+ public CropExtras getCropExtras(){
+ return mCropExtras;
+ }
+
public GeometryMetadata() {
}
@@ -86,7 +102,11 @@ public class GeometryMetadata {
mCropBounds.set(g.mCropBounds);
mPhotoBounds.set(g.mPhotoBounds);
mFlip = g.mFlip;
- mBounds = g.mBounds;
+
+ mUseCropExtras = g.mUseCropExtras;
+ if (g.mCropExtras != null){
+ mCropExtras = new CropExtras(g.mCropExtras);
+ }
}
public float getScaleFactor() {
@@ -184,48 +204,16 @@ public class GeometryMetadata {
+ ",photoRect=" + mPhotoBounds.toShortString() + "]";
}
- // TODO: refactor away
- protected static Matrix getHorizontalMatrix(float width) {
- Matrix flipHorizontalMatrix = new Matrix();
- flipHorizontalMatrix.setScale(-1, 1);
- flipHorizontalMatrix.postTranslate(width, 0);
- return flipHorizontalMatrix;
- }
-
protected static void concatHorizontalMatrix(Matrix m, float width) {
m.postScale(-1, 1);
m.postTranslate(width, 0);
}
- // TODO: refactor away
- protected static Matrix getVerticalMatrix(float height) {
- Matrix flipVerticalMatrix = new Matrix();
- flipVerticalMatrix.setScale(1, -1);
- flipVerticalMatrix.postTranslate(0, height);
- return flipVerticalMatrix;
- }
-
protected static void concatVerticalMatrix(Matrix m, float height) {
m.postScale(1, -1);
m.postTranslate(0, height);
}
- // TODO: refactor away
- public static Matrix getFlipMatrix(float width, float height, FLIP type) {
- if (type == FLIP.HORIZONTAL) {
- return getHorizontalMatrix(width);
- } else if (type == FLIP.VERTICAL) {
- return getVerticalMatrix(height);
- } else if (type == FLIP.BOTH) {
- Matrix flipper = getVerticalMatrix(height);
- flipper.postConcat(getHorizontalMatrix(width));
- return flipper;
- } else {
- Matrix m = new Matrix();
- m.reset(); // identity
- return m;
- }
- }
public static void concatMirrorMatrix(Matrix m, float width, float height, FLIP type) {
if (type == FLIP.HORIZONTAL) {
@@ -331,46 +319,10 @@ public class GeometryMetadata {
return m1;
}
- // TODO: refactor away
- public Matrix getFlipMatrix(float width, float height) {
- FLIP type = getFlipType();
- return getFlipMatrix(width, height, type);
- }
-
public boolean hasSwitchedWidthHeight() {
return (((int) (mRotation / 90)) % 2) != 0;
}
- // TODO: refactor away
- public Matrix buildGeometryMatrix(float width, float height, float scaling, float dx, float dy,
- float rotation) {
- float dx0 = width / 2;
- float dy0 = height / 2;
- Matrix m = getFlipMatrix(width, height);
- m.postTranslate(-dx0, -dy0);
- m.postRotate(rotation);
- m.postScale(scaling, scaling);
- m.postTranslate(dx, dy);
- return m;
- }
-
- // TODO: refactor away
- public Matrix buildGeometryMatrix(float width, float height, float scaling, float dx, float dy,
- boolean onlyRotate) {
- float rot = mRotation;
- if (!onlyRotate) {
- rot += mStraightenRotation;
- }
- return buildGeometryMatrix(width, height, scaling, dx, dy, rot);
- }
-
- // TODO: refactor away
- public Matrix buildGeometryUIMatrix(float scaling, float dx, float dy) {
- float w = mPhotoBounds.width();
- float h = mPhotoBounds.height();
- return buildGeometryMatrix(w, h, scaling, dx, dy, false);
- }
-
public static Matrix buildPhotoMatrix(RectF photo, RectF crop, float rotation,
float straighten, FLIP type) {
Matrix m = new Matrix();
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
index a352a16e7..594e008a2 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
@@ -29,22 +29,25 @@ import android.util.AttributeSet;
import android.util.Log;
import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.CropExtras;
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
+ // 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 final float MIN_CROP_WIDTH_HEIGHT = 0.1f;
+ private static int mMinSideSize = 100;
private static int mTouchTolerance = 45;
private boolean mFirstDraw = true;
@@ -53,23 +56,30 @@ public class ImageCrop extends ImageGeometry {
private boolean mFixAspectRatio = false;
private float mLastRot = 0;
- private final Paint borderPaint;
+ 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 int mAspectTextSize = 24;
- public void setAspectTextSize(int textSize){
+ public void setAspectTextSize(int textSize) {
mAspectTextSize = textSize;
}
- public void setAspectString(String a){
+ public void setAspectString(String a) {
mAspect = a;
}
@@ -80,10 +90,6 @@ public class ImageCrop extends ImageGeometry {
Resources resources = context.getResources();
cropIndicator = resources.getDrawable(R.drawable.camera_crop);
indicatorSize = (int) resources.getDimension(R.dimen.crop_indicator_size);
- borderPaint = new Paint();
- borderPaint.setStyle(Paint.Style.STROKE);
- borderPaint.setColor(mBorderColor);
- borderPaint.setStrokeWidth(2f);
}
public ImageCrop(Context context, AttributeSet attrs) {
@@ -91,10 +97,6 @@ public class ImageCrop extends ImageGeometry {
Resources resources = context.getResources();
cropIndicator = resources.getDrawable(R.drawable.camera_crop);
indicatorSize = (int) resources.getDimension(R.dimen.crop_indicator_size);
- borderPaint = new Paint();
- borderPaint.setStyle(Paint.Style.STROKE);
- borderPaint.setColor(mBorderColor);
- borderPaint.setStrokeWidth(2f);
}
@Override
@@ -102,84 +104,46 @@ public class ImageCrop extends ImageGeometry {
return getContext().getString(R.string.crop);
}
- private void swapAspect(){
+ private void swapAspect() {
+ if (mDoingCropIntentAction) {
+ return;
+ }
float temp = mAspectWidth;
mAspectWidth = mAspectHeight;
mAspectHeight = temp;
}
- public static void setTouchTolerance(int tolerance){
+ /**
+ * Set tolerance for crop marker selection (in pixels)
+ */
+ public static void setTouchTolerance(int tolerance) {
mTouchTolerance = tolerance;
}
- private boolean switchCropBounds(int moving_corner, RectF dst) {
- RectF crop = getCropBoundsDisplayed();
- float dx1 = 0;
- float dy1 = 0;
- float dx2 = 0;
- float dy2 = 0;
- if ((moving_corner & MOVE_RIGHT) != 0) {
- dx1 = mCurrentX - crop.right;
- } else if ((moving_corner & MOVE_LEFT) != 0) {
- dx1 = mCurrentX - crop.left;
- }
- if ((moving_corner & MOVE_BOTTOM) != 0) {
- dy1 = mCurrentY - crop.bottom;
- } else if ((moving_corner & MOVE_TOP) != 0) {
- dy1 = mCurrentY - crop.top;
- }
- RectF newCrop = null;
- //Fix opposite corner in place and move sides
- if (moving_corner == BOTTOM_RIGHT) {
- newCrop = new RectF(crop.left, crop.top, crop.left + crop.height(), crop.top
- + crop.width());
- } else if (moving_corner == BOTTOM_LEFT) {
- newCrop = new RectF(crop.right - crop.height(), crop.top, crop.right, crop.top
- + crop.width());
- } else if (moving_corner == TOP_LEFT) {
- newCrop = new RectF(crop.right - crop.height(), crop.bottom - crop.width(),
- crop.right, crop.bottom);
- } else if (moving_corner == TOP_RIGHT) {
- newCrop = new RectF(crop.left, crop.bottom - crop.width(), crop.left
- + crop.height(), crop.bottom);
- }
- if ((moving_corner & MOVE_RIGHT) != 0) {
- dx2 = mCurrentX - newCrop.right;
- } else if ((moving_corner & MOVE_LEFT) != 0) {
- dx2 = mCurrentX - newCrop.left;
- }
- if ((moving_corner & MOVE_BOTTOM) != 0) {
- dy2 = mCurrentY - newCrop.bottom;
- } else if ((moving_corner & MOVE_TOP) != 0) {
- dy2 = mCurrentY - newCrop.top;
- }
- if (Math.sqrt(dx1*dx1 + dy1*dy1) > Math.sqrt(dx2*dx2 + dy2*dy2)){
- Matrix m = getCropBoundDisplayMatrix();
- Matrix m0 = new Matrix();
- if (!m.invert(m0)){
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO INVERT CROP MATRIX");
- return false;
- }
- if (!m0.mapRect(newCrop)){
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO MAP RECTANGLE TO RECTANGLE");
- return false;
- }
- swapAspect();
- dst.set(newCrop);
- return true;
- }
- return false;
+ /**
+ * Set minimum side length for crop box (in pixels)
+ */
+ public static void setMinCropSize(int minHeightWidth) {
+ mMinSideSize = minHeightWidth;
+ }
+
+ public void setExtras(CropExtras e) {
+ mCropExtras = e;
+ }
+
+ public void setCropActionFlag(boolean f) {
+ mDoingCropIntentAction = f;
}
- public void apply(float w, float h){
+ public void apply(float w, float h) {
mFixAspectRatio = true;
mAspectWidth = w;
mAspectHeight = h;
setLocalCropBounds(getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
getLocalStraighten()));
- cropSetup();
+ if (mVisibilityGained) {
+ cropSetup();
+ }
saveAndSetPreset();
invalidate();
}
@@ -194,202 +158,159 @@ public class ImageCrop extends ImageGeometry {
mAspectHeight = h / scale;
setLocalCropBounds(getUntranslatedStraightenCropBounds(photobounds,
getLocalStraighten()));
- cropSetup();
+ if (mVisibilityGained) {
+ cropSetup();
+ }
saveAndSetPreset();
invalidate();
}
public void applyClear() {
mFixAspectRatio = false;
+ mAspectWidth = 1;
+ mAspectHeight = 1;
setLocalCropBounds(getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
getLocalStraighten()));
- cropSetup();
+ if (mVisibilityGained) {
+ cropSetup();
+ }
saveAndSetPreset();
invalidate();
}
- private float getScaledMinWidthHeight() {
- RectF disp = new RectF(0, 0, getWidth(), getHeight());
- float scaled = Math.min(disp.width(), disp.height()) * MIN_CROP_WIDTH_HEIGHT
- / computeScale(getWidth(), getHeight());
- return scaled;
- }
-
- protected Matrix getCropRotationMatrix(float rotation, RectF localImage) {
- Matrix m = getLocalGeoFlipMatrix(localImage.width(), localImage.height());
- m.postRotate(rotation, localImage.centerX(), localImage.centerY());
- if (!m.rectStaysRect()) {
- return null;
- }
- return m;
- }
-
- protected Matrix getCropBoundDisplayMatrix(){
- Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
- if (m == null) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO MAP CROP BOUNDS TO RECTANGLE");
- m = new Matrix();
- }
- float zoom = computeScale(getWidth(), getHeight());
- m.postTranslate(mXOffset, mYOffset);
- m.postScale(zoom, zoom, mCenterX, mCenterY);
- return m;
- }
-
- protected RectF getCropBoundsDisplayed() {
- RectF bounds = getLocalCropBounds();
- RectF crop = new RectF(bounds);
- Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
-
- if (m == null) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO MAP CROP BOUNDS TO RECTANGLE");
- m = new Matrix();
+ public void clear() {
+ if (mCropExtras != null) {
+ int x = mCropExtras.getAspectX();
+ int y = mCropExtras.getAspectY();
+ if (mDoingCropIntentAction && x > 0 && y > 0) {
+ apply(x, y);
+ }
} else {
- m.mapRect(crop);
+ applyClear();
}
- m = new Matrix();
- float zoom = computeScale(getWidth(), getHeight());
- m.setScale(zoom, zoom, mCenterX, mCenterY);
- m.preTranslate(mXOffset, mYOffset);
- m.mapRect(crop);
- return crop;
}
- private RectF getRotatedCropBounds() {
- RectF bounds = getLocalCropBounds();
- RectF crop = new RectF(bounds);
- Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
-
- if (m == null) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO MAP CROP BOUNDS TO RECTANGLE");
- return null;
- } else {
- m.mapRect(crop);
- }
- return crop;
+ 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(), getLocalFlip(), displayCenter);
+ m.preScale(scale, scale);
+ return m;
}
- private RectF getUnrotatedCropBounds(RectF cropBounds) {
- Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
-
- if (m == null) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO GET ROTATION MATRIX");
- return null;
- }
- Matrix m0 = new Matrix();
- if (!m.invert(m0)) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO INVERT ROTATION MATRIX");
- return null;
- }
- RectF crop = new RectF(cropBounds);
- if (!m0.mapRect(crop)) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO UNROTATE CROPPING BOUNDS");
- return null;
- }
- return crop;
+ 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(), getLocalFlip(), displayCenter);
+ m1.preScale(scale, scale);
+ return m1;
}
- private RectF getRotatedStraightenBounds() {
- RectF straightenBounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
- getLocalStraighten());
- Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
-
- if (m == null) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO MAP STRAIGHTEN BOUNDS TO RECTANGLE");
- return null;
- } else {
- m.mapRect(straightenBounds);
- }
- return straightenBounds;
+ /**
+ * 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 void setCropBounds(RectF bounds) {
- // Avoid cropping smaller than minimum width or height.
+ public boolean setCropBounds(RectF bounds) {
RectF cbounds = new RectF(bounds);
- float minWidthHeight = getScaledMinWidthHeight();
- float aw = mAspectWidth;
- float ah = mAspectHeight;
- if (mFixAspectRatio) {
- minWidthHeight /= aw * ah;
- int r = (int) (getLocalRotation() / 90);
- if (r % 2 != 0) {
- float temp = aw;
- aw = ah;
- ah = temp;
- }
- }
-
+ 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();
- if (mFixAspectRatio) {
- if (newWidth < (minWidthHeight * aw) || newHeight < (minWidthHeight * ah)) {
- newWidth = minWidthHeight * aw;
- newHeight = minWidthHeight * ah;
- }
- } else {
- if (newWidth < minWidthHeight) {
- newWidth = minWidthHeight;
- }
- if (newHeight < minWidthHeight) {
- newHeight = minWidthHeight;
- }
- }
+ float scale = getTransformState(null, null, null);
+ float minWidthHeight = mMinSideSize / scale;
RectF pbounds = getLocalPhotoBounds();
- if (pbounds.width() < minWidthHeight) {
- newWidth = pbounds.width();
+
+ // if photo is smaller than minimum, refuse to set crop bounds
+ if (pbounds.width() < minWidthHeight || pbounds.height() < minWidthHeight) {
+ return false;
}
- if (pbounds.height() < minWidthHeight) {
- newHeight = pbounds.height();
+
+ // if incoming crop is smaller than minimum, refuse to set crop bounds
+ if (newWidth < minWidthHeight || newHeight < minWidthHeight) {
+ return false;
}
- cbounds.set(cbounds.left, cbounds.top, cbounds.left + newWidth, cbounds.top + newHeight);
- RectF straightenBounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
- getLocalStraighten());
- cbounds.intersect(straightenBounds);
+ float newX = bounds.centerX() - (getWidth() / 2f);
+ float newY = bounds.centerY() - (getHeight() / 2f);
+ mOffset[0] = newX;
+ mOffset[1] = newY;
- if (mFixAspectRatio) {
- fixAspectRatio(cbounds, aw, ah);
- }
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) {
- RectF cropped = getCropBoundsDisplayed();
+ Matrix m = getCropBoundDisplayedMatrix();
+ RectF cropped = getLocalCropBounds();
+ m.mapRect(cropped);
+ mBounded = getBoundedCrop(cropped);
movingEdges = 0;
- // Check left or right.
float left = Math.abs(x - cropped.left);
float right = Math.abs(x - cropped.right);
- if ((left <= mTouchTolerance) && (left < 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) {
+ else if ((right <= mTouchTolerance) && ((y + mTouchTolerance) >= cropped.top)
+ && ((y - mTouchTolerance) <= cropped.bottom)) {
movingEdges |= MOVE_RIGHT;
}
// Check top or bottom.
- float top = Math.abs(y - cropped.top);
- float bottom = Math.abs(y - cropped.bottom);
- if ((top <= mTouchTolerance) & (top < bottom)) {
+ if ((top <= mTouchTolerance) && ((x + mTouchTolerance) >= cropped.left)
+ && ((x - mTouchTolerance) <= cropped.right) && (top < bottom)) {
movingEdges |= MOVE_TOP;
}
- else if (bottom <= mTouchTolerance) {
+ else if ((bottom <= mTouchTolerance) && ((x + mTouchTolerance) >= cropped.left)
+ && ((x - mTouchTolerance) <= cropped.right)) {
movingEdges |= MOVE_BOTTOM;
}
- // Check inside block.
- if (cropped.contains(x, y) && (movingEdges == 0)) {
+ if (movingEdges == 0) {
movingEdges = MOVE_BLOCK;
}
if (mFixAspectRatio && (movingEdges != MOVE_BLOCK)) {
@@ -398,7 +319,7 @@ public class ImageCrop extends ImageGeometry {
invalidate();
}
- private int fixEdgeToCorner(int moving_edges){
+ private int fixEdgeToCorner(int moving_edges) {
if (moving_edges == MOVE_LEFT) {
moving_edges |= MOVE_TOP;
}
@@ -414,9 +335,9 @@ public class ImageCrop extends ImageGeometry {
return moving_edges;
}
- private RectF fixedCornerResize(RectF r, int moving_corner, float dx, float dy){
+ private RectF fixedCornerResize(RectF r, int moving_corner, float dx, float dy) {
RectF newCrop = null;
- //Fix opposite corner in place and move sides
+ // 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);
@@ -434,120 +355,90 @@ public class ImageCrop extends ImageGeometry {
}
private void moveEdges(float dX, float dY) {
- RectF cropped = getRotatedCropBounds();
- float minWidthHeight = getScaledMinWidthHeight();
- float scale = computeScale(getWidth(), getHeight());
- float deltaX = dX / scale;
- float deltaY = dY / scale;
- int select = movingEdges;
- if (mFixAspectRatio && (select != MOVE_BLOCK)) {
-
- // TODO: add in orientation change for fixed aspect
- /*if (select == TOP_LEFT || select == TOP_RIGHT ||
- select == BOTTOM_LEFT || select == BOTTOM_RIGHT){
- RectF blank = new RectF();
- if(switchCropBounds(select, blank)){
- setCropBounds(blank);
- return;
- }
- }*/
- if (select == MOVE_LEFT) {
- select |= MOVE_TOP;
- }
- if (select == MOVE_TOP) {
- select |= MOVE_LEFT;
- }
- if (select == MOVE_RIGHT) {
- select |= MOVE_BOTTOM;
- }
- if (select == MOVE_BOTTOM) {
- select |= MOVE_RIGHT;
- }
- }
-
- if (select == MOVE_BLOCK) {
- RectF straight = getRotatedStraightenBounds();
- // Move the whole cropped bounds within the photo display bounds.
- deltaX = (deltaX > 0) ? Math.min(straight.right - cropped.right, deltaX)
- : Math.max(straight.left - cropped.left, deltaX);
- deltaY = (deltaY > 0) ? Math.min(straight.bottom - cropped.bottom, deltaY)
- : Math.max(straight.top - cropped.top, deltaY);
- cropped.offset(deltaX, deltaY);
+ 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 ((select & MOVE_LEFT) != 0) {
- dx = Math.min(cropped.left + deltaX, cropped.right - minWidthHeight) - cropped.left;
+ if ((movingEdges & MOVE_LEFT) != 0) {
+ dx = Math.min(crop.left + dX, crop.right - minWidthHeight) - crop.left;
}
- if ((select & MOVE_TOP) != 0) {
- dy = Math.min(cropped.top + deltaY, cropped.bottom - minWidthHeight) - cropped.top;
+ if ((movingEdges & MOVE_TOP) != 0) {
+ dy = Math.min(crop.top + dY, crop.bottom - minWidthHeight) - crop.top;
}
- if ((select & MOVE_RIGHT) != 0) {
- dx = Math.max(cropped.right + deltaX, cropped.left + minWidthHeight)
- - cropped.right;
+ if ((movingEdges & MOVE_RIGHT) != 0) {
+ dx = Math.max(crop.right + dX, crop.left + minWidthHeight)
+ - crop.right;
}
- if ((select & MOVE_BOTTOM) != 0) {
- dy = Math.max(cropped.bottom + deltaY, cropped.top + minWidthHeight)
- - cropped.bottom;
+ if ((movingEdges & MOVE_BOTTOM) != 0) {
+ dy = Math.max(crop.bottom + dY, crop.top + minWidthHeight)
+ - crop.bottom;
}
if (mFixAspectRatio) {
- RectF crop = getCropBoundsDisplayed();
- float [] l1 = {crop.left, crop.bottom};
- float [] l2 = {crop.right, crop.top};
- if(movingEdges == TOP_LEFT || movingEdges == BOTTOM_RIGHT){
+ 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[] 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, select, dx * scale, dy * scale);
- Matrix m = getCropBoundDisplayMatrix();
- Matrix m0 = new Matrix();
- if (!m.invert(m0)){
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO INVERT CROP MATRIX");
- return;
- }
- if (!m0.mapRect(newCrop)){
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO MAP RECTANGLE TO RECTANGLE");
- return;
- }
+ RectF newCrop = fixedCornerResize(crop, movingEdges, dx, dy);
+
+ mBounded.fixedAspectResizeInner(newCrop);
+ newCrop = mBounded.getInner();
setCropBounds(newCrop);
return;
} else {
- if ((select & MOVE_LEFT) != 0) {
- cropped.left += dx;
+ if ((movingEdges & MOVE_LEFT) != 0) {
+ crop.left += dx;
}
- if ((select & MOVE_TOP) != 0) {
- cropped.top += dy;
+ if ((movingEdges & MOVE_TOP) != 0) {
+ crop.top += dy;
}
- if ((select & MOVE_RIGHT) != 0) {
- cropped.right += dx;
+ if ((movingEdges & MOVE_RIGHT) != 0) {
+ crop.right += dx;
}
- if ((select & MOVE_BOTTOM) != 0) {
- cropped.bottom += dy;
+ if ((movingEdges & MOVE_BOTTOM) != 0) {
+ crop.bottom += dy;
}
}
}
- movingEdges = select;
- Matrix m = getCropRotationMatrix(getLocalRotation(), getLocalPhotoBounds());
- Matrix m0 = new Matrix();
- if (!m.invert(m0)) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO INVERT ROTATION MATRIX");
- }
- if (!m0.mapRect(cropped)) {
- if (LOGV)
- Log.v(LOGTAG, "FAILED TO UNROTATE CROPPING BOUNDS");
- }
- setCropBounds(cropped);
+ mBounded.resizeInner(crop);
+ crop = mBounded.getInner();
+ setCropBounds(crop);
}
private void drawIndicator(Canvas canvas, Drawable indicator, float centerX, float centerY) {
@@ -560,7 +451,8 @@ public class ImageCrop extends ImageGeometry {
@Override
protected void setActionDown(float x, float y) {
super.setActionDown(x, y);
- detectMovingEdges(x, y);
+ detectMovingEdges(x + mOffset[0], y + mOffset[1]);
+
}
@Override
@@ -571,20 +463,54 @@ public class ImageCrop extends ImageGeometry {
@Override
protected void setActionMove(float x, float y) {
- if (movingEdges != 0){
+
+ if (movingEdges != 0) {
moveEdges(x - mCurrentX, y - mCurrentY);
}
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) {
- RectF cb = getRotatedCropBounds();
- fixAspectRatio(cb, mAspectWidth, mAspectHeight);
- RectF cb0 = getUnrotatedCropBounds(cb);
- setCropBounds(cb0);
- } else {
- setCropBounds(getLocalCropBounds());
+ 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);
+ }
}
}
@@ -592,7 +518,7 @@ public class ImageCrop extends ImageGeometry {
public void imageLoaded() {
super.imageLoaded();
syncLocalToMasterGeometry();
- applyClear();
+ clear();
invalidate();
}
@@ -600,7 +526,7 @@ public class ImageCrop extends ImageGeometry {
protected void gainedVisibility() {
float rot = getLocalRotation();
// if has changed orientation via rotate
- if( ((int) ((rot - mLastRot) / 90)) % 2 != 0 ){
+ if (((int) ((rot - mLastRot) / 90)) % 2 != 0) {
swapAspect();
}
cropSetup();
@@ -610,7 +536,6 @@ public class ImageCrop extends ImageGeometry {
@Override
public void resetParameter() {
super.resetParameter();
- cropSetup();
}
@Override
@@ -635,7 +560,6 @@ public class ImageCrop extends ImageGeometry {
@Override
protected void drawShape(Canvas canvas, Bitmap image) {
- // TODO: move style to xml
gPaint.setAntiAlias(true);
gPaint.setFilterBitmap(true);
gPaint.setDither(true);
@@ -645,91 +569,102 @@ public class ImageCrop extends ImageGeometry {
cropSetup();
mFirstDraw = false;
}
- float rotation = getLocalRotation();
- RectF crop = drawTransformed(canvas, image, gPaint);
+ RectF crop = drawTransformed(canvas, image, gPaint, mOffset);
gPaint.setColor(mBorderColor);
gPaint.setStrokeWidth(3);
gPaint.setStyle(Paint.Style.STROKE);
- drawRuleOfThird(canvas, crop, gPaint);
-
- if (mFixAspectRatio){
- 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;
+
+ 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);
+ gPaint.setTextAlign(Paint.Align.CENTER);
+ gPaint.setTextSize(mAspectTextSize);
+ canvas.drawText(mAspect, centX, centY, gPaint);
+ }
}
- gPaint.setColor(mBorderColor);
- gPaint.setStrokeWidth(3);
- gPaint.setStyle(Paint.Style.STROKE);
- drawStraighten(canvas, gPaint);
-
- int decoded_moving = decoder(movingEdges, rotation);
- canvas.save();
- canvas.rotate(rotation, mCenterX, mCenterY);
- RectF scaledCrop = unrotatedCropBounds();
- boolean notMoving = decoded_moving == 0;
- if (((decoded_moving & MOVE_TOP) != 0) || notMoving) {
- drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.top);
- }
- if (((decoded_moving & MOVE_BOTTOM) != 0) || notMoving) {
- drawIndicator(canvas, cropIndicator, scaledCrop.centerX(), scaledCrop.bottom);
- }
- if (((decoded_moving & MOVE_LEFT) != 0) || notMoving) {
- drawIndicator(canvas, cropIndicator, scaledCrop.left, scaledCrop.centerY());
- }
- if (((decoded_moving & MOVE_RIGHT) != 0) || notMoving) {
- drawIndicator(canvas, cropIndicator, scaledCrop.right, scaledCrop.centerY());
+ if (doThirds) {
+ drawRuleOfThird(canvas, crop, gPaint);
+
}
- canvas.restore();
- }
-
- 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;
- }
-
- protected int decoder(int movingEdges, float rotation) {
- int rot = constrainedRotation(rotation);
- switch (rot) {
- case 90:
- return bitCycleLeft(movingEdges, 3, 4);
- case 180:
- return bitCycleLeft(movingEdges, 2, 4);
- case 270:
- return bitCycleLeft(movingEdges, 1, 4);
- default:
- return movingEdges;
+
+ 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());
+ }
}
}
+
}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java
index 42dd139bc..c8ae444da 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java
@@ -33,7 +33,7 @@ import com.android.gallery3d.filtershow.imageshow.GeometryMetadata.FLIP;
import com.android.gallery3d.filtershow.presets.ImagePreset;
public abstract class ImageGeometry extends ImageSlave {
- private boolean mVisibilityGained = false;
+ protected boolean mVisibilityGained = false;
private boolean mHasDrawn = false;
protected static final float MAX_STRAIGHTEN_ANGLE = 45;
@@ -191,8 +191,8 @@ public abstract class ImageGeometry extends ImageSlave {
return r * 90;
}
- protected Matrix getLocalGeoFlipMatrix(float width, float height) {
- return mLocalGeometry.getFlipMatrix(width, height);
+ protected boolean isHeightWidthSwapped() {
+ return ((int) (getLocalRotation() / 90)) % 2 != 0;
}
protected void setLocalStraighten(float r) {
@@ -217,32 +217,6 @@ public abstract class ImageGeometry extends ImageSlave {
return getLocalRotation() + getLocalStraighten();
}
- protected static float[] getCornersFromRect(RectF r) {
- // Order is:
- // 0------->1
- // ^ |
- // | v
- // 3<-------2
- float[] corners = {
- r.left, r.top, // 0
- r.right, r.top, // 1
- r.right, r.bottom,// 2
- r.left, r.bottom // 3
- };
- return corners;
- }
-
- // If edge point [x, y] in array [x0, y0, x1, y1, ...] is outside of the
- // image bound rectangle, clamps it to the edge of the rectangle.
- protected static void getEdgePoints(RectF imageBound, float[] array) {
- 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);
- }
- }
-
protected static Path drawClosedPath(Canvas canvas, Paint paint, float[] points) {
Path crop = new Path();
crop.moveTo(points[0], points[1]);
@@ -254,16 +228,6 @@ public abstract class ImageGeometry extends ImageSlave {
return crop;
}
- protected static void fixAspectRatio(RectF r, float w, float h) {
- float scale = Math.min(r.width() / w, r.height() / h);
- float centX = r.centerX();
- float centY = r.centerY();
- float hw = scale * w / 2;
- float hh = scale * h / 2;
- r.set(centX - hw, centY - hh, centX + hw, centY + hh);
-
- }
-
protected static float getNewHeightForWidthAspect(float width, float w, float h) {
return width * h / w;
}
@@ -290,11 +254,11 @@ public abstract class ImageGeometry extends ImageSlave {
}
protected void gainedVisibility() {
- // TODO: Override this stub.
+ // Override this stub.
}
protected void lostVisibility() {
- // TODO: Override this stub.
+ // Override this stub.
}
@Override
@@ -327,7 +291,7 @@ public abstract class ImageGeometry extends ImageSlave {
}
protected int getLocalValue() {
- return 0; // TODO: Override this
+ return 0; // Override this
}
protected void setActionDown(float x, float y) {
@@ -402,110 +366,19 @@ public abstract class ImageGeometry extends ImageSlave {
return new RectF(left, top, right, bottom);
}
- protected Matrix getGeoMatrix(RectF r, boolean onlyRotate) {
- RectF pbounds = getLocalPhotoBounds();
- float scale = GeometryMath
- .scale(pbounds.width(), pbounds.height(), getWidth(), getHeight());
- if (((int) (getLocalRotation() / 90)) % 2 != 0) {
- scale = GeometryMath.scale(pbounds.width(), pbounds.height(), getHeight(), getWidth());
- }
- float yoff = getHeight() / 2;
- float xoff = getWidth() / 2;
- float w = r.left * 2 + r.width();
- float h = r.top * 2 + r.height();
- return mLocalGeometry.buildGeometryMatrix(w, h, scale, xoff, yoff, onlyRotate);
- }
-
- protected void drawImageBitmap(Canvas canvas, Bitmap bitmap, Paint paint, Matrix m) {
- canvas.save();
- canvas.drawBitmap(bitmap, m, paint);
- canvas.restore();
- }
-
- protected void drawImageBitmap(Canvas canvas, Bitmap bitmap, Paint paint) {
- float scale = computeScale(getWidth(), getHeight());
- float yoff = getHeight() / 2;
- float xoff = getWidth() / 2;
- Matrix m = mLocalGeometry.buildGeometryUIMatrix(scale, xoff, yoff);
- drawImageBitmap(canvas, bitmap, paint, m);
- }
-
protected RectF straightenBounds() {
RectF bounds = getUntranslatedStraightenCropBounds(getLocalPhotoBounds(),
getLocalStraighten());
- Matrix m = getGeoMatrix(bounds, true);
- m.mapRect(bounds);
- return bounds;
- }
-
- protected void drawStraighten(Canvas canvas, Paint paint) {
- RectF bounds = straightenBounds();
- canvas.save();
- canvas.drawRect(bounds, paint);
- canvas.restore();
- }
-
- protected RectF unrotatedCropBounds() {
- RectF bounds = getLocalCropBounds();
- RectF pbounds = getLocalPhotoBounds();
float scale = computeScale(getWidth(), getHeight());
- float yoff = getHeight() / 2;
- float xoff = getWidth() / 2;
- Matrix m = mLocalGeometry.buildGeometryMatrix(pbounds.width(), pbounds.height(), scale,
- xoff, yoff, 0);
- m.mapRect(bounds);
- return bounds;
- }
-
- protected RectF cropBounds() {
- RectF bounds = getLocalCropBounds();
- Matrix m = getGeoMatrix(getLocalPhotoBounds(), true);
- m.mapRect(bounds);
+ bounds = GeometryMath.scaleRect(bounds, scale);
+ float dx = (getWidth() / 2) - bounds.centerX();
+ float dy = (getHeight() / 2) - bounds.centerY();
+ bounds.offset(dx, dy);
return bounds;
}
- // Fails for non-90 degree
- protected void drawCrop(Canvas canvas, Paint paint) {
- RectF bounds = cropBounds();
- canvas.save();
- canvas.drawRect(bounds, paint);
- canvas.restore();
- }
-
- protected void drawCropSafe(Canvas canvas, Paint paint) {
- Matrix m = getGeoMatrix(getLocalPhotoBounds(), true);
- RectF crop = getLocalCropBounds();
- if (!m.rectStaysRect()) {
- float[] corners = getCornersFromRect(crop);
- m.mapPoints(corners);
- drawClosedPath(canvas, paint, corners);
- } else {
- m.mapRect(crop);
- Path path = new Path();
- path.addRect(crop, Path.Direction.CCW);
- canvas.drawPath(path, paint);
- }
- }
-
- protected void drawTransformedBitmap(Canvas canvas, Bitmap bitmap, Paint paint, boolean clip) {
- paint.setARGB(255, 0, 0, 0);
- drawImageBitmap(canvas, bitmap, paint);
- paint.setColor(Color.WHITE);
- paint.setStyle(Style.STROKE);
- paint.setStrokeWidth(2);
- drawCropSafe(canvas, paint);
- paint.setColor(getDefaultBackgroundColor());
- paint.setStyle(Paint.Style.FILL);
- drawShadows(canvas, paint, unrotatedCropBounds());
- }
-
- protected void drawShadows(Canvas canvas, Paint p, RectF innerBounds) {
- RectF display = new RectF(0, 0, getWidth(), getHeight());
- drawShadows(canvas, p, innerBounds, display, getLocalRotation(), getWidth() / 2,
- getHeight() / 2);
- }
-
- protected static void drawShadows(Canvas canvas, Paint p, RectF innerBounds, RectF outerBounds,
+ 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);
@@ -527,6 +400,15 @@ public abstract class ImageGeometry extends ImageSlave {
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) {
if (getDirtyGeometryFlag()) {
@@ -547,21 +429,38 @@ public abstract class ImageGeometry extends ImageSlave {
// TODO: Override this stub.
}
- protected RectF drawTransformed(Canvas canvas, Bitmap photo, Paint p) {
- p.setARGB(255, 0, 0, 0);
+ /**
+ * 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 (((int) (getLocalRotation() / 90)) % 2 != 0) {
+ if (isHeightWidthSwapped()) {
scale = computeScale(getHeight(), getWidth());
}
// put in screen coordinates
- RectF scaledCrop = GeometryMath.scaleRect(cropBounds, scale);
- RectF scaledPhoto = GeometryMath.scaleRect(photoBounds, scale);
- float[] displayCenter = {
- getWidth() / 2f, getHeight() / 2f
- };
+ 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(), getLocalFlip(), displayCenter);
@@ -569,9 +468,11 @@ public abstract class ImageGeometry extends ImageSlave {
getLocalRotation(), getLocalStraighten(), getLocalFlip(), 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();
@@ -580,6 +481,11 @@ public abstract class ImageGeometry extends ImageSlave {
p.setStyle(Style.STROKE);
p.setStrokeWidth(2);
canvas.drawPath(path, p);
+
+ p.setColor(getDefaultBackgroundColor());
+ p.setAlpha(128);
+ p.setStyle(Paint.Style.FILL);
+ drawShadows(canvas, p, scaledCrop);
return scaledCrop;
}
@@ -590,7 +496,7 @@ public abstract class ImageGeometry extends ImageSlave {
float imageHeight = cropBounds.height();
float scale = GeometryMath.scale(imageWidth, imageHeight, getWidth(), getHeight());
// checks if local rotation is an odd multiple of 90.
- if (((int) (getLocalRotation() / 90)) % 2 != 0) {
+ if (isHeightWidthSwapped()) {
scale = GeometryMath.scale(imageWidth, imageHeight, getHeight(), getWidth());
}
// put in screen coordinates
@@ -618,6 +524,8 @@ public abstract class ImageGeometry extends ImageSlave {
p.setStyle(Paint.Style.FILL);
scaledCrop.offset(displayCenter[0] - scaledCrop.centerX(), displayCenter[1]
- scaledCrop.centerY());
- drawShadows(canvas, p, scaledCrop);
+ 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/ImageRedEyes.java b/src/com/android/gallery3d/filtershow/imageshow/ImageRedEyes.java
new file mode 100644
index 000000000..5119dff3c
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageRedEyes.java
@@ -0,0 +1,148 @@
+
+package com.android.gallery3d.filtershow.imageshow;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+import com.android.gallery3d.filtershow.filters.ImageFilterRedEye;
+import com.android.gallery3d.filtershow.filters.RedEyeCandidate;
+
+public class ImageRedEyes extends ImageSlave {
+
+ private static final String LOGTAG = "ImageRedEyes";
+ private RectF mCurrentRect = null;
+ private static float mTouchPadding = 80;
+
+ public static void setTouchPadding(float padding) {
+ mTouchPadding = padding;
+ }
+
+ public ImageRedEyes(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ImageRedEyes(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void resetParameter() {
+ ImageFilterRedEye filter = (ImageFilterRedEye) getCurrentFilter();
+ if (filter != null) {
+ filter.clear();
+ }
+ mCurrentRect = null;
+ invalidate();
+ }
+
+ @Override
+ public void updateImage() {
+ super.updateImage();
+ invalidate();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+ float ex = event.getX();
+ float ey = event.getY();
+
+ ImageFilterRedEye filter = (ImageFilterRedEye) getCurrentFilter();
+
+ // let's transform (ex, ey) to displayed image coordinates
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ mCurrentRect = new RectF();
+ mCurrentRect.left = ex - mTouchPadding;
+ mCurrentRect.top = ey - mTouchPadding;
+ }
+ if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ mCurrentRect.right = ex + mTouchPadding;
+ mCurrentRect.bottom = ey + mTouchPadding;
+ }
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ if (mCurrentRect != null) {
+ // transform to original coordinates
+ GeometryMetadata geo = getImagePreset().mGeoData;
+ Matrix originalToScreen = geo.getOriginalToScreen(true,
+ mImageLoader.getOriginalBounds().width(),
+ mImageLoader.getOriginalBounds().height(),
+ getWidth(), getHeight());
+ Matrix originalNoRotateToScreen = geo.getOriginalToScreen(false,
+ mImageLoader.getOriginalBounds().width(),
+ mImageLoader.getOriginalBounds().height(),
+ getWidth(), getHeight());
+
+ Matrix invert = new Matrix();
+ originalToScreen.invert(invert);
+ RectF r = new RectF(mCurrentRect);
+ invert.mapRect(r);
+ RectF r2 = new RectF(mCurrentRect);
+ invert.reset();
+ originalNoRotateToScreen.invert(invert);
+ invert.mapRect(r2);
+ filter.addRect(r, r2);
+ this.resetImageCaches(this);
+ }
+ mCurrentRect = null;
+ }
+ invalidate();
+ return true;
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ Paint paint = new Paint();
+ paint.setStyle(Style.STROKE);
+ paint.setColor(Color.RED);
+ paint.setStrokeWidth(2);
+ if (mCurrentRect != null) {
+ paint.setColor(Color.RED);
+ RectF drawRect = new RectF(mCurrentRect);
+ canvas.drawRect(drawRect, paint);
+ }
+
+ GeometryMetadata geo = getImagePreset().mGeoData;
+ Matrix originalToScreen = geo.getOriginalToScreen(false,
+ mImageLoader.getOriginalBounds().width(),
+ mImageLoader.getOriginalBounds().height(), getWidth(), getHeight());
+ Matrix originalRotateToScreen = geo.getOriginalToScreen(true,
+ mImageLoader.getOriginalBounds().width(),
+ mImageLoader.getOriginalBounds().height(), getWidth(), getHeight());
+
+ ImageFilterRedEye filter = (ImageFilterRedEye) getCurrentFilter();
+ for (RedEyeCandidate candidate : filter.getCandidates()) {
+ RectF rect = candidate.getRect();
+ RectF drawRect = new RectF();
+ originalToScreen.mapRect(drawRect, rect);
+ RectF fullRect = new RectF();
+ originalRotateToScreen.mapRect(fullRect, rect);
+ paint.setColor(Color.BLUE);
+ canvas.drawRect(fullRect, paint);
+ canvas.drawLine(fullRect.centerX(), fullRect.top,
+ fullRect.centerX(), fullRect.bottom, paint);
+ canvas.drawLine(fullRect.left, fullRect.centerY(),
+ fullRect.right, fullRect.centerY(), paint);
+ paint.setColor(Color.GREEN);
+ float dw = drawRect.width();
+ float dh = drawRect.height();
+ float dx = fullRect.centerX() - dw/2;
+ float dy = fullRect.centerY() - dh/2;
+ drawRect.set(dx, dy, dx + dw, dy + dh);
+ canvas.drawRect(drawRect, paint);
+ canvas.drawLine(drawRect.centerX(), drawRect.top,
+ drawRect.centerX(), drawRect.bottom, paint);
+ canvas.drawLine(drawRect.left, drawRect.centerY(),
+ drawRect.right, drawRect.centerY(), paint);
+ canvas.drawCircle(drawRect.centerX(), drawRect.centerY(),
+ mTouchPadding, paint);
+ }
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
index 0145c24dc..d9df7b7fc 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
@@ -23,6 +23,7 @@ import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.net.Uri;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.GestureDetector;
@@ -83,7 +84,7 @@ public class ImageShow extends View implements OnGestureListener,
private HistoryAdapter mHistoryAdapter = null;
private ImageStateAdapter mImageStateAdapter = null;
- private Rect mImageBounds = new Rect();
+ protected Rect mImageBounds = new Rect();
private boolean mTouchShowOriginal = false;
private long mTouchShowOriginalDate = 0;
@@ -230,6 +231,7 @@ public class ImageShow extends View implements OnGestureListener,
}
updateSeekBar(parameter, minp, maxp);
invalidate();
+ mActivity.enableSave(hasModifications());
}
@Override
@@ -323,6 +325,10 @@ public class ImageShow extends View implements OnGestureListener,
return dst;
}
+ public Rect getImageCropBounds() {
+ return GeometryMath.roundNearest(getImagePreset().mGeoData.getPreviewCropBounds());
+ }
+
public Rect getDisplayedImageBounds() {
return mImageBounds;
}
@@ -396,6 +402,7 @@ public class ImageShow extends View implements OnGestureListener,
public void updateImagePresets(boolean force) {
ImagePreset preset = getImagePreset();
if (preset == null) {
+ mActivity.enableSave(false);
return;
}
if (force) {
@@ -419,6 +426,7 @@ public class ImageShow extends View implements OnGestureListener,
mFiltersOnlyImage = null;
}
}
+ mActivity.enableSave(hasModifications());
}
public void requestFilteredImages() {
@@ -643,13 +651,13 @@ public class ImageShow extends View implements OnGestureListener,
}
public void updateImage() {
+ invalidate();
if (!updateGeometryFlags()) {
return;
}
Bitmap bitmap = mImageLoader.getOriginalBitmapLarge();
if (bitmap != null) {
imageSizeChanged(bitmap);
- invalidate();
}
}
@@ -666,6 +674,14 @@ public class ImageShow extends View implements OnGestureListener,
mImageLoader.saveImage(getImagePreset(), filterShowActivity, file);
}
+ public void saveToUri(Bitmap f, Uri u, String m, FilterShowActivity filterShowActivity) {
+ mImageLoader.saveToUri(f, u, m, filterShowActivity);
+ }
+
+ public void returnFilteredResult(FilterShowActivity filterShowActivity) {
+ mImageLoader.returnFilteredResult(getImagePreset(), filterShowActivity);
+ }
+
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java b/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java
index 6a79e18a1..2a3ee2856 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageSmallFilter.java
@@ -47,6 +47,13 @@ public class ImageSmallFilter extends ImageShow implements View.OnClickListener
protected final int mTextColor = Color.WHITE;
private ImageSmallFilter mNullFilter;
+ private Bitmap mOverlayBitmap = null;
+ private final int mOverlayTint = Color.argb(100, 0, 0, 0);
+
+ public void setOverlayBitmap(Bitmap bitmap){
+ mOverlayBitmap = bitmap;
+ }
+
public static void setMargin(int value) {
mMargin = value;
}
@@ -77,6 +84,10 @@ public class ImageSmallFilter extends ImageShow implements View.OnClickListener
mImagePreset.add(mImageFilter);
}
+ public ImageFilter getImageFilter() {
+ return mImageFilter;
+ }
+
@Override
public void setSelected(boolean value) {
if (mIsSelected != value) {
@@ -188,6 +199,13 @@ public class ImageSmallFilter extends ImageShow implements View.OnClickListener
mPaint.setTextSize(mTextSize);
mPaint.setColor(mTextColor);
canvas.drawText(mImageFilter.getName(), x, y - mTextMargin, mPaint);
+ if (mOverlayBitmap != null) {
+ mPaint.setColor(mOverlayTint);
+ canvas.drawRect(0, mMargin, getWidth(), getWidth() + mMargin, mPaint);
+ Rect d = new Rect(0, mMargin, getWidth() - mMargin, getWidth());
+ mPaint.setColor(Color.BLACK);
+ drawImage(canvas, mOverlayBitmap, d);
+ }
}
public void drawImage(Canvas canvas, Bitmap image, Rect destination) {
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
index 57a22aab3..7a539da8f 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
@@ -20,6 +20,7 @@ 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.Path;
import android.graphics.RectF;
@@ -105,10 +106,10 @@ public class ImageStraighten extends ImageGeometry {
@Override
protected void drawShape(Canvas canvas, Bitmap image) {
- drawTransformed(canvas, image, gPaint);
+ float [] o = {0, 0};
+ RectF bounds = drawTransformed(canvas, image, gPaint, o);
// Draw the grid
- RectF bounds = straightenBounds();
Path path = new Path();
path.addRect(bounds, Path.Direction.CCW);
gPaint.setARGB(255, 255, 255, 255);
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageWithIcon.java b/src/com/android/gallery3d/filtershow/imageshow/ImageWithIcon.java
deleted file mode 100644
index a332fa72a..000000000
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageWithIcon.java
+++ /dev/null
@@ -1,50 +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.Rect;
-
-/**
- * TODO: Insert description here. (generated by hoford)
- */
-public class ImageWithIcon extends ImageSmallFilter {
- /**
- * @param context
- */
- public ImageWithIcon(Context context) {
- super(context);
- // TODO(hoford): Auto-generated constructor stub
- }
-
- private Bitmap bitmap;
-
- public void setIcon(Bitmap bitmap){
- this.bitmap = bitmap;
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- if (bitmap != null) {
- Rect d = new Rect(0, mMargin, getWidth() - mMargin, getWidth());
- drawImage(canvas, bitmap, d);
- }
- }
-}
diff --git a/src/com/android/gallery3d/filtershow/tools/BitmapTask.java b/src/com/android/gallery3d/filtershow/tools/BitmapTask.java
new file mode 100644
index 000000000..62801c1f2
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/tools/BitmapTask.java
@@ -0,0 +1,68 @@
+/*
+ * 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.tools;
+
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+
+/**
+ * Asynchronous task filtering or doign I/O with bitmaps.
+ */
+public class BitmapTask <T> extends AsyncTask<T, Void, Bitmap> {
+
+ private Callbacks<T> mCallbacks;
+ private static final String LOGTAG = "BitmapTask";
+
+ public BitmapTask(Callbacks<T> callbacks) {
+ mCallbacks = callbacks;
+ }
+
+ @Override
+ protected Bitmap doInBackground(T... params) {
+ if (params == null || mCallbacks == null) {
+ return null;
+ }
+ return mCallbacks.onExecute(params[0]);
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap result) {
+ if (mCallbacks == null) {
+ return;
+ }
+ mCallbacks.onComplete(result);
+ }
+
+ @Override
+ protected void onCancelled() {
+ if (mCallbacks == null) {
+ return;
+ }
+ mCallbacks.onCancel();
+ }
+
+ /**
+ * Callbacks for the asynchronous task.
+ */
+ public interface Callbacks<P> {
+ void onComplete(Bitmap result);
+
+ void onCancel();
+
+ Bitmap onExecute(P param);
+ }
+}
diff --git a/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java b/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java
index 9c55623d1..30659e677 100644
--- a/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java
+++ b/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java
@@ -52,9 +52,6 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
private static final String LOGTAG = "SaveCopyTask";
- private static final int DEFAULT_COMPRESS_QUALITY = 95;
- private static final String DEFAULT_SAVE_DIRECTORY = "EditedOnlinePhotos";
-
/**
* Saves the bitmap in the final destination
*/
@@ -62,7 +59,7 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
OutputStream os = null;
try {
os = new FileOutputStream(destination);
- bitmap.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, os);
+ bitmap.compress(CompressFormat.JPEG, ImageLoader.DEFAULT_COMPRESS_QUALITY, os);
} catch (FileNotFoundException e) {
Log.v(LOGTAG,"Error in writing "+destination.getAbsolutePath());
} finally {
@@ -123,7 +120,7 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
File saveDirectory = getSaveDirectory(context, sourceUri);
if ((saveDirectory == null) || !saveDirectory.canWrite()) {
saveDirectory = new File(Environment.getExternalStorageDirectory(),
- DEFAULT_SAVE_DIRECTORY);
+ ImageLoader.DEFAULT_SAVE_DIRECTORY);
}
// Create the directory if it doesn't exist
if (!saveDirectory.exists()) saveDirectory.mkdirs();
@@ -137,19 +134,6 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
return new File(saveDirectory, filename + ".JPG");
}
- private Bitmap loadMutableBitmap() throws FileNotFoundException {
- BitmapFactory.Options options = new BitmapFactory.Options();
- // TODO: on <3.x we need a copy of the bitmap (inMutable doesn't
- // exist)
- options.inMutable = true;
-
- InputStream is = context.getContentResolver().openInputStream(sourceUri);
- Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
- int orientation = ImageLoader.getOrientation(context, sourceUri);
- bitmap = ImageLoader.rotateToPortrait(bitmap, orientation);
- return bitmap;
- }
-
private static final String[] COPY_EXIF_ATTRIBUTES = new String[] {
ExifInterface.TAG_APERTURE,
ExifInterface.TAG_DATETIME,
@@ -228,7 +212,7 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> {
ImagePreset preset = params[0];
try {
- Bitmap bitmap = preset.apply(loadMutableBitmap());
+ Bitmap bitmap = preset.apply(ImageLoader.loadMutableBitmap(context, sourceUri));
Object xmp = null;
InputStream is = null;