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