/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php * * 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.ide.eclipse.adt.internal.editors.layout.gle2; import com.android.ide.common.api.Rect; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.PaletteData; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Display; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; import java.awt.image.WritableRaster; import java.util.List; /** * Various generic SWT utilities such as image conversion. */ public class SwtUtils { private SwtUtils() { } /** * Returns the {@link PaletteData} describing the ARGB ordering expected from integers * representing pixels for AWT {@link BufferedImage}. * * @param imageType the {@link BufferedImage#getType()} type * @return A new {@link PaletteData} suitable for AWT images. */ public static PaletteData getAwtPaletteData(int imageType) { switch (imageType) { case BufferedImage.TYPE_INT_RGB: case BufferedImage.TYPE_INT_ARGB: case BufferedImage.TYPE_INT_ARGB_PRE: return new PaletteData(0x00FF0000, 0x0000FF00, 0x000000FF); case BufferedImage.TYPE_3BYTE_BGR: case BufferedImage.TYPE_4BYTE_ABGR: case BufferedImage.TYPE_4BYTE_ABGR_PRE: return new PaletteData(0x000000FF, 0x0000FF00, 0x00FF0000); default: throw new UnsupportedOperationException("RGB type not supported yet."); } } /** * Converts an AWT {@link BufferedImage} into an equivalent SWT {@link Image}. Whether * the transparency data is transferred is optional, and this method can also apply an * alpha adjustment during the conversion. * <p/> * Implementation details: on Windows, the returned {@link Image} will have an ordering * matching the Windows DIB (e.g. RGBA, not ARGB). Callers must make sure to use * <code>Image.getImageData().paletteData</code> to get the right pixels out of the image. * * @param display The display where the SWT image will be shown * @param awtImage The AWT {@link BufferedImage} * @param transferAlpha If true, copy alpha data out of the source image * @param globalAlpha If -1, do nothing, otherwise adjust the alpha of the final image * by the given amount in the range [0,255] * @return A new SWT {@link Image} with the same contents as the source * {@link BufferedImage} */ public static Image convertToSwt(Display display, BufferedImage awtImage, boolean transferAlpha, int globalAlpha) { int width = awtImage.getWidth(); int height = awtImage.getHeight(); WritableRaster raster = awtImage.getRaster(); int[] imageDataBuffer = ((DataBufferInt) raster.getDataBuffer()).getData(); ImageData imageData = new ImageData(width, height, 32, getAwtPaletteData(awtImage.getType())); imageData.setPixels(0, 0, imageDataBuffer.length, imageDataBuffer, 0); if (transferAlpha) { byte[] alphaData = new byte[height * width]; for (int y = 0; y < height; y++) { byte[] alphaRow = new byte[width]; for (int x = 0; x < width; x++) { int alpha = awtImage.getRGB(x, y) >>> 24; // We have to multiply in the alpha now since if we // set ImageData.alpha, it will ignore the alphaData. if (globalAlpha != -1) { alpha = alpha * globalAlpha >> 8; } alphaRow[x] = (byte) alpha; } System.arraycopy(alphaRow, 0, alphaData, y * width, width); } imageData.alphaData = alphaData; } else if (globalAlpha != -1) { imageData.alpha = globalAlpha; } return new Image(display, imageData); } /** * Converts a direct-color model SWT image to an equivalent AWT image. If the image * does not have a supported color model, returns null. This method does <b>NOT</b> * preserve alpha in the source image. * * @param swtImage the SWT image to be converted to AWT * @return an AWT image representing the source SWT image */ public static BufferedImage convertToAwt(Image swtImage) { ImageData swtData = swtImage.getImageData(); BufferedImage awtImage = new BufferedImage(swtData.width, swtData.height, BufferedImage.TYPE_INT_ARGB); PaletteData swtPalette = swtData.palette; if (swtPalette.isDirect) { PaletteData awtPalette = getAwtPaletteData(awtImage.getType()); if (swtPalette.equals(awtPalette)) { // No color conversion needed. for (int y = 0; y < swtData.height; y++) { for (int x = 0; x < swtData.width; x++) { int pixel = swtData.getPixel(x, y); awtImage.setRGB(x, y, 0xFF000000 | pixel); } } } else { // We need to remap the colors int sr = -awtPalette.redShift + swtPalette.redShift; int sg = -awtPalette.greenShift + swtPalette.greenShift; int sb = -awtPalette.blueShift + swtPalette.blueShift; for (int y = 0; y < swtData.height; y++) { for (int x = 0; x < swtData.width; x++) { int pixel = swtData.getPixel(x, y); int r = pixel & swtPalette.redMask; int g = pixel & swtPalette.greenMask; int b = pixel & swtPalette.blueMask; r = (sr < 0) ? r >>> -sr : r << sr; g = (sg < 0) ? g >>> -sg : g << sg; b = (sb < 0) ? b >>> -sb : b << sb; pixel = 0xFF000000 | r | g | b; awtImage.setRGB(x, y, pixel); } } } } else { return null; } return awtImage; } /** * Creates a new image from a source image where the contents from a given set of * bounding boxes are copied into the new image and the rest is left transparent. A * scale can be applied to make the resulting image larger or smaller than the source * image. Note that the alpha channel in the original image is ignored, and the alpha * values for the painted rectangles will be set to a specific value passed into this * function. * * @param image the source image * @param rectangles the set of rectangles (bounding boxes) to copy from the source * image * @param boundingBox the bounding rectangle of the rectangle list, which can be * computed by {@link ImageUtils#getBoundingRectangle} * @param scale a scale factor to apply to the result, e.g. 0.5 to shrink the * destination down 50%, 1.0 to leave it alone and 2.0 to zoom in by * doubling the image size * @param alpha the alpha (in the range 0-255) that painted bits should be set to * @return a pair of the rendered cropped image, and the location within the source * image that the crop begins (multiplied by the scale). May return null if * there are no selected items. */ public static Image drawRectangles(Image image, List<Rectangle> rectangles, Rectangle boundingBox, double scale, byte alpha) { if (rectangles.size() == 0 || boundingBox == null || boundingBox.isEmpty()) { return null; } ImageData srcData = image.getImageData(); int destWidth = (int) (scale * boundingBox.width); int destHeight = (int) (scale * boundingBox.height); ImageData destData = new ImageData(destWidth, destHeight, srcData.depth, srcData.palette); byte[] alphaData = new byte[destHeight * destWidth]; destData.alphaData = alphaData; for (Rectangle bounds : rectangles) { int dx1 = bounds.x - boundingBox.x; int dy1 = bounds.y - boundingBox.y; int dx2 = dx1 + bounds.width; int dy2 = dy1 + bounds.height; dx1 *= scale; dy1 *= scale; dx2 *= scale; dy2 *= scale; int sx1 = bounds.x; int sy1 = bounds.y; int sx2 = sx1 + bounds.width; int sy2 = sy1 + bounds.height; if (scale == 1.0d) { for (int dy = dy1, sy = sy1; dy < dy2; dy++, sy++) { for (int dx = dx1, sx = sx1; dx < dx2; dx++, sx++) { destData.setPixel(dx, dy, srcData.getPixel(sx, sy)); alphaData[dy * destWidth + dx] = alpha; } } } else { // Scaled copy. int sxDelta = sx2 - sx1; int dxDelta = dx2 - dx1; int syDelta = sy2 - sy1; int dyDelta = dy2 - dy1; for (int dy = dy1, sy = sy1; dy < dy2; dy++, sy = (dy - dy1) * syDelta / dyDelta + sy1) { for (int dx = dx1, sx = sx1; dx < dx2; dx++, sx = (dx - dx1) * sxDelta / dxDelta + sx1) { assert sx < sx2 && sy < sy2; destData.setPixel(dx, dy, srcData.getPixel(sx, sy)); alphaData[dy * destWidth + dx] = alpha; } } } } return new Image(image.getDevice(), destData); } /** * Converts the given SWT {@link Rectangle} into an ADT {@link Rect} * * @param swtRect the SWT {@link Rectangle} * @return an equivalent {@link Rect} */ public static Rect toRect(Rectangle swtRect) { return new Rect(swtRect.x, swtRect.y, swtRect.width, swtRect.height); } /** * Sets the values of the given ADT {@link Rect} to the values of the given SWT * {@link Rectangle} * * @param target the ADT {@link Rect} to modify * @param source the SWT {@link Rectangle} to read values from */ public static void set(Rect target, Rectangle source) { target.set(source.x, source.y, source.width, source.height); } /** * Compares an ADT {@link Rect} with an SWT {@link Rectangle} and returns true if they * are equivalent * * @param r1 the ADT {@link Rect} * @param r2 the SWT {@link Rectangle} * @return true if the two rectangles are equivalent */ public static boolean equals(Rect r1, Rectangle r2) { return r1.x == r2.x && r1.y == r2.y && r1.w == r2.width && r1.h == r2.height; } }