1 /*
2  * Copyright 2018 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 androidx.compose.ui.graphics
18 
19 import androidx.compose.runtime.Immutable
20 import androidx.compose.ui.graphics.colorspace.ColorSpace
21 import androidx.compose.ui.graphics.colorspace.ColorSpaces
22 import androidx.compose.ui.graphics.internal.JvmDefaultWithCompatibility
23 
24 /**
25  * Graphics object that represents a 2 dimensional array of pixel information represented as ARGB
26  * values
27  */
28 @JvmDefaultWithCompatibility
29 interface ImageBitmap {
30 
31     /** The number of image pixels along the ImageBitmap's horizontal axis. */
32     val width: Int
33 
34     /** The number of image pixels along the ImageBitmap's vertical axis. */
35     val height: Int
36 
37     /** ColorSpace the Image renders in */
38     val colorSpace: ColorSpace
39 
40     /** Determines whether or not the ImageBitmap contains an alpha channel */
41     val hasAlpha: Boolean
42 
43     /**
44      * Returns the current configuration of this Image, either:
45      *
46      * @see ImageBitmapConfig.Argb8888
47      * @see ImageBitmapConfig.Rgb565
48      * @see ImageBitmapConfig.Alpha8
49      * @see ImageBitmapConfig.Gpu
50      */
51     val config: ImageBitmapConfig
52 
53     /**
54      * Copies the pixel data within the ImageBitmap into the given array. Each value is represented
55      * as ARGB values packed into an Int. The stride parameter allows the caller to allow for gaps
56      * in the returned pixels array between rows. For normal packed, results, the stride value is
57      * equivalent to the width of the [ImageBitmap]. The returned colors are non-premultiplied ARGB
58      * values in the [ColorSpaces.Srgb] color space.
59      *
60      * Note this method can block so it is recommended to not invoke this method in performance
61      * critical code paths
62      *
63      * @sample androidx.compose.ui.graphics.samples.ImageBitmapReadPixelsSample
64      * @param buffer The array to store the [ImageBitmap]'s colors. By default this allocates an
65      *   [IntArray] large enough to store all the pixel information. Consumers of this API are
66      *   advised to use the smallest [IntArray] necessary to extract relevant pixel information,
67      *   that is the 2 dimensional area of the section of the [ImageBitmap] to be queried.
68      * @param startX The x-coordinate of the first pixel to read from the [ImageBitmap]
69      * @param startY The y-coordinate of the first pixel to read from the [ImageBitmap]
70      * @param width The number of pixels to read from each row
71      * @param height The number of rows to read
72      * @param bufferOffset The first index to write into the buffer array, this defaults to 0
73      * @param stride The number of entries in [buffer] to skip between rows (must be >= [width]
74      */
readPixelsnull75     fun readPixels(
76         buffer: IntArray,
77         startX: Int = 0,
78         startY: Int = 0,
79         width: Int = this.width,
80         height: Int = this.height,
81         bufferOffset: Int = 0,
82         stride: Int = width
83     )
84 
85     /**
86      * Builds caches associated with the ImageBitmap that are used for drawing it. This method can
87      * be used as a signal to upload textures to the GPU to eventually be rendered
88      */
89     fun prepareToDraw()
90 
91     /** Provide an empty companion object to hang platform-specific companion extensions onto. */
92     companion object {}
93 }
94 
95 /**
96  * Convenience method to extract pixel information from the given ImageBitmap into a [PixelMap] that
97  * supports for querying pixel information based on
98  *
99  * Note this method can block so it is recommended to not invoke this method in performance critical
100  * code paths
101  *
102  * @sample androidx.compose.ui.graphics.samples.ImageBitmapToPixelMapSample
103  * @param startX The x-coordinate of the first pixel to read from the [ImageBitmap]
104  * @param startY The y-coordinate of the first pixel to read from the [ImageBitmap]
105  * @param width The number of pixels to read from each row
106  * @param height The number of rows to read
107  * @param buffer The array to store the [ImageBitmap]'s colors. By default this allocates an
108  *   [IntArray] large enough to store all the pixel information. Consumers of this API are advised
109  *   to use the smallest [IntArray] necessary to extract relevant pixel information
110  * @param bufferOffset The first index to write into the buffer array, this defaults to 0
111  * @param stride The number of entries in [buffer] to skip between rows (must be >= [width]
112  * @see ImageBitmap.readPixels
113  */
toPixelMapnull114 fun ImageBitmap.toPixelMap(
115     startX: Int = 0,
116     startY: Int = 0,
117     width: Int = this.width,
118     height: Int = this.height,
119     buffer: IntArray = IntArray(width * height),
120     bufferOffset: Int = 0,
121     stride: Int = width
122 ): PixelMap {
123     readPixels(buffer, startX, startY, width, height, bufferOffset, stride)
124     return PixelMap(buffer, width, height, bufferOffset, stride)
125 }
126 
127 /**
128  * Possible ImageBitmap configurations. An ImageBitmap configuration describes how pixels are
129  * stored. This affects the quality (color depth) as well as the ability to display
130  * transparent/translucent colors.
131  */
132 @Immutable
133 @kotlin.jvm.JvmInline
134 value class ImageBitmapConfig internal constructor(val value: Int) {
135     companion object {
136         /**
137          * Each pixel is stored on 4 bytes. Each channel (RGB and alpha for translucency) is stored
138          * with 8 bits of precision (256 possible values.)
139          *
140          * This configuration is very flexible and offers the best quality. It should be used
141          * whenever possible.
142          *
143          *      Use this formula to pack into 32 bits:
144          * ```
145          * val color =
146          *    ((A and 0xff) shl 24) or
147          *    ((B and 0xff) shl 16) or
148          *    ((G and 0xff) shl 8) or
149          *    (R and 0xff)
150          * ```
151          */
152         val Argb8888 = ImageBitmapConfig(0)
153 
154         /**
155          * Each pixel is stored as a single translucency (alpha) channel. This is very useful to
156          * efficiently store masks for instance. No color information is stored. With this
157          * configuration, each pixel requires 1 byte of memory.
158          */
159         val Alpha8 = ImageBitmapConfig(1)
160 
161         /**
162          * Each pixel is stored on 2 bytes and only the RGB channels are encoded: red is stored with
163          * 5 bits of precision (32 possible values), green is stored with 6 bits of precision (64
164          * possible values) and blue is stored with 5 bits of precision.
165          *
166          * This configuration can produce slight visual artifacts depending on the configuration of
167          * the source. For instance, without dithering, the result might show a greenish tint. To
168          * get better results dithering should be applied.
169          *
170          * This configuration may be useful when using opaque bitmaps that do not require high color
171          * fidelity.
172          *
173          *      Use this formula to pack into 16 bits:
174          * ```
175          *  val color =
176          *      ((R and 0x1f) shl 11) or
177          *      ((G and 0x3f) shl 5) or
178          *      (B and 0x1f)
179          * ```
180          */
181         val Rgb565 = ImageBitmapConfig(2)
182 
183         /**
184          * Each pixel is stored on 8 bytes. Each channel (RGB and alpha for translucency) is stored
185          * as a half-precision floating point value.
186          *
187          * This configuration is particularly suited for wide-gamut and HDR content.
188          *
189          *      Use this formula to pack into 64 bits:
190          * ```
191          *    val color =
192          *      ((A and 0xffff) shl 48) or
193          *      ((B and 0xffff) shl 32) or
194          *      ((G and 0xffff) shl 16) or
195          *      (R and 0xffff)
196          * ```
197          */
198         val F16 = ImageBitmapConfig(3)
199 
200         /**
201          * Special configuration, when an ImageBitmap is stored only in graphic memory. ImageBitmaps
202          * in this configuration are always immutable.
203          *
204          * It is optimal for cases, when the only operation with the ImageBitmap is to draw it on a
205          * screen.
206          */
207         val Gpu = ImageBitmapConfig(4)
208     }
209 
toStringnull210     override fun toString() =
211         when (this) {
212             Argb8888 -> "Argb8888"
213             Alpha8 -> "Alpha8"
214             Rgb565 -> "Rgb565"
215             F16 -> "F16"
216             Gpu -> "Gpu"
217             else -> "Unknown"
218         }
219 }
220 
ActualImageBitmapnull221 internal expect fun ActualImageBitmap(
222     width: Int,
223     height: Int,
224     config: ImageBitmapConfig,
225     hasAlpha: Boolean,
226     colorSpace: ColorSpace
227 ): ImageBitmap
228 
229 fun ImageBitmap(
230     width: Int,
231     height: Int,
232     config: ImageBitmapConfig = ImageBitmapConfig.Argb8888,
233     hasAlpha: Boolean = true,
234     colorSpace: ColorSpace = ColorSpaces.Srgb
235 ): ImageBitmap = ActualImageBitmap(width, height, config, hasAlpha, colorSpace)
236 
237 /**
238  * Decodes a byte array of a Bitmap to an ImageBitmap.
239  *
240  * @return The converted ImageBitmap.
241  */
242 fun ByteArray.decodeToImageBitmap(): ImageBitmap = createImageBitmap(this)
243 
244 internal expect fun createImageBitmap(bytes: ByteArray): ImageBitmap
245