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) {
    252         int imageType = getBufferedImageType();
    253 
    254         // create the image
    255         BufferedImage image = new BufferedImage(width, height, imageType);
    256 
    257         if (colors != null) {
    258             image.setRGB(0, 0, width, height, colors, offset, stride);
    259         }
    260 
    261         // create a delegate with the content of the stream.
    262         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
    263 
    264         return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
    265                             Bitmap.getDefaultDensity());
    266     }
    267 
    268     @LayoutlibDelegate
    269     /*package*/ static Bitmap nativeCopy(long srcBitmap, int nativeConfig, boolean isMutable) {
    270         Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(srcBitmap);
    271         if (srcBmpDelegate == null) {
    272             return null;
    273         }
    274 
    275         BufferedImage srcImage = srcBmpDelegate.getImage();
    276 
    277         int width = srcImage.getWidth();
    278         int height = srcImage.getHeight();
    279 
    280         int imageType = getBufferedImageType();
    281 
    282         // create the image
    283         BufferedImage image = new BufferedImage(width, height, imageType);
    284 
    285         // copy the source image into the image.
    286         int[] argb = new int[width * height];
    287         srcImage.getRGB(0, 0, width, height, argb, 0, width);
    288         image.setRGB(0, 0, width, height, argb, 0, width);
    289 
    290         // create a delegate with the content of the stream.
    291         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
    292 
    293         return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
    294                 Bitmap.getDefaultDensity());
    295     }
    296 
    297     @LayoutlibDelegate
    298     /*package*/ static Bitmap nativeCopyAshmem(long nativeSrcBitmap) {
    299         // Unused method; no implementation provided.
    300         assert false;
    301         return null;
    302     }
    303 
    304     @LayoutlibDelegate
    305     /*package*/ static Bitmap nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig) {
    306         // Unused method; no implementation provided.
    307         assert false;
    308         return null;
    309     }
    310 
    311     @LayoutlibDelegate
    312     /*package*/ static long nativeGetNativeFinalizer() {
    313         synchronized (Bitmap_Delegate.class) {
    314             if (sFinalizer == -1) {
    315                 sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
    316             }
    317             return sFinalizer;
    318         }
    319     }
    320 
    321     @LayoutlibDelegate
    322     /*package*/ static boolean nativeRecycle(long nativeBitmap) {
    323         // In our case reycle() is a no-op. We will let the finalizer to dispose the bitmap.
    324         return true;
    325     }
    326 
    327     @LayoutlibDelegate
    328     /*package*/ static void nativeReconfigure(long nativeBitmap, int width, int height,
    329             int config, boolean isPremultiplied) {
    330         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
    331                 "Bitmap.reconfigure() is not supported", null /*data*/);
    332     }
    333 
    334     @LayoutlibDelegate
    335     /*package*/ static boolean nativeCompress(long nativeBitmap, int format, int quality,
    336             OutputStream stream, byte[] tempStorage) {
    337         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
    338                 "Bitmap.compress() is not supported", null /*data*/);
    339         return true;
    340     }
    341 
    342     @LayoutlibDelegate
    343     /*package*/ static void nativeErase(long nativeBitmap, int color) {
    344         // get the delegate from the native int.
    345         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    346         if (delegate == null) {
    347             return;
    348         }
    349 
    350         BufferedImage image = delegate.mImage;
    351 
    352         Graphics2D g = image.createGraphics();
    353         try {
    354             g.setColor(new java.awt.Color(color, true));
    355 
    356             g.fillRect(0, 0, image.getWidth(), image.getHeight());
    357         } finally {
    358             g.dispose();
    359         }
    360     }
    361 
    362     @LayoutlibDelegate
    363     /*package*/ static int nativeRowBytes(long nativeBitmap) {
    364         // get the delegate from the native int.
    365         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    366         if (delegate == null) {
    367             return 0;
    368         }
    369 
    370         return delegate.mImage.getWidth();
    371     }
    372 
    373     @LayoutlibDelegate
    374     /*package*/ static int nativeConfig(long nativeBitmap) {
    375         // get the delegate from the native int.
    376         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    377         if (delegate == null) {
    378             return 0;
    379         }
    380 
    381         return delegate.mConfig.nativeInt;
    382     }
    383 
    384     @LayoutlibDelegate
    385     /*package*/ static boolean nativeHasAlpha(long nativeBitmap) {
    386         // get the delegate from the native int.
    387         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    388         return delegate == null || delegate.mHasAlpha;
    389 
    390     }
    391 
    392     @LayoutlibDelegate
    393     /*package*/ static boolean nativeHasMipMap(long nativeBitmap) {
    394         // get the delegate from the native int.
    395         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    396         return delegate == null || delegate.mHasMipMap;
    397 
    398     }
    399 
    400     @LayoutlibDelegate
    401     /*package*/ static int nativeGetPixel(long nativeBitmap, int x, int y) {
    402         // get the delegate from the native int.
    403         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    404         if (delegate == null) {
    405             return 0;
    406         }
    407 
    408         return delegate.mImage.getRGB(x, y);
    409     }
    410 
    411     @LayoutlibDelegate
    412     /*package*/ static void nativeGetPixels(long nativeBitmap, int[] pixels, int offset,
    413             int stride, int x, int y, int width, int height) {
    414         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    415         if (delegate == null) {
    416             return;
    417         }
    418 
    419         delegate.getImage().getRGB(x, y, width, height, pixels, offset, stride);
    420     }
    421 
    422 
    423     @LayoutlibDelegate
    424     /*package*/ static void nativeSetPixel(long nativeBitmap, int x, int y, int color) {
    425         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    426         if (delegate == null) {
    427             return;
    428         }
    429 
    430         delegate.getImage().setRGB(x, y, color);
    431     }
    432 
    433     @LayoutlibDelegate
    434     /*package*/ static void nativeSetPixels(long nativeBitmap, int[] colors, int offset,
    435             int stride, int x, int y, int width, int height) {
    436         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    437         if (delegate == null) {
    438             return;
    439         }
    440 
    441         delegate.getImage().setRGB(x, y, width, height, colors, offset, stride);
    442     }
    443 
    444     @LayoutlibDelegate
    445     /*package*/ static void nativeCopyPixelsToBuffer(long nativeBitmap, Buffer dst) {
    446         // FIXME implement native delegate
    447         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
    448                 "Bitmap.copyPixelsToBuffer is not supported.", null, null /*data*/);
    449     }
    450 
    451     @LayoutlibDelegate
    452     /*package*/ static void nativeCopyPixelsFromBuffer(long nb, Buffer src) {
    453         // FIXME implement native delegate
    454         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
    455                 "Bitmap.copyPixelsFromBuffer is not supported.", null, null /*data*/);
    456     }
    457 
    458     @LayoutlibDelegate
    459     /*package*/ static int nativeGenerationId(long nativeBitmap) {
    460         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    461         if (delegate == null) {
    462             return 0;
    463         }
    464 
    465         return delegate.mGenerationId;
    466     }
    467 
    468     @LayoutlibDelegate
    469     /*package*/ static Bitmap nativeCreateFromParcel(Parcel p) {
    470         // This is only called by Bitmap.CREATOR (Parcelable.Creator<Bitmap>), which is only
    471         // used during aidl call so really this should not be called.
    472         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
    473                 "AIDL is not suppored, and therefore Bitmaps cannot be created from parcels.",
    474                 null /*data*/);
    475         return null;
    476     }
    477 
    478     @LayoutlibDelegate
    479     /*package*/ static boolean nativeWriteToParcel(long nativeBitmap, boolean isMutable,
    480             int density, Parcel p) {
    481         // This is only called when sending a bitmap through aidl, so really this should not
    482         // be called.
    483         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
    484                 "AIDL is not suppored, and therefore Bitmaps cannot be written to parcels.",
    485                 null /*data*/);
    486         return false;
    487     }
    488 
    489     @LayoutlibDelegate
    490     /*package*/ static Bitmap nativeExtractAlpha(long nativeBitmap, long nativePaint,
    491             int[] offsetXY) {
    492         Bitmap_Delegate bitmap = sManager.getDelegate(nativeBitmap);
    493         if (bitmap == null) {
    494             return null;
    495         }
    496 
    497         // get the paint which can be null if nativePaint is 0.
    498         Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint);
    499 
    500         if (paint != null && paint.getMaskFilter() != null) {
    501             Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
    502                     "MaskFilter not supported in Bitmap.extractAlpha",
    503                     null, null /*data*/);
    504         }
    505 
    506         int alpha = paint != null ? paint.getAlpha() : 0xFF;
    507         BufferedImage image = createCopy(bitmap.getImage(), BufferedImage.TYPE_INT_ARGB, alpha);
    508 
    509         // create the delegate. The actual Bitmap config is only an alpha channel
    510         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ALPHA_8);
    511 
    512         // the density doesn't matter, it's set by the Java method.
    513         return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.MUTABLE),
    514                 Density.DEFAULT_DENSITY /*density*/);
    515     }
    516 
    517     @LayoutlibDelegate
    518     /*package*/ static boolean nativeIsPremultiplied(long nativeBitmap) {
    519         // get the delegate from the native int.
    520         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    521         return delegate != null && delegate.mIsPremultiplied;
    522 
    523     }
    524 
    525     @LayoutlibDelegate
    526     /*package*/ static void nativeSetPremultiplied(long nativeBitmap, boolean isPremul) {
    527         // get the delegate from the native int.
    528         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    529         if (delegate == null) {
    530             return;
    531         }
    532 
    533         delegate.mIsPremultiplied = isPremul;
    534     }
    535 
    536     @LayoutlibDelegate
    537     /*package*/ static void nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha,
    538             boolean isPremul) {
    539         // get the delegate from the native int.
    540         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    541         if (delegate == null) {
    542             return;
    543         }
    544 
    545         delegate.mHasAlpha = hasAlpha;
    546     }
    547 
    548     @LayoutlibDelegate
    549     /*package*/ static void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap) {
    550         // get the delegate from the native int.
    551         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    552         if (delegate == null) {
    553             return;
    554         }
    555 
    556         delegate.mHasMipMap = hasMipMap;
    557     }
    558 
    559     @LayoutlibDelegate
    560     /*package*/ static boolean nativeSameAs(long nb0, long nb1) {
    561         Bitmap_Delegate delegate1 = sManager.getDelegate(nb0);
    562         if (delegate1 == null) {
    563             return false;
    564         }
    565 
    566         Bitmap_Delegate delegate2 = sManager.getDelegate(nb1);
    567         if (delegate2 == null) {
    568             return false;
    569         }
    570 
    571         BufferedImage image1 = delegate1.getImage();
    572         BufferedImage image2 = delegate2.getImage();
    573         if (delegate1.mConfig != delegate2.mConfig ||
    574                 image1.getWidth() != image2.getWidth() ||
    575                 image1.getHeight() != image2.getHeight()) {
    576             return false;
    577         }
    578 
    579         // get the internal data
    580         int w = image1.getWidth();
    581         int h = image2.getHeight();
    582         int[] argb1 = new int[w*h];
    583         int[] argb2 = new int[w*h];
    584 
    585         image1.getRGB(0, 0, w, h, argb1, 0, w);
    586         image2.getRGB(0, 0, w, h, argb2, 0, w);
    587 
    588         // compares
    589         if (delegate1.mConfig == Config.ALPHA_8) {
    590             // in this case we have to manually compare the alpha channel as the rest is garbage.
    591             final int length = w*h;
    592             for (int i = 0 ; i < length ; i++) {
    593                 if ((argb1[i] & 0xFF000000) != (argb2[i] & 0xFF000000)) {
    594                     return false;
    595                 }
    596             }
    597             return true;
    598         }
    599 
    600         return Arrays.equals(argb1, argb2);
    601     }
    602 
    603     @LayoutlibDelegate
    604     /*package*/ static int nativeGetAllocationByteCount(long nativeBitmap) {
    605         // get the delegate from the native int.
    606         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
    607         if (delegate == null) {
    608             return 0;
    609         }
    610         return nativeRowBytes(nativeBitmap) * delegate.mImage.getHeight();
    611 
    612     }
    613 
    614     @LayoutlibDelegate
    615     /*package*/ static void nativePrepareToDraw(long nativeBitmap) {
    616         // do nothing as Bitmap_Delegate does not have caches
    617     }
    618 
    619     @LayoutlibDelegate
    620     /*package*/ static Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap) {
    621         Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(nativeBitmap);
    622         if (srcBmpDelegate == null) {
    623             return null;
    624         }
    625 
    626         BufferedImage srcImage = srcBmpDelegate.getImage();
    627 
    628         // create the image
    629         BufferedImage image = new BufferedImage(srcImage.getColorModel(), srcImage.copyData(null),
    630                 srcImage.isAlphaPremultiplied(), null);
    631 
    632         // create a delegate with the content of the stream.
    633         Bitmap_Delegate delegate = new Bitmap_Delegate(image, srcBmpDelegate.getConfig());
    634 
    635         return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.NONE),
    636                 Bitmap.getDefaultDensity());
    637     }
    638 
    639     @LayoutlibDelegate
    640     /*package*/ static Bitmap nativeCreateHardwareBitmap(GraphicBuffer buffer) {
    641         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
    642                 "Bitmap.nativeCreateHardwareBitmap() is not supported", null /*data*/);
    643         return null;
    644     }
    645 
    646     @LayoutlibDelegate
    647     /*package*/ static GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap) {
    648         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
    649                 "Bitmap.nativeCreateGraphicBufferHandle() is not supported", null /*data*/);
    650         return null;
    651     }
    652 
    653     @LayoutlibDelegate
    654     /*package*/ static boolean nativeIsSRGB(long nativeBitmap) {
    655         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
    656                 "Color spaces are not supported", null /*data*/);
    657         return false;
    658     }
    659 
    660     @LayoutlibDelegate
    661     /*package*/ static boolean nativeGetColorSpace(long nativePtr, float[] xyz, float[] params) {
    662         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
    663                 "Color spaces are not supported", null /*data*/);
    664         return false;
    665     }
    666 
    667     // ---- Private delegate/helper methods ----
    668 
    669     private Bitmap_Delegate(BufferedImage image, Config config) {
    670         mImage = image;
    671         mConfig = config;
    672     }
    673 
    674     private static Bitmap createBitmap(Bitmap_Delegate delegate,
    675             Set<BitmapCreateFlags> createFlags, int density) {
    676         // get its native_int
    677         long nativeInt = sManager.addNewDelegate(delegate);
    678 
    679         int width = delegate.mImage.getWidth();
    680         int height = delegate.mImage.getHeight();
    681         boolean isMutable = createFlags.contains(BitmapCreateFlags.MUTABLE);
    682         boolean isPremultiplied = createFlags.contains(BitmapCreateFlags.PREMULTIPLIED);
    683 
    684         // and create/return a new Bitmap with it
    685         return new Bitmap(nativeInt, width, height, density, isMutable,
    686                           isPremultiplied, null /*ninePatchChunk*/, null /* layoutBounds */);
    687     }
    688 
    689     private static Set<BitmapCreateFlags> getPremultipliedBitmapCreateFlags(boolean isMutable) {
    690         Set<BitmapCreateFlags> createFlags = EnumSet.of(BitmapCreateFlags.PREMULTIPLIED);
    691         if (isMutable) {
    692             createFlags.add(BitmapCreateFlags.MUTABLE);
    693         }
    694         return createFlags;
    695     }
    696 
    697     /**
    698      * Creates and returns a copy of a given BufferedImage.
    699      * <p/>
    700      * if alpha is different than 255, then it is applied to the alpha channel of each pixel.
    701      *
    702      * @param image the image to copy
    703      * @param imageType the type of the new image
    704      * @param alpha an optional alpha modifier
    705      * @return a new BufferedImage
    706      */
    707     /*package*/ static BufferedImage createCopy(BufferedImage image, int imageType, int alpha) {
    708         int w = image.getWidth();
    709         int h = image.getHeight();
    710 
    711         BufferedImage result = new BufferedImage(w, h, imageType);
    712 
    713         int[] argb = new int[w * h];
    714         image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
    715 
    716         if (alpha != 255) {
    717             final int length = argb.length;
    718             for (int i = 0 ; i < length; i++) {
    719                 int a = (argb[i] >>> 24 * alpha) / 255;
    720                 argb[i] = (a << 24) | (argb[i] & 0x00FFFFFF);
    721             }
    722         }
    723 
    724         result.setRGB(0, 0, w, h, argb, 0, w);
    725 
    726         return result;
    727     }
    728 
    729 }
    730