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