1 /*
2  * Copyright (C) 2021 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 com.example.testapp
18 
19 import android.graphics.Bitmap
20 import android.graphics.Canvas
21 import android.renderscript.Element
22 import android.renderscript.RenderScript
23 import android.renderscript.toolkit.Range2d
24 import android.renderscript.toolkit.Rgba3dArray
25 import android.renderscript.toolkit.YuvFormat
26 import java.nio.ByteBuffer
27 import java.util.Random
28 import kotlin.math.floor
29 import kotlin.math.max
30 import kotlin.math.min
31 
32 /**
33  * A vector of 4 integers.
34  */
35 class Int4(
36     var x: Int = 0,
37     var y: Int = 0,
38     var z: Int = 0,
39     var w: Int = 0
40 ) {
plusnull41     operator fun plus(other: Int4) = Int4(x + other.x, y + other.y, z + other.z, w + other.w)
42     operator fun plus(n: Int) = Int4(x + n, y + n, z + n, w + n)
43 
44     operator fun minus(other: Int4) = Int4(x - other.x, y - other.y, z - other.z, w - other.w)
45     operator fun minus(n: Int) = Int4(x - n, y - n, z - n, w - n)
46 
47     operator fun times(other: Int4) = Int4(x * other.x, y * other.y, z * other.z, w * other.w)
48     operator fun times(n: Int) = Int4(x * n, y * n, z * n, w * n)
49 
50     fun toFloat4() = Float4(x.toFloat(), y.toFloat(), z.toFloat(), w.toFloat())
51 }
52 
53 fun min(a: Int4, b: Int4) = Int4(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z), min(a.w, b.w))
54 
55 /**
56  * A vector of 4 floats.
57  */
58 data class Float4(
59     var x: Float = 0f,
60     var y: Float = 0f,
61     var z: Float = 0f,
62     var w: Float = 0f
63 ) {
64     operator fun plus(other: Float4) = Float4(x + other.x, y + other.y, z + other.z, w + other.w)
65     operator fun plus(f: Float) = Float4(x + f, y + f, z + f, w + f)
66 
67     operator fun minus(other: Float4) = Float4(x - other.x, y - other.y, z - other.z, w - other.w)
68     operator fun minus(f: Float) = Float4(x - f, y - f, z - f, w - f)
69 
70     operator fun times(other: Float4) = Float4(x * other.x, y * other.y, z * other.z, w * other.w)
71     operator fun times(f: Float) = Float4(x * f, y * f, z * f, w * f)
72 
73     operator fun div(other: Float4) = Float4(x / other.x, y / other.y, z / other.z, w / other.w)
74     operator fun div(f: Float) = Float4(x / f, y / f, z / f, w / f)
75 
76     fun intFloor() = Int4(floor(x).toInt(), floor(y).toInt(), floor(z).toInt(), floor(w).toInt())
77 }
78 
79 /**
80  * Convert a UByteArray to a Float4 vector
81  */
82 @ExperimentalUnsignedTypes
toFloat4null83 fun UByteArray.toFloat4(): Float4 {
84     require(size == 4)
85     return Float4(this[0].toFloat(), this[1].toFloat(), this[2].toFloat(), this[3].toFloat())
86 }
87 
88 /**
89  * Convert a ByteArray to a Float4 vector
90  */
91 @ExperimentalUnsignedTypes
toFloat4null92 fun ByteArray.toFloat4(): Float4 {
93     require(size == 4)
94     return Float4(
95         this[0].toUByte().toFloat(),
96         this[1].toUByte().toFloat(),
97         this[2].toUByte().toFloat(),
98         this[3].toUByte().toFloat()
99     )
100 }
101 
102 data class Dimension(val sizeX: Int, val sizeY: Int, val sizeZ: Int)
103 
104 /**
105  * An RGBA value represented by 4 Int.
106  *
107  * Note that the arithmetical operations consider a 0..255 value the equivalent of 0f..1f.
108  * After adding or subtracting, the value is clamped. After multiplying, the value is rescaled to
109  * stay in the 0..255 range. This is useful for the Blend operation.
110  */
111 @ExperimentalUnsignedTypes
112 data class Rgba(
113     var r: Int = 0,
114     var g: Int = 0,
115     var b: Int = 0,
116     var a: Int = 0
117 ) {
plusnull118     operator fun plus(other: Rgba) =
119         Rgba(r + other.r, g + other.g, b + other.b, a + other.a).clampToUByteRange()
120 
121     operator fun minus(other: Rgba) =
122         Rgba(r - other.r, g - other.g, b - other.b, a - other.a).clampToUByteRange()
123 
124     operator fun times(other: Rgba) = Rgba(r * other.r, g * other.g, b * other.b, a * other.a) shr 8
125     operator fun times(scalar: Int) = Rgba(r * scalar, g * scalar, b * scalar, a * scalar) shr 8
126 
127     infix fun xor(other: Rgba) = Rgba(r xor other.r, g xor other.g, b xor other.b, a xor other.a)
128 
129     infix fun shr(other: Int) = Rgba(r shr other, g shr other, b shr other, a shr other)
130 
131     private fun clampToUByteRange() = Rgba(
132         r.clampToUByteRange(),
133         g.clampToUByteRange(),
134         b.clampToUByteRange(),
135         a.clampToUByteRange()
136     )
137 }
138 
139 /**
140  * A 2D array of UByte vectors, stored in row-major format.
141  *
142  * Arrays of vectorSize == 3 are padded to 4.
143  */
144 @ExperimentalUnsignedTypes
145 class Vector2dArray(
146     val values: UByteArray,
147     val vectorSize: Int,
148     val sizeX: Int,
149     val sizeY: Int
150 ) {
151     /**
152      * If true, index access that would try to get a value that's out of bounds will simply
153      * return the border value instead. E.g. for [3, -3] would return the value for [3, 0],
154      * assuming that the sizeX > 3.
155      */
156     var clipReadToRange: Boolean = false
157 
158     operator fun get(x: Int, y: Int): UByteArray {
159         var fixedX = x
160         var fixedY = y
161         if (clipReadToRange) {
162             fixedX = min(max(x, 0), sizeX - 1)
163             fixedY = min(max(y, 0), sizeY - 1)
164         } else {
165             require(x in 0 until sizeX && y in 0 until sizeY) { "Out of bounds" }
166         }
167         val start = indexOfVector(fixedX, fixedY)
168         return UByteArray(paddedSize(vectorSize)) { values[start + it] }
169     }
170 
171     operator fun set(x: Int, y: Int, value: UByteArray) {
172         require(value.size == paddedSize(vectorSize)) { "Not the expected vector size" }
173         require(x in 0 until sizeX && y in 0 until sizeY) { "Out of bounds" }
174         val start = indexOfVector(x, y)
175         for (i in value.indices) {
176             values[start + i] = value[i]
177         }
178     }
179 
180     private fun indexOfVector(x: Int, y: Int) = ((y * sizeX) + x) * paddedSize(vectorSize)
181 
182     fun createSameSized() = Vector2dArray(UByteArray(values.size), vectorSize, sizeX, sizeY)
183 
184     fun forEach(restriction: Range2d?, work: (Int, Int) -> (Unit)) {
185         forEachCell(sizeX, sizeY, restriction, work)
186     }
187 }
188 
189 /**
190  * A 2D array of float vectors, stored in row-major format.
191  *
192  * Arrays of vectorSize == 3 are padded to 4.
193  */
194 class FloatVector2dArray(
195     val values: FloatArray,
196     val vectorSize: Int,
197     val sizeX: Int,
198     val sizeY: Int
199 ) {
200     /**
201      * If true, index access that would try to get a value that's out of bounds will simply
202      * return the border value instead. E.g. for [3, -3] would return the value for [3, 0],
203      * assuming that the sizeX > 3.
204      */
205     var clipAccessToRange: Boolean = false
206 
getnull207     operator fun get(x: Int, y: Int): FloatArray {
208         var fixedX = x
209         var fixedY = y
210         if (clipAccessToRange) {
211             fixedX = min(max(x, 0), sizeX - 1)
212             fixedY = min(max(y, 0), sizeY - 1)
213         } else {
214             require(x in 0 until sizeX && y in 0 until sizeY) { "Out of bounds" }
215         }
216         val start = indexOfVector(fixedX, fixedY)
217         return FloatArray(vectorSize) { values[start + it] }
218     }
219 
setnull220     operator fun set(x: Int, y: Int, value: FloatArray) {
221         require(x in 0 until sizeX && y in 0 until sizeY) { "Out of bounds" }
222         val start = indexOfVector(x, y)
223         for (i in value.indices) {
224             values[start + i] = value[i]
225         }
226     }
227 
indexOfVectornull228     private fun indexOfVector(x: Int, y: Int) = ((y * sizeX) + x) * paddedSize(vectorSize)
229 
230     fun createSameSized() = FloatVector2dArray(FloatArray(values.size), vectorSize, sizeX, sizeY)
231 
232     fun forEach(restriction: Range2d?, work: (Int, Int) -> (Unit)) {
233         forEachCell(sizeX, sizeY, restriction, work)
234     }
235 }
236 
237 /**
238  * A 2D array of RGBA data.
239  */
240 @ExperimentalUnsignedTypes
241 class Rgba2dArray(
242     private val values: ByteArray,
243     val sizeX: Int,
244     val sizeY: Int
245 ) {
getnull246     operator fun get(x: Int, y: Int): Rgba {
247         val i = indexOfVector(x, y)
248         return Rgba(
249             values[i].toUByte().toInt(),
250             values[i + 1].toUByte().toInt(),
251             values[i + 2].toUByte().toInt(),
252             values[i + 3].toUByte().toInt()
253         )
254     }
255 
setnull256     operator fun set(x: Int, y: Int, value: Rgba) {
257         // Verify that x, y, z, w are in the 0..255 range
258         require(value.r in 0..255)
259         require(value.g in 0..255)
260         require(value.b in 0..255)
261         require(value.a in 0..255)
262         val i = indexOfVector(x, y)
263         values[i] = value.r.toUByte().toByte()
264         values[i + 1] = value.g.toUByte().toByte()
265         values[i + 2] = value.b.toUByte().toByte()
266         values[i + 3] = value.a.toUByte().toByte()
267     }
268 
indexOfVectornull269     private fun indexOfVector(x: Int, y: Int) = ((y * sizeX) + x) * 4
270 
271     fun forEachCell(restriction: Range2d?, work: (Int, Int) -> (Unit)) =
272         forEachCell(sizeX, sizeY, restriction, work)
273 }
274 
275 /**
276  * Return a value that's between start and end, with the fraction indicating how far along.
277  */
278 fun mix(start: Float, end: Float, fraction: Float) = start + (end - start) * fraction
279 
280 fun mix(a: Float4, b: Float4, fraction: Float) = Float4(
281     mix(a.x, b.x, fraction),
282     mix(a.y, b.y, fraction),
283     mix(a.z, b.z, fraction),
284     mix(a.w, b.w, fraction)
285 )
286 
287 /**
288  * For vectors of size 3, the original RenderScript has them occupy the same space as a size 4.
289  * While RenderScript had a method to avoid this padding, it did not apply to Intrinsics.
290  *
291  * To preserve compatibility, the Toolkit doing the same.
292  */
293 fun paddedSize(vectorSize: Int) = if (vectorSize == 3) 4 else vectorSize
294 
295 /**
296  * Create a ByteArray of the specified size filled with random data.
297  */
298 fun randomByteArray(seed: Long, sizeX: Int, sizeY: Int, elementSize: Int): ByteArray {
299     val r = Random(seed)
300     return ByteArray(sizeX * sizeY * elementSize) { (r.nextInt(255) - 128).toByte() }
301 }
302 
303 /**
304  * Create a FloatArray of the specified size filled with random data.
305  *
306  * By default, the random data is between 0f and 1f. The factor can be used to scale that.
307  */
randomFloatArraynull308 fun randomFloatArray(
309     seed: Long,
310     sizeX: Int,
311     sizeY: Int,
312     elementSize: Int,
313     factor: Float = 1f
314 ): FloatArray {
315     val r = Random(seed)
316     return FloatArray(sizeX * sizeY * elementSize) { r.nextFloat() * factor }
317 }
318 
319 /**
320  * Create a cube of the specified size filled with random data.
321  */
randomCubenull322 fun randomCube(seed: Long, cubeSize: Dimension): ByteArray {
323     val r = Random(seed)
324     return ByteArray(cubeSize.sizeX * cubeSize.sizeY * cubeSize.sizeZ * 4) {
325         (r.nextInt(255) - 128).toByte()
326     }
327 }
328 
329 /**
330  * Create the identity cube, i.e. one that if used in Lut3d, the output is the same as the input
331  */
332 @ExperimentalUnsignedTypes
identityCubenull333 fun identityCube(cubeSize: Dimension): ByteArray {
334     val data = ByteArray(cubeSize.sizeX * cubeSize.sizeY * cubeSize.sizeZ * 4)
335     val cube = Rgba3dArray(data, cubeSize.sizeX, cubeSize.sizeY, cubeSize.sizeZ)
336     for (z in 0 until cubeSize.sizeZ) {
337         for (y in 0 until cubeSize.sizeY) {
338             for (x in 0 until cubeSize.sizeX) {
339                 cube[x, y, z] =
340                     byteArrayOf(
341                         (x * 255 / (cubeSize.sizeX - 1)).toByte(),
342                         (y * 255 / (cubeSize.sizeY - 1)).toByte(),
343                         (z * 255 / (cubeSize.sizeZ - 1)).toByte(),
344                         (255).toByte()
345                     )
346             }
347         }
348     }
349     return data
350 }
351 
randomYuvArraynull352 fun randomYuvArray(seed: Long, sizeX: Int, sizeY: Int, format: YuvFormat): ByteArray {
353     // YUV formats are not well defined for odd dimensions
354     require(sizeX % 2 == 0 && sizeY % 2 == 0)
355     val halfSizeX = sizeX / 2
356     val halfSizeY = sizeY / 2
357     var totalSize = 0
358     when (format) {
359         YuvFormat.YV12 -> {
360             val strideX = roundUpTo16(sizeX)
361             totalSize = strideX * sizeY + roundUpTo16(strideX / 2) * halfSizeY * 2
362         }
363         YuvFormat.NV21 -> totalSize = sizeX * sizeY + halfSizeX * halfSizeY * 2
364         else -> require(false) { "Unknown YUV format $format" }
365     }
366 
367     return randomByteArray(seed, totalSize, 1, 1)
368 }
369 
370 /**
371  * Converts a float to a byte, clamping to make it fit the limited range.
372  */
373 @ExperimentalUnsignedTypes
Floatnull374 fun Float.clampToUByte(): UByte = min(255, max(0, (this + 0.5f).toInt())).toUByte()
375 
376 /**
377  * Converts a FloatArray to UByteArray, clamping.
378  */
379 @ExperimentalUnsignedTypes
380 fun FloatArray.clampToUByte() = UByteArray(size) { this[it].clampToUByte() }
381 
382 /**
383  * Limits an Int to what can fit in a UByte.
384  */
clampToUByteRangenull385 fun Int.clampToUByteRange(): Int = min(255, max(0, this))
386 
387 /**
388  * Converts an Int to a UByte, clamping.
389  */
390 @ExperimentalUnsignedTypes
391 fun Int.clampToUByte(): UByte = this.clampToUByteRange().toUByte()
392 
393 /**
394  * Converts a float (0f .. 1f) to a byte (0 .. 255)
395  */
396 @ExperimentalUnsignedTypes
397 fun unitFloatClampedToUByte(num: Float): UByte = (num * 255f).clampToUByte()
398 
399 /**
400  * Convert a byte (0 .. 255) to a float (0f .. 1f)
401  */
402 @ExperimentalUnsignedTypes
403 fun byteToUnitFloat(num: UByte) = num.toFloat() * 0.003921569f
404 
405 @ExperimentalUnsignedTypes
406 fun UByteArray.toFloatArray() = FloatArray(size) { this[it].toFloat() }
407 
408 /**
409  * For each cell that's in the 2D array defined by sizeX and sizeY, and clipped down by the
410  * restriction, invoke the work function.
411  */
forEachCellnull412 fun forEachCell(sizeX: Int, sizeY: Int, restriction: Range2d?, work: (Int, Int) -> (Unit)) {
413     val startX = restriction?.startX ?: 0
414     val startY = restriction?.startY ?: 0
415     val endX = restriction?.endX ?: sizeX
416     val endY = restriction?.endY ?: sizeY
417     for (y in startY until endY) {
418         for (x in startX until endX) {
419             work(x, y)
420         }
421     }
422 }
423 
<lambda>null424 operator fun FloatArray.times(other: FloatArray) = FloatArray(size) { this[it] * other[it] }
<lambda>null425 operator fun FloatArray.times(other: Float) = FloatArray(size) { this[it] * other }
<lambda>null426 operator fun FloatArray.plus(other: FloatArray) = FloatArray(size) { this[it] + other[it] }
<lambda>null427 operator fun FloatArray.minus(other: FloatArray) = FloatArray(size) { this[it] - other[it] }
428 
renderScriptVectorElementForU8null429 fun renderScriptVectorElementForU8(rs: RenderScript?, vectorSize: Int): Element {
430     when (vectorSize) {
431         1 -> return Element.U8(rs)
432         2 -> return Element.U8_2(rs)
433         3 -> return Element.U8_3(rs)
434         4 -> return Element.U8_4(rs)
435     }
436     throw java.lang.IllegalArgumentException("RenderScriptToolkit tests. Only vectors of size 1-4 are supported. $vectorSize provided.")
437 }
438 
renderScriptVectorElementForI32null439 fun renderScriptVectorElementForI32(rs: RenderScript?, vectorSize: Int): Element {
440     when (vectorSize) {
441         1 -> return Element.I32(rs)
442         2 -> return Element.I32_2(rs)
443         3 -> return Element.I32_3(rs)
444         4 -> return Element.I32_4(rs)
445     }
446     throw java.lang.IllegalArgumentException("RenderScriptToolkit tests. Only vectors of size 1-4 are supported. $vectorSize provided.")
447 }
448 
449 /* When we'll handle floats
renderScriptVectorElementForF32null450 fun renderScriptVectorElementForF32(rs: RenderScript?, vectorSize: Int): Element {
451     when (vectorSize) {
452         1 -> return Element.F32(rs)
453         2 -> return Element.F32_2(rs)
454         3 -> return Element.F32_3(rs)
455         4 -> return Element.F32_4(rs)
456     }
457     throw java.lang.IllegalArgumentException("RenderScriptToolkit tests. Only vectors of size 1-4 are supported. $vectorSize provided.")
458 }*/
459 
460 fun renderScriptElementForBitmap(context: RenderScript, bitmap: Bitmap): Element {
461     return when (val config = bitmap.config) {
462         Bitmap.Config.ALPHA_8 -> Element.A_8(context)
463         Bitmap.Config.ARGB_8888 -> Element.RGBA_8888(context)
464         else -> throw IllegalArgumentException("RenderScript Toolkit can't support bitmaps with config $config.")
465     }
466 }
467 
getBitmapBytesnull468 fun getBitmapBytes(bitmap: Bitmap): ByteArray {
469     val buffer: ByteBuffer = ByteBuffer.allocate(bitmap.byteCount)
470     bitmap.copyPixelsToBuffer(buffer)
471     return buffer.array()
472 }
473 
vectorSizeOfBitmapnull474 fun vectorSizeOfBitmap(bitmap: Bitmap): Int {
475     return when (val config = bitmap.config) {
476         Bitmap.Config.ALPHA_8 -> 1
477         Bitmap.Config.ARGB_8888 -> 4
478         else -> throw IllegalArgumentException("RenderScript Toolkit can't support bitmaps with config $config.")
479     }
480 }
481 
duplicateBitmapnull482 fun duplicateBitmap(original: Bitmap): Bitmap {
483     val copy = Bitmap.createBitmap(original.width, original.height, original.config)
484     val canvas = Canvas(copy)
485     canvas.drawBitmap(original, 0f, 0f, null)
486     return copy
487 }
488 
489 @ExperimentalUnsignedTypes
logArraynull490 fun logArray(prefix: String, array: ByteArray, number: Int = 20) {
491     val values = array.joinToString(limit = number) { it.toUByte().toString() }
492     println("$prefix[${array.size}] $values}\n")
493 }
494 
logArraynull495 fun logArray(prefix: String, array: IntArray, number: Int = 20) {
496     val values = array.joinToString(limit = number)
497     println("$prefix[${array.size}] $values}\n")
498 }
499 
logArraynull500 fun logArray(prefix: String, array: FloatArray?, number: Int = 20) {
501     val values = array?.joinToString(limit = number) { "%.2f".format(it) } ?: "(null)"
502     println("$prefix[${array?.size}] $values}\n")
503 }
504 
roundUpTo16null505 fun roundUpTo16(value: Int): Int {
506     require(value >= 0)
507     return (value + 15) and 15.inv()
508 }
509