diff options
48 files changed, 1565 insertions, 515 deletions
diff --git a/src/com/android/camera/CaptureAnimManager.java b/src/com/android/camera/CaptureAnimManager.java index 4643c379f..ec38290b3 100644 --- a/src/com/android/camera/CaptureAnimManager.java +++ b/src/com/android/camera/CaptureAnimManager.java @@ -33,9 +33,9 @@ public class CaptureAnimManager { // times mark endpoint of animation phase private static final int TIME_FLASH = 200; private static final int TIME_HOLD = 400; - private static final int TIME_SLIDE = 700; - private static final int TIME_HOLD2 = 1000; - private static final int TIME_SLIDE2 = 1200; + private static final int TIME_SLIDE = 800; + private static final int TIME_HOLD2 = 3300; + private static final int TIME_SLIDE2 = 4100; private static final int ANIM_BOTH = 0; private static final int ANIM_FLASH = 1; diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java index 4d01bc320..245ef5990 100644 --- a/src/com/android/camera/VideoModule.java +++ b/src/com/android/camera/VideoModule.java @@ -2216,7 +2216,6 @@ public class VideoModule implements CameraModule, initializeMiscControls(); showTimeLapseUI(mCaptureTimeLapse); initializeVideoSnapshot(); - resizeForPreviewAspectRatio(); // from onResume() showVideoSnapshotUI(false); @@ -2279,6 +2278,7 @@ public class VideoModule implements CameraModule, } private void updateOnScreenIndicators() { + if (mParameters == null) return; updateFlashOnScreenIndicator(mParameters.getFlashMode()); } @@ -2436,7 +2436,7 @@ public class VideoModule implements CameraModule, } private void initializeZoom() { - if (!mParameters.isZoomSupported()) return; + if (mParameters == null || !mParameters.isZoomSupported()) return; mZoomMax = mParameters.getMaxZoom(); mZoomRatios = mParameters.getZoomRatios(); // Currently we use immediate zoom for fast zooming to get better UX and @@ -2448,6 +2448,7 @@ public class VideoModule implements CameraModule, } private void initializeVideoSnapshot() { + if (mParameters == null) return; if (Util.isVideoSnapshotSupported(mParameters) && !mIsVideoCaptureIntent) { mActivity.setSingleTapUpListener(mPreviewFrameLayout); // Show the tap to focus toast if this is the first start. @@ -2462,6 +2463,7 @@ public class VideoModule implements CameraModule, } void showVideoSnapshotUI(boolean enabled) { + if (mParameters == null) return; if (Util.isVideoSnapshotSupported(mParameters) && !mIsVideoCaptureIntent) { if (ApiHelper.HAS_SURFACE_TEXTURE && enabled) { ((CameraScreenNail) mActivity.mCameraScreenNail).animateCapture(mDisplayRotation); diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java index f7147eac1..7e8a3f582 100644 --- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java +++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java @@ -71,6 +71,7 @@ import com.android.gallery3d.filtershow.imageshow.ImageZoom; import com.android.gallery3d.filtershow.imageshow.MasterImage; import com.android.gallery3d.filtershow.presets.ImagePreset; import com.android.gallery3d.filtershow.provider.SharedImageProvider; +import com.android.gallery3d.filtershow.tools.BitmapTask; import com.android.gallery3d.filtershow.tools.SaveCopyTask; import com.android.gallery3d.filtershow.ui.FilterIconButton; import com.android.gallery3d.filtershow.ui.FramedTextButton; @@ -814,7 +815,6 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, } public void hideImageViews() { - mImageShow.setShowControls(false); // reset for (View view : mImageViews) { view.setVisibility(View.GONE); } @@ -1090,11 +1090,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mCropExtras.getOutputFormat(), this); } if (mSaveAsWallpaper) { - try { - WallpaperManager.getInstance(this).setBitmap(filtered); - } catch (IOException e) { - Log.w(LOGTAG, "fail to set wall paper", e); - } + setWallpaperInBackground(filtered); } if (mReturnAsExtra) { if (filtered != null) { @@ -1117,6 +1113,28 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, } } + void setWallpaperInBackground(final Bitmap bmap) { + Toast.makeText(this, R.string.setting_wallpaper, Toast.LENGTH_LONG).show(); + BitmapTask.Callbacks<FilterShowActivity> cb = new BitmapTask.Callbacks<FilterShowActivity>() { + @Override + public void onComplete(Bitmap result) {} + + @Override + public void onCancel() {} + + @Override + public Bitmap onExecute(FilterShowActivity param) { + try { + WallpaperManager.getInstance(param).setBitmap(bmap); + } catch (IOException e) { + Log.w(LOGTAG, "fail to set wall paper", e); + } + return null; + } + }; + (new BitmapTask<FilterShowActivity>(cb)).execute(this); + } + public void done() { if (mOutputted) { hideSavingProgress(); diff --git a/src/com/android/gallery3d/filtershow/PanelController.java b/src/com/android/gallery3d/filtershow/PanelController.java index 18a9585a6..5bda246da 100644 --- a/src/com/android/gallery3d/filtershow/PanelController.java +++ b/src/com/android/gallery3d/filtershow/PanelController.java @@ -375,18 +375,20 @@ public class PanelController implements OnClickListener { ImageShow image = null; mActivity.hideImageViews(); for (View view : mImageViews) { + image = (ImageShow) view; if (view.getId() == id) { view.setVisibility(View.VISIBLE); - image = (ImageShow) view; + image.select(); } else { view.setVisibility(View.GONE); + image.unselect(); } } return image; } public void showDefaultImageView() { - showImageView(R.id.imageShow).setShowControls(false); + showImageView(R.id.imageShow); MasterImage.getImage().setCurrentFilter(null); MasterImage.getImage().setCurrentFilterRepresentation(null); } @@ -498,7 +500,7 @@ public class PanelController implements OnClickListener { } mUtilityPanel.hideAccessoryViews(); - if (view instanceof FilterIconButton) { + if (view instanceof FilterIconButton && view.getId() != R.id.applyEffect) { mCurrentEditor = null; FilterIconButton component = (FilterIconButton) view; FilterRepresentation representation = component.getFilterRepresentation(); @@ -515,7 +517,6 @@ public class PanelController implements OnClickListener { mCurrentImage = showImageView(representation.getEditorId()); } } - mCurrentImage.setShowControls(representation.showEditingControls()); mUtilityPanel.setShowParameter(representation.showParameterValue()); mCurrentImage.select(); @@ -533,7 +534,7 @@ public class PanelController implements OnClickListener { switch (view.getId()) { case R.id.tinyplanetButton: { - mCurrentImage = showImageView(R.id.imageTinyPlanet).setShowControls(true); + mCurrentImage = showImageView(R.id.imageTinyPlanet); String ename = mCurrentImage.getContext().getString(R.string.tinyplanet); mUtilityPanel.setEffectName(ename); if (!mDisableFilterButtons) { @@ -556,8 +557,8 @@ public class PanelController implements OnClickListener { if (mCurrentImage instanceof ImageCrop && mUtilityPanel.firstTimeCropDisplayed) { ((ImageCrop) mCurrentImage).clear(); mUtilityPanel.firstTimeCropDisplayed = false; + ((ImageCrop) mCurrentImage).setFixedAspect(mFixedAspect); } - ((ImageCrop) mCurrentImage).setFixedAspect(mFixedAspect); break; } case R.id.rotateButton: { diff --git a/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java b/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java index 419abe85d..7d5b52921 100644 --- a/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java +++ b/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java @@ -48,6 +48,7 @@ public class FilteringPipeline implements Handler.Callback { private final static int NEW_RENDERING_REQUEST = 1; private final static int COMPUTE_PRESET = 2; private final static int COMPUTE_RENDERING_REQUEST = 3; + private final static int COMPUTE_PARTIAL_RENDERING_REQUEST = 4; private Handler mProcessingHandler = null; private final Handler mUIHandler = new Handler() { @@ -81,7 +82,13 @@ public class FilteringPipeline implements Handler.Callback { mUIHandler.sendMessage(uimsg); break; } - case COMPUTE_RENDERING_REQUEST: { + case COMPUTE_RENDERING_REQUEST: + case COMPUTE_PARTIAL_RENDERING_REQUEST: { + if (msg.what == COMPUTE_PARTIAL_RENDERING_REQUEST) { + if (mProcessingHandler.hasMessages(COMPUTE_PARTIAL_RENDERING_REQUEST)) { + return false; + } + } RenderingRequest request = (RenderingRequest) msg.obj; render(request); Message uimsg = mUIHandler.obtainMessage(NEW_RENDERING_REQUEST); @@ -95,6 +102,7 @@ public class FilteringPipeline implements Handler.Callback { private static float RESIZE_FACTOR = 0.8f; private static float MAX_PROCESS_TIME = 100; // in ms + private static long HIRES_DELAY = 100; // in ms private float mResizeFactor = 1.0f; private long mResizeTime = 0; @@ -165,9 +173,17 @@ public class FilteringPipeline implements Handler.Callback { if (mOriginalAllocation == null) { return; } - Message msg = mProcessingHandler.obtainMessage(COMPUTE_RENDERING_REQUEST); + int type = COMPUTE_RENDERING_REQUEST; + if (request.getType() == RenderingRequest.PARTIAL_RENDERING) { + type = COMPUTE_PARTIAL_RENDERING_REQUEST; + } + Message msg = mProcessingHandler.obtainMessage(type); msg.obj = request; - mProcessingHandler.sendMessage(msg); + if (type == COMPUTE_PARTIAL_RENDERING_REQUEST) { + mProcessingHandler.sendMessageDelayed(msg, HIRES_DELAY); + } else { + mProcessingHandler.sendMessage(msg); + } } public synchronized void updatePreviewBuffer() { @@ -210,11 +226,15 @@ public class FilteringPipeline implements Handler.Callback { if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) { return "GEOMETRY_RENDERING"; } + if (request.getType() == RenderingRequest.PARTIAL_RENDERING) { + return "PARTIAL_RENDERING"; + } return "UNKNOWN TYPE!"; } private void render(RenderingRequest request) { - if (request.getBitmap() == null + if ((request.getType() != RenderingRequest.PARTIAL_RENDERING + && request.getBitmap() == null) || request.getImagePreset() == null) { return; } @@ -225,11 +245,21 @@ public class FilteringPipeline implements Handler.Callback { Bitmap bitmap = request.getBitmap(); ImagePreset preset = request.getImagePreset(); setPresetParameters(preset); + + if (request.getType() == RenderingRequest.PARTIAL_RENDERING) { + bitmap = MasterImage.getImage().getImageLoader().getScaleOneImageForPreset(null, preset, request.getBounds(), request.getDestination(), false); + if (bitmap == null) { + return; + } + bitmap = preset.applyGeometry(bitmap); + } + if (request.getType() == RenderingRequest.FILTERS_RENDERING) { FiltersManager.getManager().resetBitmapsRS(); } - if (request.getType() != RenderingRequest.ICON_RENDERING) { + if (request.getType() != RenderingRequest.ICON_RENDERING + && request.getType() != RenderingRequest.PARTIAL_RENDERING) { updateOriginalAllocation(preset); } if (DEBUG) { @@ -243,9 +273,11 @@ public class FilteringPipeline implements Handler.Callback { } else if (request.getType() == RenderingRequest.FILTERS_RENDERING) { mFiltersOnlyOriginalAllocation.copyTo(bitmap); } + if (request.getType() == RenderingRequest.FULL_RENDERING || request.getType() == RenderingRequest.FILTERS_RENDERING - || request.getType() == RenderingRequest.ICON_RENDERING) { + || request.getType() == RenderingRequest.ICON_RENDERING + || request.getType() == RenderingRequest.PARTIAL_RENDERING) { Bitmap bmp = preset.apply(bitmap); request.setBitmap(bmp); } diff --git a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java index 698992ac9..df4f3fd3a 100644 --- a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java +++ b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java @@ -91,6 +91,7 @@ public class ImageLoader { public static final int ORI_TRANSPOSE = ExifInterface.ORIENTATION_TRANSPOSE; public static final int ORI_TRANSVERSE = ExifInterface.ORIENTATION_TRANSVERSE; + private static final int BITMAP_LOAD_BACKOUT_ATTEMPTS = 5; private Context mContext = null; private Uri mUri = null; @@ -264,12 +265,12 @@ public class ImageLoader { bitmap.getHeight(), matrix, true); } - private Bitmap loadRegionBitmap(Uri uri, Rect bounds) { + private Bitmap loadRegionBitmap(Uri uri, BitmapFactory.Options options, Rect bounds) { InputStream is = null; try { is = mContext.getContentResolver().openInputStream(uri); BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is, false); - return decoder.decodeRegion(bounds, null); + return decoder.decodeRegion(bounds, options); } catch (FileNotFoundException e) { Log.e(LOGTAG, "FileNotFoundException: " + uri); } catch (Exception e) { @@ -369,11 +370,28 @@ public class ImageLoader { // FIXME: 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) { + Rect destination, boolean force) { mLoadingLock.lock(); Bitmap bmp = mZoomCache.getImage(imagePreset, bounds); if (force || bmp == null) { - bmp = loadRegionBitmap(mUri, bounds); + BitmapFactory.Options options = null; + if (destination != null) { + options = new BitmapFactory.Options(); + if (bounds.width() > destination.width()) { + int sampleSize = 1; + int w = bounds.width(); + while (w > destination.width()) { + sampleSize *= 2; + w /= sampleSize; + } + options.inSampleSize = sampleSize; + } + } + bmp = loadRegionBitmap(mUri, options, bounds); + if (destination != null) { + mLoadingLock.unlock(); + return bmp; + } if (bmp != null) { // TODO: this workaround for RS might not be needed ultimately Bitmap bmp2 = bmp.copy(Bitmap.Config.ARGB_8888, true); @@ -406,28 +424,97 @@ public class ImageLoader { public static Bitmap loadMutableBitmap(Context context, Uri sourceUri) { BitmapFactory.Options options = new BitmapFactory.Options(); + return loadMutableBitmap(context, sourceUri, options); + } + + public static Bitmap loadMutableBitmap(Context context, Uri sourceUri, + BitmapFactory.Options options) { // TODO: on <3.x we need a copy of the bitmap (inMutable doesn't // exist) options.inMutable = true; + Bitmap bitmap = decodeUriWithBackouts(context, sourceUri, options); + if (bitmap == null) { + return null; + } + int orientation = ImageLoader.getOrientation(context, sourceUri); + bitmap = ImageLoader.rotateToPortrait(bitmap, orientation); + return bitmap; + } + + public static Bitmap decodeUriWithBackouts(Context context, Uri sourceUri, + BitmapFactory.Options options) { + boolean noBitmap = true; + int num_tries = 0; + InputStream is = getInputStream(context, sourceUri); + + if (options.inSampleSize < 1) { + options.inSampleSize = 1; + } + // Stopgap fix for low-memory devices. + Bitmap bmap = null; + while (noBitmap) { + if (is == null) { + return null; + } + try { + // Try to decode, downsample if low-memory. + bmap = BitmapFactory.decodeStream(is, null, options); + noBitmap = false; + } catch (java.lang.OutOfMemoryError e) { + // Try 5 times before failing for good. + if (++num_tries >= BITMAP_LOAD_BACKOUT_ATTEMPTS) { + throw e; + } + is = null; + bmap = null; + System.gc(); + is = getInputStream(context, sourceUri); + options.inSampleSize *= 2; + } + } + Utils.closeSilently(is); + return bmap; + } + + private static InputStream getInputStream(Context context, Uri sourceUri) { InputStream is = null; - Bitmap bitmap = null; try { is = context.getContentResolver().openInputStream(sourceUri); - bitmap = BitmapFactory.decodeStream(is, null, options); } catch (FileNotFoundException e) { Log.w(LOGTAG, "could not load bitmap ", e); - is = null; - bitmap = null; - } finally { Utils.closeSilently(is); + is = null; } - if (bitmap == null) { - return null; + return is; + } + + public static Bitmap decodeResourceWithBackouts(Resources res, BitmapFactory.Options options, + int id) { + boolean noBitmap = true; + int num_tries = 0; + if (options.inSampleSize < 1) { + options.inSampleSize = 1; } - int orientation = ImageLoader.getOrientation(context, sourceUri); - bitmap = ImageLoader.rotateToPortrait(bitmap, orientation); - return bitmap; + // Stopgap fix for low-memory devices. + Bitmap bmap = null; + while (noBitmap) { + try { + // Try to decode, downsample if low-memory. + bmap = BitmapFactory.decodeResource( + res, id, options); + noBitmap = false; + } catch (java.lang.OutOfMemoryError e) { + // Try 5 times before failing for good. + if (++num_tries >= BITMAP_LOAD_BACKOUT_ATTEMPTS) { + throw e; + } + bmap = null; + System.gc(); + options.inSampleSize *= 2; + } + } + return bmap; } public void returnFilteredResult(ImagePreset preset, @@ -451,13 +538,36 @@ public class ImageLoader { if (param == null || mUri == null) { return null; } - Bitmap bitmap = loadMutableBitmap(mContext, mUri); - if (bitmap == null) { - Log.w(LOGTAG, "Failed to save image!"); - return null; + BitmapFactory.Options options = new BitmapFactory.Options(); + boolean noBitmap = true; + int num_tries = 0; + if (options.inSampleSize < 1) { + options.inSampleSize = 1; } - bitmap = param.applyGeometry(bitmap); - return param.apply(bitmap); + Bitmap bitmap = null; + // Stopgap fix for low-memory devices. + while (noBitmap) { + try { + // Try to do bitmap operations, downsample if low-memory + bitmap = loadMutableBitmap(mContext, mUri, options); + if (bitmap == null) { + Log.w(LOGTAG, "Failed to save image!"); + return null; + } + bitmap = param.applyGeometry(bitmap); + bitmap = param.apply(bitmap); + noBitmap = false; + } catch (java.lang.OutOfMemoryError e) { + // Try 5 times before failing for good. + if (++num_tries >= 5) { + throw e; + } + bitmap = null; + System.gc(); + options.inSampleSize *= 2; + } + } + return bitmap; } }; diff --git a/src/com/android/gallery3d/filtershow/cache/RenderingRequest.java b/src/com/android/gallery3d/filtershow/cache/RenderingRequest.java index 1e9f6b83a..3ec74e266 100644 --- a/src/com/android/gallery3d/filtershow/cache/RenderingRequest.java +++ b/src/com/android/gallery3d/filtershow/cache/RenderingRequest.java @@ -17,6 +17,7 @@ package com.android.gallery3d.filtershow.cache; import android.graphics.Bitmap; +import android.graphics.Rect; import com.android.gallery3d.app.Log; import com.android.gallery3d.filtershow.imageshow.MasterImage; import com.android.gallery3d.filtershow.presets.ImagePreset; @@ -27,29 +28,46 @@ public class RenderingRequest { private Bitmap mBitmap = null; private ImagePreset mImagePreset = null; private RenderingRequestCaller mCaller = null; + private Rect mBounds = null; + private Rect mDestination = null; private int mType = FULL_RENDERING; - public static int FULL_RENDERING = 0; - public static int FILTERS_RENDERING = 1; - public static int GEOMETRY_RENDERING = 2; - public static int ICON_RENDERING = 3; + public static final int FULL_RENDERING = 0; + public static final int FILTERS_RENDERING = 1; + public static final int GEOMETRY_RENDERING = 2; + public static final int ICON_RENDERING = 3; + public static final int PARTIAL_RENDERING = 4; private static final Bitmap.Config mConfig = Bitmap.Config.ARGB_8888; + public static void post(Bitmap source, ImagePreset preset, int type, RenderingRequestCaller caller) { + RenderingRequest.post(source, preset, type, caller, null, null); + } + public static void post(Bitmap source, ImagePreset preset, int type, - RenderingRequestCaller caller) { - if (source == null || preset == null || caller == null) { + RenderingRequestCaller caller, Rect bounds, Rect destination) { + if ((type != PARTIAL_RENDERING && source == null) || preset == null || caller == null) { Log.v(LOGTAG, "something null: source: " + source + " or preset: " + preset + " or caller: " + caller); return; } RenderingRequest request = new RenderingRequest(); Bitmap bitmap = null; - if (type == FULL_RENDERING || type == GEOMETRY_RENDERING || type == ICON_RENDERING) { + if (type == FULL_RENDERING + || type == GEOMETRY_RENDERING + || type == ICON_RENDERING) { bitmap = preset.applyGeometry(source); - } else { + } else if (type != PARTIAL_RENDERING) { bitmap = Bitmap.createBitmap(source.getWidth(), source.getHeight(), mConfig); } + request.setBitmap(bitmap); ImagePreset passedPreset = new ImagePreset(preset); passedPreset.setImageLoader(MasterImage.getImage().getImageLoader()); + + if (type == PARTIAL_RENDERING) { + request.setBounds(bounds); + request.setDestination(destination); + passedPreset.setPartialRendering(true, bounds); + } + request.setImagePreset(passedPreset); request.setType(type); request.setCaller(caller); @@ -103,4 +121,20 @@ public class RenderingRequest { public void setCaller(RenderingRequestCaller caller) { mCaller = caller; } + + public Rect getBounds() { + return mBounds; + } + + public void setBounds(Rect bounds) { + mBounds = bounds; + } + + public Rect getDestination() { + return mDestination; + } + + public void setDestination(Rect destination) { + mDestination = destination; + } } diff --git a/src/com/android/gallery3d/filtershow/editors/EditorVignette.java b/src/com/android/gallery3d/filtershow/editors/EditorVignette.java new file mode 100644 index 000000000..a60c1681e --- /dev/null +++ b/src/com/android/gallery3d/filtershow/editors/EditorVignette.java @@ -0,0 +1,55 @@ +/* + * 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.editors; + +import android.content.Context; +import android.util.Log; +import android.widget.FrameLayout; + +import com.android.gallery3d.R; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; +import com.android.gallery3d.filtershow.filters.FilterTinyPlanetRepresentation; +import com.android.gallery3d.filtershow.filters.FilterVignetteRepresentation; +import com.android.gallery3d.filtershow.imageshow.ImageVignette; + +public class EditorVignette extends BasicEditor { + public static final int ID = R.id.vignetteEditor; + private static final String LOGTAG = "EditorVignettePlanet"; + ImageVignette mImageVignette; + + public EditorVignette() { + super(ID, R.layout.filtershow_vignette_editor, R.id.imageVignette); + } + + @Override + public void createEditor(Context context, FrameLayout frameLayout) { + super.createEditor(context, frameLayout); + mImageVignette = (ImageVignette) mImageShow; + mImageVignette.setEditor(this); + } + + @Override + public void reflectCurrentFilter() { + super.reflectCurrentFilter(); + + FilterRepresentation rep = getLocalRepresentation(); + if (rep != null && getLocalRepresentation() instanceof FilterVignetteRepresentation) { + FilterVignetteRepresentation drawRep = (FilterVignetteRepresentation) rep; + mImageVignette.setRepresentation(drawRep); + } + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java index 43660d6a0..377bd2b6f 100644 --- a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java +++ b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java @@ -42,6 +42,7 @@ public class BaseFiltersManager { filters.add(new ImageFilterVignette()); filters.add(new ImageFilterContrast()); filters.add(new ImageFilterShadows()); + filters.add(new ImageFilterHighlights()); filters.add(new ImageFilterVibrance()); filters.add(new ImageFilterSharpen()); filters.add(new ImageFilterCurves()); @@ -89,6 +90,7 @@ public class BaseFiltersManager { representations.add(getRepresentation(ImageFilterVignette.class)); representations.add(getRepresentation(ImageFilterContrast.class)); representations.add(getRepresentation(ImageFilterShadows.class)); + representations.add(getRepresentation(ImageFilterHighlights.class)); representations.add(getRepresentation(ImageFilterVibrance.class)); representations.add(getRepresentation(ImageFilterSharpen.class)); representations.add(getRepresentation(ImageFilterCurves.class)); diff --git a/src/com/android/gallery3d/filtershow/filters/FilterCurvesRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterCurvesRepresentation.java index 6c831708e..3511c67af 100644 --- a/src/com/android/gallery3d/filtershow/filters/FilterCurvesRepresentation.java +++ b/src/com/android/gallery3d/filtershow/filters/FilterCurvesRepresentation.java @@ -19,6 +19,7 @@ public class FilterCurvesRepresentation extends FilterRepresentation { setShowEditingControls(false); setShowParameterValue(false); setShowUtilityPanel(true); + setSupportsPartialRendering(true); for (int i = 0; i < mSplines.length; i++) { mSplines[i] = new Spline(); mSplines[i].reset(); diff --git a/src/com/android/gallery3d/filtershow/filters/FilterDrawRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterDrawRepresentation.java index e41f0a622..8b8504bbc 100644 --- a/src/com/android/gallery3d/filtershow/filters/FilterDrawRepresentation.java +++ b/src/com/android/gallery3d/filtershow/filters/FilterDrawRepresentation.java @@ -150,6 +150,10 @@ public class FilterDrawRepresentation extends FilterRepresentation { mCurrent = null; } + public void clearCurrentSection() { + mCurrent = null; + } + public void clear() { mCurrent = null; mDrawing.clear(); diff --git a/src/com/android/gallery3d/filtershow/filters/FilterFxRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterFxRepresentation.java index 859bf327c..d4128dc79 100644 --- a/src/com/android/gallery3d/filtershow/filters/FilterFxRepresentation.java +++ b/src/com/android/gallery3d/filtershow/filters/FilterFxRepresentation.java @@ -37,6 +37,7 @@ public class FilterFxRepresentation extends FilterRepresentation { setShowEditingControls(false); setShowParameterValue(false); setShowUtilityPanel(false); + setSupportsPartialRendering(true); } public String toString() { diff --git a/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java index 513cdcdef..83f2a1b87 100644 --- a/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java +++ b/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java @@ -25,6 +25,7 @@ public class FilterRepresentation implements Cloneable { private String mName; private int mPriority = TYPE_NORMAL; private Class mFilterClass; + private boolean mSupportsPartialRendering = false; private int mTextId = 0; private int mEditorId = BasicEditor.ID; private int mButtonId = 0; @@ -52,6 +53,7 @@ public class FilterRepresentation implements Cloneable { representation.setName(getName()); representation.setPriority(getPriority()); representation.setFilterClass(getFilterClass()); + representation.setSupportsPartialRendering(supportsPartialRendering()); representation.setTextId(getTextId()); representation.setEditorId(getEditorId()); representation.setButtonId(getButtonId()); @@ -70,6 +72,7 @@ public class FilterRepresentation implements Cloneable { if (representation.mFilterClass == representation.mFilterClass && representation.mName.equalsIgnoreCase(mName) && representation.mPriority == mPriority + && representation.mSupportsPartialRendering == mSupportsPartialRendering && representation.mTextId == mTextId && representation.mEditorId == mEditorId && representation.mButtonId == mButtonId @@ -106,6 +109,14 @@ public class FilterRepresentation implements Cloneable { return false; } + public boolean supportsPartialRendering() { + return mSupportsPartialRendering; + } + + public void setSupportsPartialRendering(boolean value) { + mSupportsPartialRendering = value; + } + public void useParametersFrom(FilterRepresentation a) { } diff --git a/src/com/android/gallery3d/filtershow/filters/FilterVignetteRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterVignetteRepresentation.java new file mode 100644 index 000000000..eef54ef58 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/FilterVignetteRepresentation.java @@ -0,0 +1,114 @@ +/* + * 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 com.android.gallery3d.R; +import com.android.gallery3d.filtershow.editors.EditorVignette; +import com.android.gallery3d.filtershow.imageshow.Oval; + +public class FilterVignetteRepresentation extends FilterBasicRepresentation implements Oval { + private static final String LOGTAG = "FilterVignetteRepresentation"; + private float mCenterX = Float.NaN; + private float mCenterY; + private float mRadiusX = Float.NaN; + private float mRadiusY; + + public FilterVignetteRepresentation() { + super("Vignette", -100, 50, 100); + setShowParameterValue(true); + setPriority(FilterRepresentation.TYPE_VIGNETTE); + setTextId(R.string.vignette); + setButtonId(R.id.vignetteEditor); + setEditorId(EditorVignette.ID); + setName("Vignette"); + setFilterClass(ImageFilterVignette.class); + + setMinimum(-100); + setMaximum(100); + setDefaultValue(0); + } + + @Override + public void useParametersFrom(FilterRepresentation a) { + super.useParametersFrom(a); + mCenterX = ((FilterVignetteRepresentation) a).mCenterX; + mCenterY = ((FilterVignetteRepresentation) a).mCenterY; + mRadiusX = ((FilterVignetteRepresentation) a).mRadiusX; + mRadiusY = ((FilterVignetteRepresentation) a).mRadiusY; + } + + @Override + public FilterRepresentation clone() throws CloneNotSupportedException { + FilterVignetteRepresentation representation = (FilterVignetteRepresentation) super + .clone(); + representation.mCenterX = mCenterX; + representation.mCenterY = mCenterY; + + return representation; + } + + @Override + public void setCenter(float centerX, float centerY) { + mCenterX = centerX; + mCenterY = centerY; + } + + @Override + public float getCenterX() { + return mCenterX; + } + + @Override + public float getCenterY() { + return mCenterY; + } + + @Override + public void setRadius(float radiusX, float radiusY) { + mRadiusX = radiusX; + mRadiusY = radiusY; + } + + @Override + public void setRadiusX(float radiusX) { + mRadiusX = radiusX; + } + + @Override + public void setRadiusY(float radiusY) { + mRadiusY = radiusY; + } + + @Override + public float getRadiusX() { + return mRadiusX; + } + + @Override + public float getRadiusY() { + return mRadiusY; + } + + public boolean isCenterSet() { + return mCenterX != Float.NaN; + } + + @Override + public boolean isNil() { + return getValue() == 0; + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java index c92ac012d..a4626cdb2 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java @@ -36,6 +36,7 @@ public class ImageFilterBwFilter extends SimpleImageFilter { representation.setMinimum(-180); representation.setTextId(R.string.bwfilter); representation.setButtonId(R.id.bwfilterButton); + representation.setSupportsPartialRendering(true); return representation; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java index 2f94e3d17..2097f0d6e 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java @@ -37,6 +37,7 @@ public class ImageFilterContrast extends SimpleImageFilter { representation.setMinimum(-100); representation.setMaximum(100); representation.setDefaultValue(0); + representation.setSupportsPartialRendering(true); return representation; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterEdge.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterEdge.java index 55c709573..46a9a294c 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterEdge.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterEdge.java @@ -32,6 +32,7 @@ public class ImageFilterEdge extends SimpleImageFilter { representation.setFilterClass(ImageFilterEdge.class); representation.setTextId(R.string.edge); representation.setButtonId(R.id.edgeButton); + representation.setSupportsPartialRendering(true); return representation; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java index 7a8df71af..b0b0b2dd8 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java @@ -36,6 +36,7 @@ public class ImageFilterExposure extends SimpleImageFilter { representation.setMinimum(-100); representation.setMaximum(100); representation.setDefaultValue(0); + representation.setSupportsPartialRendering(true); return representation; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterHighlights.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterHighlights.java new file mode 100644 index 000000000..12f032dcd --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterHighlights.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.filtershow.filters; + +import com.android.gallery3d.R; + +import android.graphics.Bitmap; +import android.util.Log; + +public class ImageFilterHighlights extends SimpleImageFilter { + private static final String LOGTAG = "ImageFilterVignette"; + + public ImageFilterHighlights() { + mName = "Highlights"; + } + + SplineMath mSpline = new SplineMath(5); + double[] mHighlightCurve = { 0.0, 0.32, 0.418, 0.476, 0.642 }; + + public FilterRepresentation getDefaultRepresentation() { + FilterBasicRepresentation representation = + (FilterBasicRepresentation) super.getDefaultRepresentation(); + representation.setName("Shadows"); + representation.setFilterClass(ImageFilterHighlights.class); + representation.setTextId(R.string.highlight_recovery); + representation.setButtonId(R.id.highlightRecoveryButton); + representation.setMinimum(-100); + representation.setMaximum(100); + representation.setDefaultValue(0); + representation.setSupportsPartialRendering(true); + return representation; + } + + native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float[] luminanceMap); + + @Override + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { + if (getParameters() == null) { + return bitmap; + } + float p = getParameters().getValue(); + double t = p/100.; + for (int i = 0; i < 5; i++) { + double x = i / 4.; + double y = mHighlightCurve[i] *t+x*(1-t); + mSpline.setPoint(i, x, y); + } + + float[][] curve = mSpline.calculatetCurve(256); + float[] luminanceMap = new float[curve.length]; + for (int i = 0; i < luminanceMap.length; i++) { + luminanceMap[i] = curve[i][1]; + } + int w = bitmap.getWidth(); + int h = bitmap.getHeight(); + + nativeApplyFilter(bitmap, w, h, luminanceMap); + return bitmap; + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java index 8c484c72e..b1f9f7365 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java @@ -39,6 +39,7 @@ public class ImageFilterHue extends SimpleImageFilter { representation.setTextId(R.string.hue); representation.setButtonId(R.id.hueButton); representation.setEditorId(BasicEditor.ID); + representation.setSupportsPartialRendering(true); return representation; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterKMeans.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterKMeans.java index f48bd047a..6f785ef54 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterKMeans.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterKMeans.java @@ -45,6 +45,7 @@ public class ImageFilterKMeans extends SimpleImageFilter { representation.setPreviewValue(4); representation.setTextId(R.string.kmeans); representation.setButtonId(R.id.kmeansButton); + representation.setSupportsPartialRendering(true); return representation; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterNegative.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterNegative.java index 841c5c913..c256686fb 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterNegative.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterNegative.java @@ -19,6 +19,7 @@ public class ImageFilterNegative extends ImageFilter { representation.setShowEditingControls(false); representation.setShowParameterValue(false); representation.setEditorId(ImageOnlyEditor.ID); + representation.setSupportsPartialRendering(true); return representation; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java index d5297904d..74712be47 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java @@ -43,9 +43,11 @@ public abstract class ImageFilterRS extends ImageFilter { || (bitmap.getHeight() != sOldBitmap.getHeight())) { if (mInPixelsAllocation != null) { mInPixelsAllocation.destroy(); + mInPixelsAllocation = null; } if (mOutPixelsAllocation != null) { mOutPixelsAllocation.destroy(); + mOutPixelsAllocation = null; } Bitmap bitmapBuffer = bitmap.copy(mBitmapConfig, true); mOutPixelsAllocation = Allocation.createFromBitmap(mRS, bitmapBuffer, diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java index 6cd833206..0febe4957 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java @@ -37,6 +37,7 @@ public class ImageFilterSaturated extends SimpleImageFilter { representation.setMinimum(-100); representation.setMaximum(100); representation.setDefaultValue(0); + representation.setSupportsPartialRendering(true); return representation; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java index e17823955..fd67ee8fc 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java @@ -37,6 +37,7 @@ public class ImageFilterShadows extends SimpleImageFilter { representation.setMinimum(-100); representation.setMaximum(100); representation.setDefaultValue(0); + representation.setSupportsPartialRendering(true); return representation; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java index 9c99d57d0..8afa47451 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java @@ -37,7 +37,8 @@ public class ImageFilterSharpen extends ImageFilterRS { representation.setTextId(R.string.sharpness); representation.setButtonId(R.id.sharpenButton); representation.setOverlayId(R.drawable.filtershow_button_colors_sharpen); - representation.setEditorId(R.id.imageZoom); + representation.setEditorId(R.id.imageShow); + representation.setSupportsPartialRendering(true); return representation; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java index a57af71df..ea315d326 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java @@ -36,6 +36,7 @@ public class ImageFilterVibrance extends SimpleImageFilter { representation.setMinimum(-100); representation.setMaximum(100); representation.setDefaultValue(0); + representation.setSupportsPartialRendering(true); return representation; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java index 465d90bfd..9ff737e32 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java @@ -16,47 +16,67 @@ package com.android.gallery3d.filtershow.filters; -import com.android.gallery3d.R; - import android.graphics.Bitmap; +import android.graphics.Matrix; + import com.android.gallery3d.app.Log; public class ImageFilterVignette extends SimpleImageFilter { - private static final String LOGTAG = "ImageFilterVignette"; public ImageFilterVignette() { mName = "Vignette"; } + @Override public FilterRepresentation getDefaultRepresentation() { - FilterBasicRepresentation representation = - (FilterBasicRepresentation) super.getDefaultRepresentation(); - representation.setName("Vignette"); - representation.setFilterClass(ImageFilterVignette.class); - representation.setPriority(FilterRepresentation.TYPE_VIGNETTE); - representation.setTextId(R.string.vignette); - representation.setButtonId(R.id.vignetteButton); - - representation.setMinimum(-100); - representation.setMaximum(100); - representation.setDefaultValue(0); - + FilterVignetteRepresentation representation = new FilterVignetteRepresentation(); return representation; } - native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float strength); + native protected void nativeApplyFilter( + Bitmap bitmap, int w, int h, int cx, int cy, float radx, float rady, float strength); + + private float calcRadius(float cx, float cy, int w, int h) { + float d = cx; + if (d < (w - cx)) { + d = w - cx; + } + if (d < cy) { + d = cy; + } + if (d < (h - cy)) { + d = h - cy; + } + return d * d * 2.0f; + } @Override public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { - if (getParameters() == null) { + FilterVignetteRepresentation rep = (FilterVignetteRepresentation) getParameters(); + if (rep == null) { return bitmap; } int w = bitmap.getWidth(); int h = bitmap.getHeight(); - float value = getParameters().getValue() / 100.0f; - nativeApplyFilter(bitmap, w, h, value); - + float value = rep.getValue() / 100.0f; + float cx = w / 2; + float cy = h / 2; + float r = calcRadius(cx, cy, w, h); + float rx = r; + float ry = r; + if (rep.isCenterSet()) { + Matrix m = getOriginalToScreenMatrix(w, h); + cx = rep.getCenterX(); + cy = rep.getCenterY(); + float[] center = new float[] { cx, cy }; + m.mapPoints(center); + cx = center[0]; + cy = center[1]; + rx = m.mapRadius(rep.getRadiusX()); + ry = m.mapRadius(rep.getRadiusY()); + } + nativeApplyFilter(bitmap, w, h, (int) cx, (int) cy, rx, ry, value); return bitmap; } } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java index 2f4852306..c4c293a4b 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java @@ -37,6 +37,7 @@ public class ImageFilterWBalance extends ImageFilter { representation.setShowEditingControls(false); representation.setShowParameterValue(false); representation.setEditorId(ImageOnlyEditor.ID); + representation.setSupportsPartialRendering(true); return representation; } diff --git a/src/com/android/gallery3d/filtershow/filters/SplineMath.java b/src/com/android/gallery3d/filtershow/filters/SplineMath.java new file mode 100644 index 000000000..5b12d0a61 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/SplineMath.java @@ -0,0 +1,166 @@ +package com.android.gallery3d.filtershow.filters; + + +public class SplineMath { + double[][] mPoints = new double[6][2]; + double[] mDerivatives; + SplineMath(int n) { + mPoints = new double[n][2]; + } + + public void setPoint(int index, double x, double y) { + mPoints[index][0] = x; + mPoints[index][1] = y; + mDerivatives = null; + } + + public float[][] calculatetCurve(int n) { + float[][] curve = new float[n][2]; + double[][] points = new double[mPoints.length][2]; + for (int i = 0; i < mPoints.length; i++) { + + points[i][0] = mPoints[i][0]; + points[i][1] = mPoints[i][1]; + + } + double[] derivatives = solveSystem(points); + float start = (float) points[0][0]; + float end = (float) (points[points.length - 1][0]); + + curve[0][0] = (float) (points[0][0]); + curve[0][1] = (float) (points[0][1]); + int last = curve.length - 1; + curve[last][0] = (float) (points[points.length - 1][0]); + curve[last][1] = (float) (points[points.length - 1][1]); + + for (int i = 0; i < curve.length; i++) { + + double[] cur = null; + double[] next = null; + double x = start + i * (end - start) / (curve.length - 1); + int pivot = 0; + for (int j = 0; j < points.length - 1; j++) { + if (x >= points[j][0] && x <= points[j + 1][0]) { + pivot = j; + } + } + cur = points[pivot]; + next = points[pivot + 1]; + if (x <= next[0]) { + double x1 = cur[0]; + double x2 = next[0]; + double y1 = cur[1]; + double y2 = next[1]; + + // Use the second derivatives to apply the cubic spline + // equation: + double delta = (x2 - x1); + double delta2 = delta * delta; + double b = (x - x1) / delta; + double a = 1 - b; + double ta = a * y1; + double tb = b * y2; + double tc = (a * a * a - a) * derivatives[pivot]; + double td = (b * b * b - b) * derivatives[pivot + 1]; + double y = ta + tb + (delta2 / 6) * (tc + td); + + curve[i][0] = (float) (x); + curve[i][1] = (float) (y); + } else { + curve[i][0] = (float) (next[0]); + curve[i][1] = (float) (next[1]); + } + } + return curve; + } + + public double getValue(double x) { + double[] cur = null; + double[] next = null; + if (mDerivatives == null) + mDerivatives = solveSystem(mPoints); + int pivot = 0; + for (int j = 0; j < mPoints.length - 1; j++) { + pivot = j; + if (x <= mPoints[j][0]) { + break; + } + } + cur = mPoints[pivot]; + next = mPoints[pivot + 1]; + double x1 = cur[0]; + double x2 = next[0]; + double y1 = cur[1]; + double y2 = next[1]; + + // Use the second derivatives to apply the cubic spline + // equation: + double delta = (x2 - x1); + double delta2 = delta * delta; + double b = (x - x1) / delta; + double a = 1 - b; + double ta = a * y1; + double tb = b * y2; + double tc = (a * a * a - a) * mDerivatives[pivot]; + double td = (b * b * b - b) * mDerivatives[pivot + 1]; + double y = ta + tb + (delta2 / 6) * (tc + td); + + return y; + + } + + double[] solveSystem(double[][] points) { + int n = points.length; + double[][] system = new double[n][3]; + double[] result = new double[n]; // d + double[] solution = new double[n]; // returned coefficients + system[0][1] = 1; + system[n - 1][1] = 1; + double d6 = 1.0 / 6.0; + double d3 = 1.0 / 3.0; + + // let's create a tridiagonal matrix representing the + // system, and apply the TDMA algorithm to solve it + // (see http://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm) + for (int i = 1; i < n - 1; i++) { + double deltaPrevX = points[i][0] - points[i - 1][0]; + double deltaX = points[i + 1][0] - points[i - 1][0]; + double deltaNextX = points[i + 1][0] - points[i][0]; + double deltaNextY = points[i + 1][1] - points[i][1]; + double deltaPrevY = points[i][1] - points[i - 1][1]; + system[i][0] = d6 * deltaPrevX; // a_i + system[i][1] = d3 * deltaX; // b_i + system[i][2] = d6 * deltaNextX; // c_i + result[i] = (deltaNextY / deltaNextX) - (deltaPrevY / deltaPrevX); // d_i + } + + // Forward sweep + for (int i = 1; i < n; i++) { + // m = a_i/b_i-1 + double m = system[i][0] / system[i - 1][1]; + // b_i = b_i - m(c_i-1) + system[i][1] = system[i][1] - m * system[i - 1][2]; + // d_i = d_i - m(d_i-1) + result[i] = result[i] - m * result[i - 1]; + } + + // Back substitution + solution[n - 1] = result[n - 1] / system[n - 1][1]; + for (int i = n - 2; i >= 0; --i) { + solution[i] = (result[i] - system[i][2] * solution[i + 1]) / system[i][1]; + } + return solution; + } + + public static void main(String[] args) { + SplineMath s = new SplineMath(10); + for (int i = 0; i < 10; i++) { + s.setPoint(i, i, i); + } + float[][] curve = s.calculatetCurve(40); + + for (int j = 0; j < curve.length; j++) { + System.out.println(curve[j][0] + "," + curve[j][1]); + } + } +} diff --git a/src/com/android/gallery3d/filtershow/imageshow/EclipseControl.java b/src/com/android/gallery3d/filtershow/imageshow/EclipseControl.java new file mode 100644 index 000000000..b4ca8e1f1 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/imageshow/EclipseControl.java @@ -0,0 +1,256 @@ +/* + * 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.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.RadialGradient; +import android.graphics.RectF; +import android.graphics.Shader; + +import com.android.gallery3d.R; + +public class EclipseControl { + private float mCenterX = Float.NaN; + private float mCenterY = 0; + private float mRadiusX = 200; + private float mRadiusY = 300; + private static int MIN_TOUCH_DIST = 80;// should be a resource & in dips + + private float[] handlex = new float[9]; + private float[] handley = new float[9]; + private int mSliderColor; + private int mCenterDotSize = 40; + private float mDownX; + private float mDownY; + private float mDownCenterX; + private float mDownCenterY; + private float mDownRadiusX; + private float mDownRadiusY; + private Matrix mScrToImg; + + private final static int HAN_CENTER = 0; + private final static int HAN_NORTH = 7; + private final static int HAN_NE = 8; + private final static int HAN_EAST = 1; + private final static int HAN_SE = 2; + private final static int HAN_SOUTH = 3; + private final static int HAN_SW = 4; + private final static int HAN_WEST = 5; + private final static int HAN_NW = 6; + + public EclipseControl(Context context) { + mSliderColor = context.getResources().getColor(R.color.slider_line_color); + } + + public void setRadius(float x, float y) { + mRadiusX = x; + mRadiusY = y; + } + + public void setCenter(float x, float y) { + mCenterX = x; + mCenterY = y; + } + + public int getCloseHandle(float x, float y) { + float min = Float.MAX_VALUE; + int handle = -1; + for (int i = 0; i < handlex.length; i++) { + float dx = handlex[i] - x; + float dy = handley[i] - y; + float dist = dx * dx + dy * dy; + if (dist < min) { + min = dist; + handle = i; + } + } + + if (min < MIN_TOUCH_DIST * MIN_TOUCH_DIST) { + return handle; + } + for (int i = 0; i < handlex.length; i++) { + float dx = handlex[i] - x; + float dy = handley[i] - y; + float dist = (float) Math.sqrt(dx * dx + dy * dy); + } + + return -1; + } + + public void setScrToImageMatrix(Matrix scrToImg) { + mScrToImg = scrToImg; + } + + public void actionDown(float x, float y, Oval oval) { + float[] point = new float[] { + x, y }; + mScrToImg.mapPoints(point); + mDownX = point[0]; + mDownY = point[1]; + mDownCenterX = oval.getCenterX(); + mDownCenterY = oval.getCenterY(); + mDownRadiusX = oval.getRadiusX(); + mDownRadiusY = oval.getRadiusY(); + } + + public void actionMove(int handle, float x, float y, Oval oval) { + float[] point = new float[] { + x, y }; + mScrToImg.mapPoints(point); + x = point[0]; + y = point[1]; + int sign = 1; + switch (handle) { + case HAN_CENTER: + float ctrdx = mDownX - mDownCenterX; + float ctrdy = mDownY - mDownCenterY; + oval.setCenter(x - ctrdx, y - ctrdy); + // setRepresentation(mVignetteRep); + break; + case HAN_NORTH: + sign = -1; + case HAN_SOUTH: + float raddy = mDownRadiusY - Math.abs(mDownY - mDownCenterY); + oval.setRadiusY(Math.abs(y - oval.getCenterY() + sign * raddy)); + break; + case HAN_EAST: + sign = -1; + case HAN_WEST: + float raddx = mDownRadiusX - Math.abs(mDownX - mDownCenterX); + oval.setRadiusX(Math.abs(x - oval.getCenterX() - sign * raddx)); + break; + case HAN_SE: + case HAN_NE: + case HAN_SW: + case HAN_NW: + float sin45 = (float) Math.sin(45); + float dr = (mDownRadiusX + mDownRadiusY) * sin45; + float ctr_dx = mDownX - mDownCenterX; + float ctr_dy = mDownY - mDownCenterY; + float downRad = Math.abs(ctr_dx) + Math.abs(ctr_dy) - dr; + float rx = oval.getRadiusX(); + float ry = oval.getRadiusY(); + float r = (Math.abs(rx) + Math.abs(ry)) * sin45; + float dx = x - oval.getCenterX(); + float dy = y - oval.getCenterY(); + float nr = Math.abs(Math.abs(dx) + Math.abs(dy) - downRad); + oval.setRadius(rx * nr / r, ry * nr / r); + + break; + } + } + + void paintPoint(Canvas canvas, float x, float y) { + if (x == Float.NaN) { + return; + } + + Paint paint = new Paint(); + + paint.setStyle(Paint.Style.FILL); + paint.setColor(Color.BLUE); + int[] colors3 = new int[] { + mSliderColor, mSliderColor, 0x66000000, 0 }; + RadialGradient g = new RadialGradient(x, y, mCenterDotSize, colors3, new float[] { + 0, .3f, .31f, 1 }, Shader.TileMode.CLAMP); + paint.setShader(g); + canvas.drawCircle(x, y, mCenterDotSize, paint); + } + + void paintRadius(Canvas canvas, float cx, float cy, float rx, float ry) { + if (cx == Float.NaN) { + return; + } + int mSliderColor = 0xFF33B5E5; + Paint paint = new Paint(); + RectF rect = new RectF(cx - rx, cy - ry, cx + rx, cy + ry); + paint.setAntiAlias(true); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(6); + paint.setColor(Color.BLACK); + paintOvallines(canvas, rect, paint, cx, cy, rx, ry); + + paint.setStrokeWidth(3); + paint.setColor(Color.WHITE); + paintOvallines(canvas, rect, paint, cx, cy, rx, ry); + } + + public void paintOvallines( + Canvas canvas, RectF rect, Paint paint, float cx, float cy, float rx, float ry) { + canvas.drawOval(rect, paint); + float da = 4; + float arclen = da + da; + for (int i = 0; i < 361; i += 90) { + float dx = rx + 10; + float dy = ry + 10; + rect.left = cx - dx; + rect.top = cy - dy; + rect.right = cx + dx; + rect.bottom = cy + dy; + canvas.drawArc(rect, i - da, arclen, false, paint); + dx = rx - 10; + dy = ry - 10; + rect.left = cx - dx; + rect.top = cy - dy; + rect.right = cx + dx; + rect.bottom = cy + dy; + canvas.drawArc(rect, i - da, arclen, false, paint); + } + da *= 2; + for (int i = 45; i < 361; i += 90) { + double angle = Math.PI * i / 180.; + float x = cx + (float) (rx * Math.cos(angle)); + float y = cy + (float) (ry * Math.sin(angle)); + canvas.drawRect(x - da, y - da, x + da, y + da, paint); + } + rect.left = cx - rx; + rect.top = cy - ry; + rect.right = cx + rx; + rect.bottom = cy + ry; + } + + public void fillHandles(Canvas canvas, float cx, float cy, float rx, float ry) { + handlex[0] = cx; + handley[0] = cy; + int k = 1; + + for (int i = 0; i < 360; i += 45) { + double angle = Math.PI * i / 180.; + + float x = cx + (float) (rx * Math.cos(angle)); + float y = cy + (float) (ry * Math.sin(angle)); + handlex[k] = x; + handley[k] = y; + + k++; + } + } + + public void draw(Canvas canvas) { + paintRadius(canvas, mCenterX, mCenterY, mRadiusX, mRadiusY); + fillHandles(canvas, mCenterX, mCenterY, mRadiusX, mRadiusY); + paintPoint(canvas, mCenterX, mCenterY); + } + + public boolean isUndefined() { + return Float.isNaN(mCenterX); + } +} diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageDraw.java b/src/com/android/gallery3d/filtershow/imageshow/ImageDraw.java index 0cd229968..479652ce3 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageDraw.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageDraw.java @@ -27,11 +27,13 @@ public class ImageDraw extends ImageShow { public ImageDraw(Context context, AttributeSet attrs) { super(context, attrs); resetParameter(); + super.setOriginalDisabled(true); } public ImageDraw(Context context) { super(context); resetParameter(); + super.setOriginalDisabled(true); } public void setEditor(EditorDraw editorDraw) { @@ -82,20 +84,32 @@ public class ImageDraw extends ImageShow { float[] mTmpPoint = new float[2]; // so we do not malloc @Override public boolean onTouchEvent(MotionEvent event) { - if (event.getPointerCount() != 1) { - return false; + boolean ret = super.onTouchEvent(event); + if (event.getPointerCount() > 1) { + if (mFRep.getCurrentDrawing() != null) { + mFRep.clearCurrentSection(); + mEditorDraw.commitLocalRepresentation(); + } + return ret; + } + if (event.getAction() != MotionEvent.ACTION_DOWN) { + if (mFRep.getCurrentDrawing() == null) { + return ret; + } } ImageFilterDraw filter = (ImageFilterDraw) getCurrentFilter(); if (event.getAction() == MotionEvent.ACTION_DOWN) { + calcScreenMapping(); mTmpPoint[0] = event.getX(); mTmpPoint[1] = event.getY(); mToOrig.mapPoints(mTmpPoint); mFRep.startNewSection(mType, mCurrentColor, mCurrentSize, mTmpPoint[0], mTmpPoint[1]); - } + if (event.getAction() == MotionEvent.ACTION_MOVE) { + int historySize = event.getHistorySize(); final int pointerCount = event.getPointerCount(); for (int h = 0; h < historySize; h++) { @@ -108,6 +122,7 @@ public class ImageDraw extends ImageShow { } } } + if (event.getAction() == MotionEvent.ACTION_UP) { mTmpPoint[0] = event.getX(); mTmpPoint[1] = event.getY(); @@ -119,19 +134,10 @@ public class ImageDraw extends ImageShow { return true; } - Matrix mRotateToScreen; - Matrix mToScreen; - Matrix mToOrig = new Matrix(); + Matrix mRotateToScreen = new Matrix(); + Matrix mToOrig; private void calcScreenMapping() { - - GeometryMetadata geo = getImagePreset().mGeoData; - mToScreen = geo.getOriginalToScreen(false, - mImageLoader.getOriginalBounds().width(), - mImageLoader.getOriginalBounds().height(), getWidth(), getHeight()); - mRotateToScreen = geo.getOriginalToScreen(true, - mImageLoader.getOriginalBounds().width(), - mImageLoader.getOriginalBounds().height(), getWidth(), getHeight()); - mRotateToScreen.invert(mToOrig); + mToOrig = getScreenToImageMatrix(true); mToOrig.invert(mRotateToScreen); } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImagePoint.java b/src/com/android/gallery3d/filtershow/imageshow/ImagePoint.java index 06b055d3c..625cdbe0d 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImagePoint.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImagePoint.java @@ -75,13 +75,9 @@ public abstract class ImagePoint extends ImageShow { paint.setColor(Color.RED); paint.setStrokeWidth(2); - 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()); + Matrix originalToScreen = getImageToScreenMatrix(false); + Matrix originalRotateToScreen = getImageToScreenMatrix(true); + if (mRedEyeRep != null) { for (FilterPoint candidate : mRedEyeRep.getCandidates()) { drawPoint(candidate, canvas, originalToScreen, originalRotateToScreen, paint); diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageRedEye.java b/src/com/android/gallery3d/filtershow/imageshow/ImageRedEye.java index c3ff5e151..40433a02e 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageRedEye.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageRedEye.java @@ -46,6 +46,15 @@ public class ImageRedEye extends ImagePoint { public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); + + if (event.getPointerCount() > 1) { + return true; + } + + if (didFinishScalingOperation()) { + return true; + } + float ex = event.getX(); float ey = event.getY(); @@ -62,16 +71,8 @@ public class ImageRedEye extends ImagePoint { 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 originalNoRotateToScreen = getImageToScreenMatrix(false); + Matrix originalToScreen = getImageToScreenMatrix(true); Matrix invert = new Matrix(); originalToScreen.invert(invert); RectF r = new RectF(mCurrentRect); diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java index 463756839..39e0cc82b 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java @@ -17,38 +17,28 @@ package com.android.gallery3d.filtershow.imageshow; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.RectF; +import android.graphics.*; import android.net.Uri; import android.os.Handler; import android.util.AttributeSet; import android.util.Log; -import android.view.GestureDetector; +import android.view.*; import android.view.GestureDetector.OnDoubleTapListener; import android.view.GestureDetector.OnGestureListener; -import android.view.MotionEvent; -import android.view.View; import android.widget.LinearLayout; -import android.widget.SeekBar; -import android.widget.SeekBar.OnSeekBarChangeListener; import com.android.gallery3d.filtershow.FilterShowActivity; import com.android.gallery3d.filtershow.PanelController; import com.android.gallery3d.filtershow.cache.ImageLoader; +import com.android.gallery3d.filtershow.cache.RenderingRequestCaller; import com.android.gallery3d.filtershow.filters.ImageFilter; import com.android.gallery3d.filtershow.presets.ImagePreset; -import com.android.gallery3d.filtershow.ui.SliderListener; import java.io.File; public class ImageShow extends View implements OnGestureListener, - OnDoubleTapListener, - SliderListener, - OnSeekBarChangeListener { + ScaleGestureDetector.OnScaleGestureListener, + OnDoubleTapListener { private static final String LOGTAG = "ImageShow"; @@ -64,9 +54,10 @@ public class ImageShow extends View implements OnGestureListener, private static int mBackgroundColor = Color.RED; private GestureDetector mGestureDetector = null; + private ScaleGestureDetector mScaleGestureDetector = null; protected Rect mImageBounds = new Rect(); - + private boolean mOriginalDisabled = false; private boolean mTouchShowOriginal = false; private long mTouchShowOriginalDate = 0; private final long mTouchShowOriginalDelayMin = 200; // 200ms @@ -75,10 +66,9 @@ public class ImageShow extends View implements OnGestureListener, private static int UNVEIL_HORIZONTAL = 1; private static int UNVEIL_VERTICAL = 2; - private int mTouchDownX = 0; - private int mTouchDownY = 0; - protected float mTouchX = 0; - protected float mTouchY = 0; + private Point mTouchDown = new Point(); + private Point mTouch = new Point(); + private boolean mFinishedScalingOperation = false; private static int mOriginalTextMargin = 8; private static int mOriginalTextSize = 26; @@ -88,16 +78,10 @@ public class ImageShow extends View implements OnGestureListener, return new GeometryMetadata(getImagePreset().mGeoData); } - public void setGeometry(GeometryMetadata d) { - getImagePreset().mGeoData.set(d); - } - - private boolean mShowControls = false; private String mToast = null; private boolean mShowToast = false; private boolean mImportantToast = false; - private SeekBar mSeekBar = null; private PanelController mController = null; private FilterShowActivity mActivity = null; @@ -137,30 +121,9 @@ public class ImageShow extends View implements OnGestureListener, private final Handler mHandler = new Handler(); public void select() { - if (mSeekBar != null) { - mSeekBar.setOnSeekBarChangeListener(this); - } - } - - private int parameterToUI(int parameter, int minp, int maxp, int uimax) { - return (uimax * (parameter - minp)) / (maxp - minp); - } - - private int uiToParameter(int ui, int minp, int maxp, int uimax) { - return ((maxp - minp) * ui) / uimax + minp; - } - - public void updateSeekBar(int parameter, int minp, int maxp) { - if (mSeekBar == null) { - return; - } - int seekMax = mSeekBar.getMax(); - int progress = parameterToUI(parameter, minp, maxp, seekMax); - mSeekBar.setProgress(progress); } public void unselect() { - } public boolean hasModifications() { @@ -182,7 +145,6 @@ public class ImageShow extends View implements OnGestureListener, return mController; } - @Override public void onNewValue(int parameter) { if (getImagePreset() != null) { getImagePreset().fillImageStateAdapter(MasterImage.getImage().getState()); @@ -194,15 +156,8 @@ public class ImageShow extends View implements OnGestureListener, mActivity.enableSave(hasModifications()); } - @Override - public void onTouchDown(float x, float y) { - mTouchX = x; - mTouchY = y; - invalidate(); - } - - @Override - public void onTouchUp() { + public Point getTouchPoint() { + return mTouch; } public ImageShow(Context context, AttributeSet attrs) { @@ -223,6 +178,7 @@ public class ImageShow extends View implements OnGestureListener, public void setupGestureDetector(Context context) { mGestureDetector = new GestureDetector(context, this); + mScaleGestureDetector = new ScaleGestureDetector(context, this); } @Override @@ -232,10 +188,6 @@ public class ImageShow extends View implements OnGestureListener, setMeasuredDimension(parentWidth, parentHeight); } - public void setSeekBar(SeekBar seekBar) { - mSeekBar = seekBar; - } - public ImageFilter getCurrentFilter() { return MasterImage.getImage().getCurrentFilter(); } @@ -269,6 +221,42 @@ public class ImageShow extends View implements OnGestureListener, return GeometryMath.roundNearest(getImagePreset().mGeoData.getPreviewCropBounds()); } + /* consider moving the following 2 methods into a subclass */ + /** + * This function calculates a Image to Screen Transformation matrix + * + * @param reflectRotation set true if you want the rotation encoded + * @return Image to Screen transformation matrix + */ + protected Matrix getImageToScreenMatrix(boolean reflectRotation) { + GeometryMetadata geo = getImagePreset().mGeoData; + if (geo == null || mImageLoader == null + || mImageLoader.getOriginalBounds() == null) { + return new Matrix(); + } + Matrix m = geo.getOriginalToScreen(reflectRotation, + mImageLoader.getOriginalBounds().width(), + mImageLoader.getOriginalBounds().height(), getWidth(), getHeight()); + Point translate = MasterImage.getImage().getTranslation(); + float scaleFactor = MasterImage.getImage().getScaleFactor(); + m.postTranslate(translate.x, translate.y); + m.postScale(scaleFactor, scaleFactor, getWidth()/2.0f, getHeight()/2.0f); + return m; + } + + /** + * This function calculates a to Screen Image Transformation matrix + * + * @param reflectRotation set true if you want the rotation encoded + * @return Screen to Image transformation matrix + */ + protected Matrix getScreenToImageMatrix(boolean reflectRotation) { + Matrix m = getImageToScreenMatrix(reflectRotation); + Matrix invert = new Matrix(); + m.invert(invert); + return invert; + } + public Rect getDisplayedImageBounds() { return mImageBounds; } @@ -308,8 +296,18 @@ public class ImageShow extends View implements OnGestureListener, @Override public void onDraw(Canvas canvas) { + MasterImage.getImage().setImageShowSize(getWidth(), getHeight()); + canvas.save(); + // TODO: center scale on gesture + float cx = canvas.getWidth()/2.0f; + float cy = canvas.getHeight()/2.0f; + float scaleFactor = MasterImage.getImage().getScaleFactor(); + Point translation = MasterImage.getImage().getTranslation(); + canvas.scale(scaleFactor, scaleFactor, cx, cy); + canvas.translate(translation.x, translation.y); drawBackground(canvas); defaultDrawImage(canvas); + canvas.restore(); if (showTitle() && getImagePreset() != null) { mPaint.setARGB(200, 0, 0, 0); @@ -322,6 +320,12 @@ public class ImageShow extends View implements OnGestureListener, 1.5f * mTextPadding, mPaint); } + Bitmap partialPreview = MasterImage.getImage().getPartialImage(); + if (partialPreview != null) { + Rect src = new Rect(0, 0, partialPreview.getWidth(), partialPreview.getHeight()); + Rect dest = new Rect(0, 0, getWidth(), getHeight()); + canvas.drawBitmap(partialPreview, src, dest, mPaint); + } drawToast(canvas); } @@ -370,7 +374,7 @@ public class ImageShow extends View implements OnGestureListener, canvas.save(); if (image != null) { if (mShowOriginalDirection == 0) { - if ((mTouchY - mTouchDownY) > (mTouchX - mTouchDownX)) { + if ((mTouch.y - mTouchDown.y) > (mTouch.x - mTouchDown.x)) { mShowOriginalDirection = UNVEIL_VERTICAL; } else { mShowOriginalDirection = UNVEIL_HORIZONTAL; @@ -381,9 +385,9 @@ public class ImageShow extends View implements OnGestureListener, int py = 0; if (mShowOriginalDirection == UNVEIL_VERTICAL) { px = mImageBounds.width(); - py = (int) (mTouchY - mImageBounds.top); + py = (int) (mTouch.y - mImageBounds.top); } else { - px = (int) (mTouchX - mImageBounds.left); + px = (int) (mTouch.x - mImageBounds.left); py = mImageBounds.height(); } @@ -395,11 +399,11 @@ public class ImageShow extends View implements OnGestureListener, paint.setColor(Color.BLACK); if (mShowOriginalDirection == UNVEIL_VERTICAL) { - canvas.drawLine(mImageBounds.left, mTouchY - 1, - mImageBounds.right, mTouchY - 1, paint); + canvas.drawLine(mImageBounds.left, mTouch.y - 1, + mImageBounds.right, mTouch.y - 1, paint); } else { - canvas.drawLine(mTouchX - 1, mImageBounds.top, - mTouchX - 1, mImageBounds.bottom, paint); + canvas.drawLine(mTouch.x - 1, mImageBounds.top, + mTouch.x - 1, mImageBounds.bottom, paint); } Rect bounds = new Rect(); @@ -435,34 +439,10 @@ public class ImageShow extends View implements OnGestureListener, } } - public ImageShow setShowControls(boolean value) { - mShowControls = value; - if (mShowControls) { - if (mSeekBar != null) { - mSeekBar.setVisibility(View.VISIBLE); - } - } else { - if (mSeekBar != null) { - mSeekBar.setVisibility(View.INVISIBLE); - } - } - return this; - } - - public boolean showControls() { - return mShowControls; - } - - public boolean showHires() { - return true; - } - public boolean showTitle() { return false; } - - public void setImageLoader(ImageLoader loader) { mImageLoader = loader; if (mImageLoader != null) { @@ -531,35 +511,73 @@ public class ImageShow extends View implements OnGestureListener, mImageLoader.returnFilteredResult(getImagePreset(), filterShowActivity); } + public boolean scaleInProgress() { + return mScaleGestureDetector.isInProgress(); + } + + protected boolean isOriginalDisabled() { + return mOriginalDisabled; + } + + protected void setOriginalDisabled(boolean originalDisabled) { + mOriginalDisabled = originalDisabled; + } + @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); - if (mGestureDetector != null) { - mGestureDetector.onTouchEvent(event); + mGestureDetector.onTouchEvent(event); + boolean scaleInProgress = scaleInProgress(); + mScaleGestureDetector.onTouchEvent(event); + if (!scaleInProgress() && scaleInProgress) { + // If we were scaling, the scale will stop but we will + // still issue an ACTION_UP. Let the subclasses know. + mFinishedScalingOperation = true; } + int ex = (int) event.getX(); int ey = (int) event.getY(); if (event.getAction() == MotionEvent.ACTION_DOWN) { - mTouchDownX = ex; - mTouchDownY = ey; + mTouchDown.x = ex; + mTouchDown.y = ey; mTouchShowOriginalDate = System.currentTimeMillis(); mShowOriginalDirection = 0; + MasterImage.getImage().setOriginalTranslation(MasterImage.getImage().getTranslation()); } + if (event.getAction() == MotionEvent.ACTION_MOVE) { - mTouchX = ex; - mTouchY = ey; - if (!mActivity.isShowingHistoryPanel() + mTouch.x = ex; + mTouch.y = ey; + + if (event.getPointerCount() == 2) { + float scaleFactor = MasterImage.getImage().getScaleFactor(); + if (scaleFactor >= 1) { + float translateX = (mTouch.x - mTouchDown.x) / scaleFactor; + float translateY = (mTouch.y - mTouchDown.y) / scaleFactor; + Point originalTranslation = MasterImage.getImage().getOriginalTranslation(); + Point translation = MasterImage.getImage().getTranslation(); + translation.x = (int) (originalTranslation.x + translateX); + translation.y = (int) (originalTranslation.y + translateY); + MasterImage.getImage().setTranslation(translation); + } + } else if (!mOriginalDisabled && !mActivity.isShowingHistoryPanel() && (System.currentTimeMillis() - mTouchShowOriginalDate - > mTouchShowOriginalDelayMin)) { + > mTouchShowOriginalDelayMin) + && event.getPointerCount() == 1) { mTouchShowOriginal = true; } } + if (event.getAction() == MotionEvent.ACTION_UP) { mTouchShowOriginal = false; - mTouchDownX = 0; - mTouchDownY = 0; - mTouchX = 0; - mTouchY = 0; + mTouchDown.x = 0; + mTouchDown.y = 0; + mTouch.x = 0; + mTouch.y = 0; + if (MasterImage.getImage().getScaleFactor() <= 1) { + MasterImage.getImage().setScaleFactor(1); + MasterImage.getImage().resetTranslation(); + } } invalidate(); return true; @@ -570,48 +588,6 @@ public class ImageShow extends View implements OnGestureListener, invalidate(); } - public float getImageRotation() { - return getImagePreset().mGeoData.getRotation(); - } - - public float getImageRotationZoomFactor() { - return getImagePreset().mGeoData.getScaleFactor(); - } - - public void setImageRotation(float r) { - getImagePreset().mGeoData.setRotation(r); - } - - public void setImageRotationZoomFactor(float f) { - getImagePreset().mGeoData.setScaleFactor(f); - } - - public void setImageRotation(float imageRotation, - float imageRotationZoomFactor) { - float r = getImageRotation(); - if (imageRotation != r) { - invalidate(); - } - setImageRotation(imageRotation); - setImageRotationZoomFactor(imageRotationZoomFactor); - } - - @Override - public void onProgressChanged(SeekBar arg0, int progress, boolean arg2) { - int parameter = progress; - onNewValue(parameter); - } - - @Override - public void onStartTrackingTouch(SeekBar arg0) { - // TODO Auto-generated method stub - } - - @Override - public void onStopTrackingTouch(SeekBar arg0) { - // TODO Auto-generated method stub - } - @Override public boolean onDoubleTap(MotionEvent arg0) { // TODO Auto-generated method stub @@ -638,13 +614,17 @@ public class ImageShow extends View implements OnGestureListener, @Override public boolean onFling(MotionEvent startEvent, MotionEvent endEvent, float arg2, float arg3) { + if (mActivity == null) { + return false; + } if ((!mActivity.isShowingHistoryPanel() && startEvent.getX() > endEvent.getX()) || (mActivity.isShowingHistoryPanel() && endEvent.getX() > startEvent.getX())) { if (!mTouchShowOriginal || (mTouchShowOriginal && (System.currentTimeMillis() - mTouchShowOriginalDate < mTouchShowOriginalDelayMax))) { - mActivity.toggleHistoryPanel(); + // TODO fix gesture. + // mActivity.toggleHistoryPanel(); } } return true; @@ -673,11 +653,46 @@ public class ImageShow extends View implements OnGestureListener, } public boolean useUtilityPanel() { - return false; + return true; } public void openUtilityPanel(final LinearLayout accessoryViewList) { // TODO Auto-generated method stub } + @Override + public boolean onScale(ScaleGestureDetector detector) { + float scaleFactor = MasterImage.getImage().getScaleFactor(); + scaleFactor = scaleFactor * detector.getScaleFactor(); + if (scaleFactor > 2) { + scaleFactor = 2; + } + if (scaleFactor < 0.5) { + scaleFactor = 0.5f; + } + MasterImage.getImage().setScaleFactor(scaleFactor); + return true; + } + + @Override + public boolean onScaleBegin(ScaleGestureDetector detector) { + return true; + } + + @Override + public void onScaleEnd(ScaleGestureDetector detector) { + if (MasterImage.getImage().getScaleFactor() < 1) { + MasterImage.getImage().setScaleFactor(1); + invalidate(); + } + } + + public boolean didFinishScalingOperation() { + if (mFinishedScalingOperation) { + mFinishedScalingOperation = false; + return true; + } + return false; + } + } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java index 7a539da8f..dfd950565 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java @@ -22,7 +22,6 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; -import android.graphics.Path; import android.graphics.RectF; import android.util.AttributeSet; @@ -110,8 +109,6 @@ public class ImageStraighten extends ImageGeometry { RectF bounds = drawTransformed(canvas, image, gPaint, o); // Draw the grid - Path path = new Path(); - path.addRect(bounds, Path.Direction.CCW); gPaint.setARGB(255, 255, 255, 255); gPaint.setStrokeWidth(3); gPaint.setStyle(Paint.Style.FILL_AND_STROKE); @@ -122,7 +119,7 @@ public class ImageStraighten extends ImageGeometry { if (mMode == MODES.MOVE) { canvas.save(); - canvas.clipPath(path); + canvas.clipRect(bounds); int n = 16; float step = dWidth / n; diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageTinyPlanet.java b/src/com/android/gallery3d/filtershow/imageshow/ImageTinyPlanet.java index 3e95d4e15..3795d1f21 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageTinyPlanet.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageTinyPlanet.java @@ -22,6 +22,7 @@ import android.graphics.Paint; import android.util.AttributeSet; import android.view.MotionEvent; +import com.android.gallery3d.filtershow.editors.BasicEditor; import com.android.gallery3d.filtershow.editors.EditorTinyPlanet; import com.android.gallery3d.filtershow.filters.FilterCurvesRepresentation; import com.android.gallery3d.filtershow.filters.FilterTinyPlanetRepresentation; @@ -37,7 +38,7 @@ public class ImageTinyPlanet extends ImageShow { private float mCenterY = 0; private float mStartAngle = 0; private FilterTinyPlanetRepresentation mTinyPlanetRep; - private EditorTinyPlanet mEditorTinyPlanet; + private BasicEditor mEditorTinyPlanet; public ImageTinyPlanet(Context context) { super(context); @@ -94,7 +95,7 @@ public class ImageTinyPlanet extends ImageShow { mTinyPlanetRep = tinyPlanetRep; } - public void setEditor(EditorTinyPlanet editorTinyPlanet) { + public void setEditor(BasicEditor editorTinyPlanet) { mEditorTinyPlanet = editorTinyPlanet; } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageVignette.java b/src/com/android/gallery3d/filtershow/imageshow/ImageVignette.java new file mode 100644 index 000000000..a51d10276 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageVignette.java @@ -0,0 +1,142 @@ +/* + * 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.Canvas; +import android.graphics.Matrix; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; + +import com.android.gallery3d.filtershow.editors.EditorVignette; +import com.android.gallery3d.filtershow.filters.FilterVignetteRepresentation; + +public class ImageVignette extends ImageShow { + private static final String LOGTAG = "ImageVignette"; + + private FilterVignetteRepresentation mVignetteRep; + private EditorVignette mEditorVignette; + + private int mActiveHandle = -1; + + EclipseControl mElipse; + + public ImageVignette(Context context) { + super(context); + mElipse = new EclipseControl(context); + } + + public ImageVignette(Context context, AttributeSet attrs) { + super(context, attrs); + mElipse = new EclipseControl(context); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + int mask = event.getActionMasked(); + if (mActiveHandle == -1) { + if (MotionEvent.ACTION_DOWN != mask) { + return super.onTouchEvent(event); + } + if (event.getPointerCount() == 1) { + mActiveHandle = mElipse.getCloseHandle(event.getX(), event.getY()); + } + if (mActiveHandle == -1) { + return super.onTouchEvent(event); + } + } else { + switch (mask) { + case MotionEvent.ACTION_UP: + mActiveHandle = -1; + break; + case MotionEvent.ACTION_DOWN: + if (event.getPointerCount() == 1) { + Log.v(LOGTAG, "################### ACTION_DOWN odd " + mActiveHandle + + " touches=1"); + } + break; + } + } + float x = event.getX(); + float y = event.getY(); + + mElipse.setScrToImageMatrix(getScreenToImageMatrix(true)); + + switch (mask) { + case (MotionEvent.ACTION_DOWN): + mElipse.actionDown(x, y, mVignetteRep); + break; + case (MotionEvent.ACTION_UP): + case (MotionEvent.ACTION_MOVE): + + mElipse.actionMove(mActiveHandle, x, y, mVignetteRep); + setRepresentation(mVignetteRep); + break; + } + resetImageCaches(this); + invalidate(); + mEditorVignette.commitLocalRepresentation(); + return true; + } + + public void setRepresentation(FilterVignetteRepresentation vignetteRep) { + mVignetteRep = vignetteRep; + Matrix toImg = getScreenToImageMatrix(false); + Matrix toScr = new Matrix(); + toImg.invert(toScr); + + float[] c = new float[] { + mVignetteRep.getCenterX(), mVignetteRep.getCenterY() }; + if (Float.isNaN(c[0])) { + float cx = mImageLoader.getOriginalBounds().width() / 2; + float cy = mImageLoader.getOriginalBounds().height() / 2; + float rx = Math.min(cx, cy) * .8f; + float ry = rx; + mVignetteRep.setCenter(cx, cy); + mVignetteRep.setRadius(rx, ry); + + c[0] = cx; + c[1] = cy; + toScr.mapPoints(c); + if (getWidth() != 0) { + mElipse.setCenter(c[0], c[1]); + mElipse.setRadius(c[0] * 0.8f, c[1] * 0.8f); + } + } else { + + toScr.mapPoints(c); + + mElipse.setCenter(c[0], c[1]); + mElipse.setRadius(toScr.mapRadius(mVignetteRep.getRadiusX()), + toScr.mapRadius(mVignetteRep.getRadiusY())); + } + } + + public void setEditor(EditorVignette editorVignette) { + mEditorVignette = editorVignette; + } + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + setRepresentation(mVignetteRep); + mElipse.draw(canvas); + + } + +} diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageZoom.java b/src/com/android/gallery3d/filtershow/imageshow/ImageZoom.java index 222519c11..eb568c303 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageZoom.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageZoom.java @@ -17,11 +17,7 @@ package com.android.gallery3d.filtershow.imageshow; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Matrix; -import android.graphics.Rect; -import android.graphics.RectF; +import android.graphics.*; import android.util.AttributeSet; import android.view.MotionEvent; @@ -53,9 +49,7 @@ public class ImageZoom extends ImageShow { mTouchDown = false; } - @Override public void onTouchDown(float x, float y) { - super.onTouchDown(x, y); if (mZoomedIn || mTouchDown) { return; } @@ -75,7 +69,8 @@ public class ImageZoom extends ImageShow { float ratio = (float) getWidth() / (float) getHeight(); float mh = mMaxSize; float mw = ratio * mh; - RectF zoomRect = new RectF(mTouchX - mw, mTouchY - mh, mTouchX + mw, mTouchY + mw); + Point touch = getTouchPoint(); + RectF zoomRect = new RectF(touch.x - mw, touch.y - mh, touch.x + mw, touch.y + mw); inverse.mapRect(zoomRect); zoomRect.set(zoomRect.centerX() - mw, zoomRect.centerY() - mh, zoomRect.centerX() + mw, zoomRect.centerY() + mh); @@ -84,7 +79,6 @@ public class ImageZoom extends ImageShow { invalidate(); } - @Override public void onTouchUp() { mTouchDown = false; } @@ -96,7 +90,7 @@ public class ImageZoom extends ImageShow { Bitmap filteredImage = null; if ((mZoomedIn || mTouchDown) && mImageLoader != null) { filteredImage = mImageLoader.getScaleOneImageForPreset(this, getImagePreset(), - mZoomBounds, false); + mZoomBounds, null, false); } else { filteredImage = getFilteredImage(); } diff --git a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java index 3172c79dc..9eafe2236 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java +++ b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java @@ -16,8 +16,7 @@ package com.android.gallery3d.filtershow.imageshow; -import android.graphics.Bitmap; -import android.graphics.RectF; +import android.graphics.*; import com.android.gallery3d.filtershow.FilterShowActivity; import com.android.gallery3d.filtershow.HistoryAdapter; @@ -44,6 +43,7 @@ public class MasterImage implements RenderingRequestCaller { private Bitmap mGeometryOnlyBitmap = null; private Bitmap mFiltersOnlyBitmap = null; + private Bitmap mPartialBitmap = null; private ImageLoader mLoader = null; private HistoryAdapter mHistory = null; @@ -57,6 +57,12 @@ public class MasterImage implements RenderingRequestCaller { private GeometryMetadata mPreviousGeometry = null; + private float mScaleFactor = 1.0f; + private Point mTranslation = new Point(); + private Point mOriginalTranslation = new Point(); + + private Point mImageShowSize = new Point(); + private MasterImage() { } @@ -188,6 +194,10 @@ public class MasterImage implements RenderingRequestCaller { return mGeometryOnlyBitmap; } + public Bitmap getPartialImage() { + return mPartialBitmap; + } + public void notifyObservers() { for (ImageShow observer : mObservers) { observer.invalidate(); @@ -216,6 +226,7 @@ public class MasterImage implements RenderingRequestCaller { } } invalidatePreview(); + needsUpdateFullResPreview(); mActivity.enableSave(hasModifications()); } @@ -232,11 +243,67 @@ public class MasterImage implements RenderingRequestCaller { updatePresets(false); } + public void invalidatePartialPreview() { + if (mPartialBitmap != null) { + mPartialBitmap = null; + notifyObservers(); + } + } + public void invalidatePreview() { mFilteredPreview.invalidate(); + invalidatePartialPreview(); + needsUpdateFullResPreview(); FilteringPipeline.getPipeline().updatePreviewBuffer(); } + public void setImageShowSize(int w, int h) { + if (mImageShowSize.x != w || mImageShowSize.y != h) { + mImageShowSize.set(w, h); + needsUpdateFullResPreview(); + } + } + + private Matrix getImageToScreenMatrix(boolean reflectRotation) { + GeometryMetadata geo = mPreset.mGeoData; + if (geo == null || mLoader == null + || mLoader.getOriginalBounds() == null + || mImageShowSize.x == 0) { + return new Matrix(); + } + Matrix m = geo.getOriginalToScreen(reflectRotation, + mLoader.getOriginalBounds().width(), + mLoader.getOriginalBounds().height(), mImageShowSize.x, mImageShowSize.y); + Point translate = getTranslation(); + float scaleFactor = getScaleFactor(); + m.postTranslate(translate.x, translate.y); + m.postScale(scaleFactor, scaleFactor, mImageShowSize.x/2.0f, mImageShowSize.y/2.0f); + return m; + } + + private Matrix getScreenToImageMatrix(boolean reflectRotation) { + Matrix m = getImageToScreenMatrix(reflectRotation); + Matrix invert = new Matrix(); + m.invert(invert); + return invert; + } + + public void needsUpdateFullResPreview() { + if (!mPreset.canDoPartialRendering()) { + invalidatePartialPreview(); + return; + } + Matrix m = getScreenToImageMatrix(true); + RectF r = new RectF(0, 0, mImageShowSize.x, mImageShowSize.y); + RectF dest = new RectF(); + m.mapRect(dest, r); + Rect bounds = new Rect(); + dest.roundOut(bounds); + RenderingRequest.post(null, mPreset, RenderingRequest.PARTIAL_RENDERING, + this, bounds, new Rect(0, 0, mImageShowSize.x, mImageShowSize.y)); + invalidatePartialPreview(); + } + @Override public void available(RenderingRequest request) { if (request.getBitmap() == null) { @@ -248,6 +315,10 @@ public class MasterImage implements RenderingRequestCaller { if (request.getType() == RenderingRequest.FILTERS_RENDERING) { mFiltersOnlyBitmap = request.getBitmap(); } + if (request.getType() == RenderingRequest.PARTIAL_RENDERING) { + mPartialBitmap = request.getBitmap(); + notifyObservers(); + } } public static void reset() { @@ -263,4 +334,38 @@ public class MasterImage implements RenderingRequestCaller { listener.geometryChanged(); } } + + public float getScaleFactor() { + return mScaleFactor; + } + + public void setScaleFactor(float scaleFactor) { + mScaleFactor = scaleFactor; + needsUpdateFullResPreview(); + } + + public Point getTranslation() { + return mTranslation; + } + + public void setTranslation(Point translation) { + mTranslation.x = translation.x; + mTranslation.y = translation.y; + needsUpdateFullResPreview(); + } + + public Point getOriginalTranslation() { + return mOriginalTranslation; + } + + public void setOriginalTranslation(Point originalTranslation) { + mOriginalTranslation.x = originalTranslation.x; + mOriginalTranslation.y = originalTranslation.y; + } + + public void resetTranslation() { + mTranslation.x = 0; + mTranslation.y = 0; + needsUpdateFullResPreview(); + } } diff --git a/src/com/android/gallery3d/filtershow/ui/SliderListener.java b/src/com/android/gallery3d/filtershow/imageshow/Oval.java index 6d4718d87..28f278f1c 100644 --- a/src/com/android/gallery3d/filtershow/ui/SliderListener.java +++ b/src/com/android/gallery3d/filtershow/imageshow/Oval.java @@ -14,10 +14,16 @@ * limitations under the License. */ -package com.android.gallery3d.filtershow.ui; +package com.android.gallery3d.filtershow.imageshow; + +public interface Oval { + void setCenter(float x, float y); + void setRadius(float w, float h); + float getCenterX(); + float getCenterY(); + float getRadiusX(); + float getRadiusY(); + void setRadiusY(float y); + void setRadiusX(float x); -public interface SliderListener { - public void onNewValue(int value); - public void onTouchDown(float x, float y); - public void onTouchUp(); } diff --git a/src/com/android/gallery3d/filtershow/presets/ImagePreset.java b/src/com/android/gallery3d/filtershow/presets/ImagePreset.java index 84266c55d..ae5a03414 100644 --- a/src/com/android/gallery3d/filtershow/presets/ImagePreset.java +++ b/src/com/android/gallery3d/filtershow/presets/ImagePreset.java @@ -17,6 +17,7 @@ package com.android.gallery3d.filtershow.presets; import android.graphics.Bitmap; +import android.graphics.Rect; import android.util.Log; import com.android.gallery3d.filtershow.ImageStateAdapter; @@ -52,6 +53,8 @@ public class ImagePreset { private boolean mDoApplyFilters = true; public final GeometryMetadata mGeoData = new GeometryMetadata(); + private boolean mPartialRendering = false; + private Rect mPartialRenderingBounds; public ImagePreset() { setup(); @@ -421,6 +424,22 @@ public class ImagePreset { return bitmap; } + public boolean canDoPartialRendering() { + if (mGeoData.hasModifications()) { + return false; + } + for (int i = 0; i < mFilters.size(); i++) { + FilterRepresentation representation = null; + synchronized (mFilters) { + representation = mFilters.elementAt(i); + } + if (!representation.supportsPartialRendering()) { + return false; + } + } + return true; + } + public void fillImageStateAdapter(ImageStateAdapter imageStateAdapter) { if (imageStateAdapter == null) { return; @@ -446,4 +465,17 @@ public class ImagePreset { public void setScaleFactor(float value) { mScaleFactor = value; } + + public void setPartialRendering(boolean partialRendering, Rect bounds) { + mPartialRendering = partialRendering; + mPartialRenderingBounds = bounds; + } + + public boolean isPartialRendering() { + return mPartialRendering; + } + + public Rect getPartialRenderingBounds() { + return mPartialRenderingBounds; + } } diff --git a/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java b/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java index e378fe2b7..89cfa6bdf 100644 --- a/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java +++ b/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java @@ -22,6 +22,7 @@ import android.content.Context; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; +import android.graphics.BitmapFactory; import android.net.Uri; import android.os.AsyncTask; import android.os.Environment; @@ -173,37 +174,52 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> { } ImagePreset preset = params[0]; InputStream is = null; - try { - Bitmap bitmap = ImageLoader.loadMutableBitmap(context, sourceUri); - if (bitmap == null) { - return null; - } - bitmap = preset.applyGeometry(bitmap); - bitmap = preset.apply(bitmap); + BitmapFactory.Options options = new BitmapFactory.Options(); + boolean noBitmap = true; + int num_tries = 0; + // Stopgap fix for low-memory devices. + while(noBitmap) { + try { + // Try to do bitmap operations, downsample if low-memory + Bitmap bitmap = ImageLoader.loadMutableBitmap(context, sourceUri, options); + if (bitmap == null) { + return null; + } + bitmap = preset.applyGeometry(bitmap); + bitmap = preset.apply(bitmap); - Object xmp = null; - if (preset.isPanoramaSafe()) { - is = context.getContentResolver().openInputStream(sourceUri); - xmp = XmpUtilHelper.extractXMPMeta(is); - } - ExifData exif = getExifData(sourceUri); - if (exif != null) { - exif.addDateTimeStampTag(ExifTag.TAG_DATE_TIME, System.currentTimeMillis(), - TimeZone.getDefault()); - // Since the image has been modified, set the orientation to normal. - exif.addTag(ExifTag.TAG_ORIENTATION).setValue(ExifTag.Orientation.TOP_LEFT); + Object xmp = null; + if (preset.isPanoramaSafe()) { + is = context.getContentResolver().openInputStream(sourceUri); + xmp = XmpUtilHelper.extractXMPMeta(is); + } + ExifData exif = getExifData(sourceUri); + if (exif != null) { + exif.addDateTimeStampTag(ExifTag.TAG_DATE_TIME, System.currentTimeMillis(), + TimeZone.getDefault()); + // Since the image has been modified, set the orientation to normal. + exif.addTag(ExifTag.TAG_ORIENTATION).setValue(ExifTag.Orientation.TOP_LEFT); + } + saveBitmap(bitmap, this.destinationFile, xmp, exif); + bitmap.recycle(); + noBitmap = false; + } catch (FileNotFoundException ex) { + Log.w(LOGTAG, "Failed to save image!", ex); + return null; + } catch (java.lang.OutOfMemoryError e) { + // Try 5 times before failing for good. + if (++num_tries >= 5) { + throw e; + } + System.gc(); + options.inSampleSize *= 2; + } finally { + Utils.closeSilently(is); } - saveBitmap(bitmap, this.destinationFile, xmp, exif); - - Uri uri = insertContent(context, sourceUri, this.destinationFile, saveFileName); - bitmap.recycle(); - return uri; - } catch (FileNotFoundException ex) { - Log.w(LOGTAG, "Failed to save image!", ex); - return null; - } finally { - Utils.closeSilently(is); } + Uri uri = insertContent(context, sourceUri, this.destinationFile, saveFileName); + return uri; + } @Override diff --git a/src/com/android/gallery3d/filtershow/ui/ImageCurves.java b/src/com/android/gallery3d/filtershow/ui/ImageCurves.java index f581fc733..e54c83eff 100644 --- a/src/com/android/gallery3d/filtershow/ui/ImageCurves.java +++ b/src/com/android/gallery3d/filtershow/ui/ImageCurves.java @@ -233,6 +233,16 @@ public class ImageCurves extends ImageShow { @Override public synchronized boolean onTouchEvent(MotionEvent e) { + super.onTouchEvent(e); + + if (e.getPointerCount() != 1) { + return true; + } + + if (didFinishScalingOperation()) { + return true; + } + float posX = e.getX() / getWidth(); float posY = e.getY(); float margin = Spline.curveHandleSize() / 2; @@ -255,7 +265,6 @@ public class ImageCurves extends ImageShow { mDoingTouchMove = false; return true; } - mDoingTouchMove = true; if (mDidDelete) { return true; @@ -265,28 +274,31 @@ public class ImageCurves extends ImageShow { return true; } - Spline spline = getSpline(mCurrentCurveIndex); - int pick = mCurrentPick; - if (mCurrentControlPoint == null) { - pick = pickControlPoint(posX, posY); - if (pick == -1) { - mCurrentControlPoint = new ControlPoint(posX, posY); - pick = spline.addPoint(mCurrentControlPoint); - mDidAddPoint = true; - } else { - mCurrentControlPoint = spline.getPoint(pick); + if (e.getActionMasked() == MotionEvent.ACTION_MOVE) { + mDoingTouchMove = true; + Spline spline = getSpline(mCurrentCurveIndex); + int pick = mCurrentPick; + if (mCurrentControlPoint == null) { + pick = pickControlPoint(posX, posY); + if (pick == -1) { + mCurrentControlPoint = new ControlPoint(posX, posY); + pick = spline.addPoint(mCurrentControlPoint); + mDidAddPoint = true; + } else { + mCurrentControlPoint = spline.getPoint(pick); + } + mCurrentPick = pick; } - mCurrentPick = pick; - } - if (spline.isPointContained(posX, pick)) { - spline.movePoint(pick, posX, posY); - } else if (pick != -1 && spline.getNbPoints() > 2) { - spline.deletePoint(pick); - mDidDelete = true; + if (spline.isPointContained(posX, pick)) { + spline.movePoint(pick, posX, posY); + } else if (pick != -1 && spline.getNbPoints() > 2) { + spline.deletePoint(pick); + mDidDelete = true; + } + updateCachedImage(); + invalidate(); } - updateCachedImage(); - invalidate(); return true; } diff --git a/src/com/android/gallery3d/filtershow/ui/SliderController.java b/src/com/android/gallery3d/filtershow/ui/SliderController.java deleted file mode 100644 index 7139acebb..000000000 --- a/src/com/android/gallery3d/filtershow/ui/SliderController.java +++ /dev/null @@ -1,189 +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.ui; - -import android.graphics.Canvas; -import android.graphics.Paint; -import android.view.MotionEvent; - -public class SliderController { - private static final String LOGTAG = "SliderController"; - - private float mCenterX; - private float mCenterY; - private float mCurrentX; - private float mCurrentY; - private int mValue = 100; - int mOriginalValue = 0; - - private int mWidth = 0; - private int mHeight = 0; - - private String mToast = null; - - private final Paint mPaint = new Paint(); - - private SliderListener mListener = null; - - private MODES mMode = MODES.NONE; - private static int mTextSize = 128; - - private enum MODES { - NONE, DOWN, UP, MOVE - } - - public void onDraw(Canvas canvas) { - if (mMode == MODES.NONE || mMode == MODES.UP) { - return; - } - } - - public void drawToast(Canvas canvas) { - if (mToast != null) { - canvas.save(); - mPaint.setTextSize(mTextSize); - float textWidth = mPaint.measureText(mToast); - int toastX = (int) ((getWidth() - textWidth) / 2.0f); - int toastY = (int) (getHeight() / 3.0f); - - mPaint.setARGB(255, 0, 0, 0); - canvas.drawText(mToast, toastX - 2, toastY - 2, mPaint); - canvas.drawText(mToast, toastX - 2, toastY, mPaint); - canvas.drawText(mToast, toastX, toastY - 2, mPaint); - canvas.drawText(mToast, toastX + 2, toastY + 2, mPaint); - canvas.drawText(mToast, toastX + 2, toastY, mPaint); - canvas.drawText(mToast, toastX, toastY + 2, mPaint); - mPaint.setARGB(255, 255, 255, 255); - canvas.drawText(mToast, toastX, toastY, mPaint); - canvas.restore(); - } - } - - protected int computeValue() { - int delta = (int) (100 * (getCurrentX() - getCenterX()) / getWidth()); - int value = mOriginalValue + delta; - if (value < -100) { - value = -100; - } else if (value > 100) { - value = 100; - } - setValue(value); - mToast = "" + value; - return value; - } - - public void setValue(int value) { - mValue = value; - } - - public int getWidth() { - return mWidth; - } - - public int getHeight() { - return mHeight; - } - - public void setWidth(int value) { - mWidth = value; - } - - public void setHeight(int value) { - mHeight = value; - } - - public float getCurrentX() { - return mCurrentX; - } - - public float getCurrentY() { - return mCurrentY; - } - - public float getCenterX() { - return mCenterX; - } - - public float getCenterY() { - return mCenterY; - } - - public void setActionDown(float x, float y) { - mCenterX = x; - mCenterY = y; - mCurrentX = x; - mCurrentY = y; - mMode = MODES.DOWN; - if (mListener != null) { - mListener.onTouchDown(x, y); - } - } - - public void setActionMove(float x, float y) { - mCurrentX = x; - mCurrentY = y; - mMode = MODES.MOVE; - computeValue(); - if (mListener != null) { - mListener.onNewValue(mValue); - } - } - - public void setActionUp() { - mMode = MODES.UP; - mOriginalValue = computeValue(); - if (mListener != null) { - mListener.onTouchUp(); - } - } - - public void setNoAction() { - mMode = MODES.NONE; - } - - public void setListener(SliderListener listener) { - mListener = listener; - } - - public boolean onTouchEvent(MotionEvent event) { - setNoAction(); - switch (event.getActionMasked()) { - case (MotionEvent.ACTION_DOWN): { - setActionDown(event.getX(), event.getY()); - break; - } - case (MotionEvent.ACTION_UP): { - setActionUp(); - break; - } - case (MotionEvent.ACTION_CANCEL): { - setActionUp(); - break; - } - case (MotionEvent.ACTION_MOVE): { - setActionMove(event.getX(), event.getY()); - break; - } - } - return true; - } - - public void reset() { - mOriginalValue = 0; - } - -} diff --git a/src_pd/com/android/gallery3d/filtershow/editors/EditorManager.java b/src_pd/com/android/gallery3d/filtershow/editors/EditorManager.java index 67ccf9c80..92962cbb1 100644 --- a/src_pd/com/android/gallery3d/filtershow/editors/EditorManager.java +++ b/src_pd/com/android/gallery3d/filtershow/editors/EditorManager.java @@ -28,6 +28,7 @@ public class EditorManager { editorPlaceHolder.addEditor(new EditorCurves()); editorPlaceHolder.addEditor(new EditorTinyPlanet()); editorPlaceHolder.addEditor(new EditorDraw()); + editorPlaceHolder.addEditor(new EditorVignette()); } } diff --git a/tests/src/com/android/gallery3d/StressTests.java b/tests/src/com/android/gallery3d/StressTests.java index 32eefdcec..b991e9e8d 100755 --- a/tests/src/com/android/gallery3d/StressTests.java +++ b/tests/src/com/android/gallery3d/StressTests.java @@ -41,7 +41,7 @@ public class StressTests extends TestSuite { result.addTestSuite(CameraLatency.class); result.addTestSuite(CameraStartUp.class); result.addTestSuite(ImageCapture.class); - result.addTestSuite(SwitchPreview.class); +// result.addTestSuite(SwitchPreview.class); return result; } } diff --git a/tests/src/com/android/gallery3d/stress/CameraStressTestRunner.java b/tests/src/com/android/gallery3d/stress/CameraStressTestRunner.java index 57ae69125..d3fb10dad 100755 --- a/tests/src/com/android/gallery3d/stress/CameraStressTestRunner.java +++ b/tests/src/com/android/gallery3d/stress/CameraStressTestRunner.java @@ -25,8 +25,8 @@ public class CameraStressTestRunner extends InstrumentationTestRunner { // Default recorder settings public static int mVideoDuration = 20000; // set default to 20 seconds - public static int mVideoIterations = 100; // set default to 100 videos - public static int mImageIterations = 100; // set default to 100 images + public static int mVideoIterations = 1; // set default to 1 video + public static int mImageIterations = 10; // set default to 10 images @Override public TestSuite getAllTests() { |