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"
<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>
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();
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.
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;
ImagePreset preset;
boolean flatten;
int quality;
+ float sizeFactor;
}
static class UpdateBitmap implements Update {
}
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;
request.preset = preset;
request.flatten = flatten;
request.quality = quality;
+ request.sizeFactor = sizeFactor;
postRequest(request);
}
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;
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;
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;
}
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());
}
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;
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;
}
}
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++;
// Process the image
mImageSavingTask.saveImage(sourceUri, selectedUri, destinationFile,
- preset, flatten, quality);
+ preset, flatten, quality, sizeFactor);
}
public void updateNotificationWithBitmap(Bitmap bitmap) {
}
}
- 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) {
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");
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);
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;
}
@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
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;
+ }
+
}