diff options
| author | Jay Wang <jaywang@codeaurora.org> | 2016-06-09 12:49:49 -0700 |
|---|---|---|
| committer | Jay Wang <jaywang@codeaurora.org> | 2016-08-08 12:07:32 -0700 |
| commit | f19b070884f491ecae432914131a302876e1fcf6 (patch) | |
| tree | 2c3ddb4f766e1aa6e390b46f901cacb8183c8533 /src | |
| parent | 933ff5d0c4124f4e57a08d6e6a18fa1705241dab (diff) | |
| download | android_packages_apps_Gallery2-f19b070884f491ecae432914131a302876e1fcf6.tar.gz android_packages_apps_Gallery2-f19b070884f491ecae432914131a302876e1fcf6.tar.bz2 android_packages_apps_Gallery2-f19b070884f491ecae432914131a302876e1fcf6.zip | |
Gallery: Add TruePortrait post proc feature
Add TruePortrait post processing feature to Gallery
CRs-Fixed: 1029226
Change-Id: Id7ea0a8f810990952008fc28d30df27421c91d14
Diffstat (limited to 'src')
44 files changed, 2971 insertions, 223 deletions
diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java index 157087972..c12635f06 100644 --- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java +++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java @@ -57,7 +57,6 @@ import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; import android.view.Gravity; -import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; @@ -90,6 +89,7 @@ import com.android.gallery3d.filtershow.category.EditorCropPanel; import com.android.gallery3d.filtershow.category.MainPanel; import com.android.gallery3d.filtershow.category.StraightenPanel; import com.android.gallery3d.filtershow.category.SwipableView; +import com.android.gallery3d.filtershow.category.TruePortraitMaskEditorPanel; import com.android.gallery3d.filtershow.category.TrueScannerPanel; import com.android.gallery3d.filtershow.data.UserPresetsManager; import com.android.gallery3d.filtershow.editors.Editor; @@ -98,6 +98,9 @@ import com.android.gallery3d.filtershow.editors.EditorDualCamFusion; import com.android.gallery3d.filtershow.editors.EditorManager; import com.android.gallery3d.filtershow.editors.EditorPanel; import com.android.gallery3d.filtershow.editors.EditorStraighten; +import com.android.gallery3d.filtershow.editors.EditorTruePortraitFusion; +import com.android.gallery3d.filtershow.editors.EditorTruePortraitImageOnly; +import com.android.gallery3d.filtershow.editors.EditorTruePortraitMask; import com.android.gallery3d.filtershow.editors.HazeBusterEditor; import com.android.gallery3d.filtershow.editors.ImageOnlyEditor; import com.android.gallery3d.filtershow.editors.SeeStraightEditor; @@ -125,6 +128,7 @@ import com.android.gallery3d.filtershow.state.StateAdapter; import com.android.gallery3d.filtershow.tools.DualCameraNativeEngine; import com.android.gallery3d.filtershow.tools.DualCameraNativeEngine.DdmStatus; import com.android.gallery3d.filtershow.tools.SaveImage; +import com.android.gallery3d.filtershow.tools.TruePortraitNativeEngine; import com.android.gallery3d.filtershow.tools.XmpPresets; import com.android.gallery3d.filtershow.tools.XmpPresets.XMresults; import com.android.gallery3d.filtershow.ui.ExportDialog; @@ -132,13 +136,15 @@ import com.android.gallery3d.filtershow.ui.FramedTextButton; import com.android.gallery3d.mpo.MpoParser; import com.android.gallery3d.util.GalleryUtils; import com.android.photos.data.GalleryBitmapPool; +import com.thundersoft.hz.selfportrait.detect.FaceDetect; +import com.thundersoft.hz.selfportrait.detect.FaceInfo; import com.thundersoft.hz.selfportrait.makeup.engine.MakeupEngine; import static android.app.Activity.RESULT_OK; public class FilterShowActivity extends FragmentActivity implements OnItemClickListener, - OnShareTargetSelectedListener, DialogInterface.OnShowListener, - DialogInterface.OnDismissListener, PopupMenu.OnDismissListener{ +OnShareTargetSelectedListener, DialogInterface.OnShowListener, +DialogInterface.OnDismissListener, PopupMenu.OnDismissListener{ private String mAction = ""; MasterImage mMasterImage = null; @@ -176,6 +182,7 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL private LoadHighresBitmapTask mHiResBitmapTask; private ParseMpoDataTask mParseMpoTask; private LoadMpoDataTask mLoadMpoTask; + private LoadTruePortraitTask mLoadTruePortraitTask; private Uri mOriginalImageUri = null; private ImagePreset mOriginalPreset = null; @@ -195,6 +202,7 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL private CategoryAdapter mCategoryVersionsAdapter = null; private CategoryAdapter mCategoryMakeupAdapter = null; private CategoryAdapter mCategoryDualCamAdapter = null; + private CategoryAdapter mCategoryTruePortraitAdapter = null; private int mCurrentPanel = MainPanel.LOOKS; private Vector<FilterUserPresetRepresentation> mVersions = new Vector<FilterUserPresetRepresentation>(); @@ -365,20 +373,31 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL transaction.commitAllowingStateLoss(); } - public void loadEditorPanel(final FilterRepresentation representation, - final Editor currentEditor) { - if (currentEditor.showsActionBar()) { + public void loadEditorPanel(final FilterRepresentation representation) { + final int currentId = representation.getEditorId(); + + // show representation + if (mCurrentEditor != null) { + mCurrentEditor.detach(); + } + mCurrentEditor = mEditorPlaceHolder.showEditor(currentId); + + if (mCurrentEditor.showsActionBar()) { setActionBar(); showActionBar(true); } else { showActionBar(false); } - if (representation.getEditorId() == ImageOnlyEditor.ID) { - currentEditor.reflectCurrentFilter(); + if (currentId == ImageOnlyEditor.ID) { + mCurrentEditor.reflectCurrentFilter(); + return; + } + if (currentId == EditorTruePortraitImageOnly.ID) { + mCurrentEditor.reflectCurrentFilter(); + setActionBarForEffects(mCurrentEditor); return; } - final int currentId = currentEditor.getID(); if (currentId == EditorCrop.ID) { loadEditorCropPanel(); return; @@ -419,12 +438,30 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL }.run(); return; } + if(currentId == EditorTruePortraitMask.ID) { + new Runnable() { + @Override + public void run() { + setActionBarForEffects(mCurrentEditor); + TruePortraitMaskEditorPanel panel = new TruePortraitMaskEditorPanel(); + FragmentTransaction transaction = + getSupportFragmentManager().beginTransaction(); + transaction.remove(getSupportFragmentManager().findFragmentByTag( + MainPanel.FRAGMENT_TAG)); + transaction.replace(R.id.main_panel_container, panel, + MainPanel.FRAGMENT_TAG); + transaction.commit(); + } + }.run(); + return; + } + Runnable showEditor = new Runnable() { @Override public void run() { EditorPanel panel = new EditorPanel(); panel.setEditor(currentId); - setActionBarForEffects(currentEditor); + setActionBarForEffects(mCurrentEditor); Fragment main = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG); if (main instanceof MainPanel) { @@ -484,6 +521,10 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG); if (currentPanel instanceof MainPanel) { ((MainPanel) currentPanel).removeEditorPanelFragment(); + if (mCurrentEditor != null) { + mCurrentEditor.detach(); + } + mCurrentEditor = null; } } @@ -540,8 +581,8 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL } public void toggleComparisonButtonVisibility() { - if (imgComparison.getVisibility() == View.VISIBLE) - imgComparison.setVisibility(View.GONE); + if (imgComparison.getVisibility() == View.VISIBLE) + imgComparison.setVisibility(View.GONE); } private void showSaveButtonIfNeed() { @@ -592,10 +633,6 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL } public void setActionBarForEffects(final Editor currentEditor) { - View view; - LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); - view = inflater.inflate(R.layout.filtershow_editor_panel, null); - View editControl = view.findViewById(R.id.controlArea); ActionBar actionBar = getActionBar(); actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); actionBar.setBackgroundDrawable(new ColorDrawable(getResources() @@ -628,7 +665,11 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL }); if (currentEditor != null) { - currentEditor.setUpEditorUI(editTitle, actionControl, editControl); + if(!currentEditor.showsActionBarControls()) { + cancelButton.setVisibility(View.GONE); + applyButton.setVisibility(View.GONE); + } + currentEditor.setEditorTitle(editTitle); currentEditor.reflectCurrentFilter(); if (currentEditor.useUtilityPanel()) { currentEditor.openUtilityPanel((LinearLayout) actionControl); @@ -658,6 +699,12 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL int position = adapter.undo(); masterImage.onHistoryItemClick(position); invalidateViews(); + + if(!masterImage.hasFusionApplied()) { + masterImage.setFusionUnderlay(null); + masterImage.setScaleFactor(1); + masterImage.resetTranslation(); + } } public void adjustCompareButton(boolean scaled) { @@ -686,6 +733,7 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL fillVersions(); fillMakeup(); fillDualCamera(); + fillTruePortrait(); } public void setupStatePanel() { @@ -808,6 +856,21 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL } } + private void fillTruePortrait() { + FiltersManager filtersManager = FiltersManager.getManager(); + ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getTruePortrait(); + if (mCategoryTruePortraitAdapter != null) { + mCategoryTruePortraitAdapter.clear(); + } + mCategoryTruePortraitAdapter = new CategoryAdapter(this); + for (FilterRepresentation representation : filtersRepresentations) { + if (representation.getTextId() != 0) { + representation.setName(getString(representation.getTextId())); + } + mCategoryTruePortraitAdapter.add(new Action(this, representation)); + } + } + private void fillTrueScanner() { FiltersManager filtersManager = FiltersManager.getManager(); ArrayList<FilterRepresentation> trueScannerRepresentations = filtersManager.getTrueScanner(); @@ -926,6 +989,11 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL mainPanel.updateDualCameraButton(); } } + + if(TruePortraitNativeEngine.getInstance().isLibLoaded()) { + mLoadTruePortraitTask = new LoadTruePortraitTask(); + mLoadTruePortraitTask.execute(uri); + } } private void fillBorders() { @@ -987,6 +1055,10 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL return mCategoryDualCamAdapter; } + public CategoryAdapter getCategoryTruePortraitAdapter() { + return mCategoryTruePortraitAdapter; + } + public void removeFilterRepresentation(FilterRepresentation filterRepresentation) { if (filterRepresentation == null) { return; @@ -1006,8 +1078,8 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL return; } if (!(filterRepresentation instanceof FilterRotateRepresentation) - && !(filterRepresentation instanceof FilterMirrorRepresentation) - && MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) { + && !(filterRepresentation instanceof FilterMirrorRepresentation) + && MasterImage.getImage().getCurrentFilterRepresentation() == filterRepresentation) { return; } if (filterRepresentation instanceof FilterUserPresetRepresentation @@ -1085,12 +1157,7 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL } useFilterRepresentation(representation); - // show representation - if (mCurrentEditor != null) { - mCurrentEditor.detach(); - } - mCurrentEditor = mEditorPlaceHolder.showEditor(representation.getEditorId()); - loadEditorPanel(representation, mCurrentEditor); + loadEditorPanel(representation); } public Editor getEditor(int editorID) { @@ -1122,6 +1189,7 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL mCategoryMakeupAdapter.reflectImagePreset(preset); } mCategoryDualCamAdapter.reflectImagePreset(preset); + mCategoryTruePortraitAdapter.reflectImagePreset(preset); } public View getMainStatePanelContainer(int id) { @@ -1221,16 +1289,15 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL mAuxImgData == null) { // parse failed MasterImage.getImage().setDepthMapLoadingStatus(DdmStatus.DDM_FAILED); + Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG); + if (currentPanel instanceof MainPanel) { + MainPanel mainPanel = (MainPanel) currentPanel; + mainPanel.updateDualCameraButton(); + } } else { mLoadMpoTask = new LoadMpoDataTask(); mLoadMpoTask.execute(mPrimaryImgData, mAuxImgData); } - - Fragment currentPanel = getSupportFragmentManager().findFragmentByTag(MainPanel.FRAGMENT_TAG); - if (currentPanel instanceof MainPanel) { - MainPanel mainPanel = (MainPanel) currentPanel; - mainPanel.updateDualCameraButton(); - } } } @@ -1288,7 +1355,40 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL if(mLoadingDialog != null && mLoadingDialog.isShowing()) { mLoadingDialog.dismiss(); } - } + } + + private class LoadTruePortraitTask extends AsyncTask<Uri, Void, Boolean> { + + @Override + protected Boolean doInBackground(Uri... params) { + boolean result = false; + Bitmap src = ImageLoader.loadBitmap(FilterShowActivity.this, params[0], null); + FaceDetect fDetect = new FaceDetect(); + fDetect.initialize(); + FaceInfo[] faceInfos = fDetect.dectectFeatures(src); + fDetect.uninitialize(); + + if(faceInfos != null && faceInfos.length > 0) { + Rect[] faces = new Rect[faceInfos.length]; + for(int i=0; i<faceInfos.length; i++) { + faces[i] = faceInfos[i].face; + } + + result = TruePortraitNativeEngine.getInstance().init(FilterShowActivity.this, src, faces); + } else { + TruePortraitNativeEngine.getInstance().setFacesDetected(false); + } + + src.recycle(); + src = null; + + return result; + } + + @Override + protected void onPostExecute(Boolean result) { + } + } private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> { int mBitmapSize; @@ -1362,6 +1462,7 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL mCategoryGeometryAdapter.imageLoaded(); mCategoryFiltersAdapter.imageLoaded(); mCategoryDualCamAdapter.imageLoaded(); + mCategoryTruePortraitAdapter.imageLoaded(); if(mCategoryMakeupAdapter != null) { mCategoryMakeupAdapter.imageLoaded(); } @@ -1421,6 +1522,10 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL mLoadMpoTask.cancel(false); } + if(mLoadTruePortraitTask != null) { + mLoadTruePortraitTask.cancel(false); + } + mUserPresetsManager.close(); unregisterReceiver(mHandlerReceiver); doUnbindService(); @@ -1515,11 +1620,15 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL menu.clear(); getMenuInflater().inflate(R.menu.filtershow_activity_menu, menu); mShareActionProvider = (ShareActionProvider) menu.findItem( - R.id.menu_share).getActionProvider(); + R.id.menu_share).getActionProvider(); mShareActionProvider.setShareIntent(getDefaultShareIntent()); mShareActionProvider.setOnShareTargetSelectedListener(this); mMenu = menu; setupMenu(); + + if(mCurrentEditor != null) { + mCurrentEditor.onPrepareOptionsMenu(menu); + } return true; } @@ -1560,7 +1669,7 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - /*case R.id.undoButton: { + /*case R.id.undoButton: { HistoryManager adapter = mMasterImage.getHistory(); int position = adapter.undo(); mMasterImage.onHistoryItemClick(position); @@ -1575,26 +1684,26 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL invalidateViews(); return true; }*/ - case R.id.resetHistoryButton: { - resetHistory(); - return true; - } - /*case R.id.showImageStateButton: { + case R.id.resetHistoryButton: { + resetHistory(); + return true; + } + /*case R.id.showImageStateButton: { toggleImageStatePanel(); return true; }*/ - case R.id.exportFlattenButton: { - showExportOptionsDialog(); - return true; - } - case android.R.id.home: { - saveImage(); - return true; - } - case R.id.manageUserPresets: { - manageUserPresets(); - return true; - } + case R.id.exportFlattenButton: { + showExportOptionsDialog(); + return true; + } + case android.R.id.home: { + saveImage(); + return true; + } + case R.id.manageUserPresets: { + manageUserPresets(); + return true; + } } return false; } @@ -1891,18 +2000,18 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL R.string.save_before_exit); mBackAlertDialogBuilder.setPositiveButton(mPopUpText, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - done(); - } - }); + @Override + public void onClick(DialogInterface dialog, int id) { + done(); + } + }); mBackAlertDialogBuilder.setNegativeButton(mCancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - dialog.dismiss(); - } - }); + @Override + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + } + }); } public void cannotLoadImage() { @@ -1942,8 +2051,13 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL } else if (requestCode == SELECT_FUSION_UNDERLAY) { Uri underlayImageUri = data.getData(); // find fusion representation - EditorDualCamFusion editor = (EditorDualCamFusion)getEditor(EditorDualCamFusion.ID); - editor.setUnderlayImageUri(underlayImageUri); + if(mCurrentEditor instanceof EditorDualCamFusion) { + EditorDualCamFusion editor = (EditorDualCamFusion)mCurrentEditor; + editor.setUnderlayImageUri(underlayImageUri); + } else if (mCurrentEditor instanceof EditorTruePortraitFusion) { + EditorTruePortraitFusion editor = (EditorTruePortraitFusion)mCurrentEditor; + editor.setUnderlayImageUri(underlayImageUri); + } } } } diff --git a/src/com/android/gallery3d/filtershow/cache/BitmapCache.java b/src/com/android/gallery3d/filtershow/cache/BitmapCache.java index 10cadcfd2..82129c2c6 100644 --- a/src/com/android/gallery3d/filtershow/cache/BitmapCache.java +++ b/src/com/android/gallery3d/filtershow/cache/BitmapCache.java @@ -16,18 +16,18 @@ package com.android.gallery3d.filtershow.cache; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashMap; + import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.PorterDuff; +import android.graphics.Color; import android.util.Log; import com.android.gallery3d.filtershow.pipeline.Buffer; import com.android.gallery3d.filtershow.pipeline.CacheProcessing; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.HashMap; - public class BitmapCache { private static final String LOGTAG = "BitmapCache"; private HashMap<Long, ArrayList<WeakReference<Bitmap>>> @@ -207,8 +207,8 @@ public class BitmapCache { public synchronized Bitmap getBitmapCopy(Bitmap source, int type) { Bitmap bitmap = getBitmap(source.getWidth(), source.getHeight(), type); + bitmap.eraseColor(Color.TRANSPARENT); Canvas canvas = new Canvas(bitmap); - canvas.drawColor(0, PorterDuff.Mode.CLEAR); canvas.drawBitmap(source, 0, 0, null); return bitmap; } diff --git a/src/com/android/gallery3d/filtershow/category/Action.java b/src/com/android/gallery3d/filtershow/category/Action.java index 9c0587bfc..75982d851 100644 --- a/src/com/android/gallery3d/filtershow/category/Action.java +++ b/src/com/android/gallery3d/filtershow/category/Action.java @@ -30,6 +30,7 @@ import android.widget.ListAdapter; import org.codeaurora.gallery.R; import com.android.gallery3d.filtershow.FilterShowActivity; +import com.android.gallery3d.filtershow.cache.BitmapCache; import com.android.gallery3d.filtershow.filters.FilterDrawRepresentation; import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation; import com.android.gallery3d.filtershow.pipeline.RenderingRequest; @@ -121,6 +122,16 @@ public class Action implements RenderingRequestCaller { if (getType() == Action.ADD_ACTION) { return; } + + if(mRepresentation.getOverlayOnly()) { + mImageFrame = imageFrame; + int w = mImageFrame.width(); + int h = mImageFrame.height(); + mImage = MasterImage.getImage().getBitmapCache().getBitmap(w, h, BitmapCache.ICON); + drawOverlay(true); + return; + } + Bitmap temp = MasterImage.getImage().getTemporaryThumbnailBitmap(); if (temp != null) { mImage = temp; @@ -175,14 +186,7 @@ public class Action implements RenderingRequestCaller { canvas.drawBitmap(source, m, new Paint(Paint.FILTER_BITMAP_FLAG)); } - @Override - public void available(RenderingRequest request) { - clearBitmap(); - mImage = request.getBitmap(); - if (mImage == null) { - mImageFrame = null; - return; - } + private void drawOverlay(boolean scale) { if (mRepresentation.getOverlayId() != 0 && mOverlayBitmap == null) { mOverlayBitmap = BitmapFactory.decodeResource( mContext.getResources(), @@ -196,9 +200,22 @@ public class Action implements RenderingRequestCaller { } else { Canvas canvas = new Canvas(mImage); canvas.drawARGB(128, 0, 0, 0); - drawCenteredImage(mOverlayBitmap, mImage, false); + drawCenteredImage(mOverlayBitmap, mImage, scale); } } + } + + @Override + public void available(RenderingRequest request) { + clearBitmap(); + mImage = request.getBitmap(); + if (mImage == null) { + mImageFrame = null; + return; + } + + drawOverlay(false); + if (mAdapter != null) { mAdapter.notifyDataSetChanged(); } diff --git a/src/com/android/gallery3d/filtershow/category/CategoryAdapter.java b/src/com/android/gallery3d/filtershow/category/CategoryAdapter.java index 4062bf0ef..6c4f9fe87 100644 --- a/src/com/android/gallery3d/filtershow/category/CategoryAdapter.java +++ b/src/com/android/gallery3d/filtershow/category/CategoryAdapter.java @@ -80,15 +80,18 @@ public class CategoryAdapter extends ArrayAdapter<Action> { mSelectedPosition = 0; mAddButtonText = getContext().getString(R.string.filtershow_add_button_looks); } -// if (category == MainPanel.MAKEUP) { -// mSelectedPosition = 0; -// } + // if (category == MainPanel.MAKEUP) { + // mSelectedPosition = 0; + // } if (category == MainPanel.BORDERS) { mSelectedPosition = 0; } if (category == MainPanel.DUALCAM) { mSelectedPosition = 0; } + if (category == MainPanel.TRUEPORTRAIT) { + mSelectedPosition = 0; + } if (category == MainPanel.TRUESCANNER) { mSelectedPosition = 0; @@ -149,6 +152,10 @@ public class CategoryAdapter extends ArrayAdapter<Action> { return (Integer) v.getTag() == mSelectedPosition; } + public int getCurrentSelection() { + return mSelectedPosition; + } + private void invalidateView(int position) { View child = null; if (mContainer instanceof ListView) { @@ -235,6 +242,11 @@ public class CategoryAdapter extends ArrayAdapter<Action> { if (pos != -1) { rep = preset.getFilterRepresentation(pos); } + } else if (mCategory == MainPanel.TRUEPORTRAIT) { + int pos = preset.getPositionForType(FilterRepresentation.TYPE_TRUEPORTRAIT); + if (pos != -1) { + rep = preset.getFilterRepresentation(pos); + } } else if (mCategory == MainPanel.FILTERS || mCategory == MainPanel.MAKEUP) { selected = UNKNOWN_INDEX; } diff --git a/src/com/android/gallery3d/filtershow/category/CategoryPanel.java b/src/com/android/gallery3d/filtershow/category/CategoryPanel.java index 87f347586..3b91f024e 100644 --- a/src/com/android/gallery3d/filtershow/category/CategoryPanel.java +++ b/src/com/android/gallery3d/filtershow/category/CategoryPanel.java @@ -90,6 +90,14 @@ public class CategoryPanel extends Fragment implements View.OnClickListener { activity.updateCategories(); break; } + case MainPanel.TRUEPORTRAIT: { + mAdapter = activity.getCategoryTruePortraitAdapter(); + if (mAdapter != null) { + mAdapter.initializeSelection(MainPanel.TRUEPORTRAIT); + } + activity.updateCategories(); + break; + } case MainPanel.TRUESCANNER: { mAdapter = activity.getCategoryTrueScannerAdapter(); if (mAdapter != null) { diff --git a/src/com/android/gallery3d/filtershow/category/MainPanel.java b/src/com/android/gallery3d/filtershow/category/MainPanel.java index 6571c46a3..4c909789d 100644 --- a/src/com/android/gallery3d/filtershow/category/MainPanel.java +++ b/src/com/android/gallery3d/filtershow/category/MainPanel.java @@ -17,6 +17,8 @@ package com.android.gallery3d.filtershow.category; import android.content.res.Configuration; +import android.content.Context; +import android.content.DialogInterface; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; @@ -40,6 +42,9 @@ import com.android.gallery3d.filtershow.imageshow.MasterImage; import com.android.gallery3d.filtershow.state.StatePanel; import com.android.gallery3d.filtershow.tools.DualCameraNativeEngine; import com.android.gallery3d.filtershow.tools.DualCameraNativeEngine.DdmStatus; +import com.android.gallery3d.filtershow.tools.TruePortraitNativeEngine; +import com.android.gallery3d.filtershow.ui.DoNotShowAgainDialog; +import com.android.gallery3d.util.GalleryUtils; public class MainPanel extends Fragment implements BottomPanel.BottomPanelDelegate { @@ -56,6 +61,7 @@ public class MainPanel extends Fragment implements BottomPanel.BottomPanelDelega private ImageButton trueScannerButton; private ImageButton hazeBusterButton; private ImageButton seeStraightButton; + private ImageButton truePortraitButton; public static final String FRAGMENT_TAG = "MainPanel"; public static final String EDITOR_TAG = "coming-from-editor-panel"; @@ -69,6 +75,7 @@ public class MainPanel extends Fragment implements BottomPanel.BottomPanelDelega public static final int TRUESCANNER = 7; public static final int HAZEBUSTER = 8; public static final int SEESTRAIGHT = 9; + public static final int TRUEPORTRAIT = 10; private int mCurrentSelected = -1; private int mPreviousToggleVersions = -1; @@ -105,6 +112,10 @@ public class MainPanel extends Fragment implements BottomPanel.BottomPanelDelega dualCamButton.setSelected(value); break; } + case TRUEPORTRAIT: { + truePortraitButton.setSelected(value); + break; + } case TRUESCANNER: { if (trueScannerButton != null) { trueScannerButton.setSelected(value); @@ -139,7 +150,7 @@ public class MainPanel extends Fragment implements BottomPanel.BottomPanelDelega @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { + Bundle savedInstanceState) { mMainView = (LinearLayout) inflater.inflate( R.layout.filtershow_main_panel, null, false); @@ -204,6 +215,11 @@ public class MainPanel extends Fragment implements BottomPanel.BottomPanelDelega seeStraightButton.setVisibility(View.GONE); } + truePortraitButton = (ImageButton) bottomPanel.findViewById(R.id.truePortraitButton); + if(!TruePortraitNativeEngine.getInstance().isLibLoaded()) { + truePortraitButton.setVisibility(View.GONE); + } + looksButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -235,9 +251,42 @@ public class MainPanel extends Fragment implements BottomPanel.BottomPanelDelega } }); + truePortraitButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(final View v) { + Context context = getActivity(); + boolean skipIntro = GalleryUtils.getBooleanPref(context, + context.getString(R.string.pref_trueportrait_intro_show_key), false); + final boolean facesDetected = TruePortraitNativeEngine.getInstance().facesDetected(); + if(skipIntro && facesDetected) { + showPanel(TRUEPORTRAIT); + } else if(!skipIntro) { + DoNotShowAgainDialog dialog = new DoNotShowAgainDialog( + R.string.trueportrait, R.string.trueportrait_intro, + R.string.pref_trueportrait_intro_show_key) { + @Override + public void onDismiss(DialogInterface dialog) { + super.onDismiss(dialog); + if(facesDetected) { + showPanel(TRUEPORTRAIT); + } else { + v.setEnabled(false); + TruePortraitNativeEngine.getInstance().showNoFaceDetectedDialog(getFragmentManager()); + } + } + }; + dialog.show(getFragmentManager(), "trueportrait_intro"); + } else { + v.setEnabled(false); + TruePortraitNativeEngine.getInstance().showNoFaceDetectedDialog(getFragmentManager()); + } + } + }); + trueScannerButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + showPanel(TRUESCANNER); } }); @@ -449,6 +498,19 @@ public class MainPanel extends Fragment implements BottomPanel.BottomPanelDelega } } + public void loadCategoryTruePortraitPanel() { + if (mCurrentSelected == TRUEPORTRAIT) { + return; + } + boolean fromRight = isRightAnimation(TRUEPORTRAIT); + selection(mCurrentSelected, false); + CategoryPanel categoryPanel = new CategoryPanel(); + categoryPanel.setAdapter(TRUEPORTRAIT); + setCategoryFragment(categoryPanel, fromRight); + mCurrentSelected = TRUEPORTRAIT; + selection(mCurrentSelected, true); + } + public void showPanel(int currentPanel) { FilterShowActivity activity = (FilterShowActivity) getActivity(); switch (currentPanel) { @@ -492,6 +554,10 @@ public class MainPanel extends Fragment implements BottomPanel.BottomPanelDelega loadCategoryMakeupPanel(); break; } + case TRUEPORTRAIT: { + loadCategoryTruePortraitPanel(); + break; + } } if (currentPanel > 0) { activity.adjustCompareButton(true); diff --git a/src/com/android/gallery3d/filtershow/category/TruePortraitMaskEditorPanel.java b/src/com/android/gallery3d/filtershow/category/TruePortraitMaskEditorPanel.java new file mode 100644 index 000000000..03935edc0 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/category/TruePortraitMaskEditorPanel.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.gallery3d.filtershow.category; + + +import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import org.codeaurora.gallery.R; +import com.android.gallery3d.filtershow.FilterShowActivity; +import com.android.gallery3d.filtershow.editors.EditorTruePortraitMask; +import com.android.gallery3d.filtershow.ui.DoNotShowAgainDialog; +import com.android.gallery3d.util.GalleryUtils; + +public class TruePortraitMaskEditorPanel extends Fragment { + private View mMainView; + private EditorTruePortraitMask mEditor; + + @Override + public void onAttach(Context context) { + super.onAttach(context); + FilterShowActivity filterShowActivity = (FilterShowActivity) context; + mEditor = (EditorTruePortraitMask) filterShowActivity.getEditor(EditorTruePortraitMask.ID); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + mMainView = inflater.inflate(R.layout.filtershow_trueportrait_editor_panel, container, false); + mEditor.setUpEditorUI(mMainView, null); + return mMainView; + } + + @Override + public void onResume() { + super.onResume(); + Context context = getActivity(); + boolean skipIntro = GalleryUtils.getBooleanPref(context, + context.getString(R.string.pref_trueportrait_edit_intro_show_key), false); + if(!skipIntro) { + DoNotShowAgainDialog dialog = new DoNotShowAgainDialog( + R.string.trueportrait_touch_up, R.string.trueportrait_edit_intro, + R.string.pref_trueportrait_edit_intro_show_key); + dialog.setCancelable(false); + dialog.show(getFragmentManager(), "trueportrait_edit_intro"); + } + } + + @Override + public void onDetach() { + if (mEditor != null) { + mEditor.detach(); + } + super.onDetach(); + } +}
\ No newline at end of file diff --git a/src/com/android/gallery3d/filtershow/controller/BasicSlider.java b/src/com/android/gallery3d/filtershow/controller/BasicSlider.java index eb6a491f4..d0d0fcaa6 100644 --- a/src/com/android/gallery3d/filtershow/controller/BasicSlider.java +++ b/src/com/android/gallery3d/filtershow/controller/BasicSlider.java @@ -20,7 +20,6 @@ import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; @@ -41,7 +40,7 @@ public class BasicSlider implements Control { mParameter = (ParameterInteger) parameter; LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - LinearLayout lp = (LinearLayout) inflater.inflate( + View lp = inflater.inflate( R.layout.filtershow_seekbar, container, true); mSeekBar = (SeekBar) lp.findViewById(R.id.primarySeekBar); mSeekBar.setVisibility(View.VISIBLE); diff --git a/src/com/android/gallery3d/filtershow/editors/Editor.java b/src/com/android/gallery3d/filtershow/editors/Editor.java index f68689388..e5d9e486e 100644 --- a/src/com/android/gallery3d/filtershow/editors/Editor.java +++ b/src/com/android/gallery3d/filtershow/editors/Editor.java @@ -16,10 +16,11 @@ package com.android.gallery3d.filtershow.editors; +import java.util.ArrayList; +import java.util.Collection; + import android.content.Context; import android.content.res.Configuration; -import android.util.AttributeSet; -import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -41,9 +42,6 @@ import com.android.gallery3d.filtershow.imageshow.ImageShow; import com.android.gallery3d.filtershow.imageshow.MasterImage; import com.android.gallery3d.filtershow.pipeline.ImagePreset; -import java.util.ArrayList; -import java.util.Collection; - /** * Base class for Editors Must contain a mImageShow and a top level view */ @@ -102,6 +100,10 @@ public class Editor implements OnSeekBarChangeListener, SwapButton.SwapButtonLis return !showsSeekBar(); } + public boolean showsActionBarControls() { + return showsActionBar(); + } + /*public void setUpEditorUI(View actionButton, View editControl, Button editTitle, Button stateButton) { mEditTitle = editTitle; @@ -116,14 +118,12 @@ public class Editor implements OnSeekBarChangeListener, SwapButton.SwapButtonLis mFilterTitle = stateButton; MasterImage.getImage().resetGeometryImages(false); mEditControl = editControl; - setUtilityPanelUI(null, editControl); + setEditPanelUI(editControl); } - public void setUpEditorUI(Button editTitle, View actionButton, - View editControl) { + public void setEditorTitle(Button editTitle) { mEditTitle = editTitle; mButton = editTitle; - setUtilityPanelUI(actionButton, editControl); } public void setBasicFilterUI(TextView textFilterName, @@ -132,21 +132,18 @@ public class Editor implements OnSeekBarChangeListener, SwapButton.SwapButtonLis mBasicFilterValue = textFilterValue; } - public boolean showsPopupIndicator() { + public boolean showsPopupIndicator() { return false; } /** - * @param actionButton the would be the area for menu etc * @param editControl this is the black area for sliders etc */ - public void setUtilityPanelUI(View actionButton, View editControl) { - - AttributeSet aset; + public void setEditPanelUI(View editControl) { Context context = editControl.getContext(); LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - LinearLayout lp = (LinearLayout) inflater.inflate( + View lp = inflater.inflate( R.layout.filtershow_seekbar, (ViewGroup) editControl, true); mSeekBar = (SeekBar) lp.findViewById(R.id.primarySeekBar); mSeekBar.setOnSeekBarChangeListener(this); @@ -298,7 +295,7 @@ public class Editor implements OnSeekBarChangeListener, SwapButton.SwapButtonLis String[] split = text.split("[+-]"); int length = split.length; if (length == 2) - mBasicFilterValue.setText(split[1] != null ? split[1] : ""); + mBasicFilterValue.setText(split[1] != null ? split[1] : ""); mBasicFilterText.setVisibility(View.VISIBLE); mBasicFilterValue.setVisibility(View.VISIBLE); mButton.setVisibility(View.INVISIBLE); @@ -380,4 +377,12 @@ public class Editor implements OnSeekBarChangeListener, SwapButton.SwapButtonLis mImageShow.detach(); } } + + public void resume() { + + } + + public void onPrepareOptionsMenu(Menu menu) { + + } } diff --git a/src/com/android/gallery3d/filtershow/editors/EditorChanSat.java b/src/com/android/gallery3d/filtershow/editors/EditorChanSat.java index 0cc2ee322..664aa6c40 100644 --- a/src/com/android/gallery3d/filtershow/editors/EditorChanSat.java +++ b/src/com/android/gallery3d/filtershow/editors/EditorChanSat.java @@ -168,12 +168,11 @@ public class EditorChanSat extends ParametricEditor implements OnSeekBarChangeLi } @Override - public void setUtilityPanelUI(View actionButton, View editControl) { + public void setEditPanelUI(View editControl) { if (useCompact(mContext)) { - super.setUtilityPanelUI(actionButton, editControl); + super.setEditPanelUI(editControl); return; } - mActionButton = actionButton; mEditControl = editControl; mEditTitle.setCompoundDrawables(null, null, null, null); LinearLayout group = (LinearLayout) editControl; diff --git a/src/com/android/gallery3d/filtershow/editors/EditorColorBorder.java b/src/com/android/gallery3d/filtershow/editors/EditorColorBorder.java index 71f4dcb58..90252859f 100644 --- a/src/com/android/gallery3d/filtershow/editors/EditorColorBorder.java +++ b/src/com/android/gallery3d/filtershow/editors/EditorColorBorder.java @@ -17,15 +17,9 @@ package com.android.gallery3d.filtershow.editors; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Color; -import android.util.Log; -import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; -import android.view.ViewGroup; import android.widget.Button; import android.widget.FrameLayout; import android.widget.LinearLayout; @@ -34,9 +28,7 @@ import android.widget.SeekBar; import org.codeaurora.gallery.R; import com.android.gallery3d.filtershow.FilterShowActivity; -import com.android.gallery3d.filtershow.controller.BitmapCaller; import com.android.gallery3d.filtershow.controller.ColorChooser; -import com.android.gallery3d.filtershow.controller.FilterView; import com.android.gallery3d.filtershow.filters.FilterColorBorderRepresentation; import com.android.gallery3d.filtershow.filters.FilterRepresentation; import com.android.gallery3d.filtershow.filters.ImageFilterColorBorder; @@ -187,9 +179,9 @@ public class EditorColorBorder extends ParametricEditor { } @Override - public void setUtilityPanelUI(View actionButton, View editControl) { + public void setEditPanelUI(View editControl) { if (ParametricEditor.useCompact(mContext)) { - super.setUtilityPanelUI(actionButton, editControl); + super.setEditPanelUI(editControl); return; } mSeekBar = (SeekBar) editControl.findViewById(R.id.primarySeekBar); diff --git a/src/com/android/gallery3d/filtershow/editors/EditorCrop.java b/src/com/android/gallery3d/filtershow/editors/EditorCrop.java index e4db1f23d..425915736 100644 --- a/src/com/android/gallery3d/filtershow/editors/EditorCrop.java +++ b/src/com/android/gallery3d/filtershow/editors/EditorCrop.java @@ -145,8 +145,8 @@ public class EditorCrop extends Editor implements EditorInfo { } @Override - public void setUtilityPanelUI(View actionButton, View editControl) { - super.setUtilityPanelUI(actionButton,editControl); + public void setEditPanelUI(View editControl) { + super.setEditPanelUI(editControl); setMenuIcon(true); } diff --git a/src/com/android/gallery3d/filtershow/editors/EditorCurves.java b/src/com/android/gallery3d/filtershow/editors/EditorCurves.java index 475c86422..e305f7abd 100644 --- a/src/com/android/gallery3d/filtershow/editors/EditorCurves.java +++ b/src/com/android/gallery3d/filtershow/editors/EditorCurves.java @@ -61,8 +61,8 @@ public class EditorCurves extends Editor { } } @Override - public void setUtilityPanelUI(View actionButton, View editControl) { - super.setUtilityPanelUI(actionButton,editControl); + public void setEditPanelUI(View editControl) { + super.setEditPanelUI(editControl); setMenuIcon(true); } diff --git a/src/com/android/gallery3d/filtershow/editors/EditorDraw.java b/src/com/android/gallery3d/filtershow/editors/EditorDraw.java index 20d788510..5298cab1c 100644 --- a/src/com/android/gallery3d/filtershow/editors/EditorDraw.java +++ b/src/com/android/gallery3d/filtershow/editors/EditorDraw.java @@ -241,9 +241,9 @@ public class EditorDraw extends ParametricEditor implements FilterView { } @Override - public void setUtilityPanelUI(View actionButton, View editControl) { + public void setEditPanelUI(View editControl) { if (ParametricEditor.useCompact(mContext)) { - super.setUtilityPanelUI(actionButton, editControl); + super.setEditPanelUI(editControl); return; } diff --git a/src/com/android/gallery3d/filtershow/editors/EditorDualCamFusion.java b/src/com/android/gallery3d/filtershow/editors/EditorDualCamFusion.java index d20b15511..2371ccb15 100644 --- a/src/com/android/gallery3d/filtershow/editors/EditorDualCamFusion.java +++ b/src/com/android/gallery3d/filtershow/editors/EditorDualCamFusion.java @@ -76,7 +76,7 @@ public class EditorDualCamFusion extends ImageOnlyEditor { } @Override - public void setUtilityPanelUI(View actionButton, View editControl) { + public void setEditPanelUI(View editControl) { editControl.setVisibility(View.GONE); } diff --git a/src/com/android/gallery3d/filtershow/editors/EditorGrad.java b/src/com/android/gallery3d/filtershow/editors/EditorGrad.java index 3624a6e10..25000117b 100644 --- a/src/com/android/gallery3d/filtershow/editors/EditorGrad.java +++ b/src/com/android/gallery3d/filtershow/editors/EditorGrad.java @@ -140,9 +140,9 @@ public class EditorGrad extends ParametricEditor } @Override - public void setUtilityPanelUI(View actionButton, View editControl) { + public void setEditPanelUI(View editControl) { if (ParametricEditor.useCompact(mContext)) { - super.setUtilityPanelUI(actionButton, editControl); + super.setEditPanelUI(editControl); return; } mSeekBar = (SeekBar) editControl.findViewById(R.id.primarySeekBar); diff --git a/src/com/android/gallery3d/filtershow/editors/EditorMakeup.java b/src/com/android/gallery3d/filtershow/editors/EditorMakeup.java index 26ecd4f49..50377c1ee 100644 --- a/src/com/android/gallery3d/filtershow/editors/EditorMakeup.java +++ b/src/com/android/gallery3d/filtershow/editors/EditorMakeup.java @@ -31,8 +31,8 @@ public class EditorMakeup extends BasicEditor { } @Override - public void setUtilityPanelUI(View actionButton, View editControl) { - super.setUtilityPanelUI(actionButton, editControl); + public void setEditPanelUI(View editControl) { + super.setEditPanelUI(editControl); mSeekBar = (SeekBar) editControl.findViewById(R.id.primarySeekBar); if (mSeekBar != null) { mSeekBar.setVisibility(View.INVISIBLE); diff --git a/src/com/android/gallery3d/filtershow/editors/EditorPanel.java b/src/com/android/gallery3d/filtershow/editors/EditorPanel.java index 34f711df9..00c0aa795 100644 --- a/src/com/android/gallery3d/filtershow/editors/EditorPanel.java +++ b/src/com/android/gallery3d/filtershow/editors/EditorPanel.java @@ -22,6 +22,7 @@ import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.util.Log; import android.view.LayoutInflater; +import android.view.Menu; import android.view.View; import android.view.ViewGroup; import android.widget.Button; @@ -68,10 +69,6 @@ public class EditorPanel extends Fragment { int position = adapter.undo(); masterImage.onHistoryItemClick(position); ((FilterShowActivity)getActivity()).invalidateViews(); - - if(!masterImage.hasFusionApplied()) { - masterImage.setFusionUnderlay(null); - } } @Override @@ -100,6 +97,14 @@ public class EditorPanel extends Fragment { } @Override + public void onResume() { + if (mEditor != null) { + mEditor.resume(); + } + super.onResume(); + } + + @Override public void onDetach() { if (mEditor != null) { mEditor.detach(); diff --git a/src/com/android/gallery3d/filtershow/editors/EditorTruePortraitBasic.java b/src/com/android/gallery3d/filtershow/editors/EditorTruePortraitBasic.java new file mode 100644 index 000000000..a09e216b5 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/editors/EditorTruePortraitBasic.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.gallery3d.filtershow.editors; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.LinearLayout; + +import org.codeaurora.gallery.R; +import com.android.gallery3d.filtershow.FilterShowActivity; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; + +public class EditorTruePortraitBasic extends BasicEditor { + private static final String LOGTAG = "EditorTruePortraitBasic"; + public static final int ID = R.id.editorTruePortraitBasic; + + public EditorTruePortraitBasic() { + super(ID); + } + + @Override + public boolean showsActionBar() { + return true; + } + + @Override + public boolean showsActionBarControls() { + return false; + } + + @Override + public void openUtilityPanel(LinearLayout accessoryViewList) { + accessoryViewList.removeAllViews(); + + LayoutInflater inflater = (LayoutInflater) mContext.getSystemService + (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.filtershow_actionbar_trueportrait, accessoryViewList); + + View editMask = accessoryViewList.findViewById(R.id.editMask); + editMask.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + FilterRepresentation representation = new FilterRepresentation(""); + representation.setEditorId(EditorTruePortraitMask.ID); + ((FilterShowActivity)mContext).loadEditorPanel(representation); + } + }); + } + + @Override + public void onPrepareOptionsMenu(Menu menu) { + menu.clear(); + } +} diff --git a/src/com/android/gallery3d/filtershow/editors/EditorTruePortraitFusion.java b/src/com/android/gallery3d/filtershow/editors/EditorTruePortraitFusion.java new file mode 100644 index 000000000..cfeea1375 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/editors/EditorTruePortraitFusion.java @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.gallery3d.filtershow.editors; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import android.content.Context; +import android.content.DialogInterface; +import android.net.Uri; +import android.support.v4.app.FragmentManager; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.LinearLayout; + +import org.codeaurora.gallery.R; +import com.android.gallery3d.filtershow.FilterShowActivity; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; +import com.android.gallery3d.filtershow.filters.FilterTruePortraitFusionRepresentation; +import com.android.gallery3d.filtershow.imageshow.ImageTruePortraitFusion; +import com.android.gallery3d.filtershow.imageshow.MasterImage; +import com.android.gallery3d.filtershow.ui.DoNotShowAgainDialog; +import com.android.gallery3d.util.GalleryUtils; + +public class EditorTruePortraitFusion extends Editor { + public static final String TAG = "EditorTruePortraitFusion"; + public static final int ID = R.id.editorTruePortraitFusion; + + protected ImageTruePortraitFusion mImageFusion; + private Uri mUnderlayUri = Uri.EMPTY; + + public EditorTruePortraitFusion() { + super(ID); + } + + @Override + public boolean showsActionBar() { + return true; + } + + @Override + public boolean showsActionBarControls() { + return false; + } + + @Override + public void onPrepareOptionsMenu(Menu menu) { + menu.clear(); + } + + @Override + public void createEditor(Context context, FrameLayout frameLayout) { + super.createEditor(context, frameLayout); + if (mImageFusion == null) { + mImageFusion = new ImageTruePortraitFusion(context); + } + mView = mImageShow = mImageFusion; + mImageFusion.setEditor(this); + } + + @Override + public void setEditPanelUI(View editControl) { + ViewGroup controlContainer = (ViewGroup)editControl; + controlContainer.removeAllViews(); + + LayoutInflater inflater = (LayoutInflater) mContext.getSystemService + (Context.LAYOUT_INFLATER_SERVICE); + View controls = inflater.inflate(R.layout.filtershow_seekbar, controlContainer); + View seekbar = controls.findViewById(R.id.primarySeekBar); + seekbar.setVisibility(View.GONE); + View saveButton = controls.findViewById(R.id.slider_save); + if (saveButton != null) { + saveButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + FilterShowActivity activity = (FilterShowActivity) mContext; + finalApplyCalled(); + activity.leaveSeekBarPanel(); + } + }); + } + View cancelButton = controls.findViewById(R.id.slider_cancel); + if (cancelButton != null) { + cancelButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + FilterShowActivity activity = (FilterShowActivity) mContext; + activity.cancelCurrentFilter(); + activity.leaveSeekBarPanel(); + } + }); + } + } + + @Override + public void openUtilityPanel(final LinearLayout accessoryViewList) { + accessoryViewList.removeAllViews(); + + LayoutInflater inflater = (LayoutInflater) mContext.getSystemService + (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.filtershow_actionbar_trueportrait_fusion, accessoryViewList); + + View pickUnderlayBtn = accessoryViewList.findViewById(R.id.pick_underlay); + pickUnderlayBtn.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + MasterImage.getImage().getActivity().pickImage(FilterShowActivity.SELECT_FUSION_UNDERLAY); + } + }); + + View editMaskBtn = accessoryViewList.findViewById(R.id.editMask); + editMaskBtn.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + FilterRepresentation representation = new FilterRepresentation(""); + representation.setEditorId(EditorTruePortraitMask.ID); + ((FilterShowActivity)mContext).loadEditorPanel(representation); + } + }); + + // Look for previous underlay + String fusionUnderlay = GalleryUtils.getStringPref(mContext, + mContext.getString(R.string.pref_trueportrait_fusion_underlay_key), null); + Uri fusionUri = Uri.EMPTY; + + if(fusionUnderlay != null) { + fusionUri = Uri.parse(fusionUnderlay); + if(!uriExists(mContext, fusionUri)) + fusionUri = Uri.EMPTY; + } + + setUnderlayImageUri(fusionUri); + } + + @Override + public void resume() { + if(mUnderlayUri.equals(Uri.EMPTY)) { + // No underlay set. + boolean skipIntro = GalleryUtils.getBooleanPref(mContext, + mContext.getString(R.string.pref_trueportrait_fusion_intro_show_key), false); + if(!skipIntro) { + DoNotShowAgainDialog dialog = new DoNotShowAgainDialog( + R.string.fusion_pick_background, R.string.trueportrait_fusion_intro, + R.string.pref_trueportrait_fusion_intro_show_key) { + @Override + public void onDismiss(DialogInterface dialog) { + super.onDismiss(dialog); + MasterImage.getImage().getActivity().pickImage(FilterShowActivity.SELECT_FUSION_UNDERLAY); + } + }; + FragmentManager fm = ((FilterShowActivity)mContext).getSupportFragmentManager(); + dialog.setCancelable(false); + dialog.show(fm, "trueportrait_fusion_intro"); + } + } + } + + public void setUnderlayImageUri(Uri uri) { + mUnderlayUri = uri; + FilterRepresentation filter = getLocalRepresentation(); + if(filter instanceof FilterTruePortraitFusionRepresentation) { + mImageFusion.setUnderlay(uri); + commitLocalRepresentation(); + + // save fusion underlay uri + GalleryUtils.setStringPref(mContext, + mContext.getString(R.string.pref_trueportrait_fusion_underlay_key), + (uri != null)?uri.toString():null); + } + } + + @Override + public void reflectCurrentFilter() { + super.reflectCurrentFilter(); + FilterRepresentation rep = getLocalRepresentation(); + if (rep != null && rep instanceof FilterTruePortraitFusionRepresentation) { + FilterTruePortraitFusionRepresentation fusionRep = (FilterTruePortraitFusionRepresentation) rep; + mImageFusion.setRepresentation(fusionRep); + } + } + + private boolean uriExists(Context context, Uri uri) { + try { + InputStream stream = context.getContentResolver().openInputStream(uri); + stream.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + return false; + } catch (IOException e) { + e.printStackTrace(); + } + return true; + } +} diff --git a/src/com/android/gallery3d/filtershow/editors/EditorTruePortraitImageOnly.java b/src/com/android/gallery3d/filtershow/editors/EditorTruePortraitImageOnly.java new file mode 100644 index 000000000..781b2e417 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/editors/EditorTruePortraitImageOnly.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.gallery3d.filtershow.editors; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.FrameLayout; +import android.widget.LinearLayout; + +import org.codeaurora.gallery.R; +import com.android.gallery3d.filtershow.FilterShowActivity; +import com.android.gallery3d.filtershow.filters.FilterRepresentation; +import com.android.gallery3d.filtershow.imageshow.ImageShow; + +public class EditorTruePortraitImageOnly extends Editor { + private static final String LOGTAG = "EditorTruePortraitImageOnly"; + public static final int ID = R.id.editorTruePortraitImageOnly; + + public EditorTruePortraitImageOnly() { + super(ID); + } + + @Override + public void createEditor(Context context, FrameLayout frameLayout) { + super.createEditor(context, frameLayout); + mView = mImageShow = new ImageShow(context); + } + + @Override + public boolean showsSeekBar() { + return false; + } + + @Override + public boolean showsActionBarControls() { + return false; + } + + @Override + public void openUtilityPanel(LinearLayout accessoryViewList) { + accessoryViewList.removeAllViews(); + + LayoutInflater inflater = (LayoutInflater) mContext.getSystemService + (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.filtershow_actionbar_trueportrait, accessoryViewList); + + View editMask = accessoryViewList.findViewById(R.id.editMask); + editMask.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + FilterRepresentation representation = new FilterRepresentation(""); + representation.setEditorId(EditorTruePortraitMask.ID); + ((FilterShowActivity)mContext).loadEditorPanel(representation); + } + }); + } + + @Override + public void onPrepareOptionsMenu(Menu menu) { + menu.clear(); + } +} diff --git a/src/com/android/gallery3d/filtershow/editors/EditorTruePortraitMask.java b/src/com/android/gallery3d/filtershow/editors/EditorTruePortraitMask.java new file mode 100644 index 000000000..7c5842f7f --- /dev/null +++ b/src/com/android/gallery3d/filtershow/editors/EditorTruePortraitMask.java @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.gallery3d.filtershow.editors; + + +import android.content.Context; +import android.support.v4.app.FragmentManager; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.CompoundButton; +import android.widget.FrameLayout; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; +import android.widget.ToggleButton; + +import org.codeaurora.gallery.R; +import com.android.gallery3d.filtershow.FilterShowActivity; +import com.android.gallery3d.filtershow.imageshow.ImageTruePortraitMask; +import com.android.gallery3d.filtershow.imageshow.MasterImage; +import com.android.gallery3d.filtershow.ui.AlertMsgDialog; + +public class EditorTruePortraitMask extends Editor { + private static final String LOGTAG = "EditorTruePortraitMask"; + public static final int ID = R.id.editorTruePortraitMask; + + private AlertMsgDialog mHelpDialog; + private ImageTruePortraitMask mTruePortraitImage; + private ToggleButton mForeground; + private ToggleButton mBackground; + private ImageButton mExitButton; + private ImageButton mApplyButton; + private View mMaskUndoButton; + private View mBrushSizeButton; + private View[] mBrushSizes = new View[3]; + private int mBrushIndex = 0; + + private static final int[] BRUSH_SIZES = { + 10, 30, 50 + }; + + private OnClickListener mDoneClickListener = new OnClickListener() { + @Override + public void onClick(View view) { + if(view.getId() == R.id.done) { + finalApplyCalled(); + } + FilterShowActivity activity = (FilterShowActivity) mContext; + activity.backToMain(); + activity.setActionBar(); + } + }; + + public EditorTruePortraitMask() { + super(ID); + } + + @Override + public void createEditor(Context context, FrameLayout frameLayout) { + super.createEditor(context, frameLayout); + unpack(R.id.truePortraitMaskEditor, R.layout.filtershow_trueportrait_mask_editor); + mTruePortraitImage = (ImageTruePortraitMask)mImageShow; + mTruePortraitImage.setEditor(this); + } + + @Override + public boolean showsSeekBar() { + return false; + } + + @Override + public boolean showsActionBarControls() { + return false; + } + + @Override + public void openUtilityPanel(LinearLayout accessoryViewList) { + accessoryViewList.removeAllViews(); + + LayoutInflater inflater = (LayoutInflater) mContext.getSystemService + (Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.filtershow_actionbar_trueportrait_mask, accessoryViewList); + + mMaskUndoButton = accessoryViewList.findViewById(R.id.maskUndo); + mMaskUndoButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + mTruePortraitImage.undoLastEdit(); + } + }); + refreshUndoButton(); + + View maskHelp = accessoryViewList.findViewById(R.id.maskHelp); + maskHelp.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + if(mHelpDialog == null) + mHelpDialog = new AlertMsgDialog(R.string.help, R.string.trueportrait_edit_help); + + FragmentManager fm = ((FilterShowActivity)mContext).getSupportFragmentManager(); + mHelpDialog.show(fm, "tp_edit_help"); + } + }); + + mForeground = (ToggleButton)accessoryViewList.findViewById(R.id.maskForeground); + mForeground.setChecked(true); + mForeground.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if(mBackground.isChecked() == isChecked) + mBackground.toggle(); + mTruePortraitImage.invalidate(); + showToast(); + } + }); + + mBackground = (ToggleButton)accessoryViewList.findViewById(R.id.maskBackground); + mBackground.setChecked(false); + mBackground.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if(mForeground.isChecked() == isChecked) + mForeground.toggle(); + } + }); + } + + @Override + public void setUpEditorUI(View editControl, Button stateButton) { + mExitButton = (ImageButton) editControl.findViewById(R.id.cancel); + mApplyButton = (ImageButton) editControl.findViewById(R.id.done); + mExitButton.setOnClickListener(mDoneClickListener); + mApplyButton.setOnClickListener(mDoneClickListener); + + mBrushSizeButton = editControl.findViewById(R.id.brush_size); + mBrushSizeButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + cycleBrushSize(); + } + }); + + mBrushSizes[0] = editControl.findViewById(R.id.brush_size_sm); + mBrushSizes[0].setSelected(false); + mBrushSizes[1] = editControl.findViewById(R.id.brush_size_med); + mBrushSizes[1].setSelected(false); + mBrushSizes[2] = editControl.findViewById(R.id.brush_size_large); + mBrushSizes[2].setSelected(false); + + mBrushSizes[mBrushIndex].setSelected(true); + } + + @Override + public void onPrepareOptionsMenu(Menu menu) { + menu.clear(); + } + + @Override + public void finalApplyCalled() { + mTruePortraitImage.applyMaskUpdates(); + MasterImage.getImage().invalidateFiltersOnly(); + } + + private void showToast() { + Toast toast; + if(isBackgroundMode()) { + toast = Toast.makeText(mContext, R.string.trueportrait_edit_background_toast, Toast.LENGTH_SHORT); + } else { + toast = Toast.makeText(mContext, R.string.trueportrait_edit_foreground_toast, Toast.LENGTH_SHORT); + } + + toast.show(); + } + + public void refreshUndoButton() { + mMaskUndoButton.setEnabled(mTruePortraitImage.canUndoEdit()); + } + + public boolean isBackgroundMode() { + return mBackground.isChecked(); + } + + public int cycleBrushSize() { + mBrushSizes[mBrushIndex].setSelected(false); + mBrushIndex = (mBrushIndex+1)%BRUSH_SIZES.length; + mBrushSizes[mBrushIndex].setSelected(true); + return getBrushSize(); + } + + public int getBrushSize() { + return BRUSH_SIZES[mBrushIndex]; + } +} diff --git a/src/com/android/gallery3d/filtershow/editors/EditorVignette.java b/src/com/android/gallery3d/filtershow/editors/EditorVignette.java index 8b5fcb505..e3dd7ff67 100644 --- a/src/com/android/gallery3d/filtershow/editors/EditorVignette.java +++ b/src/com/android/gallery3d/filtershow/editors/EditorVignette.java @@ -187,12 +187,11 @@ public class EditorVignette extends ParametricEditor { } @Override - public void setUtilityPanelUI(View actionButton, View editControl) { + public void setEditPanelUI(View editControl) { if (useCompact(mContext)) { - super.setUtilityPanelUI(actionButton, editControl); + super.setEditPanelUI(editControl); return; } - mActionButton = actionButton; mEditControl = editControl; mEditTitle.setCompoundDrawables(null, null, null, null); LinearLayout group = (LinearLayout) editControl; diff --git a/src/com/android/gallery3d/filtershow/editors/ParametricEditor.java b/src/com/android/gallery3d/filtershow/editors/ParametricEditor.java index c13c9f822..3ee36134f 100644 --- a/src/com/android/gallery3d/filtershow/editors/ParametricEditor.java +++ b/src/com/android/gallery3d/filtershow/editors/ParametricEditor.java @@ -135,7 +135,8 @@ public class ParametricEditor extends Editor { public void reflectCurrentFilter() { super.reflectCurrentFilter(); if (getLocalRepresentation() != null - && getLocalRepresentation() instanceof FilterBasicRepresentation) { + && getLocalRepresentation() instanceof FilterBasicRepresentation + && mControl != null) { FilterBasicRepresentation interval = (FilterBasicRepresentation) getLocalRepresentation(); mControl.setPrameter(interval); } @@ -164,8 +165,7 @@ public class ParametricEditor extends Editor { } @Override - public void setUtilityPanelUI(View actionButton, View editControl) { - mActionButton = actionButton; + public void setEditPanelUI(View editControl) { mEditControl = editControl; FilterRepresentation rep = getLocalRepresentation(); Parameter param = getParameterToEdit(rep); diff --git a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java index e6ac0a6ef..b9c24fc27 100644 --- a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java +++ b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java @@ -21,6 +21,8 @@ import android.graphics.Color; import android.util.Log; import org.codeaurora.gallery.R; +import com.android.gallery3d.filtershow.editors.EditorTruePortraitBasic; +import com.android.gallery3d.filtershow.editors.EditorTruePortraitImageOnly; import com.android.gallery3d.filtershow.editors.ImageOnlyEditor; import com.android.gallery3d.filtershow.pipeline.ImagePreset; @@ -42,6 +44,7 @@ public abstract class BaseFiltersManager implements FiltersManagerInterface { protected ArrayList<FilterRepresentation> mTrueScanner = new ArrayList<FilterRepresentation>(); protected ArrayList<FilterRepresentation> mHazeBuster = new ArrayList<FilterRepresentation>(); protected ArrayList<FilterRepresentation> mSeeStraight = new ArrayList<FilterRepresentation>(); + protected ArrayList<FilterRepresentation> mTruePortrait = new ArrayList<FilterRepresentation>(); private static int mImageBorderSize = 4; // in percent protected void init() { @@ -152,6 +155,8 @@ public abstract class BaseFiltersManager implements FiltersManagerInterface { filters.add(ImageFilterDualCamera.class); filters.add(ImageFilterDualCamFusion.class); filters.add(ImageFilterDualCamSketch.class); + filters.add(ImageFilterTruePortrait.class); + filters.add(ImageFilterTruePortraitFusion.class); if(SimpleMakeupImageFilter.HAS_TS_MAKEUP) { filters.add(ImageFilterMakeupWhiten.class); @@ -185,6 +190,10 @@ public abstract class BaseFiltersManager implements FiltersManagerInterface { return mDualCam; } + public ArrayList<FilterRepresentation> getTruePortrait() { + return mTruePortrait; + } + public ArrayList<FilterRepresentation> getTools() { return mTools; } @@ -462,6 +471,74 @@ public abstract class BaseFiltersManager implements FiltersManagerInterface { mDualCam.add(getRepresentation(ImageFilterDualCamFusion.class)); } + public void addTruePortrait(Context context) { + int[] textId = { + R.string.none, + R.string.blur, + R.string.motion_blur, + R.string.halo, + R.string.sketch + }; + + int[] overlayId = { + R.drawable.ic_tp_normal, + R.drawable.ic_tp_bokeh, + R.drawable.ic_tp_motion_blur, + R.drawable.ic_tp_halo, + R.drawable.ic_tp_sketch + }; + + String[] serializationNames = { + "TRUE_PORTRAIT_NONE", + "TRUE_PORTRAIT_BLUR", + "TRUE_PORTRAIT_MOTION_BLUR", + "TRUE_PORTRAIT_HALO", + "TRUE_PORTRAIT_SKETCH" + }; + + int[][] minMaxValues = { + {0,0,0}, + {0,3,7}, + {0,3,7}, + {0,3,7}, + {0,0,0} + }; + + boolean[] showParams = { + false, + true, + true, + true, + false + }; + + int[] editorIDs = { + ImageOnlyEditor.ID, + EditorTruePortraitBasic.ID, + EditorTruePortraitBasic.ID, + EditorTruePortraitBasic.ID, + EditorTruePortraitImageOnly.ID + }; + + for (int i = 0; i < textId.length; i++) { + FilterRepresentation tPortrait = + new FilterBasicRepresentation(context.getString(textId[i]), + minMaxValues[i][0], minMaxValues[i][1], minMaxValues[i][2]); + tPortrait.setFilterClass(ImageFilterTruePortrait.class); + tPortrait.setFilterType(FilterRepresentation.TYPE_TRUEPORTRAIT); + tPortrait.setTextId(textId[i]); + tPortrait.setOverlayId(overlayId[i]); + tPortrait.setOverlayOnly(true); + tPortrait.setSerializationName(serializationNames[i]); + tPortrait.setShowParameterValue(showParams[i]); + tPortrait.setEditorId(editorIDs[i]); + mTruePortrait.add(tPortrait); + addRepresentation(tPortrait); + } + + mTruePortrait.add(getRepresentation(ImageFilterTruePortraitFusion.class)); + } + public void removeRepresentation(ArrayList<FilterRepresentation> list, FilterRepresentation representation) { for (int i = 0; i < list.size(); i++) { diff --git a/src/com/android/gallery3d/filtershow/filters/FilterDualCamFusionRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterDualCamFusionRepresentation.java index f8d8263d3..8caa9b2b8 100644 --- a/src/com/android/gallery3d/filtershow/filters/FilterDualCamFusionRepresentation.java +++ b/src/com/android/gallery3d/filtershow/filters/FilterDualCamFusionRepresentation.java @@ -40,7 +40,7 @@ import org.codeaurora.gallery.R; import com.android.gallery3d.filtershow.editors.EditorDualCamFusion; -public class FilterDualCamFusionRepresentation extends FilterRepresentation { +public class FilterDualCamFusionRepresentation extends FilterRepresentation implements FilterFusionRepresentation { private static final String LOGTAG = "FilterFusionRepresentation"; public static final String SERIALIZATION_NAME = "FUSION"; @@ -112,6 +112,7 @@ public class FilterDualCamFusionRepresentation extends FilterRepresentation { return mPoint; } + @Override public void setUnderlay(Uri uri) { if(uri != null) { mUri = uri.toString(); @@ -120,6 +121,7 @@ public class FilterDualCamFusionRepresentation extends FilterRepresentation { } } + @Override public void setUnderlay(String uri) { if(uri != null) mUri = uri; @@ -127,10 +129,12 @@ public class FilterDualCamFusionRepresentation extends FilterRepresentation { mUri = ""; } + @Override public boolean hasUnderlay() { return (mUri != null) && (mUri.isEmpty() == false); } + @Override public String getUnderlay() { return mUri; } diff --git a/src/com/android/gallery3d/filtershow/filters/FilterFusionRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterFusionRepresentation.java new file mode 100644 index 000000000..ff850c8bc --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/FilterFusionRepresentation.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.gallery3d.filtershow.filters; + +import android.net.Uri; + +public interface FilterFusionRepresentation { + public void setUnderlay(Uri uri); + public void setUnderlay(String uri); + public boolean hasUnderlay(); + public String getUnderlay(); +}
\ No newline at end of file diff --git a/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java index 8d1911315..bded1f506 100644 --- a/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java +++ b/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java @@ -50,6 +50,7 @@ public class FilterRepresentation { public static final byte TYPE_GEOMETRY = 7; public static final byte TYPE_MAKEUP = 8; public static final byte TYPE_DUALCAM = 9; + public static final byte TYPE_TRUEPORTRAIT = 10; protected static final String NAME_TAG = "Name"; public FilterRepresentation(String name) { diff --git a/src/com/android/gallery3d/filtershow/filters/FilterTruePortraitFusionRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterTruePortraitFusionRepresentation.java new file mode 100644 index 000000000..3864eacfd --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/FilterTruePortraitFusionRepresentation.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.gallery3d.filtershow.filters; + +import java.io.IOException; + +import android.net.Uri; +import android.util.JsonReader; +import android.util.JsonWriter; + +import org.codeaurora.gallery.R; +import com.android.gallery3d.filtershow.editors.EditorTruePortraitFusion; + + +public class FilterTruePortraitFusionRepresentation extends FilterRepresentation implements FilterFusionRepresentation { + private static final String LOGTAG = "FilterTruePortraitFusionRepresentation"; + public static final String SERIALIZATION_NAME = "TP_FUSION"; + private static final String SERIAL_UNDERLAY_IMAGE = "image"; + + private String mUri = ""; + + public FilterTruePortraitFusionRepresentation() { + super("Fusion"); + setSerializationName(SERIALIZATION_NAME); + setFilterType(FilterRepresentation.TYPE_TRUEPORTRAIT); + setFilterClass(ImageFilterTruePortraitFusion.class); + setEditorId(EditorTruePortraitFusion.ID); + setShowParameterValue(false); + setTextId(R.string.fusion); + setOverlayId(R.drawable.ic_tp_fusion); + setOverlayOnly(true); + } + + @Override + public FilterRepresentation copy() { + FilterTruePortraitFusionRepresentation representation = + new FilterTruePortraitFusionRepresentation(); + copyAllParameters(representation); + return representation; + } + + @Override + protected void copyAllParameters(FilterRepresentation representation) { + super.copyAllParameters(representation); + representation.useParametersFrom(this); + } + + public void useParametersFrom(FilterRepresentation a) { + super.useParametersFrom(a); + if (a instanceof FilterTruePortraitFusionRepresentation) { + FilterTruePortraitFusionRepresentation representation = (FilterTruePortraitFusionRepresentation) a; + setUnderlay(representation.getUnderlay()); + } + } + + @Override + public boolean equals(FilterRepresentation representation) { + if (!super.equals(representation)) { + return false; + } + if (representation instanceof FilterTruePortraitFusionRepresentation) { + FilterTruePortraitFusionRepresentation fusion = (FilterTruePortraitFusionRepresentation) representation; + if (fusion.mUri.equals(mUri)) { + return true; + } + } + return false; + } + + @Override + public void setUnderlay(Uri uri) { + if(uri != null) { + mUri = uri.toString(); + } else { + mUri = ""; + } + } + + @Override + public void setUnderlay(String uri) { + if(uri != null) + mUri = uri; + else + mUri = ""; + } + + @Override + public boolean hasUnderlay() { + return (mUri != null) && (mUri.isEmpty() == false); + } + + @Override + public String getUnderlay() { + return mUri; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("fusion - underlay: ").append(getUnderlay()); + + return sb.toString(); + } + + // Serialization... + public void serializeRepresentation(JsonWriter writer) throws IOException { + writer.beginObject(); + { + writer.name(NAME_TAG); + writer.value(getName()); + writer.name(SERIAL_UNDERLAY_IMAGE); + writer.value(mUri); + } + writer.endObject(); + } + + public void deSerializeRepresentation(JsonReader reader) throws IOException { + reader.beginObject(); + while (reader.hasNext()) { + String name = reader.nextName(); + if (name.equalsIgnoreCase(NAME_TAG)) { + setName(reader.nextString()); + } else if (name.equalsIgnoreCase(SERIAL_UNDERLAY_IMAGE)) { + setUnderlay(reader.nextString()); + } else { + reader.skipValue(); + } + } + reader.endObject(); + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterTruePortrait.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterTruePortrait.java new file mode 100644 index 000000000..86a2a7404 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterTruePortrait.java @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.gallery3d.filtershow.filters; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.Log; +import android.widget.Toast; + +import org.codeaurora.gallery.R; +import com.android.gallery3d.filtershow.cache.BitmapCache; +import com.android.gallery3d.filtershow.cache.ImageLoader; +import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils; +import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils.GeometryHolder; +import com.android.gallery3d.filtershow.imageshow.MasterImage; +import com.android.gallery3d.filtershow.pipeline.FilterEnvironment; +import com.android.gallery3d.filtershow.pipeline.ImagePreset; +import com.android.gallery3d.filtershow.tools.TruePortraitNativeEngine; +import com.android.gallery3d.filtershow.tools.TruePortraitNativeEngine.EffectType; + +public class ImageFilterTruePortrait extends ImageFilter { + private static final String TAG = "ImageFilterTruePortrait"; + private static Toast sSegmentToast; + + private FilterRepresentation mParameters; + private Paint mPaint = new Paint(); + + public void useRepresentation(FilterRepresentation representation) { + mParameters = representation; + } + + public FilterRepresentation getParameters() { + return mParameters; + } + + @Override + public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { + if (getParameters() == null) { + return bitmap; + } + + Bitmap filteredBitmap = null; + boolean result = false; + int orientation = MasterImage.getImage().getOrientation(); + Rect originalBounds = MasterImage.getImage().getOriginalBounds(); + int filteredW; + int filteredH; + + if(quality == FilterEnvironment.QUALITY_FINAL) { + filteredW = originalBounds.width(); + filteredH = originalBounds.height(); + } else { + Bitmap originalBmp = MasterImage.getImage().getOriginalBitmapHighres(); + filteredW = originalBmp.getWidth(); + filteredH = originalBmp.getHeight(); + + // image is rotated + if (orientation == ImageLoader.ORI_ROTATE_90 || + orientation == ImageLoader.ORI_ROTATE_270 || + orientation == ImageLoader.ORI_TRANSPOSE || + orientation == ImageLoader.ORI_TRANSVERSE) { + int tmp = filteredW; + filteredW = filteredH; + filteredH = tmp; + } + + // non even width or height + if(filteredW%2 != 0 || filteredH%2 != 0) { + float aspect = (float)filteredH / (float)filteredW; + if(filteredW >= filteredH) { + filteredW = MasterImage.MAX_BITMAP_DIM; + filteredH = (int)(filteredW * aspect); + } else { + filteredH = MasterImage.MAX_BITMAP_DIM; + filteredW = (int)(filteredH / aspect); + } + } + } + + filteredBitmap = MasterImage.getImage().getBitmapCache().getBitmap(filteredW, filteredH, BitmapCache.FILTERS); + + result = applyEffect(filteredBitmap); + + if(result == false) { + Log.e(TAG, "Imagelib API failed"); + sActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + if(sSegmentToast == null) { + sSegmentToast = Toast.makeText(sActivity, "No Faces Found", Toast.LENGTH_SHORT); + } + sSegmentToast.show(); + } + }); + + return bitmap; + } else { + + mPaint.reset(); + mPaint.setAntiAlias(true); + if(quality == FilterEnvironment.QUALITY_FINAL) { + mPaint.setFilterBitmap(true); + mPaint.setDither(true); + } + + if(needsClear()) { + bitmap.setHasAlpha(true); + bitmap.eraseColor(Color.TRANSPARENT); + } + + Canvas canvas = new Canvas(bitmap); + ImagePreset preset = getEnvironment().getImagePreset(); + int bmWidth = bitmap.getWidth(); + int bmHeight = bitmap.getHeight(); + GeometryHolder holder; + if(preset.getDoApplyGeometry()) { + holder = GeometryMathUtils.unpackGeometry(preset.getGeometryFilters()); + } else { + holder = new GeometryHolder(); + } + + RectF crop = new RectF(); + Matrix m = GeometryMathUtils.getOriginalToScreen(holder, crop, true, + filteredW, filteredH, bmWidth, bmHeight); + + canvas.save(); + canvas.clipRect(crop); + canvas.drawBitmap(filteredBitmap, m, mPaint); + canvas.restore(); + + MasterImage.getImage().getBitmapCache().cache(filteredBitmap); + } + + return bitmap; + } + + protected boolean applyEffect(Bitmap filteredBitmap) { + FilterBasicRepresentation basicRep = (FilterBasicRepresentation) getParameters(); + int value = basicRep.getValue(); + + boolean result = false; + switch(mParameters.getTextId()) { + case R.string.blur: + result = TruePortraitNativeEngine.getInstance().applyEffect(EffectType.BLUR, value, filteredBitmap); + break; + case R.string.motion_blur: + result = TruePortraitNativeEngine.getInstance().applyEffect(EffectType.MOTION_BLUR, value, filteredBitmap); + break; + case R.string.halo: + result = TruePortraitNativeEngine.getInstance().applyEffect(EffectType.HALO, value, filteredBitmap); + break; + case R.string.sketch: + result = TruePortraitNativeEngine.getInstance().applyEffect(EffectType.SKETCH, value, filteredBitmap); + break; + } + + return result; + } + + protected boolean needsClear() { + return false; + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterTruePortraitFusion.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterTruePortraitFusion.java new file mode 100644 index 000000000..59a15a325 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterTruePortraitFusion.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.gallery3d.filtershow.filters; + +import android.graphics.Bitmap; + +import com.android.gallery3d.filtershow.tools.TruePortraitNativeEngine; + +public class ImageFilterTruePortraitFusion extends ImageFilterTruePortrait { + private static final String TAG = "ImageFilterTruePortraitFusion"; + + public ImageFilterTruePortraitFusion() { + mName = "Fusion"; + } + + @Override + protected boolean applyEffect(Bitmap filteredBitmap) { + filteredBitmap.setHasAlpha(true); + return TruePortraitNativeEngine.getInstance().nativeGetForegroundImg(filteredBitmap); + } + + @Override + protected boolean needsClear() { + return true; + } + + @Override + public FilterRepresentation getDefaultRepresentation() { + return new FilterTruePortraitFusionRepresentation(); + } +} diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterTruePortraitMask.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterTruePortraitMask.java new file mode 100644 index 000000000..88601c4ff --- /dev/null +++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterTruePortraitMask.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.gallery3d.filtershow.filters; + + +public class ImageFilterTruePortraitMask extends ImageFilter { + private static final String TAG = "ImageFilterTruePortraitMask"; + + private FilterBasicRepresentation mParameters; + + public void useRepresentation(FilterRepresentation representation) { + FilterBasicRepresentation parameters = (FilterBasicRepresentation) representation; + mParameters = parameters; + } + + public FilterBasicRepresentation getParameters() { + return mParameters; + } +} diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java index c3a7b7f38..7926d6fa6 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java @@ -66,8 +66,8 @@ public class ImageShow extends View implements OnGestureListener, protected int mBackgroundColor; - private GestureDetector mGestureDetector = null; - private ScaleGestureDetector mScaleGestureDetector = null; + protected GestureDetector mGestureDetector = null; + protected ScaleGestureDetector mScaleGestureDetector = null; private RectF mFusionBounds = new RectF(); protected Rect mImageBounds = new Rect(); @@ -78,32 +78,32 @@ public class ImageShow extends View implements OnGestureListener, private int mShadowMargin = 15; // not scaled, fixed in the asset private boolean mShadowDrawn = false; - private Point mTouchDown = new Point(); - private Point mTouch = new Point(); - private boolean mFinishedScalingOperation = false; + protected Point mTouchDown = new Point(); + protected Point mTouch = new Point(); + protected boolean mFinishedScalingOperation = false; private boolean mZoomIn = false; Point mOriginalTranslation = new Point(); float mOriginalScale; float mStartFocusX, mStartFocusY; - private EdgeEffectCompat mEdgeEffect = null; - private static final int EDGE_LEFT = 1; - private static final int EDGE_TOP = 2; - private static final int EDGE_RIGHT = 3; - private static final int EDGE_BOTTOM = 4; - private int mCurrentEdgeEffect = 0; - private int mEdgeSize = 100; + protected EdgeEffectCompat mEdgeEffect = null; + protected static final int EDGE_LEFT = 1; + protected static final int EDGE_TOP = 2; + protected static final int EDGE_RIGHT = 3; + protected static final int EDGE_BOTTOM = 4; + protected int mCurrentEdgeEffect = 0; + protected int mEdgeSize = 100; - private static final int mAnimationSnapDelay = 200; - private static final int mAnimationZoomDelay = 400; - private ValueAnimator mAnimatorScale = null; - private ValueAnimator mAnimatorTranslateX = null; - private ValueAnimator mAnimatorTranslateY = null; + protected static final int mAnimationSnapDelay = 200; + protected static final int mAnimationZoomDelay = 400; + protected ValueAnimator mAnimatorScale = null; + protected ValueAnimator mAnimatorTranslateX = null; + protected ValueAnimator mAnimatorTranslateY = null; protected boolean mAllowScaleAndTranslate = false; - private enum InteractionMode { + protected enum InteractionMode { NONE, SCALE, MOVE @@ -298,16 +298,7 @@ public class ImageShow extends View implements OnGestureListener, mShadowDrawn = false; if (!drawCompareImage(canvas, MasterImage.getImage().getOriginalBitmapLarge())) { - Bitmap highresPreview = MasterImage.getImage().getHighresImage(); - Bitmap fullHighres = MasterImage.getImage().getPartialImage(); - - boolean isDoingNewLookAnimation = MasterImage.getImage().onGoingNewLookAnimation(); - - if (highresPreview == null || isDoingNewLookAnimation) { - drawImageAndAnimate(canvas, getFilteredImage()); - } else { - drawImageAndAnimate(canvas, highresPreview); - } + drawImageAndAnimate(canvas, getImageToDraw()); } canvas.restore(); @@ -337,6 +328,17 @@ public class ImageShow extends View implements OnGestureListener, } } + protected Bitmap getImageToDraw() { + Bitmap highresPreview = MasterImage.getImage().getHighresImage(); + boolean isDoingNewLookAnimation = MasterImage.getImage().onGoingNewLookAnimation(); + + if (highresPreview == null || isDoingNewLookAnimation) { + return getFilteredImage(); + } else { + return highresPreview; + } + } + private void drawHighresImage(Canvas canvas, Bitmap fullHighres) { Matrix originalToScreen = MasterImage.getImage().originalImageToScreen(); if (fullHighres != null && originalToScreen != null) { @@ -774,7 +776,7 @@ public class ImageShow extends View implements OnGestureListener, return true; } - private void constrainTranslation(Point translation, float scale) { + protected void constrainTranslation(Point translation, float scale) { int currentEdgeEffect = 0; if (mAllowScaleAndTranslate || scale <= 1) { mCurrentEdgeEffect = 0; diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageTruePortraitFusion.java b/src/com/android/gallery3d/filtershow/imageshow/ImageTruePortraitFusion.java new file mode 100644 index 000000000..8413c5dd9 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageTruePortraitFusion.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.gallery3d.filtershow.imageshow; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.net.Uri; +import android.util.AttributeSet; + +import com.android.gallery3d.filtershow.cache.ImageLoader; +import com.android.gallery3d.filtershow.editors.EditorTruePortraitFusion; +import com.android.gallery3d.filtershow.filters.FilterTruePortraitFusionRepresentation; + +public class ImageTruePortraitFusion extends ImageShow { + private static final String LOGTAG = "ImageTruePortraitFusion"; + protected EditorTruePortraitFusion mEditor; + protected FilterTruePortraitFusionRepresentation mRepresentation; + private Bitmap mUnderlay; + + public ImageTruePortraitFusion(Context context, AttributeSet attrs) { + super(context, attrs); + mAllowScaleAndTranslate = true; + } + + public ImageTruePortraitFusion(Context context) { + super(context); + mAllowScaleAndTranslate = true; + } + + public void setUnderlay(Uri uri) { + mRepresentation.setUnderlay(uri); + + if(mUnderlay != null) { + mUnderlay.recycle(); + mUnderlay = null; + MasterImage.getImage().setFusionUnderlay(null); + } + + mUnderlay = ImageLoader.loadConstrainedBitmap(uri, getContext(), MasterImage.MAX_BITMAP_DIM, new Rect(), false); + MasterImage.getImage().setFusionUnderlay(mUnderlay); + invalidate(); + } + + public void setEditor(EditorTruePortraitFusion editor) { + mEditor = editor; + } + + public void setRepresentation(FilterTruePortraitFusionRepresentation representation) { + mRepresentation = representation; + } + + @Override + public boolean enableComparison() { + return false; + } +} diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageTruePortraitMask.java b/src/com/android/gallery3d/filtershow/imageshow/ImageTruePortraitMask.java new file mode 100644 index 000000000..052035714 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageTruePortraitMask.java @@ -0,0 +1,675 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.filtershow.imageshow; + +import java.util.Arrays; +import java.util.Stack; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.LightingColorFilter; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Path; +import android.graphics.Point; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.ScaleGestureDetector; + +import com.android.gallery3d.filtershow.editors.EditorTruePortraitMask; +import com.android.gallery3d.filtershow.filters.FilterDrawRepresentation.StrokeData; +import com.android.gallery3d.filtershow.tools.TruePortraitNativeEngine; + +public class ImageTruePortraitMask extends ImageShow { + + private static final String LOGTAG = "ImageTruePortraitMask"; + + private boolean mZoomIn = false; + private Point mScaleTranslation = new Point(); + private float mScaleStartFocusX, mScaleStartFocusY; + + private Point mTranslation = new Point(); + private Point mOriginalTranslation = new Point(); + private float mScaleFactor = 1f; + private float mMaxScaleFactor = 3f; + + private Paint mMaskPaintForeground = new Paint(); + private Paint mMaskPaintBackground = new Paint(); + private Paint mMaskEditPaint = new Paint(); + + private StrokeData mCurrent; + private Stack<StrokeData> mDrawing = new Stack<StrokeData>(); + + private EditorTruePortraitMask mEditor; + private Bitmap mPreview; + private Bitmap mOriginalMask; + private Bitmap mMask; + + private Mode mMode = Mode.MODE_NONE; + private enum Mode { + MODE_NONE, + MODE_MOVE, + MODE_SCALE, + MODE_DRAW + } + + public ImageTruePortraitMask(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setup(); + } + + public ImageTruePortraitMask(Context context, AttributeSet attrs) { + super(context, attrs); + setup(); + } + + public ImageTruePortraitMask(Context context) { + super(context); + setup(); + } + + private void setup() { + mMaskPaintForeground.setAntiAlias(true); + mMaskPaintForeground.setColor(Color.RED); + mMaskPaintForeground.setColorFilter(new LightingColorFilter(Color.RED, 1)); + mMaskPaintForeground.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN)); + + mMaskPaintBackground.setAntiAlias(true); + mMaskPaintBackground.setColor(Color.GREEN); + mMaskPaintBackground.setColorFilter(new ColorMatrixColorFilter(new float[] { + // Set color to GREEN and inverse ALPHA channel + // of original mask to get background + 0,0,0,0,0, + 0,0,0,0,255, + 0,0,0,0,0, + 0,0,0,-1,255 + })); + mMaskPaintBackground.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN)); + + mMaskEditPaint.setAntiAlias(true); + mMaskEditPaint.setStyle(Style.STROKE); + mMaskEditPaint.setStrokeCap(Paint.Cap.ROUND); + } + + @Override + public void attach() { + mPreview = TruePortraitNativeEngine.getInstance().getPreview(); + mOriginalMask = TruePortraitNativeEngine.getInstance().getMask(); + mMask = Bitmap.createBitmap(mOriginalMask.getWidth(), mOriginalMask.getHeight(), mOriginalMask.getConfig()); + } + + @Override + public void detach() { + if(mPreview != null) { + mPreview.recycle(); + mPreview = null; + } + + if(mOriginalMask != null) { + mOriginalMask.recycle(); + mOriginalMask = null; + } + + if(mMask != null) { + mMask.recycle(); + mMask = null; + } + + mDrawing.clear(); + } + + public void setEditor(EditorTruePortraitMask editor) { + mEditor = editor; + } + + @Override + public void onDraw(Canvas canvas) { + toggleComparisonButtonVisibility(); + + mPaint.reset(); + mPaint.setAntiAlias(true); + mPaint.setFilterBitmap(true); + + drawImage(canvas, mPreview, mPaint); + updateEditsToMask(mMask); + drawImage(canvas, mMask, + mEditor.isBackgroundMode()?mMaskPaintBackground:mMaskPaintForeground); + + if (!mEdgeEffect.isFinished()) { + canvas.save(); + float dx = (getHeight() - getWidth()) / 2f; + if (getWidth() > getHeight()) { + dx = - (getWidth() - getHeight()) / 2f; + } + if (mCurrentEdgeEffect == EDGE_BOTTOM) { + canvas.rotate(180, getWidth()/2, getHeight()/2); + } else if (mCurrentEdgeEffect == EDGE_RIGHT) { + canvas.rotate(90, getWidth()/2, getHeight()/2); + canvas.translate(0, dx); + } else if (mCurrentEdgeEffect == EDGE_LEFT) { + canvas.rotate(270, getWidth()/2, getHeight()/2); + canvas.translate(0, dx); + } + if (mCurrentEdgeEffect != 0) { + mEdgeEffect.draw(canvas); + } + canvas.restore(); + invalidate(); + } else { + mCurrentEdgeEffect = 0; + } + } + + private void drawImage(Canvas canvas, Bitmap image, Paint paint) { + if (image == null) { + return; + } + + Matrix m = computeImageToScreen(image.getWidth(), image.getHeight()); + if (m == null) { + return; + } + + canvas.save(); + image.setHasAlpha(true); + canvas.drawBitmap(image, m, paint); + canvas.restore(); + } + + private void updateEditsToMask(Bitmap mask) { + // clear mask and reset with original + mask.eraseColor(Color.TRANSPARENT); + Canvas canvas = new Canvas(mask); + canvas.drawBitmap(mOriginalMask, 0, 0, null); + + for(StrokeData sd:mDrawing) { + drawEdit(canvas, sd); + } + + // update current edit + if(mCurrent != null) { + drawEdit(canvas, mCurrent); + } + } + + private void drawEdit(Canvas canvas, StrokeData sd) { + if (sd == null) { + return; + } + if (sd.mPath == null) { + return; + } + mMaskEditPaint.setColor(sd.mColor); + if(sd.mColor == Color.TRANSPARENT) + mMaskEditPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + else + mMaskEditPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); + mMaskEditPaint.setStrokeWidth(sd.mRadius); + + canvas.drawPath(sd.mPath, mMaskEditPaint); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + int action = event.getAction(); + action = action & MotionEvent.ACTION_MASK; + + boolean scaleInProgress = scaleInProgress(); + mGestureDetector.onTouchEvent(event); + mScaleGestureDetector.onTouchEvent(event); + + if (mMode == Mode.MODE_SCALE) { + return true; + } + if (!scaleInProgress() && scaleInProgress) { + // If we were scaling, the scale will stop but we will + // still issue an ACTION_UP. Let the subclasses know. + mFinishedScalingOperation = true; + } + + int ex = (int) event.getX(); + int ey = (int) event.getY(); + if (action == MotionEvent.ACTION_DOWN) { + mTouchDown.x = ex; + mTouchDown.y = ey; + if(event.getPointerCount() == 2) { + mMode = Mode.MODE_MOVE; + setOriginalTranslation(getTranslation()); + } else if (event.getPointerCount() == 1) { + mMode = Mode.MODE_DRAW; + Point mapPoint = mapPoint(ex, ey); + startNewSection(mapPoint.x, mapPoint.y); + } + } else if (action == MotionEvent.ACTION_MOVE) { + mTouch.x = ex; + mTouch.y = ey; + + float scaleFactor = getScaleFactor(); + if (scaleFactor > 1 && event.getPointerCount() == 2 + && mMode == Mode.MODE_MOVE) { + float translateX = (mTouch.x - mTouchDown.x) / scaleFactor; + float translateY = (mTouch.y - mTouchDown.y) / scaleFactor; + Point originalTranslation = getOriginalTranslation(); + Point translation = getTranslation(); + translation.x = (int) (originalTranslation.x + translateX); + translation.y = (int) (originalTranslation.y + translateY); + setTranslation(translation); + } else if(event.getPointerCount() == 1 + && mMode == Mode.MODE_DRAW) { + Point mapPoint = mapPoint(ex, ey); + addPoint(mapPoint.x, mapPoint.y); + } + } else if (action == MotionEvent.ACTION_UP + || action == MotionEvent.ACTION_CANCEL + || action == MotionEvent.ACTION_OUTSIDE) { + if(event.getPointerCount() == 1 + && mMode == Mode.MODE_DRAW) { + Point mapPoint = mapPoint(ex, ey); + endSection(mapPoint.x, mapPoint.y); + } + + mMode = Mode.MODE_NONE; + mTouchDown.x = 0; + mTouchDown.y = 0; + mTouch.x = 0; + mTouch.y = 0; + if (getScaleFactor() <= 1) { + setScaleFactor(1); + setTranslation(new Point(0,0)); + } + } + + float scaleFactor = getScaleFactor(); + Point translation = getTranslation(); + constrainTranslation(translation, scaleFactor); + setTranslation(translation); + + invalidate(); + return true; + } + + private void startAnimTranslation(int fromX, int toX, + int fromY, int toY, int delay) { + if (fromX == toX && fromY == toY) { + return; + } + if (mAnimatorTranslateX != null) { + mAnimatorTranslateX.cancel(); + } + if (mAnimatorTranslateY != null) { + mAnimatorTranslateY.cancel(); + } + mAnimatorTranslateX = ValueAnimator.ofInt(fromX, toX); + mAnimatorTranslateY = ValueAnimator.ofInt(fromY, toY); + mAnimatorTranslateX.setDuration(delay); + mAnimatorTranslateY.setDuration(delay); + mAnimatorTranslateX.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + Point translation = getTranslation(); + translation.x = (Integer) animation.getAnimatedValue(); + setTranslation(translation); + invalidate(); + } + }); + mAnimatorTranslateY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + Point translation = getTranslation(); + translation.y = (Integer) animation.getAnimatedValue(); + setTranslation(translation); + invalidate(); + } + }); + mAnimatorTranslateX.start(); + mAnimatorTranslateY.start(); + } + + private void applyTranslationConstraints() { + float scaleFactor = getScaleFactor(); + Point translation = getTranslation(); + int x = translation.x; + int y = translation.y; + constrainTranslation(translation, scaleFactor); + + if (x != translation.x || y != translation.y) { + startAnimTranslation(x, translation.x, + y, translation.y, + mAnimationSnapDelay); + } + } + + @Override + public boolean onDoubleTap(MotionEvent arg0) { + if(hasFusionApplied()) + return true; + + mZoomIn = !mZoomIn; + float scale = 1.0f; + final float x = arg0.getX(); + final float y = arg0.getY(); + if (mZoomIn) { + scale = getMaxScaleFactor(); + } + if (scale != getScaleFactor()) { + if (mAnimatorScale != null) { + mAnimatorScale.cancel(); + } + mAnimatorScale = ValueAnimator.ofFloat( + getScaleFactor(), + scale + ); + float translateX = (getWidth() / 2 - x); + float translateY = (getHeight() / 2 - y); + Point translation = getTranslation(); + int startTranslateX = translation.x; + int startTranslateY = translation.y; + if (scale != 1.0f) { + translation.x = (int) (mScaleTranslation.x + translateX); + translation.y = (int) (mScaleTranslation.y + translateY); + } else { + translation.x = 0; + translation.y = 0; + } + constrainTranslation(translation, scale); + + startAnimTranslation(startTranslateX, translation.x, + startTranslateY, translation.y, + mAnimationZoomDelay); + mAnimatorScale.setDuration(mAnimationZoomDelay); + mAnimatorScale.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + setScaleFactor((Float) animation.getAnimatedValue()); + invalidate(); + } + }); + mAnimatorScale.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + } + + @Override + public void onAnimationEnd(Animator animation) { + applyTranslationConstraints(); + invalidate(); + } + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + }); + mAnimatorScale.start(); + } + return true; + } + + protected void constrainTranslation(Point translation, float scale) { + int currentEdgeEffect = 0; + if (mAllowScaleAndTranslate || scale <= 1) { + mCurrentEdgeEffect = 0; + mEdgeEffect.finish(); + return; + } + + Rect originalBounds = MasterImage.getImage().getOriginalBounds(); + Matrix originalToScreen = computeImageToScreen(originalBounds.width(), originalBounds.height()); + RectF screenPos = new RectF(originalBounds); + originalToScreen.mapRect(screenPos); + + boolean rightConstraint = screenPos.right < getWidth(); + boolean leftConstraint = screenPos.left > 0; + boolean topConstraint = screenPos.top > 0; + boolean bottomConstraint = screenPos.bottom < getHeight(); + + if (screenPos.width() > getWidth()) { + if (rightConstraint && !leftConstraint) { + float tx = screenPos.right - translation.x * scale; + translation.x = (int) ((getWidth() - tx) / scale); + currentEdgeEffect = EDGE_RIGHT; + } else if (leftConstraint && !rightConstraint) { + float tx = screenPos.left - translation.x * scale; + translation.x = (int) ((-tx) / scale); + currentEdgeEffect = EDGE_LEFT; + } + } else { + float tx = screenPos.right - translation.x * scale; + float dx = (getWidth() - screenPos.width()) / 2f; + translation.x = (int) ((getWidth() - tx - dx) / scale); + } + + if (screenPos.height() > getHeight()) { + if (bottomConstraint && !topConstraint) { + float ty = screenPos.bottom - translation.y * scale; + translation.y = (int) ((getHeight() - ty) / scale); + currentEdgeEffect = EDGE_BOTTOM; + } else if (topConstraint && !bottomConstraint) { + float ty = screenPos.top - translation.y * scale; + translation.y = (int) ((-ty) / scale); + currentEdgeEffect = EDGE_TOP; + } + } else { + float ty = screenPos.bottom - translation.y * scale; + float dy = (getHeight()- screenPos.height()) / 2f; + translation.y = (int) ((getHeight() - ty - dy) / scale); + } + + if (mCurrentEdgeEffect != currentEdgeEffect) { + if (mCurrentEdgeEffect == 0 || currentEdgeEffect != 0) { + mCurrentEdgeEffect = currentEdgeEffect; + mEdgeEffect.finish(); + } + mEdgeEffect.setSize(getWidth(), mEdgeSize); + } + if (currentEdgeEffect != 0) { + mEdgeEffect.onPull(mEdgeSize); + } + } + + @Override + public boolean onScale(ScaleGestureDetector detector) { + float scaleFactor = getScaleFactor(); + + scaleFactor = scaleFactor * detector.getScaleFactor(); + if (scaleFactor > getMaxScaleFactor()) { + scaleFactor = getMaxScaleFactor(); + } + + if (scaleFactor < 1.0f) { + scaleFactor = 1.0f; + } + + setScaleFactor(scaleFactor); + float focusx = detector.getFocusX(); + float focusy = detector.getFocusY(); + float translateX = (focusx - mScaleStartFocusX) / scaleFactor; + float translateY = (focusy - mScaleStartFocusY) / scaleFactor; + Point translation = getTranslation(); + translation.x = (int) (mScaleTranslation.x + translateX); + translation.y = (int) (mScaleTranslation.y + translateY); + setTranslation(translation); + invalidate(); + return true; + } + + @Override + public boolean onScaleBegin(ScaleGestureDetector detector) { + Point pos = getTranslation(); + mScaleTranslation.x = pos.x; + mScaleTranslation.y = pos.y; + mScaleStartFocusX = detector.getFocusX(); + mScaleStartFocusY = detector.getFocusY(); + mMode = Mode.MODE_SCALE; + return true; + } + + @Override + public void onScaleEnd(ScaleGestureDetector detector) { + mMode = Mode.MODE_NONE; + if (getScaleFactor() < 1) { + setScaleFactor(1); + invalidate(); + } + } + + @Override + public boolean hasFusionApplied() { + return false; + } + + private Matrix computeImageToScreen(int bmWidth, int bmHeight) { + float scale = 1f; + float translateX = 0; + float translateY = 0; + + RectF size = new RectF(0, 0, + bmWidth, bmHeight); + float scaleW = getWidth() / size.width(); + float scaleH = getHeight() / size.height(); + scale = Math.min(scaleW, scaleH); + translateX = (getWidth() - (size.width() * scale)) / 2.0f; + translateY = (getHeight() - (size.height() * scale)) / 2.0f; + + Point translation = getTranslation(); + Matrix m = new Matrix(); + m.postScale(scale, scale); + m.postTranslate(translateX, translateY); + m.postScale(getScaleFactor(), getScaleFactor(), + getWidth() / 2.0f, + getHeight() / 2.0f); + m.postTranslate(translation.x * getScaleFactor(), + translation.y * getScaleFactor()); + return m; + } + + private Matrix computeScreenToImage(int bmWidth, int bmHeight) { + Matrix m = computeImageToScreen(bmWidth, bmHeight); + Matrix inverse = new Matrix(); + m.invert(inverse); + return inverse; + } + + private Point mapPoint(int x, int y) { + Matrix screenToImage = computeScreenToImage(mMask.getWidth(), mMask.getHeight()); + float[] tmpPoint = {x,y}; + screenToImage.mapPoints(tmpPoint); + + return new Point((int)tmpPoint[0], (int)tmpPoint[1]); + } + + private void setTranslation(Point translation) { + mTranslation.x = translation.x; + mTranslation.y = translation.y; + } + + private void setOriginalTranslation(Point translation) { + mOriginalTranslation.x = translation.x; + mOriginalTranslation.y = translation.y; + } + + private void setScaleFactor(float scale) { + mScaleFactor = scale; + } + + private Point getTranslation() { + return mTranslation; + } + + private Point getOriginalTranslation() { + return mOriginalTranslation; + } + + private float getScaleFactor() { + return mScaleFactor; + } + + private float getMaxScaleFactor() { + return mMaxScaleFactor; + } + + private void fillStrokeParameters(StrokeData sd){ + sd.mColor = mEditor.isBackgroundMode()?Color.TRANSPARENT:Color.BLACK; + sd.mRadius = mEditor.getBrushSize(); + } + + private void startNewSection(float x, float y) { + mCurrent = new StrokeData(); + fillStrokeParameters(mCurrent); + mCurrent.mPath = new Path(); + mCurrent.mPath.moveTo(x, y); + mCurrent.mPoints[0] = x; + mCurrent.mPoints[1] = y; + mCurrent.noPoints = 1; + } + + private void addPoint(float x, float y) { + int len = mCurrent.noPoints * 2; + mCurrent.mPath.lineTo(x, y); + if ((len+2) > mCurrent.mPoints.length) { + mCurrent.mPoints = Arrays.copyOf(mCurrent.mPoints, mCurrent.mPoints.length * 2); + } + mCurrent.mPoints[len] = x; + mCurrent.mPoints[len + 1] = y; + mCurrent.noPoints++; + } + + private void endSection(float x, float y) { + addPoint(x, y); + mDrawing.push(mCurrent); + mCurrent = null; + mEditor.refreshUndoButton(); + } + + private void resetDrawing() { + mCurrent = null; + } + + public boolean canUndoEdit() { + return !mDrawing.isEmpty(); + } + + public void undoLastEdit() { + if(!mDrawing.isEmpty()) { + mDrawing.pop(); + mEditor.refreshUndoButton(); + invalidate(); + } + } + + public void applyMaskUpdates() { + TruePortraitNativeEngine.getInstance().updateMask(mDrawing); + } +} diff --git a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java index 09bf27bd9..0a13da737 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java +++ b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java @@ -40,6 +40,7 @@ import com.android.gallery3d.filtershow.filters.FilterDualCamFusionRepresentatio import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation; import com.android.gallery3d.filtershow.filters.FilterRepresentation; import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation; +import com.android.gallery3d.filtershow.filters.FilterTruePortraitFusionRepresentation; import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation; import com.android.gallery3d.filtershow.filters.ImageFilter; import com.android.gallery3d.filtershow.history.HistoryItem; @@ -442,9 +443,12 @@ public class MasterImage implements RenderingRequestCaller { } public synchronized boolean hasFusionApplied() { - FilterRepresentation representation = + FilterRepresentation dcRepresentation = mPreset.getFilterWithSerializationName(FilterDualCamFusionRepresentation.SERIALIZATION_NAME); - if(representation instanceof FilterDualCamFusionRepresentation) { + FilterRepresentation tpRepresentation = + mPreset.getFilterWithSerializationName(FilterTruePortraitFusionRepresentation.SERIALIZATION_NAME); + if(dcRepresentation instanceof FilterDualCamFusionRepresentation || + tpRepresentation instanceof FilterTruePortraitFusionRepresentation) { return true; } return false; diff --git a/src/com/android/gallery3d/filtershow/pipeline/Buffer.java b/src/com/android/gallery3d/filtershow/pipeline/Buffer.java index 0cdbed76d..c3120d72b 100644 --- a/src/com/android/gallery3d/filtershow/pipeline/Buffer.java +++ b/src/com/android/gallery3d/filtershow/pipeline/Buffer.java @@ -18,9 +18,10 @@ package com.android.gallery3d.filtershow.pipeline; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.PorterDuff; +import android.graphics.Color; import android.renderscript.Allocation; import android.renderscript.RenderScript; + import com.android.gallery3d.filtershow.cache.BitmapCache; import com.android.gallery3d.filtershow.imageshow.MasterImage; @@ -57,8 +58,8 @@ public class Buffer { } public synchronized void useBitmap(Bitmap bitmap) { + mBitmap.eraseColor(Color.TRANSPARENT); Canvas canvas = new Canvas(mBitmap); - canvas.drawColor(0, PorterDuff.Mode.CLEAR); canvas.drawBitmap(bitmap, 0, 0, null); } diff --git a/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java b/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java index 1640dac66..445407df9 100644 --- a/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java +++ b/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java @@ -42,6 +42,7 @@ import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation; import com.android.gallery3d.filtershow.filters.FilterRepresentation; import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation; import com.android.gallery3d.filtershow.filters.FilterStraightenRepresentation; +import com.android.gallery3d.filtershow.filters.FilterTruePortraitFusionRepresentation; import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation; import com.android.gallery3d.filtershow.filters.FiltersManager; import com.android.gallery3d.filtershow.filters.ImageFilter; @@ -333,15 +334,19 @@ public class ImagePreset { public void removeFilter(FilterRepresentation filterRepresentation) { if (filterRepresentation.getFilterType() == FilterRepresentation.TYPE_BORDER - || filterRepresentation.getFilterType() == FilterRepresentation.TYPE_DUALCAM) { + || filterRepresentation.getFilterType() == FilterRepresentation.TYPE_DUALCAM + || filterRepresentation.getFilterType() == FilterRepresentation.TYPE_TRUEPORTRAIT) { for (int i = 0; i < mFilters.size(); i++) { FilterRepresentation filter = mFilters.elementAt(i); if (filter.getFilterType() == filterRepresentation.getFilterType()) { mFilters.remove(i); // reset fusion underlay image. - if(filter instanceof FilterDualCamFusionRepresentation) { + if(filter instanceof FilterDualCamFusionRepresentation || + filter instanceof FilterTruePortraitFusionRepresentation) { MasterImage.getImage().setFusionUnderlay(null); + MasterImage.getImage().setScaleFactor(1); + MasterImage.getImage().resetTranslation(); } break; } @@ -399,6 +404,11 @@ public class ImagePreset { if (!isNoneDualCamFilter(representation)) { mFilters.add(representation); } + } else if (representation.getFilterType() == FilterRepresentation.TYPE_TRUEPORTRAIT) { + removeFilter(representation); + if (!isNoneTruePortraitFilter(representation)) { + mFilters.add(representation); + } } else if (representation.getFilterType() == FilterRepresentation.TYPE_FX) { boolean replaced = false; for (int i = 0; i < mFilters.size(); i++) { @@ -422,6 +432,7 @@ public class ImagePreset { // Enforces Filter type ordering for borders and dual cam FilterRepresentation border = null; FilterRepresentation dualcam = null; + FilterRepresentation trueportrait = null; for (int i = 0; i < mFilters.size();) { FilterRepresentation rep = mFilters.elementAt(i); if (rep.getFilterType() == FilterRepresentation.TYPE_BORDER) { @@ -432,6 +443,10 @@ public class ImagePreset { dualcam = rep; mFilters.remove(i); continue; + } else if (rep.getFilterType() == FilterRepresentation.TYPE_TRUEPORTRAIT) { + trueportrait = rep; + mFilters.remove(i); + continue; } i++; } @@ -448,6 +463,16 @@ public class ImagePreset { } mFilters.add(i, dualcam); } + if (trueportrait != null) { + int i = 0; + for (; i < mFilters.size(); i++) { + FilterRepresentation rep = mFilters.elementAt(i); + if (rep.getFilterType() != FilterRepresentation.TYPE_GEOMETRY) { + break; + } + } + mFilters.add(i, trueportrait); + } } private boolean isNoneBorderFilter(FilterRepresentation representation) { @@ -465,6 +490,11 @@ public class ImagePreset { ((FilterDualCamSketchRepresentation) representation).getTextId() == R.string.none; } + private boolean isNoneTruePortraitFilter(FilterRepresentation representation) { + return representation.getTextId() == R.string.none && + representation.getFilterType() == FilterRepresentation.TYPE_TRUEPORTRAIT; + } + public FilterRepresentation getRepresentation(FilterRepresentation filterRepresentation) { for (int i = 0; i < mFilters.size(); i++) { FilterRepresentation representation = mFilters.elementAt(i); @@ -478,6 +508,7 @@ public class ImagePreset { public Bitmap apply(Bitmap original, FilterEnvironment environment) { Bitmap bitmap = original; bitmap = applyDualCamera(bitmap, environment); + bitmap = applyTruePortrait(bitmap, environment); bitmap = applyFilters(bitmap, -1, -1, environment); return applyBorder(bitmap, environment); } @@ -540,6 +571,25 @@ public class ImagePreset { return bitmap; } + public Bitmap applyTruePortrait(Bitmap bitmap, FilterEnvironment environment) { + // Apply trueportrait filters + // Returns a new bitmap. + for (FilterRepresentation representation:mFilters) { + if (representation.getFilterType() == FilterRepresentation.TYPE_TRUEPORTRAIT) { + Bitmap tmp = bitmap; + bitmap = environment.applyRepresentation(representation, bitmap); + if (tmp != bitmap) { + environment.cache(tmp); + } + if (environment.needsStop()) { + return bitmap; + } + } + } + + return bitmap; + } + public Bitmap applyBorder(Bitmap bitmap, FilterEnvironment environment) { // get the border from the list of filters. FilterRepresentation border = getFilterRepresentationForType( @@ -580,6 +630,10 @@ public class ImagePreset { // skip the dual cam as it's already applied. continue; } + if (representation.getFilterType() == FilterRepresentation.TYPE_TRUEPORTRAIT) { + // skip the true portrait as it's already applied. + continue; + } Bitmap tmp = bitmap; bitmap = environment.applyRepresentation(representation, bitmap); if (tmp != bitmap) { diff --git a/src/com/android/gallery3d/filtershow/pipeline/ProcessingService.java b/src/com/android/gallery3d/filtershow/pipeline/ProcessingService.java index b5e9609e2..d42834457 100644 --- a/src/com/android/gallery3d/filtershow/pipeline/ProcessingService.java +++ b/src/com/android/gallery3d/filtershow/pipeline/ProcessingService.java @@ -307,6 +307,7 @@ public class ProcessingService extends Service { filtersManager.addTrueScanner(); filtersManager.addHazeBuster(); filtersManager.addSeeStraight(); + filtersManager.addTruePortrait(this); FiltersManager highresFiltersManager = FiltersManager.getHighresManager(); highresFiltersManager.addLooks(this); @@ -318,6 +319,7 @@ public class ProcessingService extends Service { highresFiltersManager.addTrueScanner(); highresFiltersManager.addHazeBuster(); highresFiltersManager.addSeeStraight(); + highresFiltersManager.addTruePortrait(this); } private void tearDownPipeline() { diff --git a/src/com/android/gallery3d/filtershow/tools/SaveImage.java b/src/com/android/gallery3d/filtershow/tools/SaveImage.java index 0f615da25..87492a335 100644 --- a/src/com/android/gallery3d/filtershow/tools/SaveImage.java +++ b/src/com/android/gallery3d/filtershow/tools/SaveImage.java @@ -50,7 +50,9 @@ import com.android.gallery3d.exif.ExifInterface; import com.android.gallery3d.filtershow.FilterShowActivity; import com.android.gallery3d.filtershow.cache.ImageLoader; import com.android.gallery3d.filtershow.filters.FilterDualCamFusionRepresentation; +import com.android.gallery3d.filtershow.filters.FilterFusionRepresentation; import com.android.gallery3d.filtershow.filters.FilterRepresentation; +import com.android.gallery3d.filtershow.filters.FilterTruePortraitFusionRepresentation; import com.android.gallery3d.filtershow.filters.FiltersManager; import com.android.gallery3d.filtershow.imageshow.MasterImage; import com.android.gallery3d.filtershow.pipeline.CachingPipeline; @@ -361,35 +363,11 @@ public class SaveImage { if (mPreviewImage != null) { Bitmap previewBmp; // Check for Fusion - FilterDualCamFusionRepresentation fusionRep = - (FilterDualCamFusionRepresentation) preset.getFilterWithSerializationName( - FilterDualCamFusionRepresentation.SERIALIZATION_NAME); + FilterFusionRepresentation fusionRep = findFusionRepresentation(preset); boolean hasFusion = (fusionRep != null && fusionRep.hasUnderlay()); if(hasFusion) { - // fusion. decode underlay image and get dest rect - Uri underLayUri = Uri.parse(fusionRep.getUnderlay()); - Bitmap underlay = ImageLoader.loadConstrainedBitmap(underLayUri, mContext, - Math.max(mPreviewImage.getWidth(), mPreviewImage.getHeight()), null, false); - RectF destRect = new RectF(); - Rect imageBounds = MasterImage.getImage().getImageBounds(); - Rect underlayBounds = MasterImage.getImage().getFusionBounds(); - float underlayScaleFactor = (float)underlay.getWidth() - / (float)underlayBounds.width(); - - destRect.left = (imageBounds.left - underlayBounds.left) * underlayScaleFactor; - destRect.right = (imageBounds.right - underlayBounds.left) * underlayScaleFactor; - destRect.top = (imageBounds.top - underlayBounds.top) * underlayScaleFactor; - destRect.bottom = (imageBounds.bottom - underlayBounds.top) * underlayScaleFactor; - - Canvas canvas = new Canvas(underlay); - Paint paint = new Paint(); - paint.reset(); - paint.setAntiAlias(true); - paint.setFilterBitmap(true); - paint.setDither(true); - - canvas.drawBitmap(mPreviewImage, null, destRect, paint); - previewBmp = underlay; + previewBmp = flattenFusion(Uri.parse(fusionRep.getUnderlay()), mPreviewImage, + Math.max(mPreviewImage.getWidth(), mPreviewImage.getHeight()), 0); } else { previewBmp = mPreviewImage; } @@ -464,31 +442,11 @@ public class SaveImage { bitmap = pipeline.renderFinalImage(bitmap, preset); // Check for Fusion - FilterDualCamFusionRepresentation fusionRep = (FilterDualCamFusionRepresentation) preset.getFilterWithSerializationName( - FilterDualCamFusionRepresentation.SERIALIZATION_NAME); - - if(fusionRep != null && fusionRep.hasUnderlay()) { - // fusion. decode underlay image and get dest rect - Uri underLayUri = Uri.parse(fusionRep.getUnderlay()); - Bitmap underlay = ImageLoader.loadBitmapWithBackouts(mContext, underLayUri, sampleSize); - RectF destRect = new RectF(); - Rect imageBounds = MasterImage.getImage().getImageBounds(); - Rect underlayBounds = MasterImage.getImage().getFusionBounds(); - float underlayScaleFactor = (float)underlay.getWidth() / (float)underlayBounds.width(); - - destRect.left = (imageBounds.left - underlayBounds.left) * underlayScaleFactor; - destRect.right = (imageBounds.right - underlayBounds.left) * underlayScaleFactor; - destRect.top = (imageBounds.top - underlayBounds.top) * underlayScaleFactor; - destRect.bottom = (imageBounds.bottom - underlayBounds.top) * underlayScaleFactor; - - Canvas canvas = new Canvas(underlay); - Paint paint = new Paint(); - paint.reset(); - paint.setAntiAlias(true); - paint.setFilterBitmap(true); - paint.setDither(true); - - canvas.drawBitmap(bitmap, null, destRect, paint); + FilterFusionRepresentation fusionRep = findFusionRepresentation(preset); + boolean hasFusion = (fusionRep != null && fusionRep.hasUnderlay()); + if(hasFusion) { + Bitmap underlay = flattenFusion( + Uri.parse(fusionRep.getUnderlay()), bitmap, 0, sampleSize); bitmap.recycle(); bitmap = underlay; @@ -832,4 +790,56 @@ public class SaveImage { return false; } + private FilterFusionRepresentation findFusionRepresentation(ImagePreset preset) { + FilterDualCamFusionRepresentation dcRepresentation = + (FilterDualCamFusionRepresentation)preset.getFilterWithSerializationName( + FilterDualCamFusionRepresentation.SERIALIZATION_NAME); + FilterTruePortraitFusionRepresentation tpRepresentation = + (FilterTruePortraitFusionRepresentation)preset.getFilterWithSerializationName( + FilterTruePortraitFusionRepresentation.SERIALIZATION_NAME); + + FilterFusionRepresentation fusionRep = null; + + if(dcRepresentation != null) + fusionRep = (FilterFusionRepresentation) dcRepresentation; + else if (tpRepresentation != null) + fusionRep = (FilterFusionRepresentation) tpRepresentation; + + return fusionRep; + } + + private Bitmap flattenFusion(Uri underlayUri, Bitmap bitmap, int sizeConstraint, int sampleSize) { + // fusion. decode underlay image and get dest rect + + Bitmap underlay = null; + + if(sizeConstraint != 0) { + underlay = ImageLoader.loadConstrainedBitmap(underlayUri, mContext, + sizeConstraint, null, false); + } else if (sampleSize != 0) { + underlay = ImageLoader.loadBitmapWithBackouts(mContext, underlayUri, sampleSize); + } + + RectF destRect = new RectF(); + Rect imageBounds = MasterImage.getImage().getImageBounds(); + Rect underlayBounds = MasterImage.getImage().getFusionBounds(); + float underlayScaleFactor = (float)underlay.getWidth() + / (float)underlayBounds.width(); + + destRect.left = (imageBounds.left - underlayBounds.left) * underlayScaleFactor; + destRect.right = (imageBounds.right - underlayBounds.left) * underlayScaleFactor; + destRect.top = (imageBounds.top - underlayBounds.top) * underlayScaleFactor; + destRect.bottom = (imageBounds.bottom - underlayBounds.top) * underlayScaleFactor; + + Canvas canvas = new Canvas(underlay); + Paint paint = new Paint(); + paint.reset(); + paint.setAntiAlias(true); + paint.setFilterBitmap(true); + paint.setDither(true); + + canvas.drawBitmap(bitmap, null, destRect, paint); + + return underlay; + } } diff --git a/src/com/android/gallery3d/filtershow/tools/TruePortraitNativeEngine.java b/src/com/android/gallery3d/filtershow/tools/TruePortraitNativeEngine.java new file mode 100644 index 000000000..03972638c --- /dev/null +++ b/src/com/android/gallery3d/filtershow/tools/TruePortraitNativeEngine.java @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.gallery3d.filtershow.tools; + +import java.util.Stack; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Point; +import android.graphics.Rect; +import android.support.v4.app.FragmentManager; +import android.util.Log; + +import org.codeaurora.gallery.R; +import com.android.gallery3d.filtershow.filters.FilterDrawRepresentation.StrokeData; +import com.android.gallery3d.filtershow.ui.AlertMsgDialog; + +public class TruePortraitNativeEngine { + private static final String TAG = "TruePortraitNativeEngine"; + static { + try { + System.loadLibrary("jni_trueportrait"); + mLibLoaded = true; + Log.v(TAG, "successfully loaded true portrait lib"); + } catch (UnsatisfiedLinkError e) { + Log.e(TAG, "failed to load true portrait lib"); + mLibLoaded = false; + } + } + + public static enum EffectType { + BLUR, + MOTION_BLUR, + HALO, + SKETCH + }; + + private static final int MASK_UPDATE_FOREGROUND = 0x7F000000; + private static final int MASK_UPDATE_BACKGROUND = 0x81000000; + + private static boolean mLibLoaded; + private static TruePortraitNativeEngine mInstance; + + private Bitmap mSketchBm; + private boolean mFacesDetected; + private Point mPreviewSize = new Point(); + private Bitmap mUpdateBm; + + private TruePortraitNativeEngine() {} + + public static void createInstance() { + if(mInstance == null) { + mInstance = new TruePortraitNativeEngine(); + } + } + + public static TruePortraitNativeEngine getInstance() { + createInstance(); + return mInstance; + } + + public boolean isLibLoaded() { + return mLibLoaded; + } + + public boolean init(Context context, Bitmap src, Rect[] faces) { + setFacesDetected(true); + if(nativeInit(src, faces.length, faces)) { + nativeGetPreviewSize(mPreviewSize); + mSketchBm = BitmapFactory.decodeResource(context.getResources(), R.raw.sketch_bm); + return true; + } else { + mSketchBm = null; + return false; + } + } + + public void release() { + if(mUpdateBm != null) { + mUpdateBm.recycle(); + mUpdateBm = null; + } + + if(mSketchBm != null) { + mSketchBm.recycle(); + mSketchBm = null; + } + + nativeRelease(); + } + + public void setFacesDetected(boolean detected) { + mFacesDetected = detected; + } + + public boolean facesDetected() { + return mFacesDetected; + } + + public void showNoFaceDetectedDialog(FragmentManager fragmentManager) { + AlertMsgDialog dialog = new AlertMsgDialog(R.string.trueportrait, R.string.trueportrait_no_face); + dialog.show(fragmentManager, "tp_no_face"); + } + + public boolean applyEffect(EffectType type, int intensity, Bitmap outBm) { + boolean result = false; + switch(type) { + case BLUR: + result = nativeApplyBlur(intensity, outBm); + break; + case MOTION_BLUR: + result = nativeApplyMotionBlur(intensity, outBm); + break; + case HALO: + result = nativeApplyHalo(intensity, outBm); + break; + case SKETCH: + result = nativeApplySketch(outBm, mSketchBm); + break; + } + + return result; + } + + public Bitmap getMask() { + Bitmap mask = Bitmap.createBitmap(mPreviewSize.x, mPreviewSize.y, Config.ALPHA_8); + if(mask != null) { + nativeGetPreviewMask(mask); + } + + return mask; + } + + public Bitmap getPreview() { + Bitmap previewBm = Bitmap.createBitmap(mPreviewSize.x, mPreviewSize.y, Config.ARGB_8888); + if(previewBm != null) { + nativeGetPreviewImage(previewBm); + } + + return previewBm; + } + + public boolean updateMask(Stack<StrokeData> edits) { + if(mUpdateBm == null) { + mUpdateBm = Bitmap.createBitmap(mPreviewSize.x, mPreviewSize.y, Config.ALPHA_8); + } + + mUpdateBm.eraseColor(Color.TRANSPARENT); + Canvas canvas = new Canvas(mUpdateBm); + for(StrokeData sd:edits) { + drawEdit(canvas, sd); + } + + // DEBUG + for(int i = 0; i<mPreviewSize.y; i++) { + StringBuilder sb = new StringBuilder(); + sb.append("[ "); + for(int j = 0; j<mPreviewSize.x; j++) { + int pixel = mUpdateBm.getPixel(j, i); + sb.append(Color.alpha(pixel)); + sb.append(" "); + } + sb.append("]"); + Log.e(TAG, sb.toString()); + } + // + return nativeUpdatePreviewMask(mUpdateBm); + } + + private void drawEdit(Canvas canvas, StrokeData sd) { + if (sd == null) { + return; + } + if (sd.mPath == null) { + return; + } + // USER_INPUT_MASK_UNCHANGED_VALUE = 0 + // USER_INPUT_MASK_BACKGROUND_VALUE = 129 + // USER_INPUT_MASK_FOREGROUND_VALUE = 127 + Paint paint = new Paint(); + paint.setAntiAlias(false); + paint.setStyle(Style.STROKE); + paint.setStrokeCap(Paint.Cap.ROUND); + if(sd.mColor == Color.TRANSPARENT) + paint.setColor(MASK_UPDATE_BACKGROUND); + else + paint.setColor(MASK_UPDATE_FOREGROUND); + paint.setStrokeWidth(sd.mRadius); + + canvas.drawPath(sd.mPath, paint); + } + + native public boolean nativeInit(Bitmap srcRGBA, int numFaces, Rect[] faces); + + native public void nativeRelease(); + + native public boolean nativeGetPreviewSize(Point point); + + native public boolean nativeGetPreviewImage(Bitmap outBm); + + native public boolean nativeGetPreviewMask(Bitmap outBm); + + native public boolean nativeUpdatePreviewMask(Bitmap mask); + + native public boolean nativeApplyBlur(int intensity, Bitmap outBm); + + native public boolean nativeApplyMotionBlur(int intensity, Bitmap outBm); + + native public boolean nativeApplyHalo(int intensity, Bitmap outBm); + + native public boolean nativeApplySketch(Bitmap outBm, Bitmap sketchBm); + + native public boolean nativeGetForegroundImg(Bitmap outBm); +} diff --git a/src/com/android/gallery3d/filtershow/ui/AlertMsgDialog.java b/src/com/android/gallery3d/filtershow/ui/AlertMsgDialog.java new file mode 100644 index 000000000..2041eb2ce --- /dev/null +++ b/src/com/android/gallery3d/filtershow/ui/AlertMsgDialog.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.gallery3d.filtershow.ui; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; + +import org.codeaurora.gallery.R; + +public class AlertMsgDialog extends DialogFragment { + private int mTitleId; + private int mMessageId; + + public AlertMsgDialog(int titleId, int msgId) { + mTitleId = titleId; + mMessageId = msgId; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder ab = new AlertDialog.Builder(getActivity()); + ab.setTitle(mTitleId); + ab.setMessage(mMessageId); + ab.setCancelable(false); + ab.setNeutralButton(R.string.ok, null); + return ab.create(); + } +} diff --git a/src/com/android/gallery3d/filtershow/ui/DoNotShowAgainDialog.java b/src/com/android/gallery3d/filtershow/ui/DoNotShowAgainDialog.java new file mode 100644 index 000000000..aad403bd1 --- /dev/null +++ b/src/com/android/gallery3d/filtershow/ui/DoNotShowAgainDialog.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.gallery3d.filtershow.ui; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CheckBox; +import android.widget.TextView; + +import org.codeaurora.gallery.R; +import com.android.gallery3d.util.GalleryUtils; + +public class DoNotShowAgainDialog extends DialogFragment { + private int mSharedPrefKeyId; + private int mTitleId; + private int mMessageId; + private CheckBox mDoNotShowAgainChk; + + public DoNotShowAgainDialog(int titleId, int msgId, int sharedPrefKeyId) { + mTitleId = titleId; + mMessageId = msgId; + mSharedPrefKeyId = sharedPrefKeyId; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + LayoutInflater inflater = getActivity().getLayoutInflater(); + View view = inflater.inflate(R.layout.do_not_show_again_dialog, null); + TextView message = (TextView) view.findViewById(R.id.message); + message.setText(mMessageId); + mDoNotShowAgainChk = (CheckBox) view.findViewById(R.id.do_not_show_chk); + + AlertDialog.Builder ab = new AlertDialog.Builder(getActivity()); + ab.setTitle(mTitleId); + ab.setView(view); + ab.setCancelable(false); + ab.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + Context context = getActivity(); + GalleryUtils.setBooleanPref(context, + context.getString(mSharedPrefKeyId), mDoNotShowAgainChk.isChecked()); + } + }); + return ab.create(); + } +} diff --git a/src/com/android/gallery3d/util/GalleryUtils.java b/src/com/android/gallery3d/util/GalleryUtils.java index a0580016b..3a50dbfbd 100644 --- a/src/com/android/gallery3d/util/GalleryUtils.java +++ b/src/com/android/gallery3d/util/GalleryUtils.java @@ -423,7 +423,7 @@ public class GalleryUtils { return (h > 0 && w / h >= 2); } // Newly added methods - public static int getIntPref(Context context, String name, int def) { + public static int getIntPref(Context context, String name, int def) { SharedPreferences prefs = context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE); return prefs.getInt(name, def); @@ -437,6 +437,34 @@ public class GalleryUtils { ed.commit(); } + public static boolean getBooleanPref(Context context, String name, boolean def) { + SharedPreferences prefs = + context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE); + return prefs.getBoolean(name, def); + } + + public static void setBooleanPref(Context context, String name, boolean value) { + SharedPreferences prefs = + context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE); + Editor ed = prefs.edit(); + ed.putBoolean(name, value); + ed.commit(); + } + + public static String getStringPref(Context context, String name, String def) { + SharedPreferences prefs = + context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE); + return prefs.getString(name, def); + } + + public static void setStringPref(Context context, String name, String value) { + SharedPreferences prefs = + context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE); + Editor ed = prefs.edit(); + ed.putString(name, value); + ed.commit(); + } + public static boolean isTelephonyCallInProgress(Context context) { TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); |
