Add Toolkit class definition and test files.
Adds the Java/Kt class definition of the RenderScript Toolkit.
Adds the JNI file that calls the C++ code.
Add test files. For each Intrinsic, we do a 3-way comparison:
- Toolkit
- RenderScript Intrinsic
- Reference code written in Kotlin
Bug: 178476084
Test: This CL does not have build files and has not been independently tested. that will come with the next CL.
Change-Id: I0c8f85465a2dd42b42a98f6c7847b55fe13b5994
diff --git a/toolkit/java/Toolkit.kt b/toolkit/java/Toolkit.kt
new file mode 100644
index 0000000..41dc432
--- /dev/null
+++ b/toolkit/java/Toolkit.kt
@@ -0,0 +1,1558 @@
+/*
+ * 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 android.renderscript.toolkit
+
+import android.graphics.Bitmap
+import java.lang.IllegalArgumentException
+
+// This string is used for error messages.
+private const val externalName = "RenderScript Toolkit"
+
+/**
+ * A collection of high-performance graphic utility functions like blur and blend.
+ *
+ * This toolkit provides ten image manipulation functions: blend, blur, color matrix, convolve,
+ * histogram, histogramDot, lut, lut3d, resize, and YUV to RGB. These functions execute
+ * multithreaded on the CPU.
+ *
+ * Most of the functions have two variants: one that manipulates Bitmaps, the other ByteArrays.
+ * For ByteArrays, you need to specify the width and height of the data to be processed, as
+ * well as the number of bytes per pixel. For most use cases, this will be 4.
+ *
+ * You should instantiate the Toolkit once and reuse it throughout your application.
+ * On instantiation, the Toolkit creates a thread pool that's used for processing all the functions.
+ * You can limit the number of poolThreads used by the Toolkit via the constructor. The poolThreads
+ * are destroyed once the Toolkit is destroyed, after any pending work is done.
+ *
+ * This library is thread safe. You can call methods from different poolThreads. The functions will
+ * execute sequentially.
+ *
+ * A native C++ version of this Toolkit is available.
+ *
+ * This toolkit can be used as a replacement for most RenderScript Intrinsic functions. Compared
+ * to RenderScript, it's simpler to use and more than twice as fast on the CPU. However RenderScript
+ * Intrinsics allow more flexibility for the type of allocation supported. In particular, this
+ * toolkit does not support allocations of floats.
+ */
+class Toolkit {
+ /**
+ * Blends a source buffer with the destination buffer.
+ *
+ * Blends a source buffer and a destination buffer, placing the result in the destination
+ * buffer. The blending is done pairwise between two corresponding RGBA values found in
+ * each buffer. The mode parameter specifies one of fifteen supported blending operations.
+ * See {@link BlendingMode}.
+ *
+ * A variant of this method is also available to blend Bitmaps.
+ *
+ * An optional range parameter can be set to restrict the operation to a rectangular subset
+ * of each buffer. If provided, the range must be wholly contained with the dimensions
+ * described by sizeX and sizeY.
+ *
+ * The source and destination buffer must have the same dimensions. Both arrays should have
+ * a size greater or equal to sizeX * sizeY * 4. The buffers have a row-major layout.
+ *
+ * @param mode The specific blending operation to do.
+ * @param sourceArray The RGBA input buffer.
+ * @param destArray The destination buffer. Used for input and output.
+ * @param sizeX The width of both buffers, as a number of RGBA values.
+ * @param sizeY The height of both buffers, as a number of RGBA values.
+ * @param restriction When not null, restricts the operation to a 2D range of pixels.
+ */
+ @JvmOverloads
+ fun blend(
+ mode: BlendingMode,
+ sourceArray: ByteArray,
+ destArray: ByteArray,
+ sizeX: Int,
+ sizeY: Int,
+ restriction: Range2d? = null
+ ) {
+ require(sourceArray.size >= sizeX * sizeY * 4) {
+ "$externalName blend. sourceArray is too small for the given dimensions. " +
+ "$sizeX*$sizeY*4 < ${sourceArray.size}."
+ }
+ require(destArray.size >= sizeX * sizeY * 4) {
+ "$externalName blend. sourceArray is too small for the given dimensions. " +
+ "$sizeX*$sizeY*4 < ${sourceArray.size}."
+ }
+ validateRestriction("blend", sizeX, sizeY, restriction)
+
+ nativeBlend(nativeHandle, mode.value, sourceArray, destArray, sizeX, sizeY, restriction)
+ }
+
+ /**
+ * Blends a source bitmap with the destination bitmap.
+ *
+ * Blends a source bitmap and a destination bitmap, placing the result in the destination
+ * bitmap. The blending is done pairwise between two corresponding RGBA values found in
+ * each bitmap. The mode parameter specify one of fifteen supported blending operations.
+ * See {@link BlendingMode}.
+ *
+ * A variant of this method is available to blend ByteArrays.
+ *
+ * The bitmaps should have identical width and height, and have a config of ARGB_8888.
+ * Bitmaps with a stride different than width * vectorSize are not currently supported.
+ *
+ * An optional range parameter can be set to restrict the operation to a rectangular subset
+ * of each bitmap. If provided, the range must be wholly contained with the dimensions
+ * of the bitmap.
+ *
+ * @param mode The specific blending operation to do.
+ * @param sourceBitmap The RGBA input buffer.
+ * @param destBitmap The destination buffer. Used for input and output.
+ * @param restriction When not null, restricts the operation to a 2D range of pixels.
+ */
+ @JvmOverloads
+ fun blend(
+ mode: BlendingMode,
+ sourceBitmap: Bitmap,
+ destBitmap: Bitmap,
+ restriction: Range2d? = null
+ ) {
+ validateBitmap("blend", sourceBitmap)
+ validateBitmap("blend", destBitmap)
+ require(
+ sourceBitmap.width == destBitmap.width &&
+ sourceBitmap.height == destBitmap.height
+ ) {
+ "$externalName blend. Source and destination bitmaps should be the same size. " +
+ "${sourceBitmap.width}x${sourceBitmap.height} and " +
+ "${destBitmap.width}x${destBitmap.height} provided."
+ }
+ require(sourceBitmap.config == destBitmap.config) {
+ "RenderScript Toolkit blend. Source and destination bitmaps should have the same " +
+ "config. ${sourceBitmap.config} and ${destBitmap.config} provided."
+ }
+ validateRestriction("blend", sourceBitmap.width, sourceBitmap.height, restriction)
+
+ nativeBlendBitmap(nativeHandle, mode.value, sourceBitmap, destBitmap, restriction)
+ }
+
+ /**
+ * Blurs an image.
+ *
+ * Performs a Gaussian blur of an image and returns result in a ByteArray buffer. A variant of
+ * this method is available to blur Bitmaps.
+ *
+ * The radius determines which pixels are used to compute each blurred pixels. This Toolkit
+ * accepts values between 1 and 25. Larger values create a more blurred effect but also
+ * take longer to compute. When the radius extends past the edge, the edge pixel will
+ * be used as replacement for the pixel that's out off boundary.
+ *
+ * Each input pixel can either be represented by four bytes (RGBA format) or one byte
+ * for the less common blurring of alpha channel only image.
+ *
+ * An optional range parameter can be set to restrict the operation to a rectangular subset
+ * of each buffer. If provided, the range must be wholly contained with the dimensions
+ * described by sizeX and sizeY. NOTE: The output buffer will still be full size, with the
+ * section that's not blurred all set to 0. This is to stay compatible with RenderScript.
+ *
+ * The source buffer should be large enough for sizeX * sizeY * mVectorSize bytes. It has a
+ * row-major layout.
+ *
+ * @param inputArray The buffer of the image to be blurred.
+ * @param vectorSize Either 1 or 4, the number of bytes in each cell, i.e. A vs. RGBA.
+ * @param sizeX The width of both buffers, as a number of 1 or 4 byte cells.
+ * @param sizeY The height of both buffers, as a number of 1 or 4 byte cells.
+ * @param radius The radius of the pixels used to blur, a value from 1 to 25.
+ * @param restriction When not null, restricts the operation to a 2D range of pixels.
+ * @return The blurred pixels, a ByteArray of size.
+ */
+ @JvmOverloads
+ fun blur(
+ inputArray: ByteArray,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ radius: Int = 5,
+ restriction: Range2d? = null
+ ): ByteArray {
+ require(vectorSize == 1 || vectorSize == 4) {
+ "$externalName blur. The vectorSize should be 1 or 4. $vectorSize provided."
+ }
+ require(inputArray.size >= sizeX * sizeY * vectorSize) {
+ "$externalName blur. inputArray is too small for the given dimensions. " +
+ "$sizeX*$sizeY*$vectorSize < ${inputArray.size}."
+ }
+ require(radius in 1..25) {
+ "$externalName blur. The radius should be between 1 and 25. $radius provided."
+ }
+ validateRestriction("blur", sizeX, sizeY, restriction)
+
+ val outputArray = ByteArray(inputArray.size)
+ nativeBlur(
+ nativeHandle, inputArray, vectorSize, sizeX, sizeY, radius, outputArray, restriction
+ )
+ return outputArray
+ }
+
+ /**
+ * Blurs an image.
+ *
+ * Performs a Gaussian blur of a Bitmap and returns result as a Bitmap. A variant of
+ * this method is available to blur ByteArrays.
+ *
+ * The radius determines which pixels are used to compute each blurred pixels. This Toolkit
+ * accepts values between 1 and 25. Larger values create a more blurred effect but also
+ * take longer to compute. When the radius extends past the edge, the edge pixel will
+ * be used as replacement for the pixel that's out off boundary.
+ *
+ * This method supports input Bitmap of config ARGB_8888 and ALPHA_8. Bitmaps with a stride
+ * different than width * vectorSize are not currently supported. The returned Bitmap has the
+ * same config.
+ *
+ * An optional range parameter can be set to restrict the operation to a rectangular subset
+ * of each buffer. If provided, the range must be wholly contained with the dimensions
+ * described by sizeX and sizeY. NOTE: The output Bitmap will still be full size, with the
+ * section that's not blurred all set to 0. This is to stay compatible with RenderScript.
+ *
+ * @param inputBitmap The buffer of the image to be blurred.
+ * @param radius The radius of the pixels used to blur, a value from 1 to 25. Default is 5.
+ * @param restriction When not null, restricts the operation to a 2D range of pixels.
+ * @return The blurred Bitmap.
+ */
+ @JvmOverloads
+ fun blur(inputBitmap: Bitmap, radius: Int = 5, restriction: Range2d? = null): Bitmap {
+ validateBitmap("blur", inputBitmap)
+ require(radius in 1..25) {
+ "$externalName blur. The radius should be between 1 and 25. $radius provided."
+ }
+ validateRestriction("blur", inputBitmap.width, inputBitmap.height, restriction)
+
+ val outputBitmap = createCompatibleBitmap(inputBitmap)
+ nativeBlurBitmap(nativeHandle, inputBitmap, outputBitmap, radius, restriction)
+ return outputBitmap
+ }
+
+ /**
+ * Identity matrix that can be passed to the {@link RenderScriptToolkit::colorMatrix} method.
+ *
+ * Using this matrix will result in no change to the pixel through multiplication although
+ * the pixel value can still be modified by the add vector, or transformed to a different
+ * format.
+ */
+ val identityMatrix
+ get() = floatArrayOf(
+ 1f, 0f, 0f, 0f,
+ 0f, 1f, 0f, 0f,
+ 0f, 0f, 1f, 0f,
+ 0f, 0f, 0f, 1f
+ )
+
+ /**
+ * Matrix to turn color pixels to a grey scale.
+ *
+ * Use this matrix with the {@link RenderScriptToolkit::colorMatrix} method to convert an
+ * image from color to greyscale.
+ */
+ val greyScaleColorMatrix
+ get() = floatArrayOf(
+ 0.299f, 0.299f, 0.299f, 0f,
+ 0.587f, 0.587f, 0.587f, 0f,
+ 0.114f, 0.114f, 0.114f, 0f,
+ 0f, 0f, 0f, 1f
+ )
+
+ /**
+ * Matrix to convert RGB to YUV.
+ *
+ * Use this matrix with the {@link RenderScriptToolkit::colorMatrix} method to convert the
+ * first three bytes of each pixel from RGB to YUV. This leaves the last byte (the alpha
+ * channel) untouched.
+ *
+ * This is a simplistic conversion. Most YUV buffers have more complicated format, not supported
+ * by this method.
+ */
+ val rgbToYuvMatrix
+ get() = floatArrayOf(
+ 0.299f, -0.14713f, 0.615f, 0f,
+ 0.587f, -0.28886f, -0.51499f, 0f,
+ 0.114f, 0.436f, -0.10001f, 0f,
+ 0f, 0f, 0f, 1f
+ )
+
+ /**
+ * Matrix to convert YUV to RGB.
+ *
+ * Use this matrix with the {@link RenderScriptToolkit::colorMatrix} method to convert the
+ * first three bytes of each pixel from YUV to RGB. This leaves the last byte (the alpha
+ * channel) untouched.
+ *
+ * This is a simplistic conversion. Most YUV buffers have more complicated format, not supported
+ * by this method. Use {@link RenderScriptToolkit::yuvToRgb} to convert these buffers.
+ */
+ val yuvToRgbMatrix
+ get() = floatArrayOf(
+ 1f, 1f, 1f, 0f,
+ 0f, -0.39465f, 2.03211f, 0f,
+ 1.13983f, -0.5806f, 0f, 0f,
+ 0f, 0f, 0f, 1f
+ )
+
+ /**
+ * Transform an image using a color matrix.
+ *
+ * Converts a 2D array of vectors of unsigned bytes, multiplying each vectors by a 4x4 matrix
+ * and adding an optional vector.
+ *
+ * Each input vector is composed of 1-4 unsigned bytes. If less than 4 bytes, it's extended to
+ * 4, padding with zeroes. The unsigned bytes are converted from 0-255 to 0.0-1.0 floats
+ * before the multiplication is done.
+ *
+ * The resulting value is normalized from 0.0-1.0 to a 0-255 value and stored in the output.
+ * If the output vector size is less than four, the unused channels are discarded.
+ *
+ * If addVector is not specified, a vector of zeroes is added, i.e. a noop.
+ *
+ * Like the RenderScript Intrinsics, vectorSize of size 3 are padded to occupy 4 bytes.
+ *
+ * Check identityMatrix, greyScaleColorMatrix, rgbToYuvMatrix, and yuvToRgbMatrix for sample
+ * matrices. The YUV conversion may not work for all color spaces.
+ *
+ * @param inputArray The buffer of the image to be converted.
+ * @param inputVectorSize The number of bytes in each input cell, a value from 1 to 4.
+ * @param sizeX The width of both buffers, as a number of 1 to 4 byte cells.
+ * @param sizeY The height of both buffers, as a number of 1 to 4 byte cells.
+ * @param outputVectorSize The number of bytes in each output cell, a value from 1 to 4.
+ * @param matrix The 4x4 matrix to multiply, in row major format.
+ * @param addVector A vector of four floats that's added to the result of the multiplication.
+ * @param restriction When not null, restricts the operation to a 2D range of pixels.
+ * @return The converted buffer.
+ */
+ @JvmOverloads
+ fun colorMatrix(
+ inputArray: ByteArray,
+ inputVectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ outputVectorSize: Int,
+ matrix: FloatArray,
+ addVector: FloatArray = floatArrayOf(0f, 0f, 0f, 0f),
+ restriction: Range2d? = null
+ ): ByteArray {
+ require(inputVectorSize in 1..4) {
+ "$externalName colorMatrix. The inputVectorSize should be between 1 and 4. " +
+ "$inputVectorSize provided."
+ }
+ require(outputVectorSize in 1..4) {
+ "$externalName colorMatrix. The outputVectorSize should be between 1 and 4. " +
+ "$outputVectorSize provided."
+ }
+ require(inputArray.size >= sizeX * sizeY * inputVectorSize) {
+ "$externalName colorMatrix. inputArray is too small for the given dimensions. " +
+ "$sizeX*$sizeY*$inputVectorSize < ${inputArray.size}."
+ }
+ require(matrix.size == 16) {
+ "$externalName colorMatrix. matrix should have 16 entries. ${matrix.size} provided."
+ }
+ require(addVector.size == 4) {
+ "$externalName colorMatrix. addVector should have 4 entries. " +
+ "${addVector.size} provided."
+ }
+ validateRestriction("colorMatrix", sizeX, sizeY, restriction)
+
+ val outputArray = ByteArray(sizeX * sizeY * paddedSize(outputVectorSize))
+ nativeColorMatrix(
+ nativeHandle, inputArray, inputVectorSize, sizeX, sizeY, outputArray, outputVectorSize,
+ matrix, addVector, restriction
+ )
+ return outputArray
+ }
+
+ /**
+ * Transform an image using a color matrix.
+ *
+ * Converts a bitmap, multiplying each RGBA value by a 4x4 matrix and adding an optional vector.
+ * Each byte of the RGBA is converted from 0-255 to 0.0-1.0 floats before the multiplication
+ * is done.
+ *
+ * Bitmaps with a stride different than width * vectorSize are not currently supported.
+ *
+ * The resulting value is normalized from 0.0-1.0 to a 0-255 value and stored in the output.
+ *
+ * If addVector is not specified, a vector of zeroes is added, i.e. a noop.
+ *
+ * Check identityMatrix, greyScaleColorMatrix, rgbToYuvMatrix, and yuvToRgbMatrix for sample
+ * matrices. The YUV conversion may not work for all color spaces.
+ *
+ * @param inputBitmap The image to be converted.
+ * @param matrix The 4x4 matrix to multiply, in row major format.
+ * @param addVector A vector of four floats that's added to the result of the multiplication.
+ * @param restriction When not null, restricts the operation to a 2D range of pixels.
+ * @return The converted buffer.
+ */
+ @JvmOverloads
+ fun colorMatrix(
+ inputBitmap: Bitmap,
+ matrix: FloatArray,
+ addVector: FloatArray = floatArrayOf(0f, 0f, 0f, 0f),
+ restriction: Range2d? = null
+ ): Bitmap {
+ validateBitmap("colorMatrix", inputBitmap)
+ require(matrix.size == 16) {
+ "$externalName colorMatrix. matrix should have 16 entries. ${matrix.size} provided."
+ }
+ require(addVector.size == 4) {
+ "$externalName colorMatrix. addVector should have 4 entries."
+ }
+ validateRestriction("colorMatrix", inputBitmap.width, inputBitmap.height, restriction)
+
+ val outputBitmap = createCompatibleBitmap(inputBitmap)
+ nativeColorMatrixBitmap(
+ nativeHandle,
+ inputBitmap,
+ outputBitmap,
+ matrix,
+ addVector,
+ restriction
+ )
+ return outputBitmap
+ }
+
+ /**
+ * Convolve a ByteArray.
+ *
+ * Applies a 3x3 or 5x5 convolution to the input array using the provided coefficients.
+ * A variant of this method is available to convolve Bitmaps.
+ *
+ * For 3x3 convolutions, 9 coefficients must be provided. For 5x5, 25 coefficients are needed.
+ * The coefficients should be provided in row-major format.
+ *
+ * When the square extends past the edge, the edge values will be used as replacement for the
+ * values that's are off boundary.
+ *
+ * Each input cell can either be represented by one to four bytes. Each byte is multiplied
+ * and accumulated independently of the other bytes of the cell.
+ *
+ * An optional range parameter can be set to restrict the convolve operation to a rectangular
+ * subset of each buffer. If provided, the range must be wholly contained with the dimensions
+ * described by sizeX and sizeY. NOTE: The output buffer will still be full size, with the
+ * section that's not convolved all set to 0. This is to stay compatible with RenderScript.
+ *
+ * The source array should be large enough for sizeX * sizeY * vectorSize bytes. It has a
+ * row-major layout. The output array will have the same dimensions.
+ *
+ * Like the RenderScript Intrinsics, vectorSize of size 3 are padded to occupy 4 bytes.
+ *
+ * @param inputArray The buffer of the image to be blurred.
+ * @param vectorSize The number of bytes in each cell, a value from 1 to 4.
+ * @param sizeX The width of both buffers, as a number of 1 or 4 byte cells.
+ * @param sizeY The height of both buffers, as a number of 1 or 4 byte cells.
+ * @param coefficients A FloatArray of size 9 or 25, containing the multipliers.
+ * @param restriction When not null, restricts the operation to a 2D range of pixels.
+ * @return The convolved array.
+ */
+ @JvmOverloads
+ fun convolve(
+ inputArray: ByteArray,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ coefficients: FloatArray,
+ restriction: Range2d? = null
+ ): ByteArray {
+ require(vectorSize in 1..4) {
+ "$externalName convolve. The vectorSize should be between 1 and 4. " +
+ "$vectorSize provided."
+ }
+ require(inputArray.size >= sizeX * sizeY * vectorSize) {
+ "$externalName convolve. inputArray is too small for the given dimensions. " +
+ "$sizeX*$sizeY*$vectorSize < ${inputArray.size}."
+ }
+ require(coefficients.size == 9 || coefficients.size == 25) {
+ "$externalName convolve. Only 3x3 or 5x5 convolutions are supported. " +
+ "${coefficients.size} coefficients provided."
+ }
+ validateRestriction("convolve", sizeX, sizeY, restriction)
+
+ val outputArray = ByteArray(inputArray.size)
+ nativeConvolve(
+ nativeHandle,
+ inputArray,
+ vectorSize,
+ sizeX,
+ sizeY,
+ outputArray,
+ coefficients,
+ restriction
+ )
+ return outputArray
+ }
+
+ /**
+ * Convolve a Bitmap.
+ *
+ * Applies a 3x3 or 5x5 convolution to the input Bitmap using the provided coefficients.
+ * A variant of this method is available to convolve ByteArrays. Bitmaps with a stride different
+ * than width * vectorSize are not currently supported.
+ *
+ * For 3x3 convolutions, 9 coefficients must be provided. For 5x5, 25 coefficients are needed.
+ * The coefficients should be provided in row-major format.
+ *
+ * Each input cell can either be represented by one to four bytes. Each byte is multiplied
+ * and accumulated independently of the other bytes of the cell.
+ *
+ * An optional range parameter can be set to restrict the convolve operation to a rectangular
+ * subset of each buffer. If provided, the range must be wholly contained with the dimensions
+ * described by sizeX and sizeY. NOTE: The output Bitmap will still be full size, with the
+ * section that's not convolved all set to 0. This is to stay compatible with RenderScript.
+ *
+ * @param inputBitmap The image to be blurred.
+ * @param coefficients A FloatArray of size 9 or 25, containing the multipliers.
+ * @param restriction When not null, restricts the operation to a 2D range of pixels.
+ * @return The convolved Bitmap.
+ */
+ @JvmOverloads
+ fun convolve(
+ inputBitmap: Bitmap,
+ coefficients: FloatArray,
+ restriction: Range2d? = null
+ ): Bitmap {
+ validateBitmap("convolve", inputBitmap)
+ require(coefficients.size == 9 || coefficients.size == 25) {
+ "$externalName convolve. Only 3x3 or 5x5 convolutions are supported. " +
+ "${coefficients.size} coefficients provided."
+ }
+ validateRestriction("convolve", inputBitmap, restriction)
+
+ val outputBitmap = createCompatibleBitmap(inputBitmap)
+ nativeConvolveBitmap(nativeHandle, inputBitmap, outputBitmap, coefficients, restriction)
+ return outputBitmap
+ }
+
+ /**
+ * Compute the histogram of an image.
+ *
+ * Tallies how many times each of the 256 possible values of a byte is found in the input.
+ * A variant of this method is available to do the histogram of a Bitmap.
+ *
+ * An input cell can be represented by one to four bytes. The tally is done independently
+ * for each of the bytes of the cell. Correspondingly, the returned IntArray will have
+ * 256 * vectorSize entries. The counts for value 0 are consecutive, followed by those for
+ * value 1, etc.
+ *
+ * An optional range parameter can be set to restrict the operation to a rectangular subset
+ * of each buffer. If provided, the range must be wholly contained with the dimensions
+ * described by sizeX and sizeY.
+ *
+ * The source buffer should be large enough for sizeX * sizeY * vectorSize bytes. It has a
+ * row-major layout.
+ *
+ * Like the RenderScript Intrinsics, vectorSize of size 3 are padded to occupy 4 bytes.
+ *
+ * @param inputArray The buffer of the image to be analyzed.
+ * @param vectorSize The number of bytes in each cell, a value from 1 to 4.
+ * @param sizeX The width of the input buffers, as a number of 1 to 4 byte cells.
+ * @param sizeY The height of the input buffers, as a number of 1 to 4 byte cells.
+ * @param restriction When not null, restricts the operation to a 2D range of pixels.
+ * @return The resulting array of counts.
+ */
+ @JvmOverloads
+ fun histogram(
+ inputArray: ByteArray,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ restriction: Range2d? = null
+ ): IntArray {
+ require(vectorSize in 1..4) {
+ "$externalName histogram. The vectorSize should be between 1 and 4. " +
+ "$vectorSize provided."
+ }
+ require(inputArray.size >= sizeX * sizeY * vectorSize) {
+ "$externalName histogram. inputArray is too small for the given dimensions. " +
+ "$sizeX*$sizeY*$vectorSize < ${inputArray.size}."
+ }
+ validateRestriction("histogram", sizeX, sizeY, restriction)
+
+ val outputArray = IntArray(256 * paddedSize(vectorSize))
+ nativeHistogram(
+ nativeHandle,
+ inputArray,
+ vectorSize,
+ sizeX,
+ sizeY,
+ outputArray,
+ restriction
+ )
+ return outputArray
+ }
+
+ /**
+ * Compute the histogram of an image.
+ *
+ * Tallies how many times each of the 256 possible values of a byte is found in the bitmap.
+ * This method supports Bitmaps of config ARGB_8888 and ALPHA_8.
+ *
+ * For ARGB_8888, the tally is done independently of the four bytes. Correspondingly, the
+ * returned IntArray will have 4 * 256 entries. The counts for value 0 are consecutive,
+ * followed by those for value 1, etc.
+ *
+ * For ALPHA_8, an IntArray of size 256 is returned.
+ *
+ * Bitmaps with a stride different than width * vectorSize are not currently supported.
+ *
+ * A variant of this method is available to do the histogram of a ByteArray.
+ *
+ * An optional range parameter can be set to restrict the operation to a rectangular subset
+ * of each buffer. If provided, the range must be wholly contained with the dimensions
+ * described by sizeX and sizeY.
+ *
+ * @param inputBitmap The bitmap to be analyzed.
+ * @param restriction When not null, restricts the operation to a 2D range of pixels.
+ * @return The resulting array of counts.
+ */
+ @JvmOverloads
+ fun histogram(
+ inputBitmap: Bitmap,
+ restriction: Range2d? = null
+ ): IntArray {
+ validateBitmap("histogram", inputBitmap)
+ validateRestriction("histogram", inputBitmap, restriction)
+
+ val outputArray = IntArray(256 * vectorSize(inputBitmap))
+ nativeHistogramBitmap(nativeHandle, inputBitmap, outputArray, restriction)
+ return outputArray
+ }
+
+ /**
+ * Compute the histogram of the dot product of an image.
+ *
+ * This method supports cells of 1 to 4 bytes in length. For each cell of the array,
+ * the dot product of its bytes with the provided coefficients is computed. The resulting
+ * floating point value is converted to an unsigned byte and tallied in the histogram.
+ *
+ * If coefficients is null, the coefficients used for RGBA luminosity calculation will be used,
+ * i.e. the values [0.299f, 0.587f, 0.114f, 0.f].
+ *
+ * Each coefficients must be >= 0 and their sum must be 1.0 or less. There must be the same
+ * number of coefficients as vectorSize.
+ *
+ * A variant of this method is available to do the histogram of a Bitmap.
+ *
+ * An optional range parameter can be set to restrict the operation to a rectangular subset
+ * of each buffer. If provided, the range must be wholly contained with the dimensions
+ * described by sizeX and sizeY.
+ *
+ * The source buffer should be large enough for sizeX * sizeY * vectorSize bytes. The returned
+ * array will have 256 ints.
+ *
+ * Like the RenderScript Intrinsics, vectorSize of size 3 are padded to occupy 4 bytes.
+ *
+ * @param inputArray The buffer of the image to be analyzed.
+ * @param vectorSize The number of bytes in each cell, a value from 1 to 4.
+ * @param sizeX The width of the input buffers, as a number of 1 to 4 byte cells.
+ * @param sizeY The height of the input buffers, as a number of 1 to 4 byte cells.
+ * @param coefficients The dot product multipliers. Size should equal vectorSize. Can be null.
+ * @param restriction When not null, restricts the operation to a 2D range of pixels.
+ * @return The resulting vector of counts.
+ */
+ @JvmOverloads
+ fun histogramDot(
+ inputArray: ByteArray,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ coefficients: FloatArray? = null,
+ restriction: Range2d? = null
+ ): IntArray {
+ require(vectorSize in 1..4) {
+ "$externalName histogramDot. The vectorSize should be between 1 and 4. " +
+ "$vectorSize provided."
+ }
+ require(inputArray.size >= sizeX * sizeY * vectorSize) {
+ "$externalName histogramDot. inputArray is too small for the given dimensions. " +
+ "$sizeX*$sizeY*$vectorSize < ${inputArray.size}."
+ }
+ validateHistogramDotCoefficients(coefficients, vectorSize)
+ validateRestriction("histogramDot", sizeX, sizeY, restriction)
+
+ val outputArray = IntArray(256)
+ val actualCoefficients = coefficients ?: floatArrayOf(0.299f, 0.587f, 0.114f, 0f)
+ nativeHistogramDot(
+ nativeHandle,
+ inputArray,
+ vectorSize,
+ sizeX,
+ sizeY,
+ outputArray,
+ actualCoefficients,
+ restriction
+ )
+ return outputArray
+ }
+
+ /**
+ * Compute the histogram of the dot product of an image.
+ *
+ * This method supports Bitmaps of config ARGB_8888 and ALPHA_8. For each pixel of the bitmap,
+ * the dot product of its bytes with the provided coefficients is computed. The resulting
+ * floating point value is converted to an unsigned byte and tallied in the histogram.
+ *
+ * If coefficients is null, the coefficients used for RGBA luminosity calculation will be used,
+ * i.e. the values [0.299f, 0.587f, 0.114f, 0.f].
+ *
+ * Each coefficients must be >= 0 and their sum must be 1.0 or less. For ARGB_8888, four values
+ * must be provided; for ALPHA_8, one.
+ *
+ * Bitmaps with a stride different than width * vectorSize are not currently supported.
+ *
+ * A variant of this method is available to do the histogram of a ByteArray.
+ *
+ * An optional range parameter can be set to restrict the operation to a rectangular subset
+ * of each buffer. If provided, the range must be wholly contained with the dimensions
+ * described by sizeX and sizeY.
+ *
+ * The returned array will have 256 ints.
+ *
+ * @param inputBitmap The bitmap to be analyzed.
+ * @param coefficients The one or four values used for the dot product. Can be null.
+ * @param restriction When not null, restricts the operation to a 2D range of pixels.
+ * @return The resulting vector of counts.
+ */
+ @JvmOverloads
+ fun histogramDot(
+ inputBitmap: Bitmap,
+ coefficients: FloatArray? = null,
+ restriction: Range2d? = null
+ ): IntArray {
+ validateBitmap("histogramDot", inputBitmap)
+ validateHistogramDotCoefficients(coefficients, vectorSize(inputBitmap))
+ validateRestriction("histogramDot", inputBitmap, restriction)
+
+ val outputArray = IntArray(256)
+ val actualCoefficients = coefficients ?: floatArrayOf(0.299f, 0.587f, 0.114f, 0f)
+ nativeHistogramDotBitmap(
+ nativeHandle, inputBitmap, outputArray, actualCoefficients, restriction
+ )
+ return outputArray
+ }
+
+ /**
+ * Transform an image using a look up table
+ *
+ * Transforms an image by using a per-channel lookup table. Each channel of the input has an
+ * independent lookup table. The tables are 256 entries in size and can cover the full value
+ * range of a byte.
+ *
+ * The input array should be in RGBA format, where four consecutive bytes form an cell.
+ * A variant of this method is available to transform a Bitmap.
+ *
+ * An optional range parameter can be set to restrict the operation to a rectangular subset
+ * of each buffer. If provided, the range must be wholly contained with the dimensions
+ * described by sizeX and sizeY. NOTE: The output Bitmap will still be full size, with the
+ * section that's not convolved all set to 0. This is to stay compatible with RenderScript.
+ *
+ * The source array should be large enough for sizeX * sizeY * vectorSize bytes. The returned
+ * ray has the same dimensions as the input. The arrays have a row-major layout.
+ *
+ * @param inputArray The buffer of the image to be transformed.
+ * @param sizeX The width of both buffers, as a number of 4 byte cells.
+ * @param sizeY The height of both buffers, as a number of 4 byte cells.
+ * @param table The four arrays of 256 values that's used to convert each channel.
+ * @param restriction When not null, restricts the operation to a 2D range of pixels.
+ * @return The transformed image.
+ */
+ @JvmOverloads
+ fun lut(
+ inputArray: ByteArray,
+ sizeX: Int,
+ sizeY: Int,
+ table: LookupTable,
+ restriction: Range2d? = null
+ ): ByteArray {
+ require(inputArray.size >= sizeX * sizeY * 4) {
+ "$externalName lut. inputArray is too small for the given dimensions. " +
+ "$sizeX*$sizeY*4 < ${inputArray.size}."
+ }
+ validateRestriction("lut", sizeX, sizeY, restriction)
+
+ val outputArray = ByteArray(inputArray.size)
+ nativeLut(
+ nativeHandle,
+ inputArray,
+ outputArray,
+ sizeX,
+ sizeY,
+ table.red,
+ table.green,
+ table.blue,
+ table.alpha,
+ restriction
+ )
+ return outputArray
+ }
+
+ /**
+ * Transform an image using a look up table
+ *
+ * Transforms an image by using a per-channel lookup table. Each channel of the input has an
+ * independent lookup table. The tables are 256 entries in size and can cover the full value
+ * range of a byte.
+ *
+ * The input Bitmap should be in config ARGB_8888. A variant of this method is available to
+ * transform a ByteArray. Bitmaps with a stride different than width * vectorSize are not
+ * currently supported.
+ *
+ * An optional range parameter can be set to restrict the operation to a rectangular subset
+ * of each buffer. If provided, the range must be wholly contained with the dimensions
+ * described by sizeX and sizeY. NOTE: The output Bitmap will still be full size, with the
+ * section that's not convolved all set to 0. This is to stay compatible with RenderScript.
+ *
+ * @param inputBitmap The buffer of the image to be transformed.
+ * @param table The four arrays of 256 values that's used to convert each channel.
+ * @param restriction When not null, restricts the operation to a 2D range of pixels.
+ * @return The transformed image.
+ */
+ @JvmOverloads
+ fun lut(
+ inputBitmap: Bitmap,
+ table: LookupTable,
+ restriction: Range2d? = null
+ ): Bitmap {
+ validateBitmap("lut", inputBitmap)
+ validateRestriction("lut", inputBitmap, restriction)
+
+ val outputBitmap = createCompatibleBitmap(inputBitmap)
+ nativeLutBitmap(
+ nativeHandle,
+ inputBitmap,
+ outputBitmap,
+ table.red,
+ table.green,
+ table.blue,
+ table.alpha,
+ restriction
+ )
+ return outputBitmap
+ }
+
+ /**
+ * Transform an image using a 3D look up table
+ *
+ * Transforms an image, converting RGB to RGBA by using a 3D lookup table. The incoming R, G,
+ * and B values are normalized to the dimensions of the provided 3D buffer. The eight nearest
+ * values in that 3D buffer are sampled and linearly interpolated. The resulting RGBA entry
+ * is returned in the output array.
+ *
+ * The input array should be in RGBA format, where four consecutive bytes form an cell.
+ * The fourth byte of each input cell is ignored. A variant of this method is also available
+ * to transform Bitmaps.
+ *
+ * An optional range parameter can be set to restrict the operation to a rectangular subset
+ * of each buffer. If provided, the range must be wholly contained with the dimensions
+ * described by sizeX and sizeY. NOTE: The output array will still be full size, with the
+ * section that's not convolved all set to 0. This is to stay compatible with RenderScript.
+ *
+ * The source array should be large enough for sizeX * sizeY * vectorSize bytes. The returned
+ * array will have the same dimensions. The arrays have a row-major layout.
+ *
+ * @param inputArray The buffer of the image to be transformed.
+ * @param sizeX The width of both buffers, as a number of 4 byte cells.
+ * @param sizeY The height of both buffers, as a number of 4 byte cells.
+ * @param cube The translation cube.
+ * @param restriction When not null, restricts the operation to a 2D range of pixels.
+ * @return The transformed image.
+ */
+ @JvmOverloads
+ fun lut3d(
+ inputArray: ByteArray,
+ sizeX: Int,
+ sizeY: Int,
+ cube: Rgba3dArray,
+ restriction: Range2d? = null
+ ): ByteArray {
+ require(inputArray.size >= sizeX * sizeY * 4) {
+ "$externalName lut3d. inputArray is too small for the given dimensions. " +
+ "$sizeX*$sizeY*4 < ${inputArray.size}."
+ }
+ require(
+ cube.sizeX >= 2 && cube.sizeY >= 2 && cube.sizeZ >= 2 &&
+ cube.sizeX <= 256 && cube.sizeY <= 256 && cube.sizeZ <= 256
+ ) {
+ "$externalName lut3d. The dimensions of the cube should be between 2 and 256. " +
+ "(${cube.sizeX}, ${cube.sizeY}, ${cube.sizeZ}) provided."
+ }
+ validateRestriction("lut3d", sizeX, sizeY, restriction)
+
+ val outputArray = ByteArray(inputArray.size)
+ nativeLut3d(
+ nativeHandle, inputArray, outputArray, sizeX, sizeY, cube.values, cube.sizeX,
+ cube.sizeY, cube.sizeZ, restriction
+ )
+ return outputArray
+ }
+
+ /**
+ * Transform an image using a 3D look up table
+ *
+ * Transforms an image, converting RGB to RGBA by using a 3D lookup table. The incoming R, G,
+ * and B values are normalized to the dimensions of the provided 3D buffer. The eight nearest
+ * values in that 3D buffer are sampled and linearly interpolated. The resulting RGBA entry
+ * is returned in the output array.
+ *
+ * The input bitmap should be in RGBA_8888 format. The A channel is preserved. A variant of this
+ * method is also available to transform ByteArray. Bitmaps with a stride different than
+ * width * vectorSize are not currently supported.
+ *
+ * An optional range parameter can be set to restrict the operation to a rectangular subset
+ * of each buffer. If provided, the range must be wholly contained with the dimensions
+ * described by sizeX and sizeY. NOTE: The output array will still be full size, with the
+ * section that's not convolved all set to 0. This is to stay compatible with RenderScript.
+ *
+ * The source array should be large enough for sizeX * sizeY * vectorSize bytes. The returned
+ * array will have the same dimensions. The arrays have a row-major layout.
+ *
+ * @param inputBitmap The image to be transformed.
+ * @param cube The translation cube.
+ * @param restriction When not null, restricts the operation to a 2D range of pixels.
+ * @return The transformed image.
+ */
+ @JvmOverloads
+ fun lut3d(
+ inputBitmap: Bitmap,
+ cube: Rgba3dArray,
+ restriction: Range2d? = null
+ ): Bitmap {
+ validateBitmap("lut3d", inputBitmap)
+ validateRestriction("lut3d", inputBitmap, restriction)
+
+ val outputBitmap = createCompatibleBitmap(inputBitmap)
+ nativeLut3dBitmap(
+ nativeHandle, inputBitmap, outputBitmap, cube.values, cube.sizeX,
+ cube.sizeY, cube.sizeZ, restriction
+ )
+ return outputBitmap
+ }
+
+ /**
+ * Resize an image.
+ *
+ * Resizes an image using bicubic interpolation.
+ *
+ * This method supports elements of 1 to 4 bytes in length. Each byte of the element is
+ * interpolated independently from the others.
+ *
+ * An optional range parameter can be set to restrict the operation to a rectangular subset
+ * of the output buffer. The corresponding scaled range of the input will be used. If provided,
+ * the range must be wholly contained with the dimensions described by outputSizeX and
+ * outputSizeY.
+ *
+ * The input and output arrays have a row-major layout. The input array should be
+ * large enough for sizeX * sizeY * vectorSize bytes.
+ *
+ * Like the RenderScript Intrinsics, vectorSize of size 3 are padded to occupy 4 bytes.
+ *
+ * @param inputArray The buffer of the image to be resized.
+ * @param vectorSize The number of bytes in each element of both buffers. A value from 1 to 4.
+ * @param inputSizeX The width of the input buffer, as a number of 1-4 byte elements.
+ * @param inputSizeY The height of the input buffer, as a number of 1-4 byte elements.
+ * @param outputSizeX The width of the output buffer, as a number of 1-4 byte elements.
+ * @param outputSizeY The height of the output buffer, as a number of 1-4 byte elements.
+ * @param restriction When not null, restricts the operation to a 2D range of pixels.
+ * @return An array that contains the rescaled image.
+ */
+ @JvmOverloads
+ fun resize(
+ inputArray: ByteArray,
+ vectorSize: Int,
+ inputSizeX: Int,
+ inputSizeY: Int,
+ outputSizeX: Int,
+ outputSizeY: Int,
+ restriction: Range2d? = null
+ ): ByteArray {
+ require(vectorSize in 1..4) {
+ "$externalName resize. The vectorSize should be between 1 and 4. $vectorSize provided."
+ }
+ require(inputArray.size >= inputSizeX * inputSizeY * vectorSize) {
+ "$externalName resize. inputArray is too small for the given dimensions. " +
+ "$inputSizeX*$inputSizeY*$vectorSize < ${inputArray.size}."
+ }
+ validateRestriction("resize", outputSizeX, outputSizeY, restriction)
+
+ val outputArray = ByteArray(outputSizeX * outputSizeY * paddedSize(vectorSize))
+ nativeResize(
+ nativeHandle,
+ inputArray,
+ vectorSize,
+ inputSizeX,
+ inputSizeY,
+ outputArray,
+ outputSizeX,
+ outputSizeY,
+ restriction
+ )
+ return outputArray
+ }
+
+ /**
+ * Resize an image.
+ *
+ * Resizes an image using bicubic interpolation.
+ *
+ * This method supports input Bitmap of config ARGB_8888 and ALPHA_8. The returned Bitmap
+ * has the same config. Bitmaps with a stride different than width * vectorSize are not
+ * currently supported.
+ *
+ * An optional range parameter can be set to restrict the operation to a rectangular subset
+ * of the output buffer. The corresponding scaled range of the input will be used. If provided,
+ * the range must be wholly contained with the dimensions described by outputSizeX and
+ * outputSizeY.
+ *
+ * @param inputBitmap The Bitmap to be resized.
+ * @param outputSizeX The width of the output buffer, as a number of 1-4 byte elements.
+ * @param outputSizeY The height of the output buffer, as a number of 1-4 byte elements.
+ * @param restriction When not null, restricts the operation to a 2D range of pixels.
+ * @return A Bitmap that contains the rescaled image.
+ */
+ @JvmOverloads
+ fun resize(
+ inputBitmap: Bitmap,
+ outputSizeX: Int,
+ outputSizeY: Int,
+ restriction: Range2d? = null
+ ): Bitmap {
+ validateBitmap("resize", inputBitmap)
+ validateRestriction("resize", outputSizeX, outputSizeY, restriction)
+
+ val outputBitmap = Bitmap.createBitmap(outputSizeX, outputSizeY, Bitmap.Config.ARGB_8888)
+ nativeResizeBitmap(nativeHandle, inputBitmap, outputBitmap, restriction)
+ return outputBitmap
+ }
+
+ /**
+ * Convert an image from YUV to RGB.
+ *
+ * Converts a YUV buffer to RGB. The input array should be supplied in a supported YUV format.
+ * The output is RGBA; the alpha channel will be set to 255.
+ *
+ * Note that for YV12 and a sizeX that's not a multiple of 32, the RenderScript Intrinsic may
+ * not have converted the image correctly. This Toolkit method should.
+ *
+ * @param inputArray The buffer of the image to be converted.
+ * @param sizeX The width in pixels of the image.
+ * @param sizeY The height in pixels of the image.
+ * @param format Either YV12 or NV21.
+ * @return The converted image as a byte array.
+ */
+ fun yuvToRgb(inputArray: ByteArray, sizeX: Int, sizeY: Int, format: YuvFormat): ByteArray {
+ require(sizeX % 2 == 0 && sizeY % 2 == 0) {
+ "$externalName yuvToRgb. Non-even dimensions are not supported. " +
+ "$sizeX and $sizeY were provided."
+ }
+
+ val outputArray = ByteArray(sizeX * sizeY * 4)
+ nativeYuvToRgb(nativeHandle, inputArray, outputArray, sizeX, sizeY, format.value)
+ return outputArray
+ }
+
+ /**
+ * Convert an image from YUV to an RGB Bitmap.
+ *
+ * Converts a YUV buffer to an RGB Bitmap. The input array should be supplied in a supported
+ * YUV format. The output is RGBA; the alpha channel will be set to 255.
+ *
+ * Note that for YV12 and a sizeX that's not a multiple of 32, the RenderScript Intrinsic may
+ * not have converted the image correctly. This Toolkit method should.
+ *
+ * @param inputArray The buffer of the image to be converted.
+ * @param sizeX The width in pixels of the image.
+ * @param sizeY The height in pixels of the image.
+ * @param format Either YV12 or NV21.
+ * @return The converted image.
+ */
+ fun yuvToRgbBitmap(inputArray: ByteArray, sizeX: Int, sizeY: Int, format: YuvFormat): Bitmap {
+ require(sizeX % 2 == 0 && sizeY % 2 == 0) {
+ "$externalName yuvToRgbBitmap. Non-even dimensions are not supported. " +
+ "$sizeX and $sizeY were provided."
+ }
+
+ val outputBitmap = Bitmap.createBitmap(sizeX, sizeY, Bitmap.Config.ARGB_8888)
+ nativeYuvToRgbBitmap(nativeHandle, inputArray, sizeX, sizeY, outputBitmap, format.value)
+ return outputBitmap
+ }
+
+ companion object {
+ init {
+ System.loadLibrary("renderscript-toolkit")
+ }
+ }
+
+ private var nativeHandle: Long = 0
+
+ private external fun createNative(): Long
+
+ private external fun destroyNative(nativeHandle: Long)
+
+ private external fun nativeBlend(
+ nativeHandle: Long,
+ mode: Int,
+ sourceArray: ByteArray,
+ destArray: ByteArray,
+ sizeX: Int,
+ sizeY: Int,
+ restriction: Range2d?
+ )
+
+ private external fun nativeBlendBitmap(
+ nativeHandle: Long,
+ mode: Int,
+ sourceBitmap: Bitmap,
+ destBitmap: Bitmap,
+ restriction: Range2d?
+ )
+
+ private external fun nativeBlur(
+ nativeHandle: Long,
+ inputArray: ByteArray,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ radius: Int,
+ outputArray: ByteArray,
+ restriction: Range2d?
+ )
+
+ private external fun nativeBlurBitmap(
+ nativeHandle: Long,
+ inputBitmap: Bitmap,
+ outputBitmap: Bitmap,
+ radius: Int,
+ restriction: Range2d?
+ )
+
+ private external fun nativeColorMatrix(
+ nativeHandle: Long,
+ inputArray: ByteArray,
+ inputVectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ outputArray: ByteArray,
+ outputVectorSize: Int,
+ matrix: FloatArray,
+ addVector: FloatArray,
+ restriction: Range2d?
+ )
+
+ private external fun nativeColorMatrixBitmap(
+ nativeHandle: Long,
+ inputBitmap: Bitmap,
+ outputBitmap: Bitmap,
+ matrix: FloatArray,
+ addVector: FloatArray,
+ restriction: Range2d?
+ )
+
+ private external fun nativeConvolve(
+ nativeHandle: Long,
+ inputArray: ByteArray,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ outputArray: ByteArray,
+ coefficients: FloatArray,
+ restriction: Range2d?
+ )
+
+ private external fun nativeConvolveBitmap(
+ nativeHandle: Long,
+ inputBitmap: Bitmap,
+ outputBitmap: Bitmap,
+ coefficients: FloatArray,
+ restriction: Range2d?
+ )
+
+ private external fun nativeHistogram(
+ nativeHandle: Long,
+ inputArray: ByteArray,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ outputArray: IntArray,
+ restriction: Range2d?
+ )
+
+ private external fun nativeHistogramBitmap(
+ nativeHandle: Long,
+ inputBitmap: Bitmap,
+ outputArray: IntArray,
+ restriction: Range2d?
+ )
+
+ private external fun nativeHistogramDot(
+ nativeHandle: Long,
+ inputArray: ByteArray,
+ vectorSize: Int,
+ sizeX: Int,
+ sizeY: Int,
+ outputArray: IntArray,
+ coefficients: FloatArray,
+ restriction: Range2d?
+ )
+
+ private external fun nativeHistogramDotBitmap(
+ nativeHandle: Long,
+ inputBitmap: Bitmap,
+ outputArray: IntArray,
+ coefficients: FloatArray,
+ restriction: Range2d?
+ )
+
+ private external fun nativeLut(
+ nativeHandle: Long,
+ inputArray: ByteArray,
+ outputArray: ByteArray,
+ sizeX: Int,
+ sizeY: Int,
+ red: ByteArray,
+ green: ByteArray,
+ blue: ByteArray,
+ alpha: ByteArray,
+ restriction: Range2d?
+ )
+
+ private external fun nativeLutBitmap(
+ nativeHandle: Long,
+ inputBitmap: Bitmap,
+ outputBitmap: Bitmap,
+ red: ByteArray,
+ green: ByteArray,
+ blue: ByteArray,
+ alpha: ByteArray,
+ restriction: Range2d?
+ )
+
+ private external fun nativeLut3d(
+ nativeHandle: Long,
+ inputArray: ByteArray,
+ outputArray: ByteArray,
+ sizeX: Int,
+ sizeY: Int,
+ cube: ByteArray,
+ cubeSizeX: Int,
+ cubeSizeY: Int,
+ cubeSizeZ: Int,
+ restriction: Range2d?
+ )
+
+ private external fun nativeLut3dBitmap(
+ nativeHandle: Long,
+ inputBitmap: Bitmap,
+ outputBitmap: Bitmap,
+ cube: ByteArray,
+ cubeSizeX: Int,
+ cubeSizeY: Int,
+ cubeSizeZ: Int,
+ restriction: Range2d?
+ )
+
+ private external fun nativeResize(
+ nativeHandle: Long,
+ inputArray: ByteArray,
+ vectorSize: Int,
+ inputSizeX: Int,
+ inputSizeY: Int,
+ outputArray: ByteArray,
+ outputSizeX: Int,
+ outputSizeY: Int,
+ restriction: Range2d?
+ )
+
+ private external fun nativeResizeBitmap(
+ nativeHandle: Long,
+ inputBitmap: Bitmap,
+ outputBitmap: Bitmap,
+ restriction: Range2d?
+ )
+
+ private external fun nativeYuvToRgb(
+ nativeHandle: Long,
+ inputArray: ByteArray,
+ outputArray: ByteArray,
+ sizeX: Int,
+ sizeY: Int,
+ format: Int
+ )
+
+ private external fun nativeYuvToRgbBitmap(
+ nativeHandle: Long,
+ inputArray: ByteArray,
+ sizeX: Int,
+ sizeY: Int,
+ outputBitmap: Bitmap,
+ value: Int
+ )
+
+ fun finalize() {
+ destroyNative(nativeHandle)
+ }
+
+ init {
+ nativeHandle = createNative()
+ }
+}
+
+/**
+ * Determines how a source buffer is blended into a destination buffer.
+ * See {@link RenderScriptToolkit::blend}.
+ *
+ * blend only works on 4 byte RGBA data. In the descriptions below, ".a" represents
+ * the alpha channel.
+ */
+enum class BlendingMode(val value: Int) {
+ /**
+ * dest = 0
+ *
+ * The destination is cleared, i.e. each pixel is set to (0, 0, 0, 0)
+ */
+ CLEAR(0),
+
+ /**
+ * dest = src
+ *
+ * Sets each pixel of the destination to the corresponding one in the source.
+ */
+ SRC(1),
+
+ /**
+ * dest = dest
+ *
+ * Leaves the destination untouched. This is a no-op.
+ */
+ DST(2),
+
+ /**
+ * dest = src + dest * (1.0 - src.a)
+ */
+ SRC_OVER(3),
+
+ /**
+ * dest = dest + src * (1.0 - dest.a)
+ */
+ DST_OVER(4),
+
+ /**
+ * dest = src * dest.a
+ */
+ SRC_IN(5),
+
+ /**
+ * dest = dest * src.a
+ */
+ DST_IN(6),
+
+ /**
+ * dest = src * (1.0 - dest.a)
+ */
+ SRC_OUT(7),
+
+ /**
+ * dest = dest * (1.0 - src.a)
+ */
+ DST_OUT(8),
+
+ /**
+ * dest.rgb = src.rgb * dest.a + (1.0 - src.a) * dest.rgb, dest.a = dest.a
+ */
+ SRC_ATOP(9),
+
+ /**
+ * dest = dest.rgb * src.a + (1.0 - dest.a) * src.rgb, dest.a = src.a
+ */
+ DST_ATOP(10),
+
+ /**
+ * dest = {src.r ^ dest.r, src.g ^ dest.g, src.b ^ dest.b, src.a ^ dest.a}
+ *
+ * Note: this is NOT the Porter/Duff XOR mode; this is a bitwise xor.
+ */
+ XOR(11),
+
+ /**
+ * dest = src * dest
+ */
+ MULTIPLY(12),
+
+ /**
+ * dest = min(src + dest, 1.0)
+ */
+ ADD(13),
+
+ /**
+ * dest = max(dest - src, 0.0)
+ */
+ SUBTRACT(14)
+}
+
+/**
+ * A translation table used by the lut method. For each potential red, green, blue, and alpha
+ * value, specifies it's replacement value.
+ *
+ * The fields are initialized to be a no-op operation, i.e. replace 1 by 1, 2 by 2, etc.
+ * You can modify just the values you're interested in having a translation.
+ */
+class LookupTable {
+ var red = ByteArray(256) { it.toByte() }
+ var green = ByteArray(256) { it.toByte() }
+ var blue = ByteArray(256) { it.toByte() }
+ var alpha = ByteArray(256) { it.toByte() }
+}
+
+/**
+ * The YUV formats supported by yuvToRgb.
+ */
+enum class YuvFormat(val value: Int) {
+ NV21(0x11),
+ YV12(0x32315659),
+}
+
+/**
+ * Define a range of data to process.
+ *
+ * This class is used to restrict a [Toolkit] operation to a rectangular subset of the input
+ * tensor.
+ *
+ * @property startX The index of the first value to be included on the X axis.
+ * @property endX The index after the last value to be included on the X axis.
+ * @property startY The index of the first value to be included on the Y axis.
+ * @property endY The index after the last value to be included on the Y axis.
+ */
+data class Range2d(
+ val startX: Int,
+ val endX: Int,
+ val startY: Int,
+ val endY: Int
+) {
+ constructor() : this(0, 0, 0, 0)
+}
+
+class Rgba3dArray(val values: ByteArray, val sizeX: Int, val sizeY: Int, val sizeZ: Int) {
+ init {
+ require(values.size >= sizeX * sizeY * sizeZ * 4)
+ }
+
+ operator fun get(x: Int, y: Int, z: Int): ByteArray {
+ val index = indexOfVector(x, y, z)
+ return ByteArray(4) { values[index + it] }
+ }
+
+ operator fun set(x: Int, y: Int, z: Int, value: ByteArray) {
+ require(value.size == 4)
+ val index = indexOfVector(x, y, z)
+ for (i in 0..3) {
+ values[index + i] = value[i]
+ }
+ }
+
+ private fun indexOfVector(x: Int, y: Int, z: Int): Int {
+ require(x in 0 until sizeX)
+ require(y in 0 until sizeY)
+ require(z in 0 until sizeZ)
+ return ((z * sizeY + y) * sizeX + x) * 4
+ }
+}
+
+private fun validateBitmap(
+ function: String,
+ inputBitmap: Bitmap,
+ alphaAllowed: Boolean = true
+) {
+ if (alphaAllowed) {
+ require(
+ inputBitmap.config == Bitmap.Config.ARGB_8888 ||
+ inputBitmap.config == Bitmap.Config.ALPHA_8
+ ) {
+ "$externalName. $function supports only ARGB_8888 and ALPHA_8 bitmaps. " +
+ "${inputBitmap.config} provided."
+ }
+ } else {
+ require(inputBitmap.config == Bitmap.Config.ARGB_8888) {
+ "$externalName. $function supports only ARGB_8888. " +
+ "${inputBitmap.config} provided."
+ }
+ }
+ require(inputBitmap.width * vectorSize(inputBitmap) == inputBitmap.rowBytes) {
+ "$externalName $function. Only bitmaps with rowSize equal to the width * vectorSize are " +
+ "currently supported. Provided were rowBytes=${inputBitmap.rowBytes}, " +
+ "width={${inputBitmap.width}, and vectorSize=${vectorSize(inputBitmap)}."
+ }
+}
+
+private fun createCompatibleBitmap(inputBitmap: Bitmap) =
+ Bitmap.createBitmap(inputBitmap.width, inputBitmap.height, inputBitmap.config)
+
+private fun validateHistogramDotCoefficients(
+ coefficients: FloatArray?,
+ vectorSize: Int
+) {
+ require(coefficients == null || coefficients.size == vectorSize) {
+ "$externalName histogramDot. The coefficients should be null or have $vectorSize values."
+ }
+ if (coefficients !== null) {
+ var sum = 0f
+ for (i in 0 until vectorSize) {
+ require(coefficients[i] >= 0.0f) {
+ "$externalName histogramDot. Coefficients should not be negative. " +
+ "Coefficient $i was ${coefficients[i]}."
+ }
+ sum += coefficients[i]
+ }
+ require(sum <= 1.0f) {
+ "$externalName histogramDot. Coefficients should add to 1 or less. Their sum is $sum."
+ }
+ }
+}
+
+private fun validateRestriction(tag: String, bitmap: Bitmap, restriction: Range2d? = null) {
+ validateRestriction(tag, bitmap.width, bitmap.height, restriction)
+}
+
+private fun validateRestriction(tag: String, sizeX: Int, sizeY: Int, restriction: Range2d? = null) {
+ if (restriction == null) return
+ require(restriction.startX < sizeX && restriction.endX <= sizeX) {
+ "$externalName $tag. sizeX should be greater than restriction.startX and greater " +
+ "or equal to restriction.endX. $sizeX, ${restriction.startX}, " +
+ "and ${restriction.endX} were provided respectively."
+ }
+ require(restriction.startY < sizeY && restriction.endY <= sizeY) {
+ "$externalName $tag. sizeY should be greater than restriction.startY and greater " +
+ "or equal to restriction.endY. $sizeY, ${restriction.startY}, " +
+ "and ${restriction.endY} were provided respectively."
+ }
+ require(restriction.startX < restriction.endX) {
+ "$externalName $tag. Restriction startX should be less than endX. " +
+ "${restriction.startX} and ${restriction.endX} were provided respectively."
+ }
+ require(restriction.startY < restriction.endY) {
+ "$externalName $tag. Restriction startY should be less than endY. " +
+ "${restriction.startY} and ${restriction.endY} were provided respectively."
+ }
+}
+
+private fun vectorSize(bitmap: Bitmap): Int {
+ return when (bitmap.config) {
+ Bitmap.Config.ARGB_8888 -> 4
+ Bitmap.Config.ALPHA_8 -> 1
+ else -> throw IllegalArgumentException(
+ "$externalName. Only ARGB_8888 and ALPHA_8 Bitmap are supported."
+ )
+ }
+}
+
+private fun paddedSize(vectorSize: Int) = if (vectorSize == 3) 4 else vectorSize