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