1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.javascript.host.canvas;
16
17 import java.io.IOException;
18
19 import org.apache.commons.lang3.StringUtils;
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22 import org.htmlunit.corejs.javascript.Context;
23 import org.htmlunit.corejs.javascript.Function;
24 import org.htmlunit.corejs.javascript.Scriptable;
25 import org.htmlunit.html.HtmlImage;
26 import org.htmlunit.javascript.HtmlUnitScriptable;
27 import org.htmlunit.javascript.JavaScriptEngine;
28 import org.htmlunit.javascript.configuration.JsxClass;
29 import org.htmlunit.javascript.configuration.JsxConstructor;
30 import org.htmlunit.javascript.configuration.JsxFunction;
31 import org.htmlunit.javascript.configuration.JsxGetter;
32 import org.htmlunit.javascript.configuration.JsxSetter;
33 import org.htmlunit.javascript.host.html.HTMLCanvasElement;
34 import org.htmlunit.javascript.host.html.HTMLImageElement;
35 import org.htmlunit.platform.Platform;
36 import org.htmlunit.platform.canvas.rendering.RenderingBackend;
37 import org.htmlunit.platform.canvas.rendering.RenderingBackend.WindingRule;
38 import org.htmlunit.protocol.data.DataURLConnection;
39 import org.htmlunit.util.MimeType;
40
41
42
43
44
45
46
47
48
49 @JsxClass
50 public class CanvasRenderingContext2D extends HtmlUnitScriptable {
51
52 private static final Log LOG = LogFactory.getLog(CanvasRenderingContext2D.class);
53
54 private HTMLCanvasElement canvas_;
55 private RenderingBackend renderingBackend_;
56
57
58
59
60 public CanvasRenderingContext2D() {
61 super();
62 }
63
64
65
66
67 @JsxConstructor
68 public void jsConstructor() {
69
70 }
71
72
73
74
75
76 public CanvasRenderingContext2D(final HTMLCanvasElement canvas) {
77 super();
78 canvas_ = canvas;
79 renderingBackend_ = null;
80 }
81
82 private RenderingBackend getRenderingBackend() {
83 if (renderingBackend_ == null) {
84 final int imageWidth = Math.max(1, canvas_.getWidth());
85 final int imageHeight = Math.max(1, canvas_.getHeight());
86
87 renderingBackend_ = Platform.getRenderingBackend(imageWidth, imageHeight);
88 }
89 return renderingBackend_;
90 }
91
92
93
94
95
96
97 @JsxGetter
98 public double getGlobalAlpha() {
99 return getRenderingBackend().getGlobalAlpha();
100 }
101
102
103
104
105
106 @JsxSetter
107 public void setGlobalAlpha(final double globalAlpha) {
108 getRenderingBackend().setGlobalAlpha(globalAlpha);
109 }
110
111
112
113
114
115 @JsxGetter
116 public HtmlUnitScriptable getFillStyle() {
117 LOG.info("CanvasRenderingContext2D.getFillStyle() not yet implemented");
118 return null;
119 }
120
121
122
123
124
125 @JsxSetter
126 public void setFillStyle(final String fillStyle) {
127 getRenderingBackend().setFillStyle(fillStyle);
128 }
129
130
131
132
133
134 @JsxGetter
135 public HtmlUnitScriptable getStrokeStyle() {
136 LOG.info("CanvasRenderingContext2D.getStrokeStyle() not yet implemented");
137 return null;
138 }
139
140
141
142
143
144 @JsxSetter
145 public void setStrokeStyle(final String strokeStyle) {
146 getRenderingBackend().setStrokeStyle(strokeStyle);
147 }
148
149
150
151
152
153 @JsxGetter
154 public double getLineWidth() {
155 return getRenderingBackend().getLineWidth();
156 }
157
158
159
160
161
162 @JsxSetter
163 public void setLineWidth(final Object lineWidth) {
164 if (!JavaScriptEngine.isUndefined(lineWidth)) {
165 final double width = JavaScriptEngine.toNumber(lineWidth);
166 if (!Double.isNaN(width)) {
167 getRenderingBackend().setLineWidth((int) width);
168 }
169 }
170 }
171
172
173
174
175
176
177
178
179
180
181 @JsxFunction
182 public void arc(final double x, final double y, final double radius, final double startAngle,
183 final double endAngle, final boolean anticlockwise) {
184 getRenderingBackend().arc(x, y, radius, startAngle, endAngle, anticlockwise);
185 }
186
187
188
189
190
191
192
193
194
195 @JsxFunction
196 public void arcTo(final double x1, final double y1, final double x2, final double y2,
197 final double radius) {
198 LOG.info("CanvasRenderingContext2D.arcTo() not yet implemented");
199 }
200
201
202
203
204 @JsxFunction
205 public void beginPath() {
206 getRenderingBackend().beginPath();
207 }
208
209
210
211
212
213
214
215
216
217
218 @JsxFunction
219 public void bezierCurveTo(final double cp1x, final double cp1y, final double cp2x, final double cp2y,
220 final double x, final double y) {
221 getRenderingBackend().bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
222 }
223
224
225
226
227
228
229
230
231 @JsxFunction
232 public void clearRect(final double x, final double y, final double w, final double h) {
233 getRenderingBackend().clearRect(x, y, w, h);
234 }
235
236
237
238
239
240
241
242
243
244 @JsxFunction
245 public static void clip(final Context context, final Scriptable scope,
246 final Scriptable thisObj, final Object[] args, final Function function) {
247 if (!(thisObj instanceof CanvasRenderingContext2D)) {
248 throw JavaScriptEngine.reportRuntimeError(
249 "CanvasRenderingContext2D.getImageData() failed - this is not a CanvasRenderingContext2D");
250 }
251 final CanvasRenderingContext2D canvas = (CanvasRenderingContext2D) thisObj;
252
253 RenderingBackend.WindingRule windingRule = WindingRule.NON_ZERO;
254 if (args.length == 1) {
255 final String windingRuleParam = JavaScriptEngine.toString(args[0]);
256 if ("evenodd".contentEquals(windingRuleParam)) {
257 windingRule = WindingRule.EVEN_ODD;
258 }
259 canvas.getRenderingBackend().clip(windingRule, null);
260 }
261
262 if (args.length > 1) {
263 if (!(args[0] instanceof Path2D)) {
264 throw JavaScriptEngine.reportRuntimeError(
265 "CanvasRenderingContext2D.clip() failed - the first parameter has to be a Path2D");
266 }
267
268 final String windingRuleParam = JavaScriptEngine.toString(args[1]);
269 if ("evenodd".contentEquals(windingRuleParam)) {
270 windingRule = WindingRule.EVEN_ODD;
271 }
272
273 LOG.info("CanvasRenderingContext2D.clip(path, fillRule) not yet implemented");
274
275 }
276
277 canvas.getRenderingBackend().clip(WindingRule.NON_ZERO, null);
278 }
279
280
281
282
283 @JsxFunction
284 public void closePath() {
285 getRenderingBackend().closePath();
286 }
287
288
289
290
291
292
293
294
295
296
297
298 @JsxFunction
299 public static ImageData createImageData(final Context context, final Scriptable scope,
300 final Scriptable thisObj, final Object[] args, final Function function) {
301 if (!(thisObj instanceof CanvasRenderingContext2D)) {
302 throw JavaScriptEngine.reportRuntimeError(
303 "CanvasRenderingContext2D.getImageData() failed - this is not a CanvasRenderingContext2D");
304 }
305 final CanvasRenderingContext2D canvas = (CanvasRenderingContext2D) thisObj;
306
307 if (args.length > 0 && args[0] instanceof ImageData) {
308 final ImageData imageDataParameter = (ImageData) args[0];
309 final ImageData imageData = new ImageData(null,
310 0, 0, imageDataParameter.getWidth(), imageDataParameter.getHeight());
311 imageData.setParentScope(canvas.getParentScope());
312 imageData.setPrototype(canvas.getPrototype(imageData.getClass()));
313 return imageData;
314 }
315
316 if (args.length > 1) {
317 final int width = Math.abs((int) JavaScriptEngine.toInteger(args, 0));
318 final int height = Math.abs((int) JavaScriptEngine.toInteger(args, 1));
319 final ImageData imageData = new ImageData(null, 0, 0, width, height);
320 imageData.setParentScope(canvas.getParentScope());
321 imageData.setPrototype(canvas.getPrototype(imageData.getClass()));
322 return imageData;
323 }
324
325 throw JavaScriptEngine.reportRuntimeError(
326 "CanvasRenderingContext2D.getImageData() failed - "
327 + "wrong parameters given (" + StringUtils.join(args, ", ") + ")");
328 }
329
330
331
332
333
334
335
336
337
338
339
340 @JsxFunction
341 public CanvasGradient createLinearGradient(final double x0, final double y0, final double r0, final double x1,
342 final Object y1, final Object r1) {
343 final CanvasGradient canvasGradient = new CanvasGradient();
344 canvasGradient.setParentScope(getParentScope());
345 canvasGradient.setPrototype(getPrototype(canvasGradient.getClass()));
346 return canvasGradient;
347 }
348
349
350
351
352 @JsxFunction
353 public void createPattern() {
354 LOG.info("CanvasRenderingContext2D.createPattern() not yet implemented");
355 }
356
357
358
359
360
361
362
363
364
365
366
367 @JsxFunction
368 public CanvasGradient createRadialGradient(final double x0, final double y0,
369 final double r0, final double x1, final double y1, final double r1) {
370 final CanvasGradient canvasGradient = new CanvasGradient();
371 canvasGradient.setParentScope(getParentScope());
372 canvasGradient.setPrototype(getPrototype(canvasGradient.getClass()));
373 return canvasGradient;
374 }
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391 @JsxFunction
392 @SuppressWarnings("unused")
393 public void drawImage(final Object image, final int sx, final int sy, final Object sWidth, final Object sHeight,
394 final Object dx, final Object dy, final Object dWidth, final Object dHeight) {
395
396 if (image instanceof HTMLImageElement) {
397 final HTMLImageElement imageElem = (HTMLImageElement) image;
398 try {
399 final org.htmlunit.platform.image.ImageData imageData
400 = ((HtmlImage) imageElem.getDomNodeOrDie()).getImageData();
401
402
403
404 if (JavaScriptEngine.isUndefined(sWidth)) {
405 getRenderingBackend().drawImage(imageData, 0, 0, null, null, sx, sy, null, null);
406 }
407
408
409
410 else if (JavaScriptEngine.isUndefined(dx)) {
411 final int dWidthI = JavaScriptEngine.toInt32(sWidth);
412 final int dHeightI = JavaScriptEngine.toInt32(sHeight);
413
414 getRenderingBackend().drawImage(imageData, 0, 0, null, null, sx, sy, dWidthI, dHeightI);
415 }
416
417
418
419 else {
420 final int sWidthI = JavaScriptEngine.toInt32(sWidth);
421 final int sHeightI = JavaScriptEngine.toInt32(sHeight);
422
423 final int dxI = JavaScriptEngine.toInt32(dx);
424 final int dyI = JavaScriptEngine.toInt32(dy);
425 final int dWidthI = JavaScriptEngine.toInt32(dWidth);
426 final int dHeightI = JavaScriptEngine.toInt32(dHeight);
427
428 getRenderingBackend().drawImage(imageData,
429 sx, sy, sWidthI, sHeightI, dxI, dyI, dWidthI, dHeightI);
430 }
431 }
432 catch (final IOException ex) {
433 LOG.info("There is no ImageReader available for you imgage with src '" + imageElem.getSrc() + "'"
434 + "Please have a look at https://www.htmlunit.org/images-howto.html "
435 + "for a possible solution.");
436 }
437 }
438 }
439
440
441
442
443
444
445
446 public String toDataURL(String type) {
447 try {
448 if (type == null) {
449 type = MimeType.IMAGE_PNG;
450 }
451 return DataURLConnection.DATA_PREFIX + type + ";base64," + getRenderingBackend().encodeToString(type);
452 }
453 catch (final IOException ex) {
454 throw JavaScriptEngine.throwAsScriptRuntimeEx(ex);
455 }
456 }
457
458
459
460
461
462
463
464
465
466
467
468
469 @JsxFunction
470 public void ellipse(final double x, final double y,
471 final double radiusX, final double radiusY,
472 final double rotation, final double startAngle, final double endAngle,
473 final boolean anticlockwise) {
474 getRenderingBackend().ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise);
475 }
476
477
478
479
480 @JsxFunction
481 public void fill() {
482 getRenderingBackend().fill();
483 }
484
485
486
487
488
489
490
491
492 @JsxFunction
493 public void fillRect(final int x, final int y, final int w, final int h) {
494 getRenderingBackend().fillRect(x, y, w, h);
495 }
496
497
498
499
500
501
502
503 @JsxFunction
504 public void fillText(final String text, final double x, final double y) {
505 getRenderingBackend().fillText(text, x, y);
506 }
507
508
509
510
511
512
513
514
515
516 @JsxFunction
517 public ImageData getImageData(final int sx, final int sy, final int sw, final int sh) {
518 final ImageData imageData = new ImageData(getRenderingBackend(), sx, sy, sw, sh);
519 imageData.setParentScope(getParentScope());
520 imageData.setPrototype(getPrototype(imageData.getClass()));
521 return imageData;
522 }
523
524
525
526
527 @JsxFunction(functionName = "getLineDash")
528 public void lineDash() {
529 LOG.info("CanvasRenderingContext2D.getLineDash() not yet implemented");
530 }
531
532
533
534
535 @JsxFunction(functionName = "getLineData")
536 public void lineData() {
537 LOG.info("CanvasRenderingContext2D.getLineData() not yet implemented");
538 }
539
540
541
542
543 @JsxFunction
544 public void isPointInPath() {
545 LOG.info("CanvasRenderingContext2D.isPointInPath() not yet implemented");
546 }
547
548
549
550
551
552
553 @JsxFunction
554 public void lineTo(final double x, final double y) {
555 getRenderingBackend().lineTo(x, y);
556 }
557
558
559
560
561
562
563 @JsxFunction
564 public TextMetrics measureText(final Object text) {
565 if (text == null || JavaScriptEngine.isUndefined(text)) {
566 throw JavaScriptEngine.typeError("Missing argument for CanvasRenderingContext2D.measureText().");
567 }
568
569 final String textValue = JavaScriptEngine.toString(text);
570
571
572 final int width = textValue.length() * getBrowserVersion().getPixesPerChar();
573
574 final TextMetrics metrics = new TextMetrics(width);
575 metrics.setParentScope(getParentScope());
576 metrics.setPrototype(getPrototype(metrics.getClass()));
577 return metrics;
578 }
579
580
581
582
583
584
585 @JsxFunction
586 public void moveTo(final double x, final double y) {
587 getRenderingBackend().moveTo(x, y);
588 }
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604 @JsxFunction
605 public void putImageData(final ImageData imageData,
606 final int dx, final int dy, final Object dirtyX, final Object dirtyY,
607 final Object dirtyWidth, final Object dirtyHeight) {
608 int dirtyXArg = 0;
609 int dirtyYArg = 0;
610 int dirtyWidthArg = imageData.getWidth();
611 int dirtyHeightArg = imageData.getHeight();
612
613 if (!JavaScriptEngine.isUndefined(dirtyX)) {
614 dirtyXArg = (int) JavaScriptEngine.toInteger(dirtyX);
615
616 if (JavaScriptEngine.isUndefined(dirtyY)
617 || JavaScriptEngine.isUndefined(dirtyWidth)
618 || JavaScriptEngine.isUndefined(dirtyHeight)) {
619 throw JavaScriptEngine.reportRuntimeError(
620 "CanvasRenderingContext2D.putImageData() failed - seven parameters expected");
621 }
622 dirtyYArg = (int) JavaScriptEngine.toInteger(dirtyY);
623 dirtyWidthArg = (int) JavaScriptEngine.toInteger(dirtyWidth);
624 dirtyHeightArg = (int) JavaScriptEngine.toInteger(dirtyHeight);
625 }
626
627 getRenderingBackend().putImageData(
628 imageData.getData().getBuffer().getBuffer(), imageData.getHeight(), imageData.getWidth(),
629 dx, dy, dirtyXArg, dirtyYArg, dirtyWidthArg, dirtyHeightArg);
630 }
631
632
633
634
635
636
637
638
639 @JsxFunction
640 public void quadraticCurveTo(final double controlPointX, final double controlPointY,
641 final double endPointX, final double endPointY) {
642 getRenderingBackend().quadraticCurveTo(controlPointX, controlPointY, endPointX, endPointY);
643 }
644
645
646
647
648
649
650
651
652 @JsxFunction
653 public void rect(final double x, final double y, final double w, final double h) {
654 getRenderingBackend().rect(x, y, w, h);
655 }
656
657
658
659
660 @JsxFunction
661 public void restore() {
662 getRenderingBackend().restore();
663 }
664
665
666
667
668
669 @JsxFunction
670 public void rotate(final double angle) {
671 getRenderingBackend().rotate(angle);
672 }
673
674
675
676
677 @JsxFunction
678 public void save() {
679 getRenderingBackend().save();
680 }
681
682
683
684
685
686
687 @JsxFunction
688 public void scale(final Object x, final Object y) {
689 LOG.info("CanvasRenderingContext2D.scale() not yet implemented");
690 }
691
692
693
694
695 @JsxFunction
696 public void setLineDash() {
697 LOG.info("CanvasRenderingContext2D.setLineDash() not yet implemented");
698 }
699
700
701
702
703
704
705
706
707
708
709
710
711 @JsxFunction
712 public void setTransform(final double m11, final double m12,
713 final double m21, final double m22, final double dx, final double dy) {
714 getRenderingBackend().setTransform(m11, m12, m21, m22, dx, dy);
715 }
716
717
718
719
720 @JsxFunction
721 public void stroke() {
722 getRenderingBackend().stroke();
723 }
724
725
726
727
728
729
730
731
732 @JsxFunction
733 public void strokeRect(final int x, final int y, final int w, final int h) {
734 getRenderingBackend().strokeRect(x, y, w, h);
735 }
736
737
738
739
740 @JsxFunction
741 public void strokeText() {
742 LOG.info("CanvasRenderingContext2D.strokeText() not yet implemented");
743 }
744
745
746
747
748
749
750
751
752
753
754
755
756 @JsxFunction
757 public void transform(final double m11, final double m12,
758 final double m21, final double m22, final double dx, final double dy) {
759 getRenderingBackend().transform(m11, m12, m21, m22, dx, dy);
760 }
761
762
763
764
765
766
767 @JsxFunction
768 public void translate(final int x, final int y) {
769 getRenderingBackend().translate(x, y);
770 }
771
772
773
774
775
776 @JsxGetter
777 public HTMLCanvasElement getCanvas() {
778 return canvas_;
779 }
780 }