blob: 706adc809ab9b37d8c3087e8d793b55f85839942 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.assetstudiolib;
import com.android.resources.Density;
import com.google.common.io.Closeables;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.imageio.ImageIO;
/**
* The base Generator class.
*/
public abstract class GraphicGenerator {
/**
* Options used for all generators.
*/
public static class Options {
/** Minimum version (API level) of the SDK to generate icons for */
public int minSdk = 1;
/** Source image to use as a basis for the icon */
public BufferedImage sourceImage;
/** The density to generate the icon with */
public Density density = Density.XHIGH;
}
/** Shapes that can be used for icon backgrounds */
public static enum Shape {
/** No background */
NONE("none"),
/** Circular background */
CIRCLE("circle"),
/** Square background */
SQUARE("square");
/** Id, used in filenames to identify associated stencils */
public final String id;
Shape(String id) {
this.id = id;
}
}
/** Foreground effects styles */
public static enum Style {
/** No effects */
SIMPLE("fore1");
/** Id, used in filenames to identify associated stencils */
public final String id;
Style(String id) {
this.id = id;
}
}
/**
* Generate a single icon using the given options
*
* @param context render context to use for looking up resources etc
* @param options options controlling the appearance of the icon
* @return a {@link BufferedImage} with the generated icon
*/
public abstract BufferedImage generate(GraphicGeneratorContext context, Options options);
/**
* Computes the target filename (relative to the Android project folder)
* where an icon rendered with the given options should be stored. This is
* also used as the map keys in the result map used by
* {@link #generate(String, Map, GraphicGeneratorContext, Options, String)}.
*
* @param options the options object used by the generator for the current
* image
* @param name the base name to use when creating the path
* @return a path relative to the res/ folder where the image should be
* stored (will always use / as a path separator, not \ on Windows)
*/
protected String getIconPath(Options options, String name) {
return getIconFolder(options) + '/' + getIconName(options, name);
}
/**
* Gets name of the file itself. It is sometimes modified by options, for
* example in unselected tabs we change foo.png to foo-unselected.png
*/
protected String getIconName(Options options, String name) {
return name + ".png"; //$NON-NLS-1$
}
/**
* Gets name of the folder to contain the resource. It usually includes the
* density, but is also sometimes modified by options. For example, in some
* notification icons we add in -v9 or -v11.
*/
protected String getIconFolder(Options options) {
return "res/drawable-" + options.density.getResourceValue(); //$NON-NLS-1$
}
/**
* Generates a full set of icons into the given map. The values in the map
* will be the generated images, and each value is keyed by the
* corresponding relative path of the image, which is determined by the
* {@link #getIconPath(Options, String)} method.
*
* @param category the current category to place images into (if null the
* density name will be used)
* @param categoryMap the map to put images into, should not be null. The
* map is a map from a category name, to a map from file path to
* image.
* @param context a generator context which for example can load resources
* @param options options to apply to this generator
* @param name the base name of the icons to generate
*/
public void generate(String category, Map<String, Map<String, BufferedImage>> categoryMap,
GraphicGeneratorContext context, Options options, String name) {
Density[] densityValues = Density.values();
// Sort density values into ascending order
Arrays.sort(densityValues, new Comparator<Density>() {
@Override
public int compare(Density d1, Density d2) {
return d1.getDpiValue() - d2.getDpiValue();
}
});
for (Density density : densityValues) {
if (!density.isValidValueForDevice()) {
continue;
}
if (density == Density.LOW || density == Density.TV ||
(density == Density.XXHIGH && !(this instanceof LauncherIconGenerator))) {
// TODO don't manually check and instead gracefully handle missing stencils.
// Not yet supported -- missing stencil image
continue;
}
options.density = density;
BufferedImage image = generate(context, options);
if (image != null) {
String mapCategory = category;
if (mapCategory == null) {
mapCategory = options.density.getResourceValue();
}
Map<String, BufferedImage> imageMap = categoryMap.get(mapCategory);
if (imageMap == null) {
imageMap = new LinkedHashMap<String, BufferedImage>();
categoryMap.put(mapCategory, imageMap);
}
imageMap.put(getIconPath(options, name), image);
}
}
}
/**
* Returns the scale factor to apply for a given MDPI density to compute the
* absolute pixel count to use to draw an icon of the given target density
*
* @param density the density
* @return a factor to multiple mdpi distances with to compute the target density
*/
public static float getMdpiScaleFactor(Density density) {
return density.getDpiValue() / (float) Density.MEDIUM.getDpiValue();
}
/**
* Returns one of the built in stencil images, or null
*
* @param relativePath stencil path such as "launcher-stencil/square/web/back.png"
* @return the image, or null
* @throws IOException if an unexpected I/O error occurs
*/
@SuppressWarnings("resource") // Eclipse doesn't know about Closeables#closeQuietly yet
public static BufferedImage getStencilImage(String relativePath) throws IOException {
InputStream is = GraphicGenerator.class.getResourceAsStream(relativePath);
try {
return ImageIO.read(is);
} finally {
Closeables.closeQuietly(is);
}
}
/**
* Returns the icon (32x32) for a given clip art image.
*
* @param name the name of the image to be loaded (which can be looked up via
* {@link #getClipartNames()})
* @return the icon image
* @throws IOException if the image cannot be loaded
*/
@SuppressWarnings("resource") // Eclipse doesn't know about Closeables#closeQuietly yet
public static BufferedImage getClipartIcon(String name) throws IOException {
InputStream is = GraphicGenerator.class.getResourceAsStream(
"/images/clipart/small/" + name);
try {
return ImageIO.read(is);
} finally {
Closeables.closeQuietly(is);
}
}
/**
* Returns the full size clip art image for a given image name.
*
* @param name the name of the image to be loaded (which can be looked up via
* {@link #getClipartNames()})
* @return the clip art image
* @throws IOException if the image cannot be loaded
*/
@SuppressWarnings("resource") // Eclipse doesn't know about Closeables#closeQuietly yet
public static BufferedImage getClipartImage(String name) throws IOException {
InputStream is = GraphicGenerator.class.getResourceAsStream(
"/images/clipart/big/" + name);
try {
return ImageIO.read(is);
} finally {
Closeables.closeQuietly(is);
}
}
/**
* Returns the names of available clip art images which can be obtained by passing the
* name to {@link #getClipartIcon(String)} or
* {@link GraphicGenerator#getClipartImage(String)}
*
* @return an iterator for the available image names
*/
public static Iterator<String> getClipartNames() {
List<String> names = new ArrayList<String>(80);
try {
String pathPrefix = "images/clipart/big/"; //$NON-NLS-1$
ProtectionDomain protectionDomain = GraphicGenerator.class.getProtectionDomain();
URL url = protectionDomain.getCodeSource().getLocation();
File file;
try {
file = new File(url.toURI());
} catch (URISyntaxException e) {
file = new File(url.getPath());
}
final ZipFile zipFile = new JarFile(file);
Enumeration<? extends ZipEntry> enumeration = zipFile.entries();
while (enumeration.hasMoreElements()) {
ZipEntry zipEntry = enumeration.nextElement();
String name = zipEntry.getName();
if (!name.startsWith(pathPrefix) || !name.endsWith(".png")) { //$NON-NLS-1$
continue;
}
int lastSlash = name.lastIndexOf('/');
if (lastSlash != -1) {
name = name.substring(lastSlash + 1);
}
names.add(name);
}
} catch (final Exception e) {
e.printStackTrace();
}
return names.iterator();
}
}