Home | History | Annotate | Download | only in effect
      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 
    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      */
    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      */
    462     public static boolean isEffectSupported(String effectName) {
    463         return getEffectClassByName(effectName) != null;
    464     }
    465 
    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 
    487     private Effect instantiateEffect(Class effectClass, String name) {
    488         // Make sure this is an Effect subclass
    489         try {
    490             effectClass.asSubclass(Effect.class);
    491         } catch (ClassCastException e) {
    492             throw new IllegalArgumentException("Attempting to allocate effect '" + effectClass
    493                 + "' which is not a subclass of Effect!", e);
    494         }
    495 
    496         // Look for the correct constructor
    497         Constructor effectConstructor = null;
    498         try {
    499             effectConstructor = effectClass.getConstructor(EffectContext.class, String.class);
    500         } catch (NoSuchMethodException e) {
    501             throw new RuntimeException("The effect class '" + effectClass + "' does not have "
    502                 + "the required constructor.", e);
    503         }
    504 
    505         // Construct the effect
    506         Effect effect = null;
    507         try {
    508             effect = (Effect)effectConstructor.newInstance(mEffectContext, name);
    509         } catch (Throwable t) {
    510             throw new RuntimeException("There was an error constructing the effect '" + effectClass
    511                 + "'!", t);
    512         }
    513 
    514         return effect;
    515     }
    516 }
    517