• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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.ddmuilib;
18 
19 import com.android.ddmlib.Log;
20 
21 import org.eclipse.jface.resource.ImageDescriptor;
22 import org.eclipse.swt.SWT;
23 import org.eclipse.swt.graphics.Color;
24 import org.eclipse.swt.graphics.GC;
25 import org.eclipse.swt.graphics.Image;
26 import org.eclipse.swt.widgets.Display;
27 
28 import java.io.InputStream;
29 import java.net.URL;
30 import java.util.HashMap;
31 
32 /**
33  * Class to load images stored in a jar file.
34  * All images are loaded from /images/<var>filename</var>
35  *
36  * Because Java requires to know the jar file in which to load the image from, a class is required
37  * when getting the instance. Instances are cached and associated to the class passed to
38  * {@link #getLoader(Class)}.
39  *
40  * {@link #getDdmUiLibLoader()} use {@link ImageLoader#getClass()} as the class. This is to be used
41  * to load images from ddmuilib.
42  *
43  * Loaded images are stored so that 2 calls with the same filename will return the same object.
44  * This also means that {@link Image} object returned by the loader should never be disposed.
45  *
46  */
47 public class ImageLoader {
48 
49     private static final String PATH = "/images/"; //$NON-NLS-1$
50 
51     private final HashMap<String, Image> mLoadedImages = new HashMap<String, Image>();
52     private static final HashMap<Class<?>, ImageLoader> mInstances =
53             new HashMap<Class<?>, ImageLoader>();
54     private final Class<?> mClass;
55 
56     /**
57      * Private constructor, creating an instance associated with a class.
58      * The class is used to identify which jar file the images are loaded from.
59      */
ImageLoader(Class<?> theClass)60     private ImageLoader(Class<?> theClass) {
61         if (theClass == null) {
62             theClass = ImageLoader.class;
63         }
64         mClass = theClass;
65     }
66 
67     /**
68      * Returns the {@link ImageLoader} instance to load images from ddmuilib.jar
69      */
getDdmUiLibLoader()70     public static ImageLoader getDdmUiLibLoader() {
71         return getLoader(null);
72     }
73 
74     /**
75      * Returns an {@link ImageLoader} to load images based on a given class.
76      *
77      * The loader will load images from the jar from which the class was loaded. using
78      * {@link Class#getResource(String)} and {@link Class#getResourceAsStream(String)}.
79      *
80      * Since all images are loaded using the path /images/<var>filename</var>, any class from the
81      * jar will work. However since the loader is cached and reused when the query provides the same
82      * class instance, and since the loader will also cache the loaded images, it is recommended
83      * to always use the same class for a given Jar file.
84      *
85      */
getLoader(Class<?> theClass)86     public static ImageLoader getLoader(Class<?> theClass) {
87         ImageLoader instance = mInstances.get(theClass);
88         if (instance == null) {
89             instance = new ImageLoader(theClass);
90             mInstances.put(theClass, instance);
91         }
92 
93         return instance;
94     }
95 
96     /**
97      * Disposes all images for all instances.
98      * This should only be called when the program exits.
99      */
dispose()100     public static void dispose() {
101         for (ImageLoader loader : mInstances.values()) {
102             loader.doDispose();
103         }
104     }
105 
doDispose()106     private synchronized void doDispose() {
107         for (Image image : mLoadedImages.values()) {
108             image.dispose();
109         }
110 
111         mLoadedImages.clear();
112     }
113 
114     /**
115      * Returns an {@link ImageDescriptor} for a given filename.
116      *
117      * This searches for an image located at /images/<var>filename</var>.
118      *
119      * @param filename the filename of the image to load.
120      */
loadDescriptor(String filename)121     public ImageDescriptor loadDescriptor(String filename) {
122         URL url = mClass.getResource(PATH + filename);
123         // TODO cache in a map
124         return ImageDescriptor.createFromURL(url);
125     }
126 
127     /**
128      * Returns an {@link Image} for a given filename.
129      *
130      * This searches for an image located at /images/<var>filename</var>.
131      *
132      * @param filename the filename of the image to load.
133      * @param display the Display object
134      */
loadImage(String filename, Display display)135     public synchronized Image loadImage(String filename, Display display) {
136         Image img = mLoadedImages.get(filename);
137         if (img == null) {
138             String tmp = PATH + filename;
139             InputStream imageStream = mClass.getResourceAsStream(tmp);
140 
141             if (imageStream != null) {
142                 img = new Image(display, imageStream);
143                 if (img == null) {
144                     throw new NullPointerException("couldn't load " + tmp);
145                 }
146 
147                 mLoadedImages.put(filename, img);
148 
149                 return img;
150             }
151         }
152 
153         return img;
154     }
155 
156     /**
157      * Loads an image from a resource. This method used a class to locate the
158      * resources, and then load the filename from /images inside the resources.<br>
159      * Extra parameters allows for creation of a replacement image of the
160      * loading failed.
161      *
162      * @param loader the image loader used.
163      * @param display the Display object
164      * @param fileName the file name
165      * @param width optional width to create replacement Image. If -1, null be
166      *            be returned if the loading fails.
167      * @param height optional height to create replacement Image. If -1, null be
168      *            be returned if the loading fails.
169      * @param phColor optional color to create replacement Image. If null, Blue
170      *            color will be used.
171      * @return a new Image or null if the loading failed and the optional
172      *         replacement size was -1
173      */
loadImage(Display display, String fileName, int width, int height, Color phColor)174     public Image loadImage(Display display, String fileName, int width, int height,
175             Color phColor) {
176 
177         Image img = loadImage(fileName, display);
178 
179         if (img == null) {
180             Log.w("ddms", "Couldn't load " + fileName);
181             // if we had the extra parameter to create replacement image then we
182             // create and return it.
183             if (width != -1 && height != -1) {
184                 return createPlaceHolderArt(display, width, height,
185                         phColor != null ? phColor : display
186                                 .getSystemColor(SWT.COLOR_BLUE));
187             }
188 
189             // otherwise, just return null
190             return null;
191         }
192 
193         return img;
194     }
195 
196     /**
197      * Create place-holder art with the specified color.
198      */
createPlaceHolderArt(Display display, int width, int height, Color color)199     public static Image createPlaceHolderArt(Display display, int width,
200             int height, Color color) {
201         Image img = new Image(display, width, height);
202         GC gc = new GC(img);
203         gc.setForeground(color);
204         gc.drawLine(0, 0, width, height);
205         gc.drawLine(0, height - 1, width, -1);
206         gc.dispose();
207         return img;
208     }
209 }
210