Home | History | Annotate | Download | only in gle2
      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 
     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      */
     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      */
     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      */
     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      */
    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      */
    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      */
    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