• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.graphics;
18 
19 import com.android.ide.common.rendering.api.LayoutLog;
20 import com.android.layoutlib.bridge.Bridge;
21 import com.android.layoutlib.bridge.impl.DelegateManager;
22 import com.android.resources.Density;
23 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
24 
25 import android.graphics.Bitmap.Config;
26 import android.os.Parcel;
27 
28 import java.awt.Graphics2D;
29 import java.awt.image.BufferedImage;
30 import java.io.File;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.OutputStream;
34 import java.nio.Buffer;
35 import java.util.Arrays;
36 import java.util.EnumSet;
37 import java.util.Set;
38 
39 import javax.imageio.ImageIO;
40 
41 /**
42  * Delegate implementing the native methods of android.graphics.Bitmap
43  *
44  * Through the layoutlib_create tool, the original native methods of Bitmap have been replaced
45  * by calls to methods of the same name in this delegate class.
46  *
47  * This class behaves like the original native implementation, but in Java, keeping previously
48  * native data into its own objects and mapping them to int that are sent back and forth between
49  * it and the original Bitmap class.
50  *
51  * @see DelegateManager
52  *
53  */
54 public final class Bitmap_Delegate {
55 
56     public enum BitmapCreateFlags {
57         PREMULTIPLIED, MUTABLE
58     }
59 
60     // ---- delegate manager ----
61     private static final DelegateManager<Bitmap_Delegate> sManager =
62             new DelegateManager<Bitmap_Delegate>(Bitmap_Delegate.class);
63 
64     // ---- delegate helper data ----
65 
66     // ---- delegate data ----
67     private final Config mConfig;
68     private BufferedImage mImage;
69     private boolean mHasAlpha = true;
70     private boolean mHasMipMap = false;      // TODO: check the default.
71     private int mGenerationId = 0;
72 
73 
74     // ---- Public Helper methods ----
75 
76     /**
77      * Returns the native delegate associated to a given {@link Bitmap_Delegate} object.
78      */
getDelegate(Bitmap bitmap)79     public static Bitmap_Delegate getDelegate(Bitmap bitmap) {
80         return sManager.getDelegate(bitmap.mNativeBitmap);
81     }
82 
83     /**
84      * Returns the native delegate associated to a given an int referencing a {@link Bitmap} object.
85      */
getDelegate(int native_bitmap)86     public static Bitmap_Delegate getDelegate(int native_bitmap) {
87         return sManager.getDelegate(native_bitmap);
88     }
89 
90     /**
91      * Creates and returns a {@link Bitmap} initialized with the given file content.
92      *
93      * @param input the file from which to read the bitmap content
94      * @param isMutable whether the bitmap is mutable
95      * @param density the density associated with the bitmap
96      *
97      * @see Bitmap#isMutable()
98      * @see Bitmap#getDensity()
99      */
createBitmap(File input, boolean isMutable, Density density)100     public static Bitmap createBitmap(File input, boolean isMutable, Density density)
101             throws IOException {
102         return createBitmap(input, getPremultipliedBitmapCreateFlags(isMutable), density);
103     }
104 
105     /**
106      * Creates and returns a {@link Bitmap} initialized with the given file content.
107      *
108      * @param input the file from which to read the bitmap content
109      * @param density the density associated with the bitmap
110      *
111      * @see Bitmap#isPremultiplied()
112      * @see Bitmap#isMutable()
113      * @see Bitmap#getDensity()
114      */
createBitmap(File input, Set<BitmapCreateFlags> createFlags, Density density)115     public static Bitmap createBitmap(File input, Set<BitmapCreateFlags> createFlags,
116             Density density) throws IOException {
117         // create a delegate with the content of the file.
118         Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input), Config.ARGB_8888);
119 
120         return createBitmap(delegate, createFlags, density.getDpiValue());
121     }
122 
123     /**
124      * Creates and returns a {@link Bitmap} initialized with the given stream content.
125      *
126      * @param input the stream from which to read the bitmap content
127      * @param isMutable whether the bitmap is mutable
128      * @param density the density associated with the bitmap
129      *
130      * @see Bitmap#isMutable()
131      * @see Bitmap#getDensity()
132      */
createBitmap(InputStream input, boolean isMutable, Density density)133     public static Bitmap createBitmap(InputStream input, boolean isMutable, Density density)
134             throws IOException {
135         return createBitmap(input, getPremultipliedBitmapCreateFlags(isMutable), density);
136     }
137 
138     /**
139      * Creates and returns a {@link Bitmap} initialized with the given stream content.
140      *
141      * @param input the stream from which to read the bitmap content
142      * @param createFlags
143      * @param density the density associated with the bitmap
144      *
145      * @see Bitmap#isPremultiplied()
146      * @see Bitmap#isMutable()
147      * @see Bitmap#getDensity()
148      */
createBitmap(InputStream input, Set<BitmapCreateFlags> createFlags, Density density)149     public static Bitmap createBitmap(InputStream input, Set<BitmapCreateFlags> createFlags,
150             Density density) throws IOException {
151         // create a delegate with the content of the stream.
152         Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input), Config.ARGB_8888);
153 
154         return createBitmap(delegate, createFlags, density.getDpiValue());
155     }
156 
157     /**
158      * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
159      *
160      * @param image the bitmap content
161      * @param isMutable whether the bitmap is mutable
162      * @param density the density associated with the bitmap
163      *
164      * @see Bitmap#isMutable()
165      * @see Bitmap#getDensity()
166      */
createBitmap(BufferedImage image, boolean isMutable, Density density)167     public static Bitmap createBitmap(BufferedImage image, boolean isMutable,
168             Density density) throws IOException {
169         return createBitmap(image, getPremultipliedBitmapCreateFlags(isMutable), density);
170     }
171 
172     /**
173      * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
174      *
175      * @param image the bitmap content
176      * @param createFlags
177      * @param density the density associated with the bitmap
178      *
179      * @see Bitmap#isPremultiplied()
180      * @see Bitmap#isMutable()
181      * @see Bitmap#getDensity()
182      */
createBitmap(BufferedImage image, Set<BitmapCreateFlags> createFlags, Density density)183     public static Bitmap createBitmap(BufferedImage image, Set<BitmapCreateFlags> createFlags,
184             Density density) throws IOException {
185         // create a delegate with the given image.
186         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888);
187 
188         return createBitmap(delegate, createFlags, density.getDpiValue());
189     }
190 
191     /**
192      * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}.
193      */
getImage(Bitmap bitmap)194     public static BufferedImage getImage(Bitmap bitmap) {
195         // get the delegate from the native int.
196         Bitmap_Delegate delegate = sManager.getDelegate(bitmap.mNativeBitmap);
197         if (delegate == null) {
198             return null;
199         }
200 
201         return delegate.mImage;
202     }
203 
getBufferedImageType(int nativeBitmapConfig)204     public static int getBufferedImageType(int nativeBitmapConfig) {
205         switch (Config.nativeToConfig(nativeBitmapConfig)) {
206             case ALPHA_8:
207                 return BufferedImage.TYPE_INT_ARGB;
208             case RGB_565:
209                 return BufferedImage.TYPE_INT_ARGB;
210             case ARGB_4444:
211                 return BufferedImage.TYPE_INT_ARGB;
212             case ARGB_8888:
213                 return BufferedImage.TYPE_INT_ARGB;
214         }
215 
216         return BufferedImage.TYPE_INT_ARGB;
217     }
218 
219     /**
220      * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}.
221      */
getImage()222     public BufferedImage getImage() {
223         return mImage;
224     }
225 
226     /**
227      * Returns the Android bitmap config. Note that this not the config of the underlying
228      * Java2D bitmap.
229      */
getConfig()230     public Config getConfig() {
231         return mConfig;
232     }
233 
234     /**
235      * Returns the hasAlpha rendering hint
236      * @return true if the bitmap alpha should be used at render time
237      */
hasAlpha()238     public boolean hasAlpha() {
239         return mHasAlpha && mConfig != Config.RGB_565;
240     }
241 
hasMipMap()242     public boolean hasMipMap() {
243         // TODO: check if more checks are required as in hasAlpha.
244         return mHasMipMap;
245     }
246     /**
247      * Update the generationId.
248      *
249      * @see Bitmap#getGenerationId()
250      */
change()251     public void change() {
252         mGenerationId++;
253     }
254 
255     // ---- native methods ----
256 
257     @LayoutlibDelegate
nativeCreate(int[] colors, int offset, int stride, int width, int height, int nativeConfig, boolean isMutable)258     /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width,
259             int height, int nativeConfig, boolean isMutable) {
260         int imageType = getBufferedImageType(nativeConfig);
261 
262         // create the image
263         BufferedImage image = new BufferedImage(width, height, imageType);
264 
265         if (colors != null) {
266             image.setRGB(0, 0, width, height, colors, offset, stride);
267         }
268 
269         // create a delegate with the content of the stream.
270         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
271 
272         return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
273                             Bitmap.getDefaultDensity());
274     }
275 
276     @LayoutlibDelegate
nativeCopy(int srcBitmap, int nativeConfig, boolean isMutable)277     /*package*/ static Bitmap nativeCopy(int srcBitmap, int nativeConfig, boolean isMutable) {
278         Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(srcBitmap);
279         if (srcBmpDelegate == null) {
280             return null;
281         }
282 
283         BufferedImage srcImage = srcBmpDelegate.getImage();
284 
285         int width = srcImage.getWidth();
286         int height = srcImage.getHeight();
287 
288         int imageType = getBufferedImageType(nativeConfig);
289 
290         // create the image
291         BufferedImage image = new BufferedImage(width, height, imageType);
292 
293         // copy the source image into the image.
294         int[] argb = new int[width * height];
295         srcImage.getRGB(0, 0, width, height, argb, 0, width);
296         image.setRGB(0, 0, width, height, argb, 0, width);
297 
298         // create a delegate with the content of the stream.
299         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
300 
301         return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
302                 Bitmap.getDefaultDensity());
303     }
304 
305     @LayoutlibDelegate
nativeDestructor(int nativeBitmap)306     /*package*/ static void nativeDestructor(int nativeBitmap) {
307         sManager.removeJavaReferenceFor(nativeBitmap);
308     }
309 
310     @LayoutlibDelegate
nativeRecycle(int nativeBitmap)311     /*package*/ static boolean nativeRecycle(int nativeBitmap) {
312         sManager.removeJavaReferenceFor(nativeBitmap);
313         return true;
314     }
315 
316     @LayoutlibDelegate
nativeReconfigure(int nativeBitmap, int width, int height, int config, int allocSize)317     /*package*/ static void nativeReconfigure(int nativeBitmap, int width, int height,
318             int config, int allocSize) {
319         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
320                 "Bitmap.reconfigure() is not supported", null /*data*/);
321     }
322 
323     @LayoutlibDelegate
nativeCompress(int nativeBitmap, int format, int quality, OutputStream stream, byte[] tempStorage)324     /*package*/ static boolean nativeCompress(int nativeBitmap, int format, int quality,
325             OutputStream stream, byte[] tempStorage) {
326         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
327                 "Bitmap.compress() is not supported", null /*data*/);
328         return true;
329     }
330 
331     @LayoutlibDelegate
nativeErase(int nativeBitmap, int color)332     /*package*/ static void nativeErase(int nativeBitmap, int color) {
333         // get the delegate from the native int.
334         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
335         if (delegate == null) {
336             return;
337         }
338 
339         BufferedImage image = delegate.mImage;
340 
341         Graphics2D g = image.createGraphics();
342         try {
343             g.setColor(new java.awt.Color(color, true));
344 
345             g.fillRect(0, 0, image.getWidth(), image.getHeight());
346         } finally {
347             g.dispose();
348         }
349     }
350 
351     @LayoutlibDelegate
nativeRowBytes(int nativeBitmap)352     /*package*/ static int nativeRowBytes(int nativeBitmap) {
353         // get the delegate from the native int.
354         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
355         if (delegate == null) {
356             return 0;
357         }
358 
359         return delegate.mImage.getWidth();
360     }
361 
362     @LayoutlibDelegate
nativeConfig(int nativeBitmap)363     /*package*/ static int nativeConfig(int 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.mConfig.nativeInt;
371     }
372 
373     @LayoutlibDelegate
nativeHasAlpha(int nativeBitmap)374     /*package*/ static boolean nativeHasAlpha(int nativeBitmap) {
375         // get the delegate from the native int.
376         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
377         if (delegate == null) {
378             return true;
379         }
380 
381         return delegate.mHasAlpha;
382     }
383 
384     @LayoutlibDelegate
nativeHasMipMap(int nativeBitmap)385     /*package*/ static boolean nativeHasMipMap(int nativeBitmap) {
386         // get the delegate from the native int.
387         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
388         if (delegate == null) {
389             return true;
390         }
391 
392         return delegate.mHasMipMap;
393     }
394 
395     @LayoutlibDelegate
nativeGetPixel(int nativeBitmap, int x, int y, boolean isPremultiplied)396     /*package*/ static int nativeGetPixel(int nativeBitmap, int x, int y,
397             boolean isPremultiplied) {
398         // get the delegate from the native int.
399         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
400         if (delegate == null) {
401             return 0;
402         }
403 
404         // TODO: Support isPremultiplied.
405         return delegate.mImage.getRGB(x, y);
406     }
407 
408     @LayoutlibDelegate
nativeGetPixels(int nativeBitmap, int[] pixels, int offset, int stride, int x, int y, int width, int height, boolean isPremultiplied)409     /*package*/ static void nativeGetPixels(int nativeBitmap, int[] pixels, int offset,
410             int stride, int x, int y, int width, int height, boolean isPremultiplied) {
411         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
412         if (delegate == null) {
413             return;
414         }
415 
416         delegate.getImage().getRGB(x, y, width, height, pixels, offset, stride);
417     }
418 
419 
420     @LayoutlibDelegate
nativeSetPixel(int nativeBitmap, int x, int y, int color, boolean isPremultiplied)421     /*package*/ static void nativeSetPixel(int nativeBitmap, int x, int y, int color,
422             boolean isPremultiplied) {
423         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
424         if (delegate == null) {
425             return;
426         }
427 
428         delegate.getImage().setRGB(x, y, color);
429     }
430 
431     @LayoutlibDelegate
nativeSetPixels(int nativeBitmap, int[] colors, int offset, int stride, int x, int y, int width, int height, boolean isPremultiplied)432     /*package*/ static void nativeSetPixels(int nativeBitmap, int[] colors, int offset,
433             int stride, int x, int y, int width, int height, boolean isPremultiplied) {
434         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
435         if (delegate == null) {
436             return;
437         }
438 
439         delegate.getImage().setRGB(x, y, width, height, colors, offset, stride);
440     }
441 
442     @LayoutlibDelegate
nativeCopyPixelsToBuffer(int nativeBitmap, Buffer dst)443     /*package*/ static void nativeCopyPixelsToBuffer(int nativeBitmap, Buffer dst) {
444         // FIXME implement native delegate
445         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
446                 "Bitmap.copyPixelsToBuffer is not supported.", null, null /*data*/);
447     }
448 
449     @LayoutlibDelegate
nativeCopyPixelsFromBuffer(int nb, Buffer src)450     /*package*/ static void nativeCopyPixelsFromBuffer(int nb, Buffer src) {
451         // FIXME implement native delegate
452         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
453                 "Bitmap.copyPixelsFromBuffer is not supported.", null, null /*data*/);
454     }
455 
456     @LayoutlibDelegate
nativeGenerationId(int nativeBitmap)457     /*package*/ static int nativeGenerationId(int nativeBitmap) {
458         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
459         if (delegate == null) {
460             return 0;
461         }
462 
463         return delegate.mGenerationId;
464     }
465 
466     @LayoutlibDelegate
nativeCreateFromParcel(Parcel p)467     /*package*/ static Bitmap nativeCreateFromParcel(Parcel p) {
468         // This is only called by Bitmap.CREATOR (Parcelable.Creator<Bitmap>), which is only
469         // used during aidl call so really this should not be called.
470         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
471                 "AIDL is not suppored, and therefore Bitmaps cannot be created from parcels.",
472                 null /*data*/);
473         return null;
474     }
475 
476     @LayoutlibDelegate
nativeWriteToParcel(int nativeBitmap, boolean isMutable, int density, Parcel p)477     /*package*/ static boolean nativeWriteToParcel(int nativeBitmap, boolean isMutable,
478             int density, Parcel p) {
479         // This is only called when sending a bitmap through aidl, so really this should not
480         // be called.
481         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
482                 "AIDL is not suppored, and therefore Bitmaps cannot be written to parcels.",
483                 null /*data*/);
484         return false;
485     }
486 
487     @LayoutlibDelegate
nativeExtractAlpha(int nativeBitmap, int nativePaint, int[] offsetXY)488     /*package*/ static Bitmap nativeExtractAlpha(int nativeBitmap, int nativePaint,
489             int[] offsetXY) {
490         Bitmap_Delegate bitmap = sManager.getDelegate(nativeBitmap);
491         if (bitmap == null) {
492             return null;
493         }
494 
495         // get the paint which can be null if nativePaint is 0.
496         Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint);
497 
498         if (paint != null && paint.getMaskFilter() != null) {
499             Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
500                     "MaskFilter not supported in Bitmap.extractAlpha",
501                     null, null /*data*/);
502         }
503 
504         int alpha = paint != null ? paint.getAlpha() : 0xFF;
505         BufferedImage image = createCopy(bitmap.getImage(), BufferedImage.TYPE_INT_ARGB, alpha);
506 
507         // create the delegate. The actual Bitmap config is only an alpha channel
508         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ALPHA_8);
509 
510         // the density doesn't matter, it's set by the Java method.
511         return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.MUTABLE),
512                 Density.DEFAULT_DENSITY /*density*/);
513     }
514 
515     @LayoutlibDelegate
nativePrepareToDraw(int nativeBitmap)516     /*package*/ static void nativePrepareToDraw(int nativeBitmap) {
517         // nothing to be done here.
518     }
519 
520     @LayoutlibDelegate
nativeSetHasAlpha(int nativeBitmap, boolean hasAlpha)521     /*package*/ static void nativeSetHasAlpha(int nativeBitmap, boolean hasAlpha) {
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
nativeSetHasMipMap(int nativeBitmap, boolean hasMipMap)532     /*package*/ static void nativeSetHasMipMap(int 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
nativeSameAs(int nb0, int nb1)543     /*package*/ static boolean nativeSameAs(int nb0, int 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     // ---- Private delegate/helper methods ----
587 
Bitmap_Delegate(BufferedImage image, Config config)588     private Bitmap_Delegate(BufferedImage image, Config config) {
589         mImage = image;
590         mConfig = config;
591     }
592 
createBitmap(Bitmap_Delegate delegate, Set<BitmapCreateFlags> createFlags, int density)593     private static Bitmap createBitmap(Bitmap_Delegate delegate,
594             Set<BitmapCreateFlags> createFlags, int density) {
595         // get its native_int
596         int nativeInt = sManager.addNewDelegate(delegate);
597 
598         int width = delegate.mImage.getWidth();
599         int height = delegate.mImage.getHeight();
600         boolean isMutable = createFlags.contains(BitmapCreateFlags.MUTABLE);
601         boolean isPremultiplied = createFlags.contains(BitmapCreateFlags.PREMULTIPLIED);
602 
603         // and create/return a new Bitmap with it
604         return new Bitmap(nativeInt, null /* buffer */, width, height, density, isMutable,
605                           isPremultiplied, null /*ninePatchChunk*/, null /* layoutBounds */);
606     }
607 
getPremultipliedBitmapCreateFlags(boolean isMutable)608     private static Set<BitmapCreateFlags> getPremultipliedBitmapCreateFlags(boolean isMutable) {
609         Set<BitmapCreateFlags> createFlags = EnumSet.of(BitmapCreateFlags.PREMULTIPLIED);
610         if (isMutable) {
611             createFlags.add(BitmapCreateFlags.MUTABLE);
612         }
613         return createFlags;
614     }
615 
616     /**
617      * Creates and returns a copy of a given BufferedImage.
618      * <p/>
619      * if alpha is different than 255, then it is applied to the alpha channel of each pixel.
620      *
621      * @param image the image to copy
622      * @param imageType the type of the new image
623      * @param alpha an optional alpha modifier
624      * @return a new BufferedImage
625      */
createCopy(BufferedImage image, int imageType, int alpha)626     /*package*/ static BufferedImage createCopy(BufferedImage image, int imageType, int alpha) {
627         int w = image.getWidth();
628         int h = image.getHeight();
629 
630         BufferedImage result = new BufferedImage(w, h, imageType);
631 
632         int[] argb = new int[w * h];
633         image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
634 
635         if (alpha != 255) {
636             final int length = argb.length;
637             for (int i = 0 ; i < length; i++) {
638                 int a = (argb[i] >>> 24 * alpha) / 255;
639                 argb[i] = (a << 24) | (argb[i] & 0x00FFFFFF);
640             }
641         }
642 
643         result.setRGB(0, 0, w, h, argb, 0, w);
644 
645         return result;
646     }
647 
648 }
649