Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2010 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 android.graphics;
     18 
     19 import com.android.ide.common.rendering.api.LayoutLog;
     20 import com.android.layoutlib.bridge.Bridge;
     21 import com.android.layoutlib.bridge.impl.DelegateManager;
     22 import com.android.resources.Density;
     23 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
     24 
     25 import android.graphics.Bitmap.Config;
     26 import android.os.Parcel;
     27 
     28 import java.awt.Graphics2D;
     29 import java.awt.image.BufferedImage;
     30 import java.io.File;
     31 import java.io.IOException;
     32 import java.io.InputStream;
     33 import java.io.OutputStream;
     34 import java.nio.Buffer;
     35 import java.util.Arrays;
     36 import java.util.EnumSet;
     37 import java.util.Set;
     38 
     39 import javax.imageio.ImageIO;
     40 
     41 /**
     42  * Delegate implementing the native methods of android.graphics.Bitmap
     43  *
     44  * Through the layoutlib_create tool, the original native methods of Bitmap have been replaced
     45  * by calls to methods of the same name in this delegate class.
     46  *
     47  * This class behaves like the original native implementation, but in Java, keeping previously
     48  * native data into its own objects and mapping them to int that are sent back and forth between
     49  * it and the original Bitmap class.
     50  *
     51  * @see DelegateManager
     52  *
     53  */
     54 public final class Bitmap_Delegate {
     55 
     56     public enum BitmapCreateFlags {
     57         PREMULTIPLIED, MUTABLE
     58     }
     59 
     60     // ---- delegate manager ----
     61     private static final DelegateManager<Bitmap_Delegate> sManager =
     62             new DelegateManager<Bitmap_Delegate>(Bitmap_Delegate.class);
     63 
     64     // ---- delegate helper data ----
     65 
     66     // ---- delegate data ----
     67     private final Config mConfig;
     68     private BufferedImage mImage;
     69     private boolean mHasAlpha = true;
     70     private boolean mHasMipMap = false;      // TODO: check the default.
     71     private int mGenerationId = 0;
     72 
     73 
     74     // ---- Public Helper methods ----
     75 
     76     /**
     77      * Returns the native delegate associated to a given {@link Bitmap_Delegate} object.
     78      */
     79     public static Bitmap_Delegate getDelegate(Bitmap bitmap) {
     80         return sManager.getDelegate(bitmap.mNativeBitmap);
     81     }
     82 
     83     /**
     84      * Returns the native delegate associated to a given an int referencing a {@link Bitmap} object.
     85      */
     86     public static Bitmap_Delegate getDelegate(int native_bitmap) {
     87         return sManager.getDelegate(native_bitmap);
     88     }
     89 
     90     /**
     91      * Creates and returns a {@link Bitmap} initialized with the given file content.
     92      *
     93      * @param input the file from which to read the bitmap content
     94      * @param isMutable whether the bitmap is mutable
     95      * @param density the density associated with the bitmap
     96      *
     97      * @see Bitmap#isMutable()
     98      * @see Bitmap#getDensity()
     99      */
    100     public static Bitmap createBitmap(File input, boolean isMutable, Density density)
    101             throws IOException {
    102         return createBitmap(input, getPremultipliedBitmapCreateFlags(isMutable), density);
    103     }
    104 
    105     /**
    106      * Creates and returns a {@link Bitmap} initialized with the given file content.
    107      *
    108      * @param input the file from which to read the bitmap content
    109      * @param density the density associated with the bitmap
    110      *
    111      * @see Bitmap#isPremultiplied()
    112      * @see Bitmap#isMutable()
    113      * @see Bitmap#getDensity()
    114      */
    115     public static Bitmap createBitmap(File input, Set<BitmapCreateFlags> createFlags,
    116             Density density) throws IOException {
    117         // create a delegate with the content of the file.
    118         Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input), Config.ARGB_8888);
    119 
    120         return createBitmap(delegate, createFlags, density.getDpiValue());
    121     }
    122 
    123     /**
    124      * Creates and returns a {@link Bitmap} initialized with the given stream content.
    125      *
    126      * @param input the stream from which to read the bitmap content
    127      * @param isMutable whether the bitmap is mutable
    128      * @param density the density associated with the bitmap
    129      *
    130      * @see Bitmap#isMutable()
    131      * @see Bitmap#getDensity()
    132      */
    133     public static Bitmap createBitmap(InputStream input, boolean isMutable, Density density)
    134             throws IOException {
    135         return createBitmap(input, getPremultipliedBitmapCreateFlags(isMutable), density);
    136     }
    137 
    138     /**
    139      * Creates and returns a {@link Bitmap} initialized with the given stream content.
    140      *
    141      * @param input the stream from which to read the bitmap content
    142      * @param createFlags
    143      * @param density the density associated with the bitmap
    144      *
    145      * @see Bitmap#isPremultiplied()
    146      * @see Bitmap#isMutable()
    147      * @see Bitmap#getDensity()
    148      */
    149     public static Bitmap createBitmap(InputStream input, Set<BitmapCreateFlags> createFlags,
    150             Density density) throws IOException {
    151         // create a delegate with the content of the stream.
    152         Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input), Config.ARGB_8888);
    153 
    154         return createBitmap(delegate, createFlags, density.getDpiValue());
    155     }
    156 
    157     /**
    158      * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
    159      *
    160      * @param image the bitmap content
    161      * @param isMutable whether the bitmap is mutable
    162      * @param density the density associated with the bitmap
    163      *
    164      * @see Bitmap#isMutable()
    165      * @see Bitmap#getDensity()
    166      */
    167     public static Bitmap createBitmap(BufferedImage image, boolean isMutable,
    168             Density density) throws IOException {
    169         return createBitmap(image, getPremultipliedBitmapCreateFlags(isMutable), density);
    170     }
    171 
    172     /**
    173      * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
    174      *
    175      * @param image the bitmap content
    176      * @param createFlags
    177      * @param density the density associated with the bitmap
    178      *
    179      * @see Bitmap#isPremultiplied()
    180      * @see Bitmap#isMutable()
    181      * @see Bitmap#getDensity()
    182      */
    183     public static Bitmap createBitmap(BufferedImage image, Set<BitmapCreateFlags> createFlags,
    184             Density density) throws IOException {
    185         // create a delegate with the given image.
    186         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888);
    187 
    188         return createBitmap(delegate, createFlags, density.getDpiValue());
    189     }
    190 
    191     /**
    192      * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}.
    193      */
    194     public static BufferedImage getImage(Bitmap bitmap) {
    195         // get the delegate from the native int.
    196         Bitmap_Delegate delegate = sManager.getDelegate(bitmap.mNativeBitmap);
    197         if (delegate == null) {
    198             return null;
    199         }
    200 
    201         return delegate.mImage;
    202     }
    203 
    204     public static int getBufferedImageType(int nativeBitmapConfig) {
    205         switch (Config.nativeToConfig(nativeBitmapConfig)) {
    206             case ALPHA_8:
    207                 return BufferedImage.TYPE_INT_ARGB;
    208             case RGB_565:
    209                 return BufferedImage.TYPE_INT_ARGB;
    210             case ARGB_4444:
    211                 return BufferedImage.TYPE_INT_ARGB;
    212             case ARGB_8888:
    213                 return BufferedImage.TYPE_INT_ARGB;
    214         }
    215 
    216         return BufferedImage.TYPE_INT_ARGB;
    217     }
    218 
    219     /**
    220      * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}.
    221      */
    222     public BufferedImage getImage() {
    223         return mImage;
    224     }
    225 
    226     /**
    227      * Returns the Android bitmap config. Note that this not the config of the underlying
    228      * Java2D bitmap.
    229      */
    230     public Config getConfig() {
    231         return mConfig;
    232     }
    233 
    234     /**
    235      * Returns the hasAlpha rendering hint
    236      * @return true if the bitmap alpha should be used at render time
    237      */
    238     public boolean hasAlpha() {
    239         return mHasAlpha && mConfig != Config.RGB_565;
    240     }
    241 
    242     public boolean hasMipMap() {
    243         // TODO: check if more checks are required as in hasAlpha.
    244         return mHasMipMap;
    245     }
    246     /**
    247      * Update the generationId.
    248      *
    249      * @see Bitmap#getGenerationId()
    250      */
    251     public void change() {
    252         mGenerationId++;
    253     }
    254 
    255     // ---- native methods ----
    256 
    257     @LayoutlibDelegate
    258     /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width,
    259             int height, int nativeConfig, boolean isMutable) {
    260         int imageType = getBufferedImageType(nativeConfig);
    261 
    262         // create the image
    263         BufferedImage image = new BufferedImage(width, height, imageType);
    264 
    265         if (colors != null) {
    266             image.setRGB(0, 0, width, height, colors, offset, stride);
    267         }
    268 
    269         // create a delegate with the content of the stream.
    270         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
    271 
    272         return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
    273                             Bitmap.getDefaultDensity());
    274     }
    275 
    276     @LayoutlibDelegate
    277     /*package*/ static Bitmap nativeCopy(int srcBitmap, int nativeConfig, boolean isMutable) {
    278         Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(srcBitmap);
    279         if (srcBmpDelegate == null) {
    280             return null;
    281         }
    282 
    283         BufferedImage srcImage = srcBmpDelegate.getImage();
    284 
    285         int width = srcImage.getWidth();
    286         int height = srcImage.getHeight();
    287 
    288         int imageType = getBufferedImageType(nativeConfig);
    289 
    290         // create the image
    291         BufferedImage image = new BufferedImage(width, height, imageType);
    292 
    293         // copy the source image into the image.
    294         int[] argb = new int[width * height];
    295         srcImage.getRGB(0, 0, width, height, argb, 0, width);
    296         image.setRGB(0, 0, width, height, argb, 0, width);
    297 
    298         // create a delegate with the content of the stream.
    299         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
    300 
    301         return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
    302                 Bitmap.getDefaultDensity());
    303     }
    304 
    305     @LayoutlibDelegate
    306     /*package*/ static void nativeDestructor(int nativeBitmap) {
    307         sManager.removeJavaReferenceFor(nativeBitmap);
    308     }
    309 
    310     @LayoutlibDelegate
    311     /*package*/ static boolean nativeRecycle(int nativeBitmap) {
    312         sManager.removeJavaReferenceFor(nativeBitmap);
    313         return true;
    314     }
    315 
    316     @LayoutlibDelegate
    317     /*package*/ static boolean nativeCompress(int nativeBitmap, int format, int quality,
    318             OutputStream stream, byte[] tempStorage) {
    319         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
    320                 "Bitmap.compress() is not supported", null /*data*/);
    321         return true;
    322     }
    323 
    324     @LayoutlibDelegate
    325     /*package*/ static void nativeErase(int nativeBitmap, int color) {
    326         // get the delegate from the native int.
    327         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    328         if (delegate == null) {
    329             return;
    330         }
    331 
    332         BufferedImage image = delegate.mImage;
    333 
    334         Graphics2D g = image.createGraphics();
    335         try {
    336             g.setColor(new java.awt.Color(color, true));
    337 
    338             g.fillRect(0, 0, image.getWidth(), image.getHeight());
    339         } finally {
    340             g.dispose();
    341         }
    342     }
    343 
    344     @LayoutlibDelegate
    345     /*package*/ static int nativeWidth(int nativeBitmap) {
    346         // get the delegate from the native int.
    347         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    348         if (delegate == null) {
    349             return 0;
    350         }
    351 
    352         return delegate.mImage.getWidth();
    353     }
    354 
    355     @LayoutlibDelegate
    356     /*package*/ static int nativeHeight(int nativeBitmap) {
    357         // get the delegate from the native int.
    358         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    359         if (delegate == null) {
    360             return 0;
    361         }
    362 
    363         return delegate.mImage.getHeight();
    364     }
    365 
    366     @LayoutlibDelegate
    367     /*package*/ static int nativeRowBytes(int nativeBitmap) {
    368         // get the delegate from the native int.
    369         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    370         if (delegate == null) {
    371             return 0;
    372         }
    373 
    374         return delegate.mImage.getWidth();
    375     }
    376 
    377     @LayoutlibDelegate
    378     /*package*/ static int nativeConfig(int nativeBitmap) {
    379         // get the delegate from the native int.
    380         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    381         if (delegate == null) {
    382             return 0;
    383         }
    384 
    385         return delegate.mConfig.nativeInt;
    386     }
    387 
    388     @LayoutlibDelegate
    389     /*package*/ static boolean nativeHasAlpha(int nativeBitmap) {
    390         // get the delegate from the native int.
    391         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    392         if (delegate == null) {
    393             return true;
    394         }
    395 
    396         return delegate.mHasAlpha;
    397     }
    398 
    399     @LayoutlibDelegate
    400     /*package*/ static boolean nativeHasMipMap(int nativeBitmap) {
    401         // get the delegate from the native int.
    402         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    403         if (delegate == null) {
    404             return true;
    405         }
    406 
    407         return delegate.mHasMipMap;
    408     }
    409 
    410     @LayoutlibDelegate
    411     /*package*/ static int nativeGetPixel(int nativeBitmap, int x, int y) {
    412         // get the delegate from the native int.
    413         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    414         if (delegate == null) {
    415             return 0;
    416         }
    417 
    418         return delegate.mImage.getRGB(x, y);
    419     }
    420 
    421     @LayoutlibDelegate
    422     /*package*/ static void nativeGetPixels(int nativeBitmap, int[] pixels, int offset,
    423             int stride, int x, int y, int width, int height) {
    424         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    425         if (delegate == null) {
    426             return;
    427         }
    428 
    429         delegate.getImage().getRGB(x, y, width, height, pixels, offset, stride);
    430     }
    431 
    432 
    433     @LayoutlibDelegate
    434     /*package*/ static void nativeSetPixel(int nativeBitmap, int x, int y, int color) {
    435         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    436         if (delegate == null) {
    437             return;
    438         }
    439 
    440         delegate.getImage().setRGB(x, y, color);
    441     }
    442 
    443     @LayoutlibDelegate
    444     /*package*/ static void nativeSetPixels(int nativeBitmap, int[] colors, int offset,
    445             int stride, int x, int y, int width, int height) {
    446         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    447         if (delegate == null) {
    448             return;
    449         }
    450 
    451         delegate.getImage().setRGB(x, y, width, height, colors, offset, stride);
    452     }
    453 
    454     @LayoutlibDelegate
    455     /*package*/ static void nativeCopyPixelsToBuffer(int nativeBitmap, Buffer dst) {
    456         // FIXME implement native delegate
    457         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
    458                 "Bitmap.copyPixelsToBuffer is not supported.", null, null /*data*/);
    459     }
    460 
    461     @LayoutlibDelegate
    462     /*package*/ static void nativeCopyPixelsFromBuffer(int nb, Buffer src) {
    463         // FIXME implement native delegate
    464         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
    465                 "Bitmap.copyPixelsFromBuffer is not supported.", null, null /*data*/);
    466     }
    467 
    468     @LayoutlibDelegate
    469     /*package*/ static int nativeGenerationId(int nativeBitmap) {
    470         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    471         if (delegate == null) {
    472             return 0;
    473         }
    474 
    475         return delegate.mGenerationId;
    476     }
    477 
    478     @LayoutlibDelegate
    479     /*package*/ static Bitmap nativeCreateFromParcel(Parcel p) {
    480         // This is only called by Bitmap.CREATOR (Parcelable.Creator<Bitmap>), which is only
    481         // used during aidl call so really this should not be called.
    482         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
    483                 "AIDL is not suppored, and therefore Bitmaps cannot be created from parcels.",
    484                 null /*data*/);
    485         return null;
    486     }
    487 
    488     @LayoutlibDelegate
    489     /*package*/ static boolean nativeWriteToParcel(int nativeBitmap, boolean isMutable,
    490             int density, Parcel p) {
    491         // This is only called when sending a bitmap through aidl, so really this should not
    492         // be called.
    493         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
    494                 "AIDL is not suppored, and therefore Bitmaps cannot be written to parcels.",
    495                 null /*data*/);
    496         return false;
    497     }
    498 
    499     @LayoutlibDelegate
    500     /*package*/ static Bitmap nativeExtractAlpha(int nativeBitmap, int nativePaint,
    501             int[] offsetXY) {
    502         Bitmap_Delegate bitmap = sManager.getDelegate(nativeBitmap);
    503         if (bitmap == null) {
    504             return null;
    505         }
    506 
    507         // get the paint which can be null if nativePaint is 0.
    508         Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint);
    509 
    510         if (paint != null && paint.getMaskFilter() != null) {
    511             Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
    512                     "MaskFilter not supported in Bitmap.extractAlpha",
    513                     null, null /*data*/);
    514         }
    515 
    516         int alpha = paint != null ? paint.getAlpha() : 0xFF;
    517         BufferedImage image = createCopy(bitmap.getImage(), BufferedImage.TYPE_INT_ARGB, alpha);
    518 
    519         // create the delegate. The actual Bitmap config is only an alpha channel
    520         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ALPHA_8);
    521 
    522         // the density doesn't matter, it's set by the Java method.
    523         return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.MUTABLE),
    524                 Density.DEFAULT_DENSITY /*density*/);
    525     }
    526 
    527     @LayoutlibDelegate
    528     /*package*/ static void nativePrepareToDraw(int nativeBitmap) {
    529         // nothing to be done here.
    530     }
    531 
    532     @LayoutlibDelegate
    533     /*package*/ static void nativeSetHasAlpha(int nativeBitmap, boolean hasAlpha) {
    534         // get the delegate from the native int.
    535         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    536         if (delegate == null) {
    537             return;
    538         }
    539 
    540         delegate.mHasAlpha = hasAlpha;
    541     }
    542 
    543     @LayoutlibDelegate
    544     /*package*/ static void nativeSetHasMipMap(int nativeBitmap, boolean hasMipMap) {
    545         // get the delegate from the native int.
    546         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    547         if (delegate == null) {
    548             return;
    549         }
    550 
    551         delegate.mHasMipMap = hasMipMap;
    552     }
    553 
    554     @LayoutlibDelegate
    555     /*package*/ static boolean nativeSameAs(int nb0, int nb1) {
    556         Bitmap_Delegate delegate1 = sManager.getDelegate(nb0);
    557         if (delegate1 == null) {
    558             return false;
    559         }
    560 
    561         Bitmap_Delegate delegate2 = sManager.getDelegate(nb1);
    562         if (delegate2 == null) {
    563             return false;
    564         }
    565 
    566         BufferedImage image1 = delegate1.getImage();
    567         BufferedImage image2 = delegate2.getImage();
    568         if (delegate1.mConfig != delegate2.mConfig ||
    569                 image1.getWidth() != image2.getWidth() ||
    570                 image1.getHeight() != image2.getHeight()) {
    571             return false;
    572         }
    573 
    574         // get the internal data
    575         int w = image1.getWidth();
    576         int h = image2.getHeight();
    577         int[] argb1 = new int[w*h];
    578         int[] argb2 = new int[w*h];
    579 
    580         image1.getRGB(0, 0, w, h, argb1, 0, w);
    581         image2.getRGB(0, 0, w, h, argb2, 0, w);
    582 
    583         // compares
    584         if (delegate1.mConfig == Config.ALPHA_8) {
    585             // in this case we have to manually compare the alpha channel as the rest is garbage.
    586             final int length = w*h;
    587             for (int i = 0 ; i < length ; i++) {
    588                 if ((argb1[i] & 0xFF000000) != (argb2[i] & 0xFF000000)) {
    589                     return false;
    590                 }
    591             }
    592             return true;
    593         }
    594 
    595         return Arrays.equals(argb1, argb2);
    596     }
    597 
    598     // ---- Private delegate/helper methods ----
    599 
    600     private Bitmap_Delegate(BufferedImage image, Config config) {
    601         mImage = image;
    602         mConfig = config;
    603     }
    604 
    605     private static Bitmap createBitmap(Bitmap_Delegate delegate,
    606             Set<BitmapCreateFlags> createFlags, int density) {
    607         // get its native_int
    608         int nativeInt = sManager.addNewDelegate(delegate);
    609 
    610         int width = delegate.mImage.getWidth();
    611         int height = delegate.mImage.getHeight();
    612         boolean isMutable = createFlags.contains(BitmapCreateFlags.MUTABLE);
    613         boolean isPremultiplied = createFlags.contains(BitmapCreateFlags.PREMULTIPLIED);
    614 
    615         // and create/return a new Bitmap with it
    616         return new Bitmap(nativeInt, null /* buffer */, width, height, density, isMutable,
    617                           isPremultiplied, null /*ninePatchChunk*/, null /* layoutBounds */);
    618     }
    619 
    620     private static Set<BitmapCreateFlags> getPremultipliedBitmapCreateFlags(boolean isMutable) {
    621         Set<BitmapCreateFlags> createFlags = EnumSet.of(BitmapCreateFlags.PREMULTIPLIED);
    622         if (isMutable) {
    623             createFlags.add(BitmapCreateFlags.MUTABLE);
    624         }
    625         return createFlags;
    626     }
    627 
    628     /**
    629      * Creates and returns a copy of a given BufferedImage.
    630      * <p/>
    631      * if alpha is different than 255, then it is applied to the alpha channel of each pixel.
    632      *
    633      * @param image the image to copy
    634      * @param imageType the type of the new image
    635      * @param alpha an optional alpha modifier
    636      * @return a new BufferedImage
    637      */
    638     /*package*/ static BufferedImage createCopy(BufferedImage image, int imageType, int alpha) {
    639         int w = image.getWidth();
    640         int h = image.getHeight();
    641 
    642         BufferedImage result = new BufferedImage(w, h, imageType);
    643 
    644         int[] argb = new int[w * h];
    645         image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
    646 
    647         if (alpha != 255) {
    648             final int length = argb.length;
    649             for (int i = 0 ; i < length; i++) {
    650                 int a = (argb[i] >>> 24 * alpha) / 255;
    651                 argb[i] = (a << 24) | (argb[i] & 0x00FFFFFF);
    652             }
    653         }
    654 
    655         result.setRGB(0, 0, w, h, argb, 0, w);
    656 
    657         return result;
    658     }
    659 
    660 }
    661