react-native-mask-segment-canvas 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +904 -0
  2. package/dist/components/MaskSegmentCanvas.d.ts +6 -0
  3. package/dist/components/MaskSegmentCanvas.d.ts.map +1 -0
  4. package/dist/components/MaskSegmentCanvas.js +2012 -0
  5. package/dist/components/MaskSegmentCanvas.js.map +1 -0
  6. package/dist/components/MaskSegmentCanvas.types.d.ts +189 -0
  7. package/dist/components/MaskSegmentCanvas.types.d.ts.map +1 -0
  8. package/dist/components/MaskSegmentCanvas.types.js +2 -0
  9. package/dist/components/MaskSegmentCanvas.types.js.map +1 -0
  10. package/dist/index.d.ts +6 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +5 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/shaders/regionPaint.sksl.d.ts +3 -0
  15. package/dist/shaders/regionPaint.sksl.d.ts.map +1 -0
  16. package/dist/shaders/regionPaint.sksl.js +72 -0
  17. package/dist/shaders/regionPaint.sksl.js.map +1 -0
  18. package/dist/utils/compositePaintedImage.d.ts +44 -0
  19. package/dist/utils/compositePaintedImage.d.ts.map +1 -0
  20. package/dist/utils/compositePaintedImage.js +146 -0
  21. package/dist/utils/compositePaintedImage.js.map +1 -0
  22. package/dist/utils/exportUtils.d.ts +20 -0
  23. package/dist/utils/exportUtils.d.ts.map +1 -0
  24. package/dist/utils/exportUtils.js +32 -0
  25. package/dist/utils/exportUtils.js.map +1 -0
  26. package/dist/utils/freqLayerPrep.d.ts +23 -0
  27. package/dist/utils/freqLayerPrep.d.ts.map +1 -0
  28. package/dist/utils/freqLayerPrep.js +168 -0
  29. package/dist/utils/freqLayerPrep.js.map +1 -0
  30. package/dist/utils/maskSegmentRuntime.d.ts +43 -0
  31. package/dist/utils/maskSegmentRuntime.d.ts.map +1 -0
  32. package/dist/utils/maskSegmentRuntime.js +181 -0
  33. package/dist/utils/maskSegmentRuntime.js.map +1 -0
  34. package/dist/utils/maskSegmentation.d.ts +133 -0
  35. package/dist/utils/maskSegmentation.d.ts.map +1 -0
  36. package/dist/utils/maskSegmentation.js +1600 -0
  37. package/dist/utils/maskSegmentation.js.map +1 -0
  38. package/dist/utils/maskSemanticPalette.d.ts +31 -0
  39. package/dist/utils/maskSemanticPalette.d.ts.map +1 -0
  40. package/dist/utils/maskSemanticPalette.js +125 -0
  41. package/dist/utils/maskSemanticPalette.js.map +1 -0
  42. package/dist/utils/opencvAdapter.d.ts +116 -0
  43. package/dist/utils/opencvAdapter.d.ts.map +1 -0
  44. package/dist/utils/opencvAdapter.js +353 -0
  45. package/dist/utils/opencvAdapter.js.map +1 -0
  46. package/dist/utils/paintColorMapTexture.d.ts +5 -0
  47. package/dist/utils/paintColorMapTexture.d.ts.map +1 -0
  48. package/dist/utils/paintColorMapTexture.js +203 -0
  49. package/dist/utils/paintColorMapTexture.js.map +1 -0
  50. package/dist/utils/paintShaderRuntime.d.ts +40 -0
  51. package/dist/utils/paintShaderRuntime.d.ts.map +1 -0
  52. package/dist/utils/paintShaderRuntime.js +76 -0
  53. package/dist/utils/paintShaderRuntime.js.map +1 -0
  54. package/dist/utils/pickMapTexture.d.ts +4 -0
  55. package/dist/utils/pickMapTexture.d.ts.map +1 -0
  56. package/dist/utils/pickMapTexture.js +24 -0
  57. package/dist/utils/pickMapTexture.js.map +1 -0
  58. package/dist/utils/pngImage.d.ts +49 -0
  59. package/dist/utils/pngImage.d.ts.map +1 -0
  60. package/dist/utils/pngImage.js +438 -0
  61. package/dist/utils/pngImage.js.map +1 -0
  62. package/dist/utils/resolveAssetPath.d.ts +3 -0
  63. package/dist/utils/resolveAssetPath.d.ts.map +1 -0
  64. package/dist/utils/resolveAssetPath.js +56 -0
  65. package/dist/utils/resolveAssetPath.js.map +1 -0
  66. package/dist/utils/resolveImageUrl.d.ts +3 -0
  67. package/dist/utils/resolveImageUrl.d.ts.map +1 -0
  68. package/dist/utils/resolveImageUrl.js +51 -0
  69. package/dist/utils/resolveImageUrl.js.map +1 -0
  70. package/dist/utils/skiaImage.d.ts +4 -0
  71. package/dist/utils/skiaImage.d.ts.map +1 -0
  72. package/dist/utils/skiaImage.js +12 -0
  73. package/dist/utils/skiaImage.js.map +1 -0
  74. package/package.json +100 -0
  75. package/patches/react-native-fast-opencv+0.4.8.patch +122 -0
  76. package/src/components/MaskSegmentCanvas.tsx +2832 -0
  77. package/src/components/MaskSegmentCanvas.types.ts +216 -0
  78. package/src/globals.d.ts +19 -0
  79. package/src/index.ts +45 -0
  80. package/src/shaders/regionPaint.sksl.ts +71 -0
  81. package/src/upng-js.d.ts +33 -0
  82. package/src/utils/compositePaintedImage.ts +201 -0
  83. package/src/utils/exportUtils.ts +40 -0
  84. package/src/utils/freqLayerPrep.ts +267 -0
  85. package/src/utils/maskSegmentRuntime.ts +257 -0
  86. package/src/utils/maskSegmentation.ts +2294 -0
  87. package/src/utils/maskSemanticPalette.ts +187 -0
  88. package/src/utils/opencvAdapter.ts +539 -0
  89. package/src/utils/paintColorMapTexture.ts +239 -0
  90. package/src/utils/paintShaderRuntime.tsx +150 -0
  91. package/src/utils/pickMapTexture.ts +37 -0
  92. package/src/utils/pngImage.ts +591 -0
  93. package/src/utils/resolveAssetPath.ts +64 -0
  94. package/src/utils/resolveImageUrl.ts +63 -0
  95. package/src/utils/skiaImage.ts +25 -0
@@ -0,0 +1,239 @@
1
+ import {
2
+ Skia,
3
+ AlphaType,
4
+ ColorType,
5
+ type SkImage,
6
+ } from '@shopify/react-native-skia';
7
+ import type { BgrColor } from '../components/MaskSegmentCanvas.types';
8
+
9
+ /**
10
+ * Separable box blur on premultiplied RGBA (4-channel 0-255).
11
+ * Feather radius in pixels (at the working pick buffer resolution).
12
+ * Works on premultiplied values so that color naturally diffuses alongside alpha
13
+ * into the feather region — preventing "scatter dots" at resolution-mismatched boundaries.
14
+ */
15
+ function boxBlurRgbaPremul(
16
+ rgba: Uint8Array,
17
+ cols: number,
18
+ rows: number,
19
+ radius: number,
20
+ ): Uint8Array {
21
+ const r = Math.max(1, Math.min(16, Math.round(radius)));
22
+ const tmp = new Uint8Array(cols * rows * 4);
23
+ const dst = new Uint8Array(cols * rows * 4);
24
+ // Horizontal pass
25
+ for (let y = 0; y < rows; y++) {
26
+ for (let x = 0; x < cols; x++) {
27
+ let sumR = 0, sumG = 0, sumB = 0, sumA = 0;
28
+ let cnt = 0;
29
+ const base = y * cols * 4;
30
+ for (let dx = -r; dx <= r; dx++) {
31
+ const xx = x + dx;
32
+ if (xx >= 0 && xx < cols) {
33
+ const idx = base + xx * 4;
34
+ sumR += rgba[idx];
35
+ sumG += rgba[idx + 1];
36
+ sumB += rgba[idx + 2];
37
+ sumA += rgba[idx + 3];
38
+ cnt++;
39
+ }
40
+ }
41
+ const o = base + x * 4;
42
+ tmp[o] = Math.round(sumR / cnt);
43
+ tmp[o + 1] = Math.round(sumG / cnt);
44
+ tmp[o + 2] = Math.round(sumB / cnt);
45
+ tmp[o + 3] = Math.round(sumA / cnt);
46
+ }
47
+ }
48
+ // Vertical pass
49
+ for (let x = 0; x < cols; x++) {
50
+ for (let y = 0; y < rows; y++) {
51
+ let sumR = 0, sumG = 0, sumB = 0, sumA = 0;
52
+ let cnt = 0;
53
+ for (let dy = -r; dy <= r; dy++) {
54
+ const yy = y + dy;
55
+ if (yy >= 0 && yy < rows) {
56
+ const idx = yy * cols * 4 + x * 4;
57
+ sumR += tmp[idx];
58
+ sumG += tmp[idx + 1];
59
+ sumB += tmp[idx + 2];
60
+ sumA += tmp[idx + 3];
61
+ cnt++;
62
+ }
63
+ }
64
+ const o = y * cols * 4 + x * 4;
65
+ dst[o] = Math.round(sumR / cnt);
66
+ dst[o + 1] = Math.round(sumG / cnt);
67
+ dst[o + 2] = Math.round(sumB / cnt);
68
+ dst[o + 3] = Math.round(sumA / cnt);
69
+ }
70
+ }
71
+ return dst;
72
+ }
73
+
74
+ /**
75
+ * Separable dilation (max filter) on premultiplied RGBA.
76
+ * Fills small holes (alpha=0) inside painted regions by taking the
77
+ * per-channel maximum in the neighborhood. Applied after boxBlur to
78
+ * eliminate scatter dots caused by segmentation gaps in the pick buffer.
79
+ */
80
+ function dilateRgbaPremul(
81
+ rgba: Uint8Array,
82
+ cols: number,
83
+ rows: number,
84
+ radius: number,
85
+ ): Uint8Array {
86
+ const r = Math.max(1, Math.min(8, Math.round(radius)));
87
+ const tmp = new Uint8Array(cols * rows * 4);
88
+ const dst = new Uint8Array(cols * rows * 4);
89
+ // Horizontal pass
90
+ for (let y = 0; y < rows; y++) {
91
+ for (let x = 0; x < cols; x++) {
92
+ let maxR = 0, maxG = 0, maxB = 0, maxA = 0;
93
+ const base = y * cols * 4;
94
+ for (let dx = -r; dx <= r; dx++) {
95
+ const xx = x + dx;
96
+ if (xx >= 0 && xx < cols) {
97
+ const idx = base + xx * 4;
98
+ if (rgba[idx + 3] > maxA) {
99
+ maxR = rgba[idx];
100
+ maxG = rgba[idx + 1];
101
+ maxB = rgba[idx + 2];
102
+ maxA = rgba[idx + 3];
103
+ } else if (rgba[idx + 3] === maxA && maxA > 0) {
104
+ maxR = Math.max(maxR, rgba[idx]);
105
+ maxG = Math.max(maxG, rgba[idx + 1]);
106
+ maxB = Math.max(maxB, rgba[idx + 2]);
107
+ }
108
+ }
109
+ }
110
+ const o = base + x * 4;
111
+ tmp[o] = maxR;
112
+ tmp[o + 1] = maxG;
113
+ tmp[o + 2] = maxB;
114
+ tmp[o + 3] = maxA;
115
+ }
116
+ }
117
+ // Vertical pass
118
+ for (let x = 0; x < cols; x++) {
119
+ for (let y = 0; y < rows; y++) {
120
+ let maxR = 0, maxG = 0, maxB = 0, maxA = 0;
121
+ for (let dy = -r; dy <= r; dy++) {
122
+ const yy = y + dy;
123
+ if (yy >= 0 && yy < rows) {
124
+ const idx = yy * cols * 4 + x * 4;
125
+ if (tmp[idx + 3] > maxA) {
126
+ maxR = tmp[idx];
127
+ maxG = tmp[idx + 1];
128
+ maxB = tmp[idx + 2];
129
+ maxA = tmp[idx + 3];
130
+ } else if (tmp[idx + 3] === maxA && maxA > 0) {
131
+ maxR = Math.max(maxR, tmp[idx]);
132
+ maxG = Math.max(maxG, tmp[idx + 1]);
133
+ maxB = Math.max(maxB, tmp[idx + 2]);
134
+ }
135
+ }
136
+ }
137
+ const o = y * cols * 4 + x * 4;
138
+ dst[o] = maxR;
139
+ dst[o + 1] = maxG;
140
+ dst[o + 2] = maxB;
141
+ dst[o + 3] = maxA;
142
+ }
143
+ }
144
+ return dst;
145
+ }
146
+
147
+ /** 按 pickMap 展开的上色颜色图(与 pick 同尺寸,未上色像素 a=0)。支持 maskFeather 产生软边缘 alpha。 */
148
+ export function buildPaintColorMapImage(
149
+ pickBuffer: Uint8Array,
150
+ cols: number,
151
+ rows: number,
152
+ paintedRegions: Map<number, BgrColor>,
153
+ featherRadius = 0,
154
+ ): SkImage {
155
+ const pixelCount = cols * rows;
156
+ const colorByPickCode = new Map<number, BgrColor>();
157
+ for (const [regionId, color] of paintedRegions) {
158
+ colorByPickCode.set(regionId + 1, color);
159
+ }
160
+
161
+ // ── Perf fast-path: no feather → 1-pass direct unpremul RGBA (O(N)) ──
162
+ // Skips premul buffer, boxBlur H+V, dilate H+V, and unpremul conversion.
163
+ // The GPU shader handles boundary noise via unpremultiply (rgb /= a) +
164
+ // smoothstep gate, so no CPU-side dilate/filter is needed.
165
+ if (featherRadius <= 0.1 && featherRadius >= -0.1) {
166
+ const rgba = new Uint8Array(pixelCount * 4);
167
+ for (let i = 0; i < pixelCount; i++) {
168
+ const code = pickBuffer[i];
169
+ const color = code > 0 ? colorByPickCode.get(code) : undefined;
170
+ const o = i * 4;
171
+ if (color) {
172
+ rgba[o] = color.r;
173
+ rgba[o + 1] = color.g;
174
+ rgba[o + 2] = color.b;
175
+ rgba[o + 3] = 255;
176
+ }
177
+ }
178
+ const data = Skia.Data.fromBytes(rgba);
179
+ return Skia.Image.MakeImage(
180
+ { width: cols, height: rows, alphaType: AlphaType.Unpremul, colorType: ColorType.RGBA_8888 },
181
+ data,
182
+ cols * 4,
183
+ )!;
184
+ }
185
+
186
+ // ── Feathered path: premul + boxBlur + dilate + unpremul ──
187
+ // Build hard premultiplied RGBA: where a pick code maps to a painted color,
188
+ // store (R, G, B, 255) in premul space. Unpainted pixels stay (0,0,0,0).
189
+ const hardPremul = new Uint8Array(pixelCount * 4);
190
+ for (let i = 0; i < pixelCount; i++) {
191
+ const code = pickBuffer[i];
192
+ const color = code > 0 ? colorByPickCode.get(code) : undefined;
193
+ const o = i * 4;
194
+ if (color) {
195
+ hardPremul[o] = color.r;
196
+ hardPremul[o + 1] = color.g;
197
+ hardPremul[o + 2] = color.b;
198
+ hardPremul[o + 3] = 255;
199
+ }
200
+ }
201
+
202
+ // Box-blur premultiplied RGBA so color naturally diffuses alongside alpha
203
+ // into the feather region. This prevents scatter dots from bilinear
204
+ // interpolation at resolution-mismatched boundaries (where unpainted=black
205
+ // would otherwise leak into boundary samples).
206
+ const blurredPremul = boxBlurRgbaPremul(hardPremul, cols, rows, featherRadius);
207
+
208
+ // Dilation pass: fill small holes (alpha=0) inside painted regions that
209
+ // survive the box blur due to segmentation gaps in the pick buffer.
210
+ const filledPremul = dilateRgbaPremul(blurredPremul, cols, rows, featherRadius);
211
+
212
+ // Unpremultiply and store as RGBA for the final SkImage.
213
+ const rgba = new Uint8Array(pixelCount * 4);
214
+ for (let i = 0; i < pixelCount; i++) {
215
+ const o = i * 4;
216
+ const a = filledPremul[o + 3];
217
+ if (a > 0) {
218
+ rgba[o] = Math.min(255, Math.round((filledPremul[o] * 255) / a));
219
+ rgba[o + 1] = Math.min(255, Math.round((filledPremul[o + 1] * 255) / a));
220
+ rgba[o + 2] = Math.min(255, Math.round((filledPremul[o + 2] * 255) / a));
221
+ rgba[o + 3] = a;
222
+ }
223
+ }
224
+
225
+ const data = Skia.Data.fromBytes(rgba);
226
+ return Skia.Image.MakeImage(
227
+ {
228
+ width: cols,
229
+ height: rows,
230
+ // Unpremul: rgb stores the target paint color (unmodulated), alpha stores coverage (soft edge feather).
231
+ // This ensures ImageShader sampling returns the intended paint color + separate coverage alpha,
232
+ // without premultiplication assumptions that could cause dark fringes on soft edges.
233
+ alphaType: AlphaType.Unpremul,
234
+ colorType: ColorType.RGBA_8888,
235
+ },
236
+ data,
237
+ cols * 4,
238
+ )!;
239
+ }
@@ -0,0 +1,150 @@
1
+ import React from 'react';
2
+ import {
3
+ Skia,
4
+ Fill,
5
+ Shader,
6
+ ImageShader,
7
+ Group,
8
+ drawAsImage,
9
+ type SkImage,
10
+ type SkRuntimeEffect,
11
+ } from '@shopify/react-native-skia';
12
+ import { REGION_PAINT_SKSL } from '../shaders/regionPaint.sksl';
13
+ import type { BgrColor } from '../components/MaskSegmentCanvas.types';
14
+ import { getMaskSegmentRuntimeConfig } from './maskSegmentRuntime';
15
+ import { buildPaintColorMapImage } from './paintColorMapTexture';
16
+
17
+ let cachedEffect: SkRuntimeEffect | null = null;
18
+
19
+ export function getRegionPaintEffect(): SkRuntimeEffect {
20
+ if (cachedEffect) {
21
+ return cachedEffect;
22
+ }
23
+ const effect = Skia.RuntimeEffect.Make(REGION_PAINT_SKSL);
24
+ if (!effect) {
25
+ throw new Error('regionPaint SkSL compile failed');
26
+ }
27
+ cachedEffect = effect;
28
+ return effect;
29
+ }
30
+
31
+ export type PaintShaderTextures = {
32
+ originImage: SkImage;
33
+ paintColorMap: SkImage;
34
+ lowFreqImage: SkImage;
35
+ highFreqImage: SkImage;
36
+ };
37
+
38
+ export function buildPaintShaderUniforms(showOrigin: boolean) {
39
+ const paintCfg = getMaskSegmentRuntimeConfig().paint;
40
+ return {
41
+ colorBaseOpacity: paintCfg.colorBaseOpacity,
42
+ lLightOpacity: paintCfg.lLightOpacity,
43
+ textureOpacity: paintCfg.textureOpacity,
44
+ showOrigin: showOrigin ? 1 : 0,
45
+ };
46
+ }
47
+
48
+ export type PaintShaderLayerProps = PaintShaderTextures & {
49
+ x: number;
50
+ y: number;
51
+ width: number;
52
+ height: number;
53
+ showOrigin?: boolean;
54
+ };
55
+
56
+ function createPaintShaderTree(props: PaintShaderLayerProps) {
57
+ const {
58
+ originImage,
59
+ paintColorMap,
60
+ lowFreqImage,
61
+ highFreqImage,
62
+ x,
63
+ y,
64
+ width,
65
+ height,
66
+ showOrigin = false,
67
+ } = props;
68
+ const effect = getRegionPaintEffect();
69
+ const uniforms = buildPaintShaderUniforms(showOrigin);
70
+ const imageShaderProps = {
71
+ fit: 'fill' as const,
72
+ tx: 'clamp' as const,
73
+ ty: 'clamp' as const,
74
+ rect: { x, y, width, height },
75
+ };
76
+
77
+ return (
78
+ <Fill>
79
+ <Shader source={effect} uniforms={uniforms}>
80
+ <ImageShader image={originImage} {...imageShaderProps} />
81
+ <ImageShader image={paintColorMap} {...imageShaderProps} />
82
+ <ImageShader image={lowFreqImage} {...imageShaderProps} />
83
+ <ImageShader image={highFreqImage} {...imageShaderProps} />
84
+ </Shader>
85
+ </Fill>
86
+ );
87
+ }
88
+
89
+ /** Canvas 内全屏上色 Shader 层 */
90
+ export function PaintShaderLayer(props: PaintShaderLayerProps) {
91
+ return createPaintShaderTree(props);
92
+ }
93
+
94
+ export function createPaintColorMapForPaint(
95
+ pickBuffer: Uint8Array,
96
+ cols: number,
97
+ rows: number,
98
+ paintedRegions: Map<number, BgrColor>,
99
+ ): SkImage {
100
+ const paintCfg = getMaskSegmentRuntimeConfig().paint;
101
+ // Prefer color feather for the recolor application alpha (controls soft edge blend for both base color and texture overlay).
102
+ // texture feather kept in config for future differentiation (e.g. high-freq strength roll-off).
103
+ const feather = paintCfg.maskFeatherColor ?? 0;
104
+ return buildPaintColorMapImage(pickBuffer, cols, rows, paintedRegions, feather);
105
+ }
106
+
107
+ export type OffscreenPaintInput = PaintShaderTextures & {
108
+ width: number;
109
+ height: number;
110
+ showOrigin?: boolean;
111
+ };
112
+
113
+ /** 离屏渲染与预览同源的 shader 合成图 */
114
+ export async function renderPaintedImageOffscreen(
115
+ input: OffscreenPaintInput,
116
+ ): Promise<SkImage | null> {
117
+ const { width, height, showOrigin = false, ...textures } = input;
118
+ if (!textures.originImage || !textures.paintColorMap || !textures.lowFreqImage || !textures.highFreqImage) {
119
+ console.warn('[VIZ-SAVE] renderPaintedImageOffscreen: missing one or more shader textures, will fallback');
120
+ return null;
121
+ }
122
+ // drawAsImage expects the *scene content*, not a <Canvas> host component.
123
+ // Passing a <Canvas> could cause the internal player to construct paints with
124
+ // undefined values (leading to k.charAt errors in color/enum code).
125
+ const element = (
126
+ <Group>
127
+ {createPaintShaderTree({
128
+ ...textures,
129
+ x: 0,
130
+ y: 0,
131
+ width,
132
+ height,
133
+ showOrigin,
134
+ })}
135
+ </Group>
136
+ );
137
+ return drawAsImage(element, { width, height });
138
+ }
139
+
140
+ export function releasePaintShaderTextures(textures: {
141
+ originImage?: SkImage | null;
142
+ paintColorMap?: SkImage | null;
143
+ lowFreqImage?: SkImage | null;
144
+ highFreqImage?: SkImage | null;
145
+ }) {
146
+ textures.originImage?.dispose();
147
+ textures.paintColorMap?.dispose();
148
+ textures.lowFreqImage?.dispose();
149
+ textures.highFreqImage?.dispose();
150
+ }
@@ -0,0 +1,37 @@
1
+ import {
2
+ Skia,
3
+ AlphaType,
4
+ ColorType,
5
+ type SkImage,
6
+ } from '@shopify/react-native-skia';
7
+
8
+ /** pickMap 像素值 regionId+1 → RGBA 纹理(R 通道存查表码) */
9
+ export function pickBufferToSkImage(
10
+ pickBuffer: Uint8Array,
11
+ cols: number,
12
+ rows: number,
13
+ ): SkImage | null {
14
+ if (pickBuffer.length !== cols * rows) {
15
+ return null;
16
+ }
17
+ const rgba = new Uint8Array(cols * rows * 4);
18
+ for (let i = 0; i < pickBuffer.length; i++) {
19
+ const o = i * 4;
20
+ const code = pickBuffer[i];
21
+ rgba[o] = code;
22
+ rgba[o + 1] = code;
23
+ rgba[o + 2] = code;
24
+ rgba[o + 3] = 255;
25
+ }
26
+ const data = Skia.Data.fromBytes(rgba);
27
+ return Skia.Image.MakeImage(
28
+ {
29
+ width: cols,
30
+ height: rows,
31
+ alphaType: AlphaType.Opaque,
32
+ colorType: ColorType.RGBA_8888,
33
+ },
34
+ data,
35
+ cols * 4,
36
+ );
37
+ }