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