1 /*
2  * Copyright 2019 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 @file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
18 
19 package androidx.compose.ui.unit
20 
21 import androidx.compose.runtime.Immutable
22 import androidx.compose.runtime.Stable
23 import androidx.compose.ui.util.fastCoerceAtLeast
24 import androidx.compose.ui.util.fastCoerceIn
25 import kotlin.jvm.JvmInline
26 import kotlin.math.min
27 
28 /**
29  * Immutable constraints for measuring layouts, used by [layouts][androidx.compose.ui.layout.Layout]
30  * or [layout modifiers][androidx.compose.ui.layout.LayoutModifier] to measure their layout
31  * children. The parent chooses the [Constraints] defining a range, in pixels, within which the
32  * measured layout should choose a size:
33  * - `minWidth` <= `chosenWidth` <= `maxWidth`
34  * - `minHeight` <= `chosenHeight` <= `maxHeight`
35  *
36  * For more details about how layout measurement works, see
37  * [androidx.compose.ui.layout.MeasurePolicy] or
38  * [androidx.compose.ui.layout.LayoutModifier.measure].
39  *
40  * A set of [Constraints] can have infinite maxWidth and/or maxHeight. This is a trick often used by
41  * parents to ask their children for their preferred size: unbounded constraints force children
42  * whose default behavior is to fill the available space (always size to maxWidth/maxHeight) to have
43  * an opinion about their preferred size. Most commonly, when measured with unbounded [Constraints],
44  * these children will fallback to size themselves to wrap their content, instead of expanding to
45  * fill the available space (this is not always true as it depends on the child layout model, but is
46  * a common behavior for core layout components).
47  *
48  * [Constraints] uses a [Long] to represent four values, [minWidth], [minHeight], [maxWidth], and
49  * [maxHeight]. The range of the values varies to allow for at most 256K in one dimension. There are
50  * four possible maximum ranges, 13 bits/18 bits, and 15 bits/16 bits for either width or height,
51  * depending on the needs. For example, a width could range up to 18 bits and the height up to 13
52  * bits. Alternatively, the width could range up to 16 bits and the height up to 15 bits. The height
53  * and width requirements can be reversed, with a height of up to 18 bits and width of 13 bits or
54  * height of 16 bits and width of 15 bits. Any constraints exceeding this range will fail.
55  */
56 @Immutable
57 @JvmInline
58 value class Constraints(@PublishedApi internal val value: Long) {
59     /**
60      * Indicates how the bits are assigned. One of:
61      * - MinFocusWidth
62      * - MaxFocusWidth
63      * - MinFocusHeight
64      * - MaxFocusHeight
65      */
66     private inline val focusIndex
67         get() = (value and FocusMask).toInt()
68 
69     /** The minimum width that the measurement can take, in pixels. */
70     val minWidth: Int
71         get() {
72             val mask = widthMask(indexToBitOffset(focusIndex))
73             return ((value shr 2).toInt() and mask)
74         }
75 
76     /**
77      * The maximum width that the measurement can take, in pixels. This will either be a positive
78      * value greater than or equal to [minWidth] or [Constraints.Infinity].
79      */
80     val maxWidth: Int
81         get() {
82             val mask = widthMask(indexToBitOffset(focusIndex))
83             val width = ((value shr 33).toInt() and mask)
84             return if (width == 0) Infinity else width - 1
85         }
86 
87     /** The minimum height that the measurement can take, in pixels. */
88     val minHeight: Int
89         get() {
90             val bitOffset = indexToBitOffset(focusIndex)
91             val mask = heightMask(bitOffset)
92             val offset = minHeightOffsets(bitOffset)
93             return (value shr offset).toInt() and mask
94         }
95 
96     /**
97      * The maximum height that the measurement can take, in pixels. This will either be a positive
98      * value greater than or equal to [minHeight] or [Constraints.Infinity].
99      */
100     val maxHeight: Int
101         get() {
102             val bitOffset = indexToBitOffset(focusIndex)
103             val mask = heightMask(bitOffset)
104             val offset = minHeightOffsets(bitOffset) + 31
105             val height = (value shr offset).toInt() and mask
106             return if (height == 0) Infinity else height - 1
107         }
108 
109     /**
110      * `false` when [maxWidth] is [Infinity] and `true` if [maxWidth] is a non-[Infinity] value.
111      *
112      * @see hasBoundedHeight
113      */
114     val hasBoundedWidth: Boolean
115         get() {
116             val mask = widthMask(indexToBitOffset(focusIndex))
117             return ((value shr 33).toInt() and mask) != 0
118         }
119 
120     /**
121      * `false` when [maxHeight] is [Infinity] and `true` if [maxHeight] is a non-[Infinity] value.
122      *
123      * @see hasBoundedWidth
124      */
125     val hasBoundedHeight: Boolean
126         get() {
127             val bitOffset = indexToBitOffset(focusIndex)
128             val mask = heightMask(bitOffset)
129             val offset = minHeightOffsets(bitOffset) + 31
130             return ((value shr offset).toInt() and mask) != 0
131         }
132 
133     /** Whether there is exactly one width value that satisfies the constraints. */
134     @Stable
135     val hasFixedWidth: Boolean
136         get() {
137             val mask = widthMask(indexToBitOffset(focusIndex))
138             val minWidth = ((value shr 2).toInt() and mask)
139             val maxWidth =
<lambda>null140                 ((value shr 33).toInt() and mask).let { if (it == 0) Infinity else it - 1 }
141             return minWidth == maxWidth
142         }
143 
144     /** Whether there is exactly one height value that satisfies the constraints. */
145     @Stable
146     val hasFixedHeight: Boolean
147         get() {
148             val bitOffset = indexToBitOffset(focusIndex)
149             val mask = heightMask(bitOffset)
150             val offset = minHeightOffsets(bitOffset)
151             val minHeight = (value shr offset).toInt() and mask
152             val maxHeight =
<lambda>null153                 ((value shr (offset + 31)).toInt() and mask).let {
154                     if (it == 0) Infinity else it - 1
155                 }
156             return minHeight == maxHeight
157         }
158 
159     /**
160      * Whether the area of a component respecting these constraints will definitely be 0. This is
161      * true when at least one of maxWidth and maxHeight are 0.
162      */
163     @Stable
164     val isZero: Boolean
165         get() {
166             val bitOffset = indexToBitOffset(focusIndex)
167             val maxWidth = ((value shr 33).toInt() and widthMask(bitOffset)) - 1
168             val offset = minHeightOffsets(bitOffset) + 31
169             val maxHeight = ((value shr offset).toInt() and heightMask(bitOffset)) - 1
170             return (maxWidth == 0) or (maxHeight == 0)
171         }
172 
173     /**
174      * Copies the existing [Constraints], replacing some of [minWidth], [minHeight], [maxWidth], or
175      * [maxHeight] as desired. [minWidth] and [minHeight] must be positive and [maxWidth] and
176      * [maxHeight] must be greater than or equal to [minWidth] and [minHeight], respectively, or
177      * [Infinity].
178      */
copynull179     fun copy(
180         minWidth: Int = this.minWidth,
181         maxWidth: Int = this.maxWidth,
182         minHeight: Int = this.minHeight,
183         maxHeight: Int = this.maxHeight
184     ): Constraints {
185         requirePrecondition(
186             maxWidth >= minWidth && maxHeight >= minHeight && minWidth >= 0 && minHeight >= 0
187         ) {
188             """
189                 maxWidth must be >= than minWidth,
190                 maxHeight must be >= than minHeight,
191                 minWidth and minHeight must be >= 0
192             """
193                 .trimIndent()
194         }
195         return createConstraints(minWidth, maxWidth, minHeight, maxHeight)
196     }
197 
198     /**
199      * Copies the existing [Constraints], setting [minWidth] and [minHeight] to 0, and preserving
200      * [maxWidth] and [maxHeight] as-is.
201      */
copyMaxDimensionsnull202     inline fun copyMaxDimensions() = Constraints(value and MaxDimensionsAndFocusMask)
203 
204     override fun toString(): String {
205         val maxWidth = maxWidth
206         val maxWidthStr = if (maxWidth == Infinity) "Infinity" else maxWidth.toString()
207         val maxHeight = maxHeight
208         val maxHeightStr = if (maxHeight == Infinity) "Infinity" else maxHeight.toString()
209         return "Constraints(minWidth = $minWidth, maxWidth = $maxWidthStr, " +
210             "minHeight = $minHeight, maxHeight = $maxHeightStr)"
211     }
212 
213     companion object {
214         /**
215          * A value that [maxWidth] or [maxHeight] will be set to when the constraint should be
216          * considered infinite. [hasBoundedWidth] or [hasBoundedHeight] will be `false` when
217          * [maxWidth] or [maxHeight] is [Infinity], respectively.
218          */
219         const val Infinity = Int.MAX_VALUE
220 
221         /** Creates constraints for fixed size in both dimensions. */
222         @Stable
fixednull223         fun fixed(width: Int, height: Int): Constraints {
224             requirePrecondition((width >= 0) and (height >= 0)) { "width and height must be >= 0" }
225             return createConstraints(width, width, height, height)
226         }
227 
228         /** Creates constraints for fixed width and unspecified height. */
229         @Stable
fixedWidthnull230         fun fixedWidth(width: Int): Constraints {
231             requirePrecondition(width >= 0) { "width must be >= 0" }
232             return createConstraints(
233                 minWidth = width,
234                 maxWidth = width,
235                 minHeight = 0,
236                 maxHeight = Infinity
237             )
238         }
239 
240         /** Creates constraints for fixed height and unspecified width. */
241         @Stable
fixedHeightnull242         fun fixedHeight(height: Int): Constraints {
243             requirePrecondition(height >= 0) { "height must be >= 0" }
244             return createConstraints(
245                 minWidth = 0,
246                 maxWidth = Infinity,
247                 minHeight = height,
248                 maxHeight = height
249             )
250         }
251 
252         // This should be removed before the next release
253         @Deprecated(
254             "Replace with fitPrioritizingWidth",
255             replaceWith =
256                 ReplaceWith(
257                     "Constraints.fitPrioritizingWidth(minWidth, maxWidth, minHeight, maxHeight)"
258                 )
259         )
260         @Stable
restrictConstraintsnull261         fun restrictConstraints(
262             minWidth: Int,
263             maxWidth: Int,
264             minHeight: Int,
265             maxHeight: Int,
266             prioritizeWidth: Boolean = true
267         ): Constraints {
268             return if (prioritizeWidth) {
269                 fitPrioritizingWidth(minWidth, maxWidth, minHeight, maxHeight)
270             } else {
271                 fitPrioritizingHeight(minWidth, maxWidth, minHeight, maxHeight)
272             }
273         }
274 
275         /**
276          * Returns [Constraints] that match as close as possible to the values passed. If the
277          * dimensions are outside of those that can be represented, the constraints are limited to
278          * those that can be represented.
279          *
280          * [Constraints] is a `value class` based on a [Long] and 4 integers must be limited to fit
281          * within its size. The larger dimension has up to 18 bits (262,143) and the smaller as few
282          * as 13 bits (8191). The width is granted as much space as it needs or caps the size to 18
283          * bits. The height is given the remaining space.
284          *
285          * This can be useful when layout constraints are possible to be extremely large, but not
286          * everything is possible to display on the device. For example a text layout where an
287          * entire chapter of a book is measured in one Layout and it isn't possible to break up the
288          * content to show in a `LazyColumn`.
289          */
290         @Stable
fitPrioritizingWidthnull291         fun fitPrioritizingWidth(
292             minWidth: Int,
293             maxWidth: Int,
294             minHeight: Int,
295             maxHeight: Int,
296         ): Constraints {
297             val minW = min(minWidth, MaxFocusMask - 1)
298             val maxW =
299                 if (maxWidth == Infinity) {
300                     Infinity
301                 } else {
302                     min(maxWidth, MaxFocusMask - 1)
303                 }
304             val consumed = if (maxW == Infinity) minW else maxW
305             val maxAllowed = maxAllowedForSize(consumed)
306             val maxH = if (maxHeight == Infinity) Infinity else min(maxAllowed, maxHeight)
307             val minH = min(maxAllowed, minHeight)
308             return Constraints(minW, maxW, minH, maxH)
309         }
310 
311         /**
312          * Returns [Constraints] that match as close as possible to the values passed. If the
313          * dimensions are outside of those that can be represented, the constraints are limited to
314          * those that can be represented.
315          *
316          * [Constraints] is a `value class` based on a [Long] and 4 integers must be limited to fit
317          * within its size. The larger dimension has up to 18 bits (262,143) and the smaller as few
318          * as 13 bits (8191). The height is granted as much space as it needs or caps the size to 18
319          * bits. The width is given the remaining space.
320          *
321          * This can be useful when layout constraints are possible to be extremely large, but not
322          * everything is possible to display on the device. For example a text layout where an
323          * entire chapter of a book is measured in one Layout and it isn't possible to break up the
324          * content to show in a `LazyColumn`.
325          */
326         @Stable
fitPrioritizingHeightnull327         fun fitPrioritizingHeight(
328             minWidth: Int,
329             maxWidth: Int,
330             minHeight: Int,
331             maxHeight: Int,
332         ): Constraints {
333             val minH = min(minHeight, MaxFocusMask - 1)
334             val maxH =
335                 if (maxHeight == Infinity) {
336                     Infinity
337                 } else {
338                     min(maxHeight, MaxFocusMask - 1)
339                 }
340             val consumed = if (maxH == Infinity) minH else maxH
341             val maxAllowed = maxAllowedForSize(consumed)
342             val maxW = if (maxWidth == Infinity) Infinity else min(maxAllowed, maxWidth)
343             val minW = min(maxAllowed, minWidth)
344             return Constraints(minW, maxW, minH, maxH)
345         }
346     }
347 }
348 
349 // Redefinition of Constraints.Infinity to bypass the companion object
350 private const val Infinity = Int.MAX_VALUE
351 
352 /**
353  * The mask to retrieve the focus:
354  * - MaxFocusHeight = 0x0. The bit distribution when the focus of the bits should be on the height,
355  *   and a a maximal number of bits assigned to the height. 13 bits assigned to width, 18 bits
356  *   assigned to height.
357  * - MinFocusHeight = 0x1. The bit distribution when the focus of the bits should be on the height,
358  *   but only a minimal difference in focus. 15 bits assigned to width, 16 bits assigned to height.
359  * - MinFocusWidth = 0x2. The bit distribution when the focus of the bits should be on the width,
360  *   but only a minimal difference in focus. 16 bits assigned to width, 15 bits assigned to height.
361  * - MaxFocusWidth = 0x3 .The bit distribution when the focus of the bits should be on the width,
362  *   and a maximal number of bits assigned to the width. 18 bits assigned to width, 13 bits assigned
363  *   to height.
364  */
365 private const val FocusMask = 0x3L
366 
367 /** The number of bits used for the focused dimension when there is minimal focus. */
368 private const val MinFocusBits = 16
369 private const val MaxAllowedForMinFocusBits = (1 shl (31 - MinFocusBits)) - 2
370 
371 /** The mask to use for the focused dimension when there is minimal focus. */
372 private const val MinFocusMask = 0xFFFF // 64K (16 bits)
373 
374 /** The number of bits used for the non-focused dimension when there is minimal focus. */
375 private const val MinNonFocusBits = 15
376 private const val MaxAllowedForMinNonFocusBits = (1 shl (31 - MinNonFocusBits)) - 2
377 
378 /** The mask to use for the non-focused dimension when there is minimal focus. */
379 private const val MinNonFocusMask = 0x7FFF // 32K (15 bits)
380 
381 /** The number of bits to use for the focused dimension when there is maximal focus. */
382 private const val MaxFocusBits = 18
383 private const val MaxAllowedForMaxFocusBits = (1 shl (31 - MaxFocusBits)) - 2
384 
385 /** The mask to use for the focused dimension when there is maximal focus. */
386 private const val MaxFocusMask = 0x3FFFF // 256K-1 (18 bits)
387 
388 /** The number of bits to use for the non-focused dimension when there is maximal focus. */
389 private const val MaxNonFocusBits = 13
390 private const val MaxAllowedForMaxNonFocusBits = (1 shl (31 - MaxNonFocusBits)) - 2
391 
392 /** The mask to use for the non-focused dimension when there is maximal focus. */
393 private const val MaxNonFocusMask = 0x1FFF // 8K (13 bits)
394 
395 // 0xFFFFFFFE_00000003UL.toLong(), written as a signed value to declare it const
396 @PublishedApi internal const val MaxDimensionsAndFocusMask = -0x00000001_FFFFFFFDL
397 
398 // Wrap those throws in functions to avoid inlining the string building at the call sites
399 // Keep internal for codegen
throwInvalidConstraintExceptionnull400 internal fun throwInvalidConstraintException(widthVal: Int, heightVal: Int) {
401     throw IllegalArgumentException(
402         "Can't represent a width of $widthVal and height of $heightVal in Constraints"
403     )
404 }
405 
406 // Keep internal for codegen
throwInvalidConstraintsSizeExceptionnull407 internal fun throwInvalidConstraintsSizeException(size: Int): Nothing {
408     throw IllegalArgumentException("Can't represent a size of $size in Constraints")
409 }
410 
411 /** Creates a [Constraints], only checking that the values fit in the packed Long. */
createConstraintsnull412 internal fun createConstraints(
413     minWidth: Int,
414     maxWidth: Int,
415     minHeight: Int,
416     maxHeight: Int
417 ): Constraints {
418     val heightVal = if (maxHeight == Infinity) minHeight else maxHeight
419     val heightBits = bitsNeedForSizeUnchecked(heightVal)
420 
421     val widthVal = if (maxWidth == Infinity) minWidth else maxWidth
422     val widthBits = bitsNeedForSizeUnchecked(widthVal)
423 
424     if (widthBits + heightBits > 31) {
425         throwInvalidConstraintException(widthVal, heightVal)
426     }
427 
428     // Same as if (maxWidth == Infinity) 0 else maxWidth + 1 but branchless
429     // in DEX and saves 2 instructions on aarch64. Relies on integer overflow
430     // since Infinity == Int.MAX_VALUE
431     var maxWidthValue = maxWidth + 1
432     maxWidthValue = maxWidthValue and (maxWidthValue shr 31).inv()
433 
434     var maxHeightValue = maxHeight + 1
435     maxHeightValue = maxHeightValue and (maxHeightValue shr 31).inv()
436 
437     // widthBits can be one of 13, 15, 16, or 18
438     // by subtracting 13 we obtain a bit offset as expected by bitOffsetToIndex()
439     val bitOffset = widthBits - 13
440     val focus = bitOffsetToIndex(bitOffset)
441 
442     val minHeightOffset = minHeightOffsets(bitOffset)
443     val maxHeightOffset = minHeightOffset + 31
444 
445     val value =
446         focus.toLong() or
447             (minWidth.toLong() shl 2) or
448             (maxWidthValue.toLong() shl 33) or
449             (minHeight.toLong() shl minHeightOffset) or
450             (maxHeightValue.toLong() shl maxHeightOffset)
451     return Constraints(value)
452 }
453 
bitsNeedForSizeUncheckednull454 internal fun bitsNeedForSizeUnchecked(size: Int): Int {
455     return when {
456         size < MaxNonFocusMask -> MaxNonFocusBits
457         size < MinNonFocusMask -> MinNonFocusBits
458         size < MinFocusMask -> MinFocusBits
459         size < MaxFocusMask -> MaxFocusBits
460         else -> 255
461     }
462 }
463 
maxAllowedForSizenull464 private inline fun maxAllowedForSize(size: Int): Int {
465     return when {
466         size < MaxNonFocusMask -> MaxAllowedForMaxNonFocusBits
467         size < MinNonFocusMask -> MaxAllowedForMinNonFocusBits
468         size < MinFocusMask -> MaxAllowedForMinFocusBits
469         size < MaxFocusMask -> MaxAllowedForMaxFocusBits
470         else -> throwInvalidConstraintsSizeException(size)
471     }
472 }
473 
474 /**
475  * Create a [Constraints]. [minWidth] and [minHeight] must be positive and [maxWidth] and
476  * [maxHeight] must be greater than or equal to [minWidth] and [minHeight], respectively, or
477  * [Infinity][Constraints.Infinity].
478  */
479 @Stable
Constraintsnull480 fun Constraints(
481     minWidth: Int = 0,
482     maxWidth: Int = Infinity,
483     minHeight: Int = 0,
484     maxHeight: Int = Infinity
485 ): Constraints {
486     requirePrecondition(
487         (maxWidth >= minWidth) and (maxHeight >= minHeight) and (minWidth >= 0) and (minHeight >= 0)
488     ) {
489         """
490             maxWidth must be >= than minWidth,
491             maxHeight must be >= than minHeight,
492             minWidth and minHeight must be >= 0
493         """
494             .trimIndent()
495     }
496     return createConstraints(minWidth, maxWidth, minHeight, maxHeight)
497 }
498 
499 /**
500  * Takes [otherConstraints] and returns the result of coercing them in the current constraints. Note
501  * this means that any size satisfying the resulting constraints will satisfy the current
502  * constraints, but they might not satisfy the [otherConstraints] when the two set of constraints
503  * are disjoint. Examples (showing only width, height works the same): (minWidth=2,
504  * maxWidth=10).constrain(minWidth=7, maxWidth=12) -> (minWidth = 7, maxWidth = 10) (minWidth=2,
505  * maxWidth=10).constrain(minWidth=11, maxWidth=12) -> (minWidth=10, maxWidth=10) (minWidth=2,
506  * maxWidth=10).constrain(minWidth=5, maxWidth=7) -> (minWidth=5, maxWidth=7)
507  */
Constraintsnull508 fun Constraints.constrain(otherConstraints: Constraints): Constraints {
509     val minWidth = minWidth
510     val maxWidth = maxWidth
511     val minHeight = minHeight
512     val maxHeight = maxHeight
513     return Constraints(
514         minWidth = otherConstraints.minWidth.fastCoerceIn(minWidth, maxWidth),
515         maxWidth = otherConstraints.maxWidth.fastCoerceIn(minWidth, maxWidth),
516         minHeight = otherConstraints.minHeight.fastCoerceIn(minHeight, maxHeight),
517         maxHeight = otherConstraints.maxHeight.fastCoerceIn(minHeight, maxHeight)
518     )
519 }
520 
521 /** Takes a size and returns the closest size to it that satisfies the constraints. */
522 @Stable
Constraintsnull523 fun Constraints.constrain(size: IntSize) =
524     IntSize(
525         width = size.width.fastCoerceIn(minWidth, maxWidth),
526         height = size.height.fastCoerceIn(minHeight, maxHeight)
527     )
528 
529 /** Takes a width and returns the closest size to it that satisfies the constraints. */
530 @Stable fun Constraints.constrainWidth(width: Int) = width.fastCoerceIn(minWidth, maxWidth)
531 
532 /** Takes a height and returns the closest size to it that satisfies the constraints. */
533 @Stable fun Constraints.constrainHeight(height: Int) = height.fastCoerceIn(minHeight, maxHeight)
534 
535 /** Takes a size and returns whether it satisfies the current constraints. */
536 @Stable
537 fun Constraints.isSatisfiedBy(size: IntSize): Boolean {
538     return size.width in minWidth..maxWidth && size.height in minHeight..maxHeight
539 }
540 
541 /** Returns the Constraints obtained by offsetting the current instance with the given values. */
542 @Stable
Constraintsnull543 fun Constraints.offset(horizontal: Int = 0, vertical: Int = 0) =
544     Constraints(
545         (minWidth + horizontal).fastCoerceAtLeast(0),
546         addMaxWithMinimum(maxWidth, horizontal),
547         (minHeight + vertical).fastCoerceAtLeast(0),
548         addMaxWithMinimum(maxHeight, vertical)
549     )
550 
551 private inline fun addMaxWithMinimum(max: Int, value: Int): Int {
552     return if (max == Infinity) {
553         max
554     } else {
555         (max + value).fastCoerceAtLeast(0)
556     }
557 }
558 
559 // NOTE: The functions below are not on the companion object to avoid unnecessary
560 //       static resolutions. Even inlined, calling these functions requires a
561 //       static inline access in case initialization is required. Making them top
562 //       level functions addresses this issues.
563 
564 // The following describes the computations performed by the 4 functions below.
565 // Each function is a mapping from an index to a value.
566 //
567 // index = 0 -> MaxFocusHeight = 15
568 // index = 1 -> MinFocusHeight = 17
569 // index = 2 -> MinFocusWidth  = 18
570 // index = 3 -> MaxFocusWidth  = 20
571 //
572 // MinHeightOffsets = 2 + 13 + (index and 0x1 shl 1) + ((index and 0x2 shr 1) * 3)
573 //
574 // The sub-expression `(index and 0x1 shl 1) + ((index and 0x2 shr 1) * 3)` applies
575 // the following mapping:
576 //
577 // 0 -> 0
578 // 1 -> 2
579 // 2 -> 3
580 // 3 -> 5
581 //
582 // From this mapping we can build all the other mappings:
583 //
584 // index = 0 -> MaxNonFocusMask = 0x1fff  (13 bits)
585 // index = 1 -> MinNonFocusMask = 0x7fff  (15 bits)
586 // index = 2 -> MinFocusMask    = 0xffff  (16 bits)
587 // index = 3 -> MaxFocusMask    = 0x3ffff (18 bits)
588 //
589 // WidthMask = (1 shl (13 + (index and 0x1 shl 1) + ((index and 0x2 shr 1) * 3))) - 1
590 //
591 // Here we used the pattern `(1 << n) - 1` to set all first n bits to 1.
592 //
593 // index = 0 -> MaxFocusMask    = 0x3ffff
594 // index = 1 -> MinFocusMask    = 0xffff
595 // index = 2 -> MinNonFocusMask = 0x7fff
596 // index = 3 -> MaxNonFocusMask = 0x1fff
597 //
598 // HeightMask = (1 shl (18 - (index and 0x1 shl 1) - ((index and 0x2 shr 1) * 3))) - 1
599 //
600 // To optimize computations, the common part that follows is factored into the
601 // `indexToBitOffset` function:
602 //
603 // (index and 0x1 shl 1) + ((index and 0x2 shr 1) * 3)
604 //
605 // We are therefore left with:
606 //
607 // MinHeightOffsets = 15 + indexToBitOffset(index)
608 // WidthMask = (1 shl (13 + indexToBitOffset(index))) - 1
609 // HeightMask = (1 shl (18 - indexToBitOffset(index))) - 1
610 
611 /**
612  * Maps an index (MaxFocusHeight, MinFocusHeight, MinFocusWidth, MaxFocusWidth) to a "bit offset":
613  * 0, 2, 3 or 5. That bit offset is used by [minHeightOffsets], [widthMask], and [heightMask] to
614  * compute other values without the need of lookup tables. For instance, [minHeightOffsets] returns
615  * `2 + 13 + bitOffset`.
616  */
indexToBitOffsetnull617 private inline fun indexToBitOffset(index: Int) =
618     (index and 0x1 shl 1) + ((index and 0x2 shr 1) * 3)
619 
620 /** Maps a bit offset (0, 2, 3, or 5) to an index. It's the inverse of indexToBitOffset() */
621 private inline fun bitOffsetToIndex(bits: Int) = (bits shr 1) + (bits and 0x1)
622 
623 /**
624  * Minimum Height shift offsets into Long value, indexed by FocusMask Max offsets are these + 31
625  * Width offsets are always either 2 (min) or 33 (max)
626  */
627 private inline fun minHeightOffsets(bitOffset: Int) = 15 + bitOffset
628 
629 /** The mask to use for both minimum and maximum width. */
630 private inline fun widthMask(bitOffset: Int) = (1 shl (13 + bitOffset)) - 1
631 
632 /** The mask to use for both minimum and maximum height. */
633 private inline fun heightMask(bitOffset: Int) = (1 shl (18 - bitOffset)) - 1
634