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.annotation.Nullable;
26 import android.graphics.Bitmap.Config;
27 import android.os.Parcel;
28 
29 import java.awt.Graphics2D;
30 import java.awt.image.BufferedImage;
31 import java.io.File;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.io.OutputStream;
35 import java.nio.Buffer;
36 import java.util.Arrays;
37 import java.util.EnumSet;
38 import java.util.Set;
39 
40 import javax.imageio.ImageIO;
41 
42 /**
43  * Delegate implementing the native methods of android.graphics.Bitmap
44  *
45  * Through the layoutlib_create tool, the original native methods of Bitmap have been replaced
46  * by calls to methods of the same name in this delegate class.
47  *
48  * This class behaves like the original native implementation, but in Java, keeping previously
49  * native data into its own objects and mapping them to int that are sent back and forth between
50  * it and the original Bitmap class.
51  *
52  * @see DelegateManager
53  *
54  */
55 public final class Bitmap_Delegate {
56 
57 
58     public enum BitmapCreateFlags {
59         PREMULTIPLIED, MUTABLE
60     }
61 
62     // ---- delegate manager ----
63     private static final DelegateManager<Bitmap_Delegate> sManager =
64             new DelegateManager<Bitmap_Delegate>(Bitmap_Delegate.class);
65 
66     // ---- delegate helper data ----
67 
68     // ---- delegate data ----
69     private final Config mConfig;
70     private BufferedImage mImage;
71     private boolean mHasAlpha = true;
72     private boolean mHasMipMap = false;      // TODO: check the default.
73     private boolean mIsPremultiplied = true;
74     private int mGenerationId = 0;
75 
76 
77     // ---- Public Helper methods ----
78 
79     /**
80      * Returns the native delegate associated to a given an int referencing a {@link Bitmap} object.
81      */
getDelegate(long native_bitmap)82     public static Bitmap_Delegate getDelegate(long native_bitmap) {
83         return sManager.getDelegate(native_bitmap);
84     }
85 
86     @Nullable
getDelegate(@ullable Bitmap bitmap)87     public static Bitmap_Delegate getDelegate(@Nullable Bitmap bitmap) {
88         // refSkPixelRef is a hack to get the native pointer: see #nativeRefPixelRef()
89         return bitmap == null ? null : getDelegate(bitmap.refSkPixelRef());
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 
getBufferedImageType()190     private static int getBufferedImageType() {
191         return BufferedImage.TYPE_INT_ARGB;
192     }
193 
194     /**
195      * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}.
196      */
getImage()197     public BufferedImage getImage() {
198         return mImage;
199     }
200 
201     /**
202      * Returns the Android bitmap config. Note that this not the config of the underlying
203      * Java2D bitmap.
204      */
getConfig()205     public Config getConfig() {
206         return mConfig;
207     }
208 
209     /**
210      * Returns the hasAlpha rendering hint
211      * @return true if the bitmap alpha should be used at render time
212      */
hasAlpha()213     public boolean hasAlpha() {
214         return mHasAlpha && mConfig != Config.RGB_565;
215     }
216 
217     /**
218      * Update the generationId.
219      *
220      * @see Bitmap#getGenerationId()
221      */
change()222     public void change() {
223         mGenerationId++;
224     }
225 
226     // ---- native methods ----
227 
228     @LayoutlibDelegate
nativeCreate(int[] colors, int offset, int stride, int width, int height, int nativeConfig, boolean isMutable)229     /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width,
230             int height, int nativeConfig, boolean isMutable) {
231         int imageType = getBufferedImageType();
232 
233         // create the image
234         BufferedImage image = new BufferedImage(width, height, imageType);
235 
236         if (colors != null) {
237             image.setRGB(0, 0, width, height, colors, offset, stride);
238         }
239 
240         // create a delegate with the content of the stream.
241         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
242 
243         return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
244                             Bitmap.getDefaultDensity());
245     }
246 
247     @LayoutlibDelegate
nativeCopy(long srcBitmap, int nativeConfig, boolean isMutable)248     /*package*/ static Bitmap nativeCopy(long srcBitmap, int nativeConfig, boolean isMutable) {
249         Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(srcBitmap);
250         if (srcBmpDelegate == null) {
251             return null;
252         }
253 
254         BufferedImage srcImage = srcBmpDelegate.getImage();
255 
256         int width = srcImage.getWidth();
257         int height = srcImage.getHeight();
258 
259         int imageType = getBufferedImageType();
260 
261         // create the image
262         BufferedImage image = new BufferedImage(width, height, imageType);
263 
264         // copy the source image into the image.
265         int[] argb = new int[width * height];
266         srcImage.getRGB(0, 0, width, height, argb, 0, width);
267         image.setRGB(0, 0, width, height, argb, 0, width);
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
nativeCopyAshmem(long nativeSrcBitmap)277     /*package*/ static Bitmap nativeCopyAshmem(long nativeSrcBitmap) {
278         // Unused method; no implementation provided.
279         assert false;
280         return null;
281     }
282 
283     @LayoutlibDelegate
nativeDestructor(long nativeBitmap)284     /*package*/ static void nativeDestructor(long nativeBitmap) {
285         sManager.removeJavaReferenceFor(nativeBitmap);
286     }
287 
288     @LayoutlibDelegate
nativeRecycle(long nativeBitmap)289     /*package*/ static boolean nativeRecycle(long nativeBitmap) {
290         sManager.removeJavaReferenceFor(nativeBitmap);
291         return true;
292     }
293 
294     @LayoutlibDelegate
nativeReconfigure(long nativeBitmap, int width, int height, int config, int allocSize, boolean isPremultiplied)295     /*package*/ static void nativeReconfigure(long nativeBitmap, int width, int height,
296             int config, int allocSize, boolean isPremultiplied) {
297         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
298                 "Bitmap.reconfigure() is not supported", null /*data*/);
299     }
300 
301     @LayoutlibDelegate
nativeCompress(long nativeBitmap, int format, int quality, OutputStream stream, byte[] tempStorage)302     /*package*/ static boolean nativeCompress(long nativeBitmap, int format, int quality,
303             OutputStream stream, byte[] tempStorage) {
304         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
305                 "Bitmap.compress() is not supported", null /*data*/);
306         return true;
307     }
308 
309     @LayoutlibDelegate
nativeErase(long nativeBitmap, int color)310     /*package*/ static void nativeErase(long nativeBitmap, int color) {
311         // get the delegate from the native int.
312         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
313         if (delegate == null) {
314             return;
315         }
316 
317         BufferedImage image = delegate.mImage;
318 
319         Graphics2D g = image.createGraphics();
320         try {
321             g.setColor(new java.awt.Color(color, true));
322 
323             g.fillRect(0, 0, image.getWidth(), image.getHeight());
324         } finally {
325             g.dispose();
326         }
327     }
328 
329     @LayoutlibDelegate
nativeRowBytes(long nativeBitmap)330     /*package*/ static int nativeRowBytes(long nativeBitmap) {
331         // get the delegate from the native int.
332         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
333         if (delegate == null) {
334             return 0;
335         }
336 
337         return delegate.mImage.getWidth();
338     }
339 
340     @LayoutlibDelegate
nativeConfig(long nativeBitmap)341     /*package*/ static int nativeConfig(long nativeBitmap) {
342         // get the delegate from the native int.
343         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
344         if (delegate == null) {
345             return 0;
346         }
347 
348         return delegate.mConfig.nativeInt;
349     }
350 
351     @LayoutlibDelegate
nativeHasAlpha(long nativeBitmap)352     /*package*/ static boolean nativeHasAlpha(long nativeBitmap) {
353         // get the delegate from the native int.
354         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
355         return delegate == null || delegate.mHasAlpha;
356 
357     }
358 
359     @LayoutlibDelegate
nativeHasMipMap(long nativeBitmap)360     /*package*/ static boolean nativeHasMipMap(long nativeBitmap) {
361         // get the delegate from the native int.
362         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
363         return delegate == null || delegate.mHasMipMap;
364 
365     }
366 
367     @LayoutlibDelegate
nativeGetPixel(long nativeBitmap, int x, int y)368     /*package*/ static int nativeGetPixel(long nativeBitmap, int x, int y) {
369         // get the delegate from the native int.
370         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
371         if (delegate == null) {
372             return 0;
373         }
374 
375         return delegate.mImage.getRGB(x, y);
376     }
377 
378     @LayoutlibDelegate
nativeGetPixels(long nativeBitmap, int[] pixels, int offset, int stride, int x, int y, int width, int height)379     /*package*/ static void nativeGetPixels(long nativeBitmap, int[] pixels, int offset,
380             int stride, int x, int y, int width, int height) {
381         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
382         if (delegate == null) {
383             return;
384         }
385 
386         delegate.getImage().getRGB(x, y, width, height, pixels, offset, stride);
387     }
388 
389 
390     @LayoutlibDelegate
nativeSetPixel(long nativeBitmap, int x, int y, int color)391     /*package*/ static void nativeSetPixel(long nativeBitmap, int x, int y, int color) {
392         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
393         if (delegate == null) {
394             return;
395         }
396 
397         delegate.getImage().setRGB(x, y, color);
398     }
399 
400     @LayoutlibDelegate
nativeSetPixels(long nativeBitmap, int[] colors, int offset, int stride, int x, int y, int width, int height)401     /*package*/ static void nativeSetPixels(long nativeBitmap, int[] colors, int offset,
402             int stride, int x, int y, int width, int height) {
403         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
404         if (delegate == null) {
405             return;
406         }
407 
408         delegate.getImage().setRGB(x, y, width, height, colors, offset, stride);
409     }
410 
411     @LayoutlibDelegate
nativeCopyPixelsToBuffer(long nativeBitmap, Buffer dst)412     /*package*/ static void nativeCopyPixelsToBuffer(long nativeBitmap, Buffer dst) {
413         // FIXME implement native delegate
414         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
415                 "Bitmap.copyPixelsToBuffer is not supported.", null, null /*data*/);
416     }
417 
418     @LayoutlibDelegate
nativeCopyPixelsFromBuffer(long nb, Buffer src)419     /*package*/ static void nativeCopyPixelsFromBuffer(long nb, Buffer src) {
420         // FIXME implement native delegate
421         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
422                 "Bitmap.copyPixelsFromBuffer is not supported.", null, null /*data*/);
423     }
424 
425     @LayoutlibDelegate
nativeGenerationId(long nativeBitmap)426     /*package*/ static int nativeGenerationId(long nativeBitmap) {
427         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
428         if (delegate == null) {
429             return 0;
430         }
431 
432         return delegate.mGenerationId;
433     }
434 
435     @LayoutlibDelegate
nativeCreateFromParcel(Parcel p)436     /*package*/ static Bitmap nativeCreateFromParcel(Parcel p) {
437         // This is only called by Bitmap.CREATOR (Parcelable.Creator<Bitmap>), which is only
438         // used during aidl call so really this should not be called.
439         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
440                 "AIDL is not suppored, and therefore Bitmaps cannot be created from parcels.",
441                 null /*data*/);
442         return null;
443     }
444 
445     @LayoutlibDelegate
nativeWriteToParcel(long nativeBitmap, boolean isMutable, int density, Parcel p)446     /*package*/ static boolean nativeWriteToParcel(long nativeBitmap, boolean isMutable,
447             int density, Parcel p) {
448         // This is only called when sending a bitmap through aidl, so really this should not
449         // be called.
450         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
451                 "AIDL is not suppored, and therefore Bitmaps cannot be written to parcels.",
452                 null /*data*/);
453         return false;
454     }
455 
456     @LayoutlibDelegate
nativeExtractAlpha(long nativeBitmap, long nativePaint, int[] offsetXY)457     /*package*/ static Bitmap nativeExtractAlpha(long nativeBitmap, long nativePaint,
458             int[] offsetXY) {
459         Bitmap_Delegate bitmap = sManager.getDelegate(nativeBitmap);
460         if (bitmap == null) {
461             return null;
462         }
463 
464         // get the paint which can be null if nativePaint is 0.
465         Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint);
466 
467         if (paint != null && paint.getMaskFilter() != null) {
468             Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
469                     "MaskFilter not supported in Bitmap.extractAlpha",
470                     null, null /*data*/);
471         }
472 
473         int alpha = paint != null ? paint.getAlpha() : 0xFF;
474         BufferedImage image = createCopy(bitmap.getImage(), BufferedImage.TYPE_INT_ARGB, alpha);
475 
476         // create the delegate. The actual Bitmap config is only an alpha channel
477         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ALPHA_8);
478 
479         // the density doesn't matter, it's set by the Java method.
480         return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.MUTABLE),
481                 Density.DEFAULT_DENSITY /*density*/);
482     }
483 
484     @LayoutlibDelegate
nativeIsPremultiplied(long nativeBitmap)485     /*package*/ static boolean nativeIsPremultiplied(long nativeBitmap) {
486         // get the delegate from the native int.
487         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
488         return delegate != null && delegate.mIsPremultiplied;
489 
490     }
491 
492     @LayoutlibDelegate
nativeSetPremultiplied(long nativeBitmap, boolean isPremul)493     /*package*/ static void nativeSetPremultiplied(long nativeBitmap, boolean isPremul) {
494         // get the delegate from the native int.
495         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
496         if (delegate == null) {
497             return;
498         }
499 
500         delegate.mIsPremultiplied = isPremul;
501     }
502 
503     @LayoutlibDelegate
nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha, boolean isPremul)504     /*package*/ static void nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha,
505             boolean isPremul) {
506         // get the delegate from the native int.
507         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
508         if (delegate == null) {
509             return;
510         }
511 
512         delegate.mHasAlpha = hasAlpha;
513     }
514 
515     @LayoutlibDelegate
nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap)516     /*package*/ static void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap) {
517         // get the delegate from the native int.
518         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
519         if (delegate == null) {
520             return;
521         }
522 
523         delegate.mHasMipMap = hasMipMap;
524     }
525 
526     @LayoutlibDelegate
nativeSameAs(long nb0, long nb1)527     /*package*/ static boolean nativeSameAs(long nb0, long nb1) {
528         Bitmap_Delegate delegate1 = sManager.getDelegate(nb0);
529         if (delegate1 == null) {
530             return false;
531         }
532 
533         Bitmap_Delegate delegate2 = sManager.getDelegate(nb1);
534         if (delegate2 == null) {
535             return false;
536         }
537 
538         BufferedImage image1 = delegate1.getImage();
539         BufferedImage image2 = delegate2.getImage();
540         if (delegate1.mConfig != delegate2.mConfig ||
541                 image1.getWidth() != image2.getWidth() ||
542                 image1.getHeight() != image2.getHeight()) {
543             return false;
544         }
545 
546         // get the internal data
547         int w = image1.getWidth();
548         int h = image2.getHeight();
549         int[] argb1 = new int[w*h];
550         int[] argb2 = new int[w*h];
551 
552         image1.getRGB(0, 0, w, h, argb1, 0, w);
553         image2.getRGB(0, 0, w, h, argb2, 0, w);
554 
555         // compares
556         if (delegate1.mConfig == Config.ALPHA_8) {
557             // in this case we have to manually compare the alpha channel as the rest is garbage.
558             final int length = w*h;
559             for (int i = 0 ; i < length ; i++) {
560                 if ((argb1[i] & 0xFF000000) != (argb2[i] & 0xFF000000)) {
561                     return false;
562                 }
563             }
564             return true;
565         }
566 
567         return Arrays.equals(argb1, argb2);
568     }
569 
570     // Only used by AssetAtlasService, which we don't care about.
571     @LayoutlibDelegate
nativeRefPixelRef(long nativeBitmap)572     /*package*/ static long nativeRefPixelRef(long nativeBitmap) {
573         // Hack: This is called by Bitmap.refSkPixelRef() and LayoutLib uses that method to get
574         // the native pointer from a Bitmap. So, we return nativeBitmap here.
575         return nativeBitmap;
576     }
577 
578     // ---- Private delegate/helper methods ----
579 
Bitmap_Delegate(BufferedImage image, Config config)580     private Bitmap_Delegate(BufferedImage image, Config config) {
581         mImage = image;
582         mConfig = config;
583     }
584 
createBitmap(Bitmap_Delegate delegate, Set<BitmapCreateFlags> createFlags, int density)585     private static Bitmap createBitmap(Bitmap_Delegate delegate,
586             Set<BitmapCreateFlags> createFlags, int density) {
587         // get its native_int
588         long nativeInt = sManager.addNewDelegate(delegate);
589 
590         int width = delegate.mImage.getWidth();
591         int height = delegate.mImage.getHeight();
592         boolean isMutable = createFlags.contains(BitmapCreateFlags.MUTABLE);
593         boolean isPremultiplied = createFlags.contains(BitmapCreateFlags.PREMULTIPLIED);
594 
595         // and create/return a new Bitmap with it
596         return new Bitmap(nativeInt, null /* buffer */, width, height, density, isMutable,
597                           isPremultiplied, null /*ninePatchChunk*/, null /* layoutBounds */);
598     }
599 
getPremultipliedBitmapCreateFlags(boolean isMutable)600     private static Set<BitmapCreateFlags> getPremultipliedBitmapCreateFlags(boolean isMutable) {
601         Set<BitmapCreateFlags> createFlags = EnumSet.of(BitmapCreateFlags.PREMULTIPLIED);
602         if (isMutable) {
603             createFlags.add(BitmapCreateFlags.MUTABLE);
604         }
605         return createFlags;
606     }
607 
608     /**
609      * Creates and returns a copy of a given BufferedImage.
610      * <p/>
611      * if alpha is different than 255, then it is applied to the alpha channel of each pixel.
612      *
613      * @param image the image to copy
614      * @param imageType the type of the new image
615      * @param alpha an optional alpha modifier
616      * @return a new BufferedImage
617      */
createCopy(BufferedImage image, int imageType, int alpha)618     /*package*/ static BufferedImage createCopy(BufferedImage image, int imageType, int alpha) {
619         int w = image.getWidth();
620         int h = image.getHeight();
621 
622         BufferedImage result = new BufferedImage(w, h, imageType);
623 
624         int[] argb = new int[w * h];
625         image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
626 
627         if (alpha != 255) {
628             final int length = argb.length;
629             for (int i = 0 ; i < length; i++) {
630                 int a = (argb[i] >>> 24 * alpha) / 255;
631                 argb[i] = (a << 24) | (argb[i] & 0x00FFFFFF);
632             }
633         }
634 
635         result.setRGB(0, 0, w, h, argb, 0, w);
636 
637         return result;
638     }
639 
640 }
641