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