xref: /aosp_15_r20/frameworks/rs/toolkit/test/ReferenceBlur.kt (revision e1eccf28f96817838ad6867f7f39d2351ec11f56)
1*e1eccf28SAndroid Build Coastguard Worker /*
<lambda>null2*e1eccf28SAndroid Build Coastguard Worker  * Copyright (C) 2021 The Android Open Source Project
3*e1eccf28SAndroid Build Coastguard Worker  *
4*e1eccf28SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*e1eccf28SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*e1eccf28SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*e1eccf28SAndroid Build Coastguard Worker  *
8*e1eccf28SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*e1eccf28SAndroid Build Coastguard Worker  *
10*e1eccf28SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*e1eccf28SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*e1eccf28SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*e1eccf28SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*e1eccf28SAndroid Build Coastguard Worker  * limitations under the License.
15*e1eccf28SAndroid Build Coastguard Worker  */
16*e1eccf28SAndroid Build Coastguard Worker 
17*e1eccf28SAndroid Build Coastguard Worker package com.example.testapp
18*e1eccf28SAndroid Build Coastguard Worker 
19*e1eccf28SAndroid Build Coastguard Worker import android.renderscript.toolkit.Range2d
20*e1eccf28SAndroid Build Coastguard Worker import kotlin.math.max
21*e1eccf28SAndroid Build Coastguard Worker import kotlin.math.min
22*e1eccf28SAndroid Build Coastguard Worker import kotlin.math.pow
23*e1eccf28SAndroid Build Coastguard Worker import kotlin.math.sqrt
24*e1eccf28SAndroid Build Coastguard Worker 
25*e1eccf28SAndroid Build Coastguard Worker /**
26*e1eccf28SAndroid Build Coastguard Worker  * Reference implementation of a Blur operation.
27*e1eccf28SAndroid Build Coastguard Worker  */
28*e1eccf28SAndroid Build Coastguard Worker @ExperimentalUnsignedTypes
29*e1eccf28SAndroid Build Coastguard Worker fun referenceBlur(inputArray: ByteArray,
30*e1eccf28SAndroid Build Coastguard Worker                   vectorSize: Int,
31*e1eccf28SAndroid Build Coastguard Worker                   sizeX: Int,
32*e1eccf28SAndroid Build Coastguard Worker                   sizeY: Int,
33*e1eccf28SAndroid Build Coastguard Worker                   radius: Int = 5, restriction: Range2d?): ByteArray {
34*e1eccf28SAndroid Build Coastguard Worker     val maxRadius = 25
35*e1eccf28SAndroid Build Coastguard Worker     require (radius in 1..maxRadius) {
36*e1eccf28SAndroid Build Coastguard Worker         "RenderScriptToolkit blur. Radius should be between 1 and $maxRadius. $radius provided."
37*e1eccf28SAndroid Build Coastguard Worker     }
38*e1eccf28SAndroid Build Coastguard Worker     val gaussian = buildGaussian(radius)
39*e1eccf28SAndroid Build Coastguard Worker 
40*e1eccf28SAndroid Build Coastguard Worker     // Convert input data to float so that the blurring goes faster.
41*e1eccf28SAndroid Build Coastguard Worker     val inputValues = FloatArray(inputArray.size) { byteToUnitFloat(inputArray[it].toUByte()) }
42*e1eccf28SAndroid Build Coastguard Worker     val inputInFloat = FloatVector2dArray(inputValues, vectorSize, sizeX, sizeY)
43*e1eccf28SAndroid Build Coastguard Worker 
44*e1eccf28SAndroid Build Coastguard Worker     val scratch = horizontalBlur(inputInFloat, gaussian, radius, restriction)
45*e1eccf28SAndroid Build Coastguard Worker     val outInFloat = verticalBlur(scratch, gaussian, radius, restriction)
46*e1eccf28SAndroid Build Coastguard Worker 
47*e1eccf28SAndroid Build Coastguard Worker     // Convert the results back to bytes.
48*e1eccf28SAndroid Build Coastguard Worker     return ByteArray(outInFloat.values.size) { unitFloatClampedToUByte(outInFloat.values[it]).toByte() }
49*e1eccf28SAndroid Build Coastguard Worker }
50*e1eccf28SAndroid Build Coastguard Worker 
51*e1eccf28SAndroid Build Coastguard Worker /**
52*e1eccf28SAndroid Build Coastguard Worker  * Blurs along the horizontal direction using the specified gaussian weights.
53*e1eccf28SAndroid Build Coastguard Worker  */
horizontalBlurnull54*e1eccf28SAndroid Build Coastguard Worker private fun horizontalBlur(
55*e1eccf28SAndroid Build Coastguard Worker     input: FloatVector2dArray,
56*e1eccf28SAndroid Build Coastguard Worker     gaussian: FloatArray,
57*e1eccf28SAndroid Build Coastguard Worker     radius: Int,
58*e1eccf28SAndroid Build Coastguard Worker     restriction: Range2d?
59*e1eccf28SAndroid Build Coastguard Worker ): FloatVector2dArray {
60*e1eccf28SAndroid Build Coastguard Worker     var expandedRestriction: Range2d? = null
61*e1eccf28SAndroid Build Coastguard Worker     if (restriction != null) {
62*e1eccf28SAndroid Build Coastguard Worker         // Expand the restriction in the vertical direction so that the vertical pass
63*e1eccf28SAndroid Build Coastguard Worker         // will have all the data it needs.
64*e1eccf28SAndroid Build Coastguard Worker         expandedRestriction = Range2d(
65*e1eccf28SAndroid Build Coastguard Worker             restriction.startX,
66*e1eccf28SAndroid Build Coastguard Worker             restriction.endX,
67*e1eccf28SAndroid Build Coastguard Worker             max(restriction.startY - radius, 0),
68*e1eccf28SAndroid Build Coastguard Worker             min(restriction.endY + radius, input.sizeY)
69*e1eccf28SAndroid Build Coastguard Worker         )
70*e1eccf28SAndroid Build Coastguard Worker     }
71*e1eccf28SAndroid Build Coastguard Worker 
72*e1eccf28SAndroid Build Coastguard Worker     input.clipAccessToRange = true
73*e1eccf28SAndroid Build Coastguard Worker     val out = input.createSameSized()
74*e1eccf28SAndroid Build Coastguard Worker     out.forEach(expandedRestriction) { x, y ->
75*e1eccf28SAndroid Build Coastguard Worker         for ((gaussianIndex, delta: Int) in (-radius..radius).withIndex()) {
76*e1eccf28SAndroid Build Coastguard Worker             val v = input[x + delta, y] * gaussian[gaussianIndex]
77*e1eccf28SAndroid Build Coastguard Worker             out[x, y] += v
78*e1eccf28SAndroid Build Coastguard Worker         }
79*e1eccf28SAndroid Build Coastguard Worker     }
80*e1eccf28SAndroid Build Coastguard Worker     return out
81*e1eccf28SAndroid Build Coastguard Worker }
82*e1eccf28SAndroid Build Coastguard Worker 
83*e1eccf28SAndroid Build Coastguard Worker /**
84*e1eccf28SAndroid Build Coastguard Worker  * Blurs along the horizontal direction using the specified gaussian weights.
85*e1eccf28SAndroid Build Coastguard Worker  */
verticalBlurnull86*e1eccf28SAndroid Build Coastguard Worker private fun verticalBlur(
87*e1eccf28SAndroid Build Coastguard Worker     input: FloatVector2dArray,
88*e1eccf28SAndroid Build Coastguard Worker     gaussian: FloatArray,
89*e1eccf28SAndroid Build Coastguard Worker     radius: Int,
90*e1eccf28SAndroid Build Coastguard Worker     restriction: Range2d?
91*e1eccf28SAndroid Build Coastguard Worker ): FloatVector2dArray {
92*e1eccf28SAndroid Build Coastguard Worker     input.clipAccessToRange = true
93*e1eccf28SAndroid Build Coastguard Worker     val out = input.createSameSized()
94*e1eccf28SAndroid Build Coastguard Worker     out.forEach(restriction) { x, y ->
95*e1eccf28SAndroid Build Coastguard Worker         for ((gaussianIndex, delta: Int) in (-radius..radius).withIndex()) {
96*e1eccf28SAndroid Build Coastguard Worker             val v = input[x, y + delta] * gaussian[gaussianIndex]
97*e1eccf28SAndroid Build Coastguard Worker             out[x, y] += v
98*e1eccf28SAndroid Build Coastguard Worker         }
99*e1eccf28SAndroid Build Coastguard Worker     }
100*e1eccf28SAndroid Build Coastguard Worker     return out
101*e1eccf28SAndroid Build Coastguard Worker }
102*e1eccf28SAndroid Build Coastguard Worker 
103*e1eccf28SAndroid Build Coastguard Worker /**
104*e1eccf28SAndroid Build Coastguard Worker  * Builds an array of gaussian weights that will be used for doing the horizontal and vertical
105*e1eccf28SAndroid Build Coastguard Worker  * blur.
106*e1eccf28SAndroid Build Coastguard Worker  *
107*e1eccf28SAndroid Build Coastguard Worker  * @return An array of (2 * radius + 1) floats.
108*e1eccf28SAndroid Build Coastguard Worker  */
buildGaussiannull109*e1eccf28SAndroid Build Coastguard Worker private fun buildGaussian(radius: Int): FloatArray {
110*e1eccf28SAndroid Build Coastguard Worker     val e: Float = kotlin.math.E.toFloat()
111*e1eccf28SAndroid Build Coastguard Worker     val pi: Float = kotlin.math.PI.toFloat()
112*e1eccf28SAndroid Build Coastguard Worker     val sigma: Float = 0.4f * radius.toFloat() + 0.6f
113*e1eccf28SAndroid Build Coastguard Worker     val coefficient1: Float = 1.0f / (sqrt(2.0f * pi) * sigma)
114*e1eccf28SAndroid Build Coastguard Worker     val coefficient2: Float = -1.0f / (2.0f * sigma * sigma)
115*e1eccf28SAndroid Build Coastguard Worker 
116*e1eccf28SAndroid Build Coastguard Worker     var sum = 0.0f
117*e1eccf28SAndroid Build Coastguard Worker     val gaussian = FloatArray(radius * 2 + 1)
118*e1eccf28SAndroid Build Coastguard Worker     for (r in -radius..radius) {
119*e1eccf28SAndroid Build Coastguard Worker         val floatR: Float = r.toFloat()
120*e1eccf28SAndroid Build Coastguard Worker         val v: Float = coefficient1 * e.pow(floatR * floatR * coefficient2)
121*e1eccf28SAndroid Build Coastguard Worker         gaussian[r + radius] = v
122*e1eccf28SAndroid Build Coastguard Worker         sum += v
123*e1eccf28SAndroid Build Coastguard Worker     }
124*e1eccf28SAndroid Build Coastguard Worker 
125*e1eccf28SAndroid Build Coastguard Worker     // Normalize so that the sum of the weights equal 1f.
126*e1eccf28SAndroid Build Coastguard Worker     val normalizeFactor: Float = 1.0f / sum
127*e1eccf28SAndroid Build Coastguard Worker     for (r in -radius..radius) {
128*e1eccf28SAndroid Build Coastguard Worker         gaussian[r + radius] *= normalizeFactor
129*e1eccf28SAndroid Build Coastguard Worker     }
130*e1eccf28SAndroid Build Coastguard Worker     return gaussian
131*e1eccf28SAndroid Build Coastguard Worker }
132