• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Shape(String id)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 
Style(String id)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      */
generate(GraphicGeneratorContext context, Options options)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      */
getIconPath(Options options, String name)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      */
getIconName(Options options, String name)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      */
getIconFolder(Options options)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      */
generate(String category, Map<String, Map<String, BufferedImage>> categoryMap, GraphicGeneratorContext context, Options options, String name)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             @Override
155             public int compare(Density d1, Density d2) {
156                 return d1.getDpiValue() - d2.getDpiValue();
157             }
158         });
159 
160         for (Density density : densityValues) {
161             if (!density.isValidValueForDevice()) {
162                 continue;
163             }
164             if (density == Density.TV) {
165                 // Not yet supported -- missing stencil image
166                 continue;
167             }
168             options.density = density;
169             BufferedImage image = generate(context, options);
170             if (image != null) {
171                 String mapCategory = category;
172                 if (mapCategory == null) {
173                     mapCategory = options.density.getResourceValue();
174                 }
175                 Map<String, BufferedImage> imageMap = categoryMap.get(mapCategory);
176                 if (imageMap == null) {
177                     imageMap = new LinkedHashMap<String, BufferedImage>();
178                     categoryMap.put(mapCategory, imageMap);
179                 }
180                 imageMap.put(getIconPath(options, name), image);
181             }
182         }
183     }
184 
185     /**
186      * Returns the scale factor to apply for a given MDPI density to compute the
187      * absolute pixel count to use to draw an icon of the given target density
188      *
189      * @param density the density
190      * @return a factor to multiple mdpi distances with to compute the target density
191      */
getMdpiScaleFactor(Density density)192     public static float getMdpiScaleFactor(Density density) {
193         return density.getDpiValue() / (float) Density.MEDIUM.getDpiValue();
194     }
195 
196     /**
197      * Returns one of the built in stencil images, or null
198      *
199      * @param relativePath stencil path such as "launcher-stencil/square/web/back.png"
200      * @return the image, or null
201      * @throws IOException if an unexpected I/O error occurs
202      */
getStencilImage(String relativePath)203     public static BufferedImage getStencilImage(String relativePath) throws IOException {
204         InputStream is = GraphicGenerator.class.getResourceAsStream(relativePath);
205         return ImageIO.read(is);
206     }
207 
208     /**
209      * Returns the icon (32x32) for a given clip art image.
210      *
211      * @param name the name of the image to be loaded (which can be looked up via
212      *            {@link #getClipartNames()})
213      * @return the icon image
214      * @throws IOException if the image cannot be loaded
215      */
getClipartIcon(String name)216     public static BufferedImage getClipartIcon(String name) throws IOException {
217         InputStream is = GraphicGenerator.class.getResourceAsStream(
218                 "/images/clipart/small/" + name);
219         return ImageIO.read(is);
220     }
221 
222     /**
223      * Returns the full size clip art image for a given image name.
224      *
225      * @param name the name of the image to be loaded (which can be looked up via
226      *            {@link #getClipartNames()})
227      * @return the clip art image
228      * @throws IOException if the image cannot be loaded
229      */
getClipartImage(String name)230     public static BufferedImage getClipartImage(String name) throws IOException {
231         InputStream is = GraphicGenerator.class.getResourceAsStream(
232                 "/images/clipart/big/" + name);
233         return ImageIO.read(is);
234     }
235 
236     /**
237      * Returns the names of available clip art images which can be obtained by passing the
238      * name to {@link #getClipartIcon(String)} or
239      * {@link GraphicGenerator#getClipartImage(String)}
240      *
241      * @return an iterator for the available image names
242      */
getClipartNames()243     public static Iterator<String> getClipartNames() {
244         List<String> names = new ArrayList<String>(80);
245         try {
246             String pathPrefix = "images/clipart/big/"; //$NON-NLS-1$
247             ProtectionDomain protectionDomain = GraphicGenerator.class.getProtectionDomain();
248             URL url = protectionDomain.getCodeSource().getLocation();
249             File file;
250             try {
251                 file = new File(url.toURI());
252             } catch (URISyntaxException e) {
253                 file = new File(url.getPath());
254             }
255             final ZipFile zipFile = new JarFile(file);
256             Enumeration<? extends ZipEntry> enumeration = zipFile.entries();
257             while (enumeration.hasMoreElements()) {
258                 ZipEntry zipEntry = enumeration.nextElement();
259                 String name = zipEntry.getName();
260                 if (!name.startsWith(pathPrefix) || !name.endsWith(".png")) { //$NON-NLS-1$
261                     continue;
262                 }
263 
264                 int lastSlash = name.lastIndexOf('/');
265                 if (lastSlash != -1) {
266                     name = name.substring(lastSlash + 1);
267                 }
268                 names.add(name);
269             }
270         } catch (final Exception e) {
271             e.printStackTrace();
272         }
273 
274         return names.iterator();
275     }
276 }
277