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