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