ImageCurves.java revision 15a056e3cc1970bc0b99dba71df8222ba0345859
10d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
20d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardpackage com.android.gallery3d.filtershow.ui;
30d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
40d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport android.content.Context;
53992ae6cf1cbb7b57a22d8287c40451558408335nicolasroardimport android.graphics.Bitmap;
60d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport android.graphics.Canvas;
73992ae6cf1cbb7b57a22d8287c40451558408335nicolasroardimport android.graphics.Color;
80d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport android.graphics.Paint;
90d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport android.graphics.Path;
103992ae6cf1cbb7b57a22d8287c40451558408335nicolasroardimport android.graphics.PorterDuff;
113992ae6cf1cbb7b57a22d8287c40451558408335nicolasroardimport android.graphics.PorterDuffXfermode;
123992ae6cf1cbb7b57a22d8287c40451558408335nicolasroardimport android.os.AsyncTask;
130d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport android.util.AttributeSet;
140d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroardimport android.view.MotionEvent;
150d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
16c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroardimport com.android.gallery3d.R;
1792e2341248e99c691f38820503984bc5e2f18811nicolasroardimport com.android.gallery3d.filtershow.filters.ImageFilterCurves;
1892e2341248e99c691f38820503984bc5e2f18811nicolasroardimport com.android.gallery3d.filtershow.imageshow.ImageSlave;
1992e2341248e99c691f38820503984bc5e2f18811nicolasroardimport com.android.gallery3d.filtershow.presets.ImagePreset;
2092e2341248e99c691f38820503984bc5e2f18811nicolasroard
215448bf8095483574649afcc2add7f153670c7450nicolasroardpublic class ImageCurves extends ImageSlave {
220d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
230d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    private static final String LOGTAG = "ImageCurves";
240d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    Paint gPaint = new Paint();
250d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    Path gPathSpline = new Path();
263992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard
27c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard    private int mCurrentCurveIndex = Spline.RGB;
280d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    private boolean mDidAddPoint = false;
290d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    private boolean mDidDelete = false;
300d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    private ControlPoint mCurrentControlPoint = null;
313992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard    private ImagePreset mLastPreset = null;
323992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard    int[] redHistogram = new int[256];
333992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard    int[] greenHistogram = new int[256];
343992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard    int[] blueHistogram = new int[256];
353992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard    Path gHistoPath = new Path();
360d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
37c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard    boolean mDoingTouchMove = false;
38c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard
390d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    public ImageCurves(Context context) {
400d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        super(context);
410d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        resetCurve();
420d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    }
430d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
440d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    public ImageCurves(Context context, AttributeSet attrs) {
450d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        super(context, attrs);
460d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        resetCurve();
470d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    }
480d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
493992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard    public void nextChannel() {
503992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        mCurrentCurveIndex = ((mCurrentCurveIndex + 1) % 4);
513992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        invalidate();
523992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard    }
533992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard
5492e2341248e99c691f38820503984bc5e2f18811nicolasroard    @Override
550d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    public boolean showTitle() {
560d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        return false;
570d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    }
580d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
59d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard    private ImageFilterCurves curves() {
605448bf8095483574649afcc2add7f153670c7450nicolasroard        if (getMaster() != null) {
610d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard            String filterName = getFilterName();
62d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard            return (ImageFilterCurves) getImagePreset().getFilter(filterName);
630d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        }
64d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard        return null;
65d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard    }
66d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard
67d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard    private Spline getSpline(int index) {
68d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard        return curves().getSpline(index);
690d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    }
700d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
7192e2341248e99c691f38820503984bc5e2f18811nicolasroard    @Override
7292e2341248e99c691f38820503984bc5e2f18811nicolasroard    public void resetParameter() {
7392e2341248e99c691f38820503984bc5e2f18811nicolasroard        super.resetParameter();
7492e2341248e99c691f38820503984bc5e2f18811nicolasroard        resetCurve();
753992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        mLastPreset = null;
763992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        invalidate();
7792e2341248e99c691f38820503984bc5e2f18811nicolasroard    }
7892e2341248e99c691f38820503984bc5e2f18811nicolasroard
790d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    public void resetCurve() {
80d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard        if (getMaster() != null && curves() != null) {
81d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard            curves().reset();
82d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard            updateCachedImage();
830d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        }
840d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    }
850d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
8692e2341248e99c691f38820503984bc5e2f18811nicolasroard    @Override
870d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    public void onDraw(Canvas canvas) {
880d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        super.onDraw(canvas);
890d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
900d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        gPaint.setAntiAlias(true);
910d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
923992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        if (getImagePreset() != mLastPreset) {
933992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            new ComputeHistogramTask().execute(mFilteredImage);
943992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            mLastPreset = getImagePreset();
950d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        }
960d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
9715a056e3cc1970bc0b99dba71df8222ba0345859nicolasroard        if (curves() == null) {
9815a056e3cc1970bc0b99dba71df8222ba0345859nicolasroard            return;
9915a056e3cc1970bc0b99dba71df8222ba0345859nicolasroard        }
10015a056e3cc1970bc0b99dba71df8222ba0345859nicolasroard
1013992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        if (mCurrentCurveIndex == Spline.RGB || mCurrentCurveIndex == Spline.RED) {
1023992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            drawHistogram(canvas, redHistogram, Color.RED, PorterDuff.Mode.SCREEN);
1033992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        }
1043992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        if (mCurrentCurveIndex == Spline.RGB || mCurrentCurveIndex == Spline.GREEN) {
1053992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            drawHistogram(canvas, greenHistogram, Color.GREEN, PorterDuff.Mode.SCREEN);
1063992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        }
1073992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        if (mCurrentCurveIndex == Spline.RGB || mCurrentCurveIndex == Spline.BLUE) {
1083992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            drawHistogram(canvas, blueHistogram, Color.BLUE, PorterDuff.Mode.SCREEN);
1093992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        }
1103992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        // We only display the other channels curves when showing the RGB curve
1113992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        if (mCurrentCurveIndex == Spline.RGB) {
1123992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            for (int i = 0; i < 4; i++) {
113d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard                Spline spline = getSpline(i);
1143992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                if (i != mCurrentCurveIndex && !spline.isOriginal()) {
1153992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                    // And we only display a curve if it has more than two
1163992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                    // points
117c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard                    spline.draw(canvas, Spline.colorForCurve(i), getWidth(),
118c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard                            getHeight(), false, mDoingTouchMove);
1193992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                }
1200d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard            }
1210d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        }
1223992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        // ...but we always display the current curve.
123d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard        getSpline(mCurrentCurveIndex)
1243992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                .draw(canvas, Spline.colorForCurve(mCurrentCurveIndex), getWidth(), getHeight(),
125c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard                        true, mDoingTouchMove);
1263992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        drawToast(canvas);
1270d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
1280d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    }
1290d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
1300d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    private int pickControlPoint(float x, float y) {
1310d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        int pick = 0;
132d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard        Spline spline = getSpline(mCurrentCurveIndex);
133d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard        float px = spline.getPoint(0).x;
134d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard        float py = spline.getPoint(0).y;
1350d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        double delta = Math.sqrt((px - x) * (px - x) + (py - y) * (py - y));
136d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard        for (int i = 1; i < spline.getNbPoints(); i++) {
137d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard            px = spline.getPoint(i).x;
138d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard            py = spline.getPoint(i).y;
1390d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard            double currentDelta = Math.sqrt((px - x) * (px - x) + (py - y)
1400d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard                    * (py - y));
1410d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard            if (currentDelta < delta) {
1420d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard                delta = currentDelta;
1430d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard                pick = i;
1440d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard            }
1450d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        }
1460d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
1470d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        if (!mDidAddPoint && (delta * getWidth() > 100)
148d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard                && (spline.getNbPoints() < 10)) {
1490d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard            return -1;
1500d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        }
1510d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
1523992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        return pick;
1530d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    }
1540d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
1550d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    private String getFilterName() {
1563992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        return "Curves";
1570d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    }
1580d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
1590d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    @Override
1600d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    public synchronized boolean onTouchEvent(MotionEvent e) {
1610d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        float posX = e.getX() / getWidth();
1623992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        float posY = e.getY();
1633992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        float margin = Spline.curveHandleSize() / 2;
1643992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        if (posY < margin) {
1653992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            posY = margin;
1663992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        }
1673992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        if (posY > getHeight() - margin) {
1683992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            posY = getHeight() - margin;
1693992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        }
1703992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        posY = (posY - margin) / (getHeight() - 2 * margin);
1710d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
1720d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        if (e.getActionMasked() == MotionEvent.ACTION_UP) {
1730d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard            mCurrentControlPoint = null;
174d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard            updateCachedImage();
1750d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard            mDidAddPoint = false;
1760d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard            if (mDidDelete) {
1770d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard                mDidDelete = false;
1780d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard            }
179c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard            mDoingTouchMove = false;
1800d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard            return true;
1810d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        }
182c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard        mDoingTouchMove = true;
1830d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
1840d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        if (mDidDelete) {
1850d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard            return true;
1860d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        }
1870d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
188d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard        Spline spline = getSpline(mCurrentCurveIndex);
1890d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        int pick = pickControlPoint(posX, posY);
1900d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        if (mCurrentControlPoint == null) {
1910d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard            if (pick == -1) {
1920d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard                mCurrentControlPoint = new ControlPoint(posX, posY);
193d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard                spline.addPoint(mCurrentControlPoint);
1940d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard                mDidAddPoint = true;
1950d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard            } else {
196d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard                mCurrentControlPoint = spline.getPoint(pick);
1970d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard            }
1980d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        }
1993992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard
200d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard        if (spline.isPointContained(posX, pick)) {
2013992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            mCurrentControlPoint.x = posX;
2023992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            mCurrentControlPoint.y = posY;
203c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard            spline.didMovePoint(mCurrentControlPoint);
204c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard        } else if (pick != -1 && spline.getNbPoints() > 2) {
205d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard            spline.deletePoint(pick);
2063992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            mDidDelete = true;
2070d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        }
208d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard        updateCachedImage();
2090d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        return true;
2100d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    }
2110d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
212d7899c56b8df278dfd6720ae11eadc2f89fe8094nicolasroard    public synchronized void updateCachedImage() {
2130d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        // update image
2140d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        if (getImagePreset() != null) {
2150d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard            mImageLoader.resetImageForPreset(getImagePreset(), this);
2160d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard            invalidate();
2170d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard        }
2180d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard    }
2190d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard
2203992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard    class ComputeHistogramTask extends AsyncTask<Bitmap, Void, int[]> {
2213992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        @Override
2223992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        protected int[] doInBackground(Bitmap... params) {
2233992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            int[] histo = new int[256 * 3];
2243992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            Bitmap bitmap = params[0];
2253992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            int w = bitmap.getWidth();
2263992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            int h = bitmap.getHeight();
2273992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            int[] pixels = new int[w * h];
2283992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            bitmap.getPixels(pixels, 0, w, 0, 0, w, h);
2293992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            for (int i = 0; i < w; i++) {
2303992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                for (int j = 0; j < h; j++) {
2313992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                    int index = j * w + i;
2323992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                    int r = Color.red(pixels[index]);
2333992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                    int g = Color.green(pixels[index]);
2343992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                    int b = Color.blue(pixels[index]);
2353992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                    histo[r]++;
2363992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                    histo[256 + g]++;
2373992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                    histo[512 + b]++;
2383992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                }
2393992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            }
2403992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            return histo;
2413992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        }
2423992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard
2433992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        @Override
2443992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        protected void onPostExecute(int[] result) {
2453992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            System.arraycopy(result, 0, redHistogram, 0, 256);
2463992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            System.arraycopy(result, 256, greenHistogram, 0, 256);
2473992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            System.arraycopy(result, 512, blueHistogram, 0, 256);
2483992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            invalidate();
2493992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        }
2503992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard    }
2513992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard
2523992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard    private void drawHistogram(Canvas canvas, int[] histogram, int color, PorterDuff.Mode mode) {
2533992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        int max = 0;
2543992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        for (int i = 0; i < histogram.length; i++) {
2553992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            if (histogram[i] > max) {
2563992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                max = histogram[i];
2573992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            }
2583992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        }
2593992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        float w = getWidth();
2603992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        float h = getHeight();
2613992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        float wl = w / histogram.length;
2623992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        float wh = (0.3f * h) / max;
2633992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        Paint paint = new Paint();
2643992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        paint.setARGB(100, 255, 255, 255);
2653992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        paint.setStrokeWidth((int) Math.ceil(wl));
2663992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard
2673992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        Paint paint2 = new Paint();
2683992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        paint2.setColor(color);
2693992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        paint2.setStrokeWidth(6);
2703992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        paint2.setXfermode(new PorterDuffXfermode(mode));
2713992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        gHistoPath.reset();
2723992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        gHistoPath.moveTo(0, h);
2733992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        boolean firstPointEncountered = false;
2743992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        float prev = 0;
2753992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        float last = 0;
2763992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        for (int i = 0; i < histogram.length; i++) {
2773992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            float x = i * wl;
2783992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            float l = histogram[i] * wh;
2793992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            if (l != 0) {
2803992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                float v = h - (l + prev) / 2.0f;
2813992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                if (!firstPointEncountered) {
2823992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                    gHistoPath.lineTo(x, h);
2833992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                    firstPointEncountered = true;
2843992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                }
2853992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                gHistoPath.lineTo(x, v);
2863992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                prev = l;
2873992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard                last = x;
2883992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard            }
2893992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        }
2903992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        gHistoPath.lineTo(last, h);
2913992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        gHistoPath.lineTo(w, h);
2923992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        gHistoPath.close();
2933992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        canvas.drawPath(gHistoPath, paint2);
2943992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        paint2.setStrokeWidth(2);
2953992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        paint2.setStyle(Paint.Style.STROKE);
2963992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        paint2.setARGB(255, 200, 200, 200);
2973992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard        canvas.drawPath(gHistoPath, paint2);
2983992ae6cf1cbb7b57a22d8287c40451558408335nicolasroard    }
299c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard
300c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard    public void setChannel(int itemId) {
301c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard        switch (itemId) {
302c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard            case R.id.curve_menu_rgb: {
303c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard                mCurrentCurveIndex = Spline.RGB;
304c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard                break;
305c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard            }
306c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard            case R.id.curve_menu_red: {
307c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard                mCurrentCurveIndex = Spline.RED;
308c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard                break;
309c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard            }
310c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard            case R.id.curve_menu_green: {
311c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard                mCurrentCurveIndex = Spline.GREEN;
312c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard                break;
313c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard            }
314c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard            case R.id.curve_menu_blue: {
315c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard                mCurrentCurveIndex = Spline.BLUE;
316c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard                break;
317c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard            }
318c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard        }
319c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard        invalidate();
320c5590eb1a20b112e67e4c43684790587f844fc6bnicolasroard    }
3210d7cdf8e763fb65c32bfad65245b3753deb75737nicolasroard}
322