aboutsummaryrefslogtreecommitdiff
path: root/test-app/src/main/java/com/google/android/renderscript_test
diff options
context:
space:
mode:
Diffstat (limited to 'test-app/src/main/java/com/google/android/renderscript_test')
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/AllTests.kt1244
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/BufferUtils.kt508
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/IntrinsicBlend.kt188
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/IntrinsicBlur.kt108
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/IntrinsicColorMatrix.kt162
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/IntrinsicConvolve.kt140
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/IntrinsicHistogram.kt196
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/IntrinsicLut.kt118
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/IntrinsicLut3d.kt123
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/IntrinsicResize.kt119
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/IntrinsicYuvToRgb.kt70
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/MainActivity.kt39
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/ReferenceBlend.kt82
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/ReferenceBlur.kt131
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/ReferenceColorMatrix.kt57
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/ReferenceConvolve.kt68
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/ReferenceHistogram.kt98
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/ReferenceLut.kt48
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/ReferenceLut3d.kt74
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/ReferenceResize.kt157
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/ReferenceYuvToRgb.kt112
-rw-r--r--test-app/src/main/java/com/google/android/renderscript_test/TimingTracker.kt64
22 files changed, 3906 insertions, 0 deletions
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/AllTests.kt b/test-app/src/main/java/com/google/android/renderscript_test/AllTests.kt
new file mode 100644
index 0000000..934456f
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/AllTests.kt
@@ -0,0 +1,1244 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.renderscript.RenderScript
+import com.google.android.renderscript.BlendingMode
+import com.google.android.renderscript.LookupTable
+import com.google.android.renderscript.Range2d
+import com.google.android.renderscript.Rgba3dArray
+import com.google.android.renderscript.Toolkit
+import com.google.android.renderscript.YuvFormat
+import kotlin.math.abs
+import kotlin.math.min
+
+data class TestLayout(
+ val sizeX: Int,
+ val sizeY: Int,
+ val restriction: Range2d?
+)
+
+// List of dimensions (sizeX, sizeY) to try when generating random data.
+val commonLayoutsToTry = listOf(
+ // Small layouts to start with
+ TestLayout(3, 4, null),
+ TestLayout(3, 4, Range2d(0, 1, 0, 3)),
+ TestLayout(3, 4, Range2d(2, 3, 1, 4)),
+ // The size of most of the original RenderScript Intrinsic tests
+ TestLayout(160, 100, null),
+ /* Other tests, if you're patient:
+ TestLayout(10, 14, null),
+ TestLayout(10, 14, Range2d(2, 3, 8, 14)),
+ TestLayout(125, 227, Range2d(50, 125, 100, 227)),
+ TestLayout(800, 600, null),
+ // Weirdly shaped ones
+ TestLayout(1, 1, null), // A single item
+ estLayout(16000, 1, null), // A single item
+ TestLayout(1, 16000, null), // One large row
+ // A very large test
+ TestLayout(1024, 2048, null),
+ */
+)
+
+enum class Intrinsic {
+ BLEND,
+ BLUR,
+ COLOR_MATRIX,
+ CONVOLVE,
+ HISTOGRAM,
+ LUT,
+ LUT3D,
+ RESIZE,
+ YUV_TO_RGB,
+}
+
+
+class Tester(context: Context, private val validate: Boolean) {
+ private val renderscriptContext = RenderScript.create(context)
+ private val testImage1 = BitmapFactory.decodeResource(context.resources, R.drawable.img800x450a)
+ private val testImage2 = BitmapFactory.decodeResource(context.resources, R.drawable.img800x450b)
+
+ init {
+ validateTestImage(testImage1)
+ validateTestImage(testImage2)
+ }
+
+ /**
+ * Verify that the test images are in format that works for our tests.
+ */
+ private fun validateTestImage(bitmap: Bitmap) {
+ require(bitmap.config == Bitmap.Config.ARGB_8888)
+ require(bitmap.rowBytes == bitmap.width * 4) {
+ "Can't handle bitmaps that have extra padding. " +
+ "${bitmap.rowBytes} != ${bitmap.width} * 4." }
+ require(bitmap.byteCount == bitmap.rowBytes * bitmap.height)
+ }
+
+ fun destroy() {
+ renderscriptContext.destroy()
+ }
+
+ // Test one single intrinsic. Return true on success and false on failure.
+ @ExperimentalUnsignedTypes
+ fun testOne(intrinsic: Intrinsic, timer: TimingTracker) =
+ when(intrinsic) {
+ Intrinsic.BLEND -> ::testBlend
+ Intrinsic.BLUR -> ::testBlur
+ Intrinsic.COLOR_MATRIX -> ::testColorMatrix
+ Intrinsic.CONVOLVE -> ::testConvolve
+ Intrinsic.HISTOGRAM -> ::testHistogram
+ Intrinsic.LUT -> ::testLut
+ Intrinsic.LUT3D -> ::testLut3d
+ Intrinsic.RESIZE -> ::testResize
+ Intrinsic.YUV_TO_RGB -> ::testYuvToRgb
+ }.let { test -> test(timer) }
+
+ @ExperimentalUnsignedTypes
+ private fun testBlend(timer: TimingTracker): Boolean {
+ return BlendingMode.values().all { mode ->
+ testOneBitmapBlend(timer, testImage1, testImage2, mode, null) and
+ testOneBitmapBlend(
+ timer, testImage1, testImage2, mode,
+ Range2d(6, 23, 2, 4)
+ ) and
+ commonLayoutsToTry.all { (sizeX, sizeY, restriction) ->
+ testOneRandomBlend(timer, sizeX, sizeY, mode, restriction)
+ }
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneRandomBlend(
+ timer: TimingTracker,
+ sizeX: Int,
+ sizeY: Int,
+ mode: BlendingMode,
+ restriction: Range2d?
+ ): Boolean {
+ val sourceArray = randomByteArray(0x50521f0, sizeX, sizeY, 4)
+ val destArray = randomByteArray(0x2932147, sizeX, sizeY, 4)
+ // Make clones because these will be modified by the blend.
+ val intrinsicDestArray = destArray.clone()
+ val referenceDestArray = destArray.clone()
+ val toolkitDestArray = destArray.clone()
+
+ timer.measure("IntrinsicBlend") {
+ intrinsicBlend(
+ renderscriptContext, mode, sourceArray, intrinsicDestArray, sizeX, sizeY,
+ restriction
+ )
+ }
+ timer.measure("ToolkitBlend") {
+ Toolkit.blend(mode, sourceArray, toolkitDestArray, sizeX, sizeY, restriction)
+ }
+ if (!validate) return true
+
+ timer.measure("ReferenceBlend") {
+ referenceBlend(mode, sourceArray, referenceDestArray, sizeX, sizeY, restriction)
+ }
+
+ return validateSame(
+ "Blend_$mode", intrinsicDestArray, referenceDestArray, toolkitDestArray
+ ) {
+ println("blend $mode ($sizeX, $sizeY) $restriction")
+ logArray("Blend_$mode src", sourceArray, 48)
+ logArray("Blend_$mode dst", destArray, 48)
+ logArray("Blend_$mode reference out", referenceDestArray, 48)
+ logArray("Blend_$mode intrinsic out", intrinsicDestArray, 48)
+ logArray("Blend_$mode toolkit out", toolkitDestArray, 48)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneBitmapBlend(
+ timer: TimingTracker,
+ sourceBitmap: Bitmap,
+ destBitmap: Bitmap,
+ mode: BlendingMode,
+ restriction: Range2d?
+ ): Boolean {
+ // Make clones because these will be modified by the blend.
+ val intrinsicDestBitmap = duplicateBitmap(destBitmap)
+ val toolkitDestBitmap = duplicateBitmap(destBitmap)
+ val referenceDestBitmap = duplicateBitmap(destBitmap)
+
+ timer.measure("IntrinsicBlend") {
+ intrinsicBlend(
+ renderscriptContext, mode, sourceBitmap, intrinsicDestBitmap, restriction
+ )
+ }
+ timer.measure("ToolkitBlend") {
+ Toolkit.blend(mode, sourceBitmap, toolkitDestBitmap, restriction)
+ }
+ if (!validate) return true
+
+ val referenceDestArray = getBitmapBytes(referenceDestBitmap)
+ timer.measure("ReferenceBlend") {
+ referenceBlend(
+ mode, getBitmapBytes(sourceBitmap), referenceDestArray, sourceBitmap.width,
+ sourceBitmap.height, restriction
+ )
+ }
+
+ val intrinsicDestArray = getBitmapBytes(intrinsicDestBitmap)
+ val toolkitDestArray = getBitmapBytes(toolkitDestBitmap)
+ return validateSame(
+ "BlendBitmap_$mode", intrinsicDestArray, referenceDestArray, toolkitDestArray
+ ) {
+ println("BlendBitmap $mode $restriction")
+ //logArray("BlendBitmap_$mode src", sourceArray, 48)
+ //logArray("BlendBitmap_$mode dst", destArray, 48)
+ logArray("BlendBitmap_$mode reference out", referenceDestArray, 48)
+ logArray("BlendBitmap_$mode intrinsic out", intrinsicDestArray, 48)
+ logArray("BlendBitmap_$mode toolkit out", toolkitDestArray, 48)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testBlur(timer: TimingTracker): Boolean {
+ return arrayOf(1, 3, 8, 25).all { radius ->
+ testOneBitmapBlur(timer, testImage1, radius, null) and
+ testOneBitmapBlur(timer, testImage1, radius, Range2d(6, 23, 2, 4)) and
+ commonLayoutsToTry.all { (sizeX, sizeY, restriction) ->
+ arrayOf(1, 4).all { vectorSize ->
+ testOneRandomBlur(timer, vectorSize, sizeX, sizeY, radius, restriction)
+ }
+ }
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneRandomBlur(
+ timer: TimingTracker,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ radius: Int,
+ restriction: Range2d?
+ ): Boolean {
+ val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, vectorSize)
+ val intrinsicOutArray = timer.measure("IntrinsicBlur") {
+ intrinsicBlur(
+ renderscriptContext, inputArray, vectorSize, sizeX, sizeY, radius, restriction
+ )
+ }
+ val toolkitOutArray = timer.measure("ToolkitBlur") {
+ Toolkit.blur(inputArray, vectorSize, sizeX, sizeY, radius, restriction)
+ }
+ if (!validate) return true
+
+ val referenceOutArray = timer.measure("ReferenceBlur") {
+ referenceBlur(inputArray, vectorSize, sizeX, sizeY, radius, restriction)
+ }
+ return validateSame("blur", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
+ println("blur $vectorSize ($sizeX, $sizeY) radius = $radius $restriction")
+ logArray("blur input ", inputArray)
+ logArray("blur reference out", referenceOutArray)
+ logArray("blur intrinsic out", intrinsicOutArray)
+ logArray("blur toolkit out", toolkitOutArray)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneBitmapBlur(
+ timer: TimingTracker,
+ bitmap: Bitmap,
+ radius: Int,
+ restriction: Range2d?
+ ): Boolean {
+ val intrinsicOutArray = timer.measure("IntrinsicBlur") {
+ intrinsicBlur(renderscriptContext, bitmap, radius, restriction)
+ }
+
+ val toolkitOutBitmap = timer.measure("ToolkitBlur") {
+ Toolkit.blur(bitmap, radius, restriction)
+ }
+ if (!validate) return true
+
+ val referenceOutArray = timer.measure("ReferenceBlur") {
+ referenceBlur(
+ getBitmapBytes(bitmap),
+ vectorSizeOfBitmap(bitmap),
+ bitmap.width,
+ bitmap.height,
+ radius,
+ restriction
+ )
+ }
+
+ val toolkitOutArray = getBitmapBytes(toolkitOutBitmap)
+ return validateSame("blur", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
+ println("BlurBitmap ${bitmap.config} $radius $restriction")
+ logArray("blur reference out", referenceOutArray)
+ logArray("blur intrinsic out", intrinsicOutArray)
+ logArray("blur toolkit out", toolkitOutArray)
+ }
+ }
+
+ enum class ColorMatrixConversionType {
+ RGB_TO_YUV,
+ YUV_TO_RGB,
+ GREYSCALE,
+ RANDOM
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testColorMatrix(timer: TimingTracker): Boolean {
+ return ColorMatrixConversionType.values().all { conversion ->
+ testOneBitmapColorMatrix(timer, testImage1, conversion, null) and
+ testOneBitmapColorMatrix(
+ timer,
+ testImage1,
+ conversion,
+ Range2d(6, 23, 2, 4)
+ ) and
+ commonLayoutsToTry.all { (sizeX, sizeY, restriction) ->
+ (1..4).all { inputVectorSize ->
+ (1..4).all { outputVectorSize ->
+ testOneRandomColorMatrix(
+ timer,
+ inputVectorSize,
+ sizeX,
+ sizeY,
+ outputVectorSize,
+ conversion,
+ restriction
+ )
+ }
+ }
+ }
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneRandomColorMatrix(
+ timer: TimingTracker,
+ inputVectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ outputVectorSize: Int,
+ conversion: ColorMatrixConversionType,
+ restriction: Range2d?
+ ): Boolean {
+ val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, paddedSize(inputVectorSize))
+ val addVector = randomFloatArray(0x243238, 4, 1, 1, 0.3f)
+ val matrix = when (conversion) {
+ ColorMatrixConversionType.RGB_TO_YUV -> Toolkit.rgbToYuvMatrix
+ ColorMatrixConversionType.YUV_TO_RGB -> Toolkit.yuvToRgbMatrix
+ ColorMatrixConversionType.GREYSCALE -> Toolkit.greyScaleColorMatrix
+ ColorMatrixConversionType.RANDOM -> randomFloatArray(0x234348, 4, 4, 1)
+ }
+
+ val intrinsicOutArray = timer.measure("IntrinsicColorMatrix") {
+ intrinsicColorMatrix(
+ renderscriptContext,
+ conversion,
+ inputArray,
+ inputVectorSize,
+ sizeX,
+ sizeY,
+ outputVectorSize,
+ matrix,
+ addVector,
+ restriction
+ )
+ }
+ val toolkitOutArray = timer.measure("ToolkitColorMatrix") {
+ Toolkit.colorMatrix(
+ inputArray,
+ inputVectorSize,
+ sizeX,
+ sizeY,
+ outputVectorSize,
+ matrix,
+ addVector,
+ restriction
+ )
+ }
+ if (!validate) return true
+
+ val referenceOutArray = timer.measure("ReferenceColorMatrix") {
+ referenceColorMatrix(
+ inputArray, inputVectorSize, sizeX, sizeY, outputVectorSize, matrix, addVector,
+ restriction
+ )
+ }
+
+ return validateSame("colorMatrix", intrinsicOutArray, referenceOutArray, toolkitOutArray,
+ outputVectorSize == 3) {
+ println("colorMatrix ($sizeX, $sizeY) $inputVectorSize->$outputVectorSize $restriction")
+ logArray("colorMatrix matrix ", matrix, 16)
+ logArray("colorMatrix addVector", addVector, 4)
+ logArray("colorMatrix in ", inputArray)
+ logArray("colorMatrix reference out", referenceOutArray, 300)
+ logArray("colorMatrix intrinsic out", intrinsicOutArray, 300)
+ logArray("colorMatrix toolkit out", toolkitOutArray, 300)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneBitmapColorMatrix(
+ timer: TimingTracker,
+ bitmap: Bitmap,
+ conversion: ColorMatrixConversionType,
+ restriction: Range2d?
+ ): Boolean {
+ val addVector = randomFloatArray(0x243238, 4, 1, 1, 0.3f)
+ val matrix = when (conversion) {
+ ColorMatrixConversionType.RGB_TO_YUV -> Toolkit.rgbToYuvMatrix
+ ColorMatrixConversionType.YUV_TO_RGB -> Toolkit.yuvToRgbMatrix
+ ColorMatrixConversionType.GREYSCALE -> Toolkit.greyScaleColorMatrix
+ ColorMatrixConversionType.RANDOM -> randomFloatArray(0x234348, 4, 4, 1)
+ }
+
+ val intrinsicOutArray = timer.measure("IntrinsicColorMatrix") {
+ intrinsicColorMatrix(
+ renderscriptContext, conversion, bitmap, matrix, addVector, restriction
+ )
+ }
+ val toolkitOutBitmap = timer.measure("ToolkitColorMatrix") {
+ Toolkit.colorMatrix(bitmap, matrix, addVector, restriction)
+ }
+ if (!validate) return true
+
+ val referenceOutArray = timer.measure("ReferenceColorMatrix") {
+ referenceColorMatrix(
+ getBitmapBytes(bitmap), vectorSizeOfBitmap(bitmap), bitmap.width, bitmap.height,
+ vectorSizeOfBitmap(bitmap), matrix, addVector, restriction
+ )
+ }
+
+ val toolkitOutArray = getBitmapBytes(toolkitOutBitmap)
+ return validateSame("ColorMatrix", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
+ println("colorMatrixBitmap $restriction")
+ logArray("colorMatrixBitmap matrix ", matrix, 16)
+ logArray("colorMatrixBitmap addVector", addVector, 4)
+ logArray("colorMatrixBitmap reference out", referenceOutArray)
+ logArray("colorMatrixBitmap intrinsic out", intrinsicOutArray)
+ logArray("colorMatrixBitmap toolkit out", toolkitOutArray)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testConvolve(timer: TimingTracker): Boolean {
+ val coefficientsToTry = listOf(
+ randomFloatArray(0x2937021, 3, 3, 1, 0.1f),
+ randomFloatArray(0x2937021, 5, 5, 1, 0.05f)
+ )
+ return coefficientsToTry.all { coefficients ->
+ testOneBitmapConvolve(timer, testImage1, coefficients, null) and
+ testOneBitmapConvolve(timer, testImage1, coefficients, Range2d(6, 23, 2, 4)) and
+
+ commonLayoutsToTry.all { (sizeX, sizeY, restriction) ->
+ (1..4).all { vectorSize ->
+ testOneRandomConvolve(
+ timer,
+ vectorSize,
+ sizeX,
+ sizeY,
+ coefficients,
+ restriction
+ )
+ }
+ }
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneRandomConvolve(
+ timer: TimingTracker,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ coefficients: FloatArray,
+ restriction: Range2d?
+ ): Boolean {
+ val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, paddedSize(vectorSize))
+
+ val intrinsicOutArray = timer.measure("IntrinsicConvolve") {
+ intrinsicConvolve(
+ renderscriptContext, inputArray, vectorSize, sizeX, sizeY, coefficients, restriction
+ )
+ }
+ val toolkitOutArray = timer.measure("ToolkitConvolve") {
+ Toolkit.convolve(inputArray, vectorSize, sizeX, sizeY, coefficients, restriction)
+ }
+ if (!validate) return true
+
+ val referenceOutArray = timer.measure("ReferenceConvolve") {
+ referenceConvolve(inputArray, vectorSize, sizeX, sizeY, coefficients, restriction)
+ }
+
+ val task = if (coefficients.size == 9) "convolve3x3 $vectorSize" else "convolve5x5 $vectorSize"
+ return validateSame(task, intrinsicOutArray, referenceOutArray, toolkitOutArray) {
+ println("Convolve $vectorSize ($sizeX, $sizeY) $restriction")
+ logArray("Convolve coefficients", coefficients, 25)
+ logArray("Convolve in ", inputArray)
+ logArray("Convolve reference out", referenceOutArray)
+ logArray("Convolve intrinsic out", intrinsicOutArray)
+ logArray("Convolve toolkit out", toolkitOutArray)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneBitmapConvolve(
+ timer: TimingTracker,
+ bitmap: Bitmap,
+ coefficients: FloatArray,
+ restriction: Range2d?
+ ): Boolean {
+ val intrinsicOutArray = timer.measure("IntrinsicConvolve") {
+ intrinsicConvolve(renderscriptContext, bitmap, coefficients, restriction)
+ }
+ val toolkitOutBitmap = timer.measure("ToolkitConvolve") {
+ Toolkit.convolve(bitmap, coefficients, restriction)
+ }
+ if (!validate) return true
+
+ val referenceOutArray = timer.measure("ReferenceConvolve") {
+ referenceConvolve(
+ getBitmapBytes(bitmap), vectorSizeOfBitmap(bitmap), bitmap.width, bitmap.height,
+ coefficients, restriction
+ )
+ }
+
+ val task = if (coefficients.size == 9) "convolve3x3" else "convolve5x5"
+ val toolkitOutArray = getBitmapBytes(toolkitOutBitmap)
+ return validateSame(task, intrinsicOutArray, referenceOutArray, toolkitOutArray) {
+ println("ConvolveBitmap $restriction")
+ logArray("ConvolveBitmap coefficients", coefficients, 25)
+ //logArray("ConvolveBitmap in ", inputArray)
+ logArray("ConvolveBitmap reference out", referenceOutArray)
+ logArray("ConvolveBitmap intrinsic out", intrinsicOutArray)
+ logArray("ConvolveBitmap toolkit out", toolkitOutArray)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testHistogram(timer: TimingTracker): Boolean {
+ val coefficients = floatArrayOf(0.1f, 0.3f, 0.5f, 0.05f)
+ return testOneBitmapHistogram(timer, testImage1, null) and
+ testOneBitmapHistogram(timer, testImage1, Range2d(6, 23, 2, 4)) and
+ testOneBitmapHistogramDot(timer, testImage1, null, null) and
+ testOneBitmapHistogramDot(timer, testImage1, coefficients, null) and
+ testOneBitmapHistogramDot(timer, testImage1, coefficients, Range2d(6, 23, 2, 4)) and
+ commonLayoutsToTry.all { (sizeX, sizeY, restriction) ->
+ (1..4).all { vectorSize ->
+ testOneRandomHistogram(timer, vectorSize, sizeX, sizeY, restriction) &&
+ testOneRandomHistogramDot(
+ timer,
+ vectorSize,
+ sizeX,
+ sizeY,
+ null,
+ restriction
+ ) &&
+ testOneRandomHistogramDot(
+ timer,
+ vectorSize,
+ sizeX,
+ sizeY,
+ coefficients.sliceArray(0 until vectorSize),
+ restriction
+ )
+ }
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneRandomHistogram(
+ timer: TimingTracker,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ restriction: Range2d?
+ ): Boolean {
+ val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, paddedSize(vectorSize))
+
+ val intrinsicOutput = timer.measure("IntrinsicHistogram") {
+ intrinsicHistogram(
+ renderscriptContext, inputArray, vectorSize, sizeX, sizeY, restriction
+ )
+ }
+ val toolkitOutput = timer.measure("ToolkitHistogram") {
+ Toolkit.histogram(inputArray, vectorSize, sizeX, sizeY, restriction)
+ }
+ if (!validate) return true
+
+ val referenceOutput = timer.measure("ReferenceHistogram") {
+ referenceHistogram(
+ inputArray, vectorSize, sizeX, sizeY, restriction
+ )
+ }
+
+ return validateSame("histogram", intrinsicOutput, referenceOutput, toolkitOutput, 0) {
+ println("histogram $vectorSize ($sizeX, $sizeY) $restriction")
+ logArray("histogram in ", inputArray, 200)
+ logArray("histogram reference out", referenceOutput, 200)
+ logArray("histogram intrinsic out", intrinsicOutput, 200)
+ logArray("histogram toolkit out", toolkitOutput, 200)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneBitmapHistogram(
+ timer: TimingTracker,
+ bitmap: Bitmap,
+ restriction: Range2d?
+ ): Boolean {
+ val intrinsicOutput = timer.measure("IntrinsicHistogram") {
+ intrinsicHistogram(renderscriptContext, bitmap, restriction)
+ }
+ val toolkitOutput = timer.measure("ToolkitHistogram") {
+ Toolkit.histogram(bitmap, restriction)
+ }
+ if (!validate) return true
+
+ val referenceOutput = timer.measure("ReferenceHistogram") {
+ referenceHistogram(
+ getBitmapBytes(bitmap), vectorSizeOfBitmap(bitmap), bitmap.width, bitmap.height,
+ restriction
+ )
+ }
+
+ return validateSame("histogram", intrinsicOutput, referenceOutput, toolkitOutput, 0) {
+ println("HistogramBitmap $restriction")
+ logArray("HistogramBitmap reference out", referenceOutput)
+ logArray("HistogramBitmap intrinsic out", intrinsicOutput)
+ logArray("HistogramBitmap toolkit out", toolkitOutput)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneRandomHistogramDot(
+ timer: TimingTracker,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ coefficients: FloatArray?, restriction: Range2d?
+ ): Boolean {
+ val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, paddedSize(vectorSize))
+
+ val intrinsicOutArray = timer.measure("IntrinsicHistogramDot") {
+ intrinsicHistogramDot(
+ renderscriptContext, inputArray, vectorSize, sizeX, sizeY, coefficients, restriction
+ )
+ }
+ val toolkitOutArray = timer.measure("ToolkitHistogramDot") {
+ Toolkit.histogramDot(
+ inputArray, vectorSize, sizeX, sizeY, coefficients, restriction
+ )
+ }
+ if (!validate) return true
+
+ val referenceOutArray = timer.measure("ReferenceHistogramDot") {
+ referenceHistogramDot(inputArray, vectorSize, sizeX, sizeY, coefficients, restriction)
+ }
+
+ return validateSame("histogramDot", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
+ println("histogramDot $vectorSize ($sizeX, $sizeY) $restriction")
+ logArray("histogramDot coefficients ", coefficients)
+ logArray("histogramDot in ", inputArray)
+ logArray("histogramDot reference out", referenceOutArray, 256)
+ logArray("histogramDot intrinsic out", intrinsicOutArray, 256)
+ logArray("histogramDot toolkit out", toolkitOutArray, 256)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneBitmapHistogramDot(
+ timer: TimingTracker,
+ bitmap: Bitmap,
+ coefficients: FloatArray?,
+ restriction: Range2d?
+ ): Boolean {
+ val intrinsicOutArray = timer.measure("IntrinsicHistogramDot") {
+ intrinsicHistogramDot(renderscriptContext, bitmap, coefficients, restriction)
+ }
+ val toolkitOutArray = timer.measure("ToolkitHistogramDot") {
+ Toolkit.histogramDot(bitmap, coefficients, restriction)
+ }
+ if (!validate) return true
+
+ val referenceOutArray = timer.measure("ReferenceHistogramDot") {
+ referenceHistogramDot(
+ getBitmapBytes(bitmap),
+ vectorSizeOfBitmap(bitmap),
+ bitmap.width,
+ bitmap.height,
+ coefficients,
+ restriction
+ )
+ }
+
+ return validateSame(
+ "HistogramDotBitmap",
+ intrinsicOutArray,
+ referenceOutArray,
+ toolkitOutArray
+ ) {
+ println("HistogramDotBitmap $restriction")
+ logArray("HistogramDotBitmap coefficients ", coefficients)
+ //logArray("HistogramDotBitmap in ", inputArray)
+ logArray("HistogramDotBitmap reference out", referenceOutArray, 256)
+ logArray("HistogramDotBitmap intrinsic out", intrinsicOutArray, 256)
+ logArray("HistogramDotBitmap toolkit out", toolkitOutArray, 256)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testLut(timer: TimingTracker): Boolean {
+ return testOneBitmapLut(timer, testImage1, null) and
+ testOneBitmapLut(timer, testImage1, Range2d(6, 23, 2, 4)) and
+ commonLayoutsToTry.all { (sizeX, sizeY, restriction) ->
+ testOneRandomLut(timer, sizeX, sizeY, restriction)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneRandomLut(
+ timer: TimingTracker,
+ sizeX: Int,
+ sizeY: Int,
+ restriction: Range2d?
+ ): Boolean {
+ val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, 4)
+ val newRed = randomByteArray(0x32425, 256, 1, 1)
+ val newGreen = randomByteArray(0x1F3225, 256, 1, 1)
+ val newBlue = randomByteArray(0x32D4F27, 256, 1, 1)
+ val newAlpha = randomByteArray(0x3A20001, 256, 1, 1)
+ val table = LookupTable()
+ table.red = newRed
+ table.blue = newBlue
+ table.green = newGreen
+ table.alpha = newAlpha
+
+ val intrinsicOutArray = timer.measure("IntrinsicLUT") {
+ intrinsicLut(
+ renderscriptContext, inputArray, sizeX, sizeY, newRed, newGreen, newBlue, newAlpha,
+ restriction
+ )
+ }
+ val toolkitOutArray = timer.measure("ToolkitLUT") {
+ Toolkit.lut(inputArray, sizeX, sizeY, table, restriction)
+ }
+ if (!validate) return true
+
+ val referenceOutArray = timer.measure("ReferenceLUT") {
+ referenceLut(inputArray, sizeX, sizeY, table, restriction)
+ }
+
+ return validateSame("LUT", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
+ println("lut ($sizeX, $sizeY) $restriction")
+ logArray("LUT red ", newRed, 256)
+ logArray("LUT green", newGreen, 256)
+ logArray("LUT blue ", newBlue, 256)
+ logArray("LUT alpha", newAlpha, 256)
+ logArray("LUT in ", inputArray)
+ logArray("LUT reference out", referenceOutArray)
+ logArray("LUT intrinsic out", intrinsicOutArray)
+ logArray("LUT toolkit out", toolkitOutArray)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneBitmapLut(
+ timer: TimingTracker,
+ bitmap: Bitmap,
+ restriction: Range2d?
+ ): Boolean {
+ val newRed = randomByteArray(0x32425, 256, 1, 1)
+ val newGreen = randomByteArray(0x1F3225, 256, 1, 1)
+ val newBlue = randomByteArray(0x32D4F27, 256, 1, 1)
+ val newAlpha = randomByteArray(0x3A20001, 256, 1, 1)
+ val table = LookupTable()
+ table.red = newRed
+ table.blue = newBlue
+ table.green = newGreen
+ table.alpha = newAlpha
+
+ val intrinsicOutArray = timer.measure("IntrinsicLUT") {
+ intrinsicLut(
+ renderscriptContext, bitmap, newRed, newGreen, newBlue, newAlpha, restriction
+ )
+ }
+ val toolkitOutBitmap = timer.measure("ToolkitLUT") {
+ Toolkit.lut(bitmap, table, restriction)
+ }
+ if (!validate) return true
+
+ val referenceOutArray = timer.measure("ReferenceLUT") {
+ referenceLut(
+ getBitmapBytes(bitmap),
+ bitmap.width,
+ bitmap.height,
+ table,
+ restriction
+ )
+ }
+
+ val toolkitOutArray = getBitmapBytes(toolkitOutBitmap)
+ return validateSame("LutBitmap", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
+ println("LutBitmap $restriction")
+ logArray("LutBitmap red ", newRed, 256)
+ logArray("LutBitmap green", newGreen, 256)
+ logArray("LutBitmap blue ", newBlue, 256)
+ logArray("LutBitmap alpha", newAlpha, 256)
+ //logArray("LutBitmap in ", inputArray, 80)
+ logArray("LutBitmap reference out", referenceOutArray)
+ logArray("LutBitmap intrinsic out", intrinsicOutArray)
+ logArray("LutBitmap toolkit out", toolkitOutArray)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testLut3d(timer: TimingTracker): Boolean {
+ val cubeSizesToTry = listOf(
+ Dimension(2, 2, 2),
+ Dimension(32, 32, 16),
+ Dimension(256, 256, 256)
+ )
+ return cubeSizesToTry.all { cubeSize ->
+ val identityCube = identityCube(cubeSize)
+ val randomCube = randomCube(0x23424, cubeSize)
+ testOneBitmapLut3d(timer, testImage1, cubeSize, identityCube, 1, null) and
+ testOneBitmapLut3d(timer, testImage2, cubeSize, randomCube, 3, null) and
+ testOneBitmapLut3d(timer, testImage2, cubeSize, randomCube, 3, Range2d(6, 23, 2, 4)) and
+ commonLayoutsToTry.all { (sizeX, sizeY, restriction) ->
+ testOneRandomLut3d(timer, sizeX, sizeY, cubeSize, identityCube, 1, restriction) &&
+ testOneRandomLut3d(
+ timer,
+ sizeX,
+ sizeY,
+ cubeSize,
+ randomCube,
+ 3,
+ restriction
+ )
+ }
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneRandomLut3d(
+ timer: TimingTracker,
+ sizeX: Int,
+ sizeY: Int,
+ cubeSize: Dimension,
+ cubeArray: ByteArray,
+ allowedIntError: Int, restriction: Range2d?
+ ): Boolean {
+ val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, 4)
+
+ val intrinsicOutArray = timer.measure("IntrinsicLut3d") {
+ intrinsicLut3d(
+ renderscriptContext, inputArray, sizeX, sizeY, cubeArray, cubeSize, restriction
+ )
+ }
+ val toolkitOutArray = timer.measure("ToolkitLut3d") {
+ val toolkitCube = Rgba3dArray(cubeArray, cubeSize.sizeX, cubeSize.sizeY, cubeSize.sizeZ)
+ Toolkit.lut3d(inputArray, sizeX, sizeY, toolkitCube, restriction)
+ }
+ if (!validate) return true
+
+ val referenceOutArray = timer.measure("ReferenceLut3d") {
+ val cube = Rgba3dArray(cubeArray, cubeSize.sizeX, cubeSize.sizeY, cubeSize.sizeZ)
+ referenceLut3d(inputArray, sizeX, sizeY, cube, restriction)
+ }
+
+ return validateSame(
+ "lut3d",
+ intrinsicOutArray,
+ referenceOutArray,
+ toolkitOutArray,
+ false,
+ allowedIntError
+ ) {
+ println("lut3d ($sizeX, $sizeY) $restriction")
+ logArray("lut3d cube", cubeArray, 256)
+ logArray("lut3d in ", inputArray, 64)
+ logArray("lut3d reference out", referenceOutArray, 64)
+ logArray("lut3d intrinsic out", intrinsicOutArray, 64)
+ logArray("lut3d toolkit out", toolkitOutArray)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneBitmapLut3d(
+ timer: TimingTracker,
+ bitmap: Bitmap,
+ cubeSize: Dimension,
+ cubeArray: ByteArray,
+ allowedIntError: Int, restriction: Range2d?
+ ): Boolean {
+ val intrinsicOutArray = timer.measure("IntrinsicLut3d") {
+ intrinsicLut3d(renderscriptContext, bitmap, cubeArray, cubeSize, restriction)
+ }
+ val toolkitOutBitmap = timer.measure("ToolkitLut3d") {
+ val toolkitCube = Rgba3dArray(cubeArray, cubeSize.sizeX, cubeSize.sizeY, cubeSize.sizeZ)
+ Toolkit.lut3d(bitmap, toolkitCube, restriction)
+ }
+ if (!validate) return true
+
+ val referenceOutArray = timer.measure("ReferenceLut3d") {
+ val cube = Rgba3dArray(cubeArray, cubeSize.sizeX, cubeSize.sizeY, cubeSize.sizeZ)
+ referenceLut3d(getBitmapBytes(bitmap), bitmap.width, bitmap.height, cube, restriction)
+ }
+
+ val toolkitOutArray = getBitmapBytes(toolkitOutBitmap)
+ return validateSame(
+ "Lut3dBitmap",
+ intrinsicOutArray,
+ referenceOutArray,
+ toolkitOutArray,
+ false,
+ allowedIntError
+ ) {
+ println("Lut3dBitmap $restriction")
+ logArray("Lut3dBitmap cube", cubeArray, 256)
+ //logArray("Lut3dBitmap in ", inputArray, 64)
+ logArray("Lut3dBitmap reference out", referenceOutArray, 64)
+ logArray("Lut3dBitmap intrinsic out", intrinsicOutArray, 64)
+ logArray("Lut3dBitmap toolkit out", toolkitOutArray)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testResize(timer: TimingTracker): Boolean {
+ val factorsToTry = listOf(
+ Pair(1f, 1f),
+ Pair(0.5f, 1f),
+ Pair(2f, 2f),
+ Pair(0.5f, 2f),
+ Pair(2f, 0.5f),
+ // The RenderScript Intrinsic tests used the above factors. It's tempting to use
+ // less regular ones like Pair(6.37f, 0.17f) however this creates small offset
+ // errors between the result provided by the C++ code and the SIMD code. This is
+ // due to the SIMD code using a scaled integer to increment going from one pixel to the
+ // next, while the C++ code uses float operations.
+ )
+ val layoutsToTry = listOf(
+ TestLayout(37, 47, null),
+ TestLayout(60, 10, null),
+ TestLayout(6, 4, Range2d(1, 3, 0, 2)),
+ TestLayout(10, 14, Range2d(2, 3, 3, 7)),
+ )
+
+ return factorsToTry.all { (scaleX, scaleY) ->
+ // Do one resize that's greater than 4x, as that's used in the code but don't do it
+ // for everything, as some images will get very large
+ testOneRandomResize(timer, 1, 25, 30, 6f, 6f, null) and
+ testOneBitmapResize(timer, testImage1, scaleX, scaleY, null) and
+ testOneBitmapResize(timer, testImage1, scaleX, scaleY, Range2d(6, 23, 2, 4)) and
+ layoutsToTry.all { (sizeX, sizeY, restriction) ->
+ (1..4).all { vectorSize ->
+ testOneRandomResize(
+ timer,
+ vectorSize,
+ sizeX,
+ sizeY,
+ scaleX,
+ scaleY,
+ restriction
+ )
+ }
+ }
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneRandomResize(
+ timer: TimingTracker,
+ vectorSize: Int,
+ inSizeX: Int,
+ inSizeY: Int,
+ scaleX: Float,
+ scaleY: Float,
+ restriction: Range2d?
+ ): Boolean {
+ val inputArray = randomByteArray(0x50521f0, inSizeX, inSizeY, paddedSize(vectorSize))
+ val outSizeX = (inSizeX * scaleX).toInt()
+ val outSizeY = (inSizeY * scaleY).toInt()
+
+ val intrinsicOutArray = timer.measure("IntrinsicResize") {
+ intrinsicResize(
+ renderscriptContext, inputArray, vectorSize, inSizeX, inSizeY, outSizeX, outSizeY,
+ restriction
+ )
+ }
+ val toolkitOutArray = timer.measure("ToolkitResize") {
+ Toolkit.resize(
+ inputArray, vectorSize, inSizeX, inSizeY, outSizeX, outSizeY, restriction
+ )
+ }
+ if (!validate) return true
+
+ val referenceOutArray = timer.measure("ReferenceResize") {
+ referenceResize(
+ inputArray, vectorSize, inSizeX, inSizeY, outSizeX, outSizeY, restriction
+ )
+ }
+
+ return validateSame("resize", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
+ println("resize $vectorSize ($inSizeX, $inSizeY) by ($scaleX, $scaleY) to ($outSizeX, $outSizeY), $restriction")
+ logArray("resize in ", inputArray)
+ logArray("resize reference out", referenceOutArray)
+ logArray("resize intrinsic out", intrinsicOutArray)
+ logArray("resize toolkit out", toolkitOutArray)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneBitmapResize(
+ timer: TimingTracker,
+ bitmap: Bitmap,
+ scaleX: Float,
+ scaleY: Float,
+ restriction: Range2d?
+ ): Boolean {
+ // println("Doing resize $inSizeX x $inSizeY x $vectorSize, $scaleX x $scaleY, $restriction")
+ val outSizeX = (bitmap.width * scaleX).toInt()
+ val outSizeY = (bitmap.height * scaleY).toInt()
+
+ val intrinsicOutArray = timer.measure("IntrinsicResize") {
+ intrinsicResize(renderscriptContext, bitmap, outSizeX, outSizeY, restriction)
+ }
+ val toolkitOutBitmap = timer.measure("ToolkitResize") {
+ Toolkit.resize(bitmap, outSizeX, outSizeY, restriction)
+ }
+ if (!validate) return true
+
+ val referenceOutArray = timer.measure("ReferenceResize") {
+ referenceResize(
+ getBitmapBytes(bitmap),
+ vectorSizeOfBitmap(bitmap),
+ bitmap.width,
+ bitmap.height,
+ outSizeX,
+ outSizeY,
+ restriction
+ )
+ }
+
+ val toolkitOutArray = getBitmapBytes(toolkitOutBitmap)
+ return validateSame("ResizeBitmap", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
+ println("ResizeBitmap by ($scaleX, $scaleY) to ($outSizeX, $outSizeY), $restriction")
+ //logArray("ResizeBitmap in ", inputArray, 100)
+ logArray("ResizeBitmap reference out", referenceOutArray)
+ logArray("ResizeBitmap intrinsic out", intrinsicOutArray)
+ logArray("ResizeBitmap toolkit out", toolkitOutArray)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testYuvToRgb(timer: TimingTracker): Boolean {
+ val layoutsToTry = listOf(
+ // Don't try sizeX with odd values. That's not allowed by definition of some
+ // of the video formats.
+ TestLayout(10, 14, null),
+ TestLayout(64, 40, null),
+ TestLayout(96, 94, null),
+ )
+ return layoutsToTry.all { (sizeX, sizeY, _) ->
+ YuvFormat.values().all { format ->
+ testOneRandomYuvToRgb(timer, sizeX, sizeY, format) and
+ testOneRandomYuvToRgbBitmap(timer, sizeX, sizeY, format)
+ }
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneRandomYuvToRgb(
+ timer: TimingTracker,
+ sizeX: Int,
+ sizeY: Int,
+ format: YuvFormat
+ ): Boolean {
+ // The RenderScript Intrinsic does not handle this combination correctly.
+ if (format == YuvFormat.YV12 && sizeX % 32 != 0) {
+ return true
+ }
+ val inputArray = randomYuvArray(0x50521f0, sizeX, sizeY, format)
+
+ val intrinsicOutArray = timer.measure("IntrinsicYuvToRgb") {
+ intrinsicYuvToRgb(renderscriptContext, inputArray, sizeX, sizeY, format)
+ }
+ val toolkitOutArray = timer.measure("ToolkitYuvToRgb") {
+ Toolkit.yuvToRgb(inputArray, sizeX, sizeY, format)
+ }
+ if (!validate) return true
+
+ val referenceOutArray = timer.measure("ReferenceYuvToRgb") {
+ referenceYuvToRgb(inputArray, sizeX, sizeY, format)
+ }
+
+ return validateSame("yuvToRgb", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
+ println("yuvToRgb ($sizeX, $sizeY) $format")
+ logArray("yuvToRgb in ", inputArray)
+ logArray("yuvToRgb reference out", referenceOutArray)
+ logArray("yuvToRgb intrinsic out", intrinsicOutArray)
+ logArray("yuvToRgb toolkit out", toolkitOutArray)
+ }
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun testOneRandomYuvToRgbBitmap(
+ timer: TimingTracker,
+ sizeX: Int,
+ sizeY: Int,
+ format: YuvFormat
+ ): Boolean {
+ // The RenderScript Intrinsic does not handle this combination correctly.
+ if (format == YuvFormat.YV12 && sizeX % 32 != 0) {
+ return true
+ }
+ val inputArray = randomYuvArray(0x50521f0, sizeX, sizeY, format)
+
+ val intrinsicOutArray = timer.measure("IntrinsicYuvToRgb") {
+ intrinsicYuvToRgb(renderscriptContext, inputArray, sizeX, sizeY, format)
+ }
+ val toolkitOutBitmap = timer.measure("ToolkitYuvToRgb") {
+ Toolkit.yuvToRgbBitmap(inputArray, sizeX, sizeY, format)
+ }
+ if (!validate) return true
+
+ val referenceOutArray = timer.measure("ReferenceYuvToRgb") {
+ referenceYuvToRgb(inputArray, sizeX, sizeY, format)
+ }
+
+ val toolkitOutArray = getBitmapBytes(toolkitOutBitmap)
+ return validateSame("yuvToRgb", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
+ println("yuvToRgb ($sizeX, $sizeY) $format")
+ logArray("yuvToRgb in ", inputArray)
+ logArray("yuvToRgb reference out", referenceOutArray)
+ logArray("yuvToRgb intrinsic out", intrinsicOutArray)
+ logArray("yuvToRgb toolkit out", toolkitOutArray)
+ }
+ }
+
+ /**
+ * Verifies that the arrays returned by the Intrinsic, the reference code, and the Toolkit
+ * are all within a margin of error.
+ *
+ * RenderScript Intrinsic test (rc/android/cts/rscpp/RSCppTest.java) used 3 for ints.
+ * For floats, rc/android/cts/rscpp/verify.rscript uses 0.0001f.
+ */
+ @ExperimentalUnsignedTypes
+ private fun validateSame(
+ task: String,
+ intrinsic: ByteArray,
+ reference: ByteArray,
+ toolkit: ByteArray,
+ skipFourth: Boolean = false,
+ allowedIntDelta: Int = 3,
+ errorLogging: () -> Unit
+ ): Boolean {
+ val success = validateAgainstReference(
+ task, reference, "Intrinsic", intrinsic, skipFourth, allowedIntDelta
+ ) and validateAgainstReference(
+ task, reference, "Toolkit", toolkit, skipFourth, allowedIntDelta
+ )
+ if (!success) {
+ println("$task FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!")
+ errorLogging()
+ }
+ return success
+ }
+
+ private fun validateSame(
+ task: String,
+ intrinsic: IntArray,
+ reference: IntArray,
+ toolkit: IntArray,
+ allowedIntDelta: Int = 3,
+ errorLogging: () -> Unit
+ ): Boolean {
+ val success = validateAgainstReference(
+ task, reference, "Intrinsic", intrinsic, allowedIntDelta
+ ) and validateAgainstReference(
+ task, reference, "Toolkit", toolkit, allowedIntDelta
+ )
+ if (!success) {
+ println("$task FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!")
+ errorLogging()
+ }
+ return success
+ }
+
+ @ExperimentalUnsignedTypes
+ private fun validateAgainstReference(
+ task: String,
+ in1: ByteArray,
+ name2: String,
+ in2: ByteArray,
+ skipFourth: Boolean,
+ allowedIntDelta: Int
+ ): Boolean {
+ if (in1.size != in2.size) {
+ println("$task. Sizes don't match: Reference ${in1.size}, $name2 ${in2.size}")
+ return false
+ }
+ var same = true
+ val maxDetails = 80
+ val diffs = CharArray(min(in1.size, maxDetails)) {'.'}
+ for (i in in1.indices) {
+ if (skipFourth && i % 4 == 3) {
+ continue
+ }
+ val delta = abs(in1[i].toUByte().toInt() - in2[i].toUByte().toInt())
+ if (delta > allowedIntDelta) {
+ if (same) {
+ println(
+ "$task. At $i, Reference is ${in1[i].toUByte()}, $name2 is ${in2[i].toUByte()}"
+ )
+ }
+ if (i < maxDetails) diffs[i] = 'X'
+ same = false
+ }
+ }
+ if (!same) {
+ for (i in 0 until (min(in1.size, maxDetails) / 4)) print("%-3d|".format(i))
+ println()
+ println(diffs)
+ }
+ return same
+ }
+
+ private fun validateAgainstReference(
+ task: String,
+ in1: IntArray,
+ name2: String,
+ in2: IntArray,
+ allowedIntDelta: Int
+ ): Boolean {
+ if (in1.size != in2.size) {
+ println("$task. Sizes don't match: Reference ${in1.size}, $name2 ${in2.size}")
+ return false
+ }
+ for (i in in1.indices) {
+ val delta = abs(in1[i] - in2[i])
+ if (delta > allowedIntDelta) {
+ println("$task. At $i, Reference is ${in1[i]}, $name2 is ${in2[i]}")
+ return false
+ }
+ }
+ return true
+ }
+}
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/BufferUtils.kt b/test-app/src/main/java/com/google/android/renderscript_test/BufferUtils.kt
new file mode 100644
index 0000000..51ece8e
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/BufferUtils.kt
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.renderscript.Element
+import android.renderscript.RenderScript
+import com.google.android.renderscript.Range2d
+import com.google.android.renderscript.Rgba3dArray
+import com.google.android.renderscript.YuvFormat
+import java.nio.ByteBuffer
+import java.util.Random
+import kotlin.math.floor
+import kotlin.math.max
+import kotlin.math.min
+
+/**
+ * A vector of 4 integers.
+ */
+class Int4(
+ var x: Int = 0,
+ var y: Int = 0,
+ var z: Int = 0,
+ var w: Int = 0
+) {
+ operator fun plus(other: Int4) = Int4(x + other.x, y + other.y, z + other.z, w + other.w)
+ operator fun plus(n: Int) = Int4(x + n, y + n, z + n, w + n)
+
+ operator fun minus(other: Int4) = Int4(x - other.x, y - other.y, z - other.z, w - other.w)
+ operator fun minus(n: Int) = Int4(x - n, y - n, z - n, w - n)
+
+ operator fun times(other: Int4) = Int4(x * other.x, y * other.y, z * other.z, w * other.w)
+ operator fun times(n: Int) = Int4(x * n, y * n, z * n, w * n)
+
+ fun toFloat4() = Float4(x.toFloat(), y.toFloat(), z.toFloat(), w.toFloat())
+}
+
+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))
+
+/**
+ * A vector of 4 floats.
+ */
+data class Float4(
+ var x: Float = 0f,
+ var y: Float = 0f,
+ var z: Float = 0f,
+ var w: Float = 0f
+) {
+ operator fun plus(other: Float4) = Float4(x + other.x, y + other.y, z + other.z, w + other.w)
+ operator fun plus(f: Float) = Float4(x + f, y + f, z + f, w + f)
+
+ operator fun minus(other: Float4) = Float4(x - other.x, y - other.y, z - other.z, w - other.w)
+ operator fun minus(f: Float) = Float4(x - f, y - f, z - f, w - f)
+
+ operator fun times(other: Float4) = Float4(x * other.x, y * other.y, z * other.z, w * other.w)
+ operator fun times(f: Float) = Float4(x * f, y * f, z * f, w * f)
+
+ operator fun div(other: Float4) = Float4(x / other.x, y / other.y, z / other.z, w / other.w)
+ operator fun div(f: Float) = Float4(x / f, y / f, z / f, w / f)
+
+ fun intFloor() = Int4(floor(x).toInt(), floor(y).toInt(), floor(z).toInt(), floor(w).toInt())
+}
+
+/**
+ * Convert a UByteArray to a Float4 vector
+ */
+@ExperimentalUnsignedTypes
+fun UByteArray.toFloat4(): Float4 {
+ require(size == 4)
+ return Float4(this[0].toFloat(), this[1].toFloat(), this[2].toFloat(), this[3].toFloat())
+}
+
+/**
+ * Convert a ByteArray to a Float4 vector
+ */
+@ExperimentalUnsignedTypes
+fun ByteArray.toFloat4(): Float4 {
+ require(size == 4)
+ return Float4(
+ this[0].toUByte().toFloat(),
+ this[1].toUByte().toFloat(),
+ this[2].toUByte().toFloat(),
+ this[3].toUByte().toFloat()
+ )
+}
+
+data class Dimension(val sizeX: Int, val sizeY: Int, val sizeZ: Int)
+
+/**
+ * An RGBA value represented by 4 Int.
+ *
+ * Note that the arithmetical operations consider a 0..255 value the equivalent of 0f..1f.
+ * After adding or subtracting, the value is clamped. After multiplying, the value is rescaled to
+ * stay in the 0..255 range. This is useful for the Blend operation.
+ */
+@ExperimentalUnsignedTypes
+data class Rgba(
+ var r: Int = 0,
+ var g: Int = 0,
+ var b: Int = 0,
+ var a: Int = 0
+) {
+ operator fun plus(other: Rgba) =
+ Rgba(r + other.r, g + other.g, b + other.b, a + other.a).clampToUByteRange()
+
+ operator fun minus(other: Rgba) =
+ Rgba(r - other.r, g - other.g, b - other.b, a - other.a).clampToUByteRange()
+
+ operator fun times(other: Rgba) = Rgba(r * other.r, g * other.g, b * other.b, a * other.a) shr 8
+ operator fun times(scalar: Int) = Rgba(r * scalar, g * scalar, b * scalar, a * scalar) shr 8
+
+ infix fun xor(other: Rgba) = Rgba(r xor other.r, g xor other.g, b xor other.b, a xor other.a)
+
+ infix fun shr(other: Int) = Rgba(r shr other, g shr other, b shr other, a shr other)
+
+ private fun clampToUByteRange() = Rgba(
+ r.clampToUByteRange(),
+ g.clampToUByteRange(),
+ b.clampToUByteRange(),
+ a.clampToUByteRange()
+ )
+}
+
+/**
+ * A 2D array of UByte vectors, stored in row-major format.
+ *
+ * Arrays of vectorSize == 3 are padded to 4.
+ */
+@ExperimentalUnsignedTypes
+class Vector2dArray(
+ val values: UByteArray,
+ val vectorSize: Int,
+ val sizeX: Int,
+ val sizeY: Int
+) {
+ /**
+ * If true, index access that would try to get a value that's out of bounds will simply
+ * return the border value instead. E.g. for [3, -3] would return the value for [3, 0],
+ * assuming that the sizeX > 3.
+ */
+ var clipReadToRange: Boolean = false
+
+ operator fun get(x: Int, y: Int): UByteArray {
+ var fixedX = x
+ var fixedY = y
+ if (clipReadToRange) {
+ fixedX = min(max(x, 0), sizeX - 1)
+ fixedY = min(max(y, 0), sizeY - 1)
+ } else {
+ require(x in 0 until sizeX && y in 0 until sizeY) { "Out of bounds" }
+ }
+ val start = indexOfVector(fixedX, fixedY)
+ return UByteArray(paddedSize(vectorSize)) { values[start + it] }
+ }
+
+ operator fun set(x: Int, y: Int, value: UByteArray) {
+ require(value.size == paddedSize(vectorSize)) { "Not the expected vector size" }
+ require(x in 0 until sizeX && y in 0 until sizeY) { "Out of bounds" }
+ val start = indexOfVector(x, y)
+ for (i in value.indices) {
+ values[start + i] = value[i]
+ }
+ }
+
+ private fun indexOfVector(x: Int, y: Int) = ((y * sizeX) + x) * paddedSize(vectorSize)
+
+ fun createSameSized() = Vector2dArray(UByteArray(values.size), vectorSize, sizeX, sizeY)
+
+ fun forEach(restriction: Range2d?, work: (Int, Int) -> (Unit)) {
+ forEachCell(sizeX, sizeY, restriction, work)
+ }
+}
+
+/**
+ * A 2D array of float vectors, stored in row-major format.
+ *
+ * Arrays of vectorSize == 3 are padded to 4.
+ */
+class FloatVector2dArray(
+ val values: FloatArray,
+ val vectorSize: Int,
+ val sizeX: Int,
+ val sizeY: Int
+) {
+ /**
+ * If true, index access that would try to get a value that's out of bounds will simply
+ * return the border value instead. E.g. for [3, -3] would return the value for [3, 0],
+ * assuming that the sizeX > 3.
+ */
+ var clipAccessToRange: Boolean = false
+
+ operator fun get(x: Int, y: Int): FloatArray {
+ var fixedX = x
+ var fixedY = y
+ if (clipAccessToRange) {
+ fixedX = min(max(x, 0), sizeX - 1)
+ fixedY = min(max(y, 0), sizeY - 1)
+ } else {
+ require(x in 0 until sizeX && y in 0 until sizeY) { "Out of bounds" }
+ }
+ val start = indexOfVector(fixedX, fixedY)
+ return FloatArray(vectorSize) { values[start + it] }
+ }
+
+ operator fun set(x: Int, y: Int, value: FloatArray) {
+ require(x in 0 until sizeX && y in 0 until sizeY) { "Out of bounds" }
+ val start = indexOfVector(x, y)
+ for (i in value.indices) {
+ values[start + i] = value[i]
+ }
+ }
+
+ private fun indexOfVector(x: Int, y: Int) = ((y * sizeX) + x) * paddedSize(vectorSize)
+
+ fun createSameSized() = FloatVector2dArray(FloatArray(values.size), vectorSize, sizeX, sizeY)
+
+ fun forEach(restriction: Range2d?, work: (Int, Int) -> (Unit)) {
+ forEachCell(sizeX, sizeY, restriction, work)
+ }
+}
+
+/**
+ * A 2D array of RGBA data.
+ */
+@ExperimentalUnsignedTypes
+class Rgba2dArray(
+ private val values: ByteArray,
+ val sizeX: Int,
+ val sizeY: Int
+) {
+ operator fun get(x: Int, y: Int): Rgba {
+ val i = indexOfVector(x, y)
+ return Rgba(
+ values[i].toUByte().toInt(),
+ values[i + 1].toUByte().toInt(),
+ values[i + 2].toUByte().toInt(),
+ values[i + 3].toUByte().toInt()
+ )
+ }
+
+ operator fun set(x: Int, y: Int, value: Rgba) {
+ // Verify that x, y, z, w are in the 0..255 range
+ require(value.r in 0..255)
+ require(value.g in 0..255)
+ require(value.b in 0..255)
+ require(value.a in 0..255)
+ val i = indexOfVector(x, y)
+ values[i] = value.r.toUByte().toByte()
+ values[i + 1] = value.g.toUByte().toByte()
+ values[i + 2] = value.b.toUByte().toByte()
+ values[i + 3] = value.a.toUByte().toByte()
+ }
+
+ private fun indexOfVector(x: Int, y: Int) = ((y * sizeX) + x) * 4
+
+ fun forEachCell(restriction: Range2d?, work: (Int, Int) -> (Unit)) =
+ forEachCell(sizeX, sizeY, restriction, work)
+}
+
+/**
+ * Return a value that's between start and end, with the fraction indicating how far along.
+ */
+fun mix(start: Float, end: Float, fraction: Float) = start + (end - start) * fraction
+
+fun mix(a: Float4, b: Float4, fraction: Float) = Float4(
+ mix(a.x, b.x, fraction),
+ mix(a.y, b.y, fraction),
+ mix(a.z, b.z, fraction),
+ mix(a.w, b.w, fraction)
+)
+
+/**
+ * For vectors of size 3, the original RenderScript has them occupy the same space as a size 4.
+ * While RenderScript had a method to avoid this padding, it did not apply to Intrinsics.
+ *
+ * To preserve compatibility, the Toolkit doing the same.
+ */
+fun paddedSize(vectorSize: Int) = if (vectorSize == 3) 4 else vectorSize
+
+/**
+ * Create a ByteArray of the specified size filled with random data.
+ */
+fun randomByteArray(seed: Long, sizeX: Int, sizeY: Int, elementSize: Int): ByteArray {
+ val r = Random(seed)
+ return ByteArray(sizeX * sizeY * elementSize) { (r.nextInt(255) - 128).toByte() }
+}
+
+/**
+ * Create a FloatArray of the specified size filled with random data.
+ *
+ * By default, the random data is between 0f and 1f. The factor can be used to scale that.
+ */
+fun randomFloatArray(
+ seed: Long,
+ sizeX: Int,
+ sizeY: Int,
+ elementSize: Int,
+ factor: Float = 1f
+): FloatArray {
+ val r = Random(seed)
+ return FloatArray(sizeX * sizeY * elementSize) { r.nextFloat() * factor }
+}
+
+/**
+ * Create a cube of the specified size filled with random data.
+ */
+fun randomCube(seed: Long, cubeSize: Dimension): ByteArray {
+ val r = Random(seed)
+ return ByteArray(cubeSize.sizeX * cubeSize.sizeY * cubeSize.sizeZ * 4) {
+ (r.nextInt(255) - 128).toByte()
+ }
+}
+
+/**
+ * Create the identity cube, i.e. one that if used in Lut3d, the output is the same as the input
+ */
+@ExperimentalUnsignedTypes
+fun identityCube(cubeSize: Dimension): ByteArray {
+ val data = ByteArray(cubeSize.sizeX * cubeSize.sizeY * cubeSize.sizeZ * 4)
+ val cube = Rgba3dArray(data, cubeSize.sizeX, cubeSize.sizeY, cubeSize.sizeZ)
+ for (z in 0 until cubeSize.sizeZ) {
+ for (y in 0 until cubeSize.sizeY) {
+ for (x in 0 until cubeSize.sizeX) {
+ cube[x, y, z] =
+ byteArrayOf(
+ (x * 255 / (cubeSize.sizeX - 1)).toByte(),
+ (y * 255 / (cubeSize.sizeY - 1)).toByte(),
+ (z * 255 / (cubeSize.sizeZ - 1)).toByte(),
+ (255).toByte()
+ )
+ }
+ }
+ }
+ return data
+}
+
+fun randomYuvArray(seed: Long, sizeX: Int, sizeY: Int, format: YuvFormat): ByteArray {
+ // YUV formats are not well defined for odd dimensions
+ require(sizeX % 2 == 0 && sizeY % 2 == 0)
+ val halfSizeX = sizeX / 2
+ val halfSizeY = sizeY / 2
+ var totalSize = 0
+ when (format) {
+ YuvFormat.YV12 -> {
+ val strideX = roundUpTo16(sizeX)
+ totalSize = strideX * sizeY + roundUpTo16(strideX / 2) * halfSizeY * 2
+ }
+ YuvFormat.NV21 -> totalSize = sizeX * sizeY + halfSizeX * halfSizeY * 2
+ else -> require(false) { "Unknown YUV format $format" }
+ }
+
+ return randomByteArray(seed, totalSize, 1, 1)
+}
+
+/**
+ * Converts a float to a byte, clamping to make it fit the limited range.
+ */
+@ExperimentalUnsignedTypes
+fun Float.clampToUByte(): UByte = min(255, max(0, (this + 0.5f).toInt())).toUByte()
+
+/**
+ * Converts a FloatArray to UByteArray, clamping.
+ */
+@ExperimentalUnsignedTypes
+fun FloatArray.clampToUByte() = UByteArray(size) { this[it].clampToUByte() }
+
+/**
+ * Limits an Int to what can fit in a UByte.
+ */
+fun Int.clampToUByteRange(): Int = min(255, max(0, this))
+
+/**
+ * Converts an Int to a UByte, clamping.
+ */
+@ExperimentalUnsignedTypes
+fun Int.clampToUByte(): UByte = this.clampToUByteRange().toUByte()
+
+/**
+ * Converts a float (0f .. 1f) to a byte (0 .. 255)
+ */
+@ExperimentalUnsignedTypes
+fun unitFloatClampedToUByte(num: Float): UByte = (num * 255f).clampToUByte()
+
+/**
+ * Convert a byte (0 .. 255) to a float (0f .. 1f)
+ */
+@ExperimentalUnsignedTypes
+fun byteToUnitFloat(num: UByte) = num.toFloat() * 0.003921569f
+
+@ExperimentalUnsignedTypes
+fun UByteArray.toFloatArray() = FloatArray(size) { this[it].toFloat() }
+
+/**
+ * For each cell that's in the 2D array defined by sizeX and sizeY, and clipped down by the
+ * restriction, invoke the work function.
+ */
+fun forEachCell(sizeX: Int, sizeY: Int, restriction: Range2d?, work: (Int, Int) -> (Unit)) {
+ val startX = restriction?.startX ?: 0
+ val startY = restriction?.startY ?: 0
+ val endX = restriction?.endX ?: sizeX
+ val endY = restriction?.endY ?: sizeY
+ for (y in startY until endY) {
+ for (x in startX until endX) {
+ work(x, y)
+ }
+ }
+}
+
+operator fun FloatArray.times(other: FloatArray) = FloatArray(size) { this[it] * other[it] }
+operator fun FloatArray.times(other: Float) = FloatArray(size) { this[it] * other }
+operator fun FloatArray.plus(other: FloatArray) = FloatArray(size) { this[it] + other[it] }
+operator fun FloatArray.minus(other: FloatArray) = FloatArray(size) { this[it] - other[it] }
+
+fun renderScriptVectorElementForU8(rs: RenderScript?, vectorSize: Int): Element {
+ when (vectorSize) {
+ 1 -> return Element.U8(rs)
+ 2 -> return Element.U8_2(rs)
+ 3 -> return Element.U8_3(rs)
+ 4 -> return Element.U8_4(rs)
+ }
+ throw java.lang.IllegalArgumentException("RenderScriptToolkit tests. Only vectors of size 1-4 are supported. $vectorSize provided.")
+}
+
+fun renderScriptVectorElementForI32(rs: RenderScript?, vectorSize: Int): Element {
+ when (vectorSize) {
+ 1 -> return Element.I32(rs)
+ 2 -> return Element.I32_2(rs)
+ 3 -> return Element.I32_3(rs)
+ 4 -> return Element.I32_4(rs)
+ }
+ throw java.lang.IllegalArgumentException("RenderScriptToolkit tests. Only vectors of size 1-4 are supported. $vectorSize provided.")
+}
+
+/* When we'll handle floats
+fun renderScriptVectorElementForF32(rs: RenderScript?, vectorSize: Int): Element {
+ when (vectorSize) {
+ 1 -> return Element.F32(rs)
+ 2 -> return Element.F32_2(rs)
+ 3 -> return Element.F32_3(rs)
+ 4 -> return Element.F32_4(rs)
+ }
+ throw java.lang.IllegalArgumentException("RenderScriptToolkit tests. Only vectors of size 1-4 are supported. $vectorSize provided.")
+}*/
+
+fun renderScriptElementForBitmap(context: RenderScript, bitmap: Bitmap): Element {
+ return when (val config = bitmap.config) {
+ Bitmap.Config.ALPHA_8 -> Element.A_8(context)
+ Bitmap.Config.ARGB_8888 -> Element.RGBA_8888(context)
+ else -> throw IllegalArgumentException("RenderScript Toolkit can't support bitmaps with config $config.")
+ }
+}
+
+fun getBitmapBytes(bitmap: Bitmap): ByteArray {
+ val buffer: ByteBuffer = ByteBuffer.allocate(bitmap.byteCount)
+ bitmap.copyPixelsToBuffer(buffer)
+ return buffer.array()
+}
+
+fun vectorSizeOfBitmap(bitmap: Bitmap): Int {
+ return when (val config = bitmap.config) {
+ Bitmap.Config.ALPHA_8 -> 1
+ Bitmap.Config.ARGB_8888 -> 4
+ else -> throw IllegalArgumentException("RenderScript Toolkit can't support bitmaps with config $config.")
+ }
+}
+
+fun duplicateBitmap(original: Bitmap): Bitmap {
+ val copy = Bitmap.createBitmap(original.width, original.height, original.config)
+ val canvas = Canvas(copy)
+ canvas.drawBitmap(original, 0f, 0f, null)
+ return copy
+}
+
+@ExperimentalUnsignedTypes
+fun logArray(prefix: String, array: ByteArray, number: Int = 20) {
+ val values = array.joinToString(limit = number) { it.toUByte().toString() }
+ println("$prefix[${array.size}] $values}\n")
+}
+
+fun logArray(prefix: String, array: IntArray, number: Int = 20) {
+ val values = array.joinToString(limit = number)
+ println("$prefix[${array.size}] $values}\n")
+}
+
+fun logArray(prefix: String, array: FloatArray?, number: Int = 20) {
+ val values = array?.joinToString(limit = number) { "%.2f".format(it) } ?: "(null)"
+ println("$prefix[${array?.size}] $values}\n")
+}
+
+fun roundUpTo16(value: Int): Int {
+ require(value >= 0)
+ return (value + 15) and 15.inv()
+}
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicBlend.kt b/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicBlend.kt
new file mode 100644
index 0000000..17176df
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicBlend.kt
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import android.graphics.Bitmap
+import android.renderscript.Allocation
+import android.renderscript.Element
+import android.renderscript.RenderScript
+import android.renderscript.Script
+import android.renderscript.ScriptIntrinsicBlend
+import android.renderscript.Type
+import com.google.android.renderscript.BlendingMode
+import com.google.android.renderscript.Range2d
+
+/**
+ * Does a Blend operation using the RenderScript Intrinsics.
+ */
+fun intrinsicBlend(
+ context: RenderScript,
+ mode: BlendingMode,
+ sourceArray: ByteArray,
+ destArray: ByteArray,
+ sizeX: Int,
+ sizeY: Int,
+ restriction: Range2d?
+) {
+ val scriptBlend = ScriptIntrinsicBlend.create(context, Element.U8_4(context))
+ val builder = Type.Builder(context, Element.U8_4(context))
+ builder.setX(sizeX)
+ builder.setY(sizeY)
+ val arrayType = builder.create()
+ val sourceAllocation = Allocation.createTyped(context, arrayType)
+ val destAllocation = Allocation.createTyped(context, arrayType)
+ sourceAllocation.copyFrom(sourceArray)
+ destAllocation.copyFrom(destArray)
+
+ callBlendForEach(scriptBlend, sourceAllocation, destAllocation, mode, restriction)
+ destAllocation.copyTo(destArray)
+
+ sourceAllocation.destroy()
+ destAllocation.destroy()
+ arrayType.destroy()
+ scriptBlend.destroy()
+}
+
+fun intrinsicBlend(
+ context: RenderScript,
+ mode: BlendingMode,
+ sourceBitmap: Bitmap,
+ destBitmap: Bitmap,
+ restriction: Range2d?
+) {
+ val scriptBlend = ScriptIntrinsicBlend.create(context, Element.U8_4(context))
+ val sourceAllocation = Allocation.createFromBitmap(context, sourceBitmap)
+ val destAllocation = Allocation.createFromBitmap(context, destBitmap)
+ sourceAllocation.copyFrom(sourceBitmap)
+ destAllocation.copyFrom(destBitmap)
+
+ callBlendForEach(scriptBlend, sourceAllocation, destAllocation, mode, restriction)
+ destAllocation.copyTo(destBitmap)
+
+ sourceAllocation.destroy()
+ destAllocation.destroy()
+ scriptBlend.destroy()
+}
+
+private fun callBlendForEach(
+ scriptBlend: ScriptIntrinsicBlend,
+ sourceAllocation: Allocation,
+ destAllocation: Allocation,
+ mode: BlendingMode,
+ restriction: Range2d?
+) {
+ if (restriction != null) {
+ val options = Script.LaunchOptions()
+ options.setX(restriction.startX, restriction.endX)
+ options.setY(restriction.startY, restriction.endY)
+ when (mode) {
+ BlendingMode.CLEAR -> scriptBlend.forEachClear(
+ sourceAllocation, destAllocation, options
+ )
+ BlendingMode.SRC -> scriptBlend.forEachSrc(
+ sourceAllocation, destAllocation, options
+ )
+ BlendingMode.DST -> scriptBlend.forEachDst(
+ sourceAllocation, destAllocation, options
+ )
+ BlendingMode.SRC_OVER -> scriptBlend.forEachSrcOver(
+ sourceAllocation, destAllocation, options
+ )
+ BlendingMode.DST_OVER -> scriptBlend.forEachDstOver(
+ sourceAllocation, destAllocation, options
+ )
+ BlendingMode.SRC_IN -> scriptBlend.forEachSrcIn(
+ sourceAllocation, destAllocation, options
+ )
+ BlendingMode.DST_IN -> scriptBlend.forEachDstIn(
+ sourceAllocation, destAllocation, options
+ )
+ BlendingMode.SRC_OUT -> scriptBlend.forEachSrcOut(
+ sourceAllocation, destAllocation, options
+ )
+ BlendingMode.DST_OUT -> scriptBlend.forEachDstOut(
+ sourceAllocation, destAllocation, options
+ )
+ BlendingMode.SRC_ATOP -> scriptBlend.forEachSrcAtop(
+ sourceAllocation, destAllocation, options
+ )
+ BlendingMode.DST_ATOP -> scriptBlend.forEachDstAtop(
+ sourceAllocation, destAllocation, options
+ )
+ BlendingMode.XOR -> scriptBlend.forEachXor(
+ sourceAllocation, destAllocation, options
+ )
+ BlendingMode.MULTIPLY -> scriptBlend.forEachMultiply(
+ sourceAllocation, destAllocation, options
+ )
+ BlendingMode.ADD -> scriptBlend.forEachAdd(
+ sourceAllocation, destAllocation, options
+ )
+ BlendingMode.SUBTRACT -> scriptBlend.forEachSubtract(
+ sourceAllocation, destAllocation, options
+ )
+ }
+ } else {
+ when (mode) {
+ BlendingMode.CLEAR -> scriptBlend.forEachClear(
+ sourceAllocation, destAllocation
+ )
+ BlendingMode.SRC -> scriptBlend.forEachSrc(
+ sourceAllocation, destAllocation
+ )
+ BlendingMode.DST -> scriptBlend.forEachDst(
+ sourceAllocation, destAllocation
+ )
+ BlendingMode.SRC_OVER -> scriptBlend.forEachSrcOver(
+ sourceAllocation, destAllocation
+ )
+ BlendingMode.DST_OVER -> scriptBlend.forEachDstOver(
+ sourceAllocation, destAllocation
+ )
+ BlendingMode.SRC_IN -> scriptBlend.forEachSrcIn(
+ sourceAllocation, destAllocation
+ )
+ BlendingMode.DST_IN -> scriptBlend.forEachDstIn(
+ sourceAllocation, destAllocation
+ )
+ BlendingMode.SRC_OUT -> scriptBlend.forEachSrcOut(
+ sourceAllocation, destAllocation
+ )
+ BlendingMode.DST_OUT -> scriptBlend.forEachDstOut(
+ sourceAllocation, destAllocation
+ )
+ BlendingMode.SRC_ATOP -> scriptBlend.forEachSrcAtop(
+ sourceAllocation, destAllocation
+ )
+ BlendingMode.DST_ATOP -> scriptBlend.forEachDstAtop(
+ sourceAllocation, destAllocation
+ )
+ BlendingMode.XOR -> scriptBlend.forEachXor(
+ sourceAllocation, destAllocation
+ )
+ BlendingMode.MULTIPLY -> scriptBlend.forEachMultiply(
+ sourceAllocation, destAllocation
+ )
+ BlendingMode.ADD -> scriptBlend.forEachAdd(
+ sourceAllocation, destAllocation
+ )
+ BlendingMode.SUBTRACT -> scriptBlend.forEachSubtract(
+ sourceAllocation, destAllocation
+ )
+ }
+ }
+}
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicBlur.kt b/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicBlur.kt
new file mode 100644
index 0000000..0fd503f
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicBlur.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import android.graphics.Bitmap
+import android.renderscript.Allocation
+import android.renderscript.Element
+import android.renderscript.RenderScript
+import android.renderscript.Script
+import android.renderscript.ScriptIntrinsicBlur
+import android.renderscript.Type
+import com.google.android.renderscript.Range2d
+
+/**
+ * Does a Blur operation using the RenderScript Intrinsics.
+ */
+fun intrinsicBlur(
+ context: RenderScript,
+ inputArray: ByteArray,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ radius: Int,
+ restriction: Range2d?
+): ByteArray {
+ val scriptBlur = ScriptIntrinsicBlur.create(
+ context,
+ if (vectorSize == 4) Element.RGBA_8888(context) else Element.U8(context)
+ )
+ val builder =
+ Type.Builder(
+ context,
+ renderScriptVectorElementForU8(context, vectorSize)
+ )
+ builder.setX(sizeX)
+ builder.setY(sizeY)
+ val arrayType = builder.create()
+ val inputAllocation = Allocation.createTyped(context, arrayType)
+ inputAllocation.copyFrom(inputArray)
+ val outAllocation = Allocation.createTyped(context, arrayType)
+
+ val intrinsicOutArray = ByteArray(sizeX * sizeY * vectorSize)
+ scriptBlur.setRadius(radius.toFloat())
+ scriptBlur.setInput(inputAllocation)
+
+ if (restriction != null) {
+ outAllocation.copyFrom(intrinsicOutArray) // To initialize to zero
+ val options = Script.LaunchOptions()
+ options.setX(restriction.startX, restriction.endX)
+ options.setY(restriction.startY, restriction.endY)
+ scriptBlur.forEach(outAllocation, options)
+ } else {
+ scriptBlur.forEach(outAllocation)
+ }
+ outAllocation.copyTo(intrinsicOutArray)
+ inputAllocation.destroy()
+ outAllocation.destroy()
+ arrayType.destroy()
+ scriptBlur.destroy()
+ return intrinsicOutArray
+}
+
+fun intrinsicBlur(
+ context: RenderScript,
+ bitmap: Bitmap,
+ radius: Int,
+ restriction: Range2d?
+): ByteArray {
+ val baseElement = renderScriptElementForBitmap(context, bitmap)
+ val scriptBlur = ScriptIntrinsicBlur.create(context, baseElement)
+ val inputAllocation = Allocation.createFromBitmap(context, bitmap)
+ inputAllocation.copyFrom(bitmap)
+ val outAllocation = Allocation.createTyped(context, inputAllocation.type)
+ val intrinsicOutArray = ByteArray(bitmap.byteCount)
+
+ scriptBlur.setRadius(radius.toFloat())
+ scriptBlur.setInput(inputAllocation)
+
+ if (restriction != null) {
+ outAllocation.copyFrom(intrinsicOutArray) // To initialize to zero
+ val options = Script.LaunchOptions()
+ options.setX(restriction.startX, restriction.endX)
+ options.setY(restriction.startY, restriction.endY)
+ scriptBlur.forEach(outAllocation, options)
+ } else {
+ scriptBlur.forEach(outAllocation)
+ }
+ outAllocation.copyTo(intrinsicOutArray)
+
+ inputAllocation.destroy()
+ outAllocation.destroy()
+ scriptBlur.destroy()
+ return intrinsicOutArray
+}
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicColorMatrix.kt b/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicColorMatrix.kt
new file mode 100644
index 0000000..224b925
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicColorMatrix.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import android.graphics.Bitmap
+import android.renderscript.Allocation
+import android.renderscript.Matrix4f
+import android.renderscript.RenderScript
+import android.renderscript.Script
+import android.renderscript.ScriptIntrinsicColorMatrix
+import android.renderscript.Type
+import android.renderscript.Float4
+import com.google.android.renderscript.Range2d
+
+/**
+ * Does a ColorMatrix operation using the RenderScript Intrinsics.
+ */
+fun intrinsicColorMatrix(
+ context: RenderScript,
+ conversion: Tester.ColorMatrixConversionType,
+ inputArray: ByteArray,
+ inputVectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ outputVectorSize: Int,
+ matrix: FloatArray,
+ addVector: FloatArray,
+ restriction: Range2d?
+): ByteArray {
+ val scriptColorMatrix = ScriptIntrinsicColorMatrix.create(context)
+ val inputBuilder = Type.Builder(
+ context, renderScriptVectorElementForU8(
+ context,
+ inputVectorSize
+ )
+ )
+ inputBuilder.setX(sizeX)
+ inputBuilder.setY(sizeY)
+ val inputArrayType = inputBuilder.create()
+ val inputAllocation = Allocation.createTyped(context, inputArrayType)
+ val outputBuilder = Type.Builder(
+ context, renderScriptVectorElementForU8(
+ context,
+ outputVectorSize
+ )
+ )
+ outputBuilder.setX(sizeX)
+ outputBuilder.setY(sizeY)
+ val outputArrayType = outputBuilder.create()
+ val outAllocation = Allocation.createTyped(context, outputArrayType)
+
+ inputAllocation.copyFrom(inputArray)
+ val intrinsicOutArray = ByteArray(sizeX * sizeY * paddedSize(outputVectorSize))
+ when (conversion) {
+ Tester.ColorMatrixConversionType.RGB_TO_YUV -> scriptColorMatrix.setRGBtoYUV()
+ Tester.ColorMatrixConversionType.YUV_TO_RGB -> scriptColorMatrix.setYUVtoRGB()
+ Tester.ColorMatrixConversionType.GREYSCALE -> scriptColorMatrix.setGreyscale()
+ Tester.ColorMatrixConversionType.RANDOM -> {
+ val m = Matrix4f()
+ var index = 0
+ // RS is column major
+ for (x in 0..3) {
+ for (y in 0..3) {
+ m.set(x, y, matrix[index++])
+ }
+ }
+ scriptColorMatrix.setColorMatrix(m)
+ }
+ }
+ val vector = Float4(
+ addVector[0],
+ addVector[1],
+ addVector[2],
+ addVector[3]
+ )
+ scriptColorMatrix.setAdd(vector)
+ if (restriction != null) {
+ outAllocation.copyFrom(intrinsicOutArray) // To initialize to zero
+ val options = Script.LaunchOptions()
+ options.setX(restriction.startX, restriction.endX)
+ options.setY(restriction.startY, restriction.endY)
+ scriptColorMatrix.forEach(inputAllocation, outAllocation, options)
+ } else {
+ scriptColorMatrix.forEach(inputAllocation, outAllocation)
+ }
+ outAllocation.copyTo(intrinsicOutArray)
+
+ inputAllocation.destroy()
+ outAllocation.destroy()
+ inputArrayType.destroy()
+ outputArrayType.destroy()
+ scriptColorMatrix.destroy()
+ return intrinsicOutArray
+}
+
+fun intrinsicColorMatrix(
+ context: RenderScript,
+ conversion: Tester.ColorMatrixConversionType,
+ bitmap: Bitmap,
+ matrix: FloatArray,
+ addVector: FloatArray,
+ restriction: Range2d?
+): ByteArray {
+ val scriptColorMatrix = ScriptIntrinsicColorMatrix.create(context)
+ val inputAllocation = Allocation.createFromBitmap(context, bitmap)
+ inputAllocation.copyFrom(bitmap)
+ val outAllocation = Allocation.createTyped(context, inputAllocation.type)
+ val intrinsicOutArray = ByteArray(bitmap.byteCount)
+
+ when (conversion) {
+ Tester.ColorMatrixConversionType.RGB_TO_YUV -> scriptColorMatrix.setRGBtoYUV()
+ Tester.ColorMatrixConversionType.YUV_TO_RGB -> scriptColorMatrix.setYUVtoRGB()
+ Tester.ColorMatrixConversionType.GREYSCALE -> scriptColorMatrix.setGreyscale()
+ Tester.ColorMatrixConversionType.RANDOM -> {
+ val m = Matrix4f()
+ var index = 0
+ // RS is column major
+ for (x in 0..3) {
+ for (y in 0..3) {
+ m.set(x, y, matrix[index++])
+ }
+ }
+ scriptColorMatrix.setColorMatrix(m)
+ }
+ }
+ val vector = Float4(
+ addVector[0],
+ addVector[1],
+ addVector[2],
+ addVector[3]
+ )
+ scriptColorMatrix.setAdd(vector)
+ if (restriction != null) {
+ outAllocation.copyFrom(intrinsicOutArray) // To initialize to zero
+ val options = Script.LaunchOptions()
+ options.setX(restriction.startX, restriction.endX)
+ options.setY(restriction.startY, restriction.endY)
+ scriptColorMatrix.forEach(inputAllocation, outAllocation, options)
+ } else {
+ scriptColorMatrix.forEach(inputAllocation, outAllocation)
+ }
+ outAllocation.copyTo(intrinsicOutArray)
+
+ inputAllocation.destroy()
+ outAllocation.destroy()
+ scriptColorMatrix.destroy()
+ return intrinsicOutArray
+}
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicConvolve.kt b/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicConvolve.kt
new file mode 100644
index 0000000..6b8fecb
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicConvolve.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import android.graphics.Bitmap
+import android.renderscript.Allocation
+import android.renderscript.Element
+import android.renderscript.RenderScript
+import android.renderscript.Script
+import android.renderscript.ScriptIntrinsicConvolve3x3
+import android.renderscript.ScriptIntrinsicConvolve5x5
+import android.renderscript.Type
+import com.google.android.renderscript.Range2d
+
+/**
+ * Does a Convolve operation using the RenderScript Intrinsics.
+ */
+fun intrinsicConvolve(
+ context: RenderScript,
+ inputArray: ByteArray,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ coefficients: FloatArray,
+ restriction: Range2d?
+): ByteArray {
+ val baseElement = renderScriptVectorElementForU8(context, vectorSize)
+ val builder = Type.Builder(context, baseElement)
+ builder.setX(sizeX)
+ builder.setY(sizeY)
+ val arrayType = builder.create()
+ val inputAllocation = Allocation.createTyped(context, arrayType)
+ val outAllocation = Allocation.createTyped(context, arrayType)
+ inputAllocation.copyFrom(inputArray)
+ val intrinsicOutArray = ByteArray(sizeX * sizeY * paddedSize(vectorSize))
+ if (restriction != null) {
+ outAllocation.copyFrom(intrinsicOutArray) // To initialize to zero
+ }
+ invokeConvolveKernel(
+ coefficients,
+ context,
+ baseElement,
+ inputAllocation,
+ restriction,
+ outAllocation
+ )
+ outAllocation.copyTo(intrinsicOutArray)
+ inputAllocation.destroy()
+ outAllocation.destroy()
+ arrayType.destroy()
+ return intrinsicOutArray
+}
+
+fun intrinsicConvolve(
+ context: RenderScript,
+ bitmap: Bitmap,
+ coefficients: FloatArray,
+ restriction: Range2d?
+): ByteArray {
+ val baseElement = renderScriptElementForBitmap(context, bitmap)
+
+ val inputAllocation = Allocation.createFromBitmap(context, bitmap)
+ val outAllocation = Allocation.createTyped(context, inputAllocation.type)
+ val intrinsicOutArray = ByteArray(bitmap.byteCount)
+ inputAllocation.copyFrom(bitmap)
+ if (restriction != null) {
+ outAllocation.copyFrom(intrinsicOutArray) // To initialize to zero
+ }
+ invokeConvolveKernel(
+ coefficients,
+ context,
+ baseElement,
+ inputAllocation,
+ restriction,
+ outAllocation
+ )
+ outAllocation.copyTo(intrinsicOutArray)
+ inputAllocation.destroy()
+ outAllocation.destroy()
+ return intrinsicOutArray
+}
+
+private fun invokeConvolveKernel(
+ coefficients: FloatArray,
+ context: RenderScript,
+ baseElement: Element,
+ inputAllocation: Allocation?,
+ restriction: Range2d?,
+ outAllocation: Allocation?
+) {
+ when (coefficients.size) {
+ 9 -> {
+ val scriptConvolve3x3 =
+ ScriptIntrinsicConvolve3x3.create(context, baseElement)
+ scriptConvolve3x3.setCoefficients(coefficients)
+ scriptConvolve3x3.setInput(inputAllocation)
+ if (restriction != null) {
+ val options = Script.LaunchOptions()
+ options.setX(restriction.startX, restriction.endX)
+ options.setY(restriction.startY, restriction.endY)
+ scriptConvolve3x3.forEach(outAllocation, options)
+ } else {
+ scriptConvolve3x3.forEach(outAllocation)
+ }
+ scriptConvolve3x3.destroy()
+ }
+ 25 -> {
+ val scriptConvolve5x5 =
+ ScriptIntrinsicConvolve5x5.create(context, baseElement)
+ scriptConvolve5x5.setCoefficients(coefficients)
+ scriptConvolve5x5.setInput(inputAllocation)
+ if (restriction != null) {
+ val options = Script.LaunchOptions()
+ options.setX(restriction.startX, restriction.endX)
+ options.setY(restriction.startY, restriction.endY)
+ scriptConvolve5x5.forEach(outAllocation, options)
+ } else {
+ scriptConvolve5x5.forEach(outAllocation)
+ }
+ scriptConvolve5x5.destroy()
+ }
+ else -> {
+ throw IllegalArgumentException("RenderScriptToolkit tests. Only 3x3 and 5x5 convolutions are supported. ${coefficients.size} coefficients provided.")
+ }
+ }
+}
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicHistogram.kt b/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicHistogram.kt
new file mode 100644
index 0000000..31a27d9
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicHistogram.kt
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import android.graphics.Bitmap
+import android.renderscript.Allocation
+import android.renderscript.Element
+import android.renderscript.RenderScript
+import android.renderscript.Script
+import android.renderscript.ScriptIntrinsicHistogram
+import android.renderscript.Type
+import com.google.android.renderscript.Range2d
+
+/**
+ * Does a Histogram operation using the RenderScript Intrinsics.
+ */
+fun intrinsicHistogram(
+ context: RenderScript,
+ inputArray: ByteArray,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ restriction: Range2d?
+): IntArray {
+ val element = renderScriptVectorElementForU8(context, vectorSize)
+ val scriptHistogram = ScriptIntrinsicHistogram.create(context, element)
+ val builder = Type.Builder(context, element)
+ builder.setX(sizeX)
+ builder.setY(sizeY)
+ val arrayType = builder.create()
+ val inputAllocation = Allocation.createTyped(context, arrayType)
+ val outAllocation =
+ Allocation.createSized(
+ context,
+ renderScriptVectorElementForI32(context, vectorSize),
+ 256
+ )
+ inputAllocation.copyFrom(inputArray)
+ scriptHistogram.setOutput(outAllocation)
+ if (restriction != null) {
+ val options = Script.LaunchOptions()
+ options.setX(restriction.startX, restriction.endX)
+ options.setY(restriction.startY, restriction.endY)
+ scriptHistogram.forEach(inputAllocation, options)
+ } else {
+ scriptHistogram.forEach(inputAllocation)
+ }
+
+ val intrinsicOutArray = IntArray(256 * paddedSize(vectorSize))
+ outAllocation.copyTo(intrinsicOutArray)
+ inputAllocation.destroy()
+ outAllocation.destroy()
+ arrayType.destroy()
+ scriptHistogram.destroy()
+ return intrinsicOutArray
+}
+
+fun intrinsicHistogram(
+ context: RenderScript,
+ bitmap: Bitmap,
+ restriction: Range2d?
+): IntArray {
+ val baseElement = renderScriptElementForBitmap(context, bitmap)
+ val scriptHistogram = ScriptIntrinsicHistogram.create(context, baseElement)
+ val inputAllocation = Allocation.createFromBitmap(context, bitmap)
+ inputAllocation.copyFrom(bitmap)
+ val vectorSize = vectorSizeOfBitmap(bitmap)
+ val outAllocation =
+ Allocation.createSized(
+ context,
+ renderScriptVectorElementForI32(context, vectorSize),
+ 256
+ )
+ scriptHistogram.setOutput(outAllocation)
+ if (restriction != null) {
+ val options = Script.LaunchOptions()
+ options.setX(restriction.startX, restriction.endX)
+ options.setY(restriction.startY, restriction.endY)
+ scriptHistogram.forEach(inputAllocation, options)
+ } else {
+ scriptHistogram.forEach(inputAllocation)
+ }
+
+ val intrinsicOutArray = IntArray(256 * vectorSize)
+ outAllocation.copyTo(intrinsicOutArray)
+ inputAllocation.destroy()
+ outAllocation.destroy()
+ scriptHistogram.destroy()
+ return intrinsicOutArray
+}
+
+fun intrinsicHistogramDot(
+ context: RenderScript,
+ inputArray: ByteArray,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ coefficients: FloatArray?,
+ restriction: Range2d?
+): IntArray {
+ val element = renderScriptVectorElementForU8(context, vectorSize)
+ val scriptHistogram = ScriptIntrinsicHistogram.create(context, element)
+ val builder = Type.Builder(context, element)
+ builder.setX(sizeX)
+ builder.setY(sizeY)
+ val arrayType = builder.create()
+ val inputAllocation = Allocation.createTyped(context, arrayType)
+ val outAllocation =
+ Allocation.createSized(context, Element.I32(context), 256)
+ inputAllocation.copyFrom(inputArray)
+
+ if (coefficients != null) {
+ require(coefficients.size == vectorSize) {
+ "RenderScriptToolkit tests. $vectorSize coefficients are required for histogram. " +
+ "${coefficients.size} provided."
+ }
+ scriptHistogram.setDotCoefficients(
+ coefficients[0],
+ if (vectorSize > 1) coefficients[1] else 0f,
+ if (vectorSize > 2) coefficients[2] else 0f,
+ if (vectorSize > 3) coefficients[3] else 0f
+ )
+ }
+ scriptHistogram.setOutput(outAllocation)
+ if (restriction != null) {
+ val options = Script.LaunchOptions()
+ options.setX(restriction.startX, restriction.endX)
+ options.setY(restriction.startY, restriction.endY)
+ scriptHistogram.forEach_Dot(inputAllocation, options)
+ } else {
+ scriptHistogram.forEach_Dot(inputAllocation)
+ }
+ val intrinsicOutArray = IntArray(256)
+ outAllocation.copyTo(intrinsicOutArray)
+ inputAllocation.destroy()
+ outAllocation.destroy()
+ arrayType.destroy()
+ scriptHistogram.destroy()
+ return intrinsicOutArray
+}
+
+fun intrinsicHistogramDot(
+ context: RenderScript,
+ bitmap: Bitmap,
+ coefficients: FloatArray?,
+ restriction: Range2d?
+): IntArray {
+ val baseElement = renderScriptElementForBitmap(context, bitmap)
+ val scriptHistogram = ScriptIntrinsicHistogram.create(context, baseElement)
+ val inputAllocation = Allocation.createFromBitmap(context, bitmap)
+ inputAllocation.copyFrom(bitmap)
+ val outAllocation =
+ Allocation.createSized(context, Element.I32(context), 256)
+
+ if (coefficients != null) {
+ require(coefficients.size == 4) {
+ "RenderScriptToolkit tests. Four coefficients are required for histogram. " +
+ "${coefficients.size} provided."
+ }
+ scriptHistogram.setDotCoefficients(
+ coefficients[0],
+ coefficients[1],
+ coefficients[2],
+ coefficients[3]
+ )
+ }
+ scriptHistogram.setOutput(outAllocation)
+ if (restriction != null) {
+ val options = Script.LaunchOptions()
+ options.setX(restriction.startX, restriction.endX)
+ options.setY(restriction.startY, restriction.endY)
+ scriptHistogram.forEach_Dot(inputAllocation, options)
+ } else {
+ scriptHistogram.forEach_Dot(inputAllocation)
+ }
+ val intrinsicOutArray = IntArray(256)
+ outAllocation.copyTo(intrinsicOutArray)
+ inputAllocation.destroy()
+ outAllocation.destroy()
+ scriptHistogram.destroy()
+ return intrinsicOutArray
+}
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicLut.kt b/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicLut.kt
new file mode 100644
index 0000000..108eb87
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicLut.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import android.graphics.Bitmap
+import android.renderscript.Allocation
+import android.renderscript.Element
+import android.renderscript.RenderScript
+import android.renderscript.Script
+import android.renderscript.ScriptIntrinsicLUT
+import android.renderscript.Type
+import com.google.android.renderscript.Range2d
+
+/**
+ * Does a LookUpTable operation using the RenderScript Intrinsics.
+ */
+@ExperimentalUnsignedTypes
+fun intrinsicLut(
+ context: RenderScript,
+ inputArray: ByteArray,
+ sizeX: Int,
+ sizeY: Int,
+ newRed: ByteArray,
+ newGreen: ByteArray,
+ newBlue: ByteArray,
+ newAlpha: ByteArray,
+ restriction: Range2d?
+): ByteArray {
+ val scriptLut: ScriptIntrinsicLUT = ScriptIntrinsicLUT.create(
+ context,
+ Element.U8_4(context)
+ )
+ val builder = Type.Builder(context, Element.U8_4(context))
+ builder.setX(sizeX)
+ builder.setY(sizeY)
+ val arrayType = builder.create()
+ val inputAllocation = Allocation.createTyped(context, arrayType)
+ val outAllocation = Allocation.createTyped(context, arrayType)
+ inputAllocation.copyFrom(inputArray)
+ val intrinsicOutArray = ByteArray(sizeX * sizeY * 4)
+
+ for (v in 0..255) {
+ scriptLut.setRed(v, newRed[v].toUByte().toInt())
+ scriptLut.setGreen(v, newGreen[v].toUByte().toInt())
+ scriptLut.setBlue(v, newBlue[v].toUByte().toInt())
+ scriptLut.setAlpha(v, newAlpha[v].toUByte().toInt())
+ }
+ if (restriction != null) {
+ outAllocation.copyFrom(intrinsicOutArray) // To initialize to zero
+ val options = Script.LaunchOptions()
+ options.setX(restriction.startX, restriction.endX)
+ options.setY(restriction.startY, restriction.endY)
+ scriptLut.forEach(inputAllocation, outAllocation, options)
+ } else {
+ scriptLut.forEach(inputAllocation, outAllocation)
+ }
+
+ outAllocation.copyTo(intrinsicOutArray)
+ inputAllocation.destroy()
+ outAllocation.destroy()
+ arrayType.destroy()
+ scriptLut.destroy()
+ return intrinsicOutArray
+}
+
+@ExperimentalUnsignedTypes
+fun intrinsicLut(
+ context: RenderScript,
+ bitmap: Bitmap,
+ newRed: ByteArray,
+ newGreen: ByteArray,
+ newBlue: ByteArray,
+ newAlpha: ByteArray,
+ restriction: Range2d?
+): ByteArray {
+ val baseElement = renderScriptElementForBitmap(context, bitmap)
+ val scriptLut: ScriptIntrinsicLUT = ScriptIntrinsicLUT.create(context, baseElement)
+ val inputAllocation = Allocation.createFromBitmap(context, bitmap)
+ inputAllocation.copyFrom(bitmap)
+ val outAllocation = Allocation.createTyped(context, inputAllocation.type)
+ val intrinsicOutArray = ByteArray(bitmap.byteCount)
+
+ for (v in 0..255) {
+ scriptLut.setRed(v, newRed[v].toUByte().toInt())
+ scriptLut.setGreen(v, newGreen[v].toUByte().toInt())
+ scriptLut.setBlue(v, newBlue[v].toUByte().toInt())
+ scriptLut.setAlpha(v, newAlpha[v].toUByte().toInt())
+ }
+ if (restriction != null) {
+ outAllocation.copyFrom(intrinsicOutArray) // To initialize to zero
+ val options = Script.LaunchOptions()
+ options.setX(restriction.startX, restriction.endX)
+ options.setY(restriction.startY, restriction.endY)
+ scriptLut.forEach(inputAllocation, outAllocation, options)
+ } else {
+ scriptLut.forEach(inputAllocation, outAllocation)
+ }
+
+ outAllocation.copyTo(intrinsicOutArray)
+ inputAllocation.destroy()
+ outAllocation.destroy()
+ scriptLut.destroy()
+ return intrinsicOutArray
+}
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicLut3d.kt b/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicLut3d.kt
new file mode 100644
index 0000000..c9f08f6
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicLut3d.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import android.graphics.Bitmap
+import android.renderscript.Allocation
+import android.renderscript.Element
+import android.renderscript.RenderScript
+import android.renderscript.Script
+import android.renderscript.ScriptIntrinsic3DLUT
+import android.renderscript.Type
+import com.google.android.renderscript.Range2d
+
+/**
+ * Does a 3D LookUpTable operation using the RenderScript Intrinsics.
+ */
+fun intrinsicLut3d(
+ context: RenderScript,
+ inputArray: ByteArray,
+ sizeX: Int,
+ sizeY: Int,
+ cubeArray: ByteArray,
+ cubeSize: Dimension,
+ restriction: Range2d?
+): ByteArray {
+ val scriptLut3d: ScriptIntrinsic3DLUT = ScriptIntrinsic3DLUT.create(
+ context, Element.U8_4(
+ context
+ )
+ )
+ val builder = Type.Builder(context, Element.U8_4(context))
+ builder.setX(sizeX)
+ builder.setY(sizeY)
+ val arrayType = builder.create()
+ val inputAllocation = Allocation.createTyped(context, arrayType)
+ val outAllocation = Allocation.createTyped(context, arrayType)
+ inputAllocation.copyFrom(inputArray)
+ val intrinsicOutArray = ByteArray(sizeX * sizeY * 4)
+
+ val cubeTypeBuilder: Type.Builder =
+ Type.Builder(context, Element.U8_4(context))
+ cubeTypeBuilder.setX(cubeSize.sizeX)
+ cubeTypeBuilder.setY(cubeSize.sizeY)
+ cubeTypeBuilder.setZ(cubeSize.sizeZ)
+ val cubeType: Type = cubeTypeBuilder.create()
+ val cubeAllocation = Allocation.createTyped(context, cubeType)
+ cubeAllocation.copyFrom(cubeArray)
+ scriptLut3d.setLUT(cubeAllocation)
+ if (restriction != null) {
+ outAllocation.copyFrom(intrinsicOutArray) // To initialize to zero
+ val options = Script.LaunchOptions()
+ options.setX(restriction.startX, restriction.endX)
+ options.setY(restriction.startY, restriction.endY)
+ scriptLut3d.forEach(inputAllocation, outAllocation, options)
+ } else {
+ scriptLut3d.forEach(inputAllocation, outAllocation)
+ }
+
+ outAllocation.copyTo(intrinsicOutArray)
+ inputAllocation.destroy()
+ outAllocation.destroy()
+ cubeAllocation.destroy()
+ arrayType.destroy()
+ cubeType.destroy()
+ scriptLut3d.destroy()
+ return intrinsicOutArray
+}
+
+fun intrinsicLut3d(
+ context: RenderScript,
+ bitmap: Bitmap,
+ cubeArray: ByteArray,
+ cubeSize: Dimension,
+ restriction: Range2d?
+): ByteArray {
+ val baseElement = renderScriptElementForBitmap(context, bitmap)
+ val scriptLut3d: ScriptIntrinsic3DLUT = ScriptIntrinsic3DLUT.create(context, baseElement)
+ val inputAllocation = Allocation.createFromBitmap(context, bitmap)
+ inputAllocation.copyFrom(bitmap)
+ val outAllocation = Allocation.createTyped(context, inputAllocation.type)
+ val intrinsicOutArray = ByteArray(bitmap.byteCount)
+
+ val cubeTypeBuilder: Type.Builder =
+ Type.Builder(context, Element.U8_4(context))
+ cubeTypeBuilder.setX(cubeSize.sizeX)
+ cubeTypeBuilder.setY(cubeSize.sizeY)
+ cubeTypeBuilder.setZ(cubeSize.sizeZ)
+ val cubeType: Type = cubeTypeBuilder.create()
+ val cubeAllocation = Allocation.createTyped(context, cubeType)
+ cubeAllocation.copyFrom(cubeArray)
+ scriptLut3d.setLUT(cubeAllocation)
+ if (restriction != null) {
+ outAllocation.copyFrom(intrinsicOutArray) // To initialize to zero
+ val options = Script.LaunchOptions()
+ options.setX(restriction.startX, restriction.endX)
+ options.setY(restriction.startY, restriction.endY)
+ scriptLut3d.forEach(inputAllocation, outAllocation, options)
+ } else {
+ scriptLut3d.forEach(inputAllocation, outAllocation)
+ }
+
+ outAllocation.copyTo(intrinsicOutArray)
+ inputAllocation.destroy()
+ outAllocation.destroy()
+ cubeAllocation.destroy()
+ cubeType.destroy()
+ scriptLut3d.destroy()
+ return intrinsicOutArray
+}
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicResize.kt b/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicResize.kt
new file mode 100644
index 0000000..647efdd
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicResize.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import android.graphics.Bitmap
+import android.renderscript.Allocation
+import android.renderscript.RenderScript
+import android.renderscript.Script
+import android.renderscript.ScriptIntrinsicResize
+import android.renderscript.Type
+import com.google.android.renderscript.Range2d
+
+/**
+ * Does a Resize operation using the RenderScript Intrinsics.
+ */
+fun intrinsicResize(
+ context: RenderScript,
+ inputArray: ByteArray,
+ vectorSize: Int,
+ inSizeX: Int,
+ inSizeY: Int,
+ outSizeX: Int,
+ outSizeY: Int,
+ restriction: Range2d?
+): ByteArray {
+ val scriptResize = ScriptIntrinsicResize.create(context)
+ val builder = Type.Builder(
+ context,
+ renderScriptVectorElementForU8(context, vectorSize)
+ )
+ builder.setX(inSizeX)
+ builder.setY(inSizeY)
+ val inputArrayType = builder.create()
+ val inputAllocation = Allocation.createTyped(context, inputArrayType)
+ builder.setX(outSizeX)
+ builder.setY(outSizeY)
+ val outputArrayType = builder.create()
+ val outAllocation = Allocation.createTyped(context, outputArrayType)
+ val intrinsicOutArray = ByteArray(outSizeX * outSizeY * paddedSize(vectorSize))
+
+ inputAllocation.copyFrom(inputArray)
+ scriptResize.setInput(inputAllocation)
+ if (restriction != null) {
+ outAllocation.copyFrom(intrinsicOutArray) // To initialize to zero
+ val options = Script.LaunchOptions()
+ options.setX(restriction.startX, restriction.endX)
+ options.setY(restriction.startY, restriction.endY)
+ scriptResize.forEach_bicubic(outAllocation, options)
+ } else {
+ scriptResize.forEach_bicubic(outAllocation)
+ }
+ outAllocation.copyTo(intrinsicOutArray)
+
+ inputAllocation.destroy()
+ outAllocation.destroy()
+ scriptResize.destroy()
+ inputArrayType.destroy()
+ outputArrayType.destroy()
+ return intrinsicOutArray
+}
+
+fun intrinsicResize(
+ context: RenderScript,
+ bitmap: Bitmap,
+ outSizeX: Int,
+ outSizeY: Int,
+ restriction: Range2d?
+): ByteArray {
+ val scriptResize = ScriptIntrinsicResize.create(context)
+ val inputAllocation = Allocation.createFromBitmap(context, bitmap)
+ inputAllocation.copyFrom(bitmap)
+
+ val vectorSize = when (bitmap.config) {
+ Bitmap.Config.ARGB_8888 -> 4
+ Bitmap.Config.ALPHA_8 -> 1
+ else -> error("Unrecognized bitmap config $bitmap.config")
+ }
+ val builder = Type.Builder(
+ context,
+ renderScriptVectorElementForU8(context, vectorSize)
+ )
+ builder.setX(outSizeX)
+ builder.setY(outSizeY)
+ val outputArrayType = builder.create()
+ val outAllocation = Allocation.createTyped(context, outputArrayType)
+ val intrinsicOutArray = ByteArray(outSizeX * outSizeY * vectorSize)
+
+ scriptResize.setInput(inputAllocation)
+ if (restriction != null) {
+ outAllocation.copyFrom(intrinsicOutArray) // To initialize to zero
+ val options = Script.LaunchOptions()
+ options.setX(restriction.startX, restriction.endX)
+ options.setY(restriction.startY, restriction.endY)
+ scriptResize.forEach_bicubic(outAllocation, options)
+ } else {
+ scriptResize.forEach_bicubic(outAllocation)
+ }
+ outAllocation.copyTo(intrinsicOutArray)
+
+ inputAllocation.destroy()
+ outAllocation.destroy()
+ outputArrayType.destroy()
+ scriptResize.destroy()
+ return intrinsicOutArray
+}
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicYuvToRgb.kt b/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicYuvToRgb.kt
new file mode 100644
index 0000000..2ae8a89
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/IntrinsicYuvToRgb.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import android.graphics.ImageFormat
+import android.renderscript.Allocation
+import android.renderscript.Element
+import android.renderscript.RenderScript
+import android.renderscript.ScriptIntrinsicYuvToRGB
+import android.renderscript.Type
+import com.google.android.renderscript.YuvFormat
+
+/**
+ * Does a YUV to RGB operation using the RenderScript Intrinsics.
+ */
+fun intrinsicYuvToRgb(
+ context: RenderScript,
+ inputArray: ByteArray,
+ sizeX: Int,
+ sizeY: Int,
+ format: YuvFormat
+): ByteArray {
+ val scriptYuvToRgb = ScriptIntrinsicYuvToRGB.create(
+ context,
+ Element.YUV(context)
+ )
+ val inputBuilder = Type.Builder(context, Element.YUV(context))
+ inputBuilder.setX(sizeX)
+ inputBuilder.setY(sizeY)
+ when (format) {
+ YuvFormat.NV21 -> inputBuilder.setYuvFormat(ImageFormat.NV21)
+ YuvFormat.YV12 -> inputBuilder.setYuvFormat(ImageFormat.YV12)
+ else -> require(false) { "Unknown YUV format $format" }
+ }
+ val inputArrayType = inputBuilder.create()
+ val inputAllocation = Allocation.createTyped(context, inputArrayType)
+
+ val outputBuilder = Type.Builder(context, Element.U8_4(context))
+ outputBuilder.setX(sizeX)
+ outputBuilder.setY(sizeY)
+ val outputArrayType = outputBuilder.create()
+ val outAllocation = Allocation.createTyped(context, outputArrayType)
+ val intrinsicOutArray = ByteArray(sizeX * sizeY * 4)
+
+ inputAllocation.copyFrom(inputArray)
+ scriptYuvToRgb.setInput(inputAllocation)
+ scriptYuvToRgb.forEach(outAllocation)
+ outAllocation.copyTo(intrinsicOutArray)
+
+ inputAllocation.destroy()
+ outAllocation.destroy()
+ inputArrayType.destroy()
+ outputArrayType.destroy()
+ scriptYuvToRgb.destroy()
+ return intrinsicOutArray
+}
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/MainActivity.kt b/test-app/src/main/java/com/google/android/renderscript_test/MainActivity.kt
new file mode 100644
index 0000000..10ea5f7
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/MainActivity.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+
+
+@ExperimentalUnsignedTypes
+class MainActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+
+ // To debug resources not destroyed
+ // "A resource failed to call destroy."
+ try {
+ Class.forName("dalvik.system.CloseGuard")
+ .getMethod("setEnabled", Boolean::class.javaPrimitiveType)
+ .invoke(null, true)
+ } catch (e: ReflectiveOperationException) {
+ throw RuntimeException(e)
+ }
+ }
+}
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/ReferenceBlend.kt b/test-app/src/main/java/com/google/android/renderscript_test/ReferenceBlend.kt
new file mode 100644
index 0000000..1d9b1fd
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/ReferenceBlend.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import com.google.android.renderscript.BlendingMode
+import com.google.android.renderscript.Range2d
+
+/**
+ * Reference implementation of a Blend operation.
+ *
+ * See the class Rgba for details of arithmetic operation using that class.
+ */
+@ExperimentalUnsignedTypes
+fun referenceBlend(
+ mode: BlendingMode,
+ sourceArray: ByteArray,
+ destArray: ByteArray,
+ sizeX: Int,
+ sizeY: Int,
+ restriction: Range2d?
+) {
+ val source = Rgba2dArray(sourceArray, sizeX, sizeY)
+ val dest = Rgba2dArray(destArray, sizeX, sizeY)
+
+ /**
+ * For each corresponding RGBA value of the source and destination arrays, invoke the blend
+ * function and store the result in the destination array.
+ */
+ fun blendEachPair(blendFunction: (src: Rgba, dst: Rgba) -> Rgba) {
+ dest.forEachCell(restriction) { x, y ->
+ dest[x, y] = blendFunction(source[x, y], dest[x, y])
+ }
+ }
+
+ when (mode) {
+ BlendingMode.CLEAR -> blendEachPair { _, _ -> Rgba(0, 0, 0, 0) }
+ BlendingMode.SRC -> blendEachPair { src, _ -> src }
+ BlendingMode.DST -> { /* This doesn't do anything. */ }
+ BlendingMode.SRC_OVER -> blendEachPair { src, dst -> blendOver(src, dst) }
+ BlendingMode.DST_OVER -> blendEachPair { src, dst -> blendOver(dst, src) }
+ BlendingMode.SRC_IN -> blendEachPair { src, dst -> blendIn(src, dst) }
+ BlendingMode.DST_IN -> blendEachPair { src, dst -> blendIn(dst, src) }
+ BlendingMode.SRC_OUT -> blendEachPair { src, dst -> blendOut(src, dst) }
+ BlendingMode.DST_OUT -> blendEachPair { src, dst -> blendOut(dst, src) }
+ BlendingMode.SRC_ATOP -> blendEachPair { src, dst -> blendAtop(src, dst) }
+ BlendingMode.DST_ATOP -> blendEachPair { src, dst -> blendAtop(dst, src) }
+ BlendingMode.XOR -> blendEachPair { src, dst -> src xor dst }
+ BlendingMode.MULTIPLY -> blendEachPair { src, dst -> src * dst }
+ BlendingMode.ADD -> blendEachPair { src, dst -> dst + src }
+ BlendingMode.SUBTRACT -> blendEachPair { src, dst -> dst - src }
+ }
+}
+
+@ExperimentalUnsignedTypes
+private fun blendOver(src: Rgba, dst: Rgba) = src + (dst * (255 - src.a))
+
+@ExperimentalUnsignedTypes
+private fun blendIn(src: Rgba, dst: Rgba) = src * dst.a
+
+@ExperimentalUnsignedTypes
+private fun blendOut(src: Rgba, dst: Rgba) = src * (255 - dst.a)
+
+@ExperimentalUnsignedTypes
+private fun blendAtop(src: Rgba, dst: Rgba): Rgba {
+ val value = src * dst.a + dst * (255 - src.a)
+ value.a = dst.a
+ return value
+}
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/ReferenceBlur.kt b/test-app/src/main/java/com/google/android/renderscript_test/ReferenceBlur.kt
new file mode 100644
index 0000000..7b4792d
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/ReferenceBlur.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import com.google.android.renderscript.Range2d
+import kotlin.math.max
+import kotlin.math.min
+import kotlin.math.pow
+import kotlin.math.sqrt
+
+/**
+ * Reference implementation of a Blur operation.
+ */
+@ExperimentalUnsignedTypes
+fun referenceBlur(inputArray: ByteArray,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ radius: Int = 5, restriction: Range2d?): ByteArray {
+ val maxRadius = 25
+ require (radius in 1..maxRadius) {
+ "RenderScriptToolkit blur. Radius should be between 1 and $maxRadius. $radius provided."
+ }
+ val gaussian = buildGaussian(radius)
+
+ // Convert input data to float so that the blurring goes faster.
+ val inputValues = FloatArray(inputArray.size) { byteToUnitFloat(inputArray[it].toUByte()) }
+ val inputInFloat = FloatVector2dArray(inputValues, vectorSize, sizeX, sizeY)
+
+ val scratch = horizontalBlur(inputInFloat, gaussian, radius, restriction)
+ val outInFloat = verticalBlur(scratch, gaussian, radius, restriction)
+
+ // Convert the results back to bytes.
+ return ByteArray(outInFloat.values.size) { unitFloatClampedToUByte(outInFloat.values[it]).toByte() }
+}
+
+/**
+ * Blurs along the horizontal direction using the specified gaussian weights.
+ */
+private fun horizontalBlur(
+ input: FloatVector2dArray,
+ gaussian: FloatArray,
+ radius: Int,
+ restriction: Range2d?
+): FloatVector2dArray {
+ var expandedRestriction: Range2d? = null
+ if (restriction != null) {
+ // Expand the restriction in the vertical direction so that the vertical pass
+ // will have all the data it needs.
+ expandedRestriction = Range2d(
+ restriction.startX,
+ restriction.endX,
+ max(restriction.startY - radius, 0),
+ min(restriction.endY + radius, input.sizeY)
+ )
+ }
+
+ input.clipAccessToRange = true
+ val out = input.createSameSized()
+ out.forEach(expandedRestriction) { x, y ->
+ for ((gaussianIndex, delta: Int) in (-radius..radius).withIndex()) {
+ val v = input[x + delta, y] * gaussian[gaussianIndex]
+ out[x, y] += v
+ }
+ }
+ return out
+}
+
+/**
+ * Blurs along the horizontal direction using the specified gaussian weights.
+ */
+private fun verticalBlur(
+ input: FloatVector2dArray,
+ gaussian: FloatArray,
+ radius: Int,
+ restriction: Range2d?
+): FloatVector2dArray {
+ input.clipAccessToRange = true
+ val out = input.createSameSized()
+ out.forEach(restriction) { x, y ->
+ for ((gaussianIndex, delta: Int) in (-radius..radius).withIndex()) {
+ val v = input[x, y + delta] * gaussian[gaussianIndex]
+ out[x, y] += v
+ }
+ }
+ return out
+}
+
+/**
+ * Builds an array of gaussian weights that will be used for doing the horizontal and vertical
+ * blur.
+ *
+ * @return An array of (2 * radius + 1) floats.
+ */
+private fun buildGaussian(radius: Int): FloatArray {
+ val e: Float = kotlin.math.E.toFloat()
+ val pi: Float = kotlin.math.PI.toFloat()
+ val sigma: Float = 0.4f * radius.toFloat() + 0.6f
+ val coefficient1: Float = 1.0f / (sqrt(2.0f * pi) * sigma)
+ val coefficient2: Float = -1.0f / (2.0f * sigma * sigma)
+
+ var sum = 0.0f
+ val gaussian = FloatArray(radius * 2 + 1)
+ for (r in -radius..radius) {
+ val floatR: Float = r.toFloat()
+ val v: Float = coefficient1 * e.pow(floatR * floatR * coefficient2)
+ gaussian[r + radius] = v
+ sum += v
+ }
+
+ // Normalize so that the sum of the weights equal 1f.
+ val normalizeFactor: Float = 1.0f / sum
+ for (r in -radius..radius) {
+ gaussian[r + radius] *= normalizeFactor
+ }
+ return gaussian
+}
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/ReferenceColorMatrix.kt b/test-app/src/main/java/com/google/android/renderscript_test/ReferenceColorMatrix.kt
new file mode 100644
index 0000000..0800873
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/ReferenceColorMatrix.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import com.google.android.renderscript.Range2d
+
+/**
+ * Reference implementation of a ColorMatrix operation.
+ */
+@ExperimentalUnsignedTypes
+fun referenceColorMatrix(inputArray: ByteArray,
+ inputVectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ outputVectorSize: Int,
+ matrix: FloatArray, addVector: FloatArray,
+ restriction: Range2d?): ByteArray {
+ require (matrix.size == 16) { "RenderScriptToolkit colorMatrix. Matrix should have 16 values. ${matrix.size} provided." }
+
+ val input = Vector2dArray(inputArray.asUByteArray(), inputVectorSize, sizeX, sizeY)
+ val outputArray = ByteArray(sizeX * sizeY * paddedSize(outputVectorSize))
+ val output = Vector2dArray(outputArray.asUByteArray(), outputVectorSize, sizeX, sizeY)
+
+ output.forEach (restriction) { x, y ->
+ val inUByteValue = input[x, y]
+ val inFloatValue = FloatArray(4) { if (it >= inputVectorSize) 0f else byteToUnitFloat(inUByteValue[it]) }
+ val outFloatValue = multiplyAndAdd(matrix, inFloatValue, addVector)
+ val outUByteValue = UByteArray(paddedSize(output.vectorSize)) { unitFloatClampedToUByte(outFloatValue[it]) }
+ output[x, y] = outUByteValue
+ }
+ return outputArray
+}
+
+private fun multiplyAndAdd(matrix: FloatArray, inVector: FloatArray, addVector: FloatArray): FloatArray {
+ // In RenderScript, matrix were set in column major format
+ val result = addVector.clone()
+ for (i in 0..3) {
+ for (j in 0..3) {
+ result[i] += matrix[j * 4 + i] * inVector[j]
+ }
+ }
+ return result
+}
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/ReferenceConvolve.kt b/test-app/src/main/java/com/google/android/renderscript_test/ReferenceConvolve.kt
new file mode 100644
index 0000000..4c87099
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/ReferenceConvolve.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import com.google.android.renderscript.Range2d
+
+/**
+ * Reference implementation of a Convolve operation.
+ */
+@ExperimentalUnsignedTypes
+fun referenceConvolve(
+ inputArray: ByteArray,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ coefficients: FloatArray,
+ restriction: Range2d?
+): ByteArray {
+ val input = Vector2dArray(inputArray.asUByteArray(), vectorSize, sizeX, sizeY)
+ val radius = when (coefficients.size) {
+ 9 -> 1
+ 25 -> 2
+ else -> {
+ throw IllegalArgumentException("RenderScriptToolkit Convolve. Only 3x3 and 5x5 convolutions are supported. ${coefficients.size} coefficients provided.")
+ }
+ }
+
+ input.clipReadToRange = true
+ val output = input.createSameSized()
+ input.forEach(restriction) { x, y ->
+ output[x, y] = convolveOne(input, x, y, coefficients, radius)
+ }
+ return output.values.asByteArray()
+}
+
+@ExperimentalUnsignedTypes
+private fun convolveOne(
+ inputAlloc: Vector2dArray,
+ x: Int,
+ y: Int,
+ coefficients: FloatArray,
+ radius: Int
+): UByteArray {
+ var sum = FloatArray(paddedSize(inputAlloc.vectorSize))
+ var coefficientIndex = 0
+ for (deltaY in -radius..radius) {
+ for (deltaX in -radius..radius) {
+ val inputVector = inputAlloc[x + deltaX, y + deltaY]
+ sum += inputVector.toFloatArray() * coefficients[coefficientIndex]
+ coefficientIndex++
+ }
+ }
+ return sum.clampToUByte()
+}
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/ReferenceHistogram.kt b/test-app/src/main/java/com/google/android/renderscript_test/ReferenceHistogram.kt
new file mode 100644
index 0000000..0e26457
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/ReferenceHistogram.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import com.google.android.renderscript.Range2d
+
+/**
+ * Reference implementation of a Histogram operation.
+ *
+ * Return an array of 4 * 256 ints.
+ * Position 0 is the number of R with a value of 0,
+ * Position 1 is the number of G with a value of 0,
+ * Position 2 is the number of B with a value of 0,
+ * Position 3 is the number of A with a value of 0,
+ * Position 4 is the number of R with a value of 1,
+ * etc.
+*/
+@ExperimentalUnsignedTypes
+fun referenceHistogram(
+ inputArray: ByteArray,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ restriction: Range2d?
+): IntArray {
+ val input = Vector2dArray(inputArray.asUByteArray(), vectorSize, sizeX, sizeY)
+
+ val counts = IntArray(paddedSize(input.vectorSize) * 256)
+ input.forEach(restriction) { x, y ->
+ val value = input[x, y]
+ for (i in 0 until vectorSize) {
+ counts[value[i].toInt() * paddedSize(input.vectorSize) + i]++
+ }
+ }
+ return counts
+}
+
+/**
+ * Reference implementation of a HistogramDot operation.
+ *
+ * Each RGBA input value is dot-multiplied first by the specified coefficients.
+ * The resulting value is converted to an integer and used for the histogram.
+ */
+@ExperimentalUnsignedTypes
+fun referenceHistogramDot(
+ inputArray: ByteArray,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ coefficients: FloatArray?,
+ restriction: Range2d?
+): IntArray {
+ val floatCoefficients = coefficients ?: floatArrayOf(0.299f, 0.587f, 0.114f, 0f)
+ val input = Vector2dArray(inputArray.asUByteArray(), vectorSize, sizeX, sizeY)
+ var coefficientSum = 0f
+ for (c in floatCoefficients) {
+ require (c >= 0) {
+ "RenderScriptToolkit histogramDot. Coefficients must be positive. $c provided."
+ }
+ coefficientSum += c
+ }
+ require(coefficientSum <= 1f) { "RenderScriptToolkit histogramDot. Coefficients should " +
+ "add to 1.0 or less. $coefficientSum provided." }
+
+ // Compute integer
+ val intCoefficients = IntArray(input.vectorSize) { (floatCoefficients[it] * 256f + 0.5f).toInt() }
+
+ val counts = IntArray(256)
+ input.forEach(restriction) { x, y ->
+ val value = input[x, y]
+ // While we could do the computation using floats, we won't get the same results as
+ // the existing intrinsics.
+ var sum = 0
+ // We don't use value.indices because we want to accumulate only 3 values, in the case
+ // of vectorSize == 3.
+ for (i in 0 until vectorSize) {
+ sum += intCoefficients[i] * value[i].toInt()
+ }
+ // Round up and normalize
+ val index = (sum + 0x7f) shr 8
+ counts[index]++
+ }
+ return counts
+}
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/ReferenceLut.kt b/test-app/src/main/java/com/google/android/renderscript_test/ReferenceLut.kt
new file mode 100644
index 0000000..3d42739
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/ReferenceLut.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import com.google.android.renderscript.LookupTable
+import com.google.android.renderscript.Range2d
+
+/**
+ * Reference implementation of a LookUpTable operation.
+ */
+@ExperimentalUnsignedTypes
+fun referenceLut(
+ inputArray: ByteArray,
+ sizeX: Int,
+ sizeY: Int,
+ table: LookupTable,
+ restriction: Range2d?
+): ByteArray {
+ val input = Vector2dArray(inputArray.asUByteArray(), 4, sizeX, sizeY)
+
+ val output = input.createSameSized()
+ input.forEach(restriction) { x, y ->
+ val oldValue = input[x, y]
+ val newValue = byteArrayOf(
+ table.red[oldValue[0].toInt()],
+ table.green[oldValue[1].toInt()],
+ table.blue[oldValue[2].toInt()],
+ table.alpha[oldValue[3].toInt()]
+ )
+ output[x, y] = newValue.asUByteArray()
+ }
+ return output.values.asByteArray()
+}
+
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/ReferenceLut3d.kt b/test-app/src/main/java/com/google/android/renderscript_test/ReferenceLut3d.kt
new file mode 100644
index 0000000..529cb77
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/ReferenceLut3d.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import com.google.android.renderscript.Range2d
+import com.google.android.renderscript.Rgba3dArray
+
+/**
+ * Reference implementation of a 3D LookUpTable operation.
+ */
+@ExperimentalUnsignedTypes
+fun referenceLut3d(
+ inputArray: ByteArray,
+ sizeX: Int,
+ sizeY: Int,
+ cube: Rgba3dArray,
+ restriction: Range2d?
+): ByteArray {
+ val input = Vector2dArray(inputArray.asUByteArray(), 4, sizeX, sizeY)
+ val output = input.createSameSized()
+ input.forEach(restriction) { x, y ->
+ output[x, y] = lookup(input[x, y], cube)
+ }
+ return output.values.asByteArray()
+}
+
+@ExperimentalUnsignedTypes
+private fun lookup(input: UByteArray, cube: Rgba3dArray): UByteArray {
+ // Calculate the two points at opposite edges of the size 1
+ // cube that contains our point.
+ val maxIndex = Int4(cube.sizeX - 1, cube.sizeY - 1, cube.sizeZ - 1, 0)
+ val baseCoordinate: Float4 = input.toFloat4() * maxIndex.toFloat4() / 255f
+ val point1: Int4 = baseCoordinate.intFloor()
+ val point2: Int4 = min(point1 + 1, maxIndex)
+ val fractionAwayFromPoint1: Float4 = baseCoordinate - point1.toFloat4()
+
+ // Get the RGBA values at each of the four corners of the size 1 cube.
+ val v000 = cube[point1.x, point1.y, point1.z].toFloat4()
+ val v100 = cube[point2.x, point1.y, point1.z].toFloat4()
+ val v010 = cube[point1.x, point2.y, point1.z].toFloat4()
+ val v110 = cube[point2.x, point2.y, point1.z].toFloat4()
+ val v001 = cube[point1.x, point1.y, point2.z].toFloat4()
+ val v101 = cube[point2.x, point1.y, point2.z].toFloat4()
+ val v011 = cube[point1.x, point2.y, point2.z].toFloat4()
+ val v111 = cube[point2.x, point2.y, point2.z].toFloat4()
+
+ // Do the linear mixing of these eight values.
+ val yz00 = mix(v000, v100, fractionAwayFromPoint1.x)
+ val yz10 = mix(v010, v110, fractionAwayFromPoint1.x)
+ val yz01 = mix(v001, v101, fractionAwayFromPoint1.x)
+ val yz11 = mix(v011, v111, fractionAwayFromPoint1.x)
+
+ val z0 = mix(yz00, yz10, fractionAwayFromPoint1.y)
+ val z1 = mix(yz01, yz11, fractionAwayFromPoint1.y)
+
+ val v = mix(z0, z1, fractionAwayFromPoint1.z)
+
+ // Preserve the alpha of the original value
+ return ubyteArrayOf(v.x.clampToUByte(), v.y.clampToUByte(), v.z.clampToUByte(), input[3])
+}
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/ReferenceResize.kt b/test-app/src/main/java/com/google/android/renderscript_test/ReferenceResize.kt
new file mode 100644
index 0000000..06d3653
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/ReferenceResize.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import com.google.android.renderscript.Range2d
+import kotlin.math.floor
+import kotlin.math.max
+
+var trace = false
+
+/**
+ * Reference implementation of a Resize operation.
+ */
+@ExperimentalUnsignedTypes
+fun referenceResize(inputArray: ByteArray,
+ vectorSize: Int,
+ inSizeX: Int,
+ inSizeY: Int,
+ outSizeX: Int, outSizeY: Int,
+ restriction: Range2d?): ByteArray {
+ val input = Vector2dArray(inputArray.asUByteArray(), vectorSize, inSizeX, inSizeY)
+ val scaleX: Float = input.sizeX.toFloat() / outSizeX.toFloat()
+ val scaleY: Float = input.sizeY.toFloat() / outSizeY.toFloat()
+ val outArray = UByteArray(outSizeX * outSizeY * paddedSize(input.vectorSize))
+ val out = Vector2dArray(outArray, input.vectorSize, outSizeX, outSizeY)
+ out.forEach (restriction) { x, y ->
+ if (x == 1827 && y == 46) {
+ println("Found it")
+ trace = true
+ }
+ val o = bicubicU4(x, y, input, scaleX, scaleY)
+ out[x, y] = o.clampToUByte()
+ }
+ return out.values.asByteArray()
+}
+
+private fun cubicInterpolateF(p0: FloatArray, p1: FloatArray, p2: FloatArray, p3: FloatArray,
+ x: Float): FloatArray {
+ return p1 + (p2 - p0 + (p0 * 2f - p1 * 5f + p2 * 4f - p3
+ + ((p1 - p2) * 3f + p3 - p0) * x) * x) * x * 0.5f
+}
+
+@ExperimentalUnsignedTypes
+private fun bicubicU4(x: Int, y: Int, gIn: Vector2dArray, scaleX: Float, scaleY: Float): FloatArray {
+ var xf: Float = (x + 0.5f) * scaleX - 0.5f
+ var yf: Float = (y + 0.5f) * scaleY - 0.5f
+
+ val startX: Int = floor(xf - 1).toInt()
+ val startY: Int = floor(yf - 1).toInt()
+ xf -= floor(xf)
+ yf -= floor(yf)
+ val maxX: Int = gIn.sizeX - 1
+ val maxY: Int = gIn.sizeY - 1
+
+ val xs0: Int = max(0, startX + 0)
+ val xs1: Int = max(0, startX + 1)
+ val xs2: Int = kotlin.math.min(maxX, startX + 2)
+ val xs3: Int = kotlin.math.min(maxX, startX + 3)
+
+ val ys0: Int = max(0, startY + 0)
+ val ys1: Int = max(0, startY + 1)
+ val ys2: Int = kotlin.math.min(maxY, startY + 2)
+ val ys3: Int = kotlin.math.min(maxY, startY + 3)
+
+ val p00 = gIn[xs0, ys0].toFloatArray()
+ val p01 = gIn[xs1, ys0].toFloatArray()
+ val p02 = gIn[xs2, ys0].toFloatArray()
+ val p03 = gIn[xs3, ys0].toFloatArray()
+ val p0 = cubicInterpolateF(p00, p01, p02, p03, xf)
+
+ val p10 = gIn[xs0, ys1].toFloatArray()
+ val p11 = gIn[xs1, ys1].toFloatArray()
+ val p12 = gIn[xs2, ys1].toFloatArray()
+ val p13 = gIn[xs3, ys1].toFloatArray()
+ val p1 = cubicInterpolateF(p10, p11, p12, p13, xf)
+
+ val p20 = gIn[xs0, ys2].toFloatArray()
+ val p21 = gIn[xs1, ys2].toFloatArray()
+ val p22 = gIn[xs2, ys2].toFloatArray()
+ val p23 = gIn[xs3, ys2].toFloatArray()
+ val p2 = cubicInterpolateF(p20, p21, p22, p23, xf)
+
+ val p30 = gIn[xs0, ys3].toFloatArray()
+ val p31 = gIn[xs1, ys3].toFloatArray()
+ val p32 = gIn[xs2, ys3].toFloatArray()
+ val p33 = gIn[xs3, ys3].toFloatArray()
+ val p3 = cubicInterpolateF(p30, p31, p32, p33, xf)
+
+ return cubicInterpolateF(p0, p1, p2, p3, yf)
+}
+
+
+/* To be used if we implement Floats
+private fun bicubic_F4(x: Int, y: Int, gin: ByteArray, sizeX: Int, sizeY: Int, scaleX: Float, scaleY: Float): Float4 {
+ var xf: Float = (x + 0.5f) * scaleX - 0.5f
+ var yf: Float = (y + 0.5f) * scaleY - 0.5f
+
+ val startX: Int = floor(xf - 1).toInt()
+ val startY: Int = floor(yf - 1).toInt()
+ xf = xf - floor(xf)
+ yf = yf - floor(yf)
+ val maxX: Int = sizeX - 1
+ val maxY: Int = sizeY - 1
+
+ val xs0: Int = max(0, startX + 0)
+ val xs1: Int = max(0, startX + 1)
+ val xs2: Int = min(maxX, startX + 2)
+ val xs3: Int = min(maxX, startX + 3)
+
+ val ys0: Int = max(0, startY + 0)
+ val ys1: Int = max(0, startY + 1)
+ val ys2: Int = min(maxY, startY + 2)
+ val ys3: Int = min(maxY, startY + 3)
+
+ val p00: Float4 = rsGetElementAt_Float4(gIn, xs0, ys0)
+ val p01: Float4 = rsGetElementAt_Float4(gIn, xs1, ys0)
+ val p02: Float4 = rsGetElementAt_Float4(gIn, xs2, ys0)
+ val p03: Float4 = rsGetElementAt_Float4(gIn, xs3, ys0)
+ val p0: Float4 = cubicInterpolate_F4(p00, p01, p02, p03, xf)
+
+ val p10: Float4 = rsGetElementAt_Float4(gIn, xs0, ys1)
+ val p11: Float4 = rsGetElementAt_Float4(gIn, xs1, ys1)
+ val p12: Float4 = rsGetElementAt_Float4(gIn, xs2, ys1)
+ val p13: Float4 = rsGetElementAt_Float4(gIn, xs3, ys1)
+ val p1: Float4 = cubicInterpolate_F4(p10, p11, p12, p13, xf)
+
+ val p20: Float4 = rsGetElementAt_Float4(gIn, xs0, ys2)
+ val p21: Float4 = rsGetElementAt_Float4(gIn, xs1, ys2)
+ val p22: Float4 = rsGetElementAt_Float4(gIn, xs2, ys2)
+ val p23: Float4 = rsGetElementAt_Float4(gIn, xs3, ys2)
+ val p2: Float4 = cubicInterpolate_F4(p20, p21, p22, p23, xf)
+
+ val p30: Float4 = rsGetElementAt_Float4(gIn, xs0, ys3)
+ val p31: Float4 = rsGetElementAt_Float4(gIn, xs1, ys3)
+ val p32: Float4 = rsGetElementAt_Float4(gIn, xs2, ys3)
+ val p33: Float4 = rsGetElementAt_Float4(gIn, xs3, ys3)
+ val p3: Float4 = cubicInterpolate_F4(p30, p31, p32, p33, xf)
+
+ val p: Float4 = cubicInterpolate_F4(p0, p1, p2, p3, yf)
+
+ return p
+}
+*/
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/ReferenceYuvToRgb.kt b/test-app/src/main/java/com/google/android/renderscript_test/ReferenceYuvToRgb.kt
new file mode 100644
index 0000000..aa58516
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/ReferenceYuvToRgb.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+import com.google.android.renderscript.YuvFormat
+import java.lang.IllegalArgumentException
+
+/**
+ * Reference implementation of a YUV to RGB operation.
+ */
+@ExperimentalUnsignedTypes
+fun referenceYuvToRgb(inputSignedArray: ByteArray, sizeX: Int, sizeY: Int, format: YuvFormat): ByteArray {
+ require(sizeX % 2 == 0) { "The width of the input should be even."}
+ val inputArray = inputSignedArray.asUByteArray()
+
+ val outputArray = ByteArray(sizeX * sizeY * 4)
+ val output = Vector2dArray(outputArray.asUByteArray(), 4, sizeX, sizeY)
+
+ when (format) {
+ YuvFormat.NV21 -> {
+ val startY = 0
+ val startU = sizeX * sizeY + 1
+ val startV = sizeX * sizeY
+
+ for (y in 0 until sizeY) {
+ for (x in 0 until sizeX) {
+ val offsetY = y * sizeX + x
+ val offsetU = ((y shr 1) * sizeX + (x shr 1) * 2)
+ val offsetV = ((y shr 1) * sizeX + (x shr 1) * 2)
+ output[x, y] = yuvToRGBA4(
+ inputArray[startY + offsetY],
+ inputArray[startU + offsetU],
+ inputArray[startV + offsetV]
+ )
+ }
+ }
+ }
+
+ YuvFormat.YV12 -> {
+ /* According to https://developer.android.com/reference/kotlin/android/graphics/ImageFormat#yv12,
+ * strideX and strideUV should be aligned to 16 byte boundaries. If we do this, we
+ * won't get the same results as RenderScript.
+ *
+ * We may want to test & require that sizeX is a multiple of 16/32.
+ */
+ val strideX = roundUpTo16(sizeX) // sizeX //
+ val strideUV = roundUpTo16(strideX / 2) // strideX / 2 //
+ val startY = 0
+ val startU = strideX * sizeY
+ val startV = startU + strideUV * sizeY / 2
+
+ for (y in 0 until sizeY) {
+ for (x in 0 until sizeX) {
+ val offsetY = y * sizeX + x
+ val offsetUV = (y shr 1) * strideUV + (x shr 1)
+ output[x, y] = yuvToRGBA4(
+ inputArray[startY + offsetY],
+ inputArray[startU + offsetUV],
+ inputArray[startV + offsetUV],
+ )
+ }
+ }
+ }
+ else -> throw IllegalArgumentException("Unknown YUV format $format")
+ }
+
+ return outputArray
+}
+
+@ExperimentalUnsignedTypes
+private fun yuvToRGBA4(y: UByte, u: UByte, v: UByte): UByteArray {
+ val intY = y.toInt() - 16
+ val intU = u.toInt() - 128
+ val intV = v.toInt() - 128
+ val p = intArrayOf(
+ intY * 298 + intV * 409 + 128 shr 8,
+ intY * 298 - intU * 100 - intV * 208 + 128 shr 8,
+ intY * 298 + intU * 516 + 128 shr 8,
+ 255
+ )
+ return UByteArray(4) { p[it].clampToUByte() }
+}
+
+/* To be used if we support Float
+private fun yuvToRGBA_f4(y: UByte, u: UByte, v: UByte): UByteArray {
+ val yuv_U_values = floatArrayOf(0f, -0.392f * 0.003921569f, 2.02f * 0.003921569f, 0f)
+ val yuv_V_values = floatArrayOf(1.603f * 0.003921569f, -0.815f * 0.003921569f, 0f, 0f)
+
+ var color = FloatArray(4) {y.toFloat() * 0.003921569f}
+ val fU = FloatArray(4) {u.toFloat() - 128f}
+ val fV = FloatArray(4) {v.toFloat() - 128f}
+
+ color += fU * yuv_U_values;
+ color += fV * yuv_V_values;
+ //color = clamp(color, 0.f, 1.f);
+ return UByteArray(4) { unitFloatClampedToUByte(color[it]) }
+}
+*/
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/TimingTracker.kt b/test-app/src/main/java/com/google/android/renderscript_test/TimingTracker.kt
new file mode 100644
index 0000000..0368368
--- /dev/null
+++ b/test-app/src/main/java/com/google/android/renderscript_test/TimingTracker.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.renderscript_test
+
+class TimingTracker(
+ private val numberOfIterations: Int = 1,
+ private var numberOfIterationsToIgnore: Int = 0
+) {
+ init {
+ require(numberOfIterations > numberOfIterationsToIgnore)
+ }
+ private val timings = mutableMapOf<String, IntArray>()
+ private var currentIteration: Int = 0
+ fun nextIteration() {
+ currentIteration++
+ }
+ fun <T> measure(name: String, workToTime: () -> T): T {
+ val start = System.nanoTime()
+ val t = workToTime()
+ if (currentIteration >= numberOfIterationsToIgnore) {
+ val end = System.nanoTime()
+ val deltaInMicroseconds: Int = ((end - start) / 1000).toInt()
+ val timing = timings.getOrPut(name) {
+ IntArray(numberOfIterations - numberOfIterationsToIgnore)
+ }
+ timing[currentIteration - numberOfIterationsToIgnore] += deltaInMicroseconds
+ }
+ return t
+ }
+ fun report(): String {
+ var minimum: Int = Int.MAX_VALUE
+ for (timing in timings.values) {
+ val m = timing.minOrNull()
+ if (m != null && m < minimum) minimum = m
+ }
+
+ println(timings.map { (name, timing) -> name + ": " + timing.minOrNull() }.joinToString(separator = "\n"))
+
+ var minimums =
+ timings.map { (name, timing) -> name + ": " + timing.minOrNull() }.joinToString()
+ var all =
+ timings.map { (name, timing) -> name + ": " + timing.joinToString() }.joinToString()
+ var normalized =
+ timings.map { (name, timing) -> name + ": " + timing.joinToString { "%.2f".format(it.toFloat() / minimum) } }
+ .joinToString()
+
+ return "Minimums: $minimums\n\nAll: $all\n\nNormalized: $normalized\n"
+ }
+}
+