View Javadoc
1   /*
2    * Copyright (c) 2002-2017 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package com.gargoylesoftware.htmlunit.javascript.host.canvas;
16  
17  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_CANVAS_DRAW_THROWS_FOR_MISSING_IMG;
18  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.CHROME;
19  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.EDGE;
20  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.FF;
21  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.FF52;
22  
23  import java.io.IOException;
24  
25  import javax.imageio.ImageReader;
26  
27  import com.gargoylesoftware.htmlunit.gae.GAEUtils;
28  import com.gargoylesoftware.htmlunit.html.HtmlImage;
29  import com.gargoylesoftware.htmlunit.javascript.SimpleScriptable;
30  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxClass;
31  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxConstructor;
32  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxFunction;
33  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxGetter;
34  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxSetter;
35  import com.gargoylesoftware.htmlunit.javascript.host.canvas.rendering.AwtRenderingBackend;
36  import com.gargoylesoftware.htmlunit.javascript.host.canvas.rendering.GaeRenderingBackend;
37  import com.gargoylesoftware.htmlunit.javascript.host.canvas.rendering.RenderingBackend;
38  import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLCanvasElement;
39  import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLImageElement;
40  
41  import net.sourceforge.htmlunit.corejs.javascript.Context;
42  import net.sourceforge.htmlunit.corejs.javascript.Undefined;
43  
44  /**
45   * A JavaScript object for {@code CanvasRenderingContext2D}.
46   *
47   * @author Ahmed Ashour
48   * @author Marc Guillemot
49   * @author Frank Danek
50   * @author Ronald Brill
51   */
52  @JsxClass
53  public class CanvasRenderingContext2D extends SimpleScriptable {
54      /** The number of (horizontal) pixels to assume that each character occupies. */
55      private static final int PIXELS_PER_CHAR = 10;
56  
57      private final HTMLCanvasElement canvas_;
58      private RenderingBackend renderingBackend_;
59  
60      /**
61       * Default constructor.
62       */
63      @JsxConstructor({CHROME, FF, EDGE})
64      public CanvasRenderingContext2D() {
65          canvas_ = null;
66          renderingBackend_ = null;
67      }
68  
69      /**
70       * Constructs in association with {@link HTMLCanvasElement}.
71       * @param canvas the {@link HTMLCanvasElement}
72       */
73      public CanvasRenderingContext2D(final HTMLCanvasElement canvas) {
74          canvas_ = canvas;
75          renderingBackend_ = null;
76      }
77  
78      private RenderingBackend getRenderingBackend() {
79          if (renderingBackend_ == null) {
80              final int imageWidth = Math.max(1, canvas_.getWidth());
81              final int imageHeight = Math.max(1, canvas_.getHeight());
82              if (GAEUtils.isGaeMode()) {
83                  renderingBackend_ = new GaeRenderingBackend(imageWidth, imageHeight);
84              }
85              else {
86                  renderingBackend_ = new AwtRenderingBackend(imageWidth, imageHeight);
87              }
88          }
89          return renderingBackend_;
90      }
91  
92      /**
93       * Returns the {@code fillStyle} property.
94       * @return the {@code fillStyle} property
95       */
96      @JsxGetter
97      public Object getFillStyle() {
98          return null;
99      }
100 
101     /**
102      * Sets the {@code fillStyle} property.
103      * @param fillStyle the {@code fillStyle} property
104      */
105     @JsxSetter
106     public void setFillStyle(final String fillStyle) {
107         getRenderingBackend().setFillStyle(fillStyle);
108     }
109 
110     /**
111      * Returns the {@code strokeStyle} property.
112      * @return the {@code strokeStyle} property
113      */
114     @JsxGetter
115     public Object getStrokeStyle() {
116         return null;
117     }
118 
119     /**
120      * Sets the {@code strokeStyle} property.
121      * @param strokeStyle the {@code strokeStyle} property
122      */
123     @JsxSetter
124     public void setStrokeStyle(final Object strokeStyle) {
125         //empty
126     }
127 
128     /**
129      * Returns the {@code lineWidth} property.
130      * @return the {@code lineWidth} property
131      */
132     @JsxGetter
133     public double getLineWidth() {
134         return 0;
135     }
136 
137     /**
138      * Sets the {@code lineWidth} property.
139      * @param lineWidth the {@code lineWidth} property
140      */
141     @JsxSetter
142     public void setLineWidth(final Object lineWidth) {
143         //empty
144     }
145 
146     /**
147      * Returns the {@code globalAlpha} property.
148      * @return the {@code globalAlpha} property
149      */
150     @JsxGetter
151     public double getGlobalAlpha() {
152         return 0;
153     }
154 
155     /**
156      * Sets the {@code globalAlpha} property.
157      * @param globalAlpha the {@code globalAlpha} property
158      */
159     @JsxSetter
160     public void setGlobalAlpha(final Object globalAlpha) {
161         //empty
162     }
163 
164     /**
165      * Draws an arc.
166      * @param x the x
167      * @param y the y
168      * @param radius the radius
169      * @param startAngle the start angle
170      * @param endAngle the end angle
171      * @param anticlockwise is anti-clockwise
172      */
173     @JsxFunction
174     public void arc(final double x, final double y, final double radius, final double startAngle,
175                 final double endAngle, final boolean anticlockwise) {
176         //empty
177     }
178 
179     /**
180      * Draws an arc.
181      * @param x1 the x1
182      * @param y1 the y1
183      * @param x2 the x2
184      * @param y2 the y2
185      * @param radius the radius
186      */
187     @JsxFunction
188     public void arcTo(final double x1, final double y1, final double x2, final double y2,
189                 final double radius) {
190         //empty
191     }
192 
193     /**
194      * Begins the subpaths.
195      */
196     @JsxFunction
197     public void beginPath() {
198         //empty
199     }
200 
201     /**
202      * Draws a cubic Bézier curve.
203      * @param cp1x the cp1x
204      * @param cp1y the cp1y
205      * @param cp2x the cp2x
206      * @param cp2y the cp2y
207      * @param x the x
208      * @param y the y
209      */
210     @JsxFunction
211     public void bezierCurveTo(final double cp1x, final double cp1y, final double cp2x, final double cp2y,
212             final double x, final double y) {
213         //empty
214     }
215 
216     /**
217      * Clears the specified rectangular area.
218      * @param x the x
219      * @param y the y
220      * @param w the width
221      * @param h the height
222      */
223     @JsxFunction
224     public void clearRect(final int x, final int y, final int w, final int h) {
225         getRenderingBackend().clearRect(x, y, w, h);
226     }
227 
228     /**
229      * Creates a new clipping region.
230      */
231     @JsxFunction
232     public void clip() {
233         //empty
234     }
235 
236     /**
237      * Closes the subpaths.
238      */
239     @JsxFunction
240     public void closePath() {
241         //empty
242     }
243 
244     /**
245      * Creates a new, blank ImageData object with the specified dimensions.
246      */
247     @JsxFunction
248     public void createImageData() {
249         //empty
250     }
251 
252     /**
253      * Creates linear gradient.
254      * @param x0 the x0
255      * @param y0 the y0
256      * @param r0 the r0
257      * @param x1 the x1
258      * @param y1 the y1
259      * @param r1 the r1
260      * @return the new CanvasGradient
261      */
262     @JsxFunction
263     public CanvasGradient createLinearGradient(final double x0, final double y0, final double r0, final double x1,
264             final Object y1, final Object r1) {
265         final CanvasGradient canvasGradient = new CanvasGradient();
266         canvasGradient.setParentScope(getParentScope());
267         canvasGradient.setPrototype(getPrototype(canvasGradient.getClass()));
268         return canvasGradient;
269     }
270 
271     /**
272      * Creates a pattern.
273      */
274     @JsxFunction
275     public void createPattern() {
276         //empty
277     }
278 
279     /**
280      * Creates a gradient.
281      * @param x0 the x axis of the coordinate of the start circle
282      * @param y0 the y axis of the coordinate of the start circle
283      * @param r0 the radius of the start circle
284      * @param x1 the x axis of the coordinate of the end circle
285      * @param y1 the y axis of the coordinate of the end circle
286      * @param r1 the radius of the end circle
287      * @return the new CanvasGradient
288      */
289     @JsxFunction
290     public CanvasGradient createRadialGradient(final double x0, final double y0,
291                             final double r0, final double x1, final double y1, final double r1) {
292         final CanvasGradient canvasGradient = new CanvasGradient();
293         canvasGradient.setParentScope(getParentScope());
294         canvasGradient.setPrototype(getPrototype(canvasGradient.getClass()));
295         return canvasGradient;
296     }
297 
298     /**
299      * Draws images onto the canvas.
300      *
301      * @param image an element to draw into the context
302      * @param sx the X coordinate of the top left corner of the sub-rectangle of the source image
303      *        to draw into the destination context
304      * @param sy the Y coordinate of the top left corner of the sub-rectangle of the source image
305      *        to draw into the destination context
306      * @param sWidth the width of the sub-rectangle of the source image to draw into the destination context
307      * @param sHeight the height of the sub-rectangle of the source image to draw into the destination context
308      * @param dx the X coordinate in the destination canvas at which to place the top-left corner of the source image
309      * @param dy the Y coordinate in the destination canvas at which to place the top-left corner of the source image
310      * @param dWidth the width to draw the image in the destination canvas. This allows scaling of the drawn image
311      * @param dHeight the height to draw the image in the destination canvas. This allows scaling of the drawn image
312      */
313     @JsxFunction
314     @SuppressWarnings("unused")
315     public void drawImage(final Object image, final int sx, final int sy, final Object sWidth, final Object sHeight,
316             final Object dx, final Object dy, final Object dWidth, final Object dHeight) {
317         final Integer dxI;
318         final Integer dyI;
319         Integer dWidthI = null;
320         Integer dHeightI = null;
321         Integer sWidthI = null;
322         Integer sHeightI = null;
323         if (dx != Undefined.instance) {
324             dxI = ((Number) dx).intValue();
325             dyI = ((Number) dy).intValue();
326             dWidthI = ((Number) dWidth).intValue();
327             dHeightI = ((Number) dHeight).intValue();
328         }
329         else {
330             dxI = sx;
331             dyI = sy;
332         }
333         if (sWidth != Undefined.instance) {
334             sWidthI = ((Number) sWidth).intValue();
335             sHeightI = ((Number) sHeight).intValue();
336         }
337 
338         try {
339             if (image instanceof HTMLImageElement) {
340                 final ImageReader imageReader =
341                         ((HtmlImage) ((HTMLImageElement) image).getDomNodeOrDie()).getImageReader();
342                 getRenderingBackend().drawImage(imageReader, dxI, dyI);
343             }
344         }
345         catch (final IOException ioe) {
346             if (getBrowserVersion().hasFeature(JS_CANVAS_DRAW_THROWS_FOR_MISSING_IMG)) {
347                 throw Context.throwAsScriptRuntimeEx(ioe);
348             }
349         }
350     }
351 
352     /**
353      * Returns the Data URL.
354      *
355      * @param type an optional type
356      * @return the dataURL
357      */
358     public String toDataURL(String type) {
359         try {
360             if (type == null) {
361                 type = "image/png";
362             }
363             return "data:" + type + ";base64," + getRenderingBackend().encodeToString(type);
364         }
365         catch (final IOException ioe) {
366             throw Context.throwAsScriptRuntimeEx(ioe);
367         }
368     }
369 
370     /**
371      * Paints the specified ellipse.
372      * @param x the x
373      * @param y the y
374      * @param radiusX the radiusX
375      * @param radiusY the radiusY
376      * @param rotation the rotation
377      * @param startAngle the startAngle
378      * @param endAngle the endAngle
379      * @param anticlockwise the anticlockwise
380      */
381     @JsxFunction({CHROME, FF52})
382     public void ellipse(final double x, final double y,
383                     final double radiusX, final double radiusY,
384                     final double rotation, final double startAngle, final double endAngle,
385                     final boolean anticlockwise) {
386         //empty
387     }
388 
389     /**
390      * Fills the shape.
391      */
392     @JsxFunction
393     public void fill() {
394         //empty
395     }
396 
397     /**
398      * Paints the specified rectangular area.
399      * @param x the x
400      * @param y the y
401      * @param w the width
402      * @param h the height
403      */
404     @JsxFunction
405     public void fillRect(final int x, final int y, final int w, final int h) {
406         getRenderingBackend().fillRect(x, y, w, h);
407     }
408 
409     /**
410      * Fills a given text at the given (x, y) position.
411      * @param text the text
412      * @param x the x
413      * @param y the y
414      */
415     @JsxFunction
416     public void fillText(final String text, final int x, final int y) {
417         getRenderingBackend().fillText(text, x, y);
418     }
419 
420     /**
421      * Returns the {@code ImageData} object.
422      * @param sx x
423      * @param sy y
424      * @param sw width
425      * @param sh height
426      * @return the {@code ImageData} object
427      */
428     @JsxFunction
429     public ImageData getImageData(final int sx, final int sy, final int sw, final int sh) {
430         final ImageData imageData = new ImageData(getRenderingBackend(), sx, sy, sw, sh);
431         imageData.setParentScope(getParentScope());
432         imageData.setPrototype(getPrototype(imageData.getClass()));
433         return imageData;
434     }
435 
436     /**
437      * Dummy placeholder.
438      */
439     @JsxFunction
440     public void getLineDash() {
441         //empty
442     }
443 
444     /**
445      * Dummy placeholder.
446      */
447     @JsxFunction
448     public void getLineData() {
449         //empty
450     }
451 
452     /**
453      * Dummy placeholder.
454      */
455     @JsxFunction
456     public void isPointInPath() {
457         //empty
458     }
459 
460     /**
461      * Connect the last point to the given point.
462      * @param x the x
463      * @param y the y
464      */
465     @JsxFunction
466     public void lineTo(final double x, final double y) {
467         //empty
468     }
469 
470     /**
471      * Calculate TextMetrics for the given text.
472      * @param text the text to measure
473      * @return the text metrics
474      */
475     @JsxFunction
476     public TextMetrics measureText(final Object text) {
477         if (text == null || Undefined.instance == text) {
478             throw Context.throwAsScriptRuntimeEx(
479                     new RuntimeException("Missing argument for CanvasRenderingContext2D.measureText()."));
480         }
481 
482         final String textValue = Context.toString(text);
483 
484         // TODO take font into account
485         final int width = textValue.length() * PIXELS_PER_CHAR;
486 
487         final TextMetrics metrics = new TextMetrics(width);
488         metrics.setParentScope(getParentScope());
489         metrics.setPrototype(getPrototype(metrics.getClass()));
490         return metrics;
491     }
492 
493     /**
494      * Creates a new subpath.
495      * @param x the x
496      * @param y the y
497      */
498     @JsxFunction
499     public void moveTo(final double x, final double y) {
500         //empty
501     }
502 
503     /**
504      * Dummy placeholder.
505      */
506     @JsxFunction
507     public void putImageData() {
508         //empty
509     }
510 
511     /**
512      * Draws a quadratic Bézier curve.
513      * @param controlPointX the x-coordinate of the control point
514      * @param controlPointY the y-coordinate of the control point
515      * @param endPointX the x-coordinate of the end point
516      * @param endPointY the y-coordinate of the end point
517      */
518     @JsxFunction
519     public void quadraticCurveTo(final double controlPointX, final double controlPointY,
520             final double endPointX, final double endPointY) {
521         //empty
522     }
523 
524     /**
525      * Renders a rectangle.
526      * @param x the x
527      * @param y the y
528      * @param w the width
529      * @param h the height
530      */
531     @JsxFunction
532     public void rect(final double x, final double y, final double w, final double h) {
533         //empty
534     }
535 
536     /**
537      * Pops state stack and restore state.
538      */
539     @JsxFunction
540     public void restore() {
541         //empty
542     }
543 
544     /**
545      * Dummy placeholder.
546      */
547     @JsxFunction
548     public void rotate() {
549         //empty
550     }
551 
552     /**
553      * Pushes state on state stack.
554      */
555     @JsxFunction
556     public void save() {
557         //empty
558     }
559 
560     /**
561      * Changes the transformation matrix to apply a scaling transformation with the given characteristics.
562      * @param x the scale factor in the horizontal direction
563      * @param y the scale factor in the vertical direction
564      */
565     @JsxFunction
566     public void scale(final Object x, final Object y) {
567       //empty
568     }
569 
570     /**
571      * Dummy placeholder.
572      */
573     @JsxFunction
574     public void setLineDash() {
575         //empty
576     }
577 
578     /**
579      * Dummy placeholder.
580      */
581     @JsxFunction
582     public void setTransform() {
583         //empty
584     }
585 
586     /**
587      * Calculates the strokes of all the subpaths of the current path.
588      */
589     @JsxFunction
590     public void stroke() {
591         //empty
592     }
593 
594     /**
595      * Strokes the specified rectangular area.
596      * @param x the x
597      * @param y the y
598      * @param w the width
599      * @param h the height
600      */
601     @JsxFunction
602     public void strokeRect(final int x, final int y, final int w, final int h) {
603         getRenderingBackend().strokeRect(x, y, w, h);
604     }
605 
606     /**
607      * Dummy placeholder.
608      */
609     @JsxFunction
610     public void strokeText() {
611         //empty
612     }
613 
614     /**
615      * Dummy placeholder.
616      */
617     @JsxFunction
618     public void transform() {
619         //empty
620     }
621 
622     /**
623      * Changes the transformation matrix to apply a translation transformation with the given characteristics.
624      * @param x the translation distance in the horizontal direction
625      * @param y the translation distance in the vertical direction
626      */
627     @JsxFunction
628     public void translate(final Object x, final Object y) {
629       // empty
630     }
631 
632     /**
633      * Returns the associated {@link HTMLCanvasElement}.
634      * @return the associated {@link HTMLCanvasElement}
635      */
636     @JsxGetter
637     public HTMLCanvasElement getCanvas() {
638         return canvas_;
639     }
640 }