Home | History | Annotate | Download | only in assetstudiolib
      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 package com.android.assetstudiolib;
     18 
     19 import com.android.resources.Density;
     20 
     21 import java.awt.image.BufferedImage;
     22 import java.io.File;
     23 import java.io.IOException;
     24 import java.io.InputStream;
     25 import java.net.URISyntaxException;
     26 import java.net.URL;
     27 import java.security.ProtectionDomain;
     28 import java.util.ArrayList;
     29 import java.util.Arrays;
     30 import java.util.Comparator;
     31 import java.util.Enumeration;
     32 import java.util.Iterator;
     33 import java.util.LinkedHashMap;
     34 import java.util.List;
     35 import java.util.Map;
     36 import java.util.jar.JarFile;
     37 import java.util.zip.ZipEntry;
     38 import java.util.zip.ZipFile;
     39 
     40 import javax.imageio.ImageIO;
     41 
     42 /**
     43  * The base Generator class.
     44  */
     45 public abstract class GraphicGenerator {
     46     /**
     47      * Options used for all generators.
     48      */
     49     public static class Options {
     50         /** Minimum version (API level) of the SDK to generate icons for */
     51         public int minSdk = 1;
     52 
     53         /** Source image to use as a basis for the icon */
     54         public BufferedImage sourceImage;
     55 
     56         /** The density to generate the icon with */
     57         public Density density = Density.XHIGH;
     58     }
     59 
     60     /** Shapes that can be used for icon backgrounds */
     61     public static enum Shape {
     62         /** Circular background */
     63         CIRCLE("circle"),
     64         /** Square background */
     65         SQUARE("square");
     66 
     67         /** Id, used in filenames to identify associated stencils */
     68         public final String id;
     69 
     70         Shape(String id) {
     71             this.id = id;
     72         }
     73     }
     74 
     75     /** Foreground effects styles */
     76     public static enum Style {
     77         /** No effects */
     78         SIMPLE("fore1"),
     79         /** "Fancy" effects */
     80         FANCY("fore2"),
     81         /** A glossy look */
     82         GLOSSY("fore3");
     83 
     84         /** Id, used in filenames to identify associated stencils */
     85         public final String id;
     86 
     87         Style(String id) {
     88             this.id = id;
     89         }
     90     }
     91 
     92     /**
     93      * Generate a single icon using the given options
     94      *
     95      * @param context render context to use for looking up resources etc
     96      * @param options options controlling the appearance of the icon
     97      * @return a {@link BufferedImage} with the generated icon
     98      */
     99     public abstract BufferedImage generate(GraphicGeneratorContext context, Options options);
    100 
    101     /**
    102      * Computes the target filename (relative to the Android project folder)
    103      * where an icon rendered with the given options should be stored. This is
    104      * also used as the map keys in the result map used by
    105      * {@link #generate(String, Map, GraphicGeneratorContext, Options, String)}.
    106      *
    107      * @param options the options object used by the generator for the current
    108      *            image
    109      * @param name the base name to use when creating the path
    110      * @return a path relative to the res/ folder where the image should be
    111      *         stored (will always use / as a path separator, not \ on Windows)
    112      */
    113     protected String getIconPath(Options options, String name) {
    114         return getIconFolder(options) + '/' + getIconName(options, name);
    115     }
    116 
    117     /**
    118      * Gets name of the file itself. It is sometimes modified by options, for
    119      * example in unselected tabs we change foo.png to foo-unselected.png
    120      */
    121     protected String getIconName(Options options, String name) {
    122         return name + ".png"; //$NON-NLS-1$
    123     }
    124 
    125     /**
    126      * Gets name of the folder to contain the resource. It usually includes the
    127      * density, but is also sometimes modified by options. For example, in some
    128      * notification icons we add in -v9 or -v11.
    129      */
    130     protected String getIconFolder(Options options) {
    131         return "res/drawable-" + options.density.getResourceValue(); //$NON-NLS-1$
    132     }
    133 
    134     /**
    135      * Generates a full set of icons into the given map. The values in the map
    136      * will be the generated images, and each value is keyed by the
    137      * corresponding relative path of the image, which is determined by the
    138      * {@link #getIconPath(Options, String)} method.
    139      *
    140      * @param category the current category to place images into (if null the
    141      *            density name will be used)
    142      * @param categoryMap the map to put images into, should not be null. The
    143      *            map is a map from a category name, to a map from file path to
    144      *            image.
    145      * @param context a generator context which for example can load resources
    146      * @param options options to apply to this generator
    147      * @param name the base name of the icons to generate
    148      */
    149     public void generate(String category, Map<String, Map<String, BufferedImage>> categoryMap,
    150             GraphicGeneratorContext context, Options options, String name) {
    151         Density[] densityValues = Density.values();
    152         // Sort density values into ascending order
    153         Arrays.sort(densityValues, new Comparator<Density>() {
    154             public int compare(Density d1, Density d2) {
    155                 return d1.getDpiValue() - d2.getDpiValue();
    156             }
    157         });
    158 
    159         for (Density density : densityValues) {
    160             if (!density.isValidValueForDevice()) {
    161                 continue;
    162             }
    163             if (density == Density.TV) {
    164                 // Not yet supported -- missing stencil image
    165                 continue;
    166             }
    167             options.density = density;
    168             BufferedImage image = generate(context, options);
    169             if (image != null) {
    170                 String mapCategory = category;
    171                 if (mapCategory == null) {
    172                     mapCategory = options.density.getResourceValue();
    173                 }
    174                 Map<String, BufferedImage> imageMap = categoryMap.get(mapCategory);
    175                 if (imageMap == null) {
    176                     imageMap = new LinkedHashMap<String, BufferedImage>();
    177                     categoryMap.put(mapCategory, imageMap);
    178                 }
    179                 imageMap.put(getIconPath(options, name), image);
    180             }
    181         }
    182     }
    183 
    184     /**
    185      * Returns the scale factor to apply for a given HDPI density to compute the
    186      * absolute pixel count to use to draw an icon of the given target density
    187      *
    188      * @param density the density
    189      * @return a factor to multiple hdpi distances with to compute the target density
    190      */
    191     public static float getHdpiScaleFactor(Density density) {
    192         // We used to do this:
    193         //return density.getDpiValue() / (float) Density.DEFAULT_DENSITY;
    194         // However, the HTML5 version of the AssetStudio would end up with different
    195         // sizes for the assets, because it uses this table:
    196         //    studio.util.getMultBaseHdpi = function(density) {
    197         //        switch (density) {
    198         //          case 'xhdpi': return 1.333333;
    199         //          case  'hdpi': return 1.0;
    200         //          case  'mdpi': return 0.666667;
    201         //          case  'ldpi': return 0.5;
    202         //        }
    203         //        return 1.0;
    204         //      };
    205         // This corresponds to dividing the dpi value not by Density.MEDIUM but
    206         // Density.HIGH:
    207         return density.getDpiValue() / (float) Density.HIGH.getDpiValue();
    208     }
    209 
    210     /**
    211      * Returns one of the built in stencil images, or null
    212      *
    213      * @param relativePath stencil path such as "launcher-stencil/square/web/back.png"
    214      * @return the image, or null
    215      * @throws IOException if an unexpected I/O error occurs
    216      */
    217     public static BufferedImage getStencilImage(String relativePath) throws IOException {
    218         InputStream is = GraphicGenerator.class.getResourceAsStream(relativePath);
    219         return ImageIO.read(is);
    220     }
    221 
    222     /**
    223      * Returns the icon (32x32) for a given clip art image.
    224      *
    225      * @param name the name of the image to be loaded (which can be looked up via
    226      *            {@link #getClipartNames()})
    227      * @return the icon image
    228      * @throws IOException if the image cannot be loaded
    229      */
    230     public static BufferedImage getClipartIcon(String name) throws IOException {
    231         InputStream is = GraphicGenerator.class.getResourceAsStream(
    232                 "/images/clipart/small/" + name);
    233         return ImageIO.read(is);
    234     }
    235 
    236     /**
    237      * Returns the full size clip art image for a given image name.
    238      *
    239      * @param name the name of the image to be loaded (which can be looked up via
    240      *            {@link #getClipartNames()})
    241      * @return the clip art image
    242      * @throws IOException if the image cannot be loaded
    243      */
    244     public static BufferedImage getClipartImage(String name) throws IOException {
    245         InputStream is = GraphicGenerator.class.getResourceAsStream(
    246                 "/images/clipart/big/" + name);
    247         return ImageIO.read(is);
    248     }
    249 
    250     /**
    251      * Returns the names of available clip art images which can be obtained by passing the
    252      * name to {@link #getClipartIcon(String)} or
    253      * {@link GraphicGenerator#getClipartImage(String)}
    254      *
    255      * @return an iterator for the available image names
    256      */
    257     public static Iterator<String> getClipartNames() {
    258         List<String> names = new ArrayList<String>(80);
    259         try {
    260             String pathPrefix = "images/clipart/big/"; //$NON-NLS-1$
    261             ProtectionDomain protectionDomain = GraphicGenerator.class.getProtectionDomain();
    262             URL url = protectionDomain.getCodeSource().getLocation();
    263             File file;
    264             try {
    265                 file = new File(url.toURI());
    266             } catch (URISyntaxException e) {
    267                 file = new File(url.getPath());
    268             }
    269             final ZipFile zipFile = new JarFile(file);
    270             Enumeration<? extends ZipEntry> enumeration = zipFile.entries();
    271             while (enumeration.hasMoreElements()) {
    272                 ZipEntry zipEntry = enumeration.nextElement();
    273                 String name = zipEntry.getName();
    274                 if (!name.startsWith(pathPrefix) || !name.endsWith(".png")) { //$NON-NLS-1$
    275                     continue;
    276                 }
    277 
    278                 int lastSlash = name.lastIndexOf('/');
    279                 if (lastSlash != -1) {
    280                     name = name.substring(lastSlash + 1);
    281                 }
    282                 names.add(name);
    283             }
    284         } catch (final Exception e) {
    285             e.printStackTrace();
    286         }
    287 
    288         return names.iterator();
    289     }
    290 }
    291