1 /*
<lambda>null2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // TODO Rename to something better
18 package com.example.testapp
19 
20 import android.content.Context
21 import android.graphics.Bitmap
22 import android.graphics.BitmapFactory
23 import android.renderscript.RenderScript
24 import android.renderscript.toolkit.BlendingMode
25 import android.renderscript.toolkit.LookupTable
26 import android.renderscript.toolkit.Range2d
27 import android.renderscript.toolkit.Rgba3dArray
28 import android.renderscript.toolkit.Toolkit
29 import android.renderscript.toolkit.YuvFormat
30 import kotlin.math.abs
31 import kotlin.math.min
32 
33 data class TestLayout(
34     val sizeX: Int,
35     val sizeY: Int,
36     val restriction: Range2d?
37 )
38 
39 // List of dimensions (sizeX, sizeY) to try when generating random data.
40 val commonLayoutsToTry = listOf(
41     // Small layouts to start with
42     TestLayout(3, 4, null),
43     TestLayout(3, 4, Range2d(0, 1, 0, 3)),
44     TestLayout(3, 4, Range2d(2, 3, 1, 4)),
45     TestLayout(10, 14, null),
46     TestLayout(10, 14, Range2d(2, 3, 8, 14)),
47     // The size of most CTS intrinsic tests
48     TestLayout(160, 100, null),
49     TestLayout(125, 227, Range2d(50, 125, 100, 227)),
50     // A larger one
51     TestLayout(800, 600, null),
52     // Weirdly shaped ones
53     TestLayout(1, 1, null), // A single item
54     // TODO This size makes Intrinsic Blur fail.
55     TestLayout(16000, 1, null), // A single item
56     TestLayout(1, 16000, null), // One large row
57     // A very large test
58     TestLayout(1024, 2048, null),
59 )
60 
61 
62 class Tester(context: Context, private val validate: Boolean) {
63     private val renderscriptContext = RenderScript.create(context)
64     private val toolkit = Toolkit()
65     private val testImage1 = BitmapFactory.decodeResource(context.resources, R.drawable.img800x450a)
66     private val testImage2 = BitmapFactory.decodeResource(context.resources, R.drawable.img800x450b)
67 
68     init {
69         validateTestImage(testImage1)
70         validateTestImage(testImage2)
71     }
72 
73     /**
74      * Verify that the test images are in format that works for our tests.
75      */
76     private fun validateTestImage(bitmap: Bitmap) {
77         require(bitmap.config == Bitmap.Config.ARGB_8888)
78         require(bitmap.rowBytes == bitmap.width * 4) {
79             "Can't handle bitmaps that have extra padding. " +
80                 "${bitmap.rowBytes} != ${bitmap.width} * 4." }
81         require(bitmap.byteCount == bitmap.rowBytes * bitmap.height)
82     }
83 
84     fun destroy() {
85         renderscriptContext.destroy()
86     }
87 
88     @ExperimentalUnsignedTypes
89     fun testAll(timer: TimingTracker): String {
90         val tests  = listOf(
91             Pair("blend", ::testBlend),
92             Pair("blur", ::testBlur),
93             Pair("colorMatrix", ::testColorMatrix),
94             Pair("convolve", ::testConvolve),
95             Pair("histogram", ::testHistogram),
96             Pair("lut", ::testLut),
97             Pair("lut3d", ::testLut3d),
98             Pair("resize", ::testResize),
99             Pair("yuvToRgb", ::testYuvToRgb),
100         )
101         val results = Array(tests.size) { "" }
102         for (i in tests.indices) {
103             val (name, test) = tests[i]
104             println("Doing $name")
105             val success = test(timer)
106             results[i] = "$name " + if (success) "succeeded" else "FAILED! FAILED! FAILED! FAILED!"
107             println("      ${results[i]}")
108         }
109 
110         return results.joinToString("\n")
111     }
112 
113     @ExperimentalUnsignedTypes
114     private fun testBlend(timer: TimingTracker): Boolean {
115         return BlendingMode.values().all { mode ->
116             testOneBitmapBlend(timer, testImage1, testImage2, mode, null) and
117                     testOneBitmapBlend(
118                         timer, testImage1, testImage2, mode,
119                         Range2d(6, 23, 2, 4)
120                     ) and
121                     commonLayoutsToTry.all { (sizeX, sizeY, restriction) ->
122                         testOneRandomBlend(timer, sizeX, sizeY, mode, restriction)
123                     }
124         }
125     }
126 
127     @ExperimentalUnsignedTypes
128     private fun testOneRandomBlend(
129         timer: TimingTracker,
130         sizeX: Int,
131         sizeY: Int,
132         mode: BlendingMode,
133         restriction: Range2d?
134     ): Boolean {
135         val sourceArray = randomByteArray(0x50521f0, sizeX, sizeY, 4)
136         val destArray = randomByteArray(0x2932147, sizeX, sizeY, 4)
137         // Make clones because these will be modified by the blend.
138         val intrinsicDestArray = destArray.clone()
139         val referenceDestArray = destArray.clone()
140         val toolkitDestArray = destArray.clone()
141 
142         timer.measure("IntrinsicBlend") {
143             intrinsicBlend(
144                 renderscriptContext, mode, sourceArray, intrinsicDestArray, sizeX, sizeY,
145                 restriction
146             )
147         }
148         timer.measure("ToolkitBlend") {
149             toolkit.blend(mode, sourceArray, toolkitDestArray, sizeX, sizeY, restriction)
150         }
151         if (!validate) return true
152 
153         timer.measure("ReferenceBlend") {
154             referenceBlend(mode, sourceArray, referenceDestArray, sizeX, sizeY, restriction)
155         }
156 
157         return validateSame(
158             "Blend_$mode", intrinsicDestArray, referenceDestArray, toolkitDestArray
159         ) {
160             println("blend $mode ($sizeX, $sizeY) $restriction")
161             logArray("Blend_$mode src", sourceArray, 48)
162             logArray("Blend_$mode dst", destArray, 48)
163             logArray("Blend_$mode reference out", referenceDestArray, 48)
164             logArray("Blend_$mode intrinsic out", intrinsicDestArray, 48)
165             logArray("Blend_$mode toolkit   out", toolkitDestArray, 48)
166         }
167     }
168 
169     @ExperimentalUnsignedTypes
170     private fun testOneBitmapBlend(
171         timer: TimingTracker,
172         sourceBitmap: Bitmap,
173         destBitmap: Bitmap,
174         mode: BlendingMode,
175         restriction: Range2d?
176     ): Boolean {
177         // Make clones because these will be modified by the blend.
178         val intrinsicDestBitmap = duplicateBitmap(destBitmap)
179         val toolkitDestBitmap = duplicateBitmap(destBitmap)
180         val referenceDestBitmap = duplicateBitmap(destBitmap)
181 
182         timer.measure("IntrinsicBlend") {
183             intrinsicBlend(
184                 renderscriptContext, mode, sourceBitmap, intrinsicDestBitmap, restriction
185             )
186         }
187         timer.measure("ToolkitBlend") {
188             toolkit.blend(mode, sourceBitmap, toolkitDestBitmap, restriction)
189         }
190         if (!validate) return true
191 
192         val referenceDestArray = getBitmapBytes(referenceDestBitmap)
193         timer.measure("ReferenceBlend") {
194             referenceBlend(
195                 mode, getBitmapBytes(sourceBitmap), referenceDestArray, sourceBitmap.width,
196                 sourceBitmap.height, restriction
197             )
198         }
199 
200         val intrinsicDestArray = getBitmapBytes(intrinsicDestBitmap)
201         val toolkitDestArray = getBitmapBytes(toolkitDestBitmap)
202         return validateSame(
203             "BlendBitmap_$mode", intrinsicDestArray, referenceDestArray, toolkitDestArray
204         ) {
205             println("BlendBitmap $mode $restriction")
206             //logArray("BlendBitmap_$mode src", sourceArray, 48)
207             //logArray("BlendBitmap_$mode dst", destArray, 48)
208             logArray("BlendBitmap_$mode reference out", referenceDestArray, 48)
209             logArray("BlendBitmap_$mode intrinsic out", intrinsicDestArray, 48)
210             logArray("BlendBitmap_$mode toolkit   out", toolkitDestArray, 48)
211         }
212     }
213 
214     @ExperimentalUnsignedTypes
215     private fun testBlur(timer: TimingTracker): Boolean {
216         return arrayOf(1, 3, 8, 25).all { radius ->
217             testOneBitmapBlur(timer, testImage1, radius, null) and
218                     testOneBitmapBlur(timer, testImage1, radius, Range2d(6, 23, 2, 4)) and
219                     commonLayoutsToTry.all { (sizeX, sizeY, restriction) ->
220                         arrayOf(1, 4).all { vectorSize ->
221                             testOneRandomBlur(timer, vectorSize, sizeX, sizeY, radius, restriction)
222                         }
223                     }
224         }
225     }
226 
227     @ExperimentalUnsignedTypes
228     private fun testOneRandomBlur(
229         timer: TimingTracker,
230         vectorSize: Int,
231         sizeX: Int,
232         sizeY: Int,
233         radius: Int,
234         restriction: Range2d?
235     ): Boolean {
236         val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, vectorSize)
237         val intrinsicOutArray = timer.measure("IntrinsicBlur") {
238             intrinsicBlur(
239                 renderscriptContext, inputArray, vectorSize, sizeX, sizeY, radius, restriction
240             )
241         }
242         val toolkitOutArray = timer.measure("ToolkitBlur") {
243             toolkit.blur(inputArray, vectorSize, sizeX, sizeY, radius, restriction)
244         }
245         if (!validate) return true
246 
247         val referenceOutArray = timer.measure("ReferenceBlur") {
248             referenceBlur(inputArray, vectorSize, sizeX, sizeY, radius, restriction)
249         }
250         return validateSame("blur", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
251             println("blur $vectorSize ($sizeX, $sizeY) radius = $radius $restriction")
252             logArray("blur input        ", inputArray)
253             logArray("blur reference out", referenceOutArray)
254             logArray("blur intrinsic out", intrinsicOutArray)
255             logArray("blur toolkit   out", toolkitOutArray)
256         }
257     }
258 
259     @ExperimentalUnsignedTypes
260     private fun testOneBitmapBlur(
261         timer: TimingTracker,
262         bitmap: Bitmap,
263         radius: Int,
264         restriction: Range2d?
265     ): Boolean {
266         val intrinsicOutArray = timer.measure("IntrinsicBlur") {
267             intrinsicBlur(renderscriptContext, bitmap, radius, restriction)
268         }
269 
270         val toolkitOutBitmap = timer.measure("ToolkitBlur") {
271             toolkit.blur(bitmap, radius, restriction)
272         }
273         if (!validate) return true
274 
275         val referenceOutArray = timer.measure("ReferenceBlur") {
276             referenceBlur(
277                 getBitmapBytes(bitmap),
278                 vectorSizeOfBitmap(bitmap),
279                 bitmap.width,
280                 bitmap.height,
281                 radius,
282                 restriction
283             )
284         }
285 
286         val toolkitOutArray = getBitmapBytes(toolkitOutBitmap)
287         return validateSame("blur", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
288             println("BlurBitmap ${bitmap.config} $radius $restriction")
289             logArray("blur reference out", referenceOutArray)
290             logArray("blur intrinsic out", intrinsicOutArray)
291             logArray("blur toolkit   out", toolkitOutArray)
292         }
293     }
294 
295     enum class ColorMatrixConversionType {
296         RGB_TO_YUV,
297         YUV_TO_RGB,
298         GREYSCALE,
299         RANDOM
300     }
301 
302     @ExperimentalUnsignedTypes
303     private fun testColorMatrix(timer: TimingTracker): Boolean {
304         return ColorMatrixConversionType.values().all { conversion ->
305             testOneBitmapColorMatrix(timer, testImage1, conversion, null) and
306                     testOneBitmapColorMatrix(
307                         timer,
308                         testImage1,
309                         conversion,
310                         Range2d(6, 23, 2, 4)
311                     ) and
312                     commonLayoutsToTry.all { (sizeX, sizeY, restriction) ->
313                         (1..4).all { inputVectorSize ->
314                             (1..4).all { outputVectorSize ->
315                                 testOneRandomColorMatrix(
316                                     timer,
317                                     inputVectorSize,
318                                     sizeX,
319                                     sizeY,
320                                     outputVectorSize,
321                                     conversion,
322                                     restriction
323                                 )
324                             }
325                         }
326                     }
327         }
328     }
329 
330     @ExperimentalUnsignedTypes
331     private fun testOneRandomColorMatrix(
332         timer: TimingTracker,
333         inputVectorSize: Int,
334         sizeX: Int,
335         sizeY: Int,
336         outputVectorSize: Int,
337         conversion: ColorMatrixConversionType,
338         restriction: Range2d?
339     ): Boolean {
340         val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, paddedSize(inputVectorSize))
341         val addVector = randomFloatArray(0x243238, 4, 1, 1, 0.3f)
342         val matrix = when (conversion) {
343             ColorMatrixConversionType.RGB_TO_YUV -> toolkit.rgbToYuvMatrix
344             ColorMatrixConversionType.YUV_TO_RGB -> toolkit.yuvToRgbMatrix
345             ColorMatrixConversionType.GREYSCALE -> toolkit.greyScaleColorMatrix
346             ColorMatrixConversionType.RANDOM -> randomFloatArray(0x234348, 4, 4, 1)
347         }
348 
349         val intrinsicOutArray = timer.measure("IntrinsicColorMatrix") {
350             intrinsicColorMatrix(
351                 renderscriptContext,
352                 conversion,
353                 inputArray,
354                 inputVectorSize,
355                 sizeX,
356                 sizeY,
357                 outputVectorSize,
358                 matrix,
359                 addVector,
360                 restriction
361             )
362         }
363         val toolkitOutArray = timer.measure("ToolkitColorMatrix") {
364             toolkit.colorMatrix(
365                 inputArray,
366                 inputVectorSize,
367                 sizeX,
368                 sizeY,
369                 outputVectorSize,
370                 matrix,
371                 addVector,
372                 restriction
373             )
374         }
375         if (!validate) return true
376 
377         val referenceOutArray = timer.measure("ReferenceColorMatrix") {
378             referenceColorMatrix(
379                 inputArray, inputVectorSize, sizeX, sizeY, outputVectorSize, matrix, addVector,
380                 restriction
381             )
382         }
383 
384         return validateSame("colorMatrix", intrinsicOutArray, referenceOutArray, toolkitOutArray,
385             outputVectorSize == 3) {
386             println("colorMatrix ($sizeX, $sizeY) $inputVectorSize->$outputVectorSize $restriction")
387             logArray("colorMatrix matrix   ", matrix, 16)
388             logArray("colorMatrix addVector", addVector, 4)
389             logArray("colorMatrix in           ", inputArray)
390             logArray("colorMatrix reference out", referenceOutArray, 300)
391             logArray("colorMatrix intrinsic out", intrinsicOutArray, 300)
392             logArray("colorMatrix toolkit   out", toolkitOutArray, 300)
393         }
394     }
395 
396     @ExperimentalUnsignedTypes
397     private fun testOneBitmapColorMatrix(
398         timer: TimingTracker,
399         bitmap: Bitmap,
400         conversion: ColorMatrixConversionType,
401         restriction: Range2d?
402     ): Boolean {
403         val addVector = randomFloatArray(0x243238, 4, 1, 1, 0.3f)
404         val matrix = when (conversion) {
405             ColorMatrixConversionType.RGB_TO_YUV -> toolkit.rgbToYuvMatrix
406             ColorMatrixConversionType.YUV_TO_RGB -> toolkit.yuvToRgbMatrix
407             ColorMatrixConversionType.GREYSCALE -> toolkit.greyScaleColorMatrix
408             ColorMatrixConversionType.RANDOM -> randomFloatArray(0x234348, 4, 4, 1)
409         }
410 
411         val intrinsicOutArray = timer.measure("IntrinsicColorMatrix") {
412             intrinsicColorMatrix(
413                 renderscriptContext, conversion, bitmap, matrix, addVector, restriction
414             )
415         }
416         val toolkitOutBitmap = timer.measure("ToolkitColorMatrix") {
417             toolkit.colorMatrix(bitmap, matrix, addVector, restriction)
418         }
419         if (!validate) return true
420 
421         val referenceOutArray = timer.measure("ReferenceColorMatrix") {
422             referenceColorMatrix(
423                 getBitmapBytes(bitmap), vectorSizeOfBitmap(bitmap), bitmap.width, bitmap.height,
424                 vectorSizeOfBitmap(bitmap), matrix, addVector, restriction
425             )
426         }
427 
428         val toolkitOutArray = getBitmapBytes(toolkitOutBitmap)
429         return validateSame("ColorMatrix", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
430             println("colorMatrixBitmap $restriction")
431             logArray("colorMatrixBitmap matrix   ", matrix, 16)
432             logArray("colorMatrixBitmap addVector", addVector, 4)
433             logArray("colorMatrixBitmap reference out", referenceOutArray)
434             logArray("colorMatrixBitmap intrinsic out", intrinsicOutArray)
435             logArray("colorMatrixBitmap toolkit   out", toolkitOutArray)
436         }
437     }
438 
439     @ExperimentalUnsignedTypes
440     private fun testConvolve(timer: TimingTracker): Boolean {
441         val coefficientsToTry = listOf(
442             randomFloatArray(0x2937021, 3, 3, 1, 0.1f),
443             randomFloatArray(0x2937021, 5, 5, 1, 0.05f)
444         )
445         return coefficientsToTry.all { coefficients ->
446             testOneBitmapConvolve(timer, testImage1, coefficients, null) and
447                     testOneBitmapConvolve(timer, testImage1, coefficients, Range2d(6, 23, 2, 4)) and
448 
449                     commonLayoutsToTry.all { (sizeX, sizeY, restriction) ->
450                         (1..4).all { vectorSize ->
451                             testOneRandomConvolve(
452                                 timer,
453                                 vectorSize,
454                                 sizeX,
455                                 sizeY,
456                                 coefficients,
457                                 restriction
458                             )
459                         }
460                     }
461         }
462     }
463 
464     @ExperimentalUnsignedTypes
465     private fun testOneRandomConvolve(
466         timer: TimingTracker,
467         vectorSize: Int,
468         sizeX: Int,
469         sizeY: Int,
470         coefficients: FloatArray,
471         restriction: Range2d?
472     ): Boolean {
473         val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, paddedSize(vectorSize))
474 
475         val intrinsicOutArray = timer.measure("IntrinsicConvolve") {
476             intrinsicConvolve(
477                 renderscriptContext, inputArray, vectorSize, sizeX, sizeY, coefficients, restriction
478             )
479         }
480         val toolkitOutArray = timer.measure("ToolkitConvolve") {
481             toolkit.convolve(inputArray, vectorSize, sizeX, sizeY, coefficients, restriction)
482         }
483         if (!validate) return true
484 
485         val referenceOutArray = timer.measure("ReferenceConvolve") {
486             referenceConvolve(inputArray, vectorSize, sizeX, sizeY, coefficients, restriction)
487         }
488 
489         val task = if (coefficients.size == 9) "convolve3x3 $vectorSize" else "convolve5x5 $vectorSize"
490         return validateSame(task, intrinsicOutArray, referenceOutArray, toolkitOutArray) {
491             println("Convolve $vectorSize ($sizeX, $sizeY) $restriction")
492             logArray("Convolve coefficients", coefficients, 25)
493             logArray("Convolve in           ", inputArray)
494             logArray("Convolve reference out", referenceOutArray)
495             logArray("Convolve intrinsic out", intrinsicOutArray)
496             logArray("Convolve toolkit   out", toolkitOutArray)
497         }
498     }
499 
500     @ExperimentalUnsignedTypes
501     private fun testOneBitmapConvolve(
502         timer: TimingTracker,
503         bitmap: Bitmap,
504         coefficients: FloatArray,
505         restriction: Range2d?
506     ): Boolean {
507         val intrinsicOutArray = timer.measure("IntrinsicConvolve") {
508             intrinsicConvolve(renderscriptContext, bitmap, coefficients, restriction)
509         }
510         val toolkitOutBitmap = timer.measure("ToolkitConvolve") {
511             toolkit.convolve(bitmap, coefficients, restriction)
512         }
513         if (!validate) return true
514 
515         val referenceOutArray = timer.measure("ReferenceConvolve") {
516             referenceConvolve(
517                 getBitmapBytes(bitmap), vectorSizeOfBitmap(bitmap), bitmap.width, bitmap.height,
518                 coefficients, restriction
519             )
520         }
521 
522         val task = if (coefficients.size == 9) "convolve3x3" else "convolve5x5"
523         val toolkitOutArray = getBitmapBytes(toolkitOutBitmap)
524         return validateSame(task, intrinsicOutArray, referenceOutArray, toolkitOutArray) {
525             println("ConvolveBitmap $restriction")
526             logArray("ConvolveBitmap coefficients", coefficients, 25)
527             //logArray("ConvolveBitmap in           ", inputArray)
528             logArray("ConvolveBitmap reference out", referenceOutArray)
529             logArray("ConvolveBitmap intrinsic out", intrinsicOutArray)
530             logArray("ConvolveBitmap toolkit   out", toolkitOutArray)
531         }
532     }
533 
534     @ExperimentalUnsignedTypes
535     private fun testHistogram(timer: TimingTracker): Boolean {
536         val coefficients = floatArrayOf(0.1f, 0.3f, 0.5f, 0.05f)
537         return testOneBitmapHistogram(timer, testImage1, null) and
538                 testOneBitmapHistogram(timer, testImage1, Range2d(6, 23, 2, 4)) and
539                 testOneBitmapHistogramDot(timer, testImage1, null, null) and
540                 testOneBitmapHistogramDot(timer, testImage1, coefficients, null) and
541                 testOneBitmapHistogramDot(timer, testImage1, coefficients, Range2d(6, 23, 2, 4)) and
542         commonLayoutsToTry.all { (sizeX, sizeY, restriction) ->
543             (1..4).all { vectorSize ->
544                 testOneRandomHistogram(timer, vectorSize, sizeX, sizeY, restriction) &&
545                         testOneRandomHistogramDot(
546                             timer,
547                             vectorSize,
548                             sizeX,
549                             sizeY,
550                             null,
551                             restriction
552                         ) &&
553                         testOneRandomHistogramDot(
554                             timer,
555                             vectorSize,
556                             sizeX,
557                             sizeY,
558                             coefficients.sliceArray(0 until vectorSize),
559                             restriction
560                         )
561             }
562         }
563     }
564 
565     @ExperimentalUnsignedTypes
566     private fun testOneRandomHistogram(
567         timer: TimingTracker,
568         vectorSize: Int,
569         sizeX: Int,
570         sizeY: Int,
571         restriction: Range2d?
572     ): Boolean {
573         val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, paddedSize(vectorSize))
574 
575         val intrinsicOutput = timer.measure("IntrinsicHistogram") {
576             intrinsicHistogram(
577                 renderscriptContext, inputArray, vectorSize, sizeX, sizeY, restriction
578             )
579         }
580         val toolkitOutput = timer.measure("ToolkitHistogram") {
581             toolkit.histogram(inputArray, vectorSize, sizeX, sizeY, restriction)
582         }
583         if (!validate) return true
584 
585         val referenceOutput = timer.measure("ReferenceHistogram") {
586             referenceHistogram(
587                 inputArray, vectorSize, sizeX, sizeY, restriction
588             )
589         }
590 
591         return validateSame("histogram", intrinsicOutput, referenceOutput, toolkitOutput, 0) {
592             println("histogram $vectorSize ($sizeX, $sizeY) $restriction")
593             logArray("histogram in           ", inputArray, 200)
594             logArray("histogram reference out", referenceOutput, 200)
595             logArray("histogram intrinsic out", intrinsicOutput, 200)
596             logArray("histogram toolkit   out", toolkitOutput, 200)
597         }
598     }
599 
600     @ExperimentalUnsignedTypes
601     private fun testOneBitmapHistogram(
602         timer: TimingTracker,
603         bitmap: Bitmap,
604         restriction: Range2d?
605     ): Boolean {
606         val intrinsicOutput = timer.measure("IntrinsicHistogram") {
607             intrinsicHistogram(renderscriptContext, bitmap, restriction)
608         }
609         val toolkitOutput = timer.measure("ToolkitHistogram") {
610             toolkit.histogram(bitmap, restriction)
611         }
612         if (!validate) return true
613 
614         val referenceOutput = timer.measure("ReferenceHistogram") {
615             referenceHistogram(
616                 getBitmapBytes(bitmap), vectorSizeOfBitmap(bitmap), bitmap.width, bitmap.height,
617                 restriction
618             )
619         }
620 
621         return validateSame("histogram", intrinsicOutput, referenceOutput, toolkitOutput, 0) {
622             println("HistogramBitmap $restriction")
623             logArray("HistogramBitmap reference out", referenceOutput)
624             logArray("HistogramBitmap intrinsic out", intrinsicOutput)
625             logArray("HistogramBitmap toolkit   out", toolkitOutput)
626         }
627     }
628 
629     @ExperimentalUnsignedTypes
630     private fun testOneRandomHistogramDot(
631         timer: TimingTracker,
632         vectorSize: Int,
633         sizeX: Int,
634         sizeY: Int,
635         coefficients: FloatArray?, restriction: Range2d?
636     ): Boolean {
637         val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, paddedSize(vectorSize))
638 
639         val intrinsicOutArray = timer.measure("IntrinsicHistogramDot") {
640             intrinsicHistogramDot(
641                 renderscriptContext, inputArray, vectorSize, sizeX, sizeY, coefficients, restriction
642             )
643         }
644         val toolkitOutArray = timer.measure("ToolkitHistogramDot") {
645             toolkit.histogramDot(
646                 inputArray, vectorSize, sizeX, sizeY, coefficients, restriction
647             )
648         }
649         if (!validate) return true
650 
651         val referenceOutArray = timer.measure("ReferenceHistogramDot") {
652             referenceHistogramDot(inputArray, vectorSize, sizeX, sizeY, coefficients, restriction)
653         }
654 
655         return validateSame("histogramDot", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
656             println("histogramDot $vectorSize ($sizeX, $sizeY) $restriction")
657             logArray("histogramDot coefficients ", coefficients)
658             logArray("histogramDot in           ", inputArray)
659             logArray("histogramDot reference out", referenceOutArray, 256)
660             logArray("histogramDot intrinsic out", intrinsicOutArray, 256)
661             logArray("histogramDot toolkit   out", toolkitOutArray, 256)
662         }
663     }
664 
665     @ExperimentalUnsignedTypes
666     private fun testOneBitmapHistogramDot(
667         timer: TimingTracker,
668         bitmap: Bitmap,
669         coefficients: FloatArray?,
670         restriction: Range2d?
671     ): Boolean {
672         val intrinsicOutArray = timer.measure("IntrinsicHistogramDot") {
673             intrinsicHistogramDot(renderscriptContext, bitmap, coefficients, restriction)
674         }
675         val toolkitOutArray = timer.measure("ToolkitHistogramDot") {
676             toolkit.histogramDot(bitmap, coefficients, restriction)
677         }
678         if (!validate) return true
679 
680         val referenceOutArray = timer.measure("ReferenceHistogramDot") {
681             referenceHistogramDot(
682                 getBitmapBytes(bitmap),
683                 vectorSizeOfBitmap(bitmap),
684                 bitmap.width,
685                 bitmap.height,
686                 coefficients,
687                 restriction
688             )
689         }
690 
691         return validateSame(
692             "HistogramDotBitmap",
693             intrinsicOutArray,
694             referenceOutArray,
695             toolkitOutArray
696         ) {
697             println("HistogramDotBitmap $restriction")
698             logArray("HistogramDotBitmap coefficients ", coefficients)
699             //logArray("HistogramDotBitmap in           ", inputArray)
700             logArray("HistogramDotBitmap reference out", referenceOutArray, 256)
701             logArray("HistogramDotBitmap intrinsic out", intrinsicOutArray, 256)
702             logArray("HistogramDotBitmap toolkit   out", toolkitOutArray, 256)
703         }
704     }
705 
706     @ExperimentalUnsignedTypes
707     private fun testLut(timer: TimingTracker): Boolean {
708         return testOneBitmapLut(timer, testImage1, null) and
709                 testOneBitmapLut(timer, testImage1, Range2d(6, 23, 2, 4)) and
710         commonLayoutsToTry.all { (sizeX, sizeY, restriction) ->
711             testOneRandomLut(timer, sizeX, sizeY, restriction)
712         }
713     }
714 
715     @ExperimentalUnsignedTypes
716     private fun testOneRandomLut(
717         timer: TimingTracker,
718         sizeX: Int,
719         sizeY: Int,
720         restriction: Range2d?
721     ): Boolean {
722         val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, 4)
723         val newRed = randomByteArray(0x32425, 256, 1, 1)
724         val newGreen = randomByteArray(0x1F3225, 256, 1, 1)
725         val newBlue = randomByteArray(0x32D4F27, 256, 1, 1)
726         val newAlpha = randomByteArray(0x3A20001, 256, 1, 1)
727         val table = LookupTable()
728         table.red = newRed
729         table.blue = newBlue
730         table.green = newGreen
731         table.alpha = newAlpha
732 
733         val intrinsicOutArray = timer.measure("IntrinsicLUT") {
734             intrinsicLut(
735                 renderscriptContext, inputArray, sizeX, sizeY, newRed, newGreen, newBlue, newAlpha,
736                 restriction
737             )
738         }
739         val toolkitOutArray = timer.measure("ToolkitLUT") {
740             toolkit.lut(inputArray, sizeX, sizeY, table, restriction)
741         }
742         if (!validate) return true
743 
744         val referenceOutArray = timer.measure("ReferenceLUT") {
745             referenceLut(inputArray, sizeX, sizeY, table, restriction)
746         }
747 
748         return validateSame("LUT", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
749             println("lut ($sizeX, $sizeY) $restriction")
750             logArray("LUT red  ", newRed, 256)
751             logArray("LUT green", newGreen, 256)
752             logArray("LUT blue ", newBlue, 256)
753             logArray("LUT alpha", newAlpha, 256)
754             logArray("LUT in           ", inputArray)
755             logArray("LUT reference out", referenceOutArray)
756             logArray("LUT intrinsic out", intrinsicOutArray)
757             logArray("LUT toolkit   out", toolkitOutArray)
758         }
759     }
760 
761     @ExperimentalUnsignedTypes
762     private fun testOneBitmapLut(
763         timer: TimingTracker,
764         bitmap: Bitmap,
765         restriction: Range2d?
766     ): Boolean {
767         val newRed = randomByteArray(0x32425, 256, 1, 1)
768         val newGreen = randomByteArray(0x1F3225, 256, 1, 1)
769         val newBlue = randomByteArray(0x32D4F27, 256, 1, 1)
770         val newAlpha = randomByteArray(0x3A20001, 256, 1, 1)
771         val table = LookupTable()
772         table.red = newRed
773         table.blue = newBlue
774         table.green = newGreen
775         table.alpha = newAlpha
776 
777         val intrinsicOutArray = timer.measure("IntrinsicLUT") {
778             intrinsicLut(
779                 renderscriptContext, bitmap, newRed, newGreen, newBlue, newAlpha, restriction
780             )
781         }
782         val toolkitOutBitmap = timer.measure("ToolkitLUT") {
783             toolkit.lut(bitmap, table, restriction)
784         }
785         if (!validate) return true
786 
787         val referenceOutArray = timer.measure("ReferenceLUT") {
788             referenceLut(
789                 getBitmapBytes(bitmap),
790                 bitmap.width,
791                 bitmap.height,
792                 table,
793                 restriction
794             )
795         }
796 
797         val toolkitOutArray = getBitmapBytes(toolkitOutBitmap)
798         return validateSame("LutBitmap", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
799             println("LutBitmap $restriction")
800             logArray("LutBitmap red  ", newRed, 256)
801             logArray("LutBitmap green", newGreen, 256)
802             logArray("LutBitmap blue ", newBlue, 256)
803             logArray("LutBitmap alpha", newAlpha, 256)
804             //logArray("LutBitmap in           ", inputArray, 80)
805             logArray("LutBitmap reference out", referenceOutArray)
806             logArray("LutBitmap intrinsic out", intrinsicOutArray)
807             logArray("LutBitmap toolkit   out", toolkitOutArray)
808         }
809     }
810 
811     @ExperimentalUnsignedTypes
812     private fun testLut3d(timer: TimingTracker): Boolean {
813         val cubeSizesToTry = listOf(
814             Dimension(2, 2, 2),
815             Dimension(32, 32, 16),
816             Dimension(256, 256, 256)
817         )
818         return cubeSizesToTry.all { cubeSize ->
819                 val identityCube = identityCube(cubeSize)
820                 val randomCube = randomCube(0x23424, cubeSize)
821                 testOneBitmapLut3d(timer, testImage1, cubeSize, identityCube, 1, null) and
822                         testOneBitmapLut3d(timer, testImage2, cubeSize, randomCube, 3, null) and
823                         testOneBitmapLut3d(timer, testImage2, cubeSize, randomCube, 3, Range2d(6, 23, 2, 4)) and
824                 commonLayoutsToTry.all { (sizeX, sizeY, restriction) ->
825                     testOneRandomLut3d(timer, sizeX, sizeY, cubeSize, identityCube, 1, restriction) &&
826                             testOneRandomLut3d(
827                                 timer,
828                                 sizeX,
829                                 sizeY,
830                                 cubeSize,
831                                 randomCube,
832                                 3,
833                                 restriction
834                             )
835                 }
836             }
837     }
838 
839     @ExperimentalUnsignedTypes
840     private fun testOneRandomLut3d(
841         timer: TimingTracker,
842         sizeX: Int,
843         sizeY: Int,
844         cubeSize: Dimension,
845         cubeArray: ByteArray,
846         allowedIntError: Int, restriction: Range2d?
847     ): Boolean {
848         val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, 4)
849 
850         val intrinsicOutArray = timer.measure("IntrinsicLut3d") {
851             intrinsicLut3d(
852                 renderscriptContext, inputArray, sizeX, sizeY, cubeArray, cubeSize, restriction
853             )
854         }
855         val toolkitOutArray = timer.measure("ToolkitLut3d") {
856             val toolkitCube = Rgba3dArray(cubeArray, cubeSize.sizeX, cubeSize.sizeY, cubeSize.sizeZ)
857             toolkit.lut3d(inputArray, sizeX, sizeY, toolkitCube, restriction)
858         }
859         if (!validate) return true
860 
861         val referenceOutArray = timer.measure("ReferenceLut3d") {
862             val cube = Rgba3dArray(cubeArray, cubeSize.sizeX, cubeSize.sizeY, cubeSize.sizeZ)
863             referenceLut3d(inputArray, sizeX, sizeY, cube, restriction)
864         }
865 
866         return validateSame(
867             "lut3d",
868             intrinsicOutArray,
869             referenceOutArray,
870             toolkitOutArray,
871             false,
872             allowedIntError
873         ) {
874             println("lut3d ($sizeX, $sizeY) $restriction")
875             logArray("lut3d cube", cubeArray, 256)
876             logArray("lut3d in           ", inputArray, 64)
877             logArray("lut3d reference out", referenceOutArray, 64)
878             logArray("lut3d intrinsic out", intrinsicOutArray, 64)
879             logArray("lut3d toolkit   out", toolkitOutArray)
880         }
881     }
882 
883     @ExperimentalUnsignedTypes
884     private fun testOneBitmapLut3d(
885         timer: TimingTracker,
886         bitmap: Bitmap,
887         cubeSize: Dimension,
888         cubeArray: ByteArray,
889         allowedIntError: Int, restriction: Range2d?
890     ): Boolean {
891         val intrinsicOutArray = timer.measure("IntrinsicLut3d") {
892             intrinsicLut3d(renderscriptContext, bitmap, cubeArray, cubeSize, restriction)
893         }
894         val toolkitOutBitmap = timer.measure("ToolkitLut3d") {
895             val toolkitCube = Rgba3dArray(cubeArray, cubeSize.sizeX, cubeSize.sizeY, cubeSize.sizeZ)
896             toolkit.lut3d(bitmap, toolkitCube, restriction)
897         }
898         if (!validate) return true
899 
900         val referenceOutArray = timer.measure("ReferenceLut3d") {
901             val cube = Rgba3dArray(cubeArray, cubeSize.sizeX, cubeSize.sizeY, cubeSize.sizeZ)
902             referenceLut3d(getBitmapBytes(bitmap), bitmap.width, bitmap.height, cube, restriction)
903         }
904 
905         val toolkitOutArray = getBitmapBytes(toolkitOutBitmap)
906         return validateSame(
907             "Lut3dBitmap",
908             intrinsicOutArray,
909             referenceOutArray,
910             toolkitOutArray,
911             false,
912             allowedIntError
913         ) {
914             println("Lut3dBitmap $restriction")
915             logArray("Lut3dBitmap cube", cubeArray, 256)
916             //logArray("Lut3dBitmap in           ", inputArray, 64)
917             logArray("Lut3dBitmap reference out", referenceOutArray, 64)
918             logArray("Lut3dBitmap intrinsic out", intrinsicOutArray, 64)
919             logArray("Lut3dBitmap toolkit   out", toolkitOutArray)
920         }
921     }
922 
923     @ExperimentalUnsignedTypes
924     private fun testResize(timer: TimingTracker): Boolean {
925         val factorsToTry = listOf(
926             Pair(1f, 1f),
927             Pair(0.5f, 1f),
928             Pair(2f, 2f),
929             Pair(0.5f, 2f),
930             Pair(2f, 0.5f),
931             // The RenderScript Intrinsic tests used the above factors. It's tempting to use
932             // less regular ones like Pair(6.37f, 0.17f) however this creates small offset
933             // errors between the result provided by the C++ code and the SIMD code. This is
934             // due to the SIMD code using a scaled integer to increment going from one pixel to the
935             // next, while the C++ code uses float operations.
936         )
937         val layoutsToTry = listOf(
938             TestLayout(37, 47, null),
939             TestLayout(60, 10, null),
940             TestLayout(6, 4, Range2d(1, 3, 0, 2)),
941             TestLayout(10, 14, Range2d(2, 3, 3, 7)),
942         )
943 
944         return factorsToTry.all { (scaleX, scaleY) ->
945             // Do one resize that's greater than 4x, as that's used in the code but don't do it
946             // for everything, as some images will get very large
947             testOneRandomResize(timer, 1, 25, 30, 6f, 6f, null) and
948             testOneBitmapResize(timer, testImage1, scaleX, scaleY, null) and
949                     testOneBitmapResize(timer, testImage1, scaleX, scaleY, Range2d(6, 23, 2, 4)) and
950                     layoutsToTry.all { (sizeX, sizeY, restriction) ->
951                         (1..4).all { vectorSize ->
952                             testOneRandomResize(
953                                 timer,
954                                 vectorSize,
955                                 sizeX,
956                                 sizeY,
957                                 scaleX,
958                                 scaleY,
959                                 restriction
960                             )
961                         }
962                     }
963         }
964     }
965 
966     @ExperimentalUnsignedTypes
967     private fun testOneRandomResize(
968         timer: TimingTracker,
969         vectorSize: Int,
970         inSizeX: Int,
971         inSizeY: Int,
972         scaleX: Float,
973         scaleY: Float,
974         restriction: Range2d?
975     ): Boolean {
976         val inputArray = randomByteArray(0x50521f0, inSizeX, inSizeY, paddedSize(vectorSize))
977         val outSizeX = (inSizeX * scaleX).toInt()
978         val outSizeY = (inSizeY * scaleY).toInt()
979 
980         val intrinsicOutArray = timer.measure("IntrinsicResize") {
981             intrinsicResize(
982                 renderscriptContext, inputArray, vectorSize, inSizeX, inSizeY, outSizeX, outSizeY,
983                 restriction
984             )
985         }
986         val toolkitOutArray = timer.measure("ToolkitResize") {
987             toolkit.resize(
988                 inputArray, vectorSize, inSizeX, inSizeY, outSizeX, outSizeY, restriction
989             )
990         }
991         if (!validate) return true
992 
993         val referenceOutArray = timer.measure("ReferenceResize") {
994             referenceResize(
995                 inputArray, vectorSize, inSizeX, inSizeY, outSizeX, outSizeY, restriction
996             )
997         }
998 
999         return validateSame("resize", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
1000             println("resize $vectorSize ($inSizeX, $inSizeY) by ($scaleX, $scaleY) to ($outSizeX, $outSizeY), $restriction")
1001             logArray("resize in           ", inputArray)
1002             logArray("resize reference out", referenceOutArray)
1003             logArray("resize intrinsic out", intrinsicOutArray)
1004             logArray("resize toolkit   out", toolkitOutArray)
1005         }
1006     }
1007 
1008     @ExperimentalUnsignedTypes
1009     private fun testOneBitmapResize(
1010         timer: TimingTracker,
1011         bitmap: Bitmap,
1012         scaleX: Float,
1013         scaleY: Float,
1014         restriction: Range2d?
1015     ): Boolean {
1016         // println("Doing resize $inSizeX x $inSizeY x $vectorSize, $scaleX x $scaleY, $restriction")
1017         val outSizeX = (bitmap.width * scaleX).toInt()
1018         val outSizeY = (bitmap.height * scaleY).toInt()
1019 
1020         val intrinsicOutArray = timer.measure("IntrinsicResize") {
1021             intrinsicResize(renderscriptContext, bitmap, outSizeX, outSizeY, restriction)
1022         }
1023         val toolkitOutBitmap = timer.measure("ToolkitResize") {
1024             toolkit.resize(bitmap, outSizeX, outSizeY, restriction)
1025         }
1026         if (!validate) return true
1027 
1028         val referenceOutArray = timer.measure("ReferenceResize") {
1029             referenceResize(
1030                 getBitmapBytes(bitmap),
1031                 vectorSizeOfBitmap(bitmap),
1032                 bitmap.width,
1033                 bitmap.height,
1034                 outSizeX,
1035                 outSizeY,
1036                 restriction
1037             )
1038         }
1039 
1040         val toolkitOutArray = getBitmapBytes(toolkitOutBitmap)
1041         return validateSame("ResizeBitmap", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
1042             println("ResizeBitmap by ($scaleX, $scaleY) to ($outSizeX, $outSizeY), $restriction")
1043             //logArray("ResizeBitmap in           ", inputArray, 100)
1044             logArray("ResizeBitmap reference out", referenceOutArray)
1045             logArray("ResizeBitmap intrinsic out", intrinsicOutArray)
1046             logArray("ResizeBitmap toolkit   out", toolkitOutArray)
1047         }
1048     }
1049 
1050     @ExperimentalUnsignedTypes
1051     private fun testYuvToRgb(timer: TimingTracker): Boolean {
1052         val layoutsToTry = listOf(
1053             // Don't try sizeX with odd values. That's not allowed by definition of some
1054             // of the video formats.
1055             TestLayout(10, 14, null),
1056             TestLayout(64, 40, null),
1057             TestLayout(96, 94, null),
1058         )
1059         return layoutsToTry.all { (sizeX, sizeY, _) ->
1060             YuvFormat.values().all { format ->
1061                 testOneRandomYuvToRgb(timer, sizeX, sizeY, format) and
1062                 testOneRandomYuvToRgbBitmap(timer, sizeX, sizeY, format)
1063             }
1064         }
1065     }
1066 
1067     @ExperimentalUnsignedTypes
1068     private fun testOneRandomYuvToRgb(
1069         timer: TimingTracker,
1070         sizeX: Int,
1071         sizeY: Int,
1072         format: YuvFormat
1073     ): Boolean {
1074         // The RenderScript Intrinsic does not handle this combination correctly.
1075         if (format == YuvFormat.YV12 && sizeX % 32 != 0) {
1076             return true
1077         }
1078         val inputArray = randomYuvArray(0x50521f0, sizeX, sizeY, format)
1079 
1080         val intrinsicOutArray = timer.measure("IntrinsicYuvToRgb") {
1081             intrinsicYuvToRgb(renderscriptContext, inputArray, sizeX, sizeY, format)
1082         }
1083         val toolkitOutArray = timer.measure("ToolkitYuvToRgb") {
1084             toolkit.yuvToRgb(inputArray, sizeX, sizeY, format)
1085         }
1086         if (!validate) return true
1087 
1088         val referenceOutArray = timer.measure("ReferenceYuvToRgb") {
1089             referenceYuvToRgb(inputArray, sizeX, sizeY, format)
1090         }
1091 
1092         return validateSame("yuvToRgb", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
1093             println("yuvToRgb ($sizeX, $sizeY) $format")
1094             logArray("yuvToRgb in           ", inputArray)
1095             logArray("yuvToRgb reference out", referenceOutArray)
1096             logArray("yuvToRgb intrinsic out", intrinsicOutArray)
1097             logArray("yuvToRgb toolkit   out", toolkitOutArray)
1098         }
1099     }
1100 
1101     @ExperimentalUnsignedTypes
1102     private fun testOneRandomYuvToRgbBitmap(
1103         timer: TimingTracker,
1104         sizeX: Int,
1105         sizeY: Int,
1106         format: YuvFormat
1107     ): Boolean {
1108         // The RenderScript Intrinsic does not handle this combination correctly.
1109         if (format == YuvFormat.YV12 && sizeX % 32 != 0) {
1110             return true
1111         }
1112         val inputArray = randomYuvArray(0x50521f0, sizeX, sizeY, format)
1113 
1114         val intrinsicOutArray = timer.measure("IntrinsicYuvToRgb") {
1115             intrinsicYuvToRgb(renderscriptContext, inputArray, sizeX, sizeY, format)
1116         }
1117         val toolkitOutBitmap = timer.measure("ToolkitYuvToRgb") {
1118             toolkit.yuvToRgbBitmap(inputArray, sizeX, sizeY, format)
1119         }
1120         if (!validate) return true
1121 
1122         val referenceOutArray = timer.measure("ReferenceYuvToRgb") {
1123             referenceYuvToRgb(inputArray, sizeX, sizeY, format)
1124         }
1125 
1126         val toolkitOutArray = getBitmapBytes(toolkitOutBitmap)
1127         return validateSame("yuvToRgb", intrinsicOutArray, referenceOutArray, toolkitOutArray) {
1128             println("yuvToRgb ($sizeX, $sizeY) $format")
1129             logArray("yuvToRgb in           ", inputArray)
1130             logArray("yuvToRgb reference out", referenceOutArray)
1131             logArray("yuvToRgb intrinsic out", intrinsicOutArray)
1132             logArray("yuvToRgb toolkit   out", toolkitOutArray)
1133         }
1134     }
1135 
1136     /**
1137      * Verifies that the arrays returned by the Intrinsic, the reference code, and the Toolkit
1138      * are all within a margin of error.
1139      *
1140      * RenderScript Intrinsic test (rc/android/cts/rscpp/RSCppTest.java) used 3 for ints.
1141      * For floats, rc/android/cts/rscpp/verify.rscript uses 0.0001f.
1142      */
1143     @ExperimentalUnsignedTypes
1144     private fun validateSame(
1145         task: String,
1146         intrinsic: ByteArray,
1147         reference: ByteArray,
1148         toolkit: ByteArray,
1149         skipFourth: Boolean = false,
1150         allowedIntDelta: Int = 3,
1151         errorLogging: () -> Unit
1152     ): Boolean {
1153         val success = validateAgainstReference(
1154             task, reference, "Intrinsic", intrinsic, skipFourth, allowedIntDelta
1155         ) and validateAgainstReference(
1156             task, reference, "Toolkit", toolkit, skipFourth, allowedIntDelta
1157         )
1158         if (!success) {
1159             println("$task FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!")
1160             errorLogging()
1161         }
1162         return success
1163     }
1164 
1165     private fun validateSame(
1166         task: String,
1167         intrinsic: IntArray,
1168         reference: IntArray,
1169         toolkit: IntArray,
1170         allowedIntDelta: Int = 3,
1171         errorLogging: () -> Unit
1172     ): Boolean {
1173         val success = validateAgainstReference(
1174             task, reference, "Intrinsic", intrinsic, allowedIntDelta
1175         ) and validateAgainstReference(
1176             task, reference, "Toolkit", toolkit, allowedIntDelta
1177         )
1178         if (!success) {
1179             println("$task FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!")
1180             errorLogging()
1181         }
1182         return success
1183     }
1184 
1185     @ExperimentalUnsignedTypes
1186     private fun validateAgainstReference(
1187         task: String,
1188         in1: ByteArray,
1189         name2: String,
1190         in2: ByteArray,
1191         skipFourth: Boolean,
1192         allowedIntDelta: Int
1193     ): Boolean {
1194         if (in1.size != in2.size) {
1195             println("$task. Sizes don't match: Reference ${in1.size}, $name2 ${in2.size}")
1196             return false
1197         }
1198         var same = true
1199         val maxDetails = 80
1200         val diffs = CharArray(min(in1.size, maxDetails)) {'.'}
1201         for (i in in1.indices) {
1202             if (skipFourth && i % 4 == 3) {
1203                 continue
1204             }
1205             val delta = abs(in1[i].toUByte().toInt() - in2[i].toUByte().toInt())
1206             if (delta > allowedIntDelta) {
1207                 if (same) {
1208                     println(
1209                         "$task. At $i, Reference is ${in1[i].toUByte()}, $name2 is ${in2[i].toUByte()}"
1210                     )
1211                 }
1212                 if (i < maxDetails) diffs[i] = 'X'
1213                 same = false
1214             }
1215         }
1216         if (!same) {
1217             for (i in 0 until (min(in1.size, maxDetails) / 4)) print("%-3d|".format(i))
1218             println()
1219             println(diffs)
1220         }
1221         return same
1222     }
1223 
1224     private fun validateAgainstReference(
1225         task: String,
1226         in1: IntArray,
1227         name2: String,
1228         in2: IntArray,
1229         allowedIntDelta: Int
1230     ): Boolean {
1231         if (in1.size != in2.size) {
1232             println("$task. Sizes don't match: Reference ${in1.size}, $name2 ${in2.size}")
1233             return false
1234         }
1235         for (i in in1.indices) {
1236             val delta = abs(in1[i] - in2[i])
1237             if (delta > allowedIntDelta) {
1238                 println("$task. At $i, Reference is ${in1[i]}, $name2 is ${in2[i]}")
1239                 return false
1240             }
1241         }
1242         return true
1243     }
1244 }
1245