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