RenderingContext.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. import FillStylePattern from './FillStylePattern';
  2. import FillStyleLinearGradient from './FillStyleLinearGradient';
  3. import FillStyleRadialGradient from './FillStyleRadialGradient';
  4. import GImage from '../env/image.js';
  5. import {
  6. ArrayBufferToBase64,
  7. Base64ToUint8ClampedArray
  8. } from '../env/tool.js';
  9. export default class CanvasRenderingContext2D {
  10. _drawCommands = '';
  11. _globalAlpha = 1.0;
  12. _fillStyle = 'rgb(0,0,0)';
  13. _strokeStyle = 'rgb(0,0,0)';
  14. _lineWidth = 1;
  15. _lineCap = 'butt';
  16. _lineJoin = 'miter';
  17. _miterLimit = 10;
  18. _globalCompositeOperation = 'source-over';
  19. _textAlign = 'start';
  20. _textBaseline = 'alphabetic';
  21. _font = '10px sans-serif';
  22. _savedGlobalAlpha = [];
  23. timer = null;
  24. componentId = null;
  25. _notCommitDrawImageCache = [];
  26. _needRedrawImageCache = [];
  27. _redrawCommands = '';
  28. _autoSaveContext = true;
  29. // _imageMap = new GHashMap();
  30. // _textureMap = new GHashMap();
  31. constructor() {
  32. this.className = 'CanvasRenderingContext2D';
  33. //this.save()
  34. }
  35. setFillStyle(value) {
  36. this.fillStyle = value;
  37. }
  38. set fillStyle(value) {
  39. this._fillStyle = value;
  40. if (typeof(value) == 'string') {
  41. this._drawCommands = this._drawCommands.concat("F" + value + ";");
  42. } else if (value instanceof FillStylePattern) {
  43. const image = value._img;
  44. if (!image.complete) {
  45. image.onload = () => {
  46. var index = this._needRedrawImageCache.indexOf(image);
  47. if (index > -1) {
  48. this._needRedrawImageCache.splice(index, 1);
  49. CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
  50. this._redrawflush(true);
  51. }
  52. }
  53. this._notCommitDrawImageCache.push(image);
  54. } else {
  55. CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
  56. }
  57. //CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
  58. this._drawCommands = this._drawCommands.concat("G" + image._id + "," + value._style + ";");
  59. } else if (value instanceof FillStyleLinearGradient) {
  60. var command = "D" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," +
  61. value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," +
  62. value._stop_count;
  63. for (var i = 0; i < value._stop_count; ++i) {
  64. command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
  65. }
  66. this._drawCommands = this._drawCommands.concat(command + ";");
  67. } else if (value instanceof FillStyleRadialGradient) {
  68. var command = "H" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + value._start_pos._r
  69. .toFixed(2) + "," +
  70. value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," + value._end_pos._r.toFixed(2) + "," +
  71. value._stop_count;
  72. for (var i = 0; i < value._stop_count; ++i) {
  73. command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
  74. }
  75. this._drawCommands = this._drawCommands.concat(command + ";");
  76. }
  77. }
  78. get fillStyle() {
  79. return this._fillStyle;
  80. }
  81. get globalAlpha() {
  82. return this._globalAlpha;
  83. }
  84. setGlobalAlpha(value) {
  85. this.globalAlpha = value;
  86. }
  87. set globalAlpha(value) {
  88. this._globalAlpha = value;
  89. this._drawCommands = this._drawCommands.concat("a" + value.toFixed(2) + ";");
  90. }
  91. get strokeStyle() {
  92. return this._strokeStyle;
  93. }
  94. setStrokeStyle(value) {
  95. this.strokeStyle = value;
  96. }
  97. set strokeStyle(value) {
  98. this._strokeStyle = value;
  99. if (typeof(value) == 'string') {
  100. this._drawCommands = this._drawCommands.concat("S" + value + ";");
  101. } else if (value instanceof FillStylePattern) {
  102. CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
  103. this._drawCommands = this._drawCommands.concat("G" + image._id + "," + value._style + ";");
  104. } else if (value instanceof FillStyleLinearGradient) {
  105. var command = "D" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," +
  106. value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," +
  107. value._stop_count;
  108. for (var i = 0; i < value._stop_count; ++i) {
  109. command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
  110. }
  111. this._drawCommands = this._drawCommands.concat(command + ";");
  112. } else if (value instanceof FillStyleRadialGradient) {
  113. var command = "H" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + value._start_pos._r
  114. .toFixed(2) + "," +
  115. value._end_pos._x.toFixed(2) + "," + value._end_pos._y + ",".toFixed(2) + value._end_pos._r.toFixed(2) + "," +
  116. value._stop_count;
  117. for (var i = 0; i < value._stop_count; ++i) {
  118. command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
  119. }
  120. this._drawCommands = this._drawCommands.concat(command + ";");
  121. }
  122. }
  123. get lineWidth() {
  124. return this._lineWidth;
  125. }
  126. setLineWidth(value) {
  127. this.lineWidth = value;
  128. }
  129. set lineWidth(value) {
  130. this._lineWidth = value;
  131. this._drawCommands = this._drawCommands.concat("W" + value + ";");
  132. }
  133. get lineCap() {
  134. return this._lineCap;
  135. }
  136. setLineCap(value) {
  137. this.lineCap = value;
  138. }
  139. set lineCap(value) {
  140. this._lineCap = value;
  141. this._drawCommands = this._drawCommands.concat("C" + value + ";");
  142. }
  143. get lineJoin() {
  144. return this._lineJoin;
  145. }
  146. setLineJoin(value) {
  147. this.lineJoin = value
  148. }
  149. set lineJoin(value) {
  150. this._lineJoin = value;
  151. this._drawCommands = this._drawCommands.concat("J" + value + ";");
  152. }
  153. get miterLimit() {
  154. return this._miterLimit;
  155. }
  156. setMiterLimit(value) {
  157. this.miterLimit = value
  158. }
  159. set miterLimit(value) {
  160. this._miterLimit = value;
  161. this._drawCommands = this._drawCommands.concat("M" + value + ";");
  162. }
  163. get globalCompositeOperation() {
  164. return this._globalCompositeOperation;
  165. }
  166. set globalCompositeOperation(value) {
  167. this._globalCompositeOperation = value;
  168. let mode = 0;
  169. switch (value) {
  170. case "source-over":
  171. mode = 0;
  172. break;
  173. case "source-atop":
  174. mode = 5;
  175. break;
  176. case "source-in":
  177. mode = 0;
  178. break;
  179. case "source-out":
  180. mode = 2;
  181. break;
  182. case "destination-over":
  183. mode = 4;
  184. break;
  185. case "destination-atop":
  186. mode = 4;
  187. break;
  188. case "destination-in":
  189. mode = 4;
  190. break;
  191. case "destination-out":
  192. mode = 3;
  193. break;
  194. case "lighter":
  195. mode = 1;
  196. break;
  197. case "copy":
  198. mode = 2;
  199. break;
  200. case "xor":
  201. mode = 6;
  202. break;
  203. default:
  204. mode = 0;
  205. }
  206. this._drawCommands = this._drawCommands.concat("B" + mode + ";");
  207. }
  208. get textAlign() {
  209. return this._textAlign;
  210. }
  211. setTextAlign(value) {
  212. this.textAlign = value
  213. }
  214. set textAlign(value) {
  215. this._textAlign = value;
  216. let Align = 0;
  217. switch (value) {
  218. case "start":
  219. Align = 0;
  220. break;
  221. case "end":
  222. Align = 1;
  223. break;
  224. case "left":
  225. Align = 2;
  226. break;
  227. case "center":
  228. Align = 3;
  229. break;
  230. case "right":
  231. Align = 4;
  232. break;
  233. default:
  234. Align = 0;
  235. }
  236. this._drawCommands = this._drawCommands.concat("A" + Align + ";");
  237. }
  238. get textBaseline() {
  239. return this._textBaseline;
  240. }
  241. setTextBaseline(value) {
  242. this.textBaseline = value
  243. }
  244. set textBaseline(value) {
  245. this._textBaseline = value;
  246. let baseline = 0;
  247. switch (value) {
  248. case "alphabetic":
  249. baseline = 0;
  250. break;
  251. case "middle":
  252. baseline = 1;
  253. break;
  254. case "top":
  255. baseline = 2;
  256. break;
  257. case "hanging":
  258. baseline = 3;
  259. break;
  260. case "bottom":
  261. baseline = 4;
  262. break;
  263. case "ideographic":
  264. baseline = 5;
  265. break;
  266. default:
  267. baseline = 0;
  268. break;
  269. }
  270. this._drawCommands = this._drawCommands.concat("E" + baseline + ";");
  271. }
  272. get font() {
  273. return this._font;
  274. }
  275. setFontSize(size) {
  276. var str = this._font;
  277. var strs = str.trim().split(/\s+/);
  278. for (var i = 0; i < strs.length; i++) {
  279. var values = ["normal", "italic", "oblique", "normal", "small-caps", "normal", "bold",
  280. "bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900",
  281. "normal", "ultra-condensed", "extra-condensed", "condensed", "semi-condensed",
  282. "semi-expanded", "expanded", "extra-expanded", "ultra-expanded"
  283. ];
  284. if (-1 == values.indexOf(strs[i].trim())) {
  285. if (typeof size === 'string') {
  286. strs[i] = size;
  287. } else if (typeof size === 'number') {
  288. strs[i] = String(size) + 'px';
  289. }
  290. break;
  291. }
  292. }
  293. this.font = strs.join(" ");
  294. }
  295. set font(value) {
  296. this._font = value;
  297. this._drawCommands = this._drawCommands.concat("j" + value + ";");
  298. }
  299. setTransform(a, b, c, d, tx, ty) {
  300. this._drawCommands = this._drawCommands.concat("t" +
  301. (a === 1 ? "1" : a.toFixed(2)) + "," +
  302. (b === 0 ? "0" : b.toFixed(2)) + "," +
  303. (c === 0 ? "0" : c.toFixed(2)) + "," +
  304. (d === 1 ? "1" : d.toFixed(2)) + "," + tx.toFixed(2) + "," + ty.toFixed(2) + ";");
  305. }
  306. transform(a, b, c, d, tx, ty) {
  307. this._drawCommands = this._drawCommands.concat("f" +
  308. (a === 1 ? "1" : a.toFixed(2)) + "," +
  309. (b === 0 ? "0" : b.toFixed(2)) + "," +
  310. (c === 0 ? "0" : c.toFixed(2)) + "," +
  311. (d === 1 ? "1" : d.toFixed(2)) + "," + tx + "," + ty + ";");
  312. }
  313. resetTransform() {
  314. this._drawCommands = this._drawCommands.concat("m;");
  315. }
  316. scale(a, d) {
  317. this._drawCommands = this._drawCommands.concat("k" + a.toFixed(2) + "," +
  318. d.toFixed(2) + ";");
  319. }
  320. rotate(angle) {
  321. this._drawCommands = this._drawCommands
  322. .concat("r" + angle.toFixed(6) + ";");
  323. }
  324. translate(tx, ty) {
  325. this._drawCommands = this._drawCommands.concat("l" + tx.toFixed(2) + "," + ty.toFixed(2) + ";");
  326. }
  327. save() {
  328. this._savedGlobalAlpha.push(this._globalAlpha);
  329. this._drawCommands = this._drawCommands.concat("v;");
  330. }
  331. restore() {
  332. this._drawCommands = this._drawCommands.concat("e;");
  333. this._globalAlpha = this._savedGlobalAlpha.pop();
  334. }
  335. createPattern(img, pattern) {
  336. if (typeof img === 'string') {
  337. var imgObj = new GImage();
  338. imgObj.src = img;
  339. img = imgObj;
  340. }
  341. return new FillStylePattern(img, pattern);
  342. }
  343. createLinearGradient(x0, y0, x1, y1) {
  344. return new FillStyleLinearGradient(x0, y0, x1, y1);
  345. }
  346. createRadialGradient = function(x0, y0, r0, x1, y1, r1) {
  347. return new FillStyleRadialGradient(x0, y0, r0, x1, y1, r1);
  348. };
  349. createCircularGradient = function(x0, y0, r0) {
  350. return new FillStyleRadialGradient(x0, y0, 0, x0, y0, r0);
  351. };
  352. strokeRect(x, y, w, h) {
  353. this._drawCommands = this._drawCommands.concat("s" + x + "," + y + "," + w + "," + h + ";");
  354. }
  355. clearRect(x, y, w, h) {
  356. this._drawCommands = this._drawCommands.concat("c" + x + "," + y + "," + w +
  357. "," + h + ";");
  358. }
  359. clip() {
  360. this._drawCommands = this._drawCommands.concat("p;");
  361. }
  362. resetClip() {
  363. this._drawCommands = this._drawCommands.concat("q;");
  364. }
  365. closePath() {
  366. this._drawCommands = this._drawCommands.concat("o;");
  367. }
  368. moveTo(x, y) {
  369. this._drawCommands = this._drawCommands.concat("g" + x.toFixed(2) + "," + y.toFixed(2) + ";");
  370. }
  371. lineTo(x, y) {
  372. this._drawCommands = this._drawCommands.concat("i" + x.toFixed(2) + "," + y.toFixed(2) + ";");
  373. }
  374. quadraticCurveTo = function(cpx, cpy, x, y) {
  375. this._drawCommands = this._drawCommands.concat("u" + cpx + "," + cpy + "," + x + "," + y + ";");
  376. }
  377. bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y, ) {
  378. this._drawCommands = this._drawCommands.concat(
  379. "z" + cp1x.toFixed(2) + "," + cp1y.toFixed(2) + "," + cp2x.toFixed(2) + "," + cp2y.toFixed(2) + "," +
  380. x.toFixed(2) + "," + y.toFixed(2) + ";");
  381. }
  382. arcTo(x1, y1, x2, y2, radius) {
  383. this._drawCommands = this._drawCommands.concat("h" + x1 + "," + y1 + "," + x2 + "," + y2 + "," + radius + ";");
  384. }
  385. beginPath() {
  386. this._drawCommands = this._drawCommands.concat("b;");
  387. }
  388. fillRect(x, y, w, h) {
  389. this._drawCommands = this._drawCommands.concat("n" + x + "," + y + "," + w +
  390. "," + h + ";");
  391. }
  392. rect(x, y, w, h) {
  393. this._drawCommands = this._drawCommands.concat("w" + x + "," + y + "," + w + "," + h + ";");
  394. }
  395. fill() {
  396. this._drawCommands = this._drawCommands.concat("L;");
  397. }
  398. stroke(path) {
  399. this._drawCommands = this._drawCommands.concat("x;");
  400. }
  401. arc(x, y, radius, startAngle, endAngle, anticlockwise) {
  402. let ianticlockwise = 0;
  403. if (anticlockwise) {
  404. ianticlockwise = 1;
  405. }
  406. this._drawCommands = this._drawCommands.concat(
  407. "y" + x.toFixed(2) + "," + y.toFixed(2) + "," +
  408. radius.toFixed(2) + "," + startAngle + "," + endAngle + "," + ianticlockwise +
  409. ";"
  410. );
  411. }
  412. fillText(text, x, y) {
  413. let tmptext = text.replace(/!/g, "!!");
  414. tmptext = tmptext.replace(/,/g, "!,");
  415. tmptext = tmptext.replace(/;/g, "!;");
  416. this._drawCommands = this._drawCommands.concat("T" + tmptext + "," + x + "," + y + ",0.0;");
  417. }
  418. strokeText = function(text, x, y) {
  419. let tmptext = text.replace(/!/g, "!!");
  420. tmptext = tmptext.replace(/,/g, "!,");
  421. tmptext = tmptext.replace(/;/g, "!;");
  422. this._drawCommands = this._drawCommands.concat("U" + tmptext + "," + x + "," + y + ",0.0;");
  423. }
  424. measureText(text) {
  425. return CanvasRenderingContext2D.GBridge.measureText(text, this.font, this.componentId);
  426. }
  427. isPointInPath = function(x, y) {
  428. throw new Error('GCanvas not supported yet');
  429. }
  430. drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {
  431. if (typeof image === 'string') {
  432. var imgObj = new GImage();
  433. imgObj.src = image;
  434. image = imgObj;
  435. }
  436. if (image instanceof GImage) {
  437. if (!image.complete) {
  438. imgObj.onload = () => {
  439. var index = this._needRedrawImageCache.indexOf(image);
  440. if (index > -1) {
  441. this._needRedrawImageCache.splice(index, 1);
  442. CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
  443. this._redrawflush(true);
  444. }
  445. }
  446. this._notCommitDrawImageCache.push(image);
  447. } else {
  448. CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
  449. }
  450. var srcArgs = [image, sx, sy, sw, sh, dx, dy, dw, dh];
  451. var args = [];
  452. for (var arg in srcArgs) {
  453. if (typeof(srcArgs[arg]) != 'undefined') {
  454. args.push(srcArgs[arg]);
  455. }
  456. }
  457. this.__drawImage.apply(this, args);
  458. //this.__drawImage(image,sx, sy, sw, sh, dx, dy, dw, dh);
  459. }
  460. }
  461. __drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {
  462. const numArgs = arguments.length;
  463. function drawImageCommands() {
  464. if (numArgs === 3) {
  465. const x = parseFloat(sx) || 0.0;
  466. const y = parseFloat(sy) || 0.0;
  467. return ("d" + image._id + ",0,0," +
  468. image.width + "," + image.height + "," +
  469. x + "," + y + "," + image.width + "," + image.height + ";");
  470. } else if (numArgs === 5) {
  471. const x = parseFloat(sx) || 0.0;
  472. const y = parseFloat(sy) || 0.0;
  473. const width = parseInt(sw) || image.width;
  474. const height = parseInt(sh) || image.height;
  475. return ("d" + image._id + ",0,0," +
  476. image.width + "," + image.height + "," +
  477. x + "," + y + "," + width + "," + height + ";");
  478. } else if (numArgs === 9) {
  479. sx = parseFloat(sx) || 0.0;
  480. sy = parseFloat(sy) || 0.0;
  481. sw = parseInt(sw) || image.width;
  482. sh = parseInt(sh) || image.height;
  483. dx = parseFloat(dx) || 0.0;
  484. dy = parseFloat(dy) || 0.0;
  485. dw = parseInt(dw) || image.width;
  486. dh = parseInt(dh) || image.height;
  487. return ("d" + image._id + "," +
  488. sx + "," + sy + "," + sw + "," + sh + "," +
  489. dx + "," + dy + "," + dw + "," + dh + ";");
  490. }
  491. }
  492. this._drawCommands += drawImageCommands();
  493. }
  494. _flush(reserve, callback) {
  495. const commands = this._drawCommands;
  496. this._drawCommands = '';
  497. CanvasRenderingContext2D.GBridge.render2d(this.componentId, commands, callback);
  498. this._needRender = false;
  499. }
  500. _redrawflush(reserve, callback) {
  501. const commands = this._redrawCommands;
  502. CanvasRenderingContext2D.GBridge.render2d(this.componentId, commands, callback);
  503. if (this._needRedrawImageCache.length == 0) {
  504. this._redrawCommands = '';
  505. }
  506. }
  507. draw(reserve, callback) {
  508. if (!reserve) {
  509. this._globalAlpha = this._savedGlobalAlpha.pop();
  510. this._savedGlobalAlpha.push(this._globalAlpha);
  511. this._redrawCommands = this._drawCommands;
  512. this._needRedrawImageCache = this._notCommitDrawImageCache;
  513. if (this._autoSaveContext) {
  514. this._drawCommands = ("v;" + this._drawCommands);
  515. this._autoSaveContext = false;
  516. } else {
  517. this._drawCommands = ("e;X;v;" + this._drawCommands);
  518. }
  519. } else {
  520. this._needRedrawImageCache = this._needRedrawImageCache.concat(this._notCommitDrawImageCache);
  521. this._redrawCommands += this._drawCommands;
  522. if (this._autoSaveContext) {
  523. this._drawCommands = ("v;" + this._drawCommands);
  524. this._autoSaveContext = false;
  525. }
  526. }
  527. this._notCommitDrawImageCache = [];
  528. if (this._flush) {
  529. this._flush(reserve, callback);
  530. }
  531. }
  532. getImageData(x, y, w, h, callback) {
  533. CanvasRenderingContext2D.GBridge.getImageData(this.componentId, x, y, w, h, function(res) {
  534. res.data = Base64ToUint8ClampedArray(res.data);
  535. if (typeof(callback) == 'function') {
  536. callback(res);
  537. }
  538. });
  539. }
  540. putImageData(data, x, y, w, h, callback) {
  541. if (data instanceof Uint8ClampedArray) {
  542. data = ArrayBufferToBase64(data);
  543. CanvasRenderingContext2D.GBridge.putImageData(this.componentId, data, x, y, w, h, function(res) {
  544. if (typeof(callback) == 'function') {
  545. callback(res);
  546. }
  547. });
  548. }
  549. }
  550. toTempFilePath(x, y, width, height, destWidth, destHeight, fileType, quality, callback) {
  551. CanvasRenderingContext2D.GBridge.toTempFilePath(this.componentId, x, y, width, height, destWidth, destHeight,
  552. fileType, quality,
  553. function(res) {
  554. if (typeof(callback) == 'function') {
  555. callback(res);
  556. }
  557. });
  558. }
  559. }