View Javadoc
1   package nl.tudelft.simulation.dsol.web.animation;
2   
3   import java.awt.AlphaComposite;
4   import java.awt.BasicStroke;
5   import java.awt.Canvas;
6   import java.awt.Color;
7   import java.awt.Composite;
8   import java.awt.Font;
9   import java.awt.FontMetrics;
10  import java.awt.Graphics;
11  import java.awt.Graphics2D;
12  import java.awt.GraphicsConfiguration;
13  import java.awt.Image;
14  import java.awt.Paint;
15  import java.awt.Rectangle;
16  import java.awt.RenderingHints;
17  import java.awt.RenderingHints.Key;
18  import java.awt.Shape;
19  import java.awt.Stroke;
20  import java.awt.font.FontRenderContext;
21  import java.awt.font.GlyphVector;
22  import java.awt.geom.AffineTransform;
23  import java.awt.geom.Ellipse2D;
24  import java.awt.geom.Line2D;
25  import java.awt.geom.Path2D;
26  import java.awt.geom.PathIterator;
27  import java.awt.geom.Rectangle2D;
28  import java.awt.image.BufferedImage;
29  import java.awt.image.BufferedImageOp;
30  import java.awt.image.ImageObserver;
31  import java.awt.image.RenderedImage;
32  import java.awt.image.renderable.RenderableImage;
33  import java.text.AttributedCharacterIterator;
34  import java.util.LinkedHashMap;
35  import java.util.Map;
36  
37  import org.djutils.logger.CategoryLogger;
38  
39  import nl.tudelft.simulation.dsol.animation.gis.SerializablePath;
40  import nl.tudelft.simulation.dsol.logger.Cat;
41  
42  /**
43   * HTMLGraphics.java.
44   * <p>
45   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
46   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
47   * </p>
48   * @author <a href="https://github.com/averbraeck" target="_blank">Alexander Verbraeck</a>
49   */
50  public class HtmlGraphics2d extends Graphics2D
51  {
52      /** the current color of the background for drawing. */
53      Color background = Color.WHITE;
54  
55      /** the current drawing color. */
56      Color color = Color.BLACK;
57  
58      /** the current font. */
59      Font font = new Font(Font.SANS_SERIF, Font.PLAIN, 10);
60  
61      /** the drawing canvas. */
62      Canvas canvas = new Canvas();
63  
64      /** the cached current font properties. */
65      FontMetrics fontMetrics = this.canvas.getFontMetrics(this.font);
66  
67      /** the current paint. */
68      Paint paint = Color.BLACK;
69  
70      /** the current stroke. */
71      Stroke stroke = new BasicStroke();
72  
73      /** TODO: the current rendering hints. */
74      RenderingHints renderingHints = new RenderingHints(new LinkedHashMap<Key, Object>());
75  
76      /** the current affine transform. */
77      AffineTransform affineTransform = new AffineTransform();
78  
79      /** TODO: the current composite. What is that? */
80      Composite composite = AlphaComposite.Clear;
81  
82      /** the commands to send over the channel to the HTML5 code. */
83      StringBuffer commands = new StringBuffer();
84  
85      /**
86       * Clear the commands and put the start tag in.
87       */
88      public void clearCommand()
89      {
90          this.commands = new StringBuffer();
91          this.commands.append("<animate>\n");
92      }
93  
94      /**
95       * Close the commands and put the end tag in.
96       * @return the current set of commands
97       */
98      public String closeAndGetCommands()
99      {
100         this.commands.append("</animate>\n");
101         return this.commands.toString();
102     }
103 
104     /**
105      * Add a draw command.
106      * @param drawCommand the tag for the draw command
107      * @param params the params for the draw command
108      */
109     protected void addDraw(final String drawCommand, final Object... params)
110     {
111         this.commands.append("<draw>" + drawCommand);
112         for (Object param : params)
113         {
114             this.commands.append("," + param.toString());
115         }
116         this.commands.append("</draw>\n");
117     }
118 
119     /**
120      * add AffineTransform to the command.
121      */
122     protected void addAffineTransform()
123     {
124         this.commands.append(",");
125         this.commands.append(this.affineTransform.getScaleX());
126         this.commands.append(",");
127         this.commands.append(this.affineTransform.getShearY());
128         this.commands.append(",");
129         this.commands.append(this.affineTransform.getShearX());
130         this.commands.append(",");
131         this.commands.append(this.affineTransform.getScaleY());
132         this.commands.append(",");
133         this.commands.append(this.affineTransform.getTranslateX());
134         this.commands.append(",");
135         this.commands.append(this.affineTransform.getTranslateY());
136     }
137 
138     /**
139      * add Color to the command.
140      * @param c the color
141      */
142     protected void addColor(final Color c)
143     {
144         this.commands.append(",");
145         this.commands.append(c.getRed());
146         this.commands.append(",");
147         this.commands.append(c.getGreen());
148         this.commands.append(",");
149         this.commands.append(c.getBlue());
150         this.commands.append(",");
151         this.commands.append(c.getAlpha());
152         this.commands.append(",");
153         this.commands.append(c.getTransparency());
154     }
155 
156     /**
157      * add font data to the command, font-name, font-size, bold/italic/plain.
158      */
159     protected void addFontData()
160     {
161         this.commands.append(",");
162         String javaFontName = this.font.getFontName().toLowerCase();
163         String htmlFontName;
164         if (javaFontName.contains("arial") || javaFontName.contains("helvetica") || javaFontName.contains("verdana")
165                 || javaFontName.contains("tahoma") || javaFontName.contains("segoe") || javaFontName.contains("sans"))
166             htmlFontName = "sans-serif";
167         else if (javaFontName.contains("times") || javaFontName.contains("cambria") || javaFontName.contains("georgia")
168                 || javaFontName.contains("serif"))
169             htmlFontName = "serif";
170         else if (javaFontName.contains("courier") || javaFontName.contains("consol") || javaFontName.contains("mono"))
171             htmlFontName = "monospace";
172         else
173             htmlFontName = "sans-serif";
174         this.commands.append(htmlFontName);
175         this.commands.append(",");
176         this.commands.append(this.font.getSize2D());
177         this.commands.append(",");
178         if (this.font.isBold())
179             this.commands.append("bold");
180         else if (this.font.isItalic())
181             this.commands.append("italic");
182         else
183             this.commands.append("plain");
184     }
185 
186     /**
187      * Add fill command, transform.m11(h-scale), transform.m12(h-skew), transform.m21(v-skew), transform.m22(v-scale),
188      * transform.dx(h-translate), transform.dy(v-translate), color.r, color.g, color.b, color.alpha, color.transparency,
189      * params...
190      * @param fillCommand the tag to use
191      * @param params the params to send
192      */
193     protected void addTransformFill(final String fillCommand, final Object... params)
194     {
195         this.commands.append("<transformFill>" + fillCommand);
196         addAffineTransform();
197         if (this.paint instanceof Color)
198             addColor((Color) this.paint);
199         else
200             addColor(this.color);
201         for (Object param : params)
202         {
203             this.commands.append("," + param.toString());
204         }
205         this.commands.append("</transformFill>\n");
206     }
207 
208     /**
209      * Add command, transform.m11(h-scale), transform.m12(h-skew), transform.m21(v-skew), transform.m22(v-scale),
210      * transform.dx(h-translate), transform.dy(v-translate), linecolor.r, linecolor.g, linecolor.b, linecolor.alpha,
211      * linecolor.transparency, line-width, params...
212      * @param drawCommand the tag to use
213      * @param params the params
214      */
215     protected void addTransformDraw(final String drawCommand, final Object... params)
216     {
217         this.commands.append("<transformDraw>" + drawCommand);
218         addAffineTransform();
219         if (this.paint instanceof Color)
220             addColor((Color) this.paint);
221         else
222             addColor(this.color);
223         if (this.stroke instanceof BasicStroke)
224             this.commands.append("," + ((BasicStroke) this.stroke).getLineWidth());
225         else
226             this.commands.append(", 0.1");
227         for (Object param : params)
228         {
229             this.commands.append("," + param.toString());
230         }
231         this.commands.append("</transformDraw>\n");
232     }
233 
234     /**
235      * adds a float array to the command.
236      * @param array the array
237      * @param length the number of points from the array to write
238      */
239     private void addFloatArray(final float[] array, final int length)
240     {
241         for (int i = 0; i < length; i++)
242         {
243             this.commands.append(", " + array[i]);
244         }
245     }
246 
247     /**
248      * adds a double array to the command.
249      * @param array the array
250      * @param length the number of points from the array to write
251      */
252     private void addDoubleArray(final double[] array, final int length)
253     {
254         for (int i = 0; i < length; i++)
255         {
256             this.commands.append(", " + array[i]);
257         }
258     }
259 
260     /**
261      * Add a path2D to the command. In case of fill:<br>
262      * FILL, transform.m11(h-scale), transform.m12(h-skew), transform.m21(v-skew), transform.m22(v-scale),
263      * transform.dx(h-translate), transform.dy(v-translate), fillcolor.r, fillcolor.g, fillcolor.b, fillcolor.alpha,
264      * fillcolor.transparency, winding_rule[WIND_EVEN_ODD/WIND_NON_ZERO], COMMAND, coords, COMMAND, coords, ... <br>
265      * In case of draw:<br>
266      * DRAW, transform.m11(h-scale), transform.m12(h-skew), transform.m21(v-skew), transform.m22(v-scale),
267      * transform.dx(h-translate), transform.dy(v-translate), strokecolor.r, strokecolor.g, strokecolor.b, strokecolor.alpha,
268      * strokecolor.transparency, line_width, COMMAND, coords, COMMAND, coords, ... <br>
269      * where command can be one of the following:<br>
270      * - CLOSE, followed by no coordinates<br>
271      * - CUBICTO, followed by 3 coordinates (6 numbers)<br>
272      * - LINETO, followed by 1 coordinate (2 numbers)<br>
273      * - MOVETO, followed by 1 coordinate (2 numbers)<br>
274      * - QUADTO, followed by 2 coordinates (4 numbers)<br>
275      * @param path Path2D.Float; the path to draw
276      * @param fill
277      */
278     protected void addTransformPathFloat(final Path2D.Float path, final boolean fill)
279     {
280         if (fill)
281             this.commands.append("<transformPath>FILL");
282         else
283             this.commands.append("<transformPath>DRAW");
284         addAffineTransform();
285         addColor(this.color);
286         if (fill)
287         {
288             if (path.getWindingRule() == Path2D.WIND_EVEN_ODD)
289                 this.commands.append(",WIND_EVEN_ODD");
290             else
291                 this.commands.append(",WIND_NON_ZERO");
292         }
293         else
294         {
295             if (this.stroke instanceof BasicStroke)
296                 this.commands.append("," + ((BasicStroke) this.stroke).getLineWidth());
297             else
298                 this.commands.append(", 0.1");
299         }
300         float[] coords = new float[6];
301         PathIterator i = path.getPathIterator(null);
302         while (!i.isDone())
303         {
304             int segment = i.currentSegment(coords);
305             switch (segment)
306             {
307                 case PathIterator.SEG_CLOSE:
308                     this.commands.append(",CLOSE");
309                     break;
310                 case PathIterator.SEG_CUBICTO:
311                     this.commands.append(",CUBICTO");
312                     addFloatArray(coords, 6);
313                     break;
314                 case PathIterator.SEG_LINETO:
315                     this.commands.append(",LINETO");
316                     addFloatArray(coords, 2);
317                     break;
318                 case PathIterator.SEG_MOVETO:
319                     this.commands.append(",MOVETO");
320                     addFloatArray(coords, 2);
321                     break;
322                 case PathIterator.SEG_QUADTO:
323                     this.commands.append(",QUADTO");
324                     addFloatArray(coords, 4);
325                     break;
326                 default:
327                     throw new RuntimeException("unkown segment");
328             }
329             i.next();
330         }
331         this.commands.append("</transformPath>\n");
332     }
333 
334     /**
335      * Add a path2D to the command. In case of fill:<br>
336      * FILL, transform.m11(h-scale), transform.m12(h-skew), transform.m21(v-skew), transform.m22(v-scale),
337      * transform.dx(h-translate), transform.dy(v-translate), fillcolor.r, fillcolor.g, fillcolor.b, fillcolor.alpha,
338      * fillcolor.transparency, winding_rule[WIND_EVEN_ODD/WIND_NON_ZERO], COMMAND, coords, COMMAND, coords, ... <br>
339      * In case of draw:<br>
340      * DRAW, transform.m11(h-scale), transform.m12(h-skew), transform.m21(v-skew), transform.m22(v-scale),
341      * transform.dx(h-translate), transform.dy(v-translate), strokecolor.r, strokecolor.g, strokecolor.b, strokecolor.alpha,
342      * strokecolor.transparency, line_width, COMMAND, coords, COMMAND, coords, ... <br>
343      * where command can be one of the following:<br>
344      * - CLOSE, followed by no coordinates<br>
345      * - CUBICTO, followed by 3 coordinates (6 numbers)<br>
346      * - LINETO, followed by 1 coordinate (2 numbers)<br>
347      * - MOVETO, followed by 1 coordinate (2 numbers)<br>
348      * - QUADTO, followed by 2 coordinates (4 numbers)<br>
349      * @param path Path2D.Double; the path to draw
350      * @param fill
351      */
352     protected void addTransformPathDouble(final Path2D.Double path, final boolean fill)
353     {
354         if (fill)
355             this.commands.append("<transformPath>FILL");
356         else
357             this.commands.append("<transformPath>DRAW");
358         addAffineTransform();
359         addColor(this.color);
360         if (fill)
361         {
362             if (path.getWindingRule() == Path2D.WIND_EVEN_ODD)
363                 this.commands.append(",WIND_EVEN_ODD");
364             else
365                 this.commands.append(",WIND_NON_ZERO");
366         }
367         else
368         {
369             if (this.stroke instanceof BasicStroke)
370                 this.commands.append("," + ((BasicStroke) this.stroke).getLineWidth());
371             else
372                 this.commands.append(", 0.1");
373         }
374         double[] coords = new double[6];
375         PathIterator i = path.getPathIterator(null);
376         while (!i.isDone())
377         {
378             int segment = i.currentSegment(coords);
379             switch (segment)
380             {
381                 case PathIterator.SEG_CLOSE:
382                     this.commands.append(",CLOSE");
383                     break;
384                 case PathIterator.SEG_CUBICTO:
385                     this.commands.append(",CUBICTO");
386                     addDoubleArray(coords, 6);
387                     break;
388                 case PathIterator.SEG_LINETO:
389                     this.commands.append(",LINETO");
390                     addDoubleArray(coords, 2);
391                     break;
392                 case PathIterator.SEG_MOVETO:
393                     this.commands.append(",MOVETO");
394                     addDoubleArray(coords, 2);
395                     break;
396                 case PathIterator.SEG_QUADTO:
397                     this.commands.append(",QUADTO");
398                     addDoubleArray(coords, 4);
399                     break;
400                 default:
401                     throw new RuntimeException("unkown segment");
402             }
403             i.next();
404         }
405         this.commands.append("</transformPath>\n");
406     }
407 
408     /**
409      * Add string, 0=command, 1=transform.m11(h-scale), 2=transform.m12(h-skew), 3=transform.m21(v-skew),
410      * 4=transform.m22(v-scale), 5=transform.dx(h-translate), 6=transform.dy(v-translate), 7=color.r, 8=color.g, 9=color.b,
411      * 10=color.alpha, 11=color.transparency, 12=fontname, 13=fontsize, 14=fontstyle(normal/italic/bold), 15=x, 16=y, 17=text.
412      * @param drawCommand the tag to use
413      * @param params the params
414      */
415     protected void addTransformText(final String drawCommand, final Object... params)
416     {
417         this.commands.append("<transformText>" + drawCommand);
418         addAffineTransform();
419         addColor(this.color);
420         addFontData();
421         for (Object param : params)
422         {
423             this.commands.append("," + param.toString());
424         }
425         this.commands.append("</transformText>\n");
426     }
427 
428     @Override
429     public void draw(final Shape shape)
430     {
431         drawFillShape(shape, false);
432     }
433 
434     /**
435      * Draw or fill a shape.
436      * @param shape the shape
437      * @param fill filled or not
438      */
439     protected void drawFillShape(final Shape shape, final boolean fill)
440     {
441         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.draw(shape: {})", shape.getClass().getSimpleName());
442         if (shape instanceof Ellipse2D.Double)
443         {
444             Ellipse2D.Double ellipse = (Ellipse2D.Double) shape;
445             if (fill)
446                 addTransformFill("fillOval", ellipse.getCenterX(), ellipse.getCenterY(), ellipse.width / 2.0,
447                         ellipse.height / 2.0);
448             else
449                 addTransformDraw("drawOval", ellipse.getCenterX(), ellipse.getCenterY(), ellipse.width / 2.0,
450                         ellipse.height / 2.0);
451         }
452         else if (shape instanceof Ellipse2D.Float)
453         {
454             Ellipse2D.Float ellipse = (Ellipse2D.Float) shape;
455             if (fill)
456                 addTransformFill("fillOval", ellipse.getCenterX(), ellipse.getCenterY(), ellipse.width / 2.0,
457                         ellipse.height / 2.0);
458             else
459                 addTransformDraw("drawOval", ellipse.getCenterX(), ellipse.getCenterY(), ellipse.width / 2.0,
460                         ellipse.height / 2.0);
461         }
462         else if (shape instanceof Line2D.Double)
463         {
464             Line2D.Double line = (Line2D.Double) shape;
465             addTransformDraw("drawLine", line.x1, line.y1, line.x2, line.y2);
466         }
467         else if (shape instanceof Line2D.Float)
468         {
469             Line2D.Float line = (Line2D.Float) shape;
470             addTransformDraw("drawLine", line.x1, line.y1, line.x2, line.y2);
471         }
472         else if (shape instanceof Rectangle2D.Double)
473         {
474             Rectangle2D.Double rect = (Rectangle2D.Double) shape;
475             if (fill)
476                 addTransformFill("fillRect", rect.x, rect.y, rect.width, rect.height);
477             else
478                 addTransformDraw("drawRect", rect.x, rect.y, rect.width, rect.height);
479         }
480         else if (shape instanceof Rectangle2D.Float)
481         {
482             Rectangle2D.Float rect = (Rectangle2D.Float) shape;
483             if (fill)
484                 addTransformFill("fillRect", rect.x, rect.y, rect.width, rect.height);
485             else
486                 addTransformDraw("drawRect", rect.x, rect.y, rect.width, rect.height);
487         }
488         else if (shape instanceof SerializablePath)
489         {
490             Path2D.Float path = (Path2D.Float) shape;
491             addTransformPathFloat(path, fill);
492         }
493         else if (shape instanceof Path2D.Float)
494         {
495             Path2D.Float path = (Path2D.Float) shape;
496             addTransformPathFloat(path, fill);
497         }
498         else if (shape instanceof Path2D.Double)
499         {
500             Path2D.Double path = (Path2D.Double) shape;
501             addTransformPathDouble(path, fill);
502         }
503 
504     }
505 
506     @Override
507     public boolean drawImage(final Image img, final AffineTransform xform, final ImageObserver obs)
508     {
509         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawImage()");
510         return true;
511     }
512 
513     @Override
514     public void drawImage(final BufferedImage img, final BufferedImageOp op, final int x, final int y)
515     {
516         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawImage()");
517     }
518 
519     @Override
520     public void drawRenderedImage(final RenderedImage img, final AffineTransform xform)
521     {
522         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawRenderedImage()");
523     }
524 
525     @Override
526     public void drawRenderableImage(final RenderableImage img, final AffineTransform xform)
527     {
528         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawRenderableImage()");
529     }
530 
531     @Override
532     public void drawString(final String str, final int x, final int y)
533     {
534         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawString()");
535         addTransformText("drawString", x, y, str);
536     }
537 
538     @Override
539     public void drawString(final String str, final float x, final float y)
540     {
541         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawString()");
542         addTransformText("drawString", x, y, str);
543     }
544 
545     @Override
546     public void drawString(final AttributedCharacterIterator iterator, final int x, final int y)
547     {
548         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawString()");
549     }
550 
551     @Override
552     public void drawString(final AttributedCharacterIterator iterator, final float x, final float y)
553     {
554         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawString()");
555     }
556 
557     @Override
558     public void drawGlyphVector(final GlyphVector g, final float x, final float y)
559     {
560         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawGlyphVector()");
561     }
562 
563     @Override
564     public void fill(final Shape shape)
565     {
566         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.fill()");
567         drawFillShape(shape, true);
568     }
569 
570     @Override
571     public boolean hit(final Rectangle rect, final Shape s, final boolean onStroke)
572     {
573         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.hit()");
574         return false;
575     }
576 
577     @Override
578     public GraphicsConfiguration getDeviceConfiguration()
579     {
580         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.getDeviceConfiguration()");
581         return null;
582     }
583 
584     @Override
585     public void setComposite(final Composite comp)
586     {
587         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.setComposite()");
588     }
589 
590     @Override
591     public void setPaint(final Paint paint)
592     {
593         this.paint = paint;
594         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.setPaint()");
595     }
596 
597     @Override
598     public void setStroke(final Stroke s)
599     {
600         this.stroke = s;
601         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.setStroke()");
602     }
603 
604     @Override
605     public void setRenderingHint(final Key hintKey, final Object hintValue)
606     {
607         if (hintValue != null)
608         {
609             this.renderingHints.put(hintKey, hintValue);
610         }
611         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.setRenderingHint()");
612     }
613 
614     @Override
615     public Object getRenderingHint(final Key hintKey)
616     {
617         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.getRenderingHint()");
618         return this.renderingHints.get(hintKey);
619     }
620 
621     @Override
622     public void setRenderingHints(final Map<?, ?> hints)
623     {
624         this.renderingHints.clear();
625         this.renderingHints.putAll(hints);
626         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.setRenderingHints()");
627     }
628 
629     @Override
630     public void addRenderingHints(final Map<?, ?> hints)
631     {
632         this.renderingHints.putAll(hints);
633         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.addRenderingHints()");
634     }
635 
636     @Override
637     public RenderingHints getRenderingHints()
638     {
639         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.getRenderingHints()");
640         return this.renderingHints;
641     }
642 
643     @Override
644     public void translate(final int x, final int y)
645     {
646         this.affineTransform.translate(x, y);
647         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.translate()");
648     }
649 
650     @Override
651     public void translate(final double tx, final double ty)
652     {
653         this.affineTransform.translate(tx, ty);
654         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.translate()");
655     }
656 
657     @Override
658     public void rotate(final double theta)
659     {
660         this.affineTransform.rotate(theta);
661         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.rotate()");
662     }
663 
664     @Override
665     public void rotate(final double theta, final double x, final double y)
666     {
667         this.affineTransform.rotate(theta, x, y);
668         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.rotate()");
669     }
670 
671     @Override
672     public void scale(final double sx, final double sy)
673     {
674         this.affineTransform.scale(sx, sy);
675         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.scale()");
676     }
677 
678     @Override
679     public void shear(final double shx, final double shy)
680     {
681         this.affineTransform.shear(shx, shy);
682         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.shear()");
683     }
684 
685     @Override
686     public void transform(final AffineTransform Tx)
687     {
688         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.transform()");
689     }
690 
691     @Override
692     public void setTransform(final AffineTransform Tx)
693     {
694         this.affineTransform = (AffineTransform) Tx.clone();
695         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.setTransform()");
696     }
697 
698     @Override
699     public AffineTransform getTransform()
700     {
701         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.getTransform()");
702         return this.affineTransform;
703     }
704 
705     @Override
706     public Paint getPaint()
707     {
708         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.getPaint()");
709         return this.paint;
710     }
711 
712     @Override
713     public Composite getComposite()
714     {
715         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.getComposite()");
716         return this.composite;
717     }
718 
719     @Override
720     public void setBackground(final Color color)
721     {
722         this.background = color;
723         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.setBackground()");
724     }
725 
726     @Override
727     public Color getBackground()
728     {
729         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.getBackground()");
730         return this.background;
731     }
732 
733     @Override
734     public Stroke getStroke()
735     {
736         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.getStroke()");
737         return this.stroke;
738     }
739 
740     @Override
741     public void clip(final Shape s)
742     {
743         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.clip()");
744     }
745 
746     @Override
747     public FontRenderContext getFontRenderContext()
748     {
749         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.getFontRenderContext()");
750         return new FontRenderContext(this.affineTransform, true, true);
751     }
752 
753     @Override
754     public Graphics create()
755     {
756         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.create()");
757         return new HtmlGraphics2d(); // TODO: clone
758     }
759 
760     @Override
761     public Color getColor()
762     {
763         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.getColor()");
764         return this.color;
765     }
766 
767     @Override
768     public void setColor(final Color c)
769     {
770         this.color = c;
771         this.paint = c; // TODO see how difference between paint and color should be handled
772         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.setColor()");
773     }
774 
775     @Override
776     public void setPaintMode()
777     {
778         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.setPaintMode()");
779     }
780 
781     @Override
782     public void setXORMode(final Color c1)
783     {
784         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.setXORMode()");
785     }
786 
787     @Override
788     public Font getFont()
789     {
790         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.getFont()");
791         return this.font;
792     }
793 
794     @Override
795     public void setFont(final Font font)
796     {
797         this.font = font;
798         this.fontMetrics = this.canvas.getFontMetrics(this.font);
799         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.setFont()");
800     }
801 
802     @Override
803     public FontMetrics getFontMetrics(final Font f)
804     {
805         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.getFontMetrics()");
806         return this.fontMetrics;
807     }
808 
809     @Override
810     public Rectangle getClipBounds()
811     {
812         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.getClipBounds()");
813         return null;
814     }
815 
816     @Override
817     public void clipRect(final int x, final int y, final int width, final int height)
818     {
819         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.clipRect()");
820     }
821 
822     @Override
823     public void setClip(final int x, final int y, final int width, final int height)
824     {
825         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.setClip()");
826     }
827 
828     @Override
829     public Shape getClip()
830     {
831         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.getClip()");
832         return null;
833     }
834 
835     @Override
836     public void setClip(final Shape clip)
837     {
838         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.setClip()");
839     }
840 
841     @Override
842     public void copyArea(final int x, final int y, final int width, final int height, final int dx, final int dy)
843     {
844         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.copyArea()");
845     }
846 
847     @Override
848     public void drawLine(final int x1, final int y1, final int x2, final int y2)
849     {
850         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawLine()");
851         addTransformDraw("drawLine", x1, y1, x2, y2);
852     }
853 
854     @Override
855     public void fillRect(final int x, final int y, final int width, final int height)
856     {
857         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.fillRect()");
858         addTransformFill("fillRect", x, y, width, height);
859     }
860 
861     @Override
862     public void clearRect(final int x, final int y, final int width, final int height)
863     {
864         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.clearRect()");
865         addTransformDraw("clearRect", x, y, width, height);
866     }
867 
868     @Override
869     public void drawRoundRect(final int x, final int y, final int width, final int height, final int arcWidth,
870             final int arcHeight)
871     {
872         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawRoundRect()");
873     }
874 
875     @Override
876     public void fillRoundRect(final int x, final int y, final int width, final int height, final int arcWidth,
877             final int arcHeight)
878     {
879         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.fillRoundRect()");
880     }
881 
882     @Override
883     public void drawOval(final int x, final int y, final int width, final int height)
884     {
885         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawOval()");
886         addTransformDraw("drawOval", x, y, width, height);
887     }
888 
889     @Override
890     public void fillOval(final int x, final int y, final int width, final int height)
891     {
892         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.fillOval()");
893         addTransformFill("fillOval", x, y, width, height);
894     }
895 
896     @Override
897     public void drawArc(final int x, final int y, final int width, final int height, final int startAngle, final int arcAngle)
898     {
899         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawArc()");
900     }
901 
902     @Override
903     public void fillArc(final int x, final int y, final int width, final int height, final int startAngle, final int arcAngle)
904     {
905         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.fillArc()");
906     }
907 
908     @Override
909     public void drawPolyline(final int[] xPoints, final int[] yPoints, final int nPoints)
910     {
911         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.fillPolyline()");
912     }
913 
914     @Override
915     public void drawPolygon(final int[] xPoints, final int[] yPoints, final int nPoints)
916     {
917         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawPolygon()");
918     }
919 
920     @Override
921     public void fillPolygon(final int[] xPoints, final int[] yPoints, final int nPoints)
922     {
923         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.fillPolygon()");
924     }
925 
926     @Override
927     public boolean drawImage(final Image img, final int x, final int y, final ImageObserver observer)
928     {
929         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawImage()");
930         return false;
931     }
932 
933     @Override
934     public boolean drawImage(final Image img, final int x, final int y, final int width, final int height,
935             final ImageObserver observer)
936     {
937         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawImage()");
938         return false;
939     }
940 
941     @Override
942     public boolean drawImage(final Image img, final int x, final int y, final Color bgcolor, final ImageObserver observer)
943     {
944         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawImage()");
945         return false;
946     }
947 
948     @Override
949     public boolean drawImage(final Image img, final int x, final int y, final int width, final int height, final Color bgcolor,
950             final ImageObserver observer)
951     {
952         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawImage()");
953         return false;
954     }
955 
956     @Override
957     public boolean drawImage(final Image img, final int dx1, final int dy1, final int dx2, final int dy2, final int sx1,
958             final int sy1, final int sx2, final int sy2, final ImageObserver observer)
959     {
960         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawImage()");
961         return false;
962     }
963 
964     @Override
965     public boolean drawImage(final Image img, final int dx1, final int dy1, final int dx2, final int dy2, final int sx1,
966             final int sy1, final int sx2, final int sy2, final Color bgcolor, final ImageObserver observer)
967     {
968         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.drawImage()");
969         return false;
970     }
971 
972     @Override
973     public void dispose()
974     {
975         CategoryLogger.filter(Cat.WEB).trace("HTMLGraphics2D.dispose()");
976     }
977 
978 }