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