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