This project is an Android build package and Kotlin wrapper for Rockchip's librga (im2d API). It provides hardware-accelerated 2D graphics operations optimized for Rockchip SoCs, including pre-compiled native binaries and an easy-to-use JNI interface.
This project forces the use of RGA3 cores for all operations in the JNI layer to ensure reliability on modern Rockchip SoCs.
- RGA2 Limitation: The RGA2 hardware is primarily designed for a 32-bit addressing space. On Android devices with more than 4GB of RAM, memory allocated by the application layer (like
Bitmapor generic physical continuous memory) is likely to reside in high addresses above 4GB. - Failure Symptoms: When RGA2 attempts to access these high addresses, it causes address overflow, leading to hardware errors, illegal memory access, or kernel crashes (often seen in
dmesgasRGA2 invalid address). - RGA3 Advantage: RGA3 cores support 40-bit+ addressing, making them the only reliable choice for hardware acceleration on devices with 8GB, 16GB, or more RAM.
- Removal of Fill Operation: The hardware
fillfeature is unique to the RGA2 core. Forcing it to run on RGA3 or using it on high-memory devices would lead to the same 4GB limitation and potential crashes. To ensure system stability, this library has removed theimfillmethod.
Each hardware operation (resize, crop, etc.) is now explicitly scheduled to RGA3 cores (Core0 and Core1) within the native JNI implementation. This eliminates the need for manual configuration and prevents unpredictable failures from system defaults.
- Pre-compiled Binaries: Includes optimized
librga.soandlibrga.aforarm64-v8aandarmeabi-v7a. - Hardware Acceleration: Full support for Rockchip's RGA hardware engine.
- Kotlin Wrapper: Clean, idiomatic Kotlin API for image processing.
- Comprehensive Operations: Support for resize, crop, rotate, flip, blend, and color space conversion (NV21/RGBA/etc.).
By default, the librga module is configured as an Android Application (com.android.application) so you can directly install and run it on a Rockchip device to verify functionality.
To run the test application:
- Open the project in Android Studio.
- Run the
librgaconfiguration. - You will see the RGA JNI Test interface where you can run individual tests (Copy, Resize, Crop, etc.) or all tests at once.
To build the AAR Library:
- Open
librga/build.gradle. - Change the plugin from
com.android.applicationtocom.android.library.plugins { // id 'com.android.application' // Comment this out id 'com.android.library' // Use this for AAR build id 'org.jetbrains.kotlin.android' }
- Remove or comment out the
applicationIdindefaultConfig:defaultConfig { // applicationId "com.rockchip.librga" // Remove for library build ... }
- Run the assemble task:
./gradlew :librga:assembleRelease
- The AAR will be generated at
librga/build/outputs/aar/librga-release.aar.
The library automatically configures and forces the use of RGA3 cores (Core0 and Core1) for all operations within the native JNI layer. This ensures compatibility with large-memory Android devices and optimal performance without requiring any manual configuration from the developer.
val srcBuffer = Rga.createRgaBufferFromBitmap(srcBitmap)
val dstBuffer = Rga.createRgaBufferFromBitmap(dstBitmap)
// Call hardware resize (automatically uses RGA3)
val result = Rga.imresize(srcBuffer, dstBuffer)
if (result == Rga.IM_STATUS_SUCCESS) {
Rga.copyRgaBufferToBitmap(dstBuffer, dstBitmap)
}For complex pipelines, use the Job system to batch multiple operations. This is more efficient as it reduces JNI overhead and allows the driver to optimize execution on RGA3.
// 1. Begin a new job
val jobHandle = Rga.imbeginJob()
if (jobHandle > 0) {
// 2. Add multiple tasks to the job
// Task 1: Rotate (src -> intermediate)
Rga.imrotateTask(jobHandle, srcBuffer, intermediateBuffer, Rga.IM_HAL_TRANSFORM_ROT_90)
// Task 2: Vertical Flip (intermediate -> dst)
Rga.imflipTask(jobHandle, intermediateBuffer, dstBuffer, Rga.IM_HAL_TRANSFORM_FLIP_V)
// 3. Submit and execute the job
val result = Rga.imendJob(jobHandle, Rga.IM_SYNC)
if (result == Rga.IM_STATUS_SUCCESS) {
Rga.copyRgaBufferToBitmap(dstBuffer, dstBitmap)
}
}librga/: The core Android Library module.librga/src/main/jniLibs/: Pre-compiled Rockchip RGA native libraries.
This library uses a hybrid approach for native dependencies to ensure both performance and ease of use:
- Pre-compiled Binaries: Rockchip's proprietary
librga.soandlibrga.aare stored injniLibs. These are provided by the SoC vendor and do not have public source code. - Source-built JNI: The
librga_jni.so(the actual wrapper) is compiled fromsrc/main/cpp/librga_jni.cppduring the Gradle build process. This ensures the wrapper is always in sync with the Kotlin API. - Automatic Bundling: When building the project, the Android Gradle Plugin automatically bundles the vendor libraries, the compiled JNI wrapper, and the required
libc++_shared.sointo the final AAR.
Supported ABIs: arm64-v8a, armeabi-v7a.
For the latest RGA documentation, hardware specifications, and the underlying native implementation, please refer to the official Rockchip repository:
This Android wrapper is built based on the im2d API provided by the official repository above.
- Job / Multi-tasking (Batch Processing)
- Copy
- Resize
- Rescale
- Crop
- Rotate
- Flip
- Translate
- Blend
- Composite
- Color Format Conversion
// Status codes
const val IM_STATUS_SUCCESS = 1
// Rotation transforms
const val IM_HAL_TRANSFORM_ROT_90 = 1 shl 0
const val IM_HAL_TRANSFORM_ROT_180 = 1 shl 1
const val IM_HAL_TRANSFORM_ROT_270 = 1 shl 2
const val IM_HAL_TRANSFORM_FLIP_H = 1 shl 3 // Horizontal flip
const val IM_HAL_TRANSFORM_FLIP_V = 1 shl 4 // Vertical flip
const val IM_HAL_TRANSFORM_FLIP_H_V = 1 shl 5 // Horizontal and vertical flip
// Blend modes (Porter-Duff)
const val IM_ALPHA_BLEND_SRC_OVER = 1 shl 6
const val IM_ALPHA_BLEND_SRC = 1 shl 7
const val IM_ALPHA_BLEND_DST = 1 shl 8
const val IM_ALPHA_BLEND_SRC_IN = 1 shl 9
const val IM_ALPHA_BLEND_DST_IN = 1 shl 10
const val IM_ALPHA_BLEND_SRC_OUT = 1 shl 11
const val IM_ALPHA_BLEND_DST_OUT = 1 shl 12
const val IM_ALPHA_BLEND_DST_OVER = 1 shl 13
const val IM_ALPHA_BLEND_SRC_ATOP = 1 shl 14
const val IM_ALPHA_BLEND_DST_ATOP = 1 shl 15
const val IM_ALPHA_BLEND_XOR = 1 shl 16
// Common pixel formats
const val RK_FORMAT_RGBA_8888 = 0x0
const val RK_FORMAT_RGBX_8888 = 0x1
const val RK_FORMAT_RGB_888 = 0x2
const val RK_FORMAT_BGRA_8888 = 0x3
const val RK_FORMAT_RGB_565 = 0x4
const val RK_FORMAT_RGBA_5551 = 0x5
const val RK_FORMAT_RGBA_4444 = 0x6
const val RK_FORMAT_BGR_888 = 0x7
const val RK_FORMAT_YCbCr_422_SP = 0x8
const val RK_FORMAT_YCbCr_422_P = 0x9
const val RK_FORMAT_YCbCr_420_SP = 0xa
const val RK_FORMAT_YCbCr_420_P = 0xb
const val RK_FORMAT_YCrCb_422_SP = 0xc
const val RK_FORMAT_YCrCb_422_P = 0xd
const val RK_FORMAT_YCrCb_420_SP = 0xe
const val RK_FORMAT_YCrCb_420_P = 0xf
// Scheduler configuration
const val IM_CONFIG_SCHEDULER_CORE = 0
const val IM_SCHEDULER_RGA3_CORE0 = 1 shl 0
const val IM_SCHEDULER_RGA3_CORE1 = 1 shl 1
const val IM_SCHEDULER_RGA2_CORE0 = 1 shl 2
const val IM_SCHEDULER_RGA2_CORE1 = 1 shl 3Represents an image buffer for RGA operations.
data class RgaBuffer(
val width: Int,
val height: Int,
val format: Int,
val wstride: Int = width,
val hstride: Int = height,
val fd: Int = -1, // File descriptor (for buffer sharing)
val handle: Int = 0, // Buffer handle
val ptr: ByteBuffer? = null, // Direct ByteBuffer
val hardwareBuffer: Any? = null // Android HardwareBuffer (API 26+)
)Defines a rectangular region for cropping and filling operations.
data class RgaRect(
val x: Int,
val y: Int,
val width: Int,
val height: Int
)external fun imbeginJob(flags: Long = 0): Long
external fun imendJob(jobHandle: Long, syncMode: Int = IM_SYNC): Int
external fun imcancelJob(jobHandle: Long): IntCopies source image to destination.
external fun imcopy(src: RgaBuffer, dst: RgaBuffer): Int
external fun imcopyTask(jobHandle: Long, src: RgaBuffer, dst: RgaBuffer): IntExample:
val srcBuffer = Rga.createRgaBufferFromBitmap(sourceBitmap)
val dstBuffer = Rga.createRgaBufferFromBitmap(destinationBitmap)
val result = Rga.imcopy(srcBuffer, dstBuffer)
// Or use Task API inside a job:
// Rga.imcopyTask(jobHandle, srcBuffer, dstBuffer)Resizes source image to destination dimensions.
external fun imresize(src: RgaBuffer, dst: RgaBuffer, fx: Double = 0.0, fy: Double = 0.0): Int
external fun imresizeTask(jobHandle: Long, src: RgaBuffer, dst: RgaBuffer, fx: Double = 0.0, fy: Double = 0.0): IntExample:
val srcBuffer = Rga.createRgaBufferFromBitmap(sourceBitmap)
val dstBuffer = Rga.createRgaBufferFromBitmap(destinationBitmap)
val result = Rga.imresize(srcBuffer, dstBuffer, 0.5, 0.5) // Scale to 50%Rescales source image to destination dimensions using specific scale factors.
external fun imrescale(src: RgaBuffer, dst: RgaBuffer, fx: Double, fy: Double): Int
external fun imrescaleTask(jobHandle: Long, src: RgaBuffer, dst: RgaBuffer, fx: Double, fy: Double): IntExample:
val srcBuffer = Rga.createRgaBufferFromBitmap(sourceBitmap)
val dstBuffer = Rga.createRgaBufferFromBitmap(destinationBitmap)
val result = Rga.imrescale(srcBuffer, dstBuffer, 0.5, 0.5) // Scale to 50%external fun imcrop(src: RgaBuffer, dst: RgaBuffer, rect: RgaRect): Int
external fun imcropTask(jobHandle: Long, src: RgaBuffer, dst: RgaBuffer, rect: RgaRect): Intexternal fun imrotate(src: RgaBuffer, dst: RgaBuffer, rotation: Int): Int
external fun imrotateTask(jobHandle: Long, src: RgaBuffer, dst: RgaBuffer, rotation: Int): Intexternal fun imflip(src: RgaBuffer, dst: RgaBuffer, mode: Int): Int
external fun imflipTask(jobHandle: Long, src: RgaBuffer, dst: RgaBuffer, mode: Int): Intexternal fun imtranslate(src: RgaBuffer, dst: RgaBuffer, x: Int, y: Int): Int
external fun imtranslateTask(jobHandle: Long, src: RgaBuffer, dst: RgaBuffer, x: Int, y: Int): Intexternal fun imblend(src: RgaBuffer, dst: RgaBuffer, mode: Int = IM_ALPHA_BLEND_SRC_OVER): Int
external fun imblendTask(jobHandle: Long, src: RgaBuffer, dst: RgaBuffer, mode: Int = IM_ALPHA_BLEND_SRC_OVER): Intexternal fun imcomposite(srcA: RgaBuffer, srcB: RgaBuffer, dst: RgaBuffer, mode: Int = IM_ALPHA_BLEND_SRC_OVER): Int
external fun imcompositeTask(jobHandle: Long, srcA: RgaBuffer, srcB: RgaBuffer, dst: RgaBuffer, mode: Int = IM_ALPHA_BLEND_SRC_OVER): Intexternal fun imcvtcolor(src: RgaBuffer, dst: RgaBuffer, sfmt: Int, dfmt: Int): Int
external fun imcvtcolorTask(jobHandle: Long, src: RgaBuffer, dst: RgaBuffer, sfmt: Int, dfmt: Int): Intfun createRgaBufferFromBitmap(bitmap: android.graphics.Bitmap, format: Int = Rga.RK_FORMAT_RGBA_8888): RgaBufferExample:
val buffer = Rga.createRgaBufferFromBitmap(bitmap)fun copyRgaBufferToBitmap(srcBuffer: RgaBuffer, dstBitmap: android.graphics.Bitmap)Example:
Rga.copyRgaBufferToBitmap(buffer, bitmap)Crops a region from an RGA buffer and returns it as a new Bitmap. Handles hardware cropping and format conversion.
fun cropToBitmap(srcBuffer: RgaBuffer, rect: android.graphics.Rect): android.graphics.BitmapPerforms hardware-accelerated color space conversion from NV21/YUV to RGBA.
fun convertNv21ToRgba(srcBuffer: RgaBuffer, dstBuffer: RgaBuffer)Copies the content of an RGBA RgaBuffer to an Android Bitmap.
fun copyRgbaToBitmap(rgbaBuffer: RgaBuffer, dstBitmap: android.graphics.Bitmap)fun bitmapToByteBuffer(bitmap: android.graphics.Bitmap): java.nio.ByteBufferExample:
val byteBuffer = Rga.bitmapToByteBuffer(bitmap)fun createBufferFromFd(fd: Int, width: Int, height: Int, format: Int, wstride: Int = width, hstride: Int = height): RgaBufferExample:
val buffer = Rga.createBufferFromFd(fd, width, height, Rga.RK_FORMAT_RGBA_8888)fun createBufferFromByteBuffer(buffer: java.nio.ByteBuffer, width: Int, height: Int, format: Int, wstride: Int = width, hstride: Int = height): RgaBufferExample:
val buffer = Rga.createBufferFromByteBuffer(byteBuffer, width, height, Rga.RK_FORMAT_RGBA_8888)fun createRgaBufferFromNv21(nv21Data: ByteArray, width: Int, height: Int, format: Int = Rga.RK_FORMAT_YCrCb_420_SP): RgaBufferExample:
val buffer = Rga.createRgaBufferFromNv21(nv21ByteArray, width, height)fun fillRgaBufferWithNv21(buffer: java.nio.ByteBuffer, nv21Data: ByteArray, width: Int, height: Int, format: Int = Rga.RK_FORMAT_YCrCb_420_SP): RgaBufferExample:
val buffer = Rga.fillRgaBufferWithNv21(existingByteBuffer, nv21ByteArray, width, height)To re-package this library as a standalone project, follow these steps:
-
Root Directory:
build.gradlegradle.propertiesgradlewgradlew.batsettings.gradle.gitignore
-
Gradle Wrapper Directory:
gradle/(entire directory)
-
Library Source Directory:
librga/(entire directory)
-
Create a new directory for your re-packaged project:
mkdir your-new-project-name cd your-new-project-name -
Copy the required files from the original project:
# Copy root files cp /path/to/original/project/build.gradle . cp /path/to/original/project/gradle.properties . cp /path/to/original/project/gradlew . cp /path/to/original/project/gradlew.bat . cp /path/to/original/project/settings.gradle . cp /path/to/original/project/.gitignore . # Copy gradle wrapper directory cp -r /path/to/original/project/gradle/ . # Copy library directory cp -r /path/to/original/project/librga/ .
-
Update settings.gradle (if needed): Make sure the module is properly included:
include ':librga' rootProject.name = "YourNewProjectName"
-
Update build.gradle (module level - librga/build.gradle): Adjust the library configuration as needed for your use case:
android { // Update namespace, version, etc. as needed namespace 'com.yourcompany.yourlibrary' compileSdk 34 defaultConfig { minSdk 21 targetSdk 34 versionCode 1 versionName "1.0" } // Other configurations... }
-
Update package names in source files (optional): If you want to change the package name, update:
librga/src/main/kotlin/com/rockchip/librga/Rga.ktlibrga/src/main/kotlin/com/rockchip/librga/TestActivity.ktlibrga/src/main/AndroidManifest.xmllibrga/src/main/res/values/strings.xml
-
Build the project:
./gradlew build
-
Test the library: Create a sample app to verify that the re-packaged library works correctly.
-
Add the library to your project: Place the
librgadirectory in your project root and include it in yoursettings.gradle:include ':librga' -
Add dependency in your app's build.gradle:
dependencies { implementation project(':librga') }
-
Initialize and use the RGA library:
// The RGA library is automatically initialized when the object is accessed val result = Rga.imcopy(srcBuffer, dstBuffer)
This project is licensed under the Apache 2.0 License - see the LICENSE file for details.
