/* * 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.filters; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Path; import android.graphics.PathMeasure; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import com.android.gallery3d.R; import com.android.gallery3d.filtershow.filters.FilterDrawRepresentation.StrokeData; import com.android.gallery3d.filtershow.imageshow.MasterImage; import com.android.gallery3d.filtershow.presets.FilterEnvironment; import com.android.gallery3d.filtershow.presets.ImagePreset; import java.util.Vector; public class ImageFilterDraw extends ImageFilter { private static final String LOGTAG = "ImageFilterDraw"; public final static byte SIMPLE_STYLE = 0; public final static byte BRUSH_STYLE_SPATTER = 1; public final static byte BRUSH_STYLE_MARKER = 2; public final static int NUMBER_OF_STYLES = 3; Bitmap mOverlayBitmap; // this accelerates interaction int mCachedStrokes = -1; int mCurrentStyle = 0; FilterDrawRepresentation mParameters = new FilterDrawRepresentation(); public ImageFilterDraw() { mName = "Image Draw"; } DrawStyle[] mDrawingsTypes = new DrawStyle[] { new SimpleDraw(), new Brush(R.drawable.brush_marker), new Brush(R.drawable.brush_spatter) }; { for (int i = 0; i < mDrawingsTypes.length; i++) { mDrawingsTypes[i].setType((byte) i); } } @Override public FilterRepresentation getDefaultRepresentation() { return new FilterDrawRepresentation(); } @Override public void useRepresentation(FilterRepresentation representation) { FilterDrawRepresentation parameters = (FilterDrawRepresentation) representation; mParameters = parameters; } public void setStyle(byte style) { mCurrentStyle = style % mDrawingsTypes.length; } public int getStyle() { return mCurrentStyle; } public static interface DrawStyle { public void setType(byte type); public void paint(FilterDrawRepresentation.StrokeData sd, Canvas canvas, Matrix toScrMatrix, int quality); } class SimpleDraw implements DrawStyle { byte mType; @Override public void setType(byte type) { mType = type; } @Override public void paint(FilterDrawRepresentation.StrokeData sd, Canvas canvas, Matrix toScrMatrix, int quality) { if (sd == null) { return; } if (sd.mPath == null) { return; } Paint paint = new Paint(); paint.setStyle(Style.STROKE); paint.setColor(sd.mColor); paint.setStrokeWidth(toScrMatrix.mapRadius(sd.mRadius)); // done this way because of a bug in path.transform(matrix) Path mCacheTransPath = new Path(); mCacheTransPath.addPath(sd.mPath, toScrMatrix); canvas.drawPath(mCacheTransPath, paint); } } class Brush implements DrawStyle { int mBrushID; Bitmap mBrush; byte mType; public Brush(int brushID) { mBrushID = brushID; } public Bitmap getBrush() { if (mBrush == null) { BitmapFactory.Options opt = new BitmapFactory.Options(); opt.inPreferredConfig = Bitmap.Config.ALPHA_8; mBrush = MasterImage.getImage().getImageLoader().decodeImage(mBrushID, opt); mBrush = mBrush.extractAlpha(); } return mBrush; } @Override public void paint(FilterDrawRepresentation.StrokeData sd, Canvas canvas, Matrix toScrMatrix, int quality) { if (sd == null || sd.mPath == null) { return; } Paint paint = new Paint(); paint.setStyle(Style.STROKE); paint.setAntiAlias(true); Path mCacheTransPath = new Path(); mCacheTransPath.addPath(sd.mPath, toScrMatrix); draw(canvas, paint, sd.mColor, toScrMatrix.mapRadius(sd.mRadius) * 2, mCacheTransPath); } public Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) { Matrix m = new Matrix(); m.setScale(dstWidth / (float) src.getWidth(), dstHeight / (float) src.getHeight()); Bitmap result = Bitmap.createBitmap(dstWidth, dstHeight, src.getConfig()); Canvas canvas = new Canvas(result); Paint paint = new Paint(); paint.setFilterBitmap(filter); canvas.drawBitmap(src, m, paint); return result; } void draw(Canvas canvas, Paint paint, int color, float size, Path path) { PathMeasure mPathMeasure = new PathMeasure(); float[] mPosition = new float[2]; float[] mTan = new float[2]; mPathMeasure.setPath(path, false); paint.setAntiAlias(true); paint.setColor(color); paint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)); Bitmap brush; // done this way because of a bug in // Bitmap.createScaledBitmap(getBrush(),(int) size,(int) size,true); brush = createScaledBitmap(getBrush(), (int) size, (int) size, true); float len = mPathMeasure.getLength(); float s2 = size / 2; float step = s2 / 8; for (float i = 0; i < len; i += step) { mPathMeasure.getPosTan(i, mPosition, mTan); // canvas.drawCircle(pos[0], pos[1], size, paint); canvas.drawBitmap(brush, mPosition[0] - s2, mPosition[1] - s2, paint); } } @Override public void setType(byte type) { mType = type; } } void paint(FilterDrawRepresentation.StrokeData sd, Canvas canvas, Matrix toScrMatrix, int quality) { mDrawingsTypes[sd.mType].paint(sd, canvas, toScrMatrix, quality); } public void drawData(Canvas canvas, Matrix originalRotateToScreen, int quality) { Paint paint = new Paint(); if (quality == FilterEnvironment.QUALITY_FINAL) { paint.setAntiAlias(true); } paint.setStyle(Style.STROKE); paint.setColor(Color.RED); paint.setStrokeWidth(40); if (mParameters.getDrawing().isEmpty() && mParameters.getCurrentDrawing() == null) { return; } if (quality == FilterEnvironment.QUALITY_FINAL) { for (FilterDrawRepresentation.StrokeData strokeData : mParameters.getDrawing()) { paint(strokeData, canvas, originalRotateToScreen, quality); } return; } if (mOverlayBitmap == null || mOverlayBitmap.getWidth() != canvas.getWidth() || mOverlayBitmap.getHeight() != canvas.getHeight() || mParameters.getDrawing().size() < mCachedStrokes) { mOverlayBitmap = Bitmap.createBitmap( canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888); mCachedStrokes = 0; } if (mCachedStrokes < mParameters.getDrawing().size()) { fillBuffer(originalRotateToScreen); } canvas.drawBitmap(mOverlayBitmap, 0, 0, paint); StrokeData stroke = mParameters.getCurrentDrawing(); if (stroke != null) { paint(stroke, canvas, originalRotateToScreen, quality); } } public void fillBuffer(Matrix originalRotateToScreen) { Canvas drawCache = new Canvas(mOverlayBitmap); Vector v = mParameters.getDrawing(); int n = v.size(); for (int i = mCachedStrokes; i < n; i++) { paint(v.get(i), drawCache, originalRotateToScreen, FilterEnvironment.QUALITY_PREVIEW); } mCachedStrokes = n; } public void draw(Canvas canvas, Matrix originalRotateToScreen) { for (FilterDrawRepresentation.StrokeData strokeData : mParameters.getDrawing()) { paint(strokeData, canvas, originalRotateToScreen, FilterEnvironment.QUALITY_PREVIEW); } mDrawingsTypes[mCurrentStyle].paint( null, canvas, originalRotateToScreen, FilterEnvironment.QUALITY_PREVIEW); } @Override public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { int w = bitmap.getWidth(); int h = bitmap.getHeight(); Matrix m = getOriginalToScreenMatrix(w, h); drawData(new Canvas(bitmap), m, quality); return bitmap; } }