• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
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 package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
17 
18 import com.android.ide.common.api.Rect;
19 
20 import org.eclipse.swt.SWTException;
21 import org.eclipse.swt.graphics.Image;
22 import org.eclipse.swt.graphics.ImageData;
23 import org.eclipse.swt.graphics.PaletteData;
24 import org.eclipse.swt.graphics.Rectangle;
25 import org.eclipse.swt.widgets.Display;
26 
27 import java.awt.Graphics;
28 import java.awt.image.BufferedImage;
29 import java.awt.image.DataBuffer;
30 import java.awt.image.DataBufferByte;
31 import java.awt.image.DataBufferInt;
32 import java.awt.image.WritableRaster;
33 import java.util.List;
34 
35 /**
36  * Various generic SWT utilities such as image conversion.
37  */
38 public class SwtUtils {
39 
SwtUtils()40     private SwtUtils() {
41     }
42 
43     /**
44      * Returns the {@link PaletteData} describing the ARGB ordering expected from integers
45      * representing pixels for AWT {@link BufferedImage}.
46      *
47      * @param imageType the {@link BufferedImage#getType()} type
48      * @return A new {@link PaletteData} suitable for AWT images.
49      */
getAwtPaletteData(int imageType)50     public static PaletteData getAwtPaletteData(int imageType) {
51         switch (imageType) {
52             case BufferedImage.TYPE_INT_RGB:
53             case BufferedImage.TYPE_INT_ARGB:
54             case BufferedImage.TYPE_INT_ARGB_PRE:
55                 return new PaletteData(0x00FF0000, 0x0000FF00, 0x000000FF);
56 
57             case BufferedImage.TYPE_3BYTE_BGR:
58             case BufferedImage.TYPE_4BYTE_ABGR:
59             case BufferedImage.TYPE_4BYTE_ABGR_PRE:
60                 return new PaletteData(0x000000FF, 0x0000FF00, 0x00FF0000);
61 
62             default:
63                 throw new UnsupportedOperationException("RGB type not supported yet.");
64         }
65     }
66 
67     /**
68      * Returns true if the given type of {@link BufferedImage} is supported for
69      * conversion. For unsupported formats, use
70      * {@link #convertToCompatibleFormat(BufferedImage)} first.
71      *
72      * @param imageType the {@link BufferedImage#getType()}
73      * @return true if we can convert the given buffered image format
74      */
isSupportedPaletteType(int imageType)75     private static boolean isSupportedPaletteType(int imageType) {
76         switch (imageType) {
77             case BufferedImage.TYPE_INT_RGB:
78             case BufferedImage.TYPE_INT_ARGB:
79             case BufferedImage.TYPE_INT_ARGB_PRE:
80             case BufferedImage.TYPE_3BYTE_BGR:
81             case BufferedImage.TYPE_4BYTE_ABGR:
82             case BufferedImage.TYPE_4BYTE_ABGR_PRE:
83                 return true;
84             default:
85                 return false;
86         }
87     }
88 
89     /** Converts the given arbitrary {@link BufferedImage} to another {@link BufferedImage}
90      * in a format that is supported (see {@link #isSupportedPaletteType(int)})
91      *
92      * @param image the image to be converted
93      * @return a new image that is in a guaranteed compatible format
94      */
convertToCompatibleFormat(BufferedImage image)95     private static BufferedImage convertToCompatibleFormat(BufferedImage image) {
96         BufferedImage converted = new BufferedImage(image.getWidth(), image.getHeight(),
97                 BufferedImage.TYPE_INT_ARGB);
98         Graphics graphics = converted.getGraphics();
99         graphics.drawImage(image, 0, 0, null);
100         graphics.dispose();
101 
102         return converted;
103     }
104 
105     /**
106      * Converts an AWT {@link BufferedImage} into an equivalent SWT {@link Image}. Whether
107      * the transparency data is transferred is optional, and this method can also apply an
108      * alpha adjustment during the conversion.
109      * <p/>
110      * Implementation details: on Windows, the returned {@link Image} will have an ordering
111      * matching the Windows DIB (e.g. RGBA, not ARGB). Callers must make sure to use
112      * <code>Image.getImageData().paletteData</code> to get the right pixels out of the image.
113      *
114      * @param display The display where the SWT image will be shown
115      * @param awtImage The AWT {@link BufferedImage}
116      * @param transferAlpha If true, copy alpha data out of the source image
117      * @param globalAlpha If -1, do nothing, otherwise adjust the alpha of the final image
118      *            by the given amount in the range [0,255]
119      * @return A new SWT {@link Image} with the same contents as the source
120      *         {@link BufferedImage}
121      */
convertToSwt(Display display, BufferedImage awtImage, boolean transferAlpha, int globalAlpha)122     public static Image convertToSwt(Display display, BufferedImage awtImage,
123             boolean transferAlpha, int globalAlpha) {
124         if (!isSupportedPaletteType(awtImage.getType())) {
125             awtImage = convertToCompatibleFormat(awtImage);
126         }
127 
128         int width = awtImage.getWidth();
129         int height = awtImage.getHeight();
130 
131         WritableRaster raster = awtImage.getRaster();
132         DataBuffer dataBuffer = raster.getDataBuffer();
133         ImageData imageData =
134             new ImageData(width, height, 32, getAwtPaletteData(awtImage.getType()));
135 
136         if (dataBuffer instanceof DataBufferInt) {
137             int[] imageDataBuffer = ((DataBufferInt) dataBuffer).getData();
138             imageData.setPixels(0, 0, imageDataBuffer.length, imageDataBuffer, 0);
139         } else if (dataBuffer instanceof DataBufferByte) {
140             byte[] imageDataBuffer = ((DataBufferByte) dataBuffer).getData();
141             try {
142                 imageData.setPixels(0, 0, imageDataBuffer.length, imageDataBuffer, 0);
143             } catch (SWTException se) {
144                 // Unsupported depth
145                 return convertToSwt(display, convertToCompatibleFormat(awtImage),
146                         transferAlpha, globalAlpha);
147             }
148         }
149 
150         if (transferAlpha) {
151             byte[] alphaData = new byte[height * width];
152             for (int y = 0; y < height; y++) {
153                 byte[] alphaRow = new byte[width];
154                 for (int x = 0; x < width; x++) {
155                     int alpha = awtImage.getRGB(x, y) >>> 24;
156 
157                     // We have to multiply in the alpha now since if we
158                     // set ImageData.alpha, it will ignore the alphaData.
159                     if (globalAlpha != -1) {
160                         alpha = alpha * globalAlpha >> 8;
161                     }
162 
163                     alphaRow[x] = (byte) alpha;
164                 }
165                 System.arraycopy(alphaRow, 0, alphaData, y * width, width);
166             }
167 
168             imageData.alphaData = alphaData;
169         } else if (globalAlpha != -1) {
170             imageData.alpha = globalAlpha;
171         }
172 
173         return new Image(display, imageData);
174     }
175 
176     /**
177      * Converts a direct-color model SWT image to an equivalent AWT image. If the image
178      * does not have a supported color model, returns null. This method does <b>NOT</b>
179      * preserve alpha in the source image.
180      *
181      * @param swtImage the SWT image to be converted to AWT
182      * @return an AWT image representing the source SWT image
183      */
convertToAwt(Image swtImage)184     public static BufferedImage convertToAwt(Image swtImage) {
185         ImageData swtData = swtImage.getImageData();
186         BufferedImage awtImage =
187             new BufferedImage(swtData.width, swtData.height, BufferedImage.TYPE_INT_ARGB);
188         PaletteData swtPalette = swtData.palette;
189         if (swtPalette.isDirect) {
190             PaletteData awtPalette = getAwtPaletteData(awtImage.getType());
191 
192             if (swtPalette.equals(awtPalette)) {
193                 // No color conversion needed.
194                 for (int y = 0; y < swtData.height; y++) {
195                     for (int x = 0; x < swtData.width; x++) {
196                       int pixel = swtData.getPixel(x, y);
197                       awtImage.setRGB(x, y, 0xFF000000 | pixel);
198                     }
199                 }
200             } else {
201                 // We need to remap the colors
202                 int sr = -awtPalette.redShift   + swtPalette.redShift;
203                 int sg = -awtPalette.greenShift + swtPalette.greenShift;
204                 int sb = -awtPalette.blueShift  + swtPalette.blueShift;
205 
206                 for (int y = 0; y < swtData.height; y++) {
207                     for (int x = 0; x < swtData.width; x++) {
208                       int pixel = swtData.getPixel(x, y);
209 
210                       int r = pixel & swtPalette.redMask;
211                       int g = pixel & swtPalette.greenMask;
212                       int b = pixel & swtPalette.blueMask;
213                       r = (sr < 0) ? r >>> -sr : r << sr;
214                       g = (sg < 0) ? g >>> -sg : g << sg;
215                       b = (sb < 0) ? b >>> -sb : b << sb;
216 
217                       pixel = 0xFF000000 | r | g | b;
218                       awtImage.setRGB(x, y, pixel);
219                     }
220                 }
221             }
222         } else {
223             return null;
224         }
225 
226         return awtImage;
227     }
228 
229     /**
230      * Creates a new image from a source image where the contents from a given set of
231      * bounding boxes are copied into the new image and the rest is left transparent. A
232      * scale can be applied to make the resulting image larger or smaller than the source
233      * image. Note that the alpha channel in the original image is ignored, and the alpha
234      * values for the painted rectangles will be set to a specific value passed into this
235      * function.
236      *
237      * @param image the source image
238      * @param rectangles the set of rectangles (bounding boxes) to copy from the source
239      *            image
240      * @param boundingBox the bounding rectangle of the rectangle list, which can be
241      *            computed by {@link ImageUtils#getBoundingRectangle}
242      * @param scale a scale factor to apply to the result, e.g. 0.5 to shrink the
243      *            destination down 50%, 1.0 to leave it alone and 2.0 to zoom in by
244      *            doubling the image size
245      * @param alpha the alpha (in the range 0-255) that painted bits should be set to
246      * @return a pair of the rendered cropped image, and the location within the source
247      *         image that the crop begins (multiplied by the scale). May return null if
248      *         there are no selected items.
249      */
drawRectangles(Image image, List<Rectangle> rectangles, Rectangle boundingBox, double scale, byte alpha)250     public static Image drawRectangles(Image image,
251             List<Rectangle> rectangles, Rectangle boundingBox, double scale, byte alpha) {
252 
253         if (rectangles.size() == 0 || boundingBox == null || boundingBox.isEmpty()) {
254             return null;
255         }
256 
257         ImageData srcData = image.getImageData();
258         int destWidth = (int) (scale * boundingBox.width);
259         int destHeight = (int) (scale * boundingBox.height);
260 
261         ImageData destData = new ImageData(destWidth, destHeight, srcData.depth, srcData.palette);
262         byte[] alphaData = new byte[destHeight * destWidth];
263         destData.alphaData = alphaData;
264 
265         for (Rectangle bounds : rectangles) {
266             int dx1 = bounds.x - boundingBox.x;
267             int dy1 = bounds.y - boundingBox.y;
268             int dx2 = dx1 + bounds.width;
269             int dy2 = dy1 + bounds.height;
270 
271             dx1 *= scale;
272             dy1 *= scale;
273             dx2 *= scale;
274             dy2 *= scale;
275 
276             int sx1 = bounds.x;
277             int sy1 = bounds.y;
278             int sx2 = sx1 + bounds.width;
279             int sy2 = sy1 + bounds.height;
280 
281             if (scale == 1.0d) {
282                 for (int dy = dy1, sy = sy1; dy < dy2; dy++, sy++) {
283                     for (int dx = dx1, sx = sx1; dx < dx2; dx++, sx++) {
284                         destData.setPixel(dx, dy, srcData.getPixel(sx, sy));
285                         alphaData[dy * destWidth + dx] = alpha;
286                     }
287                 }
288             } else {
289                 // Scaled copy.
290                 int sxDelta = sx2 - sx1;
291                 int dxDelta = dx2 - dx1;
292                 int syDelta = sy2 - sy1;
293                 int dyDelta = dy2 - dy1;
294                 for (int dy = dy1, sy = sy1; dy < dy2; dy++, sy = (dy - dy1) * syDelta / dyDelta
295                         + sy1) {
296                     for (int dx = dx1, sx = sx1; dx < dx2; dx++, sx = (dx - dx1) * sxDelta
297                             / dxDelta + sx1) {
298                         assert sx < sx2 && sy < sy2;
299                         destData.setPixel(dx, dy, srcData.getPixel(sx, sy));
300                         alphaData[dy * destWidth + dx] = alpha;
301                     }
302                 }
303             }
304         }
305 
306         return new Image(image.getDevice(), destData);
307     }
308 
309     /**
310      * Creates a new empty/blank image of the given size
311      *
312      * @param display the display to associate the image with
313      * @param width the width of the image
314      * @param height the height of the image
315      * @return a new blank image of the given size
316      */
317     public static Image createEmptyImage(Display display, int width, int height) {
318         BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
319         return SwtUtils.convertToSwt(display, image, false, 0);
320     }
321 
322     /**
323      * Converts the given SWT {@link Rectangle} into an ADT {@link Rect}
324      *
325      * @param swtRect the SWT {@link Rectangle}
326      * @return an equivalent {@link Rect}
327      */
328     public static Rect toRect(Rectangle swtRect) {
329         return new Rect(swtRect.x, swtRect.y, swtRect.width, swtRect.height);
330     }
331 
332     /**
333      * Sets the values of the given ADT {@link Rect} to the values of the given SWT
334      * {@link Rectangle}
335      *
336      * @param target the ADT {@link Rect} to modify
337      * @param source the SWT {@link Rectangle} to read values from
338      */
339     public static void set(Rect target, Rectangle source) {
340         target.set(source.x, source.y, source.width, source.height);
341     }
342 
343     /**
344      * Compares an ADT {@link Rect} with an SWT {@link Rectangle} and returns true if they
345      * are equivalent
346      *
347      * @param r1 the ADT {@link Rect}
348      * @param r2 the SWT {@link Rectangle}
349      * @return true if the two rectangles are equivalent
350      */
351     public static boolean equals(Rect r1, Rectangle r2) {
352         return r1.x == r2.x && r1.y == r2.y && r1.w == r2.width && r1.h == r2.height;
353 
354     }
355 }
356