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