1 /* 2 * Copyright (C) 2011 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 18 package android.media.effect; 19 20 import java.lang.reflect.Constructor; 21 22 /** 23 * <p>The EffectFactory class defines the list of available Effects, and provides functionality to 24 * inspect and instantiate them. Some effects may not be available on all platforms, so before 25 * creating a certain effect, the application should confirm that the effect is supported on this 26 * platform by calling {@link #isEffectSupported(String)}.</p> 27 */ 28 public class EffectFactory { 29 30 private EffectContext mEffectContext; 31 32 private final static String[] EFFECT_PACKAGES = { 33 "android.media.effect.effects.", // Default effect package 34 "" // Allows specifying full class path 35 }; 36 37 /** List of Effects */ 38 /** 39 * <p>Copies the input texture to the output.</p> 40 * <p>Available parameters: None</p> 41 * @hide 42 */ 43 public final static String EFFECT_IDENTITY = "IdentityEffect"; 44 45 /** 46 * <p>Adjusts the brightness of the image.</p> 47 * <p>Available parameters:</p> 48 * <table> 49 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 50 * <tr><td><code>brightness</code></td> 51 * <td>The brightness multiplier.</td> 52 * <td>Positive float. 1.0 means no change; 53 larger values will increase brightness.</td> 54 * </tr> 55 * </table> 56 */ 57 public final static String EFFECT_BRIGHTNESS = 58 "android.media.effect.effects.BrightnessEffect"; 59 60 /** 61 * <p>Adjusts the contrast of the image.</p> 62 * <p>Available parameters:</p> 63 * <table> 64 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 65 * <tr><td><code>contrast</code></td> 66 * <td>The contrast multiplier.</td> 67 * <td>Float. 1.0 means no change; 68 larger values will increase contrast.</td> 69 * </tr> 70 * </table> 71 */ 72 public final static String EFFECT_CONTRAST = 73 "android.media.effect.effects.ContrastEffect"; 74 75 /** 76 * <p>Applies a fisheye lens distortion to the image.</p> 77 * <p>Available parameters:</p> 78 * <table> 79 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 80 * <tr><td><code>scale</code></td> 81 * <td>The scale of the distortion.</td> 82 * <td>Float, between 0 and 1. Zero means no distortion.</td> 83 * </tr> 84 * </table> 85 */ 86 public final static String EFFECT_FISHEYE = 87 "android.media.effect.effects.FisheyeEffect"; 88 89 /** 90 * <p>Replaces the background of the input frames with frames from a 91 * selected video. Requires an initial learning period with only the 92 * background visible before the effect becomes active. The effect will wait 93 * until it does not see any motion in the scene before learning the 94 * background and starting the effect.</p> 95 * 96 * <p>Available parameters:</p> 97 * <table> 98 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 99 * <tr><td><code>source</code></td> 100 * <td>A URI for the background video to use. This parameter must be 101 * supplied before calling apply() for the first time.</td> 102 * <td>String, such as from 103 * {@link android.net.Uri#toString Uri.toString()}</td> 104 * </tr> 105 * </table> 106 * 107 * <p>If the update listener is set for this effect using 108 * {@link Effect#setUpdateListener}, it will be called when the effect has 109 * finished learning the background, with a null value for the info 110 * parameter.</p> 111 */ 112 public final static String EFFECT_BACKDROPPER = 113 "android.media.effect.effects.BackDropperEffect"; 114 115 /** 116 * <p>Attempts to auto-fix the image based on histogram equalization.</p> 117 * <p>Available parameters:</p> 118 * <table> 119 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 120 * <tr><td><code>scale</code></td> 121 * <td>The scale of the adjustment.</td> 122 * <td>Float, between 0 and 1. Zero means no adjustment, while 1 indicates the maximum 123 * amount of adjustment.</td> 124 * </tr> 125 * </table> 126 */ 127 public final static String EFFECT_AUTOFIX = 128 "android.media.effect.effects.AutoFixEffect"; 129 130 /** 131 * <p>Adjusts the range of minimal and maximal color pixel intensities.</p> 132 * <p>Available parameters:</p> 133 * <table> 134 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 135 * <tr><td><code>black</code></td> 136 * <td>The value of the minimal pixel.</td> 137 * <td>Float, between 0 and 1.</td> 138 * </tr> 139 * <tr><td><code>white</code></td> 140 * <td>The value of the maximal pixel.</td> 141 * <td>Float, between 0 and 1.</td> 142 * </tr> 143 * </table> 144 */ 145 public final static String EFFECT_BLACKWHITE = 146 "android.media.effect.effects.BlackWhiteEffect"; 147 148 /** 149 * <p>Crops an upright rectangular area from the image. If the crop region falls outside of 150 * the image bounds, the results are undefined.</p> 151 * <p>Available parameters:</p> 152 * <table> 153 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 154 * <tr><td><code>xorigin</code></td> 155 * <td>The origin's x-value.</td> 156 * <td>Integer, between 0 and width of the image.</td> 157 * </tr> 158 * <tr><td><code>yorigin</code></td> 159 * <td>The origin's y-value.</td> 160 * <td>Integer, between 0 and height of the image.</td> 161 * </tr> 162 * <tr><td><code>width</code></td> 163 * <td>The width of the cropped image.</td> 164 * <td>Integer, between 1 and the width of the image minus xorigin.</td> 165 * </tr> 166 * <tr><td><code>height</code></td> 167 * <td>The height of the cropped image.</td> 168 * <td>Integer, between 1 and the height of the image minus yorigin.</td> 169 * </tr> 170 * </table> 171 */ 172 public final static String EFFECT_CROP = 173 "android.media.effect.effects.CropEffect"; 174 175 /** 176 * <p>Applies a cross process effect on image, in which the red and green channels are 177 * enhanced while the blue channel is restricted.</p> 178 * <p>Available parameters: None</p> 179 */ 180 public final static String EFFECT_CROSSPROCESS = 181 "android.media.effect.effects.CrossProcessEffect"; 182 183 /** 184 * <p>Applies black and white documentary style effect on image..</p> 185 * <p>Available parameters: None</p> 186 */ 187 public final static String EFFECT_DOCUMENTARY = 188 "android.media.effect.effects.DocumentaryEffect"; 189 190 191 /** 192 * <p>Overlays a bitmap (with premultiplied alpha channel) onto the input image. The bitmap 193 * is stretched to fit the input image.</p> 194 * <p>Available parameters:</p> 195 * <table> 196 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 197 * <tr><td><code>bitmap</code></td> 198 * <td>The overlay bitmap.</td> 199 * <td>A non-null Bitmap instance.</td> 200 * </tr> 201 * </table> 202 */ 203 public final static String EFFECT_BITMAPOVERLAY = 204 "android.media.effect.effects.BitmapOverlayEffect"; 205 206 /** 207 * <p>Representation of photo using only two color tones.</p> 208 * <p>Available parameters:</p> 209 * <table> 210 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 211 * <tr><td><code>first_color</code></td> 212 * <td>The first color tone.</td> 213 * <td>Integer, representing an ARGB color with 8 bits per channel. May be created using 214 * {@link android.graphics.Color Color} class.</td> 215 * </tr> 216 * <tr><td><code>second_color</code></td> 217 * <td>The second color tone.</td> 218 * <td>Integer, representing an ARGB color with 8 bits per channel. May be created using 219 * {@link android.graphics.Color Color} class.</td> 220 * </tr> 221 * </table> 222 */ 223 public final static String EFFECT_DUOTONE = 224 "android.media.effect.effects.DuotoneEffect"; 225 226 /** 227 * <p>Applies back-light filling to the image.</p> 228 * <p>Available parameters:</p> 229 * <table> 230 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 231 * <tr><td><code>strength</code></td> 232 * <td>The strength of the backlight.</td> 233 * <td>Float, between 0 and 1. Zero means no change.</td> 234 * </tr> 235 * </table> 236 */ 237 public final static String EFFECT_FILLLIGHT = 238 "android.media.effect.effects.FillLightEffect"; 239 240 /** 241 * <p>Flips image vertically and/or horizontally.</p> 242 * <p>Available parameters:</p> 243 * <table> 244 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 245 * <tr><td><code>vertical</code></td> 246 * <td>Whether to flip image vertically.</td> 247 * <td>Boolean</td> 248 * </tr> 249 * <tr><td><code>horizontal</code></td> 250 * <td>Whether to flip image horizontally.</td> 251 * <td>Boolean</td> 252 * </tr> 253 * </table> 254 */ 255 public final static String EFFECT_FLIP = 256 "android.media.effect.effects.FlipEffect"; 257 258 /** 259 * <p>Applies film grain effect to image.</p> 260 * <p>Available parameters:</p> 261 * <table> 262 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 263 * <tr><td><code>strength</code></td> 264 * <td>The strength of the grain effect.</td> 265 * <td>Float, between 0 and 1. Zero means no change.</td> 266 * </tr> 267 * </table> 268 */ 269 public final static String EFFECT_GRAIN = 270 "android.media.effect.effects.GrainEffect"; 271 272 /** 273 * <p>Converts image to grayscale.</p> 274 * <p>Available parameters: None</p> 275 */ 276 public final static String EFFECT_GRAYSCALE = 277 "android.media.effect.effects.GrayscaleEffect"; 278 279 /** 280 * <p>Applies lomo-camera style effect to image.</p> 281 * <p>Available parameters: None</p> 282 */ 283 public final static String EFFECT_LOMOISH = 284 "android.media.effect.effects.LomoishEffect"; 285 286 /** 287 * <p>Inverts the image colors.</p> 288 * <p>Available parameters: None</p> 289 */ 290 public final static String EFFECT_NEGATIVE = 291 "android.media.effect.effects.NegativeEffect"; 292 293 /** 294 * <p>Applies posterization effect to image.</p> 295 * <p>Available parameters: None</p> 296 */ 297 public final static String EFFECT_POSTERIZE = 298 "android.media.effect.effects.PosterizeEffect"; 299 300 /** 301 * <p>Removes red eyes on specified region.</p> 302 * <p>Available parameters:</p> 303 * <table> 304 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 305 * <tr><td><code>centers</code></td> 306 * <td>Multiple center points (x, y) of the red eye regions.</td> 307 * <td>An array of floats, where (f[2*i], f[2*i+1]) specifies the center of the i'th eye. 308 * Coordinate values are expected to be normalized between 0 and 1.</td> 309 * </tr> 310 * </table> 311 */ 312 public final static String EFFECT_REDEYE = 313 "android.media.effect.effects.RedEyeEffect"; 314 315 /** 316 * <p>Rotates the image. The output frame size must be able to fit the rotated version of 317 * the input image. Note that the rotation snaps to a the closest multiple of 90 degrees.</p> 318 * <p>Available parameters:</p> 319 * <table> 320 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 321 * <tr><td><code>angle</code></td> 322 * <td>The angle of rotation in degrees.</td> 323 * <td>Integer value. This will be rounded to the nearest multiple of 90.</td> 324 * </tr> 325 * </table> 326 */ 327 public final static String EFFECT_ROTATE = 328 "android.media.effect.effects.RotateEffect"; 329 330 /** 331 * <p>Adjusts color saturation of image.</p> 332 * <p>Available parameters:</p> 333 * <table> 334 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 335 * <tr><td><code>scale</code></td> 336 * <td>The scale of color saturation.</td> 337 * <td>Float, between -1 and 1. 0 means no change, while -1 indicates full desaturation, 338 * i.e. grayscale.</td> 339 * </tr> 340 * </table> 341 */ 342 public final static String EFFECT_SATURATE = 343 "android.media.effect.effects.SaturateEffect"; 344 345 /** 346 * <p>Converts image to sepia tone.</p> 347 * <p>Available parameters: None</p> 348 */ 349 public final static String EFFECT_SEPIA = 350 "android.media.effect.effects.SepiaEffect"; 351 352 /** 353 * <p>Sharpens the image.</p> 354 * <p>Available parameters:</p> 355 * <table> 356 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 357 * <tr><td><code>scale</code></td> 358 * <td>The degree of sharpening.</td> 359 * <td>Float, between 0 and 1. 0 means no change.</td> 360 * </tr> 361 * </table> 362 */ 363 public final static String EFFECT_SHARPEN = 364 "android.media.effect.effects.SharpenEffect"; 365 366 /** 367 * <p>Rotates the image according to the specified angle, and crops the image so that no 368 * non-image portions are visible.</p> 369 * <p>Available parameters:</p> 370 * <table> 371 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 372 * <tr><td><code>angle</code></td> 373 * <td>The angle of rotation.</td> 374 * <td>Float, between -45 and +45.</td> 375 * </tr> 376 * </table> 377 */ 378 public final static String EFFECT_STRAIGHTEN = 379 "android.media.effect.effects.StraightenEffect"; 380 381 /** 382 * <p>Adjusts color temperature of the image.</p> 383 * <p>Available parameters:</p> 384 * <table> 385 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 386 * <tr><td><code>scale</code></td> 387 * <td>The value of color temperature.</td> 388 * <td>Float, between 0 and 1, with 0 indicating cool, and 1 indicating warm. A value of 389 * of 0.5 indicates no change.</td> 390 * </tr> 391 * </table> 392 */ 393 public final static String EFFECT_TEMPERATURE = 394 "android.media.effect.effects.ColorTemperatureEffect"; 395 396 /** 397 * <p>Tints the photo with specified color.</p> 398 * <p>Available parameters:</p> 399 * <table> 400 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 401 * <tr><td><code>tint</code></td> 402 * <td>The color of the tint.</td> 403 * <td>Integer, representing an ARGB color with 8 bits per channel. May be created using 404 * {@link android.graphics.Color Color} class.</td> 405 * </tr> 406 * </table> 407 */ 408 public final static String EFFECT_TINT = 409 "android.media.effect.effects.TintEffect"; 410 411 /** 412 * <p>Adds a vignette effect to image, i.e. fades away the outer image edges.</p> 413 * <p>Available parameters:</p> 414 * <table> 415 * <tr><td>Parameter name</td><td>Meaning</td><td>Valid values</td></tr> 416 * <tr><td><code>scale</code></td> 417 * <td>The scale of vignetting.</td> 418 * <td>Float, between 0 and 1. 0 means no change.</td> 419 * </tr> 420 * </table> 421 */ 422 public final static String EFFECT_VIGNETTE = 423 "android.media.effect.effects.VignetteEffect"; 424 EffectFactory(EffectContext effectContext)425 EffectFactory(EffectContext effectContext) { 426 mEffectContext = effectContext; 427 } 428 429 /** 430 * Instantiate a new effect with the given effect name. 431 * 432 * <p>The effect's parameters will be set to their default values.</p> 433 * 434 * <p>Note that the EGL context associated with the current EffectContext need not be made 435 * current when creating an effect. This allows the host application to instantiate effects 436 * before any EGL context has become current.</p> 437 * 438 * @param effectName The name of the effect to create. 439 * @return A new Effect instance. 440 * @throws IllegalArgumentException if the effect with the specified name is not supported or 441 * not known. 442 */ createEffect(String effectName)443 public Effect createEffect(String effectName) { 444 Class effectClass = getEffectClassByName(effectName); 445 if (effectClass == null) { 446 throw new IllegalArgumentException("Cannot instantiate unknown effect '" + 447 effectName + "'!"); 448 } 449 return instantiateEffect(effectClass, effectName); 450 } 451 452 /** 453 * Check if an effect is supported on this platform. 454 * 455 * <p>Some effects may only be available on certain platforms. Use this method before 456 * instantiating an effect to make sure it is supported.</p> 457 * 458 * @param effectName The name of the effect. 459 * @return true, if the effect is supported on this platform. 460 * @throws IllegalArgumentException if the effect name is not known. 461 */ isEffectSupported(String effectName)462 public static boolean isEffectSupported(String effectName) { 463 return getEffectClassByName(effectName) != null; 464 } 465 getEffectClassByName(String className)466 private static Class getEffectClassByName(String className) { 467 Class effectClass = null; 468 469 // Get context's classloader; otherwise cannot load non-framework effects 470 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); 471 472 // Look for the class in the imported packages 473 for (String packageName : EFFECT_PACKAGES) { 474 try { 475 effectClass = contextClassLoader.loadClass(packageName + className); 476 } catch (ClassNotFoundException e) { 477 continue; 478 } 479 // Exit loop if class was found. 480 if (effectClass != null) { 481 break; 482 } 483 } 484 return effectClass; 485 } 486 instantiateEffect(Class effectClass, String name)487 private Effect instantiateEffect(Class effectClass, String name) { 488 // Make sure this is an Effect subclass 489 if (!Effect.class.isAssignableFrom(effectClass)) { 490 throw new IllegalArgumentException("Attempting to allocate effect '" + effectClass 491 + "' which is not a subclass of Effect!"); 492 } 493 494 // Look for the correct constructor 495 Constructor effectConstructor = null; 496 try { 497 effectConstructor = effectClass.getConstructor(EffectContext.class, String.class); 498 } catch (NoSuchMethodException e) { 499 throw new RuntimeException("The effect class '" + effectClass + "' does not have " 500 + "the required constructor.", e); 501 } 502 503 // Construct the effect 504 Effect effect = null; 505 try { 506 effect = (Effect)effectConstructor.newInstance(mEffectContext, name); 507 } catch (Throwable t) { 508 throw new RuntimeException("There was an error constructing the effect '" + effectClass 509 + "'!", t); 510 } 511 512 return effect; 513 } 514 } 515