Skip to content
This repository was archived by the owner on Oct 23, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package com.android.example.rsmigration

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.View
Expand Down Expand Up @@ -83,15 +84,20 @@ class MainActivity : AppCompatActivity() {
mInputImage = loadBitmap(R.drawable.data)

// Set up image processors
mImageProcessors =
arrayOf(
// RenderScript intrinsics
RenderScriptImageProcessor(this, useIntrinsic = true),
// RenderScript script kernels
RenderScriptImageProcessor(this, useIntrinsic = false),
// Vulkan compute pipeline
VulkanImageProcessor(this)
)
val imageProcessors = mutableListOf(
// RenderScript intrinsics
RenderScriptImageProcessor(this, useIntrinsic = true),
// RenderScript script kernels
RenderScriptImageProcessor(this, useIntrinsic = false),
// Vulkan compute pipeline
VulkanImageProcessor(this))

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// RenderEffect
imageProcessors.add(RenderEffectImageProcessor())
}
mImageProcessors = imageProcessors.toTypedArray()

mImageProcessors.forEach { processor ->
processor.configureInputAndOutput(mInputImage, NUMBER_OF_OUTPUT_IMAGES)
}
Expand Down Expand Up @@ -275,10 +281,10 @@ class MainActivity : AppCompatActivity() {
mLatencyTextView.setText(R.string.benchmark_running_text)

// Start a new thread to run benchmark without blocking the UI thread.
Thread(Runnable {
Thread {
synchronized(mLock) {
// Run benchmark.
val avgMs = runBenchmark(mCurrentProcessor, mFilterMode, mSeekBar.progress);
val avgMs = runBenchmark(mCurrentProcessor, mFilterMode, mSeekBar.progress)

// Display benchmark result and re-enable widgets.
this@MainActivity.runOnUiThread {
Expand All @@ -289,6 +295,6 @@ class MainActivity : AppCompatActivity() {
mBenchmarkButton.isEnabled = true
}
}
}).start()
}.start()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package com.android.example.rsmigration

import android.annotation.SuppressLint
import android.graphics.*
import android.hardware.HardwareBuffer
import android.media.ImageReader
import android.os.Build
import androidx.annotation.RequiresApi
import java.lang.RuntimeException
import kotlin.math.cos
import kotlin.math.sin

@RequiresApi(Build.VERSION_CODES.S)
class RenderEffectImageProcessor : ImageProcessor {
override val name = "RenderEffect"
private var params: Params? = null

override fun configureInputAndOutput(inputImage: Bitmap, numberOfOutputImages: Int) {
params = Params(
inputImage,
numberOfOutputImages
)
}

private fun applyEffect(it: Params, renderEffect: RenderEffect, outputIndex: Int): Bitmap {
it.renderNode.setRenderEffect(renderEffect)
val renderCanvas = it.renderNode.beginRecording()
renderCanvas.drawBitmap(it.bitmap, 0f, 0f, null)
it.renderNode.endRecording()
it.hardwareRenderer.createRenderRequest()
.setWaitForPresent(true)
.syncAndDraw()

val image = it.imageReader.acquireNextImage() ?: throw RuntimeException("No Image")
val hardwareBuffer = image.hardwareBuffer ?: throw RuntimeException("No HardwareBuffer")
val bitmap = Bitmap.wrapHardwareBuffer(hardwareBuffer, null)
?: throw RuntimeException("Create Bitmap Failed")
hardwareBuffer.close()
image.close()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardwareBuffer can be closed at this point because Bitmap.wrapHardwareBuffer already acquires another reference. If the hardwareBuffer is closed here, the hardwareBuffers field in Params can also be removed, I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent suggestion!

return bitmap
}

override fun rotateHue(radian: Float, outputIndex: Int): Bitmap {
params?.let {
val colorMatrix = if (radian == 0f) {
ColorMatrix()
} else {
val cos = cos(radian.toDouble())
val sin = sin(radian.toDouble())
ColorMatrix(floatArrayOf(
(.299 + .701 * cos + .168 * sin).toFloat(), //0
(.587 - .587 * cos + .330 * sin).toFloat(), //1
(.114 - .114 * cos - .497 * sin).toFloat(), //2
0f, 0f, //3,4
(.299 - .299 * cos - .328 * sin).toFloat(), //5
(.587 + .413 * cos + .035 * sin).toFloat(), //6
(.114 - .114 * cos + .292 * sin).toFloat(), //7
0f, 0f, //8,9
(.299 - .300 * cos + 1.25 * sin).toFloat(), //10
(.587 - .588 * cos - 1.05 * sin).toFloat(), //11
(.114 + .886 * cos - .203 * sin).toFloat(), //12
0f, 0f, 0f, 0f, 0f, //13,14,15,16,17
1f, 0f //18,19
))
}
val colorFilterEffect =
RenderEffect.createColorFilterEffect(ColorMatrixColorFilter(colorMatrix))
return applyEffect(it, colorFilterEffect, outputIndex)
}
throw RuntimeException("Not configured!")
}

override fun blur(radius: Float, outputIndex: Int): Bitmap {
params?.let {
val blurRenderEffect = RenderEffect.createBlurEffect(
radius, radius,
Shader.TileMode.MIRROR
)
return applyEffect(it, blurRenderEffect, outputIndex)
}
throw RuntimeException("Not configured!")
}

override fun cleanup() {
params?.let {
params = null
it.imageReader.close()
it.renderNode.discardDisplayList()
it.hardwareRenderer.destroy()
}
}

inner class Params(val bitmap: Bitmap, numberOfOutputImages: Int) {
@SuppressLint("WrongConstant")
val imageReader = ImageReader.newInstance(
bitmap.width, bitmap.height,
PixelFormat.RGBA_8888, numberOfOutputImages,
HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT
)
val renderNode = RenderNode("RenderEffect")
val hardwareRenderer = HardwareRenderer()

init {
hardwareRenderer.setSurface(imageReader.surface)
hardwareRenderer.setContentRoot(renderNode)
renderNode.setPosition(0, 0, imageReader.width, imageReader.height)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<string-array name="processor_array" tools:ignore="InconsistentArrays">
<item>@string/renderscript_intrinsics</item>
<item>@string/renderscript_scripts</item>
<item>@string/vulkan</item>
<item>@string/render_effect</item>
</string-array>

</resources>
17 changes: 11 additions & 6 deletions RenderScriptMigrationSample/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name">RenderScript Migration Sample</string>
<string name="image_content_desc">Image</string>
<string name="latency_text">Latency: %1$f ms</string>
<string name="benchmark_running_text">Running benchmark...</string>
<string name="benchmark_running_text">Running benchmark</string>
<string name="benchmark_result_text">Avg. Latency: %1$f ms</string>
<string name="benchmark_button">Benchmark</string>
<string name="prcessor_spinner_label">Choose processor:</string>
<string-array name="processor_array">
<item>Renderscript Intrinsics</item>
<item>Renderscript Scripts</item>
<item>Vulkan</item>
<string name="renderscript_intrinsics">Renderscript Intrinsics</string>
<string name="renderscript_scripts">Renderscript Scripts</string>
<string name="vulkan">Vulkan</string>
<string name="render_effect">RenderEffect</string>

<string-array name="processor_array" tools:ignore="InconsistentArrays">
<item>@string/renderscript_intrinsics</item>
<item>@string/renderscript_scripts</item>
<item>@string/vulkan</item>
</string-array>
<string name="filter_spinner_label">Choose filter:</string>
<string-array name="filter_array">
Expand Down