OSDN Git Service

Fix export dialog (size export)
authornicolasroard <nicolasroard@google.com>
Sun, 11 Aug 2013 20:55:29 +0000 (13:55 -0700)
committernicolasroard <nicolasroard@google.com>
Sun, 11 Aug 2013 22:13:08 +0000 (15:13 -0700)
Change-Id: Ifa4bb6a990622bfc00f6b4fe4998417fec32e650

res/layout/filtershow_export_dialog.xml
res/values/filtershow_strings.xml
src/com/android/gallery3d/filtershow/imageshow/GeometryMathUtils.java
src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java
src/com/android/gallery3d/filtershow/pipeline/ImageSavingTask.java
src/com/android/gallery3d/filtershow/pipeline/ProcessingService.java
src/com/android/gallery3d/filtershow/tools/SaveImage.java
src/com/android/gallery3d/filtershow/ui/ExportDialog.java

index 2021075..c7ce1e7 100644 (file)
         android:layout_height="wrap_content"
         android:layout_gravity="center_horizontal|center_vertical"
         android:layout_margin="7dp"
-        android:text="@string/select_compression"/>
+        android:text="@string/select_output_settings"/>
 
-    <LinearLayout
-            android:orientation="horizontal"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
+    <GridLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:alignmentMode="alignBounds"
+        android:useDefaultMargins="true"
+        android:columnOrderPreserved="false"
+        android:columnCount="5">
+
+        <TextView
+                android:text="@string/size"
+                android:layout_gravity="right|center_vertical"/>
+
+        <EditText
+                android:id="@+id/editableWidth"
+                android:layout_gravity="right|center_vertical"
+                android:layout_width="100dp"
+                android:inputType="number"/>
+
+        <TextView
+                android:text="@string/x"
+                android:layout_gravity="center"/>
+
+        <EditText
+                android:id="@+id/editableHeight"
+                android:layout_gravity="left|center_vertical"
+                android:layout_width="100dp"
+                android:inputType="number"/>
+
+        <TextView
+                android:id="@+id/estimadedSize"
+                android:layout_gravity="center"/>
+
+        <TextView
+                android:text="@string/quality"
+                android:layout_margin="7dp"
+                android:layout_gravity="right|center_vertical" />
 
         <SeekBar
                 android:id="@+id/qualitySeekBar"
-                android:layout_width="0dp"
-                android:layout_height="wrap_content"
-                android:layout_weight="3"
                 android:layout_margin="7dp"
+                android:layout_gravity="center"
                 android:max="100"
-                android:progress="100"/>
+                android:progress="100"
+                android:layout_width="400dp"
+                android:layout_columnSpan="3"/>
 
         <TextView
                 android:id="@+id/qualityTextView"
-                android:layout_width="0dp"
-                android:layout_height="wrap_content"
-                android:layout_weight="1"
-                android:layout_marginLeft="7dp"
-                android:layout_gravity="center_vertical|center_horizontal"/>
+                android:layout_margin="7dp"
+                android:layout_gravity="left|center_vertical"/>
 
-    </LinearLayout>
+    </GridLayout>
 
     <LinearLayout
             android:orientation="horizontal"
index f2c1bf1..23007bb 100644 (file)
     <string name="hide_imagestate_panel">Hide Applied Effects</string>
     <!--  Text for the menu item to export a flattened photo[CHAR LIMIT=30] -->
     <string name="export_flattened">Export Flattened Image</string>
+
+    <!-- Export Dialog-->
+
     <!--  Text for selecting export image quality [CHAR LIMIT=100] -->
-    <string name="select_compression">Select output quality.</string>
+    <string name="select_output_settings">Select Output Settings.</string>
     <!--  Text for quality value tag [CHAR LIMIT=30] -->
     <string name="quality">Quality</string>
+    <!--  Text for size value tag [CHAR LIMIT=30] -->
+    <string name="size">Size</string>
+    <!--  Text for size x [CHAR LIMIT=1] -->
+    <string name="x">x</string>
 
     <!--  Name for the overflow menu item for settings [CHAR LIMIT=20] -->
     <string name="menu_settings">Settings</string>
index 8358e1a..1cefe08 100644 (file)
@@ -282,6 +282,15 @@ public final class GeometryMathUtils {
         unpackGeometry(outHolder, geometry);
     }
 
+    public static Rect finalGeometryRect(int width, int height,
+                                         Collection<FilterRepresentation> geometry) {
+        GeometryHolder holder = unpackGeometry(geometry);
+        RectF crop = getTrueCropRect(holder, width, height);
+        Rect frame = new Rect();
+        crop.roundOut(frame);
+        return frame;
+    }
+
     private static Bitmap applyFullGeometryMatrix(Bitmap image, GeometryHolder holder) {
         int width = image.getWidth();
         int height = image.getHeight();
index e28cc35..88e8d42 100644 (file)
@@ -421,6 +421,10 @@ public class ImagePreset {
         return null;
     }
 
+    public Rect finalGeometryRect(int width, int height) {
+        return GeometryMathUtils.finalGeometryRect(width, height, getGeometryFilters());
+    }
+
     public Bitmap applyGeometry(Bitmap bitmap, FilterEnvironment environment) {
         // Apply any transform -- 90 rotate, flip, straighten, crop
         // Returns a new bitmap.
index b760edd..c29376f 100644 (file)
@@ -18,6 +18,7 @@ package com.android.gallery3d.filtershow.pipeline;
 
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.net.Uri;
 import com.android.gallery3d.filtershow.cache.ImageLoader;
 import com.android.gallery3d.filtershow.filters.FiltersManager;
@@ -35,6 +36,7 @@ public class ImageSavingTask extends ProcessingTask {
         ImagePreset preset;
         boolean flatten;
         int quality;
+        float sizeFactor;
     }
 
     static class UpdateBitmap implements Update {
@@ -55,7 +57,8 @@ public class ImageSavingTask extends ProcessingTask {
     }
 
     public void saveImage(Uri sourceUri, Uri selectedUri,
-                          File destinationFile, ImagePreset preset, boolean flatten, int quality) {
+                          File destinationFile, ImagePreset preset, boolean flatten,
+                          int quality, float sizeFactor) {
         SaveRequest request = new SaveRequest();
         request.sourceUri = sourceUri;
         request.selectedUri = selectedUri;
@@ -63,6 +66,7 @@ public class ImageSavingTask extends ProcessingTask {
         request.preset = preset;
         request.flatten = flatten;
         request.quality = quality;
+        request.sizeFactor = sizeFactor;
         postRequest(request);
     }
 
@@ -89,7 +93,8 @@ public class ImageSavingTask extends ProcessingTask {
                         postUpdate(updateProgress);
                     }
                 });
-        Uri uri = saveImage.processAndSaveImage(preset, !flatten, request.quality);
+        Uri uri = saveImage.processAndSaveImage(preset, !flatten,
+                request.quality, request.sizeFactor);
         URIResult result = new URIResult();
         result.uri = uri;
         return result;
index 4f736f8..518f232 100644 (file)
@@ -32,7 +32,6 @@ import com.android.gallery3d.R;
 import com.android.gallery3d.filtershow.FilterShowActivity;
 import com.android.gallery3d.filtershow.filters.FiltersManager;
 import com.android.gallery3d.filtershow.filters.ImageFilter;
-import com.android.gallery3d.filtershow.imageshow.MasterImage;
 import com.android.gallery3d.filtershow.tools.SaveImage;
 
 import java.io.File;
@@ -51,6 +50,7 @@ public class ProcessingService extends Service {
     private static final String DESTINATION_FILE = "destinationFile";
     private static final String SAVING = "saving";
     private static final String FLATTEN = "flatten";
+    private static final String SIZE_FACTOR = "sizeFactor";
 
     private ProcessingTaskController mProcessingTaskController;
     private ImageSavingTask mImageSavingTask;
@@ -139,13 +139,14 @@ public class ProcessingService extends Service {
     }
 
     public static Intent getSaveIntent(Context context, ImagePreset preset, File destination,
-            Uri selectedImageUri, Uri sourceImageUri, boolean doFlatten, int quality) {
+            Uri selectedImageUri, Uri sourceImageUri, boolean doFlatten, int quality, float sizeFactor) {
         Intent processIntent = new Intent(context, ProcessingService.class);
         processIntent.putExtra(ProcessingService.SOURCE_URI,
                 sourceImageUri.toString());
         processIntent.putExtra(ProcessingService.SELECTED_URI,
                 selectedImageUri.toString());
         processIntent.putExtra(ProcessingService.QUALITY, quality);
+        processIntent.putExtra(ProcessingService.SIZE_FACTOR, sizeFactor);
         if (destination != null) {
             processIntent.putExtra(ProcessingService.DESTINATION_FILE, destination.toString());
         }
@@ -192,6 +193,7 @@ public class ProcessingService extends Service {
             String selected = intent.getStringExtra(SELECTED_URI);
             String destination = intent.getStringExtra(DESTINATION_FILE);
             int quality = intent.getIntExtra(QUALITY, 100);
+            float sizeFactor = intent.getFloatExtra(SIZE_FACTOR, 1);
             boolean flatten = intent.getBooleanExtra(FLATTEN, false);
             Uri sourceUri = Uri.parse(source);
             Uri selectedUri = null;
@@ -206,7 +208,8 @@ public class ProcessingService extends Service {
             preset.readJsonFromString(presetJson);
             mNeedsAlive = false;
             mSaving = true;
-            handleSaveRequest(sourceUri, selectedUri, destinationFile, preset, flatten, quality);
+            handleSaveRequest(sourceUri, selectedUri, destinationFile, preset,
+                    flatten, quality, sizeFactor);
         }
         return START_REDELIVER_INTENT;
     }
@@ -224,7 +227,8 @@ public class ProcessingService extends Service {
     }
 
     public void handleSaveRequest(Uri sourceUri, Uri selectedUri,
-            File destinationFile, ImagePreset preset, boolean flatten, int quality) {
+            File destinationFile, ImagePreset preset, boolean flatten,
+            int quality, float sizeFactor) {
         mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
 
         mNotificationId++;
@@ -242,7 +246,7 @@ public class ProcessingService extends Service {
         // Process the image
 
         mImageSavingTask.saveImage(sourceUri, selectedUri, destinationFile,
-                preset, flatten, quality);
+                preset, flatten, quality, sizeFactor);
     }
 
     public void updateNotificationWithBitmap(Bitmap bitmap) {
index 83cbd01..5aafbff 100644 (file)
@@ -310,7 +310,8 @@ public class SaveImage {
         }
     }
 
-    public Uri processAndSaveImage(ImagePreset preset, boolean doAuxBackup, int quality) {
+    public Uri processAndSaveImage(ImagePreset preset, boolean doAuxBackup,
+                                   int quality, float sizeFactor) {
 
         Uri uri = resetToOriginalImageIfNeeded(preset, doAuxBackup);
         if (uri != null) {
@@ -341,6 +342,12 @@ public class SaveImage {
                 if (bitmap == null) {
                     return null;
                 }
+                if (sizeFactor != 1f) {
+                    // if we have a valid size
+                    int w = (int) (bitmap.getWidth() * sizeFactor);
+                    int h = (int) (bitmap.getHeight() * sizeFactor);
+                    bitmap = Bitmap.createScaledBitmap(bitmap, w, h, true);
+                }
                 updateProgress();
                 CachingPipeline pipeline = new CachingPipeline(FiltersManager.getManager(),
                         "Saving");
@@ -458,7 +465,7 @@ public class SaveImage {
         Uri sourceImageUri = MasterImage.getImage().getUri();
 
         Intent processIntent = ProcessingService.getSaveIntent(filterShowActivity, preset,
-                destination, selectedImageUri, sourceImageUri, false, 90);
+                destination, selectedImageUri, sourceImageUri, false, 90, 1f);
 
         filterShowActivity.startService(processIntent);
 
index 4b30e7b..b0046e1 100644 (file)
 package com.android.gallery3d.filtershow.ui;
 
 import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Handler;
 import android.support.v4.app.DialogFragment;
+import android.text.Editable;
+import android.text.TextWatcher;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.EditText;
 import android.widget.SeekBar;
 import android.widget.TextView;
 
 import com.android.gallery3d.R;
 import com.android.gallery3d.filtershow.FilterShowActivity;
 import com.android.gallery3d.filtershow.imageshow.MasterImage;
+import com.android.gallery3d.filtershow.pipeline.ImagePreset;
 import com.android.gallery3d.filtershow.pipeline.ProcessingService;
 import com.android.gallery3d.filtershow.tools.SaveImage;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 
-public class ExportDialog extends DialogFragment implements View.OnClickListener, SeekBar.OnSeekBarChangeListener{
+public class ExportDialog extends DialogFragment implements View.OnClickListener,
+        SeekBar.OnSeekBarChangeListener {
     SeekBar mSeekBar;
     TextView mSeekVal;
+    EditText mWidthText;
+    EditText mHeightText;
+    TextView mEstimatedSize;
+    int mQuality = 95;
+    int mExportWidth = 0;
+    int mExportHeight = 0;
+    Rect mOriginalBounds;
+    int mCompressedSize;
+    Rect mCompressedBounds;
+    float mExportCompressionMargin = 1.1f;
+    float mRatio;
     String mSliderLabel;
+    boolean mEditing = false;
+    Handler mHandler;
+    int mUpdateDelay = 1000;
+    Runnable mUpdateRunnable = new Runnable() {
+        @Override
+        public void run() {
+            updateCompressionFactor();
+            updateSize();
+        }
+    };
+
+    private class Watcher implements TextWatcher {
+        private EditText mEditText;
+        Watcher(EditText text) {
+            mEditText = text;
+        }
+
+        @Override
+        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+        }
+
+        @Override
+        public void onTextChanged(CharSequence s, int start, int before, int count) {
+        }
+
+        @Override
+        public void afterTextChanged(Editable s) {
+            textChanged(mEditText);
+        }
+    }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
+        mHandler = new Handler(getActivity().getMainLooper());
+
         View view = inflater.inflate(R.layout.filtershow_export_dialog, container);
         mSeekBar = (SeekBar) view.findViewById(R.id.qualitySeekBar);
         mSeekVal = (TextView) view.findViewById(R.id.qualityTextView);
         mSliderLabel = getString(R.string.quality) + ": ";
+        mSeekBar.setProgress(mQuality);
         mSeekVal.setText(mSliderLabel + mSeekBar.getProgress());
         mSeekBar.setOnSeekBarChangeListener(this);
+        mWidthText = (EditText) view.findViewById(R.id.editableWidth);
+        mHeightText = (EditText) view.findViewById(R.id.editableHeight);
+        mEstimatedSize = (TextView) view.findViewById(R.id.estimadedSize);
+
+        mOriginalBounds = MasterImage.getImage().getOriginalBounds();
+        ImagePreset preset = MasterImage.getImage().getPreset();
+        mOriginalBounds = preset.finalGeometryRect(mOriginalBounds.width(),
+                mOriginalBounds.height());
+        mRatio = mOriginalBounds.width() / (float) mOriginalBounds.height();
+        mWidthText.setText("" + mOriginalBounds.width());
+        mHeightText.setText("" + mOriginalBounds.height());
+        mExportWidth = mOriginalBounds.width();
+        mExportHeight = mOriginalBounds.height();
+        mWidthText.addTextChangedListener(new Watcher(mWidthText));
+        mHeightText.addTextChangedListener(new Watcher(mHeightText));
+
         view.findViewById(R.id.cancel).setOnClickListener(this);
         view.findViewById(R.id.done).setOnClickListener(this);
         getDialog().setTitle(R.string.export_flattened);
+
+        updateCompressionFactor();
+        updateSize();
         return view;
     }
 
@@ -61,12 +133,19 @@ public class ExportDialog extends DialogFragment implements View.OnClickListener
 
     @Override
     public void onStartTrackingTouch(SeekBar arg0) {
-     // Do nothing
+        // Do nothing
     }
 
     @Override
     public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
         mSeekVal.setText(mSliderLabel + arg1);
+        mQuality = mSeekBar.getProgress();
+        scheduleUpdateCompressionFactor();
+    }
+
+    private void scheduleUpdateCompressionFactor() {
+        mHandler.removeCallbacks(mUpdateRunnable);
+        mHandler.postDelayed(mUpdateRunnable, mUpdateDelay);
     }
 
     @Override
@@ -79,12 +158,80 @@ public class ExportDialog extends DialogFragment implements View.OnClickListener
                 FilterShowActivity activity = (FilterShowActivity) getActivity();
                 Uri sourceUri = MasterImage.getImage().getUri();
                 File dest = SaveImage.getNewFile(activity, sourceUri);
+                float scaleFactor = mExportWidth / (float) mOriginalBounds.width();
                 Intent processIntent = ProcessingService.getSaveIntent(activity, MasterImage
                         .getImage().getPreset(), dest, activity.getSelectedImageUri(), sourceUri,
-                        true, mSeekBar.getProgress());
+                        true, mSeekBar.getProgress(), scaleFactor);
                 activity.startService(processIntent);
                 dismiss();
                 break;
         }
     }
+
+    public void updateCompressionFactor() {
+        Bitmap bitmap = MasterImage.getImage().getFilteredImage();
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        bitmap.compress(Bitmap.CompressFormat.JPEG, mQuality, out);
+        mCompressedSize = out.size();
+        mCompressedBounds = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+    }
+
+    public void updateSize() {
+        if (mCompressedBounds == null) {
+            return;
+        }
+        // This is a rough estimate of the final save size. There's some loose correlation
+        // between a compressed jpeg and a larger version of it in function of the image
+        // area. Not a perfect estimate by far.
+        float originalArea = mCompressedBounds.width() * mCompressedBounds.height();
+        float newArea = mExportWidth * mExportHeight;
+        float factor = originalArea / (float) mCompressedSize;
+        float compressedSize = newArea / factor;
+        compressedSize *= mExportCompressionMargin;
+        float size = compressedSize / 1024.f / 1024.f;
+        size = ((int) (size * 100)) / 100f;
+        String estimatedSize = "" + size + " Mb";
+        mEstimatedSize.setText(estimatedSize);
+    }
+
+    private void textChanged(EditText text) {
+        if (mEditing) {
+            return;
+        }
+        mEditing = true;
+        int width = 0;
+        int height = 0;
+        if (text.getId() == R.id.editableWidth) {
+            if (mWidthText.getText() != null) {
+                String value = String.valueOf(mWidthText.getText());
+                if (value.length() > 0) {
+                    width = Integer.parseInt(value);
+                    if (width > mOriginalBounds.width()) {
+                        width = mOriginalBounds.width();
+                        mWidthText.setText("" + width);
+                    }
+                    height = (int) (width / mRatio);
+                }
+                mHeightText.setText("" + height);
+            }
+        } else if (text.getId() == R.id.editableHeight) {
+            if (mHeightText.getText() != null) {
+                String value = String.valueOf(mHeightText.getText());
+                if (value.length() > 0) {
+                    height = Integer.parseInt(value);
+                    if (height > mOriginalBounds.height()) {
+                        height = mOriginalBounds.height();
+                        mHeightText.setText("" + height);
+                    }
+                    width = (int) (height * mRatio);
+                }
+                mWidthText.setText("" + width);
+            }
+        }
+        mExportWidth = width;
+        mExportHeight = height;
+        updateSize();
+        mEditing = false;
+    }
+
 }