blob: c4ca900e17304bc253e43c10fde9b0b94166b3ab [file] [log] [blame]
/*
* 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.ui;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.view.MotionEvent;
import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.filters.ImageFilterCurves;
import com.android.gallery3d.filtershow.imageshow.ImageSlave;
import com.android.gallery3d.filtershow.presets.ImagePreset;
public class ImageCurves extends ImageSlave {
private static final String LOGTAG = "ImageCurves";
Paint gPaint = new Paint();
Path gPathSpline = new Path();
private int mCurrentCurveIndex = Spline.RGB;
private boolean mDidAddPoint = false;
private boolean mDidDelete = false;
private ControlPoint mCurrentControlPoint = null;
private ImagePreset mLastPreset = null;
int[] redHistogram = new int[256];
int[] greenHistogram = new int[256];
int[] blueHistogram = new int[256];
Path gHistoPath = new Path();
boolean mDoingTouchMove = false;
public ImageCurves(Context context) {
super(context);
resetCurve();
}
public ImageCurves(Context context, AttributeSet attrs) {
super(context, attrs);
resetCurve();
}
public void nextChannel() {
mCurrentCurveIndex = ((mCurrentCurveIndex + 1) % 4);
invalidate();
}
@Override
public boolean showTitle() {
return false;
}
private ImageFilterCurves curves() {
if (getMaster() != null) {
String filterName = getFilterName();
return (ImageFilterCurves) getImagePreset().getFilter(filterName);
}
return null;
}
private Spline getSpline(int index) {
return curves().getSpline(index);
}
@Override
public void resetParameter() {
super.resetParameter();
resetCurve();
mLastPreset = null;
invalidate();
}
public void resetCurve() {
if (getMaster() != null && curves() != null) {
curves().reset();
updateCachedImage();
}
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
gPaint.setAntiAlias(true);
if (getImagePreset() != mLastPreset && getFilteredImage() != null) {
new ComputeHistogramTask().execute(getFilteredImage());
mLastPreset = getImagePreset();
}
if (curves() == null) {
return;
}
if (mCurrentCurveIndex == Spline.RGB || mCurrentCurveIndex == Spline.RED) {
drawHistogram(canvas, redHistogram, Color.RED, PorterDuff.Mode.SCREEN);
}
if (mCurrentCurveIndex == Spline.RGB || mCurrentCurveIndex == Spline.GREEN) {
drawHistogram(canvas, greenHistogram, Color.GREEN, PorterDuff.Mode.SCREEN);
}
if (mCurrentCurveIndex == Spline.RGB || mCurrentCurveIndex == Spline.BLUE) {
drawHistogram(canvas, blueHistogram, Color.BLUE, PorterDuff.Mode.SCREEN);
}
// We only display the other channels curves when showing the RGB curve
if (mCurrentCurveIndex == Spline.RGB) {
for (int i = 0; i < 4; i++) {
Spline spline = getSpline(i);
if (i != mCurrentCurveIndex && !spline.isOriginal()) {
// And we only display a curve if it has more than two
// points
spline.draw(canvas, Spline.colorForCurve(i), getWidth(),
getHeight(), false, mDoingTouchMove);
}
}
}
// ...but we always display the current curve.
getSpline(mCurrentCurveIndex)
.draw(canvas, Spline.colorForCurve(mCurrentCurveIndex), getWidth(), getHeight(),
true, mDoingTouchMove);
drawToast(canvas);
}
private int pickControlPoint(float x, float y) {
int pick = 0;
Spline spline = getSpline(mCurrentCurveIndex);
float px = spline.getPoint(0).x;
float py = spline.getPoint(0).y;
double delta = Math.sqrt((px - x) * (px - x) + (py - y) * (py - y));
for (int i = 1; i < spline.getNbPoints(); i++) {
px = spline.getPoint(i).x;
py = spline.getPoint(i).y;
double currentDelta = Math.sqrt((px - x) * (px - x) + (py - y)
* (py - y));
if (currentDelta < delta) {
delta = currentDelta;
pick = i;
}
}
if (!mDidAddPoint && (delta * getWidth() > 100)
&& (spline.getNbPoints() < 10)) {
return -1;
}
return pick;
}
private String getFilterName() {
return "Curves";
}
@Override
public synchronized boolean onTouchEvent(MotionEvent e) {
float posX = e.getX() / getWidth();
float posY = e.getY();
float margin = Spline.curveHandleSize() / 2;
if (posY < margin) {
posY = margin;
}
if (posY > getHeight() - margin) {
posY = getHeight() - margin;
}
posY = (posY - margin) / (getHeight() - 2 * margin);
if (e.getActionMasked() == MotionEvent.ACTION_UP) {
mCurrentControlPoint = null;
updateCachedImage();
mDidAddPoint = false;
if (mDidDelete) {
mDidDelete = false;
}
mDoingTouchMove = false;
return true;
}
mDoingTouchMove = true;
if (mDidDelete) {
return true;
}
if (curves() == null) {
return true;
}
Spline spline = getSpline(mCurrentCurveIndex);
int pick = pickControlPoint(posX, posY);
if (mCurrentControlPoint == null) {
if (pick == -1) {
mCurrentControlPoint = new ControlPoint(posX, posY);
pick = spline.addPoint(mCurrentControlPoint);
mDidAddPoint = true;
} else {
mCurrentControlPoint = spline.getPoint(pick);
}
}
if (spline.isPointContained(posX, pick)) {
spline.didMovePoint(mCurrentControlPoint);
spline.movePoint(pick, posX, posY);
} else if (pick != -1 && spline.getNbPoints() > 2) {
spline.deletePoint(pick);
mDidDelete = true;
}
updateCachedImage();
invalidate();
return true;
}
public synchronized void updateCachedImage() {
// update image
if (getImagePreset() != null) {
resetImageCaches(this);
invalidate();
}
}
class ComputeHistogramTask extends AsyncTask<Bitmap, Void, int[]> {
@Override
protected int[] doInBackground(Bitmap... params) {
int[] histo = new int[256 * 3];
Bitmap bitmap = params[0];
int w = bitmap.getWidth();
int h = bitmap.getHeight();
int[] pixels = new int[w * h];
bitmap.getPixels(pixels, 0, w, 0, 0, w, h);
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
int index = j * w + i;
int r = Color.red(pixels[index]);
int g = Color.green(pixels[index]);
int b = Color.blue(pixels[index]);
histo[r]++;
histo[256 + g]++;
histo[512 + b]++;
}
}
return histo;
}
@Override
protected void onPostExecute(int[] result) {
System.arraycopy(result, 0, redHistogram, 0, 256);
System.arraycopy(result, 256, greenHistogram, 0, 256);
System.arraycopy(result, 512, blueHistogram, 0, 256);
invalidate();
}
}
private void drawHistogram(Canvas canvas, int[] histogram, int color, PorterDuff.Mode mode) {
int max = 0;
for (int i = 0; i < histogram.length; i++) {
if (histogram[i] > max) {
max = histogram[i];
}
}
float w = getWidth();
float h = getHeight();
float wl = w / histogram.length;
float wh = (0.3f * h) / max;
Paint paint = new Paint();
paint.setARGB(100, 255, 255, 255);
paint.setStrokeWidth((int) Math.ceil(wl));
Paint paint2 = new Paint();
paint2.setColor(color);
paint2.setStrokeWidth(6);
paint2.setXfermode(new PorterDuffXfermode(mode));
gHistoPath.reset();
gHistoPath.moveTo(0, h);
boolean firstPointEncountered = false;
float prev = 0;
float last = 0;
for (int i = 0; i < histogram.length; i++) {
float x = i * wl;
float l = histogram[i] * wh;
if (l != 0) {
float v = h - (l + prev) / 2.0f;
if (!firstPointEncountered) {
gHistoPath.lineTo(x, h);
firstPointEncountered = true;
}
gHistoPath.lineTo(x, v);
prev = l;
last = x;
}
}
gHistoPath.lineTo(last, h);
gHistoPath.lineTo(w, h);
gHistoPath.close();
canvas.drawPath(gHistoPath, paint2);
paint2.setStrokeWidth(2);
paint2.setStyle(Paint.Style.STROKE);
paint2.setARGB(255, 200, 200, 200);
canvas.drawPath(gHistoPath, paint2);
}
public void setChannel(int itemId) {
switch (itemId) {
case R.id.curve_menu_rgb: {
mCurrentCurveIndex = Spline.RGB;
break;
}
case R.id.curve_menu_red: {
mCurrentCurveIndex = Spline.RED;
break;
}
case R.id.curve_menu_green: {
mCurrentCurveIndex = Spline.GREEN;
break;
}
case R.id.curve_menu_blue: {
mCurrentCurveIndex = Spline.BLUE;
break;
}
}
invalidate();
}
}