@hxa-rn/react-native-gcanvas 6.0.24-beta.1 → 6.0.26
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.
- package/CHANGELOG.md +2 -2
- package/LICENSE +203 -21
- package/README.md +207 -207
- package/harmony/gcanvas/LICENSE +203 -21
- package/harmony/gcanvas/NOTICE +32 -32
- package/harmony/gcanvas/OAT.xml +37 -37
- package/harmony/gcanvas/build-profile.json5 +16 -16
- package/harmony/gcanvas/hvigorfile.ts +1 -1
- package/harmony/gcanvas/index.ets +1 -1
- package/harmony/gcanvas/oh-package.json5 +14 -14
- package/harmony/gcanvas/src/main/cpp/CMakeLists.txt +126 -126
- package/harmony/gcanvas/src/main/cpp/GCanvasNapi.cpp +485 -485
- package/harmony/gcanvas/src/main/cpp/GCanvasXComponent.cpp +106 -106
- package/harmony/gcanvas/src/main/cpp/GcanvasPackage.h +24 -24
- package/harmony/gcanvas/src/main/ets/GCanvasModuleTurboModule.ts +448 -448
- package/harmony/gcanvas/src/main/ets/GCanvasNativeEngine.ts +206 -206
- package/harmony/gcanvas/src/main/ets/components/GCanvasView.ets +64 -64
- package/harmony/gcanvas/src/main/module.json5 +21 -21
- package/harmony/gcanvas/src/main/resources/base/element/string.json +12 -12
- package/harmony/gcanvas/src/main/resources/en_US/element/string.json +8 -8
- package/harmony/gcanvas/src/main/resources/zh_CN/element/string.json +12 -12
- package/harmony/gcanvas.har +0 -0
- package/package.json +4 -4
- package/packages/gcanvas/.babelrc +4 -4
- package/packages/gcanvas/.eslintrc.js +133 -133
- package/packages/gcanvas/package.json +19 -19
- package/packages/gcanvas/rollup.config.js +13 -13
- package/packages/gcanvas/src/bridge/react-native.js +286 -286
- package/packages/gcanvas/src/bridge/weex.js +210 -210
- package/packages/gcanvas/src/context/2d/FillStyleLinearGradient.js +16 -16
- package/packages/gcanvas/src/context/2d/FillStylePattern.js +7 -7
- package/packages/gcanvas/src/context/2d/FillStyleRadialGradient.js +16 -16
- package/packages/gcanvas/src/context/2d/OffScreenRenderingContext.js +48 -48
- package/packages/gcanvas/src/context/2d/RenderingContext.js +787 -787
- package/packages/gcanvas/src/context/webgl/ActiveInfo.js +10 -10
- package/packages/gcanvas/src/context/webgl/Buffer.js +20 -20
- package/packages/gcanvas/src/context/webgl/Framebuffer.js +20 -20
- package/packages/gcanvas/src/context/webgl/GLenum.js +300 -300
- package/packages/gcanvas/src/context/webgl/GLmethod.js +141 -141
- package/packages/gcanvas/src/context/webgl/GLtype.js +22 -22
- package/packages/gcanvas/src/context/webgl/Program.js +20 -20
- package/packages/gcanvas/src/context/webgl/Renderbuffer.js +20 -20
- package/packages/gcanvas/src/context/webgl/RenderingContext.js +1479 -1479
- package/packages/gcanvas/src/context/webgl/Shader.js +21 -21
- package/packages/gcanvas/src/context/webgl/ShaderPrecisionFormat.js +10 -10
- package/packages/gcanvas/src/context/webgl/Texture.js +21 -21
- package/packages/gcanvas/src/context/webgl/UniformLocation.js +21 -21
- package/packages/gcanvas/src/context/webgl/classUtils.js +2 -2
- package/packages/gcanvas/src/env/canvas.js +210 -210
- package/packages/gcanvas/src/env/image.js +76 -76
- package/packages/gcanvas/src/env/off-screen-canvas.js +74 -74
- package/packages/gcanvas/src/index.js +56 -56
- package/src/components/CanvasView.js +20 -20
- package/src/components/GCanvasComponent.js +408 -408
- package/src/index.js +69 -69
- package/src/packages/gcanvas/.babelrc +4 -4
- package/src/packages/gcanvas/.eslintrc.js +133 -133
- package/src/packages/gcanvas/package.json +19 -19
- package/src/packages/gcanvas/rollup.config.js +13 -13
- package/src/packages/gcanvas/src/bridge/react-native.js +288 -288
- package/src/packages/gcanvas/src/bridge/weex.js +210 -210
- package/src/packages/gcanvas/src/context/2d/FillStyleLinearGradient.js +16 -16
- package/src/packages/gcanvas/src/context/2d/FillStylePattern.js +7 -7
- package/src/packages/gcanvas/src/context/2d/FillStyleRadialGradient.js +16 -16
- package/src/packages/gcanvas/src/context/2d/OffScreenRenderingContext.js +48 -48
- package/src/packages/gcanvas/src/context/2d/RenderingContext.js +801 -801
- package/src/packages/gcanvas/src/context/webgl/ActiveInfo.js +10 -10
- package/src/packages/gcanvas/src/context/webgl/Buffer.js +20 -20
- package/src/packages/gcanvas/src/context/webgl/Framebuffer.js +20 -20
- package/src/packages/gcanvas/src/context/webgl/GLenum.js +300 -300
- package/src/packages/gcanvas/src/context/webgl/GLmethod.js +141 -141
- package/src/packages/gcanvas/src/context/webgl/GLtype.js +22 -22
- package/src/packages/gcanvas/src/context/webgl/Program.js +20 -20
- package/src/packages/gcanvas/src/context/webgl/Renderbuffer.js +20 -20
- package/src/packages/gcanvas/src/context/webgl/RenderingContext.js +1479 -1479
- package/src/packages/gcanvas/src/context/webgl/Shader.js +21 -21
- package/src/packages/gcanvas/src/context/webgl/ShaderPrecisionFormat.js +10 -10
- package/src/packages/gcanvas/src/context/webgl/Texture.js +21 -21
- package/src/packages/gcanvas/src/context/webgl/UniformLocation.js +21 -21
- package/src/packages/gcanvas/src/context/webgl/classUtils.js +2 -2
- package/src/packages/gcanvas/src/env/canvas.js +210 -210
- package/src/packages/gcanvas/src/env/image.js +76 -76
- package/src/packages/gcanvas/src/env/off-screen-canvas.js +74 -74
- package/src/packages/gcanvas/src/index.js +56 -56
- package/src/specs/v1/GCanvasViewNativeComponent.ts +42 -42
- package/src/specs/v1/NativeGCanvasModule.ts +140 -140
|
@@ -1,801 +1,801 @@
|
|
|
1
|
-
import ImageData from '@canvas/image-data/index';
|
|
2
|
-
import base64 from 'base64-js';
|
|
3
|
-
import FillStylePattern from './FillStylePattern';
|
|
4
|
-
import FillStyleLinearGradient from './FillStyleLinearGradient';
|
|
5
|
-
import FillStyleRadialGradient from './FillStyleRadialGradient';
|
|
6
|
-
|
|
7
|
-
export default class CanvasRenderingContext2D {
|
|
8
|
-
_drawCommands = '';
|
|
9
|
-
|
|
10
|
-
_savedGlobalAlpha = [];
|
|
11
|
-
|
|
12
|
-
timer = null;
|
|
13
|
-
componentId = null;
|
|
14
|
-
|
|
15
|
-
// _imageMap = new GHashMap();
|
|
16
|
-
// _textureMap = new GHashMap();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
_resetContextAttributes({
|
|
20
|
-
globalAlpha = 1.0,
|
|
21
|
-
|
|
22
|
-
fillStyle = 'rgb(0,0,0)',
|
|
23
|
-
strokeStyle = 'rgb(0,0,0)',
|
|
24
|
-
|
|
25
|
-
shadowColor = 'rgb(0,0,0)',
|
|
26
|
-
shadowBlur = 0,
|
|
27
|
-
shadowOffsetX = 0,
|
|
28
|
-
shadowOffsetY = 0,
|
|
29
|
-
imageSmoothingEnabled = 1,
|
|
30
|
-
lineWidth = 1,
|
|
31
|
-
lineCap = 'butt',
|
|
32
|
-
lineJoin = 'miter',
|
|
33
|
-
lineDash = [],
|
|
34
|
-
lineDashOffset = 0,
|
|
35
|
-
|
|
36
|
-
miterLimit = 10,
|
|
37
|
-
|
|
38
|
-
globalCompositeOperation = 'source-over',
|
|
39
|
-
|
|
40
|
-
textAlign = 'start',
|
|
41
|
-
textBaseline = 'alphabetic',
|
|
42
|
-
|
|
43
|
-
font = '10px sans-serif',
|
|
44
|
-
}) {
|
|
45
|
-
this.globalAlpha = globalAlpha;
|
|
46
|
-
|
|
47
|
-
this.fillStyle = fillStyle;
|
|
48
|
-
this.strokeStyle = strokeStyle;
|
|
49
|
-
|
|
50
|
-
this.shadowColor = shadowColor;
|
|
51
|
-
this.shadowBlur = shadowBlur;
|
|
52
|
-
this.shadowOffsetX = shadowOffsetX;
|
|
53
|
-
this.shadowOffsetY = shadowOffsetY;
|
|
54
|
-
this.imageSmoothingEnabled = imageSmoothingEnabled;
|
|
55
|
-
this.lineWidth = lineWidth;
|
|
56
|
-
this.lineCap = lineCap;
|
|
57
|
-
this.lineJoin = lineJoin;
|
|
58
|
-
this.setLineDash(lineDash);
|
|
59
|
-
this.lineDashOffset = lineDashOffset;
|
|
60
|
-
|
|
61
|
-
this.miterLimit = miterLimit;
|
|
62
|
-
|
|
63
|
-
this.globalCompositeOperation = globalCompositeOperation;
|
|
64
|
-
|
|
65
|
-
this.textAlign = textAlign;
|
|
66
|
-
|
|
67
|
-
this.textBaseline = textBaseline;
|
|
68
|
-
|
|
69
|
-
this.font = font;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
constructor(canvas) {
|
|
73
|
-
this._canvas = canvas;
|
|
74
|
-
this.className = 'CanvasRenderingContext2D';
|
|
75
|
-
this._resetContextAttributes({});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
get canvas() {
|
|
79
|
-
return this._canvas;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
set fillStyle(value) {
|
|
83
|
-
this._fillStyle = value;
|
|
84
|
-
|
|
85
|
-
if (typeof value == 'string') {
|
|
86
|
-
this._drawCommands = this._drawCommands.concat('F' + value + ';');
|
|
87
|
-
} else if (value instanceof FillStylePattern) {
|
|
88
|
-
const image = value._img;
|
|
89
|
-
const ret = CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
|
|
90
|
-
this._drawCommands = this._drawCommands.concat('G' + image._id + ',' + value._style + ';');
|
|
91
|
-
const flush = () => this.flushJsCommands2CallNative('sync', 'execWithoutDisplay');
|
|
92
|
-
// 等 bind 完成再提交 G。立刻 flush 时 TextureMgr 还是空的,
|
|
93
|
-
// fillRect 图案宽高为 0,右半会一直停在浅底,再点也不变。
|
|
94
|
-
if (ret && typeof ret.then === 'function') {
|
|
95
|
-
this._patternBind = ret.then(flush, flush);
|
|
96
|
-
} else {
|
|
97
|
-
flush();
|
|
98
|
-
this._patternBind = Promise.resolve();
|
|
99
|
-
}
|
|
100
|
-
} else if (value instanceof FillStyleLinearGradient) {
|
|
101
|
-
var command = 'D' + value._start_pos._x.toFixed(2) + ',' + value._start_pos._y.toFixed(2) + ','
|
|
102
|
-
+ value._end_pos._x.toFixed(2) + ',' + value._end_pos._y.toFixed(2) + ','
|
|
103
|
-
+ value._stop_count;
|
|
104
|
-
for (var i = 0; i < value._stop_count; ++i) {
|
|
105
|
-
command += ',' + value._stops[i]._pos + ',' + value._stops[i]._color;
|
|
106
|
-
}
|
|
107
|
-
this._drawCommands = this._drawCommands.concat(command + ';');
|
|
108
|
-
} else if (value instanceof FillStyleRadialGradient) {
|
|
109
|
-
var command = 'H' + value._start_pos._x.toFixed(2) + ',' + value._start_pos._y.toFixed(2) + ',' + value._start_pos._r.toFixed(2) + ','
|
|
110
|
-
+ value._end_pos._x.toFixed(2) + ',' + value._end_pos._y.toFixed(2) + ',' + value._end_pos._r.toFixed(2) + ','
|
|
111
|
-
+ value._stop_count;
|
|
112
|
-
for (var i = 0; i < value._stop_count; ++i) {
|
|
113
|
-
command += ',' + value._stops[i]._pos + ',' + value._stops[i]._color;
|
|
114
|
-
}
|
|
115
|
-
this._drawCommands = this._drawCommands.concat(command + ';');
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
get fillStyle() {
|
|
120
|
-
return this._fillStyle;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
get globalAlpha() {
|
|
124
|
-
return this._globalAlpha;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
set globalAlpha(value) {
|
|
128
|
-
this._globalAlpha = value;
|
|
129
|
-
this._drawCommands = this._drawCommands.concat('a' + value.toFixed(2) + ';');
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
get strokeStyle() {
|
|
134
|
-
return this._strokeStyle;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
set strokeStyle(value) {
|
|
138
|
-
this._strokeStyle = value;
|
|
139
|
-
|
|
140
|
-
if (typeof value == 'string') {
|
|
141
|
-
this._drawCommands = this._drawCommands.concat('S' + value + ';');
|
|
142
|
-
} else if (value instanceof FillStylePattern) {
|
|
143
|
-
const image = value._img;
|
|
144
|
-
const ret = CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
|
|
145
|
-
this._drawCommands = this._drawCommands.concat('G' + image._id + ',' + value._style + ';');
|
|
146
|
-
const flush = () => this.flushJsCommands2CallNative('sync', 'execWithoutDisplay');
|
|
147
|
-
if (ret && typeof ret.then === 'function') {
|
|
148
|
-
this._patternBind = ret.then(flush, flush);
|
|
149
|
-
} else {
|
|
150
|
-
flush();
|
|
151
|
-
this._patternBind = Promise.resolve();
|
|
152
|
-
}
|
|
153
|
-
} else if (value instanceof FillStyleLinearGradient) {
|
|
154
|
-
var command = 'D' + value._start_pos._x.toFixed(2) + ',' + value._start_pos._y.toFixed(2) + ','
|
|
155
|
-
+ value._end_pos._x.toFixed(2) + ',' + value._end_pos._y.toFixed(2) + ','
|
|
156
|
-
+ value._stop_count;
|
|
157
|
-
|
|
158
|
-
for (var i = 0; i < value._stop_count; ++i) {
|
|
159
|
-
command += ',' + value._stops[i]._pos + ',' + value._stops[i]._color;
|
|
160
|
-
}
|
|
161
|
-
this._drawCommands = this._drawCommands.concat(command + ';');
|
|
162
|
-
} else if (value instanceof FillStyleRadialGradient) {
|
|
163
|
-
var command = 'H' + value._start_pos._x.toFixed(2) + ',' + value._start_pos._y.toFixed(2) + ',' + value._start_pos._r.toFixed(2) + ','
|
|
164
|
-
+ value._end_pos._x.toFixed(2) + ',' + value._end_pos._y + ','.toFixed(2) + value._end_pos._r.toFixed(2) + ','
|
|
165
|
-
+ value._stop_count;
|
|
166
|
-
|
|
167
|
-
for (var i = 0; i < value._stop_count; ++i) {
|
|
168
|
-
command += ',' + value._stops[i]._pos + ',' + value._stops[i]._color;
|
|
169
|
-
}
|
|
170
|
-
this._drawCommands = this._drawCommands.concat(command + ';');
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
get shadowColor() {
|
|
175
|
-
return this._shadowColor;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
set shadowColor(value) {
|
|
179
|
-
this._shadowColor = value;
|
|
180
|
-
this._drawCommands = this._drawCommands.concat('K' + value + ';');
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
get shadowBlur() {
|
|
184
|
-
return this._shadowBlur;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
set shadowBlur(value) {
|
|
188
|
-
this._shadowBlur = value;
|
|
189
|
-
this._drawCommands = this._drawCommands.concat('Z' + value + ';');
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
get shadowOffsetX() {
|
|
193
|
-
return this._shadowOffsetX;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
set shadowOffsetX(value) {
|
|
197
|
-
this._shadowOffsetX = value;
|
|
198
|
-
this._drawCommands = this._drawCommands.concat('X' + value + ';');
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
get shadowOffsetY() {
|
|
202
|
-
return this._shadowOffsetY;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
set shadowOffsetY(value) {
|
|
206
|
-
this._shadowOffsetY = value;
|
|
207
|
-
this._drawCommands = this._drawCommands.concat('Y' + value + ';');
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
get imageSmoothingEnabled() {
|
|
211
|
-
return this._imageSmoothingEnabled;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
set imageSmoothingEnabled(value) {
|
|
215
|
-
// 'if' can be here because needDisableImageSmoothing in ios/BridgeModule/GCanvasPlugin.mm
|
|
216
|
-
// and core/android/3d/view/grenderer.cpp also do the work when resetGlViewport
|
|
217
|
-
if (this._imageSmoothingEnabled !== Number(value)) {
|
|
218
|
-
this._imageSmoothingEnabled = Number(value);
|
|
219
|
-
this._drawCommands = this._drawCommands.concat('O' + this._imageSmoothingEnabled + ';');
|
|
220
|
-
this.flushJsCommands2CallNative('sync', 'execWithoutDisplay');
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
get lineDashOffset() {
|
|
225
|
-
return this._lineDashOffset;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
set lineDashOffset(value) {
|
|
229
|
-
this._lineWidth = value;
|
|
230
|
-
this._drawCommands = this._drawCommands.concat('N' + value + ';');
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
get lineWidth() {
|
|
234
|
-
return this._lineWidth;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
set lineWidth(value) {
|
|
238
|
-
this._lineWidth = value;
|
|
239
|
-
this._drawCommands = this._drawCommands.concat('W' + value + ';');
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
get lineCap() {
|
|
243
|
-
return this._lineCap;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
set lineCap(value) {
|
|
247
|
-
this._lineCap = value;
|
|
248
|
-
this._drawCommands = this._drawCommands.concat('C' + value + ';');
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
get lineJoin() {
|
|
252
|
-
return this._lineJoin;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
set lineJoin(value) {
|
|
256
|
-
this._lineJoin = value;
|
|
257
|
-
this._drawCommands = this._drawCommands.concat('J' + value + ';');
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
get miterLimit() {
|
|
261
|
-
return this._miterLimit;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
set miterLimit(value) {
|
|
265
|
-
this._miterLimit = value;
|
|
266
|
-
this._drawCommands = this._drawCommands.concat('M' + value + ';');
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
get globalCompositeOperation() {
|
|
270
|
-
return this._globalCompositeOperation;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
set globalCompositeOperation(value) {
|
|
274
|
-
// to avoid blinks, ref to https://github.com/flyskywhy/react-native-gcanvas/issues/31
|
|
275
|
-
this.flushJsCommands2CallNative('sync', 'execWithoutDisplay');
|
|
276
|
-
|
|
277
|
-
this._globalCompositeOperation = value;
|
|
278
|
-
let mode = 0;
|
|
279
|
-
switch (value) {
|
|
280
|
-
case 'source-over':
|
|
281
|
-
mode = 0;
|
|
282
|
-
break;
|
|
283
|
-
case 'source-atop':
|
|
284
|
-
mode = 5;
|
|
285
|
-
break;
|
|
286
|
-
case 'source-in':
|
|
287
|
-
mode = 0; // TODO
|
|
288
|
-
break;
|
|
289
|
-
case 'source-out':
|
|
290
|
-
mode = 10;
|
|
291
|
-
break;
|
|
292
|
-
case 'destination-over':
|
|
293
|
-
mode = 4;
|
|
294
|
-
break;
|
|
295
|
-
case 'destination-atop':
|
|
296
|
-
mode = 4; // TODO
|
|
297
|
-
break;
|
|
298
|
-
case 'destination-in':
|
|
299
|
-
mode = 11;
|
|
300
|
-
break;
|
|
301
|
-
case 'destination-out':
|
|
302
|
-
mode = 3;
|
|
303
|
-
break;
|
|
304
|
-
case 'lighter':
|
|
305
|
-
mode = 1;
|
|
306
|
-
break;
|
|
307
|
-
case 'copy':
|
|
308
|
-
mode = 7;
|
|
309
|
-
break;
|
|
310
|
-
case 'xor':
|
|
311
|
-
mode = 6;
|
|
312
|
-
break;
|
|
313
|
-
// TODO: add more mode ref to
|
|
314
|
-
// https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
|
|
315
|
-
// GCompositeOperation in core/src/gcanvas/GContext2dType.h
|
|
316
|
-
// GBlendOperationFuncs in core/src/gcanvas/GCanvas2dContext.cpp
|
|
317
|
-
default:
|
|
318
|
-
mode = 0;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
this._drawCommands = this._drawCommands.concat('B' + mode + ';');
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
get textAlign() {
|
|
325
|
-
return this._textAlign;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
set textAlign(value) {
|
|
329
|
-
this._textAlign = value;
|
|
330
|
-
let Align = 0;
|
|
331
|
-
switch (value) {
|
|
332
|
-
case 'start':
|
|
333
|
-
Align = 0;
|
|
334
|
-
break;
|
|
335
|
-
case 'end':
|
|
336
|
-
Align = 1;
|
|
337
|
-
break;
|
|
338
|
-
case 'left':
|
|
339
|
-
Align = 2;
|
|
340
|
-
break;
|
|
341
|
-
case 'center':
|
|
342
|
-
Align = 3;
|
|
343
|
-
break;
|
|
344
|
-
case 'right':
|
|
345
|
-
Align = 4;
|
|
346
|
-
break;
|
|
347
|
-
default:
|
|
348
|
-
Align = 0;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
this._drawCommands = this._drawCommands.concat('A' + Align + ';');
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
get textBaseline() {
|
|
355
|
-
return this._textBaseline;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
set textBaseline(value) {
|
|
359
|
-
this._textBaseline = value;
|
|
360
|
-
let baseline = 0;
|
|
361
|
-
switch (value) {
|
|
362
|
-
case 'alphabetic':
|
|
363
|
-
baseline = 0;
|
|
364
|
-
break;
|
|
365
|
-
case 'middle':
|
|
366
|
-
baseline = 1;
|
|
367
|
-
break;
|
|
368
|
-
case 'top':
|
|
369
|
-
baseline = 2;
|
|
370
|
-
break;
|
|
371
|
-
case 'hanging':
|
|
372
|
-
baseline = 3;
|
|
373
|
-
break;
|
|
374
|
-
case 'bottom':
|
|
375
|
-
baseline = 4;
|
|
376
|
-
break;
|
|
377
|
-
case 'ideographic':
|
|
378
|
-
baseline = 5;
|
|
379
|
-
break;
|
|
380
|
-
default:
|
|
381
|
-
baseline = 0;
|
|
382
|
-
break;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
this._drawCommands = this._drawCommands.concat('E' + baseline + ';');
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
get font() {
|
|
389
|
-
return this._font;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
set font(value) {
|
|
393
|
-
this._font = value;
|
|
394
|
-
this._drawCommands = this._drawCommands.concat('j' + value + ';');
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
getLineDash() {
|
|
398
|
-
return this._lineDash;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
setLineDash(value) {
|
|
402
|
-
if( Array.isArray(value) ) {
|
|
403
|
-
this._lineDash = value;
|
|
404
|
-
this._drawCommands = this._drawCommands.concat('I'+ value.length +','+ value.join(',') + ';');
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
setTransform(a, b, c, d, tx, ty) {
|
|
408
|
-
this._drawCommands = this._drawCommands.concat('t'
|
|
409
|
-
+ (a === 1 ? '1' : a.toFixed(2)) + ','
|
|
410
|
-
+ (b === 0 ? '0' : b.toFixed(2)) + ','
|
|
411
|
-
+ (c === 0 ? '0' : c.toFixed(2)) + ','
|
|
412
|
-
+ (d === 1 ? '1' : d.toFixed(2)) + ',' + tx.toFixed(2) + ',' + ty.toFixed(2) + ';');
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
transform(a, b, c, d, tx, ty) {
|
|
416
|
-
this._drawCommands = this._drawCommands.concat('f'
|
|
417
|
-
+ (a === 1 ? '1' : a.toFixed(2)) + ','
|
|
418
|
-
+ (b === 0 ? '0' : b.toFixed(2)) + ','
|
|
419
|
-
+ (c === 0 ? '0' : c.toFixed(2)) + ','
|
|
420
|
-
+ (d === 1 ? '1' : d.toFixed(2)) + ',' + tx + ',' + ty + ';');
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
resetTransform() {
|
|
424
|
-
this._drawCommands = this._drawCommands.concat('m;');
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
scale(a, d) {
|
|
428
|
-
this._drawCommands = this._drawCommands.concat('k' + a.toFixed(2) + ','
|
|
429
|
-
+ d.toFixed(2) + ';');
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
rotate(angle) {
|
|
433
|
-
this._drawCommands = this._drawCommands
|
|
434
|
-
.concat('r' + angle.toFixed(6) + ';');
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
translate(tx, ty) {
|
|
438
|
-
this._drawCommands = this._drawCommands.concat('l' + tx.toFixed(2) + ',' + ty.toFixed(2) + ';');
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
save() {
|
|
442
|
-
this._savedGlobalAlpha.push(this._globalAlpha);
|
|
443
|
-
this._drawCommands = this._drawCommands.concat('v;');
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
restore() {
|
|
447
|
-
this._drawCommands = this._drawCommands.concat('e;');
|
|
448
|
-
this._globalAlpha = this._savedGlobalAlpha.pop();
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
createPattern(img, pattern) {
|
|
452
|
-
return new FillStylePattern(img, pattern);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
createLinearGradient(x0, y0, x1, y1) {
|
|
456
|
-
return new FillStyleLinearGradient(x0, y0, x1, y1);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
createRadialGradient = function(x0, y0, r0, x1, y1, r1) {
|
|
460
|
-
return new FillStyleRadialGradient(x0, y0, r0, x1, y1, r1);
|
|
461
|
-
};
|
|
462
|
-
|
|
463
|
-
strokeRect(x, y, w, h) {
|
|
464
|
-
this._drawCommands = this._drawCommands.concat('s' + x + ',' + y + ',' + w + ',' + h + ';');
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
clearRect(x, y, w, h) {
|
|
469
|
-
this._drawCommands = this._drawCommands.concat('c' + x + ',' + y + ',' + w
|
|
470
|
-
+ ',' + h + ';');
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
clip() {
|
|
474
|
-
this._drawCommands = this._drawCommands.concat('p;');
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
resetClip() {
|
|
478
|
-
this._drawCommands = this._drawCommands.concat('q;');
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
closePath() {
|
|
482
|
-
this._drawCommands = this._drawCommands.concat('o;');
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
moveTo(x, y) {
|
|
486
|
-
this._drawCommands = this._drawCommands.concat('g' + x.toFixed(2) + ',' + y.toFixed(2) + ';');
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
lineTo(x, y) {
|
|
490
|
-
this._drawCommands = this._drawCommands.concat('i' + x.toFixed(2) + ',' + y.toFixed(2) + ';');
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
quadraticCurveTo = function(cpx, cpy, x, y) {
|
|
494
|
-
this._drawCommands = this._drawCommands.concat('u' + cpx + ',' + cpy + ',' + x + ',' + y + ';');
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y, ) {
|
|
498
|
-
this._drawCommands = this._drawCommands.concat(
|
|
499
|
-
'z' + cp1x.toFixed(2) + ',' + cp1y.toFixed(2) + ',' + cp2x.toFixed(2) + ',' + cp2y.toFixed(2) + ',' +
|
|
500
|
-
x.toFixed(2) + ',' + y.toFixed(2) + ';');
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
arcTo(x1, y1, x2, y2, radius) {
|
|
504
|
-
this._drawCommands = this._drawCommands.concat('h' + x1 + ',' + y1 + ',' + x2 + ',' + y2 + ',' + radius + ';');
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
beginPath() {
|
|
508
|
-
this._drawCommands = this._drawCommands.concat('b;');
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
fillRect(x, y, w, h) {
|
|
513
|
-
this._drawCommands = this._drawCommands.concat('n' + x + ',' + y + ',' + w
|
|
514
|
-
+ ',' + h + ';');
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
rect(x, y, w, h) {
|
|
518
|
-
this._drawCommands = this._drawCommands.concat('w' + x + ',' + y + ',' + w + ',' + h + ';');
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
fill() {
|
|
522
|
-
this._drawCommands = this._drawCommands.concat('L;');
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
stroke(path) {
|
|
526
|
-
this._drawCommands = this._drawCommands.concat('x;');
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
arc(x, y, radius, startAngle, endAngle, anticlockwise) {
|
|
530
|
-
let ianticlockwise = 0;
|
|
531
|
-
if (anticlockwise) {
|
|
532
|
-
ianticlockwise = 1;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
this._drawCommands = this._drawCommands.concat(
|
|
536
|
-
'y' + x.toFixed(2) + ',' + y.toFixed(2) + ','
|
|
537
|
-
+ radius.toFixed(2) + ',' + startAngle + ',' + endAngle + ',' + ianticlockwise
|
|
538
|
-
+ ';'
|
|
539
|
-
);
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
fillText(text, x, y) {
|
|
543
|
-
let tmptext = text.replace(/!/g, '!!');
|
|
544
|
-
tmptext = tmptext.replace(/,/g, '!,');
|
|
545
|
-
tmptext = tmptext.replace(/;/g, '!;');
|
|
546
|
-
this._drawCommands = this._drawCommands.concat('T' + tmptext + ',' + x + ',' + y + ',0.0;');
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
strokeText = function(text, x, y) {
|
|
550
|
-
let tmptext = text.replace(/!/g, '!!');
|
|
551
|
-
tmptext = tmptext.replace(/,/g, '!,');
|
|
552
|
-
tmptext = tmptext.replace(/;/g, '!;');
|
|
553
|
-
this._drawCommands = this._drawCommands.concat('U' + tmptext + ',' + x + ',' + y + ',0.0;');
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
measureText = function(text) {
|
|
557
|
-
cancelAnimationFrame(this._canvas._renderLoopId);
|
|
558
|
-
this.flushJsCommands2CallNative('sync', 'execWithoutDisplay');
|
|
559
|
-
let tmpMeasure = CanvasRenderingContext2D.GBridge.callNative(
|
|
560
|
-
this.componentId,
|
|
561
|
-
'V' + text + ';',
|
|
562
|
-
false,
|
|
563
|
-
'2d',
|
|
564
|
-
'sync',
|
|
565
|
-
'execWithoutDisplay',
|
|
566
|
-
);
|
|
567
|
-
this._canvas._renderLoopId = requestAnimationFrame(this._canvas._renderLoop.bind(this._canvas));
|
|
568
|
-
|
|
569
|
-
let textMetrics = {
|
|
570
|
-
width: 37.343750,
|
|
571
|
-
actualBoundingBoxAscent: 18.4,
|
|
572
|
-
actualBoundingBoxDescent: 5.6,
|
|
573
|
-
};
|
|
574
|
-
try {
|
|
575
|
-
const {width, height, actualBoundingBoxAscent, actualBoundingBoxDescent} = JSON.parse(tmpMeasure);
|
|
576
|
-
textMetrics.width = width;
|
|
577
|
-
if (actualBoundingBoxAscent > 0 && actualBoundingBoxDescent > 0) {
|
|
578
|
-
textMetrics.actualBoundingBoxAscent = actualBoundingBoxAscent;
|
|
579
|
-
textMetrics.actualBoundingBoxDescent = actualBoundingBoxDescent;
|
|
580
|
-
} else {
|
|
581
|
-
textMetrics.actualBoundingBoxAscent = height * 1.15 / 1.5;
|
|
582
|
-
textMetrics.actualBoundingBoxDescent = height - textMetrics.actualBoundingBoxAscent;
|
|
583
|
-
}
|
|
584
|
-
} catch (err) {
|
|
585
|
-
__DEV__ && console.warn('measureText parse error:' + tmpMeasure);
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
return textMetrics;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
isPointInPath = function(x, y) {
|
|
592
|
-
throw new Error('GCanvas not supported yet');
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {
|
|
596
|
-
const numArgs = arguments.length;
|
|
597
|
-
let srcX, srcY, srcW, srcH, dstX, dstY, dstW, dstH;
|
|
598
|
-
|
|
599
|
-
if (numArgs === 3) {
|
|
600
|
-
srcX = 0;
|
|
601
|
-
srcY = 0;
|
|
602
|
-
srcW = image.width;
|
|
603
|
-
srcH = image.height;
|
|
604
|
-
dstX = parseFloat(sx) || 0.0;
|
|
605
|
-
dstY = parseFloat(sy) || 0.0;
|
|
606
|
-
dstW = image.width;
|
|
607
|
-
dstH = image.height;
|
|
608
|
-
} else if (numArgs === 5) {
|
|
609
|
-
srcX = 0;
|
|
610
|
-
srcY = 0;
|
|
611
|
-
srcW = image.width;
|
|
612
|
-
srcH = image.height;
|
|
613
|
-
dstX = parseFloat(sx) || 0.0;
|
|
614
|
-
dstY = parseFloat(sy) || 0.0;
|
|
615
|
-
dstW = parseInt(sw) || image.width;
|
|
616
|
-
dstH = parseInt(sh) || image.height;
|
|
617
|
-
} else if (numArgs === 9) {
|
|
618
|
-
srcX = parseFloat(sx) || 0.0;
|
|
619
|
-
srcY = parseFloat(sy) || 0.0;
|
|
620
|
-
srcW = parseInt(sw) || image.width;
|
|
621
|
-
srcH = parseInt(sh) || image.height;
|
|
622
|
-
dstX = parseFloat(dx) || 0.0;
|
|
623
|
-
dstY = parseFloat(dy) || 0.0;
|
|
624
|
-
dstW = parseInt(dw) || image.width;
|
|
625
|
-
dstH = parseInt(dh) || image.height;
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
const imageIsCanvas = image.hasOwnProperty('nodeName') && image.nodeName.toLowerCase() === 'canvas';
|
|
629
|
-
if (imageIsCanvas) {
|
|
630
|
-
if (!image._context) {
|
|
631
|
-
return;
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
// If use 'execWithDisplay' here, will cause 2nd imageIsCanvas flick of
|
|
635
|
-
// https://github.com/flyskywhy/GCanvasRNExamples/blob/master/app/components/AudioWaveSurfer.js
|
|
636
|
-
//
|
|
637
|
-
// Even if use flushJsCommands2CallNative here, will cause imageIsCanvas not display on screen if
|
|
638
|
-
// not follow by other ctx.some().
|
|
639
|
-
this.flushJsCommands2CallNative('sync', 'execWithoutDisplay');
|
|
640
|
-
|
|
641
|
-
image._context.flushJsCommands2CallNative('sync');
|
|
642
|
-
|
|
643
|
-
let sCanvasId = image.id;
|
|
644
|
-
// even README.md said "despite of values into `canvas.width =` and `canvas.height =`",
|
|
645
|
-
// because canvas of sCanvasId is mostly comes from document.createElement('canvas')
|
|
646
|
-
// that just like offscreen canvas, so it will use style {position: 'absolute'} and
|
|
647
|
-
// that means it's clientWidth will not change (to avoid re-render as offscreen), so
|
|
648
|
-
// it's meaningful to use canvas.width usually be changed after document.createElement('canvas')
|
|
649
|
-
let sCanvasWidth = image.width;
|
|
650
|
-
let sCanvasHeight = image.height;
|
|
651
|
-
CanvasRenderingContext2D.GBridge.drawCanvas2Canvas({
|
|
652
|
-
srcComponentId: sCanvasId,
|
|
653
|
-
dstComponentId: this.componentId,
|
|
654
|
-
tw: sCanvasWidth,
|
|
655
|
-
th: sCanvasHeight,
|
|
656
|
-
sx: srcX,
|
|
657
|
-
sy: srcY,
|
|
658
|
-
sw: srcW,
|
|
659
|
-
sh: srcH,
|
|
660
|
-
dx: dstX,
|
|
661
|
-
dy: dstY,
|
|
662
|
-
dw: dstW,
|
|
663
|
-
dh: dstH,
|
|
664
|
-
});
|
|
665
|
-
} else {
|
|
666
|
-
CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
|
|
667
|
-
this._drawCommands += 'd' + image._id + ','
|
|
668
|
-
+ srcX + ',' + srcY + ',' + srcW + ',' + srcH + ','
|
|
669
|
-
+ dstX + ',' + dstY + ',' + dstW + ',' + dstH + ';';
|
|
670
|
-
|
|
671
|
-
// If use 'execWithDisplay' here, will cause 2nd image flick with 'forceCanvas = true' of
|
|
672
|
-
// https://github.com/flyskywhy/GCanvasRNExamples/blob/master/app/components/Pixi.js ,
|
|
673
|
-
// because in '2d' not 'webgl' PIXI, every PIXI app tick will let all image sprites be drawImage().
|
|
674
|
-
//
|
|
675
|
-
// Even if use flushJsCommands2CallNative here, will cause image not display on screen if
|
|
676
|
-
// not follow by other ctx.some().
|
|
677
|
-
// this.flushJsCommands2CallNative('sync', 'execWithoutDisplay');
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
createImageData(widthOrImagedata, height) {
|
|
682
|
-
if (arguments.length === 2) {
|
|
683
|
-
return new ImageData(widthOrImagedata, height);
|
|
684
|
-
} else {
|
|
685
|
-
return new ImageData(widthOrImagedata.width, widthOrImagedata.height);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
// not a Web canvas API, used by @flyskywhy/react-native-gcanvas
|
|
690
|
-
flushJsCommands2CallNative(
|
|
691
|
-
methodType = 'async',
|
|
692
|
-
optionType = 'execWithDisplay',
|
|
693
|
-
) {
|
|
694
|
-
const commands = this._drawCommands;
|
|
695
|
-
this._drawCommands = ''; // let cmds cache be empty
|
|
696
|
-
|
|
697
|
-
if (commands !== '') {
|
|
698
|
-
CanvasRenderingContext2D.GBridge.callNative(
|
|
699
|
-
this.componentId,
|
|
700
|
-
commands,
|
|
701
|
-
false,
|
|
702
|
-
'2d',
|
|
703
|
-
methodType,
|
|
704
|
-
optionType,
|
|
705
|
-
);
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
// no need ctx.getImageData(x * PixelRatio.get(), y * PixelRatio.get(), w * PixelRatio.get(), h * PixelRatio.get())
|
|
710
|
-
// e.g. ctx.getImageData(0, 0, 2, 2) will return a `w === 2, h === 2` ImageData
|
|
711
|
-
// since PixelsSampler() in GetImageData() of core/src/gcanvas/GCanvas2dContext.cpp
|
|
712
|
-
// will scale the image automatically
|
|
713
|
-
getImageData(sx, sy, sw, sh) {
|
|
714
|
-
// if not stop _renderLoop(), sometimes will cause display issue with 'lightener' tool of https://github.com/flyskywhy/PixelShapeRN
|
|
715
|
-
cancelAnimationFrame(this._canvas._renderLoopId);
|
|
716
|
-
// If use 'async' here, will cause last commands be executed after getImageData's 'sync'.
|
|
717
|
-
if (this._drawCommands.includes(';T') || this._drawCommands.startsWith('T')) {
|
|
718
|
-
// Use 'sync' + 'execWithDisplay' here to make sure last graphics by fillText() be generated
|
|
719
|
-
// before execute getImageData's 'R', ref to context.fillText in
|
|
720
|
-
// https://github.com/flyskywhy/react-native-runescape-text/blob/main/src/classes/Motion.js
|
|
721
|
-
// TODO: more commands other than 'T' if someone issue on github
|
|
722
|
-
this.flushJsCommands2CallNative('sync', 'execWithDisplay');
|
|
723
|
-
} else {
|
|
724
|
-
// If use 'execWithDisplay' here, will cause low JS FPS on iOS with 'lightener' tool of https://github.com/flyskywhy/PixelShapeRN ,
|
|
725
|
-
// because 'lightener' will call getImageData frequently while moving finger, will cause setNeedsDisplay() then
|
|
726
|
-
// drawInRect() be invoked more than 1 times in 16ms e.g. 1times/1ms thus cause low JS FPS!
|
|
727
|
-
this.flushJsCommands2CallNative('sync', 'execWithoutDisplay');
|
|
728
|
-
}
|
|
729
|
-
// now can getImageData from last generated (even not displayed) graphics, otherwise will `return new ImageData(w, h)`
|
|
730
|
-
// thus `this.ctx.putImageData()` has no effect with the first 'Click me to draw some on canvas' in README.md
|
|
731
|
-
|
|
732
|
-
let x = sx;
|
|
733
|
-
let y = sy;
|
|
734
|
-
let w = sw;
|
|
735
|
-
let h = sh;
|
|
736
|
-
if (sx + sw > this.canvas.clientWidth) {
|
|
737
|
-
x = 0;
|
|
738
|
-
w = this.canvas.clientWidth;
|
|
739
|
-
}
|
|
740
|
-
if (sy + sh > this.canvas.clientHeight) {
|
|
741
|
-
y = 0;
|
|
742
|
-
h = this.canvas.clientHeight;
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
let base64Data = CanvasRenderingContext2D.GBridge.callNative(
|
|
746
|
-
this.componentId,
|
|
747
|
-
'R' + x + ',' + y + ',' + w + ',' + h + ';',
|
|
748
|
-
false,
|
|
749
|
-
'2d',
|
|
750
|
-
'sync',
|
|
751
|
-
'execWithoutDisplay',
|
|
752
|
-
);
|
|
753
|
-
|
|
754
|
-
// start _renderLoop() again, and thus display last generated graphics on screen
|
|
755
|
-
// TODO: allow empty commands invoke callNative() once in flushJsCommands2CallNative()
|
|
756
|
-
// while start _renderLoop() just in case no commands after getImageData's 'R'
|
|
757
|
-
// thus no display
|
|
758
|
-
// TODO: this.flushJsCommands2CallNative('sync', 'execWithDisplay') on Android
|
|
759
|
-
// test with 'lightener' tool of https://github.com/flyskywhy/PixelShapeRN
|
|
760
|
-
// to see if low JS FPS
|
|
761
|
-
this._canvas._renderLoopId = requestAnimationFrame(this._canvas._renderLoop.bind(this._canvas));
|
|
762
|
-
|
|
763
|
-
if (base64Data === '') {
|
|
764
|
-
console.warn('getImageData: not good to be here, should refactor source code somewhere');
|
|
765
|
-
return new ImageData(w, h);
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
return new ImageData(new Uint8ClampedArray(base64.toByteArray(base64Data)), w, h);
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
// no need ctx.getImageData(imageData, x * PixelRatio.get(), y * PixelRatio.get())
|
|
772
|
-
// and the imageData is also not `w * PixelRatio.get(), h * PixelRatio.get()`
|
|
773
|
-
putImageData(imageData, x, y, dirtyX, dirtyY, dirtyWidth, dirtyHeight) {
|
|
774
|
-
const base64Data = base64.fromByteArray(imageData.data);
|
|
775
|
-
let tw = imageData.width; // textureWidth
|
|
776
|
-
let th = imageData.height;
|
|
777
|
-
|
|
778
|
-
let sx, sy, sw, sh;
|
|
779
|
-
|
|
780
|
-
if (arguments.length === 3) {
|
|
781
|
-
sx = 0;
|
|
782
|
-
sy = 0;
|
|
783
|
-
sw = tw;
|
|
784
|
-
sh = th;
|
|
785
|
-
} else {
|
|
786
|
-
sx = dirtyX;
|
|
787
|
-
sy = dirtyY;
|
|
788
|
-
// dirtyWidth in https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData
|
|
789
|
-
// means the width of the (src) image data
|
|
790
|
-
sw = dirtyWidth;
|
|
791
|
-
sh = dirtyHeight;
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
if (this._canvas._isAutoClearRectBeforePutImageData) {
|
|
795
|
-
this.clearRect(x + sx, y + sy, sw, sh);
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
this._drawCommands = this._drawCommands.concat('P' + base64Data + ',' + tw + ',' + th + ','
|
|
799
|
-
+ x + ',' + y + ',' + sx + ',' + sy + ',' + sw + ',' + sh + ';');
|
|
800
|
-
}
|
|
801
|
-
}
|
|
1
|
+
import ImageData from '@canvas/image-data/index';
|
|
2
|
+
import base64 from 'base64-js';
|
|
3
|
+
import FillStylePattern from './FillStylePattern';
|
|
4
|
+
import FillStyleLinearGradient from './FillStyleLinearGradient';
|
|
5
|
+
import FillStyleRadialGradient from './FillStyleRadialGradient';
|
|
6
|
+
|
|
7
|
+
export default class CanvasRenderingContext2D {
|
|
8
|
+
_drawCommands = '';
|
|
9
|
+
|
|
10
|
+
_savedGlobalAlpha = [];
|
|
11
|
+
|
|
12
|
+
timer = null;
|
|
13
|
+
componentId = null;
|
|
14
|
+
|
|
15
|
+
// _imageMap = new GHashMap();
|
|
16
|
+
// _textureMap = new GHashMap();
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
_resetContextAttributes({
|
|
20
|
+
globalAlpha = 1.0,
|
|
21
|
+
|
|
22
|
+
fillStyle = 'rgb(0,0,0)',
|
|
23
|
+
strokeStyle = 'rgb(0,0,0)',
|
|
24
|
+
|
|
25
|
+
shadowColor = 'rgb(0,0,0)',
|
|
26
|
+
shadowBlur = 0,
|
|
27
|
+
shadowOffsetX = 0,
|
|
28
|
+
shadowOffsetY = 0,
|
|
29
|
+
imageSmoothingEnabled = 1,
|
|
30
|
+
lineWidth = 1,
|
|
31
|
+
lineCap = 'butt',
|
|
32
|
+
lineJoin = 'miter',
|
|
33
|
+
lineDash = [],
|
|
34
|
+
lineDashOffset = 0,
|
|
35
|
+
|
|
36
|
+
miterLimit = 10,
|
|
37
|
+
|
|
38
|
+
globalCompositeOperation = 'source-over',
|
|
39
|
+
|
|
40
|
+
textAlign = 'start',
|
|
41
|
+
textBaseline = 'alphabetic',
|
|
42
|
+
|
|
43
|
+
font = '10px sans-serif',
|
|
44
|
+
}) {
|
|
45
|
+
this.globalAlpha = globalAlpha;
|
|
46
|
+
|
|
47
|
+
this.fillStyle = fillStyle;
|
|
48
|
+
this.strokeStyle = strokeStyle;
|
|
49
|
+
|
|
50
|
+
this.shadowColor = shadowColor;
|
|
51
|
+
this.shadowBlur = shadowBlur;
|
|
52
|
+
this.shadowOffsetX = shadowOffsetX;
|
|
53
|
+
this.shadowOffsetY = shadowOffsetY;
|
|
54
|
+
this.imageSmoothingEnabled = imageSmoothingEnabled;
|
|
55
|
+
this.lineWidth = lineWidth;
|
|
56
|
+
this.lineCap = lineCap;
|
|
57
|
+
this.lineJoin = lineJoin;
|
|
58
|
+
this.setLineDash(lineDash);
|
|
59
|
+
this.lineDashOffset = lineDashOffset;
|
|
60
|
+
|
|
61
|
+
this.miterLimit = miterLimit;
|
|
62
|
+
|
|
63
|
+
this.globalCompositeOperation = globalCompositeOperation;
|
|
64
|
+
|
|
65
|
+
this.textAlign = textAlign;
|
|
66
|
+
|
|
67
|
+
this.textBaseline = textBaseline;
|
|
68
|
+
|
|
69
|
+
this.font = font;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
constructor(canvas) {
|
|
73
|
+
this._canvas = canvas;
|
|
74
|
+
this.className = 'CanvasRenderingContext2D';
|
|
75
|
+
this._resetContextAttributes({});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get canvas() {
|
|
79
|
+
return this._canvas;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
set fillStyle(value) {
|
|
83
|
+
this._fillStyle = value;
|
|
84
|
+
|
|
85
|
+
if (typeof value == 'string') {
|
|
86
|
+
this._drawCommands = this._drawCommands.concat('F' + value + ';');
|
|
87
|
+
} else if (value instanceof FillStylePattern) {
|
|
88
|
+
const image = value._img;
|
|
89
|
+
const ret = CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
|
|
90
|
+
this._drawCommands = this._drawCommands.concat('G' + image._id + ',' + value._style + ';');
|
|
91
|
+
const flush = () => this.flushJsCommands2CallNative('sync', 'execWithoutDisplay');
|
|
92
|
+
// 等 bind 完成再提交 G。立刻 flush 时 TextureMgr 还是空的,
|
|
93
|
+
// fillRect 图案宽高为 0,右半会一直停在浅底,再点也不变。
|
|
94
|
+
if (ret && typeof ret.then === 'function') {
|
|
95
|
+
this._patternBind = ret.then(flush, flush);
|
|
96
|
+
} else {
|
|
97
|
+
flush();
|
|
98
|
+
this._patternBind = Promise.resolve();
|
|
99
|
+
}
|
|
100
|
+
} else if (value instanceof FillStyleLinearGradient) {
|
|
101
|
+
var command = 'D' + value._start_pos._x.toFixed(2) + ',' + value._start_pos._y.toFixed(2) + ','
|
|
102
|
+
+ value._end_pos._x.toFixed(2) + ',' + value._end_pos._y.toFixed(2) + ','
|
|
103
|
+
+ value._stop_count;
|
|
104
|
+
for (var i = 0; i < value._stop_count; ++i) {
|
|
105
|
+
command += ',' + value._stops[i]._pos + ',' + value._stops[i]._color;
|
|
106
|
+
}
|
|
107
|
+
this._drawCommands = this._drawCommands.concat(command + ';');
|
|
108
|
+
} else if (value instanceof FillStyleRadialGradient) {
|
|
109
|
+
var command = 'H' + value._start_pos._x.toFixed(2) + ',' + value._start_pos._y.toFixed(2) + ',' + value._start_pos._r.toFixed(2) + ','
|
|
110
|
+
+ value._end_pos._x.toFixed(2) + ',' + value._end_pos._y.toFixed(2) + ',' + value._end_pos._r.toFixed(2) + ','
|
|
111
|
+
+ value._stop_count;
|
|
112
|
+
for (var i = 0; i < value._stop_count; ++i) {
|
|
113
|
+
command += ',' + value._stops[i]._pos + ',' + value._stops[i]._color;
|
|
114
|
+
}
|
|
115
|
+
this._drawCommands = this._drawCommands.concat(command + ';');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
get fillStyle() {
|
|
120
|
+
return this._fillStyle;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
get globalAlpha() {
|
|
124
|
+
return this._globalAlpha;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
set globalAlpha(value) {
|
|
128
|
+
this._globalAlpha = value;
|
|
129
|
+
this._drawCommands = this._drawCommands.concat('a' + value.toFixed(2) + ';');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
get strokeStyle() {
|
|
134
|
+
return this._strokeStyle;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
set strokeStyle(value) {
|
|
138
|
+
this._strokeStyle = value;
|
|
139
|
+
|
|
140
|
+
if (typeof value == 'string') {
|
|
141
|
+
this._drawCommands = this._drawCommands.concat('S' + value + ';');
|
|
142
|
+
} else if (value instanceof FillStylePattern) {
|
|
143
|
+
const image = value._img;
|
|
144
|
+
const ret = CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
|
|
145
|
+
this._drawCommands = this._drawCommands.concat('G' + image._id + ',' + value._style + ';');
|
|
146
|
+
const flush = () => this.flushJsCommands2CallNative('sync', 'execWithoutDisplay');
|
|
147
|
+
if (ret && typeof ret.then === 'function') {
|
|
148
|
+
this._patternBind = ret.then(flush, flush);
|
|
149
|
+
} else {
|
|
150
|
+
flush();
|
|
151
|
+
this._patternBind = Promise.resolve();
|
|
152
|
+
}
|
|
153
|
+
} else if (value instanceof FillStyleLinearGradient) {
|
|
154
|
+
var command = 'D' + value._start_pos._x.toFixed(2) + ',' + value._start_pos._y.toFixed(2) + ','
|
|
155
|
+
+ value._end_pos._x.toFixed(2) + ',' + value._end_pos._y.toFixed(2) + ','
|
|
156
|
+
+ value._stop_count;
|
|
157
|
+
|
|
158
|
+
for (var i = 0; i < value._stop_count; ++i) {
|
|
159
|
+
command += ',' + value._stops[i]._pos + ',' + value._stops[i]._color;
|
|
160
|
+
}
|
|
161
|
+
this._drawCommands = this._drawCommands.concat(command + ';');
|
|
162
|
+
} else if (value instanceof FillStyleRadialGradient) {
|
|
163
|
+
var command = 'H' + value._start_pos._x.toFixed(2) + ',' + value._start_pos._y.toFixed(2) + ',' + value._start_pos._r.toFixed(2) + ','
|
|
164
|
+
+ value._end_pos._x.toFixed(2) + ',' + value._end_pos._y + ','.toFixed(2) + value._end_pos._r.toFixed(2) + ','
|
|
165
|
+
+ value._stop_count;
|
|
166
|
+
|
|
167
|
+
for (var i = 0; i < value._stop_count; ++i) {
|
|
168
|
+
command += ',' + value._stops[i]._pos + ',' + value._stops[i]._color;
|
|
169
|
+
}
|
|
170
|
+
this._drawCommands = this._drawCommands.concat(command + ';');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
get shadowColor() {
|
|
175
|
+
return this._shadowColor;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
set shadowColor(value) {
|
|
179
|
+
this._shadowColor = value;
|
|
180
|
+
this._drawCommands = this._drawCommands.concat('K' + value + ';');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
get shadowBlur() {
|
|
184
|
+
return this._shadowBlur;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
set shadowBlur(value) {
|
|
188
|
+
this._shadowBlur = value;
|
|
189
|
+
this._drawCommands = this._drawCommands.concat('Z' + value + ';');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
get shadowOffsetX() {
|
|
193
|
+
return this._shadowOffsetX;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
set shadowOffsetX(value) {
|
|
197
|
+
this._shadowOffsetX = value;
|
|
198
|
+
this._drawCommands = this._drawCommands.concat('X' + value + ';');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
get shadowOffsetY() {
|
|
202
|
+
return this._shadowOffsetY;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
set shadowOffsetY(value) {
|
|
206
|
+
this._shadowOffsetY = value;
|
|
207
|
+
this._drawCommands = this._drawCommands.concat('Y' + value + ';');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
get imageSmoothingEnabled() {
|
|
211
|
+
return this._imageSmoothingEnabled;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
set imageSmoothingEnabled(value) {
|
|
215
|
+
// 'if' can be here because needDisableImageSmoothing in ios/BridgeModule/GCanvasPlugin.mm
|
|
216
|
+
// and core/android/3d/view/grenderer.cpp also do the work when resetGlViewport
|
|
217
|
+
if (this._imageSmoothingEnabled !== Number(value)) {
|
|
218
|
+
this._imageSmoothingEnabled = Number(value);
|
|
219
|
+
this._drawCommands = this._drawCommands.concat('O' + this._imageSmoothingEnabled + ';');
|
|
220
|
+
this.flushJsCommands2CallNative('sync', 'execWithoutDisplay');
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
get lineDashOffset() {
|
|
225
|
+
return this._lineDashOffset;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
set lineDashOffset(value) {
|
|
229
|
+
this._lineWidth = value;
|
|
230
|
+
this._drawCommands = this._drawCommands.concat('N' + value + ';');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
get lineWidth() {
|
|
234
|
+
return this._lineWidth;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
set lineWidth(value) {
|
|
238
|
+
this._lineWidth = value;
|
|
239
|
+
this._drawCommands = this._drawCommands.concat('W' + value + ';');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
get lineCap() {
|
|
243
|
+
return this._lineCap;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
set lineCap(value) {
|
|
247
|
+
this._lineCap = value;
|
|
248
|
+
this._drawCommands = this._drawCommands.concat('C' + value + ';');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
get lineJoin() {
|
|
252
|
+
return this._lineJoin;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
set lineJoin(value) {
|
|
256
|
+
this._lineJoin = value;
|
|
257
|
+
this._drawCommands = this._drawCommands.concat('J' + value + ';');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
get miterLimit() {
|
|
261
|
+
return this._miterLimit;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
set miterLimit(value) {
|
|
265
|
+
this._miterLimit = value;
|
|
266
|
+
this._drawCommands = this._drawCommands.concat('M' + value + ';');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
get globalCompositeOperation() {
|
|
270
|
+
return this._globalCompositeOperation;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
set globalCompositeOperation(value) {
|
|
274
|
+
// to avoid blinks, ref to https://github.com/flyskywhy/react-native-gcanvas/issues/31
|
|
275
|
+
this.flushJsCommands2CallNative('sync', 'execWithoutDisplay');
|
|
276
|
+
|
|
277
|
+
this._globalCompositeOperation = value;
|
|
278
|
+
let mode = 0;
|
|
279
|
+
switch (value) {
|
|
280
|
+
case 'source-over':
|
|
281
|
+
mode = 0;
|
|
282
|
+
break;
|
|
283
|
+
case 'source-atop':
|
|
284
|
+
mode = 5;
|
|
285
|
+
break;
|
|
286
|
+
case 'source-in':
|
|
287
|
+
mode = 0; // TODO
|
|
288
|
+
break;
|
|
289
|
+
case 'source-out':
|
|
290
|
+
mode = 10;
|
|
291
|
+
break;
|
|
292
|
+
case 'destination-over':
|
|
293
|
+
mode = 4;
|
|
294
|
+
break;
|
|
295
|
+
case 'destination-atop':
|
|
296
|
+
mode = 4; // TODO
|
|
297
|
+
break;
|
|
298
|
+
case 'destination-in':
|
|
299
|
+
mode = 11;
|
|
300
|
+
break;
|
|
301
|
+
case 'destination-out':
|
|
302
|
+
mode = 3;
|
|
303
|
+
break;
|
|
304
|
+
case 'lighter':
|
|
305
|
+
mode = 1;
|
|
306
|
+
break;
|
|
307
|
+
case 'copy':
|
|
308
|
+
mode = 7;
|
|
309
|
+
break;
|
|
310
|
+
case 'xor':
|
|
311
|
+
mode = 6;
|
|
312
|
+
break;
|
|
313
|
+
// TODO: add more mode ref to
|
|
314
|
+
// https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
|
|
315
|
+
// GCompositeOperation in core/src/gcanvas/GContext2dType.h
|
|
316
|
+
// GBlendOperationFuncs in core/src/gcanvas/GCanvas2dContext.cpp
|
|
317
|
+
default:
|
|
318
|
+
mode = 0;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
this._drawCommands = this._drawCommands.concat('B' + mode + ';');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
get textAlign() {
|
|
325
|
+
return this._textAlign;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
set textAlign(value) {
|
|
329
|
+
this._textAlign = value;
|
|
330
|
+
let Align = 0;
|
|
331
|
+
switch (value) {
|
|
332
|
+
case 'start':
|
|
333
|
+
Align = 0;
|
|
334
|
+
break;
|
|
335
|
+
case 'end':
|
|
336
|
+
Align = 1;
|
|
337
|
+
break;
|
|
338
|
+
case 'left':
|
|
339
|
+
Align = 2;
|
|
340
|
+
break;
|
|
341
|
+
case 'center':
|
|
342
|
+
Align = 3;
|
|
343
|
+
break;
|
|
344
|
+
case 'right':
|
|
345
|
+
Align = 4;
|
|
346
|
+
break;
|
|
347
|
+
default:
|
|
348
|
+
Align = 0;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
this._drawCommands = this._drawCommands.concat('A' + Align + ';');
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
get textBaseline() {
|
|
355
|
+
return this._textBaseline;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
set textBaseline(value) {
|
|
359
|
+
this._textBaseline = value;
|
|
360
|
+
let baseline = 0;
|
|
361
|
+
switch (value) {
|
|
362
|
+
case 'alphabetic':
|
|
363
|
+
baseline = 0;
|
|
364
|
+
break;
|
|
365
|
+
case 'middle':
|
|
366
|
+
baseline = 1;
|
|
367
|
+
break;
|
|
368
|
+
case 'top':
|
|
369
|
+
baseline = 2;
|
|
370
|
+
break;
|
|
371
|
+
case 'hanging':
|
|
372
|
+
baseline = 3;
|
|
373
|
+
break;
|
|
374
|
+
case 'bottom':
|
|
375
|
+
baseline = 4;
|
|
376
|
+
break;
|
|
377
|
+
case 'ideographic':
|
|
378
|
+
baseline = 5;
|
|
379
|
+
break;
|
|
380
|
+
default:
|
|
381
|
+
baseline = 0;
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
this._drawCommands = this._drawCommands.concat('E' + baseline + ';');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
get font() {
|
|
389
|
+
return this._font;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
set font(value) {
|
|
393
|
+
this._font = value;
|
|
394
|
+
this._drawCommands = this._drawCommands.concat('j' + value + ';');
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
getLineDash() {
|
|
398
|
+
return this._lineDash;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
setLineDash(value) {
|
|
402
|
+
if( Array.isArray(value) ) {
|
|
403
|
+
this._lineDash = value;
|
|
404
|
+
this._drawCommands = this._drawCommands.concat('I'+ value.length +','+ value.join(',') + ';');
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
setTransform(a, b, c, d, tx, ty) {
|
|
408
|
+
this._drawCommands = this._drawCommands.concat('t'
|
|
409
|
+
+ (a === 1 ? '1' : a.toFixed(2)) + ','
|
|
410
|
+
+ (b === 0 ? '0' : b.toFixed(2)) + ','
|
|
411
|
+
+ (c === 0 ? '0' : c.toFixed(2)) + ','
|
|
412
|
+
+ (d === 1 ? '1' : d.toFixed(2)) + ',' + tx.toFixed(2) + ',' + ty.toFixed(2) + ';');
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
transform(a, b, c, d, tx, ty) {
|
|
416
|
+
this._drawCommands = this._drawCommands.concat('f'
|
|
417
|
+
+ (a === 1 ? '1' : a.toFixed(2)) + ','
|
|
418
|
+
+ (b === 0 ? '0' : b.toFixed(2)) + ','
|
|
419
|
+
+ (c === 0 ? '0' : c.toFixed(2)) + ','
|
|
420
|
+
+ (d === 1 ? '1' : d.toFixed(2)) + ',' + tx + ',' + ty + ';');
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
resetTransform() {
|
|
424
|
+
this._drawCommands = this._drawCommands.concat('m;');
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
scale(a, d) {
|
|
428
|
+
this._drawCommands = this._drawCommands.concat('k' + a.toFixed(2) + ','
|
|
429
|
+
+ d.toFixed(2) + ';');
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
rotate(angle) {
|
|
433
|
+
this._drawCommands = this._drawCommands
|
|
434
|
+
.concat('r' + angle.toFixed(6) + ';');
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
translate(tx, ty) {
|
|
438
|
+
this._drawCommands = this._drawCommands.concat('l' + tx.toFixed(2) + ',' + ty.toFixed(2) + ';');
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
save() {
|
|
442
|
+
this._savedGlobalAlpha.push(this._globalAlpha);
|
|
443
|
+
this._drawCommands = this._drawCommands.concat('v;');
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
restore() {
|
|
447
|
+
this._drawCommands = this._drawCommands.concat('e;');
|
|
448
|
+
this._globalAlpha = this._savedGlobalAlpha.pop();
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
createPattern(img, pattern) {
|
|
452
|
+
return new FillStylePattern(img, pattern);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
createLinearGradient(x0, y0, x1, y1) {
|
|
456
|
+
return new FillStyleLinearGradient(x0, y0, x1, y1);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
createRadialGradient = function(x0, y0, r0, x1, y1, r1) {
|
|
460
|
+
return new FillStyleRadialGradient(x0, y0, r0, x1, y1, r1);
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
strokeRect(x, y, w, h) {
|
|
464
|
+
this._drawCommands = this._drawCommands.concat('s' + x + ',' + y + ',' + w + ',' + h + ';');
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
clearRect(x, y, w, h) {
|
|
469
|
+
this._drawCommands = this._drawCommands.concat('c' + x + ',' + y + ',' + w
|
|
470
|
+
+ ',' + h + ';');
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
clip() {
|
|
474
|
+
this._drawCommands = this._drawCommands.concat('p;');
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
resetClip() {
|
|
478
|
+
this._drawCommands = this._drawCommands.concat('q;');
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
closePath() {
|
|
482
|
+
this._drawCommands = this._drawCommands.concat('o;');
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
moveTo(x, y) {
|
|
486
|
+
this._drawCommands = this._drawCommands.concat('g' + x.toFixed(2) + ',' + y.toFixed(2) + ';');
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
lineTo(x, y) {
|
|
490
|
+
this._drawCommands = this._drawCommands.concat('i' + x.toFixed(2) + ',' + y.toFixed(2) + ';');
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
quadraticCurveTo = function(cpx, cpy, x, y) {
|
|
494
|
+
this._drawCommands = this._drawCommands.concat('u' + cpx + ',' + cpy + ',' + x + ',' + y + ';');
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y, ) {
|
|
498
|
+
this._drawCommands = this._drawCommands.concat(
|
|
499
|
+
'z' + cp1x.toFixed(2) + ',' + cp1y.toFixed(2) + ',' + cp2x.toFixed(2) + ',' + cp2y.toFixed(2) + ',' +
|
|
500
|
+
x.toFixed(2) + ',' + y.toFixed(2) + ';');
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
arcTo(x1, y1, x2, y2, radius) {
|
|
504
|
+
this._drawCommands = this._drawCommands.concat('h' + x1 + ',' + y1 + ',' + x2 + ',' + y2 + ',' + radius + ';');
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
beginPath() {
|
|
508
|
+
this._drawCommands = this._drawCommands.concat('b;');
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
fillRect(x, y, w, h) {
|
|
513
|
+
this._drawCommands = this._drawCommands.concat('n' + x + ',' + y + ',' + w
|
|
514
|
+
+ ',' + h + ';');
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
rect(x, y, w, h) {
|
|
518
|
+
this._drawCommands = this._drawCommands.concat('w' + x + ',' + y + ',' + w + ',' + h + ';');
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
fill() {
|
|
522
|
+
this._drawCommands = this._drawCommands.concat('L;');
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
stroke(path) {
|
|
526
|
+
this._drawCommands = this._drawCommands.concat('x;');
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
arc(x, y, radius, startAngle, endAngle, anticlockwise) {
|
|
530
|
+
let ianticlockwise = 0;
|
|
531
|
+
if (anticlockwise) {
|
|
532
|
+
ianticlockwise = 1;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
this._drawCommands = this._drawCommands.concat(
|
|
536
|
+
'y' + x.toFixed(2) + ',' + y.toFixed(2) + ','
|
|
537
|
+
+ radius.toFixed(2) + ',' + startAngle + ',' + endAngle + ',' + ianticlockwise
|
|
538
|
+
+ ';'
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
fillText(text, x, y) {
|
|
543
|
+
let tmptext = text.replace(/!/g, '!!');
|
|
544
|
+
tmptext = tmptext.replace(/,/g, '!,');
|
|
545
|
+
tmptext = tmptext.replace(/;/g, '!;');
|
|
546
|
+
this._drawCommands = this._drawCommands.concat('T' + tmptext + ',' + x + ',' + y + ',0.0;');
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
strokeText = function(text, x, y) {
|
|
550
|
+
let tmptext = text.replace(/!/g, '!!');
|
|
551
|
+
tmptext = tmptext.replace(/,/g, '!,');
|
|
552
|
+
tmptext = tmptext.replace(/;/g, '!;');
|
|
553
|
+
this._drawCommands = this._drawCommands.concat('U' + tmptext + ',' + x + ',' + y + ',0.0;');
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
measureText = function(text) {
|
|
557
|
+
cancelAnimationFrame(this._canvas._renderLoopId);
|
|
558
|
+
this.flushJsCommands2CallNative('sync', 'execWithoutDisplay');
|
|
559
|
+
let tmpMeasure = CanvasRenderingContext2D.GBridge.callNative(
|
|
560
|
+
this.componentId,
|
|
561
|
+
'V' + text + ';',
|
|
562
|
+
false,
|
|
563
|
+
'2d',
|
|
564
|
+
'sync',
|
|
565
|
+
'execWithoutDisplay',
|
|
566
|
+
);
|
|
567
|
+
this._canvas._renderLoopId = requestAnimationFrame(this._canvas._renderLoop.bind(this._canvas));
|
|
568
|
+
|
|
569
|
+
let textMetrics = {
|
|
570
|
+
width: 37.343750,
|
|
571
|
+
actualBoundingBoxAscent: 18.4,
|
|
572
|
+
actualBoundingBoxDescent: 5.6,
|
|
573
|
+
};
|
|
574
|
+
try {
|
|
575
|
+
const {width, height, actualBoundingBoxAscent, actualBoundingBoxDescent} = JSON.parse(tmpMeasure);
|
|
576
|
+
textMetrics.width = width;
|
|
577
|
+
if (actualBoundingBoxAscent > 0 && actualBoundingBoxDescent > 0) {
|
|
578
|
+
textMetrics.actualBoundingBoxAscent = actualBoundingBoxAscent;
|
|
579
|
+
textMetrics.actualBoundingBoxDescent = actualBoundingBoxDescent;
|
|
580
|
+
} else {
|
|
581
|
+
textMetrics.actualBoundingBoxAscent = height * 1.15 / 1.5;
|
|
582
|
+
textMetrics.actualBoundingBoxDescent = height - textMetrics.actualBoundingBoxAscent;
|
|
583
|
+
}
|
|
584
|
+
} catch (err) {
|
|
585
|
+
__DEV__ && console.warn('measureText parse error:' + tmpMeasure);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
return textMetrics;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
isPointInPath = function(x, y) {
|
|
592
|
+
throw new Error('GCanvas not supported yet');
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {
|
|
596
|
+
const numArgs = arguments.length;
|
|
597
|
+
let srcX, srcY, srcW, srcH, dstX, dstY, dstW, dstH;
|
|
598
|
+
|
|
599
|
+
if (numArgs === 3) {
|
|
600
|
+
srcX = 0;
|
|
601
|
+
srcY = 0;
|
|
602
|
+
srcW = image.width;
|
|
603
|
+
srcH = image.height;
|
|
604
|
+
dstX = parseFloat(sx) || 0.0;
|
|
605
|
+
dstY = parseFloat(sy) || 0.0;
|
|
606
|
+
dstW = image.width;
|
|
607
|
+
dstH = image.height;
|
|
608
|
+
} else if (numArgs === 5) {
|
|
609
|
+
srcX = 0;
|
|
610
|
+
srcY = 0;
|
|
611
|
+
srcW = image.width;
|
|
612
|
+
srcH = image.height;
|
|
613
|
+
dstX = parseFloat(sx) || 0.0;
|
|
614
|
+
dstY = parseFloat(sy) || 0.0;
|
|
615
|
+
dstW = parseInt(sw) || image.width;
|
|
616
|
+
dstH = parseInt(sh) || image.height;
|
|
617
|
+
} else if (numArgs === 9) {
|
|
618
|
+
srcX = parseFloat(sx) || 0.0;
|
|
619
|
+
srcY = parseFloat(sy) || 0.0;
|
|
620
|
+
srcW = parseInt(sw) || image.width;
|
|
621
|
+
srcH = parseInt(sh) || image.height;
|
|
622
|
+
dstX = parseFloat(dx) || 0.0;
|
|
623
|
+
dstY = parseFloat(dy) || 0.0;
|
|
624
|
+
dstW = parseInt(dw) || image.width;
|
|
625
|
+
dstH = parseInt(dh) || image.height;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
const imageIsCanvas = image.hasOwnProperty('nodeName') && image.nodeName.toLowerCase() === 'canvas';
|
|
629
|
+
if (imageIsCanvas) {
|
|
630
|
+
if (!image._context) {
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// If use 'execWithDisplay' here, will cause 2nd imageIsCanvas flick of
|
|
635
|
+
// https://github.com/flyskywhy/GCanvasRNExamples/blob/master/app/components/AudioWaveSurfer.js
|
|
636
|
+
//
|
|
637
|
+
// Even if use flushJsCommands2CallNative here, will cause imageIsCanvas not display on screen if
|
|
638
|
+
// not follow by other ctx.some().
|
|
639
|
+
this.flushJsCommands2CallNative('sync', 'execWithoutDisplay');
|
|
640
|
+
|
|
641
|
+
image._context.flushJsCommands2CallNative('sync');
|
|
642
|
+
|
|
643
|
+
let sCanvasId = image.id;
|
|
644
|
+
// even README.md said "despite of values into `canvas.width =` and `canvas.height =`",
|
|
645
|
+
// because canvas of sCanvasId is mostly comes from document.createElement('canvas')
|
|
646
|
+
// that just like offscreen canvas, so it will use style {position: 'absolute'} and
|
|
647
|
+
// that means it's clientWidth will not change (to avoid re-render as offscreen), so
|
|
648
|
+
// it's meaningful to use canvas.width usually be changed after document.createElement('canvas')
|
|
649
|
+
let sCanvasWidth = image.width;
|
|
650
|
+
let sCanvasHeight = image.height;
|
|
651
|
+
CanvasRenderingContext2D.GBridge.drawCanvas2Canvas({
|
|
652
|
+
srcComponentId: sCanvasId,
|
|
653
|
+
dstComponentId: this.componentId,
|
|
654
|
+
tw: sCanvasWidth,
|
|
655
|
+
th: sCanvasHeight,
|
|
656
|
+
sx: srcX,
|
|
657
|
+
sy: srcY,
|
|
658
|
+
sw: srcW,
|
|
659
|
+
sh: srcH,
|
|
660
|
+
dx: dstX,
|
|
661
|
+
dy: dstY,
|
|
662
|
+
dw: dstW,
|
|
663
|
+
dh: dstH,
|
|
664
|
+
});
|
|
665
|
+
} else {
|
|
666
|
+
CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
|
|
667
|
+
this._drawCommands += 'd' + image._id + ','
|
|
668
|
+
+ srcX + ',' + srcY + ',' + srcW + ',' + srcH + ','
|
|
669
|
+
+ dstX + ',' + dstY + ',' + dstW + ',' + dstH + ';';
|
|
670
|
+
|
|
671
|
+
// If use 'execWithDisplay' here, will cause 2nd image flick with 'forceCanvas = true' of
|
|
672
|
+
// https://github.com/flyskywhy/GCanvasRNExamples/blob/master/app/components/Pixi.js ,
|
|
673
|
+
// because in '2d' not 'webgl' PIXI, every PIXI app tick will let all image sprites be drawImage().
|
|
674
|
+
//
|
|
675
|
+
// Even if use flushJsCommands2CallNative here, will cause image not display on screen if
|
|
676
|
+
// not follow by other ctx.some().
|
|
677
|
+
// this.flushJsCommands2CallNative('sync', 'execWithoutDisplay');
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
createImageData(widthOrImagedata, height) {
|
|
682
|
+
if (arguments.length === 2) {
|
|
683
|
+
return new ImageData(widthOrImagedata, height);
|
|
684
|
+
} else {
|
|
685
|
+
return new ImageData(widthOrImagedata.width, widthOrImagedata.height);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// not a Web canvas API, used by @flyskywhy/react-native-gcanvas
|
|
690
|
+
flushJsCommands2CallNative(
|
|
691
|
+
methodType = 'async',
|
|
692
|
+
optionType = 'execWithDisplay',
|
|
693
|
+
) {
|
|
694
|
+
const commands = this._drawCommands;
|
|
695
|
+
this._drawCommands = ''; // let cmds cache be empty
|
|
696
|
+
|
|
697
|
+
if (commands !== '') {
|
|
698
|
+
CanvasRenderingContext2D.GBridge.callNative(
|
|
699
|
+
this.componentId,
|
|
700
|
+
commands,
|
|
701
|
+
false,
|
|
702
|
+
'2d',
|
|
703
|
+
methodType,
|
|
704
|
+
optionType,
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// no need ctx.getImageData(x * PixelRatio.get(), y * PixelRatio.get(), w * PixelRatio.get(), h * PixelRatio.get())
|
|
710
|
+
// e.g. ctx.getImageData(0, 0, 2, 2) will return a `w === 2, h === 2` ImageData
|
|
711
|
+
// since PixelsSampler() in GetImageData() of core/src/gcanvas/GCanvas2dContext.cpp
|
|
712
|
+
// will scale the image automatically
|
|
713
|
+
getImageData(sx, sy, sw, sh) {
|
|
714
|
+
// if not stop _renderLoop(), sometimes will cause display issue with 'lightener' tool of https://github.com/flyskywhy/PixelShapeRN
|
|
715
|
+
cancelAnimationFrame(this._canvas._renderLoopId);
|
|
716
|
+
// If use 'async' here, will cause last commands be executed after getImageData's 'sync'.
|
|
717
|
+
if (this._drawCommands.includes(';T') || this._drawCommands.startsWith('T')) {
|
|
718
|
+
// Use 'sync' + 'execWithDisplay' here to make sure last graphics by fillText() be generated
|
|
719
|
+
// before execute getImageData's 'R', ref to context.fillText in
|
|
720
|
+
// https://github.com/flyskywhy/react-native-runescape-text/blob/main/src/classes/Motion.js
|
|
721
|
+
// TODO: more commands other than 'T' if someone issue on github
|
|
722
|
+
this.flushJsCommands2CallNative('sync', 'execWithDisplay');
|
|
723
|
+
} else {
|
|
724
|
+
// If use 'execWithDisplay' here, will cause low JS FPS on iOS with 'lightener' tool of https://github.com/flyskywhy/PixelShapeRN ,
|
|
725
|
+
// because 'lightener' will call getImageData frequently while moving finger, will cause setNeedsDisplay() then
|
|
726
|
+
// drawInRect() be invoked more than 1 times in 16ms e.g. 1times/1ms thus cause low JS FPS!
|
|
727
|
+
this.flushJsCommands2CallNative('sync', 'execWithoutDisplay');
|
|
728
|
+
}
|
|
729
|
+
// now can getImageData from last generated (even not displayed) graphics, otherwise will `return new ImageData(w, h)`
|
|
730
|
+
// thus `this.ctx.putImageData()` has no effect with the first 'Click me to draw some on canvas' in README.md
|
|
731
|
+
|
|
732
|
+
let x = sx;
|
|
733
|
+
let y = sy;
|
|
734
|
+
let w = sw;
|
|
735
|
+
let h = sh;
|
|
736
|
+
if (sx + sw > this.canvas.clientWidth) {
|
|
737
|
+
x = 0;
|
|
738
|
+
w = this.canvas.clientWidth;
|
|
739
|
+
}
|
|
740
|
+
if (sy + sh > this.canvas.clientHeight) {
|
|
741
|
+
y = 0;
|
|
742
|
+
h = this.canvas.clientHeight;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
let base64Data = CanvasRenderingContext2D.GBridge.callNative(
|
|
746
|
+
this.componentId,
|
|
747
|
+
'R' + x + ',' + y + ',' + w + ',' + h + ';',
|
|
748
|
+
false,
|
|
749
|
+
'2d',
|
|
750
|
+
'sync',
|
|
751
|
+
'execWithoutDisplay',
|
|
752
|
+
);
|
|
753
|
+
|
|
754
|
+
// start _renderLoop() again, and thus display last generated graphics on screen
|
|
755
|
+
// TODO: allow empty commands invoke callNative() once in flushJsCommands2CallNative()
|
|
756
|
+
// while start _renderLoop() just in case no commands after getImageData's 'R'
|
|
757
|
+
// thus no display
|
|
758
|
+
// TODO: this.flushJsCommands2CallNative('sync', 'execWithDisplay') on Android
|
|
759
|
+
// test with 'lightener' tool of https://github.com/flyskywhy/PixelShapeRN
|
|
760
|
+
// to see if low JS FPS
|
|
761
|
+
this._canvas._renderLoopId = requestAnimationFrame(this._canvas._renderLoop.bind(this._canvas));
|
|
762
|
+
|
|
763
|
+
if (base64Data === '') {
|
|
764
|
+
console.warn('getImageData: not good to be here, should refactor source code somewhere');
|
|
765
|
+
return new ImageData(w, h);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
return new ImageData(new Uint8ClampedArray(base64.toByteArray(base64Data)), w, h);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// no need ctx.getImageData(imageData, x * PixelRatio.get(), y * PixelRatio.get())
|
|
772
|
+
// and the imageData is also not `w * PixelRatio.get(), h * PixelRatio.get()`
|
|
773
|
+
putImageData(imageData, x, y, dirtyX, dirtyY, dirtyWidth, dirtyHeight) {
|
|
774
|
+
const base64Data = base64.fromByteArray(imageData.data);
|
|
775
|
+
let tw = imageData.width; // textureWidth
|
|
776
|
+
let th = imageData.height;
|
|
777
|
+
|
|
778
|
+
let sx, sy, sw, sh;
|
|
779
|
+
|
|
780
|
+
if (arguments.length === 3) {
|
|
781
|
+
sx = 0;
|
|
782
|
+
sy = 0;
|
|
783
|
+
sw = tw;
|
|
784
|
+
sh = th;
|
|
785
|
+
} else {
|
|
786
|
+
sx = dirtyX;
|
|
787
|
+
sy = dirtyY;
|
|
788
|
+
// dirtyWidth in https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/putImageData
|
|
789
|
+
// means the width of the (src) image data
|
|
790
|
+
sw = dirtyWidth;
|
|
791
|
+
sh = dirtyHeight;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (this._canvas._isAutoClearRectBeforePutImageData) {
|
|
795
|
+
this.clearRect(x + sx, y + sy, sw, sh);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
this._drawCommands = this._drawCommands.concat('P' + base64Data + ',' + tw + ',' + th + ','
|
|
799
|
+
+ x + ',' + y + ',' + sx + ',' + sy + ',' + sw + ',' + sh + ';');
|
|
800
|
+
}
|
|
801
|
+
}
|