• 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.AssetRepository;
20 import com.android.ide.common.rendering.api.ILayoutLog;
21 import com.android.ide.common.rendering.api.RenderResources;
22 import com.android.ide.common.rendering.api.ResourceValue;
23 import com.android.layoutlib.bridge.Bridge;
24 import com.android.layoutlib.bridge.android.BridgeContext;
25 import com.android.layoutlib.bridge.impl.DelegateManager;
26 import com.android.layoutlib.bridge.impl.RenderAction;
27 import com.android.resources.Density;
28 import com.android.resources.ResourceType;
29 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
30 
31 import android.annotation.Nullable;
32 import android.graphics.Bitmap.Config;
33 import android.hardware.HardwareBuffer;
34 import android.os.Parcel;
35 
36 import java.awt.Graphics2D;
37 import java.awt.image.BufferedImage;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.OutputStream;
41 import java.nio.Buffer;
42 import java.util.Arrays;
43 import java.util.EnumSet;
44 import java.util.Set;
45 
46 import javax.imageio.ImageIO;
47 import libcore.util.NativeAllocationRegistry_Delegate;
48 
49 import static android.content.res.AssetManager.ACCESS_STREAMING;
50 
51 /**
52  * Delegate implementing the native methods of android.graphics.Bitmap
53  *
54  * Through the layoutlib_create tool, the original native methods of Bitmap have been replaced
55  * by calls to methods of the same name in this delegate class.
56  *
57  * This class behaves like the original native implementation, but in Java, keeping previously
58  * native data into its own objects and mapping them to int that are sent back and forth between
59  * it and the original Bitmap class.
60  *
61  * @see DelegateManager
62  *
63  */
64 public final class Bitmap_Delegate {
65 
66     public enum BitmapCreateFlags {
67         NONE, PREMULTIPLIED, MUTABLE
68     }
69 
70     // ---- delegate manager ----
71     private static final DelegateManager<Bitmap_Delegate> sManager =
72             new DelegateManager<>(Bitmap_Delegate.class);
73     private static long sFinalizer = -1;
74 
75     // ---- delegate helper data ----
76 
77     // ---- delegate data ----
78     private final Config mConfig;
79     private final BufferedImage mImage;
80     private boolean mHasAlpha = true;
81     private boolean mHasMipMap = false;      // TODO: check the default.
82     private boolean mIsPremultiplied = true;
83     private int mGenerationId = 0;
84     private boolean mIsMutable;
85 
86 
87     // ---- Public Helper methods ----
88 
89     /**
90      * Returns the native delegate associated to a given an int referencing a {@link Bitmap} object.
91      */
getDelegate(long native_bitmap)92     public static Bitmap_Delegate getDelegate(long native_bitmap) {
93         return sManager.getDelegate(native_bitmap);
94     }
95 
96     /**
97      * Creates and returns a {@link Bitmap} initialized with the given stream content.
98      *
99      * @param input the stream from which to read the bitmap content
100      * @param isMutable whether the bitmap is mutable
101      * @param density the density associated with the bitmap
102      *
103      * @see Bitmap#isMutable()
104      * @see Bitmap#getDensity()
105      */
createBitmap(@ullable InputStream input, boolean isMutable, Density density)106     public static Bitmap createBitmap(@Nullable InputStream input, boolean isMutable,
107             Density density) throws IOException {
108         return createBitmap(input, getPremultipliedBitmapCreateFlags(isMutable), density);
109     }
110 
111     /**
112      * Creates and returns a {@link Bitmap} initialized with the given file content.
113      *
114      * @param input the file from which to read the bitmap content
115      * @param density the density associated with the bitmap
116      *
117      * @see Bitmap#isPremultiplied()
118      * @see Bitmap#isMutable()
119      * @see Bitmap#getDensity()
120      */
createBitmap(@ullable InputStream input, Set<BitmapCreateFlags> createFlags, Density density)121     static Bitmap createBitmap(@Nullable InputStream input, Set<BitmapCreateFlags> createFlags,
122             Density density) throws IOException {
123         // create a delegate with the content of the file.
124         BufferedImage image = input == null ? null : ImageIO.read(input);
125         if (image == null) {
126             // There was a problem decoding the image, or the decoder isn't registered. Webp maybe.
127             // Replace with a broken image icon.
128             BridgeContext currentContext = RenderAction.getCurrentContext();
129             if (currentContext != null) {
130                 RenderResources resources = currentContext.getRenderResources();
131                 ResourceValue broken = resources.getResolvedResource(
132                         BridgeContext.createFrameworkResourceReference(
133                                 ResourceType.DRAWABLE, "ic_menu_report_image"));
134                 AssetRepository assetRepository = currentContext.getAssets().getAssetRepository();
135                 try (InputStream stream =
136                         assetRepository.openNonAsset(0, broken.getValue(), ACCESS_STREAMING)) {
137                     if (stream != null) {
138                         image = ImageIO.read(stream);
139                     }
140                 }
141             }
142         }
143         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888);
144         delegate.mIsMutable = createFlags.contains(BitmapCreateFlags.MUTABLE);
145 
146         return createBitmap(delegate, createFlags, density.getDpiValue(), null);
147     }
148 
149     /**
150      * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
151      *
152      * @param image the bitmap content
153      * @param isMutable whether the bitmap is mutable
154      * @param density the density associated with the bitmap
155      *
156      * @see Bitmap#isMutable()
157      * @see Bitmap#getDensity()
158      */
createBitmap(BufferedImage image, boolean isMutable, Density density)159     public static Bitmap createBitmap(BufferedImage image, boolean isMutable, Density density) {
160         return createBitmap(image, getPremultipliedBitmapCreateFlags(isMutable), density);
161     }
162 
163     /**
164      * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
165      *
166      * @param image the bitmap content
167      * @param density the density associated with the bitmap
168      *
169      * @see Bitmap#isPremultiplied()
170      * @see Bitmap#isMutable()
171      * @see Bitmap#getDensity()
172      */
createBitmap(BufferedImage image, Set<BitmapCreateFlags> createFlags, Density density)173     public static Bitmap createBitmap(BufferedImage image, Set<BitmapCreateFlags> createFlags,
174             Density density) {
175         return createBitmap(image, null, createFlags, density);
176     }
177 
178     /**
179      * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
180      *
181      * @param image the bitmap content
182      * @param ninePatchChunk serialized ninepatch data
183      * @param density the density associated with the bitmap
184      *
185      * @see Bitmap#isPremultiplied()
186      * @see Bitmap#isMutable()
187      * @see Bitmap#getDensity()
188      */
createBitmap(BufferedImage image, byte[] ninePatchChunk, Set<BitmapCreateFlags> createFlags, Density density)189     public static Bitmap createBitmap(BufferedImage image, byte[] ninePatchChunk,
190             Set<BitmapCreateFlags> createFlags, Density density) {
191         // create a delegate with the given image.
192         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888);
193         delegate.mIsMutable = createFlags.contains(BitmapCreateFlags.MUTABLE);
194 
195         return createBitmap(delegate, createFlags, density.getDpiValue(), ninePatchChunk);
196     }
197 
getBufferedImageType()198     private static int getBufferedImageType() {
199         return BufferedImage.TYPE_INT_ARGB;
200     }
201 
202     /**
203      * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}.
204      */
getImage()205     public BufferedImage getImage() {
206         return mImage;
207     }
208 
209     /**
210      * Returns the Android bitmap config. Note that this not the config of the underlying
211      * Java2D bitmap.
212      */
getConfig()213     public Config getConfig() {
214         return mConfig;
215     }
216 
217     /**
218      * Returns the hasAlpha rendering hint
219      * @return true if the bitmap alpha should be used at render time
220      */
hasAlpha()221     public boolean hasAlpha() {
222         return mHasAlpha && mConfig != Config.RGB_565;
223     }
224 
225     /**
226      * Update the generationId.
227      *
228      * @see Bitmap#getGenerationId()
229      */
change()230     public void change() {
231         mGenerationId++;
232     }
233 
234     // ---- native methods ----
235 
236     @LayoutlibDelegate
nativeCreate(int[] colors, int offset, int stride, int width, int height, int nativeConfig, boolean isMutable, long nativeColorSpace)237     /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width,
238             int height, int nativeConfig, boolean isMutable, long nativeColorSpace) {
239         int imageType = getBufferedImageType();
240 
241         // create the image
242         BufferedImage image = new BufferedImage(width, height, imageType);
243 
244         if (colors != null) {
245             image.setRGB(0, 0, width, height, colors, offset, stride);
246         }
247 
248         // create a delegate with the content of the stream.
249         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
250         delegate.mIsMutable = isMutable;
251 
252         return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
253                             Bitmap.getDefaultDensity(), null);
254     }
255 
256     @LayoutlibDelegate
nativeCopy(long srcBitmap, int nativeConfig, boolean isMutable)257     /*package*/ static Bitmap nativeCopy(long srcBitmap, int nativeConfig, boolean isMutable) {
258         Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(srcBitmap);
259         if (srcBmpDelegate == null) {
260             return null;
261         }
262 
263         BufferedImage srcImage = srcBmpDelegate.getImage();
264 
265         int width = srcImage.getWidth();
266         int height = srcImage.getHeight();
267 
268         int imageType = getBufferedImageType();
269 
270         // create the image
271         BufferedImage image = new BufferedImage(width, height, imageType);
272 
273         // copy the source image into the image.
274         int[] argb = new int[width * height];
275         srcImage.getRGB(0, 0, width, height, argb, 0, width);
276         image.setRGB(0, 0, width, height, argb, 0, width);
277 
278         // create a delegate with the content of the stream.
279         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
280         delegate.mIsMutable = isMutable;
281 
282         return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
283                 Bitmap.getDefaultDensity(), null);
284     }
285 
286     @LayoutlibDelegate
nativeCopyAshmem(long nativeSrcBitmap)287     /*package*/ static Bitmap nativeCopyAshmem(long nativeSrcBitmap) {
288         // Unused method; no implementation provided.
289         assert false;
290         return null;
291     }
292 
293     @LayoutlibDelegate
nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig)294     /*package*/ static Bitmap nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig) {
295         // Unused method; no implementation provided.
296         assert false;
297         return null;
298     }
299 
300     @LayoutlibDelegate
nativeGetNativeFinalizer()301     /*package*/ static long nativeGetNativeFinalizer() {
302         synchronized (Bitmap_Delegate.class) {
303             if (sFinalizer == -1) {
304                 sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
305             }
306             return sFinalizer;
307         }
308     }
309 
310     @LayoutlibDelegate
nativeRecycle(long nativeBitmap)311     /*package*/ static void nativeRecycle(long nativeBitmap) {
312         // In our case recycle() is a no-op. We will let the finalizer to dispose the bitmap.
313     }
314 
315     @LayoutlibDelegate
nativeReconfigure(long nativeBitmap, int width, int height, int config, boolean isPremultiplied)316     /*package*/ static void nativeReconfigure(long nativeBitmap, int width, int height,
317             int config, boolean isPremultiplied) {
318         Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
319                 "Bitmap.reconfigure() is not supported", null, 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(ILayoutLog.TAG_UNSUPPORTED,
326                 "Bitmap.compress() is not supported", null, 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
nativeErase(long nativeBitmap, long colorSpacePtr, long color)351     /*package*/ static void nativeErase(long nativeBitmap, long colorSpacePtr, long color) {
352         nativeErase(nativeBitmap, Color.toArgb(color));
353     }
354 
355     @LayoutlibDelegate
nativeRowBytes(long nativeBitmap)356     /*package*/ static int nativeRowBytes(long nativeBitmap) {
357         // get the delegate from the native int.
358         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
359         if (delegate == null) {
360             return 0;
361         }
362 
363         return delegate.mImage.getWidth();
364     }
365 
366     @LayoutlibDelegate
nativeConfig(long nativeBitmap)367     /*package*/ static int nativeConfig(long nativeBitmap) {
368         // get the delegate from the native int.
369         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
370         if (delegate == null) {
371             return 0;
372         }
373 
374         return delegate.mConfig.nativeInt;
375     }
376 
377     @LayoutlibDelegate
nativeHasAlpha(long nativeBitmap)378     /*package*/ static boolean nativeHasAlpha(long nativeBitmap) {
379         // get the delegate from the native int.
380         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
381         return delegate == null || delegate.mHasAlpha;
382 
383     }
384 
385     @LayoutlibDelegate
nativeHasMipMap(long nativeBitmap)386     /*package*/ static boolean nativeHasMipMap(long nativeBitmap) {
387         // get the delegate from the native int.
388         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
389         return delegate == null || delegate.mHasMipMap;
390 
391     }
392 
393     @LayoutlibDelegate
nativeGetPixel(long nativeBitmap, int x, int y)394     /*package*/ static int nativeGetPixel(long nativeBitmap, int x, int y) {
395         // get the delegate from the native int.
396         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
397         if (delegate == null) {
398             return 0;
399         }
400 
401         return delegate.mImage.getRGB(x, y);
402     }
403 
404     @LayoutlibDelegate
nativeGetColor(long nativeBitmap, int x, int y)405     /*package*/ static long nativeGetColor(long nativeBitmap, int x, int y) {
406         return nativeGetPixel(nativeBitmap, x, y);
407     }
408 
409     @LayoutlibDelegate
nativeGetPixels(long nativeBitmap, int[] pixels, int offset, int stride, int x, int y, int width, int height)410     /*package*/ static void nativeGetPixels(long nativeBitmap, int[] pixels, int offset,
411             int stride, int x, int y, int width, int height) {
412         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
413         if (delegate == null) {
414             return;
415         }
416 
417         delegate.getImage().getRGB(x, y, width, height, pixels, offset, stride);
418     }
419 
420 
421     @LayoutlibDelegate
nativeSetPixel(long nativeBitmap, int x, int y, int color)422     /*package*/ static void nativeSetPixel(long nativeBitmap, int x, int y, int color) {
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(long nativeBitmap, int[] colors, int offset, int stride, int x, int y, int width, int height)432     /*package*/ static void nativeSetPixels(long nativeBitmap, int[] colors, int offset,
433             int stride, int x, int y, int width, int height) {
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(long nativeBitmap, Buffer dst)443     /*package*/ static void nativeCopyPixelsToBuffer(long nativeBitmap, Buffer dst) {
444         // FIXME implement native delegate
445         Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
446                 "Bitmap.copyPixelsToBuffer is not supported.", null, null, null /*data*/);
447     }
448 
449     @LayoutlibDelegate
nativeCopyPixelsFromBuffer(long nb, Buffer src)450     /*package*/ static void nativeCopyPixelsFromBuffer(long nb, Buffer src) {
451         // FIXME implement native delegate
452         Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
453                 "Bitmap.copyPixelsFromBuffer is not supported.", null, null, null /*data*/);
454     }
455 
456     @LayoutlibDelegate
nativeGenerationId(long nativeBitmap)457     /*package*/ static int nativeGenerationId(long 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(ILayoutLog.TAG_UNSUPPORTED,
471                 "AIDL is not suppored, and therefore Bitmaps cannot be created from parcels.",
472                 null, null /*data*/);
473         return null;
474     }
475 
476     @LayoutlibDelegate
nativeWriteToParcel(long nativeBitmap, int density, Parcel p)477     /*package*/ static boolean nativeWriteToParcel(long nativeBitmap, int density, Parcel p) {
478         // This is only called when sending a bitmap through aidl, so really this should not
479         // be called.
480         Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
481                 "AIDL is not suppored, and therefore Bitmaps cannot be written to parcels.",
482                 null, null /*data*/);
483         return false;
484     }
485 
486     @LayoutlibDelegate
nativeExtractAlpha(long nativeBitmap, long nativePaint, int[] offsetXY)487     /*package*/ static Bitmap nativeExtractAlpha(long nativeBitmap, long nativePaint,
488             int[] offsetXY) {
489         Bitmap_Delegate bitmap = sManager.getDelegate(nativeBitmap);
490         if (bitmap == null) {
491             return null;
492         }
493 
494         // get the paint which can be null if nativePaint is 0.
495         Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint);
496 
497         if (paint != null && paint.getMaskFilter() != null) {
498             Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MASKFILTER,
499                     "MaskFilter not supported in Bitmap.extractAlpha",
500                     null, null, null /*data*/);
501         }
502 
503         int alpha = paint != null ? paint.getAlpha() : 0xFF;
504         BufferedImage image = createCopy(bitmap.getImage(), BufferedImage.TYPE_INT_ARGB, alpha);
505 
506         // create the delegate. The actual Bitmap config is only an alpha channel
507         Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ALPHA_8);
508         delegate.mIsMutable = true;
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*/, null);
513     }
514 
515     @LayoutlibDelegate
nativeIsPremultiplied(long nativeBitmap)516     /*package*/ static boolean nativeIsPremultiplied(long nativeBitmap) {
517         // get the delegate from the native int.
518         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
519         return delegate != null && delegate.mIsPremultiplied;
520 
521     }
522 
523     @LayoutlibDelegate
nativeSetPremultiplied(long nativeBitmap, boolean isPremul)524     /*package*/ static void nativeSetPremultiplied(long nativeBitmap, boolean isPremul) {
525         // get the delegate from the native int.
526         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
527         if (delegate == null) {
528             return;
529         }
530 
531         delegate.mIsPremultiplied = isPremul;
532     }
533 
534     @LayoutlibDelegate
nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha, boolean isPremul)535     /*package*/ static void nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha,
536             boolean isPremul) {
537         // get the delegate from the native int.
538         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
539         if (delegate == null) {
540             return;
541         }
542 
543         delegate.mHasAlpha = hasAlpha;
544     }
545 
546     @LayoutlibDelegate
nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap)547     /*package*/ static void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap) {
548         // get the delegate from the native int.
549         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
550         if (delegate == null) {
551             return;
552         }
553 
554         delegate.mHasMipMap = hasMipMap;
555     }
556 
557     @LayoutlibDelegate
nativeSameAs(long nb0, long nb1)558     /*package*/ static boolean nativeSameAs(long nb0, long nb1) {
559         Bitmap_Delegate delegate1 = sManager.getDelegate(nb0);
560         if (delegate1 == null) {
561             return false;
562         }
563 
564         Bitmap_Delegate delegate2 = sManager.getDelegate(nb1);
565         if (delegate2 == null) {
566             return false;
567         }
568 
569         BufferedImage image1 = delegate1.getImage();
570         BufferedImage image2 = delegate2.getImage();
571         if (delegate1.mConfig != delegate2.mConfig ||
572                 image1.getWidth() != image2.getWidth() ||
573                 image1.getHeight() != image2.getHeight()) {
574             return false;
575         }
576 
577         // get the internal data
578         int w = image1.getWidth();
579         int h = image2.getHeight();
580         int[] argb1 = new int[w*h];
581         int[] argb2 = new int[w*h];
582 
583         image1.getRGB(0, 0, w, h, argb1, 0, w);
584         image2.getRGB(0, 0, w, h, argb2, 0, w);
585 
586         // compares
587         if (delegate1.mConfig == Config.ALPHA_8) {
588             // in this case we have to manually compare the alpha channel as the rest is garbage.
589             final int length = w*h;
590             for (int i = 0 ; i < length ; i++) {
591                 if ((argb1[i] & 0xFF000000) != (argb2[i] & 0xFF000000)) {
592                     return false;
593                 }
594             }
595             return true;
596         }
597 
598         return Arrays.equals(argb1, argb2);
599     }
600 
601     @LayoutlibDelegate
nativeGetAllocationByteCount(long nativeBitmap)602     /*package*/ static int nativeGetAllocationByteCount(long nativeBitmap) {
603         // get the delegate from the native int.
604         Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
605         if (delegate == null) {
606             return 0;
607         }
608         int size = nativeRowBytes(nativeBitmap) * delegate.mImage.getHeight();
609         return size < 0 ? Integer.MAX_VALUE : size;
610 
611     }
612 
613     @LayoutlibDelegate
nativePrepareToDraw(long nativeBitmap)614     /*package*/ static void nativePrepareToDraw(long nativeBitmap) {
615         // do nothing as Bitmap_Delegate does not have caches
616     }
617 
618     @LayoutlibDelegate
nativeCopyPreserveInternalConfig(long nativeBitmap)619     /*package*/ static Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap) {
620         Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(nativeBitmap);
621         if (srcBmpDelegate == null) {
622             return null;
623         }
624 
625         BufferedImage srcImage = srcBmpDelegate.getImage();
626 
627         // create the image
628         BufferedImage image = new BufferedImage(srcImage.getColorModel(), srcImage.copyData(null),
629                 srcImage.isAlphaPremultiplied(), null);
630 
631         // create a delegate with the content of the stream.
632         Bitmap_Delegate delegate = new Bitmap_Delegate(image, srcBmpDelegate.getConfig());
633         delegate.mIsMutable = srcBmpDelegate.mIsMutable;
634 
635         return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.NONE),
636                 Bitmap.getDefaultDensity(), null);
637     }
638 
639     @LayoutlibDelegate
nativeWrapHardwareBufferBitmap(HardwareBuffer buffer, long nativeColorSpace)640     /*package*/ static Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer,
641             long nativeColorSpace) {
642         Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
643                 "Bitmap.nativeWrapHardwareBufferBitmap() is not supported", null, null, null);
644         return null;
645     }
646 
647     @LayoutlibDelegate
nativeIsSRGB(long nativeBitmap)648     /*package*/ static boolean nativeIsSRGB(long nativeBitmap) {
649         Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
650                 "Color spaces are not supported", null, null /*data*/);
651         return false;
652     }
653 
654     @LayoutlibDelegate
nativeComputeColorSpace(long nativePtr)655     /*package*/ static ColorSpace nativeComputeColorSpace(long nativePtr) {
656         Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
657                 "Color spaces are not supported", null, null /*data*/);
658         return null;
659     }
660 
661     @LayoutlibDelegate
nativeSetColorSpace(long nativePtr, long nativeColorSpace)662     /*package*/ static void nativeSetColorSpace(long nativePtr, long nativeColorSpace) {
663         Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
664                 "Color spaces are not supported", null, null /*data*/);
665     }
666 
667     @LayoutlibDelegate
nativeIsSRGBLinear(long nativePtr)668     /*package*/ static boolean nativeIsSRGBLinear(long nativePtr) {
669         Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
670                 "Color spaces are not supported", null, null /*data*/);
671         return false;
672     }
673 
674     @LayoutlibDelegate
nativeSetImmutable(long nativePtr)675     /*package*/ static void nativeSetImmutable(long nativePtr) {
676         Bitmap_Delegate bmpDelegate = sManager.getDelegate(nativePtr);
677         if (bmpDelegate == null) {
678             return;
679         }
680         bmpDelegate.mIsMutable = false;
681     }
682 
683     @LayoutlibDelegate
nativeIsImmutable(long nativePtr)684     /*package*/ static boolean nativeIsImmutable(long nativePtr) {
685         Bitmap_Delegate bmpDelegate = sManager.getDelegate(nativePtr);
686         if (bmpDelegate == null) {
687             return false;
688         }
689         return !bmpDelegate.mIsMutable;
690     }
691 
692     @LayoutlibDelegate
nativeGetHardwareBuffer(long nativeBitmap)693     /*package*/ static HardwareBuffer nativeGetHardwareBuffer(long nativeBitmap) {
694         Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
695                 "HardwareBuffer is not supported", null, null /*data*/);
696         return null;
697     }
698 
699     // ---- Private delegate/helper methods ----
700 
Bitmap_Delegate(BufferedImage image, Config config)701     private Bitmap_Delegate(BufferedImage image, Config config) {
702         mImage = image;
703         mConfig = config;
704     }
705 
createBitmap(Bitmap_Delegate delegate, Set<BitmapCreateFlags> createFlags, int density, byte[] ninePatchChunk)706     private static Bitmap createBitmap(Bitmap_Delegate delegate,
707             Set<BitmapCreateFlags> createFlags, int density, byte[] ninePatchChunk) {
708         // get its native_int
709         long nativeInt = sManager.addNewDelegate(delegate);
710 
711         int width = delegate.mImage.getWidth();
712         int height = delegate.mImage.getHeight();
713         boolean isPremultiplied = createFlags.contains(BitmapCreateFlags.PREMULTIPLIED);
714 
715         // and create/return a new Bitmap with it
716         return new Bitmap(nativeInt, width, height, density, isPremultiplied,
717                 ninePatchChunk, null /* layoutBounds */, true /* fromMalloc */);
718     }
719 
getPremultipliedBitmapCreateFlags(boolean isMutable)720     private static Set<BitmapCreateFlags> getPremultipliedBitmapCreateFlags(boolean isMutable) {
721         Set<BitmapCreateFlags> createFlags = EnumSet.of(BitmapCreateFlags.PREMULTIPLIED);
722         if (isMutable) {
723             createFlags.add(BitmapCreateFlags.MUTABLE);
724         }
725         return createFlags;
726     }
727 
728     /**
729      * Creates and returns a copy of a given BufferedImage.
730      * <p/>
731      * if alpha is different than 255, then it is applied to the alpha channel of each pixel.
732      *
733      * @param image the image to copy
734      * @param imageType the type of the new image
735      * @param alpha an optional alpha modifier
736      * @return a new BufferedImage
737      */
createCopy(BufferedImage image, int imageType, int alpha)738     /*package*/ static BufferedImage createCopy(BufferedImage image, int imageType, int alpha) {
739         int w = image.getWidth();
740         int h = image.getHeight();
741 
742         BufferedImage result = new BufferedImage(w, h, imageType);
743 
744         int[] argb = new int[w * h];
745         image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
746 
747         if (alpha != 255) {
748             final int length = argb.length;
749             for (int i = 0 ; i < length; i++) {
750                 int a = (argb[i] >>> 24 * alpha) / 255;
751                 argb[i] = (a << 24) | (argb[i] & 0x00FFFFFF);
752             }
753         }
754 
755         result.setRGB(0, 0, w, h, argb, 0, w);
756 
757         return result;
758     }
759 
760 }
761