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      */
261     @JsxFunction
262     public void createLinearGradient(final double x0, final double y0, final double r0, final double x1,
263             final Object y1, final Object r1) {
264         //empty
265     }
266 
267     /**
268      * Creates a pattern.
269      */
270     @JsxFunction
271     public void createPattern() {
272         //empty
273     }
274 
275     /**
276      * Creates a gradient.
277      */
278     @JsxFunction
279     public void createRadialGradient() {
280         //empty
281     }
282 
283     /**
284      * Draws images onto the canvas.
285      *
286      * @param image an element to draw into the context
287      * @param sx the X coordinate of the top left corner of the sub-rectangle of the source image
288      *        to draw into the destination context
289      * @param sy the Y coordinate of the top left corner of the sub-rectangle of the source image
290      *        to draw into the destination context
291      * @param sWidth the width of the sub-rectangle of the source image to draw into the destination context
292      * @param sHeight the height of the sub-rectangle of the source image to draw into the destination context
293      * @param dx the X coordinate in the destination canvas at which to place the top-left corner of the source image
294      * @param dy the Y coordinate in the destination canvas at which to place the top-left corner of the source image
295      * @param dWidth the width to draw the image in the destination canvas. This allows scaling of the drawn image
296      * @param dHeight the height to draw the image in the destination canvas. This allows scaling of the drawn image
297      */
298     @JsxFunction
299     @SuppressWarnings("unused")
300     public void drawImage(final Object image, final int sx, final int sy, final Object sWidth, final Object sHeight,
301             final Object dx, final Object dy, final Object dWidth, final Object dHeight) {
302         final Integer dxI;
303         final Integer dyI;
304         Integer dWidthI = null;
305         Integer dHeightI = null;
306         Integer sWidthI = null;
307         Integer sHeightI = null;
308         if (dx != Undefined.instance) {
309             dxI = ((Number) dx).intValue();
310             dyI = ((Number) dy).intValue();
311             dWidthI = ((Number) dWidth).intValue();
312             dHeightI = ((Number) dHeight).intValue();
313         }
314         else {
315             dxI = sx;
316             dyI = sy;
317         }
318         if (sWidth != Undefined.instance) {
319             sWidthI = ((Number) sWidth).intValue();
320             sHeightI = ((Number) sHeight).intValue();
321         }
322 
323         try {
324             if (image instanceof HTMLImageElement) {
325                 final ImageReader imageReader =
326                         ((HtmlImage) ((HTMLImageElement) image).getDomNodeOrDie()).getImageReader();
327                 getRenderingBackend().drawImage(imageReader, dxI, dyI);
328             }
329         }
330         catch (final IOException ioe) {
331             if (getBrowserVersion().hasFeature(JS_CANVAS_DRAW_THROWS_FOR_MISSING_IMG)) {
332                 throw Context.throwAsScriptRuntimeEx(ioe);
333             }
334         }
335     }
336 
337     /**
338      * Returns the Data URL.
339      *
340      * @param type an optional type
341      * @return the dataURL
342      */
343     public String toDataURL(String type) {
344         try {
345             if (type == null) {
346                 type = "image/png";
347             }
348             return "data:" + type + ";base64," + getRenderingBackend().encodeToString(type);
349         }
350         catch (final IOException ioe) {
351             throw Context.throwAsScriptRuntimeEx(ioe);
352         }
353     }
354 
355     /**
356      * Paints the specified ellipse.
357      * @param x the x
358      * @param y the y
359      * @param radiusX the radiusX
360      * @param radiusY the radiusY
361      * @param rotation the rotation
362      * @param startAngle the startAngle
363      * @param endAngle the endAngle
364      * @param anticlockwise the anticlockwise
365      */
366     @JsxFunction({CHROME, FF52})
367     public void ellipse(final double x, final double y,
368                     final double radiusX, final double radiusY,
369                     final double rotation, final double startAngle, final double endAngle,
370                     final boolean anticlockwise) {
371         //empty
372     }
373 
374     /**
375      * Fills the shape.
376      */
377     @JsxFunction
378     public void fill() {
379         //empty
380     }
381 
382     /**
383      * Paints the specified rectangular area.
384      * @param x the x
385      * @param y the y
386      * @param w the width
387      * @param h the height
388      */
389     @JsxFunction
390     public void fillRect(final int x, final int y, final int w, final int h) {
391         getRenderingBackend().fillRect(x, y, w, h);
392     }
393 
394     /**
395      * Dummy placeholder.
396      */
397     @JsxFunction
398     public void fillText() {
399         //empty
400     }
401 
402     /**
403      * Returns the {@code ImageData} object.
404      * @param sx x
405      * @param sy y
406      * @param sw width
407      * @param sh height
408      * @return the {@code ImageData} object
409      */
410     @JsxFunction
411     public ImageData getImageData(final int sx, final int sy, final int sw, final int sh) {
412         final ImageData imageData = new ImageData(getRenderingBackend(), sx, sy, sw, sh);
413         imageData.setParentScope(getParentScope());
414         imageData.setPrototype(getPrototype(imageData.getClass()));
415         return imageData;
416     }
417 
418     /**
419      * Dummy placeholder.
420      */
421     @JsxFunction
422     public void getLineDash() {
423         //empty
424     }
425 
426     /**
427      * Dummy placeholder.
428      */
429     @JsxFunction
430     public void getLineData() {
431         //empty
432     }
433 
434     /**
435      * Dummy placeholder.
436      */
437     @JsxFunction
438     public void isPointInPath() {
439         //empty
440     }
441 
442     /**
443      * Connect the last point to the given point.
444      * @param x the x
445      * @param y the y
446      */
447     @JsxFunction
448     public void lineTo(final double x, final double y) {
449         //empty
450     }
451 
452     /**
453      * Calculate TextMetrics for the given text.
454      * @param text the text to measure
455      * @return the text metrics
456      */
457     @JsxFunction
458     public TextMetrics measureText(final Object text) {
459         if (text == null || Undefined.instance == text) {
460             throw Context.throwAsScriptRuntimeEx(
461                     new RuntimeException("Missing argument for CanvasRenderingContext2D.measureText()."));
462         }
463 
464         final String textValue = Context.toString(text);
465 
466         // TODO take font into account
467         final int width = textValue.length() * PIXELS_PER_CHAR;
468 
469         final TextMetrics metrics = new TextMetrics(width);
470         metrics.setParentScope(getParentScope());
471         metrics.setPrototype(getPrototype(metrics.getClass()));
472         return metrics;
473     }
474 
475     /**
476      * Creates a new subpath.
477      * @param x the x
478      * @param y the y
479      */
480     @JsxFunction
481     public void moveTo(final double x, final double y) {
482         //empty
483     }
484 
485     /**
486      * Dummy placeholder.
487      */
488     @JsxFunction
489     public void putImageData() {
490         //empty
491     }
492 
493     /**
494      * Draws a quadratic Bézier curve.
495      * @param controlPointX the x-coordinate of the control point
496      * @param controlPointY the y-coordinate of the control point
497      * @param endPointX the x-coordinate of the end point
498      * @param endPointY the y-coordinate of the end point
499      */
500     @JsxFunction
501     public void quadraticCurveTo(final double controlPointX, final double controlPointY,
502             final double endPointX, final double endPointY) {
503         //empty
504     }
505 
506     /**
507      * Renders a rectangle.
508      * @param x the x
509      * @param y the y
510      * @param w the width
511      * @param h the height
512      */
513     @JsxFunction
514     public void rect(final double x, final double y, final double w, final double h) {
515         //empty
516     }
517 
518     /**
519      * Pops state stack and restore state.
520      */
521     @JsxFunction
522     public void restore() {
523         //empty
524     }
525 
526     /**
527      * Dummy placeholder.
528      */
529     @JsxFunction
530     public void rotate() {
531         //empty
532     }
533 
534     /**
535      * Pushes state on state stack.
536      */
537     @JsxFunction
538     public void save() {
539         //empty
540     }
541 
542     /**
543      * Changes the transformation matrix to apply a scaling transformation with the given characteristics.
544      * @param x the scale factor in the horizontal direction
545      * @param y the scale factor in the vertical direction
546      */
547     @JsxFunction
548     public void scale(final Object x, final Object y) {
549       //empty
550     }
551 
552     /**
553      * Dummy placeholder.
554      */
555     @JsxFunction
556     public void setLineDash() {
557         //empty
558     }
559 
560     /**
561      * Dummy placeholder.
562      */
563     @JsxFunction
564     public void setTransform() {
565         //empty
566     }
567 
568     /**
569      * Calculates the strokes of all the subpaths of the current path.
570      */
571     @JsxFunction
572     public void stroke() {
573         //empty
574     }
575 
576     /**
577      * Strokes the specified rectangular area.
578      * @param x the x
579      * @param y the y
580      * @param w the width
581      * @param h the height
582      */
583     @JsxFunction
584     public void strokeRect(final int x, final int y, final int w, final int h) {
585         getRenderingBackend().strokeRect(x, y, w, h);
586     }
587 
588     /**
589      * Dummy placeholder.
590      */
591     @JsxFunction
592     public void strokeText() {
593         //empty
594     }
595 
596     /**
597      * Dummy placeholder.
598      */
599     @JsxFunction
600     public void transform() {
601         //empty
602     }
603 
604     /**
605      * Changes the transformation matrix to apply a translation transformation with the given characteristics.
606      * @param x the translation distance in the horizontal direction
607      * @param y the translation distance in the vertical direction
608      */
609     @JsxFunction
610     public void translate(final Object x, final Object y) {
611       // empty
612     }
613 
614     /**
615      * Returns the associated {@link HTMLCanvasElement}.
616      * @return the associated {@link HTMLCanvasElement}
617      */
618     @JsxGetter
619     public HTMLCanvasElement getCanvas() {
620         return canvas_;
621     }
622 }