OSDN Git Service

Fixed more of filtershows large bitmap reference leaks.
[android-x86/packages-apps-Gallery2.git] / src / com / android / gallery3d / filtershow / ui / ImageCurves.java
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.gallery3d.filtershow.ui;
18
19 import android.content.Context;
20 import android.graphics.Bitmap;
21 import android.graphics.Canvas;
22 import android.graphics.Color;
23 import android.graphics.Paint;
24 import android.graphics.Path;
25 import android.graphics.PorterDuff;
26 import android.graphics.PorterDuffXfermode;
27 import android.os.AsyncTask;
28 import android.util.AttributeSet;
29 import android.view.LayoutInflater;
30 import android.view.MenuItem;
31 import android.view.MotionEvent;
32 import android.view.View;
33 import android.widget.LinearLayout;
34 import android.widget.PopupMenu;
35
36 import com.android.gallery3d.R;
37 import com.android.gallery3d.filtershow.editors.EditorCurves;
38 import com.android.gallery3d.filtershow.filters.FilterCurvesRepresentation;
39 import com.android.gallery3d.filtershow.filters.FiltersManager;
40 import com.android.gallery3d.filtershow.filters.ImageFilterCurves;
41 import com.android.gallery3d.filtershow.imageshow.ImageShow;
42 import com.android.gallery3d.filtershow.presets.ImagePreset;
43
44 public class ImageCurves extends ImageShow {
45
46     private static final String LOGTAG = "ImageCurves";
47     Paint gPaint = new Paint();
48     Path gPathSpline = new Path();
49
50     private int mCurrentCurveIndex = Spline.RGB;
51     private boolean mDidAddPoint = false;
52     private boolean mDidDelete = false;
53     private ControlPoint mCurrentControlPoint = null;
54     private int mCurrentPick = -1;
55     private ImagePreset mLastPreset = null;
56     int[] redHistogram = new int[256];
57     int[] greenHistogram = new int[256];
58     int[] blueHistogram = new int[256];
59     Path gHistoPath = new Path();
60
61     boolean mDoingTouchMove = false;
62     private EditorCurves mEditorCurves;
63     private FilterCurvesRepresentation mFilterCurvesRepresentation;
64
65     public ImageCurves(Context context) {
66         super(context);
67         setLayerType(LAYER_TYPE_SOFTWARE, gPaint);
68         resetCurve();
69     }
70
71     public ImageCurves(Context context, AttributeSet attrs) {
72         super(context, attrs);
73         setLayerType(LAYER_TYPE_SOFTWARE, gPaint);
74         resetCurve();
75     }
76
77     @Override
78     public boolean useUtilityPanel() {
79         return true;
80     }
81
82     private void showPopupMenu(LinearLayout accessoryViewList) {
83         final FramedTextButton button = (FramedTextButton) accessoryViewList.findViewById(
84                 R.id.curvesUtilityButton);
85         if (button == null) {
86             return;
87         }
88         PopupMenu popupMenu = new PopupMenu(getActivity(), button);
89         popupMenu.getMenuInflater().inflate(R.menu.filtershow_menu_curves, popupMenu.getMenu());
90         popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
91             @Override
92             public boolean onMenuItemClick(MenuItem item) {
93                 setChannel(item.getItemId());
94                 button.setTextFrom(item.getItemId());
95                 return true;
96             }
97         });
98         popupMenu.show();
99     }
100
101     @Override
102     public void openUtilityPanel(final LinearLayout accessoryViewList) {
103         View view = accessoryViewList.findViewById(R.id.curvesUtilityButton);
104         if (view == null) {
105             LayoutInflater inflater = (LayoutInflater)getActivity().getSystemService
106                     (Context.LAYOUT_INFLATER_SERVICE);
107             view = inflater.inflate(R.layout.filtershow_curves_button, accessoryViewList, false);
108             accessoryViewList.addView(view, view.getLayoutParams());
109             view.setOnClickListener(new OnClickListener() {
110                 @Override
111                 public void onClick(View arg0) {
112                     showPopupMenu(accessoryViewList);
113                 }
114             });
115         }
116
117         if (view != null) {
118             view.setVisibility(View.VISIBLE);
119         }
120     }
121
122     public void nextChannel() {
123         mCurrentCurveIndex = ((mCurrentCurveIndex + 1) % 4);
124         invalidate();
125     }
126
127     @Override
128     public boolean showTitle() {
129         return false;
130     }
131
132     private ImageFilterCurves curves() {
133         String filterName = getFilterName();
134         ImagePreset p = getImagePreset();
135         if (p != null) {
136             return (ImageFilterCurves) FiltersManager.getManager().getFilter(ImageFilterCurves.class);
137         }
138         return null;
139     }
140
141     private Spline getSpline(int index) {
142         return curves().getSpline(index);
143     }
144
145     @Override
146     public void resetParameter() {
147         super.resetParameter();
148         resetCurve();
149         mLastPreset = null;
150         invalidate();
151     }
152
153     public void resetCurve() {
154         if (curves() != null) {
155             curves().reset();
156             updateCachedImage();
157         }
158     }
159
160     @Override
161     public void onDraw(Canvas canvas) {
162         super.onDraw(canvas);
163
164         gPaint.setAntiAlias(true);
165
166         if (getImagePreset() != mLastPreset && getFilteredImage() != null) {
167             new ComputeHistogramTask().execute(getFilteredImage());
168             mLastPreset = getImagePreset();
169         }
170
171         if (curves() == null) {
172             return;
173         }
174
175         if (mCurrentCurveIndex == Spline.RGB || mCurrentCurveIndex == Spline.RED) {
176             drawHistogram(canvas, redHistogram, Color.RED, PorterDuff.Mode.SCREEN);
177         }
178         if (mCurrentCurveIndex == Spline.RGB || mCurrentCurveIndex == Spline.GREEN) {
179             drawHistogram(canvas, greenHistogram, Color.GREEN, PorterDuff.Mode.SCREEN);
180         }
181         if (mCurrentCurveIndex == Spline.RGB || mCurrentCurveIndex == Spline.BLUE) {
182             drawHistogram(canvas, blueHistogram, Color.BLUE, PorterDuff.Mode.SCREEN);
183         }
184         // We only display the other channels curves when showing the RGB curve
185         if (mCurrentCurveIndex == Spline.RGB) {
186             for (int i = 0; i < 4; i++) {
187                 Spline spline = getSpline(i);
188                 if (i != mCurrentCurveIndex && !spline.isOriginal()) {
189                     // And we only display a curve if it has more than two
190                     // points
191                     spline.draw(canvas, Spline.colorForCurve(i), getWidth(),
192                             getHeight(), false, mDoingTouchMove);
193                 }
194             }
195         }
196         // ...but we always display the current curve.
197         getSpline(mCurrentCurveIndex)
198                 .draw(canvas, Spline.colorForCurve(mCurrentCurveIndex), getWidth(), getHeight(),
199                         true, mDoingTouchMove);
200         drawToast(canvas);
201
202     }
203
204     private int pickControlPoint(float x, float y) {
205         int pick = 0;
206         Spline spline = getSpline(mCurrentCurveIndex);
207         float px = spline.getPoint(0).x;
208         float py = spline.getPoint(0).y;
209         double delta = Math.sqrt((px - x) * (px - x) + (py - y) * (py - y));
210         for (int i = 1; i < spline.getNbPoints(); i++) {
211             px = spline.getPoint(i).x;
212             py = spline.getPoint(i).y;
213             double currentDelta = Math.sqrt((px - x) * (px - x) + (py - y)
214                     * (py - y));
215             if (currentDelta < delta) {
216                 delta = currentDelta;
217                 pick = i;
218             }
219         }
220
221         if (!mDidAddPoint && (delta * getWidth() > 100)
222                 && (spline.getNbPoints() < 10)) {
223             return -1;
224         }
225
226         return pick;
227     }
228
229     private String getFilterName() {
230         return "Curves";
231     }
232
233     @Override
234     public synchronized boolean onTouchEvent(MotionEvent e) {
235         super.onTouchEvent(e);
236
237         if (e.getPointerCount() != 1) {
238             return true;
239         }
240
241         if (didFinishScalingOperation()) {
242             return true;
243         }
244
245         float posX = e.getX() / getWidth();
246         float posY = e.getY();
247         float margin = Spline.curveHandleSize() / 2;
248         if (posY < margin) {
249             posY = margin;
250         }
251         if (posY > getHeight() - margin) {
252             posY = getHeight() - margin;
253         }
254         posY = (posY - margin) / (getHeight() - 2 * margin);
255
256         if (e.getActionMasked() == MotionEvent.ACTION_UP) {
257             mCurrentControlPoint = null;
258             mCurrentPick = -1;
259             updateCachedImage();
260             mDidAddPoint = false;
261             if (mDidDelete) {
262                 mDidDelete = false;
263             }
264             mDoingTouchMove = false;
265             return true;
266         }
267
268         if (mDidDelete) {
269             return true;
270         }
271
272         if (curves() == null) {
273             return true;
274         }
275
276         if (e.getActionMasked() == MotionEvent.ACTION_MOVE) {
277             mDoingTouchMove = true;
278             Spline spline = getSpline(mCurrentCurveIndex);
279             int pick = mCurrentPick;
280             if (mCurrentControlPoint == null) {
281                 pick = pickControlPoint(posX, posY);
282                 if (pick == -1) {
283                     mCurrentControlPoint = new ControlPoint(posX, posY);
284                     pick = spline.addPoint(mCurrentControlPoint);
285                     mDidAddPoint = true;
286                 } else {
287                     mCurrentControlPoint = spline.getPoint(pick);
288                 }
289                 mCurrentPick = pick;
290             }
291
292             if (spline.isPointContained(posX, pick)) {
293                 spline.movePoint(pick, posX, posY);
294             } else if (pick != -1 && spline.getNbPoints() > 2) {
295                 spline.deletePoint(pick);
296                 mDidDelete = true;
297             }
298             updateCachedImage();
299             invalidate();
300         }
301         return true;
302     }
303
304     public synchronized void updateCachedImage() {
305         if (getImagePreset() != null) {
306             resetImageCaches(this);
307             if (mEditorCurves != null) {
308                 mEditorCurves.commitLocalRepresentation();
309             }
310             invalidate();
311         }
312     }
313
314     class ComputeHistogramTask extends AsyncTask<Bitmap, Void, int[]> {
315         @Override
316         protected int[] doInBackground(Bitmap... params) {
317             int[] histo = new int[256 * 3];
318             Bitmap bitmap = params[0];
319             int w = bitmap.getWidth();
320             int h = bitmap.getHeight();
321             int[] pixels = new int[w * h];
322             bitmap.getPixels(pixels, 0, w, 0, 0, w, h);
323             for (int i = 0; i < w; i++) {
324                 for (int j = 0; j < h; j++) {
325                     int index = j * w + i;
326                     int r = Color.red(pixels[index]);
327                     int g = Color.green(pixels[index]);
328                     int b = Color.blue(pixels[index]);
329                     histo[r]++;
330                     histo[256 + g]++;
331                     histo[512 + b]++;
332                 }
333             }
334             return histo;
335         }
336
337         @Override
338         protected void onPostExecute(int[] result) {
339             System.arraycopy(result, 0, redHistogram, 0, 256);
340             System.arraycopy(result, 256, greenHistogram, 0, 256);
341             System.arraycopy(result, 512, blueHistogram, 0, 256);
342             invalidate();
343         }
344     }
345
346     private void drawHistogram(Canvas canvas, int[] histogram, int color, PorterDuff.Mode mode) {
347         int max = 0;
348         for (int i = 0; i < histogram.length; i++) {
349             if (histogram[i] > max) {
350                 max = histogram[i];
351             }
352         }
353         float w = getWidth() - Spline.curveHandleSize();
354         float h = getHeight() - Spline.curveHandleSize() / 2.0f;
355         float dx = Spline.curveHandleSize() / 2.0f;
356         float wl = w / histogram.length;
357         float wh = (0.3f * h) / max;
358         Paint paint = new Paint();
359         paint.setARGB(100, 255, 255, 255);
360         paint.setStrokeWidth((int) Math.ceil(wl));
361
362         Paint paint2 = new Paint();
363         paint2.setColor(color);
364         paint2.setStrokeWidth(6);
365         paint2.setXfermode(new PorterDuffXfermode(mode));
366         gHistoPath.reset();
367         gHistoPath.moveTo(dx, h);
368         boolean firstPointEncountered = false;
369         float prev = 0;
370         float last = 0;
371         for (int i = 0; i < histogram.length; i++) {
372             float x = i * wl + dx;
373             float l = histogram[i] * wh;
374             if (l != 0) {
375                 float v = h - (l + prev) / 2.0f;
376                 if (!firstPointEncountered) {
377                     gHistoPath.lineTo(x, h);
378                     firstPointEncountered = true;
379                 }
380                 gHistoPath.lineTo(x, v);
381                 prev = l;
382                 last = x;
383             }
384         }
385         gHistoPath.lineTo(last, h);
386         gHistoPath.lineTo(w, h);
387         gHistoPath.close();
388         canvas.drawPath(gHistoPath, paint2);
389         paint2.setStrokeWidth(2);
390         paint2.setStyle(Paint.Style.STROKE);
391         paint2.setARGB(255, 200, 200, 200);
392         canvas.drawPath(gHistoPath, paint2);
393     }
394
395     public void setChannel(int itemId) {
396         switch (itemId) {
397             case R.id.curve_menu_rgb: {
398                 mCurrentCurveIndex = Spline.RGB;
399                 break;
400             }
401             case R.id.curve_menu_red: {
402                 mCurrentCurveIndex = Spline.RED;
403                 break;
404             }
405             case R.id.curve_menu_green: {
406                 mCurrentCurveIndex = Spline.GREEN;
407                 break;
408             }
409             case R.id.curve_menu_blue: {
410                 mCurrentCurveIndex = Spline.BLUE;
411                 break;
412             }
413         }
414         mEditorCurves.commitLocalRepresentation();
415         invalidate();
416     }
417
418     public void setEditor(EditorCurves editorCurves) {
419         mEditorCurves = editorCurves;
420     }
421
422     public void setFilterDrawRepresentation(FilterCurvesRepresentation drawRep) {
423         mFilterCurvesRepresentation = drawRep;
424     }
425 }