diff options
112 files changed, 6066 insertions, 2241 deletions
diff --git a/src/com/android/camera/CaptureAnimManager.java b/src/com/android/camera/CaptureAnimManager.java index 64383aff7..4643c379f 100644 --- a/src/com/android/camera/CaptureAnimManager.java +++ b/src/com/android/camera/CaptureAnimManager.java @@ -30,13 +30,18 @@ import com.android.gallery3d.glrenderer.RawTexture; public class CaptureAnimManager { @SuppressWarnings("unused") private static final String TAG = "CAM_Capture"; + // 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 = 400; // milliseconds. + 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 ANIM_BOTH = 0; private static final int ANIM_FLASH = 1; private static final int ANIM_SLIDE = 2; + private static final int ANIM_HOLD2 = 3; + private static final int ANIM_SLIDE2 = 4; private final Interpolator mSlideInterpolator = new DecelerateInterpolator(); @@ -44,15 +49,22 @@ public class CaptureAnimManager { private long mAnimStartTime; // milliseconds. private float mX; // The center of the whole view including preview and review. private float mY; - private float mDelta; private int mDrawWidth; private int mDrawHeight; private int mAnimType; + private int mHoldX; + private int mHoldY; + private int mHoldW; + private int mHoldH; + + private int mOffset = 80; + /* preview: camera preview view. * review: view of picture just taken. */ public CaptureAnimManager() { + } public void setOrientation(int displayRotation) { @@ -83,18 +95,24 @@ public class CaptureAnimManager { mDrawHeight = h; mX = x; mY = y; + mHoldW = (int) (mDrawWidth * 0.7f); + mHoldH = (int) (mDrawHeight * 0.7f); switch (mAnimOrientation) { case 0: // Preview is on the left. - mDelta = w; + mHoldX = x + w - mOffset; + mHoldY = y + (mDrawHeight - mHoldH) / 2; break; case 90: // Preview is below. - mDelta = -h; + mHoldX = x + (mDrawWidth - mHoldW + 1) / 2; + mHoldY = y + mOffset- mHoldH; break; case 180: // Preview on the right. - mDelta = -w; + mHoldX = x - w + mOffset; + mHoldY = y + (mDrawHeight - mHoldH) / 2; break; case 270: // Preview is above. - mDelta = h; + mHoldX = x + (mDrawWidth - mHoldW + 1) / 2; + mHoldY = y + h - mOffset; break; } } @@ -104,14 +122,27 @@ public class CaptureAnimManager { RawTexture review) { long timeDiff = SystemClock.uptimeMillis() - mAnimStartTime; // Check if the animation is over - if (mAnimType == ANIM_SLIDE && timeDiff > TIME_SLIDE) return false; - if (mAnimType == ANIM_BOTH && timeDiff > TIME_HOLD + TIME_SLIDE) return false; + if (mAnimType == ANIM_SLIDE && timeDiff > TIME_SLIDE2 - TIME_HOLD) return false; + if (mAnimType == ANIM_BOTH && timeDiff > TIME_SLIDE2) return false; + // determine phase and time in phase int animStep = mAnimType; - if (mAnimType == ANIM_BOTH) { - animStep = (timeDiff < TIME_HOLD) ? ANIM_FLASH : ANIM_SLIDE; - if (animStep == ANIM_SLIDE) { + if (mAnimType == ANIM_SLIDE) { + timeDiff += TIME_HOLD; + } + if (mAnimType == ANIM_SLIDE || mAnimType == ANIM_BOTH) { + if (timeDiff < TIME_HOLD) { + animStep = ANIM_FLASH; + } else if (timeDiff < TIME_SLIDE) { + animStep = ANIM_SLIDE; timeDiff -= TIME_HOLD; + } else if (timeDiff < TIME_HOLD2) { + animStep = ANIM_HOLD2; + timeDiff -= TIME_SLIDE; + } else { + // SLIDE2 + animStep = ANIM_SLIDE2; + timeDiff -= TIME_HOLD2; } } @@ -123,24 +154,47 @@ public class CaptureAnimManager { canvas.fillRect(mX, mY, mDrawWidth, mDrawHeight, color); } } else if (animStep == ANIM_SLIDE) { - float fraction = (float) (timeDiff) / TIME_SLIDE; + float fraction = mSlideInterpolator.getInterpolation((float) (timeDiff) / (TIME_SLIDE - TIME_HOLD)); float x = mX; float y = mY; - if (mAnimOrientation == 0 || mAnimOrientation == 180) { - x = x + mDelta * mSlideInterpolator.getInterpolation(fraction); - } else { - y = y + mDelta * mSlideInterpolator.getInterpolation(fraction); + float w = 0; + float h = 0; + x = interpolate(mX, mHoldX, fraction); + y = interpolate(mY, mHoldY, fraction); + w = interpolate(mDrawWidth, mHoldW, fraction); + h = interpolate(mDrawHeight, mHoldH, fraction); + preview.directDraw(canvas, (int) mX, (int) mY, mDrawWidth, mDrawHeight); + review.draw(canvas, (int) x, (int) y, (int) w, (int) h); + } else if (animStep == ANIM_HOLD2) { + preview.directDraw(canvas, (int) mX, (int) mY, mDrawWidth, mDrawHeight); + review.draw(canvas, mHoldX, mHoldY, mHoldW, mHoldH); + } else if (animStep == ANIM_SLIDE2) { + float fraction = (float)(timeDiff) / (TIME_SLIDE2 - TIME_HOLD2); + float x = mHoldX; + float y = mHoldY; + float d = mOffset * fraction; + switch (mAnimOrientation) { + case 0: + x = mHoldX + d; + break; + case 180: + x = mHoldX - d; + break; + case 90: + y = mHoldY - d; + break; + case 270: + y = mHoldY + d; + break; } - // float alpha = canvas.getAlpha(); - // canvas.setAlpha(fraction); - preview.directDraw(canvas, (int) mX, (int) mY, - mDrawWidth, mDrawHeight); - // canvas.setAlpha(alpha); - - review.draw(canvas, (int) x, (int) y, mDrawWidth, mDrawHeight); - } else { - return false; + preview.directDraw(canvas, (int) mX, (int) mY, mDrawWidth, mDrawHeight); + review.draw(canvas, (int) x, (int) y, mHoldW, mHoldH); } return true; } + + private static float interpolate(float start, float end, float fraction) { + return start + (end - start) * fraction; + } + } diff --git a/src/com/android/camera/ComboPreferences.java b/src/com/android/camera/ComboPreferences.java index af1476eac..9155fb3eb 100644 --- a/src/com/android/camera/ComboPreferences.java +++ b/src/com/android/camera/ComboPreferences.java @@ -32,12 +32,14 @@ public class ComboPreferences implements OnSharedPreferenceChangeListener { private SharedPreferences mPrefGlobal; // global preferences private SharedPreferences mPrefLocal; // per-camera preferences - private BackupManager mBackupManager; + private String mPackageName; private CopyOnWriteArrayList<OnSharedPreferenceChangeListener> mListeners; + // TODO: Remove this WeakHashMap in the camera code refactoring private static WeakHashMap<Context, ComboPreferences> sMap = new WeakHashMap<Context, ComboPreferences>(); public ComboPreferences(Context context) { + mPackageName = context.getPackageName(); mPrefGlobal = context.getSharedPreferences( getGlobalSharedPreferencesName(context), Context.MODE_PRIVATE); mPrefGlobal.registerOnSharedPreferenceChangeListener(this); @@ -45,7 +47,6 @@ public class ComboPreferences implements synchronized (sMap) { sMap.put(context, this); } - mBackupManager = new BackupManager(context); mListeners = new CopyOnWriteArrayList<OnSharedPreferenceChangeListener>(); // The global preferences was previously stored in the default @@ -327,6 +328,6 @@ public class ComboPreferences implements for (OnSharedPreferenceChangeListener listener : mListeners) { listener.onSharedPreferenceChanged(this, key); } - mBackupManager.dataChanged(); + BackupManager.dataChanged(mPackageName); } } diff --git a/src/com/android/camera/FocusOverlayManager.java b/src/com/android/camera/FocusOverlayManager.java index 2bec18760..9acf85de9 100644 --- a/src/com/android/camera/FocusOverlayManager.java +++ b/src/com/android/camera/FocusOverlayManager.java @@ -430,6 +430,7 @@ public class FocusOverlayManager { public String getFocusMode() { if (mOverrideFocusMode != null) return mOverrideFocusMode; + if (mParameters == null) return Parameters.FOCUS_MODE_AUTO; List<String> supportedFocusModes = mParameters.getSupportedFocusModes(); if (mFocusAreaSupported && mFocusArea != null) { diff --git a/src/com/android/camera/MosaicFrameProcessor.java b/src/com/android/camera/MosaicFrameProcessor.java index c59e6b91b..efd4ad2ae 100644 --- a/src/com/android/camera/MosaicFrameProcessor.java +++ b/src/com/android/camera/MosaicFrameProcessor.java @@ -92,7 +92,8 @@ public class MosaicFrameProcessor { mPreviewBufferSize = bufSize; setupMosaicer(mPreviewWidth, mPreviewHeight, mPreviewBufferSize); setStripType(Mosaic.STRIPTYPE_WIDE); - reset(); + // no need to call reset() here. reset() should be called by the client + // after this initialization before calling other methods of this object. } public void clear() { diff --git a/src/com/android/camera/MosaicPreviewRenderer.java b/src/com/android/camera/MosaicPreviewRenderer.java index e12fe432e..26ce733aa 100644 --- a/src/com/android/camera/MosaicPreviewRenderer.java +++ b/src/com/android/camera/MosaicPreviewRenderer.java @@ -90,6 +90,7 @@ public class MosaicPreviewRenderer { break; case MSG_RELEASE: doRelease(); + mEglThreadBlockVar.open(); break; } } @@ -203,7 +204,7 @@ public class MosaicPreviewRenderer { } public void release() { - mEglHandler.sendEmptyMessage(EGLHandler.MSG_RELEASE); + mEglHandler.sendMessageSync(EGLHandler.MSG_RELEASE); } public void showPreviewFrameSync() { diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java index aa7783346..e52c83c3a 100644 --- a/src/com/android/camera/PhotoModule.java +++ b/src/com/android/camera/PhotoModule.java @@ -1438,7 +1438,11 @@ public class PhotoModule } mFocusManager.onShutterDown(); } else { - mFocusManager.onShutterUp(); + // for countdown mode, we need to postpone the shutter release + // i.e. lock the focus during countdown. + if (!mCountDownView.isCountingDown()) { + mFocusManager.onShutterUp(); + } } } @@ -2430,6 +2434,7 @@ public class PhotoModule public void onCountDownFinished() { mSnapshotOnIdle = false; mFocusManager.doSnap(); + mFocusManager.onShutterUp(); } void setPreviewFrameLayoutAspectRatio() { diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java index c136358e1..4d01bc320 100644 --- a/src/com/android/camera/VideoModule.java +++ b/src/com/android/camera/VideoModule.java @@ -652,7 +652,7 @@ public class VideoModule implements CameraModule, if (mQuickCapture) { doReturnToCaller(!recordFail); } else if (!recordFail) { - showAlert(); + showCaptureResult(); } } } else if (!recordFail){ @@ -1658,14 +1658,14 @@ public class VideoModule implements CameraModule, } } - private void showAlert() { + private void showCaptureResult() { Bitmap bitmap = null; if (mVideoFileDescriptor != null) { bitmap = Thumbnail.createVideoThumbnailBitmap(mVideoFileDescriptor.getFileDescriptor(), - mPreviewFrameLayout.getWidth()); + mDesiredPreviewWidth); } else if (mCurrentVideoFilename != null) { bitmap = Thumbnail.createVideoThumbnailBitmap(mCurrentVideoFilename, - mPreviewFrameLayout.getWidth()); + mDesiredPreviewWidth); } if (bitmap != null) { // MetadataRetriever already rotates the thumbnail. We should rotate @@ -2053,7 +2053,7 @@ public class VideoModule implements CameraModule, if (mQuickCapture) { doReturnToCaller(true); } else { - showAlert(); + showCaptureResult(); } } } @@ -2176,6 +2176,7 @@ public class VideoModule implements CameraModule, mShutterButton = mActivity.getShutterButton(); mShutterButton.setImageResource(R.drawable.btn_new_shutter_video); mShutterButton.setOnShutterButtonListener(this); + mShutterButton.setVisibility(View.VISIBLE); mShutterButton.requestFocus(); mShutterButton.enableTouch(true); @@ -2203,7 +2204,6 @@ public class VideoModule implements CameraModule, @Override public void onConfigurationChanged(Configuration newConfig) { setDisplayOrientation(); - // Change layout in response to configuration change LayoutInflater inflater = mActivity.getLayoutInflater(); ((ViewGroup) mRootView).removeAllViews(); @@ -2223,6 +2223,9 @@ public class VideoModule implements CameraModule, initializeZoom(); onFullScreenChanged(mActivity.isInCameraApp()); updateOnScreenIndicators(); + if (mIsVideoCaptureIntent && mVideoFileDescriptor != null) { + showCaptureResult(); + } } @Override diff --git a/src/com/android/camera/ui/PieMenuButton.java b/src/com/android/camera/ui/PieMenuButton.java new file mode 100644 index 000000000..e5719310a --- /dev/null +++ b/src/com/android/camera/ui/PieMenuButton.java @@ -0,0 +1,61 @@ +/* + * 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.camera.ui; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +public class PieMenuButton extends View { + private boolean mPressed; + private boolean mReadyToClick = false; + public PieMenuButton(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void drawableStateChanged() { + super.drawableStateChanged(); + mPressed = isPressed(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (MotionEvent.ACTION_UP == event.getAction() && mPressed) { + // Perform a customized click as soon as the ACTION_UP event + // is received. The reason for doing this is that Framework + // delays the performClick() call after ACTION_UP. But we do not + // want the delay because it affects an important state change + // for PieRenderer. + mReadyToClick = true; + performClick(); + } + return super.onTouchEvent(event); + } + + @Override + public boolean performClick() { + if (mReadyToClick) { + // We only respond to our customized click which happens right + // after ACTION_UP event is received, with no delay. + mReadyToClick = false; + return super.performClick(); + } + return false; + } +};
\ No newline at end of file diff --git a/src/com/android/gallery3d/app/CommonControllerOverlay.java b/src/com/android/gallery3d/app/CommonControllerOverlay.java index 089872fa5..a5aa805ef 100644 --- a/src/com/android/gallery3d/app/CommonControllerOverlay.java +++ b/src/com/android/gallery3d/app/CommonControllerOverlay.java @@ -274,10 +274,6 @@ public abstract class CommonControllerOverlay extends FrameLayout implements mBackground.layout(0, y - mTimeBar.getBarHeight(), w, y); mTimeBar.layout(pl, y - mTimeBar.getPreferredHeight(), w - pr, y); - // Needed, otherwise the framework will not re-layout in case only the - // padding is changed - mTimeBar.requestLayout(); - // Put the play/pause/next/ previous button in the center of the screen layoutCenteredView(mPlayPauseReplayView, 0, 0, w, h); diff --git a/src/com/android/gallery3d/app/PhotoPage.java b/src/com/android/gallery3d/app/PhotoPage.java index b3a6040de..44ff62feb 100644 --- a/src/com/android/gallery3d/app/PhotoPage.java +++ b/src/com/android/gallery3d/app/PhotoPage.java @@ -670,7 +670,7 @@ public abstract class PhotoPage extends ActivityState implements } private void overrideTransitionToEditor() { - ((Activity) mActivity).overridePendingTransition(android.R.anim.slide_in_left, + ((Activity) mActivity).overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); } diff --git a/src/com/android/gallery3d/filtershow/EditorPlaceHolder.java b/src/com/android/gallery3d/filtershow/EditorPlaceHolder.java index 1b6c5ea58..dee9d2e8d 100644 --- a/src/com/android/gallery3d/filtershow/EditorPlaceHolder.java +++ b/src/com/android/gallery3d/filtershow/EditorPlaceHolder.java @@ -66,6 +66,10 @@ public class EditorPlaceHolder { mOldViews = views; } + public void hide() { + mContainer.setVisibility(View.GONE); + } + public void hideOldViews() { for (View view : mOldViews) { view.setVisibility(View.GONE); diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java index 83ea06dc7..f7147eac1 100644 --- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java +++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java @@ -16,7 +16,6 @@ package com.android.gallery3d.filtershow; -import android.annotation.TargetApi; import android.app.ActionBar; import android.app.Activity; import android.app.ProgressDialog; @@ -45,34 +44,25 @@ import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.WindowManager; -import android.widget.AdapterView; +import android.widget.*; import android.widget.AdapterView.OnItemClickListener; -import android.widget.FrameLayout; -import android.widget.ImageButton; -import android.widget.LinearLayout; -import android.widget.ListView; -import android.widget.SeekBar; -import android.widget.ShareActionProvider; import android.widget.ShareActionProvider.OnShareTargetSelectedListener; -import android.widget.Toast; import com.android.gallery3d.R; import com.android.gallery3d.data.LocalAlbum; +import com.android.gallery3d.filtershow.cache.FilteringPipeline; import com.android.gallery3d.filtershow.cache.ImageLoader; import com.android.gallery3d.filtershow.editors.BasicEditor; import com.android.gallery3d.filtershow.editors.EditorDraw; import com.android.gallery3d.filtershow.editors.EditorManager; -import com.android.gallery3d.filtershow.filters.FiltersManager; -import com.android.gallery3d.filtershow.filters.ImageFilter; -import com.android.gallery3d.filtershow.filters.ImageFilterBorder; -import com.android.gallery3d.filtershow.filters.ImageFilterFx; -import com.android.gallery3d.filtershow.filters.ImageFilterParametricBorder; -import com.android.gallery3d.filtershow.filters.ImageFilterRS; -import com.android.gallery3d.filtershow.filters.ImageFilterRedEye; +import com.android.gallery3d.filtershow.editors.EditorRedEye; +import com.android.gallery3d.filtershow.editors.ImageOnlyEditor; +import com.android.gallery3d.filtershow.editors.EditorTinyPlanet; +import com.android.gallery3d.filtershow.filters.*; import com.android.gallery3d.filtershow.imageshow.ImageCrop; import com.android.gallery3d.filtershow.imageshow.ImageDraw; import com.android.gallery3d.filtershow.imageshow.ImageFlip; -import com.android.gallery3d.filtershow.imageshow.ImageRedEyes; +import com.android.gallery3d.filtershow.imageshow.ImageRedEye; import com.android.gallery3d.filtershow.imageshow.ImageRotate; import com.android.gallery3d.filtershow.imageshow.ImageShow; import com.android.gallery3d.filtershow.imageshow.ImageStraighten; @@ -84,7 +74,6 @@ import com.android.gallery3d.filtershow.provider.SharedImageProvider; import com.android.gallery3d.filtershow.tools.SaveCopyTask; import com.android.gallery3d.filtershow.ui.FilterIconButton; import com.android.gallery3d.filtershow.ui.FramedTextButton; -import com.android.gallery3d.filtershow.ui.ImageCurves; import com.android.gallery3d.filtershow.ui.Spline; import com.android.gallery3d.util.GalleryUtils; @@ -93,14 +82,14 @@ import java.io.IOException; import java.lang.ref.WeakReference; import java.util.Vector; -@TargetApi(16) public class FilterShowActivity extends Activity implements OnItemClickListener, OnShareTargetSelectedListener { // fields for supporting crop action public static final String CROP_ACTION = "com.android.camera.action.CROP"; private CropExtras mCropExtras = null; - MasterImage mMasterImage = MasterImage.getImage(); + private String mAction = ""; + MasterImage mMasterImage = null; public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET"; public static final String LAUNCH_FULLSCREEN = "launch-fullscreen"; @@ -108,7 +97,6 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, private final PanelController mPanelController = new PanelController(); private ImageLoader mImageLoader = null; private ImageShow mImageShow = null; - private ImageRedEyes mImageRedEyes = null; private ImageDraw mImageDraw = null; private ImageStraighten mImageStraighten = null; private ImageCrop mImageCrop = null; @@ -159,14 +147,17 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, private FilterIconButton mNullBorderFilter; private int mIconSeedSize = 140; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setResources(); + Resources res = getResources(); setupMasterImage(); ImageFilterRS.setRenderScriptContext(this); - ImageShow.setDefaultBackgroundColor(getResources().getColor(R.color.background_screen)); + ImageShow.setDefaultBackgroundColor(res.getColor(R.color.background_screen)); // TODO: get those values from XML. ImageZoom.setZoomedSize(getPixelsFromDip(256)); FramedTextButton.setTextSize((int) getPixelsFromDip(14)); @@ -176,11 +167,11 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, ImageShow.setTextPadding((int) getPixelsFromDip(10)); ImageShow.setOriginalTextMargin((int) getPixelsFromDip(4)); ImageShow.setOriginalTextSize((int) getPixelsFromDip(18)); - ImageShow.setOriginalText(getResources().getString(R.string.original_picture_text)); - mIconSeedSize = getResources().getDimensionPixelSize(R.dimen.thumbnail_size); + ImageShow.setOriginalText(res.getString(R.string.original_picture_text)); + mIconSeedSize = res.getDimensionPixelSize(R.dimen.thumbnail_size); - Drawable curveHandle = getResources().getDrawable(R.drawable.camera_crop); - int curveHandleSize = (int) getResources().getDimension(R.dimen.crop_indicator_size); + Drawable curveHandle = res.getDrawable(R.drawable.camera_crop); + int curveHandleSize = (int) res.getDimension(R.dimen.crop_indicator_size); Spline.setCurveHandle(curveHandle, curveHandleSize); Spline.setCurveWidth((int) getPixelsFromDip(3)); @@ -209,7 +200,6 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mImageRotate = (ImageRotate) findViewById(R.id.imageRotate); mImageFlip = (ImageFlip) findViewById(R.id.imageFlip); mImageTinyPlanet = (ImageTinyPlanet) findViewById(R.id.imageTinyPlanet); - mImageRedEyes = (ImageRedEyes) findViewById(R.id.imageRedEyes); mImageDraw = (ImageDraw) findViewById(R.id.imageDraw); mImageCrop.setAspectTextSize((int) getPixelsFromDip(18)); @@ -221,15 +211,19 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mImageViews.add(mImageRotate); mImageViews.add(mImageFlip); mImageViews.add(mImageTinyPlanet); - mImageViews.add(mImageRedEyes); mEditorPlaceHolder.setContainer((FrameLayout) findViewById(R.id.editorContainer)); mEditorPlaceHolder.addEditor(new EditorDraw()); mEditorPlaceHolder.addEditor(new BasicEditor()); + mEditorPlaceHolder.addEditor(new ImageOnlyEditor()); + mEditorPlaceHolder.addEditor(new EditorTinyPlanet()); + mEditorPlaceHolder.addEditor(new EditorRedEye()); EditorManager.addEditors(mEditorPlaceHolder); mEditorPlaceHolder.setOldViews(mImageViews); mEditorPlaceHolder.setImageLoader(mImageLoader); + mEditorPlaceHolder.hide(); + mListFx = findViewById(R.id.fxList); mListBorders = findViewById(R.id.bordersList); mListGeometry = findViewById(R.id.geometryList); @@ -257,7 +251,6 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mImageRotate.setImageLoader(mImageLoader); mImageFlip.setImageLoader(mImageLoader); mImageTinyPlanet.setImageLoader(mImageLoader); - mImageRedEyes.setImageLoader(mImageLoader); mImageDraw.setImageLoader(mImageLoader); mPanelController.setActivity(this); @@ -269,7 +262,6 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mPanelController.addImageView(findViewById(R.id.imageRotate)); mPanelController.addImageView(findViewById(R.id.imageFlip)); mPanelController.addImageView(findViewById(R.id.imageTinyPlanet)); - mPanelController.addImageView(findViewById(R.id.imageRedEyes)); mPanelController.addImageView(findViewById(R.id.imageDraw)); mPanelController.addPanel(mFxButton, mListFx, 0); @@ -280,19 +272,17 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mPanelController.addComponent(mGeometryButton, findViewById(R.id.cropButton)); mPanelController.addComponent(mGeometryButton, findViewById(R.id.rotateButton)); mPanelController.addComponent(mGeometryButton, findViewById(R.id.flipButton)); - mPanelController.addComponent(mGeometryButton, findViewById(R.id.redEyeButton)); mPanelController.addPanel(mColorsButton, mListColors, 3); - Vector<ImageFilter> filters = new Vector<ImageFilter>(); - FiltersManager.addFilters(filters, mImageLoader); + Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>(); + + FiltersManager filtersManager = FiltersManager.getManager(); + filtersManager.addEffects(filtersRepresentations); - for (ImageFilter filter : filters) { - filter.setParameter(filter.getDefaultParameter()); - filter.setName(getString(filter.getTextId())); - setupFilterButton(filter, listColors, mColorsButton); + for (FilterRepresentation representation : filtersRepresentations) { + setupFilterRepresentationButton(representation, listColors, mColorsButton); } - mPanelController.addFilter(new ImageFilterRedEye()); mPanelController.addView(findViewById(R.id.applyEffect)); findViewById(R.id.resetOperationsButton).setOnClickListener( @@ -309,11 +299,6 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, LoadBordersTask loadBorders = new LoadBordersTask(listBorders); loadBorders.execute(); - SeekBar seekBar = (SeekBar) findViewById(R.id.filterSeekBar); - seekBar.setMax(SEEK_BAR_MAX); - - mImageShow.setSeekBar(seekBar); - mImageTinyPlanet.setSeekBar(seekBar); mPanelController.setRowPanel(findViewById(R.id.secondRowPanel)); mPanelController.setUtilityPanel(this, findViewById(R.id.filterButtonsList), findViewById(R.id.panelAccessoryViewList), @@ -325,6 +310,8 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); } + mAction = intent.getAction(); + if (intent.getData() != null) { startLoadBitmap(intent.getData()); } else { @@ -332,8 +319,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, } // Handle behavior for various actions - String action = intent.getAction(); - if (action.equalsIgnoreCase(CROP_ACTION)) { + if (mAction.equalsIgnoreCase(CROP_ACTION)) { Bundle extras = intent.getExtras(); if (extras != null) { mCropExtras = new CropExtras(extras.getInt(CropExtras.KEY_OUTPUT_X, 0), @@ -362,17 +348,17 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mPanelController.setFixedAspect(mCropExtras.getAspectX() > 0 && mCropExtras.getAspectY() > 0); } - mPanelController.showComponent(findViewById(R.id.cropButton)); - } else if (action.equalsIgnoreCase(TINY_PLANET_ACTION)) { - mPanelController.showComponent(findViewById(R.id.tinyplanetButton)); } } private void startLoadBitmap(Uri uri) { final View filters = findViewById(R.id.filtersPanel); final View loading = findViewById(R.id.loading); - loading.setVisibility(View.VISIBLE); + final View imageShow = findViewById(R.id.imageShow); + imageShow.setVisibility(View.INVISIBLE); filters.setVisibility(View.INVISIBLE); + loading.setVisibility(View.VISIBLE); + View tinyPlanetView = findViewById(R.id.tinyplanetButton); if (tinyPlanetView != null) { tinyPlanetView.setVisibility(View.GONE); @@ -382,37 +368,29 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, } private class LoadBordersTask extends AsyncTask<Void, Boolean, Boolean> { - Vector<ImageFilter> mBorders; + Vector<FilterRepresentation> mBorders; LinearLayout mList; public LoadBordersTask(LinearLayout list) { mList = list; - mBorders = new Vector<ImageFilter>(); + mBorders = new Vector<FilterRepresentation>(); } @Override protected Boolean doInBackground(Void... params) { - mBorders.add(new ImageFilterBorder(null)); - Drawable npd1 = getResources().getDrawable(R.drawable.filtershow_border_4x5); - mBorders.add(new ImageFilterBorder(npd1)); - Drawable npd2 = getResources().getDrawable(R.drawable.filtershow_border_brush); - mBorders.add(new ImageFilterBorder(npd2)); - Drawable npd3 = getResources().getDrawable(R.drawable.filtershow_border_grunge); - mBorders.add(new ImageFilterBorder(npd3)); - Drawable npd4 = getResources().getDrawable(R.drawable.filtershow_border_sumi_e); - mBorders.add(new ImageFilterBorder(npd4)); - Drawable npd5 = getResources().getDrawable(R.drawable.filtershow_border_tape); - mBorders.add(new ImageFilterBorder(npd5)); - mBorders.add(new ImageFilterParametricBorder(Color.BLACK, mImageBorderSize, 0)); - mBorders.add(new ImageFilterParametricBorder(Color.BLACK, mImageBorderSize, - mImageBorderSize)); - mBorders.add(new ImageFilterParametricBorder(Color.WHITE, mImageBorderSize, 0)); - mBorders.add(new ImageFilterParametricBorder(Color.WHITE, mImageBorderSize, - mImageBorderSize)); + mBorders.add(new FilterImageBorderRepresentation(0)); + mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_4x5)); + mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_brush)); + mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_grunge)); + mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_sumi_e)); + mBorders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_tape)); + mBorders.add(new FilterColorBorderRepresentation(Color.BLACK, mImageBorderSize, 0)); + mBorders.add(new FilterColorBorderRepresentation(Color.BLACK, mImageBorderSize, mImageBorderSize)); + mBorders.add(new FilterColorBorderRepresentation(Color.WHITE, mImageBorderSize, 0)); + mBorders.add(new FilterColorBorderRepresentation(Color.WHITE, mImageBorderSize, mImageBorderSize)); int creamColor = Color.argb(255, 237, 237, 227); - mBorders.add(new ImageFilterParametricBorder(creamColor, mImageBorderSize, 0)); - mBorders.add(new ImageFilterParametricBorder(creamColor, mImageBorderSize, - mImageBorderSize)); + mBorders.add(new FilterColorBorderRepresentation(creamColor, mImageBorderSize, 0)); + mBorders.add(new FilterColorBorderRepresentation(creamColor, mImageBorderSize, mImageBorderSize)); return true; } @@ -422,9 +400,12 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, return; } for (int i = 0; i < mBorders.size(); i++) { - ImageFilter filter = mBorders.elementAt(i); + FilterRepresentation filter = mBorders.elementAt(i); filter.setName(getString(R.string.borders)); - FilterIconButton b = setupFilterButton(filter, mList, mBorderButton); + if (i == 0) { + filter.setName(getString(R.string.none)); + } + FilterIconButton b = setupFilterRepresentationButton(filter, mList, mBorderButton); if (i == 0) { mNullBorderFilter = b; mNullBorderFilter.setSelected(true); @@ -457,10 +438,6 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, if (isCancelled()) { return; } - final View filters = findViewById(R.id.filtersPanel); - final View loading = findViewById(R.id.loading); - loading.setVisibility(View.GONE); - filters.setVisibility(View.VISIBLE); if (values[0]) { mTinyPlanetButton.setVisibility(View.VISIBLE); } @@ -468,13 +445,33 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, @Override protected void onPostExecute(Boolean result) { + if (isCancelled()) { return; } + if (!result) { cannotLoadImage(); } + final View loading = findViewById(R.id.loading); + loading.setVisibility(View.GONE); + final View filters = findViewById(R.id.filtersPanel); + filters.setVisibility(View.VISIBLE); + if (PanelController.useAnimations()) { + float y = filters.getY(); + filters.setY(y + filters.getHeight()); + filters.animate().setDuration(600).y(y).withLayer().start(); + } + final View imageShow = findViewById(R.id.imageShow); + imageShow.setVisibility(View.VISIBLE); + + Bitmap largeBitmap = mImageLoader.getOriginalBitmapLarge(); + FilteringPipeline pipeline = FilteringPipeline.getPipeline(); + pipeline.setOriginal(largeBitmap); + float previewScale = (float) largeBitmap.getWidth() / (float) mImageLoader.getOriginalBounds().width(); + pipeline.setPreviewScaleFactor(previewScale); + Bitmap bmap = mImageLoader.getOriginalBitmapSmall(); if (bmap != null && bmap.getWidth() > 0 && bmap.getHeight() > 0) { float w = bmap.getWidth(); @@ -505,6 +502,13 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, } mLoadBitmapTask = null; + + if (mAction == CROP_ACTION) { + mPanelController.showComponent(findViewById(R.id.cropButton)); + } else if (mAction == TINY_PLANET_ACTION) { + mPanelController.showComponent(findViewById(R.id.tinyplanetButton)); + } + super.onPostExecute(result); } @@ -704,19 +708,22 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, mSaveButton.setEnabled(enable); } - public FilterIconButton setupFilterButton(ImageFilter filter, LinearLayout panel, View button) { + public FilterIconButton setupFilterRepresentationButton(FilterRepresentation representation, LinearLayout panel, View button) { LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); FilterIconButton icon = (FilterIconButton) inflater.inflate(R.layout.filtericonbutton, panel, false); - String text = filter.getName(); - if (filter instanceof ImageFilterBorder || filter instanceof ImageFilterParametricBorder) { - text = ""; + if (representation.getTextId() != 0) { + representation.setName(getString(representation.getTextId())); + } + String text = representation.getName(); + icon.setup(text, mPanelController, panel); + icon.setFilterRepresentation(representation); + if (representation instanceof FilterTinyPlanetRepresentation) { + // needed to hide tinyplanet on startup + icon.setId(R.id.tinyplanetButton); } - icon.setup(text, filter, this, panel); - icon.setId(filter.getButtonId()); mPanelController.addComponent(button, icon); - mPanelController.addFilter(filter); panel.addView(icon); return icon; } @@ -725,7 +732,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, // TODO: use listview // TODO: load the filters straight from the filesystem - ImageFilterFx[] fxArray = new ImageFilterFx[18]; + FilterFxRepresentation[] fxArray = new FilterFxRepresentation[18]; int p = 0; int[] drawid = { @@ -760,15 +767,23 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, for (int i = 0; i < drawid.length; i++) { Bitmap b = BitmapFactory.decodeResource(getResources(), drawid[i], o); - fxArray[p++] = new ImageFilterFx(b, getString(fxNameid[i]), fxNameid[i]); + FilterFxRepresentation fx = new FilterFxRepresentation(getString(fxNameid[i]), drawid[i], fxNameid[i]); + fx.setFxBitmap(b); + fxArray[p++] = fx; } - ImageFilterFx nullFilter = new ImageFilterFx(null, getString(R.string.none), R.string.none); - mNullFxFilter = setupFilterButton(nullFilter, listFilters, mFxButton); + FilterFxRepresentation nullFx = new FilterFxRepresentation(getString(R.string.none), 0, R.string.none); + mNullFxFilter = setupFilterRepresentationButton(nullFx, listFilters, mFxButton); mNullFxFilter.setSelected(true); + Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>(); + FiltersManager.getManager().addLooks(filtersRepresentations); + for (FilterRepresentation representation : filtersRepresentations) { + setupFilterRepresentationButton(representation, listFilters, mFxButton); + } + for (int i = 0; i < p; i++) { - setupFilterButton(fxArray[i], listFilters, mFxButton); + setupFilterRepresentationButton(fxArray[i], listFilters, mFxButton); } // Default preset (original) @@ -803,6 +818,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, for (View view : mImageViews) { view.setVisibility(View.GONE); } + mEditorPlaceHolder.hide(); } public void unselectBottomPanelButtons() { @@ -852,7 +868,8 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, int translate = translateMainPanel(viewList); if (!mShowingImageStatePanel) { mShowingImageStatePanel = true; - view.animate().setDuration(200).x(translate) + if (PanelController.useAnimations()) { + view.animate().setDuration(200).x(translate) .withLayer().withEndAction(new Runnable() { @Override public void run() { @@ -862,11 +879,22 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, .alpha(1.0f).start(); } }).start(); + } else { + view.setX(translate); + viewList.setAlpha(0); + viewList.setVisibility(View.VISIBLE); + viewList.animate().setDuration(100) + .alpha(1.0f).start(); + } } else { mShowingImageStatePanel = false; viewList.setVisibility(View.INVISIBLE); - view.animate().setDuration(200).x(0).withLayer() + if (PanelController.useAnimations()) { + view.animate().setDuration(200).x(0).withLayer() .start(); + } else { + view.setX(0); + } } invalidateOptionsMenu(); } @@ -875,6 +903,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); + setResources(); if (mShowingHistoryPanel) { toggleHistoryPanel(); } @@ -887,6 +916,8 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, ImageStateAdapter mImageStateAdapter = new ImageStateAdapter(this, R.layout.filtershow_imagestate_row); + MasterImage.reset(); + mMasterImage = MasterImage.getImage(); mMasterImage.setHistoryAdapter(mHistoryAdapter); mMasterImage.setStateAdapter(mImageStateAdapter); mMasterImage.setActivity(this); @@ -907,7 +938,8 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, int translate = translateMainPanel(viewList); if (!mShowingHistoryPanel) { mShowingHistoryPanel = true; - view.animate().setDuration(200).x(translate) + if (PanelController.useAnimations()) { + view.animate().setDuration(200).x(translate) .withLayer().withEndAction(new Runnable() { @Override public void run() { @@ -917,11 +949,22 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, .alpha(1.0f).start(); } }).start(); + } else { + view.setX(translate); + viewList.setAlpha(0); + viewList.setVisibility(View.VISIBLE); + viewList.animate().setDuration(100) + .alpha(1.0f).start(); + } } else { mShowingHistoryPanel = false; viewList.setVisibility(View.INVISIBLE); - view.animate().setDuration(200).x(0).withLayer() + if (PanelController.useAnimations()) { + view.animate().setDuration(200).x(0).withLayer() .start(); + } else { + view.setX(0); + } } invalidateOptionsMenu(); } @@ -973,23 +1016,6 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, r.getDisplayMetrics()); } - public void useFilter(ImageFilter filter) { - if (mMasterImage.getCurrentFilter() == filter) { - return; - } - ImagePreset oldPreset = mMasterImage.getPreset(); - ImagePreset copy = new ImagePreset(oldPreset); - - ImageFilter existingFilter = copy.getFilter(filter.getName()); - if (existingFilter == null) { - copy.add(filter); - } - existingFilter = copy.getFilter(filter.getName()); - mMasterImage.setPreset(copy, true); - mMasterImage.setCurrentFilter(existingFilter); - invalidateViews(); - } - @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { @@ -1018,31 +1044,31 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, private boolean mSaveToExtraUri = false; private boolean mSaveAsWallpaper = false; private boolean mReturnAsExtra = false; - private boolean outputted = false; + private boolean mOutputted = false; public void saveImage() { if (mCropExtras != null) { if (mCropExtras.getExtraOutput() != null) { mSaveToExtraUri = true; - outputted = true; + mOutputted = true; } if (mCropExtras.getSetAsWallpaper()) { mSaveAsWallpaper = true; - outputted = true; + mOutputted = true; } if (mCropExtras.getReturnData()) { mReturnAsExtra = true; - outputted = true; + mOutputted = true; } - if (outputted) { + if (mOutputted) { mImageShow.getImagePreset().mGeoData.setUseCropExtrasFlag(true); showSavingProgress(null); mImageShow.returnFilteredResult(this); } } - if (!outputted) { + if (!mOutputted) { if (mImageShow.hasModifications()) { // Get the name of the album, to which the image will be saved File saveDir = SaveCopyTask.getFinalSaveDirectory(this, mImageLoader.getUri()); @@ -1092,12 +1118,17 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, } public void done() { - if (outputted) { + if (mOutputted) { hideSavingProgress(); } finish(); } + private void setResources() { + ImageFilterBorder filterBorder = (ImageFilterBorder) FiltersManager.getManager().getFilter(ImageFilterBorder.class); + filterBorder.setResources(getResources()); + } + static { System.loadLibrary("jni_filtershow_filters"); } diff --git a/src/com/android/gallery3d/filtershow/ImageStateAdapter.java b/src/com/android/gallery3d/filtershow/ImageStateAdapter.java index 44b94e45e..58e0035bc 100644 --- a/src/com/android/gallery3d/filtershow/ImageStateAdapter.java +++ b/src/com/android/gallery3d/filtershow/ImageStateAdapter.java @@ -24,9 +24,10 @@ import android.widget.ArrayAdapter; import android.widget.TextView; import com.android.gallery3d.R; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; import com.android.gallery3d.filtershow.filters.ImageFilter; -public class ImageStateAdapter extends ArrayAdapter<ImageFilter> { +public class ImageStateAdapter extends ArrayAdapter<FilterRepresentation> { private static final String LOGTAG = "ImageStateAdapter"; public ImageStateAdapter(Context context, int textViewResourceId) { @@ -41,12 +42,12 @@ public class ImageStateAdapter extends ArrayAdapter<ImageFilter> { Context.LAYOUT_INFLATER_SERVICE); view = inflater.inflate(R.layout.filtershow_imagestate_row, null); } - ImageFilter filter = getItem(position); + FilterRepresentation filter = getItem(position); if (filter != null) { TextView itemLabel = (TextView) view.findViewById(R.id.imagestate_label); itemLabel.setText(filter.getName()); TextView itemParameter = (TextView) view.findViewById(R.id.imagestate_parameter); - itemParameter.setText("" + filter.getParameter()); + itemParameter.setText(filter.getStateRepresentation()); } return view; } diff --git a/src/com/android/gallery3d/filtershow/PanelController.java b/src/com/android/gallery3d/filtershow/PanelController.java index a0b13fb84..18a9585a6 100644 --- a/src/com/android/gallery3d/filtershow/PanelController.java +++ b/src/com/android/gallery3d/filtershow/PanelController.java @@ -16,9 +16,9 @@ package com.android.gallery3d.filtershow; +import android.annotation.TargetApi; import android.content.Context; import android.text.Html; -import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewPropertyAnimator; @@ -27,6 +27,7 @@ import android.widget.TextView; import com.android.gallery3d.R; import com.android.gallery3d.filtershow.editors.Editor; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; import com.android.gallery3d.filtershow.filters.ImageFilter; import com.android.gallery3d.filtershow.filters.ImageFilterTinyPlanet; import com.android.gallery3d.filtershow.imageshow.ImageCrop; @@ -34,7 +35,6 @@ import com.android.gallery3d.filtershow.imageshow.ImageShow; import com.android.gallery3d.filtershow.imageshow.MasterImage; import com.android.gallery3d.filtershow.presets.ImagePreset; import com.android.gallery3d.filtershow.ui.FilterIconButton; -import com.android.gallery3d.filtershow.ui.FramedTextButton; import java.util.HashMap; import java.util.Vector; @@ -49,6 +49,14 @@ public class PanelController implements OnClickListener { private boolean mDisableFilterButtons = false; private boolean mFixedAspect = false; + public static boolean useAnimations() { + int currentapiVersion = android.os.Build.VERSION.SDK_INT; + if (currentapiVersion >= android.os.Build.VERSION_CODES.JELLY_BEAN) { + return true; + } + return false; + } + public void setFixedAspect(boolean t) { mFixedAspect = t; } @@ -87,16 +95,28 @@ public class PanelController implements OnClickListener { } else { delta = w; } - anim.x(delta); + if (PanelController.useAnimations()) { + anim.x(delta); + } else { + mContainer.setX(delta); + } } else if (move == VERTICAL_MOVE) { - anim.y(h); - } - anim.setDuration(ANIM_DURATION).withLayer().withEndAction(new Runnable() { - @Override - public void run() { - mContainer.setVisibility(View.GONE); + if (PanelController.useAnimations()) { + anim.y(h); + } else { + mContainer.setY(h); } - }); + } + if (PanelController.useAnimations()) { + anim.setDuration(ANIM_DURATION).withLayer().withEndAction(new Runnable() { + @Override + public void run() { + mContainer.setVisibility(View.GONE); + } + }); + } else { + mContainer.setVisibility(View.GONE); + } return anim; } @@ -108,18 +128,20 @@ public class PanelController implements OnClickListener { ViewPropertyAnimator anim = mContainer.animate(); int w = mRowPanel.getWidth(); int h = mRowPanel.getHeight(); - if (move == HORIZONTAL_MOVE) { - if (oldPos < mPosition) { - mContainer.setX(w); - } else { - mContainer.setX(-w); + if (useAnimations()) { + if (move == HORIZONTAL_MOVE) { + if (oldPos < mPosition) { + mContainer.setX(w); + } else { + mContainer.setX(-w); + } + anim.x(0); + } else if (move == VERTICAL_MOVE) { + mContainer.setY(h); + anim.y(0); } - anim.x(0); - } else if (move == VERTICAL_MOVE) { - mContainer.setY(h); - anim.y(0); + anim.setDuration(ANIM_DURATION).withLayer(); } - anim.setDuration(ANIM_DURATION).withLayer(); return anim; } } @@ -187,12 +209,16 @@ public class PanelController implements OnClickListener { mView.setY(0); int h = mRowPanel.getHeight(); anim.y(-h); - anim.setDuration(ANIM_DURATION).withLayer().withEndAction(new Runnable() { - @Override - public void run() { - mView.setVisibility(View.GONE); - } - }); + if (PanelController.useAnimations()) { + anim.setDuration(ANIM_DURATION).withLayer().withEndAction(new Runnable() { + @Override + public void run() { + mView.setVisibility(View.GONE); + } + }); + } else { + mView.setVisibility(View.GONE); + } mSelected = false; return anim; } @@ -203,10 +229,13 @@ public class PanelController implements OnClickListener { mView.setX(0); mView.setY(-h); updateText(); + mSelected = true; ViewPropertyAnimator anim = mView.animate(); anim.y(0); - anim.setDuration(ANIM_DURATION).withLayer(); - mSelected = true; + anim.setDuration(ANIM_DURATION); + if (PanelController.useAnimations()) { + anim.withLayer(); + } return anim; } @@ -233,7 +262,6 @@ public class PanelController implements OnClickListener { private View mCurrentPanel = null; private View mRowPanel = null; private UtilityPanel mUtilityPanel = null; - private MasterImage mMasterImage = MasterImage.getImage(); private ImageShow mCurrentImage = null; private Editor mCurrentEditor = null; private FilterShowActivity mActivity = null; @@ -294,9 +322,9 @@ public class PanelController implements OnClickListener { if (mUtilityPanel == null || !mUtilityPanel.selected()) { return true; } - HistoryAdapter adapter = mMasterImage.getHistory(); + HistoryAdapter adapter = MasterImage.getImage().getHistory(); int position = adapter.undo(); - mMasterImage.onHistoryItemClick(position); + MasterImage.getImage().onHistoryItemClick(position); showPanel(mCurrentPanel); mCurrentImage.select(); if (mCurrentEditor != null) { @@ -345,6 +373,7 @@ public class PanelController implements OnClickListener { public ImageShow showImageView(int id) { ImageShow image = null; + mActivity.hideImageViews(); for (View view : mImageViews) { if (view.getId() == id) { view.setVisibility(View.VISIBLE); @@ -358,7 +387,8 @@ public class PanelController implements OnClickListener { public void showDefaultImageView() { showImageView(R.id.imageShow).setShowControls(false); - mMasterImage.setCurrentFilter(null); + MasterImage.getImage().setCurrentFilter(null); + MasterImage.getImage().setCurrentFilterRepresentation(null); } public void showPanel(View view) { @@ -368,10 +398,14 @@ public class PanelController implements OnClickListener { if (mUtilityPanel != null && mUtilityPanel.selected()) { ViewPropertyAnimator anim1 = mUtilityPanel.unselect(); removedUtilityPanel = true; - anim1.start(); + if (anim1 != null) { + anim1.start(); + } if (mCurrentPanel == view) { ViewPropertyAnimator anim2 = current.select(-1, VERTICAL_MOVE); - anim2.start(); + if (anim2 != null) { + anim2.start(); + } showDefaultImageView(); } } @@ -387,77 +421,75 @@ public class PanelController implements OnClickListener { currentPos = current.getPosition(); } ViewPropertyAnimator anim1 = panel.select(currentPos, HORIZONTAL_MOVE); - anim1.start(); + if (anim1 != null) { + anim1.start(); + } if (current != null) { ViewPropertyAnimator anim2 = current.unselect(panel.getPosition(), HORIZONTAL_MOVE); - anim2.start(); + if (anim2 != null) { + anim2.start(); + } } } else { ViewPropertyAnimator anim = panel.select(-1, VERTICAL_MOVE); - anim.start(); + if (anim != null) { + anim.start(); + } } + showDefaultImageView(); mCurrentPanel = view; } public ImagePreset getImagePreset() { - return mMasterImage.getPreset(); - } - - public ImageFilter setImagePreset(ImageFilter filter, String name) { - ImagePreset copy = new ImagePreset(getImagePreset()); - copy.add(filter); - copy.setHistoryName(name); - copy.setIsFx(false); - mMasterImage.setPreset(copy, true); - return filter; + return MasterImage.getImage().getPreset(); } - // TODO: remove this. - public void ensureFilter(String name) { - ImagePreset preset = getImagePreset(); - ImageFilter filter = preset.getFilter(name); - if (filter != null) { - // If we already have a filter, we might still want - // to push it onto the history stack. - ImagePreset copy = new ImagePreset(getImagePreset()); - copy.setHistoryName(name); - mMasterImage.setPreset(copy, true); - filter = copy.getFilter(name); - } - - if (filter == null) { - ImageFilter filterInstance = mFilters.get(name); - if (filterInstance != null) { - try { - ImageFilter newFilter = filterInstance.clone(); - newFilter.reset(); - filter = setImagePreset(newFilter, name); - } catch (CloneNotSupportedException e) { - e.printStackTrace(); - } - } + public void useFilterRepresentation(FilterRepresentation filterRepresentation) { + if (filterRepresentation == null) { + return; } - if (filter != null) { - mMasterImage.setCurrentFilter(filter); + if (MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) { + return; } + ImagePreset oldPreset = MasterImage.getImage().getPreset(); + ImagePreset copy = new ImagePreset(oldPreset); + FilterRepresentation representation = copy.getRepresentation(filterRepresentation); + if (representation == null) { + copy.addFilter(filterRepresentation); + } else { + if (filterRepresentation.allowsMultipleInstances()) { + representation.updateTempParametersFrom(filterRepresentation); + copy.setHistoryName(filterRepresentation.getName()); + } + filterRepresentation = representation; + } + MasterImage.getImage().setPreset(copy, true); + MasterImage.getImage().setCurrentFilterRepresentation(filterRepresentation); } public void showComponent(View view) { boolean doPanelTransition = true; if (view instanceof FilterIconButton) { - ImageFilter f = ((FilterIconButton) view).getImageFilter(); - doPanelTransition = f.showUtilityPanel(); + FilterRepresentation f = ((FilterIconButton) view).getFilterRepresentation(); + if (f != null) { + // FIXME: this check shouldn't be necessary (f shouldn't be null) + doPanelTransition = f.showUtilityPanel(); + } } - if (mUtilityPanel != null && !mUtilityPanel.selected() && doPanelTransition ) { + if (mUtilityPanel != null && !mUtilityPanel.selected() && doPanelTransition) { Panel current = mPanels.get(mCurrentPanel); ViewPropertyAnimator anim1 = current.unselect(-1, VERTICAL_MOVE); - anim1.start(); + if (anim1 != null) { + anim1.start(); + } if (mUtilityPanel != null) { ViewPropertyAnimator anim2 = mUtilityPanel.select(); - anim2.start(); + if (anim2 != null) { + anim2.start(); + } } } @@ -469,22 +501,23 @@ public class PanelController implements OnClickListener { if (view instanceof FilterIconButton) { mCurrentEditor = null; FilterIconButton component = (FilterIconButton) view; - ImageFilter filter = component.getImageFilter(); - if (filter.getEditingViewId() != 0) { - if (mEditorPlaceHolder.contains(filter.getEditingViewId())) { - mCurrentEditor = mEditorPlaceHolder.showEditor(filter.getEditingViewId()); - mCurrentImage = mCurrentEditor.getImageShow(); - mCurrentEditor.setPanelController(this); - } else { - mCurrentImage = showImageView(filter.getEditingViewId()); + FilterRepresentation representation = component.getFilterRepresentation(); + if (representation != null) { + mUtilityPanel.setEffectName(representation.getName()); + mUtilityPanel.setShowParameter(representation.showParameterValue()); + + if (representation.getEditorId() != 0) { + if (mEditorPlaceHolder.contains(representation.getEditorId())) { + mCurrentEditor = mEditorPlaceHolder.showEditor(representation.getEditorId()); + mCurrentImage = mCurrentEditor.getImageShow(); + mCurrentEditor.setPanelController(this); + } else { + mCurrentImage = showImageView(representation.getEditorId()); + } } - mCurrentImage.setShowControls(filter.showEditingControls()); - String ename = mCurrentImage.getContext().getString(filter.getTextId()); - mUtilityPanel.setEffectName(ename); + mCurrentImage.setShowControls(representation.showEditingControls()); + mUtilityPanel.setShowParameter(representation.showParameterValue()); - mUtilityPanel.setShowParameter(filter.showParameterValue()); - ImageFilter currentFilter = mMasterImage.getPreset().getFilter(filter.getName()); - mMasterImage.setCurrentFilter(currentFilter); mCurrentImage.select(); if (mCurrentEditor != null) { mCurrentEditor.reflectCurrentFilter(); @@ -503,7 +536,6 @@ public class PanelController implements OnClickListener { mCurrentImage = showImageView(R.id.imageTinyPlanet).setShowControls(true); String ename = mCurrentImage.getContext().getString(R.string.tinyplanet); mUtilityPanel.setEffectName(ename); - ensureFilter(ename); if (!mDisableFilterButtons) { mActivity.disableFilterButtons(); mDisableFilterButtons = true; @@ -541,15 +573,8 @@ public class PanelController implements OnClickListener { mUtilityPanel.setShowParameter(false); break; } - case R.id.redEyeButton: { - mCurrentImage = showImageView(R.id.imageRedEyes).setShowControls(true); - String ename = mCurrentImage.getContext().getString(R.string.redeye); - mUtilityPanel.setEffectName(ename); - ensureFilter(ename); - break; - } case R.id.applyEffect: { - if (mMasterImage.getCurrentFilter() instanceof ImageFilterTinyPlanet) { + if (MasterImage.getImage().getCurrentFilter() instanceof ImageFilterTinyPlanet) { mActivity.saveImage(); } else { if (mCurrentImage instanceof ImageCrop) { @@ -557,6 +582,7 @@ public class PanelController implements OnClickListener { } showPanel(mCurrentPanel); } + MasterImage.getImage().invalidateFiltersOnly(); break; } } diff --git a/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java b/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java index d415250a4..419abe85d 100644 --- a/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java +++ b/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java @@ -22,6 +22,8 @@ import android.os.Process; import android.support.v8.renderscript.*; import android.util.Log; +import com.android.gallery3d.filtershow.filters.BaseFiltersManager; +import com.android.gallery3d.filtershow.filters.FiltersManager; import com.android.gallery3d.filtershow.filters.ImageFilterRS; import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; import com.android.gallery3d.filtershow.imageshow.MasterImage; @@ -31,7 +33,6 @@ public class FilteringPipeline implements Handler.Callback { private final static FilteringPipeline gPipeline = new FilteringPipeline(); private static final String LOGTAG = "FilteringPipeline"; - private ImagePreset mPreviousPreset = null; private ImagePreset mPreviousGeometryPreset = null; private ImagePreset mPreviousFiltersPreset = null; private GeometryMetadata mPreviousGeometry = null; @@ -44,11 +45,9 @@ public class FilteringPipeline implements Handler.Callback { private HandlerThread mHandlerThread = null; private final static int NEW_PRESET = 0; - private final static int NEW_GEOMETRY_PRESET = 1; - private final static int NEW_FILTERS_PRESET = 2; - private final static int COMPUTE_PRESET = 3; - private final static int COMPUTE_GEOMETRY_PRESET = 4; - private final static int COMPUTE_FILTERS_PRESET = 5; + private final static int NEW_RENDERING_REQUEST = 1; + private final static int COMPUTE_PRESET = 2; + private final static int COMPUTE_RENDERING_REQUEST = 3; private Handler mProcessingHandler = null; private final Handler mUIHandler = new Handler() { @@ -61,16 +60,9 @@ public class FilteringPipeline implements Handler.Callback { MasterImage.getImage().notifyObservers(); break; } - case NEW_GEOMETRY_PRESET: { - TripleBufferBitmap buffer = MasterImage.getImage().getGeometryOnlyBuffer(); - buffer.swapConsumer(); - MasterImage.getImage().notifyObservers(); - break; - } - case NEW_FILTERS_PRESET: { - TripleBufferBitmap buffer = MasterImage.getImage().getFiltersOnlyBuffer(); - buffer.swapConsumer(); - MasterImage.getImage().notifyObservers(); + case NEW_RENDERING_REQUEST: { + RenderingRequest request = (RenderingRequest) msg.obj; + request.markAvailable(); break; } } @@ -89,21 +81,11 @@ public class FilteringPipeline implements Handler.Callback { mUIHandler.sendMessage(uimsg); break; } - case COMPUTE_GEOMETRY_PRESET: { - ImagePreset preset = (ImagePreset) msg.obj; - TripleBufferBitmap buffer = MasterImage.getImage().getGeometryOnlyBuffer(); - compute(buffer, preset, COMPUTE_GEOMETRY_PRESET); - buffer.swapProducer(); - Message uimsg = mUIHandler.obtainMessage(NEW_GEOMETRY_PRESET); - mUIHandler.sendMessage(uimsg); - break; - } - case COMPUTE_FILTERS_PRESET: { - ImagePreset preset = (ImagePreset) msg.obj; - TripleBufferBitmap buffer = MasterImage.getImage().getFiltersOnlyBuffer(); - compute(buffer, preset, COMPUTE_FILTERS_PRESET); - buffer.swapProducer(); - Message uimsg = mUIHandler.obtainMessage(NEW_FILTERS_PRESET); + case COMPUTE_RENDERING_REQUEST: { + RenderingRequest request = (RenderingRequest) msg.obj; + render(request); + Message uimsg = mUIHandler.obtainMessage(NEW_RENDERING_REQUEST); + uimsg.obj = request; mUIHandler.sendMessage(uimsg); break; } @@ -116,7 +98,6 @@ public class FilteringPipeline implements Handler.Callback { private float mResizeFactor = 1.0f; private long mResizeTime = 0; - private Allocation mOriginalBitmapAllocation = null; private Allocation mOriginalAllocation = null; private Allocation mFiltersOnlyOriginalAllocation = null; @@ -136,8 +117,6 @@ public class FilteringPipeline implements Handler.Callback { Log.v(LOGTAG,"setOriginal, size " + bitmap.getWidth() + " x " + bitmap.getHeight()); updateOriginalAllocation(MasterImage.getImage().getPreset()); updatePreviewBuffer(); - updateFiltersOnlyPreviewBuffer(); - updateGeometryOnlyPreviewBuffer(); } public synchronized boolean updateOriginalAllocation(ImagePreset preset) { @@ -153,10 +132,16 @@ public class FilteringPipeline implements Handler.Callback { } mResizedOriginalBitmap = Bitmap.createScaledBitmap(mOriginalBitmap, w, h, true); */ + GeometryMetadata geometry = preset.getGeometry(); if (mPreviousGeometry != null && geometry.equals(mPreviousGeometry)) { return false; } + + if (DEBUG) { + Log.v(LOGTAG, "geometry has changed"); + } + RenderScript RS = ImageFilterRS.getRenderScriptContext(); if (mFiltersOnlyOriginalAllocation != null) { mFiltersOnlyOriginalAllocation.destroy(); @@ -169,83 +154,113 @@ public class FilteringPipeline implements Handler.Callback { mResizedOriginalBitmap = preset.applyGeometry(mOriginalBitmap); mOriginalAllocation = Allocation.createFromBitmap(RS, mResizedOriginalBitmap, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); + mPreviousGeometry = new GeometryMetadata(geometry); + + FiltersManager.getManager().resetBitmapsRS(); return true; } - public synchronized void updatePreviewBuffer() { + public void postRenderingRequest(RenderingRequest request) { if (mOriginalAllocation == null) { return; } - if (!needsRepaint()) { - return; - } - if (mProcessingHandler.hasMessages(COMPUTE_PRESET)) { - mProcessingHandler.removeMessages(COMPUTE_PRESET); - } - Message msg = mProcessingHandler.obtainMessage(COMPUTE_PRESET); - ImagePreset preset = new ImagePreset(MasterImage.getImage().getPreset()); - setPresetParameters(preset); - msg.obj = preset; + Message msg = mProcessingHandler.obtainMessage(COMPUTE_RENDERING_REQUEST); + msg.obj = request; mProcessingHandler.sendMessage(msg); } - public void updateGeometryOnlyPreviewBuffer() { + public synchronized void updatePreviewBuffer() { if (mOriginalAllocation == null) { return; } - if (!needsGeometryRepaint()) { + if (mProcessingHandler.hasMessages(COMPUTE_PRESET)) { return; } - if (mProcessingHandler.hasMessages(COMPUTE_GEOMETRY_PRESET)) { - mProcessingHandler.removeMessages(COMPUTE_GEOMETRY_PRESET); - } - Message msg = mProcessingHandler.obtainMessage(COMPUTE_GEOMETRY_PRESET); - ImagePreset preset = new ImagePreset(MasterImage.getImage().getGeometryPreset()); - setPresetParameters(preset); - msg.obj = preset; - mProcessingHandler.sendMessage(msg); - } - - public void updateFiltersOnlyPreviewBuffer() { - if (mOriginalAllocation == null) { + if (!needsRepaint()) { return; } - if (!needsFiltersRepaint()) { + if (MasterImage.getImage().getPreset() == null) { return; } - if (mProcessingHandler.hasMessages(COMPUTE_FILTERS_PRESET)) { - mProcessingHandler.removeMessages(COMPUTE_FILTERS_PRESET); - } - Message msg = mProcessingHandler.obtainMessage(COMPUTE_FILTERS_PRESET); - ImagePreset preset = new ImagePreset(MasterImage.getImage().getFiltersOnlyPreset()); - setPresetParameters(preset); - - msg.obj = preset; + Message msg = mProcessingHandler.obtainMessage(COMPUTE_PRESET); + msg.obj = MasterImage.getImage().getPreset(); mProcessingHandler.sendMessage(msg); } private void setPresetParameters(ImagePreset preset) { preset.setScaleFactor(mPreviewScaleFactor); if (mPreviewScaleFactor < 1.0f) { - preset.setIsHighQuality(false); + preset.setQuality(ImagePreset.QUALITY_PREVIEW); } else { - preset.setIsHighQuality(true); + preset.setQuality(ImagePreset.QUALITY_PREVIEW); + } + } + + private String getType(RenderingRequest request) { + if (request.getType() == RenderingRequest.ICON_RENDERING) { + return "ICON_RENDERING"; + } + if (request.getType() == RenderingRequest.FILTERS_RENDERING) { + return "FILTERS_RENDERING"; + } + if (request.getType() == RenderingRequest.FULL_RENDERING) { + return "FULL_RENDERING"; + } + if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) { + return "GEOMETRY_RENDERING"; + } + return "UNKNOWN TYPE!"; + } + + private void render(RenderingRequest request) { + if (request.getBitmap() == null + || request.getImagePreset() == null) { + return; + } + if (DEBUG) { + Log.v(LOGTAG, "render image of type " + getType(request)); + } + + Bitmap bitmap = request.getBitmap(); + ImagePreset preset = request.getImagePreset(); + setPresetParameters(preset); + if (request.getType() == RenderingRequest.FILTERS_RENDERING) { + FiltersManager.getManager().resetBitmapsRS(); + } + + if (request.getType() != RenderingRequest.ICON_RENDERING) { + updateOriginalAllocation(preset); + } + if (DEBUG) { + Log.v(LOGTAG, "after update, req bitmap (" + bitmap.getWidth() + "x" + bitmap.getHeight() + +" ? resizeOriginal (" + mResizedOriginalBitmap.getWidth() + "x" + + mResizedOriginalBitmap.getHeight()); + } + if (request.getType() == RenderingRequest.FULL_RENDERING + || request.getType() == RenderingRequest.GEOMETRY_RENDERING) { + mOriginalAllocation.copyTo(bitmap); + } 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) { + Bitmap bmp = preset.apply(bitmap); + request.setBitmap(bmp); + } + if (request.getType() == RenderingRequest.FILTERS_RENDERING) { + FiltersManager.getManager().resetBitmapsRS(); } } private void compute(TripleBufferBitmap buffer, ImagePreset preset, int type) { - String thread = Thread.currentThread().getName(); - if (type == COMPUTE_PRESET && preset.same(mPreviousPreset)) { - mPreviousPreset.usePreset(preset); - preset = mPreviousPreset; - } else if (type == COMPUTE_GEOMETRY_PRESET && preset.same(mPreviousGeometryPreset)) { - mPreviousGeometryPreset.usePreset(preset); - preset = mPreviousGeometryPreset; - } else if (type == COMPUTE_FILTERS_PRESET && preset.same(mPreviousFiltersPreset)) { - mPreviousFiltersPreset.usePreset(preset); - preset = mPreviousFiltersPreset; + if (DEBUG) { + Log.v(LOGTAG, "compute preset " + preset); + preset.showFilters(); } + + String thread = Thread.currentThread().getName(); long time = System.currentTimeMillis(); if (updateOriginalAllocation(preset)) { buffer.updateBitmaps(mResizedOriginalBitmap); @@ -253,30 +268,15 @@ public class FilteringPipeline implements Handler.Callback { Bitmap bitmap = buffer.getProducer(); long time2 = System.currentTimeMillis(); - if (type != COMPUTE_FILTERS_PRESET) { - if (bitmap == null || (bitmap.getWidth() != mResizedOriginalBitmap.getWidth()) - || (bitmap.getHeight() != mResizedOriginalBitmap.getHeight())) { - buffer.updateBitmaps(mResizedOriginalBitmap); - bitmap = buffer.getProducer(); - } - mOriginalAllocation.copyTo(bitmap); - } else { - if (bitmap == null || (bitmap.getWidth() != mOriginalBitmap.getWidth()) - || (bitmap.getHeight() != mOriginalBitmap.getHeight())) { - buffer.updateBitmaps(mOriginalBitmap); - bitmap = buffer.getProducer(); - } - mFiltersOnlyOriginalAllocation.copyTo(bitmap); - } - - if (mOriginalAllocation == null || bitmap == null) { - Log.v(LOGTAG, "exiting compute because mOriginalAllocation: " + mOriginalAllocation + " or bitmap: " + bitmap); - return; + if (bitmap == null || (bitmap.getWidth() != mResizedOriginalBitmap.getWidth()) + || (bitmap.getHeight() != mResizedOriginalBitmap.getHeight())) { + buffer.updateBitmaps(mResizedOriginalBitmap); + bitmap = buffer.getProducer(); } + mOriginalAllocation.copyTo(bitmap); - if (type != COMPUTE_GEOMETRY_PRESET) { - bitmap = preset.apply(bitmap); - } + setPresetParameters(preset); + bitmap = preset.apply(bitmap); time = System.currentTimeMillis() - time; time2 = System.currentTimeMillis() - time2; @@ -286,50 +286,16 @@ public class FilteringPipeline implements Handler.Callback { + ") took " + time + " ms, " + time2 + " ms for the filter, on thread " + thread); } if (type == COMPUTE_PRESET) { - mPreviousPreset = preset; if (mResizeFactor > 0.6 && time > MAX_PROCESS_TIME && (System.currentTimeMillis() + 1000 > mResizeTime)) { mResizeTime = System.currentTimeMillis(); mResizeFactor *= RESIZE_FACTOR; } - } else if (type == COMPUTE_GEOMETRY_PRESET) { - mPreviousGeometryPreset = preset; - } else if (type == COMPUTE_FILTERS_PRESET) { - mPreviousFiltersPreset = preset; } } private synchronized boolean needsRepaint() { - ImagePreset preset = MasterImage.getImage().getPreset(); - if (preset == null || mPreviousPreset == null) { - return true; - } - if (preset.equals(mPreviousPreset)) { - return false; - } - return true; - } - - private synchronized boolean needsGeometryRepaint() { - ImagePreset preset = MasterImage.getImage().getPreset(); - if (preset == null || mPreviousGeometry == null || mPreviousGeometryPreset == null) { - return true; - } - GeometryMetadata geometry = preset.getGeometry(); - if (geometry.equals(mPreviousGeometryPreset.getGeometry())) { - return false; - } - return true; - } - - private synchronized boolean needsFiltersRepaint() { - ImagePreset preset = MasterImage.getImage().getPreset(); - if (preset == null || mPreviousFiltersPreset == null) { - return true; - } - if (preset.equals(mPreviousFiltersPreset)) { - return false; - } - return true; + TripleBufferBitmap buffer = MasterImage.getImage().getDoubleBuffer(); + return buffer.checkRepaintNeeded(); } public void setPreviewScaleFactor(float previewScaleFactor) { diff --git a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java index ade775d09..698992ac9 100644 --- a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java +++ b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java @@ -212,13 +212,13 @@ public class ImageLoader { mOriginalBitmapLarge = rotateToPortrait(mOriginalBitmapLarge, mOrientation); } mZoomOrientation = mOrientation; - FilteringPipeline pipeline = FilteringPipeline.getPipeline(); - pipeline.setOriginal(mOriginalBitmapLarge); - float previewScale = (float) mOriginalBitmapLarge.getWidth() / (float) getOriginalBounds().width(); - pipeline.setPreviewScaleFactor(previewScale); warnListeners(); } + public Bitmap decodeImage(int id, BitmapFactory.Options options) { + return BitmapFactory.decodeResource(mContext.getResources(), id, options); + } + public static Bitmap rotateToPortrait(Bitmap bitmap, int ori) { Matrix matrix = new Matrix(); int w = bitmap.getWidth(); @@ -280,7 +280,7 @@ public class ImageLoader { return null; } - static final int MAX_BITMAP_DIM = 2048; + static final int MAX_BITMAP_DIM = 900; private Bitmap loadScaledBitmap(Uri uri, int size) { InputStream is = null; @@ -392,7 +392,7 @@ public class ImageLoader { public void saveImage(ImagePreset preset, final FilterShowActivity filterShowActivity, File destination) { - preset.setIsHighQuality(true); + preset.setQuality(ImagePreset.QUALITY_FINAL); preset.setScaleFactor(1.0f); new SaveCopyTask(mContext, mUri, destination, new SaveCopyTask.Callback() { @@ -432,7 +432,7 @@ public class ImageLoader { public void returnFilteredResult(ImagePreset preset, final FilterShowActivity filterShowActivity) { - preset.setIsHighQuality(true); + preset.setQuality(ImagePreset.QUALITY_FINAL); preset.setScaleFactor(1.0f); BitmapTask.Callbacks<ImagePreset> cb = new BitmapTask.Callbacks<ImagePreset>() { @@ -448,7 +448,7 @@ public class ImageLoader { @Override public Bitmap onExecute(ImagePreset param) { - if (param == null) { + if (param == null || mUri == null) { return null; } Bitmap bitmap = loadMutableBitmap(mContext, mUri); @@ -456,6 +456,7 @@ public class ImageLoader { Log.w(LOGTAG, "Failed to save image!"); return null; } + bitmap = param.applyGeometry(bitmap); return param.apply(bitmap); } }; diff --git a/src/com/android/gallery3d/filtershow/cache/RenderingRequest.java b/src/com/android/gallery3d/filtershow/cache/RenderingRequest.java new file mode 100644 index 000000000..1e9f6b83a --- /dev/null +++ b/src/com/android/gallery3d/filtershow/cache/RenderingRequest.java @@ -0,0 +1,106 @@ +/* + * 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.cache; + +import android.graphics.Bitmap; +import com.android.gallery3d.app.Log; +import com.android.gallery3d.filtershow.imageshow.MasterImage; +import com.android.gallery3d.filtershow.presets.ImagePreset; + +public class RenderingRequest { + private static final String LOGTAG = "RenderingRequest"; + private boolean mIsDirect = false; + private Bitmap mBitmap = null; + private ImagePreset mImagePreset = null; + private RenderingRequestCaller mCaller = 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; + private static final Bitmap.Config mConfig = Bitmap.Config.ARGB_8888; + + public static void post(Bitmap source, ImagePreset preset, int type, + RenderingRequestCaller caller) { + if (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) { + bitmap = preset.applyGeometry(source); + } else { + bitmap = Bitmap.createBitmap(source.getWidth(), source.getHeight(), mConfig); + } + request.setBitmap(bitmap); + ImagePreset passedPreset = new ImagePreset(preset); + passedPreset.setImageLoader(MasterImage.getImage().getImageLoader()); + request.setImagePreset(passedPreset); + request.setType(type); + request.setCaller(caller); + request.post(); + } + + public void post() { + FilteringPipeline.getPipeline().postRenderingRequest(this); + } + + public void markAvailable() { + if (mBitmap == null || mImagePreset == null + || mCaller == null) { + return; + } + mCaller.available(this); + } + + public boolean isDirect() { + return mIsDirect; + } + + public void setDirect(boolean isDirect) { + mIsDirect = isDirect; + } + + public Bitmap getBitmap() { + return mBitmap; + } + + public void setBitmap(Bitmap bitmap) { + mBitmap = bitmap; + } + + public ImagePreset getImagePreset() { + return mImagePreset; + } + + public void setImagePreset(ImagePreset imagePreset) { + mImagePreset = imagePreset; + } + + public int getType() { + return mType; + } + + public void setType(int type) { + mType = type; + } + + public void setCaller(RenderingRequestCaller caller) { + mCaller = caller; + } +} diff --git a/src/com/android/gallery3d/filtershow/cache/RenderingRequestCaller.java b/src/com/android/gallery3d/filtershow/cache/RenderingRequestCaller.java new file mode 100644 index 000000000..240eb8f44 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/cache/RenderingRequestCaller.java @@ -0,0 +1,21 @@ +/* + * 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.cache; + +public interface RenderingRequestCaller { + public void available(RenderingRequest request); +} diff --git a/src/com/android/gallery3d/filtershow/cache/TripleBufferBitmap.java b/src/com/android/gallery3d/filtershow/cache/TripleBufferBitmap.java index c4837ad4a..cc14bf65f 100644 --- a/src/com/android/gallery3d/filtershow/cache/TripleBufferBitmap.java +++ b/src/com/android/gallery3d/filtershow/cache/TripleBufferBitmap.java @@ -30,6 +30,7 @@ public class TripleBufferBitmap { private boolean mNeedsSwap = false; private final Bitmap.Config mBitmapConfig = Bitmap.Config.ARGB_8888; + private boolean mNeedsRepaint = true; public TripleBufferBitmap() { @@ -68,4 +69,17 @@ public class TripleBufferBitmap { mConsumer = intermediate; mNeedsSwap = false; } + + public synchronized void invalidate() { + mNeedsRepaint = true; + } + + public synchronized boolean checkRepaintNeeded() { + if (mNeedsRepaint) { + mNeedsRepaint = false; + return true; + } + return false; + } + } diff --git a/src/com/android/gallery3d/filtershow/editors/BasicEditor.java b/src/com/android/gallery3d/filtershow/editors/BasicEditor.java index 1ea0da700..48aa5925a 100644 --- a/src/com/android/gallery3d/filtershow/editors/BasicEditor.java +++ b/src/com/android/gallery3d/filtershow/editors/BasicEditor.java @@ -17,15 +17,15 @@ package com.android.gallery3d.filtershow.editors; import com.android.gallery3d.R; -import com.android.gallery3d.filtershow.filters.ImageFilter; +import com.android.gallery3d.filtershow.filters.*; import android.content.Context; -import android.util.Log; import android.view.View; import android.widget.FrameLayout; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import com.android.gallery3d.filtershow.imageshow.MasterImage; +import com.android.gallery3d.filtershow.presets.ImagePreset; /** * The basic editor that all the one parameter filters @@ -47,8 +47,8 @@ public class BasicEditor extends Editor implements OnSeekBarChangeListener { protected BasicEditor(int id, int layoutID, int viewID) { super(id); - int mLayoutID = layoutID; - int mViewID = viewID; + mLayoutID = layoutID; + mViewID = viewID; } @Override @@ -61,35 +61,32 @@ public class BasicEditor extends Editor implements OnSeekBarChangeListener { @Override public void reflectCurrentFilter() { - ImageFilter filter = mImageShow.getCurrentFilter(); - if (filter == null) { - return; + super.reflectCurrentFilter(); + if (getLocalRepresentation() != null && getLocalRepresentation() instanceof FilterBasicRepresentation) { + FilterBasicRepresentation interval = (FilterBasicRepresentation) getLocalRepresentation(); + boolean f = interval.showParameterValue(); + mSeekBar.setVisibility((f) ? View.VISIBLE : View.GONE); + int value = interval.getValue(); + int min = interval.getMinimum(); + int max = interval.getMaximum(); + mSeekBar.setMax(max - min); + mSeekBar.setProgress(value - min); } - boolean f = filter.showParameterValue(); - mSeekBar.setVisibility((f) ? View.VISIBLE : View.INVISIBLE); - int parameter = filter.getParameter(); - int maxp = filter.getMaxParameter(); - int minp = filter.getMinParameter(); - mSeekBar.setMax(maxp - minp); - mSeekBar.setProgress(parameter - minp); } @Override public void onProgressChanged(SeekBar sbar, int progress, boolean arg2) { - ImageFilter filter = mImageShow.getCurrentFilter(); - if (filter == null) { - return; + if (getLocalRepresentation() != null && getLocalRepresentation() instanceof FilterBasicRepresentation) { + FilterBasicRepresentation interval = (FilterBasicRepresentation) getLocalRepresentation(); + int value = progress + interval.getMinimum(); + interval.setValue(value); + mImageShow.onNewValue(value); + mView.invalidate(); + if (interval.showParameterValue()) { + mPanelController.onNewValue(value); + } + commitLocalRepresentation(); } - int minp = filter.getMinParameter(); - int value = progress + minp; - mImageShow.onNewValue(value); - mView.invalidate(); - if (filter.showParameterValue()) { - mPanelController.onNewValue(value); - } - - Log.v(LOGTAG, " #### progress=" + value); - MasterImage.getImage().updateBuffers(); } @Override diff --git a/src/com/android/gallery3d/filtershow/editors/Editor.java b/src/com/android/gallery3d/filtershow/editors/Editor.java index a9b2fd425..bea591c63 100644 --- a/src/com/android/gallery3d/filtershow/editors/Editor.java +++ b/src/com/android/gallery3d/filtershow/editors/Editor.java @@ -25,7 +25,10 @@ import android.widget.LinearLayout; import com.android.gallery3d.filtershow.PanelController; import com.android.gallery3d.filtershow.cache.ImageLoader; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; import com.android.gallery3d.filtershow.imageshow.ImageShow; +import com.android.gallery3d.filtershow.imageshow.MasterImage; +import com.android.gallery3d.filtershow.presets.ImagePreset; /** * Base class for Editors Must contain a mImageShow and a top level view @@ -38,6 +41,7 @@ public class Editor { protected PanelController mPanelController; protected int mID; private final String LOGTAG = "Editor"; + protected FilterRepresentation mLocalRepresentation = null; public void setPanelController(PanelController panelController) { this.mPanelController = panelController; @@ -53,6 +57,7 @@ public class Editor { public void createEditor(Context context,FrameLayout frameLayout) { mContext = context; mFrameLayout = frameLayout; + mLocalRepresentation = null; } protected void unpack(int viewid, int layoutid) { @@ -105,10 +110,25 @@ public class Editor { mView.setVisibility(visible); } + public FilterRepresentation getLocalRepresentation() { + if (mLocalRepresentation == null) { + ImagePreset preset = MasterImage.getImage().getPreset(); + FilterRepresentation filterRepresentation = MasterImage.getImage().getCurrentFilterRepresentation(); + mLocalRepresentation = preset.getFilterRepresentationCopyFrom(filterRepresentation); + } + return mLocalRepresentation; + } + + public void commitLocalRepresentation() { + ImagePreset preset = MasterImage.getImage().getPreset(); + preset.updateFilterRepresentation(getLocalRepresentation()); + } + /** * called after the filter is set and the select is called */ public void reflectCurrentFilter() { + mLocalRepresentation = null; } public boolean useUtilityPanel() { diff --git a/src/com/android/gallery3d/filtershow/editors/EditorCurves.java b/src/com/android/gallery3d/filtershow/editors/EditorCurves.java index a3360483a..b6e7b2bd7 100644 --- a/src/com/android/gallery3d/filtershow/editors/EditorCurves.java +++ b/src/com/android/gallery3d/filtershow/editors/EditorCurves.java @@ -20,11 +20,13 @@ import android.content.Context; import android.widget.FrameLayout; import com.android.gallery3d.R; +import com.android.gallery3d.filtershow.filters.FilterCurvesRepresentation; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; import com.android.gallery3d.filtershow.ui.ImageCurves; public class EditorCurves extends Editor { public static final int ID = R.id.imageCurves; - + ImageCurves mImageCurves; public EditorCurves() { super(ID); } @@ -32,6 +34,17 @@ public class EditorCurves extends Editor { @Override public void createEditor(Context context, FrameLayout frameLayout) { super.createEditor(context, frameLayout); - mView = mImageShow = new ImageCurves(context); + mView = mImageShow = mImageCurves = new ImageCurves(context); + mImageCurves.setEditor(this); + } + + @Override + public void reflectCurrentFilter() { + super.reflectCurrentFilter(); + FilterRepresentation rep = getLocalRepresentation(); + if (rep != null && getLocalRepresentation() instanceof FilterCurvesRepresentation) { + FilterCurvesRepresentation drawRep = (FilterCurvesRepresentation) rep; + mImageCurves.setFilterDrawRepresentation(drawRep); + } } } diff --git a/src/com/android/gallery3d/filtershow/editors/EditorDraw.java b/src/com/android/gallery3d/filtershow/editors/EditorDraw.java index abd4ae475..aa5ec61e8 100644 --- a/src/com/android/gallery3d/filtershow/editors/EditorDraw.java +++ b/src/com/android/gallery3d/filtershow/editors/EditorDraw.java @@ -18,12 +18,10 @@ package com.android.gallery3d.filtershow.editors; import android.app.Dialog; import android.content.Context; -import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; -import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.widget.Button; import android.widget.FrameLayout; @@ -35,16 +33,16 @@ import com.android.gallery3d.R; import com.android.gallery3d.filtershow.FilterShowActivity; import com.android.gallery3d.filtershow.colorpicker.ColorGridDialog; import com.android.gallery3d.filtershow.colorpicker.RGBListener; +import com.android.gallery3d.filtershow.filters.FilterDrawRepresentation; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; import com.android.gallery3d.filtershow.filters.ImageFilterDraw; import com.android.gallery3d.filtershow.imageshow.ImageDraw; import com.android.gallery3d.filtershow.ui.FramedTextButton; -/** - * TODO: Insert description here. (generated by hoford) - */ public class EditorDraw extends Editor { private static final String LOGTAG = "EditorDraw"; public static final int ID = R.id.editorDraw; + public ImageDraw mImageDraw; public EditorDraw() { super(ID); @@ -53,7 +51,9 @@ public class EditorDraw extends Editor { @Override public void createEditor(Context context, FrameLayout frameLayout) { super.createEditor(context, frameLayout); - mView = mImageShow = new ImageDraw(context); + mView = mImageShow = mImageDraw = new ImageDraw(context); + mImageDraw.setEditor(this); + } @Override @@ -62,6 +62,17 @@ public class EditorDraw extends Editor { } @Override + public void reflectCurrentFilter() { + super.reflectCurrentFilter(); + FilterRepresentation rep = getLocalRepresentation(); + + if (rep != null && getLocalRepresentation() instanceof FilterDrawRepresentation) { + FilterDrawRepresentation drawRep = (FilterDrawRepresentation) getLocalRepresentation(); + mImageDraw.setFilterDrawRepresentation(drawRep); + } + } + + @Override public void openUtilityPanel(final LinearLayout accessoryViewList) { View view = accessoryViewList.findViewById(R.id.drawUtilityButton); if (view == null) { @@ -70,7 +81,8 @@ public class EditorDraw extends Editor { view = inflater.inflate(R.layout.filtershow_draw_button, accessoryViewList, false); accessoryViewList.addView(view, view.getLayoutParams()); view.setOnClickListener(new OnClickListener() { - @Override + + @Override public void onClick(View arg0) { showPopupMenu(accessoryViewList); } @@ -88,25 +100,30 @@ public class EditorDraw extends Editor { if (button == null) { return; } - PopupMenu popupMenu = new PopupMenu(mImageShow.getActivity(), button); + final PopupMenu popupMenu = new PopupMenu(mImageShow.getActivity(), button); popupMenu.getMenuInflater().inflate(R.menu.filtershow_menu_draw, popupMenu.getMenu()); popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { ImageFilterDraw filter = (ImageFilterDraw) mImageShow.getCurrentFilter(); - int minp = filter.getMinParameter(); - int parameter = filter.getParameter(); if (item.getItemId() == R.id.draw_menu_color) { showColorGrid(item); } else if (item.getItemId() == R.id.draw_menu_size) { showSizeDialog(item); - } else if (item.getItemId() == R.id.draw_menu_style_brush) { + } else if (item.getItemId() == R.id.draw_menu_style_brush_marker) { + ImageDraw idraw = (ImageDraw) mImageShow; + idraw.setStyle(ImageFilterDraw.BRUSH_STYLE_MARKER); + } else if (item.getItemId() == R.id.draw_menu_style_brush_spatter) { ImageDraw idraw = (ImageDraw) mImageShow; - idraw.setStyle(ImageFilterDraw.BRUSH_STYLE); + idraw.setStyle(ImageFilterDraw.BRUSH_STYLE_SPATTER); } else if (item.getItemId() == R.id.draw_menu_style_line) { ImageDraw idraw = (ImageDraw) mImageShow; idraw.setStyle(ImageFilterDraw.SIMPLE_STYLE); - + } else if (item.getItemId() == R.id.draw_menu_clear) { + ImageDraw idraw = (ImageDraw) mImageShow; + idraw.resetParameter(); + commitLocalRepresentation(); } mView.invalidate(); return true; @@ -126,6 +143,7 @@ public class EditorDraw extends Editor { Button button = (Button) dialog.findViewById(R.id.sizeAcceptButton); button.setOnClickListener(new OnClickListener() { + @Override public void onClick(View arg0) { int p = bar.getProgress(); ImageDraw idraw = (ImageDraw) mImageShow; @@ -135,9 +153,10 @@ public class EditorDraw extends Editor { }); dialog.show(); } + public void showColorGrid(final MenuItem item) { RGBListener cl = new RGBListener() { - @Override + @Override public void setColor(int rgb) { ImageDraw idraw = (ImageDraw) mImageShow; idraw.setColor(rgb); @@ -147,5 +166,4 @@ public class EditorDraw extends Editor { cpd.show(); LayoutParams params = cpd.getWindow().getAttributes(); } - } diff --git a/src/com/android/gallery3d/filtershow/editors/EditorRedEye.java b/src/com/android/gallery3d/filtershow/editors/EditorRedEye.java new file mode 100644 index 000000000..c37102b37 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/editors/EditorRedEye.java @@ -0,0 +1,61 @@ +/* + * 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.FilterRedEyeRepresentation; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; +import com.android.gallery3d.filtershow.imageshow.ImageRedEye; + +/** + * The editor with no slider for filters without UI + */ +public class EditorRedEye extends Editor { + public static int ID = R.id.editorRedEye; + private final String LOGTAG = "EditorRedEye"; + ImageRedEye mImageRedEyes; + + public EditorRedEye() { + super(ID); + } + + protected EditorRedEye(int id) { + super(id); + } + + @Override + public void createEditor(Context context, FrameLayout frameLayout) { + super.createEditor(context, frameLayout); + mView = mImageShow = mImageRedEyes= new ImageRedEye(context); + mImageRedEyes.setEditor(this); + } + + @Override + public void reflectCurrentFilter() { + super.reflectCurrentFilter(); + FilterRepresentation rep = getLocalRepresentation(); + if (rep != null && getLocalRepresentation() instanceof FilterRedEyeRepresentation) { + FilterRedEyeRepresentation redEyeRep = (FilterRedEyeRepresentation) rep; + + mImageRedEyes.setRepresentation(redEyeRep); + } + } +} diff --git a/src/com/android/gallery3d/filtershow/editors/EditorTinyPlanet.java b/src/com/android/gallery3d/filtershow/editors/EditorTinyPlanet.java new file mode 100644 index 000000000..d21950912 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/editors/EditorTinyPlanet.java @@ -0,0 +1,54 @@ +/* + * 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.imageshow.ImageTinyPlanet; + +public class EditorTinyPlanet extends BasicEditor { + public static final int ID = R.id.tinyPlanetEditor; + private static final String LOGTAG = "EditorTinyPlanet"; + ImageTinyPlanet mImageTinyPlanet; + + public EditorTinyPlanet() { + super(ID, R.layout.filtershow_tiny_planet_editor, R.id.imageTinyPlanet); + } + + @Override + public void createEditor(Context context, FrameLayout frameLayout) { + super.createEditor(context, frameLayout); + mImageTinyPlanet = (ImageTinyPlanet) mImageShow; + mImageTinyPlanet.setEditor(this); + } + + @Override + public void reflectCurrentFilter() { + super.reflectCurrentFilter(); + + FilterRepresentation rep = getLocalRepresentation(); + if (rep != null && getLocalRepresentation() instanceof FilterTinyPlanetRepresentation) { + FilterTinyPlanetRepresentation drawRep = (FilterTinyPlanetRepresentation) rep; + mImageTinyPlanet.setRepresentation(drawRep); + } + } +} diff --git a/src/com/android/gallery3d/filtershow/editors/ImageOnlyEditor.java b/src/com/android/gallery3d/filtershow/editors/ImageOnlyEditor.java new file mode 100644 index 000000000..a3fc5aab4 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/editors/ImageOnlyEditor.java @@ -0,0 +1,46 @@ +/* + * 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.widget.FrameLayout; + +import com.android.gallery3d.R; +import com.android.gallery3d.filtershow.imageshow.ImageShow; + +/** + * The editor with no slider for filters without UI + */ +public class ImageOnlyEditor extends Editor { + public static int ID = R.id.imageOnlyEditor; + private final String LOGTAG = "ImageOnlyEditor"; + + public ImageOnlyEditor() { + super(ID); + } + + protected ImageOnlyEditor(int id) { + super(id); + } + + @Override + public void createEditor(Context context, FrameLayout frameLayout) { + super.createEditor(context, frameLayout); + mView = mImageShow = new ImageShow(context); + } + +} diff --git a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java new file mode 100644 index 000000000..43660d6a0 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java @@ -0,0 +1,113 @@ +/* + * 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.filtershow.cache.ImageLoader; + +import java.util.HashMap; +import java.util.Vector; + +public class BaseFiltersManager { + + private static final String LOGTAG = "BaseFiltersManager"; + private static HashMap<Class, ImageFilter> mFilters = new HashMap<Class, ImageFilter>(); + + protected BaseFiltersManager() { + Vector<ImageFilter> filters = new Vector<ImageFilter>(); + addFilters(filters); + for (ImageFilter filter : filters) { + mFilters.put(filter.getClass(), filter); + } + } + + protected void addFilters(Vector<ImageFilter> filters) { + filters.add(new ImageFilterTinyPlanet()); + filters.add(new ImageFilterRedEye()); + filters.add(new ImageFilterWBalance()); + filters.add(new ImageFilterExposure()); + filters.add(new ImageFilterVignette()); + filters.add(new ImageFilterContrast()); + filters.add(new ImageFilterShadows()); + filters.add(new ImageFilterVibrance()); + filters.add(new ImageFilterSharpen()); + filters.add(new ImageFilterCurves()); + filters.add(new ImageFilterDraw()); + filters.add(new ImageFilterHue()); + filters.add(new ImageFilterSaturated()); + filters.add(new ImageFilterBwFilter()); + filters.add(new ImageFilterNegative()); + filters.add(new ImageFilterEdge()); + filters.add(new ImageFilterKMeans()); + filters.add(new ImageFilterFx()); + filters.add(new ImageFilterBorder()); + filters.add(new ImageFilterParametricBorder()); + } + + public ImageFilter getFilter(Class c) { + return mFilters.get(c); + } + + public ImageFilter getFilterForRepresentation(FilterRepresentation representation) { + return mFilters.get(representation.getFilterClass()); + } + + public void addFilter(Class filterClass, ImageFilter filter) { + mFilters.put(filterClass, filter); + } + + public FilterRepresentation getRepresentation(Class c) { + ImageFilter filter = mFilters.get(c); + if (filter != null) { + return filter.getDefaultRepresentation(); + } + return null; + } + + public void addLooks(Vector<FilterRepresentation> representations) { + // subclass can add representations + } + + public void addEffects(Vector<FilterRepresentation> representations) { + representations.add(getRepresentation(ImageFilterTinyPlanet.class)); + representations.add(getRepresentation(ImageFilterRedEye.class)); + representations.add(getRepresentation(ImageFilterWBalance.class)); + representations.add(getRepresentation(ImageFilterExposure.class)); + representations.add(getRepresentation(ImageFilterVignette.class)); + representations.add(getRepresentation(ImageFilterContrast.class)); + representations.add(getRepresentation(ImageFilterShadows.class)); + representations.add(getRepresentation(ImageFilterVibrance.class)); + representations.add(getRepresentation(ImageFilterSharpen.class)); + representations.add(getRepresentation(ImageFilterCurves.class)); + representations.add(getRepresentation(ImageFilterDraw.class)); + representations.add(getRepresentation(ImageFilterHue.class)); + representations.add(getRepresentation(ImageFilterSaturated.class)); + representations.add(getRepresentation(ImageFilterBwFilter.class)); + representations.add(getRepresentation(ImageFilterNegative.class)); + representations.add(getRepresentation(ImageFilterEdge.class)); + representations.add(getRepresentation(ImageFilterKMeans.class)); + } + + public void resetBitmapsRS() { + for (Class c : mFilters.keySet()) { + ImageFilter filter = mFilters.get(c); + if (filter instanceof ImageFilterRS) { + ImageFilterRS filterRS = (ImageFilterRS) filter; + filterRS.resetBitmap(); + } + } + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/FilterBasicRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterBasicRepresentation.java new file mode 100644 index 000000000..2410ebe72 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/FilterBasicRepresentation.java @@ -0,0 +1,128 @@ +/* + * 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.app.Log; + +public class FilterBasicRepresentation extends FilterRepresentation { + private static final String LOGTAG = "FilterBasicRepresentation"; + private int mMinimum; + private int mValue; + private int mMaximum; + private int mDefaultValue; + private int mPreviewValue; + + public FilterBasicRepresentation(String name, int minimum, int value, int maximum) { + super(name); + mMinimum = minimum; + mMaximum = maximum; + setValue(value); + } + + public String toString() { + return getName() + " : " + mMinimum + " < " + mValue + " < " + mMaximum; + } + + @Override + public FilterRepresentation clone() throws CloneNotSupportedException { + FilterBasicRepresentation representation = (FilterBasicRepresentation) super.clone(); + representation.setMinimum(getMinimum()); + representation.setMaximum(getMaximum()); + representation.setValue(getValue()); + Log.v(LOGTAG, "cloning from <" + this + "> to <" + representation + ">"); + return representation; + } + + public void useParametersFrom(FilterRepresentation a) { + if (a instanceof FilterBasicRepresentation) { + FilterBasicRepresentation representation = (FilterBasicRepresentation) a; + setMinimum(representation.getMinimum()); + setMaximum(representation.getMaximum()); + setValue(representation.getValue()); + setDefaultValue(representation.getDefaultValue()); + setPreviewValue(representation.getPreviewValue()); + } + } + + @Override + public boolean equals(FilterRepresentation representation) { + if (!super.equals(representation)) { + return false; + } + if (representation instanceof FilterBasicRepresentation) { + FilterBasicRepresentation basic = (FilterBasicRepresentation) representation; + if (basic.mMinimum == mMinimum + && basic.mMaximum == mMaximum + && basic.mValue == mValue + && basic.mDefaultValue == mDefaultValue + && basic.mPreviewValue == mPreviewValue) { + return true; + } + } + return false; + } + + public int getMinimum() { + return mMinimum; + } + + public void setMinimum(int minimum) { + mMinimum = minimum; + } + + public int getValue() { + return mValue; + } + + public void setValue(int value) { + mValue = value; + if (mValue < mMinimum) { + mValue = mMinimum; + } + if (mValue > mMaximum) { + mValue = mMaximum; + } + } + + public int getMaximum() { + return mMaximum; + } + + public void setMaximum(int maximum) { + mMaximum = maximum; + } + + public void setDefaultValue(int defaultValue) { + mDefaultValue = defaultValue; + } + + public int getDefaultValue() { + return mDefaultValue; + } + + public int getPreviewValue() { + return mPreviewValue; + } + + public void setPreviewValue(int previewValue) { + mPreviewValue = previewValue; + } + + public String getStateRepresentation() { + return "" + getValue(); + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/FilterColorBorderRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterColorBorderRepresentation.java new file mode 100644 index 000000000..b2664a30f --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/FilterColorBorderRepresentation.java @@ -0,0 +1,113 @@ +/* + * 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 com.android.gallery3d.filtershow.editors.ImageOnlyEditor; + +public class FilterColorBorderRepresentation extends FilterRepresentation { + private int mColor; + private int mBorderSize; + private int mBorderRadius; + + public FilterColorBorderRepresentation(int color, int size, int radius) { + super("ColorBorder"); + mColor = color; + mBorderSize = size; + mBorderRadius = radius; + setFilterClass(ImageFilterParametricBorder.class); + setPriority(FilterRepresentation.TYPE_BORDER); + setTextId(R.string.borders); + setEditorId(ImageOnlyEditor.ID); + setShowEditingControls(false); + setShowParameterValue(false); + setShowUtilityPanel(false); + } + + public String toString() { + return "FilterBorder: " + getName(); + } + + @Override + public FilterRepresentation clone() throws CloneNotSupportedException { + FilterColorBorderRepresentation representation = (FilterColorBorderRepresentation) super.clone(); + representation.setName(getName()); + representation.setColor(getColor()); + representation.setBorderSize(getBorderSize()); + representation.setBorderRadius(getBorderRadius()); + return representation; + } + + public void useParametersFrom(FilterRepresentation a) { + if (a instanceof FilterColorBorderRepresentation) { + FilterColorBorderRepresentation representation = (FilterColorBorderRepresentation) a; + setName(representation.getName()); + setColor(representation.getColor()); + setBorderSize(representation.getBorderSize()); + setBorderRadius(representation.getBorderRadius()); + } + } + + @Override + public boolean equals(FilterRepresentation representation) { + if (!super.equals(representation)) { + return false; + } + if (representation instanceof FilterColorBorderRepresentation) { + FilterColorBorderRepresentation border = (FilterColorBorderRepresentation) representation; + if (border.mColor == mColor + && border.mBorderSize == mBorderSize + && border.mBorderRadius == mBorderRadius) { + return true; + } + } + return false; + } + + public boolean allowsMultipleInstances() { + return true; + } + + @Override + public int getTextId() { + return R.string.borders; + } + + public int getColor() { + return mColor; + } + + public void setColor(int color) { + mColor = color; + } + + public int getBorderSize() { + return mBorderSize; + } + + public void setBorderSize(int borderSize) { + mBorderSize = borderSize; + } + + public int getBorderRadius() { + return mBorderRadius; + } + + public void setBorderRadius(int borderRadius) { + mBorderRadius = borderRadius; + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/FilterCurvesRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterCurvesRepresentation.java new file mode 100644 index 000000000..6c831708e --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/FilterCurvesRepresentation.java @@ -0,0 +1,54 @@ +package com.android.gallery3d.filtershow.filters; + +import com.android.gallery3d.R; +import com.android.gallery3d.filtershow.ui.Spline; + +/** + * TODO: Insert description here. (generated by hoford) + */ +public class FilterCurvesRepresentation extends FilterRepresentation { + private Spline[] mSplines = new Spline[4]; + + public FilterCurvesRepresentation() { + super("Curves"); + setFilterClass(ImageFilterCurves.class); + setTextId(R.string.curvesRGB); + setButtonId(R.id.curvesButtonRGB); + setOverlayId(R.drawable.filtershow_button_colors_curve); + setEditorId(R.id.imageCurves); + setShowEditingControls(false); + setShowParameterValue(false); + setShowUtilityPanel(true); + for (int i = 0; i < mSplines.length; i++) { + mSplines[i] = new Spline(); + mSplines[i].reset(); + } + } + + public boolean isNil() { + for (int i = 0; i < 4; i++) { + if (getSpline(i) != null && !getSpline(i).isOriginal()) { + return false; + } + } + return true; + } + + public void reset() { + Spline spline = new Spline(); + + spline.addPoint(0.0f, 1.0f); + spline.addPoint(1.0f, 0.0f); + + for (int i = 0; i < 4; i++) { + mSplines[i] = new Spline(spline); + } + } + + public void setSpline(int splineIndex, Spline s) { + mSplines[splineIndex] = s; + } + public Spline getSpline(int splineIndex) { + return mSplines[splineIndex]; + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/FilterDirectRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterDirectRepresentation.java new file mode 100644 index 000000000..3807ee1f5 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/FilterDirectRepresentation.java @@ -0,0 +1,29 @@ +/* + * 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; + +public class FilterDirectRepresentation extends FilterRepresentation { + + public FilterDirectRepresentation(String name) { + super(name); + } + + public boolean isNil() { + return true; + } + +} diff --git a/src/com/android/gallery3d/filtershow/filters/FilterDrawRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterDrawRepresentation.java new file mode 100644 index 000000000..e41f0a622 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/FilterDrawRepresentation.java @@ -0,0 +1,158 @@ +/* + * 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 android.graphics.Path; +import android.util.Log; + +import com.android.gallery3d.R; +import com.android.gallery3d.filtershow.editors.EditorDraw; + +import java.util.Vector; + +public class FilterDrawRepresentation extends FilterRepresentation { + private static final String LOGTAG = "FilterDrawRepresentation"; + + public static class StrokeData implements Cloneable { + public byte mType; + public Path mPath; + public float mRadius; + public int mColor; + public int noPoints = 0; + @Override + public String toString() { + return "stroke(" + mType + ", path(" + (mPath) + "), " + mRadius + " , " + + Integer.toHexString(mColor) + ")"; + } + @Override + public StrokeData clone() throws CloneNotSupportedException { + return (StrokeData) super.clone(); + } + } + + private Vector<StrokeData> mDrawing = new Vector<StrokeData>(); + private StrokeData mCurrent; // used in the currently drawing style + + public FilterDrawRepresentation() { + super("Draw"); + setFilterClass(ImageFilterDraw.class); + setPriority(FilterRepresentation.TYPE_VIGNETTE); + setTextId(R.string.imageDraw); + setButtonId(R.id.drawOnImageButton); + setEditorId(EditorDraw.ID); + } + + @Override + public String toString() { + return getName() + " : strokes=" + mDrawing.size() + + ((mCurrent == null) ? " no current " + : ("draw=" + mCurrent.mType + " " + mCurrent.noPoints)); + } + + public Vector<StrokeData> getDrawing() { + return mDrawing; + } + + public StrokeData getCurrentDrawing() { + return mCurrent; + } + + @Override + public FilterRepresentation clone() throws CloneNotSupportedException { + FilterDrawRepresentation representation = (FilterDrawRepresentation) super.clone(); + return representation; + } + + @Override + public boolean isNil() { + return getDrawing().isEmpty(); + } + + @Override + public void useParametersFrom(FilterRepresentation a) { + if (a instanceof FilterDrawRepresentation) { + FilterDrawRepresentation representation = (FilterDrawRepresentation) a; + try { + if (representation.mCurrent != null) { + mCurrent = (StrokeData) representation.mCurrent.clone(); + } else { + mCurrent = null; + } + if (representation.mDrawing != null) { + mDrawing = (Vector<StrokeData>) representation.mDrawing.clone(); + } else { + mDrawing = null; + } + + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + } else { + Log.v(LOGTAG, "cannot use parameters from " + a); + } + } + + @Override + public boolean equals(FilterRepresentation representation) { + if (!super.equals(representation)) { + return false; + } + if (representation instanceof FilterDrawRepresentation) { + FilterDrawRepresentation fdRep = (FilterDrawRepresentation) representation; + if (fdRep.mDrawing.size() != mDrawing.size()) + return false; + if (fdRep.mCurrent == null && mCurrent.mPath == null) { + return true; + } + if (fdRep.mCurrent != null && mCurrent.mPath != null) { + if (fdRep.mCurrent.noPoints == mCurrent.noPoints) { + return true; + } + return false; + } + } + return false; + } + + public void startNewSection(byte type, int color, float size, float x, float y) { + mCurrent = new StrokeData(); + mCurrent.mColor = color; + mCurrent.mRadius = size; + mCurrent.mType = type; + mCurrent.mPath = new Path(); + mCurrent.mPath.moveTo(x, y); + mCurrent.noPoints = 0; + } + + public void addPoint(float x, float y) { + mCurrent.noPoints++; + mCurrent.mPath.lineTo(x, y); + } + + public void endSection(float x, float y) { + mCurrent.mPath.lineTo(x, y); + mCurrent.noPoints++; + mDrawing.add(mCurrent); + 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 new file mode 100644 index 000000000..859bf327c --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/FilterFxRepresentation.java @@ -0,0 +1,108 @@ +/* + * 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 android.graphics.Bitmap; +import com.android.gallery3d.filtershow.editors.ImageOnlyEditor; + +public class FilterFxRepresentation extends FilterRepresentation { + private Bitmap mFxBitmap = null; + // TODO: When implementing serialization, we should find a unique way of + // specifying bitmaps / names (the resource IDs being random) + private int mBitmapResource = 0; + private int mNameResource = 0; + + public FilterFxRepresentation(String name, int bitmapResource, int nameResource) { + super(name); + mBitmapResource = bitmapResource; + mNameResource = nameResource; + setFilterClass(ImageFilterFx.class); + setPriority(FilterRepresentation.TYPE_FX); + setTextId(nameResource); + setEditorId(ImageOnlyEditor.ID); + setShowEditingControls(false); + setShowParameterValue(false); + setShowUtilityPanel(false); + } + + public String toString() { + return "FilterFx: " + getName(); + } + + @Override + public FilterRepresentation clone() throws CloneNotSupportedException { + FilterFxRepresentation representation = (FilterFxRepresentation) super.clone(); + representation.setName(getName()); + representation.setBitmapResource(getBitmapResource()); + representation.setNameResource(getNameResource()); + representation.setFxBitmap(getFxBitmap()); + return representation; + } + + public void useParametersFrom(FilterRepresentation a) { + if (a instanceof FilterFxRepresentation) { + FilterFxRepresentation representation = (FilterFxRepresentation) a; + setName(representation.getName()); + setBitmapResource(representation.getBitmapResource()); + setNameResource(representation.getNameResource()); + setFxBitmap(representation.getFxBitmap()); + } + } + + @Override + public boolean equals(FilterRepresentation representation) { + if (!super.equals(representation)) { + return false; + } + if (representation instanceof FilterFxRepresentation) { + FilterFxRepresentation fx = (FilterFxRepresentation) representation; + if (fx.mNameResource == mNameResource + && fx.mBitmapResource == mBitmapResource) { + return true; + } + } + return false; + } + + public boolean allowsMultipleInstances() { + return true; + } + + public Bitmap getFxBitmap() { + return mFxBitmap; + } + + public void setFxBitmap(Bitmap fxBitmap) { + mFxBitmap = fxBitmap; + } + + public int getNameResource() { + return mNameResource; + } + + public void setNameResource(int nameResource) { + mNameResource = nameResource; + } + + public int getBitmapResource() { + return mBitmapResource; + } + + public void setBitmapResource(int bitmapResource) { + mBitmapResource = bitmapResource; + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/FilterImageBorderRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterImageBorderRepresentation.java new file mode 100644 index 000000000..99c809148 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/FilterImageBorderRepresentation.java @@ -0,0 +1,91 @@ +/* + * 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 com.android.gallery3d.filtershow.editors.ImageOnlyEditor; + +public class FilterImageBorderRepresentation extends FilterRepresentation { + private int mDrawableResource = 0; + + public FilterImageBorderRepresentation(int drawableResource) { + super("ImageBorder"); + mDrawableResource = drawableResource; + setFilterClass(ImageFilterBorder.class); + setPriority(FilterRepresentation.TYPE_BORDER); + setTextId(R.string.borders); + setEditorId(ImageOnlyEditor.ID); + setShowEditingControls(false); + setShowParameterValue(false); + setShowUtilityPanel(false); + // load the drawable at init as we are in a background thread + // (see FilterShowActivity's LoadBordersTask) + ImageFilterBorder filter = (ImageFilterBorder) FiltersManager.getManager().getFilter(getFilterClass()); + filter.getDrawable(getDrawableResource()); + } + + public String toString() { + return "FilterBorder: " + getName(); + } + + @Override + public FilterRepresentation clone() throws CloneNotSupportedException { + FilterImageBorderRepresentation representation = (FilterImageBorderRepresentation) super.clone(); + representation.setName(getName()); + representation.setDrawableResource(getDrawableResource()); + return representation; + } + + public void useParametersFrom(FilterRepresentation a) { + if (a instanceof FilterImageBorderRepresentation) { + FilterImageBorderRepresentation representation = (FilterImageBorderRepresentation) a; + setName(representation.getName()); + setDrawableResource(representation.getDrawableResource()); + } + } + + @Override + public boolean equals(FilterRepresentation representation) { + if (!super.equals(representation)) { + return false; + } + if (representation instanceof FilterImageBorderRepresentation) { + FilterImageBorderRepresentation border = (FilterImageBorderRepresentation) representation; + if (border.mDrawableResource == mDrawableResource) { + return true; + } + } + return false; + } + + @Override + public int getTextId() { + return R.string.borders; + } + + public boolean allowsMultipleInstances() { + return true; + } + + public int getDrawableResource() { + return mDrawableResource; + } + + public void setDrawableResource(int drawableResource) { + mDrawableResource = drawableResource; + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/FilterPoint.java b/src/com/android/gallery3d/filtershow/filters/FilterPoint.java new file mode 100644 index 000000000..4520717a1 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/FilterPoint.java @@ -0,0 +1,21 @@ +/* + * 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; + +public interface FilterPoint { + +} diff --git a/src/com/android/gallery3d/filtershow/filters/FilterPointRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterPointRepresentation.java new file mode 100644 index 000000000..fc01650ae --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/FilterPointRepresentation.java @@ -0,0 +1,87 @@ +/* + * 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 java.util.Vector; + +public abstract class FilterPointRepresentation extends FilterRepresentation { + private static final String LOGTAG = "FilterPointRepresentation"; + private Vector<FilterPoint> mCandidates = new Vector<FilterPoint>(); + + public FilterPointRepresentation(String type, int textid, int editorID) { + super(type); + setFilterClass(ImageFilterRedEye.class); + setPriority(FilterRepresentation.TYPE_NORMAL); + setTextId(textid); + setEditorId(editorID); + } + + @Override + public FilterRepresentation clone() throws CloneNotSupportedException { + FilterPointRepresentation representation = (FilterPointRepresentation) super + .clone(); + representation.mCandidates = (Vector<FilterPoint>) mCandidates.clone(); + return representation; + } + + public boolean hasCandidates() { + return mCandidates != null; + } + + public Vector<FilterPoint> getCandidates() { + return mCandidates; + } + + @Override + public boolean isNil() { + if (getCandidates() != null && getCandidates().size() > 0) { + return false; + } + return true; + } + + public Object getCandidate(int index) { + return this.mCandidates.get(index); + } + + public void addCandidate(FilterPoint c) { + this.mCandidates.add(c); + } + + @Override + public void useParametersFrom(FilterRepresentation a) { + if (a instanceof FilterPointRepresentation) { + FilterPointRepresentation representation = (FilterPointRepresentation) a; + mCandidates.clear(); + for (FilterPoint redEyeCandidate : representation.mCandidates) { + mCandidates.add(redEyeCandidate); + } + } + } + + public void removeCandidate(RedEyeCandidate c) { + this.mCandidates.remove(c); + } + + public void clearCandidates() { + this.mCandidates.clear(); + } + + public int getNumberOfCandidates() { + return mCandidates.size(); + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/FilterRedEyeRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterRedEyeRepresentation.java new file mode 100644 index 000000000..70d016f69 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/FilterRedEyeRepresentation.java @@ -0,0 +1,53 @@ +/* + * 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 android.graphics.RectF; +import android.util.Log; + +import com.android.gallery3d.R; +import com.android.gallery3d.filtershow.editors.EditorRedEye; + +import java.util.Vector; + +public class FilterRedEyeRepresentation extends FilterPointRepresentation { + private static final String LOGTAG = "FilterRedEyeRepresentation"; + + public FilterRedEyeRepresentation() { + super("RedEye",R.string.redeye,EditorRedEye.ID); + setFilterClass(ImageFilterRedEye.class); + setOverlayId(R.drawable.photoeditor_effect_redeye); + } + + public void addRect(RectF rect, RectF bounds) { + Vector<RedEyeCandidate> intersects = new Vector<RedEyeCandidate>(); + for (int i = 0; i < getCandidates().size(); i++) { + RedEyeCandidate r = (RedEyeCandidate) getCandidate(i); + if (r.intersect(rect)) { + intersects.add(r); + } + } + for (int i = 0; i < intersects.size(); i++) { + RedEyeCandidate r = intersects.elementAt(i); + rect.union(r.mRect); + bounds.union(r.mBounds); + removeCandidate(r); + } + addCandidate(new RedEyeCandidate(rect, bounds)); + } + +} diff --git a/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java new file mode 100644 index 000000000..513cdcdef --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java @@ -0,0 +1,209 @@ +/* + * 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.app.Log; +import com.android.gallery3d.filtershow.editors.BasicEditor; +import com.android.gallery3d.filtershow.presets.ImagePreset; + +public class FilterRepresentation implements Cloneable { + private static final String LOGTAG = "FilterRepresentation"; + private String mName; + private int mPriority = TYPE_NORMAL; + private Class mFilterClass; + private int mTextId = 0; + private int mEditorId = BasicEditor.ID; + private int mButtonId = 0; + private int mOverlayId = 0; + private boolean mShowEditingControls = true; + private boolean mShowParameterValue = true; + private boolean mShowUtilityPanel = true; + + public static final byte TYPE_BORDER = 1; + public static final byte TYPE_FX = 2; + public static final byte TYPE_WBALANCE = 3; + public static final byte TYPE_VIGNETTE = 4; + public static final byte TYPE_NORMAL = 5; + public static final byte TYPE_TINYPLANET = 6; + + public FilterRepresentation mTempRepresentation = null; + + public FilterRepresentation(String name) { + mName = name; + } + + @Override + public FilterRepresentation clone() throws CloneNotSupportedException { + FilterRepresentation representation = (FilterRepresentation) super.clone(); + representation.setName(getName()); + representation.setPriority(getPriority()); + representation.setFilterClass(getFilterClass()); + representation.setTextId(getTextId()); + representation.setEditorId(getEditorId()); + representation.setButtonId(getButtonId()); + representation.setOverlayId(getOverlayId()); + representation.setShowEditingControls(showEditingControls()); + representation.setShowParameterValue(showParameterValue()); + representation.setShowUtilityPanel(showUtilityPanel()); + Log.v(LOGTAG, "cloning from <" + this + "> to <" + representation + ">"); + return representation; + } + + public boolean equals(FilterRepresentation representation) { + if (representation == null) { + return false; + } + if (representation.mFilterClass == representation.mFilterClass + && representation.mName.equalsIgnoreCase(mName) + && representation.mPriority == mPriority + && representation.mTextId == mTextId + && representation.mEditorId == mEditorId + && representation.mButtonId == mButtonId + && representation.mOverlayId == mOverlayId + && representation.mShowEditingControls == mShowEditingControls + && representation.mShowParameterValue == mShowParameterValue + && representation.mShowUtilityPanel == mShowUtilityPanel) { + return true; + } + return false; + } + + public String toString() { + return mName; + } + + public void setName(String name) { + mName = name; + } + + public String getName() { + return mName; + } + + public void setPriority(int priority) { + mPriority = priority; + } + + public int getPriority() { + return mPriority; + } + + public boolean isNil() { + return false; + } + + public void useParametersFrom(FilterRepresentation a) { + } + + public synchronized void updateTempParametersFrom(FilterRepresentation representation) { + if (mTempRepresentation == null) { + try { + mTempRepresentation = representation.clone(); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + } else { + mTempRepresentation.useParametersFrom(representation); + } + } + + public synchronized void synchronizeRepresentation() { + if (mTempRepresentation != null) { + useParametersFrom(mTempRepresentation); + } + } + + public boolean allowsMultipleInstances() { + return false; + } + + public Class getFilterClass() { + return mFilterClass; + } + + public void setFilterClass(Class filterClass) { + mFilterClass = filterClass; + } + + public boolean same(FilterRepresentation b) { + if (b == null) { + return false; + } + return getFilterClass() == b.getFilterClass(); + } + + public int getTextId() { + return mTextId; + } + + public void setTextId(int textId) { + mTextId = textId; + } + + public int getButtonId() { + return mButtonId; + } + + public void setButtonId(int buttonId) { + mButtonId = buttonId; + } + + public int getOverlayId() { + return mOverlayId; + } + + public void setOverlayId(int overlayId) { + mOverlayId = overlayId; + } + + public int getEditorId() { + return mEditorId; + } + + public void setEditorId(int editorId) { + mEditorId = editorId; + } + + public boolean showEditingControls() { + return mShowEditingControls; + } + + public void setShowEditingControls(boolean showEditingControls) { + mShowEditingControls = showEditingControls; + } + + public boolean showParameterValue() { + return mShowParameterValue; + } + + public void setShowParameterValue(boolean showParameterValue) { + mShowParameterValue = showParameterValue; + } + + public boolean showUtilityPanel() { + return mShowUtilityPanel; + } + + public void setShowUtilityPanel(boolean showUtilityPanel) { + mShowUtilityPanel = showUtilityPanel; + } + + public String getStateRepresentation() { + return ""; + } + +} diff --git a/src/com/android/gallery3d/filtershow/filters/FilterTinyPlanetRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterTinyPlanetRepresentation.java new file mode 100644 index 000000000..7b69ce9e0 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/FilterTinyPlanetRepresentation.java @@ -0,0 +1,65 @@ +/* + * 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.EditorTinyPlanet; + +public class FilterTinyPlanetRepresentation extends FilterBasicRepresentation { + private static final String LOGTAG = "FilterTinyPlanetRepresentation"; + private float mAngle = 0; + + public FilterTinyPlanetRepresentation() { + super("TinyPlanet", 0, 50, 100); + setShowParameterValue(true); + setFilterClass(ImageFilterTinyPlanet.class); + setPriority(FilterRepresentation.TYPE_TINYPLANET); + setTextId(R.string.tinyplanet); + setButtonId(R.id.tinyplanetButton); + setEditorId(EditorTinyPlanet.ID); + } + + @Override + public FilterRepresentation clone() throws CloneNotSupportedException { + FilterTinyPlanetRepresentation representation = (FilterTinyPlanetRepresentation) super + .clone(); + representation.mAngle = mAngle; + representation.setZoom(getZoom()); + return representation; + } + + public void setAngle(float angle) { + mAngle = angle; + } + + public float getAngle() { + return mAngle; + } + + public int getZoom() { + return getValue(); + } + + public void setZoom(int zoom) { + setValue(zoom); + } + + public boolean isNil() { + // TinyPlanet always has an effect + return false; + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilter.java b/src/com/android/gallery3d/filtershow/filters/ImageFilter.java index a261031c3..614c6a01d 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilter.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilter.java @@ -17,94 +17,19 @@ package com.android.gallery3d.filtershow.filters; import android.graphics.Bitmap; +import android.graphics.Matrix; import com.android.gallery3d.R; import com.android.gallery3d.filtershow.editors.BasicEditor; +import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; import com.android.gallery3d.filtershow.presets.ImagePreset; -public class ImageFilter implements Cloneable { +public abstract class ImageFilter implements Cloneable { - public static int DEFAULT_MAX_PARAMETER = 100; - public static int DEFAULT_MIN_PARAMETER = -100; - public static int DEFAULT_INITIAL_PARAMETER = 0; - - protected int mMaxParameter = DEFAULT_MAX_PARAMETER; - protected int mMinParameter = DEFAULT_MIN_PARAMETER; - protected int mPreviewParameter = mMaxParameter; - protected int mDefaultParameter = DEFAULT_INITIAL_PARAMETER; - protected int mParameter = DEFAULT_INITIAL_PARAMETER; private ImagePreset mImagePreset; protected String mName = "Original"; private final String LOGTAG = "ImageFilter"; - public static final byte TYPE_BORDER = 1; - public static final byte TYPE_FX = 2; - public static final byte TYPE_WBALANCE = 3; - public static final byte TYPE_VIGNETTE = 4; - public static final byte TYPE_NORMAL = 5; - public static final byte TYPE_TINYPLANET = 6; - private byte filterType = TYPE_NORMAL; - - public byte getFilterType() { - return filterType; - } - - protected void setFilterType(byte type) { - filterType = type; - } - - public int getButtonId() { - return 0; - } - - public int getTextId() { - return 0; - } - - public int getOverlayBitmaps() { - return 0; - } - - public int getEditingViewId() { - return BasicEditor.ID; - } - - public boolean showEditingControls() { - return true; - } - - public boolean showParameterValue() { - return true; - } - - public boolean showUtilityPanel() { - return true; - } - - @Override - public ImageFilter clone() throws CloneNotSupportedException { - ImageFilter filter = (ImageFilter) super.clone(); - filter.setName(getName()); - filter.setParameter(getParameter()); - filter.setFilterType(filterType); - filter.mMaxParameter = mMaxParameter; - filter.mMinParameter = mMinParameter; - filter.mImagePreset = mImagePreset; - filter.mDefaultParameter = mDefaultParameter; - filter.mPreviewParameter = mPreviewParameter; - return filter; - } - - public void reset() { - setParameter(mDefaultParameter); - } - - public boolean isNil() { - if (mParameter == mDefaultParameter) { - return true; - } - return false; - } public void setName(String name) { mName = name; @@ -114,7 +39,7 @@ public class ImageFilter implements Cloneable { return mName; } - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { // do nothing here, subclasses will implement filtering here return bitmap; } @@ -123,86 +48,33 @@ public class ImageFilter implements Cloneable { * Called on small bitmaps to create button icons for each filter. * Override this to provide filter-specific button icons. */ - public Bitmap iconApply(Bitmap bitmap, float scaleFactor, boolean highQuality) { - int param = getParameter(); - setParameter(getPreviewParameter()); - bitmap = apply(bitmap, scaleFactor, highQuality); - setParameter(param); - return bitmap; - } - - public int getParameter() { - return mParameter; - } - - public void setParameter(int value) { - mParameter = value; - } - - /** - * The maximum allowed value (inclusive) - * @return maximum value allowed as input to this filter - */ - public int getMaxParameter() { - return mMaxParameter; - } - - /** - * The parameter value to be used in previews. - * @return parameter value to be used to preview the filter - */ - public int getPreviewParameter() { - return mPreviewParameter; - } - - /** - * The minimum allowed value (inclusive) - * @return minimum value allowed as input to this filter - */ - public int getMinParameter() { - return mMinParameter; - } - - /** - * Returns the default value returned by this filter. - * @return default value - */ - public int getDefaultParameter() { - return mDefaultParameter; + public Bitmap iconApply(Bitmap bitmap, float scaleFactor, int quality) { + return apply(bitmap, scaleFactor, quality); } public ImagePreset getImagePreset() { return mImagePreset; } - public void setImagePreset(ImagePreset mPreset) { - this.mImagePreset = mPreset; - } - - public boolean equals(ImageFilter filter) { - if (!same(filter)) { - return false; - } - if (mParameter != filter.mParameter) { - return false; - } - return true; + public void setImagePreset(ImagePreset imagePreset) { + mImagePreset = imagePreset; } - public boolean same(ImageFilter filter) { - if (filter == null) { - return false; - } - if (!filter.getName().equalsIgnoreCase(getName())) { - return false; - } - return true; - } + public abstract void useRepresentation(FilterRepresentation representation); native protected void nativeApplyGradientFilter(Bitmap bitmap, int w, int h, int[] redGradient, int[] greenGradient, int[] blueGradient); - public void useFilter(ImageFilter a) { - setParameter(a.getParameter()); + public FilterRepresentation getDefaultRepresentation() { + return null; + } + + protected Matrix getOriginalToScreenMatrix(int w, int h) { + GeometryMetadata geo = getImagePreset().mGeoData; + Matrix originalToScreen = geo.getOriginalToScreen(true, + getImagePreset().getImageLoader().getOriginalBounds().width(), + getImagePreset().getImageLoader().getOriginalBounds().height(), + w, h); + return originalToScreen; } } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterBorder.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterBorder.java index 4fe308294..70e7f2220 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterBorder.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterBorder.java @@ -16,78 +16,38 @@ package com.android.gallery3d.filtershow.filters; +import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import com.android.gallery3d.R; +import com.android.gallery3d.filtershow.editors.ImageOnlyEditor; + +import java.util.HashMap; public class ImageFilterBorder extends ImageFilter { private static final float NINEPATCH_ICON_SCALING = 10; private static final float BITMAP_ICON_SCALING = 1 / 3.0f; - Drawable mNinePatch = null; + private FilterImageBorderRepresentation mParameters = null; + private Resources mResources = null; - @Override - public ImageFilter clone() throws CloneNotSupportedException { - ImageFilterBorder filter = (ImageFilterBorder) super.clone(); - filter.setDrawable(mNinePatch); - return filter; - } + private HashMap<Integer, Drawable> mDrawables = new HashMap<Integer, Drawable>(); - public ImageFilterBorder(Drawable ninePatch) { - setFilterType(TYPE_BORDER); + public ImageFilterBorder() { mName = "Border"; - mNinePatch = ninePatch; - } - - @Override - public boolean isNil() { - if (mNinePatch == null) { - return true; - } - return false; - } - - @Override - public int getTextId() { - return R.string.borders; - } - - @Override - public boolean showParameterValue() { - return false; - } - - @Override - public boolean showEditingControls() { - return false; - } - - @Override - public boolean showUtilityPanel() { - return false; } - @Override - public boolean same(ImageFilter filter) { - boolean isBorderFilter = super.same(filter); - if (!isBorderFilter) { - return false; - } - if (!(filter instanceof ImageFilterBorder)) { - return false; - } - ImageFilterBorder borderFilter = (ImageFilterBorder) filter; - if (mNinePatch != borderFilter.mNinePatch) { - return false; - } - return true; + public void useRepresentation(FilterRepresentation representation) { + FilterImageBorderRepresentation parameters = (FilterImageBorderRepresentation) representation; + mParameters = parameters; } - public void setDrawable(Drawable ninePatch) { - // TODO: for now we only use nine patch - mNinePatch = ninePatch; + public FilterImageBorderRepresentation getParameters() { + return mParameters; } public Bitmap applyHelper(Bitmap bitmap, float scale1, float scale2 ) { @@ -96,14 +56,15 @@ public class ImageFilterBorder extends ImageFilter { Rect bounds = new Rect(0, 0, (int) (w * scale1), (int) (h * scale1)); Canvas canvas = new Canvas(bitmap); canvas.scale(scale2, scale2); - mNinePatch.setBounds(bounds); - mNinePatch.draw(canvas); + Drawable drawable = getDrawable(getParameters().getDrawableResource()); + drawable.setBounds(bounds); + drawable.draw(canvas); return bitmap; } @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { - if (mNinePatch == null) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { + if (getParameters() == null || getParameters().getDrawableResource() == 0) { return bitmap; } float scale2 = scaleFactor * 2.0f; @@ -112,12 +73,29 @@ public class ImageFilterBorder extends ImageFilter { } @Override - public Bitmap iconApply(Bitmap bitmap, float scaleFactor, boolean highQuality) { - if (mNinePatch == null) { + public Bitmap iconApply(Bitmap bitmap, float scaleFactor, int quality) { + if (getParameters() == null || getParameters().getDrawableResource() == 0) { return bitmap; } float scale1 = NINEPATCH_ICON_SCALING; float scale2 = BITMAP_ICON_SCALING; return applyHelper(bitmap, scale1, scale2); } + + public void setResources(Resources resources) { + if (mResources != resources) { + mResources = resources; + mDrawables.clear(); + } + } + + public Drawable getDrawable(int rsc) { + Drawable drawable = mDrawables.get(rsc); + if (drawable == null && mResources != null && rsc != 0) { + drawable = new BitmapDrawable(mResources, BitmapFactory.decodeResource(mResources, rsc)); + mDrawables.put(rsc, drawable); + } + return drawable; + } + } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java index 1bb5c76ac..c92ac012d 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java @@ -22,38 +22,34 @@ import android.graphics.Bitmap; import android.graphics.Color; -public class ImageFilterBwFilter extends ImageFilter { +public class ImageFilterBwFilter extends SimpleImageFilter { public ImageFilterBwFilter() { mName = "BW Filter"; - mMaxParameter = 180; - mMinParameter = -180; } - @Override - public int getButtonId() { - return R.id.bwfilterButton; - } - - @Override - public int getTextId() { - return R.string.bwfilter; - } - - @Override - public ImageFilter clone() throws CloneNotSupportedException { - ImageFilterBwFilter filter = (ImageFilterBwFilter) super.clone(); - return filter; + public FilterRepresentation getDefaultRepresentation() { + FilterBasicRepresentation representation = (FilterBasicRepresentation) super.getDefaultRepresentation(); + representation.setName("BW Filter"); + representation.setFilterClass(ImageFilterBwFilter.class); + representation.setMaximum(180); + representation.setMinimum(-180); + representation.setTextId(R.string.bwfilter); + representation.setButtonId(R.id.bwfilterButton); + return representation; } native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, int r, int g, int b); @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { + if (getParameters() == null) { + return bitmap; + } int w = bitmap.getWidth(); int h = bitmap.getHeight(); float[] hsv = new float[] { - 180 + mParameter, 1, 1 + 180 + getParameters().getValue(), 1, 1 }; int rgb = Color.HSVToColor(hsv); int r = 0xFF & (rgb >> 16); diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java index 70e3d8589..2f94e3d17 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java @@ -20,30 +20,36 @@ import com.android.gallery3d.R; import android.graphics.Bitmap; -public class ImageFilterContrast extends ImageFilter { +public class ImageFilterContrast extends SimpleImageFilter { public ImageFilterContrast() { mName = "Contrast"; } - @Override - public int getButtonId() { - return R.id.contrastButton; - } - - @Override - public int getTextId() { - return R.string.contrast; + public FilterRepresentation getDefaultRepresentation() { + FilterBasicRepresentation representation = + (FilterBasicRepresentation) super.getDefaultRepresentation(); + representation.setName("Contrast"); + representation.setFilterClass(ImageFilterContrast.class); + representation.setTextId(R.string.contrast); + representation.setButtonId(R.id.contrastButton); + + representation.setMinimum(-100); + representation.setMaximum(100); + representation.setDefaultValue(0); + return representation; } native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float strength); @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { + if (getParameters() == null) { + return bitmap; + } int w = bitmap.getWidth(); int h = bitmap.getHeight(); - float p = mParameter; - float value = p; + float value = getParameters().getValue(); nativeApplyFilter(bitmap, w, h, value); return bitmap; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java index b7e5c2ae6..aa4cf22e6 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterCurves.java @@ -25,82 +25,26 @@ import com.android.gallery3d.filtershow.ui.Spline; public class ImageFilterCurves extends ImageFilter { private static final String LOGTAG = "ImageFilterCurves"; - private Spline[] mSplines = new Spline[4]; - - public ImageFilterCurves() { - mName = "Curves"; - reset(); - } - - @Override - public int getButtonId() { - return R.id.curvesButtonRGB; - } - - @Override - public int getTextId() { - return R.string.curvesRGB; - } - - @Override - public int getOverlayBitmaps() { - return R.drawable.filtershow_button_colors_curve; - } - - @Override - public int getEditingViewId() { - return R.id.imageCurves; - } + FilterCurvesRepresentation mParameters = new FilterCurvesRepresentation(); @Override - public boolean showParameterValue() { - return false; + public FilterRepresentation getDefaultRepresentation() { + return new FilterCurvesRepresentation(); } @Override - public boolean equals(ImageFilter filter) { - return same(filter); + public void useRepresentation(FilterRepresentation representation) { + FilterCurvesRepresentation parameters = (FilterCurvesRepresentation) representation; + mParameters = parameters; } - @Override - public ImageFilter clone() throws CloneNotSupportedException { - ImageFilterCurves filter = (ImageFilterCurves) super.clone(); - filter.mSplines = new Spline[4]; - for (int i = 0; i < 4; i++) { - if (mSplines[i] != null) { - filter.setSpline(mSplines[i], i); - } - } - return filter; - } - - @Override - public boolean isNil() { - for (int i = 0; i < 4; i++) { - if (mSplines[i] != null && !mSplines[i].isOriginal()) { - return false; - } - } - return true; - } - - @Override - public boolean same(ImageFilter filter) { - boolean isCurveFilter = super.same(filter); - if (!isCurveFilter) { - return false; - } - ImageFilterCurves curve = (ImageFilterCurves) filter; - for (int i = 0; i < 4; i++) { - if (mSplines[i] != curve.mSplines[i]) { - return false; - } - } - return true; + public ImageFilterCurves() { + mName = "Curves"; + reset(); } public void populateArray(int[] array, int curveIndex) { - Spline spline = mSplines[curveIndex]; + Spline spline = mParameters.getSpline(curveIndex); if (spline == null) { return; } @@ -111,8 +55,8 @@ public class ImageFilterCurves extends ImageFilter { } @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { - if (!mSplines[Spline.RGB].isOriginal()) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { + if (!mParameters.getSpline(Spline.RGB).isOriginal()) { int[] rgbGradient = new int[256]; populateArray(rgbGradient, Spline.RGB); nativeApplyGradientFilter(bitmap, bitmap.getWidth(), bitmap.getHeight(), @@ -120,17 +64,17 @@ public class ImageFilterCurves extends ImageFilter { } int[] redGradient = null; - if (!mSplines[Spline.RED].isOriginal()) { + if (!mParameters.getSpline(Spline.RED).isOriginal()) { redGradient = new int[256]; populateArray(redGradient, Spline.RED); } int[] greenGradient = null; - if (!mSplines[Spline.GREEN].isOriginal()) { + if (!mParameters.getSpline(Spline.GREEN).isOriginal()) { greenGradient = new int[256]; populateArray(greenGradient, Spline.GREEN); } int[] blueGradient = null; - if (!mSplines[Spline.BLUE].isOriginal()) { + if (!mParameters.getSpline(Spline.BLUE).isOriginal()) { blueGradient = new int[256]; populateArray(blueGradient, Spline.BLUE); } @@ -141,14 +85,13 @@ public class ImageFilterCurves extends ImageFilter { } public void setSpline(Spline spline, int splineIndex) { - mSplines[splineIndex] = new Spline(spline); + mParameters.setSpline(splineIndex, new Spline(spline)); } public Spline getSpline(int splineIndex) { - return mSplines[splineIndex]; + return mParameters.getSpline(splineIndex); } - @Override public void reset() { Spline spline = new Spline(); @@ -156,16 +99,15 @@ public class ImageFilterCurves extends ImageFilter { spline.addPoint(1.0f, 0.0f); for (int i = 0; i < 4; i++) { - mSplines[i] = new Spline(spline); + mParameters.setSpline(i, new Spline(spline)); } } - @Override public void useFilter(ImageFilter a) { ImageFilterCurves c = (ImageFilterCurves) a; for (int i = 0; i < 4; i++) { - if (c.mSplines[i] != null) { - setSpline(c.mSplines[i], i); + if (c.mParameters.getSpline(i) != null) { + setSpline(c.mParameters.getSpline(i), i); } } } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterDownsample.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterDownsample.java index 784028a8d..906467344 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterDownsample.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterDownsample.java @@ -23,40 +23,37 @@ import android.graphics.Rect; import com.android.gallery3d.R; import com.android.gallery3d.filtershow.cache.ImageLoader; -public class ImageFilterDownsample extends ImageFilter { +public class ImageFilterDownsample extends SimpleImageFilter { private static final int ICON_DOWNSAMPLE_FRACTION = 8; private ImageLoader mImageLoader; public ImageFilterDownsample(ImageLoader loader) { mName = "Downsample"; - mMaxParameter = 100; - mMinParameter = 1; - mPreviewParameter = 3; - mDefaultParameter = 50; - mParameter = 50; mImageLoader = loader; } - @Override - public int getButtonId() { - return R.id.downsampleButton; - } - - @Override - public int getTextId() { - return R.string.downsample; + public FilterRepresentation getDefaultRepresentation() { + FilterBasicRepresentation representation = (FilterBasicRepresentation) super.getDefaultRepresentation(); + representation.setName("Downsample"); + representation.setFilterClass(ImageFilterDownsample.class); + representation.setMaximum(100); + representation.setMinimum(1); + representation.setValue(50); + representation.setDefaultValue(50); + representation.setPreviewValue(3); + representation.setTextId(R.string.downsample); + representation.setButtonId(R.id.downsampleButton); + return representation; } @Override - public boolean isNil() { - return false; - } - - @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { + if (getParameters() == null) { + return bitmap; + } int w = bitmap.getWidth(); int h = bitmap.getHeight(); - int p = mParameter; + int p = getParameters().getValue(); // size of original precached image Rect size = mImageLoader.getOriginalBounds(); @@ -82,7 +79,7 @@ public class ImageFilterDownsample extends ImageFilter { } @Override - public Bitmap iconApply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap iconApply(Bitmap bitmap, float scaleFactor, int quality) { int w = bitmap.getWidth(); int h = bitmap.getHeight(); Bitmap ret = Bitmap.createScaledBitmap(bitmap, w / ICON_DOWNSAMPLE_FRACTION, h diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterDraw.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterDraw.java index 3177d2473..6d7614ec9 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterDraw.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterDraw.java @@ -17,6 +17,7 @@ package com.android.gallery3d.filtershow.filters; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; @@ -24,463 +25,252 @@ import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Path; import android.graphics.PathMeasure; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; +import android.util.Log; import com.android.gallery3d.R; -import com.android.gallery3d.filtershow.editors.EditorDraw; +import com.android.gallery3d.filtershow.filters.FilterDrawRepresentation.StrokeData; +import com.android.gallery3d.filtershow.imageshow.MasterImage; +import com.android.gallery3d.filtershow.presets.ImagePreset; -import java.util.Arrays; +import java.util.Vector; public class ImageFilterDraw extends ImageFilter { private static final String LOGTAG = "ImageFilterDraw"; - public final static char SIMPLE_STYLE = 0; - public final static char BRUSH_STYLE = 1; + public final static byte SIMPLE_STYLE = 0; + public final static byte BRUSH_STYLE_SPATTER = 1; + public final static byte BRUSH_STYLE_MARKER = 2; + public final static int NUMBER_OF_STYLES = 3; Bitmap mOverlayBitmap; // this accelerates interaction int mCachedStrokes = -1; int mCurrentStyle = 0; - DrawStyle[] mDrawings = new DrawStyle[] { - new SimpleDraw(), new Brush() }; - public void setStyle(char style) { - mCurrentStyle = style; + FilterDrawRepresentation mParameters = new FilterDrawRepresentation(); + + public ImageFilterDraw() { + mName = "Image Draw"; } - public static interface DrawStyle { - public DrawStyle clone(); - public void setSize(float radius); - public void setColor(int color); - public void startStroke(float x, float y); - public void stroke(float x, float y); - public void endStroke(float x, float y); - public int getNumberOfStrokes(); - public void clearCurren(); - public void paintCurrentStroke(Canvas canvas, Matrix toScrMatrix, boolean highQuality); - public int paintLast(int from, Canvas canvas, Matrix toScrMatrix, boolean highQuality); - public boolean same(DrawStyle o); - public boolean empty(); + DrawStyle[] mDrawingsTypes = new DrawStyle[] { + new SimpleDraw(), + new Brush(R.drawable.brush_marker), + new Brush(R.drawable.brush_spatter) }; + { + for (int i = 0; i < mDrawingsTypes.length; i++) { + mDrawingsTypes[i].setType((byte) i); + } - class SimpleDraw implements DrawStyle { - private Path[] mPaths = new Path[0]; - private int[] mColors = new int[0]; - private float[] mRadius = new float[0]; - private int mStrokeCnt = 0; + } - private Path mCurrentPath; - private float mCurrentRadius; - private int mCurrentColor; + @Override + public FilterRepresentation getDefaultRepresentation() { + return new FilterDrawRepresentation(); + } - @Override - public DrawStyle clone() { - SimpleDraw ret = new SimpleDraw(); - ret.mPaths = new Path[mPaths.length]; - for (int i = 0; i < ret.mPaths.length; i++) { - ret.mPaths[i] = new Path(mPaths[i]); - } - ret.mColors = Arrays.copyOf(mColors, mColors.length); - ret.mRadius = Arrays.copyOf(mRadius, mRadius.length); - ret.mStrokeCnt = mStrokeCnt; - return ret; - } + @Override + public void useRepresentation(FilterRepresentation representation) { + FilterDrawRepresentation parameters = (FilterDrawRepresentation) representation; + mParameters = parameters; + } - @Override - public boolean empty() { - return mStrokeCnt == -1; - } + public void setStyle(byte style) { + mCurrentStyle = style % mDrawingsTypes.length; + } - @Override - public void setSize(float radius) { - mCurrentRadius = radius; - } + public int getStyle() { + return mCurrentStyle; + } - @Override - public void setColor(int color) { - mCurrentColor = color; - } + public static interface DrawStyle { + public void setType(byte type); + public void paint(FilterDrawRepresentation.StrokeData sd, Canvas canvas, Matrix toScrMatrix, + int quality); + } - @Override - public void startStroke(float x, float y) { - mCurrentPath = new Path(); - mCurrentPath.moveTo(x, y); - } + class SimpleDraw implements DrawStyle { + byte mType; @Override - public void stroke(float x, float y) { - if (mCurrentPath != null) { - mCurrentPath.lineTo(x, y); - } + public void setType(byte type) { + mType = type; } @Override - public void endStroke(float x, float y) { - if (mCurrentPath != null) { - mCurrentPath.lineTo(x, y); - Path[] np = new Path[mStrokeCnt + 1]; - for (int i = 0; i < mStrokeCnt; i++) { - np[i] = mPaths[i]; - } - np[mStrokeCnt] = mCurrentPath; - mColors = Arrays.copyOf(mColors, mColors.length + 1); - mRadius = Arrays.copyOf(mRadius, mRadius.length + 1); - mRadius[mStrokeCnt] = mCurrentRadius; - mColors[mStrokeCnt] = mCurrentColor; - mPaths = np; - mStrokeCnt++; + public void paint(FilterDrawRepresentation.StrokeData sd, Canvas canvas, Matrix toScrMatrix, + int quality) { + if (sd == null) { + return; } - } - - @Override - public void clearCurren(){ - mCurrentPath = null; - } - - @Override - public void paintCurrentStroke(Canvas canvas, Matrix toScrMatrix, boolean highQuality) { - Path path = mCurrentPath; - if (path == null) { + if (sd.mPath == null) { return; } Paint paint = new Paint(); paint.setStyle(Style.STROKE); - paint.setColor(mCurrentColor); - paint.setStrokeWidth(toScrMatrix.mapRadius(mCurrentRadius)); + paint.setColor(sd.mColor); + paint.setStrokeWidth(toScrMatrix.mapRadius(sd.mRadius)); - // don this way because a bug in path.transform(matrix) + // done this way because of a bug in path.transform(matrix) Path mCacheTransPath = new Path(); - mCacheTransPath.addPath(path, toScrMatrix); + mCacheTransPath.addPath(sd.mPath, toScrMatrix); canvas.drawPath(mCacheTransPath, paint); } - - @Override - public int paintLast(int from, Canvas canvas, Matrix toScrMatrix, boolean highQuality) { - Paint paint = new Paint(); - Matrix m = new Matrix(); - canvas.save(); - canvas.concat(toScrMatrix); - paint.setStyle(Style.STROKE); - for (int i = from; i < mStrokeCnt; i++) { - paint.setColor(mColors[i]); - paint.setStrokeWidth(mRadius[i]); - canvas.drawPath(mPaths[i], paint); - } - canvas.restore(); - return mStrokeCnt; - } - - @Override - public boolean same(DrawStyle o) { - if (!(o instanceof SimpleDraw)) { - return false; - } - SimpleDraw sd = (SimpleDraw) o; - boolean same; - same = Arrays.equals(mRadius, sd.mRadius); - if (!same) { - return false; - } - same = Arrays.equals(mColors, sd.mColors); - if (!same) { - return false; - } - for (int i = 0; i < mPaths.length; i++) { - if (!mPaths[i].equals(sd.mPaths)) { - return false; - } - } - return true; - } - - @Override - public int getNumberOfStrokes() { - return mStrokeCnt; - } } class Brush implements DrawStyle { - private Path[] mPaths = new Path[0]; - private int[] mColors = new int[0]; - private float[] mRadius = new float[0]; - private int mStrokeCnt = 0; - - private Path mCurrentPath; - private float mCurrentRadius; - private int mCurrentColor; - - @Override - public DrawStyle clone() { - Brush ret = new Brush(); - ret.mPaths = new Path[mPaths.length]; - for (int i = 0; i < ret.mPaths.length; i++) { - ret.mPaths[i] = new Path(mPaths[i]); - } - ret.mColors = Arrays.copyOf(mColors, mColors.length); - ret.mRadius = Arrays.copyOf(mRadius, mRadius.length); - ret.mStrokeCnt = mStrokeCnt; - return ret; - } - - @Override - public boolean empty() { - return mStrokeCnt == -1; - } - - @Override - public void setSize(float radius) { - mCurrentRadius = radius; - } - - @Override - public void setColor(int color) { - mCurrentColor = color; - } - - @Override - public void startStroke(float x, float y) { - mCurrentPath = new Path(); - mCurrentPath.moveTo(x, y); - } - - @Override - public void stroke(float x, float y) { - if (mCurrentPath != null) { - mCurrentPath.lineTo(x, y); + int mBrushID; + Bitmap mBrush; + byte mType; + + public Brush(int brushID) { + mBrushID = brushID; + } + public Bitmap getBrush() { + if (mBrush == null) { + BitmapFactory.Options opt = new BitmapFactory.Options(); + opt.inPreferredConfig = Bitmap.Config.ALPHA_8; + mBrush = MasterImage.getImage().getImageLoader().decodeImage(mBrushID, opt); + mBrush = mBrush.extractAlpha(); } + return mBrush; } @Override - public void endStroke(float x, float y) { - if (mCurrentPath != null) { - mCurrentPath.lineTo(x, y); - Path[] np = new Path[mStrokeCnt + 1]; - for (int i = 0; i < mStrokeCnt; i++) { - np[i] = mPaths[i]; - } - np[mStrokeCnt] = mCurrentPath; - mColors = Arrays.copyOf(mColors, mColors.length + 1); - mRadius = Arrays.copyOf(mRadius, mRadius.length + 1); - mRadius[mStrokeCnt] = mCurrentRadius; - mColors[mStrokeCnt] = mCurrentColor; - mPaths = np; - mStrokeCnt++; - clearCurren(); - } - } - - @Override - public void clearCurren() { - mCurrentPath = null; - } - - @Override - public void paintCurrentStroke(Canvas canvas, Matrix toScrMatrix, boolean highQuality) { - Path path = mCurrentPath; - if (path == null) { + public void paint(FilterDrawRepresentation.StrokeData sd, Canvas canvas, Matrix toScrMatrix, + int quality) { + if (sd == null || sd.mPath == null) { return; } Paint paint = new Paint(); paint.setStyle(Style.STROKE); - - float scale = toScrMatrix.mapRadius(1); + paint.setAntiAlias(true); Path mCacheTransPath = new Path(); - mCacheTransPath.addPath(path, toScrMatrix); - draw(canvas, paint, mCurrentColor, toScrMatrix.mapRadius(mCurrentRadius), + mCacheTransPath.addPath(sd.mPath, toScrMatrix); + draw(canvas, paint, sd.mColor, toScrMatrix.mapRadius(sd.mRadius) * 2, mCacheTransPath); } - @Override - public int paintLast(int from, Canvas canvas, Matrix toScrMatrix, boolean highQuality) { - Paint paint = new Paint(); - + public Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) + { Matrix m = new Matrix(); - canvas.save(); - canvas.concat(toScrMatrix); - paint.setStyle(Style.STROKE); - for (int i = from; i < mStrokeCnt; i++) { + m.setScale(dstWidth / (float) src.getWidth(), dstHeight / (float) src.getHeight()); + Bitmap result = Bitmap.createBitmap(dstWidth, dstHeight, src.getConfig()); + Canvas canvas = new Canvas(result); - draw(canvas, paint, mColors[i], mRadius[i], mPaths[i]); - } - canvas.restore(); - return mStrokeCnt; - } + Paint paint = new Paint(); + paint.setFilterBitmap(filter); + canvas.drawBitmap(src, m, paint); - PathMeasure mPathMeasure = new PathMeasure(); + return result; + } void draw(Canvas canvas, Paint paint, int color, float size, Path path) { + PathMeasure mPathMeasure = new PathMeasure(); + float[] mPosition = new float[2]; + float[] mTan = new float[2]; mPathMeasure.setPath(path, false); - float[] pos = new float[2]; - float[] tan = new float[2]; - paint.setColor(color); - float len = mPathMeasure.getLength(); - for (float i = 0; i < len; i += (size) / 2) { - mPathMeasure.getPosTan(i, pos, tan); - canvas.drawCircle(pos[0], pos[1], size, paint); - } - } + paint.setAntiAlias(true); + paint.setColor(color); - @Override - public boolean same(DrawStyle o) { - if (!(o instanceof Brush)) { - return false; - } - Brush sd = (Brush) o; - boolean same; - same = Arrays.equals(mRadius, sd.mRadius); - if (!same) { - return false; - } - same = Arrays.equals(mColors, sd.mColors); - if (!same) { - return false; - } - for (int i = 0; i < mPaths.length; i++) { - if (!mPaths[i].equals(sd.mPaths)) { - return false; - } + paint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)); + Bitmap brush; + // done this way because of a bug in + // Bitmap.createScaledBitmap(getBrush(),(int) size,(int) size,true); + brush = createScaledBitmap(getBrush(), (int) size, (int) size, true); + float len = mPathMeasure.getLength(); + float s2 = size / 2; + float step = s2 / 8; + for (float i = 0; i < len; i += step) { + mPathMeasure.getPosTan(i, mPosition, mTan); + // canvas.drawCircle(pos[0], pos[1], size, paint); + canvas.drawBitmap(brush, mPosition[0] - s2, mPosition[1] - s2, paint); } - return true; } @Override - public int getNumberOfStrokes() { - return mStrokeCnt; + public void setType(byte type) { + mType = type; } } - public void startSection(int color, float size, float x, float y) { - mDrawings[mCurrentStyle].setColor(color); - mDrawings[mCurrentStyle].setSize(size); - mDrawings[mCurrentStyle].startStroke(x, y); + void paint(FilterDrawRepresentation.StrokeData sd, Canvas canvas, Matrix toScrMatrix, + int quality) { + mDrawingsTypes[sd.mType].paint(sd, canvas, toScrMatrix, quality); } - public void addPoint(float x, float y) { - mDrawings[mCurrentStyle].stroke(x, y); - } - - public void endSection(float x, float y) { - mDrawings[mCurrentStyle].endStroke(x, y); - } - - public ImageFilterDraw() { - mName = "Image Draw"; - } - - public void drawData(Canvas canvas, Matrix originalRotateToScreen, boolean highQuality) { + public void drawData(Canvas canvas, Matrix originalRotateToScreen, int quality) { Paint paint = new Paint(); - if (highQuality) { + if (quality == ImagePreset.QUALITY_FINAL) { paint.setAntiAlias(true); } paint.setStyle(Style.STROKE); paint.setColor(Color.RED); paint.setStrokeWidth(40); - boolean empty = true; - for (int i = 0; i < mDrawings.length; i++) { - empty &= mDrawings[i].empty(); - } - if (empty) { + + if (mParameters.getDrawing().isEmpty() && mParameters.getCurrentDrawing() == null) { return; } - if (highQuality) { - for (int i = 0; i < mDrawings.length; i++) { - mDrawings[i].paintLast(0, canvas, originalRotateToScreen, highQuality); + if (quality == ImagePreset.QUALITY_FINAL) { + for (FilterDrawRepresentation.StrokeData strokeData : mParameters.getDrawing()) { + paint(strokeData, canvas, originalRotateToScreen, quality); } - return; } + if (mOverlayBitmap == null || mOverlayBitmap.getWidth() != canvas.getWidth() || - mOverlayBitmap.getHeight() != canvas.getHeight()) { + mOverlayBitmap.getHeight() != canvas.getHeight() || + mParameters.getDrawing().size() < mCachedStrokes) { mOverlayBitmap = Bitmap.createBitmap( canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888); mCachedStrokes = 0; } - if (mCachedStrokes < mDrawings[mCurrentStyle].getNumberOfStrokes()) { + + if (mCachedStrokes < mParameters.getDrawing().size()) { fillBuffer(originalRotateToScreen); } canvas.drawBitmap(mOverlayBitmap, 0, 0, paint); - } - public void fillBuffer(Matrix originalRotateToScreen) { - Canvas drawCache = new Canvas(mOverlayBitmap); - for (int i = 0; i < mDrawings.length; i++) { - - mCachedStrokes = mDrawings[i].paintLast( - mCachedStrokes, drawCache, originalRotateToScreen, false); + StrokeData stroke = mParameters.getCurrentDrawing(); + if (stroke != null) { + paint(stroke, canvas, originalRotateToScreen, quality); } } - @Override - public int getButtonId() { - return R.id.drawOnImageButton; - } - - @Override - public int getTextId() { - return R.string.imageDraw; - } - - @Override - public int getEditingViewId() { - return EditorDraw.ID; - } - - @Override - public ImageFilter clone() throws CloneNotSupportedException { - ImageFilterDraw filter = (ImageFilterDraw) super.clone(); - - filter.mDrawings = mDrawings.clone(); - return filter; - } - - @Override - public boolean isNil() { - for (int i = 0; i < mDrawings.length; i++) { - if (mDrawings[i].getNumberOfStrokes() != 0) { - return false; - } - } - return true; - } - - @Override - public boolean same(ImageFilter filter) { - boolean isSuperSame = super.same(filter); - if (!isSuperSame || !(filter instanceof ImageFilterDraw)) { - return false; - } + public void fillBuffer(Matrix originalRotateToScreen) { + Canvas drawCache = new Canvas(mOverlayBitmap); + Vector<FilterDrawRepresentation.StrokeData> v = mParameters.getDrawing(); + int n = v.size(); - ImageFilterDraw dfilter = (ImageFilterDraw) filter; - boolean same = true; - for (int i = 0; i < mDrawings.length; i++) { - same &= mDrawings[i].same(dfilter.mDrawings[i]); + for (int i = mCachedStrokes; i < n; i++) { + paint(v.get(i), drawCache, originalRotateToScreen, ImagePreset.QUALITY_PREVIEW); } - return same; - } - - public void clear() { - mDrawings[mCurrentStyle].clearCurren(); + mCachedStrokes = n; } public void draw(Canvas canvas, Matrix originalRotateToScreen) { - for (int i = 0; i < mDrawings.length; i++) { - mDrawings[i].paintCurrentStroke(canvas, originalRotateToScreen, false); + for (FilterDrawRepresentation.StrokeData strokeData : mParameters.getDrawing()) { + paint(strokeData, canvas, originalRotateToScreen, ImagePreset.QUALITY_PREVIEW); } + mDrawingsTypes[mCurrentStyle].paint( + null, canvas, originalRotateToScreen, ImagePreset.QUALITY_PREVIEW); } @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { int w = bitmap.getWidth(); int h = bitmap.getHeight(); - short[] rect = new short[4]; - - Matrix m = new Matrix(); - m.setScale(scaleFactor, scaleFactor); - - drawData(new Canvas(bitmap), m, highQuality); + Matrix m = getOriginalToScreenMatrix(w, h); + drawData(new Canvas(bitmap), m, quality); return bitmap; } + } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterEdge.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterEdge.java index 9eda64874..55c709573 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterEdge.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterEdge.java @@ -20,35 +20,31 @@ import android.graphics.Bitmap; import com.android.gallery3d.R; -public class ImageFilterEdge extends ImageFilter { +public class ImageFilterEdge extends SimpleImageFilter { public ImageFilterEdge() { mName = "Edge"; - mPreviewParameter = 0; } - native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float p); - - @Override - public int getButtonId() { - return R.id.edgeButton; + public FilterRepresentation getDefaultRepresentation() { + FilterRepresentation representation = super.getDefaultRepresentation(); + representation.setName("Edge"); + representation.setFilterClass(ImageFilterEdge.class); + representation.setTextId(R.string.edge); + representation.setButtonId(R.id.edgeButton); + return representation; } - @Override - public int getTextId() { - return R.string.edge; - } - - @Override - public boolean isNil() { - return false; - } + native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float p); @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { + if (getParameters() == null) { + return bitmap; + } int w = bitmap.getWidth(); int h = bitmap.getHeight(); - float p = mParameter + 101; + float p = getParameters().getValue() + 101; p = (float) p / 100; nativeApplyFilter(bitmap, w, h, p); return bitmap; diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java index 63f860171..7a8df71af 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java @@ -20,30 +20,35 @@ import com.android.gallery3d.R; import android.graphics.Bitmap; -public class ImageFilterExposure extends ImageFilter { +public class ImageFilterExposure extends SimpleImageFilter { public ImageFilterExposure() { mName = "Exposure"; } - @Override - public int getButtonId() { - return R.id.exposureButton; - } - - @Override - public int getTextId() { - return R.string.exposure; + public FilterRepresentation getDefaultRepresentation() { + FilterBasicRepresentation representation = + (FilterBasicRepresentation) super.getDefaultRepresentation(); + representation.setName("Exposure"); + representation.setFilterClass(ImageFilterExposure.class); + representation.setTextId(R.string.exposure); + representation.setButtonId(R.id.exposureButton); + representation.setMinimum(-100); + representation.setMaximum(100); + representation.setDefaultValue(0); + return representation; } native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float bright); @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { + if (getParameters() == null) { + return bitmap; + } int w = bitmap.getWidth(); int h = bitmap.getHeight(); - int p = mParameter; - float value = p; + float value = getParameters().getValue(); nativeApplyFilter(bitmap, w, h, value); return bitmap; } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java index 345202fe6..820ec3e51 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterFx.java @@ -18,67 +18,40 @@ package com.android.gallery3d.filtershow.filters; import android.graphics.Bitmap; +import com.android.gallery3d.filtershow.editors.BasicEditor; +import com.android.gallery3d.filtershow.editors.ImageOnlyEditor; + public class ImageFilterFx extends ImageFilter { private static final String TAG = "ImageFilterFx"; - Bitmap fxBitmap; - int mNameResource = 0; + private FilterFxRepresentation mParameters = null; - public ImageFilterFx(Bitmap fxBitmap, String name, int nameResource) { - setFilterType(TYPE_FX); - mName = name; - this.fxBitmap = fxBitmap; - mNameResource = nameResource; + public ImageFilterFx() { } - @Override - public int getTextId() { - return mNameResource; + public void useRepresentation(FilterRepresentation representation) { + FilterFxRepresentation parameters = (FilterFxRepresentation) representation; + mParameters = parameters; } - @Override - public ImageFilter clone() throws CloneNotSupportedException { - ImageFilterFx filter = (ImageFilterFx) super.clone(); - filter.fxBitmap = this.fxBitmap; - return filter; - } - - @Override - public boolean isNil() { - if (fxBitmap != null) { - return false; - } - return true; - } - - @Override - public boolean showParameterValue() { - return false; - } - - @Override - public boolean showEditingControls() { - return false; - } - - @Override - public boolean showUtilityPanel() { - return false; + public FilterFxRepresentation getParameters() { + return mParameters; } native protected void nativeApplyFilter(Bitmap bitmap, int w, int h,Bitmap fxBitmap, int fxw, int fxh); @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { - if (fxBitmap==null) + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { + if (getParameters() == null || getParameters().getFxBitmap() ==null) { return bitmap; + } int w = bitmap.getWidth(); int h = bitmap.getHeight(); - int fxw = fxBitmap.getWidth(); - int fxh = fxBitmap.getHeight(); + int fxw = getParameters().getFxBitmap().getWidth(); + int fxh = getParameters().getFxBitmap().getHeight(); - nativeApplyFilter(bitmap, w, h, fxBitmap, fxw, fxh); + nativeApplyFilter(bitmap, w, h, getParameters().getFxBitmap(), fxw, fxh); return bitmap; } } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java index 33ecc8ab9..329ca81b9 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterGeometry.java @@ -45,6 +45,7 @@ public class ImageFilterGeometry extends ImageFilter { @Override public ImageFilter clone() throws CloneNotSupportedException { + // FIXME: clone() should not be needed. Remove when we fix geometry. ImageFilterGeometry filter = (ImageFilterGeometry) super.clone(); return filter; } @@ -66,7 +67,12 @@ public class ImageFilterGeometry extends ImageFilter { Bitmap dst, int dstWidth, int dstHeight, float straightenAngle); @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public void useRepresentation(FilterRepresentation representation) { + + } + + @Override + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { // TODO: implement bilinear or bicubic here... for now, just use // canvas to do a simple implementation... // TODO: and be more memory efficient! (do it in native?) diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterGradient.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterGradient.java deleted file mode 100644 index be4ba871d..000000000 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterGradient.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.gallery3d.filtershow.filters; - -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.LinearGradient; -import android.graphics.Paint; -import android.graphics.Paint.Style; -import android.graphics.Shader.TileMode; - -public class ImageFilterGradient extends ImageFilter { - - private Bitmap mGradientBitmap = null; - private int[] mColors = null; - private float[] mPositions = null; - - public ImageFilterGradient() { - mName = "Gradient"; - } - - @Override - public ImageFilter clone() throws CloneNotSupportedException { - ImageFilterGradient filter = (ImageFilterGradient) super.clone(); - System.arraycopy(mColors, 0, filter.mColors, 0, mColors.length); - System.arraycopy(mPositions, 0, filter.mPositions, 0, mPositions.length); - return filter; - } - - public void addColor(int color, float position) { - int length = 0; - if (mColors != null) { - length = mColors.length; - } - int[] colors = new int[length + 1]; - float[] positions = new float[length + 1]; - - for (int i = 0; i < length; i++) { - colors[i] = mColors[i]; - positions[i] = mPositions[i]; - } - - colors[length] = color; - positions[length] = position; - - mColors = colors; - mPositions = positions; - } - - @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { - createGradient(); - int[] gradient = new int[256]; - int[] redGradient = new int[256]; - int[] greenGradient = new int[256]; - int[] blueGradient = new int[256]; - mGradientBitmap.getPixels(gradient, 0, 256, 0, 0, 256, 1); - - for (int i = 0; i < 256; i++) { - redGradient[i] = Color.red(gradient[i]); - greenGradient[i] = Color.green(gradient[i]); - blueGradient[i] = Color.blue(gradient[i]); - } - nativeApplyGradientFilter(bitmap, bitmap.getWidth(), bitmap.getHeight(), - redGradient, greenGradient, blueGradient); - return bitmap; - } - - public void createGradient() { - if (mGradientBitmap != null) { - return; - } - - /* Create a 200 x 200 bitmap and fill it with black. */ - Bitmap b = Bitmap.createBitmap(256, 1, Config.ARGB_8888); - Canvas c = new Canvas(b); - c.drawColor(Color.BLACK); - - /* Create your gradient. */ - - /* - * int[] colors = new int[2]; colors[0] = Color.argb(255, 20, 20, 10); - * colors[0] = Color.BLACK; colors[1] = Color.argb(255, 228, 231, 193); - * float[] positions = new float[2]; positions[0] = 0; positions[1] = 1; - */ - - LinearGradient grad = new LinearGradient(0, 0, 255, 1, mColors, - mPositions, TileMode.CLAMP); - - /* Draw your gradient to the top of your bitmap. */ - Paint p = new Paint(); - p.setStyle(Style.FILL); - p.setShader(grad); - c.drawRect(0, 0, 256, 1, p); - mGradientBitmap = b; - } - -} diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java index 6b9869be3..8c484c72e 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java @@ -21,46 +21,37 @@ import com.android.gallery3d.filtershow.editors.BasicEditor; import android.graphics.Bitmap; -public class ImageFilterHue extends ImageFilter { +public class ImageFilterHue extends SimpleImageFilter { private ColorSpaceMatrix cmatrix = null; public ImageFilterHue() { mName = "Hue"; cmatrix = new ColorSpaceMatrix(); - mMaxParameter = 180; - mMinParameter = -180; } - @Override - public int getButtonId() { - return R.id.hueButton; - } - - @Override - public int getTextId() { - return R.string.hue; - } - - @Override - public int getEditingViewId() { - return BasicEditor.ID; - } - - @Override - public ImageFilter clone() throws CloneNotSupportedException { - ImageFilterHue filter = (ImageFilterHue) super.clone(); - filter.cmatrix = new ColorSpaceMatrix(cmatrix); - return filter; + public FilterRepresentation getDefaultRepresentation() { + FilterBasicRepresentation representation = + (FilterBasicRepresentation) super.getDefaultRepresentation(); + representation.setName("Hue"); + representation.setFilterClass(ImageFilterHue.class); + representation.setMinimum(-180); + representation.setMaximum(180); + representation.setTextId(R.string.hue); + representation.setButtonId(R.id.hueButton); + representation.setEditorId(BasicEditor.ID); + return representation; } native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float []matrix); @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { + if (getParameters() == null) { + return bitmap; + } int w = bitmap.getWidth(); int h = bitmap.getHeight(); - float p = mParameter; - float value = p; + float value = getParameters().getValue(); cmatrix.identity(); cmatrix.setHue(value); diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterKMeans.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterKMeans.java index f03baca39..f48bd047a 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterKMeans.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterKMeans.java @@ -20,17 +20,13 @@ import android.graphics.Bitmap; import android.text.format.Time; import com.android.gallery3d.R; +import com.android.gallery3d.ingest.ui.MtpThumbnailTileView; -public class ImageFilterKMeans extends ImageFilter { +public class ImageFilterKMeans extends SimpleImageFilter { private int mSeed = 0; public ImageFilterKMeans() { mName = "KMeans"; - mMaxParameter = 20; - mMinParameter = 2; - mPreviewParameter = 4; - mDefaultParameter = 4; - mParameter = 4; // set random seed for session Time t = new Time(); @@ -38,27 +34,29 @@ public class ImageFilterKMeans extends ImageFilter { mSeed = (int) t.toMillis(false); } + public FilterRepresentation getDefaultRepresentation() { + FilterBasicRepresentation representation = (FilterBasicRepresentation) super.getDefaultRepresentation(); + representation.setName("KMeans"); + representation.setFilterClass(ImageFilterKMeans.class); + representation.setMaximum(20); + representation.setMinimum(2); + representation.setValue(4); + representation.setDefaultValue(4); + representation.setPreviewValue(4); + representation.setTextId(R.string.kmeans); + representation.setButtonId(R.id.kmeansButton); + return representation; + } + native protected void nativeApplyFilter(Bitmap bitmap, int width, int height, Bitmap large_ds_bm, int lwidth, int lheight, Bitmap small_ds_bm, int swidth, int sheight, int p, int seed); @Override - public int getButtonId() { - return R.id.kmeansButton; - } - - @Override - public int getTextId() { - return R.string.kmeans; - } - - @Override - public boolean isNil() { - return false; - } - - @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { + if (getParameters() == null) { + return bitmap; + } int w = bitmap.getWidth(); int h = bitmap.getHeight(); @@ -87,8 +85,10 @@ public class ImageFilterKMeans extends ImageFilter { small_bm_ds = Bitmap.createScaledBitmap(large_bm_ds, sw, sh, true); } - int p = Math.max(mParameter, mMinParameter) % (mMaxParameter + 1); - nativeApplyFilter(bitmap, w, h, large_bm_ds, lw, lh, small_bm_ds, sw, sh, p, mSeed); + if (getParameters() != null) { + int p = Math.max(getParameters().getValue(), getParameters().getMinimum()) % (getParameters().getMaximum() + 1); + nativeApplyFilter(bitmap, w, h, large_bm_ds, lw, lh, small_bm_ds, sw, sh, p, mSeed); + } return bitmap; } } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterNegative.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterNegative.java index 04fd1e42e..841c5c913 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterNegative.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterNegative.java @@ -3,6 +3,7 @@ package com.android.gallery3d.filtershow.filters; import android.graphics.Bitmap; import com.android.gallery3d.R; +import com.android.gallery3d.filtershow.editors.ImageOnlyEditor; public class ImageFilterNegative extends ImageFilter { @@ -10,35 +11,26 @@ public class ImageFilterNegative extends ImageFilter { mName = "Negative"; } - @Override - public int getButtonId() { - return R.id.negativeButton; - } - - @Override - public int getTextId() { - return R.string.negative; + public FilterRepresentation getDefaultRepresentation() { + FilterRepresentation representation = new FilterDirectRepresentation("Negative"); + representation.setFilterClass(ImageFilterNegative.class); + representation.setTextId(R.string.negative); + representation.setButtonId(R.id.negativeButton); + representation.setShowEditingControls(false); + representation.setShowParameterValue(false); + representation.setEditorId(ImageOnlyEditor.ID); + return representation; } - @Override - public boolean isNil() { - return false; - } + native protected void nativeApplyFilter(Bitmap bitmap, int w, int h); @Override - public boolean showEditingControls() { - return false; - } + public void useRepresentation(FilterRepresentation representation) { - @Override - public boolean showParameterValue() { - return false; } - native protected void nativeApplyFilter(Bitmap bitmap, int w, int h); - @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { int w = bitmap.getWidth(); int h = bitmap.getHeight(); nativeApplyFilter(bitmap, w, h); diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterParametricBorder.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterParametricBorder.java index 681eb1221..316a286e8 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterParametricBorder.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterParametricBorder.java @@ -26,85 +26,29 @@ import android.graphics.RectF; import com.android.gallery3d.R; public class ImageFilterParametricBorder extends ImageFilter { - private int mBorderColor = Color.WHITE; - private int mBorderSize = 10; - private int mBorderCornerRadius = 10; + private FilterColorBorderRepresentation mParameters = null; public ImageFilterParametricBorder() { - setFilterType(TYPE_BORDER); mName = "Border"; } - @Override - public int getTextId() { - return R.string.borders; - } - - @Override - public boolean showParameterValue() { - return false; - } - - @Override - public boolean showEditingControls() { - return false; + public void useRepresentation(FilterRepresentation representation) { + FilterColorBorderRepresentation parameters = (FilterColorBorderRepresentation) representation; + mParameters = parameters; } - @Override - public boolean showUtilityPanel() { - return false; - } - - public ImageFilterParametricBorder(int color, int size, int radius) { - setBorder(color, size, radius); - setFilterType(TYPE_BORDER); - } - - @Override - public ImageFilter clone() throws CloneNotSupportedException { - ImageFilterParametricBorder filter = (ImageFilterParametricBorder) super.clone(); - filter.setBorder(mBorderColor, mBorderSize, mBorderCornerRadius); - return filter; - } - - @Override - public boolean isNil() { - return false; - } - - @Override - public boolean same(ImageFilter filter) { - boolean isBorderFilter = super.same(filter); - if (!isBorderFilter) { - return false; - } - if (!(filter instanceof ImageFilterParametricBorder)) { - return false; - } - ImageFilterParametricBorder borderFilter = (ImageFilterParametricBorder) filter; - if (borderFilter.mBorderColor != mBorderColor) { - return false; - } - if (borderFilter.mBorderSize != mBorderSize) { - return false; - } - if (borderFilter.mBorderCornerRadius != mBorderCornerRadius) { - return false; - } - return true; - } - - public void setBorder(int color, int size, int radius) { - mBorderColor = color; - mBorderSize = size; - mBorderCornerRadius = radius; + public FilterColorBorderRepresentation getParameters() { + return mParameters; } private void applyHelper(Canvas canvas, int w, int h) { + if (getParameters() == null) { + return; + } Path border = new Path(); border.moveTo(0, 0); - float bs = mBorderSize / 100.0f * w; - float r = mBorderCornerRadius / 100.0f * w; + float bs = getParameters().getBorderSize() / 100.0f * w; + float r = getParameters().getBorderRadius() / 100.0f * w; border.lineTo(0, h); border.lineTo(w, h); border.lineTo(w, 0); @@ -114,19 +58,19 @@ public class ImageFilterParametricBorder extends ImageFilter { Paint paint = new Paint(); paint.setAntiAlias(true); - paint.setColor(mBorderColor); + paint.setColor(getParameters().getColor()); canvas.drawPath(border, paint); } @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { Canvas canvas = new Canvas(bitmap); applyHelper(canvas, bitmap.getWidth(), bitmap.getHeight()); return bitmap; } @Override - public Bitmap iconApply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap iconApply(Bitmap bitmap, float scaleFactor, int quality) { Canvas canvas = new Canvas(bitmap); applyHelper(canvas, bitmap.getWidth() * 4, bitmap.getHeight() * 4); return bitmap; diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java index 368e29a78..d5297904d 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java @@ -21,7 +21,7 @@ import android.graphics.Bitmap; import android.support.v8.renderscript.*; import android.util.Log; -public class ImageFilterRS extends ImageFilter { +public abstract class ImageFilterRS extends ImageFilter { private final String LOGTAG = "ImageFilterRS"; private static RenderScript mRS = null; @@ -31,10 +31,13 @@ public class ImageFilterRS extends ImageFilter { private static Bitmap sOldBitmap = null; private Bitmap mOldBitmap = null; - private static Bitmap mReturnBitmap = null; private final Bitmap.Config mBitmapConfig = Bitmap.Config.ARGB_8888; - public void prepare(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public void resetBitmap() { + mOldBitmap = null; + } + + public void prepare(Bitmap bitmap, float scaleFactor, int quality) { if (sOldBitmap == null || (bitmap.getWidth() != sOldBitmap.getWidth()) || (bitmap.getHeight() != sOldBitmap.getHeight())) { @@ -53,13 +56,13 @@ public class ImageFilterRS extends ImageFilter { } mInPixelsAllocation.copyFrom(bitmap); if (mOldBitmap != sOldBitmap) { - createFilter(mResources, scaleFactor, highQuality); + createFilter(mResources, scaleFactor, quality); mOldBitmap = sOldBitmap; } } public void createFilter(android.content.res.Resources res, - float scaleFactor, boolean highQuality) { + float scaleFactor, int quality) { // Stub } @@ -72,12 +75,12 @@ public class ImageFilterRS extends ImageFilter { } @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { if (bitmap == null) { return bitmap; } try { - prepare(bitmap, scaleFactor, highQuality); + prepare(bitmap, scaleFactor, quality); runFilter(); update(bitmap); } catch (android.renderscript.RSIllegalArgumentException e) { @@ -93,8 +96,19 @@ public class ImageFilterRS extends ImageFilter { } public static void setRenderScriptContext(Activity context) { - mRS = RenderScript.create(context); + if (mRS == null) { + mRS = RenderScript.create(context); + } mResources = context.getResources(); + if (mInPixelsAllocation != null) { + mInPixelsAllocation.destroy(); + mInPixelsAllocation = null; + } + if (mOutPixelsAllocation != null) { + mOutPixelsAllocation.destroy(); + mOutPixelsAllocation = null; + } + sOldBitmap = null; } } diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterRedEye.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterRedEye.java index 9ae6f511e..511f9e90f 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterRedEye.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterRedEye.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,157 +20,53 @@ import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.RectF; -import com.android.gallery3d.R; -import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; - import java.util.Vector; public class ImageFilterRedEye extends ImageFilter { private static final String LOGTAG = "ImageFilterRedEye"; - private Vector<RedEyeCandidate> mCandidates = null; + FilterRedEyeRepresentation mParameters = new FilterRedEyeRepresentation(); public ImageFilterRedEye() { mName = "Red Eye"; } @Override - public int getButtonId() { - return R.id.redEyeButton; - } - - @Override - public int getTextId() { - return R.string.redeye; - } - - @Override - public int getEditingViewId() { - return R.id.imageRedEyes; - } - - @Override - public ImageFilter clone() throws CloneNotSupportedException { - ImageFilterRedEye filter = (ImageFilterRedEye) super.clone(); - if (mCandidates != null) { - int size = mCandidates.size(); - filter.mCandidates = new Vector<RedEyeCandidate>(); - for (int i = 0; i < size; i++) { - filter.mCandidates.add(new RedEyeCandidate(mCandidates.elementAt(i))); - } - } - return filter; + public FilterRepresentation getDefaultRepresentation() { + return new FilterRedEyeRepresentation(); } - @Override public boolean isNil() { - if (mCandidates != null && mCandidates.size() > 0) { - return false; - } - return true; - } - - @Override - public boolean same(ImageFilter filter) { - boolean isRedEyeFilter = super.same(filter); - if (!isRedEyeFilter) { - return false; - } - ImageFilterRedEye redEyeFilter = (ImageFilterRedEye) filter; - if (redEyeFilter.mCandidates == null && mCandidates == null) { - return true; - } - if (redEyeFilter.mCandidates == null || mCandidates == null) { - return false; - } - if (redEyeFilter.mCandidates.size() != mCandidates.size()) { - return false; - } - int size = mCandidates.size(); - for (int i = 0; i < size; i++) { - RedEyeCandidate c1 = mCandidates.elementAt(i); - RedEyeCandidate c2 = redEyeFilter.mCandidates.elementAt(i); - if (!c1.equals(c2)) { - return false; - } - } - return true; - } - - public Vector<RedEyeCandidate> getCandidates() { - if (mCandidates == null) { - mCandidates = new Vector<RedEyeCandidate>(); - } - return mCandidates; + return mParameters.isNil(); } - public void addRect(RectF rect, RectF bounds) { - if (mCandidates == null) { - mCandidates = new Vector<RedEyeCandidate>(); - } - Vector<RedEyeCandidate> intersects = new Vector<RedEyeCandidate>(); - for (int i = 0; i < mCandidates.size(); i++) { - RedEyeCandidate r = mCandidates.elementAt(i); - if (r.intersect(rect)) { - intersects.add(r); - } - } - for (int i = 0; i < intersects.size(); i++) { - RedEyeCandidate r = intersects.elementAt(i); - rect.union(r.mRect); - bounds.union(r.mBounds); - mCandidates.remove(r); - } - mCandidates.add(new RedEyeCandidate(rect, bounds)); + public Vector<FilterPoint> getCandidates() { + return mParameters.getCandidates(); } public void clear() { - if (mCandidates == null) { - mCandidates = new Vector<RedEyeCandidate>(); - } - mCandidates.clear(); + mParameters.clearCandidates(); } native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, short[] matrix); @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public void useRepresentation(FilterRepresentation representation) { + FilterRedEyeRepresentation parameters = (FilterRedEyeRepresentation) representation; + mParameters = parameters; + } + + @Override + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { int w = bitmap.getWidth(); int h = bitmap.getHeight(); short[] rect = new short[4]; - if (mCandidates != null && mCandidates.size() > 0) { - for (int i = 0; i < mCandidates.size(); i++) { - RectF r = new RectF(mCandidates.elementAt(i).mRect); - GeometryMetadata geo = getImagePreset().mGeoData; - Matrix originalToScreen = geo.getOriginalToScreen(true, - getImagePreset().getImageLoader().getOriginalBounds().width(), - getImagePreset().getImageLoader().getOriginalBounds().height(), - w, h); - originalToScreen.mapRect(r); - if (r.left < 0) { - r.left = 0; - } - if (r.left > w) { - r.left = w; - } - if (r.top < 0) { - r.top = 0; - } - if (r.top > h) { - r.top = h; - } - if (r.right < 0) { - r.right = 0; - } - if (r.right > w) { - r.right = w; - } - if (r.bottom < 0) { - r.bottom = 0; - } - if (r.bottom > h) { - r.bottom = h; - } + int size = mParameters.getNumberOfCandidates(); + Matrix originalToScreen = getOriginalToScreenMatrix(w, h); + for (int i = 0; i < size; i++) { + RectF r = new RectF(((RedEyeCandidate) (mParameters.getCandidate(i))).mRect); + originalToScreen.mapRect(r); + if (r.intersect(0, 0, w, h)) { rect[0] = (short) r.left; rect[1] = (short) r.top; rect[2] = (short) r.width(); diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java index 129165b3e..6cd833206 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java @@ -20,29 +20,36 @@ import com.android.gallery3d.R; import android.graphics.Bitmap; -public class ImageFilterSaturated extends ImageFilter { +public class ImageFilterSaturated extends SimpleImageFilter { public ImageFilterSaturated() { mName = "Saturated"; } @Override - public int getButtonId() { - return R.id.saturationButton; - } - - @Override - public int getTextId() { - return R.string.saturation; + public FilterRepresentation getDefaultRepresentation() { + FilterBasicRepresentation representation = + (FilterBasicRepresentation) super.getDefaultRepresentation(); + representation.setName("Saturated"); + representation.setFilterClass(ImageFilterSaturated.class); + representation.setTextId(R.string.saturation); + representation.setButtonId(R.id.saturationButton); + representation.setMinimum(-100); + representation.setMaximum(100); + representation.setDefaultValue(0); + return representation; } native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float saturation); @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { + if (getParameters() == null) { + return bitmap; + } int w = bitmap.getWidth(); int h = bitmap.getHeight(); - int p = mParameter; + int p = getParameters().getValue(); float value = 1 + p / 100.0f; nativeApplyFilter(bitmap, w, h, value); return bitmap; diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java index de8fcd5ea..e17823955 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java @@ -20,36 +20,36 @@ import com.android.gallery3d.R; import android.graphics.Bitmap; -public class ImageFilterShadows extends ImageFilter { +public class ImageFilterShadows extends SimpleImageFilter { public ImageFilterShadows() { mName = "Shadows"; } - @Override - public int getButtonId() { - return R.id.shadowRecoveryButton; - } - - @Override - public int getTextId() { - return R.string.shadow_recovery; - } - - @Override - public ImageFilter clone() throws CloneNotSupportedException { - ImageFilterShadows filter = (ImageFilterShadows) super.clone(); - return filter; + public FilterRepresentation getDefaultRepresentation() { + FilterBasicRepresentation representation = + (FilterBasicRepresentation) super.getDefaultRepresentation(); + representation.setName("Shadows"); + representation.setFilterClass(ImageFilterShadows.class); + representation.setTextId(R.string.shadow_recovery); + representation.setButtonId(R.id.shadowRecoveryButton); + representation.setMinimum(-100); + representation.setMaximum(100); + representation.setDefaultValue(0); + return representation; } native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float factor); @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { + if (getParameters() == null) { + return bitmap; + } int w = bitmap.getWidth(); int h = bitmap.getHeight(); - float p = mParameter; + float p = getParameters().getValue(); nativeApplyFilter(bitmap, w, h, p); return bitmap; diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java index 1f9bf22c0..9c99d57d0 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java @@ -24,33 +24,31 @@ public class ImageFilterSharpen extends ImageFilterRS { private ScriptC_convolve3x3 mScript; float mScaleFactor; + private FilterBasicRepresentation mParameters; + public ImageFilterSharpen() { mName = "Sharpen"; } - @Override - public int getButtonId() { - return R.id.sharpenButton; - } - - @Override - public int getTextId() { - return R.string.sharpness; - } - - @Override - public int getOverlayBitmaps() { - return R.drawable.filtershow_button_colors_sharpen; + public FilterRepresentation getDefaultRepresentation() { + FilterRepresentation representation = new FilterBasicRepresentation("Sharpen", 0, 0, 100); + representation.setShowParameterValue(true); + representation.setFilterClass(ImageFilterSharpen.class); + representation.setTextId(R.string.sharpness); + representation.setButtonId(R.id.sharpenButton); + representation.setOverlayId(R.drawable.filtershow_button_colors_sharpen); + representation.setEditorId(R.id.imageZoom); + return representation; } - @Override - public int getEditingViewId() { - return R.id.imageZoom; + public void useRepresentation(FilterRepresentation representation) { + FilterBasicRepresentation parameters = (FilterBasicRepresentation) representation; + mParameters = parameters; } @Override public void createFilter(android.content.res.Resources res, float scaleFactor, - boolean highQuality) { + int quality) { int w = mInPixelsAllocation.getType().getX(); int h = mInPixelsAllocation.getType().getY(); mScaleFactor = scaleFactor; @@ -63,7 +61,7 @@ public class ImageFilterSharpen extends ImageFilterRS { } private void computeKernel(){ - float p1 = mParameter * mScaleFactor; + float p1 = mParameters.getValue() * mScaleFactor; float value = p1 / 100.0f; float f[] = new float[9]; float p = value; @@ -81,6 +79,9 @@ public class ImageFilterSharpen extends ImageFilterRS { @Override public void runFilter() { + if (mParameters == null) { + return; + } computeKernel(); mScript.set_gIn(mInPixelsAllocation); mScript.bind_gPixels(mInPixelsAllocation); diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterStraighten.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterStraighten.java index 85fcf273f..a3bb6f980 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterStraighten.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterStraighten.java @@ -32,6 +32,7 @@ public class ImageFilterStraighten extends ImageFilter { @Override public ImageFilter clone() throws CloneNotSupportedException { + // FIXME: clone() should not be needed. Remove when we fix geometry. ImageFilterStraighten filter = (ImageFilterStraighten) super.clone(); filter.mRotation = mRotation; filter.mZoomFactor = mZoomFactor; @@ -52,7 +53,12 @@ public class ImageFilterStraighten extends ImageFilter { } @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public void useRepresentation(FilterRepresentation representation) { + + } + + @Override + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { // TODO: implement bilinear or bicubic here... for now, just use // canvas to do a simple implementation... // TODO: and be more memory efficient! (do it in native?) diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java index 36bd62630..702cc664c 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java @@ -18,22 +18,26 @@ package com.android.gallery3d.filtershow.filters; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; import android.graphics.RectF; import com.adobe.xmp.XMPException; import com.adobe.xmp.XMPMeta; import com.android.gallery3d.R; import com.android.gallery3d.app.Log; +import com.android.gallery3d.filtershow.editors.EditorTinyPlanet; import com.android.gallery3d.filtershow.presets.ImagePreset; /** * An image filter which creates a tiny planet projection. */ -public class ImageFilterTinyPlanet extends ImageFilter { - private float mAngle = 0; +public class ImageFilterTinyPlanet extends SimpleImageFilter { - private static final String TAG = ImageFilterTinyPlanet.class.getSimpleName(); + + private static final String LOGTAG = ImageFilterTinyPlanet.class.getSimpleName(); public static final String GOOGLE_PANO_NAMESPACE = "http://ns.google.com/photos/1.0/panorama/"; + FilterTinyPlanetRepresentation mParameters = new FilterTinyPlanetRepresentation(); public static final String CROPPED_AREA_IMAGE_WIDTH_PIXELS = "CroppedAreaImageWidthPixels"; @@ -49,38 +53,18 @@ public class ImageFilterTinyPlanet extends ImageFilter { "CroppedAreaTopPixels"; public ImageFilterTinyPlanet() { - setFilterType(TYPE_TINYPLANET); mName = "TinyPlanet"; - - mMinParameter = 10; - mMaxParameter = 60; - mDefaultParameter = 20; - mPreviewParameter = 20; - mParameter = 20; - mAngle = 0; } @Override - public int getButtonId() { - return R.id.tinyplanetButton; + public void useRepresentation(FilterRepresentation representation) { + FilterTinyPlanetRepresentation parameters = (FilterTinyPlanetRepresentation) representation; + mParameters = parameters; } @Override - public int getTextId() { - return R.string.tinyplanet; - } - - public void setAngle(float angle) { - mAngle = angle; - } - - public float getAngle() { - return mAngle; - } - - public boolean isNil() { - // TinyPlanet always has an effect - return false; + public FilterRepresentation getDefaultRepresentation() { + return new FilterTinyPlanetRepresentation(); } native protected void nativeApplyFilter( @@ -88,7 +72,7 @@ public class ImageFilterTinyPlanet extends ImageFilter { float angle); @Override - public Bitmap apply(Bitmap bitmapIn, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap bitmapIn, float scaleFactor, int quality) { int w = bitmapIn.getWidth(); int h = bitmapIn.getHeight(); int outputSize = (int) (w / 2f); @@ -110,11 +94,38 @@ public class ImageFilterTinyPlanet extends ImageFilter { } catch (java.lang.OutOfMemoryError e) { System.gc(); outputSize /= 2; - Log.v(TAG, "No memory to create Full Tiny Planet create half"); + Log.v(LOGTAG, "No memory to create Full Tiny Planet create half"); } } nativeApplyFilter(bitmapIn, bitmapIn.getWidth(), bitmapIn.getHeight(), mBitmapOut, - outputSize, mParameter / 100f, mAngle); + outputSize, mParameters.getZoom() / 100f, mParameters.getAngle()); + + if (true) { + // TODO(hoford): FIXME and remove this section + String text = "Tiny Planet Not Working"; + int w2 = bitmapIn.getWidth() / 2; + int h2 = bitmapIn.getHeight() / 2; + Canvas c = new Canvas(bitmapIn); + Paint p = new Paint(); + Rect src = new Rect(0, 0, mBitmapOut.getWidth(), mBitmapOut.getHeight()); + Rect dst = new Rect(0, 0, bitmapIn.getWidth(), bitmapIn.getHeight()); + c.drawBitmap(mBitmapOut, 0, 0, p); + float size = Math.min(w2, h2) / 4f; + p.setTextSize(size); + p.setColor(0xFF000000); + p.setStyle(Paint.Style.STROKE); + p.setStrokeWidth(20); + Rect bounds = new Rect(); + p.getTextBounds(text, 0, text.length(), bounds); + int tw = bounds.width() / 2; + c.drawText(text, w2 - tw, h2, p); + + p.setColor(0xFFFF0000); + p.setStyle(Paint.Style.FILL); + p.setStrokeWidth(0); + + c.drawText(text, w2 - tw, h2, p); + } return mBitmapOut; } @@ -143,7 +154,6 @@ public class ImageFilterTinyPlanet extends ImageFilter { } catch (java.lang.OutOfMemoryError e) { System.gc(); scale /= 2; - Log.v(TAG, "No memory to create Full Tiny Planet create half"); } } Canvas paddedCanvas = new Canvas(paddedBitmap); diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java index 7720d0490..a57af71df 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java @@ -20,30 +20,35 @@ import com.android.gallery3d.R; import android.graphics.Bitmap; -public class ImageFilterVibrance extends ImageFilter { +public class ImageFilterVibrance extends SimpleImageFilter { public ImageFilterVibrance() { mName = "Vibrance"; } - @Override - public int getButtonId() { - return R.id.vibranceButton; - } - - @Override - public int getTextId() { - return R.string.vibrance; + public FilterRepresentation getDefaultRepresentation() { + FilterBasicRepresentation representation = + (FilterBasicRepresentation) super.getDefaultRepresentation(); + representation.setName("Vibrance"); + representation.setFilterClass(ImageFilterVibrance.class); + representation.setTextId(R.string.vibrance); + representation.setButtonId(R.id.vibranceButton); + representation.setMinimum(-100); + representation.setMaximum(100); + representation.setDefaultValue(0); + return representation; } native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float bright); @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { + if (getParameters() == null) { + return bitmap; + } int w = bitmap.getWidth(); int h = bitmap.getHeight(); - int p = mParameter; - float value = p; + float value = getParameters().getValue(); nativeApplyFilter(bitmap, w, h, value); return bitmap; diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java index 3c904fa6c..465d90bfd 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java @@ -19,32 +19,42 @@ package com.android.gallery3d.filtershow.filters; import com.android.gallery3d.R; import android.graphics.Bitmap; +import com.android.gallery3d.app.Log; -public class ImageFilterVignette extends ImageFilter { +public class ImageFilterVignette extends SimpleImageFilter { + + private static final String LOGTAG = "ImageFilterVignette"; public ImageFilterVignette() { - setFilterType(TYPE_VIGNETTE); mName = "Vignette"; } - @Override - public int getButtonId() { - return R.id.vignetteButton; - } + 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); - @Override - public int getTextId() { - return R.string.vignette; + representation.setMinimum(-100); + representation.setMaximum(100); + representation.setDefaultValue(0); + + return representation; } native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, float strength); @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { + if (getParameters() == null) { + return bitmap; + } int w = bitmap.getWidth(); int h = bitmap.getHeight(); - float p = mParameter; - float value = p / 100.0f; + float value = getParameters().getValue() / 100.0f; nativeApplyFilter(bitmap, w, h, value); return bitmap; diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java index 8665dc54c..2f4852306 100644 --- a/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java @@ -17,6 +17,7 @@ package com.android.gallery3d.filtershow.filters; import com.android.gallery3d.R; +import com.android.gallery3d.filtershow.editors.ImageOnlyEditor; import android.graphics.Bitmap; @@ -24,36 +25,34 @@ public class ImageFilterWBalance extends ImageFilter { private static final String TAG = "ImageFilterWBalance"; public ImageFilterWBalance() { - setFilterType(TYPE_WBALANCE); mName = "WBalance"; } - @Override - public int getButtonId() { - return R.id.wbalanceButton; + public FilterRepresentation getDefaultRepresentation() { + FilterRepresentation representation = new FilterDirectRepresentation("WBalance"); + representation.setFilterClass(ImageFilterWBalance.class); + representation.setPriority(FilterRepresentation.TYPE_WBALANCE); + representation.setTextId(R.string.wbalance); + representation.setButtonId(R.id.wbalanceButton); + representation.setShowEditingControls(false); + representation.setShowParameterValue(false); + representation.setEditorId(ImageOnlyEditor.ID); + return representation; } @Override - public int getTextId() { - return R.string.wbalance; - } + public void useRepresentation(FilterRepresentation representation) { - public boolean showEditingControls() { - return false; } native protected void nativeApplyFilter(Bitmap bitmap, int w, int h, int locX, int locY); @Override - public Bitmap apply(Bitmap bitmap, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { int w = bitmap.getWidth(); int h = bitmap.getHeight(); nativeApplyFilter(bitmap, w, h, -1, -1); return bitmap; } - @Override - public boolean isNil() { - return false; - } } diff --git a/src/com/android/gallery3d/filtershow/filters/RedEyeCandidate.java b/src/com/android/gallery3d/filtershow/filters/RedEyeCandidate.java index 58d3afa3b..a40d4fa3b 100644 --- a/src/com/android/gallery3d/filtershow/filters/RedEyeCandidate.java +++ b/src/com/android/gallery3d/filtershow/filters/RedEyeCandidate.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ package com.android.gallery3d.filtershow.filters; import android.graphics.RectF; -public class RedEyeCandidate { +public class RedEyeCandidate implements FilterPoint { RectF mRect = new RectF(); RectF mBounds = new RectF(); diff --git a/src/com/android/gallery3d/filtershow/filters/SimpleImageFilter.java b/src/com/android/gallery3d/filtershow/filters/SimpleImageFilter.java new file mode 100644 index 000000000..922a16a0f --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/SimpleImageFilter.java @@ -0,0 +1,46 @@ +/* + * 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 android.graphics.Bitmap; + +public class SimpleImageFilter extends ImageFilter { + + private FilterBasicRepresentation mParameters; + + public FilterRepresentation getDefaultRepresentation() { + FilterRepresentation representation = new FilterBasicRepresentation("Default", 0, 50, 100); + representation.setShowParameterValue(true); + return representation; + } + + public void useRepresentation(FilterRepresentation representation) { + FilterBasicRepresentation parameters = (FilterBasicRepresentation) representation; + mParameters = parameters; + } + + public FilterBasicRepresentation getParameters() { + return mParameters; + } + + @Override + public Bitmap iconApply(Bitmap bitmap, float scaleFactor, int quality) { + FilterRepresentation representation = getDefaultRepresentation(); + this.useRepresentation(representation); + return apply(bitmap, scaleFactor, quality); + } +} diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryListener.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryListener.java new file mode 100644 index 000000000..549c2e7a5 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryListener.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.filtershow.imageshow; + +public interface GeometryListener { + public void geometryChanged(); +} diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java index b53284061..a3645d6f5 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java +++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryMetadata.java @@ -86,12 +86,12 @@ public class GeometryMetadata { return false; } - public Bitmap apply(Bitmap original, float scaleFactor, boolean highQuality) { + public Bitmap apply(Bitmap original, float scaleFactor, int quality) { if (!hasModifications()) { return original; } mImageFilter.setGeometryMetadata(this); - Bitmap m = mImageFilter.apply(original, scaleFactor, highQuality); + Bitmap m = mImageFilter.apply(original, scaleFactor, quality); return m; } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageDraw.java b/src/com/android/gallery3d/filtershow/imageshow/ImageDraw.java index 16076fde9..0cd229968 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageDraw.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageDraw.java @@ -5,15 +5,14 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Paint.Style; -import android.graphics.RectF; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; +import com.android.gallery3d.filtershow.editors.EditorDraw; +import com.android.gallery3d.filtershow.filters.FilterDrawRepresentation; import com.android.gallery3d.filtershow.filters.ImageFilterDraw; -import com.android.gallery3d.filtershow.filters.RedEyeCandidate; public class ImageDraw extends ImageShow { @@ -21,20 +20,36 @@ public class ImageDraw extends ImageShow { private int mCurrentColor = Color.RED; final static float INITAL_STROKE_RADIUS = 40; private float mCurrentSize = INITAL_STROKE_RADIUS; + private byte mType = 0; + private FilterDrawRepresentation mFRep; + private EditorDraw mEditorDraw; public ImageDraw(Context context, AttributeSet attrs) { super(context, attrs); + resetParameter(); } public ImageDraw(Context context) { super(context); + resetParameter(); + } + + public void setEditor(EditorDraw editorDraw) { + mEditorDraw = editorDraw; + } + public void setFilterDrawRepresentation(FilterDrawRepresentation fr) { + mFRep = fr; + } + + public Drawable getIcon(Context context) { + + return null; } @Override public void resetParameter() { - ImageFilterDraw filter = (ImageFilterDraw) getCurrentFilter(); - if (filter != null) { - filter.clear(); + if (mFRep != null) { + mFRep.clear(); } } @@ -46,9 +61,12 @@ public class ImageDraw extends ImageShow { mCurrentSize = size; } - public void setStyle(char style) { - ImageFilterDraw filter = (ImageFilterDraw) getCurrentFilter(); - filter.setStyle(style); + public void setStyle(byte style) { + mType = (byte) (style % ImageFilterDraw.NUMBER_OF_STYLES); + } + + public int getStyle() { + return mType; } public int getSize() { @@ -71,11 +89,10 @@ public class ImageDraw extends ImageShow { ImageFilterDraw filter = (ImageFilterDraw) getCurrentFilter(); if (event.getAction() == MotionEvent.ACTION_DOWN) { - mTmpPoint[0] = event.getX(); mTmpPoint[1] = event.getY(); mToOrig.mapPoints(mTmpPoint); - filter.startSection(mCurrentColor, mCurrentSize, mTmpPoint[0], mTmpPoint[1]); + mFRep.startNewSection(mType, mCurrentColor, mCurrentSize, mTmpPoint[0], mTmpPoint[1]); } if (event.getAction() == MotionEvent.ACTION_MOVE) { @@ -87,7 +104,7 @@ public class ImageDraw extends ImageShow { mTmpPoint[0] = event.getHistoricalX(p, h); mTmpPoint[1] = event.getHistoricalY(p, h); mToOrig.mapPoints(mTmpPoint); - filter.addPoint(mTmpPoint[0], mTmpPoint[1]); + mFRep.addPoint(mTmpPoint[0], mTmpPoint[1]); } } } @@ -95,10 +112,9 @@ public class ImageDraw extends ImageShow { mTmpPoint[0] = event.getX(); mTmpPoint[1] = event.getY(); mToOrig.mapPoints(mTmpPoint); - filter.endSection(mTmpPoint[0], mTmpPoint[1]); - this.resetImageCaches(this); - + mFRep.endSection(mTmpPoint[0], mTmpPoint[1]); } + mEditorDraw.commitLocalRepresentation(); invalidate(); return true; } @@ -122,11 +138,8 @@ public class ImageDraw extends ImageShow { @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); - ImageFilterDraw filter = (ImageFilterDraw) getCurrentFilter(); - if (filter != null) { - calcScreenMapping(); - filter.draw(canvas, mRotateToScreen); - } + calcScreenMapping(); + } } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java index 3d73ef686..e18f0d034 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageGeometry.java @@ -29,6 +29,7 @@ import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; +import com.android.gallery3d.app.Log; import com.android.gallery3d.filtershow.imageshow.GeometryMetadata.FLIP; import com.android.gallery3d.filtershow.presets.ImagePreset; @@ -241,6 +242,7 @@ public abstract class ImageGeometry extends ImageShow { super.onVisibilityChanged(changedView, visibility); if (visibility == View.VISIBLE) { mVisibilityGained = true; + MasterImage.getImage().invalidateFiltersOnly(); syncLocalToMasterGeometry(); updateScale(); gainedVisibility(); @@ -326,7 +328,7 @@ public abstract class ImageGeometry extends ImageShow { } public void saveAndSetPreset() { - ImagePreset lastHistoryItem = mMasterImage.getHistory().getLast(); + ImagePreset lastHistoryItem = MasterImage.getImage().getHistory().getLast(); if (lastHistoryItem != null && lastHistoryItem.historyName().equalsIgnoreCase(getName())) { getImagePreset().setGeometry(mLocalGeometry); resetImageCaches(this); @@ -336,7 +338,7 @@ public abstract class ImageGeometry extends ImageShow { copy.setGeometry(mLocalGeometry); copy.setHistoryName(getName()); copy.setIsFx(false); - mMasterImage.setPreset(copy, true); + MasterImage.getImage().setPreset(copy, true); } } invalidate(); diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImagePoint.java b/src/com/android/gallery3d/filtershow/imageshow/ImagePoint.java new file mode 100644 index 000000000..06b055d3c --- /dev/null +++ b/src/com/android/gallery3d/filtershow/imageshow/ImagePoint.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.filtershow.imageshow; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; + +import com.android.gallery3d.filtershow.editors.EditorRedEye; +import com.android.gallery3d.filtershow.filters.FilterPoint; +import com.android.gallery3d.filtershow.filters.FilterRedEyeRepresentation; +import com.android.gallery3d.filtershow.filters.ImageFilterRedEye; +import com.android.gallery3d.filtershow.filters.RedEyeCandidate; + +public abstract class ImagePoint extends ImageShow { + + private static final String LOGTAG = "ImageRedEyes"; + protected EditorRedEye mEditorRedEye; + protected FilterRedEyeRepresentation mRedEyeRep; + protected static float mTouchPadding = 80; + + public static void setTouchPadding(float padding) { + mTouchPadding = padding; + } + + public ImagePoint(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ImagePoint(Context context) { + super(context); + } + + @Override + public void resetParameter() { + ImageFilterRedEye filter = (ImageFilterRedEye) getCurrentFilter(); + if (filter != null) { + filter.clear(); + } + invalidate(); + } + + @Override + public void updateImage() { + super.updateImage(); + invalidate(); + } + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + Paint paint = new Paint(); + paint.setStyle(Style.STROKE); + 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()); + if (mRedEyeRep != null) { + for (FilterPoint candidate : mRedEyeRep.getCandidates()) { + drawPoint(candidate, canvas, originalToScreen, originalRotateToScreen, paint); + } + } + } + + protected abstract void drawPoint( + FilterPoint candidate, Canvas canvas, Matrix originalToScreen, + Matrix originalRotateToScreen, Paint paint); + + public void setEditor(EditorRedEye editorRedEye) { + mEditorRedEye = editorRedEye; + } + + public void setRepresentation(FilterRedEyeRepresentation redEyeRep) { + mRedEyeRep = redEyeRep; + } +} diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageRedEyes.java b/src/com/android/gallery3d/filtershow/imageshow/ImageRedEye.java index c012ff1cc..c3ff5e151 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageRedEyes.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageRedEye.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.android.gallery3d.filtershow.imageshow; @@ -8,54 +23,32 @@ import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.RectF; -import android.util.AttributeSet; import android.view.MotionEvent; -import com.android.gallery3d.filtershow.filters.ImageFilterRedEye; +import com.android.gallery3d.filtershow.filters.FilterPoint; import com.android.gallery3d.filtershow.filters.RedEyeCandidate; -public class ImageRedEyes extends ImageShow { - +public class ImageRedEye extends ImagePoint { private static final String LOGTAG = "ImageRedEyes"; private RectF mCurrentRect = null; - private static float mTouchPadding = 80; - - public static void setTouchPadding(float padding) { - mTouchPadding = padding; - } - - public ImageRedEyes(Context context, AttributeSet attrs) { - super(context, attrs); - } - public ImageRedEyes(Context context) { + public ImageRedEye(Context context) { super(context); } @Override public void resetParameter() { - ImageFilterRedEye filter = (ImageFilterRedEye) getCurrentFilter(); - if (filter != null) { - filter.clear(); - } - mCurrentRect = null; + super.resetParameter(); invalidate(); } @Override - public void updateImage() { - super.updateImage(); - invalidate(); - } - @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); float ex = event.getX(); float ey = event.getY(); - ImageFilterRedEye filter = (ImageFilterRedEye) getCurrentFilter(); - // let's transform (ex, ey) to displayed image coordinates if (event.getAction() == MotionEvent.ACTION_DOWN) { mCurrentRect = new RectF(); @@ -87,11 +80,12 @@ public class ImageRedEyes extends ImageShow { invert.reset(); originalNoRotateToScreen.invert(invert); invert.mapRect(r2); - filter.addRect(r, r2); + mRedEyeRep.addRect(r, r2); this.resetImageCaches(this); } mCurrentRect = null; } + mEditorRedEye.commitLocalRepresentation(); invalidate(); return true; } @@ -108,41 +102,35 @@ public class ImageRedEyes extends ImageShow { RectF drawRect = new RectF(mCurrentRect); canvas.drawRect(drawRect, paint); } + } - GeometryMetadata geo = getImagePreset().mGeoData; - Matrix originalToScreen = geo.getOriginalToScreen(false, - mImageLoader.getOriginalBounds().width(), - mImageLoader.getOriginalBounds().height(), getWidth(), getHeight()); - Matrix originalRotateToScreen = geo.getOriginalToScreen(true, - mImageLoader.getOriginalBounds().width(), - mImageLoader.getOriginalBounds().height(), getWidth(), getHeight()); - - ImageFilterRedEye filter = (ImageFilterRedEye) getCurrentFilter(); - for (RedEyeCandidate candidate : filter.getCandidates()) { - RectF rect = candidate.getRect(); - RectF drawRect = new RectF(); - originalToScreen.mapRect(drawRect, rect); - RectF fullRect = new RectF(); - originalRotateToScreen.mapRect(fullRect, rect); - paint.setColor(Color.BLUE); - canvas.drawRect(fullRect, paint); - canvas.drawLine(fullRect.centerX(), fullRect.top, - fullRect.centerX(), fullRect.bottom, paint); - canvas.drawLine(fullRect.left, fullRect.centerY(), - fullRect.right, fullRect.centerY(), paint); - paint.setColor(Color.GREEN); - float dw = drawRect.width(); - float dh = drawRect.height(); - float dx = fullRect.centerX() - dw/2; - float dy = fullRect.centerY() - dh/2; - drawRect.set(dx, dy, dx + dw, dy + dh); - canvas.drawRect(drawRect, paint); - canvas.drawLine(drawRect.centerX(), drawRect.top, - drawRect.centerX(), drawRect.bottom, paint); - canvas.drawLine(drawRect.left, drawRect.centerY(), - drawRect.right, drawRect.centerY(), paint); - canvas.drawCircle(drawRect.centerX(), drawRect.centerY(), - mTouchPadding, paint); - } + @Override + protected void drawPoint(FilterPoint point, Canvas canvas, Matrix originalToScreen, + Matrix originalRotateToScreen, Paint paint) { + RedEyeCandidate candidate = (RedEyeCandidate) point; + RectF rect = candidate.getRect(); + RectF drawRect = new RectF(); + originalToScreen.mapRect(drawRect, rect); + RectF fullRect = new RectF(); + originalRotateToScreen.mapRect(fullRect, rect); + paint.setColor(Color.BLUE); + canvas.drawRect(fullRect, paint); + canvas.drawLine(fullRect.centerX(), fullRect.top, + fullRect.centerX(), fullRect.bottom, paint); + canvas.drawLine(fullRect.left, fullRect.centerY(), + fullRect.right, fullRect.centerY(), paint); + paint.setColor(Color.GREEN); + float dw = drawRect.width(); + float dh = drawRect.height(); + float dx = fullRect.centerX() - dw / 2; + float dy = fullRect.centerY() - dh / 2; + drawRect.set(dx, dy, dx + dw, dy + dh); + canvas.drawRect(drawRect, paint); + canvas.drawLine(drawRect.centerX(), drawRect.top, + drawRect.centerX(), drawRect.bottom, paint); + canvas.drawLine(drawRect.left, drawRect.centerY(), + drawRect.right, drawRect.centerY(), paint); + canvas.drawCircle(drawRect.centerX(), drawRect.centerY(), + mTouchPadding, paint); } } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java index 93c4622fa..463756839 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java @@ -50,7 +50,6 @@ public class ImageShow extends View implements OnGestureListener, SliderListener, OnSeekBarChangeListener { - protected MasterImage mMasterImage = MasterImage.getImage(); private static final String LOGTAG = "ImageShow"; protected Paint mPaint = new Paint(); @@ -141,12 +140,6 @@ public class ImageShow extends View implements OnGestureListener, if (mSeekBar != null) { mSeekBar.setOnSeekBarChangeListener(this); } - if (getCurrentFilter() != null) { - int parameter = getCurrentFilter().getParameter(); - int maxp = getCurrentFilter().getMaxParameter(); - int minp = getCurrentFilter().getMinParameter(); - updateSeekBar(parameter, minp, maxp); - } } private int parameterToUI(int parameter, int minp, int maxp, int uimax) { @@ -178,11 +171,7 @@ public class ImageShow extends View implements OnGestureListener, } public void resetParameter() { - ImageFilter currentFilter = getCurrentFilter(); - if (currentFilter != null) { - updateSeekBar(currentFilter.getDefaultParameter(), - getCurrentFilter().getMinParameter(), getCurrentFilter().getMaxParameter()); - } + // TODO: implement reset } public void setPanelController(PanelController controller) { @@ -195,18 +184,8 @@ public class ImageShow extends View implements OnGestureListener, @Override public void onNewValue(int parameter) { - int maxp = ImageFilter.DEFAULT_MAX_PARAMETER; - int minp = ImageFilter.DEFAULT_MIN_PARAMETER; - if (getCurrentFilter() != null) { - if (getCurrentFilter().getParameter() == parameter) { - return; - } - getCurrentFilter().setParameter(parameter); - maxp = getCurrentFilter().getMaxParameter(); - minp = getCurrentFilter().getMinParameter(); - } if (getImagePreset() != null) { - getImagePreset().fillImageStateAdapter(mMasterImage.getState()); + getImagePreset().fillImageStateAdapter(MasterImage.getImage().getState()); } if (getPanelController() != null) { getPanelController().onNewValue(parameter); @@ -231,7 +210,7 @@ public class ImageShow extends View implements OnGestureListener, setupGestureDetector(context); mActivity = (FilterShowActivity) context; - mMasterImage.addObserver(this); + MasterImage.getImage().addObserver(this); } public ImageShow(Context context) { @@ -239,7 +218,7 @@ public class ImageShow extends View implements OnGestureListener, setupGestureDetector(context); mActivity = (FilterShowActivity) context; - mMasterImage.addObserver(this); + MasterImage.getImage().addObserver(this); } public void setupGestureDetector(Context context) { @@ -258,7 +237,7 @@ public class ImageShow extends View implements OnGestureListener, } public ImageFilter getCurrentFilter() { - return mMasterImage.getCurrentFilter(); + return MasterImage.getImage().getCurrentFilter(); } public void showToast(String text) { @@ -295,7 +274,7 @@ public class ImageShow extends View implements OnGestureListener, } public ImagePreset getImagePreset() { - return mMasterImage.getPreset(); + return MasterImage.getImage().getPreset(); } public void drawToast(Canvas canvas) { @@ -350,19 +329,19 @@ public class ImageShow extends View implements OnGestureListener, if (mImageLoader == null) { return; } - mMasterImage.updatePresets(true); + MasterImage.getImage().updatePresets(true); } public Bitmap getFiltersOnlyImage() { - return mMasterImage.getFiltersOnlyImage(); + return MasterImage.getImage().getFiltersOnlyImage(); } public Bitmap getGeometryOnlyImage() { - return mMasterImage.getGeometryOnlyImage(); + return MasterImage.getImage().getGeometryOnlyImage(); } public Bitmap getFilteredImage() { - return mMasterImage.getFilteredImage(); + return MasterImage.getImage().getFilteredImage(); } public void drawImage(Canvas canvas, Bitmap image) { @@ -427,8 +406,12 @@ public class ImageShow extends View implements OnGestureListener, paint.setTextSize(mOriginalTextSize); paint.getTextBounds(mOriginalText, 0, mOriginalText.length(), bounds); paint.setColor(Color.BLACK); - canvas.drawText(mOriginalText, mImageBounds.left + mOriginalTextMargin + 1, - mImageBounds.top + bounds.height() + mOriginalTextMargin + 1, paint); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(3); + canvas.drawText(mOriginalText, mImageBounds.left + mOriginalTextMargin, + mImageBounds.top + bounds.height() + mOriginalTextMargin, paint); + paint.setStyle(Paint.Style.FILL); + paint.setStrokeWidth(1); paint.setColor(Color.WHITE); canvas.drawText(mOriginalText, mImageBounds.left + mOriginalTextMargin, mImageBounds.top + bounds.height() + mOriginalTextMargin, paint); @@ -484,7 +467,7 @@ public class ImageShow extends View implements OnGestureListener, mImageLoader = loader; if (mImageLoader != null) { mImageLoader.addListener(this); - mMasterImage.setImageLoader(mImageLoader); + MasterImage.getImage().setImageLoader(mImageLoader); } } @@ -616,12 +599,6 @@ public class ImageShow extends View implements OnGestureListener, @Override public void onProgressChanged(SeekBar arg0, int progress, boolean arg2) { int parameter = progress; - if (getCurrentFilter() != null) { - int maxp = getCurrentFilter().getMaxParameter(); - int minp = getCurrentFilter().getMinParameter(); - parameter = uiToParameter(progress, minp, maxp, arg0.getMax()); - } - onNewValue(parameter); } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageTinyPlanet.java b/src/com/android/gallery3d/filtershow/imageshow/ImageTinyPlanet.java index 82d87214f..3e95d4e15 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageTinyPlanet.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageTinyPlanet.java @@ -17,9 +17,14 @@ package com.android.gallery3d.filtershow.imageshow; import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; import android.util.AttributeSet; import android.view.MotionEvent; +import com.android.gallery3d.filtershow.editors.EditorTinyPlanet; +import com.android.gallery3d.filtershow.filters.FilterCurvesRepresentation; +import com.android.gallery3d.filtershow.filters.FilterTinyPlanetRepresentation; import com.android.gallery3d.filtershow.filters.ImageFilterTinyPlanet; public class ImageTinyPlanet extends ImageShow { @@ -31,6 +36,8 @@ public class ImageTinyPlanet extends ImageShow { private float mCenterX = 0; private float mCenterY = 0; private float mStartAngle = 0; + private FilterTinyPlanetRepresentation mTinyPlanetRep; + private EditorTinyPlanet mEditorTinyPlanet; public ImageTinyPlanet(Context context) { super(context); @@ -60,7 +67,6 @@ public class ImageTinyPlanet extends ImageShow { @Override public boolean onTouchEvent(MotionEvent event) { - ImageFilterTinyPlanet filter = (ImageFilterTinyPlanet) getCurrentFilter(); float x = event.getX(); float y = event.getY(); mCurrentX = x; @@ -71,15 +77,30 @@ public class ImageTinyPlanet extends ImageShow { case (MotionEvent.ACTION_DOWN): mTouchCenterX = x; mTouchCenterY = y; - mStartAngle = filter.getAngle(); + mStartAngle = mTinyPlanetRep.getAngle(); break; case (MotionEvent.ACTION_UP): case (MotionEvent.ACTION_MOVE): - filter.setAngle(mStartAngle + getCurrentTouchAngle()); + mTinyPlanetRep.setAngle(mStartAngle + getCurrentTouchAngle()); break; } resetImageCaches(this); invalidate(); + mEditorTinyPlanet.commitLocalRepresentation(); return true; } + + public void setRepresentation(FilterTinyPlanetRepresentation tinyPlanetRep) { + mTinyPlanetRep = tinyPlanetRep; + } + + public void setEditor(EditorTinyPlanet editorTinyPlanet) { + mEditorTinyPlanet = editorTinyPlanet; + } + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + } + } diff --git a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java index 5e9ec7a7a..3172c79dc 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java +++ b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java @@ -19,23 +19,21 @@ package com.android.gallery3d.filtershow.imageshow; import android.graphics.Bitmap; import android.graphics.RectF; -import com.android.gallery3d.app.Log; import com.android.gallery3d.filtershow.FilterShowActivity; import com.android.gallery3d.filtershow.HistoryAdapter; import com.android.gallery3d.filtershow.ImageStateAdapter; -import com.android.gallery3d.filtershow.cache.TripleBufferBitmap; -import com.android.gallery3d.filtershow.cache.FilteringPipeline; -import com.android.gallery3d.filtershow.cache.ImageLoader; +import com.android.gallery3d.filtershow.cache.*; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; import com.android.gallery3d.filtershow.filters.ImageFilter; import com.android.gallery3d.filtershow.presets.ImagePreset; import java.util.Vector; -public class MasterImage { +public class MasterImage implements RenderingRequestCaller { private static final String LOGTAG = "MasterImage"; - private static MasterImage sMasterImage = new MasterImage(); + private static MasterImage sMasterImage = null; private ImageFilter mCurrentFilter = null; private ImagePreset mPreset = null; @@ -43,8 +41,9 @@ public class MasterImage { private ImagePreset mFiltersOnlyPreset = null; private TripleBufferBitmap mFilteredPreview = new TripleBufferBitmap(); - private TripleBufferBitmap mGeometryOnlyPreview = new TripleBufferBitmap(); - private TripleBufferBitmap mFiltersOnlyPreview = new TripleBufferBitmap(); + + private Bitmap mGeometryOnlyBitmap = null; + private Bitmap mFiltersOnlyBitmap = null; private ImageLoader mLoader = null; private HistoryAdapter mHistory = null; @@ -53,14 +52,25 @@ public class MasterImage { private FilterShowActivity mActivity = null; private Vector<ImageShow> mObservers = new Vector<ImageShow>(); + private FilterRepresentation mCurrentFilterRepresentation; + private Vector<GeometryListener> mGeometryListeners = new Vector<GeometryListener>(); + + private GeometryMetadata mPreviousGeometry = null; - private MasterImage() { } + private MasterImage() { + } public static MasterImage getImage() { + if (sMasterImage == null) { + sMasterImage = new MasterImage(); + } return sMasterImage; } public void addObserver(ImageShow observer) { + if (mObservers.contains(observer)) { + return; + } mObservers.add(observer); } @@ -68,6 +78,10 @@ public class MasterImage { mActivity = activity; } + public ImageLoader getLoader() { + return mLoader; + } + public synchronized ImagePreset getPreset() { return mPreset; } @@ -89,6 +103,11 @@ public class MasterImage { mHistory.addHistoryItem(mPreset); } updatePresets(true); + GeometryMetadata geo = mPreset.mGeoData; + if (!geo.equals(mPreviousGeometry)) { + notifyGeometryChange(); + } + mPreviousGeometry = new GeometryMetadata(geo); } private void setGeometry() { @@ -113,6 +132,7 @@ public class MasterImage { // We need a copy from the history mHistory.setCurrentPreset(position); } + public HistoryAdapter getHistory() { return mHistory; } @@ -133,6 +153,10 @@ public class MasterImage { mLoader = loader; } + public ImageLoader getImageLoader() { + return mLoader; + } + public void setCurrentFilter(ImageFilter filter) { mCurrentFilter = filter; } @@ -152,24 +176,16 @@ public class MasterImage { return mFilteredPreview; } - public TripleBufferBitmap getGeometryOnlyBuffer() { - return mGeometryOnlyPreview; - } - - public TripleBufferBitmap getFiltersOnlyBuffer() { - return mFiltersOnlyPreview; - } - public Bitmap getFilteredImage() { return mFilteredPreview.getConsumer(); } public Bitmap getFiltersOnlyImage() { - return mFiltersOnlyPreview.getConsumer(); + return mFiltersOnlyBitmap; } public Bitmap getGeometryOnlyImage() { - return mGeometryOnlyPreview.getConsumer(); + return mGeometryOnlyBitmap; } public void notifyObservers() { @@ -185,6 +201,8 @@ public class MasterImage { if (mGeometryOnlyPreset == null || !newPreset.same(mGeometryOnlyPreset)) { mGeometryOnlyPreset = newPreset; + RenderingRequest.post(mLoader.getOriginalBitmapLarge(), + mGeometryOnlyPreset, RenderingRequest.GEOMETRY_RENDERING, this); } } if (force || mFiltersOnlyPreset == null) { @@ -193,16 +211,56 @@ public class MasterImage { if (mFiltersOnlyPreset == null || !newPreset.same(mFiltersOnlyPreset)) { mFiltersOnlyPreset = newPreset; + RenderingRequest.post(mLoader.getOriginalBitmapLarge(), + mFiltersOnlyPreset, RenderingRequest.FILTERS_RENDERING, this); } } + invalidatePreview(); mActivity.enableSave(hasModifications()); - updateBuffers(); } - public void updateBuffers() { + public FilterRepresentation getCurrentFilterRepresentation() { + return mCurrentFilterRepresentation; + } + + public void setCurrentFilterRepresentation(FilterRepresentation currentFilterRepresentation) { + mCurrentFilterRepresentation = currentFilterRepresentation; + } + + public void invalidateFiltersOnly() { + mFiltersOnlyPreset = null; + updatePresets(false); + } + + public void invalidatePreview() { + mFilteredPreview.invalidate(); FilteringPipeline.getPipeline().updatePreviewBuffer(); - FilteringPipeline.getPipeline().updateGeometryOnlyPreviewBuffer(); - FilteringPipeline.getPipeline().updateFiltersOnlyPreviewBuffer(); } + @Override + public void available(RenderingRequest request) { + if (request.getBitmap() == null) { + return; + } + if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) { + mGeometryOnlyBitmap = request.getBitmap(); + } + if (request.getType() == RenderingRequest.FILTERS_RENDERING) { + mFiltersOnlyBitmap = request.getBitmap(); + } + } + + public static void reset() { + sMasterImage = null; + } + + public void addGeometryListener(GeometryListener listener) { + mGeometryListeners.add(listener); + } + + public void notifyGeometryChange() { + for (GeometryListener listener : mGeometryListeners) { + listener.geometryChanged(); + } + } } diff --git a/src/com/android/gallery3d/filtershow/presets/ImagePreset.java b/src/com/android/gallery3d/filtershow/presets/ImagePreset.java index d6a69af3e..84266c55d 100644 --- a/src/com/android/gallery3d/filtershow/presets/ImagePreset.java +++ b/src/com/android/gallery3d/filtershow/presets/ImagePreset.java @@ -21,10 +21,12 @@ import android.util.Log; import com.android.gallery3d.filtershow.ImageStateAdapter; import com.android.gallery3d.filtershow.cache.ImageLoader; +import com.android.gallery3d.filtershow.filters.BaseFiltersManager; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; +import com.android.gallery3d.filtershow.filters.FiltersManager; import com.android.gallery3d.filtershow.filters.ImageFilter; -import com.android.gallery3d.filtershow.filters.ImageFilterRS; import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; -import com.android.gallery3d.filtershow.imageshow.ImageShow; +import com.android.gallery3d.filtershow.imageshow.MasterImage; import java.util.Vector; @@ -32,13 +34,16 @@ public class ImagePreset { private static final String LOGTAG = "ImagePreset"; - private ImageShow mEndPoint = null; - private ImageFilter mImageBorder = null; + private FilterRepresentation mBorder = null; private float mScaleFactor = 1.0f; - private boolean mIsHighQuality = false; + public static final int QUALITY_ICON = 0; + public static final int QUALITY_PREVIEW = 1; + public static final int QUALITY_FINAL = 2; + private int mQuality = QUALITY_PREVIEW; private ImageLoader mImageLoader = null; - protected Vector<ImageFilter> mFilters = new Vector<ImageFilter>(); + private Vector<FilterRepresentation> mFilters = new Vector<FilterRepresentation>(); + protected String mName = "Original"; private String mHistoryName = "Original"; protected boolean mIsFxPreset = false; @@ -48,10 +53,6 @@ public class ImagePreset { public final GeometryMetadata mGeoData = new GeometryMetadata(); - enum FullRotate { - ZERO, NINETY, HUNDRED_EIGHTY, TWO_HUNDRED_SEVENTY - } - public ImagePreset() { setup(); } @@ -70,13 +71,12 @@ public class ImagePreset { public ImagePreset(ImagePreset source) { try { - if (source.mImageBorder != null) { - mImageBorder = source.mImageBorder.clone(); + if (source.mBorder != null) { + mBorder = source.mBorder.clone(); } for (int i = 0; i < source.mFilters.size(); i++) { - ImageFilter filter = source.mFilters.elementAt(i).clone(); - filter.setImagePreset(this); - add(filter); + FilterRepresentation representation = source.mFilters.elementAt(i).clone(); + addFilter(representation); } } catch (java.lang.CloneNotSupportedException e) { Log.v(LOGTAG, "Exception trying to clone: " + e); @@ -89,6 +89,52 @@ public class ImagePreset { mGeoData.set(source.mGeoData); } + public FilterRepresentation getFilterRepresentation(int position) { + FilterRepresentation representation = null; + try { + representation = mFilters.elementAt(position).clone(); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + return representation; + } + + public int getPositionForRepresentation(FilterRepresentation representation) { + for (int i = 0; i < mFilters.size(); i++) { + if (mFilters.elementAt(i).getFilterClass() == representation.getFilterClass()) { + return i; + } + } + return -1; + } + + public FilterRepresentation getFilterRepresentationCopyFrom(FilterRepresentation filterRepresentation) { + // TODO: add concept of position in the filters (to allow multiple instances) + if (filterRepresentation == null) { + return null; + } + int position = getPositionForRepresentation(filterRepresentation); + if (position == -1) { + return null; + } + FilterRepresentation representation = null; + try { + representation = mFilters.elementAt(position).clone(); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + return representation; + } + + public void updateFilterRepresentation(FilterRepresentation representation) { + synchronized (mFilters) { + int position = getPositionForRepresentation(representation); + FilterRepresentation old = mFilters.elementAt(position); + old.updateTempParametersFrom(representation); + } + MasterImage.getImage().invalidatePreview(); + } + public void setDoApplyGeometry(boolean value) { mDoApplyGeometry = value; } @@ -101,19 +147,19 @@ public class ImagePreset { return mDoApplyFilters; } - public GeometryMetadata getGeometry() { + public synchronized GeometryMetadata getGeometry() { return mGeoData; } public boolean hasModifications() { - if (mImageBorder != null && !mImageBorder.isNil()) { + if (mBorder != null && !mBorder.isNil()) { return true; } if (mGeoData.hasModifications()) { return true; } for (int i = 0; i < mFilters.size(); i++) { - ImageFilter filter = mFilters.elementAt(i); + FilterRepresentation filter = mFilters.elementAt(i); if (!filter.isNil()) { return true; } @@ -122,31 +168,31 @@ public class ImagePreset { } public boolean isPanoramaSafe() { - if (mImageBorder != null && !mImageBorder.isNil()) { + if (mBorder != null && !mBorder.isNil()) { return false; } if (mGeoData.hasModifications()) { return false; } - for (ImageFilter filter : mFilters) { - if (filter.getFilterType() == ImageFilter.TYPE_VIGNETTE - && !filter.isNil()) { + for (FilterRepresentation representation : mFilters) { + if (representation.getPriority() == FilterRepresentation.TYPE_VIGNETTE + && !representation.isNil()) { return false; } - if (filter.getFilterType() == ImageFilter.TYPE_TINYPLANET - && !filter.isNil()) { + if (representation.getPriority() == FilterRepresentation.TYPE_TINYPLANET + && !representation.isNil()) { return false; } } return true; } - public void setGeometry(GeometryMetadata m) { + public synchronized void setGeometry(GeometryMetadata m) { mGeoData.set(m); } - private void setBorder(ImageFilter filter) { - mImageBorder = filter; + private void setBorder(FilterRepresentation filter) { + mBorder = filter; } public boolean isFx() { @@ -180,8 +226,8 @@ public class ImagePreset { } if (mDoApplyFilters && preset.mDoApplyFilters) { for (int i = 0; i < preset.mFilters.size(); i++) { - ImageFilter a = preset.mFilters.elementAt(i); - ImageFilter b = mFilters.elementAt(i); + FilterRepresentation a = preset.mFilters.elementAt(i); + FilterRepresentation b = mFilters.elementAt(i); if (!a.equals(b)) { return false; } @@ -190,14 +236,6 @@ public class ImagePreset { return true; } - public void usePreset(ImagePreset preset) { - for (int i = 0; i < preset.mFilters.size(); i++) { - ImageFilter a = preset.mFilters.elementAt(i); - ImageFilter b = mFilters.elementAt(i); - b.useFilter(a); - } - } - public boolean same(ImagePreset preset) { if (preset == null) { return false; @@ -219,11 +257,11 @@ public class ImagePreset { return false; } - if (mDoApplyGeometry && mImageBorder != preset.mImageBorder) { + if (mDoApplyGeometry && mBorder != preset.mBorder) { return false; } - if (mImageBorder != null && !mImageBorder.same(preset.mImageBorder)) { + if (mBorder != null && !mBorder.equals(preset.mBorder)) { return false; } @@ -235,18 +273,15 @@ public class ImagePreset { if (mDoApplyFilters && preset.mDoApplyFilters) { for (int i = 0; i < preset.mFilters.size(); i++) { - ImageFilter a = preset.mFilters.elementAt(i); - ImageFilter b = mFilters.elementAt(i); + FilterRepresentation a = preset.mFilters.elementAt(i); + FilterRepresentation b = mFilters.elementAt(i); if (!a.same(b)) { return false; } } } - return true; - } - public int nbFilters() { - return mFilters.size(); + return true; } public int similarUpTo(ImagePreset preset) { @@ -255,13 +290,13 @@ public class ImagePreset { } for (int i = 0; i < preset.mFilters.size(); i++) { - ImageFilter a = preset.mFilters.elementAt(i); + FilterRepresentation a = preset.mFilters.elementAt(i); if (i < mFilters.size()) { - ImageFilter b = mFilters.elementAt(i); + FilterRepresentation b = mFilters.elementAt(i); if (!a.same(b)) { return i; } - if (a.getParameter() != b.getParameter()) { + if (!a.equals(b)) { return i; } } else { @@ -279,57 +314,58 @@ public class ImagePreset { return mHistoryName; } - public void add(ImageFilter filter) { + public void showFilters() { + Log.v(LOGTAG, "\\\\\\ showFilters -- " + mFilters.size() + " filters"); + int n = 0; + for (FilterRepresentation representation : mFilters) { + Log.v(LOGTAG, " filter " + n + " : " + representation.toString()); + n++; + } + Log.v(LOGTAG, "/// showFilters -- " + mFilters.size() + " filters"); + } - if (filter.getFilterType() == ImageFilter.TYPE_BORDER) { - setHistoryName(filter.getName()); - setBorder(filter); - } else if (filter.getFilterType() == ImageFilter.TYPE_FX) { + public void addFilter(FilterRepresentation representation) { + Log.v(LOGTAG, "*** Add Filter *** " + representation); + if (representation.getPriority() == FilterRepresentation.TYPE_BORDER) { + setHistoryName(representation.getName()); + setBorder(representation); + } else if (representation.getPriority() == FilterRepresentation.TYPE_FX) { boolean found = false; for (int i = 0; i < mFilters.size(); i++) { - byte type = mFilters.get(i).getFilterType(); + int type = mFilters.elementAt(i).getPriority(); if (found) { - if (type != ImageFilter.TYPE_VIGNETTE) { + if (type != FilterRepresentation.TYPE_VIGNETTE) { mFilters.remove(i); continue; } } - if (type == ImageFilter.TYPE_FX) { + if (type == FilterRepresentation.TYPE_FX) { mFilters.remove(i); - mFilters.add(i, filter); - setHistoryName(filter.getName()); + mFilters.add(i, representation); + setHistoryName(representation.getName()); found = true; } } if (!found) { - mFilters.add(filter); - setHistoryName(filter.getName()); + mFilters.add(representation); + setHistoryName(representation.getName()); } } else { - mFilters.add(filter); - setHistoryName(filter.getName()); + mFilters.add(representation); + setHistoryName(representation.getName()); } - filter.setImagePreset(this); } - public void remove(String filterName) { - ImageFilter filter = getFilter(filterName); - if (filter != null) { - mFilters.remove(filter); - } - } - - public int getCount() { - return mFilters.size(); - } - - public ImageFilter getFilter(String name) { + public FilterRepresentation getRepresentation(FilterRepresentation filterRepresentation) { for (int i = 0; i < mFilters.size(); i++) { - ImageFilter filter = mFilters.elementAt(i); - if (filter.getName().equalsIgnoreCase(name)) { - return filter; + FilterRepresentation representation = mFilters.elementAt(i); + if (representation.getFilterClass() == filterRepresentation.getFilterClass()) { + return representation; } } + if (mBorder != null && mBorder.getFilterClass() == filterRepresentation.getFilterClass()) { + return mBorder; + } return null; } @@ -337,10 +373,6 @@ public class ImagePreset { // do nothing here } - public void setEndpoint(ImageShow image) { - mEndPoint = image; - } - public Bitmap apply(Bitmap original) { Bitmap bitmap = original; bitmap = applyFilters(bitmap, -1, -1); @@ -350,12 +382,16 @@ public class ImagePreset { public Bitmap applyGeometry(Bitmap bitmap) { // Apply any transform -- 90 rotate, flip, straighten, crop // Returns a new bitmap. - return mGeoData.apply(bitmap, mScaleFactor, mIsHighQuality); + return mGeoData.apply(bitmap, mScaleFactor, mQuality); } public Bitmap applyBorder(Bitmap bitmap) { - if (mImageBorder != null && mDoApplyGeometry) { - bitmap = mImageBorder.apply(bitmap, mScaleFactor, mIsHighQuality); + if (mBorder != null && mDoApplyGeometry) { + ImageFilter filter = FiltersManager.getManager().getFilterForRepresentation(mBorder); + mBorder.synchronizeRepresentation(); + filter.useRepresentation(mBorder); + filter.setImagePreset(this); + bitmap = filter.apply(bitmap, mScaleFactor, mQuality); } return bitmap; } @@ -370,8 +406,15 @@ public class ImagePreset { to = mFilters.size(); } for (int i = from; i < to; i++) { - ImageFilter filter = mFilters.elementAt(i); - bitmap = filter.apply(bitmap, mScaleFactor, mIsHighQuality); + FilterRepresentation representation = null; + synchronized (mFilters) { + representation = mFilters.elementAt(i); + representation.synchronizeRepresentation(); + } + ImageFilter filter = FiltersManager.getManager().getFilterForRepresentation(representation); + filter.useRepresentation(representation); + filter.setImagePreset(this); + bitmap = filter.apply(bitmap, mScaleFactor, mQuality); } } @@ -383,6 +426,7 @@ public class ImagePreset { return; } imageStateAdapter.clear(); + // TODO: re-enable the state panel imageStateAdapter.addAll(mFilters); imageStateAdapter.notifyDataSetChanged(); } @@ -391,12 +435,12 @@ public class ImagePreset { return mScaleFactor; } - public boolean isHighQuality() { - return mIsHighQuality; + public int getQuality() { + return mQuality; } - public void setIsHighQuality(boolean value) { - mIsHighQuality = value; + public void setQuality(int value) { + mQuality = value; } public void setScaleFactor(float value) { diff --git a/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java b/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java index 38a9a3a2f..e378fe2b7 100644 --- a/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java +++ b/src/com/android/gallery3d/filtershow/tools/SaveCopyTask.java @@ -168,7 +168,7 @@ public class SaveCopyTask extends AsyncTask<ImagePreset, Void, Uri> { @Override protected Uri doInBackground(ImagePreset... params) { // TODO: Support larger dimensions for photo saving. - if (params[0] == null) { + if (params[0] == null || sourceUri == null) { return null; } ImagePreset preset = params[0]; diff --git a/src/com/android/gallery3d/filtershow/ui/FilterIconButton.java b/src/com/android/gallery3d/filtershow/ui/FilterIconButton.java index 089b24118..de2e1e5dc 100644 --- a/src/com/android/gallery3d/filtershow/ui/FilterIconButton.java +++ b/src/com/android/gallery3d/filtershow/ui/FilterIconButton.java @@ -24,15 +24,26 @@ import android.util.AttributeSet; import android.view.View; import android.widget.LinearLayout; -import com.android.gallery3d.filtershow.FilterShowActivity; -import com.android.gallery3d.filtershow.filters.ImageFilter; +import com.android.gallery3d.filtershow.PanelController; +import com.android.gallery3d.filtershow.cache.ImageLoader; +import com.android.gallery3d.filtershow.cache.RenderingRequest; +import com.android.gallery3d.filtershow.cache.RenderingRequestCaller; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; +import com.android.gallery3d.filtershow.imageshow.GeometryListener; +import com.android.gallery3d.filtershow.imageshow.MasterImage; +import com.android.gallery3d.filtershow.presets.ImagePreset; -public class FilterIconButton extends IconButton implements View.OnClickListener { +public class FilterIconButton extends IconButton implements View.OnClickListener, + RenderingRequestCaller, GeometryListener { + private static final String LOGTAG = "FilterIconButton"; private Bitmap mOverlayBitmap = null; - private FilterShowActivity mController = null; - private ImageFilter mImageFilter = null; + private PanelController mController = null; + private FilterRepresentation mFilterRepresentation = null; private LinearLayout mParentContainer = null; private View.OnClickListener mListener = null; + private Bitmap mIconBitmap = null; + private ImagePreset mPreset = null; + private Rect mDestination = null; public FilterIconButton(Context context) { super(context); @@ -46,30 +57,32 @@ public class FilterIconButton extends IconButton implements View.OnClickListener super(context, attrs, defStyle); } - public void setup(String text, ImageFilter filter, FilterShowActivity controller, - LinearLayout parent) { - mImageFilter = filter; + public void setup(String text, PanelController controller, LinearLayout parent) { mController = controller; setText(text); - - if (mImageFilter.getOverlayBitmaps() != 0) { - mOverlayBitmap = BitmapFactory.decodeResource(getResources(), - mImageFilter.getOverlayBitmaps()); - } - mParentContainer = parent; super.setOnClickListener(this); + MasterImage.getImage().addGeometryListener(this); invalidate(); } @Override protected Bitmap drawImage(Bitmap dst, Bitmap image, Rect destination) { - dst = super.drawImage(dst, image, destination); - dst = mImageFilter.iconApply(dst, 1.0f, false); - if (mOverlayBitmap != null) { - dst = super.drawImage(dst, mOverlayBitmap, destination); + if (mIconBitmap == null && mPreset == null) { + ImageLoader loader = MasterImage.getImage().getLoader(); + if (loader != null) { + ImagePreset geoPreset = new ImagePreset(MasterImage.getImage().getGeometryPreset()); + image = geoPreset.applyGeometry(image); + dst = super.drawImage(dst, image, destination); + ImagePreset mPreset = new ImagePreset(); + mPreset.addFilter(mFilterRepresentation); + mDestination = destination; + RenderingRequest.post(dst.copy(Bitmap.Config.ARGB_8888, true), mPreset, RenderingRequest.ICON_RENDERING, this); + } + return dst; + } else { + return mIconBitmap; } - return dst; } @Override @@ -79,8 +92,8 @@ public class FilterIconButton extends IconButton implements View.OnClickListener @Override public void onClick(View v) { - if (mController != null && mImageFilter != null) { - mController.useFilter(mImageFilter); + if (mController != null) { + mController.useFilterRepresentation(mFilterRepresentation); mParentContainer.dispatchSetSelected(false); setSelected(true); } @@ -89,7 +102,37 @@ public class FilterIconButton extends IconButton implements View.OnClickListener } } - public ImageFilter getImageFilter() { - return mImageFilter; + public FilterRepresentation getFilterRepresentation() { + return mFilterRepresentation; + } + + public void setFilterRepresentation(FilterRepresentation filterRepresentation) { + mFilterRepresentation = filterRepresentation; + if (mFilterRepresentation != null && mFilterRepresentation.getOverlayId() != 0) { + mOverlayBitmap = BitmapFactory.decodeResource(getResources(), + mFilterRepresentation.getOverlayId()); + } + invalidate(); + } + + @Override + public void available(RenderingRequest request) { + if (request.getBitmap() == null) { + return; + } + mIconBitmap = request.getBitmap(); + if (mOverlayBitmap != null) { + mIconBitmap = super.drawImage(mIconBitmap, mOverlayBitmap, mDestination); + } + invalidate(); + stale_icon = true; + } + + @Override + public void geometryChanged() { + stale_icon = true; + mIconBitmap = null; + mPreset = null; + invalidate(); } } diff --git a/src/com/android/gallery3d/filtershow/ui/IconButton.java b/src/com/android/gallery3d/filtershow/ui/IconButton.java index 55dfb8043..28d01dfd9 100644 --- a/src/com/android/gallery3d/filtershow/ui/IconButton.java +++ b/src/com/android/gallery3d/filtershow/ui/IconButton.java @@ -38,7 +38,7 @@ public class IconButton extends Button { protected Bitmap mImageMirror = null; protected Bitmap mIcon = null; - private boolean stale_icon = true; + protected boolean stale_icon = true; public IconButton(Context context) { this(context, null); diff --git a/src/com/android/gallery3d/filtershow/ui/ImageCurves.java b/src/com/android/gallery3d/filtershow/ui/ImageCurves.java index 80c893562..f581fc733 100644 --- a/src/com/android/gallery3d/filtershow/ui/ImageCurves.java +++ b/src/com/android/gallery3d/filtershow/ui/ImageCurves.java @@ -26,7 +26,6 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.os.AsyncTask; import android.util.AttributeSet; -import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.MotionEvent; @@ -35,6 +34,10 @@ import android.widget.LinearLayout; import android.widget.PopupMenu; import com.android.gallery3d.R; +import com.android.gallery3d.filtershow.editors.EditorCurves; +import com.android.gallery3d.filtershow.filters.BaseFiltersManager; +import com.android.gallery3d.filtershow.filters.FilterCurvesRepresentation; +import com.android.gallery3d.filtershow.filters.FiltersManager; import com.android.gallery3d.filtershow.filters.ImageFilterCurves; import com.android.gallery3d.filtershow.imageshow.ImageShow; import com.android.gallery3d.filtershow.presets.ImagePreset; @@ -57,14 +60,18 @@ public class ImageCurves extends ImageShow { Path gHistoPath = new Path(); boolean mDoingTouchMove = false; + private EditorCurves mEditorCurves; + private FilterCurvesRepresentation mFilterCurvesRepresentation; public ImageCurves(Context context) { super(context); + setLayerType(LAYER_TYPE_SOFTWARE, gPaint); resetCurve(); } public ImageCurves(Context context, AttributeSet attrs) { super(context, attrs); + setLayerType(LAYER_TYPE_SOFTWARE, gPaint); resetCurve(); } @@ -127,7 +134,7 @@ public class ImageCurves extends ImageShow { String filterName = getFilterName(); ImagePreset p = getImagePreset(); if (p != null) { - return (ImageFilterCurves) p.getFilter(filterName); + return (ImageFilterCurves) FiltersManager.getManager().getFilter(ImageFilterCurves.class); } return null; } @@ -284,9 +291,11 @@ public class ImageCurves extends ImageShow { } public synchronized void updateCachedImage() { - // update image if (getImagePreset() != null) { resetImageCaches(this); + if (mEditorCurves != null) { + mEditorCurves.commitLocalRepresentation(); + } invalidate(); } } @@ -391,6 +400,15 @@ public class ImageCurves extends ImageShow { break; } } + mEditorCurves.commitLocalRepresentation(); invalidate(); } + + public void setEditor(EditorCurves editorCurves) { + mEditorCurves = editorCurves; + } + + public void setFilterDrawRepresentation(FilterCurvesRepresentation drawRep) { + mFilterCurvesRepresentation = drawRep; + } } diff --git a/src/com/android/gallery3d/filtershow/ui/Spline.java b/src/com/android/gallery3d/filtershow/ui/Spline.java index 83341772b..2cfbd807f 100644 --- a/src/com/android/gallery3d/filtershow/ui/Spline.java +++ b/src/com/android/gallery3d/filtershow/ui/Spline.java @@ -110,6 +110,12 @@ public class Spline { return true; } + public void reset() { + mPoints.clear(); + addPoint(0.0f, 1.0f); + addPoint(1.0f, 0.0f); + } + private void drawHandles(Canvas canvas, Drawable indicator, float centerX, float centerY) { int left = (int) centerX - mCurveHandleSize / 2; int top = (int) centerY - mCurveHandleSize / 2; diff --git a/src/com/android/gallery3d/ingest/IngestActivity.java b/src/com/android/gallery3d/ingest/IngestActivity.java index 4e603bed3..893f59572 100644 --- a/src/com/android/gallery3d/ingest/IngestActivity.java +++ b/src/com/android/gallery3d/ingest/IngestActivity.java @@ -22,11 +22,14 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.res.Configuration; +import android.database.DataSetObserver; import android.mtp.MtpObjectInfo; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; +import android.support.v4.view.ViewPager; import android.util.SparseBooleanArray; import android.view.ActionMode; import android.view.Menu; @@ -36,12 +39,16 @@ import android.view.View; import android.widget.AbsListView.MultiChoiceModeListener; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; -import android.widget.GridView; import android.widget.TextView; import com.android.gallery3d.R; +import com.android.gallery3d.ingest.adapter.CheckBroker; import com.android.gallery3d.ingest.adapter.MtpAdapter; +import com.android.gallery3d.ingest.adapter.MtpPagerAdapter; +import com.android.gallery3d.ingest.data.MtpBitmapFetch; import com.android.gallery3d.ingest.ui.DateTileView; +import com.android.gallery3d.ingest.ui.IngestGridView; +import com.android.gallery3d.ingest.ui.IngestGridView.OnClearChoicesListener; import java.lang.ref.WeakReference; import java.util.Collection; @@ -51,14 +58,22 @@ public class IngestActivity extends Activity implements private IngestService mHelperService; private boolean mActive = false; - private GridView mGridView; + private IngestGridView mGridView; private MtpAdapter mAdapter; private Handler mHandler; private ProgressDialog mProgressDialog; private ActionMode mActiveActionMode; - private View mWarningOverlay; - private TextView mWarningOverlayText; + private View mWarningView; + private TextView mWarningText; + private int mLastCheckedPosition = 0; + + private ViewPager mFullscreenPager; + private MtpPagerAdapter mPagerAdapter; + private boolean mFullscreenPagerVisible = false; + + private MenuItem mMenuSwitcherItem; + private MenuItem mActionMenuSwitcherItem; @Override protected void onCreate(Bundle savedInstanceState) { @@ -66,18 +81,25 @@ public class IngestActivity extends Activity implements doBindHelperService(); setContentView(R.layout.ingest_activity_item_list); - mGridView = (GridView) findViewById(R.id.ingest_gridview); + mGridView = (IngestGridView) findViewById(R.id.ingest_gridview); mAdapter = new MtpAdapter(this); + mAdapter.registerDataSetObserver(mMasterObserver); mGridView.setAdapter(mAdapter); mGridView.setMultiChoiceModeListener(mMultiChoiceModeListener); mGridView.setOnItemClickListener(mOnItemClickListener); + mGridView.setOnClearChoicesListener(mPositionMappingCheckBroker); + + mFullscreenPager = (ViewPager) findViewById(R.id.ingest_view_pager); mHandler = new ItemListHandler(this); + + MtpBitmapFetch.configureForContext(this); } private OnItemClickListener mOnItemClickListener = new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View itemView, int position, long arg3) { + mLastCheckedPosition = position; mGridView.setItemChecked(position, !mGridView.getCheckedItemPositions().get(position)); } }; @@ -124,23 +146,18 @@ public class IngestActivity extends Activity implements mGridView.setItemChecked(i, rangeValue); } + mPositionMappingCheckBroker.onBulkCheckedChange(); mIgnoreItemCheckedStateChanges = false; + } else { + mPositionMappingCheckBroker.onCheckedChange(position, checked); } + mLastCheckedPosition = position; updateSelectedTitle(mode); } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - switch (item.getItemId()) { - case R.id.import_items: - mHelperService.importSelectedItems( - mGridView.getCheckedItemPositions(), - mAdapter); - mode.finish(); - return true; - default: - return false; - } + return onOptionsItemSelected(item); } @Override @@ -149,12 +166,16 @@ public class IngestActivity extends Activity implements inflater.inflate(R.menu.ingest_menu_item_list_selection, menu); updateSelectedTitle(mode); mActiveActionMode = mode; + mActionMenuSwitcherItem = menu.findItem(R.id.ingest_switch_view); + setSwitcherMenuState(mActionMenuSwitcherItem, mFullscreenPagerVisible); return true; } @Override public void onDestroyActionMode(ActionMode mode) { mActiveActionMode = null; + mActionMenuSwitcherItem = null; + mHandler.sendEmptyMessage(ItemListHandler.MSG_BULK_CHECKED_CHANGE); } @Override @@ -164,6 +185,34 @@ public class IngestActivity extends Activity implements } }; + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.import_items: + if (mActiveActionMode != null) { + mHelperService.importSelectedItems( + mGridView.getCheckedItemPositions(), + mAdapter); + mActiveActionMode.finish(); + } + return true; + case R.id.ingest_switch_view: + setFullscreenPagerVisibility(!mFullscreenPagerVisible); + return true; + default: + return false; + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.ingest_menu_item_list_selection, menu); + mMenuSwitcherItem = menu.findItem(R.id.ingest_switch_view); + menu.findItem(R.id.import_items).setVisible(false); + setSwitcherMenuState(mMenuSwitcherItem, mFullscreenPagerVisible); + return true; + } + @Override protected void onDestroy() { super.onDestroy(); @@ -175,7 +224,7 @@ public class IngestActivity extends Activity implements DateTileView.refreshLocale(); mActive = true; if (mHelperService != null) mHelperService.setClientActivity(this); - updateWarningOverlay(); + updateWarningView(); super.onResume(); } @@ -187,31 +236,140 @@ public class IngestActivity extends Activity implements super.onPause(); } - private void showWarningOverlay(int textResId) { - if (mWarningOverlay == null) { - mWarningOverlay = findViewById(R.id.ingest_warning_overlay); - mWarningOverlayText = - (TextView)mWarningOverlay.findViewById(R.id.ingest_warning_overlay_text); + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + MtpBitmapFetch.configureForContext(this); + } + + private void showWarningView(int textResId) { + if (mWarningView == null) { + mWarningView = findViewById(R.id.ingest_warning_view); + mWarningText = + (TextView)mWarningView.findViewById(R.id.ingest_warning_view_text); } - mWarningOverlayText.setText(textResId); - mWarningOverlay.setVisibility(View.VISIBLE); + mWarningText.setText(textResId); + mWarningView.setVisibility(View.VISIBLE); + setFullscreenPagerVisibility(false); mGridView.setVisibility(View.GONE); } - private void hideWarningOverlay() { - if (mWarningOverlay != null) { - mWarningOverlay.setVisibility(View.GONE); - mGridView.setVisibility(View.VISIBLE); + private void hideWarningView() { + if (mWarningView != null) { + mWarningView.setVisibility(View.GONE); + setFullscreenPagerVisibility(false); } } - private void updateWarningOverlay() { + private PositionMappingCheckBroker mPositionMappingCheckBroker = new PositionMappingCheckBroker(); + + private class PositionMappingCheckBroker extends CheckBroker + implements OnClearChoicesListener { + private int mLastMappingPager = -1; + private int mLastMappingGrid = -1; + + private int mapPagerToGridPosition(int position) { + if (position != mLastMappingPager) { + mLastMappingPager = position; + mLastMappingGrid = mAdapter.translatePositionWithoutLabels(position); + } + return mLastMappingGrid; + } + + private int mapGridToPagerPosition(int position) { + if (position != mLastMappingGrid) { + mLastMappingGrid = position; + mLastMappingPager = mPagerAdapter.translatePositionWithLabels(position); + } + return mLastMappingPager; + } + + @Override + public void setItemChecked(int position, boolean checked) { + mGridView.setItemChecked(mapPagerToGridPosition(position), checked); + } + + @Override + public void onCheckedChange(int position, boolean checked) { + if (mPagerAdapter != null) { + super.onCheckedChange(mapGridToPagerPosition(position), checked); + } + } + + @Override + public boolean isItemChecked(int position) { + return mGridView.getCheckedItemPositions().get(mapPagerToGridPosition(position)); + } + + @Override + public void onClearChoices() { + onBulkCheckedChange(); + } + }; + + private DataSetObserver mMasterObserver = new DataSetObserver() { + @Override + public void onChanged() { + if (mPagerAdapter != null) mPagerAdapter.notifyDataSetChanged(); + } + + @Override + public void onInvalidated() { + if (mPagerAdapter != null) mPagerAdapter.notifyDataSetChanged(); + } + }; + + private int pickFullscreenStartingPosition() { + int firstVisiblePosition = mGridView.getFirstVisiblePosition(); + if (mLastCheckedPosition <= firstVisiblePosition + || mLastCheckedPosition > mGridView.getLastVisiblePosition()) { + return firstVisiblePosition; + } else { + return mLastCheckedPosition; + } + } + + private void setSwitcherMenuState(MenuItem menuItem, boolean inFullscreenMode) { + if (menuItem == null) return; + if (!inFullscreenMode) { + menuItem.setIcon(android.R.drawable.ic_menu_zoom); + menuItem.setTitle(R.string.switch_photo_fullscreen); + } else { + menuItem.setIcon(android.R.drawable.ic_dialog_dialer); + menuItem.setTitle(R.string.switch_photo_grid); + } + } + + private void setFullscreenPagerVisibility(boolean visible) { + mFullscreenPagerVisible = visible; + if (visible) { + if (mPagerAdapter == null) { + mPagerAdapter = new MtpPagerAdapter(this, mPositionMappingCheckBroker); + mPagerAdapter.setMtpDeviceIndex(mAdapter.getMtpDeviceIndex()); + } + mFullscreenPager.setAdapter(mPagerAdapter); + mFullscreenPager.setCurrentItem(mPagerAdapter.translatePositionWithLabels( + pickFullscreenStartingPosition()), false); + } else if (mPagerAdapter != null) { + mGridView.setSelection(mAdapter.translatePositionWithoutLabels( + mFullscreenPager.getCurrentItem())); + mFullscreenPager.setAdapter(null); + } + mGridView.setVisibility(visible ? View.INVISIBLE : View.VISIBLE); + mFullscreenPager.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); + if (mActionMenuSwitcherItem != null) { + setSwitcherMenuState(mActionMenuSwitcherItem, visible); + } + setSwitcherMenuState(mMenuSwitcherItem, visible); + } + + private void updateWarningView() { if (!mAdapter.deviceConnected()) { - showWarningOverlay(R.string.ingest_no_device); + showWarningView(R.string.ingest_no_device); } else if (mAdapter.indexReady() && mAdapter.getCount() == 0) { - showWarningOverlay(R.string.ingest_empty_device); + showWarningView(R.string.ingest_empty_device); } else { - hideWarningOverlay(); + hideWarningView(); } } @@ -221,7 +379,7 @@ public class IngestActivity extends Activity implements mActiveActionMode.finish(); mActiveActionMode = null; } - updateWarningOverlay(); + updateWarningView(); } protected void notifyIndexChanged() { @@ -330,6 +488,7 @@ public class IngestActivity extends Activity implements public static final int MSG_PROGRESS_UPDATE = 0; public static final int MSG_PROGRESS_HIDE = 1; public static final int MSG_NOTIFY_CHANGED = 2; + public static final int MSG_BULK_CHECKED_CHANGE = 3; WeakReference<IngestActivity> mParentReference; @@ -352,6 +511,9 @@ public class IngestActivity extends Activity implements case MSG_NOTIFY_CHANGED: parent.UiThreadNotifyIndexChanged(); break; + case MSG_BULK_CHECKED_CHANGE: + parent.mPositionMappingCheckBroker.onBulkCheckedChange(); + break; default: break; } @@ -362,7 +524,9 @@ public class IngestActivity extends Activity implements public void onServiceConnected(ComponentName className, IBinder service) { mHelperService = ((IngestService.LocalBinder) service).getService(); mHelperService.setClientActivity(IngestActivity.this); - mAdapter.setMtpDeviceIndex(mHelperService.getIndex()); + MtpDeviceIndex index = mHelperService.getIndex(); + mAdapter.setMtpDeviceIndex(index); + if (mPagerAdapter != null) mPagerAdapter.setMtpDeviceIndex(index); } public void onServiceDisconnected(ComponentName className) { diff --git a/src/com/android/gallery3d/ingest/IngestService.java b/src/com/android/gallery3d/ingest/IngestService.java index 12b056b60..5e0ca0b68 100644 --- a/src/com/android/gallery3d/ingest/IngestService.java +++ b/src/com/android/gallery3d/ingest/IngestService.java @@ -37,7 +37,7 @@ import android.widget.Adapter; import com.android.gallery3d.R; import com.android.gallery3d.app.NotificationIds; import com.android.gallery3d.data.MtpClient; -import com.android.gallery3d.ingest.ui.MtpBitmapCache; +import com.android.gallery3d.ingest.data.MtpBitmapFetch; import com.android.gallery3d.util.BucketNames; import java.util.ArrayList; @@ -66,6 +66,7 @@ public class IngestService extends Service implements ImportTask.Listener, private boolean mRedeliverImportFinish = false; private Collection<MtpObjectInfo> mRedeliverObjectsNotImported; private boolean mRedeliverNotifyIndexChanged = false; + private boolean mRedeliverIndexFinish = false; private NotificationManager mNotificationManager; private NotificationCompat.Builder mNotificationBuilder; private long mLastProgressIndexTime = 0; @@ -108,13 +109,19 @@ public class IngestService extends Service implements ImportTask.Listener, mRedeliverImportFinish = false; mRedeliverObjectsNotImported = null; mRedeliverNotifyIndexChanged = false; + mRedeliverIndexFinish = false; mDevice = device; mIndex.setDevice(mDevice); if (mDevice != null) { MtpDeviceInfo deviceInfo = mDevice.getDeviceInfo(); - mDevicePrettyName = deviceInfo.getModel(); - mNotificationBuilder.setContentTitle(mDevicePrettyName); - new Thread(mIndex.getIndexRunnable()).start(); + if (deviceInfo == null) { + setDevice(null); + return; + } else { + mDevicePrettyName = deviceInfo.getModel(); + mNotificationBuilder.setContentTitle(mDevicePrettyName); + new Thread(mIndex.getIndexRunnable()).start(); + } } else { mDevicePrettyName = null; } @@ -144,6 +151,10 @@ public class IngestService extends Service implements ImportTask.Listener, mClientActivity.notifyIndexChanged(); mRedeliverNotifyIndexChanged = false; } + if (mRedeliverIndexFinish) { + mClientActivity.onIndexFinish(); + mRedeliverIndexFinish = false; + } } protected void importSelectedItems(SparseBooleanArray selected, Adapter adapter) { @@ -176,8 +187,8 @@ public class IngestService extends Service implements ImportTask.Listener, public void deviceRemoved(MtpDevice device) { if (device == mDevice) { setDevice(null); + MtpBitmapFetch.onDeviceDisconnected(device); } - MtpBitmapCache.onDeviceDisconnected(device); } @Override @@ -197,6 +208,7 @@ public class IngestService extends Service implements ImportTask.Listener, @Override public void onImportFinish(Collection<MtpObjectInfo> objectsNotImported) { + stopForeground(true); if (mClientActivity != null) { mClientActivity.onImportFinish(objectsNotImported); } else { @@ -207,7 +219,6 @@ public class IngestService extends Service implements ImportTask.Listener, mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_IMPORTING, mNotificationBuilder.build()); } - stopForeground(mClientActivity != null); } @Override @@ -241,6 +252,7 @@ public class IngestService extends Service implements ImportTask.Listener, .setContentText(getResources().getText(R.string.ingest_scanning_done)); mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_SCANNING, mNotificationBuilder.build()); + mRedeliverIndexFinish = true; } } diff --git a/src/com/android/gallery3d/ingest/MtpDeviceIndex.java b/src/com/android/gallery3d/ingest/MtpDeviceIndex.java index 28e115ab0..e873dd1ca 100644 --- a/src/com/android/gallery3d/ingest/MtpDeviceIndex.java +++ b/src/com/android/gallery3d/ingest/MtpDeviceIndex.java @@ -180,55 +180,90 @@ public class MtpDeviceIndex { * order */ public Object get(int position, SortOrder order) { + if (mProgress != Progress.Finished) return null; if(order == SortOrder.Ascending) { - return getAscending(position); + DateBucket bucket = mBuckets[mUnifiedLookupIndex[position]]; + if (bucket.unifiedStartIndex == position) { + return bucket.bucket; + } else { + return mMtpObjects[bucket.itemsStartIndex + position - 1 + - bucket.unifiedStartIndex]; + } } else { - return getDescending(position); + int zeroIndex = mUnifiedLookupIndex.length - 1 - position; + DateBucket bucket = mBuckets[mUnifiedLookupIndex[zeroIndex]]; + if (bucket.unifiedEndIndex == zeroIndex) { + return bucket.bucket; + } else { + return mMtpObjects[bucket.itemsStartIndex + zeroIndex + - bucket.unifiedStartIndex]; + } } } /** - * @param position Index of item to fetch, where 0 is the first item in - * ascending order - * @return position-th item in ascending order + * @param position Index of item to fetch from a view of the data that doesn't + * include labels and is in the specified order + * @return position-th item in specified order, when not including labels */ - public Object getAscending(int position) { + public MtpObjectInfo getWithoutLabels(int position, SortOrder order) { if (mProgress != Progress.Finished) return null; - DateBucket bucket = mBuckets[mUnifiedLookupIndex[position]]; - if (bucket.unifiedStartIndex == position) { - return bucket.bucket; + if (order == SortOrder.Ascending) { + return mMtpObjects[position]; } else { - return bucket.get(position - 1 - bucket.unifiedStartIndex); + return mMtpObjects[mMtpObjects.length - 1 - position]; } } /** - * @param position Index of item to fetch, where 0 is the last item in - * ascending order - * @return position-th item in descending order + * Although this is O(log(number of buckets)), and thus should not be used + * in hotspots, even if the attached device has items for every day for + * a five-year timeframe, it would still only take 11 iterations at most, + * so shouldn't be a huge issue. + * @param position Index of item to map from a view of the data that doesn't + * include labels and is in the specified order + * @param order + * @return position in a view of the data that does include labels */ - public Object getDescending(int position) { - if (mProgress != Progress.Finished) return null; - int zeroIndex = mUnifiedLookupIndex.length - 1 - position; - DateBucket bucket = mBuckets[mUnifiedLookupIndex[zeroIndex]]; - if (bucket.unifiedEndIndex == zeroIndex) { - return bucket.bucket; - } else { - return bucket.get(zeroIndex - bucket.unifiedStartIndex); + public int getPositionFromPositionWithoutLabels(int position, SortOrder order) { + if (mProgress != Progress.Finished) return -1; + if (order == SortOrder.Descending) { + position = mMtpObjects.length - 1 - position; + } + int bucketNumber = 0; + int iMin = 0; + int iMax = mBuckets.length - 1; + while (iMax >= iMin) { + int iMid = (iMax + iMin) / 2; + if (mBuckets[iMid].itemsStartIndex + mBuckets[iMid].numItems <= position) { + iMin = iMid + 1; + } else if (mBuckets[iMid].itemsStartIndex > position) { + iMax = iMid - 1; + } else { + bucketNumber = iMid; + break; + } } + int mappedPos = mBuckets[bucketNumber].unifiedStartIndex + + position - mBuckets[bucketNumber].itemsStartIndex; + if (order == SortOrder.Descending) { + mappedPos = mUnifiedLookupIndex.length - 1 - mappedPos; + } + return mappedPos; } - /** - * @param position Index of item to fetch from a view of the data that doesn't - * include labels and is in ascending order - * @return position-th item in ascending order, when not including labels - */ - public MtpObjectInfo getWithoutLabels(int position, SortOrder order) { - if (mProgress != Progress.Finished) return null; - if (order == SortOrder.Ascending) { - return mMtpObjects[position]; + public int getPositionWithoutLabelsFromPosition(int position, SortOrder order) { + if (mProgress != Progress.Finished) return -1; + if(order == SortOrder.Ascending) { + DateBucket bucket = mBuckets[mUnifiedLookupIndex[position]]; + if (bucket.unifiedStartIndex == position) position++; + return bucket.itemsStartIndex + position - 1 - bucket.unifiedStartIndex; } else { - return mMtpObjects[mMtpObjects.length - 1 - position]; + int zeroIndex = mUnifiedLookupIndex.length - 1 - position; + DateBucket bucket = mBuckets[mUnifiedLookupIndex[zeroIndex]]; + if (bucket.unifiedEndIndex == zeroIndex) zeroIndex--; + return mMtpObjects.length - 1 - bucket.itemsStartIndex + - zeroIndex + bucket.unifiedStartIndex; } } @@ -288,6 +323,7 @@ public class MtpDeviceIndex { int unifiedStartIndex; int unifiedEndIndex; int itemsStartIndex; + int numItems; public DateBucket(SimpleDate bucket) { this.bucket = bucket; @@ -302,10 +338,6 @@ public class MtpDeviceIndex { Collections.sort(tempElementsList, comparator); } - public MtpObjectInfo get(int position) { - return mMtpObjects[itemsStartIndex + position]; - } - @Override public String toString() { return bucket.toString(); @@ -413,7 +445,8 @@ public class MtpDeviceIndex { currentUnifiedIndexEntry = nextUnifiedEntry; bucket.itemsStartIndex = currentItemsEntry; - for (int j = 0; j < bucket.tempElementsList.size(); j++) { + bucket.numItems = bucket.tempElementsList.size(); + for (int j = 0; j < bucket.numItems; j++) { mMtpObjects[currentItemsEntry] = bucket.tempElementsList.get(j); currentItemsEntry++; } diff --git a/src/com/android/gallery3d/ingest/adapter/CheckBroker.java b/src/com/android/gallery3d/ingest/adapter/CheckBroker.java new file mode 100644 index 000000000..6783f23c5 --- /dev/null +++ b/src/com/android/gallery3d/ingest/adapter/CheckBroker.java @@ -0,0 +1,56 @@ +/* + * 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.ingest.adapter; + +import java.util.ArrayList; +import java.util.Collection; + +public abstract class CheckBroker { + private Collection<OnCheckedChangedListener> mListeners = + new ArrayList<OnCheckedChangedListener>(); + + public interface OnCheckedChangedListener { + public void onCheckedChanged(int position, boolean isChecked); + public void onBulkCheckedChanged(); + } + + public abstract void setItemChecked(int position, boolean checked); + + public void onCheckedChange(int position, boolean checked) { + if (isItemChecked(position) != checked) { + for (OnCheckedChangedListener l : mListeners) { + l.onCheckedChanged(position, checked); + } + } + } + + public void onBulkCheckedChange() { + for (OnCheckedChangedListener l : mListeners) { + l.onBulkCheckedChanged(); + } + } + + public abstract boolean isItemChecked(int position); + + public void registerOnCheckedChangeListener(OnCheckedChangedListener l) { + mListeners.add(l); + } + + public void unregisterOnCheckedChangeListener(OnCheckedChangedListener l) { + mListeners.remove(l); + } +} diff --git a/src/com/android/gallery3d/ingest/adapter/MtpAdapter.java b/src/com/android/gallery3d/ingest/adapter/MtpAdapter.java index 611d880db..e8dd69f8c 100644 --- a/src/com/android/gallery3d/ingest/adapter/MtpAdapter.java +++ b/src/com/android/gallery3d/ingest/adapter/MtpAdapter.java @@ -45,8 +45,7 @@ public class MtpAdapter extends BaseAdapter implements SectionIndexer { public MtpAdapter(Activity context) { super(); mContext = context; - mInflater = (LayoutInflater)context.getSystemService - (Context.LAYOUT_INFLATER_SERVICE); + mInflater = LayoutInflater.from(context); } public void setMtpDeviceIndex(MtpDeviceIndex index) { @@ -54,6 +53,10 @@ public class MtpAdapter extends BaseAdapter implements SectionIndexer { notifyDataSetChanged(); } + public MtpDeviceIndex getMtpDeviceIndex() { + return mModel; + } + @Override public void notifyDataSetChanged() { mGeneration++; @@ -177,4 +180,13 @@ public class MtpAdapter extends BaseAdapter implements SectionIndexer { public Object[] getSections() { return getCount() > 0 ? mModel.getBuckets(mSortOrder) : null; } + + public SortOrder getSortOrder() { + return mSortOrder; + } + + public int translatePositionWithoutLabels(int position) { + if (mModel == null) return -1; + return mModel.getPositionFromPositionWithoutLabels(position, mSortOrder); + } } diff --git a/src/com/android/gallery3d/ingest/adapter/MtpPagerAdapter.java b/src/com/android/gallery3d/ingest/adapter/MtpPagerAdapter.java new file mode 100644 index 000000000..9e7abc01d --- /dev/null +++ b/src/com/android/gallery3d/ingest/adapter/MtpPagerAdapter.java @@ -0,0 +1,102 @@ +/* + * 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.ingest.adapter; + +import android.content.Context; +import android.mtp.MtpObjectInfo; +import android.support.v4.view.PagerAdapter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.android.gallery3d.R; +import com.android.gallery3d.ingest.MtpDeviceIndex; +import com.android.gallery3d.ingest.MtpDeviceIndex.SortOrder; +import com.android.gallery3d.ingest.ui.MtpFullscreenView; + +public class MtpPagerAdapter extends PagerAdapter { + + private LayoutInflater mInflater; + private int mGeneration = 0; + private CheckBroker mBroker; + private MtpDeviceIndex mModel; + private SortOrder mSortOrder = SortOrder.Descending; + + private MtpFullscreenView mReusableView = null; + + public MtpPagerAdapter(Context context, CheckBroker broker) { + super(); + mInflater = LayoutInflater.from(context); + mBroker = broker; + } + + public void setMtpDeviceIndex(MtpDeviceIndex index) { + mModel = index; + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return mModel != null ? mModel.sizeWithoutLabels() : 0; + } + + @Override + public void notifyDataSetChanged() { + mGeneration++; + super.notifyDataSetChanged(); + } + + public int translatePositionWithLabels(int position) { + if (mModel == null) return -1; + return mModel.getPositionWithoutLabelsFromPosition(position, mSortOrder); + } + + @Override + public void finishUpdate(ViewGroup container) { + mReusableView = null; + super.finishUpdate(container); + } + + @Override + public boolean isViewFromObject(View view, Object object) { + return view == object; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + MtpFullscreenView v = (MtpFullscreenView)object; + container.removeView(v); + mBroker.unregisterOnCheckedChangeListener(v); + mReusableView = v; + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + MtpFullscreenView v; + if (mReusableView != null) { + v = mReusableView; + mReusableView = null; + } else { + v = (MtpFullscreenView) mInflater.inflate(R.layout.ingest_fullsize, container, false); + } + MtpObjectInfo i = mModel.getWithoutLabels(position, mSortOrder); + v.getImageView().setMtpDeviceAndObjectInfo(mModel.getDevice(), i, mGeneration); + v.setPositionAndBroker(position, mBroker); + container.addView(v); + return v; + } +} diff --git a/src/com/android/gallery3d/ingest/data/BitmapWithMetadata.java b/src/com/android/gallery3d/ingest/data/BitmapWithMetadata.java new file mode 100644 index 000000000..bbc90f670 --- /dev/null +++ b/src/com/android/gallery3d/ingest/data/BitmapWithMetadata.java @@ -0,0 +1,29 @@ +/* + * 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.ingest.data; + +import android.graphics.Bitmap; + +public class BitmapWithMetadata { + public Bitmap bitmap; + public int rotationDegrees; + + public BitmapWithMetadata(Bitmap bitmap, int rotationDegrees) { + this.bitmap = bitmap; + this.rotationDegrees = rotationDegrees; + } +} diff --git a/src/com/android/gallery3d/ingest/data/MtpBitmapFetch.java b/src/com/android/gallery3d/ingest/data/MtpBitmapFetch.java new file mode 100644 index 000000000..46a2051be --- /dev/null +++ b/src/com/android/gallery3d/ingest/data/MtpBitmapFetch.java @@ -0,0 +1,99 @@ +/* + * 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.ingest.data; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.mtp.MtpDevice; +import android.mtp.MtpObjectInfo; +import android.util.DisplayMetrics; +import android.view.WindowManager; + +import com.android.camera.Exif; +import com.android.gallery3d.common.Utils; +import com.android.gallery3d.data.BitmapPool; + +import java.util.ArrayList; + +public class MtpBitmapFetch { + private static final int BITMAP_POOL_SIZE = 32; + private static BitmapPool sThumbnailPool = new BitmapPool(BITMAP_POOL_SIZE); + private static int sMaxSize = 0; + + public static void recycleThumbnail(Bitmap b) { + if (b != null) { + sThumbnailPool.recycle(b); + } + } + + public static Bitmap getThumbnail(MtpDevice device, MtpObjectInfo info) { + byte[] imageBytes = device.getThumbnail(info.getObjectHandle()); + if (imageBytes == null) return null; + BitmapFactory.Options o = new BitmapFactory.Options(); + o.inJustDecodeBounds = true; + BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, o); + if (o.outWidth == 0 || o.outHeight == 0) return null; + o.inBitmap = sThumbnailPool.getBitmap(o.outWidth, o.outHeight); + o.inMutable = true; + o.inJustDecodeBounds = false; + o.inSampleSize = 1; + return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, o); + } + + public static BitmapWithMetadata getFullsize(MtpDevice device, MtpObjectInfo info) { + return getFullsize(device, info, sMaxSize); + } + + public static BitmapWithMetadata getFullsize(MtpDevice device, MtpObjectInfo info, int maxSide) { + byte[] imageBytes = device.getObject(info.getObjectHandle(), info.getCompressedSize()); + if (imageBytes == null) return null; + Bitmap created; + if (maxSide > 0) { + BitmapFactory.Options o = new BitmapFactory.Options(); + o.inJustDecodeBounds = true; + BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, o); + int w = o.outWidth; + int h = o.outHeight; + int comp = Math.max(h, w); + int sampleSize = 1; + while ((comp >> 1) >= maxSide) { + comp = comp >> 1; + sampleSize++; + } + o.inSampleSize = sampleSize; + o.inJustDecodeBounds = false; + created = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, o); + } else { + created = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length); + } + if (created == null) return null; + + return new BitmapWithMetadata(created, Exif.getOrientation(imageBytes)); + } + + public static void onDeviceDisconnected(MtpDevice device) { + sThumbnailPool.clear(); + } + + public static void configureForContext(Context context) { + DisplayMetrics metrics = new DisplayMetrics(); + WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); + wm.getDefaultDisplay().getMetrics(metrics); + sMaxSize = Math.max(metrics.heightPixels, metrics.widthPixels); + } +} diff --git a/src/com/android/gallery3d/ingest/ui/IngestGridView.java b/src/com/android/gallery3d/ingest/ui/IngestGridView.java new file mode 100644 index 000000000..c821259fe --- /dev/null +++ b/src/com/android/gallery3d/ingest/ui/IngestGridView.java @@ -0,0 +1,58 @@ +/* + * 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.ingest.ui; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.GridView; + +/** + * This just extends GridView with the ability to listen for calls + * to clearChoices() + */ +public class IngestGridView extends GridView { + + public interface OnClearChoicesListener { + public void onClearChoices(); + } + + private OnClearChoicesListener mOnClearChoicesListener = null; + + public IngestGridView(Context context) { + super(context); + } + + public IngestGridView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public IngestGridView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public void setOnClearChoicesListener(OnClearChoicesListener l) { + mOnClearChoicesListener = l; + } + + @Override + public void clearChoices() { + super.clearChoices(); + if (mOnClearChoicesListener != null) { + mOnClearChoicesListener.onClearChoices(); + } + } +} diff --git a/src/com/android/gallery3d/ingest/ui/MtpBitmapCache.java b/src/com/android/gallery3d/ingest/ui/MtpBitmapCache.java deleted file mode 100644 index 307531d5b..000000000 --- a/src/com/android/gallery3d/ingest/ui/MtpBitmapCache.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.gallery3d.ingest.ui; - -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.mtp.MtpDevice; -import android.util.LruCache; - -public class MtpBitmapCache extends LruCache<Integer, Bitmap> { - private static final int PER_DEVICE_CACHE_MAX_BYTES = 4194304; - private static MtpBitmapCache sInstance; - - public synchronized static MtpBitmapCache getInstanceForDevice(MtpDevice device) { - if (sInstance == null || sInstance.mDevice != device) { - sInstance = new MtpBitmapCache(PER_DEVICE_CACHE_MAX_BYTES, device); - } - return sInstance; - } - - public synchronized static void onDeviceDisconnected(MtpDevice device) { - if (sInstance != null && sInstance.mDevice == device) { - synchronized (sInstance) { - sInstance.mDevice = null; - } - sInstance = null; - } - } - - private MtpDevice mDevice; - - private MtpBitmapCache(int maxSize, MtpDevice device) { - super(maxSize); - mDevice = device; - } - - @Override - protected int sizeOf(Integer key, Bitmap value) { - return value.getByteCount(); - } - - public Bitmap getOrCreate(Integer key) { - Bitmap b = get(key); - return b == null ? createAndInsert(key) : b; - } - - private Bitmap createAndInsert(Integer key) { - MtpDevice device; - synchronized (this) { - device = mDevice; - } - if (device == null) return null; - byte[] imageBytes = device.getThumbnail(key); - if (imageBytes == null) return null; - Bitmap created = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length); - put(key, created); - return created; - } -} diff --git a/src/com/android/gallery3d/ingest/ui/MtpFullscreenView.java b/src/com/android/gallery3d/ingest/ui/MtpFullscreenView.java new file mode 100644 index 000000000..8d3884dc6 --- /dev/null +++ b/src/com/android/gallery3d/ingest/ui/MtpFullscreenView.java @@ -0,0 +1,115 @@ +/* + * 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.ingest.ui; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.CheckBox; +import android.widget.Checkable; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.RelativeLayout; + +import com.android.gallery3d.R; +import com.android.gallery3d.ingest.adapter.CheckBroker; + +public class MtpFullscreenView extends RelativeLayout implements Checkable, + CompoundButton.OnCheckedChangeListener, CheckBroker.OnCheckedChangedListener { + + private MtpImageView mImageView; + private CheckBox mCheckbox; + private int mPosition = -1; + private CheckBroker mBroker; + + public MtpFullscreenView(Context context) { + super(context); + } + + public MtpFullscreenView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public MtpFullscreenView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mImageView = (MtpImageView) findViewById(R.id.ingest_fullsize_image); + mCheckbox = (CheckBox) findViewById(R.id.ingest_fullsize_image_checkbox); + mCheckbox.setOnCheckedChangeListener(this); + } + + @Override + public boolean isChecked() { + return mCheckbox.isChecked(); + } + + @Override + public void setChecked(boolean checked) { + mCheckbox.setChecked(checked); + } + + @Override + public void toggle() { + mCheckbox.toggle(); + } + + @Override + public void onDetachedFromWindow() { + setPositionAndBroker(-1, null); + super.onDetachedFromWindow(); + } + + public MtpImageView getImageView() { + return mImageView; + } + + public int getPosition() { + return mPosition; + } + + public void setPositionAndBroker(int position, CheckBroker b) { + if (mBroker != null) { + mBroker.unregisterOnCheckedChangeListener(this); + } + mPosition = position; + mBroker = b; + if (mBroker != null) { + setChecked(mBroker.isItemChecked(position)); + mBroker.registerOnCheckedChangeListener(this); + } + } + + @Override + public void onCheckedChanged(CompoundButton arg0, boolean isChecked) { + if (mBroker != null) mBroker.setItemChecked(mPosition, isChecked); + } + + @Override + public void onCheckedChanged(int position, boolean isChecked) { + if (position == mPosition) { + setChecked(isChecked); + } + } + + @Override + public void onBulkCheckedChanged() { + if(mBroker != null) setChecked(mBroker.isItemChecked(mPosition)); + } +} diff --git a/src/com/android/gallery3d/ingest/ui/MtpImageView.java b/src/com/android/gallery3d/ingest/ui/MtpImageView.java new file mode 100644 index 000000000..5664d8a34 --- /dev/null +++ b/src/com/android/gallery3d/ingest/ui/MtpImageView.java @@ -0,0 +1,167 @@ +/* + * 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.ingest.ui; + +import android.content.Context; +import android.mtp.MtpDevice; +import android.mtp.MtpObjectInfo; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.util.AttributeSet; +import android.widget.ImageView; + +import com.android.gallery3d.ingest.data.BitmapWithMetadata; +import com.android.gallery3d.ingest.data.MtpBitmapFetch; + +import java.lang.ref.WeakReference; + +public class MtpImageView extends ImageView { + private int mObjectHandle; + private int mGeneration; + + private WeakReference<MtpImageView> mWeakReference = new WeakReference<MtpImageView>(this); + private Object mFetchLock = new Object(); + private boolean mFetchPending = false; + private MtpObjectInfo mFetchObjectInfo; + private MtpDevice mFetchDevice; + private Object mFetchResult; + + private static final FetchImageHandler sFetchHandler = FetchImageHandler.createOnNewThread(); + private static final ShowImageHandler sFetchCompleteHandler = new ShowImageHandler(); + + private void init() { + showPlaceholder(); + } + + public MtpImageView(Context context) { + super(context); + init(); + } + + public MtpImageView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public MtpImageView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + private void showPlaceholder() { + setImageResource(android.R.color.transparent); + } + + public void setMtpDeviceAndObjectInfo(MtpDevice device, MtpObjectInfo object, int gen) { + int handle = object.getObjectHandle(); + if (handle == mObjectHandle && gen == mGeneration) { + return; + } + cancelLoadingAndClear(); + showPlaceholder(); + mGeneration = gen; + mObjectHandle = handle; + synchronized (mFetchLock) { + mFetchObjectInfo = object; + mFetchDevice = device; + if (mFetchPending) return; + mFetchPending = true; + sFetchHandler.sendMessage( + sFetchHandler.obtainMessage(0, mWeakReference)); + } + } + + protected Object fetchMtpImageDataFromDevice(MtpDevice device, MtpObjectInfo info) { + return MtpBitmapFetch.getFullsize(device, info); + } + + protected void onMtpImageDataFetchedFromDevice(Object result) { + BitmapWithMetadata bitmapWithMetadata = (BitmapWithMetadata)result; + setImageBitmap(bitmapWithMetadata.bitmap); + setRotation(bitmapWithMetadata.rotationDegrees); + } + + protected void cancelLoadingAndClear() { + synchronized (mFetchLock) { + mFetchDevice = null; + mFetchObjectInfo = null; + mFetchResult = null; + } + setImageResource(android.R.color.transparent); + setRotation(0); + } + + @Override + public void onDetachedFromWindow() { + cancelLoadingAndClear(); + super.onDetachedFromWindow(); + } + + private static class FetchImageHandler extends Handler { + public FetchImageHandler(Looper l) { + super(l); + } + + public static FetchImageHandler createOnNewThread() { + HandlerThread t = new HandlerThread("MtpImageView Fetch"); + t.start(); + return new FetchImageHandler(t.getLooper()); + } + + @Override + public void handleMessage(Message msg) { + @SuppressWarnings("unchecked") + MtpImageView parent = ((WeakReference<MtpImageView>) msg.obj).get(); + if (parent == null) return; + MtpObjectInfo objectInfo; + MtpDevice device; + synchronized (parent.mFetchLock) { + parent.mFetchPending = false; + device = parent.mFetchDevice; + objectInfo = parent.mFetchObjectInfo; + } + if (device == null) return; + Object result = parent.fetchMtpImageDataFromDevice(device, objectInfo); + if (result == null) return; + synchronized (parent.mFetchLock) { + if (parent.mFetchObjectInfo != objectInfo) return; + parent.mFetchResult = result; + parent.mFetchDevice = null; + parent.mFetchObjectInfo = null; + sFetchCompleteHandler.sendMessage( + sFetchCompleteHandler.obtainMessage(0, parent.mWeakReference)); + } + } + } + + private static class ShowImageHandler extends Handler { + @Override + public void handleMessage(Message msg) { + @SuppressWarnings("unchecked") + MtpImageView parent = ((WeakReference<MtpImageView>) msg.obj).get(); + if (parent == null) return; + Object result; + synchronized (parent.mFetchLock) { + result = parent.mFetchResult; + } + if (result == null) return; + parent.onMtpImageDataFetchedFromDevice(result); + } + } +} diff --git a/src/com/android/gallery3d/ingest/ui/MtpThumbnailTileView.java b/src/com/android/gallery3d/ingest/ui/MtpThumbnailTileView.java index 2aeda73db..3307e78aa 100644 --- a/src/com/android/gallery3d/ingest/ui/MtpThumbnailTileView.java +++ b/src/com/android/gallery3d/ingest/ui/MtpThumbnailTileView.java @@ -22,26 +22,22 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.mtp.MtpDevice; import android.mtp.MtpObjectInfo; -import android.os.AsyncTask; import android.util.AttributeSet; -import android.view.View; import android.widget.Checkable; -import android.widget.ImageView; import com.android.gallery3d.R; +import com.android.gallery3d.ingest.data.MtpBitmapFetch; -public class MtpThumbnailTileView extends ImageView implements Checkable { - private static final int FADE_IN_TIME_MS = 80; + +public class MtpThumbnailTileView extends MtpImageView implements Checkable { private Paint mForegroundPaint; private boolean mIsChecked; - private int mObjectHandle; - private int mGeneration; + private Bitmap mBitmap; private void init() { mForegroundPaint = new Paint(); mForegroundPaint.setColor(getResources().getColor(R.color.ingest_highlight_semitransparent)); - showPlaceholder(); } public MtpThumbnailTileView(Context context) { @@ -66,9 +62,20 @@ public class MtpThumbnailTileView extends ImageView implements Checkable { } @Override + protected Object fetchMtpImageDataFromDevice(MtpDevice device, MtpObjectInfo info) { + return MtpBitmapFetch.getThumbnail(device, info); + } + + @Override + protected void onMtpImageDataFetchedFromDevice(Object result) { + mBitmap = (Bitmap)result; + setImageBitmap(mBitmap); + } + + @Override public void draw(Canvas canvas) { super.draw(canvas); - if (mIsChecked) { + if (isChecked()) { canvas.drawRect(canvas.getClipBounds(), mForegroundPaint); } } @@ -88,65 +95,12 @@ public class MtpThumbnailTileView extends ImageView implements Checkable { setChecked(!mIsChecked); } - private void showPlaceholder() { - setAlpha(0f); - } - - private LoadThumbnailTask mTask; - - public void setMtpDeviceAndObjectInfo(MtpDevice device, MtpObjectInfo object, int gen) { - int handle = object.getObjectHandle(); - if (handle == mObjectHandle && gen == mGeneration) { - return; - } - animate().cancel(); - if (mTask != null) { - mTask.cancel(true); - } - mGeneration = gen; - mObjectHandle = handle; - Bitmap thumbnail = MtpBitmapCache.getInstanceForDevice(device) - .get(handle); - if (thumbnail != null) { - setAlpha(1f); - setImageBitmap(thumbnail); - } else { - showPlaceholder(); - mTask = new LoadThumbnailTask(device); - mTask.execute(object); - } - } - - private class LoadThumbnailTask extends AsyncTask<MtpObjectInfo, Void, Bitmap> { - private MtpDevice mDevice; - - public LoadThumbnailTask(MtpDevice device) { - mDevice = device; - } - - @Override - protected Bitmap doInBackground(MtpObjectInfo... args) { - Bitmap result = null; - if (!isCancelled()) { - result = MtpBitmapCache.getInstanceForDevice(mDevice).getOrCreate( - args[0].getObjectHandle()); - } - mDevice = null; - return result; - } - - @Override - protected void onPostExecute(Bitmap result) { - if (isCancelled() || result == null) { - return; - } - setAlpha(0f); - setImageBitmap(result); - animate().alpha(1f).setDuration(FADE_IN_TIME_MS); - } - - @Override - protected void onCancelled() { + @Override + protected void cancelLoadingAndClear() { + super.cancelLoadingAndClear(); + if (mBitmap != null) { + MtpBitmapFetch.recycleThumbnail(mBitmap); + mBitmap = null; } } } diff --git a/src_pd/com/android/gallery3d/filtershow/editors/EditorManager.java b/src_pd/com/android/gallery3d/filtershow/editors/EditorManager.java index 5c1d004b6..67ccf9c80 100644 --- a/src_pd/com/android/gallery3d/filtershow/editors/EditorManager.java +++ b/src_pd/com/android/gallery3d/filtershow/editors/EditorManager.java @@ -26,6 +26,7 @@ public class EditorManager { public static void addEditors(EditorPlaceHolder editorPlaceHolder) { editorPlaceHolder.addEditor(new EditorZoom()); editorPlaceHolder.addEditor(new EditorCurves()); + editorPlaceHolder.addEditor(new EditorTinyPlanet()); editorPlaceHolder.addEditor(new EditorDraw()); } diff --git a/src_pd/com/android/gallery3d/filtershow/filters/FiltersManager.java b/src_pd/com/android/gallery3d/filtershow/filters/FiltersManager.java index 915041bb9..988cf2d81 100644 --- a/src_pd/com/android/gallery3d/filtershow/filters/FiltersManager.java +++ b/src_pd/com/android/gallery3d/filtershow/filters/FiltersManager.java @@ -16,33 +16,13 @@ package com.android.gallery3d.filtershow.filters; -import android.util.Log; +public class FiltersManager extends BaseFiltersManager { + private static FiltersManager gInstance = null; -import com.android.gallery3d.filtershow.cache.ImageLoader; - -import java.util.Vector; - -public class FiltersManager { - - private static final String LOGTAG = "FiltersManager"; - - public static void addFilters(Vector<ImageFilter> filters, ImageLoader imageLoader) { - filters.add(new ImageFilterTinyPlanet()); - filters.add(new ImageFilterWBalance()); - filters.add(new ImageFilterExposure()); - filters.add(new ImageFilterVignette()); - filters.add(new ImageFilterContrast()); - filters.add(new ImageFilterShadows()); - filters.add(new ImageFilterVibrance()); - filters.add(new ImageFilterSharpen()); - filters.add(new ImageFilterCurves()); - filters.add(new ImageFilterDraw()); - filters.add(new ImageFilterHue()); - filters.add(new ImageFilterSaturated()); - filters.add(new ImageFilterBwFilter()); - filters.add(new ImageFilterNegative()); - filters.add(new ImageFilterEdge()); - filters.add(new ImageFilterKMeans()); - filters.add(new ImageFilterDownsample(imageLoader)); + public static FiltersManager getManager() { + if (gInstance == null) { + gInstance = new FiltersManager(); + } + return gInstance; } } diff --git a/tests/src/com/android/gallery3d/CameraTestRunner.java b/tests/src/com/android/gallery3d/CameraTestRunner.java new file mode 100755 index 000000000..503233675 --- /dev/null +++ b/tests/src/com/android/gallery3d/CameraTestRunner.java @@ -0,0 +1,46 @@ +/* + * 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; + +import android.test.InstrumentationTestRunner; +import android.test.InstrumentationTestSuite; + +import com.android.gallery3d.functional.CameraTest; +import com.android.gallery3d.functional.ImageCaptureIntentTest; +import com.android.gallery3d.functional.VideoCaptureIntentTest; +import com.android.gallery3d.unittest.CameraUnitTest; + +import junit.framework.TestSuite; + + +public class CameraTestRunner extends InstrumentationTestRunner { + + @Override + public TestSuite getAllTests() { + TestSuite suite = new InstrumentationTestSuite(this); + suite.addTestSuite(CameraTest.class); + suite.addTestSuite(ImageCaptureIntentTest.class); + suite.addTestSuite(VideoCaptureIntentTest.class); + suite.addTestSuite(CameraUnitTest.class); + return suite; + } + + @Override + public ClassLoader getLoader() { + return CameraTestRunner.class.getClassLoader(); + } +} diff --git a/tests/src/com/android/gallery3d/StressTests.java b/tests/src/com/android/gallery3d/StressTests.java new file mode 100755 index 000000000..32eefdcec --- /dev/null +++ b/tests/src/com/android/gallery3d/StressTests.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2009 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; + +import com.android.gallery3d.stress.CameraLatency; +import com.android.gallery3d.stress.CameraStartUp; +import com.android.gallery3d.stress.ImageCapture; +import com.android.gallery3d.stress.SwitchPreview; + +import junit.framework.Test; +import junit.framework.TestSuite; + + +/** + * Instrumentation Test Runner for all Camera tests. + * + * Running all tests: + * + * adb shell am instrument \ + * -e class com.android.gallery3d.StressTests \ + * -w com.google.android.gallery3d.tests/com.android.gallery3d.stress.CameraStressTestRunner + */ + +public class StressTests extends TestSuite { + public static Test suite() { + TestSuite result = new TestSuite(); + result.addTestSuite(CameraLatency.class); + result.addTestSuite(CameraStartUp.class); + result.addTestSuite(ImageCapture.class); + result.addTestSuite(SwitchPreview.class); + return result; + } +} diff --git a/tests/src/com/android/gallery3d/functional/CameraTest.java b/tests/src/com/android/gallery3d/functional/CameraTest.java new file mode 100644 index 000000000..c293c0d4a --- /dev/null +++ b/tests/src/com/android/gallery3d/functional/CameraTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2010 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.functional; + +import com.android.camera.CameraActivity; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Environment; +import android.os.Process; +import android.provider.MediaStore; +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.LargeTest; + +import java.io.File; +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +public class CameraTest extends InstrumentationTestCase { + @LargeTest + public void testVideoCaptureIntentFdLeak() throws Exception { + Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); + intent.setClass(getInstrumentation().getTargetContext(), CameraActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.parse("file://" + + Environment.getExternalStorageDirectory().toString() + + "test_fd_leak.3gp")); + getInstrumentation().startActivitySync(intent).finish(); + // Test if the fd is closed. + for (File f: new File("/proc/" + Process.myPid() + "/fd").listFiles()) { + assertEquals(-1, f.getCanonicalPath().indexOf("test_fd_leak.3gp")); + } + } + + @LargeTest + public void testActivityLeak() throws Exception { + checkActivityLeak(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA); + checkActivityLeak(MediaStore.INTENT_ACTION_VIDEO_CAMERA); + } + + private void checkActivityLeak(String action) throws Exception { + final int TEST_COUNT = 5; + Intent intent = new Intent(action); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setClass(getInstrumentation().getTargetContext(), + CameraActivity.class); + ArrayList<WeakReference<Activity>> refs = + new ArrayList<WeakReference<Activity>>(); + for (int i = 0; i < TEST_COUNT; i++) { + Activity activity = getInstrumentation().startActivitySync(intent); + refs.add(new WeakReference<Activity>(activity)); + activity.finish(); + getInstrumentation().waitForIdleSync(); + activity = null; + } + Runtime.getRuntime().gc(); + Runtime.getRuntime().runFinalization(); + Runtime.getRuntime().gc(); + int refCount = 0; + for (WeakReference<Activity> c: refs) { + if (c.get() != null) refCount++; + } + // If applications are leaking activity, every reference is reachable. + assertTrue(refCount != TEST_COUNT); + } +} diff --git a/tests/src/com/android/gallery3d/functional/ImageCaptureIntentTest.java b/tests/src/com/android/gallery3d/functional/ImageCaptureIntentTest.java new file mode 100644 index 000000000..c2eeabcce --- /dev/null +++ b/tests/src/com/android/gallery3d/functional/ImageCaptureIntentTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2011 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.functional; + +import com.android.camera.CameraActivity; +import com.android.camera.R; + +import android.app.Activity; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; +import android.view.KeyEvent; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; + +public class ImageCaptureIntentTest extends ActivityInstrumentationTestCase2 <CameraActivity> { + private Intent mIntent; + + public ImageCaptureIntentTest() { + super(CameraActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + } + + @LargeTest + public void testNoExtraOutput() throws Exception { + setActivityIntent(mIntent); + getActivity(); + + takePicture(); + pressDone(); + + assertTrue(getActivity().isFinishing()); + assertEquals(Activity.RESULT_OK, getActivity().getResultCode()); + Intent resultData = getActivity().getResultData(); + Bitmap bitmap = (Bitmap) resultData.getParcelableExtra("data"); + assertNotNull(bitmap); + assertTrue(bitmap.getWidth() > 0); + assertTrue(bitmap.getHeight() > 0); + } + + @LargeTest + public void testExtraOutput() throws Exception { + File file = new File(Environment.getExternalStorageDirectory(), + "test.jpg"); + BufferedInputStream stream = null; + byte[] jpegData; + + try { + mIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); + setActivityIntent(mIntent); + getActivity(); + + takePicture(); + pressDone(); + + assertTrue(getActivity().isFinishing()); + assertEquals(Activity.RESULT_OK, getActivity().getResultCode()); + + // Verify the jpeg file + int fileLength = (int) file.length(); + assertTrue(fileLength > 0); + jpegData = new byte[fileLength]; + stream = new BufferedInputStream(new FileInputStream(file)); + stream.read(jpegData); + } finally { + if (stream != null) stream.close(); + file.delete(); + } + + Bitmap b = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length); + assertTrue(b.getWidth() > 0); + assertTrue(b.getHeight() > 0); + } + + @LargeTest + public void testCancel() throws Exception { + setActivityIntent(mIntent); + getActivity(); + + pressCancel(); + + assertTrue(getActivity().isFinishing()); + assertEquals(Activity.RESULT_CANCELED, getActivity().getResultCode()); + } + + @LargeTest + public void testSnapshotCancel() throws Exception { + setActivityIntent(mIntent); + getActivity(); + + takePicture(); + pressCancel(); + + assertTrue(getActivity().isFinishing()); + assertEquals(Activity.RESULT_CANCELED, getActivity().getResultCode()); + } + + private void takePicture() throws Exception { + getInstrumentation().sendKeySync( + new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_FOCUS)); + getInstrumentation().sendCharacterSync(KeyEvent.KEYCODE_CAMERA); + Thread.sleep(4000); + } + + private void pressDone() { + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + getActivity().findViewById(R.id.btn_done).performClick(); + } + }); + } + + private void pressCancel() { + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + getActivity().findViewById(R.id.btn_cancel).performClick(); + } + }); + } +} diff --git a/tests/src/com/android/gallery3d/functional/VideoCaptureIntentTest.java b/tests/src/com/android/gallery3d/functional/VideoCaptureIntentTest.java new file mode 100644 index 000000000..3802060e2 --- /dev/null +++ b/tests/src/com/android/gallery3d/functional/VideoCaptureIntentTest.java @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2011 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.functional; + +import com.android.camera.CameraActivity; +import com.android.camera.R; + +import android.app.Activity; +import android.content.ContentResolver; +import android.content.Intent; +import android.database.Cursor; +import android.media.MediaMetadataRetriever; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore; +import android.provider.MediaStore.Video.VideoColumns; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; +import android.view.KeyEvent; + +import java.io.File; + +public class VideoCaptureIntentTest extends ActivityInstrumentationTestCase2 <CameraActivity> { + private static final String TAG = "VideoCaptureIntentTest"; + private Intent mIntent; + private Uri mVideoUri; + private File mFile, mFile2; + + public VideoCaptureIntentTest() { + super(CameraActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); + } + + @Override + protected void tearDown() throws Exception { + if (mVideoUri != null) { + ContentResolver resolver = getActivity().getContentResolver(); + Uri query = mVideoUri.buildUpon().build(); + String[] projection = new String[] {VideoColumns.DATA}; + + Cursor cursor = null; + try { + cursor = resolver.query(query, projection, null, null, null); + if (cursor != null && cursor.moveToFirst()) { + new File(cursor.getString(0)).delete(); + } + } finally { + if (cursor != null) cursor.close(); + } + + resolver.delete(mVideoUri, null, null); + } + if (mFile != null) mFile.delete(); + if (mFile2 != null) mFile2.delete(); + super.tearDown(); + } + + @LargeTest + public void testNoExtraOutput() throws Exception { + setActivityIntent(mIntent); + getActivity(); + + recordVideo(); + pressDone(); + + Intent resultData = getActivity().getResultData(); + mVideoUri = resultData.getData(); + assertNotNull(mVideoUri); + verify(getActivity(), mVideoUri); + } + + @LargeTest + public void testExtraOutput() throws Exception { + mFile = new File(Environment.getExternalStorageDirectory(), "video.tmp"); + + Uri uri = Uri.fromFile(mFile); + mIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + setActivityIntent(mIntent); + getActivity(); + + recordVideo(); + pressDone(); + + verify(getActivity(), uri); + } + + @LargeTest + public void testCancel() throws Exception { + setActivityIntent(mIntent); + getActivity(); + + pressCancel(); + + assertTrue(getActivity().isFinishing()); + assertEquals(Activity.RESULT_CANCELED, getActivity().getResultCode()); + } + + @LargeTest + public void testRecordCancel() throws Exception { + setActivityIntent(mIntent); + getActivity(); + + recordVideo(); + pressCancel(); + + assertTrue(getActivity().isFinishing()); + assertEquals(Activity.RESULT_CANCELED, getActivity().getResultCode()); + } + + @LargeTest + public void testExtraSizeLimit() throws Exception { + mFile = new File(Environment.getExternalStorageDirectory(), "video.tmp"); + final long sizeLimit = 500000; // bytes + + Uri uri = Uri.fromFile(mFile); + mIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + mIntent.putExtra(MediaStore.EXTRA_SIZE_LIMIT, sizeLimit); + mIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0); // use low quality to speed up + setActivityIntent(mIntent); + getActivity(); + + recordVideo(5000); + pressDone(); + + verify(getActivity(), uri); + long length = mFile.length(); + Log.v(TAG, "Video size is " + length + " bytes."); + assertTrue(length > 0); + assertTrue("Actual size=" + length, length <= sizeLimit); + } + + @LargeTest + public void testExtraDurationLimit() throws Exception { + mFile = new File(Environment.getExternalStorageDirectory(), "video.tmp"); + final int durationLimit = 2; // seconds + + Uri uri = Uri.fromFile(mFile); + mIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + mIntent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, durationLimit); + setActivityIntent(mIntent); + getActivity(); + + recordVideo(5000); + pressDone(); + + int duration = verify(getActivity(), uri); + // The duraion should be close to to the limit. The last video duration + // also has duration, so the total duration may exceeds the limit a + // little bit. + Log.v(TAG, "Video length is " + duration + " ms."); + assertTrue(duration < (durationLimit + 1) * 1000); + } + + @LargeTest + public void testExtraVideoQuality() throws Exception { + mFile = new File(Environment.getExternalStorageDirectory(), "video.tmp"); + mFile2 = new File(Environment.getExternalStorageDirectory(), "video2.tmp"); + + Uri uri = Uri.fromFile(mFile); + mIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + mIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0); // low quality + setActivityIntent(mIntent); + getActivity(); + + recordVideo(); + pressDone(); + + verify(getActivity(), uri); + setActivity(null); + + uri = Uri.fromFile(mFile2); + mIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + mIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); // high quality + setActivityIntent(mIntent); + getActivity(); + + recordVideo(); + pressDone(); + + verify(getActivity(), uri); + assertTrue(mFile.length() <= mFile2.length()); + } + + // Verify result code, result data, and the duration. + private int verify(CameraActivity activity, Uri uri) throws Exception { + assertTrue(activity.isFinishing()); + assertEquals(Activity.RESULT_OK, activity.getResultCode()); + + // Verify the video file + MediaMetadataRetriever retriever = new MediaMetadataRetriever(); + retriever.setDataSource(activity, uri); + String duration = retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_DURATION); + assertNotNull(duration); + int durationValue = Integer.parseInt(duration); + Log.v(TAG, "Video duration is " + durationValue); + assertTrue(durationValue > 0); + return durationValue; + } + + private void recordVideo(int ms) throws Exception { + getInstrumentation().sendCharacterSync(KeyEvent.KEYCODE_CAMERA); + Thread.sleep(ms); + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + // If recording is in progress, stop it. Run these atomically in + // UI thread. + CameraActivity activity = getActivity(); + if (activity.isRecording()) { + activity.findViewById(R.id.shutter_button).performClick(); + } + } + }); + } + + private void recordVideo() throws Exception { + recordVideo(2000); + } + + private void pressDone() { + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + getActivity().findViewById(R.id.btn_done).performClick(); + } + }); + } + + private void pressCancel() { + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + getActivity().findViewById(R.id.btn_cancel).performClick(); + } + }); + } +} diff --git a/tests/src/com/android/gallery3d/power/ImageAndVideoCapture.java b/tests/src/com/android/gallery3d/power/ImageAndVideoCapture.java new file mode 100755 index 000000000..cd5079355 --- /dev/null +++ b/tests/src/com/android/gallery3d/power/ImageAndVideoCapture.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2009 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.power; + +import com.android.camera.CameraActivity; + +import android.app.Instrumentation; +import android.provider.MediaStore; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; +import android.view.KeyEvent; +import android.content.Intent; +/** + * Junit / Instrumentation test case for camera power measurement + * + * Running the test suite: + * + * adb shell am instrument \ + * -e com.android.camera.power.ImageAndVideoCapture \ + * -w com.android.camera.tests/android.test.InstrumentationTestRunner + * + */ + +public class ImageAndVideoCapture extends ActivityInstrumentationTestCase2 <CameraActivity> { + private String TAG = "ImageAndVideoCapture"; + private static final int TOTAL_NUMBER_OF_IMAGECAPTURE = 5; + private static final int TOTAL_NUMBER_OF_VIDEOCAPTURE = 5; + private static final long WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN = 1500; //1.5 sedconds + private static final long WAIT_FOR_VIDEO_CAPTURE_TO_BE_TAKEN = 10000; //10 seconds + private static final long WAIT_FOR_PREVIEW = 1500; //1.5 seconds + private static final long WAIT_FOR_STABLE_STATE = 2000; //2 seconds + + public ImageAndVideoCapture() { + super(CameraActivity.class); + } + + @Override + protected void setUp() throws Exception { + getActivity(); + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + @LargeTest + public void testLaunchCamera() { + // This test case capture the baseline for the image preview. + try { + Thread.sleep(WAIT_FOR_STABLE_STATE); + } catch (Exception e) { + Log.v(TAG, "Got exception", e); + assertTrue("testImageCaptureDoNothing", false); + } + } + + @LargeTest + public void testCapture5Image() { + // This test case will use the default camera setting + Instrumentation inst = getInstrumentation(); + try { + for (int i = 0; i < TOTAL_NUMBER_OF_IMAGECAPTURE; i++) { + Thread.sleep(WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN); + inst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_UP); + inst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER); + Thread.sleep(WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN); + } + Thread.sleep(WAIT_FOR_STABLE_STATE); + } catch (Exception e) { + Log.v(TAG, "Got exception", e); + assertTrue("testImageCapture", false); + } + } + + @LargeTest + public void testCapture5Videos() { + // This test case will use the default camera setting + Instrumentation inst = getInstrumentation(); + try { + // Switch to the video mode + Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA); + intent.setClass(getInstrumentation().getTargetContext(), + CameraActivity.class); + getActivity().startActivity(intent); + for (int i = 0; i < TOTAL_NUMBER_OF_VIDEOCAPTURE; i++) { + Thread.sleep(WAIT_FOR_PREVIEW); + // record a video + inst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER); + Thread.sleep(WAIT_FOR_VIDEO_CAPTURE_TO_BE_TAKEN); + inst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER); + Thread.sleep(WAIT_FOR_PREVIEW); + } + Thread.sleep(WAIT_FOR_STABLE_STATE); + } catch (Exception e) { + Log.v(TAG, "Got exception", e); + assertTrue("testVideoCapture", false); + } + } +} diff --git a/tests/src/com/android/gallery3d/stress/CameraLatency.java b/tests/src/com/android/gallery3d/stress/CameraLatency.java new file mode 100755 index 000000000..7177abe6c --- /dev/null +++ b/tests/src/com/android/gallery3d/stress/CameraLatency.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2009 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.stress; + +import com.android.camera.CameraActivity; + +import android.app.Instrumentation; +import android.os.Environment; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; +import android.view.KeyEvent; + +import java.io.BufferedWriter; +import java.io.FileWriter; + +/** + * Junit / Instrumentation test case for camera test + * + */ + +public class CameraLatency extends ActivityInstrumentationTestCase2 <CameraActivity> { + private String TAG = "CameraLatency"; + private static final int TOTAL_NUMBER_OF_IMAGECAPTURE = 20; + private static final long WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN = 4000; + private static final String CAMERA_TEST_OUTPUT_FILE = + Environment.getExternalStorageDirectory().toString() + "/mediaStressOut.txt"; + + private long mTotalAutoFocusTime; + private long mTotalShutterLag; + private long mTotalShutterToPictureDisplayedTime; + private long mTotalPictureDisplayedToJpegCallbackTime; + private long mTotalJpegCallbackFinishTime; + private long mAvgAutoFocusTime; + private long mAvgShutterLag = mTotalShutterLag; + private long mAvgShutterToPictureDisplayedTime; + private long mAvgPictureDisplayedToJpegCallbackTime; + private long mAvgJpegCallbackFinishTime; + + public CameraLatency() { + super(CameraActivity.class); + } + + @Override + protected void setUp() throws Exception { + getActivity(); + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + @LargeTest + public void testImageCapture() { + Log.v(TAG, "start testImageCapture test"); + Instrumentation inst = getInstrumentation(); + inst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN); + try { + for (int i = 0; i < TOTAL_NUMBER_OF_IMAGECAPTURE; i++) { + Thread.sleep(WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN); + inst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER); + Thread.sleep(WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN); + //skip the first measurement + if (i != 0) { + CameraActivity c = getActivity(); + + // if any of the latency var accessor methods return -1 then the + // camera is set to a different module other than PhotoModule so + // skip the shot and try again + if (c.getAutoFocusTime() != -1) { + mTotalAutoFocusTime += c.getAutoFocusTime(); + mTotalShutterLag += c.getShutterLag(); + mTotalShutterToPictureDisplayedTime += + c.getShutterToPictureDisplayedTime(); + mTotalPictureDisplayedToJpegCallbackTime += + c.getPictureDisplayedToJpegCallbackTime(); + mTotalJpegCallbackFinishTime += c.getJpegCallbackFinishTime(); + } + else { + i--; + continue; + } + } + } + } catch (Exception e) { + Log.v(TAG, "Got exception", e); + } + //ToDO: yslau + //1) Need to get the baseline from the cupcake so that we can add the + //failure condition of the camera latency. + //2) Only count those number with succesful capture. Set the timer to invalid + //before capture and ignore them if the value is invalid + int numberofRun = TOTAL_NUMBER_OF_IMAGECAPTURE - 1; + mAvgAutoFocusTime = mTotalAutoFocusTime / numberofRun; + mAvgShutterLag = mTotalShutterLag / numberofRun; + mAvgShutterToPictureDisplayedTime = + mTotalShutterToPictureDisplayedTime / numberofRun; + mAvgPictureDisplayedToJpegCallbackTime = + mTotalPictureDisplayedToJpegCallbackTime / numberofRun; + mAvgJpegCallbackFinishTime = + mTotalJpegCallbackFinishTime / numberofRun; + + try { + FileWriter fstream = null; + fstream = new FileWriter(CAMERA_TEST_OUTPUT_FILE, true); + BufferedWriter out = new BufferedWriter(fstream); + out.write("Camera Latency : \n"); + out.write("Number of loop: " + TOTAL_NUMBER_OF_IMAGECAPTURE + "\n"); + out.write("Avg AutoFocus = " + mAvgAutoFocusTime + "\n"); + out.write("Avg mShutterLag = " + mAvgShutterLag + "\n"); + out.write("Avg mShutterToPictureDisplayedTime = " + + mAvgShutterToPictureDisplayedTime + "\n"); + out.write("Avg mPictureDisplayedToJpegCallbackTime = " + + mAvgPictureDisplayedToJpegCallbackTime + "\n"); + out.write("Avg mJpegCallbackFinishTime = " + + mAvgJpegCallbackFinishTime + "\n"); + out.close(); + fstream.close(); + } catch (Exception e) { + fail("Camera Latency write output to file"); + } + Log.v(TAG, "The Image capture wait time = " + + WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN); + Log.v(TAG, "Avg AutoFocus = " + mAvgAutoFocusTime); + Log.v(TAG, "Avg mShutterLag = " + mAvgShutterLag); + Log.v(TAG, "Avg mShutterToPictureDisplayedTime = " + + mAvgShutterToPictureDisplayedTime); + Log.v(TAG, "Avg mPictureDisplayedToJpegCallbackTime = " + + mAvgPictureDisplayedToJpegCallbackTime); + Log.v(TAG, "Avg mJpegCallbackFinishTime = " + mAvgJpegCallbackFinishTime); + } +} + diff --git a/tests/src/com/android/gallery3d/stress/CameraStartUp.java b/tests/src/com/android/gallery3d/stress/CameraStartUp.java new file mode 100644 index 000000000..8524465ac --- /dev/null +++ b/tests/src/com/android/gallery3d/stress/CameraStartUp.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2009 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.stress; + +import com.android.camera.CameraActivity; + +import android.app.Activity; +import android.app.Instrumentation; +import android.content.Intent; +import android.os.Environment; +import android.provider.MediaStore; +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; + +import java.io.FileWriter; +import java.io.BufferedWriter; + +/** + * Test cases to measure the camera and video recorder startup time. + */ +public class CameraStartUp extends InstrumentationTestCase { + + private static final int TOTAL_NUMBER_OF_STARTUP = 20; + + private String TAG = "CameraStartUp"; + private static final String CAMERA_TEST_OUTPUT_FILE = + Environment.getExternalStorageDirectory().toString() + "/mediaStressOut.txt"; + private static int WAIT_TIME_FOR_PREVIEW = 1500; //1.5 second + + private long launchCamera() { + long startupTime = 0; + try { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClass(getInstrumentation().getTargetContext(), CameraActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + long beforeStart = System.currentTimeMillis(); + Instrumentation inst = getInstrumentation(); + Activity cameraActivity = inst.startActivitySync(intent); + long cameraStarted = System.currentTimeMillis(); + Thread.sleep(WAIT_TIME_FOR_PREVIEW); + cameraActivity.finish(); + startupTime = cameraStarted - beforeStart; + Thread.sleep(1000); + Log.v(TAG, "camera startup time: " + startupTime); + } catch (Exception e) { + Log.v(TAG, "Got exception", e); + fail("Fails to get the output file"); + } + return startupTime; + } + + private long launchVideo() { + long startupTime = 0; + + try { + Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA); + intent.setClass(getInstrumentation().getTargetContext(), CameraActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + long beforeStart = System.currentTimeMillis(); + Instrumentation inst = getInstrumentation(); + Activity recorderActivity = inst.startActivitySync(intent); + long cameraStarted = System.currentTimeMillis(); + recorderActivity.finish(); + startupTime = cameraStarted - beforeStart; + Log.v(TAG, "Video Startup Time = " + startupTime); + // wait for 1s to make sure it reach a clean stage + Thread.sleep(WAIT_TIME_FOR_PREVIEW); + Log.v(TAG, "video startup time: " + startupTime); + } catch (Exception e) { + Log.v(TAG, "Got exception", e); + fail("Fails to launch video output file"); + } + return startupTime; + } + + private void writeToOutputFile(long totalStartupTime, + String individualStartupTime, boolean firstStartUp, String Type) throws Exception { + // TODO (yslau) : Need to integrate the output data with central + // dashboard + try { + FileWriter fstream = null; + fstream = new FileWriter(CAMERA_TEST_OUTPUT_FILE, true); + BufferedWriter out = new BufferedWriter(fstream); + if (firstStartUp) { + out.write("First " + Type + " Startup: " + totalStartupTime + "\n"); + } else { + long averageStartupTime = totalStartupTime / (TOTAL_NUMBER_OF_STARTUP -1); + out.write(Type + "startup time: " + "\n"); + out.write("Number of loop: " + (TOTAL_NUMBER_OF_STARTUP -1) + "\n"); + out.write(individualStartupTime + "\n\n"); + out.write(Type + " average startup time: " + averageStartupTime + " ms\n\n"); + } + out.close(); + fstream.close(); + } catch (Exception e) { + fail("Camera write output to file"); + } + } + + @LargeTest + public void testLaunchVideo() throws Exception { + String individualStartupTime; + individualStartupTime = "Individual Video Startup Time = "; + long totalStartupTime = 0; + long startupTime = 0; + for (int i = 0; i < TOTAL_NUMBER_OF_STARTUP; i++) { + if (i == 0) { + // Capture the first startup time individually + long firstStartUpTime = launchVideo(); + writeToOutputFile(firstStartUpTime, "na", true, "Video"); + } else { + startupTime = launchVideo(); + totalStartupTime += startupTime; + individualStartupTime += startupTime + " ,"; + } + } + Log.v(TAG, "totalStartupTime =" + totalStartupTime); + writeToOutputFile(totalStartupTime, individualStartupTime, false, "Video"); + } + + @LargeTest + public void testLaunchCamera() throws Exception { + String individualStartupTime; + individualStartupTime = "Individual Camera Startup Time = "; + long totalStartupTime = 0; + long startupTime = 0; + for (int i = 0; i < TOTAL_NUMBER_OF_STARTUP; i++) { + if (i == 0) { + // Capture the first startup time individually + long firstStartUpTime = launchCamera(); + writeToOutputFile(firstStartUpTime, "na", true, "Camera"); + } else { + startupTime = launchCamera(); + totalStartupTime += startupTime; + individualStartupTime += startupTime + " ,"; + } + } + Log.v(TAG, "totalStartupTime =" + totalStartupTime); + writeToOutputFile(totalStartupTime, + individualStartupTime, false, "Camera"); + } +} diff --git a/tests/src/com/android/gallery3d/stress/CameraStressTestRunner.java b/tests/src/com/android/gallery3d/stress/CameraStressTestRunner.java new file mode 100755 index 000000000..57ae69125 --- /dev/null +++ b/tests/src/com/android/gallery3d/stress/CameraStressTestRunner.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 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.stress; + +import android.os.Bundle; +import android.test.InstrumentationTestRunner; +import android.test.InstrumentationTestSuite; +import junit.framework.TestSuite; + +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 + + @Override + public TestSuite getAllTests() { + TestSuite suite = new InstrumentationTestSuite(this); + suite.addTestSuite(ImageCapture.class); + suite.addTestSuite(VideoCapture.class); + return suite; + } + + @Override + public ClassLoader getLoader() { + return CameraStressTestRunner.class.getClassLoader(); + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + String video_iterations = (String) icicle.get("video_iterations"); + String image_iterations = (String) icicle.get("image_iterations"); + String video_duration = (String) icicle.get("video_duration"); + + if ( video_iterations != null ) { + mVideoIterations = Integer.parseInt(video_iterations); + } + if ( image_iterations != null) { + mImageIterations = Integer.parseInt(image_iterations); + } + if ( video_duration != null) { + mVideoDuration = Integer.parseInt(video_duration); + } + } +} diff --git a/tests/src/com/android/gallery3d/stress/ImageCapture.java b/tests/src/com/android/gallery3d/stress/ImageCapture.java new file mode 100755 index 000000000..e322eb516 --- /dev/null +++ b/tests/src/com/android/gallery3d/stress/ImageCapture.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2009 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.stress; + +import com.android.camera.CameraActivity; +import com.android.gallery3d.stress.CameraStressTestRunner; + +import android.app.Instrumentation; +import android.content.Intent; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; +import android.view.KeyEvent; +import android.app.Activity; + +/** + * Junit / Instrumentation test case for camera test + * + * Running the test suite: + * + * adb shell am instrument \ + * -e class com.android.camera.stress.ImageCapture \ + * -w com.google.android.camera.tests/android.test.InstrumentationTestRunner + * + */ + +public class ImageCapture extends ActivityInstrumentationTestCase2 <CameraActivity> { + private String TAG = "ImageCapture"; + private static final long WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN = 1500; //1.5 sedconds + private static final long WAIT_FOR_SWITCH_CAMERA = 3000; //3 seconds + + private TestUtil testUtil = new TestUtil(); + + // Private intent extras. + private final static String EXTRAS_CAMERA_FACING = + "android.intent.extras.CAMERA_FACING"; + + public ImageCapture() { + super(CameraActivity.class); + } + + @Override + protected void setUp() throws Exception { + testUtil.prepareOutputFile(); + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + testUtil.closeOutputFile(); + super.tearDown(); + } + + public void captureImages(String reportTag, Instrumentation inst) { + int total_num_of_images = CameraStressTestRunner.mImageIterations; + Log.v(TAG, "no of images = " + total_num_of_images); + + //TODO(yslau): Need to integrate the outoput with the central dashboard, + //write to a txt file as a temp solution + boolean memoryResult = false; + KeyEvent focusEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_FOCUS); + + try { + testUtil.writeReportHeader(reportTag, total_num_of_images); + for (int i = 0; i < total_num_of_images; i++) { + Thread.sleep(WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN); + inst.sendKeySync(focusEvent); + inst.sendCharacterSync(KeyEvent.KEYCODE_CAMERA); + Thread.sleep(WAIT_FOR_IMAGE_CAPTURE_TO_BE_TAKEN); + testUtil.writeResult(i); + } + } catch (Exception e) { + Log.v(TAG, "Got exception: " + e.toString()); + assertTrue("testImageCapture", false); + } + } + + @LargeTest + public void testBackImageCapture() throws Exception { + Instrumentation inst = getInstrumentation(); + Intent intent = new Intent(); + + intent.setClass(getInstrumentation().getTargetContext(), CameraActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(EXTRAS_CAMERA_FACING, + android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK); + Activity act = inst.startActivitySync(intent); + Thread.sleep(WAIT_FOR_SWITCH_CAMERA); + captureImages("Back Camera Image Capture\n", inst); + act.finish(); + } + + @LargeTest + public void testFrontImageCapture() throws Exception { + Instrumentation inst = getInstrumentation(); + Intent intent = new Intent(); + + intent.setClass(getInstrumentation().getTargetContext(), CameraActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(EXTRAS_CAMERA_FACING, + android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT); + Activity act = inst.startActivitySync(intent); + Thread.sleep(WAIT_FOR_SWITCH_CAMERA); + captureImages("Front Camera Image Capture\n", inst); + act.finish(); + } +} diff --git a/tests/src/com/android/gallery3d/stress/ShotToShotLatency.java b/tests/src/com/android/gallery3d/stress/ShotToShotLatency.java new file mode 100644 index 000000000..a27bd90e6 --- /dev/null +++ b/tests/src/com/android/gallery3d/stress/ShotToShotLatency.java @@ -0,0 +1,143 @@ +/* + * 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.stress; + +import android.app.Instrumentation; +import android.os.Environment; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; +import android.view.KeyEvent; +import com.android.camera.CameraActivity; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FilenameFilter; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; + +/** + * Junit / Instrumentation test case for measuring camera shot to shot latency + */ +public class ShotToShotLatency extends ActivityInstrumentationTestCase2<CameraActivity> { + private String TAG = "ShotToShotLatency"; + private static final int TOTAL_NUMBER_OF_SNAPSHOTS = 250; + private static final long SNAPSHOT_WAIT = 1000; + private static final String CAMERA_TEST_OUTPUT_FILE = + Environment.getExternalStorageDirectory().toString() + "/mediaStressOut.txt"; + private static final String CAMERA_IMAGE_DIRECTORY = + Environment.getExternalStorageDirectory().toString() + "/DCIM/Camera/"; + + public ShotToShotLatency() { + super(CameraActivity.class); + } + + @Override + protected void setUp() throws Exception { + getActivity(); + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + private void cleanupLatencyImages() { + try { + File sdcard = new File(CAMERA_IMAGE_DIRECTORY); + File[] pics = null; + FilenameFilter filter = new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.endsWith(".jpg"); + } + }; + pics = sdcard.listFiles(filter); + for (File f : pics) { + f.delete(); + } + } catch (SecurityException e) { + Log.e(TAG, "Security manager access violation: " + e.toString()); + } + } + + private void sleep(long time) { + try { + Thread.sleep(time); + } catch (InterruptedException e) { + Log.e(TAG, "Sleep InterruptedException " + e.toString()); + } + } + + @LargeTest + public void testShotToShotLatency() { + long sigmaOfDiffFromMeanSquared = 0; + double mean = 0; + double standardDeviation = 0; + ArrayList<Long> captureTimes = new ArrayList<Long>(); + ArrayList<Long> latencyTimes = new ArrayList<Long>(); + + Log.v(TAG, "start testShotToShotLatency test"); + Instrumentation inst = getInstrumentation(); + + // Generate data points + for (int i = 0; i < TOTAL_NUMBER_OF_SNAPSHOTS; i++) { + inst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER); + sleep(SNAPSHOT_WAIT); + CameraActivity c = getActivity(); + if (c.getCaptureStartTime() > 0) { + captureTimes.add(c.getCaptureStartTime()); + } + } + + // Calculate latencies + for (int j = 1; j < captureTimes.size(); j++) { + latencyTimes.add(captureTimes.get(j) - captureTimes.get(j - 1)); + } + + // Crunch numbers + for (long dataPoint : latencyTimes) { + mean += (double) dataPoint; + } + mean /= latencyTimes.size(); + + for (long dataPoint : latencyTimes) { + sigmaOfDiffFromMeanSquared += (dataPoint - mean) * (dataPoint - mean); + } + standardDeviation = Math.sqrt(sigmaOfDiffFromMeanSquared / latencyTimes.size()); + + // Report statistics + File outFile = new File(CAMERA_TEST_OUTPUT_FILE); + BufferedWriter output = null; + try { + output = new BufferedWriter(new FileWriter(outFile, true)); + output.write("Shot to shot latency - mean: " + mean + "\n"); + output.write("Shot to shot latency - standard deviation: " + standardDeviation + "\n"); + cleanupLatencyImages(); + } catch (IOException e) { + Log.e(TAG, "testShotToShotLatency IOException writing to log " + e.toString()); + } finally { + try { + if (output != null) { + output.close(); + } + } catch (IOException e) { + Log.e(TAG, "Error closing file: " + e.toString()); + } + } + } +} diff --git a/tests/src/com/android/gallery3d/stress/SwitchPreview.java b/tests/src/com/android/gallery3d/stress/SwitchPreview.java new file mode 100755 index 000000000..955d092a6 --- /dev/null +++ b/tests/src/com/android/gallery3d/stress/SwitchPreview.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2009 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.stress; + +import com.android.camera.CameraActivity; + +import android.app.Instrumentation; +import android.content.Intent; +import android.provider.MediaStore; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; +import android.os.Environment; +import android.util.Log; + +import java.io.BufferedWriter; +import java.io.FileWriter; + +/** + * Junit / Instrumentation test case for camera test + * + * Running the test suite: + * + * adb shell am instrument \ + * -e class com.android.camera.stress.SwitchPreview \ + * -w com.android.camera.tests/com.android.camera.stress.CameraStressTestRunner + * + */ +public class SwitchPreview extends ActivityInstrumentationTestCase2 <CameraActivity>{ + private String TAG = "SwitchPreview"; + private static final int TOTAL_NUMBER_OF_SWITCHING = 200; + private static final long WAIT_FOR_PREVIEW = 4000; + + private static final String CAMERA_TEST_OUTPUT_FILE = + Environment.getExternalStorageDirectory().toString() + "/mediaStressOut.txt"; + private BufferedWriter mOut; + private FileWriter mfstream; + + public SwitchPreview() { + super(CameraActivity.class); + } + + @Override + protected void setUp() throws Exception { + getActivity(); + prepareOutputFile(); + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + getActivity().finish(); + closeOutputFile(); + super.tearDown(); + } + + private void prepareOutputFile(){ + try{ + mfstream = new FileWriter(CAMERA_TEST_OUTPUT_FILE, true); + mOut = new BufferedWriter(mfstream); + } catch (Exception e){ + assertTrue("Camera Switch Mode", false); + } + } + + private void closeOutputFile() { + try { + mOut.write("\n"); + mOut.close(); + mfstream.close(); + } catch (Exception e) { + assertTrue("CameraSwitchMode close output", false); + } + } + + @LargeTest + public void testSwitchMode() { + //Switching the video and the video recorder mode + Instrumentation inst = getInstrumentation(); + try{ + mOut.write("Camera Switch Mode:\n"); + mOut.write("No of loops :" + TOTAL_NUMBER_OF_SWITCHING + "\n"); + mOut.write("loop: "); + for (int i=0; i< TOTAL_NUMBER_OF_SWITCHING; i++) { + Thread.sleep(WAIT_FOR_PREVIEW); + Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.setClass(getInstrumentation().getTargetContext(), + CameraActivity.class); + getActivity().startActivity(intent); + Thread.sleep(WAIT_FOR_PREVIEW); + intent = new Intent(); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.setClass(getInstrumentation().getTargetContext(), + CameraActivity.class); + getActivity().startActivity(intent); + mOut.write(" ," + i); + mOut.flush(); + } + } catch (Exception e){ + Log.v(TAG, "Got exception", e); + } + } +} diff --git a/tests/src/com/android/gallery3d/stress/TestUtil.java b/tests/src/com/android/gallery3d/stress/TestUtil.java new file mode 100644 index 000000000..56ab715f7 --- /dev/null +++ b/tests/src/com/android/gallery3d/stress/TestUtil.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 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.stress; + +import android.os.Environment; +import java.io.FileWriter; +import java.io.BufferedWriter; + + +/** + * Collection of utility functions used for the test. + */ +public class TestUtil { + public BufferedWriter mOut; + public FileWriter mfstream; + + public TestUtil() { + } + + public void prepareOutputFile() throws Exception { + String camera_test_output_file = + Environment.getExternalStorageDirectory().toString() + "/mediaStressOut.txt"; + mfstream = new FileWriter(camera_test_output_file, true); + mOut = new BufferedWriter(mfstream); + } + + public void closeOutputFile() throws Exception { + mOut.write("\n"); + mOut.close(); + mfstream.close(); + } + + public void writeReportHeader(String reportTag, int iteration) throws Exception { + mOut.write(reportTag); + mOut.write("No of loops :" + iteration + "\n"); + mOut.write("loop: "); + } + + public void writeResult(int iteration) throws Exception { + mOut.write(" ," + iteration); + mOut.flush(); + } +} diff --git a/tests/src/com/android/gallery3d/stress/VideoCapture.java b/tests/src/com/android/gallery3d/stress/VideoCapture.java new file mode 100755 index 000000000..dbbd124d0 --- /dev/null +++ b/tests/src/com/android/gallery3d/stress/VideoCapture.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2010 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.stress; + +import com.android.camera.CameraActivity; +import com.android.gallery3d.stress.TestUtil; + +import android.app.Activity; +import android.app.Instrumentation; +import android.content.Intent; +import android.provider.MediaStore; +import android.test.ActivityInstrumentationTestCase2; +import android.test.suitebuilder.annotation.LargeTest; +import android.view.KeyEvent; + +import com.android.gallery3d.stress.CameraStressTestRunner; + +/** + * Junit / Instrumentation test case for camera test + * + * Running the test suite: + * + * adb shell am instrument \ + * -e class com.android.camera.stress.VideoCapture \ + * -w com.google.android.camera.tests/android.test.InstrumentationTestRunner + * + */ + +public class VideoCapture extends ActivityInstrumentationTestCase2 <CameraActivity> { + private static final long WAIT_FOR_PREVIEW = 1500; //1.5 seconds + private static final long WAIT_FOR_SWITCH_CAMERA = 3000; //2 seconds + + // Private intent extras which control the camera facing. + private final static String EXTRAS_CAMERA_FACING = + "android.intent.extras.CAMERA_FACING"; + + private TestUtil testUtil = new TestUtil(); + + public VideoCapture() { + super(CameraActivity.class); + } + + @Override + protected void setUp() throws Exception { + testUtil.prepareOutputFile(); + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + testUtil.closeOutputFile(); + super.tearDown(); + } + + @LargeTest + public void captureVideos(String reportTag, Instrumentation inst) throws Exception{ + boolean memoryResult = false; + int total_num_of_videos = CameraStressTestRunner.mVideoIterations; + int video_duration = CameraStressTestRunner.mVideoDuration; + testUtil.writeReportHeader(reportTag, total_num_of_videos); + + for (int i = 0; i < total_num_of_videos; i++) { + Thread.sleep(WAIT_FOR_PREVIEW); + // record a video + inst.sendCharacterSync(KeyEvent.KEYCODE_CAMERA); + Thread.sleep(video_duration); + inst.sendCharacterSync(KeyEvent.KEYCODE_CAMERA); + testUtil.writeResult(i); + } + } + + @LargeTest + public void testBackVideoCapture() throws Exception { + Instrumentation inst = getInstrumentation(); + Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA); + + intent.setClass(getInstrumentation().getTargetContext(), CameraActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(EXTRAS_CAMERA_FACING, + android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK); + Activity act = inst.startActivitySync(intent); + Thread.sleep(WAIT_FOR_SWITCH_CAMERA); + captureVideos("Back Camera Video Capture\n", inst); + act.finish(); + } + + @LargeTest + public void testFrontVideoCapture() throws Exception { + Instrumentation inst = getInstrumentation(); + Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA); + + intent.setClass(getInstrumentation().getTargetContext(), CameraActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(EXTRAS_CAMERA_FACING, + android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT); + Activity act = inst.startActivitySync(intent); + Thread.sleep(WAIT_FOR_SWITCH_CAMERA); + captureVideos("Front Camera Video Capture\n", inst); + act.finish(); + } +} diff --git a/tests/src/com/android/gallery3d/unittest/CameraUnitTest.java b/tests/src/com/android/gallery3d/unittest/CameraUnitTest.java new file mode 100644 index 000000000..b8fb05fc2 --- /dev/null +++ b/tests/src/com/android/gallery3d/unittest/CameraUnitTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2010 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.unittest; + +import com.android.camera.Util; + +import android.graphics.Matrix; +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +@SmallTest +public class CameraUnitTest extends TestCase { + public void testRoundOrientation() { + int h = Util.ORIENTATION_HYSTERESIS; + assertEquals(0, Util.roundOrientation(0, 0)); + assertEquals(0, Util.roundOrientation(359, 0)); + assertEquals(0, Util.roundOrientation(0 + 44 + h, 0)); + assertEquals(90, Util.roundOrientation(0 + 45 + h, 0)); + assertEquals(0, Util.roundOrientation(360 - 44 - h, 0)); + assertEquals(270, Util.roundOrientation(360 - 45 - h, 0)); + + assertEquals(90, Util.roundOrientation(90, 90)); + assertEquals(90, Util.roundOrientation(90 + 44 + h, 90)); + assertEquals(180, Util.roundOrientation(90 + 45 + h, 90)); + assertEquals(90, Util.roundOrientation(90 - 44 - h, 90)); + assertEquals(0, Util.roundOrientation(90 - 45 - h, 90)); + + assertEquals(180, Util.roundOrientation(180, 180)); + assertEquals(180, Util.roundOrientation(180 + 44 + h, 180)); + assertEquals(270, Util.roundOrientation(180 + 45 + h, 180)); + assertEquals(180, Util.roundOrientation(180 - 44 - h, 180)); + assertEquals(90, Util.roundOrientation(180 - 45 - h, 180)); + + assertEquals(270, Util.roundOrientation(270, 270)); + assertEquals(270, Util.roundOrientation(270 + 44 + h, 270)); + assertEquals(0, Util.roundOrientation(270 + 45 + h, 270)); + assertEquals(270, Util.roundOrientation(270 - 44 - h, 270)); + assertEquals(180, Util.roundOrientation(270 - 45 - h, 270)); + + assertEquals(90, Util.roundOrientation(90, 0)); + assertEquals(180, Util.roundOrientation(180, 0)); + assertEquals(270, Util.roundOrientation(270, 0)); + + assertEquals(0, Util.roundOrientation(0, 90)); + assertEquals(180, Util.roundOrientation(180, 90)); + assertEquals(270, Util.roundOrientation(270, 90)); + + assertEquals(0, Util.roundOrientation(0, 180)); + assertEquals(90, Util.roundOrientation(90, 180)); + assertEquals(270, Util.roundOrientation(270, 180)); + + assertEquals(0, Util.roundOrientation(0, 270)); + assertEquals(90, Util.roundOrientation(90, 270)); + assertEquals(180, Util.roundOrientation(180, 270)); + } + + public void testPrepareMatrix() { + Matrix matrix = new Matrix(); + float[] points; + int[] expected; + + Util.prepareMatrix(matrix, false, 0, 800, 480); + points = new float[] {-1000, -1000, 0, 0, 1000, 1000, 0, 1000, -750, 250}; + expected = new int[] {0, 0, 400, 240, 800, 480, 400, 480, 100, 300}; + matrix.mapPoints(points); + assertEquals(expected, points); + + Util.prepareMatrix(matrix, false, 90, 800, 480); + points = new float[] {-1000, -1000, 0, 0, 1000, 1000, 0, 1000, -750, 250}; + expected = new int[] {800, 0, 400, 240, 0, 480, 0, 240, 300, 60}; + matrix.mapPoints(points); + assertEquals(expected, points); + + Util.prepareMatrix(matrix, false, 180, 800, 480); + points = new float[] {-1000, -1000, 0, 0, 1000, 1000, 0, 1000, -750, 250}; + expected = new int[] {800, 480, 400, 240, 0, 0, 400, 0, 700, 180}; + matrix.mapPoints(points); + assertEquals(expected, points); + + Util.prepareMatrix(matrix, true, 180, 800, 480); + points = new float[] {-1000, -1000, 0, 0, 1000, 1000, 0, 1000, -750, 250}; + expected = new int[] {0, 480, 400, 240, 800, 0, 400, 0, 100, 180}; + matrix.mapPoints(points); + assertEquals(expected, points); + } + + private void assertEquals(int expected[], float[] actual) { + for (int i = 0; i < expected.length; i++) { + assertEquals("Array index " + i + " mismatch", expected[i], Math.round(actual[i])); + } + } +} |