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