• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 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 android.annotation.CheckResult;
20 import android.annotation.ColorInt;
21 import android.annotation.ColorLong;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.WorkerThread;
25 import android.compat.annotation.UnsupportedAppUsage;
26 import android.hardware.HardwareBuffer;
27 import android.os.Build;
28 import android.os.Parcel;
29 import android.os.ParcelFileDescriptor;
30 import android.os.Parcelable;
31 import android.os.SharedMemory;
32 import android.os.StrictMode;
33 import android.os.Trace;
34 import android.util.DisplayMetrics;
35 import android.util.Half;
36 import android.util.Log;
37 import android.view.ThreadedRenderer;
38 
39 import dalvik.annotation.optimization.CriticalNative;
40 
41 import libcore.util.NativeAllocationRegistry;
42 
43 import java.io.IOException;
44 import java.io.ByteArrayOutputStream;
45 import java.io.OutputStream;
46 import java.lang.ref.WeakReference;
47 import java.nio.Buffer;
48 import java.nio.ByteBuffer;
49 import java.nio.IntBuffer;
50 import java.nio.ShortBuffer;
51 import java.util.ArrayList;
52 import java.util.WeakHashMap;
53 
54 @android.ravenwood.annotation.RavenwoodKeepWholeClass
55 public final class Bitmap implements Parcelable {
56     private static final String TAG = "Bitmap";
57 
58     /**
59      * Indicates that the bitmap was created for an unknown pixel density.
60      *
61      * @see Bitmap#getDensity()
62      * @see Bitmap#setDensity(int)
63      */
64     public static final int DENSITY_NONE = 0;
65 
66     // Estimated size of the Bitmap native allocation, not including
67     // pixel data.
68     private static final long NATIVE_ALLOCATION_SIZE = 32;
69 
70     // Convenience for JNI access
71     @UnsupportedAppUsage
72     private final long mNativePtr;
73 
74     /**
75      * Represents whether the Bitmap's content is requested to be pre-multiplied.
76      * Note that isPremultiplied() does not directly return this value, because
77      * isPremultiplied() may never return true for a 565 Bitmap or a bitmap
78      * without alpha.
79      *
80      * setPremultiplied() does directly set the value so that setConfig() and
81      * setPremultiplied() aren't order dependent, despite being setters.
82      *
83      * The native bitmap's premultiplication state is kept up to date by
84      * pushing down this preference for every config change.
85      */
86     private boolean mRequestPremultiplied;
87 
88     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769491)
89     private byte[] mNinePatchChunk; // may be null
90     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
91     private NinePatch.InsetStruct mNinePatchInsets; // may be null
92     @UnsupportedAppUsage
93     private int mWidth;
94     @UnsupportedAppUsage
95     private int mHeight;
96     private WeakReference<HardwareBuffer> mHardwareBuffer;
97     private boolean mRecycled;
98 
99     private ColorSpace mColorSpace;
100     private Gainmap mGainmap;
101 
102     /*package*/ int mDensity = getDefaultDensity();
103 
104     private static volatile int sDefaultDensity = -1;
105 
106     /**
107      * This id is not authoritative and can be duplicated if an ashmem bitmap is decoded from a
108      * parcel.
109      */
110     private long mId;
111 
112     /**
113      * For backwards compatibility, allows the app layer to change the default
114      * density when running old apps.
115      * @hide
116      */
117     @UnsupportedAppUsage
setDefaultDensity(int density)118     public static void setDefaultDensity(int density) {
119         sDefaultDensity = density;
120     }
121 
122     @SuppressWarnings("deprecation")
123     @UnsupportedAppUsage
getDefaultDensity()124     static int getDefaultDensity() {
125         if (sDefaultDensity >= 0) {
126             return sDefaultDensity;
127         }
128         sDefaultDensity = DisplayMetrics.DENSITY_DEVICE;
129         return sDefaultDensity;
130     }
131 
132     /**
133      * @hide
134      */
135     private static final WeakHashMap<Bitmap, Void> sAllBitmaps = new WeakHashMap<>();
136 
137     /**
138      * @hide
139      */
getRegistry(boolean malloc, long size)140     private static NativeAllocationRegistry getRegistry(boolean malloc, long size) {
141         final long free = nativeGetNativeFinalizer();
142         if (com.android.libcore.readonly.Flags.nativeMetrics()) {
143             Class cls = Bitmap.class;
144             return malloc ? NativeAllocationRegistry.createMalloced(cls, free, size)
145                           : NativeAllocationRegistry.createNonmalloced(cls, free, size);
146         } else {
147             ClassLoader loader = Bitmap.class.getClassLoader();
148             return malloc ? NativeAllocationRegistry.createMalloced(loader, free, size)
149                           : NativeAllocationRegistry.createNonmalloced(loader, free, size);
150         }
151     }
152 
153     /**
154      * Private constructor that must receive an already allocated native bitmap
155      * int (pointer).
156      */
157     // JNI now calls the version below this one. This is preserved due to UnsupportedAppUsage.
158     @UnsupportedAppUsage(maxTargetSdk = 28)
Bitmap(long nativeBitmap, int width, int height, int density, boolean requestPremultiplied, byte[] ninePatchChunk, NinePatch.InsetStruct ninePatchInsets)159     Bitmap(long nativeBitmap, int width, int height, int density,
160             boolean requestPremultiplied, byte[] ninePatchChunk,
161             NinePatch.InsetStruct ninePatchInsets) {
162         this(0, nativeBitmap, width, height, density, requestPremultiplied, ninePatchChunk,
163                 ninePatchInsets, true);
164     }
165 
166     // called from JNI and Bitmap_Delegate.
Bitmap(long id, long nativeBitmap, int width, int height, int density, boolean requestPremultiplied, byte[] ninePatchChunk, NinePatch.InsetStruct ninePatchInsets, boolean fromMalloc)167     Bitmap(long id, long nativeBitmap, int width, int height, int density,
168             boolean requestPremultiplied, byte[] ninePatchChunk,
169             NinePatch.InsetStruct ninePatchInsets, boolean fromMalloc) {
170         if (nativeBitmap == 0) {
171             throw new RuntimeException("internal error: native bitmap is 0");
172         }
173 
174         mId = id;
175         mWidth = width;
176         mHeight = height;
177         mRequestPremultiplied = requestPremultiplied;
178         mNinePatchChunk = ninePatchChunk;
179         mNinePatchInsets = ninePatchInsets;
180         if (density >= 0) {
181             mDensity = density;
182         }
183 
184         mNativePtr = nativeBitmap;
185         final int allocationByteCount = getAllocationByteCount();
186         getRegistry(fromMalloc, allocationByteCount).registerNativeAllocation(this, mNativePtr);
187 
188         synchronized (Bitmap.class) {
189           sAllBitmaps.put(this, null);
190         }
191     }
192 
193     /**
194      * Return the pointer to the native object.
195      *
196      * @hide
197      * Must be public for access from android.graphics.pdf,
198      * but must not be called from outside the UI module.
199      */
getNativeInstance()200     public long getNativeInstance() {
201         return mNativePtr;
202     }
203 
204     /**
205      * Native bitmap has been reconfigured, so set premult and cached
206      * width/height values
207      */
208     @SuppressWarnings("unused") // called from JNI
209     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
reinit(int width, int height, boolean requestPremultiplied)210     void reinit(int width, int height, boolean requestPremultiplied) {
211         mWidth = width;
212         mHeight = height;
213         mRequestPremultiplied = requestPremultiplied;
214         mColorSpace = null;
215     }
216 
217     /**
218      * <p>Returns the density for this bitmap.</p>
219      *
220      * <p>The default density is the same density as the current display,
221      * unless the current application does not support different screen
222      * densities in which case it is
223      * {@link android.util.DisplayMetrics#DENSITY_DEFAULT}.  Note that
224      * compatibility mode is determined by the application that was initially
225      * loaded into a process -- applications that share the same process should
226      * all have the same compatibility, or ensure they explicitly set the
227      * density of their bitmaps appropriately.</p>
228      *
229      * @return A scaling factor of the default density or {@link #DENSITY_NONE}
230      *         if the scaling factor is unknown.
231      *
232      * @see #setDensity(int)
233      * @see android.util.DisplayMetrics#DENSITY_DEFAULT
234      * @see android.util.DisplayMetrics#densityDpi
235      * @see #DENSITY_NONE
236      */
getDensity()237     public int getDensity() {
238         if (mRecycled) {
239             Log.w(TAG, "Called getDensity() on a recycle()'d bitmap! This is undefined behavior!");
240         }
241         return mDensity;
242     }
243 
244     /**
245      * <p>Specifies the density for this bitmap.  When the bitmap is
246      * drawn to a Canvas that also has a density, it will be scaled
247      * appropriately.</p>
248      *
249      * @param density The density scaling factor to use with this bitmap or
250      *        {@link #DENSITY_NONE} if the density is unknown.
251      *
252      * @see #getDensity()
253      * @see android.util.DisplayMetrics#DENSITY_DEFAULT
254      * @see android.util.DisplayMetrics#densityDpi
255      * @see #DENSITY_NONE
256      */
setDensity(int density)257     public void setDensity(int density) {
258         mDensity = density;
259     }
260 
261     /**
262      * <p>Modifies the bitmap to have a specified width, height, and {@link
263      * Config}, without affecting the underlying allocation backing the bitmap.
264      * Bitmap pixel data is not re-initialized for the new configuration.</p>
265      *
266      * <p>This method can be used to avoid allocating a new bitmap, instead
267      * reusing an existing bitmap's allocation for a new configuration of equal
268      * or lesser size. If the Bitmap's allocation isn't large enough to support
269      * the new configuration, an IllegalArgumentException will be thrown and the
270      * bitmap will not be modified.</p>
271      *
272      * <p>The result of {@link #getByteCount()} will reflect the new configuration,
273      * while {@link #getAllocationByteCount()} will reflect that of the initial
274      * configuration.</p>
275      *
276      * <p>Note: This may change this result of hasAlpha(). When converting to 565,
277      * the new bitmap will always be considered opaque. When converting from 565,
278      * the new bitmap will be considered non-opaque, and will respect the value
279      * set by setPremultiplied().</p>
280      *
281      * <p>WARNING: This method should NOT be called on a bitmap currently in use
282      * by the view system, Canvas, or the AndroidBitmap NDK API. It does not
283      * make guarantees about how the underlying pixel buffer is remapped to the
284      * new config, just that the allocation is reused. Additionally, the view
285      * system does not account for bitmap properties being modifying during use,
286      * e.g. while attached to drawables.</p>
287      *
288      * <p>In order to safely ensure that a Bitmap is no longer in use by the
289      * View system it is necessary to wait for a draw pass to occur after
290      * invalidate()'ing any view that had previously drawn the Bitmap in the last
291      * draw pass due to hardware acceleration's caching of draw commands. As
292      * an example, here is how this can be done for an ImageView:
293      * <pre class="prettyprint">
294      *      ImageView myImageView = ...;
295      *      final Bitmap myBitmap = ...;
296      *      myImageView.setImageDrawable(null);
297      *      myImageView.post(new Runnable() {
298      *          public void run() {
299      *              // myBitmap is now no longer in use by the ImageView
300      *              // and can be safely reconfigured.
301      *              myBitmap.reconfigure(...);
302      *          }
303      *      });
304      * </pre></p>
305      *
306      * @see #setWidth(int)
307      * @see #setHeight(int)
308      * @see #setConfig(Config)
309      */
reconfigure(int width, int height, @NonNull Config config)310     public void reconfigure(int width, int height, @NonNull Config config) {
311         checkRecycled("Can't call reconfigure() on a recycled bitmap");
312         if (width <= 0 || height <= 0) {
313             throw new IllegalArgumentException("width and height must be > 0");
314         }
315         if (!isMutable()) {
316             throw new IllegalStateException("only mutable bitmaps may be reconfigured");
317         }
318 
319         nativeReconfigure(mNativePtr, width, height, config.nativeInt, mRequestPremultiplied);
320         mWidth = width;
321         mHeight = height;
322         mColorSpace = null;
323     }
324 
325     /**
326      * <p>Convenience method for calling {@link #reconfigure(int, int, Config)}
327      * with the current height and config.</p>
328      *
329      * <p>WARNING: this method should not be used on bitmaps currently used by
330      * the view system, see {@link #reconfigure(int, int, Config)} for more
331      * details.</p>
332      *
333      * @see #reconfigure(int, int, Config)
334      * @see #setHeight(int)
335      * @see #setConfig(Config)
336      */
setWidth(int width)337     public void setWidth(int width) {
338         reconfigure(width, getHeight(), getConfig());
339     }
340 
341     /**
342      * <p>Convenience method for calling {@link #reconfigure(int, int, Config)}
343      * with the current width and config.</p>
344      *
345      * <p>WARNING: this method should not be used on bitmaps currently used by
346      * the view system, see {@link #reconfigure(int, int, Config)} for more
347      * details.</p>
348      *
349      * @see #reconfigure(int, int, Config)
350      * @see #setWidth(int)
351      * @see #setConfig(Config)
352      */
setHeight(int height)353     public void setHeight(int height) {
354         reconfigure(getWidth(), height, getConfig());
355     }
356 
357     /**
358      * <p>Convenience method for calling {@link #reconfigure(int, int, Config)}
359      * with the current height and width.</p>
360      *
361      * <p>WARNING: this method should not be used on bitmaps currently used by
362      * the view system, see {@link #reconfigure(int, int, Config)} for more
363      * details.</p>
364      *
365      * @see #reconfigure(int, int, Config)
366      * @see #setWidth(int)
367      * @see #setHeight(int)
368      */
setConfig(@onNull Config config)369     public void setConfig(@NonNull Config config) {
370         reconfigure(getWidth(), getHeight(), config);
371     }
372 
373     /**
374      * Sets the nine patch chunk.
375      *
376      * @param chunk The definition of the nine patch
377      */
378     @UnsupportedAppUsage
setNinePatchChunk(byte[] chunk)379     private void setNinePatchChunk(byte[] chunk) {
380         mNinePatchChunk = chunk;
381     }
382 
383     /**
384      * Free the native object associated with this bitmap, and clear the
385      * reference to the pixel data. This will not free the pixel data synchronously;
386      * it simply allows it to be garbage collected if there are no other references.
387      * The bitmap is marked as "dead", meaning it will throw an exception if
388      * getPixels() or setPixels() is called, and will draw nothing. This operation
389      * cannot be reversed, so it should only be called if you are sure there are no
390      * further uses for the bitmap. This is an advanced call, and normally need
391      * not be called, since the normal GC process will free up this memory when
392      * there are no more references to this bitmap.
393      */
recycle()394     public void recycle() {
395         if (!mRecycled) {
396             nativeRecycle(mNativePtr);
397             mNinePatchChunk = null;
398             mRecycled = true;
399             mHardwareBuffer = null;
400         }
401     }
402 
403     /**
404      * Returns true if this bitmap has been recycled. If so, then it is an error
405      * to try to access its pixels, and the bitmap will not draw.
406      *
407      * @return true if the bitmap has been recycled
408      */
isRecycled()409     public final boolean isRecycled() {
410         return mRecycled;
411     }
412 
413     /**
414      * Returns the generation ID of this bitmap. The generation ID changes
415      * whenever the bitmap is modified. This can be used as an efficient way to
416      * check if a bitmap has changed.
417      *
418      * @return The current generation ID for this bitmap.
419      */
getGenerationId()420     public int getGenerationId() {
421         if (mRecycled) {
422             Log.w(TAG, "Called getGenerationId() on a recycle()'d bitmap! This is undefined behavior!");
423         }
424         return nativeGenerationId(mNativePtr);
425     }
426 
427     /**
428      * This is called by methods that want to throw an exception if the bitmap
429      * has already been recycled.
430      * @hide
431      */
checkRecycled(String errorMessage)432     void checkRecycled(String errorMessage) {
433         if (mRecycled) {
434             throw new IllegalStateException(errorMessage);
435         }
436     }
437 
438     /**
439      * This is called by methods that want to throw an exception if the bitmap
440      * is {@link Config#HARDWARE}.
441      */
checkHardware(String errorMessage)442     private void checkHardware(String errorMessage) {
443         if (getConfig() == Config.HARDWARE) {
444             throw new IllegalStateException(errorMessage);
445         }
446     }
447 
448     /**
449      * Common code for checking that x and y are >= 0
450      *
451      * @param x x coordinate to ensure is >= 0
452      * @param y y coordinate to ensure is >= 0
453      */
checkXYSign(int x, int y)454     private static void checkXYSign(int x, int y) {
455         if (x < 0) {
456             throw new IllegalArgumentException("x must be >= 0");
457         }
458         if (y < 0) {
459             throw new IllegalArgumentException("y must be >= 0");
460         }
461     }
462 
463     /**
464      * Common code for checking that width and height are > 0
465      *
466      * @param width  width to ensure is > 0
467      * @param height height to ensure is > 0
468      */
checkWidthHeight(int width, int height)469     private static void checkWidthHeight(int width, int height) {
470         if (width <= 0) {
471             throw new IllegalArgumentException("width must be > 0");
472         }
473         if (height <= 0) {
474             throw new IllegalArgumentException("height must be > 0");
475         }
476     }
477 
478     /**
479      * Possible bitmap configurations. A bitmap configuration describes
480      * how pixels are stored. This affects the quality (color depth) as
481      * well as the ability to display transparent/translucent colors.
482      */
483     // It's touched by Graphics.cpp, so we need to make this enum usable on Ravenwood.
484     // Otherwise, all the ctors would throw, which would make the class unloadable
485     // because the static initializer needs the enum members because of `sConfigs`.
486     // TODO: Remove it once we expose the outer class.
487     @android.ravenwood.annotation.RavenwoodKeepWholeClass
488     public enum Config {
489         // these native values must match up with the enum in SkBitmap.h
490 
491         /**
492          * Each pixel is stored as a single translucency (alpha) channel.
493          * This is very useful to efficiently store masks for instance.
494          * No color information is stored.
495          * With this configuration, each pixel requires 1 byte of memory.
496          */
497         ALPHA_8(1),
498 
499         /**
500          * Each pixel is stored on 2 bytes and only the RGB channels are
501          * encoded: red is stored with 5 bits of precision (32 possible
502          * values), green is stored with 6 bits of precision (64 possible
503          * values) and blue is stored with 5 bits of precision.
504          *
505          * This configuration can produce slight visual artifacts depending
506          * on the configuration of the source. For instance, without
507          * dithering, the result might show a greenish tint. To get better
508          * results dithering should be applied.
509          *
510          * This configuration may be useful when using opaque bitmaps
511          * that do not require high color fidelity.
512          *
513          * <p>When accessing directly via #copyPixelsFromBuffer or #copyPixelsToBuffer,
514          *    use this formula to pack into 16 bits:</p>
515          * <pre class="prettyprint">
516          * short color = (R & 0x1f) << 11 | (G & 0x3f) << 5 | (B & 0x1f);
517          * </pre>
518          */
519         RGB_565(3),
520 
521         /**
522          * Each pixel is stored on 2 bytes. The three RGB color channels
523          * and the alpha channel (translucency) are stored with a 4 bits
524          * precision (16 possible values.)
525          *
526          * This configuration is mostly useful if the application needs
527          * to store translucency information but also needs to save
528          * memory.
529          *
530          * It is recommended to use {@link #ARGB_8888} instead of this
531          * configuration.
532          *
533          * Note: as of {@link android.os.Build.VERSION_CODES#KITKAT},
534          * any bitmap created with this configuration will be created
535          * using {@link #ARGB_8888} instead.
536          *
537          * @deprecated Because of the poor quality of this configuration,
538          *             it is advised to use {@link #ARGB_8888} instead.
539          */
540         @Deprecated
541         ARGB_4444(4),
542 
543         /**
544          * Each pixel is stored on 4 bytes. Each channel (RGB and alpha
545          * for translucency) is stored with 8 bits of precision (256
546          * possible values.)
547          *
548          * This configuration is very flexible and offers the best
549          * quality. It should be used whenever possible.
550          *
551          * <p>When accessing directly via #copyPixelsFromBuffer or #copyPixelsToBuffer,
552          *    use this formula to pack into 32 bits:</p>
553          * <pre class="prettyprint">
554          * int color = (A & 0xff) << 24 | (B & 0xff) << 16 | (G & 0xff) << 8 | (R & 0xff);
555          * </pre>
556          */
557         ARGB_8888(5),
558 
559         /**
560          * Each pixel is stored on 8 bytes. Each channel (RGB and alpha
561          * for translucency) is stored as a
562          * {@link android.util.Half half-precision floating point value}.
563          *
564          * This configuration is particularly suited for wide-gamut and
565          * HDR content.
566          *
567          * <p>When accessing directly via #copyPixelsFromBuffer or #copyPixelsToBuffer,
568          *    use this formula to pack into 64 bits:</p>
569          * <pre class="prettyprint">
570          * long color = (A & 0xffff) << 48 | (B & 0xffff) << 32 | (G & 0xffff) << 16 | (R & 0xffff);
571          * </pre>
572          */
573         RGBA_F16(6),
574 
575         /**
576          * Special configuration, when bitmap is stored only in graphic memory.
577          * Bitmaps in this configuration are always immutable.
578          *
579          * It is optimal for cases, when the only operation with the bitmap is to draw it on a
580          * screen.
581          */
582         HARDWARE(7),
583 
584         /**
585          * Each pixel is stored on 4 bytes. Each RGB channel is stored with 10 bits of precision
586          * (1024 possible values). There is an additional alpha channel that is stored with 2 bits
587          * of precision (4 possible values).
588          *
589          * This configuration is suited for wide-gamut and HDR content which does not require alpha
590          * blending, such that the memory cost is the same as ARGB_8888 while enabling higher color
591          * precision.
592          *
593          * <p>When accessing directly via #copyPixelsFromBuffer or #copyPixelsToBuffer,
594          *  use this formula to pack into 32 bits:</p>
595          * <pre class="prettyprint">
596          * int color = (A & 0x3) << 30 | (B & 0x3ff) << 20 | (G & 0x3ff) << 10 | (R & 0x3ff);
597          * </pre>
598          */
599         RGBA_1010102(8);
600 
601         @UnsupportedAppUsage
602         final int nativeInt;
603 
604         private static Config sConfigs[] = {
605             null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16, HARDWARE, RGBA_1010102
606         };
607 
Config(int ni)608         Config(int ni) {
609             this.nativeInt = ni;
610         }
611 
612         @UnsupportedAppUsage
nativeToConfig(int ni)613         static Config nativeToConfig(int ni) {
614             return sConfigs[ni];
615         }
616     }
617 
618     /**
619      * <p>Copy the bitmap's pixels into the specified buffer (allocated by the
620      * caller). An exception is thrown if the buffer is not large enough to
621      * hold all of the pixels (taking into account the number of bytes per
622      * pixel) or if the Buffer subclass is not one of the support types
623      * (ByteBuffer, ShortBuffer, IntBuffer).</p>
624      * <p>The content of the bitmap is copied into the buffer as-is. This means
625      * that if this bitmap stores its pixels pre-multiplied
626      * (see {@link #isPremultiplied()}, the values in the buffer will also be
627      * pre-multiplied. The pixels remain in the color space of the bitmap.</p>
628      * <p>After this method returns, the current position of the buffer is
629      * updated: the position is incremented by the number of elements written
630      * in the buffer.</p>
631      * @throws IllegalStateException if the bitmap's config is {@link Config#HARDWARE}
632      */
copyPixelsToBuffer(@onNull Buffer dst)633     public void copyPixelsToBuffer(@NonNull Buffer dst) {
634         checkHardware("unable to copyPixelsToBuffer, "
635                 + "pixel access is not supported on Config#HARDWARE bitmaps");
636         int elements = dst.remaining();
637         int shift;
638         if (dst instanceof ByteBuffer) {
639             shift = 0;
640         } else if (dst instanceof ShortBuffer) {
641             shift = 1;
642         } else if (dst instanceof IntBuffer) {
643             shift = 2;
644         } else {
645             throw new RuntimeException("unsupported Buffer subclass");
646         }
647 
648         long bufferSize = (long)elements << shift;
649         long pixelSize = getByteCount();
650 
651         if (bufferSize < pixelSize) {
652             throw new RuntimeException("Buffer not large enough for pixels");
653         }
654 
655         nativeCopyPixelsToBuffer(mNativePtr, dst);
656 
657         // now update the buffer's position
658         int position = dst.position();
659         position += pixelSize >> shift;
660         dst.position(position);
661     }
662 
663     /**
664      * <p>Copy the pixels from the buffer, beginning at the current position,
665      * overwriting the bitmap's pixels. The data in the buffer is not changed
666      * in any way (unlike setPixels(), which converts from unpremultipled 32bit
667      * to whatever the bitmap's native format is. The pixels in the source
668      * buffer are assumed to be in the bitmap's color space.</p>
669      * <p>After this method returns, the current position of the buffer is
670      * updated: the position is incremented by the number of elements read from
671      * the buffer. If you need to read the bitmap from the buffer again you must
672      * first rewind the buffer.</p>
673      * @throws IllegalStateException if the bitmap's config is {@link Config#HARDWARE}
674      */
copyPixelsFromBuffer(@onNull Buffer src)675     public void copyPixelsFromBuffer(@NonNull Buffer src) {
676         checkRecycled("copyPixelsFromBuffer called on recycled bitmap");
677         checkHardware("unable to copyPixelsFromBuffer, Config#HARDWARE bitmaps are immutable");
678 
679         int elements = src.remaining();
680         int shift;
681         if (src instanceof ByteBuffer) {
682             shift = 0;
683         } else if (src instanceof ShortBuffer) {
684             shift = 1;
685         } else if (src instanceof IntBuffer) {
686             shift = 2;
687         } else {
688             throw new RuntimeException("unsupported Buffer subclass");
689         }
690 
691         long bufferBytes = (long) elements << shift;
692         long bitmapBytes = getByteCount();
693 
694         if (bufferBytes < bitmapBytes) {
695             throw new RuntimeException("Buffer not large enough for pixels");
696         }
697 
698         nativeCopyPixelsFromBuffer(mNativePtr, src);
699 
700         // now update the buffer's position
701         int position = src.position();
702         position += bitmapBytes >> shift;
703         src.position(position);
704     }
705 
noteHardwareBitmapSlowCall()706     private void noteHardwareBitmapSlowCall() {
707         if (getConfig() == Config.HARDWARE) {
708             StrictMode.noteSlowCall("Warning: attempt to read pixels from hardware "
709                     + "bitmap, which is very slow operation");
710         }
711     }
712 
713     /**
714      * Tries to make a new bitmap based on the dimensions of this bitmap,
715      * setting the new bitmap's config to the one specified, and then copying
716      * this bitmap's pixels into the new bitmap. If the conversion is not
717      * supported, or the allocator fails, then this returns NULL.  The returned
718      * bitmap has the same density and color space as the original, except in
719      * the following cases. When copying to {@link Config#ALPHA_8}, the color
720      * space is dropped. When copying to or from {@link Config#RGBA_F16},
721      * EXTENDED or non-EXTENDED variants may be adjusted as appropriate.
722      *
723      * @param config    The desired config for the resulting bitmap
724      * @param isMutable True if the resulting bitmap should be mutable (i.e.
725      *                  its pixels can be modified)
726      * @return the new bitmap, or null if the copy could not be made.
727      * @throws IllegalArgumentException if config is {@link Config#HARDWARE} and isMutable is true
728      */
copy(@onNull Config config, boolean isMutable)729     public Bitmap copy(@NonNull Config config, boolean isMutable) {
730         checkRecycled("Can't copy a recycled bitmap");
731         if (config == Config.HARDWARE && isMutable) {
732             throw new IllegalArgumentException("Hardware bitmaps are always immutable");
733         }
734         noteHardwareBitmapSlowCall();
735         Bitmap b = nativeCopy(mNativePtr, config.nativeInt, isMutable);
736         if (b != null) {
737             b.setPremultiplied(mRequestPremultiplied);
738             b.mDensity = mDensity;
739         }
740         return b;
741     }
742 
743     /**
744      * Creates a new immutable bitmap backed by ashmem which can efficiently
745      * be passed between processes.
746      *
747      * @hide
748      */
749     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R,
750             publicAlternatives = "Use {@link #asShared()} instead")
createAshmemBitmap()751     public Bitmap createAshmemBitmap() {
752         checkRecycled("Can't copy a recycled bitmap");
753         noteHardwareBitmapSlowCall();
754         Bitmap b = nativeCopyAshmem(mNativePtr);
755         if (b != null) {
756             b.setPremultiplied(mRequestPremultiplied);
757             b.mDensity = mDensity;
758             if (hasGainmap()) {
759                 b.setGainmap(getGainmap().asShared());
760             }
761         }
762         return b;
763     }
764 
765     /**
766      * Return an immutable bitmap backed by shared memory which can be
767      * efficiently passed between processes via Parcelable.
768      *
769      * <p>If this bitmap already meets these criteria it will return itself.
770      */
771     @NonNull
asShared()772     public Bitmap asShared() {
773         if (nativeIsBackedByAshmem(mNativePtr) && nativeIsImmutable(mNativePtr)
774                 && (!hasGainmap() || getGainmap().asShared() == getGainmap())) {
775             return this;
776         }
777         Bitmap shared = createAshmemBitmap();
778         if (shared == null) {
779             throw new RuntimeException("Failed to create shared Bitmap!");
780         }
781         return shared;
782     }
783 
784     /**
785      * Returns the shared memory handle to the pixel storage if the bitmap is already using
786      * shared memory and null if it is not.  The SharedMemory object is then useful to then pass
787      * through HIDL APIs (e.g. WearOS's DisplayOffload service).
788      *
789      * @hide
790      */
getSharedMemory()791     public SharedMemory getSharedMemory() {
792         checkRecycled("Cannot access shared memory of a recycled bitmap");
793         if (nativeIsBackedByAshmem(mNativePtr)) {
794             try {
795                 int fd = nativeGetAshmemFD(mNativePtr);
796                 return SharedMemory.fromFileDescriptor(ParcelFileDescriptor.fromFd(fd));
797             } catch (IOException e) {
798                 Log.e(TAG, "Unable to create dup'd file descriptor for shared bitmap memory");
799             }
800         }
801         return null;
802     }
803 
804     /**
805      * Create a hardware bitmap backed by a {@link HardwareBuffer}.
806      *
807      * <p>The passed HardwareBuffer's usage flags must contain
808      * {@link HardwareBuffer#USAGE_GPU_SAMPLED_IMAGE}.
809      *
810      * <p>The bitmap will keep a reference to the buffer so that callers can safely close the
811      * HardwareBuffer without affecting the Bitmap. However the HardwareBuffer must not be
812      * modified while a wrapped Bitmap is accessing it. Doing so will result in undefined behavior.
813      *
814      * @param hardwareBuffer The HardwareBuffer to wrap.
815      * @param colorSpace The color space of the bitmap. Must be a {@link ColorSpace.Rgb} colorspace.
816      *                   If null, SRGB is assumed.
817      * @return A bitmap wrapping the buffer, or null if there was a problem creating the bitmap.
818      * @throws IllegalArgumentException if the HardwareBuffer has an invalid usage, or an invalid
819      *                                  colorspace is given.
820      */
821     @Nullable
wrapHardwareBuffer(@onNull HardwareBuffer hardwareBuffer, @Nullable ColorSpace colorSpace)822     public static Bitmap wrapHardwareBuffer(@NonNull HardwareBuffer hardwareBuffer,
823             @Nullable ColorSpace colorSpace) {
824         final long usage = hardwareBuffer.getUsage();
825         if ((usage & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) {
826             throw new IllegalArgumentException("usage flags must contain USAGE_GPU_SAMPLED_IMAGE.");
827         }
828         if ((usage & HardwareBuffer.USAGE_PROTECTED_CONTENT) != 0) {
829             throw new IllegalArgumentException("Bitmap is not compatible with protected buffers");
830         }
831         if (colorSpace == null) {
832             colorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
833         }
834         Bitmap bitmap = nativeWrapHardwareBufferBitmap(hardwareBuffer,
835                 colorSpace.getNativeInstance());
836         if (bitmap != null) {
837             bitmap.mHardwareBuffer = new WeakReference<HardwareBuffer>(hardwareBuffer);
838         }
839         return bitmap;
840     }
841 
842     /**
843      * Creates a new bitmap, scaled from an existing bitmap, when possible. If the
844      * specified width and height are the same as the current width and height of
845      * the source bitmap, the source bitmap is returned and no new bitmap is
846      * created.
847      *
848      * @param src       The source bitmap.
849      * @param dstWidth  The new bitmap's desired width.
850      * @param dstHeight The new bitmap's desired height.
851      * @param filter    Whether or not bilinear filtering should be used when scaling the
852      *                  bitmap. If this is true then bilinear filtering will be used when
853      *                  scaling which has better image quality at the cost of worse performance.
854      *                  If this is false then nearest-neighbor scaling is used instead which
855      *                  will have worse image quality but is faster. Recommended default
856      *                  is to set filter to 'true' as the cost of bilinear filtering is
857      *                  typically minimal and the improved image quality is significant.
858      * @return The new scaled bitmap or the source bitmap if no scaling is required.
859      * @throws IllegalArgumentException if width is <= 0, or height is <= 0
860      */
861     @NonNull
createScaledBitmap(@onNull Bitmap src, int dstWidth, int dstHeight, boolean filter)862     public static Bitmap createScaledBitmap(@NonNull Bitmap src, int dstWidth, int dstHeight,
863             boolean filter) {
864         Matrix m = new Matrix();
865 
866         final int width = src.getWidth();
867         final int height = src.getHeight();
868         if (width != dstWidth || height != dstHeight) {
869             final float sx = dstWidth / (float) width;
870             final float sy = dstHeight / (float) height;
871             m.setScale(sx, sy);
872         }
873         return Bitmap.createBitmap(src, 0, 0, width, height, m, filter);
874     }
875 
876     /**
877      * Returns a bitmap from the source bitmap. The new bitmap may
878      * be the same object as source, or a copy may have been made.  It is
879      * initialized with the same density and color space as the original bitmap.
880      */
881     @NonNull
createBitmap(@onNull Bitmap src)882     public static Bitmap createBitmap(@NonNull Bitmap src) {
883         return createBitmap(src, 0, 0, src.getWidth(), src.getHeight());
884     }
885 
886     /**
887      * Returns a bitmap from the specified subset of the source
888      * bitmap. The new bitmap may be the same object as source, or a copy may
889      * have been made. It is initialized with the same density and color space
890      * as the original bitmap.
891      *
892      * @param source   The bitmap we are subsetting
893      * @param x        The x coordinate of the first pixel in source
894      * @param y        The y coordinate of the first pixel in source
895      * @param width    The number of pixels in each row
896      * @param height   The number of rows
897      * @return A copy of a subset of the source bitmap or the source bitmap itself.
898      * @throws IllegalArgumentException if the x, y, width, height values are
899      *         outside of the dimensions of the source bitmap, or width is <= 0,
900      *         or height is <= 0
901      */
902     @NonNull
createBitmap(@onNull Bitmap source, int x, int y, int width, int height)903     public static Bitmap createBitmap(@NonNull Bitmap source, int x, int y, int width, int height) {
904         return createBitmap(source, x, y, width, height, null, false);
905     }
906 
907     /**
908      * Returns a bitmap from subset of the source bitmap,
909      * transformed by the optional matrix. The new bitmap may be the
910      * same object as source, or a copy may have been made. It is
911      * initialized with the same density and color space as the original
912      * bitmap.
913      *
914      * If the source bitmap is immutable and the requested subset is the
915      * same as the source bitmap itself, then the source bitmap is
916      * returned and no new bitmap is created.
917      *
918      * The returned bitmap will always be mutable except in the following scenarios:
919      * (1) In situations where the source bitmap is returned and the source bitmap is immutable
920      *
921      * (2) The source bitmap is a hardware bitmap. That is {@link #getConfig()} is equivalent to
922      * {@link Config#HARDWARE}
923      *
924      * @param source   The bitmap we are subsetting
925      * @param x        The x coordinate of the first pixel in source
926      * @param y        The y coordinate of the first pixel in source
927      * @param width    The number of pixels in each row
928      * @param height   The number of rows
929      * @param m        Optional matrix to be applied to the pixels
930      * @param filter   true if the source should be filtered.
931      *                   Only applies if the matrix contains more than just
932      *                   translation.
933      * @return A bitmap that represents the specified subset of source
934      * @throws IllegalArgumentException if the x, y, width, height values are
935      *         outside of the dimensions of the source bitmap, or width is <= 0,
936      *         or height is <= 0, or if the source bitmap has already been recycled
937      */
938     @NonNull
createBitmap(@onNull Bitmap source, int x, int y, int width, int height, @Nullable Matrix m, boolean filter)939     public static Bitmap createBitmap(@NonNull Bitmap source, int x, int y, int width, int height,
940             @Nullable Matrix m, boolean filter) {
941 
942         checkXYSign(x, y);
943         checkWidthHeight(width, height);
944         if (x + width > source.getWidth()) {
945             throw new IllegalArgumentException("x + width must be <= bitmap.width()");
946         }
947         if (y + height > source.getHeight()) {
948             throw new IllegalArgumentException("y + height must be <= bitmap.height()");
949         }
950         if (source.isRecycled()) {
951             throw new IllegalArgumentException("cannot use a recycled source in createBitmap");
952         }
953 
954         // check if we can just return our argument unchanged
955         if (!source.isMutable() && x == 0 && y == 0 && width == source.getWidth() &&
956                 height == source.getHeight() && (m == null || m.isIdentity())) {
957             return source;
958         }
959 
960         boolean isHardware = source.getConfig() == Config.HARDWARE;
961         if (isHardware) {
962             source.noteHardwareBitmapSlowCall();
963             source = nativeCopyPreserveInternalConfig(source.mNativePtr);
964         }
965 
966         int neww = width;
967         int newh = height;
968         Bitmap bitmap;
969         Paint paint;
970 
971         Rect srcR = new Rect(x, y, x + width, y + height);
972         RectF dstR = new RectF(0, 0, width, height);
973         RectF deviceR = new RectF();
974 
975         Config newConfig = Config.ARGB_8888;
976         final Config config = source.getConfig();
977         // GIF files generate null configs, assume ARGB_8888
978         if (config != null) {
979             switch (config) {
980                 case RGB_565:
981                     newConfig = Config.RGB_565;
982                     break;
983                 case ALPHA_8:
984                     newConfig = Config.ALPHA_8;
985                     break;
986                 case RGBA_F16:
987                     newConfig = Config.RGBA_F16;
988                     break;
989                 //noinspection deprecation
990                 case ARGB_4444:
991                 case ARGB_8888:
992                 default:
993                     newConfig = Config.ARGB_8888;
994                     break;
995             }
996         }
997 
998         ColorSpace cs = source.getColorSpace();
999 
1000         if (m == null || m.isIdentity()) {
1001             bitmap = createBitmap(null, neww, newh, newConfig, source.hasAlpha(), cs);
1002             paint = null;   // not needed
1003         } else {
1004             final boolean transformed = !m.rectStaysRect();
1005 
1006             m.mapRect(deviceR, dstR);
1007 
1008             neww = Math.round(deviceR.width());
1009             newh = Math.round(deviceR.height());
1010 
1011             Config transformedConfig = newConfig;
1012             if (transformed) {
1013                 if (transformedConfig != Config.ARGB_8888 && transformedConfig != Config.RGBA_F16) {
1014                     transformedConfig = Config.ARGB_8888;
1015                     if (cs == null) {
1016                         cs = ColorSpace.get(ColorSpace.Named.SRGB);
1017                     }
1018                 }
1019             }
1020 
1021             bitmap = createBitmap(null, neww, newh, transformedConfig,
1022                     transformed || source.hasAlpha(), cs);
1023 
1024             paint = new Paint();
1025             paint.setFilterBitmap(filter);
1026             if (transformed) {
1027                 paint.setAntiAlias(true);
1028             }
1029         }
1030 
1031         // The new bitmap was created from a known bitmap source so assume that
1032         // they use the same density
1033         bitmap.mDensity = source.mDensity;
1034         bitmap.setHasAlpha(source.hasAlpha());
1035         bitmap.setPremultiplied(source.mRequestPremultiplied);
1036 
1037         Canvas canvas = new Canvas(bitmap);
1038         canvas.translate(-deviceR.left, -deviceR.top);
1039         canvas.concat(m);
1040         canvas.drawBitmap(source, srcR, dstR, paint);
1041         canvas.setBitmap(null);
1042 
1043         // If the source has a gainmap, apply the same set of transformations to the gainmap
1044         // and set it on the output
1045         if (source.hasGainmap()) {
1046             Bitmap newMapContents = transformGainmap(source, m, neww, newh, paint, srcR, dstR,
1047                     deviceR);
1048             if (newMapContents != null) {
1049                 bitmap.setGainmap(new Gainmap(source.getGainmap(), newMapContents));
1050             }
1051         }
1052 
1053         if (isHardware) {
1054             return bitmap.copy(Config.HARDWARE, false);
1055         }
1056         return bitmap;
1057     }
1058 
transformGainmap(Bitmap source, Matrix m, int neww, int newh, Paint paint, Rect srcR, RectF dstR, RectF deviceR)1059     private static Bitmap transformGainmap(Bitmap source, Matrix m, int neww, int newh, Paint paint,
1060             Rect srcR, RectF dstR, RectF deviceR) {
1061         Canvas canvas;
1062         Bitmap sourceGainmap = source.getGainmap().getGainmapContents();
1063         // Gainmaps can be scaled relative to the base image (eg, 1/4th res)
1064         // Preserve that relative scaling between the base & gainmap in the output
1065         float scaleX = (sourceGainmap.getWidth() / (float) source.getWidth());
1066         float scaleY = (sourceGainmap.getHeight() / (float) source.getHeight());
1067         int mapw = Math.round(neww * scaleX);
1068         int maph = Math.round(newh * scaleY);
1069 
1070         if (mapw == 0 || maph == 0) {
1071             // The gainmap has been scaled away entirely, drop it
1072             return null;
1073         }
1074 
1075         // Scale the computed `srcR` used for rendering the source bitmap to the destination
1076         // to be in gainmap dimensions
1077         Rect gSrcR = new Rect((int) (srcR.left * scaleX),
1078                 (int) (srcR.top * scaleY), (int) (srcR.right * scaleX),
1079                 (int) (srcR.bottom * scaleY));
1080 
1081         // Note: createBitmap isn't used as that requires a non-null colorspace, however
1082         // gainmaps don't have a colorspace. So use `nativeCreate` directly to bypass
1083         // that colorspace enforcement requirement (#getColorSpace() allows a null return)
1084         Bitmap newMapContents = nativeCreate(null, 0, mapw, mapw, maph,
1085                 sourceGainmap.getConfig().nativeInt, true, 0);
1086         newMapContents.eraseColor(0);
1087         canvas = new Canvas(newMapContents);
1088         // Scale the translate & matrix to be in gainmap-relative dimensions
1089         canvas.scale(scaleX, scaleY);
1090         canvas.translate(-deviceR.left, -deviceR.top);
1091         canvas.concat(m);
1092         canvas.drawBitmap(sourceGainmap, gSrcR, dstR, paint);
1093         canvas.setBitmap(null);
1094         // Create a new gainmap using a copy of the metadata information from the source but
1095         // with the transformed bitmap created above
1096         return newMapContents;
1097     }
1098 
1099     /**
1100      * Returns a mutable bitmap with the specified width and height.  Its
1101      * initial density is as per {@link #getDensity}. The newly created
1102      * bitmap is in the {@link ColorSpace.Named#SRGB sRGB} color space.
1103      *
1104      * @param width    The width of the bitmap
1105      * @param height   The height of the bitmap
1106      * @param config   The bitmap config to create.
1107      * @throws IllegalArgumentException if the width or height are <= 0, or if
1108      *         Config is Config.HARDWARE, because hardware bitmaps are always immutable
1109      */
1110     @NonNull
createBitmap(int width, int height, @NonNull Config config)1111     public static Bitmap createBitmap(int width, int height, @NonNull Config config) {
1112         return createBitmap(width, height, config, true);
1113     }
1114 
1115     /**
1116      * Returns a mutable bitmap with the specified width and height.  Its
1117      * initial density is determined from the given {@link DisplayMetrics}.
1118      * The newly created bitmap is in the {@link ColorSpace.Named#SRGB sRGB}
1119      * color space.
1120      *
1121      * @param display  Display metrics for the display this bitmap will be
1122      *                 drawn on.
1123      * @param width    The width of the bitmap
1124      * @param height   The height of the bitmap
1125      * @param config   The bitmap config to create.
1126      * @throws IllegalArgumentException if the width or height are <= 0, or if
1127      *         Config is Config.HARDWARE, because hardware bitmaps are always immutable
1128      */
1129     @NonNull
createBitmap(@ullable DisplayMetrics display, int width, int height, @NonNull Config config)1130     public static Bitmap createBitmap(@Nullable DisplayMetrics display, int width,
1131             int height, @NonNull Config config) {
1132         return createBitmap(display, width, height, config, true);
1133     }
1134 
1135     /**
1136      * Returns a mutable bitmap with the specified width and height.  Its
1137      * initial density is as per {@link #getDensity}. The newly created
1138      * bitmap is in the {@link ColorSpace.Named#SRGB sRGB} color space.
1139      *
1140      * @param width    The width of the bitmap
1141      * @param height   The height of the bitmap
1142      * @param config   The bitmap config to create.
1143      * @param hasAlpha If the bitmap is ARGB_8888, RGBA_16F, or RGBA_1010102 this flag can be
1144      *                 used to mark the bitmap as opaque. Doing so will clear the bitmap in black
1145      *                 instead of transparent.
1146      *
1147      * @throws IllegalArgumentException if the width or height are <= 0, or if
1148      *         Config is Config.HARDWARE, because hardware bitmaps are always immutable
1149      */
1150     @NonNull
createBitmap(int width, int height, @NonNull Config config, boolean hasAlpha)1151     public static Bitmap createBitmap(int width, int height,
1152             @NonNull Config config, boolean hasAlpha) {
1153         return createBitmap(null, width, height, config, hasAlpha);
1154     }
1155 
1156     /**
1157      * Returns a mutable bitmap with the specified width and height.  Its
1158      * initial density is as per {@link #getDensity}.
1159      *
1160      * @param width    The width of the bitmap
1161      * @param height   The height of the bitmap
1162      * @param config   The bitmap config to create.
1163      * @param hasAlpha If the bitmap is ARGB_8888, RGBA_16F, or RGBA_1010102 this flag can be
1164      *                 used to mark the bitmap as opaque. Doing so will clear the bitmap in black
1165      *                 instead of transparent.
1166      * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16}
1167      *                   and {@link ColorSpace.Named#SRGB sRGB} or
1168      *                   {@link ColorSpace.Named#LINEAR_SRGB Linear sRGB} is provided then the
1169      *                   corresponding extended range variant is assumed.
1170      *
1171      * @throws IllegalArgumentException if the width or height are <= 0, if
1172      *         Config is Config.HARDWARE (because hardware bitmaps are always
1173      *         immutable), if the specified color space is not {@link ColorSpace.Model#RGB RGB},
1174      *         if the specified color space's transfer function is not an
1175      *         {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}, or if
1176      *         the color space is null
1177      */
1178     @NonNull
createBitmap(int width, int height, @NonNull Config config, boolean hasAlpha, @NonNull ColorSpace colorSpace)1179     public static Bitmap createBitmap(int width, int height, @NonNull Config config,
1180             boolean hasAlpha, @NonNull ColorSpace colorSpace) {
1181         return createBitmap(null, width, height, config, hasAlpha, colorSpace);
1182     }
1183 
1184     /**
1185      * Returns a mutable bitmap with the specified width and height.  Its
1186      * initial density is determined from the given {@link DisplayMetrics}.
1187      * The newly created bitmap is in the {@link ColorSpace.Named#SRGB sRGB}
1188      * color space.
1189      *
1190      * @param display  Display metrics for the display this bitmap will be
1191      *                 drawn on.
1192      * @param width    The width of the bitmap
1193      * @param height   The height of the bitmap
1194      * @param config   The bitmap config to create.
1195      * @param hasAlpha If the bitmap is ARGB_8888, RGBA_16F, or RGBA_1010102 this flag can be
1196      *                 used to mark the bitmap as opaque. Doing so will clear the bitmap in black
1197      *                 instead of transparent.
1198      *
1199      * @throws IllegalArgumentException if the width or height are <= 0, or if
1200      *         Config is Config.HARDWARE, because hardware bitmaps are always immutable
1201      */
1202     @NonNull
createBitmap(@ullable DisplayMetrics display, int width, int height, @NonNull Config config, boolean hasAlpha)1203     public static Bitmap createBitmap(@Nullable DisplayMetrics display, int width, int height,
1204             @NonNull Config config, boolean hasAlpha) {
1205         return createBitmap(display, width, height, config, hasAlpha,
1206                 ColorSpace.get(ColorSpace.Named.SRGB));
1207     }
1208 
1209     /**
1210      * Returns a mutable bitmap with the specified width and height.  Its
1211      * initial density is determined from the given {@link DisplayMetrics}.
1212      * The newly created bitmap is in the {@link ColorSpace.Named#SRGB sRGB}
1213      * color space.
1214      *
1215      * @param display  Display metrics for the display this bitmap will be
1216      *                 drawn on.
1217      * @param width    The width of the bitmap
1218      * @param height   The height of the bitmap
1219      * @param config   The bitmap config to create.
1220      * @param hasAlpha If the bitmap is ARGB_8888, RGBA_16F, or RGBA_1010102 this flag can be
1221      *                 used to mark the bitmap as opaque. Doing so will clear the bitmap in black
1222      *                 instead of transparent.
1223      * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16}
1224      *                   and {@link ColorSpace.Named#SRGB sRGB} or
1225      *                   {@link ColorSpace.Named#LINEAR_SRGB Linear sRGB} is provided then the
1226      *                   corresponding extended range variant is assumed.
1227      *
1228      * @throws IllegalArgumentException if the width or height are <= 0, if
1229      *         Config is Config.HARDWARE (because hardware bitmaps are always
1230      *         immutable), if the specified color space is not {@link ColorSpace.Model#RGB RGB},
1231      *         if the specified color space's transfer function is not an
1232      *         {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}, or if
1233      *         the color space is null
1234      */
1235     @NonNull
createBitmap(@ullable DisplayMetrics display, int width, int height, @NonNull Config config, boolean hasAlpha, @NonNull ColorSpace colorSpace)1236     public static Bitmap createBitmap(@Nullable DisplayMetrics display, int width, int height,
1237             @NonNull Config config, boolean hasAlpha, @NonNull ColorSpace colorSpace) {
1238         if (width <= 0 || height <= 0) {
1239             throw new IllegalArgumentException("width and height must be > 0");
1240         }
1241         if (config == Config.HARDWARE) {
1242             throw new IllegalArgumentException("can't create mutable bitmap with Config.HARDWARE");
1243         }
1244         if (colorSpace == null && config != Config.ALPHA_8) {
1245             throw new IllegalArgumentException("can't create bitmap without a color space");
1246         }
1247 
1248         Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true,
1249                 colorSpace == null ? 0 : colorSpace.getNativeInstance());
1250 
1251         if (display != null) {
1252             bm.mDensity = display.densityDpi;
1253         }
1254         bm.setHasAlpha(hasAlpha);
1255         if ((config == Config.ARGB_8888 || config == Config.RGBA_F16) && !hasAlpha) {
1256             nativeErase(bm.mNativePtr, 0xff000000);
1257         }
1258         // No need to initialize the bitmap to zeroes with other configs;
1259         // it is backed by a VM byte array which is by definition preinitialized
1260         // to all zeroes.
1261         return bm;
1262     }
1263 
1264     /**
1265      * Returns a immutable bitmap with the specified width and height, with each
1266      * pixel value set to the corresponding value in the colors array.  Its
1267      * initial density is as per {@link #getDensity}. The newly created
1268      * bitmap is in the {@link ColorSpace.Named#SRGB sRGB} color space.
1269      *
1270      * @param colors   Array of sRGB {@link Color colors} used to initialize the pixels.
1271      * @param offset   Number of values to skip before the first color in the
1272      *                 array of colors.
1273      * @param stride   Number of colors in the array between rows (must be >=
1274      *                 width or <= -width).
1275      * @param width    The width of the bitmap
1276      * @param height   The height of the bitmap
1277      * @param config   The bitmap config to create. If the config does not
1278      *                 support per-pixel alpha (e.g. RGB_565), then the alpha
1279      *                 bytes in the colors[] will be ignored (assumed to be FF)
1280      * @throws IllegalArgumentException if the width or height are <= 0, or if
1281      *         the color array's length is less than the number of pixels.
1282      */
1283     @NonNull
createBitmap(@onNull @olorInt int[] colors, int offset, int stride, int width, int height, @NonNull Config config)1284     public static Bitmap createBitmap(@NonNull @ColorInt int[] colors, int offset, int stride,
1285             int width, int height, @NonNull Config config) {
1286         return createBitmap(null, colors, offset, stride, width, height, config);
1287     }
1288 
1289     /**
1290      * Returns a immutable bitmap with the specified width and height, with each
1291      * pixel value set to the corresponding value in the colors array.  Its
1292      * initial density is determined from the given {@link DisplayMetrics}.
1293      * The newly created bitmap is in the {@link ColorSpace.Named#SRGB sRGB}
1294      * color space.
1295      *
1296      * @param display  Display metrics for the display this bitmap will be
1297      *                 drawn on.
1298      * @param colors   Array of sRGB {@link Color colors} used to initialize the pixels.
1299      * @param offset   Number of values to skip before the first color in the
1300      *                 array of colors.
1301      * @param stride   Number of colors in the array between rows (must be >=
1302      *                 width or <= -width).
1303      * @param width    The width of the bitmap
1304      * @param height   The height of the bitmap
1305      * @param config   The bitmap config to create. If the config does not
1306      *                 support per-pixel alpha (e.g. RGB_565), then the alpha
1307      *                 bytes in the colors[] will be ignored (assumed to be FF)
1308      * @throws IllegalArgumentException if the width or height are <= 0, or if
1309      *         the color array's length is less than the number of pixels.
1310      */
1311     @NonNull
createBitmap(@onNull DisplayMetrics display, @NonNull @ColorInt int[] colors, int offset, int stride, int width, int height, @NonNull Config config)1312     public static Bitmap createBitmap(@NonNull DisplayMetrics display,
1313             @NonNull @ColorInt int[] colors, int offset, int stride,
1314             int width, int height, @NonNull Config config) {
1315 
1316         checkWidthHeight(width, height);
1317         if (Math.abs(stride) < width) {
1318             throw new IllegalArgumentException("abs(stride) must be >= width");
1319         }
1320         int lastScanline = offset + (height - 1) * stride;
1321         int length = colors.length;
1322         if (offset < 0 || (offset + width > length) || lastScanline < 0 ||
1323                 (lastScanline + width > length)) {
1324             throw new ArrayIndexOutOfBoundsException();
1325         }
1326         if (width <= 0 || height <= 0) {
1327             throw new IllegalArgumentException("width and height must be > 0");
1328         }
1329         ColorSpace sRGB = ColorSpace.get(ColorSpace.Named.SRGB);
1330         Bitmap bm = nativeCreate(colors, offset, stride, width, height,
1331                             config.nativeInt, false, sRGB.getNativeInstance());
1332         if (display != null) {
1333             bm.mDensity = display.densityDpi;
1334         }
1335         return bm;
1336     }
1337 
1338     /**
1339      * Returns a immutable bitmap with the specified width and height, with each
1340      * pixel value set to the corresponding value in the colors array.  Its
1341      * initial density is as per {@link #getDensity}. The newly created
1342      * bitmap is in the {@link ColorSpace.Named#SRGB sRGB} color space.
1343      *
1344      * @param colors   Array of sRGB {@link Color colors} used to initialize the pixels.
1345      *                 This array must be at least as large as width * height.
1346      * @param width    The width of the bitmap
1347      * @param height   The height of the bitmap
1348      * @param config   The bitmap config to create. If the config does not
1349      *                 support per-pixel alpha (e.g. RGB_565), then the alpha
1350      *                 bytes in the colors[] will be ignored (assumed to be FF)
1351      * @throws IllegalArgumentException if the width or height are <= 0, or if
1352      *         the color array's length is less than the number of pixels.
1353      */
1354     @NonNull
createBitmap(@onNull @olorInt int[] colors, int width, int height, Config config)1355     public static Bitmap createBitmap(@NonNull @ColorInt int[] colors,
1356             int width, int height, Config config) {
1357         return createBitmap(null, colors, 0, width, width, height, config);
1358     }
1359 
1360     /**
1361      * Returns a immutable bitmap with the specified width and height, with each
1362      * pixel value set to the corresponding value in the colors array.  Its
1363      * initial density is determined from the given {@link DisplayMetrics}.
1364      * The newly created bitmap is in the {@link ColorSpace.Named#SRGB sRGB}
1365      * color space.
1366      *
1367      * @param display  Display metrics for the display this bitmap will be
1368      *                 drawn on.
1369      * @param colors   Array of sRGB {@link Color colors} used to initialize the pixels.
1370      *                 This array must be at least as large as width * height.
1371      * @param width    The width of the bitmap
1372      * @param height   The height of the bitmap
1373      * @param config   The bitmap config to create. If the config does not
1374      *                 support per-pixel alpha (e.g. RGB_565), then the alpha
1375      *                 bytes in the colors[] will be ignored (assumed to be FF)
1376      * @throws IllegalArgumentException if the width or height are <= 0, or if
1377      *         the color array's length is less than the number of pixels.
1378      */
1379     @NonNull
createBitmap(@ullable DisplayMetrics display, @NonNull @ColorInt int colors[], int width, int height, @NonNull Config config)1380     public static Bitmap createBitmap(@Nullable DisplayMetrics display,
1381             @NonNull @ColorInt int colors[], int width, int height, @NonNull Config config) {
1382         return createBitmap(display, colors, 0, width, width, height, config);
1383     }
1384 
1385     /**
1386      * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands.
1387      *
1388      * Equivalent to calling {@link #createBitmap(Picture, int, int, Config)} with
1389      * width and height the same as the Picture's width and height and a Config.HARDWARE
1390      * config.
1391      *
1392      * @param source The recorded {@link Picture} of drawing commands that will be
1393      *               drawn into the returned Bitmap.
1394      * @return An immutable bitmap with a HARDWARE config whose contents are created
1395      * from the recorded drawing commands in the Picture source.
1396      */
1397     @NonNull
createBitmap(@onNull Picture source)1398     public static Bitmap createBitmap(@NonNull Picture source) {
1399         return createBitmap(source, source.getWidth(), source.getHeight(), Config.HARDWARE);
1400     }
1401 
1402     /**
1403      * Creates a Bitmap from the given {@link Picture} source of recorded drawing commands.
1404      *
1405      * The bitmap will be immutable with the given width and height. If the width and height
1406      * are not the same as the Picture's width & height, the Picture will be scaled to
1407      * fit the given width and height.
1408      *
1409      * @param source The recorded {@link Picture} of drawing commands that will be
1410      *               drawn into the returned Bitmap.
1411      * @param width The width of the bitmap to create. The picture's width will be
1412      *              scaled to match if necessary.
1413      * @param height The height of the bitmap to create. The picture's height will be
1414      *              scaled to match if necessary.
1415      * @param config The {@link Config} of the created bitmap.
1416      *
1417      * @return An immutable bitmap with a configuration specified by the config parameter
1418      */
1419     @NonNull
createBitmap(@onNull Picture source, int width, int height, @NonNull Config config)1420     public static Bitmap createBitmap(@NonNull Picture source, int width, int height,
1421             @NonNull Config config) {
1422         if (width <= 0 || height <= 0) {
1423             throw new IllegalArgumentException("width & height must be > 0");
1424         }
1425         if (config == null) {
1426             throw new IllegalArgumentException("Config must not be null");
1427         }
1428         source.endRecording();
1429         if (source.requiresHardwareAcceleration() && config != Config.HARDWARE) {
1430             StrictMode.noteSlowCall("GPU readback");
1431         }
1432         if (config == Config.HARDWARE || source.requiresHardwareAcceleration()) {
1433             final RenderNode node = RenderNode.create("BitmapTemporary", null);
1434             node.setLeftTopRightBottom(0, 0, width, height);
1435             node.setClipToBounds(false);
1436             node.setForceDarkAllowed(false);
1437             final RecordingCanvas canvas = node.beginRecording(width, height);
1438             if (source.getWidth() != width || source.getHeight() != height) {
1439                 canvas.scale(width / (float) source.getWidth(),
1440                         height / (float) source.getHeight());
1441             }
1442             canvas.drawPicture(source);
1443             node.endRecording();
1444             Bitmap bitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
1445             if (config != Config.HARDWARE) {
1446                 bitmap = bitmap.copy(config, false);
1447             }
1448             return bitmap;
1449         } else {
1450             Bitmap bitmap = Bitmap.createBitmap(width, height, config);
1451             Canvas canvas = new Canvas(bitmap);
1452             if (source.getWidth() != width || source.getHeight() != height) {
1453                 canvas.scale(width / (float) source.getWidth(),
1454                         height / (float) source.getHeight());
1455             }
1456             canvas.drawPicture(source);
1457             canvas.setBitmap(null);
1458             bitmap.setImmutable();
1459             return bitmap;
1460         }
1461     }
1462 
1463     /**
1464      * Returns an optional array of private data, used by the UI system for
1465      * some bitmaps. Not intended to be called by applications.
1466      */
1467     @Nullable
getNinePatchChunk()1468     public byte[] getNinePatchChunk() {
1469         return mNinePatchChunk;
1470     }
1471 
1472     /**
1473      * Populates a rectangle with the bitmap's optical insets.
1474      *
1475      * @param outInsets Rect to populate with optical insets
1476      *
1477      * @hide
1478      * Must be public for access from android.graphics.drawable,
1479      * but must not be called from outside the UI module.
1480      */
getOpticalInsets(@onNull Rect outInsets)1481     public void getOpticalInsets(@NonNull Rect outInsets) {
1482         if (mNinePatchInsets == null) {
1483             outInsets.setEmpty();
1484         } else {
1485             outInsets.set(mNinePatchInsets.opticalRect);
1486         }
1487     }
1488 
1489     /**
1490      * @hide
1491      * Must be public for access from android.graphics.drawable,
1492      * but must not be called from outside the UI module.
1493      */
getNinePatchInsets()1494     public NinePatch.InsetStruct getNinePatchInsets() {
1495         return mNinePatchInsets;
1496     }
1497 
1498     /**
1499      * Specifies the known formats a bitmap can be compressed into
1500      */
1501     public enum CompressFormat {
1502         /**
1503          * Compress to the JPEG format. {@code quality} of {@code 0} means
1504          * compress for the smallest size. {@code 100} means compress for max
1505          * visual quality.
1506          */
1507         JPEG          (0),
1508         /**
1509          * Compress to the PNG format. PNG is lossless, so {@code quality} is
1510          * ignored.
1511          */
1512         PNG           (1),
1513         /**
1514          * Compress to the WEBP format. {@code quality} of {@code 0} means
1515          * compress for the smallest size. {@code 100} means compress for max
1516          * visual quality. As of {@link android.os.Build.VERSION_CODES#Q}, a
1517          * value of {@code 100} results in a file in the lossless WEBP format.
1518          * Otherwise the file will be in the lossy WEBP format.
1519          *
1520          * @deprecated in favor of the more explicit
1521          *             {@link CompressFormat#WEBP_LOSSY} and
1522          *             {@link CompressFormat#WEBP_LOSSLESS}.
1523          */
1524         @Deprecated
1525         WEBP          (2),
1526         /**
1527          * Compress to the WEBP lossy format. {@code quality} of {@code 0} means
1528          * compress for the smallest size. {@code 100} means compress for max
1529          * visual quality.
1530          */
1531         WEBP_LOSSY    (3),
1532         /**
1533          * Compress to the WEBP lossless format. {@code quality} refers to how
1534          * much effort to put into compression. A value of {@code 0} means to
1535          * compress quickly, resulting in a relatively large file size.
1536          * {@code 100} means to spend more time compressing, resulting in a
1537          * smaller file.
1538          */
1539         WEBP_LOSSLESS (4);
1540 
CompressFormat(int nativeInt)1541         CompressFormat(int nativeInt) {
1542             this.nativeInt = nativeInt;
1543         }
1544         final int nativeInt;
1545     }
1546 
1547     /**
1548      * @hide
1549      */
1550     private static final class DumpData {
1551         private int count;
1552         private int format;
1553         private long[] natives;
1554         private byte[][] buffers;
1555         private int max;
1556 
DumpData(@onNull CompressFormat format, int max)1557         public DumpData(@NonNull CompressFormat format, int max) {
1558             this.max = max;
1559             this.format = format.nativeInt;
1560             this.natives = new long[max];
1561             this.buffers = new byte[max][];
1562             this.count = 0;
1563         }
1564 
add(long nativePtr, byte[] buffer)1565         public void add(long nativePtr, byte[] buffer) {
1566             natives[count] = nativePtr;
1567             buffers[count] = buffer;
1568             count = (count >= max) ? max : count + 1;
1569         }
1570 
size()1571         public int size() {
1572             return count;
1573         }
1574     }
1575 
1576     /**
1577      * @hide
1578      */
1579     private static DumpData dumpData = null;
1580 
1581 
1582     /**
1583      * @hide
1584      *
1585      * Dump all the bitmaps with their contents compressed into dumpData
1586      *
1587      * @param format  format of the compressed image, null to clear dump data
1588      */
dumpAll(@ullable String format)1589     public static void dumpAll(@Nullable String format) {
1590         if (format == null) {
1591             /* release the dump data */
1592             dumpData = null;
1593             return;
1594         }
1595         final CompressFormat fmt;
1596         if (format.equals("jpg") || format.equals("jpeg")) {
1597             fmt = CompressFormat.JPEG;
1598         } else if (format.equals("png")) {
1599             fmt = CompressFormat.PNG;
1600         } else if (format.equals("webp")) {
1601             fmt = CompressFormat.WEBP_LOSSLESS;
1602         } else {
1603             Log.w(TAG, "No bitmaps dumped: unrecognized format " + format);
1604             return;
1605         }
1606 
1607         final ArrayList<Bitmap> allBitmaps;
1608         synchronized (Bitmap.class) {
1609           allBitmaps = new ArrayList<>(sAllBitmaps.size());
1610           for (Bitmap bitmap : sAllBitmaps.keySet()) {
1611             if (bitmap != null && !bitmap.isRecycled()) {
1612               allBitmaps.add(bitmap);
1613             }
1614           }
1615         }
1616 
1617         dumpData = new DumpData(fmt, allBitmaps.size());
1618         for (Bitmap bitmap : allBitmaps) {
1619             ByteArrayOutputStream bas = new ByteArrayOutputStream();
1620             if (bitmap.compress(fmt, 90, bas)) {
1621                 dumpData.add(bitmap.getNativeInstance(), bas.toByteArray());
1622             }
1623         }
1624         Log.i(TAG, dumpData.size() + "/" + allBitmaps.size() + " bitmaps dumped");
1625     }
1626 
1627     /**
1628      * Number of bytes of temp storage we use for communicating between the
1629      * native compressor and the java OutputStream.
1630      */
1631     private final static int WORKING_COMPRESS_STORAGE = 4096;
1632 
1633     /**
1634      * Write a compressed version of the bitmap to the specified outputstream.
1635      * If this returns true, the bitmap can be reconstructed by passing a
1636      * corresponding inputstream to BitmapFactory.decodeStream(). Note: not
1637      * all Formats support all bitmap configs directly, so it is possible that
1638      * the returned bitmap from BitmapFactory could be in a different bitdepth,
1639      * and/or may have lost per-pixel alpha (e.g. JPEG only supports opaque
1640      * pixels).
1641      *
1642      * @param format   The format of the compressed image
1643      * @param quality  Hint to the compressor, 0-100. The value is interpreted
1644      *                 differently depending on the {@link CompressFormat}.
1645      * @param stream   The outputstream to write the compressed data.
1646      * @return true if successfully compressed to the specified stream.
1647      */
1648     @WorkerThread
compress(@onNull CompressFormat format, int quality, @NonNull OutputStream stream)1649     public boolean compress(@NonNull CompressFormat format, int quality,
1650                             @NonNull OutputStream stream) {
1651         checkRecycled("Can't compress a recycled bitmap");
1652         // do explicit check before calling the native method
1653         if (stream == null) {
1654             throw new NullPointerException();
1655         }
1656         if (quality < 0 || quality > 100) {
1657             throw new IllegalArgumentException("quality must be 0..100");
1658         }
1659         StrictMode.noteSlowCall("Compression of a bitmap is slow");
1660         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "Bitmap.compress");
1661         boolean result = nativeCompress(mNativePtr, format.nativeInt,
1662                 quality, stream, new byte[WORKING_COMPRESS_STORAGE]);
1663         Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
1664         return result;
1665     }
1666 
1667     /**
1668      * Returns true if the bitmap is marked as mutable (i.e.&nbsp;can be drawn into)
1669      */
isMutable()1670     public final boolean isMutable() {
1671         return !nativeIsImmutable(mNativePtr);
1672     }
1673 
1674     /**
1675      * Marks the Bitmap as immutable. Further modifications to this Bitmap are disallowed.
1676      * After this method is called, this Bitmap cannot be made mutable again and subsequent calls
1677      * to {@link #reconfigure(int, int, Config)}, {@link #setPixel(int, int, int)},
1678      * {@link #setPixels(int[], int, int, int, int, int, int)} and {@link #eraseColor(int)} will
1679      * fail and throw an IllegalStateException.
1680      */
setImmutable()1681     private void setImmutable() {
1682         if (isMutable()) {
1683             nativeSetImmutable(mNativePtr);
1684         }
1685     }
1686 
1687     /**
1688      * <p>Indicates whether pixels stored in this bitmaps are stored pre-multiplied.
1689      * When a pixel is pre-multiplied, the RGB components have been multiplied by
1690      * the alpha component. For instance, if the original color is a 50%
1691      * translucent red <code>(128, 255, 0, 0)</code>, the pre-multiplied form is
1692      * <code>(128, 128, 0, 0)</code>.</p>
1693      *
1694      * <p>This method always returns false if {@link #getConfig()} is
1695      * {@link Bitmap.Config#RGB_565}.</p>
1696      *
1697      * <p>The return value is undefined if {@link #getConfig()} is
1698      * {@link Bitmap.Config#ALPHA_8}.</p>
1699      *
1700      * <p>This method only returns true if {@link #hasAlpha()} returns true.
1701      * A bitmap with no alpha channel can be used both as a pre-multiplied and
1702      * as a non pre-multiplied bitmap.</p>
1703      *
1704      * <p>Only pre-multiplied bitmaps may be drawn by the view system or
1705      * {@link Canvas}. If a non-pre-multiplied bitmap with an alpha channel is
1706      * drawn to a Canvas, a RuntimeException will be thrown.</p>
1707      *
1708      * @return true if the underlying pixels have been pre-multiplied, false
1709      *         otherwise
1710      *
1711      * @see Bitmap#setPremultiplied(boolean)
1712      * @see BitmapFactory.Options#inPremultiplied
1713      */
isPremultiplied()1714     public final boolean isPremultiplied() {
1715         if (mRecycled) {
1716             Log.w(TAG, "Called isPremultiplied() on a recycle()'d bitmap! This is undefined behavior!");
1717         }
1718         return nativeIsPremultiplied(mNativePtr);
1719     }
1720 
1721     /**
1722      * Sets whether the bitmap should treat its data as pre-multiplied.
1723      *
1724      * <p>Bitmaps are always treated as pre-multiplied by the view system and
1725      * {@link Canvas} for performance reasons. Storing un-pre-multiplied data in
1726      * a Bitmap (through {@link #setPixel}, {@link #setPixels}, or {@link
1727      * BitmapFactory.Options#inPremultiplied BitmapFactory.Options.inPremultiplied})
1728      * can lead to incorrect blending if drawn by the framework.</p>
1729      *
1730      * <p>This method will not affect the behavior of a bitmap without an alpha
1731      * channel, or if {@link #hasAlpha()} returns false.</p>
1732      *
1733      * <p>Calling {@link #createBitmap} or {@link #createScaledBitmap} with a source
1734      * Bitmap whose colors are not pre-multiplied may result in a RuntimeException,
1735      * since those functions require drawing the source, which is not supported for
1736      * un-pre-multiplied Bitmaps.</p>
1737      *
1738      * @see Bitmap#isPremultiplied()
1739      * @see BitmapFactory.Options#inPremultiplied
1740      */
setPremultiplied(boolean premultiplied)1741     public final void setPremultiplied(boolean premultiplied) {
1742         checkRecycled("setPremultiplied called on a recycled bitmap");
1743         mRequestPremultiplied = premultiplied;
1744         nativeSetPremultiplied(mNativePtr, premultiplied);
1745     }
1746 
1747     /** Returns the bitmap's width */
getWidth()1748     public final int getWidth() {
1749         if (mRecycled) {
1750             Log.w(TAG, "Called getWidth() on a recycle()'d bitmap! This is undefined behavior!");
1751         }
1752         return mWidth;
1753     }
1754 
1755     /** Returns the bitmap's height */
getHeight()1756     public final int getHeight() {
1757         if (mRecycled) {
1758             Log.w(TAG, "Called getHeight() on a recycle()'d bitmap! This is undefined behavior!");
1759         }
1760         return mHeight;
1761     }
1762 
1763     /**
1764      * Convenience for calling {@link #getScaledWidth(int)} with the target
1765      * density of the given {@link Canvas}.
1766      */
getScaledWidth(@onNull Canvas canvas)1767     public int getScaledWidth(@NonNull Canvas canvas) {
1768         return scaleFromDensity(getWidth(), mDensity, canvas.mDensity);
1769     }
1770 
1771     /**
1772      * Convenience for calling {@link #getScaledHeight(int)} with the target
1773      * density of the given {@link Canvas}.
1774      */
getScaledHeight(@onNull Canvas canvas)1775     public int getScaledHeight(@NonNull Canvas canvas) {
1776         return scaleFromDensity(getHeight(), mDensity, canvas.mDensity);
1777     }
1778 
1779     /**
1780      * Convenience for calling {@link #getScaledWidth(int)} with the target
1781      * density of the given {@link DisplayMetrics}.
1782      */
getScaledWidth(@onNull DisplayMetrics metrics)1783     public int getScaledWidth(@NonNull DisplayMetrics metrics) {
1784         return scaleFromDensity(getWidth(), mDensity, metrics.densityDpi);
1785     }
1786 
1787     /**
1788      * Convenience for calling {@link #getScaledHeight(int)} with the target
1789      * density of the given {@link DisplayMetrics}.
1790      */
getScaledHeight(@onNull DisplayMetrics metrics)1791     public int getScaledHeight(@NonNull DisplayMetrics metrics) {
1792         return scaleFromDensity(getHeight(), mDensity, metrics.densityDpi);
1793     }
1794 
1795     /**
1796      * Convenience method that returns the width of this bitmap divided
1797      * by the density scale factor.
1798      *
1799      * Returns the bitmap's width multiplied by the ratio of the target density to the bitmap's
1800      * source density
1801      *
1802      * @param targetDensity The density of the target canvas of the bitmap.
1803      * @return The scaled width of this bitmap, according to the density scale factor.
1804      */
getScaledWidth(int targetDensity)1805     public int getScaledWidth(int targetDensity) {
1806         return scaleFromDensity(getWidth(), mDensity, targetDensity);
1807     }
1808 
1809     /**
1810      * Convenience method that returns the height of this bitmap divided
1811      * by the density scale factor.
1812      *
1813      * Returns the bitmap's height multiplied by the ratio of the target density to the bitmap's
1814      * source density
1815      *
1816      * @param targetDensity The density of the target canvas of the bitmap.
1817      * @return The scaled height of this bitmap, according to the density scale factor.
1818      */
getScaledHeight(int targetDensity)1819     public int getScaledHeight(int targetDensity) {
1820         return scaleFromDensity(getHeight(), mDensity, targetDensity);
1821     }
1822 
1823     /**
1824      * @hide
1825      * Must be public for access from android.graphics.drawable,
1826      * but must not be called from outside the UI module.
1827      */
1828     @UnsupportedAppUsage
scaleFromDensity(int size, int sdensity, int tdensity)1829     static public int scaleFromDensity(int size, int sdensity, int tdensity) {
1830         if (sdensity == DENSITY_NONE || tdensity == DENSITY_NONE || sdensity == tdensity) {
1831             return size;
1832         }
1833 
1834         // Scale by tdensity / sdensity, rounding up.
1835         return ((size * tdensity) + (sdensity >> 1)) / sdensity;
1836     }
1837 
1838     /**
1839      * Return the number of bytes between rows in the bitmap's pixels. Note that
1840      * this refers to the pixels as stored natively by the bitmap. If you call
1841      * getPixels() or setPixels(), then the pixels are uniformly treated as
1842      * 32bit values, packed according to the Color class.
1843      *
1844      * <p>As of {@link android.os.Build.VERSION_CODES#KITKAT}, this method
1845      * should not be used to calculate the memory usage of the bitmap. Instead,
1846      * see {@link #getAllocationByteCount()}.
1847      *
1848      * @return number of bytes between rows of the native bitmap pixels.
1849      */
getRowBytes()1850     public final int getRowBytes() {
1851         if (mRecycled) {
1852             Log.w(TAG, "Called getRowBytes() on a recycle()'d bitmap! This is undefined behavior!");
1853         }
1854         return nativeRowBytes(mNativePtr);
1855     }
1856 
1857     /**
1858      * Returns the minimum number of bytes that can be used to store this bitmap's pixels.
1859      *
1860      * <p>As of {@link android.os.Build.VERSION_CODES#KITKAT}, the result of this method can
1861      * no longer be used to determine memory usage of a bitmap. See {@link
1862      * #getAllocationByteCount()}.</p>
1863      */
getByteCount()1864     public final int getByteCount() {
1865         if (mRecycled) {
1866             Log.w(TAG, "Called getByteCount() on a recycle()'d bitmap! "
1867                     + "This is undefined behavior!");
1868             return 0;
1869         }
1870         // int result permits bitmaps up to 46,340 x 46,340
1871         return getRowBytes() * getHeight();
1872     }
1873 
1874     /**
1875      * Returns the size of the allocated memory used to store this bitmap's pixels.
1876      *
1877      * <p>This can be larger than the result of {@link #getByteCount()} if a bitmap is reused to
1878      * decode other bitmaps of smaller size, or by manual reconfiguration. See {@link
1879      * #reconfigure(int, int, Config)}, {@link #setWidth(int)}, {@link #setHeight(int)}, {@link
1880      * #setConfig(Bitmap.Config)}, and {@link BitmapFactory.Options#inBitmap
1881      * BitmapFactory.Options.inBitmap}. If a bitmap is not modified in this way, this value will be
1882      * the same as that returned by {@link #getByteCount()}.</p>
1883      *
1884      * <p>This value will not change over the lifetime of a Bitmap.</p>
1885      *
1886      * @see #reconfigure(int, int, Config)
1887      */
getAllocationByteCount()1888     public final int getAllocationByteCount() {
1889         if (mRecycled) {
1890             Log.w(TAG, "Called getAllocationByteCount() on a recycle()'d bitmap! "
1891                     + "This is undefined behavior!");
1892             return 0;
1893         }
1894         return nativeGetAllocationByteCount(mNativePtr);
1895     }
1896 
1897     /**
1898      * If the bitmap's internal config is in one of the public formats, return
1899      * that config, otherwise return null.
1900      */
1901     @Nullable
getConfig()1902     public final Config getConfig() {
1903         if (mRecycled) {
1904             Log.w(TAG, "Called getConfig() on a recycle()'d bitmap! This is undefined behavior!");
1905         }
1906         return Config.nativeToConfig(nativeConfig(mNativePtr));
1907     }
1908 
1909     /** Returns true if the bitmap's config supports per-pixel alpha, and
1910      * if the pixels may contain non-opaque alpha values. For some configs,
1911      * this is always false (e.g. RGB_565), since they do not support per-pixel
1912      * alpha. However, for configs that do, the bitmap may be flagged to be
1913      * known that all of its pixels are opaque. In this case hasAlpha() will
1914      * also return false. If a config such as ARGB_8888 is not so flagged,
1915      * it will return true by default.
1916      */
hasAlpha()1917     public final boolean hasAlpha() {
1918         if (mRecycled) {
1919             Log.w(TAG, "Called hasAlpha() on a recycle()'d bitmap! This is undefined behavior!");
1920         }
1921         return nativeHasAlpha(mNativePtr);
1922     }
1923 
1924     /**
1925      * Tell the bitmap if all of the pixels are known to be opaque (false)
1926      * or if some of the pixels may contain non-opaque alpha values (true).
1927      * Note, for some configs (e.g. RGB_565) this call is ignored, since it
1928      * does not support per-pixel alpha values.
1929      *
1930      * This is meant as a drawing hint, as in some cases a bitmap that is known
1931      * to be opaque can take a faster drawing case than one that may have
1932      * non-opaque per-pixel alpha values.
1933      */
setHasAlpha(boolean hasAlpha)1934     public void setHasAlpha(boolean hasAlpha) {
1935         checkRecycled("setHasAlpha called on a recycled bitmap");
1936         nativeSetHasAlpha(mNativePtr, hasAlpha, mRequestPremultiplied);
1937     }
1938 
1939     /**
1940      * Indicates whether the renderer responsible for drawing this
1941      * bitmap should attempt to use mipmaps when this bitmap is drawn
1942      * scaled down.
1943      *
1944      * If you know that you are going to draw this bitmap at less than
1945      * 50% of its original size, you may be able to obtain a higher
1946      * quality
1947      *
1948      * This property is only a suggestion that can be ignored by the
1949      * renderer. It is not guaranteed to have any effect.
1950      *
1951      * @return true if the renderer should attempt to use mipmaps,
1952      *         false otherwise
1953      *
1954      * @see #setHasMipMap(boolean)
1955      */
hasMipMap()1956     public final boolean hasMipMap() {
1957         if (mRecycled) {
1958             Log.w(TAG, "Called hasMipMap() on a recycle()'d bitmap! This is undefined behavior!");
1959         }
1960         return nativeHasMipMap(mNativePtr);
1961     }
1962 
1963     /**
1964      * Set a hint for the renderer responsible for drawing this bitmap
1965      * indicating that it should attempt to use mipmaps when this bitmap
1966      * is drawn scaled down.
1967      *
1968      * If you know that you are going to draw this bitmap at less than
1969      * 50% of its original size, you may be able to obtain a higher
1970      * quality by turning this property on.
1971      *
1972      * Note that if the renderer respects this hint it might have to
1973      * allocate extra memory to hold the mipmap levels for this bitmap.
1974      *
1975      * This property is only a suggestion that can be ignored by the
1976      * renderer. It is not guaranteed to have any effect.
1977      *
1978      * @param hasMipMap indicates whether the renderer should attempt
1979      *                  to use mipmaps
1980      *
1981      * @see #hasMipMap()
1982      */
setHasMipMap(boolean hasMipMap)1983     public final void setHasMipMap(boolean hasMipMap) {
1984         checkRecycled("setHasMipMap called on a recycled bitmap");
1985         nativeSetHasMipMap(mNativePtr, hasMipMap);
1986     }
1987 
1988     /**
1989      * Returns the color space associated with this bitmap. If the color
1990      * space is unknown, this method returns null.
1991      */
1992     @Nullable
getColorSpace()1993     public final ColorSpace getColorSpace() {
1994         checkRecycled("getColorSpace called on a recycled bitmap");
1995         if (mColorSpace == null) {
1996             mColorSpace = nativeComputeColorSpace(mNativePtr);
1997         }
1998         return mColorSpace;
1999     }
2000 
2001     /**
2002      * <p>Modifies the bitmap to have the specified {@link ColorSpace}, without
2003      * affecting the underlying allocation backing the bitmap.</p>
2004      *
2005      * <p>This affects how the framework will interpret the color at each pixel. A bitmap
2006      * with {@link Config#ALPHA_8} never has a color space, since a color space does not
2007      * affect the alpha channel. Other {@code Config}s must always have a non-null
2008      * {@code ColorSpace}.</p>
2009      *
2010      * @throws IllegalArgumentException If the specified color space is {@code null}, not
2011      *         {@link ColorSpace.Model#RGB RGB}, or whose components min/max values reduce
2012      *         the numerical range compared to the previously assigned color space.
2013      *         Prior to {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
2014      *         <code>IllegalArgumentException</code> will also be thrown
2015      *         if the specified color space has a transfer function that is not an
2016      *         {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}. Starting from
2017      *         {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, the color spaces with non
2018      *         ICC parametric curve transfer function are allowed.
2019      *         E.g., {@link ColorSpace.Named#BT2020_HLG BT2020_HLG}.
2020      *
2021      * @throws IllegalArgumentException If the {@code Config} (returned by {@link #getConfig()})
2022      *         is {@link Config#ALPHA_8}.
2023      *
2024      * @param colorSpace to assign to the bitmap
2025      */
setColorSpace(@onNull ColorSpace colorSpace)2026     public void setColorSpace(@NonNull ColorSpace colorSpace) {
2027         checkRecycled("setColorSpace called on a recycled bitmap");
2028         if (colorSpace == null) {
2029             throw new IllegalArgumentException("The colorSpace cannot be set to null");
2030         }
2031 
2032         if (getConfig() == Config.ALPHA_8) {
2033             throw new IllegalArgumentException("Cannot set a ColorSpace on ALPHA_8");
2034         }
2035 
2036         // Keep track of the old ColorSpace for comparison, and so we can reset it in case of an
2037         // Exception.
2038         final ColorSpace oldColorSpace = getColorSpace();
2039         nativeSetColorSpace(mNativePtr, colorSpace.getNativeInstance());
2040 
2041         // This will update mColorSpace. It may not be the same as |colorSpace|, e.g. if we
2042         // corrected it because the Bitmap is F16.
2043         mColorSpace = null;
2044         final ColorSpace newColorSpace = getColorSpace();
2045 
2046         try {
2047             if (oldColorSpace.getComponentCount() != newColorSpace.getComponentCount()) {
2048                 throw new IllegalArgumentException("The new ColorSpace must have the same "
2049                         + "component count as the current ColorSpace");
2050             } else {
2051                 for (int i = 0; i < oldColorSpace.getComponentCount(); i++) {
2052                     if (oldColorSpace.getMinValue(i) < newColorSpace.getMinValue(i)) {
2053                         throw new IllegalArgumentException("The new ColorSpace cannot increase the "
2054                                 + "minimum value for any of the components compared to the current "
2055                                 + "ColorSpace. To perform this type of conversion create a new "
2056                                 + "Bitmap in the desired ColorSpace and draw this Bitmap into it.");
2057                     }
2058                     if (oldColorSpace.getMaxValue(i) > newColorSpace.getMaxValue(i)) {
2059                         throw new IllegalArgumentException("The new ColorSpace cannot decrease the "
2060                                 + "maximum value for any of the components compared to the current "
2061                                 + "ColorSpace/ To perform this type of conversion create a new "
2062                                 + "Bitmap in the desired ColorSpace and draw this Bitmap into it.");
2063                     }
2064                 }
2065             }
2066         } catch (IllegalArgumentException e) {
2067             // Undo the change to the ColorSpace.
2068             mColorSpace = oldColorSpace;
2069             nativeSetColorSpace(mNativePtr, mColorSpace.getNativeInstance());
2070             throw e;
2071         }
2072     }
2073 
2074     /**
2075      * Returns whether or not this Bitmap contains a Gainmap.
2076      */
hasGainmap()2077     public boolean hasGainmap() {
2078         checkRecycled("Bitmap is recycled");
2079         return nativeHasGainmap(mNativePtr);
2080     }
2081 
2082     /**
2083      * Returns the gainmap or null if the bitmap doesn't contain a gainmap
2084      */
getGainmap()2085     public @Nullable Gainmap getGainmap() {
2086         checkRecycled("Bitmap is recycled");
2087         if (mGainmap == null) {
2088             mGainmap = nativeExtractGainmap(mNativePtr);
2089         }
2090         return mGainmap;
2091     }
2092 
2093     /**
2094      * Sets a gainmap on this bitmap, or removes the gainmap if null
2095      */
setGainmap(@ullable Gainmap gainmap)2096     public void setGainmap(@Nullable Gainmap gainmap) {
2097         checkRecycled("Bitmap is recycled");
2098         mGainmap = gainmap;
2099         nativeSetGainmap(mNativePtr, gainmap == null ? 0 : gainmap.mNativePtr);
2100     }
2101 
2102     /**
2103      * Fills the bitmap's pixels with the specified {@link Color}.
2104      *
2105      * @throws IllegalStateException if the bitmap is not mutable.
2106      */
eraseColor(@olorInt int c)2107     public void eraseColor(@ColorInt int c) {
2108         checkRecycled("Can't erase a recycled bitmap");
2109         if (!isMutable()) {
2110             throw new IllegalStateException("cannot erase immutable bitmaps");
2111         }
2112         nativeErase(mNativePtr, c);
2113     }
2114 
2115     /**
2116      * Fills the bitmap's pixels with the specified {@code ColorLong}.
2117      *
2118      * @param color The color to fill as packed by the {@link Color} class.
2119      * @throws IllegalStateException if the bitmap is not mutable.
2120      * @throws IllegalArgumentException if the color space encoded in the
2121      *                                  {@code ColorLong} is invalid or unknown.
2122      *
2123      */
eraseColor(@olorLong long color)2124     public void eraseColor(@ColorLong long color) {
2125         checkRecycled("Can't erase a recycled bitmap");
2126         if (!isMutable()) {
2127             throw new IllegalStateException("cannot erase immutable bitmaps");
2128         }
2129 
2130         ColorSpace cs = Color.colorSpace(color);
2131         nativeErase(mNativePtr, cs.getNativeInstance(), color);
2132     }
2133 
2134     /**
2135      * Returns the {@link Color} at the specified location. Throws an exception
2136      * if x or y are out of bounds (negative or >= to the width or height
2137      * respectively). The returned color is a non-premultiplied ARGB value in
2138      * the {@link ColorSpace.Named#SRGB sRGB} color space.
2139      *
2140      * @param x    The x coordinate (0...width-1) of the pixel to return
2141      * @param y    The y coordinate (0...height-1) of the pixel to return
2142      * @return     The argb {@link Color} at the specified coordinate
2143      * @throws IllegalArgumentException if x, y exceed the bitmap's bounds
2144      * @throws IllegalStateException if the bitmap's config is {@link Config#HARDWARE}
2145      */
2146     @ColorInt
getPixel(int x, int y)2147     public int getPixel(int x, int y) {
2148         checkRecycled("Can't call getPixel() on a recycled bitmap");
2149         checkHardware("unable to getPixel(), "
2150                 + "pixel access is not supported on Config#HARDWARE bitmaps");
2151         checkPixelAccess(x, y);
2152         return nativeGetPixel(mNativePtr, x, y);
2153     }
2154 
clamp(float value, @NonNull ColorSpace cs, int index)2155     private static float clamp(float value, @NonNull ColorSpace cs, int index) {
2156         return Math.max(Math.min(value, cs.getMaxValue(index)), cs.getMinValue(index));
2157     }
2158 
2159     /**
2160      * Returns the {@link Color} at the specified location. Throws an exception
2161      * if x or y are out of bounds (negative or >= to the width or height
2162      * respectively).
2163      *
2164      * @param x    The x coordinate (0...width-1) of the pixel to return
2165      * @param y    The y coordinate (0...height-1) of the pixel to return
2166      * @return     The {@link Color} at the specified coordinate
2167      * @throws IllegalArgumentException if x, y exceed the bitmap's bounds
2168      * @throws IllegalStateException if the bitmap's config is {@link Config#HARDWARE}
2169      *
2170      */
2171     @NonNull
getColor(int x, int y)2172     public Color getColor(int x, int y) {
2173         checkRecycled("Can't call getColor() on a recycled bitmap");
2174         checkHardware("unable to getColor(), "
2175                 + "pixel access is not supported on Config#HARDWARE bitmaps");
2176         checkPixelAccess(x, y);
2177 
2178         final ColorSpace cs = getColorSpace();
2179         if (cs == null || cs.equals(ColorSpace.get(ColorSpace.Named.SRGB))) {
2180             return Color.valueOf(nativeGetPixel(mNativePtr, x, y));
2181         }
2182         // The returned value is in kRGBA_F16_SkColorType, which is packed as
2183         // four half-floats, r,g,b,a.
2184         long rgba = nativeGetColor(mNativePtr, x, y);
2185         float r = Half.toFloat((short) ((rgba >>  0) & 0xffff));
2186         float g = Half.toFloat((short) ((rgba >> 16) & 0xffff));
2187         float b = Half.toFloat((short) ((rgba >> 32) & 0xffff));
2188         float a = Half.toFloat((short) ((rgba >> 48) & 0xffff));
2189 
2190         // Skia may draw outside of the numerical range of the colorSpace.
2191         // Clamp to get an expected value.
2192         return Color.valueOf(clamp(r, cs, 0), clamp(g, cs, 1), clamp(b, cs, 2), a, cs);
2193     }
2194 
2195     /**
2196      * Returns in pixels[] a copy of the data in the bitmap. Each value is
2197      * a packed int representing a {@link Color}. The stride parameter allows
2198      * the caller to allow for gaps in the returned pixels array between
2199      * rows. For normal packed results, just pass width for the stride value.
2200      * The returned colors are non-premultiplied ARGB values in the
2201      * {@link ColorSpace.Named#SRGB sRGB} color space.
2202      *
2203      * @param pixels   The array to receive the bitmap's colors
2204      * @param offset   The first index to write into pixels[]
2205      * @param stride   The number of entries in pixels[] to skip between
2206      *                 rows (must be >= bitmap's width). Can be negative.
2207      * @param x        The x coordinate of the first pixel to read from
2208      *                 the bitmap
2209      * @param y        The y coordinate of the first pixel to read from
2210      *                 the bitmap
2211      * @param width    The number of pixels to read from each row
2212      * @param height   The number of rows to read
2213      *
2214      * @throws IllegalArgumentException if x, y, width, height exceed the
2215      *         bounds of the bitmap, or if abs(stride) < width.
2216      * @throws ArrayIndexOutOfBoundsException if the pixels array is too small
2217      *         to receive the specified number of pixels.
2218      * @throws IllegalStateException if the bitmap's config is {@link Config#HARDWARE}
2219      */
getPixels(@onNull @olorInt int[] pixels, int offset, int stride, int x, int y, int width, int height)2220     public void getPixels(@NonNull @ColorInt int[] pixels, int offset, int stride,
2221                           int x, int y, int width, int height) {
2222         checkRecycled("Can't call getPixels() on a recycled bitmap");
2223         checkHardware("unable to getPixels(), "
2224                 + "pixel access is not supported on Config#HARDWARE bitmaps");
2225         if (width == 0 || height == 0) {
2226             return; // nothing to do
2227         }
2228         checkPixelsAccess(x, y, width, height, offset, stride, pixels);
2229         nativeGetPixels(mNativePtr, pixels, offset, stride,
2230                         x, y, width, height);
2231     }
2232 
2233     /**
2234      * Shared code to check for illegal arguments passed to getPixel()
2235      * or setPixel()
2236      *
2237      * @param x x coordinate of the pixel
2238      * @param y y coordinate of the pixel
2239      */
checkPixelAccess(int x, int y)2240     private void checkPixelAccess(int x, int y) {
2241         checkXYSign(x, y);
2242         if (x >= getWidth()) {
2243             throw new IllegalArgumentException("x must be < bitmap.width()");
2244         }
2245         if (y >= getHeight()) {
2246             throw new IllegalArgumentException("y must be < bitmap.height()");
2247         }
2248     }
2249 
2250     /**
2251      * Shared code to check for illegal arguments passed to getPixels()
2252      * or setPixels()
2253      *
2254      * @param x left edge of the area of pixels to access
2255      * @param y top edge of the area of pixels to access
2256      * @param width width of the area of pixels to access
2257      * @param height height of the area of pixels to access
2258      * @param offset offset into pixels[] array
2259      * @param stride number of elements in pixels[] between each logical row
2260      * @param pixels array to hold the area of pixels being accessed
2261     */
checkPixelsAccess(int x, int y, int width, int height, int offset, int stride, int pixels[])2262     private void checkPixelsAccess(int x, int y, int width, int height,
2263                                    int offset, int stride, int pixels[]) {
2264         checkXYSign(x, y);
2265         if (width < 0) {
2266             throw new IllegalArgumentException("width must be >= 0");
2267         }
2268         if (height < 0) {
2269             throw new IllegalArgumentException("height must be >= 0");
2270         }
2271         if (x + width > getWidth()) {
2272             throw new IllegalArgumentException(
2273                     "x + width must be <= bitmap.width()");
2274         }
2275         if (y + height > getHeight()) {
2276             throw new IllegalArgumentException(
2277                     "y + height must be <= bitmap.height()");
2278         }
2279         if (Math.abs(stride) < width) {
2280             throw new IllegalArgumentException("abs(stride) must be >= width");
2281         }
2282         int lastScanline = offset + (height - 1) * stride;
2283         int length = pixels.length;
2284         if (offset < 0 || (offset + width > length)
2285                 || lastScanline < 0
2286                 || (lastScanline + width > length)) {
2287             throw new ArrayIndexOutOfBoundsException();
2288         }
2289     }
2290 
2291     /**
2292      * <p>Write the specified {@link Color} into the bitmap (assuming it is
2293      * mutable) at the x,y coordinate. The color must be a
2294      * non-premultiplied ARGB value in the {@link ColorSpace.Named#SRGB sRGB}
2295      * color space.</p>
2296      *
2297      * @param x     The x coordinate of the pixel to replace (0...width-1)
2298      * @param y     The y coordinate of the pixel to replace (0...height-1)
2299      * @param color The ARGB color to write into the bitmap
2300      *
2301      * @throws IllegalStateException if the bitmap is not mutable
2302      * @throws IllegalArgumentException if x, y are outside of the bitmap's
2303      *         bounds.
2304      */
setPixel(int x, int y, @ColorInt int color)2305     public void setPixel(int x, int y, @ColorInt int color) {
2306         checkRecycled("Can't call setPixel() on a recycled bitmap");
2307         if (!isMutable()) {
2308             throw new IllegalStateException();
2309         }
2310         checkPixelAccess(x, y);
2311         nativeSetPixel(mNativePtr, x, y, color);
2312     }
2313 
2314     /**
2315      * <p>Replace pixels in the bitmap with the colors in the array. Each element
2316      * in the array is a packed int representing a non-premultiplied ARGB
2317      * {@link Color} in the {@link ColorSpace.Named#SRGB sRGB} color space.</p>
2318      *
2319      * @param pixels   The colors to write to the bitmap
2320      * @param offset   The index of the first color to read from pixels[]
2321      * @param stride   The number of colors in pixels[] to skip between rows.
2322      *                 Normally this value will be the same as the width of
2323      *                 the bitmap, but it can be larger (or negative).
2324      * @param x        The x coordinate of the first pixel to write to in
2325      *                 the bitmap.
2326      * @param y        The y coordinate of the first pixel to write to in
2327      *                 the bitmap.
2328      * @param width    The number of colors to copy from pixels[] per row
2329      * @param height   The number of rows to write to the bitmap
2330      *
2331      * @throws IllegalStateException if the bitmap is not mutable
2332      * @throws IllegalArgumentException if x, y, width, height are outside of
2333      *         the bitmap's bounds.
2334      * @throws ArrayIndexOutOfBoundsException if the pixels array is too small
2335      *         to receive the specified number of pixels.
2336      */
setPixels(@onNull @olorInt int[] pixels, int offset, int stride, int x, int y, int width, int height)2337     public void setPixels(@NonNull @ColorInt int[] pixels, int offset, int stride,
2338             int x, int y, int width, int height) {
2339         checkRecycled("Can't call setPixels() on a recycled bitmap");
2340         if (!isMutable()) {
2341             throw new IllegalStateException();
2342         }
2343         if (width == 0 || height == 0) {
2344             return; // nothing to do
2345         }
2346         checkPixelsAccess(x, y, width, height, offset, stride, pixels);
2347         nativeSetPixels(mNativePtr, pixels, offset, stride,
2348                         x, y, width, height);
2349     }
2350 
2351     public static final @NonNull Parcelable.Creator<Bitmap> CREATOR
2352             = new Parcelable.Creator<Bitmap>() {
2353                 /**
2354                  * Rebuilds a bitmap previously stored with writeToParcel().
2355                  *
2356                  * @param p    Parcel object to read the bitmap from
2357                  * @return a new bitmap created from the data in the parcel
2358                  */
2359                 public Bitmap createFromParcel(Parcel p) {
2360                     Bitmap bm = nativeCreateFromParcel(p);
2361                     if (bm == null) {
2362                         throw new RuntimeException("Failed to unparcel Bitmap");
2363                     }
2364                     if (p.readBoolean()) {
2365                         bm.setGainmap(p.readTypedObject(Gainmap.CREATOR));
2366                     }
2367                     return bm;
2368                 }
2369                 public Bitmap[] newArray(int size) {
2370                     return new Bitmap[size];
2371                 }
2372             };
2373 
2374     /**
2375      * No special parcel contents.
2376      */
describeContents()2377     public int describeContents() {
2378         return 0;
2379     }
2380 
2381     /**
2382      * Write the bitmap and its pixels to the parcel. The bitmap can be
2383      * rebuilt from the parcel by calling CREATOR.createFromParcel().
2384      *
2385      * If this bitmap is {@link Config#HARDWARE}, it may be unparceled with a different pixel
2386      * format (e.g. 565, 8888), but the content will be preserved to the best quality permitted
2387      * by the final pixel format
2388      * @param p    Parcel object to write the bitmap data into
2389      */
writeToParcel(@onNull Parcel p, int flags)2390     public void writeToParcel(@NonNull Parcel p, int flags) {
2391         checkRecycled("Can't parcel a recycled bitmap");
2392         noteHardwareBitmapSlowCall();
2393         if (!nativeWriteToParcel(mNativePtr, mDensity, p)) {
2394             throw new RuntimeException("native writeToParcel failed");
2395         }
2396         if (hasGainmap()) {
2397             p.writeBoolean(true);
2398             p.writeTypedObject(mGainmap, flags);
2399         } else {
2400             p.writeBoolean(false);
2401         }
2402     }
2403 
2404     /**
2405      * Returns a new bitmap that captures the alpha values of the original.
2406      * This may be drawn with Canvas.drawBitmap(), where the color(s) will be
2407      * taken from the paint that is passed to the draw call.
2408      *
2409      * @return new bitmap containing the alpha channel of the original bitmap.
2410      */
2411     @CheckResult
2412     @NonNull
extractAlpha()2413     public Bitmap extractAlpha() {
2414         return extractAlpha(null, null);
2415     }
2416 
2417     /**
2418      * Returns a new bitmap that captures the alpha values of the original.
2419      * These values may be affected by the optional Paint parameter, which
2420      * can contain its own alpha, and may also contain a MaskFilter which
2421      * could change the actual dimensions of the resulting bitmap (e.g.
2422      * a blur maskfilter might enlarge the resulting bitmap). If offsetXY
2423      * is not null, it returns the amount to offset the returned bitmap so
2424      * that it will logically align with the original. For example, if the
2425      * paint contains a blur of radius 2, then offsetXY[] would contains
2426      * -2, -2, so that drawing the alpha bitmap offset by (-2, -2) and then
2427      * drawing the original would result in the blur visually aligning with
2428      * the original.
2429      *
2430      * <p>The initial density of the returned bitmap is the same as the original's.
2431      *
2432      * @param paint Optional paint used to modify the alpha values in the
2433      *              resulting bitmap. Pass null for default behavior.
2434      * @param offsetXY Optional array that returns the X (index 0) and Y
2435      *                 (index 1) offset needed to position the returned bitmap
2436      *                 so that it visually lines up with the original.
2437      * @return new bitmap containing the (optionally modified by paint) alpha
2438      *         channel of the original bitmap. This may be drawn with
2439      *         Canvas.drawBitmap(), where the color(s) will be taken from the
2440      *         paint that is passed to the draw call.
2441      */
2442     @CheckResult
2443     @NonNull
extractAlpha(@ullable Paint paint, int[] offsetXY)2444     public Bitmap extractAlpha(@Nullable Paint paint, int[] offsetXY) {
2445         checkRecycled("Can't extractAlpha on a recycled bitmap");
2446         long nativePaint = paint != null ? paint.getNativeInstance() : 0;
2447         noteHardwareBitmapSlowCall();
2448         Bitmap bm = nativeExtractAlpha(mNativePtr, nativePaint, offsetXY);
2449         if (bm == null) {
2450             throw new RuntimeException("Failed to extractAlpha on Bitmap");
2451         }
2452         bm.mDensity = mDensity;
2453         return bm;
2454     }
2455 
2456     /**
2457      *  Given another bitmap, return true if it has the same dimensions, config,
2458      *  and pixel data as this bitmap. If any of those differ, return false.
2459      *  If other is null, return false.
2460      */
2461     @WorkerThread
sameAs(@ullable Bitmap other)2462     public boolean sameAs(@Nullable Bitmap other) {
2463         StrictMode.noteSlowCall("sameAs compares pixel data, not expected to be fast");
2464         checkRecycled("Can't call sameAs on a recycled bitmap!");
2465         if (this == other) return true;
2466         if (other == null) return false;
2467         if (other.isRecycled()) {
2468             throw new IllegalArgumentException("Can't compare to a recycled bitmap!");
2469         }
2470         return nativeSameAs(mNativePtr, other.mNativePtr);
2471     }
2472 
2473     /**
2474      * Builds caches associated with the bitmap that are used for drawing it.
2475      *
2476      * <p>Starting in {@link android.os.Build.VERSION_CODES#N}, this call initiates an asynchronous
2477      * upload to the GPU on RenderThread, if the Bitmap is not already uploaded. With Hardware
2478      * Acceleration, Bitmaps must be uploaded to the GPU in order to be rendered. This is done by
2479      * default the first time a Bitmap is drawn, but the process can take several milliseconds,
2480      * depending on the size of the Bitmap. Each time a Bitmap is modified and drawn again, it must
2481      * be re-uploaded.</p>
2482      *
2483      * <p>Calling this method in advance can save time in the first frame it's used. For example, it
2484      * is recommended to call this on an image decoding worker thread when a decoded Bitmap is about
2485      * to be displayed. It is recommended to make any pre-draw modifications to the Bitmap before
2486      * calling this method, so the cached, uploaded copy may be reused without re-uploading.</p>
2487      *
2488      * In {@link android.os.Build.VERSION_CODES#KITKAT} and below, for purgeable bitmaps, this call
2489      * would attempt to ensure that the pixels have been decoded.
2490      */
prepareToDraw()2491     public void prepareToDraw() {
2492         checkRecycled("Can't prepareToDraw on a recycled bitmap!");
2493         // Kick off an update/upload of the bitmap outside of the normal
2494         // draw path.
2495         nativePrepareToDraw(mNativePtr);
2496     }
2497 
2498     /**
2499      * @return {@link HardwareBuffer} which is internally used by hardware bitmap
2500      *
2501      * Note: the HardwareBuffer does *not* have an associated {@link ColorSpace}.
2502      * To render this object the same as its rendered with this Bitmap, you
2503      * should also call {@link #getColorSpace()}.</p>
2504      *
2505      * Must not be modified while a wrapped Bitmap is accessing it. Doing so will
2506      * result in undefined behavior.</p>
2507      *
2508      * @throws IllegalStateException if the bitmap's config is not {@link Config#HARDWARE}
2509      * or if the bitmap has been recycled.
2510      */
2511     @NonNull
getHardwareBuffer()2512     public HardwareBuffer getHardwareBuffer() {
2513         checkRecycled("Can't getHardwareBuffer from a recycled bitmap");
2514         HardwareBuffer hardwareBuffer = mHardwareBuffer == null ? null : mHardwareBuffer.get();
2515         if (hardwareBuffer == null || hardwareBuffer.isClosed()) {
2516             hardwareBuffer = nativeGetHardwareBuffer(mNativePtr);
2517             mHardwareBuffer = new WeakReference<HardwareBuffer>(hardwareBuffer);
2518         }
2519         return hardwareBuffer;
2520     }
2521 
2522     //////////// native methods
2523 
nativeCreate(int[] colors, int offset, int stride, int width, int height, int nativeConfig, boolean mutable, long nativeColorSpace)2524     private static native Bitmap nativeCreate(int[] colors, int offset,
2525                                               int stride, int width, int height,
2526                                               int nativeConfig, boolean mutable,
2527                                               long nativeColorSpace);
nativeCopy(long nativeSrcBitmap, int nativeConfig, boolean isMutable)2528     private static native Bitmap nativeCopy(long nativeSrcBitmap, int nativeConfig,
2529                                             boolean isMutable);
nativeCopyAshmem(long nativeSrcBitmap)2530     private static native Bitmap nativeCopyAshmem(long nativeSrcBitmap);
nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig)2531     private static native Bitmap nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig);
nativeGetAshmemFD(long nativeBitmap)2532     private static native int nativeGetAshmemFD(long nativeBitmap);
nativeGetNativeFinalizer()2533     private static native long nativeGetNativeFinalizer();
nativeRecycle(long nativeBitmap)2534     private static native void nativeRecycle(long nativeBitmap);
2535     @UnsupportedAppUsage
nativeReconfigure(long nativeBitmap, int width, int height, int config, boolean isPremultiplied)2536     private static native void nativeReconfigure(long nativeBitmap, int width, int height,
2537                                                  int config, boolean isPremultiplied);
2538 
nativeCompress(long nativeBitmap, int format, int quality, OutputStream stream, byte[] tempStorage)2539     private static native boolean nativeCompress(long nativeBitmap, int format,
2540                                             int quality, OutputStream stream,
2541                                             byte[] tempStorage);
nativeErase(long nativeBitmap, int color)2542     private static native void nativeErase(long nativeBitmap, int color);
nativeErase(long nativeBitmap, long colorSpacePtr, long color)2543     private static native void nativeErase(long nativeBitmap, long colorSpacePtr, long color);
nativeRowBytes(long nativeBitmap)2544     private static native int nativeRowBytes(long nativeBitmap);
nativeConfig(long nativeBitmap)2545     private static native int nativeConfig(long nativeBitmap);
2546 
nativeGetPixel(long nativeBitmap, int x, int y)2547     private static native int nativeGetPixel(long nativeBitmap, int x, int y);
nativeGetColor(long nativeBitmap, int x, int y)2548     private static native long nativeGetColor(long nativeBitmap, int x, int y);
nativeGetPixels(long nativeBitmap, int[] pixels, int offset, int stride, int x, int y, int width, int height)2549     private static native void nativeGetPixels(long nativeBitmap, int[] pixels,
2550                                                int offset, int stride, int x, int y,
2551                                                int width, int height);
2552 
nativeSetPixel(long nativeBitmap, int x, int y, int color)2553     private static native void nativeSetPixel(long nativeBitmap, int x, int y, int color);
nativeSetPixels(long nativeBitmap, int[] colors, int offset, int stride, int x, int y, int width, int height)2554     private static native void nativeSetPixels(long nativeBitmap, int[] colors,
2555                                                int offset, int stride, int x, int y,
2556                                                int width, int height);
nativeCopyPixelsToBuffer(long nativeBitmap, Buffer dst)2557     private static native void nativeCopyPixelsToBuffer(long nativeBitmap,
2558                                                         Buffer dst);
nativeCopyPixelsFromBuffer(long nativeBitmap, Buffer src)2559     private static native void nativeCopyPixelsFromBuffer(long nativeBitmap, Buffer src);
nativeGenerationId(long nativeBitmap)2560     private static native int nativeGenerationId(long nativeBitmap);
2561 
nativeCreateFromParcel(Parcel p)2562     private static native Bitmap nativeCreateFromParcel(Parcel p);
2563     // returns true on success
nativeWriteToParcel(long nativeBitmap, int density, Parcel p)2564     private static native boolean nativeWriteToParcel(long nativeBitmap,
2565                                                       int density,
2566                                                       Parcel p);
2567     // returns a new bitmap built from the native bitmap's alpha, and the paint
nativeExtractAlpha(long nativeBitmap, long nativePaint, int[] offsetXY)2568     private static native Bitmap nativeExtractAlpha(long nativeBitmap,
2569                                                     long nativePaint,
2570                                                     int[] offsetXY);
2571 
nativeHasAlpha(long nativeBitmap)2572     private static native boolean nativeHasAlpha(long nativeBitmap);
nativeIsPremultiplied(long nativeBitmap)2573     private static native boolean nativeIsPremultiplied(long nativeBitmap);
nativeSetPremultiplied(long nativeBitmap, boolean isPremul)2574     private static native void nativeSetPremultiplied(long nativeBitmap,
2575                                                       boolean isPremul);
nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha, boolean requestPremul)2576     private static native void nativeSetHasAlpha(long nativeBitmap,
2577                                                  boolean hasAlpha,
2578                                                  boolean requestPremul);
nativeHasMipMap(long nativeBitmap)2579     private static native boolean nativeHasMipMap(long nativeBitmap);
nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap)2580     private static native void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap);
nativeSameAs(long nativeBitmap0, long nativeBitmap1)2581     private static native boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1);
nativePrepareToDraw(long nativeBitmap)2582     private static native void nativePrepareToDraw(long nativeBitmap);
nativeGetAllocationByteCount(long nativeBitmap)2583     private static native int nativeGetAllocationByteCount(long nativeBitmap);
nativeCopyPreserveInternalConfig(long nativeBitmap)2584     private static native Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap);
nativeWrapHardwareBufferBitmap(HardwareBuffer buffer, long nativeColorSpace)2585     private static native Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer,
2586                                                                 long nativeColorSpace);
nativeGetHardwareBuffer(long nativeBitmap)2587     private static native HardwareBuffer nativeGetHardwareBuffer(long nativeBitmap);
nativeComputeColorSpace(long nativePtr)2588     private static native ColorSpace nativeComputeColorSpace(long nativePtr);
nativeSetColorSpace(long nativePtr, long nativeColorSpace)2589     private static native void nativeSetColorSpace(long nativePtr, long nativeColorSpace);
nativeIsSRGB(long nativePtr)2590     private static native boolean nativeIsSRGB(long nativePtr);
nativeIsSRGBLinear(long nativePtr)2591     private static native boolean nativeIsSRGBLinear(long nativePtr);
2592 
nativeSetImmutable(long nativePtr)2593     private static native void nativeSetImmutable(long nativePtr);
2594 
nativeExtractGainmap(long nativePtr)2595     private static native Gainmap nativeExtractGainmap(long nativePtr);
nativeSetGainmap(long bitmapPtr, long gainmapPtr)2596     private static native void nativeSetGainmap(long bitmapPtr, long gainmapPtr);
2597 
2598     // ---------------- @CriticalNative -------------------
2599 
2600     @CriticalNative
nativeIsImmutable(long nativePtr)2601     private static native boolean nativeIsImmutable(long nativePtr);
2602 
2603     @CriticalNative
nativeIsBackedByAshmem(long nativePtr)2604     private static native boolean nativeIsBackedByAshmem(long nativePtr);
2605 
2606     @CriticalNative
nativeHasGainmap(long nativePtr)2607     private static native boolean nativeHasGainmap(long nativePtr);
2608 }
2609