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.filters; 18 19 import android.graphics.Bitmap; 20 import android.graphics.BitmapFactory; 21 import android.graphics.Canvas; 22 import android.graphics.Color; 23 import android.graphics.Matrix; 24 import android.graphics.Paint; 25 import android.graphics.Paint.Style; 26 import android.graphics.Path; 27 import android.graphics.PathMeasure; 28 import android.graphics.PorterDuff; 29 import android.graphics.PorterDuffColorFilter; 30 31 import com.android.gallery3d.R; 32 import com.android.gallery3d.app.Log; 33 import com.android.gallery3d.filtershow.cache.ImageLoader; 34 import com.android.gallery3d.filtershow.filters.FilterDrawRepresentation.StrokeData; 35 import com.android.gallery3d.filtershow.imageshow.MasterImage; 36 import com.android.gallery3d.filtershow.pipeline.FilterEnvironment; 37 38 import java.util.Vector; 39 40 public class ImageFilterDraw extends ImageFilter { 41 private static final String LOGTAG = "ImageFilterDraw"; 42 public final static byte SIMPLE_STYLE = 0; 43 public final static byte BRUSH_STYLE_SPATTER = 1; 44 public final static byte BRUSH_STYLE_MARKER = 2; 45 public final static int NUMBER_OF_STYLES = 3; 46 Bitmap mOverlayBitmap; // this accelerates interaction 47 int mCachedStrokes = -1; 48 int mCurrentStyle = 0; 49 50 FilterDrawRepresentation mParameters = new FilterDrawRepresentation(); 51 52 public ImageFilterDraw() { 53 mName = "Image Draw"; 54 } 55 56 DrawStyle[] mDrawingsTypes = new DrawStyle[] { 57 new SimpleDraw(0), 58 new SimpleDraw(1), 59 new Brush(R.drawable.brush_gauss), 60 new Brush(R.drawable.brush_marker), 61 new Brush(R.drawable.brush_spatter) 62 }; 63 { 64 for (int i = 0; i < mDrawingsTypes.length; i++) { 65 mDrawingsTypes[i].setType((byte) i); 66 } 67 68 } 69 70 @Override 71 public FilterRepresentation getDefaultRepresentation() { 72 return new FilterDrawRepresentation(); 73 } 74 75 @Override 76 public void useRepresentation(FilterRepresentation representation) { 77 FilterDrawRepresentation parameters = (FilterDrawRepresentation) representation; 78 mParameters = parameters; 79 } 80 81 public void setStyle(byte style) { 82 mCurrentStyle = style % mDrawingsTypes.length; 83 } 84 85 public int getStyle() { 86 return mCurrentStyle; 87 } 88 89 public static interface DrawStyle { 90 public void setType(byte type); 91 public void paint(FilterDrawRepresentation.StrokeData sd, Canvas canvas, Matrix toScrMatrix, 92 int quality); 93 } 94 95 class SimpleDraw implements DrawStyle { 96 byte mType; 97 int mMode; 98 99 public SimpleDraw(int mode) { 100 mMode = mode; 101 } 102 103 @Override 104 public void setType(byte type) { 105 mType = type; 106 } 107 108 @Override 109 public void paint(FilterDrawRepresentation.StrokeData sd, Canvas canvas, Matrix toScrMatrix, 110 int quality) { 111 if (sd == null) { 112 return; 113 } 114 if (sd.mPath == null) { 115 return; 116 } 117 Paint paint = new Paint(); 118 119 paint.setStyle(Style.STROKE); 120 if (mMode == 0) { 121 paint.setStrokeCap(Paint.Cap.SQUARE); 122 } else { 123 paint.setStrokeCap(Paint.Cap.ROUND); 124 } 125 paint.setAntiAlias(true); 126 paint.setColor(sd.mColor); 127 paint.setStrokeWidth(toScrMatrix.mapRadius(sd.mRadius)); 128 129 // done this way because of a bug in path.transform(matrix) 130 Path mCacheTransPath = new Path(); 131 mCacheTransPath.addPath(sd.mPath, toScrMatrix); 132 133 canvas.drawPath(mCacheTransPath, paint); 134 } 135 } 136 137 class Brush implements DrawStyle { 138 int mBrushID; 139 Bitmap mBrush; 140 byte mType; 141 142 public Brush(int brushID) { 143 mBrushID = brushID; 144 } 145 146 public Bitmap getBrush() { 147 if (mBrush == null) { 148 BitmapFactory.Options opt = new BitmapFactory.Options(); 149 opt.inPreferredConfig = Bitmap.Config.ALPHA_8; 150 mBrush = BitmapFactory.decodeResource(MasterImage.getImage().getActivity() 151 .getResources(), mBrushID, opt); 152 mBrush = mBrush.extractAlpha(); 153 } 154 return mBrush; 155 } 156 157 @Override 158 public void paint(FilterDrawRepresentation.StrokeData sd, Canvas canvas, 159 Matrix toScrMatrix, 160 int quality) { 161 if (sd == null || sd.mPath == null) { 162 return; 163 } 164 Paint paint = new Paint(); 165 paint.setStyle(Style.STROKE); 166 paint.setAntiAlias(true); 167 Path mCacheTransPath = new Path(); 168 mCacheTransPath.addPath(sd.mPath, toScrMatrix); 169 draw(canvas, paint, sd.mColor, toScrMatrix.mapRadius(sd.mRadius) * 2, 170 mCacheTransPath); 171 } 172 173 public Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) 174 { 175 Matrix m = new Matrix(); 176 m.setScale(dstWidth / (float) src.getWidth(), dstHeight / (float) src.getHeight()); 177 Bitmap result = Bitmap.createBitmap(dstWidth, dstHeight, src.getConfig()); 178 Canvas canvas = new Canvas(result); 179 180 Paint paint = new Paint(); 181 paint.setFilterBitmap(filter); 182 canvas.drawBitmap(src, m, paint); 183 184 return result; 185 186 } 187 void draw(Canvas canvas, Paint paint, int color, float size, Path path) { 188 PathMeasure mPathMeasure = new PathMeasure(); 189 float[] mPosition = new float[2]; 190 float[] mTan = new float[2]; 191 192 mPathMeasure.setPath(path, false); 193 194 paint.setAntiAlias(true); 195 paint.setColor(color); 196 197 paint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)); 198 Bitmap brush; 199 // done this way because of a bug in 200 // Bitmap.createScaledBitmap(getBrush(),(int) size,(int) size,true); 201 brush = createScaledBitmap(getBrush(), (int) size, (int) size, true); 202 float len = mPathMeasure.getLength(); 203 float s2 = size / 2; 204 float step = s2 / 8; 205 for (float i = 0; i < len; i += step) { 206 mPathMeasure.getPosTan(i, mPosition, mTan); 207 // canvas.drawCircle(pos[0], pos[1], size, paint); 208 canvas.drawBitmap(brush, mPosition[0] - s2, mPosition[1] - s2, paint); 209 } 210 } 211 212 @Override 213 public void setType(byte type) { 214 mType = type; 215 } 216 } 217 218 void paint(FilterDrawRepresentation.StrokeData sd, Canvas canvas, Matrix toScrMatrix, 219 int quality) { 220 mDrawingsTypes[sd.mType].paint(sd, canvas, toScrMatrix, quality); 221 } 222 223 public void drawData(Canvas canvas, Matrix originalRotateToScreen, int quality) { 224 Paint paint = new Paint(); 225 if (quality == FilterEnvironment.QUALITY_FINAL) { 226 paint.setAntiAlias(true); 227 } 228 paint.setStyle(Style.STROKE); 229 paint.setColor(Color.RED); 230 paint.setStrokeWidth(40); 231 232 if (mParameters.getDrawing().isEmpty() && mParameters.getCurrentDrawing() == null) { 233 mOverlayBitmap = null; 234 mCachedStrokes = -1; 235 return; 236 } 237 if (quality == FilterEnvironment.QUALITY_FINAL) { 238 239 for (FilterDrawRepresentation.StrokeData strokeData : mParameters.getDrawing()) { 240 paint(strokeData, canvas, originalRotateToScreen, quality); 241 } 242 return; 243 } 244 245 if (mOverlayBitmap == null || 246 mOverlayBitmap.getWidth() != canvas.getWidth() || 247 mOverlayBitmap.getHeight() != canvas.getHeight() || 248 mParameters.getDrawing().size() < mCachedStrokes) { 249 250 mOverlayBitmap = Bitmap.createBitmap( 251 canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888); 252 mCachedStrokes = 0; 253 } 254 255 if (mCachedStrokes < mParameters.getDrawing().size()) { 256 fillBuffer(originalRotateToScreen); 257 } 258 canvas.drawBitmap(mOverlayBitmap, 0, 0, paint); 259 260 StrokeData stroke = mParameters.getCurrentDrawing(); 261 if (stroke != null) { 262 paint(stroke, canvas, originalRotateToScreen, quality); 263 } 264 } 265 266 public void fillBuffer(Matrix originalRotateToScreen) { 267 Canvas drawCache = new Canvas(mOverlayBitmap); 268 Vector<FilterDrawRepresentation.StrokeData> v = mParameters.getDrawing(); 269 int n = v.size(); 270 271 for (int i = mCachedStrokes; i < n; i++) { 272 paint(v.get(i), drawCache, originalRotateToScreen, FilterEnvironment.QUALITY_PREVIEW); 273 } 274 mCachedStrokes = n; 275 } 276 277 public void draw(Canvas canvas, Matrix originalRotateToScreen) { 278 for (FilterDrawRepresentation.StrokeData strokeData : mParameters.getDrawing()) { 279 paint(strokeData, canvas, originalRotateToScreen, FilterEnvironment.QUALITY_PREVIEW); 280 } 281 mDrawingsTypes[mCurrentStyle].paint( 282 null, canvas, originalRotateToScreen, FilterEnvironment.QUALITY_PREVIEW); 283 } 284 285 @Override 286 public Bitmap apply(Bitmap bitmap, float scaleFactor, int quality) { 287 int w = bitmap.getWidth(); 288 int h = bitmap.getHeight(); 289 290 Matrix m = getOriginalToScreenMatrix(w, h); 291 drawData(new Canvas(bitmap), m, quality); 292 return bitmap; 293 } 294 295 } 296