• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * 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 package com.google.android.renderscript
18 
19 import android.graphics.Bitmap
20 import java.lang.IllegalArgumentException
21 
22 // This string is used for error messages.
23 private const val externalName = "RenderScript Toolkit"
24 
25 /**
26  * A collection of high-performance graphic utility functions like blur and blend.
27  *
28  * This toolkit provides ten image manipulation functions: blend, blur, color matrix, convolve,
29  * histogram, histogramDot, lut, lut3d, resize, and YUV to RGB. These functions execute
30  * multithreaded on the CPU.
31  *
32  * Most of the functions have two variants: one that manipulates Bitmaps, the other ByteArrays.
33  * For ByteArrays, you need to specify the width and height of the data to be processed, as
34  * well as the number of bytes per pixel. For most use cases, this will be 4.
35  *
36  * The Toolkit creates a thread pool that's used for processing the functions. The threads live
37  * for the duration of the application. They can be destroyed by calling the method shutdown().
38  *
39  * This library is thread safe. You can call methods from different poolThreads. The functions will
40  * execute sequentially.
41  *
42  * A native C++ version of this Toolkit is available. Check the RenderScriptToolkit.h file in the
43  * cpp directory.
44  *
45  * This toolkit can be used as a replacement for most RenderScript Intrinsic functions. Compared
46  * to RenderScript, it's simpler to use and more than twice as fast on the CPU. However RenderScript
47  * Intrinsics allow more flexibility for the type of allocation supported. In particular, this
48  * toolkit does not support allocations of floats.
49  */
50 object Toolkit {
51     /**
52      * Blends a source buffer with the destination buffer.
53      *
54      * Blends a source buffer and a destination buffer, placing the result in the destination
55      * buffer. The blending is done pairwise between two corresponding RGBA values found in
56      * each buffer. The mode parameter specifies one of fifteen supported blending operations.
57      * See {@link BlendingMode}.
58      *
59      * A variant of this method is also available to blend Bitmaps.
60      *
61      * An optional range parameter can be set to restrict the operation to a rectangular subset
62      * of each buffer. If provided, the range must be wholly contained with the dimensions
63      * described by sizeX and sizeY.
64      *
65      * The source and destination buffer must have the same dimensions. Both arrays should have
66      * a size greater or equal to sizeX * sizeY * 4. The buffers have a row-major layout.
67      *
68      * @param mode The specific blending operation to do.
69      * @param sourceArray The RGBA input buffer.
70      * @param destArray The destination buffer. Used for input and output.
71      * @param sizeX The width of both buffers, as a number of RGBA values.
72      * @param sizeY The height of both buffers, as a number of RGBA values.
73      * @param restriction When not null, restricts the operation to a 2D range of pixels.
74      */
75     @JvmOverloads
blendnull76     fun blend(
77         mode: BlendingMode,
78         sourceArray: ByteArray,
79         destArray: ByteArray,
80         sizeX: Int,
81         sizeY: Int,
82         restriction: Range2d? = null
83     ) {
84         require(sourceArray.size >= sizeX * sizeY * 4) {
85             "$externalName blend. sourceArray is too small for the given dimensions. " +
86                     "$sizeX*$sizeY*4 < ${sourceArray.size}."
87         }
88         require(destArray.size >= sizeX * sizeY * 4) {
89             "$externalName blend. sourceArray is too small for the given dimensions. " +
90                     "$sizeX*$sizeY*4 < ${sourceArray.size}."
91         }
92         validateRestriction("blend", sizeX, sizeY, restriction)
93 
94         nativeBlend(nativeHandle, mode.value, sourceArray, destArray, sizeX, sizeY, restriction)
95     }
96 
97     /**
98      * Blends a source bitmap with the destination bitmap.
99      *
100      * Blends a source bitmap and a destination bitmap, placing the result in the destination
101      * bitmap. The blending is done pairwise between two corresponding RGBA values found in
102      * each bitmap. The mode parameter specify one of fifteen supported blending operations.
103      * See {@link BlendingMode}.
104      *
105      * A variant of this method is available to blend ByteArrays.
106      *
107      * The bitmaps should have identical width and height, and have a config of ARGB_8888.
108      * Bitmaps with a stride different than width * vectorSize are not currently supported.
109      *
110      * An optional range parameter can be set to restrict the operation to a rectangular subset
111      * of each bitmap. If provided, the range must be wholly contained with the dimensions
112      * of the bitmap.
113      *
114      * @param mode The specific blending operation to do.
115      * @param sourceBitmap The RGBA input buffer.
116      * @param destBitmap The destination buffer. Used for input and output.
117      * @param restriction When not null, restricts the operation to a 2D range of pixels.
118      */
119     @JvmOverloads
blendnull120     fun blend(
121         mode: BlendingMode,
122         sourceBitmap: Bitmap,
123         destBitmap: Bitmap,
124         restriction: Range2d? = null
125     ) {
126         validateBitmap("blend", sourceBitmap)
127         validateBitmap("blend", destBitmap)
128         require(
129             sourceBitmap.width == destBitmap.width &&
130                     sourceBitmap.height == destBitmap.height
131         ) {
132             "$externalName blend. Source and destination bitmaps should be the same size. " +
133                     "${sourceBitmap.width}x${sourceBitmap.height} and " +
134                     "${destBitmap.width}x${destBitmap.height} provided."
135         }
136         require(sourceBitmap.config == destBitmap.config) {
137             "RenderScript Toolkit blend. Source and destination bitmaps should have the same " +
138                     "config. ${sourceBitmap.config} and ${destBitmap.config} provided."
139         }
140         validateRestriction("blend", sourceBitmap.width, sourceBitmap.height, restriction)
141 
142         nativeBlendBitmap(nativeHandle, mode.value, sourceBitmap, destBitmap, restriction)
143     }
144 
145     /**
146      * Blurs an image.
147      *
148      * Performs a Gaussian blur of an image and returns result in a ByteArray buffer. A variant of
149      * this method is available to blur Bitmaps.
150      *
151      * The radius determines which pixels are used to compute each blurred pixels. This Toolkit
152      * accepts values between 1 and 25. Larger values create a more blurred effect but also
153      * take longer to compute. When the radius extends past the edge, the edge pixel will
154      * be used as replacement for the pixel that's out off boundary.
155      *
156      * Each input pixel can either be represented by four bytes (RGBA format) or one byte
157      * for the less common blurring of alpha channel only image.
158      *
159      * An optional range parameter can be set to restrict the operation to a rectangular subset
160      * of each buffer. If provided, the range must be wholly contained with the dimensions
161      * described by sizeX and sizeY. NOTE: The output buffer will still be full size, with the
162      * section that's not blurred all set to 0. This is to stay compatible with RenderScript.
163      *
164      * The source buffer should be large enough for sizeX * sizeY * mVectorSize bytes. It has a
165      * row-major layout.
166      *
167      * @param inputArray The buffer of the image to be blurred.
168      * @param vectorSize Either 1 or 4, the number of bytes in each cell, i.e. A vs. RGBA.
169      * @param sizeX The width of both buffers, as a number of 1 or 4 byte cells.
170      * @param sizeY The height of both buffers, as a number of 1 or 4 byte cells.
171      * @param radius The radius of the pixels used to blur, a value from 1 to 25.
172      * @param restriction When not null, restricts the operation to a 2D range of pixels.
173      * @return The blurred pixels, a ByteArray of size.
174      */
175     @JvmOverloads
blurnull176     fun blur(
177         inputArray: ByteArray,
178         vectorSize: Int,
179         sizeX: Int,
180         sizeY: Int,
181         radius: Int = 5,
182         restriction: Range2d? = null
183     ): ByteArray {
184         require(vectorSize == 1 || vectorSize == 4) {
185             "$externalName blur. The vectorSize should be 1 or 4. $vectorSize provided."
186         }
187         require(inputArray.size >= sizeX * sizeY * vectorSize) {
188             "$externalName blur. inputArray is too small for the given dimensions. " +
189                     "$sizeX*$sizeY*$vectorSize < ${inputArray.size}."
190         }
191         require(radius in 1..25) {
192             "$externalName blur. The radius should be between 1 and 25. $radius provided."
193         }
194         validateRestriction("blur", sizeX, sizeY, restriction)
195 
196         val outputArray = ByteArray(inputArray.size)
197         nativeBlur(
198             nativeHandle, inputArray, vectorSize, sizeX, sizeY, radius, outputArray, restriction
199         )
200         return outputArray
201     }
202 
203     /**
204      * Blurs an image.
205      *
206      * Performs a Gaussian blur of a Bitmap and returns result as a Bitmap. A variant of
207      * this method is available to blur ByteArrays.
208      *
209      * The radius determines which pixels are used to compute each blurred pixels. This Toolkit
210      * accepts values between 1 and 25. Larger values create a more blurred effect but also
211      * take longer to compute. When the radius extends past the edge, the edge pixel will
212      * be used as replacement for the pixel that's out off boundary.
213      *
214      * This method supports input Bitmap of config ARGB_8888 and ALPHA_8. Bitmaps with a stride
215      * different than width * vectorSize are not currently supported. The returned Bitmap has the
216      * same config.
217      *
218      * An optional range parameter can be set to restrict the operation to a rectangular subset
219      * of each buffer. If provided, the range must be wholly contained with the dimensions
220      * described by sizeX and sizeY. NOTE: The output Bitmap will still be full size, with the
221      * section that's not blurred all set to 0. This is to stay compatible with RenderScript.
222      *
223      * @param inputBitmap The buffer of the image to be blurred.
224      * @param radius The radius of the pixels used to blur, a value from 1 to 25. Default is 5.
225      * @param restriction When not null, restricts the operation to a 2D range of pixels.
226      * @return The blurred Bitmap.
227      */
228     @JvmOverloads
blurnull229     fun blur(inputBitmap: Bitmap, radius: Int = 5, restriction: Range2d? = null): Bitmap {
230         validateBitmap("blur", inputBitmap)
231         require(radius in 1..25) {
232             "$externalName blur. The radius should be between 1 and 25. $radius provided."
233         }
234         validateRestriction("blur", inputBitmap.width, inputBitmap.height, restriction)
235 
236         val outputBitmap = createCompatibleBitmap(inputBitmap)
237         nativeBlurBitmap(nativeHandle, inputBitmap, outputBitmap, radius, restriction)
238         return outputBitmap
239     }
240 
241     /**
242      * Identity matrix that can be passed to the {@link RenderScriptToolkit::colorMatrix} method.
243      *
244      * Using this matrix will result in no change to the pixel through multiplication although
245      * the pixel value can still be modified by the add vector, or transformed to a different
246      * format.
247      */
248     val identityMatrix: FloatArray
249         get() = floatArrayOf(
250             1f, 0f, 0f, 0f,
251             0f, 1f, 0f, 0f,
252             0f, 0f, 1f, 0f,
253             0f, 0f, 0f, 1f
254         )
255 
256     /**
257      * Matrix to turn color pixels to a grey scale.
258      *
259      * Use this matrix with the {@link RenderScriptToolkit::colorMatrix} method to convert an
260      * image from color to greyscale.
261      */
262     val greyScaleColorMatrix: FloatArray
263         get() = floatArrayOf(
264             0.299f, 0.299f, 0.299f, 0f,
265             0.587f, 0.587f, 0.587f, 0f,
266             0.114f, 0.114f, 0.114f, 0f,
267             0f, 0f, 0f, 1f
268         )
269 
270     /**
271      * Matrix to convert RGB to YUV.
272      *
273      * Use this matrix with the {@link RenderScriptToolkit::colorMatrix} method to convert the
274      * first three bytes of each pixel from RGB to YUV. This leaves the last byte (the alpha
275      * channel) untouched.
276      *
277      * This is a simplistic conversion. Most YUV buffers have more complicated format, not supported
278      * by this method.
279      */
280     val rgbToYuvMatrix: FloatArray
281         get() = floatArrayOf(
282             0.299f, -0.14713f, 0.615f, 0f,
283             0.587f, -0.28886f, -0.51499f, 0f,
284             0.114f, 0.436f, -0.10001f, 0f,
285             0f, 0f, 0f, 1f
286         )
287 
288     /**
289      * Matrix to convert YUV to RGB.
290      *
291      * Use this matrix with the {@link RenderScriptToolkit::colorMatrix} method to convert the
292      * first three bytes of each pixel from YUV to RGB. This leaves the last byte (the alpha
293      * channel) untouched.
294      *
295      * This is a simplistic conversion. Most YUV buffers have more complicated format, not supported
296      * by this method. Use {@link RenderScriptToolkit::yuvToRgb} to convert these buffers.
297      */
298     val yuvToRgbMatrix: FloatArray
299         get() = floatArrayOf(
300             1f, 1f, 1f, 0f,
301             0f, -0.39465f, 2.03211f, 0f,
302             1.13983f, -0.5806f, 0f, 0f,
303             0f, 0f, 0f, 1f
304         )
305 
306     /**
307      * Transform an image using a color matrix.
308      *
309      * Converts a 2D array of vectors of unsigned bytes, multiplying each vectors by a 4x4 matrix
310      * and adding an optional vector.
311      *
312      * Each input vector is composed of 1-4 unsigned bytes. If less than 4 bytes, it's extended to
313      * 4, padding with zeroes. The unsigned bytes are converted from 0-255 to 0.0-1.0 floats
314      * before the multiplication is done.
315      *
316      * The resulting value is normalized from 0.0-1.0 to a 0-255 value and stored in the output.
317      * If the output vector size is less than four, the unused channels are discarded.
318      *
319      * If addVector is not specified, a vector of zeroes is added, i.e. a noop.
320      *
321      * Like the RenderScript Intrinsics, vectorSize of size 3 are padded to occupy 4 bytes.
322      *
323      * Check identityMatrix, greyScaleColorMatrix, rgbToYuvMatrix, and yuvToRgbMatrix for sample
324      * matrices. The YUV conversion may not work for all color spaces.
325      *
326      * @param inputArray The buffer of the image to be converted.
327      * @param inputVectorSize The number of bytes in each input cell, a value from 1 to 4.
328      * @param sizeX The width of both buffers, as a number of 1 to 4 byte cells.
329      * @param sizeY The height of both buffers, as a number of 1 to 4 byte cells.
330      * @param outputVectorSize The number of bytes in each output cell, a value from 1 to 4.
331      * @param matrix The 4x4 matrix to multiply, in row major format.
332      * @param addVector A vector of four floats that's added to the result of the multiplication.
333      * @param restriction When not null, restricts the operation to a 2D range of pixels.
334      * @return The converted buffer.
335      */
336     @JvmOverloads
colorMatrixnull337     fun colorMatrix(
338         inputArray: ByteArray,
339         inputVectorSize: Int,
340         sizeX: Int,
341         sizeY: Int,
342         outputVectorSize: Int,
343         matrix: FloatArray,
344         addVector: FloatArray = floatArrayOf(0f, 0f, 0f, 0f),
345         restriction: Range2d? = null
346     ): ByteArray {
347         require(inputVectorSize in 1..4) {
348             "$externalName colorMatrix. The inputVectorSize should be between 1 and 4. " +
349                     "$inputVectorSize provided."
350         }
351         require(outputVectorSize in 1..4) {
352             "$externalName colorMatrix. The outputVectorSize should be between 1 and 4. " +
353                     "$outputVectorSize provided."
354         }
355         require(inputArray.size >= sizeX * sizeY * inputVectorSize) {
356             "$externalName colorMatrix. inputArray is too small for the given dimensions. " +
357                     "$sizeX*$sizeY*$inputVectorSize < ${inputArray.size}."
358         }
359         require(matrix.size == 16) {
360             "$externalName colorMatrix. matrix should have 16 entries. ${matrix.size} provided."
361         }
362         require(addVector.size == 4) {
363             "$externalName colorMatrix. addVector should have 4 entries. " +
364                     "${addVector.size} provided."
365         }
366         validateRestriction("colorMatrix", sizeX, sizeY, restriction)
367 
368         val outputArray = ByteArray(sizeX * sizeY * paddedSize(outputVectorSize))
369         nativeColorMatrix(
370             nativeHandle, inputArray, inputVectorSize, sizeX, sizeY, outputArray, outputVectorSize,
371             matrix, addVector, restriction
372         )
373         return outputArray
374     }
375 
376     /**
377      * Transform an image using a color matrix.
378      *
379      * Converts a bitmap, multiplying each RGBA value by a 4x4 matrix and adding an optional vector.
380      * Each byte of the RGBA is converted from 0-255 to 0.0-1.0 floats before the multiplication
381      * is done.
382      *
383      * Bitmaps with a stride different than width * vectorSize are not currently supported.
384      *
385      * The resulting value is normalized from 0.0-1.0 to a 0-255 value and stored in the output.
386      *
387      * If addVector is not specified, a vector of zeroes is added, i.e. a noop.
388      *
389      * Check identityMatrix, greyScaleColorMatrix, rgbToYuvMatrix, and yuvToRgbMatrix for sample
390      * matrices. The YUV conversion may not work for all color spaces.
391      *
392      * @param inputBitmap The image to be converted.
393      * @param matrix The 4x4 matrix to multiply, in row major format.
394      * @param addVector A vector of four floats that's added to the result of the multiplication.
395      * @param restriction When not null, restricts the operation to a 2D range of pixels.
396      * @return The converted buffer.
397      */
398     @JvmOverloads
colorMatrixnull399     fun colorMatrix(
400         inputBitmap: Bitmap,
401         matrix: FloatArray,
402         addVector: FloatArray = floatArrayOf(0f, 0f, 0f, 0f),
403         restriction: Range2d? = null
404     ): Bitmap {
405         validateBitmap("colorMatrix", inputBitmap)
406         require(matrix.size == 16) {
407             "$externalName colorMatrix. matrix should have 16 entries. ${matrix.size} provided."
408         }
409         require(addVector.size == 4) {
410             "$externalName colorMatrix. addVector should have 4 entries."
411         }
412         validateRestriction("colorMatrix", inputBitmap.width, inputBitmap.height, restriction)
413 
414         val outputBitmap = createCompatibleBitmap(inputBitmap)
415         nativeColorMatrixBitmap(
416             nativeHandle,
417             inputBitmap,
418             outputBitmap,
419             matrix,
420             addVector,
421             restriction
422         )
423         return outputBitmap
424     }
425 
426     /**
427      * Convolve a ByteArray.
428      *
429      * Applies a 3x3 or 5x5 convolution to the input array using the provided coefficients.
430      * A variant of this method is available to convolve Bitmaps.
431      *
432      * For 3x3 convolutions, 9 coefficients must be provided. For 5x5, 25 coefficients are needed.
433      * The coefficients should be provided in row-major format.
434      *
435      * When the square extends past the edge, the edge values will be used as replacement for the
436      * values that's are off boundary.
437      *
438      * Each input cell can either be represented by one to four bytes. Each byte is multiplied
439      * and accumulated independently of the other bytes of the cell.
440      *
441      * An optional range parameter can be set to restrict the convolve operation to a rectangular
442      * subset of each buffer. If provided, the range must be wholly contained with the dimensions
443      * described by sizeX and sizeY. NOTE: The output buffer will still be full size, with the
444      * section that's not convolved all set to 0. This is to stay compatible with RenderScript.
445      *
446      * The source array should be large enough for sizeX * sizeY * vectorSize bytes. It has a
447      * row-major layout. The output array will have the same dimensions.
448      *
449      * Like the RenderScript Intrinsics, vectorSize of size 3 are padded to occupy 4 bytes.
450      *
451      * @param inputArray The buffer of the image to be blurred.
452      * @param vectorSize The number of bytes in each cell, a value from 1 to 4.
453      * @param sizeX The width of both buffers, as a number of 1 or 4 byte cells.
454      * @param sizeY The height of both buffers, as a number of 1 or 4 byte cells.
455      * @param coefficients A FloatArray of size 9 or 25, containing the multipliers.
456      * @param restriction When not null, restricts the operation to a 2D range of pixels.
457      * @return The convolved array.
458      */
459     @JvmOverloads
convolvenull460     fun convolve(
461         inputArray: ByteArray,
462         vectorSize: Int,
463         sizeX: Int,
464         sizeY: Int,
465         coefficients: FloatArray,
466         restriction: Range2d? = null
467     ): ByteArray {
468         require(vectorSize in 1..4) {
469             "$externalName convolve. The vectorSize should be between 1 and 4. " +
470                     "$vectorSize provided."
471         }
472         require(inputArray.size >= sizeX * sizeY * vectorSize) {
473             "$externalName convolve. inputArray is too small for the given dimensions. " +
474                     "$sizeX*$sizeY*$vectorSize < ${inputArray.size}."
475         }
476         require(coefficients.size == 9 || coefficients.size == 25) {
477             "$externalName convolve. Only 3x3 or 5x5 convolutions are supported. " +
478                     "${coefficients.size} coefficients provided."
479         }
480         validateRestriction("convolve", sizeX, sizeY, restriction)
481 
482         val outputArray = ByteArray(inputArray.size)
483         nativeConvolve(
484             nativeHandle,
485             inputArray,
486             vectorSize,
487             sizeX,
488             sizeY,
489             outputArray,
490             coefficients,
491             restriction
492         )
493         return outputArray
494     }
495 
496     /**
497      * Convolve a Bitmap.
498      *
499      * Applies a 3x3 or 5x5 convolution to the input Bitmap using the provided coefficients.
500      * A variant of this method is available to convolve ByteArrays. Bitmaps with a stride different
501      * than width * vectorSize are not currently supported.
502      *
503      * For 3x3 convolutions, 9 coefficients must be provided. For 5x5, 25 coefficients are needed.
504      * The coefficients should be provided in row-major format.
505      *
506      * Each input cell can either be represented by one to four bytes. Each byte is multiplied
507      * and accumulated independently of the other bytes of the cell.
508      *
509      * An optional range parameter can be set to restrict the convolve operation to a rectangular
510      * subset of each buffer. If provided, the range must be wholly contained with the dimensions
511      * described by sizeX and sizeY. NOTE: The output Bitmap will still be full size, with the
512      * section that's not convolved all set to 0. This is to stay compatible with RenderScript.
513      *
514      * @param inputBitmap The image to be blurred.
515      * @param coefficients A FloatArray of size 9 or 25, containing the multipliers.
516      * @param restriction When not null, restricts the operation to a 2D range of pixels.
517      * @return The convolved Bitmap.
518      */
519     @JvmOverloads
convolvenull520     fun convolve(
521         inputBitmap: Bitmap,
522         coefficients: FloatArray,
523         restriction: Range2d? = null
524     ): Bitmap {
525         validateBitmap("convolve", inputBitmap)
526         require(coefficients.size == 9 || coefficients.size == 25) {
527             "$externalName convolve. Only 3x3 or 5x5 convolutions are supported. " +
528                     "${coefficients.size} coefficients provided."
529         }
530         validateRestriction("convolve", inputBitmap, restriction)
531 
532         val outputBitmap = createCompatibleBitmap(inputBitmap)
533         nativeConvolveBitmap(nativeHandle, inputBitmap, outputBitmap, coefficients, restriction)
534         return outputBitmap
535     }
536 
537     /**
538      * Compute the histogram of an image.
539      *
540      * Tallies how many times each of the 256 possible values of a byte is found in the input.
541      * A variant of this method is available to do the histogram of a Bitmap.
542      *
543      * An input cell can be represented by one to four bytes. The tally is done independently
544      * for each of the bytes of the cell. Correspondingly, the returned IntArray will have
545      * 256 * vectorSize entries. The counts for value 0 are consecutive, followed by those for
546      * value 1, etc.
547      *
548      * An optional range parameter can be set to restrict the operation to a rectangular subset
549      * of each buffer. If provided, the range must be wholly contained with the dimensions
550      * described by sizeX and sizeY.
551      *
552      * The source buffer should be large enough for sizeX * sizeY * vectorSize bytes. It has a
553      * row-major layout.
554      *
555      * Like the RenderScript Intrinsics, vectorSize of size 3 are padded to occupy 4 bytes.
556      *
557      * @param inputArray The buffer of the image to be analyzed.
558      * @param vectorSize The number of bytes in each cell, a value from 1 to 4.
559      * @param sizeX The width of the input buffers, as a number of 1 to 4 byte cells.
560      * @param sizeY The height of the input buffers, as a number of 1 to 4 byte cells.
561      * @param restriction When not null, restricts the operation to a 2D range of pixels.
562      * @return The resulting array of counts.
563      */
564     @JvmOverloads
histogramnull565     fun histogram(
566         inputArray: ByteArray,
567         vectorSize: Int,
568         sizeX: Int,
569         sizeY: Int,
570         restriction: Range2d? = null
571     ): IntArray {
572         require(vectorSize in 1..4) {
573             "$externalName histogram. The vectorSize should be between 1 and 4. " +
574                     "$vectorSize provided."
575         }
576         require(inputArray.size >= sizeX * sizeY * vectorSize) {
577             "$externalName histogram. inputArray is too small for the given dimensions. " +
578                     "$sizeX*$sizeY*$vectorSize < ${inputArray.size}."
579         }
580         validateRestriction("histogram", sizeX, sizeY, restriction)
581 
582         val outputArray = IntArray(256 * paddedSize(vectorSize))
583         nativeHistogram(
584             nativeHandle,
585             inputArray,
586             vectorSize,
587             sizeX,
588             sizeY,
589             outputArray,
590             restriction
591         )
592         return outputArray
593     }
594 
595     /**
596      * Compute the histogram of an image.
597      *
598      * Tallies how many times each of the 256 possible values of a byte is found in the bitmap.
599      * This method supports Bitmaps of config ARGB_8888 and ALPHA_8.
600      *
601      * For ARGB_8888, the tally is done independently of the four bytes. Correspondingly, the
602      * returned IntArray will have 4 * 256 entries. The counts for value 0 are consecutive,
603      * followed by those for value 1, etc.
604      *
605      * For ALPHA_8, an IntArray of size 256 is returned.
606      *
607      * Bitmaps with a stride different than width * vectorSize are not currently supported.
608      *
609      * A variant of this method is available to do the histogram of a ByteArray.
610      *
611      * An optional range parameter can be set to restrict the operation to a rectangular subset
612      * of each buffer. If provided, the range must be wholly contained with the dimensions
613      * described by sizeX and sizeY.
614      *
615      * @param inputBitmap The bitmap to be analyzed.
616      * @param restriction When not null, restricts the operation to a 2D range of pixels.
617      * @return The resulting array of counts.
618      */
619     @JvmOverloads
histogramnull620     fun histogram(
621         inputBitmap: Bitmap,
622         restriction: Range2d? = null
623     ): IntArray {
624         validateBitmap("histogram", inputBitmap)
625         validateRestriction("histogram", inputBitmap, restriction)
626 
627         val outputArray = IntArray(256 * vectorSize(inputBitmap))
628         nativeHistogramBitmap(nativeHandle, inputBitmap, outputArray, restriction)
629         return outputArray
630     }
631 
632     /**
633      * Compute the histogram of the dot product of an image.
634      *
635      * This method supports cells of 1 to 4 bytes in length. For each cell of the array,
636      * the dot product of its bytes with the provided coefficients is computed. The resulting
637      * floating point value is converted to an unsigned byte and tallied in the histogram.
638      *
639      * If coefficients is null, the coefficients used for RGBA luminosity calculation will be used,
640      * i.e. the values [0.299f, 0.587f, 0.114f, 0.f].
641      *
642      * Each coefficients must be >= 0 and their sum must be 1.0 or less. There must be the same
643      * number of coefficients as vectorSize.
644      *
645      * A variant of this method is available to do the histogram of a Bitmap.
646      *
647      * An optional range parameter can be set to restrict the operation to a rectangular subset
648      * of each buffer. If provided, the range must be wholly contained with the dimensions
649      * described by sizeX and sizeY.
650      *
651      * The source buffer should be large enough for sizeX * sizeY * vectorSize bytes. The returned
652      * array will have 256 ints.
653      *
654      * Like the RenderScript Intrinsics, vectorSize of size 3 are padded to occupy 4 bytes.
655      *
656      * @param inputArray The buffer of the image to be analyzed.
657      * @param vectorSize The number of bytes in each cell, a value from 1 to 4.
658      * @param sizeX The width of the input buffers, as a number of 1 to 4 byte cells.
659      * @param sizeY The height of the input buffers, as a number of 1 to 4 byte cells.
660      * @param coefficients The dot product multipliers. Size should equal vectorSize. Can be null.
661      * @param restriction When not null, restricts the operation to a 2D range of pixels.
662      * @return The resulting vector of counts.
663      */
664     @JvmOverloads
histogramDotnull665     fun histogramDot(
666         inputArray: ByteArray,
667         vectorSize: Int,
668         sizeX: Int,
669         sizeY: Int,
670         coefficients: FloatArray? = null,
671         restriction: Range2d? = null
672     ): IntArray {
673         require(vectorSize in 1..4) {
674             "$externalName histogramDot. The vectorSize should be between 1 and 4. " +
675                     "$vectorSize provided."
676         }
677         require(inputArray.size >= sizeX * sizeY * vectorSize) {
678             "$externalName histogramDot. inputArray is too small for the given dimensions. " +
679                     "$sizeX*$sizeY*$vectorSize < ${inputArray.size}."
680         }
681         validateHistogramDotCoefficients(coefficients, vectorSize)
682         validateRestriction("histogramDot", sizeX, sizeY, restriction)
683 
684         val outputArray = IntArray(256)
685         val actualCoefficients = coefficients ?: floatArrayOf(0.299f, 0.587f, 0.114f, 0f)
686         nativeHistogramDot(
687             nativeHandle,
688             inputArray,
689             vectorSize,
690             sizeX,
691             sizeY,
692             outputArray,
693             actualCoefficients,
694             restriction
695         )
696         return outputArray
697     }
698 
699     /**
700      * Compute the histogram of the dot product of an image.
701      *
702      * This method supports Bitmaps of config ARGB_8888 and ALPHA_8. For each pixel of the bitmap,
703      * the dot product of its bytes with the provided coefficients is computed. The resulting
704      * floating point value is converted to an unsigned byte and tallied in the histogram.
705      *
706      * If coefficients is null, the coefficients used for RGBA luminosity calculation will be used,
707      * i.e. the values [0.299f, 0.587f, 0.114f, 0.f].
708      *
709      * Each coefficients must be >= 0 and their sum must be 1.0 or less. For ARGB_8888, four values
710      * must be provided; for ALPHA_8, one.
711      *
712      * Bitmaps with a stride different than width * vectorSize are not currently supported.
713      *
714      * A variant of this method is available to do the histogram of a ByteArray.
715      *
716      * An optional range parameter can be set to restrict the operation to a rectangular subset
717      * of each buffer. If provided, the range must be wholly contained with the dimensions
718      * described by sizeX and sizeY.
719      *
720      * The returned array will have 256 ints.
721      *
722      * @param inputBitmap The bitmap to be analyzed.
723      * @param coefficients The one or four values used for the dot product. Can be null.
724      * @param restriction When not null, restricts the operation to a 2D range of pixels.
725      * @return The resulting vector of counts.
726      */
727     @JvmOverloads
histogramDotnull728     fun histogramDot(
729         inputBitmap: Bitmap,
730         coefficients: FloatArray? = null,
731         restriction: Range2d? = null
732     ): IntArray {
733         validateBitmap("histogramDot", inputBitmap)
734         validateHistogramDotCoefficients(coefficients, vectorSize(inputBitmap))
735         validateRestriction("histogramDot", inputBitmap, restriction)
736 
737         val outputArray = IntArray(256)
738         val actualCoefficients = coefficients ?: floatArrayOf(0.299f, 0.587f, 0.114f, 0f)
739         nativeHistogramDotBitmap(
740             nativeHandle, inputBitmap, outputArray, actualCoefficients, restriction
741         )
742         return outputArray
743     }
744 
745     /**
746      * Transform an image using a look up table
747      *
748      * Transforms an image by using a per-channel lookup table. Each channel of the input has an
749      * independent lookup table. The tables are 256 entries in size and can cover the full value
750      * range of a byte.
751      *
752      * The input array should be in RGBA format, where four consecutive bytes form an cell.
753      * A variant of this method is available to transform a Bitmap.
754      *
755      * An optional range parameter can be set to restrict the operation to a rectangular subset
756      * of each buffer. If provided, the range must be wholly contained with the dimensions
757      * described by sizeX and sizeY. NOTE: The output Bitmap will still be full size, with the
758      * section that's not convolved all set to 0. This is to stay compatible with RenderScript.
759      *
760      * The source array should be large enough for sizeX * sizeY * vectorSize bytes. The returned
761      * ray has the same dimensions as the input. The arrays have a row-major layout.
762      *
763      * @param inputArray The buffer of the image to be transformed.
764      * @param sizeX The width of both buffers, as a number of 4 byte cells.
765      * @param sizeY The height of both buffers, as a number of 4 byte cells.
766      * @param table The four arrays of 256 values that's used to convert each channel.
767      * @param restriction When not null, restricts the operation to a 2D range of pixels.
768      * @return The transformed image.
769      */
770     @JvmOverloads
lutnull771     fun lut(
772         inputArray: ByteArray,
773         sizeX: Int,
774         sizeY: Int,
775         table: LookupTable,
776         restriction: Range2d? = null
777     ): ByteArray {
778         require(inputArray.size >= sizeX * sizeY * 4) {
779             "$externalName lut. inputArray is too small for the given dimensions. " +
780                     "$sizeX*$sizeY*4 < ${inputArray.size}."
781         }
782         validateRestriction("lut", sizeX, sizeY, restriction)
783 
784         val outputArray = ByteArray(inputArray.size)
785         nativeLut(
786             nativeHandle,
787             inputArray,
788             outputArray,
789             sizeX,
790             sizeY,
791             table.red,
792             table.green,
793             table.blue,
794             table.alpha,
795             restriction
796         )
797         return outputArray
798     }
799 
800     /**
801      * Transform an image using a look up table
802      *
803      * Transforms an image by using a per-channel lookup table. Each channel of the input has an
804      * independent lookup table. The tables are 256 entries in size and can cover the full value
805      * range of a byte.
806      *
807      * The input Bitmap should be in config ARGB_8888. A variant of this method is available to
808      * transform a ByteArray. Bitmaps with a stride different than width * vectorSize are not
809      * currently supported.
810      *
811      * An optional range parameter can be set to restrict the operation to a rectangular subset
812      * of each buffer. If provided, the range must be wholly contained with the dimensions
813      * described by sizeX and sizeY. NOTE: The output Bitmap will still be full size, with the
814      * section that's not convolved all set to 0. This is to stay compatible with RenderScript.
815      *
816      * @param inputBitmap The buffer of the image to be transformed.
817      * @param table The four arrays of 256 values that's used to convert each channel.
818      * @param restriction When not null, restricts the operation to a 2D range of pixels.
819      * @return The transformed image.
820      */
821     @JvmOverloads
lutnull822     fun lut(
823         inputBitmap: Bitmap,
824         table: LookupTable,
825         restriction: Range2d? = null
826     ): Bitmap {
827         validateBitmap("lut", inputBitmap)
828         validateRestriction("lut", inputBitmap, restriction)
829 
830         val outputBitmap = createCompatibleBitmap(inputBitmap)
831         nativeLutBitmap(
832             nativeHandle,
833             inputBitmap,
834             outputBitmap,
835             table.red,
836             table.green,
837             table.blue,
838             table.alpha,
839             restriction
840         )
841         return outputBitmap
842     }
843 
844     /**
845      * Transform an image using a 3D look up table
846      *
847      * Transforms an image, converting RGB to RGBA by using a 3D lookup table. The incoming R, G,
848      * and B values are normalized to the dimensions of the provided 3D buffer. The eight nearest
849      * values in that 3D buffer are sampled and linearly interpolated. The resulting RGBA entry
850      * is returned in the output array.
851      *
852      * The input array should be in RGBA format, where four consecutive bytes form an cell.
853      * The fourth byte of each input cell is ignored. A variant of this method is also available
854      * to transform Bitmaps.
855      *
856      * An optional range parameter can be set to restrict the operation to a rectangular subset
857      * of each buffer. If provided, the range must be wholly contained with the dimensions
858      * described by sizeX and sizeY. NOTE: The output array will still be full size, with the
859      * section that's not convolved all set to 0. This is to stay compatible with RenderScript.
860      *
861      * The source array should be large enough for sizeX * sizeY * vectorSize bytes. The returned
862      * array will have the same dimensions. The arrays have a row-major layout.
863      *
864      * @param inputArray The buffer of the image to be transformed.
865      * @param sizeX The width of both buffers, as a number of 4 byte cells.
866      * @param sizeY The height of both buffers, as a number of 4 byte cells.
867      * @param cube The translation cube.
868      * @param restriction When not null, restricts the operation to a 2D range of pixels.
869      * @return The transformed image.
870      */
871     @JvmOverloads
lut3dnull872     fun lut3d(
873         inputArray: ByteArray,
874         sizeX: Int,
875         sizeY: Int,
876         cube: Rgba3dArray,
877         restriction: Range2d? = null
878     ): ByteArray {
879         require(inputArray.size >= sizeX * sizeY * 4) {
880             "$externalName lut3d. inputArray is too small for the given dimensions. " +
881                     "$sizeX*$sizeY*4 < ${inputArray.size}."
882         }
883         require(
884             cube.sizeX >= 2 && cube.sizeY >= 2 && cube.sizeZ >= 2 &&
885                     cube.sizeX <= 256 && cube.sizeY <= 256 && cube.sizeZ <= 256
886         ) {
887             "$externalName lut3d. The dimensions of the cube should be between 2 and 256. " +
888                     "(${cube.sizeX}, ${cube.sizeY}, ${cube.sizeZ}) provided."
889         }
890         validateRestriction("lut3d", sizeX, sizeY, restriction)
891 
892         val outputArray = ByteArray(inputArray.size)
893         nativeLut3d(
894             nativeHandle, inputArray, outputArray, sizeX, sizeY, cube.values, cube.sizeX,
895             cube.sizeY, cube.sizeZ, restriction
896         )
897         return outputArray
898     }
899 
900     /**
901      * Transform an image using a 3D look up table
902      *
903      * Transforms an image, converting RGB to RGBA by using a 3D lookup table. The incoming R, G,
904      * and B values are normalized to the dimensions of the provided 3D buffer. The eight nearest
905      * values in that 3D buffer are sampled and linearly interpolated. The resulting RGBA entry
906      * is returned in the output array.
907      *
908      * The input bitmap should be in RGBA_8888 format. The A channel is preserved. A variant of this
909      * method is also available to transform ByteArray. Bitmaps with a stride different than
910      * width * vectorSize are not currently supported.
911      *
912      * An optional range parameter can be set to restrict the operation to a rectangular subset
913      * of each buffer. If provided, the range must be wholly contained with the dimensions
914      * described by sizeX and sizeY. NOTE: The output array will still be full size, with the
915      * section that's not convolved all set to 0. This is to stay compatible with RenderScript.
916      *
917      * The source array should be large enough for sizeX * sizeY * vectorSize bytes. The returned
918      * array will have the same dimensions. The arrays have a row-major layout.
919      *
920      * @param inputBitmap The image to be transformed.
921      * @param cube The translation cube.
922      * @param restriction When not null, restricts the operation to a 2D range of pixels.
923      * @return The transformed image.
924      */
925     @JvmOverloads
lut3dnull926     fun lut3d(
927         inputBitmap: Bitmap,
928         cube: Rgba3dArray,
929         restriction: Range2d? = null
930     ): Bitmap {
931         validateBitmap("lut3d", inputBitmap)
932         validateRestriction("lut3d", inputBitmap, restriction)
933 
934         val outputBitmap = createCompatibleBitmap(inputBitmap)
935         nativeLut3dBitmap(
936             nativeHandle, inputBitmap, outputBitmap, cube.values, cube.sizeX,
937             cube.sizeY, cube.sizeZ, restriction
938         )
939         return outputBitmap
940     }
941 
942     /**
943      * Resize an image.
944      *
945      * Resizes an image using bicubic interpolation.
946      *
947      * This method supports elements of 1 to 4 bytes in length. Each byte of the element is
948      * interpolated independently from the others.
949      *
950      * An optional range parameter can be set to restrict the operation to a rectangular subset
951      * of the output buffer. The corresponding scaled range of the input will be used. If provided,
952      * the range must be wholly contained with the dimensions described by outputSizeX and
953      * outputSizeY.
954      *
955      * The input and output arrays have a row-major layout. The input array should be
956      * large enough for sizeX * sizeY * vectorSize bytes.
957      *
958      * Like the RenderScript Intrinsics, vectorSize of size 3 are padded to occupy 4 bytes.
959      *
960      * @param inputArray The buffer of the image to be resized.
961      * @param vectorSize The number of bytes in each element of both buffers. A value from 1 to 4.
962      * @param inputSizeX The width of the input buffer, as a number of 1-4 byte elements.
963      * @param inputSizeY The height of the input buffer, as a number of 1-4 byte elements.
964      * @param outputSizeX The width of the output buffer, as a number of 1-4 byte elements.
965      * @param outputSizeY The height of the output buffer, as a number of 1-4 byte elements.
966      * @param restriction When not null, restricts the operation to a 2D range of pixels.
967      * @return An array that contains the rescaled image.
968      */
969     @JvmOverloads
resizenull970     fun resize(
971         inputArray: ByteArray,
972         vectorSize: Int,
973         inputSizeX: Int,
974         inputSizeY: Int,
975         outputSizeX: Int,
976         outputSizeY: Int,
977         restriction: Range2d? = null
978     ): ByteArray {
979         require(vectorSize in 1..4) {
980             "$externalName resize. The vectorSize should be between 1 and 4. $vectorSize provided."
981         }
982         require(inputArray.size >= inputSizeX * inputSizeY * vectorSize) {
983             "$externalName resize. inputArray is too small for the given dimensions. " +
984                     "$inputSizeX*$inputSizeY*$vectorSize < ${inputArray.size}."
985         }
986         validateRestriction("resize", outputSizeX, outputSizeY, restriction)
987 
988         val outputArray = ByteArray(outputSizeX * outputSizeY * paddedSize(vectorSize))
989         nativeResize(
990             nativeHandle,
991             inputArray,
992             vectorSize,
993             inputSizeX,
994             inputSizeY,
995             outputArray,
996             outputSizeX,
997             outputSizeY,
998             restriction
999         )
1000         return outputArray
1001     }
1002 
1003     /**
1004      * Resize an image.
1005      *
1006      * Resizes an image using bicubic interpolation.
1007      *
1008      * This method supports input Bitmap of config ARGB_8888 and ALPHA_8. The returned Bitmap
1009      * has the same config. Bitmaps with a stride different than width * vectorSize are not
1010      * currently supported.
1011      *
1012      * An optional range parameter can be set to restrict the operation to a rectangular subset
1013      * of the output buffer. The corresponding scaled range of the input will be used. If provided,
1014      * the range must be wholly contained with the dimensions described by outputSizeX and
1015      * outputSizeY.
1016      *
1017      * @param inputBitmap The Bitmap to be resized.
1018      * @param outputSizeX The width of the output buffer, as a number of 1-4 byte elements.
1019      * @param outputSizeY The height of the output buffer, as a number of 1-4 byte elements.
1020      * @param restriction When not null, restricts the operation to a 2D range of pixels.
1021      * @return A Bitmap that contains the rescaled image.
1022      */
1023     @JvmOverloads
resizenull1024     fun resize(
1025         inputBitmap: Bitmap,
1026         outputSizeX: Int,
1027         outputSizeY: Int,
1028         restriction: Range2d? = null
1029     ): Bitmap {
1030         validateBitmap("resize", inputBitmap)
1031         validateRestriction("resize", outputSizeX, outputSizeY, restriction)
1032 
1033         val outputBitmap = Bitmap.createBitmap(outputSizeX, outputSizeY, Bitmap.Config.ARGB_8888)
1034         nativeResizeBitmap(nativeHandle, inputBitmap, outputBitmap, restriction)
1035         return outputBitmap
1036     }
1037 
1038     /**
1039      * Convert an image from YUV to RGB.
1040      *
1041      * Converts a YUV buffer to RGB. The input array should be supplied in a supported YUV format.
1042      * The output is RGBA; the alpha channel will be set to 255.
1043      *
1044      * Note that for YV12 and a sizeX that's not a multiple of 32, the RenderScript Intrinsic may
1045      * not have converted the image correctly. This Toolkit method should.
1046      *
1047      * @param inputArray The buffer of the image to be converted.
1048      * @param sizeX The width in pixels of the image.
1049      * @param sizeY The height in pixels of the image.
1050      * @param format Either YV12 or NV21.
1051      * @return The converted image as a byte array.
1052      */
yuvToRgbnull1053     fun yuvToRgb(inputArray: ByteArray, sizeX: Int, sizeY: Int, format: YuvFormat): ByteArray {
1054         require(sizeX % 2 == 0 && sizeY % 2 == 0) {
1055             "$externalName yuvToRgb. Non-even dimensions are not supported. " +
1056                     "$sizeX and $sizeY were provided."
1057         }
1058 
1059         val outputArray = ByteArray(sizeX * sizeY * 4)
1060         nativeYuvToRgb(nativeHandle, inputArray, outputArray, sizeX, sizeY, format.value)
1061         return outputArray
1062     }
1063 
1064     /**
1065      * Convert an image from YUV to an RGB Bitmap.
1066      *
1067      * Converts a YUV buffer to an RGB Bitmap. The input array should be supplied in a supported
1068      * YUV format. The output is RGBA; the alpha channel will be set to 255.
1069      *
1070      * Note that for YV12 and a sizeX that's not a multiple of 32, the RenderScript Intrinsic may
1071      * not have converted the image correctly. This Toolkit method should.
1072      *
1073      * @param inputArray The buffer of the image to be converted.
1074      * @param sizeX The width in pixels of the image.
1075      * @param sizeY The height in pixels of the image.
1076      * @param format Either YV12 or NV21.
1077      * @return The converted image.
1078      */
yuvToRgbBitmapnull1079     fun yuvToRgbBitmap(inputArray: ByteArray, sizeX: Int, sizeY: Int, format: YuvFormat): Bitmap {
1080         require(sizeX % 2 == 0 && sizeY % 2 == 0) {
1081             "$externalName yuvToRgbBitmap. Non-even dimensions are not supported. " +
1082                     "$sizeX and $sizeY were provided."
1083         }
1084 
1085         val outputBitmap = Bitmap.createBitmap(sizeX, sizeY, Bitmap.Config.ARGB_8888)
1086         nativeYuvToRgbBitmap(nativeHandle, inputArray, sizeX, sizeY, outputBitmap, format.value)
1087         return outputBitmap
1088     }
1089 
1090     private var nativeHandle: Long = 0
1091 
1092     init {
1093         System.loadLibrary("renderscript-toolkit")
1094         nativeHandle = createNative()
1095     }
1096 
1097     /**
1098      * Shutdown the thread pool.
1099      *
1100      * Waits for the threads to complete their work and destroys them.
1101      *
1102      * An application should call this method only if it is sure that it won't call the
1103      * toolkit again, as it is irreversible.
1104      */
shutdownnull1105     fun shutdown() {
1106         destroyNative(nativeHandle)
1107         nativeHandle = 0
1108     }
1109 
createNativenull1110     private external fun createNative(): Long
1111 
1112     private external fun destroyNative(nativeHandle: Long)
1113 
1114     private external fun nativeBlend(
1115         nativeHandle: Long,
1116         mode: Int,
1117         sourceArray: ByteArray,
1118         destArray: ByteArray,
1119         sizeX: Int,
1120         sizeY: Int,
1121         restriction: Range2d?
1122     )
1123 
1124     private external fun nativeBlendBitmap(
1125         nativeHandle: Long,
1126         mode: Int,
1127         sourceBitmap: Bitmap,
1128         destBitmap: Bitmap,
1129         restriction: Range2d?
1130     )
1131 
1132     private external fun nativeBlur(
1133         nativeHandle: Long,
1134         inputArray: ByteArray,
1135         vectorSize: Int,
1136         sizeX: Int,
1137         sizeY: Int,
1138         radius: Int,
1139         outputArray: ByteArray,
1140         restriction: Range2d?
1141     )
1142 
1143     private external fun nativeBlurBitmap(
1144         nativeHandle: Long,
1145         inputBitmap: Bitmap,
1146         outputBitmap: Bitmap,
1147         radius: Int,
1148         restriction: Range2d?
1149     )
1150 
1151     private external fun nativeColorMatrix(
1152         nativeHandle: Long,
1153         inputArray: ByteArray,
1154         inputVectorSize: Int,
1155         sizeX: Int,
1156         sizeY: Int,
1157         outputArray: ByteArray,
1158         outputVectorSize: Int,
1159         matrix: FloatArray,
1160         addVector: FloatArray,
1161         restriction: Range2d?
1162     )
1163 
1164     private external fun nativeColorMatrixBitmap(
1165         nativeHandle: Long,
1166         inputBitmap: Bitmap,
1167         outputBitmap: Bitmap,
1168         matrix: FloatArray,
1169         addVector: FloatArray,
1170         restriction: Range2d?
1171     )
1172 
1173     private external fun nativeConvolve(
1174         nativeHandle: Long,
1175         inputArray: ByteArray,
1176         vectorSize: Int,
1177         sizeX: Int,
1178         sizeY: Int,
1179         outputArray: ByteArray,
1180         coefficients: FloatArray,
1181         restriction: Range2d?
1182     )
1183 
1184     private external fun nativeConvolveBitmap(
1185         nativeHandle: Long,
1186         inputBitmap: Bitmap,
1187         outputBitmap: Bitmap,
1188         coefficients: FloatArray,
1189         restriction: Range2d?
1190     )
1191 
1192     private external fun nativeHistogram(
1193         nativeHandle: Long,
1194         inputArray: ByteArray,
1195         vectorSize: Int,
1196         sizeX: Int,
1197         sizeY: Int,
1198         outputArray: IntArray,
1199         restriction: Range2d?
1200     )
1201 
1202     private external fun nativeHistogramBitmap(
1203         nativeHandle: Long,
1204         inputBitmap: Bitmap,
1205         outputArray: IntArray,
1206         restriction: Range2d?
1207     )
1208 
1209     private external fun nativeHistogramDot(
1210         nativeHandle: Long,
1211         inputArray: ByteArray,
1212         vectorSize: Int,
1213         sizeX: Int,
1214         sizeY: Int,
1215         outputArray: IntArray,
1216         coefficients: FloatArray,
1217         restriction: Range2d?
1218     )
1219 
1220     private external fun nativeHistogramDotBitmap(
1221         nativeHandle: Long,
1222         inputBitmap: Bitmap,
1223         outputArray: IntArray,
1224         coefficients: FloatArray,
1225         restriction: Range2d?
1226     )
1227 
1228     private external fun nativeLut(
1229         nativeHandle: Long,
1230         inputArray: ByteArray,
1231         outputArray: ByteArray,
1232         sizeX: Int,
1233         sizeY: Int,
1234         red: ByteArray,
1235         green: ByteArray,
1236         blue: ByteArray,
1237         alpha: ByteArray,
1238         restriction: Range2d?
1239     )
1240 
1241     private external fun nativeLutBitmap(
1242         nativeHandle: Long,
1243         inputBitmap: Bitmap,
1244         outputBitmap: Bitmap,
1245         red: ByteArray,
1246         green: ByteArray,
1247         blue: ByteArray,
1248         alpha: ByteArray,
1249         restriction: Range2d?
1250     )
1251 
1252     private external fun nativeLut3d(
1253         nativeHandle: Long,
1254         inputArray: ByteArray,
1255         outputArray: ByteArray,
1256         sizeX: Int,
1257         sizeY: Int,
1258         cube: ByteArray,
1259         cubeSizeX: Int,
1260         cubeSizeY: Int,
1261         cubeSizeZ: Int,
1262         restriction: Range2d?
1263     )
1264 
1265     private external fun nativeLut3dBitmap(
1266         nativeHandle: Long,
1267         inputBitmap: Bitmap,
1268         outputBitmap: Bitmap,
1269         cube: ByteArray,
1270         cubeSizeX: Int,
1271         cubeSizeY: Int,
1272         cubeSizeZ: Int,
1273         restriction: Range2d?
1274     )
1275 
1276     private external fun nativeResize(
1277         nativeHandle: Long,
1278         inputArray: ByteArray,
1279         vectorSize: Int,
1280         inputSizeX: Int,
1281         inputSizeY: Int,
1282         outputArray: ByteArray,
1283         outputSizeX: Int,
1284         outputSizeY: Int,
1285         restriction: Range2d?
1286     )
1287 
1288     private external fun nativeResizeBitmap(
1289         nativeHandle: Long,
1290         inputBitmap: Bitmap,
1291         outputBitmap: Bitmap,
1292         restriction: Range2d?
1293     )
1294 
1295     private external fun nativeYuvToRgb(
1296         nativeHandle: Long,
1297         inputArray: ByteArray,
1298         outputArray: ByteArray,
1299         sizeX: Int,
1300         sizeY: Int,
1301         format: Int
1302     )
1303 
1304     private external fun nativeYuvToRgbBitmap(
1305         nativeHandle: Long,
1306         inputArray: ByteArray,
1307         sizeX: Int,
1308         sizeY: Int,
1309         outputBitmap: Bitmap,
1310         value: Int
1311     )
1312 }
1313 
1314 /**
1315  * Determines how a source buffer is blended into a destination buffer.
1316  * See {@link RenderScriptToolkit::blend}.
1317  *
1318  * blend only works on 4 byte RGBA data. In the descriptions below, ".a" represents
1319  * the alpha channel.
1320  */
1321 enum class BlendingMode(val value: Int) {
1322     /**
1323      * dest = 0
1324      *
1325      * The destination is cleared, i.e. each pixel is set to (0, 0, 0, 0)
1326      */
1327     CLEAR(0),
1328 
1329     /**
1330      * dest = src
1331      *
1332      * Sets each pixel of the destination to the corresponding one in the source.
1333      */
1334     SRC(1),
1335 
1336     /**
1337      * dest = dest
1338      *
1339      * Leaves the destination untouched. This is a no-op.
1340      */
1341     DST(2),
1342 
1343     /**
1344      * dest = src + dest * (1.0 - src.a)
1345      */
1346     SRC_OVER(3),
1347 
1348     /**
1349      * dest = dest + src * (1.0 - dest.a)
1350      */
1351     DST_OVER(4),
1352 
1353     /**
1354      * dest = src * dest.a
1355      */
1356     SRC_IN(5),
1357 
1358     /**
1359      * dest = dest * src.a
1360      */
1361     DST_IN(6),
1362 
1363     /**
1364      * dest = src * (1.0 - dest.a)
1365      */
1366     SRC_OUT(7),
1367 
1368     /**
1369      * dest = dest * (1.0 - src.a)
1370      */
1371     DST_OUT(8),
1372 
1373     /**
1374      * dest.rgb = src.rgb * dest.a + (1.0 - src.a) * dest.rgb, dest.a = dest.a
1375      */
1376     SRC_ATOP(9),
1377 
1378     /**
1379      * dest = dest.rgb * src.a + (1.0 - dest.a) * src.rgb, dest.a = src.a
1380      */
1381     DST_ATOP(10),
1382 
1383     /**
1384      * dest = {src.r ^ dest.r, src.g ^ dest.g, src.b ^ dest.b, src.a ^ dest.a}
1385      *
1386      * Note: this is NOT the Porter/Duff XOR mode; this is a bitwise xor.
1387      */
1388     XOR(11),
1389 
1390     /**
1391      * dest = src * dest
1392      */
1393     MULTIPLY(12),
1394 
1395     /**
1396      * dest = min(src + dest, 1.0)
1397      */
1398     ADD(13),
1399 
1400     /**
1401      * dest = max(dest - src, 0.0)
1402      */
1403     SUBTRACT(14)
1404 }
1405 
1406 /**
1407  * A translation table used by the lut method. For each potential red, green, blue, and alpha
1408  * value, specifies it's replacement value.
1409  *
1410  * The fields are initialized to be a no-op operation, i.e. replace 1 by 1, 2 by 2, etc.
1411  * You can modify just the values you're interested in having a translation.
1412  */
1413 class LookupTable {
<lambda>null1414     var red = ByteArray(256) { it.toByte() }
<lambda>null1415     var green = ByteArray(256) { it.toByte() }
<lambda>null1416     var blue = ByteArray(256) { it.toByte() }
<lambda>null1417     var alpha = ByteArray(256) { it.toByte() }
1418 }
1419 
1420 /**
1421  * The YUV formats supported by yuvToRgb.
1422  */
1423 enum class YuvFormat(val value: Int) {
1424     NV21(0x11),
1425     YV12(0x32315659),
1426 }
1427 
1428 /**
1429  * Define a range of data to process.
1430  *
1431  * This class is used to restrict a [Toolkit] operation to a rectangular subset of the input
1432  * tensor.
1433  *
1434  * @property startX The index of the first value to be included on the X axis.
1435  * @property endX The index after the last value to be included on the X axis.
1436  * @property startY The index of the first value to be included on the Y axis.
1437  * @property endY The index after the last value to be included on the Y axis.
1438  */
1439 data class Range2d(
1440     val startX: Int,
1441     val endX: Int,
1442     val startY: Int,
1443     val endY: Int
1444 ) {
1445     constructor() : this(0, 0, 0, 0)
1446 }
1447 
1448 class Rgba3dArray(val values: ByteArray, val sizeX: Int, val sizeY: Int, val sizeZ: Int) {
1449     init {
1450         require(values.size >= sizeX * sizeY * sizeZ * 4)
1451     }
1452 
getnull1453     operator fun get(x: Int, y: Int, z: Int): ByteArray {
1454         val index = indexOfVector(x, y, z)
1455         return ByteArray(4) { values[index + it] }
1456     }
1457 
setnull1458     operator fun set(x: Int, y: Int, z: Int, value: ByteArray) {
1459         require(value.size == 4)
1460         val index = indexOfVector(x, y, z)
1461         for (i in 0..3) {
1462             values[index + i] = value[i]
1463         }
1464     }
1465 
indexOfVectornull1466     private fun indexOfVector(x: Int, y: Int, z: Int): Int {
1467         require(x in 0 until sizeX)
1468         require(y in 0 until sizeY)
1469         require(z in 0 until sizeZ)
1470         return ((z * sizeY + y) * sizeX + x) * 4
1471     }
1472 }
1473 
validateBitmapnull1474 internal fun validateBitmap(
1475     function: String,
1476     inputBitmap: Bitmap,
1477     alphaAllowed: Boolean = true
1478 ) {
1479     if (alphaAllowed) {
1480         require(
1481             inputBitmap.config == Bitmap.Config.ARGB_8888 ||
1482                     inputBitmap.config == Bitmap.Config.ALPHA_8
1483         ) {
1484             "$externalName. $function supports only ARGB_8888 and ALPHA_8 bitmaps. " +
1485                     "${inputBitmap.config} provided."
1486         }
1487     } else {
1488         require(inputBitmap.config == Bitmap.Config.ARGB_8888) {
1489             "$externalName. $function supports only ARGB_8888. " +
1490                     "${inputBitmap.config} provided."
1491         }
1492     }
1493     require(inputBitmap.width * vectorSize(inputBitmap) == inputBitmap.rowBytes) {
1494         "$externalName $function. Only bitmaps with rowSize equal to the width * vectorSize are " +
1495                 "currently supported. Provided were rowBytes=${inputBitmap.rowBytes}, " +
1496                 "width={${inputBitmap.width}, and vectorSize=${vectorSize(inputBitmap)}."
1497     }
1498 }
1499 
createCompatibleBitmapnull1500 internal fun createCompatibleBitmap(inputBitmap: Bitmap) =
1501     Bitmap.createBitmap(inputBitmap.width, inputBitmap.height, inputBitmap.config)
1502 
1503 internal fun validateHistogramDotCoefficients(
1504     coefficients: FloatArray?,
1505     vectorSize: Int
1506 ) {
1507     require(coefficients == null || coefficients.size == vectorSize) {
1508         "$externalName histogramDot. The coefficients should be null or have $vectorSize values."
1509     }
1510     if (coefficients !== null) {
1511         var sum = 0f
1512         for (i in 0 until vectorSize) {
1513             require(coefficients[i] >= 0.0f) {
1514                 "$externalName histogramDot. Coefficients should not be negative. " +
1515                         "Coefficient $i was ${coefficients[i]}."
1516             }
1517             sum += coefficients[i]
1518         }
1519         require(sum <= 1.0f) {
1520             "$externalName histogramDot. Coefficients should add to 1 or less. Their sum is $sum."
1521         }
1522     }
1523 }
1524 
validateRestrictionnull1525 internal fun validateRestriction(tag: String, bitmap: Bitmap, restriction: Range2d? = null) {
1526     validateRestriction(tag, bitmap.width, bitmap.height, restriction)
1527 }
1528 
validateRestrictionnull1529 internal fun validateRestriction(
1530     tag: String,
1531     sizeX: Int,
1532     sizeY: Int,
1533     restriction: Range2d? = null
1534 ) {
1535     if (restriction == null) return
1536     require(restriction.startX < sizeX && restriction.endX <= sizeX) {
1537         "$externalName $tag. sizeX should be greater than restriction.startX and greater " +
1538                 "or equal to restriction.endX. $sizeX, ${restriction.startX}, " +
1539                 "and ${restriction.endX} were provided respectively."
1540     }
1541     require(restriction.startY < sizeY && restriction.endY <= sizeY) {
1542         "$externalName $tag. sizeY should be greater than restriction.startY and greater " +
1543                 "or equal to restriction.endY. $sizeY, ${restriction.startY}, " +
1544                 "and ${restriction.endY} were provided respectively."
1545     }
1546     require(restriction.startX < restriction.endX) {
1547         "$externalName $tag. Restriction startX should be less than endX. " +
1548                 "${restriction.startX} and ${restriction.endX} were provided respectively."
1549     }
1550     require(restriction.startY < restriction.endY) {
1551         "$externalName $tag. Restriction startY should be less than endY. " +
1552                 "${restriction.startY} and ${restriction.endY} were provided respectively."
1553     }
1554 }
1555 
vectorSizenull1556 internal fun vectorSize(bitmap: Bitmap): Int {
1557     return when (bitmap.config) {
1558         Bitmap.Config.ARGB_8888 -> 4
1559         Bitmap.Config.ALPHA_8 -> 1
1560         else -> throw IllegalArgumentException(
1561             "$externalName. Only ARGB_8888 and ALPHA_8 Bitmap are supported."
1562         )
1563     }
1564 }
1565 
paddedSizenull1566 internal fun paddedSize(vectorSize: Int) = if (vectorSize == 3) 4 else vectorSize
1567