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